Controlling interactive polyline creation - Part 2

During the first part of this series, we looked at ways to drive the PLINE command while retaining (or regaining) the thread of execution in your application.

During this and the next post (yes, I've decided to spread the series a little thinner 🙂 we're going to look at how to completely replace the user-interface to the polyline command, a very useful technique in certain situations. This post focuses on the simple use of GetPoint() to request vertex information from the user; the next post will look at a more advanced technique, the Jig.

Even the "simple" user-interface implemented in the below code takes some effort. To keep things as simple as possible, the below UI code only allows the user to define zero-width, linear polyline segments - no arcs, widths, etc. As mentioned in the previous post, this might well be an advantage in your application, depending on whether you want to hide certain options from the user. This approach is certainly not ideal if you do want to allow interactive selection of arc segments; the two approaches suggested last time, or the one shown in the next entry, would work better in that case.

A few notes on the implementation:

  1. Temporary graphics are used to draw each polyline segment as soon as both its vertices have been defined
  2. The actual polyline entity is only created once all the vertices have been selected
  3. Point selection happens in the User Coordinate System, so we need to do some work to transform selected points to the Entity Coordinate System (or Object Coordinate System) belonging to the polyline. 2-dimensional polylines are planar entities and have their vertices defined as 2D points relative to the origin and normal of the polyline, so we use a "Plane" object to help us get the 2D points to feed to the polyline's AddVertexAt() function

Here's the code in C#:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Colors;

namespace MyPlineApp

{

  public class MyPlineCmds

  {

    [CommandMethod("MYPOLY")]

    public void MyPoly()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;

      // Get the current color, for our temp graphics

      Color col = doc.Database.Cecolor;

      // Create a point collection to store our vertices

      Point3dCollection pts = new Point3dCollection();

      // Set up the selection options

      // (used for all vertices)

      PromptPointOptions opt =

        new PromptPointOptions(

          "\nSelect polyline vertex: "

        );

      opt.AllowNone = true;

      // Get the start point for the polyline

      PromptPointResult res = ed.GetPoint(opt);

      while (res.Status == PromptStatus.OK)

      {

        // Add the selected point to the list

        pts.Add(res.Value);

        // Drag a temp line during selection

        // of subsequent points

        opt.UseBasePoint = true;

        opt.BasePoint = res.Value;

        res = ed.GetPoint(opt);

        if (res.Status == PromptStatus.OK)

        {

          // For each point selected,

          // draw a temporary segment

          ed.DrawVector(

            pts[pts.Count - 1], // start point

            res.Value,          // end point

            col.ColorIndex,     // current color

            false);             // highlighted?

        }

      }

      if (res.Status == PromptStatus.None)

      {

        // Get the current UCS

        Matrix3d ucs =

          ed.CurrentUserCoordinateSystem;

        Point3d origin = new Point3d(0, 0, 0);

        Vector3d normal = new Vector3d(0, 0, 1);

        normal = normal.TransformBy(ucs);

        // Create a temporary plane, to help with calcs

        Plane plane = new Plane(origin, normal);

        // Create the polyline, specifying

        // the number of vertices up front

        Polyline pline = new Polyline(pts.Count);

        pline.Normal = normal;

        foreach (Point3d pt in pts)

        {

          Point3d transformedPt =

            pt.TransformBy(ucs);

          pline.AddVertexAt(

            pline.NumberOfVertices,

            plane.ParameterOf(transformedPt),

            0, 0, 0

          );

        }

        // Now let's add the polyline to the modelspace

        Transaction tr =

          db.TransactionManager.StartTransaction();

        using (tr)

        {

          BlockTable bt =

            (BlockTable)tr.GetObject(

              db.BlockTableId,

              OpenMode.ForRead

            );

          BlockTableRecord btr =

            (BlockTableRecord)tr.GetObject(

              bt[BlockTableRecord.ModelSpace],

              OpenMode.ForWrite

            );

          ObjectId plineId = btr.AppendEntity(pline);

          tr.AddNewlyCreatedDBObject(pline, true);

          tr.Commit();

          ed.WriteMessage("\nPolyline entity is: " +

            plineId.ToString()

          );

        }

      }

      // Clear the temp graphics (polyline should be in

      // the same location, if selection was not cancelled)

      // We could "redraw" instead of "regen" here

      ed.Regen();

    }

  }

}

Here's what happens when we execute this code:

Command: mypoly

Select polyline vertex:

Select polyline vertex:

Select polyline vertex:

Regenerating model.

Polyline entity is: (2130239560)

Next time we'll look at how we can use a Jig for this task.

