I am having trouble calculating the angle between each of the axes of a blocks local coordinate system and the WCS. Since there is no method I am aware of to retrieve the WCS, I am just using Matrix3d.Identity to represent the WCS because the two should always be equivalent. Here is he code I am currently using to calculate the angle between each axis:
Dim transform As Matrix3d = DynamicBlockHelper.GetBlockTransform(BlockObjID) Dim angleX As Double = transform.CoordinateSystem3d.Xaxis.GetAngleTo(Matrix3d.Identity.CoordinateSystem3d.Xaxis) Dim angleY As Double = transform.CoordinateSystem3d.Yaxis.GetAngleTo(Matrix3d.Identity.CoordinateSystem3d.Yaxis) Dim angleZ As Double = transform.CoordinateSystem3d.Zaxis.GetAngleTo(Matrix3d.Identity.CoordinateSystem3d.Zaxis)
Here is the source for the method used to retrieve a block's local coordinate system in the code above:
Public Shared Function GetBlockTransform(BlockObjectID As ObjectId) As Matrix3d Dim result As Matrix3d = Nothing Dim doc As Document = ApplicationServices.Application.DocumentManager.GetDocument(BlockObjectID.Database) Using lock As DocumentLock = doc.LockDocument() Using trans As Transaction = BlockObjectID.Database.TransactionManager.StartTransaction Try Using blockRef As BlockReference = BlockObjectID.GetObject(OpenMode.ForRead) result = blockRef.BlockTransform End Using Catch ex As System.Exception Try EventLog.WriteEntry("DynamicBlockHelper.GetDynamicBlockTransform", ex.GetType().Name + ": " + ex.Message + Environment.NewLine + ex.StackTrace, EventLogEntryType.Error) Catch eex As System.Exception End Try End Try End Using End Using Return result End Function
The resulting angles are clearly incorrect, none of which even matches the value of the Rotation member of the BlockReference class for the given block. My aim is to store these angles, delete the block reference, and recreate the block reference and restore the position and rotation (x,y,z) of the new block. This is part of some code I have written to programatically redefine dynamic blocks, which requires that the block references be deleted, then the block definition be updated, then the block references be recreated and their state restored. The code works fine, except for the rotation angles.
Thank you,
Kevin
Solved! Go to Solution.
Solved by _gile. Go to Solution.
Hi,
I'm not sure to understand what you're looking for.
Anyway, instead of: Matrix3d.Identity.CoordinateSystem.Xaxis, you can simply use: Vector3d.Xaxis.
You should also have a look at the overloaded GetAngleTo(vector, referenceVector) method which returns the angle in the range of [0, 2*PI] (instead of [0, PI]).
Vector3d transformXaxis = transform.CoordinateSystem3d.Xaxis; double angleX = Vector3d.XAxis.GetAngleTo( transformXaxis, Vector3d.XAxis.CrossProduct(transformXaxis));
When I rotate about a point and the Z-Axis in AutoCAD, the result is that the vectors for both the X and Y axes are rotated by the angle given. This explains the strange results I have been experiencing where a rotation about Z results in my block being rotated double that amount when I run the code that follows, though it makes sense to represent the displacement in this way. It has been a long time since I have actively used 3D Geometry/Matrix Math/Linear Algebra. I'm sorry if my question is silly.
How would you suggest I use the 3 angles calculated to transform a block and return it to the original orientation. Currently I am doing the following, though I have eliminated some of my code in favor of showing only the API calls that seem relevant and used to create a transformation matrix, then use it to transform a block:
Dim insertionPoint As New Point3d(state.Translate.X, state.Translate.Y, state.Translate.Z) Dim transformX As Matrix3d = Matrix3d.Rotation(state.Rotate.X, Vector3d.XAxis, insertionPoint) Dim transformY As Matrix3d = Matrix3d.Rotation(state.Rotate.Y, Vector3d.YAxis, insertionPoint) Dim transformZ As Matrix3d = Matrix3d.Rotation(state.Rotate.Z, Vector3d.ZAxis, insertionPoint) Dim transformXYZ As Matrix3d = transformX * transformY * transformZ Dim blockRef As BlockReferency(insertionPoint, objID) blockRef.TransformBy(transformXYZ)
Where State.Rotate contains the three angles calculated for X,Y, and Z. And state.Translate is just the X,Y, and Z position taken from the block's coordinate system.
Perhaps I should not calcuate the angles for each axis, but instead store the three vectors in the block's local coordinate system? If so, how would I correctly use the three vectors to initialize a block's coordinate system and restore it to the original orientation?
Thanks,
Kevin
It's quite difficult to use rotations about multiple axis (a rotation of 30° about Z and then a rotation of 20° about X isn't the same as a rotation of 20° about X and then a rotation of 30° about Z).
This is the reason why AutoCAD (and most of the 3d softwares) prefer describe 3d transformations with a plane transformation in which only rotations about the Z axis of this plane are considered. This is how works the Object Coordinates System (oCS) of many autoCAD enities (bloc references,circles, polylines, texts, ...). I tried to explain this with my poor English to TheSwamp LISPers here (you'd have to register, if not done yet).
So a block reference geometry (excuding scale transformations) is completely dercribed with its position (WCS coordinates of the insertion point), its Normal (the z axis of the block OCS) and its Rotation (angle measured from the OCS X axis about the OCS Z axis). The BlockTransform Matrix3d is the combination of these transformations (more the scales one).
AutoCAD .NET provides many APIs to deal with these transformations with the Matrix3d methods.
If you explain what you want to do instead of how you try to do it, perhaps we can find a simpler route.
Simply put, I want to store the position and orientation of a 3d block, and then later restore a block with an identical definition to the same position and orientation. Likely my own lack of proper vocabulary for this area of mathematics is more to blame than your English. I have had no trouble reading your responses, and I am very grateful for your time.
If you can provide me with code to store in floating point variables the position and orientation (by this I simply mean the end result of the any rotation applied to the block reference, as represented by its "OCS"). And code to use the values of the variables previously defined to apply the exact location and rotation in 3d space of another block reference of the same definition, I will have all that I need.
I completely understand your point about order of operations affecting resulting position and orientation when dealing with linear transformations. Although I have no AutoCAD specific knowledge in this area, I did tinker with 3D realtime rendering APIs like DirectX in college, but that was a decade ago. I do not need the transformation stack applied to the block reference to cause its final position and rotation. I just care about applying the final position and rotation to another block reference of the same type, so that the block reference's geometry is in the exact location of the original block.
Thank you again for your time and patience.
Best regards,
Kevin
I think I found a solution on my own. In hindsight I feel foolish for even asking this question. I should have looked more closely at the Matrix3d class.
I used the Matrix3d.ToArray() method to retrieve an array of doubles which I assume represent the elements of the matrix. I then just pass the array of doubles returned by ToArray to the constructor of the Matrix3d class. I then pass the initialized Matrix3d instance to Entity.TransformBy. This seems to work exactly as I need it to. It is worth noting that the block definition to which the transform is applied should be aligned with the WCS when using this approach.
Do you see any problem with this method?
Thanks again,
Kevin
Sorry for not responding; I've been off work for a few days.
I made solving this problem much more complicated than it needed to be. I'm still at a novice level of understanding about many areas of the ACAD .net APIs. Sorry if I gave you a headache with this question. I can see how poorly constructed my question was now. In the future I will think more carefully before posting questions. I try not to make a habit of asking for help, prefering instead to rely on documentation and existing forum threads and the like.
To answer your most recent question, the reason I wanted the matrix represented as a set of scalar values is that I have a need to serialize the data for later use. The matrix3d class does not appear to be serializable, though I have not looked into this. I just assumed it wasn't serializable because of the way the class is constructed.
Thanks again for your help,
Kevin