明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 13283|回复: 11

[Kean专集] Kean专题(3)—Graphics_System

   关闭 [复制链接]
发表于 2009-5-16 10:24 | 显示全部楼层 |阅读模式
本帖最后由 作者 于 2009-5-16 11:16:38 编辑

原帖:http://through-the-interface.typepad.com/through_the_interface/graphics_system/
一、使用NetApi渲染Cad模型
April 05, 2007
Rendering AutoCAD models offscreen using .NET
这个问题是在内部讨论,我想分享的代码提供了我们的技术团队与您(当然我们有一些小的补充)。
This question came up in an internal discussion, and I thought I'd share the code provided by our Engineering team with you (with a few minor additions from my side, of course).
这个想法是提供一个三维场景离屏(即保存到文件所提供的不可见的图像编辑器) 。The idea is to render a 3D scene off-screen (i.e. save to file the rendered image not visible in the editor). In the below code, we do zoom to the extents of the 3D model that gets created, just to show it's there, but the rendering activity itself is not performed by the 3D view that is used to generate the graphics for AutoCAD's editor window.
A 3D model does need to be loaded in the editor for this to work, so I decided to add a simple sphere to the modelspace and set its material to one that renders nicely (Sitework.Paving - Surfacing.Riverstone.Mortared). The code doesn't import the material programmatically, so you'll want to make sure it's there in the drawing to get the full effect of the rendering (by dragging the material into the drawing view from the materials tool palette, for instance), or to change the code to point to one that exists in the active drawing.
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.GraphicsSystem;
  6. using Autodesk.AutoCAD.Interop;
  7. using System.Drawing;
  8. namespace OffscreenRendering
  9. {
  10.   public class Commands
  11.   {
  12.     [CommandMethod("OSR")]
  13.     static public void OffscreenRender()
  14.     {
  15.       CreateSphere();
  16.       RenderToFile("c:\\sphere.png");
  17.     }
  18.     static public void CreateSphere()
  19.     {
  20.       Document doc =
  21.         Application.DocumentManager.MdiActiveDocument;
  22.       Database db = doc.Database;
  23.       Editor ed = doc.Editor;
  24.       Transaction tr =
  25.         doc.TransactionManager.StartTransaction();
  26.       using (tr)
  27.       {
  28.         BlockTable bt =
  29.           (BlockTable)tr.GetObject(
  30.             db.BlockTableId,
  31.             OpenMode.ForRead
  32.           );
  33.         BlockTableRecord btr =
  34.           (BlockTableRecord)tr.GetObject(
  35.             bt[BlockTableRecord.ModelSpace],
  36.             OpenMode.ForWrite
  37.           );
  38.         Solid3d sol = new Solid3d();
  39.         sol.CreateSphere(10.0);
  40.         const string matname =
  41.           "Sitework.Paving - Surfacing.Riverstone.Mortared";
  42.         DBDictionary matdict =
  43.           (DBDictionary)tr.GetObject(
  44.             db.MaterialDictionaryId,
  45.             OpenMode.ForRead
  46.           );
  47.         if (matdict.Contains(matname))
  48.         {
  49.           sol.Material = matname;
  50.         }
  51.         else
  52.         {
  53.           ed.WriteMessage(
  54.             "\nMaterial (" + matname + ") not found" +
  55.             " - sphere will be rendered without it.",
  56.             matname
  57.           );
  58.         }
  59.         btr.AppendEntity(sol);
  60.         tr.AddNewlyCreatedDBObject(sol, true);
  61.         tr.Commit();
  62.       }
  63.       AcadApplication acadApp =
  64.         (AcadApplication)Application.AcadApplication;
  65.       acadApp.ZoomExtents();
  66.     }
  67.     static public void RenderToFile(string filename)
  68.     {
  69.       Document doc =
  70.         Application.DocumentManager.MdiActiveDocument;
  71.       Editor ed = doc.Editor;
  72.       int vpn =
  73.         System.Convert.ToInt32(
  74.           Application.GetSystemVariable("CVPORT")
  75.         );
  76.       View gsv =
  77.         doc.GraphicsManager.GetGsView(vpn, true);
  78.       using (View view = gsv.Clone(true, true))
  79.       {
  80.         Device dev =
  81.           doc.GraphicsManager.CreateAutoCADOffScreenDevice();
  82.         using (dev)
  83.         {
  84.           dev.OnSize(doc.GraphicsManager.DisplaySize);
  85.           dev.DeviceRenderType = RendererType.FullRender;
  86.           dev.Add(view);
  87.           using (Bitmap bitmap = view.RenderToImage())
  88.           {
  89.             bitmap.Save(filename);
  90.             ed.WriteMessage(
  91.               "\nRendered image saved to: " +
  92.               filename
  93.             );
  94.           }
  95.         }
  96.       }
  97.     }
  98.   }
  99. }
