Friday, July 24, 2009

Help AutoCAD Users Visually with Transient Graphics – Part 2

In my previous article (Part 1), I showed how to use AutoCAD (2009 or later) Transient Graphics to highlight entities in interest when mouse hover them. In this article, I continue on the topic of using Transient Graphics to help CAD users.

When VBA was adopted in AutoCAD (R14.01, I think), many AutoCAD programmers jumped into it and they were very pleased with VBA making AutoCAD programming a lot more smooth and powerful. However, there was one missing thing that makes Lisp programmer often laughing at VBA programmer: when a VBA programmer tries to make a VBA operation more like AutoCAD built-in command, such as move/copy/insert…, there is no way to generate a ghost image during the command execution, like AutoCAD does, while Lisp programmers can simply do (command “…”). Many VBA programmers take pains in vain trying to find a solution, and I saw questions on this regard posted in VBA forum from time to time.

Of course, this can be done using Jig with C++ ObjectARX in any version of AutoCAD, and since ObjectARX .NET API finally available (AutoCAD 2005, but 2006 is the first useable version), doing a Jig with .NET API is not that difficult any more. But it is still fairly advanced programming tricks in term of ObjectARX .NET API.

Now, with Transient Graphics available since AutoCAD 2009, we can achieve the visual effect similar to AutoCAD Jig API with transient graphics with easier coding (comparing to coding a Jig).

I am going to create a polyline transient graphic jig (yes, polyline again, because my current CAD related work mainly deals with closed polyline. You can easily extend the idea showed here to other types of entity).

Here is the code:



using System;
using System.Collections.Generic;
using System.Text;

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;

[assembly: CommandClass(typeof(TransientGraphicsSample.ADSCommands))]

namespace TransientGraphicsSample
{
public class ADSCommands : IExtensionApplication
{
public ADSCommands()
{
//
// TODO: Add constructor logic here
//
}

#region Show TG Jig

[CommandMethod("TGJig")]
public static void ShowTGJig()
{
Document dwg = Application.DocumentManager.MdiActiveDocument;

TGPolylineJig jig = new TGPolylineJig(dwg);

jig.ShowTGJig();
}

#endregion
}
public class TGPolylineJig
{
private Polyline mTGPolyline=null;
private Polyline mTargetPolyline;
private Editor mEditor;
private Database mDB;
private Document mDoc;

private Point3d mBasePoint;
private int mColorIndex = 1;

public TGPolylineJig(Document doc)
{
mDoc = doc;
mDB = doc.Database;
mEditor = doc.Editor;
}

public void ShowTGJig()
{
//Option to pick the target polyline
PromptEntityOptions eOpt =
new PromptEntityOptions("\nPick a polyline:");

//Only allow to pick polyline
eOpt.SetRejectMessage("\nYou must pick a polyline:");
eOpt.AddAllowedClass(typeof(Polyline), true);

//Do the pick
PromptEntityResult eRes = mEditor.GetEntity(eOpt);
if (eRes.Status != PromptStatus.OK) return;

//Get the target polyline
ObjectId targetId = eRes.ObjectId;
Polyline target = GetPolyline(targetId);
if (target == null) return;

mTargetPolyline = target;

//Prompt to pick base point and exit
PromptPointOptions opt1 =
new PromptPointOptions("\nClick base point:");
PromptPointOptions opt2 =
new PromptPointOptions("\nClick anywhere to exit:");

//Pick base point
PromptPointResult res = mEditor.GetPoint(opt1);
if (res.Status != PromptStatus.OK) return;

mBasePoint = res.Value;

//Set base point for second pick so that a rubberband
//will be shown when mouse is moving
opt2.BasePoint = mBasePoint;
opt2.UseBasePoint = true;

//Start monitoring pointer move
mEditor.PointMonitor +=
new PointMonitorEventHandler(mEditor_PointMonitor);

//Start asking user to do second pick (exit)
//so that the "jig" image will move with mouse
mEditor.GetPoint(opt2);

//Once the second point is picked, clear the
//transient graphic "jig". After this, since
//a new point is obtained with the jig;s visual
//help, one can then do copy/move/insert...
ClearTGPolyline();

//End pointer move monitoring
mEditor.PointMonitor -=
new PointMonitorEventHandler(mEditor_PointMonitor);
}

private void mEditor_PointMonitor
(object sender, PointMonitorEventArgs e)
{
//Whenever point moves, draw a new transient
//graphic image at mouse point
DrawTGPolyline(e.Context.RawPoint);

//rotate colorindex, so the jig's color
//changes with mouse move, thus more eye-catching
mColorIndex += 1;
if (mColorIndex > 8) mColorIndex = 1;
}

private Polyline GetPolyline(ObjectId id)
{
Polyline pl = null;

using (Transaction tran =
mDB.TransactionManager.StartOpenCloseTransaction())
{
pl = tran.GetObject(id, OpenMode.ForRead) as Polyline;
tran.Commit();
}

return pl;
}

private void DrawTGPolyline(Point3d movePoint)
{
//Clear existing transient graphic image
ClearTGPolyline();

//Get displacement increments of current mouse
//point against picked base point
double x = movePoint.X - mBasePoint.X;
double y = movePoint.Y - mBasePoint.Y;

//Create a polyline for the transient graphics
mTGPolyline =
new Polyline(mTargetPolyline.NumberOfVertices);

for (int i = 0; i < mTargetPolyline.NumberOfVertices; i++)
{
Point2d p = mTargetPolyline.GetPoint2dAt(i);
Point2d pt = new Point2d(p.X + x, p.Y + y);

mTGPolyline.AddVertexAt(i, pt, 0.0, 1.0, 1.0);
}

mTGPolyline.Closed = mTargetPolyline.Closed;
mTGPolyline.SetDatabaseDefaults();

//Set color
mTGPolyline.ColorIndex = mColorIndex;
mTGPolyline.LineWeight = LineWeight.LineWeight200;

//Add transient graphics
IntegerCollection col = new IntegerCollection();
TransientManager.CurrentTransientManager.
AddTransient
(
mTGPolyline,
TransientDrawingMode.DirectShortTerm,
128,
col
);
}

private void ClearTGPolyline()
{
if (mTGPolyline != null)
{
//Remove transient graphic effect
IntegerCollection intCol =
new IntegerCollection();

TransientManager.CurrentTransientManager.
EraseTransient(mTGPolyline, intCol);

//dispose the polyline used to
//Show transient graphic effect
mTGPolyline.Dispose();
mTGPolyline = null;
}
}
}
}



You can click here to show a short video clip.

As you can see, the code is surprisingly simple. There are a lot of enhancements you can do with this code base. For example, as previously stated, you can use other entity types rather than polyline; you can select a group of entities and do the jig on all the selected entities; you can build a small dialog box to allow user to configure LineType, Color, LineWeight for the transient graphic object before it is shown. One possible enhancement might be a bit difficult and tricky: what if the selected target entity is a BlockReference? Well, someone may want to try it.

I may continue on the topic of transient graphic in next post.

No comments:

Post a Comment