明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 11612|回复: 10

[Kean专集] Kean专题(11)—Overrules

   关闭 [复制链接]
发表于 2009-6-15 18:20 | 显示全部楼层 |阅读模式
原帖:http://through-the-interface.typepad.com/through_the_interface/overrules/
一、使用F#自定义实体的显示
March 27, 2009
Customizing the display of standard AutoCAD objects using F#
This post is one of the winning entries of the F# programming contest started at the beginning of the year. It was submitted by an old friend of mine, Qun Lu, who also happens to be a member of the AutoCAD engineering team, and makes use of a new API in AutoCAD 2010: the somewhat ominously-named Overrule API.
The Overrule API is really (and I mean really, really) cool. Yes, I know: another really cool API in AutoCAD 2010? Well, I’m honestly not one to hype things up, but I do have a tendency to get excited by technology that has incredibly interesting capabilities with a relatively low barrier of entry. And the Overrule API is one of those APIs. It’s the answer to the question posed in this previous post, which raises concerns about translating the power and complexity of custom objects to the world of .NET:
So what’s the right thing to do? Clearly we could just go ahead and expose the mechanism as it is today in ObjectARX. And yet here we are with a technology we know to be highly complex and difficult to implement, and an ideal opportunity to redesign it – enabling more people to harness it effectively at lower effort. The more favoured approach (at least from our perspective) would be to investigate further how better to meet developers’ needs for enabling custom graphics/behaviour (a.k.a. stylization) in AutoCAD – in a way that could be supported technically for many releases to come.
The Overrule API allows you to hook into the display and other aspects of the behaviour of entities inside AutoCAD. The below example is a great example: when enabled, the code overrules the display of lines and circles, to make them into coloured pipes. And all with very little code (which would also be true if the code were in C# or VB.NET).
Here’s the F# code:
  1. #light
  2. module DrawOverrule.Commands
  3. open Autodesk.AutoCAD.Runtime
  4. open Autodesk.AutoCAD.ApplicationServices
  5. open Autodesk.AutoCAD.DatabaseServices
  6. open Autodesk.AutoCAD.Geometry
  7. open Autodesk.AutoCAD.GraphicsInterface
  8. open Autodesk.AutoCAD.Colors
  9. type public DrawOverrule public () as this =
  10.   inherit DrawableOverrule()
  11.   static member public theOverrule =
  12.     new DrawOverrule()
  13.   static member private Radius = 0.5
  14.   member private this.sweepOpts = new SweepOptions()
  15.   override this.WorldDraw (d : Drawable, wd : WorldDraw) =
  16.     match d with
  17.       // Type-test and cast. If succeeds, cast to "line"
  18.       | :? Line as line ->
  19.         // Draw the line as is, with overruled attributes
  20.         base.WorldDraw(line, wd) |> ignore
  21.         if not line.Id.IsNull && line.Length > 0.0 then
  22.           // Draw a pipe around the line
  23.           let c = wd.SubEntityTraits.TrueColor
  24.           wd.SubEntityTraits.TrueColor <-
  25.             new EntityColor(0x00AfAfff)
  26.           wd.SubEntityTraits.LineWeight <-
  27.             LineWeight.LineWeight000
  28.           let clr =
  29.             new Circle
  30.               (line.StartPoint, line.EndPoint-line.StartPoint,
  31.               DrawOverrule.Radius)
  32.           let pipe = new ExtrudedSurface()
  33.           try
  34.             pipe.CreateExtrudedSurface
  35.               (clr, line.EndPoint-line.StartPoint, this.sweepOpts)
  36.           with
  37.             | e -> printfn("Failed with CreateExtrudedSurface")
  38.           clr.Dispose()
  39.           pipe.WorldDraw(wd) |> ignore
  40.           pipe.Dispose()
  41.           wd.SubEntityTraits.TrueColor <- c
  42.         true
  43.       | :? Circle as circle ->
  44.         // Draw the circle as is, with overruled attributes
  45.         base.WorldDraw(circle, wd) |> ignore
  46.         // needed to avoid ill-formed swept surface
  47.         if circle.Radius > DrawOverrule.Radius then
  48.           // draw a pipe around the cirle
  49.           let c = wd.SubEntityTraits.TrueColor
  50.           wd.SubEntityTraits.TrueColor <-
  51.             new EntityColor(0x3fffe0e0)
  52.           wd.SubEntityTraits.LineWeight <-
  53.             LineWeight.LineWeight000
  54.           let normal =
  55.             (circle.Center-circle.StartPoint).
  56.               CrossProduct(circle.Normal)
  57.           let clr =
  58.             new Circle
  59.               (circle.StartPoint, normal, DrawOverrule.Radius)
  60.           let pipe = new SweptSurface()
  61.           pipe.CreateSweptSurface(clr, circle, this.sweepOpts)
  62.           clr.Dispose()
  63.           pipe.WorldDraw(wd) |> ignore
  64.           pipe.Dispose()
  65.           wd.SubEntityTraits.TrueColor <- c
  66.         true
  67.       | _ ->
  68.         base.WorldDraw(d, wd)
  69.   override this.SetAttributes (d : Drawable, t : DrawableTraits) =
  70.     let b = base.SetAttributes(d, t)
  71.     match d with
  72.       | :? Line ->
  73.         // If d is LINE, set color to index 6
  74.         t.Color <- 6s
  75.         // and lineweight to .40 mm
  76.         t.LineWeight <- LineWeight.LineWeight040
  77.       | :? Circle ->   
  78.         // If d is CIRCLE, set color to index 2
  79.         t.Color <- 2s
  80.         // and lineweight to .60 mm
  81.         t.LineWeight <- LineWeight.LineWeight060
  82.       | _ -> ()
  83.     b
  84. let Overrule enable =
  85.   // Regen to see the effect
  86.   // (turn on/off Overruling and LWDISPLAY)
  87.   DrawableOverrule.Overruling <- enable
  88.   match enable with
  89.     | true -> Application.SetSystemVariable("LWDISPLAY", 1)
  90.     | false -> Application.SetSystemVariable("LWDISPLAY", 0)
  91.   let doc =
  92.     Application.DocumentManager.MdiActiveDocument
  93.   doc.SendStringToExecute("REGEN3\n", true, false, false)
  94.   doc.Editor.Regen()
  95. // Now we declare our commands
  96. [<CommandMethod("overrule1")>]
  97. let OverruleStart() =
  98.   // Targeting all Drawables, but only affects Lines and Circles
  99.   ObjectOverrule.AddOverrule
  100.     (RXClass.GetClass(typeof<Drawable>),
  101.     DrawOverrule.theOverrule, true)
  102.   Overrule(true)
  103. [<CommandMethod("overrule0")>]
  104. let OverruleEnd() =
  105.   Overrule(false)
Here’s what happens when we load the application, turn the overrule on using the OVERRULE1 command (OVERRULE0 is the command to turn the overrule off – it’s details like this that tell you Qun’s a real programmer… ;-) and draw some lines and circles:

Even in a 3D view – this time with the realistic visual style applied – you get the piping effect when you draw simple geometry:
To be clear: these are standard AutoCAD lines and circles. When you use the OVERRULE0 command to disable the overrule, they revert to their original form:

I expect to follow this post – in time – with various more harnessing the power of this very cool API. If you have questions or ideas about how it might be used, be sure to post a comment.
Thanks & congratulations, Qun! Your copy of “Expert F#” is on its way to you via inter-office mail. :-) More soon on the other winning entry…

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
 楼主| 发表于 2009-6-15 18:28 | 显示全部楼层
C#版本
April 08, 2009
Customizing the display of standard AutoCAD objects using .NET
The code in this post is a direct port of the F# code in this previous post, which was entered by Qun Lu in the recent F# programming contest. Someone – very validly - commented on the fact the post involved both a new language and a new API, which was probably pushing things a little from a learning perspective. :-)
Without repeating my various comments in the previous post, I will reiterate the fact that this API is extremely interesting for developers who wish to customize the appearance and behaviour of standard AutoCAD objects without going through the pain of implementing full custom objects.
Here’s the equivalent C# code that works with AutoCAD 2010:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.Runtime;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.GraphicsInterface;
  6. using Autodesk.AutoCAD.Colors;
  7. namespace DrawOverrule
  8. {
  9.   public class DrawOverrule : DrawableOverrule
  10.   {
  11.     static public DrawOverrule theOverrule =
  12.       new DrawOverrule();
  13.     static private double Radius = 0.5;
  14.     private SweepOptions sweepOpts = new SweepOptions();
  15.     public override bool WorldDraw (Drawable d, WorldDraw wd)
  16.     {
  17.       if (d is Line)
  18.       {
  19.         Line line = (Line)d;
  20.         // Draw the line as is, with overruled attributes
  21.         base.WorldDraw(line, wd);
  22.         if (!line.Id.IsNull && line.Length > 0.0)
  23.         {
  24.           // Draw a pipe around the line
  25.           EntityColor c =
  26.             wd.SubEntityTraits.TrueColor;
  27.           wd.SubEntityTraits.TrueColor =
  28.             new EntityColor(0x00AfAfff);
  29.           wd.SubEntityTraits.LineWeight =
  30.             LineWeight.LineWeight000;
  31.           Circle clr =
  32.             new Circle(
  33.               line.StartPoint,
  34.               line.EndPoint - line.StartPoint,
  35.               DrawOverrule.Radius
  36.             );
  37.           ExtrudedSurface pipe = new ExtrudedSurface();
  38.           try
  39.           {
  40.             pipe.CreateExtrudedSurface(
  41.               clr, line.EndPoint-line.StartPoint, sweepOpts
  42.             );
  43.           }
  44.           catch
  45.           {
  46.             Document doc =
  47.               Application.DocumentManager.MdiActiveDocument;
  48.             doc.Editor.WriteMessage(
  49.               "\nFailed with CreateExtrudedSurface."
  50.             );
  51.           }
  52.           clr.Dispose();
  53.           pipe.WorldDraw(wd);
  54.           pipe.Dispose();
  55.           wd.SubEntityTraits.TrueColor = c;
  56.         }
  57.         return true;
  58.       }
  59.       else if (d is Circle)
  60.       {
  61.         Circle circle = (Circle)d;
  62.         // Draw the circle as is, with overruled attributes
  63.         base.WorldDraw(circle, wd);
  64.         // Needed to avoid ill-formed swept surface
  65.         if (circle.Radius > DrawOverrule.Radius)
  66.         {
  67.           // Draw a pipe around the cirle
  68.           EntityColor c = wd.SubEntityTraits.TrueColor;
  69.           wd.SubEntityTraits.TrueColor =
  70.             new EntityColor(0x3fffe0e0);
  71.           wd.SubEntityTraits.LineWeight =
  72.             LineWeight.LineWeight000;
  73.           Vector3d normal =
  74.             (circle.Center-circle.StartPoint).
  75.               CrossProduct(circle.Normal);
  76.           Circle clr =
  77.             new Circle(
  78.               circle.StartPoint, normal, DrawOverrule.Radius
  79.             );
  80.           SweptSurface pipe = new SweptSurface();
  81.           pipe.CreateSweptSurface(clr, circle, sweepOpts);
  82.           clr.Dispose();
  83.           pipe.WorldDraw(wd);
  84.           pipe.Dispose();
  85.           wd.SubEntityTraits.TrueColor = c;
  86.         }
  87.         return true;
  88.       }
  89.       return base.WorldDraw(d, wd);
  90.     }
  91.     public override int SetAttributes (Drawable d, DrawableTraits t)
  92.     {
  93.       int b = base.SetAttributes(d, t);
  94.       if (d is Line)
  95.       {
  96.         // If d is LINE, set color to index 6
  97.         t.Color = 6;
  98.         // and lineweight to .40 mm
  99.         t.LineWeight = LineWeight.LineWeight040;
  100.       }
  101.       else if (d is Circle)
  102.       {
  103.         // If d is CIRCLE, set color to index 2
  104.         t.Color = 2;
  105.         // and lineweight to .60 mm
  106.         t.LineWeight = LineWeight.LineWeight060;
  107.       }
  108.       return b;
  109.     }
  110.   }
  111.   public class Commands
  112.   {
  113.     public void Overrule(bool enable)
  114.     {
  115.       // Regen to see the effect
  116.       // (turn on/off Overruling and LWDISPLAY)
  117.       DrawableOverrule.Overruling = enable;
  118.       if (enable)
  119.         Application.SetSystemVariable("LWDISPLAY", 1);
  120.       else
  121.         Application.SetSystemVariable("LWDISPLAY", 0);
  122.       Document doc =
  123.         Application.DocumentManager.MdiActiveDocument;
  124.       doc.SendStringToExecute("REGEN3\n", true, false, false);
  125.       doc.Editor.Regen();
  126.     }
  127.     [CommandMethod("OVERRULE1")]
  128.     public void OverruleStart()
  129.     {
  130.       ObjectOverrule.AddOverrule
  131.         (RXClass.GetClass(typeof(Drawable)),
  132.         DrawOverrule.theOverrule, true);
  133.       Overrule(true);
  134.     }
  135.     [CommandMethod("OVERRULE0")]
  136.     public void OverruleEnd()
  137.     {
  138.       Overrule(false);
  139.     }
  140.   }
  141. }
 楼主| 发表于 2009-6-16 08:44 | 显示全部楼层
二、图元炸开重定义
April 16, 2009
Overruling explode in AutoCAD 2010 using .NET
I am really starting to love the new Overrule API in AutoCAD 2010, and I still feel as though I’m just scratching the surface. This question came in overnight from Danny Polkinhorn (thanks, Danny! :-) :
It's exciting to see a very usable implementation of 'custom' objects in .NET. Obviously, this implementation protects what could be proprietary business intelligence from being sent around, but it brings up a question. What process would you use to 'explode' these elements so that you could send the drawing to someone without your code, but with the custom elements in it?
My first thought was to just see what happened when the EXPLODE command encountered the overruled lines & circles created with the code in the last post. At first I was a little discouraged:
  1. Command: EXPLODE
  2. Select objects: Specify opposite corner: 9 found
  3. 9 were not able to be exploded.
  4. Select objects: *Cancel*
  5. None found.
复制代码
My worry, at this stage, was that the EXPLODE command was excluding “simple” objects such as lines and circles during its selection process, which would mean that even if we were able to overrule the explode behaviour of our special lines & circles we would have difficulty getting our code called. Thankfully this is not the case, but I only found this out as I implemented a TransformOverrule and overruled Explode for my lines and circles.
Before looking into the specifics, here’s a quick list of the various Overrules available in AutoCAD 2010 with their overrideable methods:
Overrule (the base class)
  1. GripOverrule
  2.       GetGripPoints
  3.       GetStretchPoints
  4.       MoveGripPointsAt
  5.       MoveStretchPointsAt
  6.       OnGripStatusChanged
  7.       OsnapOverrule
  8.       GetObjectSnapPoints
  9.       IsContentSnappable
  10. SubentityOverrule
  11.       AddSubentPaths
  12.       DeleteSubentPaths
  13.       GetCompoundObjectTransform
  14.       GetGripPointsAtSubentPath
  15.       GetGsMarkersAtSubentPath
  16.       GetSubentClassId
  17.       GetSubentPathGeomExtents
  18.       GetSubentPathsAtGsMarker
  19.       MoveGripPointsAtSubentPaths
  20.       OnSubentGripStatusChanged
  21.       SubentPtr
  22.       TransformSubentPathsBy
  23. TransformOverrule
  24.       CloneMeForDragging
  25.       Explode
  26.       GetTransformedCopy
  27.       HideMeForDragging
  28.       TransformBy
  29. GeometryOverrule
  30.       GetGeomExtents
  31.       IntersectWith
  32. PropertiesOverrule
  33.       GetClassID
  34.       List
  35. ObjectOverrule
  36.       Cancel
  37.       Close
  38.       DeepClone
  39.       Erase
  40.       Open
  41.       WblockClone
  42. DrawableOverrule
  43.       SetAttributes
  44.       ViewportDraw
  45.       ViewportDrawLogicalFlags
  46.       WorldDraw
So far we’ve only actually used the DrawableOverrule, implementing SetAttributes() and WorldDraw().
[Aside: as I compiled the above list I had one of those “woah” moments. It was bit like the feeling I remember as a child while reading “ The Elfstones of Shannara”, the – much better – sequel to “ The Sword of Shannara” ( Terry Brooks’ first  fantasy novel). In “The Sword of Shannara” the main character used some  elfstones for protection, but it’s only in the sequel that you find out just how powerful they are, and that they’re actually only one of several different varieties of elfstones. Anyone who hasn’t been a fantasy/sci-fi geek in their time is almost certainly rolling their eyes (if they’re not being physically sick) at this point, so I’d better get back on track. I still enjoy a good sci-fi novel, by the way, but these days I find I can’t do fantasy that isn’t  Tolkien.]
It’s clear that to customize the explode for our lines and circles we’re going to need a TransformOverrule.
Here’s the C# code from the last post, with our additional TransformOverrules. I’ve changed a few of the class names to reflect the fact they are overrule draw behaviour vs. transform behaviour, but otherwise the code is basically the same as last time, apart from the addition of the LinePipeTransformOverrule and CirclePipeTransformOverrule classes (and the code to register/unregister them).
The Explode functions take pretty much the same geometry we used to draw the “enhanced” objects, but this time it gets returned to the system and is used to replace the original entities. This code doesn’t do anything fancy about the entity colour, etc., but we could certainly do so, if we chose.
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.Runtime;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.GraphicsInterface;
  7. using Autodesk.AutoCAD.Colors;
  8. namespace Overrules
  9. {
  10.   public abstract class PipeDrawOverrule : DrawableOverrule
  11.   {
  12.     const string regAppName = "TTIF_PIPE";
  13.     public PipeDrawOverrule()
  14.     {
  15.       // Tell AutoCAD to filter on our application name
  16.       // (this means our overrule will only be called
  17.       // on objects possessing XData with this name)
  18.       SetXDataFilter(regAppName);
  19.     }
  20.     // Get the XData for a particular object
  21.     // and return the "pipe radius" if it exists
  22.     public static double PipeRadiusForObject(DBObject obj)
  23.     {
  24.       double res = 0.0;
  25.       ResultBuffer rb = obj.XData;
  26.       if (rb != null)
  27.       {
  28.         bool foundStart = false;
  29.         foreach (TypedValue tv in rb)
  30.         {
  31.           if (tv.TypeCode == (int)DxfCode.ExtendedDataRegAppName &&
  32.               tv.Value.ToString() == regAppName)
  33.             foundStart = true;
  34.           else
  35.           {
  36.             if (foundStart == true)
  37.             {
  38.               if (tv.TypeCode == (int)DxfCode.ExtendedDataReal)
  39.               {
  40.                 res = (double)tv.Value;
  41.                 break;
  42.               }
  43.             }
  44.           }
  45.         }
  46.         rb.Dispose();
  47.       }
  48.       return res;
  49.     }
  50.     // Set the "pipe radius" in the XData of a particular object
  51.     public static void SetPipeRadiusOnObject(
  52.       Transaction tr, DBObject obj, double radius
  53.     )
  54.     {
  55.       Database db = obj.Database;
  56.       // Make sure the application is registered
  57.       // (we could separate this out to be called
  58.       // only once for a set of operations)
  59.       RegAppTable rat =
  60.         (RegAppTable)tr.GetObject(
  61.           db.RegAppTableId,
  62.           OpenMode.ForRead
  63.         );
  64.       if (!rat.Has(regAppName))
  65.       {
  66.         rat.UpgradeOpen();
  67.         RegAppTableRecord ratr = new RegAppTableRecord();
  68.         ratr.Name = regAppName;
  69.         rat.Add(ratr);
  70.         tr.AddNewlyCreatedDBObject(ratr, true);
  71.       }
  72.       // Create the XData and set it on the object
  73.       ResultBuffer rb =
  74.         new ResultBuffer(
  75.           new TypedValue(
  76.             (int)DxfCode.ExtendedDataRegAppName, regAppName
  77.           ),
  78.           new TypedValue(
  79.             (int)DxfCode.ExtendedDataReal, radius
  80.           )
  81.         );
  82.       obj.XData = rb;
  83.       rb.Dispose();
  84.     }
  85.   }
  86.   // An overrule to make a pipe out of line
  87.   public class LinePipeDrawOverrule : PipeDrawOverrule
  88.   {
  89.     static public LinePipeDrawOverrule theOverrule =
  90.       new LinePipeDrawOverrule();
  91.     private SweepOptions sweepOpts = new SweepOptions();
  92.     public override bool WorldDraw(Drawable d, WorldDraw wd)
  93.     {
  94.       double radius = 0.0;
  95.       if (d is DBObject)
  96.         radius = PipeRadiusForObject((DBObject)d);
  97.       if (radius > 0.0)
  98.       {
  99.         Line line = d as Line;
  100.         if (line != null)
  101.         {
  102.           // Draw the line as is, with overruled attributes
  103.           base.WorldDraw(line, wd);
  104.           if (!line.Id.IsNull && line.Length > 0.0)
  105.           {
  106.             // Draw a pipe around the line
  107.             EntityColor c =
  108.               wd.SubEntityTraits.TrueColor;
  109.             wd.SubEntityTraits.TrueColor =
  110.               new EntityColor(0x00AfAfff);
  111.             wd.SubEntityTraits.LineWeight =
  112.               LineWeight.LineWeight000;
  113.             Circle clr =
  114.               new Circle(
  115.                 line.StartPoint,
  116.                 line.EndPoint - line.StartPoint,
  117.                 radius
  118.               );
  119.             ExtrudedSurface pipe = new ExtrudedSurface();
  120.             try
  121.             {
  122.               pipe.CreateExtrudedSurface(
  123.                 clr, line.EndPoint - line.StartPoint, sweepOpts
  124.               );
  125.             }
  126.             catch
  127.             {
  128.               Document doc =
  129.                 Application.DocumentManager.MdiActiveDocument;
  130.               doc.Editor.WriteMessage(
  131.                 "\nFailed with CreateExtrudedSurface."
  132.               );
  133.             }
  134.             clr.Dispose();
  135.             pipe.WorldDraw(wd);
  136.             pipe.Dispose();
  137.             wd.SubEntityTraits.TrueColor = c;
  138.           }
  139.           return true;
  140.         }
  141.       }
  142.       return base.WorldDraw(d, wd);
  143.     }
  144.     public override int SetAttributes(Drawable d, DrawableTraits t)
  145.     {
  146.       int b = base.SetAttributes(d, t);
  147.       double radius = 0.0;
  148.       if (d is DBObject)
  149.         radius = PipeRadiusForObject((DBObject)d);
  150.       if (radius > 0.0)
  151.       {
  152.         // Set color to index 6
  153.         t.Color = 6;
  154.         // and lineweight to .40 mm
  155.         t.LineWeight = LineWeight.LineWeight040;
  156.       }
  157.       return b;
  158.     }
  159.   }
  160.   // An overrule to make a pipe out of circle
  161.   public class CirclePipeDrawOverrule : PipeDrawOverrule
  162.   {
  163.     static public CirclePipeDrawOverrule theOverrule =
  164.       new CirclePipeDrawOverrule();
  165.     private SweepOptions sweepOpts = new SweepOptions();
  166.     public override bool WorldDraw(Drawable d, WorldDraw wd)
  167.     {
  168.       double radius = 0.0;
  169.       if (d is DBObject)
  170.         radius = PipeRadiusForObject((DBObject)d);
  171.       if (radius > 0.0)
  172.       {
  173.         Circle circle = d as Circle;
  174.         if (circle != null)
  175.         {
  176.           // Draw the circle as is, with overruled attributes
  177.           base.WorldDraw(circle, wd);
  178.           // Needed to avoid ill-formed swept surface
  179.           if (circle.Radius > radius)
  180.           {
  181.             // Draw a pipe around the cirle
  182.             EntityColor c = wd.SubEntityTraits.TrueColor;
  183.             wd.SubEntityTraits.TrueColor =
  184.               new EntityColor(0x3fffe0e0);
  185.             wd.SubEntityTraits.LineWeight =
  186.               LineWeight.LineWeight000;
  187.             Vector3d normal =
  188.               (circle.Center - circle.StartPoint).
  189.                 CrossProduct(circle.Normal);
  190.             Circle clr =
  191.               new Circle(
  192.                 circle.StartPoint, normal, radius
  193.               );
  194.             SweptSurface pipe = new SweptSurface();
  195.             pipe.CreateSweptSurface(clr, circle, sweepOpts);
  196.             clr.Dispose();
  197.             pipe.WorldDraw(wd);
  198.             pipe.Dispose();
  199.             wd.SubEntityTraits.TrueColor = c;
  200.           }
  201.           return true;
  202.         }
  203.       }
  204.       return base.WorldDraw(d, wd);
  205.     }
  206.     public override int SetAttributes(Drawable d, DrawableTraits t)
  207.     {
  208.       int b = base.SetAttributes(d, t);
  209.       double radius = 0.0;
  210.       if (d is DBObject)
  211.         radius = PipeRadiusForObject((DBObject)d);
  212.       if (radius > 0.0)
  213.       {
  214.         // Set color to index 2
  215.         t.Color = 2;
  216.         // and lineweight to .60 mm
  217.         t.LineWeight = LineWeight.LineWeight060;
  218.       }
  219.       return b;
  220.     }
  221.   }
  222.   public class LinePipeTransformOverrule : TransformOverrule
  223.   {
  224.     static public LinePipeTransformOverrule theOverrule =
  225.       new LinePipeTransformOverrule();
  226.     private SweepOptions sweepOpts = new SweepOptions();
  227.     public override void Explode(Entity e, DBObjectCollection objs)
  228.     {
  229.       double radius = 0.0;
  230.       if (e is DBObject)
  231.         radius = PipeDrawOverrule.PipeRadiusForObject(e);
  232.       if (radius > 0.0)
  233.       {
  234.         Line line = e as Line;
  235.         if (line != null)
  236.         {
  237.           if (!line.Id.IsNull && line.Length > 0.0)
  238.           {
  239.             // Draw a pipe around the line
  240.             Circle clr =
  241.               new Circle(
  242.                 line.StartPoint,
  243.                 line.EndPoint - line.StartPoint,
  244.                 radius
  245.               );
  246.             ExtrudedSurface pipe = new ExtrudedSurface();
  247.             try
  248.             {
  249.               pipe.CreateExtrudedSurface(
  250.                 clr, line.EndPoint - line.StartPoint, sweepOpts
  251.               );
  252.             }
  253.             catch
  254.             {
  255.               Document doc =
  256.                 Application.DocumentManager.MdiActiveDocument;
  257.               doc.Editor.WriteMessage(
  258.                 "\nFailed with CreateExtrudedSurface."
  259.               );
  260.             }
  261.             clr.Dispose();
  262.             objs.Add(pipe);
  263.           }
  264.           return;
  265.         }
  266.       }
  267.       base.Explode(e, objs);
  268.     }
  269.   }
  270.   public class CirclePipeTransformOverrule : TransformOverrule
  271.   {
  272.     static public CirclePipeTransformOverrule theOverrule =
  273.       new CirclePipeTransformOverrule();
  274.     private SweepOptions sweepOpts = new SweepOptions();
  275.     public override void Explode(Entity e, DBObjectCollection objs)
  276.     {
  277.       double radius = 0.0;
  278.       if (e is DBObject)
  279.         radius = PipeDrawOverrule.PipeRadiusForObject(e);
  280.       if (radius > 0.0)
  281.       {
  282.         Circle circle = e as Circle;
  283.         if (circle != null)
  284.         {
  285.           // Needed to avoid ill-formed swept surface
  286.           if (circle.Radius > radius)
  287.           {
  288.             // Draw a pipe around the cirle
  289.             Vector3d normal =
  290.               (circle.Center - circle.StartPoint).
  291.                 CrossProduct(circle.Normal);
  292.             Circle clr =
  293.               new Circle(
  294.                 circle.StartPoint, normal, radius
  295.               );
  296.             SweptSurface pipe = new SweptSurface();
  297.             pipe.CreateSweptSurface(clr, circle, sweepOpts);
  298.             clr.Dispose();
  299.             objs.Add(pipe);
  300.           }
  301.           return;
  302.         }
  303.       }
  304.       base.Explode(e, objs);
  305.     }
  306.   }
  307.   public class Commands
  308.   {
  309.     private double _radius = 0.0;
  310.     public void Overrule(bool enable)
  311.     {
  312.       // Regen to see the effect
  313.       // (turn on/off Overruling and LWDISPLAY)
  314.       DrawableOverrule.Overruling = enable;
  315.       if (enable)
  316.         Application.SetSystemVariable("LWDISPLAY", 1);
  317.       else
  318.         Application.SetSystemVariable("LWDISPLAY", 0);
  319.       Document doc =
  320.         Application.DocumentManager.MdiActiveDocument;
  321.       doc.SendStringToExecute("REGEN3\n", true, false, false);
  322.       doc.Editor.Regen();
  323.     }
  324.     [CommandMethod("OVERRULE1")]
  325.     public void OverruleStart()
  326.     {
  327.       ObjectOverrule.AddOverrule(
  328.         RXClass.GetClass(typeof(Line)),
  329.         LinePipeDrawOverrule.theOverrule,
  330.         true
  331.       );
  332.       ObjectOverrule.AddOverrule(
  333.         RXClass.GetClass(typeof(Circle)),
  334.         CirclePipeDrawOverrule.theOverrule,
  335.         true
  336.       );
  337.       ObjectOverrule.AddOverrule(
  338.         RXClass.GetClass(typeof(Line)),
  339.         LinePipeTransformOverrule.theOverrule,
  340.         true
  341.       );
  342.       ObjectOverrule.AddOverrule(
  343.         RXClass.GetClass(typeof(Circle)),
  344.         CirclePipeTransformOverrule.theOverrule,
  345.         true
  346.       );
  347.       Overrule(true);
  348.     }
  349.     [CommandMethod("OVERRULE0")]
  350.     public void OverruleEnd()
  351.     {
  352.       ObjectOverrule.RemoveOverrule(
  353.         RXClass.GetClass(typeof(Line)),
  354.         LinePipeDrawOverrule.theOverrule
  355.       );
  356.       ObjectOverrule.RemoveOverrule(
  357.         RXClass.GetClass(typeof(Circle)),
  358.         CirclePipeDrawOverrule.theOverrule
  359.       );
  360.       ObjectOverrule.RemoveOverrule(
  361.         RXClass.GetClass(typeof(Line)),
  362.         LinePipeTransformOverrule.theOverrule
  363.       );
  364.       ObjectOverrule.RemoveOverrule(
  365.         RXClass.GetClass(typeof(Circle)),
  366.         CirclePipeTransformOverrule.theOverrule
  367.       );
  368.       Overrule(false);
  369.     }
  370.     [CommandMethod("MP", CommandFlags.UsePickSet)]
  371.     public void MakePipe()
  372.     {
  373.       Document doc =
  374.         Application.DocumentManager.MdiActiveDocument;
  375.       Database db = doc.Database;
  376.       Editor ed = doc.Editor;
  377.       // Ask the user to select the entities to make into pipes
  378.       PromptSelectionOptions pso =
  379.         new PromptSelectionOptions();
  380.       pso.AllowDuplicates = false;
  381.       pso.MessageForAdding =
  382.         "\nSelect objects to turn into pipes: ";
  383.       PromptSelectionResult selRes =
  384.         doc.Editor.GetSelection(pso);
  385.       // If the user didn't make valid selection, we return
  386.       if (selRes.Status != PromptStatus.OK)
  387.         return;
  388.       SelectionSet ss = selRes.Value;
  389.       // Ask the user for the pipe radius to set
  390.       PromptDoubleOptions pdo =
  391.         new PromptDoubleOptions(
  392.           "\nSpecify pipe radius:"
  393.         );
  394.       // Use the previous value, if if already called
  395.       if (_radius > 0.0)
  396.       {
  397.         pdo.DefaultValue = _radius;
  398.         pdo.UseDefaultValue = true;
  399.       }
  400.       pdo.AllowNegative = false;
  401.       pdo.AllowZero = false;
  402.       PromptDoubleResult pdr =
  403.         ed.GetDouble(pdo);
  404.       // Return if something went wrong
  405.       if (pdr.Status != PromptStatus.OK)
  406.         return;
  407.       // Set the "last radius" value for when
  408.       // the command is called next
  409.       _radius = pdr.Value;
  410.       // Use a transaction to edit our various objects
  411.       Transaction tr =
  412.         db.TransactionManager.StartTransaction();
  413.       using (tr)
  414.       {
  415.         // Lop through the selected objects
  416.         foreach (SelectedObject o in ss)
  417.         {
  418.           // We could choose only to add XData to the objects
  419.           // we know will use it (Lines and Circles, for now)
  420.           DBObject obj =
  421.             tr.GetObject(o.ObjectId, OpenMode.ForWrite);
  422.           PipeDrawOverrule.SetPipeRadiusOnObject(tr, obj, _radius);
  423.         }
  424.         tr.Commit();
  425.       }
  426.     }
  427.   }
  428. }
As before we can create a bunch of lines and circles, assign them radii using the MP command, and then display them using the OVERRULE1 command:


Now when we perform our EXPLODE, selecting the various objects, and instead of a rejection message we get a set of Solid3D objects replacing our lines and circles:

Too cool! :-)
As you can hopefully see, the more you look at the Overrule API in AutoCAD 2010, the more power you uncover. Keep the comments & questions coming – it’s fun to see how this little application is evolving based on your feedback.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
 楼主| 发表于 2009-6-16 09:02 | 显示全部楼层
