Reversing the direction of an AutoCAD polyline using .NET

This question came in by email, last week:

I'm trying to reverse the direction of a polyline thought the API, but I didn't find something in the documentation nor in the web. (Even nothing on your blog.) Can you help me?

I also didn't find anything in the public API, although that doesn't mean there isn't something I've missed (I seem to be on a bit of a roll in that respect, lately :-S :-).

A couple of thoughts/comments on this problem:

  • Ideally we don't want to create a brand new object, as on the one hand there may be properties we forget to copy across (extension dictionary contents, XData, etc.) but also because we'd need to worry about identity (making sure we use HandOverTo() to maintain the object's handle, etc., for other systems tham might depend upon it)
    • This seems altogether far too much like hard work, so we'll try to manipulate the vertex data directly
  • In this implementation we'll focus on the Polyline class, rather than creating a more general solution that handles Polyline2d and Polyline3d
    • These "complex" objects have references to separate vertex objects, which actually means it may not be much more difficult to support them (perhaps it's even easier?), but I just haven't looked into it as it wasn't part of the original request
    • Please post a comment if this is something you're interested in

Here's some code that extracts vertex information from a Polyline (storing it in a list of PerVertexData struct instances – with hindsight I might have named that struct differently, although it's harmless enough if you parse it using capital letters to separate words/parts of words 🙂 and then using that information in reverse to set the vertex information as we want it.

It's worth bearing in mind that the bulge is held on the index of the segment rather than the vertex, so when gathering the bulge we need to pick up the bulge of the index before for it to work properly.

Here's the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

using System.Collections.Generic;

 

namespace PolylineEditing

{

  public class Commands

  {

    // Data to be stored for each polyline vertex

 

    struct PerVertexData

    {

      public Point2d pt;

      public double bulge;

      public double startWidth;

      public double endWidth;

    }

 

    [CommandMethod("RPD")]

    static public void ReversePolylineDirection()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      var ed = doc.Editor;

 

      var peo =

        new PromptEntityOptions(

          "\nSelect polyline to reverse"

        );

      peo.SetRejectMessage("Must be a polyline.");

      peo.AddAllowedClass(typeof(Polyline), false);

 

      var per = ed.GetEntity(peo);

 

      if (per.Status != PromptStatus.OK)

        return;

 

      var tr = doc.TransactionManager.StartTransaction();

      using (tr)

      {

        var obj = tr.GetObject(per.ObjectId, OpenMode.ForRead);

        var pl = obj as Polyline;

 

        if (pl != null)

        {

          // Collect our per-vertex data

 

          List<PerVertexData> vertData =

            new List<PerVertexData>(pl.NumberOfVertices);

 

          for (int i = 0; i < pl.NumberOfVertices; i++)

          {

            PerVertexData pvd = new PerVertexData();

            pvd.bulge = (i > 0 ? pl.GetBulgeAt(i - 1) : 0);

            pvd.startWidth = (i > 0 ? pl.GetStartWidthAt(i - 1) : 0);

            pvd.endWidth = (i > 0 ? pl.GetEndWidthAt(i - 1) : 0);

            pvd.pt = pl.GetPoint2dAt(i);

 

            vertData.Add(pvd);

          }

 

          // Now let's make sure we can edit the polyline

 

          pl.UpgradeOpen();

 

          // Write the data back to the polyline, but in

          // reverse order

 

          for (int i = 0; i < pl.NumberOfVertices; i++)

          {

            PerVertexData pvd =

              vertData[pl.NumberOfVertices - (i + 1)];

            pl.SetPointAt(i, pvd.pt);

            pl.SetBulgeAt(i, -pvd.bulge);

            pl.SetStartWidthAt(i, pvd.endWidth);

            pl.SetEndWidthAt(i, pvd.startWidth);

          }

        }

        tr.Commit();

      }

    }

  }

}

When running the code against a standard Polyline, you shouldn't see any difference (unless it has a linetype that happens to indicate the direction), but you can double-check using the PEDIT command, and checking which vertex gets highlighted when you edit a vertex.

I think this implementation is solid enough, but there may yet be something I've missed. For instance: the ECS isn't being updated – unless it's happening behind the scenes – so I suppose the vertex data is always relative to the first vertex added, even when the direction is reversed and that happens to be the "last" vertex… it's not clear this is necessarily an issue, but I can imagine it could be, depending on what assumptions have been made in people's code.

If anyone has a situation this approach doesn't work for, please do post a comment and I'll look into it a bit more deeply.

Update:

Many thanks to Mark Dubbelaar for pointing out the Curve.ReverseCurve() method, which works perfectly on all manner of polyline.

Here's the updated C# code which can now handle various types of Curve:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

 

namespace CurveEditing

{

  public class Commands

  {

    [CommandMethod("RCD")]

    static public void ReverseCurveDirection()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      var ed = doc.Editor;

 

      var peo =

        new PromptEntityOptions(

          "\nSelect curve to reverse"

        );

      peo.SetRejectMessage("Must be a curve.");

      peo.AddAllowedClass(typeof(Curve), false);

 

      var per = ed.GetEntity(peo);

 

      if (per.Status != PromptStatus.OK)

        return;

 

      var tr = doc.TransactionManager.StartTransaction();

      using (tr)

      {

        var obj = tr.GetObject(per.ObjectId, OpenMode.ForWrite);

        var cur = obj as Curve;

 

        if (cur != null)

        {

          try

          {

            cur.ReverseCurve();

          }

          catch

          {

            ed.WriteMessage(

              "\nCould not reverse object of type {0}.",

              cur.GetType().Name

            );

          }

        }

 

        tr.Commit();

      }

    }

  }

}