17 responses to “Controlling interactive polyline creation - Part 2”

  1. I'm using AutoCAD 2006 but it doesn't know these methods:
    ed.DrawVector()
    ed.Regen()

    Are they for AutoCAD 2007?

    Thank you

  2. Hi Babak,

    Yes - once again I've been using AutoCAD 2007 and haven't checked the code against prior versions. If upgrading is not an option, you should be able to use P/Invoke to call ads_regen() and acedGrDraw(). See this post for instructions on where to start:

    keanw.com/...

    If someone manages to call these functions effectively from 2006, perhaps they can post the function declarations changes as a comment...

    Regards,

    Kean

  3. I'm working on a Jig Polyline function. now I plan to add keyword ("UNDO") but I failed. could you have a sample code for it?

    Thank you

  4. Hi Wesley,

    Check out this post:

    keanw.com/...

    Regards,

    Kean

  5. Hi Kean,
    really a great job!!

    I'm a beginner in autocad developing and I need to create a commandmethod to perform a rotation with an entity (a polyline-rectangle) in which the rotation point is the centre of the rectangle... is it possible?

    Thanx in advance...
    Flea

  6. Hi Flea,

    Yes, it's quite possible... the tricky piece will be determining the "center" of an arbitrary closed polyline. One "easy" way would be to generate a region from the polyline and extrude it (probably by a very small, or zero amount, if it lets you do that), and then get the mass properties to get the centroid. To be sure you may want to project the point back on the polyline's plane.

    As you can probably tell, I've never tried this myself.

    If you can assume the polyline is rectangular, then things are easier: you can get the intersection of diagonal lines going between opposite corners. These lines needn't be added to a drawing.

    Good luck!

    Kean

  7. Oh, and then "all" you need to so is to create a jig to perform the rotation, assuming you want visual feedback...

    Kean

  8. Hi Kean,

    I found the center point getting the intersection of the diagonal lines and performed the rotation statically for each of the four vertices in this way:

    Point2d ptToRotate = pline.GetPoint2dAt(i);
    pline.SetPointAt(i, ptToRotate.RotateBy(Convert.ToDouble((180 * angRes.Value) / Math.PI), ptCenter));

    Now my problem is that, after the first rotation, following rotations will rotate the rectangle by a prompted angle without considering the actual angle of the rectangle, but only relating to the center point. In practice, if I've already rotated my rect by 180° and now select a 270° angle, I need that my rect rotate only by 90° (the difference between angle selected and actual angle), and not by 270° (like it actually do).

    Thanks in advance for your kindness.

    Flea

  9. That's tricky: the rectangle doesn't have any notion of a rotation angle - it's a polyline between points. So you need to either create a custom rectangle object or attach the current rotation angle as XData and pick it up from there.

    Kean

  10. Hi Flea,

    Just to follow up - I posted something that may be of interest to you...

    Using a jig to rotate an AutoCAD entity via .NET

    Regards,

    Kean

  11. Hi Kean
    Just looking around and this post was found on "mass properties". I am trying to find how to get the mass properties for a region (specifically the centroid and principal moments). It can possibly be done by manually running (send command) the massprop command, writing the output to a text file, then pick up the text file and stripping it down. Not too slick though. Is there any way in c# to get the mass props directly??

  12. Hi Greg,

    This DevNote shows how to trap the output of the MASSPROPS command by temporarily replacing the HostApplicationServices object with your own for the duration of the command. It uses ObjectARX (C++) - I don't know if this is possible from C#... you can implement HostApplicationServices objects from C# for use with RealDWG, so I'm hopeful it is.

    I'll think about turning this into a post - would be a useful one to show.

    Kean

  13. It turns out HostApplicationServices doesn't have the overrides you need to do this from C#. So you'd need to use C++ (or stick to parsing the file) to achieve this.

    Kean

  14. Hi,
    Is it possible to convert coordinates from one coordinate system to another? ex. GEOGCS to Utm

    Pradeep

  15. Pradeep -

    This sounds like functionality that is present in Map, but not base AutoCAD (at least not that I'm aware of).

    Kean

  16. i like you posts.
    ijust want to know how to draw a line with jig and ortho on. i tried it but the ortho is not working
    jigOpts.UserInputControls = (UserInputControls.GovernedByOrthoMode)
    please let me know

  17. There should be a number of posts showing jigs with this setting (although perhaps not specifically for lines).

    Otherwise please post your request to the AutoCAD .NET Discussion Group.

    Kean

Leave a Reply to Pradeep Cancel reply

Your email address will not be published. Required fields are marked *