三、复制重定义
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:
  1. public class CopyOverrule : ObjectOverrule
  2. {
  3.   static public CopyOverrule theOverrule =
  4.     new CopyOverrule();
  5.   public override DBObject DeepClone(
  6.     DBObject dbObject, DBObject ownerObject,
  7.     IdMapping idMap, bool isPrimary
  8.   )
  9.   {
  10.     // First we deep clone the object via the parent
  11.     DBObject res =
  12.       base.DeepClone(dbObject, ownerObject, idMap, isPrimary);
  13.     // Then we check for our XData
  14.     if (PipeDrawOverrule.PipeRadiusForObject(res) > 0.0)
  15.     {
  16.       // A transaction is needed by the set function to access
  17.       // the RegApp table - we could also assume the app name
  18.       // is registered and have a separate implementation
  19.       // not taking the transaction...        
  20.       // Just as we might also have chosen to remove the XData
  21.       Transaction tr =
  22.         dbObject.Database.TransactionManager.StartTransaction();
  23.       using (tr)
  24.       {
  25.         PipeDrawOverrule.SetPipeRadiusOnObject(tr, res, 0.0);
  26.         tr.Commit();
  27.       }
  28.     }
  29.     return res;
  30.   }
  31. }
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:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.Runtime;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.GraphicsInterface;
  7. using Autodesk.AutoCAD.Colors;
  8. namespace DrawOverrules
  9. {
  10.   public abstract class PipeDrawOverrule : DrawableOverrule
  11.   {
  12.     const string regAppName = "TTIF_PIPE";
  13.     public PipeDrawOverrule()
  14.     {
  15.       // Tell AutoCAD to filter on our application name
  16.       // (this means our overrule will only be called
  17.       // on objects possessing XData with this name)
  18.       SetXDataFilter(regAppName);
  19.     }
  20.     // Get the XData for a particular object
  21.     // and return the "pipe radius" if it exists
  22.     public static double PipeRadiusForObject(DBObject obj)
  23.     {
  24.       double res = 0.0;
  25.       ResultBuffer rb = obj.XData;
  26.       if (rb != null)
  27.       {
  28.         bool foundStart = false;
  29.         foreach (TypedValue tv in rb)
  30.         {
  31.           if (tv.TypeCode == (int)DxfCode.ExtendedDataRegAppName &&
  32.               tv.Value.ToString() == regAppName)
  33.             foundStart = true;
  34.           else
  35.           {
  36.             if (foundStart == true)
  37.             {
  38.               if (tv.TypeCode == (int)DxfCode.ExtendedDataReal)
  39.               {
  40.                 res = (double)tv.Value;
  41.                 break;
  42.               }
  43.             }
  44.           }
  45.         }
  46.         rb.Dispose();
  47.       }
  48.       return res;
  49.     }
  50.     // Set the "pipe radius" in the XData of a particular object
  51.     public static void SetPipeRadiusOnObject(
  52.       Transaction tr, DBObject obj, double radius
  53.     )
  54.     {
  55.       Database db = obj.Database;
  56.       // Make sure the application is registered
  57.       // (we could separate this out to be called
  58.       // only once for a set of operations)
  59.       RegAppTable rat =
  60.         (RegAppTable)tr.GetObject(
  61.           db.RegAppTableId,
  62.           OpenMode.ForRead
  63.         );
  64.       if (!rat.Has(regAppName))
  65.       {
  66.         rat.UpgradeOpen();
  67.         RegAppTableRecord ratr = new RegAppTableRecord();
  68.         ratr.Name = regAppName;
  69.         rat.Add(ratr);
  70.         tr.AddNewlyCreatedDBObject(ratr, true);
  71.       }
  72.       // Create the XData and set it on the object
  73.       ResultBuffer rb =
  74.         new ResultBuffer(
  75.           new TypedValue(
  76.             (int)DxfCode.ExtendedDataRegAppName, regAppName
  77.           ),
  78.           new TypedValue(
  79.             (int)DxfCode.ExtendedDataReal, radius
  80.           )
  81.         );
  82.       obj.XData = rb;
  83.       rb.Dispose();
  84.     }
  85.   }
  86.   // An overrule to make a pipe out of line
  87.   public class LinePipeDrawOverrule : PipeDrawOverrule
  88.   {
  89.     static public LinePipeDrawOverrule theOverrule =
  90.       new LinePipeDrawOverrule();
  91.     private SweepOptions sweepOpts = new SweepOptions();
  92.     public override bool WorldDraw(Drawable d, WorldDraw wd)
  93.     {
  94.       double radius = 0.0;
  95.       if (d is DBObject)
  96.         radius = PipeRadiusForObject((DBObject)d);
  97.       if (radius > 0.0)
  98.       {
  99.         Line line = d as Line;
  100.         if (line != null)
  101.         {
  102.           // Draw the line as is, with overruled attributes
  103.           base.WorldDraw(line, wd);
  104.           if (!line.Id.IsNull && line.Length > 0.0)
  105.           {
  106.             // Draw a pipe around the line
  107.             EntityColor c =
  108.               wd.SubEntityTraits.TrueColor;
  109.             wd.SubEntityTraits.TrueColor =
  110.               new EntityColor(0x00AfAfff);
  111.             wd.SubEntityTraits.LineWeight =
  112.               LineWeight.LineWeight000;
  113.             Circle clr =
  114.               new Circle(
  115.                 line.StartPoint,
  116.                 line.EndPoint - line.StartPoint,
  117.                 radius
  118.               );
  119.             ExtrudedSurface pipe = new ExtrudedSurface();
  120.             try
  121.             {
  122.               pipe.CreateExtrudedSurface(
  123.                 clr, line.EndPoint - line.StartPoint, sweepOpts
  124.               );
  125.             }
  126.             catch
  127.             {
  128.               Document doc =
  129.                 Application.DocumentManager.MdiActiveDocument;
  130.               doc.Editor.WriteMessage(
  131.                 "\nFailed with CreateExtrudedSurface."
  132.               );
  133.             }
  134.             clr.Dispose();
  135.             pipe.WorldDraw(wd);
  136.             pipe.Dispose();
  137.             wd.SubEntityTraits.TrueColor = c;
  138.           }
  139.           return true;
  140.         }
  141.       }
  142.       return base.WorldDraw(d, wd);
  143.     }
  144.     public override int SetAttributes(Drawable d, DrawableTraits t)
  145.     {
  146.       int b = base.SetAttributes(d, t);
  147.       double radius = 0.0;
  148.       if (d is DBObject)
  149.         radius = PipeRadiusForObject((DBObject)d);
  150.       if (radius > 0.0)
  151.       {
  152.         // Set color to index 6
  153.         t.Color = 6;
  154.         // and lineweight to .40 mm
  155.         t.LineWeight = LineWeight.LineWeight040;
  156.       }
  157.       return b;
  158.     }
  159.   }
  160.   // An overrule to make a pipe out of circle
  161.   public class CirclePipeDrawOverrule : PipeDrawOverrule
  162.   {
  163.     static public CirclePipeDrawOverrule theOverrule =
  164.       new CirclePipeDrawOverrule();
  165.     private SweepOptions sweepOpts = new SweepOptions();
  166.     public override bool WorldDraw(Drawable d, WorldDraw wd)
  167.     {
  168.       double radius = 0.0;
  169.       if (d is DBObject)
  170.         radius = PipeRadiusForObject((DBObject)d);
  171.       if (radius > 0.0)
  172.       {
  173.         Circle circle = d as Circle;
  174.         if (circle != null)
  175.         {
  176.           // Draw the circle as is, with overruled attributes
  177.           base.WorldDraw(circle, wd);
  178.           // Needed to avoid ill-formed swept surface
  179.           if (circle.Radius > radius)
  180.           {
  181.             // Draw a pipe around the cirle
  182.             EntityColor c = wd.SubEntityTraits.TrueColor;
  183.             wd.SubEntityTraits.TrueColor =
  184.               new EntityColor(0x3fffe0e0);
  185.             wd.SubEntityTraits.LineWeight =
  186.               LineWeight.LineWeight000;
  187.             Vector3d normal =
  188.               (circle.Center - circle.StartPoint).
  189.                 CrossProduct(circle.Normal);
  190.             Circle clr =
  191.               new Circle(
  192.                 circle.StartPoint, normal, radius
  193.               );
  194.             SweptSurface pipe = new SweptSurface();
  195.             pipe.CreateSweptSurface(clr, circle, sweepOpts);
  196.             clr.Dispose();
  197.             pipe.WorldDraw(wd);
  198.             pipe.Dispose();
  199.             wd.SubEntityTraits.TrueColor = c;
  200.           }
  201.           return true;
  202.         }
  203.       }
  204.       return base.WorldDraw(d, wd);
  205.     }
  206.     public override int SetAttributes(Drawable d, DrawableTraits t)
  207.     {
  208.       int b = base.SetAttributes(d, t);
  209.       double radius = 0.0;
  210.       if (d is DBObject)
  211.         radius = PipeRadiusForObject((DBObject)d);
  212.       if (radius > 0.0)
  213.       {
  214.         // Set color to index 2
  215.         t.Color = 2;
  216.         // and lineweight to .60 mm
  217.         t.LineWeight = LineWeight.LineWeight060;
  218.       }
  219.       return b;
  220.     }
  221.   }
  222.   public class LinePipeTransformOverrule : TransformOverrule
  223.   {
  224.     static public LinePipeTransformOverrule theOverrule =
  225.       new LinePipeTransformOverrule();
  226.     private SweepOptions sweepOpts = new SweepOptions();
  227.     public override void Explode(Entity e, DBObjectCollection objs)
  228.     {
  229.       double radius = 0.0;
  230.       if (e is DBObject)
  231.         radius = PipeDrawOverrule.PipeRadiusForObject(e);
  232.       if (radius > 0.0)
  233.       {
  234.         Line line = e as Line;
  235.         if (line != null)
  236.         {
  237.           if (!line.Id.IsNull && line.Length > 0.0)
  238.           {
  239.             // Draw a pipe around the line
  240.             Circle clr =
  241.               new Circle(
  242.                 line.StartPoint,
  243.                 line.EndPoint - line.StartPoint,
  244.                 radius
  245.               );
  246.             ExtrudedSurface pipe = new ExtrudedSurface();
  247.             try
  248.             {
  249.               pipe.CreateExtrudedSurface(
  250.                 clr, line.EndPoint - line.StartPoint, sweepOpts
  251.               );
  252.             }
  253.             catch
  254.             {
  255.               Document doc =
  256.                 Application.DocumentManager.MdiActiveDocument;
  257.               doc.Editor.WriteMessage(
  258.                 "\nFailed with CreateExtrudedSurface."
  259.               );
  260.             }
  261.             clr.Dispose();
  262.             objs.Add(pipe);
  263.           }
  264.           return;
  265.         }
  266.       }
  267.       base.Explode(e, objs);
  268.     }
  269.   }
  270.   public class CirclePipeTransformOverrule : TransformOverrule
  271.   {
  272.     static public CirclePipeTransformOverrule theOverrule =
  273.       new CirclePipeTransformOverrule();
  274.     private SweepOptions sweepOpts = new SweepOptions();
  275.     public override void Explode(Entity e, DBObjectCollection objs)
  276.     {
  277.       double radius = 0.0;
  278.       if (e is DBObject)
  279.         radius = PipeDrawOverrule.PipeRadiusForObject(e);
  280.       if (radius > 0.0)
  281.       {
  282.         Circle circle = e as Circle;
  283.         if (circle != null)
  284.         {
  285.           // Needed to avoid ill-formed swept surface
  286.           if (circle.Radius > radius)
  287.           {
  288.             // Draw a pipe around the cirle
  289.             Vector3d normal =
  290.               (circle.Center - circle.StartPoint).
  291.                 CrossProduct(circle.Normal);
  292.             Circle clr =
  293.               new Circle(
  294.                 circle.StartPoint, normal, radius
  295.               );
  296.             SweptSurface pipe = new SweptSurface();
  297.             pipe.CreateSweptSurface(clr, circle, sweepOpts);
  298.             clr.Dispose();
  299.             objs.Add(pipe);
  300.           }
  301.           return;
  302.         }
  303.       }
  304.       base.Explode(e, objs);
  305.     }
  306.   }
  307.   public class CopyOverrule : ObjectOverrule
  308.   {
  309.     static public CopyOverrule theOverrule =
  310.       new CopyOverrule();
  311.     public override DBObject DeepClone(
  312.       DBObject dbObject, DBObject ownerObject,
  313.       IdMapping idMap, bool isPrimary
  314.     )
  315.     {
  316.       // First we deep clone the object via the parent
  317.       DBObject res =
  318.         base.DeepClone(dbObject, ownerObject, idMap, isPrimary);
  319.       // Then we check for our XData
  320.       if (PipeDrawOverrule.PipeRadiusForObject(res) > 0.0)
  321.       {
  322.         // A transaction is needed by the set function to access
  323.         // the RegApp table - we could also assume the app name
  324.         // is registered and have a separate implementation
  325.         // not taking the transaction...        
  326.         // Just as we might also have chosen to remove the XData
  327.         Transaction tr =
  328.           dbObject.Database.TransactionManager.StartTransaction();
  329.         using (tr)
  330.         {
  331.           PipeDrawOverrule.SetPipeRadiusOnObject(tr, res, 0.0);
  332.           tr.Commit();
  333.         }
  334.       }
  335.       return res;
  336.     }
  337.   }
  338.   public class Commands
  339.   {
  340.     private double _radius = 0.0;
  341.     public void Overrule(bool enable)
  342.     {
  343.       // Regen to see the effect
  344.       // (turn on/off Overruling and LWDISPLAY)
  345.       DrawableOverrule.Overruling = enable;
  346.       if (enable)
  347.         Application.SetSystemVariable("LWDISPLAY", 1);
  348.       else
  349.         Application.SetSystemVariable("LWDISPLAY", 0);
  350.       Document doc =
  351.         Application.DocumentManager.MdiActiveDocument;
  352.       doc.SendStringToExecute("REGEN3\n", true, false, false);
  353.       doc.Editor.Regen();
  354.     }
  355.     [CommandMethod("OVERRULE1")]
  356.     public void OverruleStart()
  357.     {
  358.       ObjectOverrule.AddOverrule(
  359.         RXClass.GetClass(typeof(Line)),
  360.         LinePipeDrawOverrule.theOverrule,
  361.         true
  362.       );
  363.       ObjectOverrule.AddOverrule(
  364.         RXClass.GetClass(typeof(Circle)),
  365.         CirclePipeDrawOverrule.theOverrule,
  366.         true
  367.       );
  368.       ObjectOverrule.AddOverrule(
  369.         RXClass.GetClass(typeof(Line)),
  370.         LinePipeTransformOverrule.theOverrule,
  371.         true
  372.       );
  373.       ObjectOverrule.AddOverrule(
  374.         RXClass.GetClass(typeof(Circle)),
  375.         CirclePipeTransformOverrule.theOverrule,
  376.         true
  377.       );
  378.       ObjectOverrule.AddOverrule(
  379.         RXClass.GetClass(typeof(Entity)),
  380.         CopyOverrule.theOverrule,
  381.         true
  382.       );
  383.       Overrule(true);
  384.     }
  385.     [CommandMethod("OVERRULE0")]
  386.     public void OverruleEnd()
  387.     {
  388.       ObjectOverrule.RemoveOverrule(
  389.         RXClass.GetClass(typeof(Line)),
  390.         LinePipeDrawOverrule.theOverrule
  391.       );
  392.       ObjectOverrule.RemoveOverrule(
  393.         RXClass.GetClass(typeof(Circle)),
  394.         CirclePipeDrawOverrule.theOverrule
  395.       );
  396.       ObjectOverrule.RemoveOverrule(
  397.         RXClass.GetClass(typeof(Line)),
  398.         LinePipeTransformOverrule.theOverrule
  399.       );
  400.       ObjectOverrule.RemoveOverrule(
  401.         RXClass.GetClass(typeof(Circle)),
  402.         CirclePipeTransformOverrule.theOverrule
  403.       );
  404.       ObjectOverrule.RemoveOverrule(
  405.         RXClass.GetClass(typeof(Entity)),
  406.         CopyOverrule.theOverrule
  407.       );
  408.       Overrule(false);
  409.     }
  410.     [CommandMethod("MP", CommandFlags.UsePickSet)]
  411.     public void MakePipe()
  412.     {
  413.       Document doc =
  414.         Application.DocumentManager.MdiActiveDocument;
  415.       Database db = doc.Database;
  416.       Editor ed = doc.Editor;
  417.       // Ask the user to select the entities to make into pipes
  418.       PromptSelectionOptions pso =
  419.         new PromptSelectionOptions();
  420.       pso.AllowDuplicates = false;
  421.       pso.MessageForAdding =
  422.         "\nSelect objects to turn into pipes: ";
  423.       PromptSelectionResult selRes =
  424.         doc.Editor.GetSelection(pso);
  425.       // If the user didn't make valid selection, we return
  426.       if (selRes.Status != PromptStatus.OK)
  427.         return;
  428.       SelectionSet ss = selRes.Value;
  429.       // Ask the user for the pipe radius to set
  430.       PromptDoubleOptions pdo =
  431.         new PromptDoubleOptions(
  432.           "\nSpecify pipe radius:"
  433.         );
  434.       // Use the previous value, if if already called
  435.       if (_radius > 0.0)
  436.       {
  437.         pdo.DefaultValue = _radius;
  438.         pdo.UseDefaultValue = true;
  439.       }
  440.       pdo.AllowNegative = false;
  441.       pdo.AllowZero = false;
  442.       PromptDoubleResult pdr =
  443.         ed.GetDouble(pdo);
  444.       // Return if something went wrong
  445.       if (pdr.Status != PromptStatus.OK)
  446.         return;
  447.       // Set the "last radius" value for when
  448.       // the command is called next
  449.       _radius = pdr.Value;
  450.       // Use a transaction to edit our various objects
  451.       Transaction tr =
  452.         db.TransactionManager.StartTransaction();
  453.       using (tr)
  454.       {
  455.         // Loop through the selected objects
  456.         foreach (SelectedObject o in ss)
  457.         {
  458.           // We could choose only to add XData to the objects
  459.           // we know will use it (Lines and Circles, for now)
  460.           DBObject obj =
  461.             tr.GetObject(o.ObjectId, OpenMode.ForWrite);
  462.           PipeDrawOverrule.SetPipeRadiusOnObject(tr, obj, _radius);
  463.         }
  464.         tr.Commit();
  465.       }
  466.     }
  467.   }
  468. }
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:

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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
 楼主| 发表于 2009-6-16 09:10 | 显示全部楼层
