Kean Walmsley


  • About the Author
    Kean on Google+

April 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      







« Visit to SaloneSatellite at Milan Design Week 2009 | Main | Modifying an AutoCAD object’s state via a dynamic property defined using .NET »

May 04, 2009

Using an AutoCAD 2010 overrule to control the copying of XData using .NET

It’s quite common for AutoCAD developers to use Extended Entity Data (XData) to tag objects their application cares about. This is certainly the approach taken in the recent series showing how to overrule the graphics display for an object – we store a “pipe radius” in XData attached to the Line or Circle we want to have a 3D profile.

That’s fine, but what if we’re storing a unique identifier with an object in XData that we do not want copied with the object? The Overrule API in AutoCAD 2010 allows you to hook into the DeepClone of an object and control what data gets copied with the object, whether via the COPY command or some other process that chooses to copy your object.

Here’s an example CopyOverrule class that makes use of the XData functions we previously defined as part of the PipeDrawOverrule class:

public class CopyOverrule : ObjectOverrule

{

  static public CopyOverrule theOverrule =

    new CopyOverrule();

 

  public override DBObject DeepClone(

    DBObject dbObject, DBObject ownerObject,

    IdMapping idMap, bool isPrimary

  )

  {

    // First we deep clone the object via the parent

 

    DBObject res =

      base.DeepClone(dbObject, ownerObject, idMap, isPrimary);

 

    // Then we check for our XData

 

    if (PipeDrawOverrule.PipeRadiusForObject(res) > 0.0)

    {

      // A transaction is needed by the set function to access

      // the RegApp table - we could also assume the app name

      // is registered and have a separate implementation

      // not taking the transaction...       

      // Just as we might also have chosen to remove the XData

 

      Transaction tr =

        dbObject.Database.TransactionManager.StartTransaction();

      using (tr)

      {

        PipeDrawOverrule.SetPipeRadiusOnObject(tr, res, 0.0);

        tr.Commit();

      }

    }

    return res;

  }

}

In this code we call the DeepClone implementation in the parent class and set the result – the copy of our object – to the res variable. If there any additional objects were copied during the DeepClone process, there should be IdPair entries in the idMap variable referring to both the originator and the copy. In this code we just check the XData on the returned object, but we might also iterate through and check the contents of the idMap.

Also, rather than removing the XData – which some people might prefer to do, depending on the behaviour of their application – we just set it to 0. Which is enough to make sure the object’s graphic display doesn’t make use of a pipe radius.

Here’s the complete code, including the code to register and unregister the overrule in the OVERRULE0 and OVERRULE1 commands:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.GraphicsInterface;

using Autodesk.AutoCAD.Colors;

 

namespace DrawOverrules

{

  public abstract class PipeDrawOverrule : DrawableOverrule

  {

    const string regAppName = "TTIF_PIPE";

 

    public PipeDrawOverrule()

    {

      // Tell AutoCAD to filter on our application name

      // (this means our overrule will only be called

      // on objects possessing XData with this name)

 

      SetXDataFilter(regAppName);

    }

 

    // Get the XData for a particular object

    // and return the "pipe radius" if it exists

 

    public static double PipeRadiusForObject(DBObject obj)

    {

      double res = 0.0;

 

      ResultBuffer rb = obj.XData;

      if (rb != null)

      {

        bool foundStart = false;

 

        foreach (TypedValue tv in rb)

        {

          if (tv.TypeCode == (int)DxfCode.ExtendedDataRegAppName &&

              tv.Value.ToString() == regAppName)

            foundStart = true;

          else

          {

            if (foundStart == true)

            {

              if (tv.TypeCode == (int)DxfCode.ExtendedDataReal)

              {

                res = (double)tv.Value;

                break;

              }

            }

          }

        }

        rb.Dispose();

      }

      return res;

    }

 

    // Set the "pipe radius" in the XData of a particular object

 

    public static void SetPipeRadiusOnObject(

      Transaction tr, DBObject obj, double radius

    )

