明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
12
返回列表 发新帖

[Kean专集] Kean专题(13)—Geometry

   关闭 [复制链接]
 楼主| 发表于 2010-12-19 18:29:07 | 显示全部楼层
一个简单的转置矩阵
December 16, 2010
A simple command to perform a matrix transformation on an AutoCAD entity using .NET
As promised in the last post and based on the overwhelming feedback in the one before that,  today we’re starting a series on how to transform AutoCAD geometry.

Before developing a fancy modeless GUI to make this really easy, we need a base command that can do the hard work. What’s needed from our basic command is the following:

Get a single entity from the pickfirst set (which will help us when calling the command from our modeless UI)
If there isn’t one selected, ask the user for it
Get the property name to transform
Only for writeable Point3d and Vector3d properties
The list of valid properties will be populated in our GUI, so we shouldn’t need much validation
If none is entered, we just transform the whole entity
Get the matrix contents as a comma-delimited string
We’ll then decompose it into the 16 doubles required to define a Matrix3d
Transform the property (or the whole entity) by the provided matrix
We will use Reflection to get and set the Point3d/Vector3d property value
To understand some of the underlying concepts, let’s talk a little about transformation matrices.

We need 4 x 4 matrices when working in 3D space to allow us to perform a full range of transformations: translation, rotation, scaling, mirroring and projection. We could achieve some of these using 3 x 3 matrices, but some of these – particular translation, but probably some of the others (I’m not 100% certain of the specifics) – need the additional cells.

We’ll be looking into different transformation matrix types in more detail when we have a simple UI to play around with them, but for now let’s focus on a simple scaling matrix.

2 0 0 0
0 2 0 0
0 0 2 0
0 0 0 1

When we apply this transformation to an entity, it is basically used to multiply the relevant properties (and basically scales them by a factor of 2).

Let’s see what that means by applying this scaling transformation to the 3D point (5, 5, 0), which could be the centre point of a circle (for instance). We need to add a unit entry (1) to the point, to make it compatible with a 4 x 4 matrix.

2 0 0 0  5
0 2 0 0 * 5
0 0 2 0  0
0 0 0 1  1

Now if we follow the rules of matrix multiplication, we can see that our resultant point is calculated like this:

a b c d  r  a*r + b*s + c*t + d*u
e f g h * s = e*r + f*s + g*t + h*u
i j k l  t  i*r + j*s + k*t + l*u
n o p q  u  n*r + o*s + p*t + q*u

This page has a nice graphical representation of multiplying a matrix with a vector.

Which means for us, specifically:

2 0 0 0  5  10 + 0 + 0 + 0
0 2 0 0 * 5 = 0 + 10 + 0 + 0
0 0 2 0  0  0 + 0 + 0 + 0
0 0 0 1  1  0 + 0 + 0 + 1

And so our transformed point – which is the top three values of the resultant 4-cell matrix – is (10, 10, 0).

