找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 1278|回复: 1

[分享] A simple command to perform a matrix transformation on entity

[复制链接]

已领礼包: 859个

财富等级: 财运亨通

发表于 2014-4-28 19:06:03 来自手机 | 显示全部楼层 |阅读模式

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

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

×
本帖最后由 csharp 于 2014-4-28 21:28 编辑

http://through-the-interface.typepad.com/through_the_interface/2010/12/a-simple-command-to-perform-a-matrix-transformation-on-an-autocad-entity-using-net.html

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.
2000
0200
0020
0001
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.
20005
0200*5
00200
00011
Now if we follow the rules of matrix multiplication, we can see that our resultant point is calculated like this:
abcdra*r + b*s + c*t + d*u
efgh*s=e*r + f*s + g*t + h*u
ijklti*r + j*s + k*t + l*u
nopqun*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:
2000510 + 0 + 0 + 0
0200*5=0 + 10 + 0 + 0
002000 + 0 + 0 + 0
000110 + 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:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System.Reflection;

namespace Transformer
{
  public class Commands
  {
    [CommandMethod("TRANS", CommandFlags.UsePickSet)]
    static public void TransformEntity()
    {
      Document doc =
        Application.DocumentManager.MdiActiveDocument;
      Database db = doc.Database;
      Editor ed = doc.Editor;

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

      ObjectId id;

      // First query the pickfirst selection set

      PromptSelectionResult psr = ed.SelectImplied();
      if (psr.Status != PromptStatus.OK || psr.Value == null)
      {
        // If nothing selected, ask the user

        PromptEntityOptions peo =
          new PromptEntityOptions(
            "\nSelect entity to transform: "
          );
        PromptEntityResult per = ed.GetEntity(peo);
        if (per.Status != PromptStatus.OK)
          return;
        id = per.ObjectId;
      }
      else
      {
        // If the pickfirst set has one entry, take it

        SelectionSet ss = psr.Value;
        if (ss.Count != 1)
        {
          ed.WriteMessage(
            "\nThis command works on a single entity."
          );
          return;
        }
        ObjectId[] ids = ss.GetObjectIds();
        id = ids[0];
      }

      PromptResult pr = ed.GetString("\nEnter property name: ");
      if (pr.Status != PromptStatus.OK)
        return;

      string prop = pr.StringResult;

      // Now let's ask for the matrix string

      pr = ed.GetString("\nEnter matrix values: ");
      if (pr.Status != PromptStatus.OK)
        return;

      // Split the string into its individual cells

      string[] cells = pr.StringResult.Split(new char[] { ',' });
      if (cells.Length != 16)
      {
        ed.WriteMessage("\nMust contain 16 entries.");
        return;
      }

      try
      {
        // Convert the array of strings into one of doubles

        double[] data = new double[cells.Length];
        for (int i = 0; i < cells.Length; i++)
        {
          data = double.Parse(cells);
        }

        // Create a 3D matrix from our cell data

        Matrix3d mat = new Matrix3d(data);

        // Now we can transform the selected entity

        Transaction tr =
          doc.TransactionManager.StartTransaction();
        using (tr)
        {
          Entity ent =
            tr.GetObject(id, OpenMode.ForWrite)
            as Entity;
          if (ent != null)
          {
            bool transformed = false;

            // If the user specified a property to modify

            if (!string.IsNullOrEmpty(prop))
            {
              // Query the property's value

              object val =
                ent.GetType().InvokeMember(
                  prop, BindingFlags.GetProperty, null, ent, null
                );

              // We only know how to transform points and vectors

              if (val is Point3d)
              {
                // Cast and transform the point result

                Point3d pt = (Point3d)val,
                        res = pt.TransformBy(mat);

                // Set it back on the selected object

                ent.GetType().InvokeMember(
                  prop, BindingFlags.SetProperty, null,
                  ent, new object[] { res }
                );
                transformed = true;
              }
              else if (val is Vector3d)
              {
                // Cast and transform the vector result

                Vector3d vec = (Vector3d)val,
                        res = vec.TransformBy(mat);

                // Set it back on the selected object

                ent.GetType().InvokeMember(
                  prop, BindingFlags.SetProperty, null,
                  ent, new object[] { res }
                );
                transformed = true;
              }
            }

            // If we didn't transform a property,
            // do the whole object

            if (!transformed)
              ent.TransformBy(mat);
          }
          tr.Commit();
        }
      }
      catch (Autodesk.AutoCAD.Runtime.Exception ex)
      {
        ed.WriteMessage(
          "\nCould not transform entity: {0}", ex.Message
        );
      }
    }
  }
}

Now let’s use the TRANS command to transform a couple of entities:
0.jpg
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:
1.jpg
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.


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

已领礼包: 859个

财富等级: 财运亨通

 楼主| 发表于 2014-4-29 07:12:43 | 显示全部楼层
其中一句要修改如下
                for (int i = 0; i < cells.Length; i++)
                {
                    data[i] =  System .Convert .ToDouble( cells[i]);
                }
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-12-18 23:50 , Processed in 0.479941 second(s), 32 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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