    {

      Database db = obj.Database;

 

      // Make sure the application is registered

      // (we could separate this out to be called

      // only once for a set of operations)

 

      RegAppTable rat =

        (RegAppTable)tr.GetObject(

          db.RegAppTableId,

          OpenMode.ForRead

        );

 

      if (!rat.Has(regAppName))

      {

        rat.UpgradeOpen();

        RegAppTableRecord ratr = new RegAppTableRecord();

        ratr.Name = regAppName;

        rat.Add(ratr);

        tr.AddNewlyCreatedDBObject(ratr, true);

      }

 

      // Create the XData and set it on the object

 

      ResultBuffer rb =

        new ResultBuffer(

          new TypedValue(

            (int)DxfCode.ExtendedDataRegAppName, regAppName

          ),

          new TypedValue(

            (int)DxfCode.ExtendedDataReal, radius

          )

        );

      obj.XData = rb;

      rb.Dispose();

    }

  }

 

  // An overrule to make a pipe out of line

 

  public class LinePipeDrawOverrule : PipeDrawOverrule

  {

    static public LinePipeDrawOverrule theOverrule =

      new LinePipeDrawOverrule();

 

    private SweepOptions sweepOpts = new SweepOptions();

 

    public override bool WorldDraw(Drawable d, WorldDraw wd)

    {

      double radius = 0.0;

 

      if (d is DBObject)

        radius = PipeRadiusForObject((DBObject)d);

 

      if (radius > 0.0)

      {

        Line line = d as Line;

 

        if (line != null)

        {

          // Draw the line as is, with overruled attributes

 

          base.WorldDraw(line, wd);

          if (!line.Id.IsNull && line.Length > 0.0)

          {

            // Draw a pipe around the line

 

            EntityColor c =

              wd.SubEntityTraits.TrueColor;

            wd.SubEntityTraits.TrueColor =

              new EntityColor(0x00AfAfff);

            wd.SubEntityTraits.LineWeight =

              LineWeight.LineWeight000;

            Circle clr =

              new Circle(

                line.StartPoint,

                line.EndPoint - line.StartPoint,

                radius

              );

            ExtrudedSurface pipe = new ExtrudedSurface();

            try

            {

              pipe.CreateExtrudedSurface(

                clr, line.EndPoint - line.StartPoint, sweepOpts

              );

            }

            catch

            {

              Document doc =

                Application.DocumentManager.MdiActiveDocument;

              doc.Editor.WriteMessage(

                "\nFailed with CreateExtrudedSurface."

              );

            }

            clr.Dispose();

            pipe.WorldDraw(wd);

            pipe.Dispose();

            wd.SubEntityTraits.TrueColor = c;

          }

          return true;

        }

      }

      return base.WorldDraw(d, wd);

    }

 

    public override int SetAttributes(Drawable d, DrawableTraits t)

    {

      int b = base.SetAttributes(d, t);

 

      double radius = 0.0;

 

      if (d is DBObject)

        radius = PipeRadiusForObject((DBObject)d);

 

      if (radius > 0.0)

      {

        // Set color to index 6

 

        t.Color = 6;

 

        // and lineweight to .40 mm

 

        t.LineWeight = LineWeight.LineWeight040;

      }

      return b;

    }

  }

 

  // An overrule to make a pipe out of circle

 

  public class CirclePipeDrawOverrule : PipeDrawOverrule

  {

    static public CirclePipeDrawOverrule theOverrule =

      new CirclePipeDrawOverrule();

 

    private SweepOptions sweepOpts = new SweepOptions();

 

    public override bool WorldDraw(Drawable d, WorldDraw wd)

    {

      double radius = 0.0;

 

      if (d is DBObject)

        radius = PipeRadiusForObject((DBObject)d);

 

      if (radius > 0.0)

      {

        Circle circle = d as Circle;

 

        if (circle != null)

        {

          // Draw the circle as is, with overruled attributes

 

          base.WorldDraw(circle, wd);

 

          // Needed to avoid ill-formed swept surface

 

          if (circle.Radius > radius)

          {

            // Draw a pipe around the cirle

 

            EntityColor c = wd.SubEntityTraits.TrueColor;

            wd.SubEntityTraits.TrueColor =

              new EntityColor(0x3fffe0e0);

            wd.SubEntityTraits.LineWeight =

              LineWeight.LineWeight000;

            Vector3d normal =

              (circle.Center - circle.StartPoint).

                CrossProduct(circle.Normal);

            Circle clr =

              new Circle(

                circle.StartPoint, normal, radius

              );

            SweptSurface pipe = new SweptSurface();

            pipe.CreateSweptSurface(clr, circle, sweepOpts);

            clr.Dispose();

            pipe.WorldDraw(wd);

            pipe.Dispose();

            wd.SubEntityTraits.TrueColor = c;

          }

          return true;

        }

      }

      return base.WorldDraw(d, wd);

    }

 

