Creating an AutoCAD multileader spraying out from central text using .NET

Some more fun with multileaders... this post shows some code that adds multiple leader lines to a multileader/MLeader, each of them "spraying" out from the central text. We ask the user for the central point and the location of the first leader along with the number of leader lines to "spray". These lines then get created at evenly spaced angles around from this initial location.

I can't think of anything immediately useful about this technique, other than it was fun to write the code and it shows how to add multiple leader lines. It also generates some fun results. 🙂

Here's the C# code:

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

namespace DimensionLibrary

{

  public class DimensionCommands

  {

    [CommandMethod("spray")]

    static public void SprayMultiLeader()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

      Database db = doc.Database;

      // Get the start point of the leader

      PromptPointResult ppr =

        ed.GetPoint(

          "\nSpecify position of text: "

        );

      if (ppr.Status != PromptStatus.OK)

        return;

      Point3d startPt = ppr.Value;

      // Get the end point of the leader

      PromptPointOptions ppo =

        new PromptPointOptions(

          "\nSpecify first arrowhead location: "

        );

      ppo.BasePoint = startPt;

      ppo.UseBasePoint = true;

      ppr = ed.GetPoint(ppo);

      if (ppr.Status != PromptStatus.OK)

        return;

      Point3d endPt = ppr.Value;

      // Get the number of leader lines

      // to "spray" out from the center

      PromptIntegerOptions pio =

        new PromptIntegerOptions(

          "\nNumber of leader lines <1>: "

        );

      pio.AllowNone = true;

      pio.AllowZero = false;

      PromptIntegerResult pir =

        ed.GetInteger(pio);

      int numLines;

      if (pir.Status == PromptStatus.None)

        numLines = 1;

      else

      {

        if (pir.Status != PromptStatus.OK)

          return;

        else

          numLines = pir.Value;

      }

      Transaction tr =

        db.TransactionManager.StartTransaction();

      using (tr)

      {

        try

        {

          BlockTable bt =

            (BlockTable)tr.GetObject(

              db.BlockTableId,

              OpenMode.ForRead

            );

          BlockTableRecord btr =

            (BlockTableRecord)tr.GetObject(

              bt[BlockTableRecord.ModelSpace],

              OpenMode.ForWrite

            );

          // Create the MLeader

          MLeader mld = new MLeader();

          for(int i=0; i < numLines; i++)

          {

            // Each leader line sprays out

            // at a different angle

            Vector3d vec = endPt - startPt;

            vec =

              vec.RotateBy(

                i * 2 * System.Math.PI / numLines,

                mld.Normal

              );

            Point3d landPt = startPt + vec;

            int ldNum = mld.AddLeader();

            int lnNum = mld.AddLeaderLine(ldNum);

            mld.AddFirstVertex(lnNum, landPt);

            mld.AddLastVertex(lnNum, startPt);

          }

          mld.LeaderLineType =

            LeaderType.SplineLeader;

          // Create the MText

          MText mt = new MText();

          mt.Contents =

            "Multileader with " +

            numLines.ToString() +

            " \nradial splines";

          mld.ContentType =

            ContentType.MTextContent;

          mld.MText = mt;

          mld.TextLocation = startPt;

          // Add the MLeader

          btr.AppendEntity(mld);

          tr.AddNewlyCreatedDBObject(mld, true);

          tr.Commit();

        }

        catch (Exception ex)

        {

          ed.WriteMessage("\nException: " + ex.Message);

          // Would also happen automatically

          // if we didn't commit

          tr.Abort();

        }

      }

    }

  }

}

Here's a 7-line multileader created by the SPRAY command:

Mleader_multiple_segment_spline

