明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 15985|回复: 12

[Kean专集] Kean专题(1)—Jigs

   关闭 [复制链接]
发表于 2009-5-14 12:43 | 显示全部楼层 |阅读模式
转载自http://through-the-interface.typepad.com/through_the_interface/jigs/
一、使用Jig动态旋转实体
2008年3月28日
通过 .NET 动态旋转AutoCAD  图元
本文由明经通道 mccad 翻译,转载请注明出处
在上一文章中有一有趣的需求与另一文章中的问题相似。原来的问题是以矩形的中心旋转矩形多段线图元,并且之后还可继续旋转它。有几点我比较感兴趣:
矩形是由四个点组成的简单多段线,它并不存在中心点和旋转角度的概念。
至少对我来说,很明显需要计算中心点并将旋转角度做为XData保存在多段线上。
要形象化的修改最好是以动态方式。
大部分示例是演示如何使用动态来创建新图元,而非修改现有图元。
因此,考虑到这一点,我使用以下C#代码来解决这个问题:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. using Autodesk.AutoCAD.Geometry;
  6. namespace RotatingRectangles
  7. {
  8.   public class Commands
  9.   {
  10.     // Define some constants we'll use to
  11.     // store our XData
  12.     // AppName is our RDS (TTIF, for
  13.     // "Through The InterFace") plus an indicator
  14.     // what it's for (ROTation)
  15.     const string kRegAppName = "TTIF_ROT";
  16.     const int kAppCode = 1001;
  17.     const int kRotCode = 1040;
  18.     class RotateJig : EntityJig
  19.     {
  20.       // Declare some internal state
  21.       double m_baseAngle, m_deltaAngle;
  22.       Point3d m_rotationPoint;
  23.       Matrix3d m_ucs;
  24.       // Constructor sets the state and clones
  25.       // the entity passed in
  26.       // (adequate for simple entities)
  27.       public RotateJig(
  28.         Entity ent,
  29.         Point3d rotationPoint,
  30.         double baseAngle,
  31.         Matrix3d ucs)
  32.         : base(ent.Clone() as Entity)
  33.       {
  34.         m_rotationPoint = rotationPoint;
  35.         m_baseAngle = baseAngle;
  36.         m_ucs = ucs;
  37.       }
  38.       protected override SamplerStatus Sampler(
  39.         JigPrompts jp
  40.       )
  41.       {
  42.         // We acquire a single angular value
  43.         JigPromptAngleOptions jo =
  44.           new JigPromptAngleOptions(
  45.             "\nAngle of rotation: "
  46.           );
  47.         jo.BasePoint = m_rotationPoint;
  48.         jo.UseBasePoint = true;
  49.         PromptDoubleResult pdr =
  50.           jp.AcquireAngle(jo);
  51.         if (pdr.Status == PromptStatus.OK)
  52.         {
  53.           // Check if it has changed or not
  54.           // (reduces flicker)
  55.           if (m_deltaAngle == pdr.Value)
  56.           {
  57.             return SamplerStatus.NoChange;
  58.           }
  59.           else
  60.           {
  61.             // Set the change in angle to
  62.             // the new value
  63.             m_deltaAngle = pdr.Value;
  64.             return SamplerStatus.OK;
  65.           }
  66.         }
  67.         return SamplerStatus.Cancel;
  68.       }
  69.       protected override bool Update()
  70.       {
  71.         // We rotate the polyline by the change
  72.         // minus the base angle
  73.         Matrix3d trans =
  74.           Matrix3d.Rotation(
  75.             m_deltaAngle - m_baseAngle,
  76.             m_ucs.CoordinateSystem3d.Zaxis,
  77.             m_rotationPoint);
  78.         Entity.TransformBy(trans);
  79.         // The base becomes the previous delta
  80.         // and the delta gets set to zero
  81.         m_baseAngle = m_deltaAngle;
  82.         m_deltaAngle = 0.0;
  83.         return true;
  84.       }
  85.       public Entity GetEntity()
  86.       {
  87.         return Entity;
  88.       }
  89.       public double GetRotation()
  90.       {
  91.         // The overall rotation is the
  92.         // base plus the delta
  93.         return m_baseAngle + m_deltaAngle;
  94.       }
  95.     }
  96.     [CommandMethod("ROT")]
  97.     public void RotateEntity()
  98.     {
  99.       Document doc =
  100.         Application.DocumentManager.MdiActiveDocument;
  101.       Editor ed = doc.Editor;
  102.       Database db = doc.Database;
  103.       // First we prompt for the entity to rotate
  104.       PromptEntityOptions peo =
  105.         new PromptEntityOptions(
  106.           "\nSelect entity to rotate: "
  107.         );
  108.       PromptEntityResult per =
  109.         ed.GetEntity(peo);
  110.       if (per.Status == PromptStatus.OK)
  111.       {
  112.         Transaction tr =
  113.           db.TransactionManager.StartTransaction();
  114.         using (tr)
  115.         {
  116.           DBObject obj =
  117.             tr.GetObject(per.ObjectId, OpenMode.ForRead);
  118.           Entity ent = obj as Entity;
  119.           // Use the origin as the default center
  120.           Point3d rotationPoint = Point3d.Origin;
  121.           // If the entity is a polyline,
  122.           // assume it is rectangular and then
  123.           // set the rotation point as its center
  124.           Polyline pl = obj as Polyline;
  125.           if (pl != null)
  126.           {
  127.             LineSegment3d ps0 =
  128.               pl.GetLineSegmentAt(0);
  129.             LineSegment3d ps1 =
  130.               pl.GetLineSegmentAt(1);
  131.             Vector3d vec =
  132.               ((ps0.EndPoint - ps0.StartPoint) / 2.0) +
  133.               ((ps1.EndPoint - ps1.StartPoint) / 2.0);
  134.             rotationPoint = pl.StartPoint + vec;
  135.           }
  136.           // Get the base rotation angle stored with the
  137.           // entity, if there was one (default is 0.0)
  138.           double baseAngle = GetStoredRotation(obj);
  139.           if (ent != null)
  140.           {
  141.             // Get the current UCS, to pass to the Jig
  142.             Matrix3d ucs =
  143.               ed.CurrentUserCoordinateSystem;
  144.             // Create our jig object
  145.             RotateJig jig =
  146.               new RotateJig(
  147.                 ent,
  148.                 rotationPoint,
  149.                 baseAngle,
  150.                 ucs
  151.               );
  152.             PromptResult res = ed.Drag(jig);
  153.             if (res.Status == PromptStatus.OK)
  154.             {
  155.               // Get the overall rotation angle
  156.               // and dispose of the temp clone
  157.               double newAngle = jig.GetRotation();
  158.               jig.GetEntity().Dispose();
  159.               // Rotate the original entity
  160.               Matrix3d trans =
  161.                 Matrix3d.Rotation(
  162.                   newAngle - baseAngle,
  163.                   ucs.CoordinateSystem3d.Zaxis,
  164.                   rotationPoint);
  165.               ent.UpgradeOpen();
  166.               ent.TransformBy(trans);
  167.               // Store the new rotation as XData
  168.               SetStoredRotation(ent, newAngle);
  169.             }
  170.           }
  171.           tr.Commit();
  172.         }
  173.       }
  174.     }
  175.     // Helper function to create a RegApp
  176.     static void AddRegAppTableRecord(string regAppName)
  177.     {
  178.       Document doc =
  179.         Application.DocumentManager.MdiActiveDocument;
  180.       Editor ed = doc.Editor;
  181.       Database db = doc.Database;
  182.       Transaction tr =
  183.         doc.TransactionManager.StartTransaction();
  184.       using (tr)
  185.       {
  186.         RegAppTable rat =
  187.           (RegAppTable)tr.GetObject(
  188.             db.RegAppTableId,
  189.             OpenMode.ForRead,
  190.             false
  191.           );
  192.         if (!rat.Has(regAppName))
  193.         {
  194.           rat.UpgradeOpen();
  195.           RegAppTableRecord ratr =
  196.             new RegAppTableRecord();
  197.           ratr.Name = regAppName;
  198.           rat.Add(ratr);
  199.           tr.AddNewlyCreatedDBObject(ratr, true);
  200.         }
  201.         tr.Commit();
  202.       }
  203.     }
  204.     // Store our rotation angle as XData
  205.     private void SetStoredRotation(
  206.       DBObject obj, double rotation)
  207.     {
  208.       AddRegAppTableRecord(kRegAppName);
  209.       ResultBuffer rb = obj.XData;
  210.       if (rb == null)
  211.       {
  212.         rb =
  213.           new ResultBuffer(
  214.             new TypedValue(kAppCode, kRegAppName),
  215.             new TypedValue(kRotCode, rotation)
  216.           );
  217.       }
  218.       else
  219.       {
  220.         // We can simply add our values - no need
  221.         // to remove the previous ones, the new ones
  222.         // are the ones that get stored
  223.         rb.Add(new TypedValue(kAppCode, kRegAppName));
  224.         rb.Add(new TypedValue(kRotCode, rotation));
  225.       }
  226.       obj.XData = rb;
  227.       rb.Dispose();
  228.     }
  229.     // Retrieve the existing rotation angle from XData
  230.     private double GetStoredRotation(DBObject obj)
  231.     {
  232.       double ret = 0.0;
  233.       ResultBuffer rb = obj.XData;
  234.       if (rb != null)
  235.       {
  236.         // If we find our group code, it means that on
  237.         // the next iteration, we'll get our rotation
  238.         bool bReadyForRot = false;
  239.         foreach (TypedValue tv in rb)
  240.         {
  241.           if (bReadyForRot)
  242.           {
  243.             if (tv.TypeCode == kRotCode)
  244.               ret = (double)tv.Value;
  245.             bReadyForRot = false;
  246.           }
  247.           if (tv.TypeCode == kAppCode)
  248.             bReadyForRot = true;
  249.         }
  250.         rb.Dispose();
  251.       }
  252.       return ret;
  253.     }
  254.   }
  255. }