    public override int SetAttributes(Drawable d, DrawableTraits t)

    {

      int b = base.SetAttributes(d, t);

 

      double radius = 0.0;

 

      if (d is DBObject)

        radius = PipeRadiusForObject((DBObject)d);

 

      if (radius > 0.0)

      {

        // Set color to index 2

 

        t.Color = 2;

 

        // and lineweight to .60 mm

 

        t.LineWeight = LineWeight.LineWeight060;

      }

      return b;

    }

  }

 

  public class LinePipeTransformOverrule : TransformOverrule

  {

    static public LinePipeTransformOverrule theOverrule =

      new LinePipeTransformOverrule();

 

    private SweepOptions sweepOpts = new SweepOptions();

 

    public override void Explode(Entity e, DBObjectCollection objs)

    {

      double radius = 0.0;

 

      if (e is DBObject)

        radius = PipeDrawOverrule.PipeRadiusForObject(e);

 

      if (radius > 0.0)

      {

        Line line = e as Line;

 

        if (line != null)

        {

          if (!line.Id.IsNull && line.Length > 0.0)

          {

            // Draw a pipe around the line

 

            Circle clr =

              new Circle(

                line.StartPoint,

                line.EndPoint - line.StartPoint,

                radius

              );

            ExtrudedSurface pipe = new ExtrudedSurface();

            try

            {

              pipe.CreateExtrudedSurface(

                clr, line.EndPoint - line.StartPoint, sweepOpts

              );

            }

            catch

            {

              Document doc =

                Application.DocumentManager.MdiActiveDocument;

              doc.Editor.WriteMessage(

                "\nFailed with CreateExtrudedSurface."

              );

            }

            clr.Dispose();

            objs.Add(pipe);

          }

          return;

        }

      }

      base.Explode(e, objs);

    }

  }

 

  public class CirclePipeTransformOverrule : TransformOverrule

  {

    static public CirclePipeTransformOverrule theOverrule =

      new CirclePipeTransformOverrule();

 

    private SweepOptions sweepOpts = new SweepOptions();

 

    public override void Explode(Entity e, DBObjectCollection objs)

    {

      double radius = 0.0;

 

      if (e is DBObject)

        radius = PipeDrawOverrule.PipeRadiusForObject(e);

 

      if (radius > 0.0)

      {

        Circle circle = e as Circle;

 

        if (circle != null)

        {

          // Needed to avoid ill-formed swept surface

 

          if (circle.Radius > radius)

          {

            // Draw a pipe around the cirle

 

            Vector3d normal =

              (circle.Center - circle.StartPoint).

                CrossProduct(circle.Normal);

            Circle clr =

              new Circle(

                circle.StartPoint, normal, radius

              );

            SweptSurface pipe = new SweptSurface();

            pipe.CreateSweptSurface(clr, circle, sweepOpts);

            clr.Dispose();

            objs.Add(pipe);

          }

          return;

        }

      }

      base.Explode(e, objs);

    }

  }

 

  public class CopyOverrule : ObjectOverrule

  {

    static public CopyOverrule theOverrule =

      new CopyOverrule();

 

    public override DBObject DeepClone(

      DBObject dbObject, DBObject ownerObject,

      IdMapping idMap, bool isPrimary

    )

    {

      // First we deep clone the object via the parent

 

      DBObject res =

        base.DeepClone(dbObject, ownerObject, idMap, isPrimary);

 

      // Then we check for our XData

 

      if (PipeDrawOverrule.PipeRadiusForObject(res) > 0.0)

      {

        // A transaction is needed by the set function to access

        // the RegApp table - we could also assume the app name

        // is registered and have a separate implementation

        // not taking the transaction...       

        // Just as we might also have chosen to remove the XData

 

        Transaction tr =

          dbObject.Database.TransactionManager.StartTransaction();

        using (tr)

        {

          PipeDrawOverrule.SetPipeRadiusOnObject(tr, res, 0.0);

          tr.Commit();

        }

      }

      return res;

    }

  }

 

  public class Commands

  {

    private double _radius = 0.0;

 

    public void Overrule(bool enable)