四、在属性窗口中显示和修改自定义属性
May 06, 2009
Modifying an AutoCAD object’s state via a dynamic property defined using .NET
I’ve been meaning to get to this one for a while. This post takes the OPM .NET implementation and shows how to use it to allow modification of data persisted with an object: in this case we’re going to use the XData in which we store the “pipe radius” for the AutoCAD 2010 overrule sample we’ve recently been developing.
To start with, I needed to migrate the OPM .NET module to work with AutoCAD 2010, which meant installing Visual Studio 2008 SP1. Other than that the code migrated very easily, and the project (with the built asdkOPMNetExt.dll assembly) can be found here. I recommend placing the module in AutoCAD’s main program folder and having it demand-load on AutoCAD startup (if you choose to use it).
[A quick comment on that, as I know some people dislike doing this... it's highly recommended to place your .NET assemblies in AutoCAD's main program folder: you will avoid a whole category of subtle problems by doing so. You needn't feel it's dangerous as long as you prefix the filename of each of your modules with your Registered Developer Symbol (RDS).]
Here’s the C# code to add to our overrule application:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.Runtime;
  4. using Autodesk.AutoCAD.Windows.OPM;
  5. using Autodesk.AutoCAD.Interop.Common;
  6. using System.Runtime.InteropServices;
  7. using System.Reflection;
  8. using System;
  9. using DrawOverrules;
  10. namespace PropertyEditing
  11. {
  12.   #region Our Custom Property
  13.   [
  14.     Guid("E64CAA14-EA92-46ea-82D6-420FA873F16F"),
  15.     ProgId("OverruleSample.PipeRadius.1"),
  16.     ClassInterface(ClassInterfaceType.None),
  17.     ComDefaultInterface(typeof(IDynamicProperty2)),
  18.     ComVisible(true)
  19.   ]
  20.   public class CustomProp : IDynamicProperty2
  21.   {
  22.     private IDynamicPropertyNotify2 m_pSink = null;
  23.     // Unique property ID
  24.     public void GetGUID(out Guid propGUID)
  25.     {
  26.       propGUID =
  27.         new Guid("F60AE3DA-0373-4d24-82D2-B2646517ABCB");
  28.     }
  29.     // Property display name
  30.     public void GetDisplayName(out string szName)
  31.     {
  32.       szName = "Pipe radius";
  33.     }
  34.     // Show/Hide property in the OPM, for this object instance
  35.     public void IsPropertyEnabled(object pUnk, out int bEnabled)
  36.     {
  37.       bEnabled = 1;
  38.     }
  39.     // Is property showing but disabled
  40.     public void IsPropertyReadOnly(out int bReadonly)
  41.     {
  42.       bReadonly = 0;
  43.     }
  44.     // Get the property description string
  45.     public void GetDescription(out string szName)
  46.     {
  47.       szName =
  48.         "Radius of the pipe profile applied to this linear entity.";
  49.     }
  50.     // OPM will typically display these in an edit field
  51.     // optional: meta data representing property type name,
  52.     // ex. ACAD_ANGLE
  53.     public void GetCurrentValueName(out string szName)
  54.     {
  55.       throw new System.NotImplementedException();
  56.     }
  57.     // What is the property type, ex. VT_R8
  58.     public void GetCurrentValueType(out ushort varType)
  59.     {
  60.       // The Property Inspector supports the following data
  61.       // types for dynamic properties:
  62.       // VT_I2, VT_I4, VT_R4, VT_R8,VT_BSTR, VT_BOOL
  63.       // and VT_USERDEFINED.
  64.       varType = 4; // VT_R4?
  65.     }
  66.     // Get the property value, passes the specific object
  67.     // we need the property value for.
  68.     public void GetCurrentValueData(object pUnk, ref object pVarData)
  69.     {
  70.       // Get the value and return it to AutoCAD
  71.       AcadObject obj = pUnk as AcadObject;
  72.       if (obj != null)
  73.       {
  74.         Document doc =
  75.           Application.DocumentManager.MdiActiveDocument;
  76.         Transaction tr =
  77.           doc.TransactionManager.StartTransaction();
  78.         using (tr)
  79.         {
  80.           DBObject o =
  81.             tr.GetObject(
  82.               new ObjectId((IntPtr)obj.ObjectID),
  83.               OpenMode.ForRead
  84.             );
  85.           pVarData =
  86.             PipeDrawOverrule.PipeRadiusForObject(o);
  87.         }
  88.       }
  89.       else
  90.         pVarData = 0.0;
  91.     }
  92.     // Set the property value, passes the specific object we
  93.     // want to set the property value for
  94.     public void SetCurrentValueData(object pUnk, object varData)
  95.     {
  96.       // Save the value returned to you
  97.       AcadObject obj = pUnk as AcadObject;
  98.       if (obj != null)
  99.       {
  100.         Document doc =
  101.           Application.DocumentManager.MdiActiveDocument;
  102.         DocumentLock dl =
  103.           doc.LockDocument(
  104.             DocumentLockMode.ProtectedAutoWrite,
  105.             null, null, true
  106.           );        
  107.         using (dl)
  108.         {
  109.           Transaction tr =
  110.             doc.TransactionManager.StartTransaction();
  111.           using (tr)
  112.           {
  113.             DBObject o =
  114.               tr.GetObject(
  115.                 new ObjectId((IntPtr)obj.ObjectID),
  116.                 OpenMode.ForWrite
  117.               );
  118.             PipeDrawOverrule.SetPipeRadiusOnObject(
  119.               tr, o, (float)varData
  120.             );
  121.             tr.Commit();
  122.           }
  123.         }
  124.       }
  125.     }
  126.     // OPM passes its implementation of IDynamicPropertyNotify, you
  127.     // cache it and call it to inform OPM your property has changed
  128.     public void Connect(object pSink)
  129.     {
  130.       m_pSink = (IDynamicPropertyNotify2)pSink;
  131.     }
  132.     public void Disconnect()
  133.     {
  134.       m_pSink = null;
  135.     }
  136.   }
  137.   #endregion
  138.   #region Application Entry Point
  139.   public class MyEntryPoint : IExtensionApplication
  140.   {
  141.     protected internal CustomProp custProp = null;
  142.     public void Initialize()
  143.     {
  144.       Assembly.LoadFrom("asdkOPMNetExt.dll");
  145.       // Add the Dynamic Property to Lines and Circles
  146.       // (might add it at the Entity level, instead)
  147.       Dictionary classDict = SystemObjects.ClassDictionary;
  148.       RXClass lineDesc = (RXClass)classDict.At("AcDbLine");
  149.       RXClass cirDesc = (RXClass)classDict.At("AcDbCircle");
  150.       custProp = new CustomProp();
  151.       IPropertyManager2 pPropMan =
  152.         (IPropertyManager2)xOPM.xGET_OPMPROPERTY_MANAGER(lineDesc);
  153.       pPropMan.AddProperty((object)custProp);
  154.       pPropMan =
  155.         (IPropertyManager2)xOPM.xGET_OPMPROPERTY_MANAGER(cirDesc);
  156.       pPropMan.AddProperty((object)custProp);
  157.     }
  158.     public void Terminate()
  159.     {
  160.       // Remove the Dynamic Property
  161.       Dictionary classDict = SystemObjects.ClassDictionary;
  162.       RXClass lineDesc = (RXClass)classDict.At("AcDbLine");
  163.       RXClass cirDesc = (RXClass)classDict.At("AcDbCircle");
  164.       IPropertyManager2 pPropMan =
  165.         (IPropertyManager2)xOPM.xGET_OPMPROPERTY_MANAGER(lineDesc);
  166.       pPropMan.RemoveProperty((object)custProp);
  167.       pPropMan =
  168.         (IPropertyManager2)xOPM.xGET_OPMPROPERTY_MANAGER(cirDesc);
  169.       pPropMan.RemoveProperty((object)custProp);
  170.       custProp = null;
  171.     }
  172.   }
  173.   #endregion
  174. }

