August 2014

Sun Mon Tue Wed Thu Fri Sat
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            








« Creating a face-recognising security cam with a Raspberry Pi – Part 3 | Main | Adding per-vertex colours to a transient AutoCAD shell using .NET »

September 28, 2012

Generating a transient box inside AutoCAD using .NET

Since posting about the ability to display transient graphics as an ongoing feature that can react to mouse input, I’ve been thinking of the steps that would be needed to generate a custom gizmo comparable with AutoCAD’s viewcube.

The post starts to go in that direction by displaying a couple of types of transient graphics in AutoCAD as a “standard” feature: firstly we’re going to show screen-fixed text (with code pulled directly from this post) and then we’re going to place a a transient box in the drawing itself.

This may be pre-cursor to displaying a box-like gizmo, but then again it may not. It’s still early days as far as this implementation is concerned, and I felt that just the code that uses WorldGeometry.Shell() was relevant to many developers, as it can be quite tricky to get these kind of graphics to display properly.

Here’s the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.GraphicsInterface;

using Autodesk.AutoCAD.Colors;

using System.Collections.Generic;

 

namespace TransientSelection

{

  public class TransientBox : Transient

  {

    // Internal state

 

    private TextStyle _style;

    Point3dCollection _verts;

    IntegerCollection _faces;

    EdgeData _edgeData;

    FaceData _faceData;

    VertexData _vertData;

 

    public TransientBox(double side)

    {

      // Create the style for our text

 

      _style = new TextStyle();

      _style.Font =

        new FontDescriptor("Calibri", false, true, 0, 0);

      _style.TextSize = 10;

 

      // Add our vertices manually (8 of them for a box)

 

      _verts = new Point3dCollection();

 

      _verts.Add(Point3d.Origin);                // 0

      _verts.Add(new Point3d(side, 0, 0));       // 1

      _verts.Add(new Point3d(side, side, 0));    // 2

      _verts.Add(new Point3d(0, side, 0));       // 3

 

      _verts.Add(new Point3d(0, 0, side));       // 4

      _verts.Add(new Point3d(side, 0, side));    // 5

      _verts.Add(new Point3d(side, side, side)); // 6

      _verts.Add(new Point3d(0, side, side));    // 7

 

      // Our faces are defined in sets of 3 vertices

      // (listed below two per line, each making a square face)

 

      int[] polys =

      {

        0, 1, 2, 2, 3, 0, // Bottom

        0, 4, 5, 5, 1, 0, // Front

        1, 5, 6, 6, 2, 1, // Right

        2, 6, 7, 7, 3, 2, // Back

        0, 3, 7, 7, 4, 0, // Left

        4, 7, 6, 6, 5, 4  // Top

      };

      const int polySize = 3;

 

      // Create our faces from the polys array

 

      _faces = new IntegerCollection();

      for (int p = 0; p < (polys.Length / polySize); p++)

      {

        _faces.Add(polySize);

        for (int v = 0; v < polySize; v++)

        {

          _faces.Add(polys[p * polySize + v]);

        }

      }

 

      // Create our edge data

 

      _edgeData = new EdgeData();

 

      // Each face's edges should have the same colour as the face

 

      _edgeData.SetColors(

        new short[]

        {

          1, 1, 1, 2, 2, 2, 3, 3, 3,

          4, 4, 4, 5, 5, 5, 6, 6, 6,

          7, 7, 7, 8, 8, 8, 9, 9, 9,

          10, 10, 10, 11, 11, 11, 12, 12, 12

        }

      );

 

      // Create our face data

 

      _faceData = new FaceData();

 

      // The face colours match their edges

 

      _faceData.SetColors(

        new short[]

        {

          1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12

        }

      );

 

      // Create our vertex data

 

      _vertData = new VertexData();

      _vertData.OrientationFlag = OrientationType.Clockwise;

    }

 

    protected override int SubSetAttributes(DrawableTraits traits)

    {

      return (int)DrawableAttributes.None;

    }

 

    protected override void SubViewportDraw(ViewportDraw vd)

    {

      // Draw our screen-fixed text

 

      DrawText(vd.Geometry, "ViewportDraw");

    }

 

    protected override bool SubWorldDraw(WorldDraw wd)

    {

      // Draw our box

 

      wd.Geometry.Shell(

        _verts, _faces, _edgeData, _faceData, _vertData, false

      );

 

      // Draw our screen-fixed text

 

      DrawText(wd.Geometry, "WorldDraw");

      return true;

    }

 

    private void DrawText(Geometry g, string text)

    {

      // We make use of another interface to push our transforms

 

      if (g != null)

      {

        // Push our transforms onto the stack

 

        g.PushOrientationTransform(OrientationBehavior.Screen);

 

        g.PushPositionTransform(

          PositionBehavior.Screen,

          new Point2d(30, 30)

        );

 

        // Draw our screen-fixed text

 

        g.Text(

          new Point3d(0, 0, 0),  // Position

          new Vector3d(0, 0, 1), // Normal

          new Vector3d(1, 0, 0), // Direction

          text,                  // Text

          true,                  // Rawness

          _style                 // TextStyle

        );

 

        // Remember to pop our transforms off the stack

 

        g.PopModelTransform();

        g.PopModelTransform();

      }

    }

 

    protected override void OnDeviceInput(DeviceInputEventArgs e)

    {

      base.OnDeviceInput(e);

    }

 

    protected override void OnPointInput(PointInputEventArgs e)

    {

      base.OnPointInput(e);

    }

  }

 

  public class Commands

  {

    TransientBox _tb = null;

 

    [CommandMethod("TB")]

    public void TransientBox()

    {

      _tb = new TransientBox(10);

 

      // Tell AutoCAD to call into this transient's extended

      // protocol when appropriate

 

      Transient.CapturedDrawable = _tb;

 

      // Go ahead and draw the transient

 

      TransientManager.CurrentTransientManager.AddTransient(

        _tb, TransientDrawingMode.Main,

        128, new IntegerCollection()

      );

    }

 

    [CommandMethod("TBR")]

    public void RemoveTransientBox()

    {

      // Erase the transient graphics and dispose of the transient

 

      if (_tb != null)

      {

        TransientManager.CurrentTransientManager.EraseTransient(

          _tb,

          new IntegerCollection()

        );

        _tb.Dispose();

        _tb = null;

      }

    }

  }

}

I originally defined the box’s faces with four sides, but then decided to split them into three-sided polygons, instead (this is something that AutoCAD will do, in any case, and it allowed me to assign different colours within a single face of the cube), mainly for “debugging” purposes.

I struggled for a long time with getting the mesh to display properly: some of the faces were displayed inappropriately above others. I originally assumed this was due to generation of the face normals – so I added code to generate them manually, as well as trying to switch on automatic face normal generation via the last argument of the Shell() method – but it turned out to be related to the transient graphics display mode I’d chosen: changing this to TransientDrawingMode.Main from TransientDrawingMode.DirectShortTem fixed all my problems. I also decided not to generate or define face normals at all, as this didn’t appear to have any effect on the quality of the geometry or the display performance.

Here’s what we see when we run the TB command and adjust the view while still in wireframe mode:

Our transient box

While we’re orbiting or when we switch to a shaded visual style, we see the cube’s faces as well as the text displayed properly:

In a shaded visual style

In the next post, we’re going to take a look at some pretty interesting capabilities of the Shell() method, to see what impact adding per-vertex colours has on our transient geometry.

blog comments powered by Disqus

Feed/Share

10 Random Posts