Creating a multileader in AutoCAD using a jig from .NET

I'm now back from a fantastic break in Italy and am trying hard to catch back up. Next week I'm off again to San Diego (work, this time), which may cause further interruptions in blog postings.

This question came through from Genésio from Brazil:

I wish jig a leader with an bubble in the and of the leader, at the same time. Can you help me. Perhaps post the solution in your blog (through the interface).

It took me a while - frustratingly long, in fact, and probably this is not exactly what Genésio is after - but here's what I managed to come up with. The "bubble" is framed MText, but it should be modifiable to use a classic bubble block, instead. I drew heavily on this previous post for the jig code.

The positioning of the text took some work, but I'm reasonably happy with the results. If anyone has tweaks to suggest, please post a comment.

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 DimensionLibrary

{

  public class DimensionCmds

  {

    class MLeaderJig : EntityJig

    {

      Point3dCollection m_pts;

      Point3d m_tempPoint;

      string m_contents;

      int m_leaderIndex;

      int m_leaderLineIndex;

      public MLeaderJig(string contents)

        : base(new MLeader())

      {

        // Store the string passed in

        m_contents = contents;

        // Create a point collection to store our vertices

        m_pts = new Point3dCollection();

        // Create mleader and set defaults

        MLeader ml = Entity as MLeader;

        ml.SetDatabaseDefaults();

        // Set up the MText contents

        ml.ContentType = ContentType.MTextContent;

        MText mt = new MText();

        mt.SetDatabaseDefaults();

        mt.Contents = m_contents;

        ml.MText = mt;

        ml.TextAlignmentType =

          TextAlignmentType.LeftAlignment;

        ml.TextAttachmentType =

          TextAttachmentType.AttachmentMiddle;

        // Set the frame and landing properties

        ml.EnableDogleg = true;

        ml.EnableFrameText = true;

        ml.EnableLanding = true;

        // Reduce the standard landing gap

        ml.LandingGap = 0.05;

        // Add a leader, but not a leader line (for now)

        m_leaderIndex = ml.AddLeader();

        m_leaderLineIndex = -1;

      }

      protected override SamplerStatus Sampler(

        JigPrompts prompts

      )

      {

        JigPromptPointOptions opts =

          new JigPromptPointOptions();

        // Not all options accept null response

        opts.UserInputControls =

          (UserInputControls.Accept3dCoordinates |

          UserInputControls.NoNegativeResponseAccepted

          );

        // Get the first point

        if (m_pts.Count == 0)

        {

          opts.UserInputControls |=

            UserInputControls.NullResponseAccepted;

          opts.Message =

            "\nStart point of multileader: ";

          opts.UseBasePoint = false;

        }

        // And the second

        else if (m_pts.Count == 1)

        {

          opts.BasePoint = m_pts[m_pts.Count - 1];

          opts.UseBasePoint = true;

          opts.Message =

            "\nSpecify multileader vertex: ";

        }

        // And subsequent points

        else if (m_pts.Count > 1)

        {

          opts.UserInputControls |=

            UserInputControls.NullResponseAccepted;

          opts.BasePoint = m_pts[m_pts.Count - 1];

          opts.UseBasePoint = true;

          opts.SetMessageAndKeywords(

            "\nSpecify multileader vertex or [End]: ",

            "End"

          );

        }

        else // Should never happen

          return SamplerStatus.Cancel;

        PromptPointResult res =

          prompts.AcquirePoint(opts);

        if (res.Status == PromptStatus.Keyword)

        {

          if (res.StringResult == "End")

          {

            return SamplerStatus.Cancel;

          }

        }

        if (m_tempPoint == res.Value)

        {

          return SamplerStatus.NoChange;

        }

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

        {

          m_tempPoint = res.Value;

          return SamplerStatus.OK;

        }

        return SamplerStatus.Cancel;

      }

      protected override bool Update()

      {

        try

        {

          if (m_pts.Count > 0)

          {

            // Set the last vertex to the new value

            MLeader ml = Entity as MLeader;

            ml.SetLastVertex(

              m_leaderLineIndex,

              m_tempPoint

            );

            // Adjust the text location

            Vector3d dogvec =

              ml.GetDogleg(m_leaderIndex);

            double doglen =

              ml.DoglegLength;

            double landgap =

              ml.LandingGap;

            ml.TextLocation =

              m_tempPoint +

              ((doglen + landgap) * dogvec);

          }

        }

        catch (System.Exception ex)

        {

          Document doc =

            Application.DocumentManager.MdiActiveDocument;

          doc.Editor.WriteMessage(

            "\nException: " + ex.Message

          );

          return false;

        }

        return true;

      }

      public void AddVertex()

      {

        MLeader ml = Entity as MLeader;

        // For the first point...

        if (m_pts.Count == 0)

        {

          // Add a leader line

          m_leaderLineIndex =

            ml.AddLeaderLine(m_leaderIndex);

          // And a start vertex

          ml.AddFirstVertex(

            m_leaderLineIndex,

            m_tempPoint

          );

          // Add a second vertex that will be set

          // within the jig

          ml.AddLastVertex(

            m_leaderLineIndex,

            new Point3d(0, 0, 0)

          );

        }

        else

        {

          // For subsequent points,

          // just add a vertex

          ml.AddLastVertex(

            m_leaderLineIndex,

            m_tempPoint

          );

        }

        // Reset the attachment point, otherwise

        // it seems to get forgotten

        ml.TextAttachmentType =

          TextAttachmentType.AttachmentMiddle;

        // Add the latest point to our history

        m_pts.Add(m_tempPoint);

      }

      public void RemoveLastVertex()

      {

        // We don't need to actually remove

        // the vertex, just reset it

        MLeader ml = Entity as MLeader;

        if (m_pts.Count >= 1)

        {

          Vector3d dogvec =

            ml.GetDogleg(m_leaderIndex);

          double doglen =

            ml.DoglegLength;

          double landgap =

            ml.LandingGap;

          ml.TextLocation =

            m_pts[m_pts.Count - 1] +

            ((doglen + landgap) * dogvec);

        }

      }

      public Entity GetEntity()

      {

        return Entity;

      }

    }

    [CommandMethod("MYML")]

    public void MyMLeaderJig()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

      Database db = doc.Database;

      // Get the text outside of the jig

      PromptStringOptions pso =

        new PromptStringOptions(

          "\nEnter text: "

        );

      pso.AllowSpaces = true;

      PromptResult pr =

        ed.GetString(pso);

      if (pr.Status == PromptStatus.OK)

      {

        // Create MleaderJig

        MLeaderJig jig =

          new MLeaderJig(pr.StringResult);

        // Loop to set vertices

        bool bSuccess = true, bComplete = false;

        while (bSuccess && !bComplete)

        {

          PromptResult dragres = ed.Drag(jig);

          bSuccess =

            (dragres.Status == PromptStatus.OK);

          if (bSuccess)

            jig.AddVertex();

          bComplete =

            (dragres.Status == PromptStatus.None);

          if (bComplete)

            jig.RemoveLastVertex();

        }

        if (bComplete)

        {

          // Append entity

          Transaction tr =

            db.TransactionManager.StartTransaction();

          using (tr)

          {

            BlockTable bt =

              (BlockTable)tr.GetObject(

                db.BlockTableId,

                OpenMode.ForRead,

                false

              );

            BlockTableRecord btr =

              (BlockTableRecord)tr.GetObject(

                bt[BlockTableRecord.ModelSpace],

                OpenMode.ForWrite,

                false

              );

            btr.AppendEntity(jig.GetEntity());

            tr.AddNewlyCreatedDBObject(

              jig.GetEntity(),

              true

            );

            tr.Commit();

          }

        }

      }

    }

  }

}