And here's the resized contents of the file created at c:\sphere.png by the OSR command:

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-5-16 10:46 | 显示全部楼层
二、模型快照
April 18, 2007
Taking a snapshot of the AutoCAD model (take 2)
In this previous post, we looked at some code to do a programmatic snapshot of AutoCAD's modelspace, saving the results to an image file.
From the discussion that followed, I realised that the code had an undesired (and unnecessary) side-effect of creating a new 3D GS View and leaving the modelspace with that view active. GS Views in AutoCAD 2007 have grey backgrounds by default, and so this change can be quite disturbing for users. The only reason we created the GS View in the first place (if one didn't already exist), was to use it to query the view position/target/up vector/field width and height and apply it to our new view. Thankfully it seems this can also be determined directly from the viewport.
So rather than calling GetGSView() and using the returned view to get that information, we now simply call SetViewFromViewport() specifying the viewport number held in CVPORT, and the graphics system manager for that document handles the rest.
Here's the updated C# code, which appears to achieve the same goals without the side-effect. Check line 124 for the new code, a few extraneous lines around it having been removed:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.GraphicsInterface;
  5. using Autodesk.AutoCAD.GraphicsSystem;
  6. using Autodesk.AutoCAD.Runtime;
  7. using Autodesk.AutoCAD.Interop;
  8. using System.Drawing;
  9. namespace OffscreenImageCreation
  10. {
  11.   public class Commands
  12.   {
  13.     [CommandMethod("OSS")]
  14.     static public void OffscreenSnapshot()
  15.     {
  16.       CreateSphere();
  17.       SnapshotToFile(
  18.         "c:\\sphere-Wireframe2D.png",
  19.         VisualStyleType.Wireframe2D
  20.       );
  21.       SnapshotToFile(
  22.         "c:\\sphere-Hidden.png",
  23.         VisualStyleType.Hidden
  24.       );
  25.       SnapshotToFile(
  26.         "c:\\sphere-Basic.png",
  27.         VisualStyleType.Basic
  28.       );
  29.       SnapshotToFile(
  30.         "c:\\sphere-ColorChange.png",
  31.         VisualStyleType.ColorChange
  32.       );
  33.       SnapshotToFile(
  34.         "c:\\sphere-Conceptual.png",
  35.         VisualStyleType.Conceptual
  36.       );
  37.       SnapshotToFile(
  38.         "c:\\sphere-Flat.png",
  39.         VisualStyleType.Flat
  40.       );
  41.       SnapshotToFile(
  42.         "c:\\sphere-Gouraud.png",
  43.         VisualStyleType.Gouraud
  44.       );
  45.       SnapshotToFile(
  46.         "c:\\sphere-Realistic.png",
  47.         VisualStyleType.Realistic
  48.       );
  49.     }
  50.     static public void CreateSphere()
  51.     {
  52.       Document doc =
  53.         Application.DocumentManager.MdiActiveDocument;
  54.       Database db = doc.Database;
  55.       Editor ed = doc.Editor;
  56.       Transaction tr =
  57.         doc.TransactionManager.StartTransaction();
  58.       using (tr)
  59.       {
  60.         BlockTable bt =
  61.           (BlockTable)tr.GetObject(
  62.             db.BlockTableId,
  63.             OpenMode.ForRead
  64.           );
  65.         BlockTableRecord btr =
  66.           (BlockTableRecord)tr.GetObject(
  67.             bt[BlockTableRecord.ModelSpace],
  68.             OpenMode.ForWrite
  69.           );
  70.         Solid3d sol = new Solid3d();
  71.         sol.CreateSphere(10.0);
  72.         const string matname =
  73.           "Sitework.Paving - Surfacing.Riverstone.Mortared";
  74.         DBDictionary matdict =
  75.           (DBDictionary)tr.GetObject(
  76.             db.MaterialDictionaryId,
  77.             OpenMode.ForRead
  78.           );
  79.         if (matdict.Contains(matname))
  80.         {
  81.           sol.Material = matname;
  82.         }
  83.         else
  84.         {
  85.           ed.WriteMessage(
  86.             "\nMaterial (" + matname + ") not found" +
  87.             " - sphere will be rendered without it.",
  88.             matname
  89.           );
  90.         }
  91.         btr.AppendEntity(sol);
  92.         tr.AddNewlyCreatedDBObject(sol, true);
  93.         tr.Commit();
  94.       }
  95.       AcadApplication acadApp =
  96.         (AcadApplication)Application.AcadApplication;
  97.       acadApp.ZoomExtents();
  98.     }
  99.     static public void SnapshotToFile(
  100.       string filename,
  101.       VisualStyleType vst
  102.     )
  103.     {
  104.       Document doc =
  105.         Application.DocumentManager.MdiActiveDocument;
  106.       Editor ed = doc.Editor;
  107.       Database db = doc.Database;
  108.       Manager gsm = doc.GraphicsManager;
  109.       // Get some AutoCAD system variables
  110.       int vpn =
  111.         System.Convert.ToInt32(
  112.           Application.GetSystemVariable("CVPORT")
  113.         );
  114.       using (View view = new View())
  115.       {
  116.         gsm.SetViewFromViewport(view, vpn);
  117.         // Set the visual style to the one passed in
  118.         view.VisualStyle = new VisualStyle(vst);
  119.         Device dev =
  120.           gsm.CreateAutoCADOffScreenDevice();
  121.         using (dev)
  122.         {
  123.           dev.OnSize(gsm.DisplaySize);
  124.           // Set the render type and the background color
  125.           dev.DeviceRenderType = RendererType.Default;
  126.           dev.BackgroundColor = Color.White;
  127.           // Add the view to the device and update it
  128.           dev.Add(view);
  129.           dev.Update();
  130.           using (Model model = gsm.CreateAutoCADModel())
  131.           {
  132.             Transaction tr =
  133.               db.TransactionManager.StartTransaction();
  134.             using (tr)
  135.             {
  136.               // Add the modelspace to the view
  137.               // It's a container but also a drawable
  138.               BlockTable bt =
  139.                 (BlockTable)tr.GetObject(
  140.                   db.BlockTableId,
  141.                   OpenMode.ForRead
  142.                 );
  143.               BlockTableRecord btr =
  144.                 (BlockTableRecord)tr.GetObject(
  145.                   bt[BlockTableRecord.ModelSpace],
  146.                   OpenMode.ForRead
  147.                 );
  148.               view.Add(btr, model);
  149.               tr.Commit();
  150.             }
  151.             // Take the snapshot
  152.             Rectangle rect = view.Viewport;
  153.             using (Bitmap bitmap = view.GetSnapshot(rect))
  154.             {
  155.               bitmap.Save(filename);
  156.               ed.WriteMessage(
  157.                 "\nSnapshot image saved to: " +
  158.                 filename
  159.               );
  160.               // Clean up
  161.               view.EraseAll();
  162.               dev.Erase(view);
  163.             }
  164.           }
  165.         }
  166.       }
  167.     }
  168.   }
  169. }
And here are the images created by the OSS command, listed by their visual style type...

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-5-16 10:50 | 显示全部楼层
三、使用Zoom
June 06, 2008
Zooming to a window or entity inside AutoCAD with .NET
This post was inspired by a recent email from Sreekar Devatha, who is just about to leave DevTech India to work in one of our Engineering teams in Singapore (all the best on your new adventure, Sreekar! :-).
It shows how to perform a programmatic zoom in .NET, whether to a window or to an entity. The .NET API in AutoCAD doesn't expose a handy "ZoomWindow" method, but there are a few options open that use officially supported APIs:
Create a ViewTableRecord and set it as the current view (the classic ObjectARX technique, as described in this DevNote on the ADN site)
Use the COM API to perform a ZoomWindow
Call the ZOOM command using one of the techniques in this previous post
The first two change the view very effectively, but don't show the nice view transitions you get with the ZOOM command (hence the 3rd option).
I ended up implementing the following C# code to show each of these techniques, which can be applied to commands that zoom either to a window (ZW) or to an entity (ZE). Just change the functions used from ZoomWin() to ZoomWin2() or ZoomWin3(), as needed:
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.Interop;
  7. namespace ZoomZoom
  8. {
  9.   public class Commands
  10.   {
  11.     // Zoom to a window specified by the user
  12.     [CommandMethod("ZW")]
  13.     static public void ZoomWindow()
  14.     {
  15.       Document doc =
  16.         Application.DocumentManager.MdiActiveDocument;
  17.       Database db = doc.Database;
  18.       Editor ed = doc.Editor;
  19.       // Get the window coordinates
  20.       PromptPointOptions ppo =
  21.         new PromptPointOptions(
  22.           "\nSpecify first corner:"
  23.         );
  24.       PromptPointResult ppr =
  25.         ed.GetPoint(ppo);
  26.       if (ppr.Status != PromptStatus.OK)
  27.         return;
  28.       Point3d min = ppr.Value;
  29.       PromptCornerOptions pco =
  30.         new PromptCornerOptions(
  31.           "\nSpecify opposite corner: ",
  32.           ppr.Value
  33.         );
  34.       ppr = ed.GetCorner(pco);
  35.       if (ppr.Status != PromptStatus.OK)
  36.         return;
  37.       Point3d max = ppr.Value;
  38.       // Call out helper function
  39.       // [Change this to ZoomWin2 or WoomWin3 to
  40.       // use different zoom techniques]
  41.       ZoomWin(ed, min, max);
  42.     }
  43.     // Zoom to the extents of an entity
  44.     [CommandMethod("ZE")]
  45.     static public void ZoomToEntity()
  46.     {
  47.       Document doc =
  48.         Application.DocumentManager.MdiActiveDocument;
  49.       Database db = doc.Database;
  50.       Editor ed = doc.Editor;
  51.       // Get the entity to which we'll zoom
  52.       PromptEntityOptions peo =
  53.         new PromptEntityOptions(
  54.           "\nSelect an entity:"
  55.         );
  56.       PromptEntityResult per = ed.GetEntity(peo);
  57.       if (per.Status != PromptStatus.OK)
  58.         return;
  59.       // Extract its extents
  60.       Extents3d ext;
  61.       Transaction tr =
  62.         db.TransactionManager.StartTransaction();
  63.       using (tr)
  64.       {
  65.         Entity ent =
  66.           (Entity)tr.GetObject(
  67.             per.ObjectId,
  68.             OpenMode.ForRead
  69.           );
  70.         ext =
  71.           ent.GeometricExtents;
  72.         tr.Commit();
  73.       }
  74.       ext.TransformBy(
  75.         ed.CurrentUserCoordinateSystem.Inverse()
  76.       );
  77.       // Call our helper function
  78.       // [Change this to ZoomWin2 or WoomWin3 to
  79.       // use different zoom techniques]
  80.       ZoomWin(ed, ext.MinPoint, ext.MaxPoint);
  81.     }
  82.     // Helper functions to zoom using different techniques
  83.     // Zoom using a view object
  84.     private static void ZoomWin(
  85.       Editor ed, Point3d min, Point3d max
  86.     )
  87.     {
  88.       Point2d min2d = new Point2d(min.X, min.Y);
  89.       Point2d max2d = new Point2d(max.X, max.Y);
  90.       ViewTableRecord view =
  91.         new ViewTableRecord();
  92.       view.CenterPoint =
  93.         min2d + ((max2d - min2d) / 2.0);
  94.       view.Height = max2d.Y - min2d.Y;
  95.       view.Width = max2d.X - min2d.X;
  96.       ed.SetCurrentView(view);
  97.     }
  98.     // Zoom via COM
  99.     private static void ZoomWin2(
  100.       Editor ed, Point3d min, Point3d max
  101.     )
  102.     {
  103.       AcadApplication app =
  104.         (AcadApplication)Application.AcadApplication;
  105.       double[] lower =
  106.         new double[3] { min.X, min.Y, min.Z };
  107.       double[] upper
  108.         = new double[3] { max.X, max.Y, max.Z };
  109.       app.ZoomWindow(lower, upper);
  110.     }
  111.     // Zoom by sending a command
  112.     private static void ZoomWin3(
  113.       Editor ed, Point3d min, Point3d max
  114.     )
  115.     {
  116.       string lower =
  117.         min.ToString().Substring(
  118.           1,
  119.           min.ToString().Length - 2
  120.         );
  121.       string upper =
  122.         max.ToString().Substring(
  123.           1,
  124.           max.ToString().Length - 2
  125.         );
  126.       string cmd =
  127.         "_.ZOOM _W " + lower + " " + upper + " ";
  128.       // Call the command synchronously using COM
  129.       AcadApplication app =
  130.         (AcadApplication)Application.AcadApplication;
  131.       app.ActiveDocument.SendCommand(cmd);
  132.       // Could also use async command calling:
  133.       //ed.Document.SendStringToExecute(
  134.       //  cmd, true, false, true
  135.       //);
  136.     }
  137.   }
  138. }
The code isn't as complete as I'd like: I haven't coded for all the view parameters (which are addressed in the DevNote quoted above in item 1, for those of you who are ADN members and can understand ObjectARX), and haven't tested for handling views not-aligned with the current UCS (etc., etc.). My main purpose was to outline the techniques, rather than to provide an exhaustive solution (and I'm currently on the train back from Geneva airport, having just flown to California and back for 3 days in the San Rafael office, which means any attempt on my side to make this exhaustive might end up exhausting me :-).

 楼主| 发表于 2009-5-16 10:55 | 显示全部楼层

四、.Net版本的BlockView例子

June 13, 2008
AutoCAD .NET version of the ObjectARX BlockView sample
Fenton Webb, from DevTech Americas, has been beavering away on a .NET port of the ObjectARX BlockView sample (found on the ObjectARX SDK under samples/graphics/BlockView). Thanks, Fents! :-)

Here is the C# source project for Visual Studio 2005. To build it you will almost certainly need to remap the acdbmgd.dll and acmgd.dll assembly references in the project.

One important note: if you load this project and try to view the BlockViewDialog in the Visual Studio Designer, the Visual Studio application will almost certainly crash. This is because the Designer is attempting to load into the dialog the GsPreviewCtrl component which depends on AutoCAD (and therefore cannot be instantiated outside of the AutoCAD process). At least that's what appears to be happening.

To temporarily allow the dialog to load - so you can make changes to the dialog's menu, etc. - you will need to edit the C# code in the BlockViewDialog.Designer.cs file. There are two edits that are needed - one that declares a variable for the control (lines 650-651) and one that instantiates it (lines 107-108):

Comment out line 650 and uncomment line 651, so...

  650     private GsPreviewCtrl mPreviewCtrl;

  651     //private System.Windows.Forms.Panel mPreviewCtrl;

... becomes...

  650     //private GsPreviewCtrl mPreviewCtrl;

  651     private System.Windows.Forms.Panel mPreviewCtrl;

And do the same for lines 107 and 108, so...

  107       //this.mPreviewCtrl = new System.Windows.Forms.Panel();

  108       this.mPreviewCtrl = new GsPreviewCtrl();

... becomes...

  107       this.mPreviewCtrl = new System.Windows.Forms.Panel();

  108       //this.mPreviewCtrl = new GsPreviewCtrl();

Then the dialog should load properly in the Visual Studio Designer, with no crash (although you'll need to change the code back to the way it was to build the application, of course).

As for what the application does, here's the original sample's ReadMe, updated to reflect the new application's behaviour:

------------------------

Block View Sample Readme

------------------------

- NETLOAD the BlockView.NET.dll module

- Run command BView at the AutoCAD command line.

- Default operation is to display the current drawing using the current view settings of the current drawing.

Function Descriptions

---------------------

File->Open

Opens an existing drawing into the Block View dialog (by clearing the GraphicsSystem.View).

File->Output Image to Disk

Allows you to output a JPG;BMP;TIFFNG files as a snaphot of the current view shown in the BlockView dialog.

File->AcGsDevice Config

View or edit the current GraphicsSystem.Device configuration settings.

View->Zoom

Allows Zoom Extents/Zoom Window/Zoom In-Out functionality

View->Settings->Show

Allows the toggling of various GraphicsSystem.View settings such as Linetype/Material and Sectioning

View->Render Mode

Allows you to switch the rendering mode.

View->View Style

Allows you to change the view style of the GraphicsSystem.View. It has the same options as the SHADEMODE command.

Tools->ReMap Colors->Custom

Allows you to remap the color palette. This is particularily useful if you want to show a GraphicsSystem.View as a paperspace layout, with a white background. In this case you will need to remap white entities to appear in a different color.

Tools->ReMap Colors->Standard Colors

Restores the Color Palette back to the original one.

Tools->Add an Entity->To This Database

Adds an entity to the Model Space and adds to the GraphicsSystem.View.

Tools->Add an Entity->Temporary

Adds an entity just to the GraphicsSystem.View.

Here's a quick snapshot of the BlockView dialog with a conceptual view of the standard 3D view sample:

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-5-16 11:03 | 显示全部楼层
五、自定义捕捉模式
(实现对曲线1/4和3/4长的捕捉。)
October 31, 2008
Implementing a custom AutoCAD object snap mode using .NET
Thanks again to Augusto Gonçalves, from our DevTech Americas team, for providing the original VB.NET code for this sample, as well as helping investigate an issue I faced during implementation.
When I saw a recent reply to a developer, showing how to implement a custom object snap in AutoCAD using .NET, I had a really strong sense of nostalgia: it reminded me of a couple of early samples I contributed to the ObjectARX SDK: the "third" sample, which showed how to create a custom osnap that snapped to a third of the way along a curve, and "divisor" which generalised the approach to fractions of any size and was my first real attempt at using C++ templates. Ah, the memories. The samples were retired from this year's SDK, but were still included up to and including the ObjectARX SDK for AutoCAD 2008.
Anyway, the code Augusto sent was very familiar, and it turns out he based it on some documentation that was probably, in turn, based on my C++ sample. So it has come full circle. :-)
One thing I hadn't realised until I saw Augusto's email was that the ability to define custom object snaps had been exposed through .NET.
Here's the C# code that implements a new "quarter" object snap, which snaps to 1/4 and 3/4 along the length of a curve.
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.Runtime;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.GraphicsInterface;
  6. [assembly:ExtensionApplication(
  7.   typeof(OsnapApp.CustomOSnapApp))
  8. ]
  9. namespace OsnapApp
  10. {
  11.   // Register and unregister custom osnap
  12.   public class CustomOSnapApp : IExtensionApplication
  13.   {
  14.     private QuarterOsnapInfo _info =
  15.       new QuarterOsnapInfo();
  16.     private QuarterGlyph _glyph =
  17.       new QuarterGlyph();
  18.     private CustomObjectSnapMode _mode;
  19.     public void Initialize()
  20.     {
  21.       // Register custom osnap on initialize
  22.       _mode =
  23.         new CustomObjectSnapMode(
  24.           "Quarter",
  25.           "Quarter",
  26.           "Quarter of length",
  27.           _glyph
  28.         );
  29.       // Which kind of entity will use the osnap
  30.       _mode.ApplyToEntityType(
  31.         RXObject.GetClass(typeof(Polyline)),
  32.         new AddObjectSnapInfo(_info.SnapInfoPolyline)
  33.       );
  34.       _mode.ApplyToEntityType(
  35.         RXObject.GetClass(typeof(Curve)),
  36.         new AddObjectSnapInfo(_info.SnapInfoCurve)
  37.       );
  38.       _mode.ApplyToEntityType(
  39.         RXObject.GetClass(typeof(Entity)),
  40.         new AddObjectSnapInfo(_info.SnapInfoEntity)
  41.       );
  42.       // Activate the osnap
  43.       CustomObjectSnapMode.Activate("_Quarter");
  44.     }
  45.     // Unregister custom osnap on terminate
  46.     public void Terminate()
  47.     {      
  48.       CustomObjectSnapMode.Deactivate("_Quarter");
  49.     }
  50.   }
  51.   // Create new quarter object snap
  52.   public class QuarterGlyph : Glyph
  53.   {
  54.       private Point3d _pt;
  55.       public override void SetLocation(Point3d point)
  56.       {
  57.         _pt = point;
  58.       }
  59.       public override void ViewportDraw(ViewportDraw vd)
  60.       {
  61.         int glyphPixels =
  62.           CustomObjectSnapMode.GlyphSize;
  63.         Point2d glyphSize =
  64.           vd.Viewport.GetNumPixelsInUnitSquare(_pt);
  65.         // Calculate the size of the glyph in WCS
  66.         //  (use for text height factor)
  67.         // We'll add 20% to the size, as otherwise
  68.         //  it looks a little too small
  69.         double glyphHeight =
  70.           (glyphPixels / glyphSize.Y) * 1.2;
  71.         string text = "¼";
  72.         // Translate the X-axis of the DCS to WCS
  73.         //  (for the text direction) and the snap
  74.         //  point itself (for the text location)
  75.         Matrix3d e2w = vd.Viewport.EyeToWorldTransform;
  76.         Vector3d dir = Vector3d.XAxis.TransformBy(e2w);
  77.         Point3d pt = _pt.TransformBy(e2w);
  78.         //  Draw the centered text representing the glyph
  79.         vd.Geometry.Text(
  80.           pt,
  81.           vd.Viewport.ViewDirection,
  82.           dir,
  83.           glyphHeight,
  84.           1,
  85.           0,
  86.           text
  87.         );
  88.       }
  89.   }
  90.   // OSnap info
  91.   public class QuarterOsnapInfo
  92.   {
  93.     public void SnapInfoEntity(
  94.       ObjectSnapContext context,
  95.       ObjectSnapInfo result)
  96.     {
  97.       // Nothing here
  98.     }
  99.     public void SnapInfoCurve(
  100.       ObjectSnapContext context,
  101.       ObjectSnapInfo result
  102.     )
  103.     {
  104.       // For any curve
  105.       Curve cv = context.PickedObject as Curve;
  106.       if (cv == null)
  107.         return;
  108.       double startParam = cv.StartParam;
  109.       double endParam = cv.EndParam;
  110.       // Add osnap at first quarter
  111.       double param =
  112.         startParam + ((endParam - startParam) * 0.25);
  113.       Point3d pt = cv.GetPointAtParameter(param);
  114.       result.SnapPoints.Add(pt);
  115.       // Add osnap at third quarter
  116.       param =
  117.         startParam + ((endParam - startParam) * 0.75);
  118.       pt = cv.GetPointAtParameter(param);
  119.       result.SnapPoints.Add(pt);
  120.       if (cv.Closed)
  121.       {
  122.         pt = cv.StartPoint;
  123.         result.SnapPoints.Add(pt);
  124.       }
  125.     }
  126.     public void SnapInfoPolyline(
  127.       ObjectSnapContext context,
  128.       ObjectSnapInfo result)
  129.     {
  130.       // For polylines
  131.       Polyline pl = context.PickedObject as Polyline;
  132.       if (pl == null)
  133.         return;
  134.       // Get the overall start and end parameters
  135.       double plStartParam = pl.StartParam;
  136.       double plEndParam = pl.EndParam;
  137.       // Get the local
  138.       double startParam = plStartParam;
  139.       double endParam = startParam + 1.0;
  140.       while (endParam <= plEndParam)
  141.       {
  142.         // Calculate the snap point per vertex...
  143.         // Add osnap at first quarter
  144.         double param =
  145.           startParam + ((endParam - startParam) * 0.25);
  146.         Point3d pt = pl.GetPointAtParameter(param);
  147.         result.SnapPoints.Add(pt);
  148.         // Add osnap at third quarter
  149.         param =
  150.           startParam + ((endParam - startParam) * 0.75);
  151.         pt = pl.GetPointAtParameter(param);
  152.         result.SnapPoints.Add(pt);
  153.         startParam = endParam;
  154.         endParam += 1.0;
  155.       }
  156.     }
  157.   }
  158. }
Some comments on the implementation:
There's a blank callback that is the base implementation for entities
We then override that for all Curve objects, using some code to divide a curve into quarters
We do yet another implementation for all Polyline objects (which are Curves, but we want to treat them as a special case)
For Polylines we snap within segments
We could have implemented this by retrieving each segment and dividing that into quarters
Instead I chose to rely on the fact that a Polyline's parameter is a "whole number" at each vertex, which means the code is the same for any kind of segment
In my original sample I adjusted the position of the text, to centre it on the snap point
In this example I haven't done this, as when I looked at the code it wasn't accurate - when you zoomed in the text appeared in the wrong position
As we're just using a single character (&frac14;) as our glyph, this isn't a significant problem
Here's what happens when we load our module and try snapping to a line inside AutoCAD:

本帖子中包含更多资源

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

x
 楼主| 发表于 2009-5-16 11:12 | 显示全部楼层

六、在绘图界面动态显示坐标

见[Jigs]的相关例子之二

Drawing text planar to the screen inside AutoCAD's drawing window using .NET

 楼主| 发表于 2009-5-16 11:30 | 显示全部楼层
七、在3D视图中变换视角
February 25, 2009
Smoothly transitioning between 3D AutoCAD views using .NET - Part 1
This inspiration for this post came during the research for this previous post, where we looked at implementing LookAt inside AutoCAD. It also has roots in the need to perform smooth transitions when zooming inside AutoCAD, which the ZOOM command manages for transitions between 2D views. Fenton Webb, from our DevTech Americas team, kindly volunteered to put together an ObjectARX sample that formed the basis for the code in this post. A huge thanks to Fents for his hard work on this!
This code presents a technique that allows smooth transitioning between 3D views in AutoCAD: you set up the parameters of the view to which you wish to change, and the function takes care of the heavy lifting. The technique follows more-or-less the same approach than that used to implement the ViewCube's smooth view transitions. It creates a GraphicsSystem.View object and manipulates it to transition smoothly to the new view.
Here's the C# code:
  1. using System;
  2. using System.Threading;
  3. using System.Drawing;
  4. using Autodesk.AutoCAD.ApplicationServices;
  5. using Autodesk.AutoCAD.EditorInput;
  6. using Autodesk.AutoCAD.DatabaseServices;
  7. using Autodesk.AutoCAD.Runtime;
  8. using Autodesk.AutoCAD.Geometry;
  9. using Autodesk.AutoCAD.GraphicsInterface;
  10. using Autodesk.AutoCAD.GraphicsSystem;
  11. using Autodesk.AutoCAD.Interop;
  12. namespace ViewTransitions
  13. {
  14.   public class MyView
  15.   {
  16.     public Point3d position;
  17.     public Point3d target;
  18.     public Vector3d upVector;
  19.     public double fieldWidth;
  20.     public double fieldHeight;
  21.     // Default constructor
  22.     public MyView(){}
  23.     // For constant defines below SWIso etc
  24.     public MyView(
  25.       double x1, double y1, double z1,
  26.       double x2, double y2, double z2,
  27.       double x3, double y3, double z3,
  28.       double x4, double y4
  29.     )
  30.     {
  31.       position = new Point3d(x1, y1, z1);
  32.       target = new Point3d(x2, y2, z2);
  33.       upVector = new Vector3d(x3, y3, z3);
  34.       fieldWidth = x4;
  35.       fieldHeight= y4;
  36.     }
  37.     public MyView(
  38.       Point3d position, Point3d target, Vector3d upVector,
  39.       double fieldWidth, double fieldHeight
  40.     )
  41.     {
  42.       this.position = position;
  43.       this.target = target;
  44.       this.upVector = upVector;
  45.       this.fieldWidth = fieldWidth;
  46.       this.fieldHeight = fieldHeight;
  47.     }
  48.   };
  49.   public class Commands
  50.   {
  51.     static MyView defaultView =
  52.       new MyView(
  53.         1930.1,1339.3,4399.3, 1930.1,1339.3,0.0,
  54.         0.0,1.0,0.0, 3279.8, 1702.6
  55.       );
  56.     static MyView topView =
  57.       new MyView(
  58.         1778.1,1108.2,635.7, 1778.1,1108.2,0.0,
  59.         0.0,1.0,0.0, 474.0, 246.0
  60.       );
  61.     static MyView bottomView =
  62.       new MyView(
  63.         1778.1,1108.2,-635.7, 1778.1,1108.2,0.0,
  64.         0.0,1.0,0.0, 474.0, 246.0
  65.       );
  66.     static MyView leftView =
  67.       new MyView(
  68.         -344.1,1108.2,66.1, 0.0,1108.2,66.1,
  69.         0.0,0.0,1.0, 256.5, 133.2
  70.       );
  71.     static MyView rightView =
  72.       new MyView(
  73.         344.1,1108.2,66.1, 0.0,1108.2,66.1,
  74.         0.0,0.0,1.0, 256.5, 133.2
  75.       );
  76.     static MyView SWIso =
  77.       new MyView(
  78.         265.1,-404.7,1579.0, 838.0,168.2,1006.2,
  79.         0.4,0.4,0.8, 739.7, 384.0
  80.       );
  81.     static MyView SEIso =
  82.       new MyView(
  83.         2105.6,780.7,393.7, 1532.7,1353.5,-179.2,
  84.         -0.4,0.4,0.8, 739.7, 384.0
  85.       );
  86.     static MyView NEIso =
  87.       new MyView(
  88.         1366.8,697.0,-345.2, 793.9,124.1,-918.0,
  89.         -0.4,-0.4,0.8, 739.7, 384.0
  90.       );
  91.     static MyView NWIso =
  92.       new MyView(
  93.         1003.9, 1882.3, 840.2, 1576.8, 1309.5,
  94.         267.3, 0.4, -0.4, 0.8, 739.7, 384.0
  95.       );
  96.     // Enacts a smooth transition from the current view
  97.     // to a new view
  98.     static Matrix3d SmoothViewTo(
  99.       MyView nv, double timeToTake
  100.     )
  101.     {
  102.       Matrix3d newViewMatrix = Matrix3d.Identity;
  103.       Document doc =
  104.         Application.DocumentManager.MdiActiveDocument;
  105.       Database db =
  106.         doc.Database;
  107.       Manager gsm =
  108.         doc.GraphicsManager;
  109.       // Get the current viewport
  110.       int vpn =
  111.         Convert.ToInt32(
  112.           Application.GetSystemVariable("CVPORT")
  113.         );
  114.       View view = gsm.GetGsView(vpn, true);
  115.       using (view)
  116.       {
  117.         // Set the frame rate to the standard eye FPS
  118.         view.BeginInteractivity(24);
  119.         Matrix3d viewMatrix = view.ViewingMatrix;
  120.         // Get the current view settings
  121.         MyView cv =
  122.           new MyView(
  123.             view.Position, view.Target, view.UpVector,
  124.             view.FieldWidth, view.FieldHeight
  125.           );
  126.         // Set up the start positions
  127.         Point3d intPos = cv.position;
  128.         Point3d intTgt = cv.target;
  129.         Vector3d intUpVec = cv.upVector;
  130.         double intWid = cv.fieldWidth;
  131.         double intHgt = cv.fieldHeight;
  132.         // Now animate the view change between the
  133.         // currentview and the viewToChangeTo
  134.         for (float mu = 0; mu <= 1; mu += 0.01F)
  135.         {
  136.           // Interpolate position
  137.           intPos =
  138.             new Point3d(
  139.               CosInterp(cv.position.X, nv.position.X, mu),
  140.               CosInterp(cv.position.Y, nv.position.Y, mu),
  141.               CosInterp(cv.position.Z, nv.position.Z, mu)
  142.             );
  143.           // Interpolate target
  144.           intTgt =
  145.             new Point3d(
  146.               CosInterp(cv.target.X, nv.target.X, mu),
  147.               CosInterp(cv.target.Y, nv.target.Y, mu),
  148.               CosInterp(cv.target.Z, nv.target.Z, mu)
  149.             );
  150.           // Interpolate upVector
  151.           intUpVec =
  152.             new Vector3d(
  153.               CosInterp(cv.upVector.X, nv.upVector.X, mu),
  154.               CosInterp(cv.upVector.Y, nv.upVector.Y, mu),
  155.               CosInterp(cv.upVector.Z, nv.upVector.Z, mu)
  156.             );
  157.           // Interpolate Width
  158.           intWid =
  159.             CosInterp(cv.fieldWidth, nv.fieldWidth, mu);
  160.           // Interpolate Height
  161.           intHgt =
  162.             CosInterp(cv.fieldHeight, nv.fieldHeight, mu);
  163.           // Now set the interpolated view
  164.           view.SetView(intPos, intTgt, intUpVec, intWid, intHgt);
  165.           // Update the control
  166.           view.Update();
  167.           // Decrease the sleep time, or rather increase the
  168.           // speed of the view change as we work
  169.           double sleepTime = timeToTake - (mu * 10);
  170.           Thread.Sleep((int)(sleepTime > 50 ? 0 : sleepTime));
  171.         }
  172.         view.EndInteractivity();
  173.         // Finally set the new view  
  174.         gsm.SetViewportFromView(vpn, view, true, true, false);
  175.         System.Windows.Forms.Application.DoEvents();
  176.       }
  177.       return newViewMatrix;
  178.     }
  179.     // Cosine interpolation
  180.     static double CosInterp(double y1, double y2, double mu)
  181.     {
  182.       double mu2;
  183.       mu2 = (1-Math.Cos(mu*Math.PI))/2;
  184.       return(y1*(1-mu2)+y2*mu2);
  185.     }
  186.     // Function to create a solid background of the same
  187.     // colour as the background of our 2D modelspace view
  188.     // (reduces the visual shock as the colour would
  189.     // otherwise switch to grey and back)
  190.     private static ObjectId CreateBackground()
  191.     {
  192.       const string bgKey = "TTIF_BG";
  193.       Document doc =
  194.         Application.DocumentManager.MdiActiveDocument;
  195.       Database db =
  196.         doc.Database;
  197.       ObjectId vtId = ObjectId.Null;
  198.       // Get the current viewport number
  199.       int vpn =
  200.         Convert.ToInt32(
  201.           Application.GetSystemVariable("CVPORT")
  202.         );
  203.       // No need to set the background if a corresponding
  204.       // 3D view already exists
  205.       View view =
  206.         doc.GraphicsManager.GetGsView(vpn, false);
  207.       if (view == null)
  208.       {
  209.         Transaction tr =
  210.           db.TransactionManager.StartTransaction();
  211.         using (tr)
  212.         {
  213.           ObjectId bgId = ObjectId.Null;
  214.           // Get or create our background dictionary
  215.           ObjectId bgdId =
  216.             Background.GetBackgroundDictionaryId(db, true);
  217.           DBDictionary bgd =
  218.             (DBDictionary)tr.GetObject(
  219.               bgdId,
  220.               OpenMode.ForRead
  221.             );
  222.           if (bgd.Contains(bgKey))
  223.           {
  224.             bgId = bgd.GetAt(bgKey);
  225.           }
  226.           else
  227.           {
  228.             // If our background doesn't exist...
  229.             // Get the 2D modelspace background colour
  230.             AcadPreferences prefs =
  231.               (AcadPreferences)Application.Preferences;
  232.             int rawCol =
  233.               (int)prefs.Display.GraphicsWinModelBackgrndColor;
  234.             // Create a background with the corresponding RGB
  235.             SolidBackground sb = new SolidBackground();
  236.             sb.Color =
  237.               new Autodesk.AutoCAD.Colors.EntityColor(
  238.                 (byte)(rawCol & 0x000000FF),
  239.                 (byte)((rawCol & 0x0000FF00) >> 8),
  240.                 (byte)((rawCol & 0x00FF0000) >> 16)
  241.               );
  242.             // Add it to the background dictionary
  243.             bgd.UpgradeOpen();
  244.             bgId = bgd.SetAt(bgKey, sb);
  245.             tr.AddNewlyCreatedDBObject(sb, true);
  246.           }
  247.           // Set the background on the active modelspace viewport
  248.           ViewportTable vt =
  249.             (ViewportTable)tr.GetObject(
  250.               db.ViewportTableId,
  251.               OpenMode.ForRead
  252.             );
  253.           foreach (ObjectId id in vt)
  254.           {
  255.             ViewportTableRecord vtr =
  256.               (ViewportTableRecord)tr.GetObject(
  257.                 id,
  258.                 OpenMode.ForRead
  259.               );
  260.             if (vtr.Name == "*Active")
  261.             {
  262.               vtId = id;
  263.               vtr.UpgradeOpen();
  264.               vtr.Background = bgId;
  265.             }
  266.           }
  267.           tr.Commit();
  268.         }
  269.       }
  270.       else
  271.         view.Dispose();
  272.       return vtId;
  273.     }
  274.     private static void RemoveBackground(ObjectId vtId)
  275.     {
  276.       Document doc =
  277.         Application.DocumentManager.MdiActiveDocument;
  278.       Database db =
  279.         doc.Database;
  280.       Transaction tr =
  281.         db.TransactionManager.StartTransaction();
  282.       using (tr)
  283.       {
  284.         // Open up the previously-modified viewport
  285.         ViewportTableRecord vtr =
  286.           (ViewportTableRecord)tr.GetObject(
  287.             vtId,
  288.             OpenMode.ForWrite
  289.           );
  290.         // And set its previous background
  291.         ObjectId obgId =
  292.           vtr.GetPreviousBackground(
  293.             DrawableType.SolidBackground
  294.           );
  295.         vtr.Background = obgId;
  296.         tr.Commit();
  297.       }
  298.     }
  299.     [CommandMethod("TV")]
  300.     static public void TransitionView()
  301.     {
  302.       Document doc =
  303.         Application.DocumentManager.MdiActiveDocument;
  304.       Database db =
  305.         doc.Database;
  306.       ObjectId vtId = CreateBackground();
  307.       SmoothViewTo(defaultView, 10);
  308.       SmoothViewTo(SWIso, 10);
  309.       SmoothViewTo(topView, 10);
  310.       SmoothViewTo(SEIso, 10);
  311.       SmoothViewTo(bottomView, 10);
  312.       SmoothViewTo(NEIso, 10);
  313.       SmoothViewTo(leftView, 10);
  314.       SmoothViewTo(NWIso, 10);
  315.       SmoothViewTo(rightView, 10);
  316.       SmoothViewTo(defaultView, 10);
  317.       if (vtId != ObjectId.Null)
  318.         RemoveBackground(vtId);
  319.     }
  320.   }
  321. }
A few comments on the code...
Fenton interpolates between views using his own "secret sauce", the CosInterp() function. This does some clever stuff to interpolate between the values provided. It's used to interpolate between the beginning and end states of the individual members of the co-ordinates of the various points and vectors - and the field width and height - that define a view.
I added some functionality to create a temporary background image attached to the active ViewportTableRecord with the same colour as the drawing canvas background (if in a standard 2D view). This allows the 3D view that gets created to have the same background colour, avoiding the shock of it flashing to grey and back. I admit that this code (in CreateBackground() and RemoveBackground()) doesn't feel especially elegant - I tried various different approaches such as modifying the view and the device attached to the view, but none of them worked in the way I wanted. So this is what I ended up with. I'd be very happy to hear from people who have found a better way to address this issue...
To see how the function works, draw some 3D geometry, load the application and run the TV command.
Here's a sample view prior to running the command:
And here's a snapshot I managed to take during the command, as the view was transitioning:

It's hard to do it justice with a static image, so the best is to give it a try.
I can see a few changes that people might want to make to the code:
the view definitions (to change their parameters or even to generate them dynamically).
the formula in the CosInterp() function.
the code towards the end of the SmoothViewTo() which pauses for a variable amount of time, depending on how close the view is from being transitioned.
When Fenton shared his original code within the DevTech team, Jeremy Tammik mentioned another interpolation algorithm based on  quaternion mathematics,  spherical linear interpolation (Slerp). In the next post in this series we'll take a look at a version implementing Slerp to do something very similar to the code in this post.

 楼主| 发表于 2009-5-16 11:33 | 显示全部楼层
March 02, 2009
Smoothly transitioning between 3D AutoCAD views using .NET - Part 2
Thanks again to Fenton Webb for providing the code behind the first post in the series and to Jeremy Tammik for providing the suggestion of this alternative implementation.
This post follows on from this previous post, which introduced a technique to smoothly transition between 3D views in AutoCAD. It applies a more standard algorithm - known as  spherical linear interpolation (or Slerp to its friends :-) - to interpolate between views, rather than interpolating individual values using Fenton's custom-built CosInterp() function. We still use CosInterp() to interpolate the width and height of the field of view, but otherwise the below code makes use of Slerp for the points and vectors it needs to adjust.
Here's the modified C# code, which can be added to the same project as that containing the code from the previous post (to compare the execution):
  1. using System;
  2. using System.Threading;
  3. using System.Drawing;
  4. using Autodesk.AutoCAD.ApplicationServices;
  5. using Autodesk.AutoCAD.EditorInput;
  6. using Autodesk.AutoCAD.DatabaseServices;
  7. using Autodesk.AutoCAD.Runtime;
  8. using Autodesk.AutoCAD.Geometry;
  9. using Autodesk.AutoCAD.GraphicsInterface;
  10. using Autodesk.AutoCAD.GraphicsSystem;
  11. using Autodesk.AutoCAD.Interop;
  12. namespace ViewTransitionsSlerp
  13. {
  14.   public class MyView
  15.   {
  16.     public Point3d position;
  17.     public Point3d target;
  18.     public Vector3d upVector;
  19.     public double fieldWidth;
  20.     public double fieldHeight;
  21.     // Default constructor
  22.     public MyView(){}
  23.     // For constant defines below SWIso etc
  24.     public MyView(
  25.       double x1, double y1, double z1,
  26.       double x2, double y2, double z2,
  27.       double x3, double y3, double z3,
  28.       double x4, double y4
  29.     )
  30.     {
  31.       position = new Point3d(x1, y1, z1);
  32.       target = new Point3d(x2, y2, z2);
  33.       upVector = new Vector3d(x3, y3, z3);
  34.       fieldWidth = x4;
  35.       fieldHeight= y4;
  36.     }
  37.     public MyView(
  38.       Point3d position, Point3d target, Vector3d upVector,
  39.       double fieldWidth, double fieldHeight
  40.     )
  41.     {
  42.       this.position = position;
  43.       this.target = target;
  44.       this.upVector = upVector;
  45.       this.fieldWidth = fieldWidth;
  46.       this.fieldHeight = fieldHeight;
  47.     }
  48.   };
  49.   public class Commands
  50.   {
  51.     static MyView defaultView =
  52.       new MyView(
  53.         1930.1,1339.3,4399.3, 1930.1,1339.3,0.0,
  54.         0.0,1.0,0.0, 3279.8, 1702.6
  55.       );
  56.     static MyView topView =
  57.       new MyView(
  58.         1778.1,1108.2,635.7, 1778.1,1108.2,0.0,
  59.         0.0,1.0,0.0, 474.0, 246.0
  60.       );
  61.     static MyView bottomView =
  62.       new MyView(
  63.         1778.1,1108.2,-635.7, 1778.1,1108.2,0.0,
  64.         0.0,1.0,0.0, 474.0, 246.0
  65.       );
  66.     static MyView leftView =
  67.       new MyView(
  68.         -344.1,1108.2,66.1, 0.0,1108.2,66.1,
  69.         0.0,0.0,1.0, 256.5, 133.2
  70.       );
  71.     static MyView rightView =
  72.       new MyView(
  73.         344.1,1108.2,66.1, 0.0,1108.2,66.1,
  74.         0.0,0.0,1.0, 256.5, 133.2
  75.       );
  76.     static MyView SWIso =
  77.       new MyView(
  78.         265.1,-404.7,1579.0, 838.0,168.2,1006.2,
  79.         0.4,0.4,0.8, 739.7, 384.0
  80.       );
  81.     static MyView SEIso =
  82.       new MyView(
  83.         2105.6,780.7,393.7, 1532.7,1353.5,-179.2,
  84.         -0.4,0.4,0.8, 739.7, 384.0
  85.       );
  86.     static MyView NEIso =
  87.       new MyView(
  88.         1366.8,697.0,-345.2, 793.9,124.1,-918.0,
  89.         -0.4,-0.4,0.8, 739.7, 384.0
  90.       );
  91.     static MyView NWIso =
  92.       new MyView(
  93.         1003.9, 1882.3, 840.2, 1576.8, 1309.5,
  94.         267.3, 0.4, -0.4, 0.8, 739.7, 384.0
  95.       );
  96.     // Enacts a smooth transition from the current view to a
  97.     // new view using spherical linear interpolation (Slerp)
  98.     static Matrix3d SmoothViewToSlerp(
  99.       MyView nv, double timeToTake
  100.     )
  101.     {
  102.       Matrix3d newViewMatrix = Matrix3d.Identity;
  103.       Document doc =
  104.         Application.DocumentManager.MdiActiveDocument;
  105.       Database db =
  106.         doc.Database;
  107.       Manager gsm =
  108.         doc.GraphicsManager;
  109.       // Get the current viewport
  110.       int vpn =
  111.         Convert.ToInt32(
  112.           Application.GetSystemVariable("CVPORT")
  113.         );
  114.       View view = gsm.GetGsView(vpn, true);
  115.       using (view)
  116.       {
  117.         // Set the frame rate to the standard eye FPS
  118.         view.BeginInteractivity(24);
  119.         Matrix3d viewMatrix = view.ViewingMatrix;
  120.         // Get the current view settings
  121.         MyView cv =
  122.           new MyView(
  123.             view.Position, view.Target, view.UpVector,
  124.             view.FieldWidth, view.FieldHeight
  125.           );
  126.         // Set up the start positions
  127.         Point3d intPos = cv.position;
  128.         Point3d intTgt = cv.target;
  129.         Vector3d intUpVec = cv.upVector;
  130.         double intWid = cv.fieldWidth;
  131.         double intHgt = cv.fieldHeight;
  132.         // Now animate the view change between the
  133.         // currentview and the viewToChangeTo
  134.         for (float mu = 0; mu <= 1; mu += 0.01F)
  135.         {
  136.           // First convert the positions to vectors
  137.           // (so we can simply call the existing function)
  138.           Vector3d startPos = cv.position - Point3d.Origin,
  139.                   endPos = nv.position - Point3d.Origin,
  140.                   // Then get the target vectors relative
  141.                   // to the view position
  142.                   from = cv.target - cv.position,
  143.                   to = nv.target - nv.position,
  144.                   // Now Slerp the various vectors
  145.                   res1 = Slerp(startPos, endPos, mu),
  146.                   res2 = Slerp(from, to, mu),
  147.                   res3 = Slerp(cv.upVector, nv.upVector, mu);
  148.           // And then we extract the relevant information...
  149.           // Get a point from the position vector
  150.           intPos = Point3d.Origin + res1;
  151.           // Get the target point relative to that position
  152.           intTgt = intPos + res2;
  153.           // And the up-vector is easy :-)
  154.           intUpVec = res3;
  155.           // Let's use our previous interpolate function
  156.           // for the field width and height
  157.           // Interpolate Width
  158.           intWid =
  159.             CosInterp(cv.fieldWidth, nv.fieldWidth, mu);
  160.           // Interpolate Height
  161.           intHgt =
  162.             CosInterp(cv.fieldHeight, nv.fieldHeight, mu);
  163.           // Now set the interpolated view
  164.           view.SetView(intPos, intTgt, intUpVec, intWid, intHgt);
  165.           // Update the control
  166.           view.Update();
  167.           // Decrease the sleep time, or rather increase the
  168.           // speed of the view change as we work
  169.           double sleepTime = timeToTake - (mu * 10);
  170.           Thread.Sleep((int)(sleepTime > 50 ? 0 : sleepTime));
  171.         }
  172.         view.EndInteractivity();
  173.         // Finally set the new view  
  174.         gsm.SetViewportFromView(vpn, view, true, true, false);
  175.         System.Windows.Forms.Application.DoEvents();
  176.       }
  177.       return newViewMatrix;
  178.     }
  179.     // Cosine interpolation
  180.     static double CosInterp(double y1, double y2, double mu)
  181.     {
  182.       double mu2;
  183.       mu2 = (1-Math.Cos(mu*Math.PI))/2;
  184.       return(y1*(1-mu2)+y2*mu2);
  185.     }
  186.     // Spherical linear interpolation
  187.     static Vector3d Slerp(Vector3d from, Vector3d to, float step)
  188.     {
  189.       if (step == 0)
  190.         return from;
  191.       if (from == to || step == 1)
  192.         return to;
  193.       // Normalize the vectors
  194.       Vector3d unitfrom = from.GetNormal(),
  195.               unitto = to.GetNormal();
  196.       // Calculate the included angle
  197.       double theta =
  198.         Math.Acos(unitfrom.DotProduct(unitto));
  199.       if (theta == 0)
  200.         return to;
  201.       // Avoid the repeated sine calculation
  202.       double st =
  203.         Math.Sin(theta);
  204.       // Return the geometric spherical linear interpolation
  205.       return
  206.         from * (Math.Sin((1 - step) * theta) / st) +
  207.         to * Math.Sin(step * theta) / st;
  208.     }
  209.     // Function to create a solid background of the same
  210.     // colour as the background of our 2D modelspace view
  211.     // (reduces the visual shock as the colour would
  212.     // otherwise switch to grey and back)
  213.     private static ObjectId CreateBackground()
  214.     {
  215.       const string bgKey = "TTIF_BG";
  216.       Document doc =
  217.         Application.DocumentManager.MdiActiveDocument;
  218.       Database db =
  219.         doc.Database;
  220.       ObjectId vtId = ObjectId.Null;
  221.       // Get the current viewport number
  222.       int vpn =
  223.         Convert.ToInt32(
  224.           Application.GetSystemVariable("CVPORT")
  225.         );
  226.       // No need to set the background if a corresponding
  227.       // 3D view already exists
  228.       View view =
  229.         doc.GraphicsManager.GetGsView(vpn, false);
  230.       if (view == null)
  231.       {
  232.         Transaction tr =
  233.           db.TransactionManager.StartTransaction();
  234.         using (tr)
  235.         {
  236.           ObjectId bgId = ObjectId.Null;
  237.           // Get or create our background dictionary
  238.           ObjectId bgdId =
  239.             Background.GetBackgroundDictionaryId(db, true);
  240.           DBDictionary bgd =
  241.             (DBDictionary)tr.GetObject(
  242.               bgdId,
  243.               OpenMode.ForRead
  244.             );
  245.           if (bgd.Contains(bgKey))
  246.           {
  247.             bgId = bgd.GetAt(bgKey);
  248.           }
  249.           else
  250.           {
  251.             // If our background doesn't exist...
  252.             // Get the 2D modelspace background colour
  253.             AcadPreferences prefs =
  254.               (AcadPreferences)Application.Preferences;
  255.             int rawCol =
  256.               (int)prefs.Display.GraphicsWinModelBackgrndColor;
  257.             // Create a background with the corresponding RGB
  258.             SolidBackground sb = new SolidBackground();
  259.             sb.Color =
  260.               new Autodesk.AutoCAD.Colors.EntityColor(
  261.                 (byte)(rawCol & 0x000000FF),
  262.                 (byte)((rawCol & 0x0000FF00) >> 8),
  263.                 (byte)((rawCol & 0x00FF0000) >> 16)
  264.               );
  265.             // Add it to the background dictionary
  266.             bgd.UpgradeOpen();
  267.             bgId = bgd.SetAt(bgKey, sb);
  268.             tr.AddNewlyCreatedDBObject(sb, true);
  269.           }
  270.           // Set the background on the active modelspace viewport
  271.           ViewportTable vt =
  272.             (ViewportTable)tr.GetObject(
  273.               db.ViewportTableId,
  274.               OpenMode.ForRead
  275.             );
  276.           foreach (ObjectId id in vt)
  277.           {
  278.             ViewportTableRecord vtr =
  279.               (ViewportTableRecord)tr.GetObject(
  280.                 id,
  281.                 OpenMode.ForRead
  282.               );
  283.             if (vtr.Name == "*Active")
  284.             {
  285.               vtId = id;
  286.               vtr.UpgradeOpen();
  287.               vtr.Background = bgId;
  288.             }
  289.           }
  290.           tr.Commit();
  291.         }
  292.       }
  293.       else
  294.         view.Dispose();
  295.       return vtId;
  296.     }
  297.     private static void RemoveBackground(ObjectId vtId)
  298.     {
  299.       Document doc =
  300.         Application.DocumentManager.MdiActiveDocument;
  301.       Database db =
  302.         doc.Database;
  303.       Transaction tr =
  304.         db.TransactionManager.StartTransaction();
  305.       using (tr)
  306.       {
  307.         // Open up the previously-modified viewport
  308.         ViewportTableRecord vtr =
  309.           (ViewportTableRecord)tr.GetObject(
  310.             vtId,
  311.             OpenMode.ForWrite
  312.           );
  313.         // And set its previous background
  314.         ObjectId obgId =
  315.           vtr.GetPreviousBackground(
  316.             DrawableType.SolidBackground
  317.           );
  318.         vtr.Background = obgId;
  319.         tr.Commit();
  320.       }
  321.     }
  322.     [CommandMethod("TVS")]
  323.     static public void TransitionViewSlerp()
  324.     {
  325.       Document doc =
  326.         Application.DocumentManager.MdiActiveDocument;
  327.       Database db =
  328.         doc.Database;
  329.       ObjectId vtId = CreateBackground();
  330.       SmoothViewToSlerp(defaultView, 10);
  331.       SmoothViewToSlerp(SWIso, 10);
  332.       SmoothViewToSlerp(topView, 10);
  333.       SmoothViewToSlerp(SEIso, 10);
  334.       SmoothViewToSlerp(bottomView, 10);
  335.       SmoothViewToSlerp(NEIso, 10);
  336.       SmoothViewToSlerp(leftView, 10);
  337.       SmoothViewToSlerp(NWIso, 10);
  338.       SmoothViewToSlerp(rightView, 10);
  339.       SmoothViewToSlerp(defaultView, 10);
  340.       if (vtId != ObjectId.Null)
  341.         RemoveBackground(vtId);
  342.     }
  343.   }
  344. }
What's especially notable about this implementation is actually how similarly it works to the previous one. Fenton came up with a pretty nice interpolation technique without knowing about Slerp which produces very similar - possibly identical, although I haven't verified them - results. Very cool.
From my side I hadn't heard of Slerp and only had the vaguest idea of what a  quaternion was - even now I wouldn't know a quaternion if it bit me on the nose, so it's a good thing the wikipedia article contains a geometric alternative to the quaternion Slerp formula.
So why use one technique over the other? There are a couple of possible differentiators that may make a difference to people.
The first is the possibility - and this is not something I've verified through performance benchmarking - that the Slerp implementation is more efficient. We need fewer calls to Slerp() than we used for CosInterp(), simply because we're interpolating multiple values at the same time. But this isn't likely to be a noticeable difference in any real-world application, so isn't something that would concern me, either way.
The second differentiator is a potential deployment issue: a bug was introduced with Service Pack 1 of the .NET Framework 3.5 that can cause problems with vector arithmetic in AutoCAD .NET applications. Jimmy Bergmark reported on this, back in August, and a hotfix was posted by Microsoft in early December. I hit this issue after having installed a pre-release version of AutoCAD 2010 (which installed the .NET Framework 3.5 SP1) but I hit the problem when executing code in AutoCAD 2009. Installing the hotfix solved the problem, but in this case the more fundamental implementation not relying on Vector3d objects proved to be more reliable.
In reality, though, avoiding vector arithmetic isn't really an option for most developers, so this is being addressed on a few fronts: AutoCAD 2009 Update 2 apparently works around it (not sure how I missed that update, but there you go) as does AutoCAD 2010... and it also seems that any day now Microsoft will be pushing out a fixed version of the .NET Framework 3.5 SP1 in a general distribution release via Windows Update (which means that any Windows user running .NET Framework 2.0 or higher will get it). So I'm a little less worried about the impact of this issue than I was when I first saw it manifest itself. For ADN members who would like additional, detailed information on this issue, please visit this DevNote on the ADN site (login required).
All this to say that the two versions of the code are much the same, when all is said and done. I've provided both mainly for the purposes of intellectual curiosity and in case the techniques shown are relevant for other scenarios.

发表于 2009-5-21 10:43 | 显示全部楼层

看样子,我要补习英语了

发表于 2009-5-21 13:57 | 显示全部楼层
学习了!谢谢!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-4-25 15:17 , Processed in 1.868917 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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