December 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      










« Post-AU wrap-up | Main | Merry Christmas, one and all! »

December 18, 2009

Faceting AutoCAD curves using .NET

We have a number of candidate “Plugins of the Month” currently in the pipeline – including Inventor versions of Screenshot and Clipboard Manager as well as a tool to streamline batch plotting from AutoCAD – but unfortunately none were looking ready enough to count on for January’s posting. So yesterday I dipped into the plugins that have generously been proposed/provided by external parties and I put together a C# version of a tool submitted by our old friend Jon Smith from COINS. Jon provided a number of C++ tools that COINS has made available for free, one of which was a handy little command called “FacetCurve”.

The tool allows you to break any AutoCAD curve – whether a line, arc, polyline, circle, ellipse, spline, etc. – into a series of line segments or facets (although you probably wouldn’t bother using it on lines, as the results aren’t very interesting :-). The command has three modes of operation: by number of segments, by maximum segment length and by fixed segment length. It can be configured to generate lines or polylines (whether they are lightweight or 3D polylines depends on whether the source curve is planar or not). This version of the tool exposes a command-line – rather than a dialog-based – user-interface.

Given the fact many people (including me!) will be taking an extended break over the holidays, the plugin probably won’t get posted before the second week of January, so there’s time for me to incorporate feedback, if you have it (whether submitted by email or as a comment on this blog).

Here’s the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.Geometry;

using System.Configuration;

using DemandLoading;

 

namespace FacetCurve

{

  public class FacetCurveApplication : IExtensionApplication

  {

    // Define a class for our custom data

 

    public enum FacetOpType

    {

      ByNumberOfSegments = 0,

      ByMaximumSegmentLength = 1,

      ByFixedSegmentLength = 2

    }

 

    public class AppData : ApplicationSettingsBase

    {

      [UserScopedSetting()]

      [DefaultSettingValue("ByNumberOfSegments")]

      public FacetOpType FacetType

      {

        get { return ((FacetOpType)this["FacetType"]); }

        set { this["FacetType"] = (FacetOpType)value; }

      }

      [UserScopedSetting()]

      [DefaultSettingValue("5")]

      public int NumberOfSegments

      {

        get { return ((int)this["NumberOfSegments"]); }

        set { this["NumberOfSegments"] = (int)value; }

      }

      [UserScopedSetting()]

      [DefaultSettingValue("100.0")]

      public double MaximumSegmentLength

      {

        get { return ((double)this["MaximumSegmentLength"]); }

        set { this["MaximumSegmentLength"] = (double)value; }

      }

      [UserScopedSetting()]

      [DefaultSettingValue("10.0")]

      public double FixedSegmentLength

      {

        get { return ((double)this["FixedSegmentLength"]); }

        set { this["FixedSegmentLength"] = (double)value; }

      }

      [UserScopedSetting()]

      [DefaultSettingValue("false")]

      public bool TransferProperties

      {

        get { return ((bool)this["TransferProperties"]); }

        set { this["TransferProperties"] = (bool)value; }

      }

      [UserScopedSetting()]

      [DefaultSettingValue("false")]

      public bool CreatePolyline

      {

        get { return ((bool)this["CreatePolyline"]); }

        set { this["CreatePolyline"] = (bool)value; }

      }

      [UserScopedSetting()]

      [DefaultSettingValue("false")]

      public bool EraseOriginalCurve

      {

        get { return ((bool)this["EraseOriginalCurve"]); }

        set { this["EraseOriginalCurve"] = (bool)value; }

      }

    }

 

    public FacetCurveApplication()

    {

    }

 

    [CommandMethod("ADNPLUGINS", "REMOVEFC", CommandFlags.Modal)]

    static public void RemoveFacetCurve()

    {

      DemandLoading.RegistryUpdate.UnregisterForDemandLoading();

 

      Editor ed =

        Autodesk.AutoCAD.ApplicationServices.Application.

        DocumentManager.MdiActiveDocument.Editor;

      ed.WriteMessage(

        "\nThe FacetCurve plugin will not be loaded" +

        " automatically in future editing sessions.");

    }

 

    [CommandMethod("ADNPLUGINS", "FACETCURVE", CommandFlags.Modal)]

    static public void FacetCurve()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

 

      // Retrieve our application settings (or create new ones)

 

      AppData ad = new AppData();

      ad.Reload();

 

      if (ad != null)

