Kean Walmsley


  • About the Author
    Kean on Google+

July 2014

Sun Mon Tue Wed Thu Fri Sat
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    








« Using transient graphics to simulate AutoCAD’s MOVE command using .NET | Main | TED, Education and Matrix Mathematics »

March 16, 2011

Adding ortho support to the transient graphics AutoCAD MOVE using .NET

As promised, I ended up burning more that a few hours this evening (after being up very early for meetings this morning), to add orthographic drawing support to the code shown in the last post. It took quite some work, switching between UCS and WCS until my head was more than a little twisted. This thread on The Swamp provided well-needed inspiration, too (thanks, MickD :-).

Here’s the C# code – I would have highlighted the modified lines in red, but the code is too wide to add 3-digit lines without it looking ugly, and – besides – I need to call it a night.

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.GraphicsInterface;

using Autodesk.AutoCAD.Runtime;

using System.Collections.Generic;

using System;

 

namespace CustomMove

{

  public class Commands

  {

    [CommandMethod("MYMOVE", CommandFlags.UsePickSet)]

    public static void CustomMoveCmd()

    {

      Document doc =

        Autodesk.AutoCAD.ApplicationServices.

          Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

      Matrix3d ucs = ed.CurrentUserCoordinateSystem;

 

      // Start by getting the objects to move

      // (will use the pickfirst set, if defined)

 

      PromptSelectionResult psr = ed.GetSelection();

      if (psr.Status != PromptStatus.OK || psr.Value.Count == 0)

        return;

 

      // Create a collection of the selected objects' IDs

 

      ObjectIdCollection ids =

        new ObjectIdCollection(psr.Value.GetObjectIds());

 

      // Ask the user to select a base point for the move

 

      PromptPointResult ppr =

        ed.GetPoint("\nSpecify base point: ");

      if (ppr.Status != PromptStatus.OK)

        return;

 

      Point3d baseUcs = ppr.Value;

 

      CoordinateSystem3d cs = ucs.CoordinateSystem3d;

 

      // Transform from UCS to WCS

 

      Matrix3d mat =

        Matrix3d.AlignCoordinateSystem(

          Point3d.Origin,

          Vector3d.XAxis,

          Vector3d.YAxis,

          Vector3d.ZAxis,

          cs.Origin,

          cs.Xaxis,

          cs.Yaxis,

          cs.Zaxis

        );

      Point3d baseWcs = baseUcs.TransformBy(mat);

      Point3d curPt = baseWcs;

 

      // A local delegate for our event handler so

      // we can remove it at the end

 

      PointMonitorEventHandler handler = null;

 

      // Our transaction

 

      Transaction tr =

        doc.Database.TransactionManager.StartTransaction();

      using (tr)

      {

        // Create our transient drawables, with associated

        // graphics, from the selected objects

 

        List<Drawable> drawables = CreateTransGraphics(tr, ids);

        try

        {

          // Add our point monitor

          // (as a delegate we have access to basePt and curPt,

          //  which avoids having to access global/member state)

 

          handler =

            delegate(object sender, PointMonitorEventArgs e)

            {

              // Get the point, with "ortho" applied, if needed

 

              Point3d pt = e.Context.ComputedPoint;

              if (IsOrthModeOn())

              {

                // Point is in WCS, we need UCS for ortho calcs

 

                Point3d destUcs = pt.TransformBy(mat.Inverse());

                pt = GetOrthoPoint(baseUcs, destUcs);

 

                // Then we transform back to WCS

 

                pt = pt.TransformBy(mat);

              }

 

              // Update our graphics and the current point

 

              UpdateTransGraphics(drawables, curPt, pt);

              curPt = pt;

            };

 

          ed.PointMonitor += handler;

 

          // Ask for the destination, during which the point

          // monitor will be updating the transient graphics

 

          PromptPointOptions opt =

            new PromptPointOptions("\nSpecify second point: ");

          opt.UseBasePoint = true;

          opt.BasePoint = baseUcs;

          ppr = ed.GetPoint(opt);

 

          // If the point was selected successfully...

 

          if (ppr.Status == PromptStatus.OK)

          {

            // ... move the entities to their destination

 

            Point3d destUcs = ppr.Value;

            Point3d dest =

              (IsOrthModeOn() ?

                GetOrthoPoint(baseUcs, destUcs) :

                destUcs);

 

            MoveEntities(tr, baseWcs, dest.TransformBy(mat), ids);

 

            // And inform the user

 

            ed.WriteMessage(

              "\n{0} object{1} moved.", ids.Count,

              ids.Count == 1 ? "" : "s"

            );

          }

        }

        catch (Autodesk.AutoCAD.Runtime.Exception ex)

        {

          ed.WriteMessage("\nException: {0}", ex.Message);

        }

        finally

        {

          // Clear any transient graphics

 

          ClearTransGraphics(drawables);

 

          // Remove the event handler

 

          if (handler != null)

            ed.PointMonitor -= handler;

 

          tr.Commit();

          tr.Dispose();

        }

      }

    }

 