试用代码前,可用 RECTANG 命令创建一水平方向矩形,然后使用自定义的ROT命令来旋转它
   
之后再调用 ROT命令运行也会正常,因为图元“记住”了旋转角度。如果用了其它的工具旋转了这个矩形,这种效果就会消失。一种方法是深层的分析矩形来测量“旋转角度”:确定矩形的长边,获得它的角度。另一种毫无意义的方法是执行命令让用户输入角度(选择两个点或直接输入)。


 楼主| 发表于 2009-5-14 13:22 | 显示全部楼层

二、在绘图界面动态显示坐标
December 12, 2008
Drawing text planar to the screen inside AutoCAD's drawing window using .NET
I've often seen the question, over the years, of how to draw text in the plane of the screen, even when the current view is not planar to the current UCS. This ability to "screen fix" text has been there, but has required a number of sometimes tricky transformations to get the right behaviour. Well, during a recent internal discussion I became aware of a really handy facility inside AutoCAD which allows you to dependably draw screen-fixed text without jumping through hoops.
In this simple example, we're implementing a DrawJig - a jig that doesn't host an entity but allows us to implement a WorldDraw() callback for something to be drawn - which draws text at a fixed screen location, with a fixed size and orientation. Our code takes the simple task of asking the user to select a point, and shows the current cursor location during the drag at an offset of 30, 30 from the bottom left corner of the drawing window.
Here's the C# code:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.Runtime;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.GraphicsInterface;
  6. namespace JigTextPlanarToScreen
  7. {
  8.   public class TextJig : DrawJig
  9.   {
  10.     private Point3d _position;
  11.     public Point3d Position
  12.     {
  13.       get { return _position; }
  14.     }
  15.     // We'll keep our style alive rather than recreating it
  16.     private TextStyle _style;
  17.     public TextJig()
  18.     {
  19.       _style = new TextStyle();
  20.       _style.Font =
  21.         new FontDescriptor("Calibri", false, true, 0, 0);
  22.       _style.TextSize = 10;
  23.     }
  24.     protected override SamplerStatus Sampler(JigPrompts prompts)
  25.     {
  26.       JigPromptPointOptions opts = new JigPromptPointOptions();
  27.       opts.UserInputControls =
  28.           UserInputControls.Accept3dCoordinates;
  29.       opts.Message = "\nSelect point: ";
  30.       PromptPointResult res = prompts.AcquirePoint(opts);
  31.       if (res.Status == PromptStatus.OK)
  32.       {
  33.         if (_position == res.Value)
  34.         {
  35.           return SamplerStatus.NoChange;
  36.         }
  37.         else
  38.         {
  39.           _position = res.Value;
  40.           return SamplerStatus.OK;
  41.         }
  42.       }
  43.       return SamplerStatus.Cancel;
  44.     }
  45.     protected override bool WorldDraw(WorldDraw draw)
  46.     {
  47.       // We make use of another interface to push our transforms
  48.       WorldGeometry2 wg2 = draw.Geometry as WorldGeometry2;
  49.       if (wg2 != null)
  50.       {
  51.         // Push our transforms onto the stack
  52.         wg2.PushOrientationTransform(
  53.           OrientationBehavior.Screen
  54.         );
  55.         wg2.PushPositionTransform(
  56.           PositionBehavior.Screen,
  57.           new Point2d(30, 30)
  58.         );
  59.         // Draw our screen-fixed text
  60.         wg2.Text(
  61.           new Point3d(0, 0, 0),  // Position
  62.           new Vector3d(0, 0, 1), // Normal
  63.           new Vector3d(1, 0, 0), // Direction
  64.           _position.ToString(), // Text
  65.           true,                  // Rawness
  66.           _style                // TextStyle
  67.         );
  68.         // Remember to pop our transforms off the stack
  69.         wg2.PopModelTransform();
  70.         wg2.PopModelTransform();
  71.       }
  72.       return true;
  73.     }
  74.     [CommandMethod("SELPT")]
  75.     static public void SelectPointWithJig()
  76.     {
  77.       Document doc =
  78.         Application.DocumentManager.MdiActiveDocument;
  79.       Editor ed = doc.Editor;
  80.       TextJig jig = new TextJig();
  81.       PromptResult res = ed.Drag(jig);
  82.       if (res.Status == PromptStatus.OK)
  83.       {
  84.         ed.WriteMessage(
  85.           "\nPoint selected: {0}",
  86.           jig.Position
  87.         );
  88.       }
  89.     }
  90.   }
  91. }
Now let's see our SELPT command in action.
First in a fairly standard view:           And now in an arbitrary 3D view:

OK, that's it for today. Right now I'm at our Developer Day event in Paris, and after this I'm taking a four-week break over the holiday season. Which means my blog output is likely to slow down (to a trickle, perhaps even stop completely) over the coming weeks. So - just in case - I'd like to wish all the readers of "Through the Interface" all the very best for the holiday season. Thank you for your continued support and readership over the last year, here's looking forward to a fun and productive 2009! :-)

 楼主| 发表于 2009-5-14 13:33 | 显示全部楼层
三、拖动一个属性块
March 18, 2009
Jigging an AutoCAD block with attributes using .NET
Thanks, once again, to Philippe Leefsma, a DevTech engineer based in Prague, for contributing the code for this post. While researching an issue he was working on Philippe stumbled across a comment on this previous post where I more-or-less said jigging attributes wasn’t possible. Ahem. Anyway, Philippe decided to – quite rightly – prove me wrong, and the result is today’s post. :-)
It turns out that the trick to jigging a block with attributes is to add the block reference to the database prior to running the jig. I’d been coming at this from another direction – working out how to call through to the right version of the ObjectARX function, the one that allows the block reference to be in-memory rather than db-resident – but Philippe’s approach means that’s no longer needed. I see this technique as potentially being useful when jigging other entities that benefit from being database resident (Solid3d objects spring to mind), so I really appreciate Philippe’s hard work on this.
Here’s the C# code which I’ve edited for posting:
  1. using Autodesk.AutoCAD.Runtime;
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Geometry;
  6. using System.Collections.Generic;
  7. namespace BlockJig
  8. {
  9.   class CBlockJig : EntityJig
  10.   {
  11.     private Point3d _pos;
  12.     private Dictionary<string, Point3d> _attPos;
  13.     private Transaction _tr;
  14.     public CBlockJig(Transaction tr, BlockReference br)
  15.       : base(br)
  16.     {
  17.       _pos = br.Position;
  18.       // Initialize our dictionary with the tag /
  19.       // AttributeDefinition position
  20.       _attPos = new Dictionary<string, Point3d>();
  21.       _tr = tr;
  22.       BlockTableRecord btr =
  23.         (BlockTableRecord)_tr.GetObject(
  24.           br.BlockTableRecord,
  25.           OpenMode.ForRead
  26.         );
  27.       if (btr.HasAttributeDefinitions)
  28.       {
  29.         foreach (ObjectId id in btr)
  30.         {
  31.           DBObject obj =
  32.             tr.GetObject(id, OpenMode.ForRead);
  33.           AttributeDefinition ad =
  34.             obj as AttributeDefinition;
  35.           if (ad != null)
  36.           {
  37.             _attPos.Add(ad.Tag, ad.Position);
  38.           }
  39.         }
  40.       }
  41.     }
  42.     protected override bool Update()
  43.     {
  44.       BlockReference br = Entity as BlockReference;
  45.       br.Position = _pos;
  46.       if (br.AttributeCollection.Count != 0)
  47.       {
  48.         foreach (ObjectId id in br.AttributeCollection)
  49.         {
  50.           DBObject obj =
  51.             _tr.GetObject(id, OpenMode.ForRead);
  52.           AttributeReference ar =
  53.             obj as AttributeReference;
  54.           // Apply block transform to att def position
  55.           if (ar != null)
  56.           {
  57.             ar.UpgradeOpen();
  58.             ar.Position =
  59.               _attPos[ar.Tag].TransformBy(br.BlockTransform);
  60.           }
  61.         }
  62.       }
  63.       return true;
  64.     }
  65.     protected override SamplerStatus Sampler(JigPrompts prompts)
  66.     {
  67.       JigPromptPointOptions opts =
  68.         new JigPromptPointOptions("\nSelect insertion point:");
  69.       opts.BasePoint = new Point3d(0, 0, 0);
  70.       opts.UserInputControls =
  71.         UserInputControls.NoZeroResponseAccepted;
  72.       PromptPointResult ppr = prompts.AcquirePoint(opts);
  73.       if (_pos == ppr.Value)
  74.       {
  75.         return SamplerStatus.NoChange;
  76.       }
  77.       _pos = ppr.Value;
  78.       return SamplerStatus.OK;
  79.     }
  80.     public PromptStatus Run()
  81.     {
  82.       Document doc =
  83.         Application.DocumentManager.MdiActiveDocument;
  84.       Editor ed = doc.Editor;
  85.       PromptResult promptResult = ed.Drag(this);
  86.       return promptResult.Status;
  87.     }
  88.   }
  89.   public class Commands
  90.   {
  91.     [CommandMethod("BJ")]
  92.     static public void BlockJig()
  93.     {
  94.       Document doc =
  95.         Application.DocumentManager.MdiActiveDocument;
  96.       Database db = doc.Database;
  97.       Editor ed = doc.Editor;
  98.       PromptStringOptions pso =
  99.         new PromptStringOptions("\nEnter block name: ");
  100.       PromptResult pr = ed.GetString(pso);
  101.       if (pr.Status != PromptStatus.OK)
  102.         return;
  103.       Transaction tr =
  104.         doc.TransactionManager.StartTransaction();
  105.       using (tr)
  106.       {
  107.         BlockTable bt =
  108.           (BlockTable)tr.GetObject(
  109.             db.BlockTableId,
  110.             OpenMode.ForRead
  111.           );
  112.         BlockTableRecord space =
  113.           (BlockTableRecord)tr.GetObject(
  114.             db.CurrentSpaceId,
  115.             OpenMode.ForRead
  116.           );
  117.         if (!bt.Has(pr.StringResult))
  118.         {
  119.           ed.WriteMessage(
  120.             "\nBlock "" + pr.StringResult + "" not found.");
  121.           return;
  122.         }
  123.         space.UpgradeOpen();
  124.         BlockTableRecord btr =
  125.           (BlockTableRecord)tr.GetObject(
  126.             bt[pr.StringResult],
  127.             OpenMode.ForRead);
  128.         // Block needs to be inserted to current space before
  129.         // being able to append attribute to it
  130.         BlockReference br =
  131.           new BlockReference(new Point3d(), btr.ObjectId);
  132.         space.AppendEntity(br);
  133.         tr.AddNewlyCreatedDBObject(br, true);
  134.         if (btr.HasAttributeDefinitions)
  135.         {
  136.           foreach (ObjectId id in btr)
  137.           {
  138.             DBObject obj =
  139.               tr.GetObject(id, OpenMode.ForRead);
  140.             AttributeDefinition ad =
  141.               obj as AttributeDefinition;
  142.             if (ad != null && !ad.Constant)
  143.             {
  144.               AttributeReference ar =
  145.                 new AttributeReference();
  146.               ar.SetAttributeFromBlock(ad, br.BlockTransform);
  147.               ar.Position =
  148.                 ad.Position.TransformBy(br.BlockTransform);
  149.               ar.TextString = ad.TextString;
  150.               br.AttributeCollection.AppendAttribute(ar);
  151.               tr.AddNewlyCreatedDBObject(ar, true);
  152.             }
  153.           }
  154.         }
  155.         // Run the jig
  156.         CBlockJig myJig = new CBlockJig(tr, br);
  157.         if (myJig.Run() != PromptStatus.OK)
  158.           return;
  159.         // Commit changes if user accepted, otherwise discard
  160.         tr.Commit();
  161.       }
  162.     }
  163.   }
  164. }
