找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 2098|回复: 1

[分享] Jigging an AutoCAD hatch using .NET (redux)

[复制链接]

已领礼包: 859个

财富等级: 财运亨通

发表于 2014-7-30 22:08:01 来自手机 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

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

×
本帖最后由 csharp 于 2014-7-31 07:54 编辑

http://through-the-interface.typepad.com/through_the_interface/2014/04/jigging-an-autocad-hatch-using-net-redux.html

Many thanks to Holger Rasch who worked out how to fix the code in this previous post. It really doesn’t matter that 3 years have passed, Holger – I have no doubt people will greatly appreciate the fact the code can now run without causing the annoying display issues it produced previously.

Holger made a few adjustments to the implementation to make sure the persistent hatch loop gets added and removed in the right places. Here’s the updated 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 Autodesk.AutoCAD.Colors;

  7. using System;



  8. namespace HatchJig

  9. {

  10.   class JigUtils

  11.   {

  12.     // Custom ArcTangent method, as the Math.Atan

  13.     // doesn't handle specific cases



  14.     public static double Atan(double y, double x)

  15.     {

  16.       if (x > 0)

  17.         return Math.Atan(y / x);

  18.       else if (x < 0)

  19.         return Math.Atan(y / x) - Math.PI;

  20.       else  // x == 0

  21.       {

  22.         if (y > 0)

  23.           return Math.PI;

  24.         else if (y < 0)

  25.           return -Math.PI;

  26.         else // if (y == 0) theta is undefined

  27.           return 0.0;

  28.       }

  29.     }



  30.     // Computes Angle between current direction

  31.     // (vector from last vertex to current vertex)

  32.     // and the last pline segment



  33.     public static double ComputeAngle(

  34.       Point3d startPoint, Point3d endPoint,

  35.       Vector3d xdir, Matrix3d ucs

  36.     )

  37.     {

  38.       var v =

  39.         new Vector3d(

  40.           (endPoint.X - startPoint.X) / 2,

  41.           (endPoint.Y - startPoint.Y) / 2,

  42.           (endPoint.Z - startPoint.Z) / 2

  43.         );



  44.       double cos = v.DotProduct(xdir);

  45.       double sin =

  46.         v.DotProduct(

  47.           Vector3d.ZAxis.TransformBy(ucs).CrossProduct(xdir)

  48.         );



  49.       return Atan(sin, cos);

  50.     }

  51.   }



  52.   public class HatchJig : DrawJig

  53.   {

  54.     Point3d _tempPoint;

  55.     bool _isArcSeg = false;

  56.     bool _isUndoing = false;

  57.     Matrix3d _ucs;

  58.     Plane _plane;

  59.     Polyline _pline = null;

  60.     Hatch _hat = null;



  61.     public HatchJig(

  62.       Matrix3d ucs, Plane plane, Polyline pl, Hatch hat

  63.     )

  64.     {

  65.       _ucs = ucs;

  66.       _plane = plane;

  67.       _pline = pl;

  68.       _hat = hat;

  69.       AddDummyVertex();

  70.     }



  71.     protected override bool WorldDraw(

  72.       Autodesk.AutoCAD.GraphicsInterface.WorldDraw wd

  73.     )

  74.     {

  75.       // Update the dummy vertex to be our 3D point

  76.       // projected onto our plane



  77.       if (_isArcSeg)

  78.       {

  79.         var lastVertex =

  80.           _pline.GetPoint3dAt(_pline.NumberOfVertices - 2);



  81.         Vector3d refDir;



  82.         if (_pline.NumberOfVertices < 3)

  83.           refDir = new Vector3d(1.0, 1.0, 0.0);

  84.         else

  85.         {

  86.           // Check bulge to see if last segment was an arc or a line



  87.           if (_pline.GetBulgeAt(_pline.NumberOfVertices - 3) != 0)

  88.           {

  89.             var arcSegment =

  90.               _pline.GetArcSegmentAt(_pline.NumberOfVertices - 3);



  91.             var tangent = arcSegment.GetTangent(lastVertex);



  92.             // Reference direction is the invert of the arc tangent

  93.             // at last vertex



  94.             refDir = tangent.Direction.MultiplyBy(-1.0);

  95.           }

  96.           else

  97.           {

  98.             var pt =

  99.               _pline.GetPoint3dAt(_pline.NumberOfVertices - 3);



  100.             refDir =

  101.               new Vector3d(

  102.                 lastVertex.X - pt.X,

  103.                 lastVertex.Y - pt.Y,

  104.                 lastVertex.Z - pt.Z

  105.               );

  106.           }

  107.         }



  108.         double angle =

  109.           JigUtils.ComputeAngle(

  110.             lastVertex, _tempPoint, refDir, _ucs

  111.           );



  112.         // Bulge is defined as tan of one fourth of included angle

  113.         // Need to double the angle since it represents the included

  114.         // angle of the arc

  115.         // So formula is: bulge = Tan(angle * 2 * 0.25)



  116.         double bulge = Math.Tan(angle * 0.5);



  117.         _pline.SetBulgeAt(_pline.NumberOfVertices - 2, bulge);

  118.       }

  119.       else

  120.       {

  121.         // Line mode. Need to remove last bulge if there was one



  122.         if (_pline.NumberOfVertices > 1)

  123.           _pline.SetBulgeAt(_pline.NumberOfVertices - 2, 0);

  124.       }



  125.       _pline.SetPointAt(

  126.         _pline.NumberOfVertices - 1, _tempPoint.Convert2d(_plane)

  127.       );



  128.       // Only when we have enough vertices



  129.       if (_pline.NumberOfVertices >2)

  130.       {

  131.         _pline.Closed = true;



  132.         AppendTheLoop();

  133.       }



  134.       if (!wd.RegenAbort)

  135.       {

  136.         wd.Geometry.Draw(_pline);



  137.         if (_pline.NumberOfVertices > 2)

  138.         {

  139.           _hat.EvaluateHatch(true);

  140.           if (!wd.RegenAbort)

  141.             wd.Geometry.Draw(_hat);



  142.           // Take it out, we will create a new one later



  143.           _hat.RemoveLoopAt(0);

  144.         }

  145.       }



  146.       return true;

  147.     }



  148.     protected override SamplerStatus Sampler(JigPrompts prompts)

  149.     {

  150.       var jigOpts = new JigPromptPointOptions();



  151.       jigOpts.UserInputControls =

  152.         (UserInputControls.NullResponseAccepted |

  153.          UserInputControls.NoNegativeResponseAccepted |

  154.          UserInputControls.GovernedByOrthoMode);



  155.       _isUndoing = false;



  156.       if (_pline.NumberOfVertices == 1)

  157.       {

  158.         // For the first vertex, just ask for the point



  159.         jigOpts.Message = "\nSpecify start point: ";

  160.       }

  161.       else if (_pline.NumberOfVertices > 1)

  162.       {

  163.         string msgAndKwds =

  164.           (_isArcSeg ?

  165.             "\nSpecify endpoint of arc or [Line/Undo]: " :

  166.             "\nSpecify next point or [Arc/Undo]: "

  167.           );



  168.         string kwds = (_isArcSeg ? "Line Undo" : "Arc Undo");



  169.         jigOpts.SetMessageAndKeywords(msgAndKwds, kwds);

  170.       }

  171.       else

  172.         return SamplerStatus.Cancel; // Should never happen



  173.       // Get the point itself



  174.       var res = prompts.AcquirePoint(jigOpts);



  175.       if (res.Status == PromptStatus.Keyword)

  176.       {

  177.         if (res.StringResult.ToUpper() == "ARC")

  178.           _isArcSeg = true;

  179.         else if (res.StringResult.ToUpper() == "LINE")

  180.           _isArcSeg = false;

  181.         else if (res.StringResult.ToUpper() == "UNDO")

  182.           _isUndoing = true;



  183.         return SamplerStatus.OK;

  184.       }

  185.       else if (res.Status == PromptStatus.OK)

  186.       {

  187.         // Check if it has changed or not (reduces flicker)



  188.         if (_tempPoint == res.Value)

  189.           return SamplerStatus.NoChange;

  190.         else

  191.         {

  192.           _tempPoint = res.Value;

  193.           return SamplerStatus.OK;

  194.         }

  195.       }



  196.       return SamplerStatus.Cancel;

  197.     }



  198.     public bool IsUndoing

  199.     {

  200.       get

  201.       {

  202.         return _isUndoing;

  203.       }

  204.     }



  205.     public void AddDummyVertex()

  206.     {

  207.       // Create a new dummy vertex... can have any initial value



  208.       _pline.AddVertexAt(

  209.         _pline.NumberOfVertices, new Point2d(0, 0), 0, 0, 0

  210.       );

  211.     }



  212.     public bool RemoveLastVertex()

  213.     {

  214.       // If there's a single vertex, don't attempt to remove the

  215.       // vertex: would cause a degenerate geometry error

  216.       // Returning false will tell the calling function to

  217.       // abort the transaction



  218.       if (_pline.NumberOfVertices == 1)

  219.         return false;



  220.       // Let's remove our dummy vertex   



  221.       if (_pline.NumberOfVertices > 0)

  222.         _pline.RemoveVertexAt(_pline.NumberOfVertices - 1);



  223.       // And then check the type of the last segment



  224.       if (_pline.NumberOfVertices >= 2)

  225.       {

  226.         double blg = _pline.GetBulgeAt(_pline.NumberOfVertices - 2);

  227.         _isArcSeg = (blg != 0);



  228.         // We have to sync the hatch with the polyline



  229.         AppendTheLoop();

  230.       }



  231.       return true;

  232.     }



  233.     private void AppendTheLoop()

  234.     {

  235.       var ids = new ObjectIdCollection();

  236.       ids.Add(_pline.ObjectId);

  237.       _hat.Associative = true;

  238.       _hat.AppendLoop(HatchLoopTypes.Default, ids);

  239.     }



  240.     [CommandMethod("HATJIG")]

  241.     public static void RunHatchJig()

  242.     {

  243.       var doc = Application.DocumentManager.MdiActiveDocument;

  244.       var db = doc.Database;

  245.       var ed = doc.Editor;



  246.       // Create a transaction, as we're jigging

  247.       // db-resident objects



  248.       using (var tr = db.TransactionManager.StartTransaction())

  249.       {

  250.         var btr =

  251.           (BlockTableRecord)tr.GetObject(

  252.             db.CurrentSpaceId, OpenMode.ForWrite

  253.           );



  254.         var normal =

  255.           Vector3d.ZAxis.TransformBy(

  256.             ed.CurrentUserCoordinateSystem

  257.           );



  258.         // We will pass a plane to our jig, to help

  259.         // with UCS transformations



  260.         var plane = new Plane(Point3d.Origin, normal);



  261.         // We also pass a db-resident polyline



  262.         var pl = new Polyline();

  263.         pl.Normal = normal;



  264.         btr.AppendEntity(pl);

  265.         tr.AddNewlyCreatedDBObject(pl, true);



  266.         // And a db-resident hatch



  267.         var hat = new Hatch();



  268.         // Use a non-solid hatch pattern, to aid jigging



  269.         hat.SetHatchPattern(

  270.           HatchPatternType.PreDefined,

  271.           "ANGLE"

  272.         );



  273.         // But let's make it transparent, for fun

  274.         // Alpha value is Truncate(255 * (100-n)/100)



  275.         hat.ColorIndex = 1;

  276.         hat.Transparency = new Transparency(127);



  277.         // Add the hatch to the modelspace & transaction



  278.         var hatId = btr.AppendEntity(hat);

  279.         tr.AddNewlyCreatedDBObject(hat, true);



  280.         // And finally pass everything to the jig



  281.         var jig =

  282.           new HatchJig(

  283.             ed.CurrentUserCoordinateSystem, plane, pl, hat

  284.           );



  285.         while (true)

  286.         {

  287.           var res = ed.Drag(jig);



  288.           switch (res.Status)

  289.           {

  290.             // New point was added, keep going



  291.             case PromptStatus.OK:

  292.               jig.AddDummyVertex();

  293.               break;



  294.             // Keyword was entered



  295.             case PromptStatus.Keyword:

  296.               if (jig.IsUndoing)

  297.                 jig.RemoveLastVertex();

  298.               break;



  299.             // The jig completed successfully



  300.             case PromptStatus.None:



  301.               // You can remove this next line if you want

  302.               // the vertex being jigged to be included



  303.               if (jig.RemoveLastVertex())

  304.               {

  305.                 tr.Commit();

  306.               }

  307.               return;



  308.             // User cancelled the command



  309.             default:

  310.               // No need to erase the polyline & hatch, as

  311.               // the transaction will simply be aborted

  312.               return;

  313.           }

  314.         }

  315.       }

  316.     }

  317.   }

  318. }
Here’s the code in action, with none of the former visual artifacts:




Jigging a hatch

Thanks again, Holger! :-)

Update:

After a comment from Robbo, I went ahead and made the generated hatch associative to its boundary by adding a single line of code (“hat.Associative = true;”) once the hatch has been made db-resident. You can, of course, comment out this line to revert to the previous behaviour.

Update 2:

Well that’s strange. It turns out Holger’s code was already setting boundary associativity… not sure how I missed that. Anyway, I have addressed an issue pointed out in the comments (thanks, aks!) where if you hit the spacebar immediately after starting the jig, an exception gets thrown. This has now been fixed in the above code.

0.jpg
untitled.png
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

已领礼包: 1336个

财富等级: 财源广进

发表于 2014-7-31 20:20:02 | 显示全部楼层
谢谢分享,长见识了。可惜不懂。
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 1 反对 0

使用道具 举报

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

本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|辽公网安备|晓东CAD家园 ( 辽ICP备15016793号 )

GMT+8, 2024-5-22 11:06 , Processed in 0.283504 second(s), 33 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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