Controlling interactive polyline creation - Part 1

I received this interesting question through by email over the weekend:

"How can I ask AutoCAD to let the user to draw a Polyline (just like the user pushed Polyline button) and then just after finishing drawing get the ObjectID of that entity? Is it possible?"

This is a fun one, as there are a number of different approaches to take. I'm going to outline (or just go ahead and implement, depending on the complexity) the various possibilities – taking the first two today and the others in (a) subsequent post(s).

The idea is to define our own command, say MYPOLY, and make sure we're left with execution control – and the object ID/entity name – after the user has defined a polyline in the drawing window.

There are two basic ways to solve this problem, and each of these has two variants. The initial (and major) choice is whether to let the standard AutoCAD PLINE command provide the user-interface for creating the polyline. Doing so is certainly simpler, assuming you want the user to have access to all the polyline options. That said, you may actually prefer to limit the user's options (for example, not to allow width or arc segments), in which case the approach to implement the UI yourself would be better suited.

So, now to tackle the first two options...

From the MYPOLY command, we want to call the PLINE command. Once the command has completed, we want to make sure our code is being executed, which will allow us to get the polyline's object ID.

This is where we get our next choice: how to find out when the PLINE command has ended. The first option (and the one typically used from Visual LISP for this type of task) is to loop until the command is finished, checking either CMDACTIVE or CMDNAMES. This is important, as polylines can have an arbitrary number of vertices, so we don't know exactly how long the command will take to complete (in terms of how many "pauses" the command will have, requesting a point selection from the user).

Here's how I'd do this in LISP (the technique is published on the ADN site in this DevNote: Waiting for (command) to finish in AutoLISP):

(defun C:MYPOLY()

  (command "_.PLINE")

  (while (= (getvar "CMDNAMES") "PLINE")

    (command pause)

  )

  (princ "\nEntity name of polyline: ")

  (princ (entlast))

  (princ)

)

And here's what happens when we execute this code:

Command: mypoly

_.PLINE

Specify start point:

Current line-width is 0.0000

Specify next point or [Arc/Halfwidth/Length/Undo/Width]:

Specify next point or [Arc/Close/Halfwidth/Length/Undo/Width]:

Specify next point or [Arc/Close/Halfwidth/Length/Undo/Width]: a

Specify endpoint of arc or

[Angle/CEnter/CLose/Direction/Halfwidth/Line/Radius/Second pt/Undo/Width]:

Specify endpoint of arc or

[Angle/CEnter/CLose/Direction/Halfwidth/Line/Radius/Second pt/Undo/Width]:

Command:

Entity name of polyline: <Entity name: 7ef90048>

The second option to wait for the command to complete is to register a callback handling the CommandEnded() event.

Here's some C# code showing this approach:

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.Runtime;

namespace MyPlineApp

{

  public class MyPlineCmds : IExtensionApplication

  {

    // Flag used to check whether it's our command

    // that launched PLINE

    private static bool myCommandStarted = false;

    public void Initialize()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      doc.CommandEnded += new

        CommandEventHandler(

          plineCommandEnded

        );

    }

    public void Terminate()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      doc.CommandEnded -= new

        CommandEventHandler(

          plineCommandEnded

        );

    }

    [CommandMethod("MYPOLY")]

    public void MyPoly()

    {

      // Set the flag and launch PLINE

      myCommandStarted = true;

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      doc.SendStringToExecute("_PLINE ",false,false,false);

    }

    private void plineCommandEnded(

      object sender,

      CommandEventArgs e)

    {

      if (myCommandStarted

        && e.GlobalCommandName.ToUpper() == "PLINE")

      {

        // We're just performing a simple check, so OK..

        // We could launch a follow-on command, if needed

        Document doc =

          Application.DocumentManager.MdiActiveDocument;

        PromptSelectionResult lastRes =

          doc.Editor.SelectLast();

        if (lastRes.Value != null

          && lastRes.Value.Count == 1)

        {

          doc.Editor.WriteMessage(

            "\nPolyline entity is: "

            + lastRes.Value[0].ObjectId

          );

        }

        myCommandStarted = false;

      }

    }

  }

}

