September 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        








« Storing custom AutoCAD application settings in the Registry using .NET | Main | An automatic numbering system for AutoCAD blocks using .NET - Part 2 »

May 07, 2008

An automatic numbering system for AutoCAD blocks using .NET - Part 1

I had this interesting request come in by email:

I have a problem where I have a block I need to insert into several parts of a drawing. The block has three attributes, 1. Point number, 2. Level, and 3. Code.

I would like to do the following;

1. be able to select objects in a drawing and have it insert the block and autosnap to circle centres, and auto number each point number attribute.
2. be able to manually select points which are automatically numbered.
3. it should remember the last point number.
4. have the option to renumber points if changes are made i could renumber all the points.
5. be able to extract the points & attributes, point number, x position, y position, level, & code to a comma delimited file.

The request was more for guidance than for custom development services, but I thought the topic was of general-enough interest to be worth spending some time working on.

Over the next few posts I'm going to serialize the implementation I put together, in more-or-less the order in which I implemented it. If further related enhancement requests come in - as comments or by email - then I'll do what I can to incorporate them into the solution (no guarantees, though :-).

Today's post looks at the first two items: automatic numbering of selected circles and manual selection of points that also get numbered.

During these posts (and in the code), I'll refer to these numbered blocks as "bubbles", as this implementation could be used to implement automatically numbered bubble (or balloon) dimensions. As you'll see in the next few posts, though, I've tried to keep the core number management implementation as independent as possible, so it could be used to manage a numbered list of any type of AutoCAD entity.

So, here's the initial C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

using System.Collections.Generic;


namespace AutoNumberedBubbles

{

  public class Commands : IExtensionApplication

  {

    // Strings identifying the block

    // and the attribute name to use


    const string blockName = "BUBBLE";

    const string attbName = "NUMBER";


    // In this version, just use a simple

    // integer to take care of the numbering


    private int m_bulletNumber = 0;


    // Constructor


    public Commands()

    {

    }


    // Functions called on initialization & termination


    public void Initialize()

    {

      try

      {

        Document doc =

          Application.DocumentManager.MdiActiveDocument;

        Editor ed = doc.Editor;


        ed.WriteMessage(

          "\nBAP  Create bubbles at points" +

          "\nBIC  Create bubbles at the center of circles"

        );

      }

      catch

      { }

    }


    public void Terminate()

    {

    }


    // Command to create bubbles at points selected

    // by the user - loops until cancelled


    [CommandMethod("BAP")]

    public void BubblesAtPoints()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;

      Autodesk.AutoCAD.ApplicationServices.

      TransactionManager tm =

        doc.TransactionManager;


      Transaction tr =

        tm.StartTransaction();

      using (tr)

      {

        // Get the information about the block

        // and attribute definitions we care about


        BlockTableRecord ms;

        ObjectId blockId;

        AttributeDefinition ad;

        List<AttributeDefinition> other;


        if (GetBlock(

              db, tr, out ms, out blockId

          ))

        {

          GetBlockAttributes(

            tr, blockId, out ad, out other

          );


          // By default the modelspace is returned to

          // us in read-only state


          ms.UpgradeOpen();


          // Loop until cancelled


          bool finished = false;

          while (!finished)

          {

            PromptPointOptions ppo =

              new PromptPointOptions("\nSelect point: ");

            ppo.AllowNone = true;


            PromptPointResult ppr =

              ed.GetPoint(ppo);

            if (ppr.Status != PromptStatus.OK)

              finished = true;

            else

              // Call a function to create our bubble

              CreateNumberedBubbleAtPoint(

                db, ms, tr, ppr.Value,

                blockId, ad, other

              );

            tm.QueueForGraphicsFlush();

            tm.FlushGraphics();

          }

        }

        tr.Commit();

      }

    }


    // Command to create a bubble at the center of

    // each of the selected circles


    [CommandMethod("BIC")]

    public void BubblesInCircles()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;


      // Allow the user to select circles


      TypedValue[] tvs =

        new TypedValue[1] {

            new TypedValue(

              (int)DxfCode.Start,

              "CIRCLE"

            )

          };

      SelectionFilter sf =

        new SelectionFilter(tvs);


      PromptSelectionResult psr =

        ed.GetSelection(sf);


      if (psr.Status == PromptStatus.OK &&

          psr.Value.Count > 0)