    {

      // Regen to see the effect

      // (turn on/off Overruling and LWDISPLAY)

 

      DrawableOverrule.Overruling = enable;

      if (enable)

        Application.SetSystemVariable("LWDISPLAY", 1);

      else

        Application.SetSystemVariable("LWDISPLAY", 0);

 

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      doc.SendStringToExecute("REGEN3\n", true, false, false);

      doc.Editor.Regen();

    }

 

    [CommandMethod("OVERRULE1")]

    public void OverruleStart()

    {

      ObjectOverrule.AddOverrule(

        RXClass.GetClass(typeof(Line)),

        LinePipeDrawOverrule.theOverrule,

        true

      );

      ObjectOverrule.AddOverrule(

        RXClass.GetClass(typeof(Circle)),

        CirclePipeDrawOverrule.theOverrule,

        true

      );

      ObjectOverrule.AddOverrule(

        RXClass.GetClass(typeof(Line)),

        LinePipeTransformOverrule.theOverrule,

        true

      );

      ObjectOverrule.AddOverrule(

        RXClass.GetClass(typeof(Circle)),

        CirclePipeTransformOverrule.theOverrule,

        true

      );

      ObjectOverrule.AddOverrule(

        RXClass.GetClass(typeof(Entity)),

        CopyOverrule.theOverrule,

        true

      );

      Overrule(true);

    }

 

    [CommandMethod("OVERRULE0")]

    public void OverruleEnd()

    {

      ObjectOverrule.RemoveOverrule(

        RXClass.GetClass(typeof(Line)),

        LinePipeDrawOverrule.theOverrule

      );

      ObjectOverrule.RemoveOverrule(

        RXClass.GetClass(typeof(Circle)),

        CirclePipeDrawOverrule.theOverrule

      );

      ObjectOverrule.RemoveOverrule(

        RXClass.GetClass(typeof(Line)),

        LinePipeTransformOverrule.theOverrule

      );

      ObjectOverrule.RemoveOverrule(

        RXClass.GetClass(typeof(Circle)),

        CirclePipeTransformOverrule.theOverrule

      );

      ObjectOverrule.RemoveOverrule(

        RXClass.GetClass(typeof(Entity)),

        CopyOverrule.theOverrule

      );

      Overrule(false);

    }

 

    [CommandMethod("MP", CommandFlags.UsePickSet)]

    public void MakePipe()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;

 

      // Ask the user to select the entities to make into pipes

 

      PromptSelectionOptions pso =

        new PromptSelectionOptions();

      pso.AllowDuplicates = false;

      pso.MessageForAdding =

        "\nSelect objects to turn into pipes: ";

 

      PromptSelectionResult selRes =

        doc.Editor.GetSelection(pso);

 

      // If the user didn't make valid selection, we return

 

      if (selRes.Status != PromptStatus.OK)

        return;

 

      SelectionSet ss = selRes.Value;

 

      // Ask the user for the pipe radius to set

 

      PromptDoubleOptions pdo =

        new PromptDoubleOptions(

          "\nSpecify pipe radius:"

        );

 

      // Use the previous value, if if already called

 

      if (_radius > 0.0)

      {

        pdo.DefaultValue = _radius;

        pdo.UseDefaultValue = true;

      }

      pdo.AllowNegative = false;

      pdo.AllowZero = false;

 

      PromptDoubleResult pdr =

        ed.GetDouble(pdo);

 

      // Return if something went wrong

 

      if (pdr.Status != PromptStatus.OK)

        return;

 

      // Set the "last radius" value for when

      // the command is called next

 

      _radius = pdr.Value;

 

      // Use a transaction to edit our various objects

 

      Transaction tr =

        db.TransactionManager.StartTransaction();

      using (tr)

      {

        // Loop through the selected objects

 

        foreach (SelectedObject o in ss)

        {

          // We could choose only to add XData to the objects

          // we know will use it (Lines and Circles, for now)

 

          DBObject obj =

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

          PipeDrawOverrule.SetPipeRadiusOnObject(tr, obj, _radius);

        }

        tr.Commit();

      }

    }

  }

}

Let’s see what happens when we attempt to copy a circular pipe using the standard COPY command (after having first created it using MP and have it display using OVERRULE1). We can see that as we select the location, the circle is displayed with a pipe radius:

Copying an overruled circle

But once we specify the location, the circle’s radius is removed:

Overruled copy of the overruled object

TrackBack

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

Listed below are links to weblogs that reference Using an AutoCAD 2010 overrule to control the copying of XData using .NET:

blog comments powered by Disqus

10 Random Posts