When you run the BJ command (short for BlockJig) and specify the name of a block in the current drawing which contains attributes, you’ll now see the attributes with their default values shown as part of the block being jigged. Implementing the code to allow editing of those attributes after insertion is left as an exercise for the reader.
Update:
This code didn't work for a few situations, such as when using justification (attributes would end up at the origin after being dragged) or with MText attributes (which would start at the origin until the mouse was moved).
A big thanks to Roland Feletic from PAUSER ZT-GMBH for helping identify and diagnose the various cases.
Here's the updated C# code:
  1. using Autodesk.AutoCAD.Runtime;
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Geometry;
  6. using System.Collections.Generic;
  7. namespace BlockJigApplication
  8. {
  9.   class AttInfo
  10.   {
  11.     private Point3d _pos;
  12.     private Point3d _aln;
  13.     private bool _aligned;
  14.     public AttInfo(Point3d pos, Point3d aln, bool aligned)
  15.     {
  16.       _pos = pos;
  17.       _aln = aln;
  18.       _aligned = aligned;
  19.     }
  20.     public Point3d Position
  21.     {
  22.       set { _pos = value; }
  23.       get { return _pos; }
  24.     }
  25.     public Point3d Alignment
  26.     {
  27.       set { _aln = value; }
  28.       get { return _aln; }
  29.     }
  30.     public bool IsAligned
  31.     {
  32.       set { _aligned = value; }
  33.       get { return _aligned; }
  34.     }
  35.   }
  36.   class BlockJig : EntityJig
  37.   {
  38.     private Point3d _pos;
  39.     private Dictionary<ObjectId, AttInfo> _attInfo;
  40.     private Transaction _tr;
  41.     public BlockJig(
  42.       Transaction tr,
  43.       BlockReference br,
  44.       Dictionary<ObjectId, AttInfo> attInfo
  45.     ) : base(br)
  46.     {
  47.       _pos = br.Position;
  48.       _attInfo = attInfo;
  49.       _tr = tr;
  50.     }
  51.     protected override bool Update()
  52.     {
  53.       BlockReference br = Entity as BlockReference;
  54.       br.Position = _pos;
  55.       if (br.AttributeCollection.Count != 0)
  56.       {
  57.         foreach (ObjectId id in br.AttributeCollection)
  58.         {
  59.           DBObject obj =
  60.             _tr.GetObject(id, OpenMode.ForRead);
  61.           AttributeReference ar =
  62.             obj as AttributeReference;
  63.           // Apply block transform to att def position
  64.           if (ar != null)
  65.           {
  66.             ar.UpgradeOpen();
  67.             AttInfo ai = _attInfo[ar.ObjectId];
  68.             ar.Position =
  69.               ai.Position.TransformBy(br.BlockTransform);
  70.             if (ai.IsAligned)
  71.             {
  72.               ar.AlignmentPoint =
  73.                 ai.Alignment.TransformBy(br.BlockTransform);
  74.             }
  75.             if (ar.IsMTextAttribute)
  76.             {
  77.               ar.UpdateMTextAttribute();
  78.             }
  79.           }
  80.         }
  81.       }
  82.       return true;
  83.     }
  84.     protected override SamplerStatus Sampler(JigPrompts prompts)
  85.     {
  86.       JigPromptPointOptions opts =
  87.         new JigPromptPointOptions("\nSelect insertion point:");
  88.       opts.BasePoint = new Point3d(0, 0, 0);
  89.       opts.UserInputControls =
  90.         UserInputControls.NoZeroResponseAccepted;
  91.       PromptPointResult ppr = prompts.AcquirePoint(opts);
  92.       if (_pos == ppr.Value)
  93.       {
  94.         return SamplerStatus.NoChange;
  95.       }
  96.       _pos = ppr.Value;
  97.       return SamplerStatus.OK;
  98.     }
  99.     public PromptStatus Run()
  100.     {
  101.       Document doc =
  102.         Application.DocumentManager.MdiActiveDocument;
  103.       Editor ed = doc.Editor;
  104.       PromptResult promptResult = ed.Drag(this);
  105.       return promptResult.Status;
  106.     }
  107.   }
  108.   public class Commands
  109.   {
  110.     [CommandMethod("BJ")]
  111.     static public void BlockJigCmd()
  112.     {
  113.       Document doc =
  114.         Application.DocumentManager.MdiActiveDocument;
  115.       Database db = doc.Database;
  116.       Editor ed = doc.Editor;
  117.       PromptStringOptions pso =
  118.         new PromptStringOptions("\nEnter block name: ");
  119.       PromptResult pr = ed.GetString(pso);
  120.       if (pr.Status != PromptStatus.OK)
  121.         return;
  122.       Transaction tr =
  123.         doc.TransactionManager.StartTransaction();
  124.       using (tr)
  125.       {
  126.         BlockTable bt =
  127.           (BlockTable)tr.GetObject(
  128.             db.BlockTableId,
  129.             OpenMode.ForRead
  130.           );
  131.         if (!bt.Has(pr.StringResult))
  132.         {
  133.           ed.WriteMessage(
  134.             "\nBlock "" + pr.StringResult + "" not found.");
  135.           return;
  136.         }
  137.         BlockTableRecord space =
  138.           (BlockTableRecord)tr.GetObject(
  139.             db.CurrentSpaceId,
  140.             OpenMode.ForWrite
  141.           );
  142.         BlockTableRecord btr =
  143.           (BlockTableRecord)tr.GetObject(
  144.             bt[pr.StringResult],
  145.             OpenMode.ForRead);
  146.         // Block needs to be inserted to current space before
  147.         // being able to append attribute to it
  148.         BlockReference br =
  149.           new BlockReference(new Point3d(), btr.ObjectId);
  150.         space.AppendEntity(br);
  151.         tr.AddNewlyCreatedDBObject(br, true);
  152.         Dictionary<ObjectId, AttInfo> attInfo =
  153.           new Dictionary<ObjectId,AttInfo>();
  154.         if (btr.HasAttributeDefinitions)
  155.         {
  156.           foreach (ObjectId id in btr)
  157.           {
  158.             DBObject obj =
  159.               tr.GetObject(id, OpenMode.ForRead);
  160.             AttributeDefinition ad =
  161.               obj as AttributeDefinition;
  162.             if (ad != null && !ad.Constant)
  163.             {
  164.               AttributeReference ar =
  165.                 new AttributeReference();
  166.               ar.SetAttributeFromBlock(ad, br.BlockTransform);
  167.               ar.Position =
  168.                 ad.Position.TransformBy(br.BlockTransform);
  169.               if (ad.Justify != AttachmentPoint.BaseLeft)
  170.               {
  171.                 ar.AlignmentPoint =
  172.                   ad.AlignmentPoint.TransformBy(br.BlockTransform);
  173.               }
  174.               if (ar.IsMTextAttribute)
  175.               {
  176.                 ar.UpdateMTextAttribute();
  177.               }
  178.               ar.TextString = ad.TextString;
  179.               ObjectId arId =
  180.                 br.AttributeCollection.AppendAttribute(ar);
  181.               tr.AddNewlyCreatedDBObject(ar, true);
  182.               // Initialize our dictionary with the ObjectId of
  183.               // the attribute reference + attribute definition info
  184.               attInfo.Add(
  185.                 arId,
  186.                 new AttInfo(
  187.                   ad.Position,
  188.                   ad.AlignmentPoint,
  189.                   ad.Justify != AttachmentPoint.BaseLeft
  190.                 )
  191.               );
  192.             }
  193.           }
  194.         }
  195.         // Run the jig
  196.         BlockJig myJig = new BlockJig(tr, br, attInfo);
  197.         if (myJig.Run() != PromptStatus.OK)
  198.           return;
  199.         // Commit changes if user accepted, otherwise discard
  200.         tr.Commit();
  201.       }
  202.     }
  203.   }
  204. }