    private static bool IsOrthModeOn()

    {

      // Check the value of the ORTHOMODE sysvar

 

      object orth =

        Autodesk.AutoCAD.ApplicationServices.Application.

        GetSystemVariable("ORTHOMODE");

 

      return Convert.ToInt32(orth) > 0;

    }

 

    private static Point3d GetOrthoPoint(

      Point3d basePt, Point3d pt

    )

    {

      // Apply an orthographic mode

 

      double x = pt.X,

            y = pt.Y,

            z = pt.Z;

 

      Vector3d vec = basePt.GetVectorTo(pt);

      double absX = Math.Abs(vec.X),

            absY = Math.Abs(vec.Y),

            absZ = Math.Abs(vec.Z);

 

      if (absX < absY && absX < absZ)

        x = basePt.X;

      else if (absY < absX && absY < absZ)

        y = basePt.Y;

      else

        z = pt.Z;

 

      return new Point3d(x, y, z);

    }

 

    private static void MoveEntities(

      Transaction tr, Point3d basePt, Point3d moveTo,

      ObjectIdCollection ids

    )

    {

      // Transform a set of entities to a new location

 

      Matrix3d mat =

        Matrix3d.Displacement(basePt.GetVectorTo(moveTo));

      foreach (ObjectId id in ids)

      {

        Entity ent = (Entity)tr.GetObject(id, OpenMode.ForWrite);

        ent.TransformBy(mat);

      }

    }

 

    private static List<Drawable> CreateTransGraphics(

      Transaction tr, ObjectIdCollection ids

    )

    {

      // Create our list of drawables to return

 

      List<Drawable> drawables = new List<Drawable>();

 

      foreach (ObjectId id in ids)

      {

        // Read each entity

 

        Entity ent = (Entity)tr.GetObject(id, OpenMode.ForRead);

 

        // Clone it, make it red & add the clone to the list

 

        Entity drawable = ent.Clone() as Entity;

        drawable.ColorIndex = 1;

        drawables.Add(drawable);

      }

 

      // Draw each one initially

 

      foreach (Drawable d in drawables)

      {

        TransientManager.CurrentTransientManager.AddTransient(

          d, TransientDrawingMode.DirectShortTerm,

          128, new IntegerCollection()

        );

      }

      return drawables;

    }

 

    private static void UpdateTransGraphics(

      List<Drawable> drawables, Point3d curPt, Point3d moveToPt

    )

    {

      // Displace each of our drawables

 

      Matrix3d mat =

        Matrix3d.Displacement(curPt.GetVectorTo(moveToPt));

 

      // Update their graphics

 

      foreach (Drawable d in drawables)

      {

        Entity e = d as Entity;

        e.TransformBy(mat);

 

        TransientManager.CurrentTransientManager.UpdateTransient(

          d, new IntegerCollection()

        );

      }

    }

 

    private static void ClearTransGraphics(

      List<Drawable> drawables

    )

    {

      // Clear the transient graphics for our drawables

 

      TransientManager.CurrentTransientManager.EraseTransients(

        TransientDrawingMode.DirectShortTerm,

        128, new IntegerCollection()

      );

 

      // Dispose of them and clear the list

 

      foreach (Drawable d in drawables)

      {

        d.Dispose();

      }

      drawables.Clear();

    }

  }

}

The code should now work irrespective of non-standard UCS and 3D views being active, supporting ORTHOMODE in 3D, also.

I think the next step is to look at paperspace viewport issues – as raised by Thorsten Meinecke – and probably to compare this implementation with that of a jig. We’ll see about that next week, I expect.

blog comments powered by Disqus

10 Random Posts