9 responses to “Creating an AutoCAD multileader spraying out from central text using .NET”

  1. Gagan Gajabaharia Avatar
    Gagan Gajabaharia

    Is there any chance of making the MLeader associated with the entity using ObjectARX .NET 2008? Thanks!

    -Gagan

  2. Kean Walmsley Avatar

    Gagan,

    I haven't looked into whether MLeaders can be associative to geometry. If it's not available as core functionality, you could certain implement your own associativity using events in .NET (probably a comibination of Database.ObjectModified and DocumentManager.DocumentLockModeWillChange).

    I'd suggest posting your question to the discussion groups or asking the ADN team (if you're a member).

    Regards,

    Kean

  3. Whenever I attempt to run your code in Civil-3D 2009, all of the leaders come out of the left side of the MLeader.

    If I select and drag the MLeader slightly, it will "recalculate" itself, and display as shown in your screen shot, with leaders coming out both sides of the text. But if I simply run the code, all leaders emanate from the left side of the text only.

    If I run your code in Civil-3D 2008, I get the same results as you, with leaders coming out both sides of the text. However, with C3D 2009, they only come out the left side.

    Any idea why, or how to get around this issue? I'd like to create an MLeader to the left of a point, and have the leader come out the right side of the text, but I can't seem to manage it in 2009.

    I have tried using the default acad.dwt template, and also "Starting from Scratch", just to try to eliminate any possible issues with the template, and I get the same result.

  4. Kean Walmsley Avatar

    This appears to be something different in base AutoCAD 2009 (and therefore the products built upon it).

    Adding this line of code forces a recalculation of the MLeader (I put it just before the line that commits the transaction):

    mld.MoveMLeader(new Vector3d(), MoveType.MoveAllPoints);

    It essentially moves all the points of the MLeader through a zero-length vector. So it does nothing, but the geometry gets corrected.

    I hope this helps you,

    Kean

  5. Thanks! That worked.

  6. I want to use built in source blocks like Box, Circle, detail callout,etc as blockcontent property for MLeader, how I can access them from drawing.

    autocad 2012.

    leader.BlockContentId = table["Box"]; // Exception on this line

    1. Kean Walmsley Avatar

      Are you sure that block exists - with exactly that name - in the drawing? What happens when you step through code to get that block from the block table?

      Kean

  7. uploads.disquscdn.c... uploads.disquscdn.c... Hello Kean,
    I am currently working on ObjectArx using C++.
    I am currently tring to make an MLeader as shown in img_1. but when i open dxf in autocad 2018, it can be seen like that.
    When i tried move slightly or copy paste the annotation, i am getting perfect mleader as seen in Img_2.
    Following your previous comment i have also use
    leader->moveMLeader(AcGeVector3d(0,0,0), AcDbMLeader::MoveType::kMoveAllPoints);line just before appending the Entity, it still does not work.
    Sharing with you the entities and properties i set up for creating the mleader.

    auto leader = new AcDbMLeader();
    leader->setLayer(data.layer_id);
    leader->setTextAttachmentDirection(AcDbMLeaderStyle::kAttachmentHorizontal);
    leader->setTextAttachmentType(AcDbMLeaderStyle::TextAttachmentType::kAttachmentAllLine);

    AcCmColor color;
    // Setting the leader color as Red
    color.setColorIndex(PTS_Color::kRed);

    // set the arrow symbol at the tip face
    leader->setArrowSymbolId(data.arrow_sym_id);
    leader->setArrowSize(arrowSize);
    int ldNum, lnNum;
    leader->addLeader(ldNum);
    leader->addLeaderLine(ldNum, lnNum);
    leader->addFirstVertex(lnNum, point_a);
    leader->addLastVertex(lnNum, point_b);
    leader->setLineWeight(AcDb::kLnWt013);
    leader->setColor(color);

    AcGeVector3d vec(-1, 0, 0);
    leader->setDoglegDirection(ldNum, vec);
    leader->setContentType(AcDbMLeaderStyle::kMTextContent);
    leader->setBlockConnectionType(AcDbMLeaderStyle::kConnectBase);

    // The Bezel radius information text
    auto txt_hgt = 3.2;
    AcDbMText *mText = create_AcDbMText(textStr, TextFont::STANDARD, txt_hgt, PTS_Color::kYellow);
    mText->setDatabaseDefaults();
    mText->setContents(textStr.c_str());
    mText->setLocation(point_b);
    mText->setAttachment(AcDbMText::AttachmentPoint::kBottomRight);
    leader->setMText(mText);

    auto blk_rec = this->get_model_space_block_table_record();
    leader->setTextStyleId(get_text_style_id_from_name(L"STYLE1", blk_rec->database()));

    leader->moveMLeader(AcGeVector3d(0,0,0), AcDbMLeader::MoveType::kMoveAllPoints);

    blk_rec->appendAcDbEntity(leader->id(), leader);

    // Close the objects
    mText->close();
    leader->close();
    blk_rec->close();
    return leader;

    1. Hello Aniket,

      Please post your support questions to the relevant Autodesk forum (in you case the ObjectARX forum).

      Many thanks,

      Kean

Leave a Reply to nav Cancel reply

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