Some comments on the implementation:
GetCurrentValueData() and SetCurrentValueData() both have to open the object to access it’s .NET protocol
We might also have used COM to access the XData, but this approach reuses previously-developed code
To modify the object we need to lock the current document
We use the ProtectedAutoWrite locking mode for this, so that all our property edits are grouped into a single undo group
We use the “protected” version of the locking mode as there’s a lock needed elsewhere, probably in the drawing code. If we use the standard AutoWrite lock we get an eLockViolation message
We’re using a new transaction for each read/modification
This feels like overkill, but then as we’re in an UI-bound operation it’s unlikely to have a perceived performance impact
We’re also using the static protocol from the DrawOverrule class for the XData retrieval/setting
With hindsight this probably should live in its own helper class, which is the original way I had it :-S :-)
Here’s a model we’re going to modify using the Properties Palette:

Now we select one of our overruled objects – a circle – and see it's new dynamic property:

When we select all our objects, we see the property varies:

Now we modify the property to be the same for all our objects:

And we can see the result of our modification:

You can see that the property currently isn’t categorised: as mentioned previously, we would have to implement ICategorizedProperty in our OPM .NET module for this to be possible. Which I will attempt, one day.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
 楼主| 发表于 2009-6-16 09:16 | 显示全部楼层
五、使用重定义亮显特定名字的块参照
June 15, 2009
Highlighting named blocks using AutoCAD 2010’s overrule API from .NET
This is a nice sample provided by Stephen Preston, who manages DevTech’s Americas team. Stephen has put this together in anticipation of his upcoming AU class on the overrule API introduced in AutoCAD 2010. [I know the final class list has not yet been announced, but Stephen is co-owner of the Customization & Programming track at this year’s AU and presumably has the inside skinny on the selected classes. Which means he has a head-start on preparing his material, lucky fellow. :-)]
The sample allows the user to enter a text string that it uses to highlight any block containing that string in its name. This is quite handy for identifying the instances of a particular block in a drawing, but it might also be modified to highlight other objects (you might want to highlight mis-spelt words or standards violations, for instance).
Here’s the C# code, reformatted for this blog:
  1. using System;
  2. using Autodesk.AutoCAD.Runtime;
  3. using Autodesk.AutoCAD.ApplicationServices;
  4. using Autodesk.AutoCAD.DatabaseServices;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.EditorInput;
  7. using Autodesk.AutoCAD.GraphicsInterface;
  8. namespace MyCustomFilterOverrule
  9. {
  10.   // This is our custom DrawableOverrule class. We're just
  11.   // overruling WorldDraw and IsApplicable.
  12.   // This class is implemented as a singleton class, and
  13.   // includes subroutines that are called by the CommandMethods
  14.   // in another class
  15.   public class MyDrawOverrule : DrawableOverrule
  16.   {
  17.     // Where properties have been defined, use the property rather
  18.     // than the raw variable.
  19.     // I'm using properties where I need some additional logic to
  20.     // run as I get/set the variable.
  21.     // The text we'll search for in our block name.
  22.     private string mTxt;
  23.     // Color Index of block highlight
  24.     private short mColor = 3;
  25.     // Used to track whether this Overrule has been registered
  26.     // (so we don't try to register it more than once).
  27.     private bool mRegistered = false;
  28.     // Used to store one and only instance of our singleton class
  29.     private static MyDrawOverrule mSingleton;
  30.     // Used to reset Overruling value to the value it had before
  31.     // we switched them on. (There may be other overrules in place)
  32.     private static bool mOldOverruleValue;
  33.     // The color we highlight blocks with
  34.     private short HighlightColor
  35.     {
  36.       get { return mColor; }
  37.       set { if (value >= 0 && value <= 127) mColor = value; }
  38.     }
  39.     // The text we'll search for in the block name
  40.     private string SearchText
  41.     {
  42.       get { return mTxt; }
  43.       set { mTxt = value; }
  44.     }
  45.     // Private constructor because its a singleton
  46.     private MyDrawOverrule()
  47.     {
  48.       // Do nothing
  49.     }
  50.     // Shared propery to return our singleton instance
  51.     // (and instantiate new instance on first call)
  52.     public static MyDrawOverrule GetInstance
  53.     {
  54.       get
  55.       {
  56.         if (mSingleton == null)
  57.         {
  58.           mSingleton = new MyDrawOverrule();
  59.         }
  60.         return mSingleton;
  61.       }
  62.     }
  63.     private void InitOverrule()
  64.     {
  65.       if (!mRegistered)
  66.       {
  67.         Overrule.AddOverrule(
  68.           RXObject.GetClass(typeof(BlockReference)), this, false
  69.         );
  70.         SetCustomFilter();
  71.         mOldOverruleValue = Overrule.Overruling;
  72.         mRegistered = true;
  73.       }
  74.       Overrule.Overruling = true;
  75.     }
  76.     // Prompts user to select the color index they want to
  77.     // highlight blocks with
  78.     public void SetColor()
  79.     {
  80.       Editor ed =
  81.         Application.DocumentManager.MdiActiveDocument.Editor;
  82.       PromptIntegerOptions opts =
  83.         new PromptIntegerOptions(
  84.           "\nEnter block finder color index: "
  85.         );
  86.       opts.DefaultValue = HighlightColor;
  87.       opts.LowerLimit = 0;
  88.       opts.UpperLimit = 127;
  89.       opts.UseDefaultValue = true;
  90.       PromptIntegerResult res = ed.GetInteger(opts);
  91.       // If requested highlight color is a new color,
  92.       // then we want to change it
  93.       if (res.Status == PromptStatus.OK &&
  94.           HighlightColor != res.Value)
  95.       {
  96.         HighlightColor = (short)res.Value;
  97.         // Regen is required to update changes on screen
  98.         ed.Regen();
  99.       }
  100.     }
  101.     public void FindText()
  102.     {
  103.       Editor ed =
  104.         Application.DocumentManager.MdiActiveDocument.Editor;
  105.       ed.WriteMessage(
  106.         "\nCurrent block search text is "{0}".", SearchText
  107.       );
  108.       PromptStringOptions opts =
  109.         new PromptStringOptions(
  110.           "\nEnter new block search text: "
  111.         );
  112.       PromptResult res = ed.GetString(opts);
  113.       // If the user cancelled then we exit the command
  114.       if (res.Status != PromptStatus.OK)
  115.         return;
  116.       // If the user didn't type any text then we remove
  117.       // the overrule and exit
  118.       if (res.StringResult == "")
  119.       {
  120.         SearchText = "";
  121.         ResetBlocks();
  122.       }
  123.       else
  124.       {
  125.         // Set search text for Overrule to that entered by user
  126.         SearchText = res.StringResult.ToUpper();
  127.         InitOverrule();
  128.         // Turn Overruling on
  129.         Overrule.Overruling = true;
  130.         // Regen is required to update changes on screen.
  131.         ed.Regen();
  132.       }
  133.     }
  134.     // Removes our overrules
  135.     public void ResetBlocks()
  136.     {
  137.       Editor ed =
  138.         Application.DocumentManager.MdiActiveDocument.Editor;
  139.       Overrule.Overruling = mOldOverruleValue;
  140.       if (mRegistered)
  141.       {
  142.         Overrule.RemoveOverrule(
  143.           RXObject.GetClass(typeof(BlockReference)), this
  144.         );
  145.         mRegistered = false;
  146.         ed.Regen();
  147.       }
  148.     }
  149.     // Overrule WorldDraw so we can draw our additional
  150.     // graphics
  151.     public override bool WorldDraw(Drawable drawable, WorldDraw wd)
  152.     {
  153.       // Better safe than sorry - check it really is a
  154.       // BlockReference before continuing.
  155.       BlockReference br = drawable as BlockReference;
  156.       if (br != null)
  157.       {
  158.         // Now we want to draw a green box around the attributes
  159.         // extents
  160.         Extents3d ext = (Extents3d)br.Bounds;
  161.         Point3d maxPt = ext.MaxPoint;
  162.         Point3d minPt = ext.MinPoint;
  163.         Point3dCollection pts = new Point3dCollection();
  164.         // These are the vertices of the highlight box
  165.         pts.Add(new Point3d(minPt.X, minPt.Y, minPt.Z));
  166.         pts.Add(new Point3d(minPt.X, maxPt.Y, minPt.Z));
  167.         pts.Add(new Point3d(maxPt.X, maxPt.Y, minPt.Z));
  168.         pts.Add(new Point3d(maxPt.X, minPt.Y, minPt.Z));
  169.         // Store current filltype and set to FillAlways
  170.         FillType oldFillType = wd.SubEntityTraits.FillType;
  171.         wd.SubEntityTraits.FillType = FillType.FillAlways;
  172.         // Store old graphics color and set to the color we want
  173.         short oldColor = wd.SubEntityTraits.Color;
  174.         wd.SubEntityTraits.Color = HighlightColor;
  175.         // Draw the filled polygon
  176.         wd.Geometry.Polygon(pts);
  177.         // Restore old settings
  178.         wd.SubEntityTraits.FillType = oldFillType;
  179.         wd.SubEntityTraits.Color = oldColor;
  180.       }
  181.       // Let the overruled Drawable draw itself.
  182.       return base.WorldDraw(drawable, wd);
  183.     }
  184.     // This function is called if we call SetCustomFilter on our
  185.     // custom overrule.
  186.     // We add our own code to return true if the BlockReference
  187.     // passed in is one we want to highlight.
  188.     public override bool IsApplicable(RXObject overruledSubject)
  189.     {
  190.       // If it's a BlockReference, we check if the Block Name
  191.       // contains our string
  192.       BlockReference br = overruledSubject as BlockReference;
  193.       if (br != null && SearchText != "")
  194.       {
  195.         // Returns whether the filter is applicable to this object
  196.         return br.Name.Contains(SearchText);
  197.       }
  198.       // Only get to here if object isn't a BlockReference
  199.       return false;
  200.     }
  201.   }
  202.   // Our command class, which relays commands to MyDrawOverrule.
  203.   public class myPlugin
  204.   {
  205.     [CommandMethod("SHOWBLOCKS")]
  206.     public static void FindText()
  207.     {
  208.       MyDrawOverrule.GetInstance.FindText();
  209.     }
  210.     [CommandMethod("SHOWCOLOR")]
  211.     public static void SetColor()
  212.     {
  213.       MyDrawOverrule.GetInstance.SetColor();
  214.     }
  215.   }
  216. }