A few comments on this code:
It's been refactored to make a single pass through the block definition to both create the block reference and collect the attribute information to store in our dictionary.
The attribute information is now held in a class, which allows us to store more than just the position in our dictionary (without using multiple dictionaries), I went to the effort of exposing public properties for the various private members, as this is generally a good technique to use (if a little redundant, here).
The dictionary now stores this attribute information against an ObjectId rather than the tag string. Roland made the excellent point that blocks can contain attributes with duplicate tags, so this is much safe. We also had to use the ObjectId of the AttributeReference, as later on inside the jig's Update() function we no longer have access to the AttributeDefinition.

 楼主| 发表于 2009-5-14 13:41 | 显示全部楼层
四、拖动三维实体
March 23, 2009
Jigging an AutoCAD solid using IronPython and .NET
After getting my feet wet in the last post with my first IronPython application running inside AutoCAD, I decided it was time to attack a slightly more challenging problem: jigging a database-resident Solid3d object. The idea had come after I’d received a question by email from David Wolfe, who wanted to have a fully rendered 3D view of a cylinder he was jigging.
I’d done something similar for a prototype application I worked on late last year (which was demoed at AU). The jig itself only collected the selection data I needed – the display of the Solid3d objects was handled either via the Transient Graphics subsystem or by modifying database-resident Solid3d objects (which were interconnected by a separate system of relationships and constraints). But anyway, the point is that only when the Solid3d objects were database-resident could I get rendered graphics to be generated for them via either the conceptual or realistic visual styles.
Which is why it occurred to me that the technique shown in this recent post for jigging db-resident blocks with attributes might also apply here, too.
And, just for fun, why not do the whole thing in Python? (Actually, which hindsight I can now think of a lot of reasons… :-)
Part of my rationale behind attempting this was that we were going to have to derive our jig class from EntityJig and make sure the appropriate methods were overridden for AutoCAD to then call our jig at the right moments. This is something I had doubts about being able to do, given my previous experience. IronPython is surprisingly good at allowing these methods to be implemented and called dynamically – something that I expect will grow on me – but the downside was that with the PYLOAD integration we used in the last post it is very hard to tell why things aren’t working. It took me a number of hours to work out that an __init__ function was needed – one which took an Entity and passed it to the constructor of the base class, EntityJig – and that without it the application would simply crash. A tighter integration of Python inside AutoCAD - and, I expect, overall better tooling for IronPython when working with .NET classes - would probably help avoid this kind of thrashing, but with what we have today it was pretty painful.
Before we look at the Python code, here’s an update version of the C# PythonLoader functionality: the only real difference being the try-catch block around the code which hosts our IronPython scripting engine and calls ExecuteFile():
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.Runtime;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using IronPython.Hosting;
  6. using Microsoft.Scripting.Hosting;
  7. using System;
  8. namespace PythonLoader
  9. {
  10.   public class CommandsAndFunctions
  11.   {
  12.     [CommandMethod("-PYLOAD")]
  13.     public static void PythonLoadCmdLine()
  14.     {
  15.       PythonLoad(true);
  16.     }
  17.     [CommandMethod("PYLOAD")]
  18.     public static void PythonLoadUI()
  19.     {
  20.       PythonLoad(false);
  21.     }
  22.     public static void PythonLoad(bool useCmdLine)
  23.     {
  24.       Document doc =
  25.         Application.DocumentManager.MdiActiveDocument;
  26.       Editor ed = doc.Editor;
  27.       short fd =
  28.         (short)Application.GetSystemVariable("FILEDIA");
  29.       // As the user to select a .py file
  30.       PromptOpenFileOptions pfo =
  31.           new PromptOpenFileOptions(
  32.             "Select Python script to load"
  33.           );
  34.       pfo.Filter = "Python script (*.py)|*.py";
  35.       pfo.PreferCommandLine =
  36.         (useCmdLine || fd == 0);
  37.       PromptFileNameResult pr =
  38.         ed.GetFileNameForOpen(pfo);
  39.       // And then try to load and execute it
  40.       if (pr.Status == PromptStatus.OK)
  41.         ExecutePythonScript(pr.StringResult);
  42.     }
  43.     [LispFunction("PYLOAD")]
  44.     public ResultBuffer PythonLoadLISP(ResultBuffer rb)
  45.     {
  46.       const int RTSTR = 5005;
  47.       Document doc =
  48.         Application.DocumentManager.MdiActiveDocument;
  49.       Editor ed = doc.Editor;
  50.       if (rb == null)
  51.       {
  52.         ed.WriteMessage("\nError: too few arguments\n");
  53.       }
  54.       else
  55.       {
  56.         // We're only really interested in the first argument
  57.         Array args = rb.AsArray();
  58.         TypedValue tv = (TypedValue)args.GetValue(0);
  59.         // Which should be the filename of our script
  60.         if (tv != null && tv.TypeCode == RTSTR)
  61.         {
  62.           // If we manage to execute it, let's return the
  63.           // filename as the result of the function
  64.           // (just as (arxload) does)
  65.           bool success =
  66.             ExecutePythonScript(Convert.ToString(tv.Value));
  67.           return
  68.             (success ?
  69.               new ResultBuffer(
  70.                 new TypedValue(RTSTR, tv.Value)
  71.               )
  72.               : null);
  73.         }
  74.       }
  75.       return null;
  76.     }
  77.     private static bool ExecutePythonScript(string file)
  78.     {
  79.       // If the file exists, let's load and execute it
  80.       // (we could/should probably add some more robust
  81.       // exception handling here)
  82.       bool ret = System.IO.File.Exists(file);
  83.       if (ret)
  84.       {
  85.         try
  86.         {
  87.           ScriptEngine engine = Python.CreateEngine();
  88.           engine.ExecuteFile(file);
  89.         }
  90.         catch (System.Exception ex)
  91.         {
  92.           Document doc =
  93.             Application.DocumentManager.MdiActiveDocument;
  94.           Editor ed = doc.Editor;
  95.           ed.WriteMessage(
  96.             "\nProblem executing script: {0}", ex.Message
  97.           );
  98.         }
  99.       }
  100.       return ret;
  101.     }
  102.   }
  103. }
And here’s the IronPython code for jigging a box inside AutoCAD:
  1. import clr
  2. path = 'C:\\Program Files\\Autodesk\\AutoCAD 2009\\'
  3. clr.AddReferenceToFileAndPath(path + 'acdbmgd.dll')
  4. clr.AddReferenceToFileAndPath(path + 'acmgd.dll')
  5. clr.AddReferenceToFileAndPath(path + 'acmgdinternal.dll')
  6. import Autodesk
  7. import Autodesk.AutoCAD.Runtime as ar
  8. import Autodesk.AutoCAD.ApplicationServices as aas
  9. import Autodesk.AutoCAD.DatabaseServices as ads
  10. import Autodesk.AutoCAD.EditorInput as aei
  11. import Autodesk.AutoCAD.Geometry as ag
  12. import Autodesk.AutoCAD.Internal as ai
  13. from Autodesk.AutoCAD.Internal import Utils
  14. # Function to register AutoCAD commands
  15. # To be used via a function decorator
  16. def autocad_command(function):
  17.   # First query the function name
  18.   n = function.__name__
  19.   # Create the callback and add the command
  20.   cc = ai.CommandCallback(function)
  21.   Utils.AddCommand('pycmds', n, n, ar.CommandFlags.Modal, cc)
  22.   # Let's now write a message to the command-line
  23.   doc = aas.Application.DocumentManager.MdiActiveDocument
  24.   ed = doc.Editor
  25.   ed.WriteMessage("\nRegistered Python command: {0}", n)
  26. # A jig to create a Solid3d - in this case a box
  27. class SolidJig(aei.EntityJig):
  28.   # Initialization function
  29.   def __init__(self, ent):
  30.     # Store the object and call the base class
  31.     self._sol = ent
  32.     aei.EntityJig.__init__(self, ent)
  33.   # The function called to run the jig
  34.   def StartJig(self, ed, pt):
  35.     # The start point is specific outside the jig
  36.     self._start = pt
  37.     self._end = pt
  38.     return ed.Drag(self)
  39.   # The sampler function
  40.   def Sampler(self, prompts):
  41.     # Set up our selection options
  42.     jo = aei.JigPromptPointOptions()
  43.     jo.UserInputControls = (
  44.       aei.UserInputControls.Accept3dCoordinates |
  45.       aei.UserInputControls.NoZeroResponseAccepted |
  46.       aei.UserInputControls.NoNegativeResponseAccepted)
  47.     jo.Message = "\nSelect end point: "      
  48.     # Get the end point of our box
  49.     res = prompts.AcquirePoint(jo)
  50.     if self._end == res.Value:
  51.       return aei.SamplerStatus.NoChange
  52.     else:
  53.       self._end = res.Value
  54.     return aei.SamplerStatus.OK
  55.   # The update function
  56.   def Update(self):
  57.     # Recreate our Solid3d box
  58.     try:
  59.       # Get the width (x) and depth (y)
  60.       x = self._end.X - self._start.X
  61.       y = self._end.Y - self._start.Y
  62.       # We need a non-zero Z value, so we copy Y
  63.       z = y
  64.       # Create our box and move it to the right place
  65.       self._sol.CreateBox(x,y,z)
  66.       self._sol.TransformBy(
  67.         ag.Matrix3d.Displacement(
  68.           ag.Vector3d(
  69.             self._start.X + x/2,
  70.             self._start.Y + y/2,
  71.             self._start.Z + z/2)))
  72.     except:
  73.       return False
  74.     return True
  75. # Create a box using a jig
  76. @autocad_command
  77. def boxjig():
  78.   doc = aas.Application.DocumentManager.MdiActiveDocument
  79.   db = doc.Database
  80.   ed = doc.Editor
  81.   # Select the start point before entering the jig
  82.   ppr = ed.GetPoint("\nSelect start point: ")
  83.   if ppr.Status == aei.PromptStatus.OK:
  84.     # We'll add our solid to the modelspace
  85.     tr = doc.TransactionManager.StartTransaction()
  86.     bt = tr.GetObject(db.BlockTableId, ads.OpenMode.ForRead)
  87.     btr = tr.GetObject(
  88.       bt[ads.BlockTableRecord.ModelSpace], ads.OpenMode.ForWrite)
  89.     # Make sure we're recording history to allow grip editing
  90.     sol = ads.Solid3d()
  91.     sol.RecordHistory = True
  92.     # Now we add our solid
  93.     btr.AppendEntity(sol)
  94.     tr.AddNewlyCreatedDBObject(sol, True)
  95.     # And call the jig before finishing
  96.     sj = SolidJig(sol)  
  97.     ppr2 = sj.StartJig(ed, ppr.Value)
  98.     # Only commit if all completed well
  99.     if ppr2.Status == aei.PromptStatus.OK:
  100.       tr.Commit()
  101.     tr.Dispose()
