000

Creating A "Move" Command With .NET API

.
In AutoCAD VBA era, although programmatic moving an entity with user interaction is fairly easy to do, but there is no way to make it behave like AutoCAD built-in "MOVE" command - the entity to be moved can be dragged with the cursor, which gives user a very good visual hint as where the entity is moving.

With AutoCAD .NET API being available, programming an custom command that allows user to drag entity or entities becomes a hot AutoCAD programming technique that every AutoCAD programmer would like to try out.

There are two ways to do entity dragging: creating a custom Jig class that derived from EntityJig or DrawJig class; or use a delegate (DragCallBack) in Editor.Drag() method. There are quite some discussions on the topic of Jig in various blogs/forums. In this article, I focus on the latter - Editor.Drag() with a delegate.

Kean Walmsly posted an article on this topic here. However, in dragging part of his code, there is no code to show a rubber band and show the entity/entities being dragged in highlight (of course this is because that was not what his post focused on).

The code shown below tries to imitate AutoCAD's "MOVE" command: the entity/entities can be selected before or after the custom command starts; the selected entity/entities being highlighted, a rubber band line being drawn from base point to the mouse cursor; the dragging method can ask user to pick base point; if no base point is required, the code finds the lower left corner of bounding box of all selected entities as base point... Enough explanations. Here are the code snippets following.

First, in the project, add a class "DragMove":

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

namespace InteractiveDrag
{
public class DragMove
{
private Document _dwg;
private SelectionSet _sset;

private Point3d _basePoint = new Point3d(0.0, 0.0, 0.0);
private bool _useBasePoint = false;
private Line _rubberLine = null;

public DragMove(Document dwg, SelectionSet ss)
{
_dwg = dwg;
_sset = ss;
}

#region public properties

public bool UseBasePoint
{
set { _useBasePoint = value; }
get { return _useBasePoint; }
}

public Point3d BasePoint
{
set { _basePoint = value; }
get { return _basePoint; }
}

#endregion

#region public methods

public void DoDrag()
{
if (_sset.Count == 0) return;

_rubberLine = null;

using (Transaction tran =
_dwg.Database.TransactionManager.StartTransaction())
{
//Highlight entities
SetHighlight(true, tran);

if (!_useBasePoint)
{
_basePoint = GetDefaultBasePoint();
}
else
{
Point3d pt;
if (!PickBasePoint(_dwg.Editor, out pt)) return;

_basePoint = pt;
_useBasePoint = true;
}

PromptPointResult ptRes = _dwg.Editor.Drag
(_sset, "\nPick point to move to: ",
delegate(Point3d pt, ref Matrix3d mat)
{
if (pt == _basePoint)
{
return SamplerStatus.NoChange;
}
else
{
if (_useBasePoint)
{
if (_rubberLine == null)
{
_rubberLine = new Line(_basePoint, pt);
_rubberLine.SetDatabaseDefaults(_dwg.Database);

IntegerCollection intCol;

//Create transient graphics: rubberband line
intCol = new IntegerCollection();
TransientManager.CurrentTransientManager.
AddTransient(_rubberLine,
TransientDrawingMode.DirectShortTerm, 128, intCol);
}
else
{
_rubberLine.EndPoint = pt;

//Update the transient graphics
IntegerCollection intCol = new IntegerCollection();
TransientManager.CurrentTransientManager.
UpdateTransient(_rubberLine, intCol);
}
}

mat = Matrix3d.Displacement(_basePoint.GetVectorTo(pt));
return SamplerStatus.OK;
}
}
);

if (_rubberLine != null)
{
//Clear transient graphics
IntegerCollection intCol = new IntegerCollection();
TransientManager.CurrentTransientManager.
EraseTransient(_rubberLine, intCol);

_rubberLine.Dispose();
_rubberLine = null;
}

if (ptRes.Status == PromptStatus.OK)
{
MoveObjects(ptRes.Value, tran);
}

//Unhighlight entities
SetHighlight(false, tran);

tran.Commit();
}
}

public void DoDrag(bool pickBasePt)
{
_useBasePoint = pickBasePt;

DoDrag();
}

public void DoDrag(Point3d basePt)
{
_basePoint = basePt;
_useBasePoint = true;

DoDrag();
}

#endregion

#region private methods

private Point3d GetDefaultBasePoint()
{
Point3d pt = new Point3d();

Extents3d exts = new Extents3d(
new Point3d(0.0, 0.0, 0.0), new Point3d(0.0, 0.0, 0.0));

using (Transaction tran =
_dwg.Database.TransactionManager.StartTransaction())
{
ObjectId[] ids=_sset.GetObjectIds();
for (int i = 0; i < ids.Length; i++ )
{
ObjectId id = ids[i];
Entity ent = (Entity)tran.GetObject(id, OpenMode.ForRead);

if (i == 0)
{
exts = ent.GeometricExtents;
}
else
{
Extents3d ext = ent.GeometricExtents;
exts.AddExtents(ext);
}
}

tran.Commit();
}

return exts.MinPoint;
}

private bool PickBasePoint(Editor ed, out Point3d pt)
{
pt = new Point3d();

PromptPointOptions opt = new PromptPointOptions("Pick base point: ");
PromptPointResult res = ed.GetPoint(opt);
if (res.Status == PromptStatus.OK)
{
pt = res.Value;
return true;
}
else
{
return false;
}
}

private void MoveObjects(Point3d pt, Transaction tran)
{
Matrix3d mat = Matrix3d.Displacement(_basePoint.GetVectorTo(pt));
foreach (ObjectId id in _sset.GetObjectIds())
{
Entity ent = (Entity)tran.GetObject(id, OpenMode.ForWrite);
ent.TransformBy(mat);
}
}

private void SetHighlight(bool highlight, Transaction tran)
{
foreach (ObjectId id in _sset.GetObjectIds())
{
Entity ent = (Entity)tran.GetObject(id, OpenMode.ForWrite);

if (highlight)
ent.Highlight();
else
ent.Unhighlight();
}
}

#endregion
}
}