Here’s what happens when we OPEN the “Mechanical – Multileaders.dwg” sample drawing, NETLOAD our application and use SHOWBLOCKS to look for the “M045” text string:

Here’s what we see if we broaden the search to include all blocks with the string “M0” in their name and change the highlight colour to 1 using SHOWCOLOR:

To clear the selection, the user simply has to run SHOWBLOCKS and specify an empty string as the search term.
Stephen will be presenting both C# and VB.NET versions of this sample application during his class at this year’s AU. If you find overrules interesting, then I strongly recommend signing up for the session (I’ll let you know when registrations are open). I’m sure that during the class Stephen will be demonstrating other interesting capabilities made available to AutoCAD .NET developers by this very cool API.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
 楼主| 发表于 2009-9-6 20:05 | 显示全部楼层
六、捕获夹点操作时的图元
August 03, 2009
Knowing when an AutoCAD object is grip-edited using overrules in .NET
This week I will mostly be posting about Overrules. [For those of you who haven’t seen  The Fast Show (called Brilliant in the US), this is an obscure reference to a character named Jesse. :-)]
Aside from this post, Stephen Preston has sent me the samples he’s put together for his AU class on this topic, so expect some serious plagiarism (although now that I’ve given him credit I suppose it’s not really plagiarism :-).
Here’s a question I received recently by email:
    Is there some posibility to write something in some of your next blogs about how to get coordinate of grip when user move on screen in realtime. By using grip_stretch command...
    Example: we first draw polyline, then select it and move grip point...how we can know current position for grip coordinate before user pick point...in realtime...
