Jigging multiple entities with the DrawJig

Today I’m giving a simple example of how to use a Jig on multiple entities.

There are 2 kinds of jigs (as far as I know, maybe there are new types in the newer API’s):

  • EntityJig
  • DrawJig

The EntityJig only allows us to jig one entity at the time, so the type we need here is the DrawJig.

This example is very simple, and by no means exploits full potential of jigging, but it might be something to get you going.

We create a command called ccnSimpleJig (ccn for Cup of Code .Net). The point of this command is to select some polylines, and then use a Jig on them to achieve a similar affect to te MOVE command.

We start by creating the command:

   1: [CommandMethod("ccnSimpleJig")]
   2: public void SimpleGeometryJig()
   3: {
   4:     Document doc = Application.DocumentManager.MdiActiveDocument;
   5:     Editor ed = doc.Editor;
   7:     using (Transaction t = doc.TransactionManager.StartTransaction())
   8:     {
   9: ...

All goes nicely in a transaction. I’m not using a nice Try Catch Finally pattern, because the point here isthe jig.

Next, we select some entities and filter out the Polylines:

   1: // select some polylines
   2: PromptSelectionOptions promptSelection = new PromptSelectionOptions();
   3: PromptSelectionResult result = ed.GetSelection(promptSelection);
   5: if (result.Status != PromptStatus.OK)
   6:     return;
   8: // Iterate results to find polylines (I know filters are smarter..)
   9: List<Polyline> polylines = new List<Polyline>();
  11: foreach (ObjectId oid in result.Value.GetObjectIds())
  12: {
  13:     DBObject ent = t.GetObject(oid, OpenMode.ForWrite);
  15:     Polyline p = ent as Polyline;
  17:     if (p == null)
  18:         continue;
  20:     polylines.Add(p);
  22: }

Then we prompt for a base or reference point:

   1: // prompt refernce point
   2: PromptPointOptions promptPoint = new PromptPointOptions("select reference point");
   3: PromptPointResult promptPointResult = ed.GetPoint(promptPoint);
   5: if (promptPointResult.Status != PromptStatus.OK)
   6:     return;

Now that we have all this, we can start the Jig, and don’t forget to commit the transaction.

   1: // Jig It.
   2: SimpleGeometryJig jig = new SimpleGeometryJig(polylines, promptPointResult.Value);
   3: PromptResult res = ed.Drag(jig);
   5: // Commit transaction to commit the moves we did.
   6: t.Commit();

The Jig itself is a class inheriting from the AutoCAD DrawJig class.
When inheriting the DrawJig, you must override (CTRL+ALT+F10 is your friend) 2 Methods: Sampler and WorldDraw.
Sampler simply tells the Jig to sample the needed data, and check if anything needs a redraw.
In WorldDraw, you can then update the entities so that the changes detected in the Sampler method are shown on the screen.

But, first things first, our SimpleGeometryJig Class. In the constructor of this class, we take in a collection of Polylines and a reference point (wich we specified in the command):

   1: private IList<Polyline> polylines;
   2: private Point3d currentPosition;
   3: private Vector2d currentVector;
   5: public SimpleGeometryJig(IList<Polyline> polylines, Point3d referencePoint)
   6: {
   7:     this.polylines = polylines;
   9:     // use first point in polyline collection as reference point
  10:     currentPosition = referencePoint;
  12:     // init current vector as 0,0,0
  13:     currentVector = new Vector2d(0, 0);
  15: }

Notice that we also use a global Vector2d. This is for tracking the movements, and makes it easy to update the polylines.

So, now our Sample method. Here we are looking for the current mouse position, and we will remember the difference of the current position with the new position. We are also returning Sampler States matching the outcome of the current Prompt (NoChange, OK, Cancel)

   1: protected override SamplerStatus Sampler(JigPrompts prompts)
   2: {
   3:     JigPromptPointOptions jigOpt = new JigPromptPointOptions("select insertion point");
   4:     jigOpt.UserInputControls = UserInputControls.Accept3dCoordinates;
   7:     PromptPointResult res = prompts.AcquirePoint(jigOpt);
   9:     if (res.Status != PromptStatus.OK)
  10:         return SamplerStatus.Cancel;
  12:     // compare points
  13:     if (res.Value.IsEqualTo(currentPosition, new Tolerance(0.1, 0.1)))
  14:         return SamplerStatus.NoChange;
  16:     // get vector to current position
  17:     Vector3d v3d = currentPosition.GetVectorTo(res.Value);
  18:     currentVector = new Vector2d(v3d.X, v3d.Y);
  21:     // reset current position
  22:     currentPosition = res.Value;
  25:     return SamplerStatus.OK;
  26: }

In the WorldDraw function, we can now edit the polylines with the calculated vector and draw these changes to the screen.

   1: protected override bool WorldDraw(Autodesk.AutoCAD.GraphicsInterface.WorldDraw draw)
   2: {
   3:     try
   4:     {
   6:         // add vector to all points of all polylines
   7:         foreach (var pl in polylines)
   8:         {
   9:             for (int i = 0; i < pl.NumberOfVertices; i++)
  10:             {
  11:                 // add vector to point
  12:                 pl.SetPointAt(i, pl.GetPoint2dAt(i).Add(currentVector));
  13:             }
  15:             draw.Geometry.Draw(pl);
  17:         }
  23:     }
  24:     catch (System.Exception)
  25:     {
  26:         return false;
  27:     }
  29:     return true;
  30: }

Voila, Jig is finished.
This is all the code we need, and we have a simple Jig that can move selected entities around.
For more useful and interesting jigs, you should take a look at Kean's site, filtered on the jig-category

Oh, and the code for this article can be found on my samples (first sample for now) page on Google Code. (More specifcally under simpledrawjig)

Posted by Bert Vanpeteghem

5 reacties:

karnyzas said...

thanks, this was useful

Felipe Mesquita said...

Very nice...

It's helping me a lot!

I'm trying to Jig multiple-types objectset.. I must treat each one, by separing them by types?

Thank you very much!

Bert Vanpeteghem said...

Hi Felipe,

It is certainly possible to do so with different types of objects too.

You'd have to do 2 things:

1. Change the list of polylines to a list of entities (IList), so that an entity of any type can be added to the list.

2. In the WorldDraw method you can no longer treat every entity as a polyline, so you'd have to check the type of the entity and move the entity accordingly.

Check type by doing:

if(entity as BlockReference != null)
moveBlockReference(entity as BlockReference);
else if(entity as Polyline != null)
movePolyline(entity as Polyline);

Where moveBlockReference is a private method that, well, moves a block reference, and movePolyline is a method that moves a polyline as described in the post.

And you do so for all the types you want to support.

I don't know if there's an easier way to handle this, maybe there's a generic way to move entities.

Hope that helps...

Felipe Mesquita said...

Yeah! Thats was exactaly my try last night... With no sucess..

I've tried to do it simpler, using the TransformBy(Matrix) method, so that, with a translation Matrix I could move more than 1 type of Object...

Didn't thought that moving entities could be that hard... But I'll keep trying, and will bring it to you if I had sucess.

One idea I had was to create a block with the selected entities, and once it is done, I could Jig a single entity. But didnn't tried to implement yet...

Thanks for your help/attention/support/blog.. Congratulations..


Felipe Mesquita said...

Moving with .TransformBy(Matrix3d) works...

I think I did something wrong at first, but now it is ok...