Through the Interface: Displaying transient AutoCAD points that respect PDMODE using .NET

Kean Walmsley

May 2015

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


« 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 =



      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)



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



      Point3d baseUcs = ppr.Value;


      CoordinateSystem3d cs = ucs.CoordinateSystem3d;


      // Transform from UCS to WCS


      Matrix3d mat =











      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 =


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



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



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


            // And inform the user



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

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




        catch (Autodesk.AutoCAD.Runtime.Exception ex)


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




          // Clear any transient graphics




          // Remove the event handler


          if (handler != null)

            ed.PointMonitor -= handler;








    private static bool IsOrthModeOn()


      // Check the value of the ORTHOMODE sysvar


      object orth =




      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;


        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 =


      foreach (ObjectId id in ids)


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





    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;




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




      // Draw each one initially


      foreach (Drawable d in drawables)



          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 =



      // Update their graphics


      foreach (Drawable d in drawables)


        Entity e = d as Entity;




          d, new IntegerCollection()





    private static void ClearTransGraphics(

      List<Drawable> drawables



      // Clear the transient graphics for our drawables




        128, new IntegerCollection()



      // Dispose of them and clear the list


      foreach (Drawable d in drawables)








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


10 Random Posts