Coincidentally I’ve been working on an internal project that uses Overrules to achieve this (or something quite similar). As I expect I’ve said before, AutoCAD 2010’s Overrule API is an incredibly powerful mechanism for hooking into and controlling object behaviour: it’s essentially our approach for providing the equivalent of custom object support to .NET programmers (which was the top item for a number of years on AutoCAD’s API wishlist).
To hook into object modification inside AutoCAD, we have a couple of options:
    * A TransformOverrule allows us to hook into an object’s TransformBy(), which tells use when it’s rotated, scaled or moved.
          o This is effective at trapping object-level transformations, whether via grips or commands such as MOVE, but won’t tell you when a specific vertex is modified (for instance)
    * A GripOverrule allows us to hook into GetGripPointsAt() and MoveGripPointsAt(), which can tell use when a grip-stretch is performed on our object.
          o This gives us more fine-grained information on a per-grip basis, but clearly won’t be called for commands such as MOVE which bypass the use of grips.
In this particular instance it makes more sense to use a GripOverrule, as we need to know when a particular polyline vertex is edited.
Here’s some C# code that implements a GripOverrule for AutoCAD entities (it works just as well for any entity with grips implemented via GetGripPointsAt() & MoveGripPointsAt(), so there’s no need to limit it just to Polylines, even if we could very easily).
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.Runtime;
  6. using System.Collections.Generic;
  7. namespace GripOverruleTest
  8. {
  9.   public class GripVectorOverrule : GripOverrule
  10.   {
  11.     // A static pointer to our overrule instance
  12.     static public GripVectorOverrule theOverrule =
  13.       new GripVectorOverrule();
  14.     // A flag to indicate whether we're overruling
  15.     static bool overruling = false;
  16.     // A single set of grips would not have worked in
  17.     // the case where multiple objects were selected.
  18.     static Dictionary<string, Point3dCollection> _gripDict =
  19.       new Dictionary<string, Point3dCollection>();
  20.     public GripVectorOverrule()
  21.     {
  22.     }
  23.     private string GetKey(Entity e)
  24.     {
  25.       // Generate a key based on the name of the object's type
  26.       // and its geometric extents
  27.       // (We cannot use the ObjectId, as this is null during
  28.       // grip-stretch operations.)
  29.       return e.GetType().Name + ":" + e.GeometricExtents.ToString();
  30.     }
  31.     // Save the locations of the grips for a particular entity
  32.     private void StoreGripInfo(Entity e, Point3dCollection grips)
  33.     {
  34.       string key = GetKey(e);
  35.       if (_gripDict.ContainsKey(key))
  36.       {
  37.         // Clear the grips if any already associated
  38.         Point3dCollection grps = _gripDict[key];
  39.         using (grps)
  40.         {
  41.           grps.Clear();
  42.         }
  43.         _gripDict.Remove(key);
  44.       }
  45.       // Now we add our grips
  46.       Point3d[] pts = new Point3d[grips.Count];
  47.       grips.CopyTo(pts, 0);
  48.       Point3dCollection gps = new Point3dCollection(pts);
  49.       _gripDict.Add(key, gps);
  50.     }
  51.     // Get the locations of the grips for an entity
  52.     private Point3dCollection RetrieveGripInfo(Entity e)
  53.     {
  54.       Point3dCollection grips = null;
  55.       string key = GetKey(e);
  56.       if (_gripDict.ContainsKey(key))
  57.       {
  58.         grips = _gripDict[key];
  59.       }
  60.       return grips;
  61.     }
  62.     public override void GetGripPoints(
  63.       Entity e,
  64.       Point3dCollection grips,
  65.       IntegerCollection snaps,
  66.       IntegerCollection geomIds
  67.     )
  68.     {
  69.       base.GetGripPoints(e, grips, snaps, geomIds);
  70.       StoreGripInfo(e, grips);
  71.     }
  72.     public override void MoveGripPointsAt(
  73.       Entity e,
  74.       IntegerCollection indices,
  75.       Vector3d offset
  76.     )
  77.     {
  78.       Document doc =
  79.         Application.DocumentManager.MdiActiveDocument;
  80.       Editor ed = doc.Editor;
  81.       Point3dCollection grips = RetrieveGripInfo(e);
  82.       if (grips != null)
  83.       {
  84.         // Could get multiple points moved at once,
  85.         // hence the integer collection
  86.         foreach (int i in indices)
  87.         {
  88.           // Get the grip point from our internal state
  89.           Point3d pt = grips[i];
  90.           // Draw a vector from the grip point to the newly
  91.           // offset location, using the index into the
  92.           // grip array as the color (excluding colours 0 and 7).
  93.           // These vectors don't getting cleared, which makes
  94.           // for a fun effect.
  95.           ed.DrawVector(
  96.             pt,
  97.             pt + offset,
  98.             (i >= 6 ? i + 2 : i + 1), // exclude colours 0 and 7
  99.             false
  100.           );
  101.         }
  102.       }
  103.       base.MoveGripPointsAt(e, indices, offset);
  104.     }
  105.     [CommandMethod("GOO")]
  106.     public void GripOverruleOnOff()
  107.     {
  108.       Document doc =
  109.         Application.DocumentManager.MdiActiveDocument;
  110.       Editor ed = doc.Editor;
  111.       if (overruling)
  112.       {
  113.         ObjectOverrule.RemoveOverrule(
  114.           RXClass.GetClass(typeof(Entity)),
  115.           GripVectorOverrule.theOverrule
  116.         );
  117.       }
  118.       else
  119.       {
  120.         ObjectOverrule.AddOverrule(
  121.           RXClass.GetClass(typeof(Entity)),
  122.           GripVectorOverrule.theOverrule,
  123.           true
  124.         );
  125.       }
  126.       overruling = !overruling;
  127.       GripOverrule.Overruling = overruling;
  128.       ed.WriteMessage(
  129.         "\nGrip overruling turned {0}.",
  130.         (overruling ? "on" : "off")
  131.       );
  132.     }
  133.   }
  134. }
