October 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  










« Generating a mesh for a 3D solid using AutoCAD’s Brep API from .NET | Main | Drawing transient graphics appropriately in AutoCAD within multiple paperspace viewports using .NET »

March 28, 2011

Displaying transient AutoCAD points that respect PDMODE using .NET

Since starting to use the transient graphics API in AutoCAD 2009, I’ve believed that any DBPoints you display would not respect PDMODE (the AutoCAD system variable allowing the user to control how points are displayed). A recent internal discussion was on this very subject, and one of our engineering team, Longchao Jiang, interjected with a comment explaining that only points on the DEFPOINTS layer get displayed without PDMODE being applied.

This was quite a revelation for me: DBPoints apparently get created on DEFPOINTS by default and simply changing them to layer 0 (say) would cause them to respect PDMODE when displayed transiently? I had to try that out.

To do so, I took the code from a couple of recent posts, one gathering points on a 3D object and another using transient graphics to implement a custom MOVE command, and plugged them together. For each of the objects selected to move, the code now gathers points from them and creates and displays these transiently along with the base geometry.

The updates to the point-gathering code were really minor – just about changing the signatures of the functions we want to use to be static and/or public – so we can simply call them from the other source file without much effort. Because the changes were minor, I’ve placed the code here for you to download.

The other source file has changed slightly more, but not very much: we now call into the other file to gather points on each object and pass them through to the modified CreateTransGraphics() function. This function now creates a DBPoint for each point, making sure it is on layer 0 before adding it to our list of transient objects to display.

Here’s the C# code from that file:

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)

      {

        // Gather our points

 

        Point3dCollection pts = new Point3dCollection();

        foreach (ObjectId id in ids)

        {

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

          PointGathering.Commands.CollectPoints(tr, ent, pts);

        }

 

        // Create our transient drawables, with associated

        // graphics, from the selected objects

 

        List<Drawable> drawables =

          CreateTransGraphics(tr, ids, pts);

        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, Point3dCollection pts

    )

    {

      // 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);

      }

 

      // Create a transient point for each member of the collection

 

      foreach (Point3d pt in pts)

      {

        // Make sure each point is on layer 0 (i.e. not DEFPOINTS)

        // and make it red, too

 

        DBPoint dbp = new DBPoint(pt);

        dbp.Layer = "0";

        dbp.ColorIndex = 1;

        drawables.Add(dbp);

      }

 

      // 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();

    }

  }

}

Here’s what happens when we execute the code and move a sphere, with PDMODE set to 0 (you should just be able to make out the points displayed as dots):

Transient graphics showing points as dots

And here’s what happens with PDMODE set to 2 (which looks a bit like something out of an old-school Star Wars video game):

Transient point graphics respecting PDMODE

Very cool – thanks for the tip, Longchao! :-)

I’m now at the airport, heading for Bangalore via Heathrow. I’ll be working from our office there for the coming week, spending time with our team there. Aside from catching up with everyone, I’m looking forward to watching India vs. Pakistan during the 2011 Cricket World Cup semi-final on Wednesday: I’m not really a cricket fan, but watching a match like this in India is a real experience (even if you’re only watching it on TV, as I’ll be doing).

blog comments powered by Disqus

Feed/Share

10 Random Posts