Interactive commands with jigs in AutoCAD .NET

CAD user interface with a 3D house model, ribbon and layer list

CAD

A command asks for “Center:” and “Radius:”, the user types numbers and only sees the result at the very end. It works, but it feels like 1995. AutoCAD’s CIRCLE command shows how it can be done better: the circle hangs on the mouse and grows with it. This preview is called a jig – and with the .NET API you can build it into your own commands.

What a jig is

In the workshop, a jig is a device that guides the tool, a drilling template for example. In AutoCAD the jig does not guide the drill but the mouse: it shows an element the way it will look if the user clicks now. The Autodesk.AutoCAD.EditorInput namespace offers two classes for this. An EntityJig drags exactly one element, and AutoCAD draws it. A DrawJig draws any amount of graphics but has to take care of everything itself. Both are started with editor.Drag(jig).

Who calls whom?

With Drag, your command hands control to AutoCAD. From now on AutoCAD calls you: on every mouse movement and every input, the Sampler method is called. If it reports a new value, Update follows and AutoCAD redraws the element. This continues until the user clicks, types a value, enters a keyword or cancels. Only then does Drag return with a PromptResult.

A circle jig in two steps

public class CircleJig : EntityJig
{
    private Point3d _center;
    private double _radius = double.NaN;

    public CircleJig() : base(new Circle(Point3d.Origin, Vector3d.ZAxis, 10.0)) { }

    public Circle Circle => (Circle)Entity;
    public bool RadiusStep { get; set; }

    // AutoCAD calls this on every mouse movement
    protected override SamplerStatus Sampler(JigPrompts prompts)
    {
        var status = SamplerStatus.Cancel;
        if (!RadiusStep)
        {
            var result = prompts.AcquirePoint(new JigPromptPointOptions("\nCenter: "));
            if (result.Status == PromptStatus.OK)
            {
                // No movement, no redraw
                status = result.Value.IsEqualTo(_center)
                    ? SamplerStatus.NoChange : SamplerStatus.OK;
                _center = result.Value;
            }
        }
        else
        {
            var options = new JigPromptDistanceOptions("\nRadius: ")
            {
                BasePoint = _center,
                UseBasePoint = true,
                UserInputControls = UserInputControls.NoZeroResponseAccepted
                    | UserInputControls.NoNegativeResponseAccepted
            };
            var result = prompts.AcquireDistance(options);
            if (result.Status == PromptStatus.OK)
            {
                status = Math.Abs(result.Value - _radius) < 1e-9
                    ? SamplerStatus.NoChange : SamplerStatus.OK;
                _radius = result.Value;
            }
        }
        return status;
    }

    // Transfers the values to the circle; AutoCAD then redraws it
    protected override bool Update()
    {
        Circle.Center = _center;
        if (RadiusStep)
        {
            Circle.Radius = _radius;
        }
        return true;
    }
}

Sampler gets the input via JigPrompts: AcquirePoint for points, AcquireDistance for distances, plus AcquireAngle and AcquireString. With BasePoint and UseBasePoint, AutoCAD measures the radius from the center and shows a rubber-band line. The UserInputControls forbid zero and negative values. AutoCAD then rejects a typed -5 by itself, without a single line of your own code. Update transfers the values and returns true if the element is valid. The command calls Drag once for each step:

[CommandMethod("MyCircleJig")]
public void MyCircleJig()
{
    var document = Application.DocumentManager.MdiActiveDocument;
    var jig = new CircleJig();
    var result = document.Editor.Drag(jig);           // step 1: center
    if (result.Status == PromptStatus.OK)
    {
        jig.RadiusStep = true;
        result = document.Editor.Drag(jig);           // step 2: radius
    }
    if (result.Status == PromptStatus.OK)
    {
        Insert(document.Database, jig.Circle);        // AppendEntity, AddNewlyCreatedDBObject, Commit
        document.Editor.WriteMessage(
            $"\nCircle with radius {jig.Circle.Radius} drawn at {jig.Circle.Center}.");
    }
    else
    {
        jig.Circle.Dispose();                         // never reached the database: dispose of it yourself
    }
}

NoChange: the inconspicuous return value

AutoCAD calls Sampler very often, not only when something has really changed. If the sampler returns OK every time, every call is followed by Update and a redraw. The preview flickers and the computer has plenty to do. That is why the sampler compares the new value with the old one and reports NoChange if they are equal. Points are compared with IsEqualTo using AutoCAD’s tolerance, numbers with a small tolerance of your own.

Keywords and dynamic dimensions

As with CIRCLE, a jig can offer options. The keywords are attached to the options of the prompt, for example with options.Keywords.Add("Diameter"). AutoCAD itself appends the list in square brackets to the text. If the user types the word, Drag returns with PromptStatus.Keyword. The command switches mode and calls Drag again – switching is the command’s job, not the sampler’s.

The jig supplies a dimension on the circle via the GetDynamicDimensionData method. It returns a DynamicDimensionDataCollection, for example with an AlignedDimension whose DynamicDimension is set to true. The text "Ø <>" puts a diameter symbol in front of the measured value. Update determines where the dimension sits.

More than one element: DrawJig

If the user is to drag out a cake with a missing slice – an arc and a polyline – an EntityJig is not enough. Instead of Update, the DrawJig overrides the WorldDraw method and uses draw.Geometry.Draw to draw any elements that are not yet in the database.

Testing without a mouse

There is no mouse in the AutoCAD console. Typed values, however, arrive there via the same Acquire methods. The logic of a jig can therefore be checked automatically:

Command: MyCircleJig
Center: 50,50
Radius: 20
Circle with radius 20 drawn at (50,50,0).

What the preview looks like and how often the sampler runs while the mouse moves, on the other hand, only real AutoCAD can show.

Typical mistakes

  • No NoChange: the preview flickers and the computer heats up.
  • Using the value before checking the status. With Cancel or Keyword there is nothing useful in it.
  • Not cleaning up after a cancel. The jig’s element only goes into the database on success; otherwise it has to be disposed of.
  • Writing the keywords into the prompt text yourself. AutoCAD appends the list anyway.
  • Catching invalid values in Update with catch instead of ruling them out beforehand.
  • Expecting the dragged element to already have an ObjectId, a handle or Xdata.
  • Polyline becomes ambiguous as soon as GraphicsInterface is imported. An alias makes it clear.
  • Using a jig just to display something. Transient graphics or an overrule are better for that.

How we help

  • Training: custom commands, input and jigs are part of the “AutoCAD .NET API with C#” course.
  • Project: we develop add-ins with commands that feel like AutoCAD itself, or make your existing ones more convenient. Arrange an initial consultation.
  • The book: the circle jig with a diameter option, the cake as a DrawJig and an exercise with an ellipse are covered in Ebi F. Eilber’s book “AutoCAD .NET API”, expected in 2027.

Source: Ebi F. Eilber, AutoCAD .NET API (book, expected 2027), chapter “Jig”

CONTACT

Select your language

First meetingarrange nowFirst meetingarrange now