One important thing to bear in mind about this sample: we have to store the grips for a particular entity during the GetGripPointsAt() call and then use them during the MoveGripPointsAt() call. This is complicated for a number of reasons...
Firstly, we can’t just store the points in a single point collection, as there could be multiple calls to GetGripPointsAt() (while the grips are retrieved and displayed by AutoCAD) followed by multiple calls to MoveGripPointsAt() (while the grips are used to manipulate the objects). So we really need to map a set of points to a particular object.
The really tricky thing is that we have an entity passed into both GetGripPointsAt() and MoveGripPointsAt(), but – and here’s the rub – the GetGripPointsAt() entity is typically the original, database-dependent entity, while the entities passed into MoveGripPointsAt() are temporary clones. The temporary clones do not have ObjectIds, which is the best way to identify objects and associate data with them in a map (for instance).
The default cloning behaviour can be overruled using a TransformOverrule – by returning false from the CloneMeForDragging() callback – but assuming we don’t do that (and we don’t really want to do that, as it opens another can of worms), we need to find another way to identify an object that is passed into GetGripPointsAt() with its clone passed in to MoveGripPointsAt().
The solution I ended up going for was to generate a key based on the class-name of the object followed by its geometric extents. This isn’t perfect, but it’s as good as I could find. There is a possibility that entities of the same type could exist at the same spot and still have different grips (which is the main problem – if they have the same grip locations then it doesn’t matter), but that will cause problems with this implementation. If people want to implement a more robust technique, they will probably need to do so for specific entity types, where they have access to more specific information that will help identify their objects: we are sticking to generic entity-level information, which makes it a little hard to be 100% certain we have the right object.
One other point… as we’re using the geometric extents to identify an object, it’s hard to clear the grip information from our dictionary: by the time the OnGripStatusChanged() method is called, the entity’s geometry has typically changed, which means we can no longer find it in the dictionary. So for a cleaner solution it’s worth clearing the dictionary at an appropriate moment (probably using some kind of command- or document locking-event).
OK, now let’s see what happens when we NETLOAD our application and run our GOO command, which toggles the use of the grip overrule:
Command: GOO
Grip overruling turned on.
OK, so far so good. Let’s create a standard AutoCAD polyline containing both line and arc segments:

When we select it, we see its grips along with the quick properties panel:

And when we use its various grips, we see temporary vectors drawn during each MoveGripPointsAt() call (which will disappear at the next REGEN):

Fun stuff! I’m looking forward to diving further into Overrules over the next week or two… :-)

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
 楼主| 发表于 2009-9-7 12:03 | 显示全部楼层
七、简单的改变直线显示
August 17, 2009
A simple overrule to change the way AutoCAD lines are displayed using .NET
Thanks to Stephen Preston, who manages our DevTech Americas team, for donating the samples from his upcoming AU class for posting on this blog.
Let’s start the week with a nice simple sample: the first from Stephen’s AU class. Looking back even to the first C# overrule sample I posted here, I can see that most have been quite complex, mainly because they’ve performed complicated things. Today’s code implements a very simple DrawableOverrule which changes the way lines are displayed in AutoCAD:
Here’s Stephen’s C# code, reformatted to fit the blog:
  1. using Autodesk.AutoCAD.Runtime;
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.GraphicsInterface;
  5. namespace MyFirstOverrule
  6. {
  7.   // This is our custom DrawableOverrule class.
  8.   // In this case we're just overruling WorldDraw
  9.   public class MyDrawOverrule : DrawableOverrule
  10.   {
  11.     public override bool WorldDraw(Drawable drawable, WorldDraw wd)
  12.     {
  13.       // Cast Drawable to Line so we can access its methods and
  14.       // properties
  15.       Line ln = (Line)drawable;
  16.       // Draw some graphics primitives
  17.       wd.Geometry.Circle(
  18.         ln.StartPoint + 0.5 * ln.Delta,
  19.         ln.Length / 5,
  20.         ln.Normal
  21.       );
  22.       // In this case we don't want the line to draw itself, nor do
  23.       // we want ViewportDraw called
  24.       return true;
  25.     }
  26.   }
  27.   public class Commands
  28.   {
  29.     //Shared member variable to store our Overrule instance
  30.     private static MyDrawOverrule _drawOverrule;
  31.     [CommandMethod("TOG")]
  32.     public static void ToggleOverrule()
  33.     {
  34.       // Initialize Overrule if first time run
  35.       if (_drawOverrule == null)
  36.       {
  37.         _drawOverrule = new MyDrawOverrule();
  38.         Overrule.AddOverrule(
  39.           RXObject.GetClass(
  40.             typeof(Line)),
  41.             _drawOverrule,
  42.             false
  43.           );
  44.         Overrule.Overruling = true;
  45.       }
  46.       else
  47.       {
  48.         // Toggle Overruling on/off
  49.         Overrule.Overruling = !Overrule.Overruling;
  50.       }
  51.       // Regen is required to update changes on screen
  52.       Application.DocumentManager.MdiActiveDocument.Editor.Regen();
  53.     }
  54.   }
  55. }
Some points to note:
    * This code chooses to replace the way lines are currently drawn
          o Rather than drawing a line, we draw a circles at the line’s mid-point with a radius relative to the line’s length
    * We’ve deliberately kept the code very simple: a single command (TOG) is used to toggle the use of the overrule
    * Rather than always relying on toggling the overall Overruling state, we force it to true when we run for the first time
          o I have another application loaded that implements overrules: the first time the TOG command was run previously, overruling was actually turned off if we don’t do it this way
To try the code, build the application against AutoCAD 2010 (or even higher, if you’re visiting us from the future :-) and draw some lines:

Now let’s NETLOAD our application and run the TOG command:

Each line has been “replaced” by a circle. But when we select one of the circles, we see it’s really just a line, and shows the grips a line would:

When we grip-edit an end-point of one of our lines, we can see it changing the line, but the graphics displayed continue to be circular:

And finally, if we finish the editing operation and run the TOG command again we see the lines has been modified:

That’s it for today’s post. I’ll probably be delving further into Stephen’s material for posts later in the week.


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
 楼主| 发表于 2009-9-12 20:17 | 显示全部楼层
