I am trying to write a tool using the .NET API (C#) that will allow me to select a block that currently exists in the drawing, and then select a line, and the tool would calculate the necessary axis of rotation and the amount of rotation to rotate the block into the same orientation as the line. Both the block and the line might be skewed in 3 dimensions. The use case for a tool like this might be for drawing a tubular space frame. I would first define nodes and center lines for the frame, and then have a block of a standard structural member. I want to insert that member multiple times and "snap" it to a center line. I tried using Alignment Parameters. However, these don't seem to work in all cases. I need to (or rather, SHOULD be able to) create a tool that allows me to re-align an existing block currently inserted into the drawing into a new orientation in 3D using any arbitrary line as a reference line. Clearly this can be done because it is done all the time in Inventor. A similar feature seems to not exist in Autocad.
In order to do this I believe I need to choose some vector to use as a reference within the block. For example, I might choose the BlockReference.BlockTransform.CoordinateSystem3d.Xaxis, and then rotate this vector so that it is equal to whatever reference line I have chosen. The problem I am encountering (and that I think many have encountered) is in understanding how to use the BlockTransform correctly to arrive at the points I need to do the necessary vector calculations to determine the axis of rotation and the degree of rotation such that I can call the BlockReference.TransformBy() method correctly and reliably.
Any suggestions or code examples? Many many posts say to "Use BlockTransform" but then don't go into any detail. There seem to me many pitfalls around when one can use the BlockTransform matrix, and on which objects, and what the results are likely to be based on one's current UCS etc.
Thanks!
Solved! Go to Solution.
Solved by DiningPhilosopher. Go to Solution.
That link is one of many examples out there; thanks. However, it's no substitute for documentation. It's very hard to extrapolate from such examples what is going one with coordinate systems and and transformations. So far I am finding that a very lengthy reverse engineering process via debugging is what I have to do to understand what's happening.
@KevinMacDonald wrote:
In order to do this I believe I need to choose some vector to use as a reference within the block. For example, I might choose the BlockReference.BlockTransform.CoordinateSystem3d.Xaxis, and then rotate this vector so that it is equal to whatever reference line I have chosen. The problem I am encountering (and that I think many have encountered) is in understanding how to use the BlockTransform correctly to arrive at the points I need to do the necessary vector calculations to determine the axis of rotation and the degree of rotation such that I can call the BlockReference.TransformBy() method correctly and reliably.
Thanks!
The current UCS has nothing to do with the operation you're trying to perform. The BlockTransform property describes the transformation from the coordinate system of objects in the block's definition, to the WCS of the space into which a reference to the block is inserted. You can use the matrix BlockTransform returns to transform points, vectors, or entities from the block definition's coordinate system to the WCS of the space that contains the block reference. So, if (for example) you wanted to use the x-axis vector (0,1,0) of the block's model coordinate system as a reference vector, you call that vector's TransformBy() method and pass it the matrix returned by BlockTransform.
Well, that was educational. I managed to write the tool I wanted in .NET. As I suspected the amount of code necessary wound up being fairly small. This tool allows you to:
The complete code is below. The command is INSERTANDROTATEBLOCK3D. I used code I found on line as a starting point. I've also attached the .NET assembly which can be NETLOADed into Autocad.
Thanks for the suggestions. My understanding of coordinate systems and transformations in Autocad is much improved.
using System; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; using AcAp = Autodesk.AutoCAD.ApplicationServices.Application; [assembly: CommandClass(typeof(_3DRotateBlock.InsertAndRotateBlock3D))] namespace _3DRotateBlock { public class InsertAndRotateBlock3D { enum AxisSelection { X, Y, Z } private static string _blockName; [CommandMethod("InsertAndRotateBlock3D")] public void Test() { Document doc = AcAp.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; PromptStringOptions pso = new PromptStringOptions("\nEnter a block to insert and rotate: "); pso.AllowSpaces = false; if (_blockName != null) { pso.DefaultValue = _blockName; } PromptResult pr = ed.GetString(pso); if (pr.Status != PromptStatus.OK) { return; } _blockName = pr.StringResult; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); if (!bt.Has(_blockName)) { AcAp.ShowAlertDialog(string.Format("Can't find block named '{0}'.", _blockName)); return; } using (BlockReference br = new BlockReference(Point3d.Origin, bt[_blockName])) { InsertBlockJig insJig = new InsertBlockJig(br); pr = ed.Drag(insJig); if (pr.Status != PromptStatus.OK) { return; } BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); btr.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); PromptPointOptions ptopts = new PromptPointOptions("\nSelect a point to align the local X Axis of the block to: "); ptopts.BasePoint = br.Position; ptopts.UseDashedLine = true; PromptPointResult ptRes = ed.GetPoint(ptopts); Point3d alignTo = ptRes.Value; if (ptRes.Status == PromptStatus.Cancel) { ed.WriteMessage("Cancelling"); return; } AxisSelection axisToUse = GetAxisSelection(ed); //Transform this point from UCS to WCS. Point3d alignmentPt = alignTo.TransformBy(ed.CurrentUserCoordinateSystem); //This assumes that some interesting reference line of the block lies along its X axis. Vector3d currentBlockXAxis; switch (axisToUse) { case AxisSelection.Y: currentBlockXAxis = br.BlockTransform.CoordinateSystem3d.Yaxis.TransformBy(br.BlockTransform); break; case AxisSelection.Z: currentBlockXAxis = br.BlockTransform.CoordinateSystem3d.Zaxis.TransformBy(br.BlockTransform); break; default: //X axis currentBlockXAxis = br.BlockTransform.CoordinateSystem3d.Xaxis.TransformBy(br.BlockTransform); break; } //The final alignment of the block in WCS. Vector3d finalBlockOrientation = new Vector3d((alignmentPt - br.Position).ToArray()); //The axis of rotation is a vector normal to these two vectors, allowing the alignment to be done in a single rotation. Vector3d axisOfRotation = currentBlockXAxis.CrossProduct(finalBlockOrientation); //The amount of rotation is the included angle between the above two vectors. double rotation = currentBlockXAxis.GetAngleTo(finalBlockOrientation); br.TransformBy(Matrix3d.Rotation(rotation, axisOfRotation, br.Position)); tr.Commit(); } } } static AxisSelection GetAxisSelection(Editor ed) { PromptStringOptions stropts = new PromptStringOptions("\nChoose axis on block to align: X/Y/Z"); stropts.DefaultValue = "X"; stropts.AllowSpaces = false; string selection = ed.GetString(stropts).StringResult.ToUpper(); try { return (AxisSelection)AxisSelection.Parse(typeof(AxisSelection), selection); } catch { return AxisSelection.X; } } class InsertBlockJig : EntityJig { Point3d _dragPt; BlockReference _br; public InsertBlockJig(BlockReference br) : base(br) { _br = br; _dragPt = br.Position; } protected override SamplerStatus Sampler(JigPrompts prompts) { string msg = "\nSpecify insertion point:"; JigPromptPointOptions jppo = new JigPromptPointOptions(msg); jppo.UserInputControls = (UserInputControls.Accept3dCoordinates | UserInputControls.NullResponseAccepted); PromptPointResult ppr = prompts.AcquirePoint(jppo); if (_dragPt == ppr.Value) { return SamplerStatus.NoChange; } else { _dragPt = ppr.Value; } return SamplerStatus.OK; } protected override bool Update() { _br.Position = _dragPt; return true; } } class RotateBlockJig : EntityJig { private BlockReference _br; private double _rot, _ucsRot; public RotateBlockJig(BlockReference br, double ucsRot) : base(br) { _br = br; _ucsRot = ucsRot; } protected override bool Update() { _br.Rotation = _rot + _ucsRot; return true; } protected override SamplerStatus Sampler(JigPrompts prompts) { JigPromptAngleOptions jpao = new JigPromptAngleOptions("\nSpecify the rotation: "); jpao.DefaultValue = 0.0; jpao.UseBasePoint = true; jpao.BasePoint = _br.Position; jpao.Cursor = CursorType.RubberBand; jpao.UserInputControls = ( UserInputControls.Accept3dCoordinates | UserInputControls.NullResponseAccepted); PromptDoubleResult pdr = prompts.AcquireAngle(jpao); if (_rot == pdr.Value) { return SamplerStatus.NoChange; } else { _rot = pdr.Value; return SamplerStatus.OK; } } // update for 'default' internal void UpdateRotation() { _br.Rotation = _rot + _ucsRot; } } } }
I apologize for the question in advance,i'm in the learning stage. any guidance to implementing your code for someone using custom code for first time? i'm looking at tutorials for lisp, etc. and familiar with VB. I just need steps for loading and using code for the first time, again any guidance would be appreciated
Can't find what you're looking for? Ask the community or share your knowledge.