      {

        bool settingschosen;

        PromptEntityResult per;

 

        do

        {

          settingschosen = false;

 

          // Ask the user for the screen window to capture

 

          PrintMode(ed, ad);

          PrintSettings(ed, ad);

 

          PromptEntityOptions peo =

            new PromptEntityOptions(

              "\nSelect curve or " +

              "[Mode/Settings]: ",

              "Mode Settings"

            );

          peo.SetRejectMessage(

            "\nSelected entity must be an arc, circle, spline, " +

            "polyline, or other type of curve."

          );

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

 

          // Get a curve or a keyword

 

          per = ed.GetEntity(peo);

 

          if (per.Status == PromptStatus.Keyword)

          {

            if (per.StringResult == "Mode")

            {

              if (GetMode(ed, ad))

                ad.Save();

              settingschosen = true;

            }

            if (per.StringResult == "Settings")

            {

              if (GetSettings(ed, ad))

                ad.Save();

              settingschosen = true;

            }

          }

        }

        while (settingschosen); // Loop if settings were modified

 

        if (per.Status == PromptStatus.OK)

        {

          // Now we facet our curve

 

          try

          {

            FacetCurve(doc, per.ObjectId, ad);

          }

          catch (Exception ex)

          {

            ed.WriteMessage(

              "\nProblem faceting this curve: {0}",

              ex

            );

          }

        }

      }

    }

 

    // Print the current application mode to the command-line

 

    private static void PrintMode(Editor ed, AppData ad)

    {

      ed.WriteMessage("\nCurrent mode: ");

      if (ad.FacetType == FacetOpType.ByNumberOfSegments)

      {

        ed.WriteMessage(

          "By number of segments, number={0}",

          ad.NumberOfSegments

        );

      }

      else if (ad.FacetType ==

        FacetOpType.ByMaximumSegmentLength)

      {

        ed.WriteMessage(

          "By maximum segment length, length={0}",

          ad.MaximumSegmentLength

        );

      }

      else if (ad.FacetType ==

        FacetOpType.ByFixedSegmentLength)

      {

        ed.WriteMessage(

          "By fixed segment length, length={0}",

          ad.FixedSegmentLength

        );

      }

    }

 

    // Print the current application settings to the command-line

 

    private static void PrintSettings(Editor ed, AppData ad)

    {

      ed.WriteMessage(

        "\nCurrent settings: Create polyline={0}, " +

        "Transfer entity properties={1}," +

        "\nErase original curve={2}",

        ad.CreatePolyline ? "Yes" : "No",

        ad.TransferProperties ? "Yes" : "No",

        ad.EraseOriginalCurve ? "Yes" : "No"

      );

    }

 

    // Ask the user to modify the application mode

 

    private static bool GetMode(Editor ed, AppData ad)