      {

        Transaction tr =

          db.TransactionManager.StartTransaction();

        using (tr)

        {

          // Get the information about the block

          // and attribute definitions we care about


          BlockTableRecord ms;

          ObjectId blockId;

          AttributeDefinition ad;

          List<AttributeDefinition> other;


          if (GetBlock(

                db, tr, out ms, out blockId

            ))

          {

            GetBlockAttributes(

              tr, blockId, out ad, out other

            );


            // By default the modelspace is returned to

            // us in read-only state


            ms.UpgradeOpen();


            foreach (SelectedObject o in psr.Value)

            {

              // For each circle in the selected list...


              DBObject obj =

                tr.GetObject(o.ObjectId, OpenMode.ForRead);

              Circle c = obj as Circle;

              if (c == null)

                ed.WriteMessage(

                  "\nObject selected is not a circle."

                );

              else

                // Call our numbering function, passing the

                // center of the circle

                CreateNumberedBubbleAtPoint(

                  db, ms, tr, c.Center,

                  blockId, ad, other

                );

            }

          }

          tr.Commit();

        }

      }

    }


    // Internal helper function to open and retrieve

    // the model-space and the block def we care about


    private bool

      GetBlock(

        Database db,

        Transaction tr,

        out BlockTableRecord ms,

        out ObjectId blockId

      )

    {

      BlockTable bt =

        (BlockTable)tr.GetObject(

          db.BlockTableId,

          OpenMode.ForRead

        );


      if (!bt.Has(blockName))

      {

        Document doc =

          Application.DocumentManager.MdiActiveDocument;

        Editor ed = doc.Editor;

        ed.WriteMessage(

          "\nCannot find block definition \"" +

          blockName +

          "\" in the current drawing."

        );


        blockId = ObjectId.Null;

        ms = null;

        return false;

      }


      ms =

        (BlockTableRecord)tr.GetObject(

          bt[BlockTableRecord.ModelSpace],

          OpenMode.ForRead

        );


      blockId = bt[blockName];


      return true;

    }


    // Internal helper function to retrieve

    // attribute info from our block

    // (we return the main attribute def

    // and then all the "others")


    private void

      GetBlockAttributes(

        Transaction tr,

        ObjectId blockId,

        out AttributeDefinition ad,

        out List<AttributeDefinition> other

      )

    {

      BlockTableRecord blk =

        (BlockTableRecord)tr.GetObject(

          blockId,

          OpenMode.ForRead

        );


      ad = null;

      other =

        new List<AttributeDefinition>();


      foreach (ObjectId attId in blk)

      {

        DBObject obj =

          (DBObject)tr.GetObject(

            attId,

            OpenMode.ForRead

          );

        AttributeDefinition ad2 =

          obj as AttributeDefinition;


        if (ad2 != null)

        {

          if (ad2.Tag == attbName)

          {

            if (ad2.Constant)

            {

              Document doc =

                Application.DocumentManager.MdiActiveDocument;

              Editor ed = doc.Editor;


              ed.WriteMessage(

                "\nAttribute to change is constant!"

              );

            }

            else

              ad = ad2;

          }

          else

            if (!ad2.Constant)

              other.Add(ad2);

        }

      }

    }


    // Internal helper function to create a bubble

    // at a particular point


    private Entity

      CreateNumberedBubbleAtPoint(

        Database db,

        BlockTableRecord btr,

        Transaction tr,

        Point3d pt,

        ObjectId blockId,

        AttributeDefinition ad,

        List<AttributeDefinition> other

      )

    {

      //  Create a new block reference


      BlockReference br =

        new BlockReference(pt, blockId);


      // Add it to the database


      br.SetDatabaseDefaults();

      ObjectId blockRefId = btr.AppendEntity(br);

      tr.AddNewlyCreatedDBObject(br, true);


      // Create an attribute reference for our main

      // attribute definition (where we'll put the

      // bubble's number)


      AttributeReference ar =

        new AttributeReference();


      // Add it to the database, and set its position, etc.


      ar.SetDatabaseDefaults();

      ar.SetAttributeFromBlock(ad, br.BlockTransform);

      ar.Position =

        ad.Position.TransformBy(br.BlockTransform);

      ar.Tag = ad.Tag;


      // Set the bubble's number


      int bubbleNumber =

        ++m_bulletNumber;


      ar.TextString = bubbleNumber.ToString();

      ar.AdjustAlignment(db);


      // Add the attribute to the block reference


      br.AttributeCollection.AppendAttribute(ar);

      tr.AddNewlyCreatedDBObject(ar, true);


      // Now we add attribute references for the

      // other attribute definitions


      foreach (AttributeDefinition ad2 in other)

      {

        AttributeReference ar2 =

          new AttributeReference();


        ar2.SetAttributeFromBlock(ad2, br.BlockTransform);

        ar2.Position =

          ad2.Position.TransformBy(br.BlockTransform);

        ar2.Tag = ad2.Tag;

        ar2.TextString = ad2.TextString;

        ar2.AdjustAlignment(db);


        br.AttributeCollection.AppendAttribute(ar2);

        tr.AddNewlyCreatedDBObject(ar2, true);

      }

      return br;

    }

  }

}

You can see only two commands have been implemented, for now: BAP (Bubbles At Points) and BIC (Bubbles In Circles). These commands will create bubbles in increasing order, starting at 1. For now there's nothing in the application to allow the numbering to continue when you reload the drawing - it will start again at 1, for now.

Here's a base drawing containing some circles (which you can download from here):

Circles

Here's what happens when we run the BIC command, selecting each of the circles, going from left to right:

Bubbles 1

And here's what happens after we run the BAP command, selecting a number of points at random:

Bubbles 2

In the next post we'll add some intelligence to the numbering system, by adding a class to take care of a lot of the hard work for us. We'll then implement some commands to use this class to get more control over the numbering.

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00d83452464869e200e5522c192f8834

Listed below are links to weblogs that reference An automatic numbering system for AutoCAD blocks using .NET - Part 1:

blog comments powered by Disqus

Feed/Share

10 Random Posts