Jigging an AutoCAD leader with directional text using .NET

I'm sure some of you will be relieved to see I can still (more or less) manage to write code for AutoCAD… the last few days I've been feeling quite under-the-weather, so today's post is a little bit of "comfort code": sometimes while you're brain is struggling to handle the unfamiliar, it's very happy to tackle the familiar.

At least that was the plan, and the reason I decided to tackle this recent question from Thomas Heitz:

I would like to write an code to change automatically the structure label style while dragging the label. So I created two labels styles: left and right. The label style would be set according to the structure position: left style at left structure side and right style at right structure side.

The first thing to bear in mind is that Thomas is talking about Civil 3D, which I don't use. So the code I'm showing today will do something approximating what Thomas is looking for, but using an MLeader object (a standard Leader doesn't have associated text, although the LEADER command does create it). Hopefully it'll still be of some use to Thomas.

So why didn't this go exactly as I'd planned? I thought it would be a simple enough one to crank out, but for one reason or another – whether my weakened mental state, my fading AutoCAD skills or the unexpected gnarliness of the problem – I ended up spending my entire evening on it. Oh well.

Here's what I managed to come up with:

Directional Leader

Much of the trickiness related to using MLeader from an EntityJig: firstly we need the MLeader to be database-resident to be jigged at all – a technique that's pretty common when jigging solids or blocks with attributes – but then there were some very subtle issues relating to geometry display. The MText would appear at the start of the leader, and then the leader would display from the origin until we moved the mouse to start the Sampler/Update cycle in the jig.

So there's some slightly quirky C# code below – such as making the MLeader invisible and only initializing it when really needed – but it works well enough for my purposes.

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Runtime;

 

namespace LeaderPlacement

{

  public class LeaderCmds

  {

    class DirectionalLeaderJig : EntityJig

    {

      private Point3d _start, _end;

      private string _contents;

      private int _index;

      private int _lineIndex;

      private bool _started;

 

      public DirectionalLeaderJig(string txt, Point3d start, MLeader ld) : base(ld)

      {

        // Store info that's passed in, but don't init the MLeader

 

        _contents = txt;

        _start = start;

        _end = start;

 

        _started = false;

      }

 

      // A fairly standard Sampler function

 

      protected override SamplerStatus Sampler(JigPrompts prompts)

      {

        var po = new JigPromptPointOptions();

        po.UserInputControls =

          (UserInputControls.Accept3dCoordinates |

           UserInputControls.NoNegativeResponseAccepted);

        po.Message = "\nEnd point";  

 

        var res = prompts.AcquirePoint(po);

 

        if (_end == res.Value)

        {

          return SamplerStatus.NoChange;

        }

        else if (res.Status == PromptStatus.OK)

        {

          _end = res.Value;

          return SamplerStatus.OK;

        }

        return SamplerStatus.Cancel;

      }

 

      protected override bool Update()

      {

        var ml = (MLeader)Entity;

 

        if (!_started)

        {

          if (_start.DistanceTo(_end) > Tolerance.Global.EqualPoint)

          {

            // When the jig actually starts - and we have mouse movement -

            // we create the MText and init the MLeader

 

            ml.ContentType = ContentType.MTextContent;

            var mt = new MText();

            mt.Contents = _contents;

            ml.MText = mt;

 

            // Create the MLeader cluster and add a line to it

 

            _index = ml.AddLeader();

            _lineIndex = ml.AddLeaderLine(_index);

 

            // Set the vertices on the line

 

            ml.AddFirstVertex(_lineIndex, _start);

            ml.AddLastVertex(_lineIndex, _end);

 

            // Make sure we don't do this again

 

            _started = true;

          }

        }

        else

        {

          // We only make the MLeader visible on the second time through

          // (this also helps avoid some strange geometry flicker)

 

          ml.Visible = true;

 

          // We already have a line, so just set its last vertex

 

          ml.SetLastVertex(_lineIndex, _end);

        }

 

        if (_started)

        {

          // Set the direction of the text to depend on the X of the end-point

          // (i.e. is if to the left or right of the start-point?)

 

          var dl = new Vector3d((_end.X >= _start.X ? 1 : -1), 0, 0);

          ml.SetDogleg(_index, dl);

        }

 

        return true;

      }

    }

 

    [CommandMethod("DL")]

    public void DirectionalLeader()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      var ed = doc.Editor;

      var db = doc.Database;

 

      // Ask the user for the string and the start point of the leader

 

      var pso = new PromptStringOptions("\nEnter text");

      pso.AllowSpaces = true;

      var pr = ed.GetString(pso);

 

      if (pr.Status != PromptStatus.OK)

        return;

 

      var ppr = ed.GetPoint("\nStart point of leader");

      if (ppr.Status != PromptStatus.OK)

        return;

 

      // Start a transaction, as we'll be jigging a db-resident object

 

      using (var tr = db.TransactionManager.StartTransaction())

      {

        var bt =

          (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead, false);

        var btr =

          (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite, false);

 

        // Create and pass in an invisible MLeader

        // This helps avoid flickering when we start the jig

 

        var ml = new MLeader();

        ml.Visible = false;

 

        // Create jig

 

        var jig = new DirectionalLeaderJig(pr.StringResult, ppr.Value, ml);

 

        // Add the MLeader to the drawing: this allows it to be displayed

 

        btr.AppendEntity(ml);

        tr.AddNewlyCreatedDBObject(ml, true);

 

        // Set end point in the jig

 

        var res = ed.Drag(jig);

 

        // If all is well, commit

 

        if (res.Status == PromptStatus.OK)

        {

          tr.Commit();

        }

      }

    }

  }

}

 

As you can see from the recording, above, the approach isn't quite right (although that depends on your requirements, I suppose): the text justification still causes the direction to jump a bit when you move across the threshhold. But it should be a good starting point for anyone who cares deeply enough about getting it working perfectly.

2 responses to “Jigging an AutoCAD leader with directional text using .NET”

  1. Good to see you back on the tools.

    1. Thanks, Dale.

      It does feel good to dip my toe back into the water from time to time. There's a lot going on with AutoCAD, and I definitely want to stay connected with both the product and the community.

      Kean

  2. Thank you very much Kean.

    This will be very useful! Keep asking Thomas 🙂

  3. >>> I’m sure some of you will be relieved to see I can still (more or less) manage to write code for AutoCAD

    Yes, It's good to see for a change ...

  4. Thanks for this.
    I had to look twice when I saw the subject and the date. I had almost given up hope of seeing AutoCAD code here again. 🙂 Very encouraging.
    Hope you are feeling well soon.

  5. Is there a way to create a jig where the last point you pick is the arrow? The way I envision it is the user always picks the second point (and third etc.) until no point is returned. The first point is set programmatically, then for as long as the user is picking points, the leader follows the points creating the arrow on at the cursor. It is essentially drawing a leader (not MLeader) backwards from what you would do in vanilla AutoCAD.

    I can make it happen if I could get some ideas.

    1. Absolutely! It's not something I've done (that I recall, anyway), but you could definitely do it this way.

      Kean

  6. Thank you very much for your help, you have saved a lot of my time

  7. thankyou !

  8. Hello!
    Can you draw a polyline with an arrow using a jig that allows you to change the direction of the arrow

    1. I would think so, but I've never done it. Please post your support questions to the AutoCAD .NET forum.

      Kean

Leave a Reply to Mark Johnston Cancel reply

Your email address will not be published. Required fields are marked *