The ReverseCurve() call is enclosed in a try-catch block because for some classes (such as Arc) this method remains unimplemented. You could also choose to restrict the classes that can be selected – to avoid it being called on classes that are derived from Curve but don't contain an implementation of the method – but for simplicity I've handled it this way.

14 responses to “Reversing the direction of an AutoCAD polyline using .NET”

  1. If you're feeling lazy, you could also P/Invoke acedCommand and invoke the PEDIT command REVERSE option. (Although one always has to be careful when P/Invoking acedCommand from .NET).

  2. There is the 'ReverseCurve' method from the Curve class

  3. So that's where it was hiding...

    I even saw it in the ObjectBrowser when I searched on "reverse", but discarded it mentally ("surely it can't be that easy?"). Ah well, the roll continues.

    Thanks, Mark - I'll update the post.

    Kean

  4. Be careful when trying to use the ReverseCurve method on the "complex" entities. It doesn't work with non-database-resident entities (i.e. those not placed in the drawing).
    I tested only Polyline3d, but I suppose Polyline 2d works the same.

  5. Thanks, Matus.

    This example is safe enough, then, but it's good to be aware of the method's limitations.

    Kean

  6. Glad someone pointed out the built-in solution, as just looking at your manual attempt at it, there's a bug there.

    I'll just leave you with this hint: When we reverse something that's linear, the end becomes the start and visa-verse.

  7. Fixed. I would have hoped that it was clear from the wording of the post that I'd expected there to be issues with the implementation. So it doesn't come as a great surprise that there was a minor issue such as this to address.

    On the one hand I'd like to thank you for pointing the issue out, but on the other hand I admit to finding it a little petty that you'd choose to do so in such vague terms.

    And it's vice versa, by the way.

    Kean

  8. Sorry, I didn't think the hint would have required so much head-scratching, I mean, it was obvious from just looking at the code alone.

  9. Kean: is there a way to tell which direction a polyline is going (clockwise or counter clockwise), so we can decide to reverse it or not?

    Tony: start a blog! Always seeing you criticize others efforts is getting old IMO. Follow the example of all you criticize; spend the extra effort put your own examples out there so we can all learn.

    Thanks!

  10. For polylines, in particular, it's tricky to calculate a simple direction property. You'd presumably want to making sure it's not self-intersecting and maybe even closed before doing some further analysis.

    What kind of polylines do you care about? I'm on vacation, this week, but could take a look next week, in case.

    Kean

  11. LWPolyline. Appreciate the response! Enjoy the vacate!

  12. For those checking out the link above.. the link is not dead, it has an extra ending point "." adndevblog.typepad.com/autocad/2013/01/how-to-test-the-winding-turn-of-a-closed-polyline.html

    Anyway, the solution is to calculate area from coordinates(Shoelace formula) and if it's positive then you have a counterclockwise and vice-versa(I suppose this is the word of the post)

  13. thx this is great. for those reading who want to determine: should i reverse the polyline or not - you'll first need to know whether it's clockwise or not. gilles has compiled a nice little static method which tells you (basically) whether your polyline is clockwise or anticlockwise and then you can run kean's code accordingly. forums.autodesk.com/ ---> almost forgot the link!!

Leave a Reply to Area51Visitor Cancel reply

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