Now let’s see the C# code to transform an entity by a user-specified matrix:

  1. using Autodesk.AutoCAD.ApplicationServices;

  2. using Autodesk.AutoCAD.EditorInput;

  3. using Autodesk.AutoCAD.DatabaseServices;

  4. using Autodesk.AutoCAD.Geometry;

  5. using Autodesk.AutoCAD.Runtime;

  6. using System.Reflection;



  7. namespace Transformer

  8. {

  9.   public class Commands

  10.   {

  11.     [CommandMethod("TRANS", CommandFlags.UsePickSet)]

  12.     static public void TransformEntity()

  13.     {

  14.       Document doc =

  15.         Application.DocumentManager.MdiActiveDocument;

  16.       Database db = doc.Database;

  17.       Editor ed = doc.Editor;



  18.       // Our selected entity (only one supported, for now)



  19.       ObjectId id;



  20.       // First query the pickfirst selection set



  21.       PromptSelectionResult psr = ed.SelectImplied();

  22.       if (psr.Status != PromptStatus.OK || psr.Value == null)

  23.       {

  24.         // If nothing selected, ask the user



  25.         PromptEntityOptions peo =

  26.           new PromptEntityOptions(

  27.             "\nSelect entity to transform: "

  28.           );

  29.         PromptEntityResult per = ed.GetEntity(peo);

  30.         if (per.Status != PromptStatus.OK)

  31.           return;

  32.         id = per.ObjectId;

  33.       }

  34.       else

  35.       {

  36.         // If the pickfirst set has one entry, take it



  37.         SelectionSet ss = psr.Value;

  38.         if (ss.Count != 1)

  39.         {

  40.           ed.WriteMessage(

  41.             "\nThis command works on a single entity."

  42.           );

  43.           return;

  44.         }

  45.         ObjectId[] ids = ss.GetObjectIds();

  46.         id = ids[0];

  47.       }



  48.       PromptResult pr = ed.GetString("\nEnter property name: ");

  49.       if (pr.Status != PromptStatus.OK)

  50.         return;



  51.       string prop = pr.StringResult;



  52.       // Now let's ask for the matrix string



  53.       pr = ed.GetString("\nEnter matrix values: ");

  54.       if (pr.Status != PromptStatus.OK)

  55.         return;



  56.       // Split the string into its individual cells



  57.       string[] cells = pr.StringResult.Split(new char[] { ',' });

  58.       if (cells.Length != 16)

  59.       {

  60.         ed.WriteMessage("\nMust contain 16 entries.");

  61.         return;

  62.       }



  63.       try

  64.       {

  65.         // Convert the array of strings into one of doubles



  66.         double[] data = new double[cells.Length];

  67.         for (int i = 0; i < cells.Length; i++)

  68.         {

  69.           data[i] = double.Parse(cells[i]);

  70.         }



  71.         // Create a 3D matrix from our cell data



  72.         Matrix3d mat = new Matrix3d(data);



  73.         // Now we can transform the selected entity



  74.         Transaction tr =

  75.           doc.TransactionManager.StartTransaction();

  76.         using (tr)

  77.         {

  78.           Entity ent =

  79.             tr.GetObject(id, OpenMode.ForWrite)

  80.             as Entity;

  81.           if (ent != null)

  82.           {

  83.             bool transformed = false;



  84.             // If the user specified a property to modify



  85.             if (!string.IsNullOrEmpty(prop))

  86.             {

  87.               // Query the property's value



  88.               object val =

  89.                 ent.GetType().InvokeMember(

  90.                   prop, BindingFlags.GetProperty, null, ent, null

  91.                 );



  92.               // We only know how to transform points and vectors



  93.               if (val is Point3d)

  94.               {

  95.                 // Cast and transform the point result



  96.                 Point3d pt = (Point3d)val,

  97.                         res = pt.TransformBy(mat);



  98.                 // Set it back on the selected object



  99.                 ent.GetType().InvokeMember(

  100.                   prop, BindingFlags.SetProperty, null,

  101.                   ent, new object[] { res }

  102.                 );

  103.                 transformed = true;

  104.               }

  105.               else if (val is Vector3d)

  106.               {

  107.                 // Cast and transform the vector result



  108.                 Vector3d vec = (Vector3d)val,

  109.                         res = vec.TransformBy(mat);



  110.                 // Set it back on the selected object



  111.                 ent.GetType().InvokeMember(

  112.                   prop, BindingFlags.SetProperty, null,

  113.                   ent, new object[] { res }

  114.                 );

  115.                 transformed = true;

  116.               }

  117.             }



  118.             // If we didn't transform a property,

  119.             // do the whole object



  120.             if (!transformed)

  121.               ent.TransformBy(mat);

  122.           }

  123.           tr.Commit();

  124.         }

  125.       }

  126.       catch (Autodesk.AutoCAD.Runtime.Exception ex)

  127.       {

  128.         ed.WriteMessage(

  129.           "\nCould not transform entity: {0}", ex.Message

  130.         );

  131.       }

  132.     }

  133.   }

  134. }


Now let’s use the TRANS command to transform a couple of entities:



We’ll use TRANS to apply the above scaling transformation matrix to the whole circle and then to the EndPoint of the line:

Command: TRANS

Select entity to transform: <selected the circle>

Enter property name:

Enter matrix values: 2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,1

Command: TRANS

Select entity to transform: <selected the line>

Enter property name: EndPoint

Enter matrix values: 2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,1

With these results:



I understand this is quite a tricky topic, so I’d appreciate your feedback: does this initial explanation help, at all? Does the level of detail work for you?

In the coming posts we’ll be looking at more complex transformation matrices – and using a GUI to play around with them – but hopefully this introductory post is a reasonably helpful start.

本帖子中包含更多资源

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

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

本版积分规则

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

GMT+8, 2024-11-26 00:40 , Processed in 0.159060 second(s), 18 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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