明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 12361|回复: 8

[Kean专集] Kean专题(16)—Solid Modeling

 关闭 [复制链接]
发表于 2009-9-14 17:07:00 | 显示全部楼层 |阅读模式
本帖最后由 作者 于 2009-9-14 17:40:50 编辑

http://through-the-interface.typepad.com/through_the_interface/solid_modeling/008
一、创建扫掠曲面
May 22, 2
Sweeping an AutoCAD surface using .NET
AutoCAD 2007 introduced more advanced solid & surface modeling tools. This post takes a look at how to generate one particular type of surface: a SweptSurface, which is created by sweeping a profile (which could be a region, a planar surface or a curve) through a particular path (which must be a curve).
The below C# code shows how to sweep an object along a curved path to create a surface. Our SAP (for SweepAlongPath) command doesn't provide all the options of the standard SWEEP command, as the point is to show how to do this programmatically, not to duplicate standard AutoCAD functionality.
We're creating a SweptSurface in our code: it's also possible to sweep a similar entity along a path to create a Solid3d, but at the time of writing this is only exposed through ObjectARX (AcDb3dSolid::createSweptSolid()). If you have a strong need to create swept solids in AutoCAD using .NET, please send me an email.
  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 SolidCreation
  7. {
  8.   public class Commands
  9.   {
  10.     [CommandMethod("SAP")]
  11.     public void SweepAlongPath()
  12.     {
  13.       Document doc =
  14.         Application.DocumentManager.MdiActiveDocument;
  15.       Database db = doc.Database;
  16.       Editor ed = doc.Editor;
  17.       // Ask the user to select a region to extrude
  18.       PromptEntityOptions peo1 =
  19.         new PromptEntityOptions(
  20.           "\nSelect profile or curve to sweep: "
  21.         );
  22.       peo1.SetRejectMessage(
  23.         "\nEntity must be a region, curve or planar surface."
  24.       );
  25.       peo1.AddAllowedClass(
  26.         typeof(Region), false);
  27.       peo1.AddAllowedClass(
  28.         typeof(Curve), false);
  29.       peo1.AddAllowedClass(
  30.         typeof(PlaneSurface), false);
  31.       PromptEntityResult per =
  32.         ed.GetEntity(peo1);
  33.       if (per.Status != PromptStatus.OK)
  34.         return;
  35.       ObjectId regId = per.ObjectId;
  36.       // Ask the user to select an extrusion path
  37.       PromptEntityOptions peo2 =
  38.         new PromptEntityOptions(
  39.           "\nSelect path along which to sweep: "
  40.         );
  41.       peo2.SetRejectMessage(
  42.         "\nEntity must be a curve."
  43.       );
  44.       peo2.AddAllowedClass(
  45.         typeof(Curve), false);
  46.       per = ed.GetEntity(peo2);
  47.       if (per.Status != PromptStatus.OK)
  48.         return;
  49.       ObjectId splId = per.ObjectId;
  50.       // Now let's create our swept surface
  51.       Transaction tr =
  52.         db.TransactionManager.StartTransaction();
  53.       using (tr)
  54.       {
  55.         try
  56.         {
  57.           Entity sweepEnt =
  58.             tr.GetObject(regId, OpenMode.ForRead) as Entity;
  59.           Curve pathEnt =
  60.             tr.GetObject(splId, OpenMode.ForRead) as Curve;
  61.           if (sweepEnt == null || pathEnt == null)
  62.           {
  63.             ed.WriteMessage(
  64.               "\nProblem opening the selected entities."
  65.             );
  66.             return;
  67.           }
  68.           // We use a builder object to create
  69.           // our SweepOptions
  70.           SweepOptionsBuilder sob =
  71.             new SweepOptionsBuilder();
  72.           // Align the entity to sweep to the path
  73.           sob.Align =
  74.             SweepOptionsAlignOption.AlignSweepEntityToPath;
  75.           // The base point is the start of the path
  76.           sob.BasePoint = pathEnt.StartPoint;
  77.           // The profile will rotate to follow the path
  78.           sob.Bank = true;
  79.           // Now generate the surface...
  80.           SweptSurface ss =
  81.             new SweptSurface();
  82.           ss.CreateSweptSurface(
  83.             sweepEnt,
  84.             pathEnt,
  85.             sob.ToSweepOptions()
  86.           );
  87.           // ... and add it to the modelspace
  88.           BlockTable bt =
  89.             (BlockTable)tr.GetObject(
  90.               db.BlockTableId,
  91.               OpenMode.ForRead
  92.             );
  93.           BlockTableRecord ms =
  94.             (BlockTableRecord)tr.GetObject(
  95.               bt[BlockTableRecord.ModelSpace],
  96.               OpenMode.ForWrite
  97.             );
  98.           ms.AppendEntity(ss);
  99.           tr.AddNewlyCreatedDBObject(ss, true);
  100.           tr.Commit();
  101.         }
  102.         catch
  103.         { }
  104.       }
  105.     }
  106.   }
  107. }
Here's an image of a very simple drawing I used to demonstrate the function. I started by creating a helix, and then copied it, essentially creating my two paths. I then drew a tiny circle at the end point of the first helix (not aligned to the path in any way - just flat in the World UCS - as the SweepOptions we choose will ask that the profile to be aligned automatically to the path), and drew a simple "S"-shaped spline at the end point of the second helix (I drew the spline elsewhere and moved it using the mid-point as a base point, selecting the end of the helix as the destination).
So we end up with two paths (both helixes) and their respective non-aligned profiles (one a circle, the other a spline):

I then ran the SAP command twice, selecting one of the profiles and its respective path each time. Here's the 2D wireframe view of what was created:

To see the geometry better, I then changed to use the conceptual visual style and orbitted around to get a better 3D view:

Update
As mentioned in this post, Solid3d.CreateSweptSolid has now been implemented in AutoCAD 2010.

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-9-14 18:06:00 | 显示全部楼层
二、通过三维对象创建剪切平面的方式创建截面对象
May 26, 2008
Sectioning an AutoCAD solid using .NET
In the last post we saw how to access some of the 3D modeling functionality introduced in AutoCAD 2007. This post continues that theme, by looking at how to section a Solid3d object programmatically inside AutoCAD. Thanks to Wayne Brill, from DevTech Americas, for providing the original code that inspired this post.
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. using System;
  7. namespace SolidSection
  8. {
  9.   public class Commands
  10.   {
  11.     [CommandMethod("SS")]
  12.     public void SectionSolid()
  13.     {
  14.       Document doc =
  15.         Application.DocumentManager.MdiActiveDocument;
  16.       Database db = doc.Database;
  17.       Editor ed = doc.Editor;
  18.       // Ask the user to select an entity to section
  19.       PromptEntityOptions peo =
  20.         new PromptEntityOptions(
  21.           "\nSelect entity to section: "
  22.         );
  23.       peo.SetRejectMessage(
  24.         "\nEntity must be a 3D solid, " +
  25.         "surface, body or region."
  26.       );
  27.       peo.AddAllowedClass(typeof(Solid3d), false);
  28.       peo.AddAllowedClass(
  29.         typeof(Autodesk.AutoCAD.DatabaseServices.Surface),
  30.         false
  31.       );
  32.       peo.AddAllowedClass(typeof(Body), false);
  33.       peo.AddAllowedClass(typeof(Region), false);
  34.       PromptEntityResult per =
  35.         ed.GetEntity(peo);
  36.       if (per.Status != PromptStatus.OK)
  37.         return;
  38.       ObjectId entId =
  39.         per.ObjectId;
  40.       // Ask the user to define a section plane
  41.       Point3dCollection pts =
  42.         new Point3dCollection();
  43.       PromptPointResult ppr =
  44.         ed.GetPoint("\nPick first point for section: ");
  45.       if (ppr.Status != PromptStatus.OK)
  46.         return;
  47.       pts.Add(ppr.Value);
  48.       PromptPointOptions ppo =
  49.         new PromptPointOptions(
  50.           "\nPick end point for section: "
  51.         );
  52.       ppo.BasePoint = ppr.Value;
  53.       ppo.UseBasePoint = true;
  54.       ppr =
  55.         ed.GetPoint(ppo);
  56.       if (ppr.Status != PromptStatus.OK)
  57.         return;
  58.       pts.Add(ppr.Value);
  59.       // Ask what type of section to create
  60.       PromptKeywordOptions pko =
  61.         new PromptKeywordOptions(
  62.           "Enter section type "
  63.         );
  64.       pko.AllowNone = true;
  65.       pko.Keywords.Add("2D");
  66.       pko.Keywords.Add("3D");
  67.       pko.Keywords.Add("Live");
  68.       pko.Keywords.Default = "3D";
  69.       PromptResult pkr =
  70.         ed.GetKeywords(pko);
  71.       if (pkr.Status != PromptStatus.OK)
  72.         return;
  73.       SectionType st;
  74.       if (pkr.StringResult == "2D")
  75.         st = SectionType.Section2d;
  76.       else if (pkr.StringResult == "Live")
  77.         st = SectionType.LiveSection;
  78.       else // pkr.StringResult == "3D"
  79.         st = SectionType.Section3d;
  80.       // Now we're ready to do the real work
  81.       Transaction tr =
  82.         db.TransactionManager.StartTransaction();
  83.       using (tr)
  84.       {
  85.         try
  86.         {
  87.           BlockTable bt =
  88.             (BlockTable)tr.GetObject(
  89.               db.BlockTableId,
  90.               OpenMode.ForRead
  91.             );
  92.           BlockTableRecord ms =
  93.             (BlockTableRecord)tr.GetObject(
  94.               bt[BlockTableRecord.ModelSpace],
  95.               OpenMode.ForWrite
  96.             );
  97.           // Now let's create our section
  98.           Section sec =
  99.             new Section(pts, Vector3d.ZAxis);
  100.           sec.State = SectionState.Plane;
  101.           // The section must be added to the drawing
  102.           ObjectId secId =
  103.             ms.AppendEntity(sec);
  104.           tr.AddNewlyCreatedDBObject(sec, true);
  105.           // Set up some of its direct properties
  106.           sec.SetHeight(
  107.             SectionHeight.HeightAboveSectionLine,
  108.             3.0
  109.           );
  110.           sec.SetHeight(
  111.             SectionHeight.HeightBelowSectionLine,
  112.             1.0
  113.           );
  114.           // ... and then its settings
  115.           SectionSettings ss =
  116.             (SectionSettings)tr.GetObject(
  117.               sec.Settings,
  118.               OpenMode.ForWrite
  119.             );
  120.           // Set our section type
  121.           ss.CurrentSectionType = st;
  122.           // We only set one additional option if "Live"
  123.           if (st == SectionType.LiveSection)
  124.             sec.EnableLiveSection(true);
  125.           else
  126.           {
  127.             // Non-live (i.e. 2D or 3D) settings
  128.             ObjectIdCollection oic =
  129.               new ObjectIdCollection();
  130.             oic.Add(entId);
  131.             ss.SetSourceObjects(st, oic);
  132.             if (st == SectionType.Section2d)
  133.             {
  134.               // 2D-specific settings
  135.               ss.SetVisibility(
  136.                 st,
  137.                 SectionGeometry.BackgroundGeometry,
  138.                 true
  139.               );
  140.               ss.SetHiddenLine(
  141.                 st,
  142.                 SectionGeometry.BackgroundGeometry,
  143.                 false
  144.               );
  145.             }
  146.             else if (st == SectionType.Section3d)
  147.             {
  148.               // 3D-specific settings
  149.               ss.SetVisibility(
  150.                 st,
  151.                 SectionGeometry.ForegroundGeometry,
  152.                 true
  153.               );
  154.             }
  155.             // Finish up the common 2D/3D settings
  156.             ss.SetGenerationOptions(
  157.               st,
  158.               SectionGeneration.SourceSelectedObjects |
  159.               SectionGeneration.DestinationFile
  160.             );
  161.           }
  162.           // Open up the main entity
  163.           Entity ent =
  164.             (Entity)tr.GetObject(
  165.               entId,
  166.               OpenMode.ForRead
  167.             );
  168.           // Generate the section geometry
  169.           Array flEnts, bgEnts, fgEnts, ftEnts, ctEnts;
  170.           sec.GenerateSectionGeometry(
  171.             ent,
  172.             out flEnts,
  173.             out bgEnts,
  174.             out fgEnts,
  175.             out ftEnts,
  176.             out ctEnts
  177.           );
  178.           // Add the geometry to the modelspace
  179.           // (start by combining the various arrays,
  180.           // so we then have one loop, not four)
  181.           int numEnts =
  182.             flEnts.Length + fgEnts.Length +
  183.             bgEnts.Length + ftEnts.Length +
  184.             ctEnts.Length;
  185.           // Create the appropriately-sized array
  186.           Array ents =
  187.           Array.CreateInstance(
  188.             typeof(Entity),
  189.             numEnts
  190.           );
  191.           // Copy across the contents of the
  192.           // various arrays
  193.           int index = 0;
  194.           flEnts.CopyTo(ents, index);
  195.           index += flEnts.Length;
  196.           fgEnts.CopyTo(ents, index);
  197.           index += fgEnts.Length;
  198.           bgEnts.CopyTo(ents, index);
  199.           index += bgEnts.Length;
  200.           ftEnts.CopyTo(ents, index);
  201.           index += ftEnts.Length;
  202.           ctEnts.CopyTo(ents, index);
  203.           // Our single loop to add entities
  204.           foreach (Entity ent2 in ents)
  205.           {
  206.             ms.AppendEntity(ent2);
  207.             tr.AddNewlyCreatedDBObject(ent2, true);
  208.           }
  209.           tr.Commit();
  210.         }
  211.         catch (System.Exception ex)
  212.         {
  213.           ed.WriteMessage(
  214.             "\nException: " + ex.Message
  215.           );
  216.         }
  217.       }
  218.     }
  219.   }
  220. }
To see the results of the various options in the SS command, I created three identical spheres in an empty drawing:

I then used the SS command, selecting each sphere in turn and selecting a similar section line for each (as close as I could get without measuring), choosing, of course, a different command option each time (2D, 3D and Live, from left to right):

Orbitting this view, we see the section planes for each sphere:

The objects we've added to the drawing for the two left-hand sections are basic (2D or 3D, depending) geometry. The third, however, includes a section object:

A quick note on the code at the end which adds the various generated geometry to the drawing: in order to avoid having multiple foreach loops (one for each of flEnts, fgEnts, bgEnts, ftEnts & ctEnts), I opted to create an über-array which then gets populated by the contents of each of the other lists. This simple exercise was a pain in C#, as you can see from the code. In fact, having five separate loops could probably be considered less ugly, depending on your perspective. This is the kind of operation that's a breeze in a language like F#, and, with hindsight, I probably should have chosen F# from the beginning for just that reason. Maybe I'll throw an F# version together for comparison's sake.

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-9-14 18:17:00 | 显示全部楼层
三、判断文件是二维还是三维
July 09, 2008
Testing whether an AutoCAD drawing is 2D or 3D using .NET
This post demonstrates a simple check for whether a drawing is two or three dimensional. The code is almost embarrassingly simple, but then the question is significant and in the absence of a "Is3D" property on the Database object this is likely to prove useful for people.
So how do we check whether a drawing is 3D? The quick answer is that in most circumstances the EXTMAX system variable will have a non-zero Z value for a 3D drawing. There are potential situations where this might not be true (and EXTMAX doesn't reflect the 3D nature of certain geometry), but given the likelihood that any real-world 3D model includes a variety of geometry, it's pretty safe to rely upon. The alternative is to iterate through and test geometry, but checking EXTMAX is quicker, by far, and the alternative should only by needed if you find a particular scenario that EXTMAX doesn't address.
Here's some C# code that tells us whether a drawing is 2D or 3D:
  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. namespace NumberOfDimensions
  7. {
  8.   public class Commands
  9.   {
  10.     [CommandMethod("IS3D")]
  11.     static public void CheckWhether3D()
  12.     {
  13.       Document doc =
  14.         Application.DocumentManager.MdiActiveDocument;
  15.       Database db = doc.Database;
  16.       Editor ed = doc.Editor;
  17.       ed.WriteMessage(
  18.         "\nDrawing is {0}.",
  19.         (IsDrawing3D(db) ? "3D" : "2D")
  20.       );
  21.     }
  22.     private static bool IsDrawing3D(Database db)
  23.     {
  24.       return (db.Extmax.Z > Tolerance.Global.EqualPoint);
  25.     }
  26.   }
  27. }
Here's what happens when we call the IS3D command on a fresh drawing, after we've drawn a 2D line and then after we've drawn a tiny sphere:
  1. Command: IS3D
  2. Drawing is 2D.
  3. Command: LINE
  4. Specify first point: 0,0,0
  5. Specify next point or [Undo]: @10,10
  6. Specify next point or [Undo]:
  7. Command: IS3D
  8. Drawing is 2D.
  9. Command: SPHERE
  10. Specify center point or [3P/2P/Ttr]: 0,0,0
  11. Specify radius or [Diameter]: 0.0001
  12. Command: IS3D
  13. Drawing is 3D.
复制代码
 楼主| 发表于 2009-9-14 20:35:00 | 显示全部楼层
本帖最后由 作者 于 2009-9-17 10:35:31 编辑

四、使用AutoCad2009新的API遍历三维实体的边界
September 01, 2008
Traversing a 3D solid's brep using AutoCAD 2009's new .NET API
In a recent webcast, Gopinath Taget, from our DevTech Americas team, showed how to use the Brep API from a .NET application: something that was made possible in AutoCAD 2009. The Brep API in AutoCAD allows you to traverse the  boundary representation of a Solid3d object. Without going into specifics - as this isn't really an area of AutoCAD I've had much reason to use, over the years - I went ahead and took the sample Gopi showed in his webcast and modified it for the purposes of this blog.
The following C# code traverses the Brep of a selected Solid3d, dumping the information to the command-line. It uses the technique shown in this previous post to retrieve the type of solid we're dealing with via COM.
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. using Autodesk.AutoCAD.BoundaryRepresentation;
  6. using Autodesk.AutoCAD.Interop.Common;
  7. using BrFace =
  8.   Autodesk.AutoCAD.BoundaryRepresentation.Face;
  9. namespace BRepTraversal
  10. {
  11.   public class Commands
  12.   {
  13.     [CommandMethod("TBR")]
  14.     static public void TraverseBRep()
  15.     {
  16.       Document doc =
  17.         Application.DocumentManager.MdiActiveDocument;
  18.       Database db = doc.Database;
  19.       Editor ed = doc.Editor;
  20.       Transaction tr =
  21.         db.TransactionManager.StartTransaction();
  22.       using (tr)
  23.       {
  24.         try
  25.         {
  26.           // Prompt for selection of a solid to be traversed
  27.           PromptEntityOptions prEntOpt =
  28.             new PromptEntityOptions(
  29.               "\nSelect a 3D solid:"
  30.             );
  31.           prEntOpt.SetRejectMessage(
  32.             "\nMust be a 3D solid."
  33.           );
  34.           prEntOpt.AddAllowedClass(typeof(Solid3d), true);
  35.           PromptEntityResult prEntRes =
  36.             ed.GetEntity(prEntOpt);
  37.           ObjectId[] objIds = { prEntRes.ObjectId };
  38.           Solid3d sol =
  39.             (Solid3d)tr.GetObject(
  40.               prEntRes.ObjectId,
  41.               OpenMode.ForRead
  42.             );
  43.           // Use COM to get the solid's type
  44.           Acad3DSolid oSol = (Acad3DSolid)sol.AcadObject;
  45.           ed.WriteMessage(
  46.             "\nSolid type: {0}",
  47.             oSol.SolidType
  48.           );
  49.           oSol = null;
  50.           // Build the BRep topology object to traverse
  51.           Brep brp = new Brep(sol);
  52.           using (brp)
  53.           {
  54.             int cmpCnt = 0;
  55.             // Get all the Complexes which are primary BRep
  56.             // elements and represent a conceptual topological
  57.             // entity of connected shell boundaries.
  58.             foreach (Complex cmp in brp.Complexes)
  59.             {
  60.               ed.WriteMessage(
  61.                 "\n  Complex number {0}",
  62.                 ++cmpCnt
  63.               );
  64.               // Get all the shells within a complex. Shells
  65.               // are secondary BRep entities that correspond
  66.               // to a collection of neighboring surfaces on a
  67.               // solid
  68.               int shlCnt = 0;
  69.               foreach (Shell shl in cmp.Shells)
  70.               {
  71.                 ed.WriteMessage(
  72.                   "\n    Shell number {0} [{1}]",
  73.                   ++shlCnt,
  74.                   shl.ShellType
  75.                 );
  76.                 // Get all the faces in a shell. Faces are
  77.                 // primary BRep topological entities that
  78.                 // directly correspond to face subentities on
  79.                 // AutoCAD entities like solid, region and body
  80.                 int fceCnt = 0;
  81.                 foreach (BrFace fce in shl.Faces)
  82.                 {
  83.                   ed.WriteMessage(
  84.                     "\n      Face number {0}",
  85.                     ++fceCnt
  86.                   );
  87.                   // Get all the boundary loops within a face
  88.                   // (Secondary BRep entities and no corresponding
  89.                   // AutoCAD entities/subentities)
  90.                   try
  91.                   {
  92.                     int lpCnt = 0;
  93.                     foreach (BoundaryLoop lp in fce.Loops)
  94.                     {
  95.                       ed.WriteMessage(
  96.                         "\n        Loop number {0} [{1}]",
  97.                         ++lpCnt,
  98.                         lp.LoopType
  99.                       );
  100.                       // Get all the Edges in a loop (Edges are
  101.                       // primary BRep entities and correspond to
  102.                       // Geometric entities). Output the
  103.                       int edgCnt = 0;
  104.                       foreach (Edge edg in lp.Edges)
  105.                       {
  106.                         ed.WriteMessage(
  107.                           "\n          Edge number {0}: " +
  108.                           "\n          Vertex 1: {1}" +
  109.                           "\n          Vertex 2: {2}",
  110.                           ++edgCnt,
  111.                           edg.Vertex1.Point,
  112.                           edg.Vertex2.Point
  113.                         );
  114.                       }
  115.                     }
  116.                   }
  117.                   catch
  118.                   {
  119.                     ed.WriteMessage(
  120.                       "\n        Problem getting loops/edges:" +
  121.                       " object is probably unbounded " +
  122.                       "(e.g. a sphere or a torus)."
  123.                     );
  124.                   }
  125.                 }
  126.               }
  127.             }
  128.           }
  129.           tr.Commit();
  130.         }
  131.         catch (System.Exception ex)
  132.         {
  133.           ed.WriteMessage(
  134.             "\nException during traversal: {0}",
  135.             ex.Message
  136.           );
  137.         }
  138.       }
  139.     }
  140.   }
  141. }
Here's what happens when we run the TBR command against a cylinder:
  1. Command:  TBR
  2. Select a 3D solid:
  3. Solid type: Cylinder
  4.   Complex number 1
  5.     Shell number 1 [ShellExterior]
  6.       Face number 1
  7.         Loop number 1 [LoopWinding]
  8.           Edge number 1:
  9.           Vertex 1: (15.0005647466013,4.38174252440491,0)
  10.           Vertex 2: (15.0005647466013,4.38174252440491,0)
  11.         Loop number 2 [LoopWinding]
  12.           Edge number 1:
  13.           Vertex 1: (15.0005647466013,4.38174252440491,5.78051528967497)
  14.           Vertex 2: (15.0005647466013,4.38174252440491,5.78051528967497)
  15.       Face number 2
  16.         Loop number 1 [LoopExterior]
  17.           Edge number 1:
  18.           Vertex 1: (15.0005647466013,4.38174252440491,0)
  19.           Vertex 2: (15.0005647466013,4.38174252440491,0)
  20.       Face number 3
  21.         Loop number 1 [LoopExterior]
  22.           Edge number 1:
  23.           Vertex 1: (15.0005647466013,4.38174252440491,5.78051528967497)
  24.           Vertex 2: (15.0005647466013,4.38174252440491,5.78051528967497)
复制代码
One point to note: in the above code I've used a try-catch block around the code to get the loops of a face and the edges of a loop. This is because the GetEnumerator() call on either a loop or an edge (which gets called implicitly by the foreach statement to loop through their respective enumerable objects) can throw an exception in the case where an object is unbounded. Here's an excerpt from the .NET reference material (currently residing in the ObjectARX Reference, which is part of the ObjectARX SDK):
    A topological object may be unbounded (that is, it may have no lower dimensional bounding topology) only in the following cases:
        * A closed surface, which is intrinsically bounded in both the u and v directions (such as a full torus or sphere), is represented by a face that has no loop boundaries.
        * A closed curve, which is intrinsically bounded (such as a full circle or ellipse), is represented by an edge that has coincident start and end vertices.
For example, here's what happens when we run the TBR command against a torus:
  1. Command:  TBR
  2. Select a 3D solid:
  3. Solid type: Torus
  4.   Complex number 1
  5.     Shell number 1 [ShellExterior]
  6.       Face number 1
  7.         Problem getting loops/edges: object is probably unbounded (e.g. a sphere or a torus).
复制代码
So we have, at least, detected the case, even if we have to deal with the overhead of catching an exception rather than checking a property for the number of Loops (something I couldn't work out how to do).

 楼主| 发表于 2009-9-14 20:42:00 | 显示全部楼层

四、在三维实体上选择最接近的面

January 20, 2009
Selecting the nearest face of an AutoCAD solid using .NET

This post has come out of an interesting discussion I had with Jim Cameron at the ADN party at AU 2008. He mentioned an idea, which he kindly later reminded me of by email, which was to develop an AutoCAD equivalent for Inventor's LookAt functionality. I didn't know about LookAt before this discussion, but it seems it allows you to look at a particular face: you pick a face and it rotates the view and zooms in to centre it on the screen.

Rather than try to attack the whole problem at once, this post tackles selecting a face (which is slightly more complicated than perhaps it might be) and in a future post I'll hopefully manage to post some code to perform the view change.

Here's the C# code:

using System.Collections.Generic;

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.GraphicsInterface;

using Autodesk.AutoCAD.BoundaryRepresentation;

using BrFace =

  Autodesk.AutoCAD.BoundaryRepresentation.Face;

using BrException =

  Autodesk.AutoCAD.BoundaryRepresentation.Exception;

namespace LookAtFace

{

  public class Commands

  {

    // Keep a list of trhe things we've drawn

    // so we can undraw them

    List<Drawable> _drawn = new List<Drawable>();

    [CommandMethod("PICKFACE")]

    public void PickFace()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;

      ClearDrawnGraphics();

      PromptEntityOptions peo =

        new PromptEntityOptions(

          "\nSelect face of solid:"

        );

      peo.SetRejectMessage("\nMust be a 3D solid.");

      peo.AddAllowedClass(typeof(Solid3d), false);

      PromptEntityResult per =

        ed.GetEntity(peo);

      if (per.Status != PromptStatus.OK)

        return;

      Transaction tr =

        db.TransactionManager.StartTransaction();

      using (tr)

      {

        Solid3d sol =

          tr.GetObject(per.ObjectId, OpenMode.ForRead)

            as Solid3d;

        if (sol != null)

        {

          Brep brp = new Brep(sol);

          using (brp)

          {

            // We're going to check interference between our

            // solid and a line we're creating between the

            // picked point and the user (we use the view

            // direction to decide in which direction to

            // draw the line)

            Point3d dir =

              (Point3d)Application.GetSystemVariable("VIEWDIR");

            Point3d picked = per.PickedPoint,

                    nearerUser =

                      per.PickedPoint - (dir - Point3d.Origin);

            // Two hits should be enough (in and out)

            const int numHits = 2;

            // Create out line

            Line3d ln = new Line3d(picked, nearerUser);

            Hit[] hits = brp.GetLineContainment(ln, numHits);

            ln.Dispose();

            if (hits == null || hits.Length < numHits)

              return;

            // Set the shortest distance to something large

            // and the index to the first item in the list

            double shortest = (picked - nearerUser).Length;

            int found = 0;

            // Loop through and check the distance to the

            // user (the depth of field).

            for (int idx = 0; idx < numHits; idx++)

            {

              Hit hit = hits[idx];

              double dist = (hit.Point - nearerUser).Length;

              if (dist < shortest)

              {

                shortest = dist;

                found = idx;

              }

            }

            // Once we have the nearest point to the screen,

            // use that one to get the containing curves

            List<Curve3d> curves = new List<Curve3d>();

            if (CheckContainment(

                  ed,

                  brp,

                  hits[found].Point,

                  ref curves

                )

            )

            {

              // If we get some back, get drawables for them and

              // pass them through to the transient graphics API

              TransientManager tm =

                TransientManager.CurrentTransientManager;

              IntegerCollection ic = new IntegerCollection();

              foreach (Curve3d curve in curves)

              {

                Drawable d = GetDrawable(curve);

                tm.AddTransient(

                  d,

                  TransientDrawingMode.DirectTopmost,

                  0,

                  ic

                );

                _drawn.Add(d);

              }

            }

          }

        }

        tr.Commit();

      }

    }

    private void ClearDrawnGraphics()

    {

      // Clear any graphics we've drawn with the transient

      // graphics API, then clear the list

      TransientManager tm =

        TransientManager.CurrentTransientManager;

      IntegerCollection ic = new IntegerCollection();

      foreach (Drawable d in _drawn)

      {

        tm.EraseTransient(d, ic);

      }

      _drawn.Clear();

    }

    private Drawable GetDrawable(Curve3d curve)

    {

      // We could support multiple curve types here, but for

      // now let's just return a line approximating it

      Line ln = new Line(curve.StartPoint, curve.EndPoint);

      ln.ColorIndex = 1;

      return ln;

    }

    private static bool CheckContainment(

      Editor ed,

      Brep brp,

      Point3d pt,

      ref List<Curve3d> curves

    )

    {

      bool res = false;

      // Use the BRep API to get the lowest level

      // container for the point

      PointContainment pc;

      BrepEntity be =

        brp.GetPointContainment(pt, out pc);

      using (be)

      {

        // Only if the point is on a boundary...

        if (pc == PointContainment.OnBoundary)

        {

          // And only if the boundary is a face...

          BrFace face = be as BrFace;

          if (face != null)

          {

            // ... do we attempt to do something

            try

            {

              foreach (BoundaryLoop bl in face.Loops)

              {

                // We'll return a curve for each edge in

                // the containing loop

                foreach (Edge edge in bl.Edges)

                {

                  curves.Add(edge.Curve);

                }

              }

              res = true;

            }

            catch (BrException)

            {

              res = false;

            }

          }

        }

      }

      return res;

    }

  }

}

A few comments on the implementation:

    * We use the standard Editor.GetEntity() selection method - it gives us the ObjectId of the selected Solid3d but also the point that was picked.
    * Using this point and the view direction, we can then draw a line (which we make as big as the diagonal of the solid's bounding box, which should be large enough) from that point in the direction of the user.
    * The Boundary Representation (BRep) API allows us to determine how this line intersects the solid: we select the intersection nearest the screen, as presumably that's the one the user was intending to pick.
    * We will then use the BRep API to test the solid to see whether the point is contained by (or - and this is more likely - on) the solid, and it very helpfully provides us with the lowest-level topological entity that contains the point (which we hope to be a face).
    * The BRep API will throw an exception when traversing (typically during calls to GetEnumerator() for various collections) for a couple of unbounded solid-types (spheres, etc.) as we traverse: in this case we simply abort the containment checking operation.
    * We use the Transient Graphics API to display the edges of the selected face. Right now we just draw lines for each curve - which will be wrong for anything with arcs or circles, for instance - but at this stage we don't care a great deal about the graphics we're drawing - this is really just to make sure we're approximately accurate, and later we'll do something more intelligent with the edges we get back for the selected face.

Here's what happens when we use the PICKFACE command to select first one face and then another of a simple box:

 楼主| 发表于 2009-9-14 20:45:00 | 显示全部楼层
本帖最后由 作者 于 2009-9-17 10:35:59 编辑

五、在三维实体上选择最接近的面
January 20, 2009
Selecting the nearest face of an AutoCAD solid using .NET
This post has come out of an interesting discussion I had with Jim Cameron at the ADN party at AU 2008. He mentioned an idea, which he kindly later reminded me of by email, which was to develop an AutoCAD equivalent for Inventor's LookAt functionality. I didn't know about LookAt before this discussion, but it seems it allows you to look at a particular face: you pick a face and it rotates the view and zooms in to centre it on the screen.
Rather than try to attack the whole problem at once, this post tackles selecting a face (which is slightly more complicated than perhaps it might be) and in a future post I'll hopefully manage to post some code to perform the view change.
Here's the C# code:
  1. using System.Collections.Generic;
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Runtime;
  6. using Autodesk.AutoCAD.Geometry;
  7. using Autodesk.AutoCAD.GraphicsInterface;
  8. using Autodesk.AutoCAD.BoundaryRepresentation;
  9. using BrFace =
  10.   Autodesk.AutoCAD.BoundaryRepresentation.Face;
  11. using BrException =
  12.   Autodesk.AutoCAD.BoundaryRepresentation.Exception;
  13. namespace LookAtFace
  14. {
  15.   public class Commands
  16.   {
  17.     // Keep a list of trhe things we've drawn
  18.     // so we can undraw them
  19.     List<Drawable> _drawn = new List<Drawable>();
  20.     [CommandMethod("PICKFACE")]
  21.     public void PickFace()
  22.     {
  23.       Document doc =
  24.         Application.DocumentManager.MdiActiveDocument;
  25.       Database db = doc.Database;
  26.       Editor ed = doc.Editor;
  27.       ClearDrawnGraphics();
  28.       PromptEntityOptions peo =
  29.         new PromptEntityOptions(
  30.           "\nSelect face of solid:"
  31.         );
  32.       peo.SetRejectMessage("\nMust be a 3D solid.");
  33.       peo.AddAllowedClass(typeof(Solid3d), false);
  34.       PromptEntityResult per =
  35.         ed.GetEntity(peo);
  36.       if (per.Status != PromptStatus.OK)
  37.         return;
  38.       Transaction tr =
  39.         db.TransactionManager.StartTransaction();
  40.       using (tr)
  41.       {
  42.         Solid3d sol =
  43.           tr.GetObject(per.ObjectId, OpenMode.ForRead)
  44.             as Solid3d;
  45.         if (sol != null)
  46.         {
  47.           Brep brp = new Brep(sol);
  48.           using (brp)
  49.           {
  50.             // We're going to check interference between our
  51.             // solid and a line we're creating between the
  52.             // picked point and the user (we use the view
  53.             // direction to decide in which direction to
  54.             // draw the line)
  55.             Point3d dir =
  56.               (Point3d)Application.GetSystemVariable("VIEWDIR");
  57.             Point3d picked = per.PickedPoint,
  58.                     nearerUser =
  59.                       per.PickedPoint - (dir - Point3d.Origin);
  60.             // Two hits should be enough (in and out)
  61.             const int numHits = 2;
  62.             // Create out line
  63.             Line3d ln = new Line3d(picked, nearerUser);
  64.             Hit[] hits = brp.GetLineContainment(ln, numHits);
  65.             ln.Dispose();
  66.             if (hits == null || hits.Length < numHits)
  67.               return;
  68.             // Set the shortest distance to something large
  69.             // and the index to the first item in the list
  70.             double shortest = (picked - nearerUser).Length;
  71.             int found = 0;
  72.             // Loop through and check the distance to the
  73.             // user (the depth of field).
  74.             for (int idx = 0; idx < numHits; idx++)
  75.             {
  76.               Hit hit = hits[idx];
  77.               double dist = (hit.Point - nearerUser).Length;
  78.               if (dist < shortest)
  79.               {
  80.                 shortest = dist;
  81.                 found = idx;
  82.               }
  83.             }
  84.             // Once we have the nearest point to the screen,
  85.             // use that one to get the containing curves
  86.             List<Curve3d> curves = new List<Curve3d>();
  87.             if (CheckContainment(
  88.                   ed,
  89.                   brp,
  90.                   hits[found].Point,
  91.                   ref curves
  92.                 )
  93.             )
  94.             {
  95.               // If we get some back, get drawables for them and
  96.               // pass them through to the transient graphics API
  97.               TransientManager tm =
  98.                 TransientManager.CurrentTransientManager;
  99.               IntegerCollection ic = new IntegerCollection();
  100.               foreach (Curve3d curve in curves)
  101.               {
  102.                 Drawable d = GetDrawable(curve);
  103.                 tm.AddTransient(
  104.                   d,
  105.                   TransientDrawingMode.DirectTopmost,
  106.                   0,
  107.                   ic
  108.                 );
  109.                 _drawn.Add(d);
  110.               }
  111.             }
  112.           }
  113.         }
  114.         tr.Commit();
  115.       }
  116.     }
  117.     private void ClearDrawnGraphics()
  118.     {
  119.       // Clear any graphics we've drawn with the transient
  120.       // graphics API, then clear the list
  121.       TransientManager tm =
  122.         TransientManager.CurrentTransientManager;
  123.       IntegerCollection ic = new IntegerCollection();
  124.       foreach (Drawable d in _drawn)
  125.       {
  126.         tm.EraseTransient(d, ic);
  127.       }
  128.       _drawn.Clear();
  129.     }
  130.     private Drawable GetDrawable(Curve3d curve)
  131.     {
  132.       // We could support multiple curve types here, but for
  133.       // now let's just return a line approximating it
  134.       Line ln = new Line(curve.StartPoint, curve.EndPoint);
  135.       ln.ColorIndex = 1;
  136.       return ln;
  137.     }
  138.     private static bool CheckContainment(
  139.       Editor ed,
  140.       Brep brp,
  141.       Point3d pt,
  142.       ref List<Curve3d> curves
  143.     )
  144.     {
  145.       bool res = false;
  146.       // Use the BRep API to get the lowest level
  147.       // container for the point
  148.       PointContainment pc;
  149.       BrepEntity be =
  150.         brp.GetPointContainment(pt, out pc);
  151.       using (be)
  152.       {
  153.         // Only if the point is on a boundary...
  154.         if (pc == PointContainment.OnBoundary)
  155.         {
  156.           // And only if the boundary is a face...
  157.           BrFace face = be as BrFace;
  158.           if (face != null)
  159.           {
  160.             // ... do we attempt to do something
  161.             try
  162.             {
  163.               foreach (BoundaryLoop bl in face.Loops)
  164.               {
  165.                 // We'll return a curve for each edge in
  166.                 // the containing loop
  167.                 foreach (Edge edge in bl.Edges)
  168.                 {
  169.                   curves.Add(edge.Curve);
  170.                 }
  171.               }
  172.               res = true;
  173.             }
  174.             catch (BrException)
  175.             {
  176.               res = false;
  177.             }
  178.           }
  179.         }
  180.       }
  181.       return res;
  182.     }
  183.   }
  184. }
A few comments on the implementation:
    * We use the standard Editor.GetEntity() selection method - it gives us the ObjectId of the selected Solid3d but also the point that was picked.
    * Using this point and the view direction, we can then draw a line (which we make as big as the diagonal of the solid's bounding box, which should be large enough) from that point in the direction of the user.
    * The Boundary Representation (BRep) API allows us to determine how this line intersects the solid: we select the intersection nearest the screen, as presumably that's the one the user was intending to pick.
    * We will then use the BRep API to test the solid to see whether the point is contained by (or - and this is more likely - on) the solid, and it very helpfully provides us with the lowest-level topological entity that contains the point (which we hope to be a face).
    * The BRep API will throw an exception when traversing (typically during calls to GetEnumerator() for various collections) for a couple of unbounded solid-types (spheres, etc.) as we traverse: in this case we simply abort the containment checking operation.
    * We use the Transient Graphics API to display the edges of the selected face. Right now we just draw lines for each curve - which will be wrong for anything with arcs or circles, for instance - but at this stage we don't care a great deal about the graphics we're drawing - this is really just to make sure we're approximately accurate, and later we'll do something more intelligent with the edges we get back for the selected face.
Here's what happens when we use the PICKFACE command to select first one face and then another of a simple box:

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-9-14 20:53:00 | 显示全部楼层
本帖最后由 作者 于 2009-9-17 10:36:32 编辑

六、实现一个LookAt命令
January 22, 2009
Implementing a LookAt command for AutoCAD using .NET
This post follows on from this previous one, where we looked at a technique for picking a face on an AutoCAD solid. Tony Tanzillo kindly pointed out this much cleaner solution for this problem, and also highlighted a really simple (and elegant) way to implement LookAt using standard AutoCAD commands. While I really like both pointers provided by Tony, I've decided to persevere with my existing - admittedly sub-optimal - approach, as much as to show ways to exercise some APIs that people may not have used themselves. Please be warned, this isn't the simplest way to address this problem, and it doesn't even do as much as I'd like, but anyway. :-)
Here's the C# code:
  1. using System;
  2. using System.Collections.Generic;
  3. using Autodesk.AutoCAD.ApplicationServices;
  4. using Autodesk.AutoCAD.DatabaseServices;
  5. using Autodesk.AutoCAD.EditorInput;
  6. using Autodesk.AutoCAD.Runtime;
  7. using Autodesk.AutoCAD.Geometry;
  8. using Autodesk.AutoCAD.BoundaryRepresentation;
  9. using BrFace =
  10.   Autodesk.AutoCAD.BoundaryRepresentation.Face;
  11. using BrException =
  12.   Autodesk.AutoCAD.BoundaryRepresentation.Exception;
  13. using Autodesk.AutoCAD.Interop;
  14. namespace LookAtFace
  15. {
  16.   public class Commands
  17.   {
  18.     [CommandMethod("lookat")]
  19.     public void PickFace()
  20.     {
  21.       Document doc =
  22.         Application.DocumentManager.MdiActiveDocument;
  23.       Database db = doc.Database;
  24.       Editor ed = doc.Editor;
  25.       PromptEntityOptions peo =
  26.         new PromptEntityOptions(
  27.           "\nSelect face of solid:"
  28.         );
  29.       peo.SetRejectMessage("\nMust be a 3D solid.");
  30.       peo.AddAllowedClass(typeof(Solid3d), false);
  31.       PromptEntityResult per =
  32.         ed.GetEntity(peo);
  33.       if (per.Status != PromptStatus.OK)
  34.         return;
  35.       Transaction tr =
  36.         db.TransactionManager.StartTransaction();
  37.       using (tr)
  38.       {
  39.         Solid3d sol =
  40.           tr.GetObject(per.ObjectId, OpenMode.ForRead)
  41.             as Solid3d;
  42.         if (sol != null)
  43.         {
  44.           Brep brp = new Brep(sol);
  45.           using (brp)
  46.           {
  47.             // We're going to check interference between our
  48.             // solid and a line we're creating between the
  49.             // picked point and the user (we use the view
  50.             // direction to decide in which direction to
  51.             // draw the line)
  52.             Point3d dirp =
  53.               (Point3d)Application.GetSystemVariable("VIEWDIR");
  54.             Vector3d dir = dirp - Point3d.Origin;
  55.             Point3d picked = per.PickedPoint,
  56.                     nearerUser = per.PickedPoint + dir;
  57.             // Two hits should be enough (in and out)
  58.             const int numHits = 2;
  59.             // Create out line
  60.             Line3d ln = new Line3d(picked, nearerUser);
  61.             Hit[] hits = brp.GetLineContainment(ln, numHits);
  62.             ln.Dispose();
  63.             if (hits == null || hits.Length < numHits)
  64.               return;
  65.             // Set the shortest distance to something large
  66.             // and the index to the first item in the list
  67.             double shortest =
  68.               (picked - nearerUser).Length * 10;
  69.             int found = 0;
  70.             // Loop through and check the distance to the
  71.             // user (the depth of field).
  72.             for (int idx = 0; idx < numHits; idx++)
  73.             {
  74.               Hit hit = hits[idx];
  75.               double dist = (hit.Point - nearerUser).Length;
  76.               if (dist < shortest)
  77.               {
  78.                 shortest = dist;
  79.                 found = idx;
  80.               }
  81.             }
  82.             Point3d closest = hits[found].Point;
  83.             // Once we have the nearest point to the screen,
  84.             // use that one to get the containing curves
  85.             List<Curve3d> curves = new List<Curve3d>();
  86.             if (CheckContainment(
  87.                   ed,
  88.                   brp,
  89.                   closest,
  90.                   ref curves
  91.                 )
  92.             )
  93.             {
  94.               // Now we want to get a plane from our curves,
  95.               // along with its normal
  96.               Plane plane = null;
  97.               if (curves.Count == 1)
  98.               {
  99.                 // If we just have one curve, hopefully it's planar
  100.                 if (!curves[0].IsPlanar(out plane))
  101.                 {
  102.                   plane = null;
  103.                 }
  104.               }
  105.               else if (curves.Count > 1)
  106.               {
  107.                 // Otherwise we use two curves to define the plane
  108.                 if (!curves[0].IsCoplanarWith(curves[1], out plane))
  109.                 {
  110.                   plane = null;
  111.                 }
  112.               }
  113.               // Assuming we have a plane, let's check the normal
  114.               // is facing outwards
  115.               if (plane != null)
  116.               {
  117.                 // Get intersections between our "normal" line
  118.                 // and the solid
  119.                 ln = new Line3d(closest, closest + plane.Normal);
  120.                 hits = brp.GetLineContainment(ln, numHits);
  121.                 // Check whether these points are actually on the
  122.                 // line (if the params are zero or positive, that
  123.                 // means they are on the line). If both are, then
  124.                 // we need to reverse the normal, as it cuts
  125.                 // through our solid
  126.                 bool reverseNeeded = false;
  127.                 double param1, param2;
  128.                 if (ln.IsOn(hits[0].Point, out param1) &&
  129.                     ln.IsOn(hits[1].Point, out param2))
  130.                 {
  131.                   if (
  132.                   (Math.Abs(param1) <= Tolerance.Global.EqualPoint
  133.                         || param1 > 0) &&
  134.                   (Math.Abs(param2) <= Tolerance.Global.EqualPoint
  135.                         || param2 > 0)
  136.                   )
  137.                   {
  138.                     reverseNeeded = true;
  139.                   }
  140.                 }
  141.                 ln.Dispose();
  142.                 // Reverse, if needed
  143.                 Vector3d norm;
  144.                 if (reverseNeeded)
  145.                 {
  146.                   norm = -plane.Normal;
  147.                 }
  148.                 else
  149.                 {
  150.                   norm = plane.Normal;
  151.                 }
  152.                 // Now we set the view based on the normal
  153.                 SetView(
  154.                   ed,
  155.                   norm,
  156.                   sol.GeometricExtents
  157.                 );
  158.               }
  159.             }
  160.           }
  161.         }
  162.         tr.Commit();
  163.       }
  164.     }
  165.     private static bool CheckContainment(
  166.       Editor ed,
  167.       Brep brp,
  168.       Point3d pt,
  169.       ref List<Curve3d> curves
  170.     )
  171.     {
  172.       bool res = false;
  173.       // Use the BRep API to get the lowest level
  174.       // container for the point
  175.       PointContainment pc;
  176.       BrepEntity be =
  177.         brp.GetPointContainment(pt, out pc);
  178.       using (be)
  179.       {
  180.         // Only if the point is on a boundary...
  181.         if (pc == PointContainment.OnBoundary)
  182.         {
  183.           // And only if the boundary is a face...
  184.           BrFace face = be as BrFace;
  185.           if (face != null)
  186.           {
  187.             // ... do we attempt to do something
  188.             try
  189.             {
  190.               foreach (BoundaryLoop bl in face.Loops)
  191.               {
  192.                 // We'll return a curve for each edge in
  193.                 // the containing loop
  194.                 foreach (Edge edge in bl.Edges)
  195.                 {
  196.                   curves.Add(edge.Curve);
  197.                 }
  198.               }
  199.               res = true;
  200.             }
  201.             catch (BrException)
  202.             {
  203.               res = false;
  204.             }
  205.           }
  206.         }
  207.       }
  208.       return res;
  209.     }
  210.     private void SetView(
  211.       Editor ed,
  212.       Vector3d viewDir,
  213.       Extents3d ext
  214.     )
  215.     {
  216.       // We do a two part zoom... one gets us in the right
  217.       // viewing direction
  218.       ViewTableRecord rec = new ViewTableRecord();
  219.       rec.IsPaperspaceView = false;
  220.       rec.ViewDirection = viewDir;
  221.       rec.CenterPoint = Point2d.Origin;
  222.       rec.ViewTwist = 0.0;
  223.       ed.SetCurrentView(rec);
  224.       // And the other does a Zoom Window
  225.       ZoomWin(
  226.         ed,
  227.         new Point2d(ext.MinPoint.X, ext.MinPoint.Y),
  228.         new Point2d(ext.MaxPoint.X, ext.MaxPoint.Y)
  229.       );
  230.     }
  231.     private static void ZoomWin(
  232.       Editor ed, Point2d min, Point2d max
  233.     )
  234.     {
  235.       string lower =
  236.         min.ToString().Substring(
  237.           1,
  238.           min.ToString().Length - 2
  239.         );
  240.       string upper =
  241.         max.ToString().Substring(
  242.           1,
  243.           max.ToString().Length - 2
  244.         );
  245.       string cmd =
  246.         "_.ZOOM _W " + lower + " " + upper + " ";
  247.       // Send the command(s)
  248.       SendQuietCommand(ed.Document, cmd);
  249.     }
  250.     #region QuietCommandCalling
  251.     const string kFinishCmd = "FINISH_COMMAND";
  252.     private static void SendQuietCommand(
  253.       Document doc,
  254.       string cmd
  255.     )
  256.     {
  257.       // Get the old value of NOMUTT
  258.       object nomutt =
  259.         Application.GetSystemVariable("NOMUTT");
  260.       // Add the string to reset NOMUTT afterwards
  261.       AcadDocument oDoc =
  262.         (AcadDocument)doc.AcadDocument;
  263.       oDoc.StartUndoMark();
  264.       cmd += "_" + kFinishCmd + " ";
  265.       // Set NOMUTT to 1, reducing cmd-line noise
  266.       Application.SetSystemVariable("NOMUTT", 1);
  267.       doc.SendStringToExecute(cmd, true, false, false);
  268.     }
  269.     [CommandMethod(kFinishCmd, CommandFlags.NoUndoMarker)]
  270.     static public void FinishCommand()
  271.     {
  272.       Document doc =
  273.         Application.DocumentManager.MdiActiveDocument;
  274.       AcadDocument oDoc =
  275.         (AcadDocument)doc.AcadDocument;
  276.       oDoc.EndUndoMark();
  277.       Application.SetSystemVariable("NOMUTT", 0);
  278.     }
  279.     #endregion
  280.   }
  281. }

Some comments on the implementation:
    * I've split the view change into two sections:
          o Change the view using ed.SetCurrentView() to have the right view direction
          o Use quiet command calling to Zoom Window on the face's extents
    * My ideal would be to have a smooth transition between the 3D views, showing the rotation of the model
          o I'm looking into ways to do this - SetCurrentView() doesn't support smooth view transitions, but I'm hoping that since the introduction of the ViewCube we have some other API of which I'm unaware that will fit the bill
Anyway, that's it for today. Please bear in mind the various caveats I've made about different approaches to solving this problem: while I don't usually like to post something using a sub-optimal technique, I think there are still pieces of the code that people will find of value.

 楼主| 发表于 2009-9-17 10:19:00 | 显示全部楼层
七、创建一个可编辑的三维实体
February 20, 2009
Creating an editable AutoCAD solid using .NET
This question came up recently on the AutoCAD .NET Discussion Group: how to create a Solid3d object which provides the user with the full set of grips to manipulate it (which I've abbreviated to "editable" in the title of this post :-). This comes down to a enhancements that were made in AutoCAD 2007 to allow better manipulation of solids via the user-interface via extended grips and a push-pull mechanism. These capabilities need to be enabled solids as you create them - and unfortunately cannot be retro-fitted to existing solid objects - by telling the solid that you would like it to record its history. And it's really that simple, you simply have to set the RecordHistory flag to true.
The below C# code demonstrates how this works, by exposing a command that prompts the user whether to set this flag to true, or not:
  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 SolidCreation
  7. {
  8.   public class Commands
  9.   {
  10.     [CommandMethod("CWH")]
  11.     public void CylinderWithHistory()
  12.     {
  13.       Document doc =
  14.         Application.DocumentManager.MdiActiveDocument;
  15.       Database db = doc.Database;
  16.       Editor ed = doc.Editor;
  17.       // Ask the user whether to create history
  18.       // and where to place the cylinder
  19.       bool createHistory = false;
  20.       PromptKeywordOptions pko =
  21.         new PromptKeywordOptions(
  22.           "\nRecord history for this cylinder?"
  23.         );
  24.       pko.AllowNone = true;
  25.       pko.Keywords.Add("Yes");
  26.       pko.Keywords.Add("No");
  27.       pko.Keywords.Default = "Yes";
  28.       PromptResult pkr =
  29.         ed.GetKeywords(pko);
  30.       if (pkr.Status != PromptStatus.OK)
  31.         return;
  32.       if (pkr.StringResult == "Yes")
  33.         createHistory = true;
  34.       PromptPointResult ppr =
  35.         ed.GetPoint("\nSelect point: ");
  36.       if (ppr.Status != PromptStatus.OK)
  37.         return;
  38.       Point3d pt = ppr.Value;
  39.       Transaction tr =
  40.         db.TransactionManager.StartTransaction();
  41.       using (tr)
  42.       {
  43.         // Create the solid and set the history flag
  44.         Solid3d sol = new Solid3d();
  45.         sol.RecordHistory = createHistory;
  46.         // Hardcode the dimensions of the cylinder
  47.         // for the purpose of this example
  48.         sol.CreateFrustum(10, 3, 3, 3);
  49.         // Add the Solid3d to the modelspace
  50.         BlockTable bt =
  51.           (BlockTable)tr.GetObject(
  52.             db.BlockTableId,
  53.             OpenMode.ForRead
  54.           );
  55.         BlockTableRecord ms =
  56.           (BlockTableRecord)tr.GetObject(
  57.             bt[BlockTableRecord.ModelSpace],
  58.             OpenMode.ForWrite
  59.           );
  60.         ms.AppendEntity(sol);
  61.         tr.AddNewlyCreatedDBObject(sol, true);
  62.         // And transform it to the selected point
  63.         sol.TransformBy(
  64.           Matrix3d.Displacement(pt - Point3d.Origin)
  65.         );
  66.         tr.Commit();
  67.       }
  68.     }
  69.   }
  70. }
Let's see the results of the CWH command - in this case we run it twice, selecting "No" to the question about history creation for the cylinder on the left and "Yes" to the same question for the cylinder on the right:

As pointed out in the thread on the discussion group, there are some limitations to the API around the history recording for solids: you cannot, for instance, programmatically manipulate the solid's history: it's only possible to create new solids that will then allow users to make use of editing operations that effect the history.
On a somewhat related note, back in this post I mentioned a hole in the .NET API around creating a swept solid. I've been informed by a friend in our Engineering team that we've now plugged it - and some other related holes - in the .NET API for AutoCAD 2010. You will now have the following methods available from the Solid3d class: CreateExtrudedSolid, CreateLoftedSolid, CreateRevolvedSolid and CreateSweptSolid (the equivalent .NET methods for Surfaces existed already, as well as the base C++ methods in ObjectARX). Thanks for the heads-up, Joel! :-)

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-9-17 10:30:00 | 显示全部楼层
本帖最后由 作者 于 2009-9-17 11:11:52 编辑

八、不规则建模
March 25, 2009
Free-form modeling in AutoCAD 2010 using .NET
A big thanks to Stephen Preston, who manages DevTech Americas and coordinates our worldwide AutoCAD workgroup as well as spending time working with the AutoCAD Engineering team (phew!), for providing this sample. Stephen originally put it together for our annual Developer Days tour late last year: I took the original sample, converted it from VB.NET to C# and made some minor changes to the code. The VB.NET version is available from the ADN website, in case.
The Free-Form Design feature in AutoCAD 2010 is one of the coolest enhancements to the product (I really like the Parametric Drawing feature, too, although as the API for that is currently C++-only it’s unfortunately going to get a little less air-time on this blog). This post looks at how to automate free-form design operations: starting with a traditional Solid3d object, converting it to a sub-division mesh (SubDMesh) which we then manipulate in a number of interesting ways before converting back to a Solid3d.
Here’s the C# code:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.Runtime;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.Colors;
  7. using System.Collections.Generic;
  8. using System;
  9. namespace FreeformModeling
  10. {
  11.   public class Commands
  12.   {
  13.     // Add a new mesh to the current space,
  14.     // setting it to a sphere primitive -
  15.     // no smoothing.
  16.     [CommandMethod("SPHM")]
  17.     public void SphericalMesh()
  18.     {
  19.       Document doc =
  20.         Application.DocumentManager.MdiActiveDocument;
  21.       Database db = doc.Database;
  22.       Transaction tr =
  23.         doc.TransactionManager.StartTransaction();
  24.       using (tr)
  25.       {
  26.         BlockTableRecord btr =
  27.           (BlockTableRecord)tr.GetObject(
  28.             db.CurrentSpaceId,
  29.             OpenMode.ForWrite
  30.           );
  31.         SubDMesh mySubD = new SubDMesh();
  32.         mySubD.SetSphere(20, 8, 8, 0);
  33.         mySubD.SetDatabaseDefaults();
  34.         btr.AppendEntity(mySubD);
  35.         tr.AddNewlyCreatedDBObject(mySubD, true);
  36.         tr.Commit();
  37.       }
  38.     }
  39.     // Make clone of solid, inflate it by a fraction of its
  40.     // extents, create a mesh from the clone, erase the clone.
  41.     private ObjectId cloneAndInflateSolid(
  42.       Transaction tr, ObjectId solidId
  43.     )
  44.     {
  45.       // Now copy the solid
  46.       Document doc =
  47.         Application.DocumentManager.MdiActiveDocument;
  48.       Database db = doc.Database;
  49.       Editor ed = doc.Editor;
  50.       ObjectId newId;
  51.       // Inflate solid by user specified fraction
  52.       // (0.05 works well with sample drawing)
  53.       PromptDoubleOptions pdo =
  54.         new PromptDoubleOptions(
  55.           "\nSpecify inflation as fraction of bounds:"
  56.         );
  57.       PromptDoubleResult pdr =
  58.         ed.GetDouble(pdo);
  59.       // Return if something went wrong.
  60.       if (pdr.Status != PromptStatus.OK)
  61.         return ObjectId.Null;
  62.       double frac = pdr.Value;
  63.       ObjectIdCollection ids = new ObjectIdCollection();
  64.       ids.Add(solidId);
  65.       IdMapping im = new IdMapping();
  66.       db.DeepCloneObjects(ids, db.CurrentSpaceId, im, false);
  67.       newId = im.Lookup(solidId).Value;
  68.       // We now have the ObjectID of the newly cloned
  69.       // solid in newId
  70.       // Inflate by length of diagonal vector across bounds
  71.       // multiplied by inflation fraction
  72.       Solid3d sol =
  73.         (Solid3d)tr.GetObject(newId, OpenMode.ForWrite);
  74.       Extents3d ext = (Extents3d)sol.Bounds;
  75.       Vector3d vec = ext.MaxPoint - ext.MinPoint;
  76.       sol.OffsetBody(vec.Length * frac);
  77.       // Define params governing mesh generation algorithm
  78.       // (See ObjectARX helpfiles for explanation of params)
  79.       MeshFaceterData fd =
  80.         new MeshFaceterData(
  81.           0.01 * vec.Length,
  82.           40 * Math.PI / 180,
  83.           2, 2, 15, 5, 5, 0
  84.         );
  85.       // Get the mesh data from the solid
  86.       // (with smoothing level 1).
  87.       MeshDataCollection mdc =
  88.         SubDMesh.GetObjectMesh(sol, fd);
  89.       // Create the mesh defined by this data.
  90.       SubDMesh sd = new SubDMesh();
  91.       sd.SetDatabaseDefaults();
  92.       sd.SetSubDMesh(mdc.VertexArray, mdc.FaceArray, 1);
  93.       // Add the mesh to database.
  94.       BlockTableRecord btr =
  95.         (BlockTableRecord)tr.GetObject(
  96.           db.CurrentSpaceId,
  97.           OpenMode.ForWrite
  98.         );
  99.       newId = btr.AppendEntity(sd);
  100.       tr.AddNewlyCreatedDBObject(sd, true);
  101.       // Erase the cloned solid
  102.       sol.Erase();
  103.       return newId;
  104.     }
  105.     // Add creases to user selected faces
  106.     // Returns true if successful
  107.     private bool addCreases(
  108.       Transaction tr, ObjectId meshId
  109.     )
  110.     {
  111.       Document doc =
  112.         Application.DocumentManager.MdiActiveDocument;
  113.       PromptSelectionOptions pso =
  114.         new PromptSelectionOptions();
  115.       pso.ForceSubSelections = true;
  116.       pso.AllowDuplicates = false;
  117.       pso.MessageForAdding = "\nSelect edges to crease: ";
  118.       PromptSelectionResult selRes =
  119.         doc.Editor.GetSelection(pso);
  120.       // If the user didn't make valid selection, we return
  121.       if (selRes.Status != PromptStatus.OK)
  122.         return false;
  123.       SelectionSet ss = selRes.Value;
  124.       // They may have picked more than one object - we need
  125.       // the subobjects of the object with the right ObjectId
  126.       SelectedObject so = null;
  127.       foreach (SelectedObject o in ss)
  128.       {
  129.         if (o.ObjectId == meshId)
  130.           so = o;
  131.       }
  132.       // If they picked on the wrong object, then just don't
  133.       // add any extrusions
  134.       if (so == null)
  135.         return false;
  136.       // We got to here so selection was valid
  137.       // This will store selected edges
  138.       List<FullSubentityPath> creasePaths =
  139.         new List<FullSubentityPath>();
  140.       DBObject obj =
  141.         tr.GetObject(so.ObjectId, OpenMode.ForRead);
  142.       SubDMesh sd = obj as SubDMesh;
  143.       if (sd != null)
  144.       {
  145.         sd.UpgradeOpen();
  146.         // Add all selected subentities to the collection
  147.         SelectedSubObject[] subEnts =
  148.           so.GetSubentities();
  149.         foreach (SelectedSubObject subEnt in subEnts)
  150.         {
  151.           if (subEnt.FullSubentityPath.SubentId.Type
  152.               == SubentityType.Edge)
  153.             creasePaths.Add(subEnt.FullSubentityPath);
  154.         }
  155.         // Add infinite creases to all those edges
  156.         // -1 means 'always' crease
  157.         sd.SetCrease(creasePaths.ToArray(), -1);
  158.         return true;
  159.       }
  160.       return false;
  161.     }
  162.     // Extrude each mesh face by a random amount, up to a
  163.     // user specified maximum. Returns true if successful.
  164.     private bool extrudeFaces(
  165.       Transaction tr, ObjectId meshId
  166.     )
  167.     {
  168.       Document doc =
  169.         Application.DocumentManager.MdiActiveDocument;
  170.       // Now we want to stretch a few faces to add a bit of
  171.       // randomness. Better just stretch outwards.
  172.       // Prompt for max random bump (0.02 works well for
  173.       // sample drawing).
  174.       PromptDoubleOptions pdo =
  175.         new PromptDoubleOptions(
  176.           "\nSpecify bump size as fraction of bounds: "
  177.         );
  178.       PromptDoubleResult pdr =
  179.         doc.Editor.GetDouble(pdo);
  180.       // If user cancels value entry, then we return.
  181.       if (pdr.Status != PromptStatus.OK)
  182.         return false;
  183.       double extrudelen = pdr.Value;
  184.       PromptSelectionOptions pso =
  185.         new PromptSelectionOptions();
  186.       pso.ForceSubSelections = true;
  187.       pso.MessageForAdding = "\nSelect faces to bump: ";
  188.       PromptSelectionResult psr =
  189.         doc.Editor.GetSelection(pso);
  190.       // If user cancels selection, then we assume they want
  191.       // to continue, but that they don't want to extrude any
  192.       // surfaces
  193.       if (psr.Status != PromptStatus.OK)
  194.         return false;
  195.       SelectionSet ss = psr.Value;
  196.       // They may have picked more than one object - we need
  197.       // the subobjects of the object with the right ObjectId
  198.       SelectedObject so = null;
  199.       foreach (SelectedObject o in ss)
  200.       {
  201.         if (o.ObjectId == meshId)
  202.           so = o;
  203.       }
  204.       if (so == null)
  205.         return false;
  206.       // If they picked on the wrong object, then just don't
  207.       // add any extrusions
  208.       // We got to here so selection was valid
  209.       // Stores faces we selected for random extrusion
  210.       List<FullSubentityPath> bumpPaths =
  211.         new List<FullSubentityPath>();
  212.       DBObject obj =
  213.         tr.GetObject(so.ObjectId, OpenMode.ForRead);
  214.       SubDMesh sd = obj as SubDMesh;
  215.       if (sd != null)
  216.       {
  217.         sd.UpgradeOpen();
  218.         SelectedSubObject[] subEnts =
  219.           so.GetSubentities();
  220.         // We'll be extruding faces by a random amount, so
  221.         // instantiate random number generator here ready
  222.         // to use inside loop
  223.         Random rnd = new Random();
  224.         // Process each selected face in turn
  225.         foreach (SelectedSubObject subEnt in subEnts)
  226.         {
  227.           // Is it a face?
  228.           if (subEnt.FullSubentityPath.SubentId.Type
  229.               == SubentityType.Face)
  230.           {
  231.             FullSubentityPath[] faces =
  232.               new FullSubentityPath[1];
  233.             faces[0] = subEnt.FullSubentityPath;
  234.             // Find normal for this face
  235.             Plane fPlane =
  236.               sd.GetFacePlane(faces[0].SubentId);
  237.             Vector3d norm = fPlane.Normal;
  238.             // Get length of diagonal across bounds to use
  239.             // in calculating bump scale
  240.             Extents3d ext = (Extents3d)sd.Bounds;
  241.             Vector3d vec = ext.MaxPoint - ext.MinPoint;
  242.             Matrix3d mat =
  243.               Matrix3d.Displacement(
  244.                 norm * 0.5 * extrudelen *
  245.                 rnd.NextDouble() * vec.Length
  246.               );
  247.             sd.TransformSubentityPathsBy(faces, mat);
  248.           }
  249.         }
  250.         return true;
  251.       }
  252.       return false;
  253.     }
  254.     // Set random color for each mesh face.
  255.     private void colorFaces(
  256.       Transaction tr, ObjectId meshId
  257.     )
  258.     {
  259.       Random rnd = new Random();
  260.       DBObject obj =
  261.         tr.GetObject(meshId, OpenMode.ForRead);
  262.       SubDMesh sd = obj as SubDMesh;
  263.       if (sd != null)
  264.       {
  265.         sd.UpgradeOpen();
  266.         // Iterate all faces and set to a random color
  267.         for (int i = 0; i <= sd.NumberOfFaces - 1; i++)
  268.         {
  269.           SubentityId sId =
  270.             new SubentityId(SubentityType.Face, i);
  271.           byte[] rgb = new byte[3];
  272.           rnd.NextBytes(rgb);
  273.           Color col =
  274.             Color.FromRgb(rgb[0], rgb[1], rgb[2]);
  275.           sd.SetSubentColor(sId, col);
  276.         }
  277.       }
  278.     }
  279.     private ObjectId convertToSolid(
  280.       Transaction tr, ObjectId meshId
  281.     )
  282.     {
  283.       Document doc =
  284.         Application.DocumentManager.MdiActiveDocument;
  285.       Database db = doc.Database;
  286.       Editor ed = doc.Editor;
  287.       ObjectId newId = ObjectId.Null;
  288.       DBObject obj =
  289.         tr.GetObject(meshId, OpenMode.ForRead);
  290.       SubDMesh sd = obj as SubDMesh;
  291.       if (sd != null)
  292.       {
  293.         sd.UpgradeOpen();
  294.         // ConvertToSolid will throw an exception if
  295.         // it can't create a solid from the mesh.
  296.         Solid3d sol = null;
  297.         try
  298.         {
  299.           sol = sd.ConvertToSolid(true, true);
  300.         }
  301.         catch
  302.         {
  303.           ed.WriteMessage(
  304.             "\nMesh was too complex to turn into a solid."
  305.           );
  306.         }
  307.         BlockTableRecord btr =
  308.           (BlockTableRecord)tr.GetObject(
  309.             db.CurrentSpaceId,
  310.             OpenMode.ForWrite
  311.           );
  312.         newId = btr.AppendEntity(sol);
  313.         tr.AddNewlyCreatedDBObject(sol, true);
  314.       }
  315.       return newId;
  316.     }
  317.     // Translates entity along x-axis by factor multiplied by
  318.     // the entity's extent along the x-axis.
  319.     private void translateEntity(
  320.       Transaction tr, ObjectId objId, double factor
  321.     )
  322.     {
  323.       Entity ent =
  324.         (Entity)tr.GetObject(
  325.           objId,
  326.           OpenMode.ForWrite
  327.         );
  328.       Extents3d ext = (Extents3d)ent.Bounds;
  329.       Point3d pt1 =
  330.         new Point3d(ext.MaxPoint.X, ext.MinPoint.Y, ext.MinPoint.Z);
  331.       Vector3d transVec =
  332.         factor * (pt1 - ext.MinPoint);
  333.       Matrix3d mat = Matrix3d.Displacement(transVec);
  334.       ent.TransformBy(mat);
  335.     }
  336.     // Creates a SubDMesh based on an 'inflated' clone of the
  337.     // selected solid. The user then gets to add creases and
  338.     // select which faces will be given a random extrusion.
  339.     // It colors each face of the mesh and then converts it to
  340.     // a smooth solid.
  341.     [CommandMethod("CC")]
  342.     public void CreateCasing()
  343.     {
  344.       Document doc =
  345.         Application.DocumentManager.MdiActiveDocument;
  346.       Editor ed = doc.Editor;
  347.       ObjectId meshId;
  348.       // First select the solid (just one)
  349.       PromptEntityOptions peo =
  350.         new PromptEntityOptions("\nSelect a solid: ");
  351.       peo.SetRejectMessage("\nObject must be a 3D solid.");
  352.       peo.AddAllowedClass(typeof(Solid3d), false);
  353.       PromptEntityResult per = ed.GetEntity(peo);
  354.       if (per.Status != PromptStatus.OK)
  355.         return;
  356.       Transaction tr =
  357.         doc.TransactionManager.StartTransaction();
  358.       using (tr)
  359.       {
  360.         meshId =
  361.           cloneAndInflateSolid(tr, per.ObjectId);
  362.         // If the above call returned a null ObjectId,
  363.         // then something went wrong - we need to stop now
  364.         if (meshId == ObjectId.Null)
  365.           return;
  366.         // Now we have our original solid and the mesh
  367.         // created from the cloned solid
  368.         // Add creases
  369.         if (!addCreases(tr, meshId))
  370.           return;
  371.         // Randomly extrude faces
  372.         if (!extrudeFaces(tr, meshId))
  373.           return;
  374.         // Give faces random colors
  375.         colorFaces(tr, meshId);
  376.         // Convert the mesh to a solid.
  377.         ObjectId solId =
  378.           convertToSolid(tr, meshId);
  379.         // Move the entities so they don't sit on
  380.         // top of one other
  381.         translateEntity(tr, meshId, 1.5);
  382.         translateEntity(tr, solId, 3);
  383.         doc.TransactionManager.QueueForGraphicsFlush();
  384.         tr.Commit();
  385.       }
  386.     }
  387.   }
  388. }
Here’s a sample 3D model – consisting of a single, composite 3D solid:

Here’s what happens when we run the CC command, selecting this solid and applying the recommended values (0.2 – i.e. 20% – for the inflation percentage and 0.05 – i.e. 5% – for the maximum random bump). I selected a number of edges and faces at random after which the code assigned random colours to all the faces, so please don’t get frustrated trying to reproduce these exact results. :-)

The objects are (going from right to left):
   1. The original solid.
   2. A sub-division mesh created by inflating the original solid and applying creases to certain edges and extruding certain faces.
   3. A traditional AutoCAD solid created from the sub-division mesh.
The best way to understand the overall functionality of the application is to scan the CC command – defined by the CreateCasing function found at the bottom of the listing – and then looking into individual functions it uses. The application also defines another command – SPHM – which shows how to create a simple, spherical sub-division mesh.

本帖子中包含更多资源

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

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

本版积分规则

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

GMT+8, 2024-11-25 18:50 , Processed in 0.230264 second(s), 25 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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