When we execute the PYLOAD command, load our .py file and execute the BOXJIG command, we’ll be able to jig a Solid3d in a non-wireframe 3D view:

Something to note: I haven’t spent time optimizing this code, so there’s a good chance it’s sub-optimal (and may well leak memory). The point was not to demonstrate a definitive approach to solving this particular problem but rather to see whether it was possible to attack a problem of non-trivial complexity with the current toolset and my general lack of knowledge of the Python language.
Well, it’s obviously possible – as I somehow managed to do it – but, to come clean, I did cheat somewhat: I had a C# project open at the same time which I regularly referred to for IntelliSense look-ups. I didn’t do my full development in C# and then convert it to Python, though, so I see this more as a stop-gap to help address some of the current limitations in the tools. That’s the story I’m going with, anyway. :-)
发表于 2009-5-22 20:40 | 显示全部楼层

谢谢lzh741206

以后,就决定在明经学习.net了。。。

发表于 2009-5-23 11:55 | 显示全部楼层
使用Jig动态旋转实体VB.NET版
  1. #Region "命名空间"
  2. Imports Autodesk.AutoCAD.ApplicationServices
  3. Imports Autodesk.AutoCAD.DatabaseServices
  4. Imports Autodesk.AutoCAD.EditorInput
  5. Imports Autodesk.AutoCAD.Runtime
  6. Imports Autodesk.AutoCAD.Geometry
  7. #End Region
  8. Public Class commands
  9.     Const kRegAppName As String = "TTIF_ROT"
  10.     Const kAppCode As Integer = 1001
  11.     Const kRotCode As Integer = 1040
  12.     Public Class RotateJig
  13.         Inherits EntityJig
  14.         Dim m_baseAngle, m_deltaAngle As Double
  15.         Dim m_rotationPoint As Point3d
  16.         Dim m_ucs As Matrix3d
  17.         Public Sub New(ByVal ent As Entity, ByVal rotationPoint As Point3d, ByVal baseAngle As Double, ByVal ucs As Matrix3d)
  18.             MyBase.new(ent.Clone())
  19.             m_rotationPoint = rotationPoint
  20.             m_baseAngle = baseAngle
  21.             m_ucs = ucs
  22.         End Sub
  23.         Protected Overrides Function Sampler(ByVal jp As Autodesk.AutoCAD.EditorInput.JigPrompts) As Autodesk.AutoCAD.EditorInput.SamplerStatus
  24.             Dim jo As New JigPromptAngleOptions(vbNewLine + "旋转角度: ")
  25.             jo.BasePoint = m_rotationPoint
  26.             jo.UseBasePoint = True
  27.             Dim pdr As PromptDoubleResult = jp.AcquireAngle(jo)
  28.             If pdr.Status = PromptStatus.OK Then
  29.                 If m_deltaAngle = pdr.Value Then
  30.                     Return SamplerStatus.NoChange
  31.                 Else
  32.                     m_deltaAngle = pdr.Value
  33.                     Return SamplerStatus.OK
  34.                 End If
  35.             Else
  36.                 Return SamplerStatus.Cancel
  37.             End If
  38.         End Function
  39.         Protected Overrides Function Update() As Boolean
  40.             Dim trans As Matrix3d = Matrix3d.Rotation(m_deltaAngle - m_baseAngle, m_ucs.CoordinateSystem3d.Zaxis, m_rotationPoint)
  41.             Entity.TransformBy(trans)
  42.             m_baseAngle = m_deltaAngle
  43.             m_deltaAngle = 0.0
  44.             Return True
  45.         End Function
  46.         Public Function GetEntity() As Entity
  47.             Return Entity
  48.         End Function
  49.         Public Function Getrotation() As Double
  50.             Return m_baseAngle + m_deltaAngle
  51.         End Function
  52.     End Class
  53.     <CommandMethod("ROT")> _
  54.     Public Sub RotateEntity()
  55.         Dim doc As Document = Application.DocumentManager.MdiActiveDocument
  56.         Dim ed As Editor = doc.Editor
  57.         Dim db As Database = doc.Database
  58.         Dim peo As New PromptEntityOptions(vbNewLine + "选择要旋转的对象: ")
  59.         Dim per As PromptEntityResult = ed.GetEntity(peo)
  60.         If per.Status = PromptStatus.OK Then
  61.             Using tr As Transaction = db.TransactionManager.StartTransaction()
  62.                 Dim obj As DBObject = tr.GetObject(per.ObjectId, OpenMode.ForRead)
  63.                 Dim ent As Entity = obj
  64.                 Dim rotationPoint As Point3d = Point3d.Origin
  65.                 Dim pl As Polyline = obj
  66.                 If pl <> Nothing Then
  67.                     Dim ps0 As LineSegment3d = pl.GetLineSegmentAt(0)
  68.                     Dim ps1 As LineSegment3d = pl.GetLineSegmentAt(1)
  69.                     Dim vec As Vector3d = (ps0.EndPoint - ps0.StartPoint) / 2 + (ps1.EndPoint - ps1.StartPoint) / 2
  70.                     rotationPoint = pl.StartPoint + vec
  71.                     Dim baseangle As Double = GetStoredRotation(obj)
  72.                     If ent <> Nothing Then
  73.                         Dim ucs As Matrix3d = ed.CurrentUserCoordinateSystem
  74.                         Dim jig As RotateJig = New RotateJig(ent, rotationPoint, baseangle, ucs)
  75.                         Dim res As PromptResult = ed.Drag(jig)
  76.                         If res.Status = PromptStatus.OK Then
  77.                             Dim newAngle As Double = jig.Getrotation()
  78.                             jig.GetEntity.Dispose()
  79.                             Dim trans As Matrix3d = Matrix3d.Rotation(newAngle - baseangle, ucs.CoordinateSystem3d.Zaxis, rotationPoint)
  80.                             ent.UpgradeOpen()
  81.                             ent.TransformBy(trans)
  82.                             SetStoredRotation(ent, newAngle)
  83.                         End If
  84.                     End If
  85.                 End If
  86.                 tr.Commit()
  87.             End Using
  88.         End If
  89.     End Sub
  90.     Public Sub AddRegAppTableRecord(ByRef regAppName As String)
  91.         Dim doc As Document = Application.DocumentManager.MdiActiveDocument
  92.         Dim ed As Editor = doc.Editor
  93.         Dim db As Database = doc.Database
  94.         Using tr As Transaction = doc.TransactionManager.StartTransaction
  95.             Dim rat As RegAppTable = tr.GetObject(db.RegAppTableId, OpenMode.ForRead, False)
  96.             If Not rat.Has(regAppName) Then
  97.                 rat.UpgradeOpen()
  98.                 Dim ratr As New RegAppTableRecord
  99.                 ratr.Name = regAppName
  100.                 rat.Add(ratr)
  101.                 tr.AddNewlyCreatedDBObject(ratr, True)
  102.             End If
  103.             tr.Commit()
  104.         End Using
  105.     End Sub
  106.     Private Sub SetStoredRotation(ByRef obj As DBObject, ByRef rotation As Double)
  107.         AddRegAppTableRecord(kRegAppName)
  108.         Dim rb As ResultBuffer = obj.XData
  109.         If rb = Nothing Then
  110.             rb = New ResultBuffer(New TypedValue(kAppCode, kRegAppName), New TypedValue(kRotCode, rotation))
  111.         Else
  112.             rb.Add(New TypedValue(kAppCode, kRegAppName))
  113.             rb.Add(New TypedValue(kRotCode, rotation))
  114.         End If
  115.         obj.XData = rb
  116.         rb.Dispose()
  117.     End Sub
  118.     Private Function GetStoredRotation(ByRef obj As DBObject) As Double
  119.         Dim ret As Double = 0.0
  120.         Dim rb As ResultBuffer = obj.XData
  121.         If rb <> Nothing Then
  122.             Dim bReadyForRot As Boolean = False
  123.             For Each tv As TypedValue In rb
  124.                 If (bReadyForRot) Then
  125.                     If tv.TypeCode = kRotCode Then
  126.                         ret = tv.Value
  127.                         bReadyForRot = False
  128.                     End If
  129.                 End If
  130.                 If tv.TypeCode = kAppCode Then
  131.                     bReadyForRot = True
  132.                 End If
  133.             Next
  134.             rb.Dispose()
  135.             Return ret
  136.         End If
  137.     End Function
  138. End Class
 楼主| 发表于 2009-5-25 15:53 | 显示全部楼层