And here's what happens when we execute this code:

Command: mypoly

Command:

Specify start point:

Current line-width is 0.0000

Specify next point or [Arc/Halfwidth/Length/Undo/Width]:

Specify next point or [Arc/Close/Halfwidth/Length/Undo/Width]:

Specify next point or [Arc/Close/Halfwidth/Length/Undo/Width]: a

Specify endpoint of arc or

[Angle/CEnter/CLose/Direction/Halfwidth/Line/Radius/Second pt/Undo/Width]:

Polyline entity is: (2130247840)

That's where I'll stop it there for now… the other two options I want to look at both revolve around defining your own user-interface. The first will simply collect a sequence of points from the user using GetPoint(), the second uses a Jig to do the same thing (I haven't yet decided whether to actually implement this last one or not – we'll see how much time I have later in the week).

12 responses to “Controlling interactive polyline creation - Part 1”

  1. Kean,
    What code is needed to make the function available in all subsequent drawings? It appears as if the code here only applies to the drawing active when loading the program. The function isnt available in the next drawing one opens.
    Thx.
    Kevin.

  2. Kevin,

    Apologies for the delay - was travelling last week.

    I assume you mean the LISP code. The .NET code should just work for subsequent drawings, but LISP needs some extra effort.

    LISP is designed to have a separate "namespace" per document, which means you need to load your code into each document (although the LISPINIT system variable should help here). Typically you would use acad.lsp or APPLOAD's Startup Suite to auto-load your code as new drawings are created.

    Cheers,

    Kean

  3. hi
    can you help me?

    I am using the pedit command through SendCommand for join several objects (arcs,polylines), and i want know how get the new object created.

  4. sorry

    i forget put that i am working in vba from autocad.

  5. gera -

    This is not a forum for support. Please head across to the Autodesk Discussion Groups and ask your questions there.

    Please do post comments when they relate specifically to code I've posted, of course.

    Kean

  6. hi Kean. Not sure you'll ever reply, because your post is so old, anyway I try :-). I have been using autocad since 1991, but not using autolisp since many years. The other day I had to modify a stupid old function of mine, now everything works thanks to your post. My question: why (command "_.pline") works even if _.pline is not capitalized, while (= (getvar "CMDNAMES") "PLINE") needs the command name to be capitalized, otherwise doesn't work? Ciao from Italy

  7. Ciao Antonio,

    AutoCAD's command processor doesn't care about case, while a standard string comparison such as that used by by the "=" operation in LISP, is case sensitive.

    We do the extra leg-work to dispatch commands irrespective of the case of the string used to invoke them, but you have to do that leg-work yourself when comparing LISP strings (as there will be times when you certainly do care about case).

    I hope this helps explain the behaviour,

    Kean

  8. silly of me, I know I knew it! thank you so much, Antonio.

  9. hi,kean:
    I add one line code after the program for test, it seems the 'pline' command always start after all the codes finished.
    for example the alertdialog code is after the 'pline', but it shows before the 'pline', why? and how to change this?
    best regards
    swaywood

    public void MyPoly()
    {
    // Set the flag and launch PLINE
    myCommandStarted = true;
    Document doc = Application.DocumentManager.MdiActiveDocument;
    doc.SendStringToExecute("_PLINE ", false, false, false);
    Application.ShowAlertDialog("test");
    }

  10. SendStringToExecute() executes asynchronously: you should use SendCommand() via COM if you need the command to execute synchronously.

    Kean

  11. Gabriel Potestades Avatar
    Gabriel Potestades

    Can it be possible to draw multiple polylines then use PEDIT without using the command line?

    1. Kean Walmsley Avatar

      Hi Gabriel,

      This is off-topic: please post to the relevant online discussion forum.

      Kean

Leave a Reply to Antonio Cancel reply

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