八、把点附着到曲线
August 24, 2009
Gluing a point to an AutoCAD curve using overrules from .NET – Part 1
Over the weekend I put together a little prototype to prove a concept for an internal project I’m working on. The idea was to force a point onto a curve (meaning anything inheriting from Curve in AutoCAD, such as Arc, Circle, Ellipse, Leader, Line, Polyline, Polyline2d, Polyline3d, Ray, Spline, Xline…), so that when the point is moved it snaps onto the curve to which it’s assigned. The solution I’ve put together is far from being complete – which is partly why I’m planning on making this a series, so I can flesh it out a little further in further posts – but it does demonstrate a reasonable technique for addressing the requirement.
The approach I chose was to use a TransformOverrule to modify the standard AutoCAD point’s TransformBy() behaviour (see this previous post for some commentary on using a TransformOverrule vs. a GripOverrule). Our TransformOverrule stores a list of curves that have had points attached to them, and during TransformBy() we check each one to see which curve this point was on. We then get the transformed point (i.e. the one being chosen by the user) and from there we get the closest point on that curve, which becomes the point’s new location.
TransformBy() is a pretty handy operation to overrule: it’s used by grip-editing and by the MOVE command, so you know these operations will lead to your object’s positional integrity being maintained (direct modification of properties, such as via the Properties Palette, won’t lead to it being called, however, so it’s not enough if you need to maintain complete control).
Some comments on the choice of storing a list of curves rather than some other association between the point and the curve:
    * Storing a map between the point (via its ObjectId) and the curve wouldn’t work, as TransformBy() often has to work on a temporary copy of an object, rather than the object itself (and the copy’s ObjectId will therefore be Null).
          o We might also have looked at an approach such as the one used previously in our GripOverrule.
    * It might be possible to attach data (perhaps XData) to the point identifying the curve it’s attached to, but this would need to be available on the temporary clone (and is something that I’d need to check works).
    * It’s possible that working through a list of curves, checking each one, could become perceptibly slow if working with huge sets of data, but at that point a more efficient spatial indexing technique could be adopted.
    * Using a list of curves also allows us to modify the implementation to allow a point to travel along a network of curves (we’ll go through this modification in a future post).
    * One drawback of this approach is that once we move the curve independently from the point, they become detached as the point is no longer on any of the curves. We’ve put a specific clause in to allow points not on curves to be moved, but it would also be good to have the point be transformed along with the curve, so they stay together (another potential future modification). In the meantime the user will have to move the point back onto the curve (using the NEAr object snap, to make sure it’s precise) for the overrule to work for it, again.
One last point: we’re not persisting the curve list, in any way, so don’t expect points to reattach to lines when a session is restarted (until the POC command is used to create further points on curves and therefore add the curves to the list of those being managed by our system).
OK, enough blather, let’s get on with looking at the C# code:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.Runtime;
  6. using System.Collections.Generic;
  7. namespace PointOnCurveTest
  8. {
  9.   public class PtTransOverrule : TransformOverrule
  10.   {
  11.     // A static pointer to our overrule instance
  12.     static public PtTransOverrule theOverrule =
  13.       new PtTransOverrule();
  14.     // A list of the curves that have had points
  15.     // attached to
  16.     static internal List<ObjectId> _curves =
  17.       new List<ObjectId>();
  18.     // A flag to indicate whether we're overruling
  19.     static bool overruling = false;
  20.     public PtTransOverrule() {}
  21.     // Out primary overruled function
  22.     public override void TransformBy(Entity e, Matrix3d mat)
  23.     {
  24.       // We only care about points
  25.       DBPoint pt = e as DBPoint;
  26.       if (pt != null)
  27.       {
  28.         Database db = HostApplicationServices.WorkingDatabase;
  29.         // For each curve, let's check whether our point is on it
  30.         bool found = false;
  31.         // We're using an Open/Close transaction, to avoid problems
  32.         // with us using transactions in an event handler
  33.         OpenCloseTransaction tr =
  34.           db.TransactionManager.StartOpenCloseTransaction();
  35.         using (tr)
  36.         {
  37.           foreach (ObjectId curId in _curves)
  38.           {
  39.             DBObject obj = tr.GetObject(curId, OpenMode.ForRead);
  40.             Curve cur = obj as Curve;
  41.             if (cur != null)
  42.             {
  43.               Point3d ptOnCurve =
  44.                 cur.GetClosestPointTo(pt.Position, false);
  45.               Vector3d dist = ptOnCurve - pt.Position;
  46.               if (dist.IsZeroLength(Tolerance.Global))
  47.               {
  48.                 Point3d pos =
  49.                   cur.GetClosestPointTo(
  50.                     pt.Position.TransformBy(mat),
  51.                     false
  52.                   );
  53.                 pt.Position = pos;
  54.                 found = true;
  55.                 break;
  56.               }
  57.             }
  58.           }
  59.           // If the point isn't on any curve, let the standard
  60.           // TransformBy() do its thing
  61.           if (!found)
  62.           {
  63.             base.TransformBy(e, mat);
  64.           }
  65.         }
  66.       }
  67.     }
  68.     [CommandMethod("POC")]
  69.     public void CreatePointOnCurve()
  70.     {
  71.       Document doc =
  72.         Application.DocumentManager.MdiActiveDocument;
  73.       Database db = doc.Database;
  74.       Editor ed = doc.Editor;
  75.       // Ask the user to select a curve
  76.       PromptEntityOptions opts =
  77.         new PromptEntityOptions(
  78.           "\nSelect curve at the point to create: "
  79.         );
  80.       opts.SetRejectMessage(
  81.         "\nEntity must be a curve."
  82.       );
  83.       opts.AddAllowedClass(typeof(Curve), false);
  84.       PromptEntityResult per = ed.GetEntity(opts);
  85.       ObjectId curId = per.ObjectId;
  86.       if (curId != ObjectId.Null)
  87.       {
  88.         // Let's make sure we'll be able to see our point
  89.         db.Pdmode = 97;  // square with a circle
  90.         db.Pdsize = -10; // relative to the viewport size
  91.         Transaction tr =
  92.           doc.TransactionManager.StartTransaction();
  93.         using (tr)
  94.         {
  95.           DBObject obj =
  96.             tr.GetObject(curId, OpenMode.ForRead);
  97.           Curve cur = obj as Curve;
  98.           if (cur != null)
  99.           {
  100.             // Out initial point should be the closest point
  101.             // on the curve to the one picked
  102.             Point3d pos =
  103.               cur.GetClosestPointTo(per.PickedPoint, false);
  104.             DBPoint pt = new DBPoint(pos);
  105.             // Add it to the same space as the curve
  106.             BlockTableRecord btr =
  107.               (BlockTableRecord)tr.GetObject(
  108.                 cur.BlockId,
  109.                 OpenMode.ForWrite
  110.               );
  111.             ObjectId ptId = btr.AppendEntity(pt);
  112.             tr.AddNewlyCreatedDBObject(pt, true);
  113.           }
  114.           tr.Commit();
  115.           // And add the curve to our central list
  116.           _curves.Add(curId);
  117.         }
  118.         // Turn on the transform overrule if it isn't already
  119.         if (!overruling)
  120.         {
  121.           ObjectOverrule.AddOverrule(
  122.             RXClass.GetClass(typeof(DBPoint)),
  123.             PtTransOverrule.theOverrule,
  124.             true
  125.           );
  126.           overruling = true;
  127.           TransformOverrule.Overruling = true;
  128.         }
  129.       }
  130.     }
  131.   }
  132. }
Now let’s see what happens when we run the POC command (for Point On Curve, but then it’s also a Proof Of Concept – geddit? :-).
The POC command will create a point at the selected location on a curve, at which point we can grip-edit it:

We can see that as we move the grip point around the drawing, the closest point to the attached curve is always used:

The point is always on the original curve, even if we try to select a point on another curve (even one that may be on the list of curves maintained by our PtTransOverrule class, if the POC command has been used on other curves):

That’s enough to get us started. Later in the week we’ll look at extending the code to create a stronger attachment between the point and the curve, but also to allow the point to travel along a network of curves. Fun, fun, fun! :-)

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
 楼主| 发表于 2009-9-12 20:20 | 显示全部楼层
August 26, 2009
Gluing a point to an AutoCAD curve using overrules from .NET – Part 2
In the last post we looked at some code to create a point on a curve, and make sure it stays on that curve when edited.
In this post we’re extending that code (albeit slightly) to work with a network of curves: the idea is that any curve which has a point created on it becomes a candidate for any point to snap onto as it moves around. This could clearly be extended to provided a better way of specifying the curves forming the network, of course.
Here’s the updated C# code
  1. sing Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.Runtime;
  6. using System.Collections.Generic;
  7. namespace PointOnCurveTest
  8. {
  9.   public class PtTransOverrule : TransformOverrule
  10.   {
  11.     // A static pointer to our overrule instance
  12.     static public PtTransOverrule theOverrule =
  13.       new PtTransOverrule();
  14.     // A list of the curves that have had points
  15.     // attached to
  16.     static internal List<ObjectId> _curves =
  17.       new List<ObjectId>();
  18.     // A flag to indicate whether we're overruling
  19.     static bool overruling = false;
  20.     public PtTransOverrule() {}
  21.     // Out primary overruled function
  22.     public override void TransformBy(Entity e, Matrix3d mat)
  23.     {
  24.       // We only care about points
  25.       DBPoint pt = e as DBPoint;
  26.       if (pt != null)
  27.       {
  28.         Database db = HostApplicationServices.WorkingDatabase;
  29.         // Work through the curves to find the closest to our
  30.         // transformed point
  31.         double min = 0.0;
  32.         Point3d bestPt = Point3d.Origin;
  33.         bool first = true;
  34.         // We're using an Open/Close transaction, to avoid
  35.         // problems with us using transactions in an event
  36.         // handler
  37.         OpenCloseTransaction tr =
  38.           db.TransactionManager.StartOpenCloseTransaction();
  39.         using (tr)
  40.         {
  41.           foreach (ObjectId curId in _curves)
  42.           {
  43.             DBObject obj =
  44.               tr.GetObject(curId, OpenMode.ForRead);
  45.             Curve cur = obj as Curve;
  46.             if (cur != null)
  47.             {
  48.               Point3d ptLoc =
  49.                 pt.Position.TransformBy(mat);
  50.               Point3d ptOnCurve =
  51.                 cur.GetClosestPointTo(ptLoc, false);
  52.               Vector3d dist = ptOnCurve - ptLoc;
  53.               if (first || dist.Length < min)
  54.               {
  55.                 first = false;
  56.                 min = dist.Length;
  57.                 bestPt = ptOnCurve;
  58.               }
  59.             }
  60.           }
  61.           pt.Position = bestPt;
  62.         }
  63.       }
  64.     }
  65.     [CommandMethod("POC")]
  66.     public void CreatePointOnCurve()
  67.     {
  68.       Document doc =
  69.         Application.DocumentManager.MdiActiveDocument;
  70.       Database db = doc.Database;
  71.       Editor ed = doc.Editor;
  72.     // Ask the user to select a curve
  73.       PromptEntityOptions opts =
  74.       new PromptEntityOptions(
  75.         "\nSelect curve at the point to create: "
  76.       );
  77.       opts.SetRejectMessage(
  78.         "\nEntity must be a curve."
  79.       );
  80.       opts.AddAllowedClass(typeof(Curve), false);
  81.       PromptEntityResult per = ed.GetEntity(opts);
  82.    
  83.       ObjectId curId = per.ObjectId;
  84.       if (curId != ObjectId.Null)
  85.       {
  86.       // Let's make sure we'll be able to see our point
  87.       db.Pdmode = 97;  // square with a circle
  88.       db.Pdsize = -10; // relative to the viewport size
  89.         Transaction tr =
  90.           doc.TransactionManager.StartTransaction();
  91.         using (tr)
  92.         {
  93.           DBObject obj =
  94.             tr.GetObject(curId, OpenMode.ForRead);
  95.           Curve cur = obj as Curve;
  96.           if (cur != null)
  97.           {
  98.           // Our initial point should be the closest point
  99.           // on the curve to the one picked
  100.             Point3d pos =
  101.               cur.GetClosestPointTo(per.PickedPoint, false);
  102.             DBPoint pt = new DBPoint(pos);
  103.           // Add it to the same space as the curve
  104.             BlockTableRecord btr =
  105.               (BlockTableRecord)tr.GetObject(
  106.                 cur.BlockId,
  107.                 OpenMode.ForWrite
  108.               );
  109.             ObjectId ptId = btr.AppendEntity(pt);
  110.             tr.AddNewlyCreatedDBObject(pt, true);
  111.           }
  112.           tr.Commit();
  113.         
  114.         // And add the curve to our central list
  115.         
  116.           _curves.Add(curId);
  117.         }
  118.       // Turn on the transform overrule if it isn't already
  119.         if (!overruling)
  120.         {
  121.           ObjectOverrule.AddOverrule(
  122.             RXClass.GetClass(typeof(DBPoint)),
  123.           PtTransOverrule.theOverrule,
  124.             true
  125.           );
  126.           overruling = true;
  127.           TransformOverrule.Overruling = true;
  128.         }
  129.       }
  130.     }
  131.   }
  132. }
Now after having run our POC command to add points to a number of curves – effectively adding them to the “network” – we can grip-move a point, and should another curve in the network be closer, the point being edited will snap across to that one:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-4-23 14:56 , Processed in 0.249173 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表