    {

      // At our top-level settings prompt, make the default

      // to exit back up

 

      PromptKeywordOptions pko =

        new PromptKeywordOptions(

          "\nSelect mode [NumberOfSegments/" +

          "MaximumSegmentLength/FixedSegmentLength]: ",

          "NumberOfSegments MaximumSegmentLength FixedSegmentLength"

        );

      switch (ad.FacetType)

      {

        case FacetOpType.ByMaximumSegmentLength:

          pko.Keywords.Default = "MaximumSegmentLength";

          break;

        case FacetOpType.ByFixedSegmentLength:

          pko.Keywords.Default = "FixedSegmentLength";

          break;

        default:

          pko.Keywords.Default = "NumberOfSegments";

          break;

      }

 

      PromptResult pr;

      bool settingschanged = false;

 

      // Start by printing the current settings

 

      PrintMode(ed, ad);

 

      pr = ed.GetKeywords(pko);

 

      if (pr.Status == PromptStatus.OK)

      {

        if (pr.StringResult == "NumberOfSegments")

        {

          // If NumberOfSegments is selected, ask for the number

 

          if (ad.FacetType != FacetOpType.ByNumberOfSegments)

          {

            ad.FacetType = FacetOpType.ByNumberOfSegments;

            settingschanged = true;

          }

 

          PromptIntegerOptions pio =

            new PromptIntegerOptions(

              "\nEnter number of segments: "

            );

          pio.DefaultValue = ad.NumberOfSegments;

          pio.UseDefaultValue = true;

 

          PromptIntegerResult pir = ed.GetInteger(pio);

 

          if (pir.Status == PromptStatus.OK)

          {

            if (ad.NumberOfSegments != pir.Value)

            {

              ad.NumberOfSegments = pir.Value;

              settingschanged = true;

            }

          }

        }

        else if (pr.StringResult == "MaximumSegmentLength")

        {

          // If MaximumSegmentLength is selected, ask for the length

 

          if (ad.FacetType != FacetOpType.ByMaximumSegmentLength)

          {

            ad.FacetType = FacetOpType.ByMaximumSegmentLength;

            settingschanged = true;

          }

 

          PromptDoubleOptions pdo =

            new PromptDoubleOptions(

              "\nEnter maximum segment length: "

            );

          pdo.DefaultValue = ad.MaximumSegmentLength;

          pdo.UseDefaultValue = true;

 

          PromptDoubleResult pdr = ed.GetDouble(pdo);

 

          if (pdr.Status == PromptStatus.OK)

          {

            if (ad.MaximumSegmentLength != pdr.Value)

            {

              ad.MaximumSegmentLength = pdr.Value;

              settingschanged = true;

            }

          }

        }

        else if (pr.StringResult == "FixedSegmentLength")

        {

          // If FixedSegmentLength is selected, ask for the length

 

          if (ad.FacetType != FacetOpType.ByFixedSegmentLength)

          {

            ad.FacetType = FacetOpType.ByFixedSegmentLength;

            settingschanged = true;

          }

 

          PromptDoubleOptions pdo =

            new PromptDoubleOptions(

              "\nEnter maximum segment length: "

            );

          pdo.DefaultValue = ad.FixedSegmentLength;

          pdo.UseDefaultValue = true;

 

          PromptDoubleResult pdr = ed.GetDouble(pdo);

 

          if (pdr.Status == PromptStatus.OK)

          {

            if (ad.FixedSegmentLength != pdr.Value)

            {

              ad.FixedSegmentLength = pdr.Value;

              settingschanged = true;

            }

          }

        }

      }

      return settingschanged;

    }

 

    // Ask the user to modify the application settings

 

    private static bool GetSettings(Editor ed, AppData ad)

    {

      // At our top-level settings prompt, make the default

      // to exit back up

 

      PromptKeywordOptions pko =

        new PromptKeywordOptions(

          "\nSetting to change " +

          "[CreatePolyline/TransferProperties/EraseOriginal/Exit]: ",

          "CreatePolyline TransferProperties EraseOriginal Exit"

        );

      pko.Keywords.Default = "Exit";

 

      PromptResult pr;

      bool settingschanged = false;

      do

      {

        // Start by printing the current settings

 

        PrintSettings(ed, ad);

 

        pr = ed.GetKeywords(pko);

 

        if (pr.Status == PromptStatus.OK)

        {

          if (pr.StringResult == "CreatePolyline")

          {

            // If CreatePolyline is different, ask whether to

            // create lines or polylines

 

            bool different =

              GetYesOrNo(

                ed,

                "\nCreate a polyline rather than lines for " +

                "multi-segment results",

                ad.CreatePolyline

              );

            if (different)

            {

              ad.CreatePolyline = !ad.CreatePolyline;

              settingschanged = true;

            }

          }

          else if (pr.StringResult == "TransferProperties")

          {

            // If TransferProperties is different, ask whether to

            // copy entity properties from the original

 

            bool different =

              GetYesOrNo(

                ed,

                "\nTransfer entity properties (layer, linetype, " +

                "etc.) from the original curve",

                ad.TransferProperties

              );

            if (different)

            {

              ad.TransferProperties = !ad.TransferProperties;

              settingschanged = true;

            }

          }

          else if (pr.StringResult == "EraseOriginal")

          {

            // If EraseOriginal is different, ask whether to

            // erase original curve

 

            bool different =

              GetYesOrNo(

                ed,

                "\nErase original curve",

                ad.EraseOriginalCurve

              );

            if (different)

            {

              ad.EraseOriginalCurve = !ad.EraseOriginalCurve;

              settingschanged = true;

            }

          }

        }

      }

      while (

        pr.Status == PromptStatus.OK &&

        pr.StringResult != "Exit"

      );  // Loop until Exit or cancel

 

      return settingschanged;

    }

 

    // Ask the user to enter yes or no to a particular question,

    // setting the default option appropriately

 

