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) { Listids = 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...
7 comments:
Hi Norman,
Great blog!
I used your code and made a command that moves a selectionset.
(had a handler error but it seemed that I had to make a copy of the set that is passed to the drag()).
But now I still have a little thing. Ortho doesn't work.
I can't get it to work. Do you have a suggestion?
I also saw your posts with the transientmanager.
Isn't there a way to get the editor.getpoint() to work with a transient that moves the selectionset.
The options (rubberline, ortho, directinput, etc) of the getpoint() are working great. I have found a good example of a code that works as well as Getpoint().
Maybe I'm asking something ridiculous (forgive me I'm a newbie) but it could be a great way to recreate the move command and have more freedom to extras to the command. The transient-getpoint combination could be great to move very large selectionsets.
Thanks,
Jeroen
Hi Jeroen,
Yes, it seems ORTHO mode cannot be turned on in Editor.Drag().
And yes, as you noticed, in one of my old post (http://drive-cad-with-code.blogspot.com/2009/07/help-autocad-users-visually-with_24.html, 2009-07), I coded an example of jig, using Transient Graphics. It was almost a custom "MOVE" command. I mean, it can be used as a base to create a complete custom "MOVE" command. I have verified that ORTHO mode can be turned on/off during mouse move when calling Editor.GetPoint(). So, if doing "MOVE" command like that, the ORTHO would not be an issue.
I may be able to give it a try.
Norman Yuan
Hi Norman,
That would be great. I used the move command to learn VB.net and VB.net for Autocad at the same time. I had no experience so I thought I recreate the move command to learn vb.net.
But it seems to be more complex (and interessting) than I thought.
If you could look into it it would be great. The different parts of this command can be used for all kinds of commands so should be very useful.
Thanks,
Jeroen
Jeroen,
I just posted another version of "MOVE" command, which takes care of "ORTHO" mode being on or off, by handling PointMonitor event, instead of using Editor.Drag().
what a nice coding....
Six Month industrial Training in Chandigarh
There's usually some tutorials within the program that might help you get started.
Solidworks Training
Thanks for the code.
I do have a comment if you will be so kind as to permit me - anonymous delegate functions are difficult to read - the code would perhaps be more easily understand if a more readable solution was provided.
Post a Comment