五、交互式创建多义线
November 08, 2006
Controlling interactive polyline creation - Part 1
I received this interesting question through by email over the weekend:
“How can I ask AutoCAD to let the user to draw a Polyline (just like the user pushed Polyline button) and then just after finishing drawing get the ObjectID of that entity? Is it possible?”
This is a fun one, as there are a number of different approaches to take. I’m going to outline (or just go ahead and implement, depending on the complexity) the various possibilities – taking the first two today and the others in (a) subsequent post(s).
The idea is to define our own command, say MYPOLY, and make sure we’re left with execution control – and the object ID/entity name – after the user has defined a polyline in the drawing window.
There are two basic ways to solve this problem, and each of these has two variants. The initial (and major) choice is whether to let the standard AutoCAD PLINE command provide the user-interface for creating the polyline. Doing so is certainly simpler, assuming you want the user to have access to all the polyline options. That said, you may actually prefer to limit the user’s options (for example, not to allow width or arc segments), in which case the approach to implement the UI yourself would be better suited.
So, now to tackle the first two options...
From the MYPOLY command, we want to call the PLINE command. Once the command has completed, we want to make sure our code is being executed, which will allow us to get the polyline's object ID.
This is where we get our next choice: how to find out when the PLINE command has ended. The first option (and the one typically used from Visual LISP for this type of task) is to loop until the command is finished, checking either CMDACTIVE or CMDNAMES. This is important, as polylines can have an arbitrary number of vertices, so we don’t know exactly how long the command will take to complete (in terms of how many “pauses” the command will have, requesting a point selection from the user).
Here’s how I’d do this in LISP (the technique is published on the ADN site in this DevNote: Waiting for (command) to finish in AutoLISP):
  1. (defun C:MYPOLY()
  2.   (command "_.PLINE")
  3.   (while (= (getvar "CMDNAMES") "PLINE")
  4.     (command pause)
  5.   )
  6.   (princ "\nEntity name of polyline: ")
  7.   (princ (entlast))
  8.   (princ)
  9. )
And here's what happens when we execute this code:
  1. Command: mypoly
  2. _.PLINE
  3. Specify start point:
  4. Current line-width is 0.0000
  5. Specify next point or [Arc/Halfwidth/Length/Undo/Width]:
  6. Specify next point or [Arc/Close/Halfwidth/Length/Undo/Width]:
  7. Specify next point or [Arc/Close/Halfwidth/Length/Undo/Width]: a
  8. Specify endpoint of arc or
  9. [Angle/CEnter/CLose/Direction/Halfwidth/Line/Radius/Second pt/Undo/Width]:
  10. Specify endpoint of arc or
  11. [Angle/CEnter/CLose/Direction/Halfwidth/Line/Radius/Second pt/Undo/Width]:
  12. Command:
  13. Entity name of polyline: <Entity name: 7ef90048>
复制代码
The second option to wait for the command to complete is to register a callback handling the CommandEnded() event.
Here’s some C# code showing this approach:
  1. using Autodesk.AutoCAD.EditorInput;
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.Runtime;
  5. namespace MyPlineApp
  6. {
  7.   public class MyPlineCmds : IExtensionApplication
  8.   {
  9.     // Flag used to check whether it's our command
  10.     // that launched PLINE
  11.     private static bool myCommandStarted = false;
  12.     public void Initialize()
  13.     {
  14.       Document doc =
  15.         Application.DocumentManager.MdiActiveDocument;
  16.       doc.CommandEnded += new
  17.         CommandEventHandler(
  18.           plineCommandEnded
  19.         );
  20.     }
  21.     public void Terminate()
  22.     {
  23.       Document doc =
  24.         Application.DocumentManager.MdiActiveDocument;
  25.       doc.CommandEnded -= new
  26.         CommandEventHandler(
  27.           plineCommandEnded
  28.         );
  29.     }
  30.     [CommandMethod("MYPOLY")]
  31.     public void MyPoly()
  32.     {
  33.       // Set the flag and launch PLINE
  34.       myCommandStarted = true;
  35.       Document doc =
  36.         Application.DocumentManager.MdiActiveDocument;
  37.       doc.SendStringToExecute("_PLINE ",false,false,false);
  38.     }
  39.     private void plineCommandEnded(
  40.       object sender,
  41.       CommandEventArgs e)
  42.     {
  43.       if (myCommandStarted
  44.         && e.GlobalCommandName.ToUpper() == "PLINE")
  45.       {
  46.         // We're just performing a simple check, so OK..
  47.         // We could launch a follow-on command, if needed
  48.         Document doc =
  49.           Application.DocumentManager.MdiActiveDocument;
  50.         PromptSelectionResult lastRes =
  51.           doc.Editor.SelectLast();
  52.         if (lastRes.Value != null
  53.           && lastRes.Value.Count == 1)
  54.         {
  55.           doc.Editor.WriteMessage(
  56.             "\nPolyline entity is: "
  57.             + lastRes.Value[0].ObjectId
  58.           );
  59.         }
  60.         myCommandStarted = false;
  61.       }
  62.     }
  63.   }
  64. }
And here's what happens when we execute this code:
  1. Command: mypoly
  2. Command:
  3. Specify start point:
  4. Current line-width is 0.0000
  5. Specify next point or [Arc/Halfwidth/Length/Undo/Width]:
  6. Specify next point or [Arc/Close/Halfwidth/Length/Undo/Width]:
  7. Specify next point or [Arc/Close/Halfwidth/Length/Undo/Width]: a
  8. Specify endpoint of arc or
  9. [Angle/CEnter/CLose/Direction/Halfwidth/Line/Radius/Second pt/Undo/Width]:
  10. Polyline entity is: (2130247840)
复制代码
That's where I'll stop it there for now… the other two options I want to look at both revolve around defining your own user-interface. The first will simply collect a sequence of points from the user using GetPoint(), the second uses a Jig to do the same thing (I haven’t yet decided whether to actually implement this last one or not – we’ll see how much time I have later in the week).

 楼主| 发表于 2009-5-25 15:55 | 显示全部楼层
