.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Rotating a block into a new orientation in 3D

5 REPLIES 5
SOLVED
Reply
Message 1 of 6
KevinMacDonald
3894 Views, 5 Replies

Rotating a block into a new orientation in 3D

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
5 REPLIES 5
Message 2 of 6
Hallex
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
Message 3 of 6
KevinMacDonald
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
Message 4 of 6


@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.

 

 

 

Message 5 of 6

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
Message 6 of 6

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.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost