December 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      










« First impressions of Kinect for Windows 2.0 pre-release | Main | Moving text in an AutoCAD block using .NET – Part 2 »

December 02, 2013

Moving text in an AutoCAD block using .NET – Part 1

I had this question come in from Bruce Gordon by email a couple of weeks ago, and it seemed like a fun one to look at:

Is it possible to write a utility to pick a text entity in a block reference and move it to a new location?

As a first step, I put together some code that launches a nested entity selection and then performs a translation of the selected entity by a specified displacement from the picked point.

Given the requirement in the original request, the code works “first time” for text entities (DBText and MText), but asks the user for confirmation if a non-text entity is selected. As the displacement is performed via a standard matrix transformation – and every Entity has its TransformBy() method – this is a somewhat artificial distinction and can be removed very easily: it works very well with non-text too, of course. The transform needs a little extra work to work with nested (and rotated) blocks, in particular: we need to aggregate the block transforms of the containing block references and use those to get transformation into the equivalent of world coordinates. We can do this by pre- and post-multiplying the actual displacement matrix with the appropriate transforms. Comments in the code should help explain the specifics.

This type of command – one that modifies a shared definition that might be instanced multiple times (which is certainly the case with block definitions and their references) – always has the risk of causing user confusion. If you’re going to implement this in the wild then I suggest doing such things as checking the number of references to the block definitions you’re modifying so that the user can be informed of changes that might otherwise lead to head-scratching.

This version of the code doesn’t display the entity being dragged across – we do a standard GetPoint() having set the base point to get rubber-banding of the point selection, only – but we’ll add a jig in Part 2 that shows the entity being moved.

Here’s the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Runtime;

 

namespace Textformations

{

  public class Commands

  {

    [CommandMethod("MTIB")]

    public static void MoveTextInBlock()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      var db = doc.Database;

      var ed = doc.Editor;

 

      // Start by getting the text (or other) object in the block

 

      var pneo =

        new PromptNestedEntityOptions("\nSelect text inside block");

      var pner = ed.GetNestedEntity(pneo);

 

      if (pner.Status != PromptStatus.OK)

        return;

 

      var selId = pner.ObjectId;

 

      // Start a transaction to access the object

 

      var oc = selId.ObjectClass;

      if (

        !oc.IsDerivedFrom(

          RXClass.GetClass(typeof(DBText))

        ) &&

        !oc.IsDerivedFrom(

          RXClass.GetClass(typeof(MText))

        )

      )

      {

        // Isn't a text object - ask whether we continue

 

        ed.WriteMessage(

          "\nObject is not text, it is a {0}.", oc.Name

        );

 

        var pko =

          new PromptKeywordOptions(

            "\nDo you want to continue? [Yes/No]", "Yes No"

          );

        pko.AppendKeywordsToMessage = true;

        pko.AllowNone = true;

        pko.Keywords.Default = "No";

 

        var pr = ed.GetKeywords(pko);

        if (

          pr.Status != PromptStatus.OK || pr.StringResult == "No"

        )

          return;

      }

 

      // Now get the displacement from the picked point

 

      var ppo = new PromptPointOptions("\nEnter displacement");

      ppo.BasePoint = pner.PickedPoint;

      ppo.UseBasePoint = true;

 

      var ppr = ed.GetPoint(ppo);

      if (ppr.Status != PromptStatus.OK)

        return;

 

      // Start a transaction to modify the object

 

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

      {

        // Get the displacement from the picked point to the newly

        // selected one

 

        var disp = ppr.Value - pner.PickedPoint;

        var mat = Matrix3d.Displacement(disp);

 

        // Unless we get a block reference container, use the

        // identity matrix as the block transform

 

        var brMat = Matrix3d.Identity;

 

        // Get the containers around the nested entity

 

        var conts = pner.GetContainers();

        foreach (var id in conts)

        {

          var br =

            tr.GetObject(id, OpenMode.ForRead) as BlockReference;

          if (br != null)

          {

            brMat = brMat.PreMultiplyBy(br.BlockTransform);

          }

        }

 

        // To get the transformation in world coordinates rather

        // than block coordinates we post-multiply by block

        // transform and pre-multiply by its inverse

 

        mat = mat.PreMultiplyBy(brMat.Inverse());

        mat = mat.PostMultiplyBy(brMat);

 

        // Transform the entity

 

        var ent = (Entity)tr.GetObject(selId, OpenMode.ForWrite);

        ent.TransformBy(mat);

 

        // Open each of the containers and set a property so that

        // they each get regenerated

 

        foreach (var id in conts)

        {

          var ent2 = tr.GetObject(id, OpenMode.ForWrite) as Entity;

          if (ent2 != null)

          {

            // We might also have called this method:

            // ent2.RecordGraphicsModified(true);

            // but setting a property works better with undo

 

            ent2.Visible = ent2.Visible;

          }

        }

 

        tr.Commit();

      }

    }

  }

}

Here’s a quick video of the command in action:

MTIB

Thanks to Bruce Gordon for asking an interesting question and then helping test this implementation. It’s certainly an interesting technique that can be used in lots of different scenarios.

I’m now at AU2013 and will probably post about the conference over the coming days, so we’ll see when I get to post the follow-up post that adds preview graphics via a jig.

blog comments powered by Disqus

Feed/Share

10 Random Posts