November 10, 2006
Controlling interactive polyline creation - Part 2
During the first part of this series, we looked at ways to drive the PLINE command while retaining (or regaining) the thread of execution in your application.
During this and the next post (yes, I've decided to spread the series a little thinner :-) we're going to look at how to completely replace the user-interface to the polyline command, a very useful technique in certain situations. This post focuses on the simple use of GetPoint() to request vertex information from the user; the next post will look at a more advanced technique, the Jig.
Even the "simple" user-interface implemented in the below code takes some effort. To keep things as simple as possible, the below UI code only allows the user to define zero-width, linear polyline segments - no arcs, widths, etc. As mentioned in the previous post, this might well be an advantage in your application, depending on whether you want to hide certain options from the user. This approach is certainly not ideal if you do want to allow interactive selection of arc segments; the two approaches suggested last time, or the one shown in the next entry, would work better in that case.
A few notes on the implementation:
Temporary graphics are used to draw each polyline segment as soon as both its vertices have been defined
The actual polyline entity is only created once all the vertices have been selected
Point selection happens in the User Coordinate System, so we need to do some work to transform selected points to the Entity Coordinate System (or Object Coordinate System) belonging to the polyline. 2-dimensional polylines are planar entities and have their vertices defined as 2D points relative to the origin and normal of the polyline, so we use a "lane" object to help us get the 2D points to feed to the polyline's AddVertexAt() function
Here's the code in C#:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.Colors;
  7. namespace MyPlineApp
  8. {
  9.   public class MyPlineCmds
  10.   {
  11.     [CommandMethod("MYPOLY")]
  12.     public void MyPoly()
  13.     {
  14.       Document doc =
  15.         Application.DocumentManager.MdiActiveDocument;
  16.       Database db = doc.Database;
  17.       Editor ed = doc.Editor;
  18.       // Get the current color, for our temp graphics
  19.       Color col = doc.Database.Cecolor;
  20.       // Create a point collection to store our vertices
  21.       Point3dCollection pts = new Point3dCollection();
  22.       // Set up the selection options
  23.       // (used for all vertices)
  24.       PromptPointOptions opt =
  25.         new PromptPointOptions(
  26.           "\nSelect polyline vertex: "
  27.         );
  28.       opt.AllowNone = true;
  29.       // Get the start point for the polyline
  30.       PromptPointResult res = ed.GetPoint(opt);
  31.       while (res.Status == PromptStatus.OK)
  32.       {
  33.         // Add the selected point to the list
  34.         pts.Add(res.Value);
  35.         // Drag a temp line during selection
  36.         // of subsequent points
  37.         opt.UseBasePoint = true;
  38.         opt.BasePoint = res.Value;
  39.         res = ed.GetPoint(opt);
  40.         if (res.Status == PromptStatus.OK)
  41.         {
  42.           // For each point selected,
  43.           // draw a temporary segment
  44.           ed.DrawVector(
  45.             pts[pts.Count - 1], // start point
  46.             res.Value,          // end point
  47.             col.ColorIndex,     // current color
  48.             false);             // highlighted?
  49.         }
  50.       }
  51.       if (res.Status == PromptStatus.None)
  52.       {
  53.         // Get the current UCS
  54.         Matrix3d ucs =
  55.           ed.CurrentUserCoordinateSystem;
  56.         Point3d origin = new Point3d(0, 0, 0);
  57.         Vector3d normal = new Vector3d(0, 0, 1);
  58.         normal = normal.TransformBy(ucs);
  59.         // Create a temporary plane, to help with calcs
  60.         Plane plane = new Plane(origin, normal);
  61.         // Create the polyline, specifying
  62.         // the number of vertices up front
  63.         Polyline pline = new Polyline(pts.Count);
  64.         pline.Normal = normal;
  65.         foreach (Point3d pt in pts)
  66.         {
  67.           Point3d transformedPt =
  68.             pt.TransformBy(ucs);
  69.           pline.AddVertexAt(
  70.             pline.NumberOfVertices,
  71.             plane.ParameterOf(transformedPt),
  72.             0, 0, 0
  73.           );
  74.         }
  75.         // Now let's add the polyline to the modelspace
  76.         Transaction tr =
  77.           db.TransactionManager.StartTransaction();
  78.         using (tr)
  79.         {
  80.           BlockTable bt =
  81.             (BlockTable)tr.GetObject(
  82.               db.BlockTableId,
  83.               OpenMode.ForRead
  84.             );
  85.           BlockTableRecord btr =
  86.             (BlockTableRecord)tr.GetObject(
  87.               bt[BlockTableRecord.ModelSpace],
  88.               OpenMode.ForWrite
  89.             );
  90.           ObjectId plineId = btr.AppendEntity(pline);
  91.           tr.AddNewlyCreatedDBObject(pline, true);
  92.           tr.Commit();
  93.           ed.WriteMessage("\nPolyline entity is: " +
  94.             plineId.ToString()
  95.           );
  96.         }
  97.       }
  98.       // Clear the temp graphics (polyline should be in
  99.       // the same location, if selection was not cancelled)
  100.       // We could "redraw" instead of "regen" here
  101.       ed.Regen();
  102.     }
  103.   }
  104. }
Here's what happens when we execute this code:
  1. Command: mypoly
  2. Select polyline vertex:
  3. Select polyline vertex:
  4. Select polyline vertex:
  5. Regenerating model.
  6. Polyline entity is: (2130239560)
复制代码
Next time we'll look at how we can use a Jig for this task.

 楼主| 发表于 2009-5-25 15:57 | 显示全部楼层
November 14, 2006
Controlling interactive polyline creation - Part 3
During the first two parts of this series we looked at different techniques for creating polylines programmatically in AutoCAD (while allowing the user to select the various vertices).
In this post look at the most advanced technique yet, the use of a "Jig". A Jig is a special construct in AutoCAD that hosts an object - in our case a polyline - and feeds user input information into this object, which then determines what graphics gets displayed. This is especially useful when dealing with complex objects, such as polylines with arc segments.
We're going to start with a fairly basic Jig, one that simply collects vertices and creates straight-line segments, but I'd quite like to take this further to support a number of different advanced capabilities: arc segments (which will require keyword implementation), dynamic dimensions, and possibly even undo (a request that just came in as a comment to the previous post).
Anyway, here's the C# code of the basic Jig implementation:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. using Autodesk.AutoCAD.Geometry;
  6. namespace MyPlineApp
  7. {
  8.   public class MyPlineCmds
  9.   {
  10.     class PlineJig : EntityJig
  11.     {
  12.       // Maintain a list of vertices...
  13.       // Not strictly necessary, as these will be stored in the
  14.       // polyline, but will not adversely impact performance
  15.       Point3dCollection m_pts;
  16.       // Use a separate variable for the most recent point...
  17.       // Again, not strictly necessary, but easier to reference
  18.       Point3d m_tempPoint;
  19.       Plane m_plane;
  20.       public PlineJig(Matrix3d ucs)
  21.         : base(new Polyline())
  22.       {
  23.         // Create a point collection to store our vertices
  24.         m_pts = new Point3dCollection();
  25.         // Create a temporary plane, to help with calcs
  26.         Point3d origin = new Point3d(0, 0, 0);
  27.         Vector3d normal = new Vector3d(0, 0, 1);
  28.         normal = normal.TransformBy(ucs);
  29.         m_plane = new Plane(origin, normal);
  30.         // Create polyline, set defaults, add dummy vertex
  31.         Polyline pline = Entity as Polyline;
  32.         pline.SetDatabaseDefaults();
  33.         pline.Normal = normal;
  34.         pline.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0);
  35.       }
  36.       protected override SamplerStatus Sampler(JigPrompts prompts)
  37.       {
  38.         JigPromptPointOptions jigOpts =
  39.           new JigPromptPointOptions();
  40.         jigOpts.UserInputControls =
  41.           (UserInputControls.Accept3dCoordinates |
  42.           UserInputControls.NullResponseAccepted |
  43.           UserInputControls.NoNegativeResponseAccepted
  44.           );
  45.         if (m_pts.Count == 0)
  46.         {
  47.           // For the first vertex, just ask for the point
  48.           jigOpts.Message =
  49.             "\nStart point of polyline: ";
  50.         }
  51.         else if (m_pts.Count > 0)
  52.         {
  53.           // For subsequent vertices, use a base point
  54.           jigOpts.BasePoint = m_pts[m_pts.Count - 1];
  55.           jigOpts.UseBasePoint = true;
  56.           jigOpts.Message = "\nPolyline vertex: ";
  57.         }
  58.         else // should never happen
  59.           return SamplerStatus.Cancel;
  60.         // Get the point itself
  61.         PromptPointResult res =
  62.           prompts.AcquirePoint(jigOpts);
  63.         // Check if it has changed or not
  64.         // (reduces flicker)
  65.         if (m_tempPoint == res.Value)
  66.         {
  67.           return SamplerStatus.NoChange;
  68.         }
  69.         else if (res.Status == PromptStatus.OK)
  70.         {
  71.           m_tempPoint = res.Value;
  72.           return SamplerStatus.OK;
  73.         }
  74.         return SamplerStatus.Cancel;
  75.       }
  76.       protected override bool Update()
  77.       {
  78.         // Update the dummy vertex to be our
  79.         // 3D point projected onto our plane
  80.         Polyline pline = Entity as Polyline;
  81.         pline.SetPointAt(
  82.           pline.NumberOfVertices - 1,
  83.           m_tempPoint.Convert2d(m_plane)
  84.         );
  85.         return true;
  86.       }
  87.       public Entity GetEntity()
  88.       {
  89.         return Entity;
  90.       }
  91.       public void AddLatestVertex()
  92.       {
  93.         // Add the latest selected point to
  94.         // our internal list...
  95.         // This point will already be in the
  96.         // most recently added pline vertex
  97.         m_pts.Add(m_tempPoint);
  98.         Polyline pline = Entity as Polyline;
  99.         // Create a new dummy vertex...
  100.         // can have any initial value
  101.         pline.AddVertexAt(
  102.           pline.NumberOfVertices,
  103.           new Point2d(0,0),
  104.           0,0,0
  105.         );
  106.       }
  107.       public void RemoveLastVertex()
  108.       {
  109.         // Let's remove our dummy vertex
  110.         Polyline pline = Entity as Polyline;
  111.         pline.RemoveVertexAt(m_pts.Count);
  112.       }
  113.     }
  114.     [CommandMethod("MYPOLY")]
  115.     public void MyPolyJig()
  116.     {
  117.       Document doc =
  118.         Application.DocumentManager.MdiActiveDocument;
  119.       Editor ed = doc.Editor;
  120.       // Get the current UCS, to pass to the Jig
  121.       Matrix3d ucs =
  122.         ed.CurrentUserCoordinateSystem;
  123.       // Create our Jig object
  124.       PlineJig jig = new PlineJig(ucs);
  125.       // Loop to set the vertices directly on the polyline
  126.       bool bSuccess = true, bComplete = false;
  127.       do
  128.       {
  129.         PromptResult res = ed.Drag(jig);
  130.         bSuccess =
  131.           (res.Status == PromptStatus.OK);
  132.         // A new point was added
  133.         if (bSuccess)
  134.           jig.AddLatestVertex();
  135.         // Null input terminates the command
  136.         bComplete =
  137.           (res.Status == PromptStatus.None);
  138.         if (bComplete)
  139.           // Let's clean-up the polyline before adding it
  140.           jig.RemoveLastVertex();
  141.       } while (bSuccess && !bComplete);
  142.       // If the jig completed successfully, add the polyline
  143.       if (bComplete)
  144.       {
  145.         // Append entity
  146.         Database db = doc.Database;
  147.         Transaction tr =
  148.           db.TransactionManager.StartTransaction();
  149.         using (tr)
  150.         {
  151.           BlockTable bt =
  152.             (BlockTable)tr.GetObject(
  153.               db.BlockTableId,
  154.               OpenMode.ForRead,
  155.               false
  156.             );
  157.           BlockTableRecord btr =
  158.             (BlockTableRecord)tr.GetObject(
  159.               bt[BlockTableRecord.ModelSpace],
  160.               OpenMode.ForWrite,
  161.               false
  162.             );
  163.           btr.AppendEntity(jig.GetEntity());
  164.           tr.AddNewlyCreatedDBObject(jig.GetEntity(), true);
  165.           tr.Commit();
  166.         }
  167.       }
  168.     }
  169.   }
  170. }
That's it for the basic Jig implementation. For further information on Jigs, I suggest taking a look at the "EllipseJig" sample on the ObjectARX SDK (under samples/dotNet) and the ObjectARX Developer's Guide (the C++ documentation is much more extensive for now). I'll also take a closer look in future posts, so please keep your questions coming.

 楼主| 发表于 2009-5-25 16:03 | 显示全部楼层