    private static bool GetYesOrNo(

      Editor ed,

      string prompt,

      bool defval

    )

    {

      bool changed = false;

 

      PromptKeywordOptions pko =

        new PromptKeywordOptions(prompt + " [Yes/No]: ", "Yes No");

 

      // The default depends on our current settings

 

      pko.Keywords.Default =

        (defval ? "Yes" : "No");

      PromptResult pr = ed.GetKeywords(pko);

 

      if (pr.Status == PromptStatus.OK)

      {

        // Change the settings, as needed

 

        bool newval =

          (pr.StringResult == "Yes");

 

        if (defval != newval)

        {

          changed = true;

        }

      }

      return changed;

    }

 

    private static void FacetCurve(

      Document doc, ObjectId curId, AppData ad

    )

    {

      Database db = doc.Database;

      Editor ed = doc.Editor;

 

      Transaction tr = doc.TransactionManager.StartTransaction();

      using (tr)

      {

        // Open our curve

 

        DBObject obj = tr.GetObject(curId, OpenMode.ForRead);

        Curve cur = obj as Curve;

        if (cur != null)

        {

          // We'll gather the points along the curve in a collection

 

          Point3dCollection pts = null;

 

          if (ad.FacetType == FacetOpType.ByNumberOfSegments)

          {

            // "By number of segments" means a simple function call

 

            pts = VectorizeCurve(cur, ad.NumberOfSegments);

          }

          else if (ad.FacetType ==

            FacetOpType.ByMaximumSegmentLength)

          {

            // "By maximum segment length" needs more work

 

            // Start by getting the length of the curve

 

            double startDist =

              cur.GetDistanceAtParameter(cur.StartParam);

            double endDist =

              cur.GetDistanceAtParameter(cur.EndParam);

            double curLen = endDist - startDist;

 

            // If shorter than the maximum segment length,

            // then there's little to do

 

            if (curLen < ad.MaximumSegmentLength)

            {

              ed.WriteMessage(

                "\nMaximum segment length too high for " +

                "this length of curve."

              );

              return;

            }

 

            // We'll start by assuming twice as many segments

            // as the number found by dividing the curve length

            // (should be an adequate starting point)

 

            int startSegs =

              (int)(2 * curLen / ad.MaximumSegmentLength);

 

            // Loop back from this number, decrementing each time,

            // to find the maximum number of segments where all

            // segments are less than the maximum segment length

 

            for (int i = startSegs; i > 0; i--)

            {

              Point3dCollection tmppts = VectorizeCurve(cur, i);

              if (tmppts.Count < 2)

                continue;

 

              // Check all lengths in the array, looking for

              // any that are longer than the maximum

              // (at which point we break)

 

              bool allshorter = true;

 

              for (int j = 0; j < tmppts.Count - 1; j++)

              {

                if (tmppts[j].DistanceTo(tmppts[j + 1]) >

                    ad.MaximumSegmentLength)

                {

                  allshorter = false;

                  break;

                }

              }

 

              // If all were shorter, save the points: if the next

              // pass through finds any segment longer than the

              // maximum, we'll use them

 

              if (allshorter)

                pts = tmppts;

              else

                break;

            }

          }

          else if (ad.FacetType == FacetOpType.ByFixedSegmentLength)

          {

            // "By fixed segment length" also needs some work

 

 

            // The algorithm uses planar intersection, so cannot

            // work with non-planar curves

 

            if (!cur.IsPlanar)

            {

              ed.WriteMessage(

                "\nFixed segment mode only works with" +

                " planar curves."

              );

              return;

            }

 

            // If planar, get the plane

 

            Plane p = cur.GetPlane();

 

            // Initialize our results collection, add the 1st point

 

            pts = new Point3dCollection();

            pts.Add(cur.StartPoint);

 

            // Loop along the length of the curve

 

            bool last = false;

            while (!last)

            {

              // We check the intersection between the curve and a

              // circle with the fixed segment length as its radius

 

              Circle c =

                new Circle(

                  pts[pts.Count-1], p.Normal, ad.FixedSegmentLength

                );

 

              Point3dCollection intPts = new Point3dCollection();

              cur.IntersectWith(

                c, Intersect.ExtendArgument, intPts, 0, 0

              );

 

              // We'll look for the closest of the intersection

              // points to the base point

 

              Point3d closest;

 

              if (intPts.Count < 1)

              {

                // Found no intersections:

                // use the curve's end point

 

                closest = cur.EndPoint;

                last = true;

              }

              else

              {

                // Found one or more intersections:

                // take the closest of them

 

                double baseParam =

                  cur.GetParameterAtPoint(pts[pts.Count-1]);

                double minParam = 999999; // Big number

                bool found = false;

 

                foreach (Point3d pt in intPts)

                {

                  // Check the point's parameter

 

                  double param = cur.GetParameterAtPoint(pt);

 

                  // If it's larger than the base parameter but

                  // smaller than the minumum found so far, use it

 

                  if (param > baseParam && param < minParam)

                  {

                    minParam = param;

                    closest = pt;

                    found = true;

 

                    // If it's the same as the curve's end point,

                    // no need to loop again

 

                    last = (pt == cur.EndPoint);

                  }

                }

 

                // If we didn't find a close intersection, it means

                // we're at the end. Use the curve's end-point for

                // the last vertex

 

                if (!found)

                {

                  closest = cur.EndPoint;

                  last = true;

                }

              }

              pts.Add(closest);

            }

          }

 

          // Now we can go and create our lines/polyline

 

          if (pts != null && pts.Count > 0)

          {

            // Open the current space for write

 

            BlockTable bt =

              (BlockTable)tr.GetObject(

                db.BlockTableId,

                OpenMode.ForRead

              );

            BlockTableRecord btr =

              (BlockTableRecord)tr.GetObject(

                db.CurrentSpaceId,

                OpenMode.ForWrite

              );

 

            if (!ad.CreatePolyline || pts.Count <= 2)

            {

              // Create a sequence of line entities

 

              if (pts.Count >= 2)

              {

                for (int i = 0; i < pts.Count - 1; i++)

                {

                  Line ln = new Line();

 

                  if (ad.TransferProperties)

                    ln.SetPropertiesFrom(cur);

 

                  ln.StartPoint = pts[i];

                  ln.EndPoint = pts[i + 1];

                  btr.AppendEntity(ln);

                  tr.AddNewlyCreatedDBObject(ln, true);

                }

              }

            }

            else

            {

              // Create a polyline (either lightweight or 3D)

 

              if (cur.IsPlanar)

              {

                // Create a lightweight 2D polyline

 

                Plane p = cur.GetPlane();

 

                Polyline pl = new Polyline(pts.Count);

                pl.Normal = p.Normal;

 

                if (ad.TransferProperties)

                  pl.SetPropertiesFrom(cur);

 

                // Add each of the vertices to the polyline,

                // converting them to the correct plane

 

                foreach (Point3d pt in pts)

                {

                  pl.AddVertexAt(

                    pl.NumberOfVertices, pt.Convert2d(p),

                    0.0, 0.0, 0.0

                  );

                }

 

                // Transform the polyline to get it to the right

                // place

 

                pl.TransformBy(

                  Matrix3d.Displacement(

                    p.GetCoordinateSystem().Origin - Point3d.Origin

                  )

                );

 

                // Add it to the drawing

 

                btr.AppendEntity(pl);

                tr.AddNewlyCreatedDBObject(pl, true);

              }

              else

              {

                // Create a 3D polyline

 

                Polyline3d pl =

                  new Polyline3d(Poly3dType.SimplePoly, pts, false);

 

                if (ad.TransferProperties)

                  pl.SetPropertiesFrom(cur);

 

                btr.AppendEntity(pl);

                tr.AddNewlyCreatedDBObject(pl, true);

              }

            }

          }

 

          // Erase the original curve if requested

 

          if (ad.EraseOriginalCurve)

          {

            cur.UpgradeOpen();

            cur.Erase();

          }

        }

        tr.Commit();

      }

    }

 