Here's what you get when you run the MYML command:

Jigged_mleader

17 responses to “Creating a multileader in AutoCAD using a jig from .NET”

  1. Could you make a sample as the project 'BlockView' in ARX SDK using .net?

  2. Could you Creat a multileader in AutoCAD using a jig from 3D point to another.
    for ex (23,43,566) to (133,455,988)

  3. This is really two questions, IMO:

    1) Can multileaders support 3D vertices?

    I haven't tried it myself, but the function signature does accept 3D points, so I assume it's 3D-capable (rather than being planar). If it were planar, then you could still do it, as long as the plane it's on intersects both points you mentioned.

    2) Can jigs support 3D input?

    There's nothing to stop you defining a jig that understands point input from the user in 3D coordinates: the trick is to make it logical. The most logical way to accept 3D input is to project the point information in the jig into the current UCS. The "jig3d" sample on the ObjectARX SDK (under samples/graphics/jig3d) shows how to do this from C++ - it shouldn't be hard to map the technique to .NET.

    As for arbitrary 3D mouse input... this is something that is just hard to do. Applications doing things like piping come across this a lot, and often solve it by allowing movement in one of several constrained directions that can be selected by clicking on glyphs that indicate the direction you want to go in. Design Review does much the same when sectioning 3D views.

    The other option is to just enter the coordinates via the keyboard. Which may very well just work with this jig, thinking about it.

    Regards,

    Kean

  4. How can I retrieve the contents (i.e value) of a text entity with .net after adding the entity to a selection set.

  5. You can open the entity and check its text property. This post shows how to iterate through a selection set. You will simply need to check whether it's a text entity and check the relevant property.

    Kean

  6. Can we implement JIG using AutoLISP?

  7. No, but you can use (grdraw) and (grvecs) to draw temporary graphics. This technique is far from providing as much control as with jigs, though.

    Kean

  8. Does this leader work from either direction, or just one? I had to translate your code into VB.net, and it only appears to draw the way you show in the picture at the end of your article (From Left to Right). Do you know if this is a limitation of the translation, or of AutoCAD?

  9. Actually it's neither - let's call it a design decision I made when writing the code. 🙂

    You can negate the "dog leg" vector (making it with a positive X value rather than a negative one) and set it back on the multi-leader. You should only need to do this if it hasn't been done before (whether when you set up the multileader initially or in the Update() function).

    Kean

  10. Hi, I would like to achieve the same. A multileader flips direction if the angle between the dogleg and leader line is less than 45°.

    Dim dogvec As Vector3d = New Vector3d(-1, 0, 0)
    in the Update() function didn't seem to have any effect. the leader is still left to right.

  11. Did you try with a positive X value, rather than a negative one?

    Kean

  12. ml.GetDogleg(m_leaderIndex) always returned (1,0,0) so I tried (-1,0,0). Other vectors have interesting results but swap the leader around.
    TextLocation and SetLastVertex are the only two items that gets updated and neither seem to have any automatic influence on the direction of leader from the text.

  13. *but don't swap the leader around.

  14. Sorry..I found it.
    ml.SetDogleg(m_leaderLineIndex, dogvec)

  15. I was able to create a new mleaderstyle, but when I create a mleader with this new style I get a left attachment at the MiddleOfTop as desired, but the right attachment is always is UnderlineTopLine. I am able to programmatically set the left attachment to Middle of top line, but I do not know how to get the Right attachment to also set to Middle of top line.
    Can you help?

    1. Sorry - I'm not able to provide support. Please post your question - with your code - to the AutoCAD .NET forum.

      Kean

      1. Thank you for a response. I understand and apologize for troubling you.

Leave a Reply to Manoj Vibhute Cancel reply

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