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            








« Heading home | Main | A framework for defining AutoCAD entity jigs using .NET »

December 03, 2012

Jigging a frustum-shaped AutoCAD solid using .NET

As promised, here’s the cleaned-up code to jig a frustum inside AutoCAD. When I took on the task of writing this code – live, during the “AutoCAD Programming Gurus Go Head to Head” class at AU – I thought to myself “that should be easy enough – I’m sure I have some code to jig a solid on my blog”. Well, I did, but it turned out the code showed how to jig a box, and the code was in Python and Ruby but not C#. So I ended up having to code for my supper, after all. ;-)

One of the main components of the request was for the jigged solid to reflect the current visual style: the main trick to getting this is to work is to add the solid to the database prior to starting the jig, a technique that also works well when jigging a block with attributes.

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;

 

namespace CreateFrustumJig

{

  // Our jig has three phases: we start by selecting the bottom

  // radiu, then the height, then the top radius of the frustum

 

  enum JigPhase

  {

    Bottom = 0,

    Height = 1,

    Top = 2

  }

 

  // Our jig class

 

  class FrustumJig : EntityJig

  {

    // Member data

 

    Matrix3d _ucs;

    Point3d _cen;

    Solid3d _sol;

    JigPhase _phase;

    double _top, _bot, _hgt;

 

    // A public property for our "phase"

 

    public JigPhase Phase

    {

      get { return _phase; }

      set { _phase = value; }

    }

 

    // The constructor

 

    public FrustumJig(Matrix3d ucs, Solid3d sol, Point3d cen)

      : base(sol)

    {

      _ucs = ucs;

      _sol = sol;

      _cen = cen;

 

      // Start at the bottom

 

      _phase = JigPhase.Bottom;

 

      // Use a number close to zero, but large enough for

      // the CreateFrustum call to work

 

      _top = 1e-5;

      _bot = _top;

      _hgt = _top;

    }

 

    protected override SamplerStatus Sampler(JigPrompts prompts)

    {

      var opts = new JigPromptDistanceOptions();

      opts.UserInputControls =

        (UserInputControls.Accept3dCoordinates

        | UserInputControls.NoZeroResponseAccepted

        | UserInputControls.NoNegativeResponseAccepted);

 

      // All our distance inputs will be with a base point

      // (and most will be from the initial center point)

 

      opts.BasePoint = _cen.TransformBy(_ucs);

      opts.UseBasePoint = true;

      opts.Cursor = CursorType.RubberBand;

 

      if (_phase == JigPhase.Bottom)

      {

        opts.Message = "\nSpecify bottom radius: ";

      }

      else if (_phase == JigPhase.Height)

      {

        opts.Message = "\nSpecify height: ";

      }

      else if (_phase == JigPhase.Top)

      {

        // We'll adjust the base point for the top radius

        // selection to be the center of the top circle

 

        opts.Message = "\nSpecify top radius: ";

        opts.BasePoint +=

          new Vector3d(0, 0, _hgt).TransformBy(_ucs);

      }

 

      PromptDoubleResult pdr = prompts.AcquireDistance(opts);

 

      if (pdr.Status == PromptStatus.OK)

      {

        // Work out the value to check against based on the phase

 

        double prev =

          (_phase == JigPhase.Bottom ? _bot :

            (_phase == JigPhase.Height ? _hgt : _top)

          );

 

        // If the difference between the new value and its

        // previous value is negligible, return "no change"

 

        if (

          System.Math.Abs(prev - pdr.Value) <

          Tolerance.Global.EqualPoint

        )

        {

          return SamplerStatus.NoChange;

        }

        else

        {

          // Otherwise we update the appropriate variable

          // based on the phase

 

          if (_phase == JigPhase.Bottom)

            _bot = pdr.Value;

          else if (_phase == JigPhase.Height)

            _hgt = pdr.Value;

          else

            _top = pdr.Value;

        }

      }

      return SamplerStatus.OK;

    }

 

    protected override bool Update()

    {

      try

      {

        // Try to create a frustum with the provided parameters

 

        _sol.CreateFrustum(_hgt, _bot, _bot, _top);

 

        // Transform it appropriately so that it sits on the

        // base point relative to the current UCS

 

        _sol.TransformBy(

          Matrix3d.Displacement(

            _cen.GetAsVector() + new Vector3d(0, 0, _hgt / 2)

          ).PreMultiplyBy(_ucs)

        );

      }

      catch (System.Exception)

      {

        return false;

      }

      return true;

    }

 

    public Entity GetEntity()

    {

      return Entity;

    }

  }

 

  public class Commands

  {

    [CommandMethod("FJ")]

    public void FrustumJig()

    {

      var doc =

        Application.DocumentManager.MdiActiveDocument;

      var db = doc.Database;

      var ed = doc.Editor;

 

      // First let's get the start position of the frustum

 

      var opts =

        new PromptPointOptions("\nSpecify frustum location: ");

      var ppr = ed.GetPoint(opts);

 

      if (ppr.Status == PromptStatus.OK)

      {

        // In order for the visual style to be respected,

        // we'll add the to-be-jigged solid to the database

 

        Transaction tr =

          doc.TransactionManager.StartTransaction();

        using (tr)

        {

          var btr =

            (BlockTableRecord)tr.GetObject(

              db.CurrentSpaceId, OpenMode.ForWrite

            );

 

          var sol = new Solid3d();

          btr.AppendEntity(sol);

          tr.AddNewlyCreatedDBObject(sol, true);

 

          // Create our jig object passing in the selected point

 

          var fj =

            new FrustumJig(

              ed.CurrentUserCoordinateSystem, sol, ppr.Value

            );

 

          // Perform the jig operation in a loop

 

          while (true)

          {

            var res = ed.Drag(fj);

 

            switch (res.Status)

            {

              // We progress the "phase" each time

 

              case PromptStatus.OK:

                if (fj.Phase == JigPhase.Bottom)

                  fj.Phase = JigPhase.Height;

                else if (fj.Phase == JigPhase.Height)

                  fj.Phase = JigPhase.Top;

                else if (fj.Phase == JigPhase.Top)

                {

                  tr.Commit();

                  return;

                }

                break;

 

              // The user cancelled the command

 

              default:

                return;

            }

          }

        }

      }

    }

  }

}

And here is our FJ command in action:

Frustum Jig

Looking at the above code, it occurs to me there’s some opportunity to factor out the input phases into a more generalised mechanism: the calling code would pass in a list of these input phases and provide a callback for the entity creation. I’ll add this to my list of things to look at – I think it might prove to be a straightforward way to simplify the code used to jig objects inside AutoCAD. We’ll see.

blog comments powered by Disqus

Feed/Share

10 Random Posts