六、关键字设置
November 17, 2006
Advanced jigging with AutoCAD .NET - adding keywords
This post extends the polyline-creation jig shown in the previous entry to support the use of keywords both for arc segments and for undo.
A few notes:
I removed the use of a separate vertex list, as it proved to be less necessary than needed
This implementation supports Undo, and the toggling between Line segment and Arc segment entry
Arc segments have a fixed bulge of 1.0, which is actually quite useful if drawing a cloud, but not really useful for much else. Generally the bulge should be adjusted according to the position of the cursor relative to the previous point, which may be something I attempt in a future post
I've also streamlined some of the other parts of code (such as using the basepoint in the jig - which we don't actually need, as we allow the polyline to draw itself)
The code has become quite a bit more complex, and could probably do with some additional performance tuning, but it should allow you to get the idea of what can be done (and one way of approaching it). I should also say that - and this holds true for any of the code samples posted in this blog - it should be thoroughly tested before being integrated into your own application. Hopefully that goes without saying...
Here's the C# code:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. using Autodesk.AutoCAD.Geometry;
  6. namespace MyPlineApp
  7. {
  8.   public class MyPlineCmds
  9.   {
  10.     class PlineJig : EntityJig
  11.     {
  12.       // Use a separate variable for the most recent point...
  13.       // Again, not strictly necessary, but easier to reference
  14.       Point3d m_tempPoint;
  15.       Plane m_plane;
  16.       bool m_isArcSeg = false;
  17.       bool m_isUndoing = false;
  18.       // At this stage, weour arc segments will
  19.       // have a fixed bulge of 1.0...
  20.       // Later we may update the routine to determine
  21.       // the bulge based on the relative location
  22.       // of the cursor
  23.       const double kBulge = 1.0;
  24.       public PlineJig(Matrix3d ucs)
  25.         : base(new Polyline())
  26.       {
  27.         // Create a temporary plane, to help with calcs
  28.         Point3d origin = new Point3d(0, 0, 0);
  29.         Vector3d normal = new Vector3d(0, 0, 1);
  30.         normal = normal.TransformBy(ucs);
  31.         m_plane = new Plane(origin, normal);
  32.         // Create polyline, set defaults, add dummy vertex
  33.         Polyline pline = Entity as Polyline;
  34.         pline.SetDatabaseDefaults();
  35.         pline.Normal = normal;
  36.         AddDummyVertex();
  37.       }
  38.       protected override SamplerStatus Sampler(JigPrompts prompts)
  39.       {
  40.         JigPromptPointOptions jigOpts =
  41.           new JigPromptPointOptions();
  42.         jigOpts.UserInputControls =
  43.           (UserInputControls.Accept3dCoordinates |
  44.           UserInputControls.NullResponseAccepted |
  45.           UserInputControls.NoNegativeResponseAccepted
  46.           );
  47.         m_isUndoing = false;
  48.         Polyline pline = Entity as Polyline;
  49.         if (pline.NumberOfVertices == 1)
  50.         {
  51.           // For the first vertex, just ask for the point
  52.           jigOpts.Message =
  53.             "\nStart point of polyline: ";
  54.         }
  55.         else if (pline.NumberOfVertices > 1)
  56.         {
  57.           // For subsequent vertices, use a base point
  58.           if (m_isArcSeg)
  59.           {
  60.             jigOpts.SetMessageAndKeywords(
  61.               "\nSpecify endpoint of arc or [Line/Undo]: ",
  62.               "Line Undo"
  63.             );
  64.           }
  65.           else
  66.           {
  67.             jigOpts.SetMessageAndKeywords(
  68.               "\nSpecify next point or [Arc/Undo]: ",
  69.               "Arc Undo"
  70.             );
  71.           }
  72.         }
  73.         else // should never happen
  74.           return SamplerStatus.Cancel;
  75.         // Get the point itself
  76.         PromptPointResult res =
  77.           prompts.AcquirePoint(jigOpts);
  78.         if (res.Status == PromptStatus.Keyword)
  79.         {
  80.           if (res.StringResult == "Arc")
  81.           {
  82.             m_isArcSeg = true;
  83.           }
  84.           else if (res.StringResult == "Line")
  85.           {
  86.             m_isArcSeg = false;
  87.           }
  88.           else if (res.StringResult == "Undo")
  89.           {
  90.             m_isUndoing = true;
  91.           }
  92.           return SamplerStatus.OK;
  93.         }
  94.         else if (res.Status == PromptStatus.OK)
  95.         {
  96.           // Check if it has changed or not
  97.           // (reduces flicker)
  98.           if (m_tempPoint == res.Value)
  99.           {
  100.             return SamplerStatus.NoChange;
  101.           }
  102.           else
  103.           {
  104.             m_tempPoint = res.Value;
  105.             return SamplerStatus.OK;
  106.           }
  107.         }
  108.         return SamplerStatus.Cancel;
  109.       }
  110.       protected override bool Update()
  111.       {
  112.         // Update the dummy vertex to be our
  113.         // 3D point projected onto our plane
  114.         Polyline pline = Entity as Polyline;
  115.         pline.SetPointAt(
  116.           pline.NumberOfVertices - 1,
  117.           m_tempPoint.Convert2d(m_plane)
  118.         );
  119.         // If it's supposed to be an arc segment,
  120.         // set the bulge appropriately
  121.         if (m_isArcSeg)
  122.         {
  123.           pline.SetBulgeAt(
  124.             pline.NumberOfVertices-1,
  125.             kBulge
  126.           );
  127.         }
  128.         // Otherwise, it's a line, so set the bulge
  129.         // to zero
  130.         else
  131.         {
  132.           pline.SetBulgeAt(
  133.             pline.NumberOfVertices-1,
  134.             0
  135.           );
  136.         }
  137.         return true;
  138.       }
  139.       public Entity GetEntity()
  140.       {
  141.         return Entity;
  142.       }
  143.       public bool IsArcSegment()
  144.       {
  145.         return m_isArcSeg;
  146.       }
  147.       public bool IsUndoing()
  148.       {
  149.         return m_isUndoing;
  150.       }
  151.       public void AddDummyVertex()
  152.       {
  153.         // Create a new dummy vertex...
  154.         // can have any initial value
  155.         Polyline pline = Entity as Polyline;
  156.         pline.AddVertexAt(
  157.           pline.NumberOfVertices,
  158.           new Point2d(0,0),
  159.           0,0,0
  160.         );
  161.       }
  162.       public void RemoveDummyVertex()
  163.       {
  164.         Polyline pline = Entity as Polyline;
  165.         // Let's first remove our dummy vertex        
  166.         if (pline.NumberOfVertices > 0)
  167.         {
  168.           pline.RemoveVertexAt(pline.NumberOfVertices - 1);
  169.         }
  170.         // And then check the type of the last segment
  171.         if (pline.NumberOfVertices >= 2)
  172.         {
  173.           double blg =
  174.             pline.GetBulgeAt(pline.NumberOfVertices - 2);
  175.           m_isArcSeg = (blg != 0);
  176.         }
  177.       }
  178.       public void AdjustSegmentType(bool isArc)
  179.       {
  180.         // Change the bulge of the previous segment,
  181.         // if necessary
  182.         double bulge = 0.0;
  183.         if (isArc)
  184.           bulge = kBulge;
  185.         Polyline pline = Entity as Polyline;
  186.         if (pline.NumberOfVertices >= 2)
  187.           pline.SetBulgeAt(pline.NumberOfVertices - 2, bulge);
  188.       }
  189.     }
  190.     [CommandMethod("MYPOLY")]
  191.     public void MyPolyJig()
  192.     {
  193.       Document doc =
  194.         Application.DocumentManager.MdiActiveDocument;
  195.       Editor ed = doc.Editor;
  196.       // Get the current UCS, to pass to the Jig
  197.       Matrix3d ucs =
  198.         ed.CurrentUserCoordinateSystem;
  199.       // Create our Jig object
  200.       PlineJig jig = new PlineJig(ucs);
  201.       // Loop to set the vertices directly on the polyline
  202.       bool bPoint = false;
  203.       bool bKeyword = false;
  204.       bool bComplete = false;
  205.       do
  206.       {
  207.         PromptResult res = ed.Drag(jig);
  208.         bPoint =
  209.           (res.Status == PromptStatus.OK);
  210.         // A new point was added
  211.         if (bPoint)
  212.           jig.AddDummyVertex();
  213.         bKeyword =
  214.           (res.Status == PromptStatus.Keyword);
  215.         if (bKeyword)
  216.         {
  217.           if (jig.IsUndoing())
  218.           {
  219.             jig.RemoveDummyVertex();
  220.           }
  221.           else
  222.           {
  223.             jig.AdjustSegmentType(jig.IsArcSegment());
  224.           }
  225.         }
  226.         // Null input terminates the command
  227.         bComplete =
  228.           (res.Status == PromptStatus.None);
  229.         if (bComplete)
  230.           // Let's clean-up the polyline before adding it
  231.           jig.RemoveDummyVertex();
  232.       } while ((bPoint || bKeyword) && !bComplete);
  233.       // If the jig completed successfully,
  234.       // add the polyline
  235.       if (bComplete)
  236.       {
  237.         Polyline pline = jig.GetEntity() as Polyline;
  238.         if (pline.NumberOfVertices > 1)
  239.         {
  240.           // Append entity
  241.           Database db = doc.Database;
  242.           Transaction tr =
  243.             db.TransactionManager.StartTransaction();
  244.           using (tr)
  245.           {
  246.             BlockTable bt =
  247.               (BlockTable)tr.GetObject(
  248.                 db.BlockTableId,
  249.                 OpenMode.ForRead,
  250.                 false
  251.               );
  252.             BlockTableRecord btr =
  253.               (BlockTableRecord)tr.GetObject(
  254.                 bt[BlockTableRecord.ModelSpace],
  255.                 OpenMode.ForWrite,
  256.                 false
  257.               );
  258.             btr.AppendEntity(pline);
  259.             tr.AddNewlyCreatedDBObject(pline, true);
  260.             tr.Commit();
  261.           }
  262.         }
  263.       }
  264.     }
  265.   }
  266. }

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

本版积分规则

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

GMT+8, 2024-4-25 01:45 , Processed in 0.214486 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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