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  










« Lots of exposure to "laser” technology | Main | Swapping AutoCAD block attribute order using .NET »

July 14, 2010

Shortening a set of AutoCAD lines using .NET

Brent Burgess commented on this previous post showing how to extend lines in AutoCAD:

Is there a different process for shortening lines, or is it similar syntax?

A very valid question… unfortunately the Curve.Extend() method only works with points or parameters that are outside the current definition of the curve, so we need to find another way (if you try, you’ll get an eInvalidInput, at least that’s what I found :-).

I chose the approach of using Curve.GetSplitCurves() on each line, passing in an array of parameters at which to split the curve. I calculate the start parameter as 25% along the length, and the end parameter as being at the same point from the end. Which means each line will end up being half its original size. Lines are generally parameterised between 0 and 1, so we’ll be passing an array containing 0.25 and 0.75 to the GetSplitCurves() method, which should consequently return an array of exactly three objects: lines from 0 to 0.25, 0.25 to 0.75 and from 0.75 to 1. We will therefore discard the first and last lines and use the middle one.

So what shall we do with this line? We could add it to the modelspace, but we really want it to replace the old one. We can use DbObject.HandOverTo() to replace the original line with this new one, retaining the original line’s XData and Extension Dictionary. To make use of this function, we need to open the original line using an open/close transaction (as the “normal” type of transaction will cause the function to return eInvalidContext), and then call Dispose() on the original object, once done.

Here’s the C# code that now contains commands to extend and shorten lines, with a shared function to handle the object selection:

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

 

namespace GeometryExtension

{

  public class Commands

  {

    [CommandMethod("EXL")]

    public void ExtendLines()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;

 

      // Call a function to return a selection set of lines

 

      SelectionSet ss =

        SelectLines(ed, "\nSelect lines to extend: ");

      if (ss != null)

      {

        // Start our transaction

 

        Transaction tr =

          db.TransactionManager.StartTransaction();

        using (tr)

        {

          // Edit each of the selected lines

 

          foreach (SelectedObject so in ss)

          {

            // We're assuming only lines are in the selection-set

            // Could also use a more defensive approach and

            // use a dynamic cast (Line ln = xxx as Line;)

 

            Line ln =

              (Line)tr.GetObject(

                so.ObjectId,

                OpenMode.ForWrite

              );

 

            // We'll extend our line by a quarter of the

            // existing length in each direction

 

            Vector3d ext = ln.Delta / 4;

 

            // First the start-point

 

            ln.Extend(true, ln.StartPoint - ext);

 

            // And then the end-point

 

            ln.Extend(false, ln.EndPoint + ext);

          }

 

          // Mustn't forget to commit

 

          tr.Commit();

        }

      }

    }

 

    [CommandMethod("SHL")]

    public void ShortenLines()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;

 

      // Call a function to return a selection set of lines

 

      SelectionSet ss =

        SelectLines(ed, "\nSelect lines to shorten: ");

      if (ss != null)

      {

        // Start our transaction

        // (Needs to be Open/Close to avoid an eInvalidContext

        //  from HandOverTo())

 

        Transaction tr =

          db.TransactionManager.StartOpenCloseTransaction();

        using (tr)

        {

          // Edit each of the selected lines

 

          foreach (SelectedObject so in ss)

          {

            // We're assuming only curves are in the selection-set

            // Could also use a more defensive approach and

            // use a dynamic cast (Curve cur = xxx as Curve;)

 

            Curve cur =

              (Curve)tr.GetObject(

                so.ObjectId,

                OpenMode.ForWrite

              );

 

            double sp = cur.StartParam,

                  ep = cur.EndParam,

                  delta = (ep-sp) * 0.25;

 

            DoubleCollection dc = new DoubleCollection();

            dc.Add(sp + delta);

            dc.Add(ep - delta);

            DBObjectCollection objs = cur.GetSplitCurves(dc);

 

            if (objs.Count == 3)

            {

              Entity ent = (Entity)objs[1];

              cur.HandOverTo(ent, true, true);

              tr.AddNewlyCreatedDBObject(ent, true);

              cur.Dispose();

              objs[0].Dispose();

              objs[2].Dispose();

            }

          }

 

          // Mustn't forget to commit

 

          tr.Commit();

        }

      }

    }

 

    // Helper function to select a set of lines

 

    private SelectionSet SelectLines(Editor ed, string msg)

    {

      // We only want to select lines...

 

      // Use an options object to specify how the

      // selection occurs (in terms of prompts)

 

      PromptSelectionOptions pso =

        new PromptSelectionOptions();

      pso.MessageForAdding =

        (string.IsNullOrEmpty(msg) ? "\nSelect lines: " : msg);

 

      // Use a filter to specify the objects that

      // get included in the selection set

 

      TypedValue[] tvs =

        new TypedValue[1] {

            new TypedValue(

              (int)DxfCode.Start,

              "LINE"

            )

          };

      SelectionFilter sf = new SelectionFilter(tvs);

 

      // Perform our restricted selection

 

      PromptSelectionResult psr = ed.GetSelection(pso, sf);

 

      if (psr.Status != PromptStatus.OK || psr.Value.Count <= 0)

        return null;

 

      return psr.Value;

    }

  }

}

Let’s see our re-factored EXL command working on a set of lines.

Before…

Lines

After…

Extended lines

Now when we shorten these same lines using the SHL command, we see them shrink back, but not quite to the same place (as 25% is relatively to the current length of the line, of course):

Reshortened lines

We could clearly extend this code to work with other curve types (I did test the shorten command to work with Arcs, for instance), but that’s left as an exercise for the reader. If anyone has an approach they prefer to use for addressing this requirement, be sure to post a comment.

blog comments powered by Disqus

Feed/Share

10 Random Posts