In a occassion when picking a series of entities, the user may not only wants to have the picked entity highlighted, but also wants a rubber band line drawn from the previously picked point (or point derived from the previously picked entity) to the moving mouse cursor before picking next entity.
As we know, using PromptPointOption in conjunction with Editor.GetPoint(), AutoCAD draws a rubber band line, if PromptPointOptions.UseBasePoint is set true and a base point is supplied. However, to pick an entity, PromptEntityOptions and Editor.GetEntity() do not provide such option as "UseBasePoint".
Here is my way to solve this issue. After the first entity is picked, I handle Editor.PointMonitor event, so that when user moves mouse to pick next entity, a Transient Graphics (Line) is drawn in the event handler. The code is fairly simple, as following.
First, as I usually do, I created a class to encapsulate the wanted functionality: PickEntities.
using System.Collections.Generic; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.GraphicsInterface; namespace PickMultiplePoints { public class PickEntities { private Document _dwg; private Editor _editor; private List_points = null; private List _entities = null; private Line _rubberLine = null; private Point3d _basePoint; private Point3d _currentPoint; public PickEntities(Document dwg) { _dwg = dwg; _editor = _dwg.Editor; } #region public properties public List PickedPoints { get { return _points; } } public List PickedEntities { get { return _entities; } } #endregion public void DoPick(bool unhighlightAtEnd) { //Pick first entity ObjectId id; Point3d pt; if (!PickFirstEntity(out id, out pt)) return; _rubberLine = null; _points = new List (); _entities = new List (); _points.Add(pt); _entities.Add(id); SetHighlight(id, true); //Start drag rubber band line while (true) { if (!PickNextEntity()) break; } //Unhighlight all picked entities if (unhighlightAtEnd) { foreach (ObjectId entId in _entities) SetHighlight(entId, false); } } #region private methods private bool PickFirstEntity( out ObjectId id, out Point3d pt) { pt = new Point3d(); id = ObjectId.Null; PromptEntityOptions opt = new PromptEntityOptions("\nPick an entity: "); PromptEntityResult res = _editor.GetEntity(opt); if (res.Status == PromptStatus.OK) { id = res.ObjectId; //if the entity is a BlockRefernece, get Insertion Point //otherwise get the picked point Point3d p; if (GetBlockInsPoint(res.ObjectId, out p)) pt = p; else pt = res.PickedPoint; return true; } else { return false; } } private bool GetBlockInsPoint(ObjectId id, out Point3d insPt) { insPt=new Point3d(); bool isBlk = false; using (Transaction tran = _dwg.Database. TransactionManager.StartTransaction()) { BlockReference blk = tran.GetObject( id, OpenMode.ForRead) as BlockReference; if (blk != null) { insPt = blk.Position; isBlk = true; } tran.Commit(); } return isBlk; } private bool PickNextEntity() { _basePoint = _points[_points.Count - 1]; string msg = "\n" + _points.Count + " picked. Pick next entity: "; PromptEntityOptions opt = new PromptEntityOptions(msg); try { //Create rubber band line _rubberLine = new Line(_basePoint, _basePoint); //Set line properties, for example _rubberLine.SetDatabaseDefaults(_dwg.Database); //Create Transient graphics IntegerCollection intCol = new IntegerCollection(); TransientManager.CurrentTransientManager. AddTransient(_rubberLine, TransientDrawingMode.DirectShortTerm, 128, intCol); _editor.PointMonitor += new PointMonitorEventHandler(_editor_PointMonitor); PromptEntityResult res =_editor.GetEntity(opt); if (res.Status == PromptStatus.OK) { bool exists = false; foreach (ObjectId ent in _entities) { if (ent == res.ObjectId) { exists = true; break; } } if (!exists) { //if the entity is a BlockRefernece, //get Insertion Point. //Otherwise get the picked point Point3d p; if (!GetBlockInsPoint(res.ObjectId, out p)) { p = res.PickedPoint; } _points.Add(p); _entities.Add(res.ObjectId); SetHighlight(res.ObjectId, true); } else { _editor.WriteMessage( "\nThe entity has already been picked!"); } return true; } else { return false; } } finally { if (_rubberLine != null) { //Clear transient graphics IntegerCollection intCol = new IntegerCollection(); TransientManager.CurrentTransientManager. EraseTransient(_rubberLine, intCol); _rubberLine.Dispose(); _rubberLine = null; } _editor.PointMonitor -= new PointMonitorEventHandler(_editor_PointMonitor); } } private void SetHighlight(ObjectId id, bool highlight) { using (Transaction tran = _dwg.Database. TransactionManager.StartTransaction()) { Entity ent = (Entity)tran.GetObject(id, OpenMode.ForWrite); if (highlight) ent.Highlight(); else ent.Unhighlight(); } } private void _editor_PointMonitor( object sender, PointMonitorEventArgs e) { //Get mouse cursor point _currentPoint = e.Context.RawPoint; //Update line _rubberLine.EndPoint = _currentPoint; //Update Transient graphics IntegerCollection intCol = new IntegerCollection(); TransientManager.CurrentTransientManager. UpdateTransient(_rubberLine, intCol); } #endregion } }
Then, I use "PickEntities" class in a command method of a command class:
using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.EditorInput; [assembly: CommandClass(typeof(PickMultiplePoints.MyCommands))] namespace PickMultiplePoints { public class MyCommands { [CommandMethod("MyPick")] public static void DoCommand() { Document dwg = Autodesk.AutoCAD.ApplicationServices. Application.DocumentManager.MdiActiveDocument; Editor ed = dwg.Editor; //Do picking PickEntities picker = new PickEntities(dwg); picker.DoPick(true); ObjectId[] ids = picker.PickedEntities.ToArray(); ed.WriteMessage( "\nMy command executed: {0} entities picked.", ids.Length); } } }
Here is the video clip of the code in action.
Use the technique presented in this article, one can also makes AutoCAD draw a polyline started from the first picked entity/point along the picked entities dynamically.
1 comment:
Great code, but it throws an unhandled exception if you cancel the command before picking the first entity:
if(!PickFirstEntity(out id, out pt)) return;
return before _entities is instanciated, so
ObjectId[] ids = picker.PickedEntities.ToArray();
throws an exception.
Post a Comment