.NET

Reply
Valued Contributor
KevinMacDonald
Posts: 75
Registered: ‎12-15-2012
Message 1 of 5 (633 Views)
Accepted Solution

Rotating a block into a new orientation in 3D

633 Views, 4 Replies
04-21-2013 06:09 PM

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!

Inventor 2013

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.

 

 

 

*Expert Elite*
Hallex
Posts: 1,569
Registered: ‎10-08-2008
Message 2 of 5 (604 Views)

Re: Rotating a block into a new orientation in 3D

04-21-2013 11:36 PM in reply to: KevinMacDonald
I didn't know much about BlockTransform,
perhaps this example may heps:
http://exchange.autodesk.com/autocadarchitecture/enu/online-help/browse#WS1a9193826455f5ff2566ffd511...
_____________________________________
C6309D9E0751D165D0934D0621DFF27919
Valued Contributor
KevinMacDonald
Posts: 75
Registered: ‎12-15-2012
Message 3 of 5 (600 Views)

Re: Rotating a block into a new orientation in 3D

04-21-2013 11:45 PM in reply to: Hallex

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.

Inventor 2013
Valued Mentor
DiningPhilosopher
Posts: 370
Registered: ‎05-06-2012
Message 4 of 5 (586 Views)

Re: Rotating a block into a new orientation in 3D

04-22-2013 06:55 AM in reply to: KevinMacDonald

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.

 

 

 

Valued Contributor
KevinMacDonald
Posts: 75
Registered: ‎12-15-2012
Message 5 of 5 (559 Views)

Re: Rotating a block into a new orientation in 3D

04-23-2013 12:33 PM in reply to: DiningPhilosopher

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:

  1. Specify the block you are inserting by name.
  2. Specify an insertion point for the block.
  3. Specify a second arbitrary 3D point that will be used to align an axis of the block to.
  4. Specify one of X/Y/Z axes on the block to align to a line between the insertion point and the second 3D point.

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;
            }
        }
    }
}

 

Inventor 2013
Announcements
Are you familiar with the Autodesk Expert Elites? The Expert Elite program is made up of customers that help other customers by sharing knowledge and exemplifying an engaging style of collaboration. To learn more, please visit our Expert Elite website.
Need installation help?

Start with some of our most frequented solutions or visit the Installation and Licensing Forum to get help installing your software.