Here is the Command class:

using System.Collections.Generic;

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

[assembly: CommandClass(typeof(InteractiveDrag.MyCommand))]

namespace InteractiveDrag
{
public class MyCommand
{
[CommandMethod("DragMove", CommandFlags.UsePickSet)]
public static void DoMove()
{
Document dwg = Autodesk.AutoCAD.ApplicationServices.
Application.DocumentManager.MdiActiveDocument;

Editor ed = dwg.Editor;

//Get PickFirst SelectionSet
PromptSelectionResult setRes = ed.SelectImplied();

if (setRes.Status != PromptStatus.OK)
{
//if not PickFirst set, ask user to pick:
ObjectId[] ids = GetUserPickedObjects(dwg);
if (ids.Length == 0)
{
ed.WriteMessage("\n*Cancelled*");
ed.WriteMessage("\n*Cancelled*");
return;
}

ed.SetImpliedSelection(ids);
setRes = ed.SelectImplied();

if (setRes.Status != PromptStatus.OK)
{
ed.Regen();
return;
}
}

//Do the dragging with the selectionSet
DragMove drag = new DragMove(dwg, setRes.Value);
drag.DoDrag(true);
}

private static ObjectId[] GetUserPickedObjects(Document dwg)
{
List ids = new List();
using (Transaction tran =
dwg.Database.TransactionManager.StartTransaction())
{
bool go = true;
while (go)
{
go = false;

PromptEntityOptions opt =
new PromptEntityOptions("\nPick an entity: ");
PromptEntityResult res = dwg.Editor.GetEntity(opt);

if (res.Status == PromptStatus.OK)
{
bool exists = false;
foreach (ObjectId id in ids)
{
if (id == res.ObjectId)
{
exists = true;
break;
}
}

if (!exists)
{
//Highlight
Entity ent = (Entity)tran.GetObject(
res.ObjectId, OpenMode.ForWrite);

ent.Highlight();

ids.Add(res.ObjectId);
go = true;
}
}
}

tran.Commit();
}

return ids.ToArray();
}
}
}

Build the project and "NETLOAD" it into AutoCAD. See this video clip for the behaviuor of the code shown above.

With some slight modification to the code one can easily do a drag-copying, drag-rotating...

Blog Archive