    private static Point3dCollection VectorizeCurve(

      Curve cur, int numSeg

    )

    {

      // Collect points along our curve

 

      Point3dCollection pts = new Point3dCollection();

 

      // Split the curve's parameter space into

      // equal parts

 

      double startParam = cur.StartParam;

      double segLen =

        (cur.EndParam - startParam) / numSeg;

 

      // Loop along it, getting points each time

 

      for (int i = 0; i < numSeg + 1; i++)

      {

        Point3d pt =

          cur.GetPointAtParameter(startParam + segLen * i);

        pts.Add(pt);

      }

      return pts;

    }

 

    // IExtensionApplication protocol

 

    public void Initialize()

    {

      try

      {

        // Create Registry entries for automatic loading

 

        RegistryUpdate.RegisterForDemandLoading();

      }

      catch

      { }

    }

 

    public void Terminate()

    {

    }

  }

}

To build it you will need to incorporate the demand-loading code from this previous post or one of the previous plugins of the month.

If you run the FACETCURVE command you will be presented with these choices:

Command: FACETCURVE

Current mode: By number of segments, number=5

Current settings: Create polyline=No, Transfer entity properties=No,

Erase original curve=No

Select curve or [Mode/Settings]:

To facet a curve using the default mode and settings, just select it. To change the mode you can enter the “Mode” keyword (or just “M”) and select the appropriate mode of operation. Here we cycle through the different modes and change back to the first one, albeit with more segments:

Select curve or [Mode/Settings]: M

Current mode: By number of segments, number=5

Select mode [NumberOfSegments/MaximumSegmentLength/FixedSegmentLength]

<NumberOfSegments>: M

Enter maximum segment length <100.0000>: 1

Current mode: By maximum segment length, length=1

Current settings: Create polyline=No, Transfer entity properties=No,

Erase original curve=No

Select curve or [Mode/Settings]: M

Current mode: By maximum segment length, length=1

Select mode [NumberOfSegments/MaximumSegmentLength/FixedSegmentLength]

<MaximumSegmentLength>: F

Enter maximum segment length <10.0000>: 3

Current mode: By fixed segment length, length=3

Current settings: Create polyline=No, Transfer entity properties=No,

Erase original curve=No

Select curve or [Mode/Settings]: M

Current mode: By fixed segment length, length=3

Select mode [NumberOfSegments/MaximumSegmentLength/FixedSegmentLength]

<FixedSegmentLength>: N

Enter number of segments <5>: 10

Current mode: By number of segments, number=10

Current settings: Create polyline=No, Transfer entity properties=No,

Erase original curve=No

Select curve or [Mode/Settings]:

To change the settings – whether to create a polyline rather than lines, to transfer entity properties from the original to the resultant entity/entities or to erase the original curve – select “Settings” (or “S”):

Select curve or [Mode/Settings]: S

Current settings: Create polyline=No, Transfer entity properties=No,

Erase original curve=No

Setting to change [CreatePolyline/TransferProperties/EraseOriginal/Exit]

<Exit>: C

Create a polyline rather than lines for multi-segment results [Yes/No] <No>:

Current settings: Create polyline=No, Transfer entity properties=No,

Erase original curve=No

Setting to change [CreatePolyline/TransferProperties/EraseOriginal/Exit]

<Exit>: T

Transfer entity properties (layer, linetype, etc.) from the original curve

[Yes/No] <No>:

Current settings: Create polyline=No, Transfer entity properties=No,

Erase original curve=No

Setting to change [CreatePolyline/TransferProperties/EraseOriginal/Exit]

<Exit>: E

Erase original curve [Yes/No] <No>:

Current settings: Create polyline=No, Transfer entity properties=No,

Erase original curve=No

Setting to change [CreatePolyline/TransferProperties/EraseOriginal/Exit] <Exit>:

Current mode: By number of segments, length=10

Current settings: Create polyline=No, Transfer entity properties=No,

Erase original curve=No

Select curve or [Mode/Settings]:

Now let’s see the results of faceting a curve using with various modes & settings. Here’s the original:

A simple spline

Here’s the faceted version with 10 segments – the segments will vary in length, as the curve’s parameter space is divided equally by the number of segments, which doesn’t necessarily result in equally sized segments.

Our spline faceted by number of segments

If we use the “maximum segment length” mode, the curve is faceted multiple times using the “by number of segments” method until none of the segments exceed the maximum segment length. Here we’ve used a segment length of 5 (for a spline-length of around 31).

Our spline faceted by maximum segment length

If we want a standard segment length – except for the last segment which may prove to be shorter, of course – then we can use the “fixed segment length” mode. Here’s we’ve used a fixed segment length of 3.

Our spline faceted by fixed segment length

And, of course, we can choose use the settings to to erase the original curve and use its standard properties (layer, linetype, etc.).

 

Our spline replaced by a faceted polyline with the same entity properties

Thanks again to COINS and to Jon for providing the original application that formed the basis for this plugin. And please let me know if you have any feedback!

blog comments powered by Disqus

Feed/Share

10 Random Posts