Wednesday, March 18, 2020

Showing Helpful Information As Tool Tip During Jig Dragging - 2

In the first part of this topic, I have built a quite simple moving jig by handling Editor.PointMonitor, where Transient Graphics is used to show a ghost image as the dragging effect. Because of PointMonitorEventArgs, it is really easy to show custom tool tip to provide useful information that would help user to decide where/how to drag an entity.

At this point, I have a good working moving jig that could prompt some information during entity dragging. However a new issue comes: how to make the jig to show different information as tool tip, based on business workflow, without having to modify the jig's code? When mouse cursor is at an entity during dragging, we now can get the entity's ObjectId, then we could obtain different information according to business requirements and show the information as tool tip, if necessary. Obviously, we do not want to modify the code in the PointMonitor event handler whenever there is different business requirement.

The approach to solve this is to inject a predefined tool tip generating interface functions, and the interface functions are implemented/coded outside the jig class. Following is the updated jig class code (red lines are the changes).

using System;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
 
namespace JigWithTooltip
{
    public class TooltipMovingJig2
    {
        private Document _dwg = null;
        private Editor _ed = null;
 
        private Entity _ghost = null;
        private Entity _entity = null;
        private Point3d _basePoint = Point3d.Origin;
        private Point3d _mousePoint = Point3d.Origin;
 
        private TransientManager _tsManager = 
            TransientManager.CurrentTransientManager;
 
        private Func<ObjectId, Point3d, string> _tipExtractFunction = null;
        private Func<ObjectId, bool> _isTooltipTargetFunc = null;
 
        public TooltipMovingJig2(Document dwg)
        {
            _dwg = dwg;
            _ed = dwg.Editor;
        }
 
        public void MoveEntity(
            Func<ObjectId, Point3d, string> tipExtractFunc = null,
            Func<ObjectId, bool> isTooltipTargetFunc = null)
        {
            if (!SelectEntity(out ObjectId entIdout _basePoint)) return;
 
            _tipExtractFunction = tipExtractFunc;
            _isTooltipTargetFunc = isTooltipTargetFunc;
 
            _mousePoint = _basePoint;
 
            using (var tran = _dwg.TransactionManager.StartTransaction())
            {
                _entity = (Entity)tran.GetObject(entIdOpenMode.ForRead);
                _entity.Highlight();
                try
                {
                    if (GetDestinationPoint(out Point3d destPoint))
                    {
                        var mt = Matrix3d.Displacement(
                            _basePoint.GetVectorTo(destPoint));
                        _entity.UpgradeOpen();
                        _entity.TransformBy(mt);
                    }
                }
                finally
                {
                    _entity.Unhighlight();
                }
 
                tran.Commit();
            }
        }
 
        #region private methods
 
        private bool SelectEntity(out ObjectId entIdout Point3d basePoint)
        {
            entId = ObjectId.Null;
            basePoint = Point3d.Origin;
 
            var res = _ed.GetEntity("\nSelect entity to move:");
            if (res.Status == PromptStatus.OK)
            {
                entId = res.ObjectId;
                basePoint = res.PickedPoint;
 
                var opt = new PromptPointOptions(
                    "\nSelect base point:");
 
                var pRes = _ed.GetPoint(opt);
                if (pRes.Status == PromptStatus.OK)
                {
                    basePoint = pRes.Value;
                }
 
                return true;
            }
            else
            {
                return false;
            }
        }
 
        private void CreateMovingGhost()
        {
            ClearMovingGhost();
 
            _ghost = _entity.Clone() as Entity;
            _ghost.ColorIndex = 2;
            var mt = Matrix3d.Displacement(_basePoint.GetVectorTo(_mousePoint));
            _ghost.TransformBy(mt);
 
            _tsManager.AddTransient(
                _ghost, 
                TransientDrawingMode.DirectTopmost, 
                128, 
                new IntegerCollection());
        }
 
        private void ClearMovingGhost()
        {
            if (_ghost != null)
            {
                _tsManager.EraseTransient(_ghost, new IntegerCollection());
                _ghost.Dispose();
                _ghost = null;
            }
        }
 
        private bool GetDestinationPoint(out Point3d destPoint)
        {
            destPoint = Point3d.Origin;
            var picked = false;
 
            var opt = new PromptPointOptions(
                "Move to:");
            opt.UseBasePoint = true;
            opt.BasePoint = _basePoint;
            opt.UseDashedLine = true;
 
            // Set system variable "PICKBOX" to at least 10 (range 0 to 20)
            // so that mouse cursor would pick up entities easily 
            // when moveving close
            var pickBox = Convert.ToInt32(
                Application.GetSystemVariable("PICKBOX"));
            bool pickBoxChanged = false;
            if (pickBox < 10)
            {
                Application.SetSystemVariable("PICKBOX", 10);
                pickBoxChanged = true;
            }
 
            var forcedCount = _ed.TurnForcedPickOn();
            _ed.PointMonitor += Editor_PointMonitor;
 
            try
            {
                var res = _ed.GetPoint(opt);
                if (res.Status == PromptStatus.OK)
                {
                    destPoint = res.Value;
                    picked = true;
                }
            }
            finally
            {
                ClearMovingGhost();
                _ed.PointMonitor -= Editor_PointMonitor;
 
                var count = _ed.TurnForcedPickOff();
                while (count > forcedCount - 1)
                {
                    count = _ed.TurnForcedPickOff();
                }
 
                // restore "PICKBOX" original value
                if (pickBoxChanged)
                    Application.SetSystemVariable("PICKBOX"pickBox);
            }
 
            return picked;
        }
 
        private void Editor_PointMonitor(object senderPointMonitorEventArgs e)
        {
            _mousePoint = e.Context.RawPoint;
            CreateMovingGhost();
 
            var paths = e.Context.GetPickedEntities();
            if (paths == null || paths.Length == 0) return;
            var ids = paths[0].GetObjectIds();
            if (ids == null || ids.Length == 0) return;
 
            var id = ids[0];
 
            if (_isTooltipTargetFunc != null)
            {
                if (!_isTooltipTargetFunc(id))
                {
                    id = ObjectId.Null;
                }
            }
            
            if (!id.IsNull)
            {
                var tip = GetDefaultTooltip(id);
                if (_tipExtractFunction != null)
                {
                    tip = _tipExtractFunction(id, _mousePoint);
                }
 
                e.AppendToolTipText(tip);
            }
        }
 
        private string GetDefaultTooltip(ObjectId entId)
        {
            return $"\nMove to/close to:{entId.ObjectClass.DxfName.ToUpper()}";
        }
 
        #endregion
    }
}

As the code shows, the jig class now has 2 Functions as its member, which can be injected from the jig's calling procedure. One function is to determine if an entity is the target entity that I want to show custom tool tip; the other is to generate actual tool tip content. Both function take ObjectId as input parameter; and the tool til generating function also takes a Point3d input as parameter, which is where the mouse cursor is and may be needed for specific tool tip content.

The jig class only defines the 2 function's signature (interface). The actual implementations of the 2 functions are done outside the jig class. They are injected into the jig class when the jig's public method MoveEntity() is called. Thus I am free to write different functions to determine whether an entity is the tool tip showing target and what tool tip content to be generated against the entity.

Following are 2 pairs of these functions: one pair is to test if the entity is closed polyline, if yes, get its area information as tool tip; the other pair - to test if the entity is curve, and show the distance of mouse cursor point to the curve's start point as tool tip information.

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
 
namespace JigWithTooltip
{
    public class EntityToolTipHelper
    {
        public static bool IsAreaToolTipTarget(ObjectId entId)
        {
            return entId.ObjectClass == RXClass.GetClass(typeof(Polyline));
        }
 
        public static string ExtractAreaToolTip(ObjectId entIdPoint3d mouseLocation)
        {
            if (entId.ObjectClass != RXClass.GetClass(typeof(Polyline))) return "";
 
            var tip = "";
 
            using (var tran = new OpenCloseTransaction())
            {
                var poly = (Polyline)tran.GetObject(entIdOpenMode.ForRead);
                if (poly.Closed)
                {
                    tip =$"AREA: {poly.Area.ToString("##########0.00")}";
                }
            }
 
            return tip;
        }
 
        public static bool IsDistanceToolTipTarget(ObjectId entId)
        {
            return entId.ObjectClass.IsDerivedFrom(RXClass.GetClass(typeof(Curve)));
        }
 
        public static string ExtractDistanceToolTip(ObjectId entIdPoint3d mousePoint)
        {
            if (!entId.ObjectClass.IsDerivedFrom(RXClass.GetClass(typeof(Curve)))) return "";
 
            var tip = "";
 
            using (var tran = new OpenCloseTransaction())
            {
                var curve = (Curve)tran.GetObject(entIdOpenMode.ForRead);
                var pt = curve.GetClosestPointTo(mousePointfalse);
                var dist = curve.GetDistAtPoint(pt);
 
                tip = $"DISTANCE FROM START POINT: {dist.ToString("########0.00")}";
            }
 
            return tip;
        }
    }
}

Now I can have a command to run the moving jig with area being prompted and another command to run the moving jig with distance being prompted:

using Autodesk.AutoCAD.Runtime;
using CadApp = Autodesk.AutoCAD.ApplicationServices.Application;
 
[assemblyCommandClass(typeof(JigWithTooltip.MyCommands))]
 
namespace JigWithTooltip
{
    public class MyCommands
    {
        [CommandMethod("DoMove")]
        public static void RunMyCommand()
        {
            var dwg = CadApp.DocumentManager.MdiActiveDocument;
            var ed = dwg.Editor;
 
            try
            {
                var mover = new TooltipMovingJig2(dwg);
                mover.MoveEntity();
            }
            catch (System.Exception ex)
            {
                ed.WriteMessage($"\nInitializing error:\n{ex.Message}\n");
            }
        }
 
        [CommandMethod("MoveToArea")]
        public static void MoveWithAreaPrompt()
        {
            var dwg = CadApp.DocumentManager.MdiActiveDocument;
            var ed = dwg.Editor;
 
            try
            {
                var mover = new TooltipMovingJig2(dwg);
                mover.MoveEntity(
                    EntityToolTipHelper.ExtractAreaToolTip,
                    EntityToolTipHelper.IsAreaToolTipTarget);
            }
            catch (System.Exception ex)
            {
                ed.WriteMessage($"\nInitializing error:\n{ex.Message}\n");
            }
        }
 
        [CommandMethod("MoveToDistance")]
        public static void MoveWithDistancePrompt()
        {
            var dwg = CadApp.DocumentManager.MdiActiveDocument;
            var ed = dwg.Editor;
 
            try
            {
                var mover = new TooltipMovingJig2(dwg);
                mover.MoveEntity(
                    EntityToolTipHelper.ExtractDistanceToolTip,
                    EntityToolTipHelper.IsDistanceToolTipTarget);
            }
            catch (System.Exception ex)
            {
                ed.WriteMessage($"\nInitializing error:\n{ex.Message}\n");
            }
        }
    }
}

As the code shows, it is very easy to implement a pair of functions outside the jig class code to make the jig smart enough to decide whether custom tool tip is wanted, and what tool tip content is to appear, Here the a video clip showing the visual effect of running the commands:


Saturday, March 14, 2020

Showing Helpful Information As Tool Tip During Jig Dargging - Updated

Recently there is a discussion thread in Autodesk's user discussion forum about showing information of an entity when mouse cursor is near, or hover, it during jig dragging. While the OP did indicate that my suggestion helped (in some way, but no details were provided), this discussion also somehow inspired extra interest for me to play it a bit. So, I decided to write something here on these topics:

1. How easy it is to create a custom "jig" with combination of Transient Graphics and Editor.PointMonitor event handling;

2. How easy to show custom tool tip because of handling PointMonitor;

3. How to obtain different tool tip content from an entity, over which the mouse cursor crosses, according to required business logic without having to modify PointMonitor event handling code when the need for different tool tip content arise while this custom job is used.

Any one who reads my blogs over the years would know that I had quite a few articles discussing Transient Graphics and handling PointMonitor. Using them to create a custom "jig" is really simple (even arguably simpler than deriving from built-in Entity/DrawJig class, in some case). Here I am going to create a custom moving jig for user to select an entity (say, a Circle); then, drag a ghost image of the entity to (or close to) another entity; during the dragging, when the mouse cursor is over, or near, the other entity, some information about the it will show as tool tip, so that user can decide if the first entity is dragged toward correct position. Obviously the tool tip content should be somewhat related to the second entity and the information is somehow not very obvious to the user (for example, the second entity may have XData attached, which may be critical for use to decide whether to drag the first entity to here or not).

Now that I have the process context clear enough, here is the code of the custom jig.

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
 
namespace JigWithTooltip
{
    public class TooltipMovingJig
    {
        private Document _dwg = null;
        private Editor _ed = null;
 
        private Entity _ghost = null;
        private Entity _entity = null;
        private Point3d _basePoint = Point3d.Origin;
        private Point3d _mousePoint = Point3d.Origin;
 
        private TransientManager _tsManager = 
            TransientManager.CurrentTransientManager;
 
        public TooltipMovingJig(Document dwg)
        {
            _dwg = dwg;
            _ed = dwg.Editor;      
        }
 
        public void MoveEntity()
        {
            if (!SelectEntity(out ObjectId entIdout _basePoint)) return;
 
            _mousePoint = _basePoint;
 
            using (var tran = _dwg.TransactionManager.StartTransaction())
            {
                _entity = (Entity)tran.GetObject(entIdOpenMode.ForRead);
                _entity.Highlight();
                try
                {
                    if (GetDestinationPoint(out Point3d destPoint))
                    {
                        var mt = Matrix3d.Displacement(
                            _basePoint.GetVectorTo(destPoint));
                        _entity.UpgradeOpen();
                        _entity.TransformBy(mt);
                    }
                }
                finally
                {
                    _entity.Unhighlight();
                }
 
                tran.Commit();
            }
        }
 
        #region private methods
 
        private bool SelectEntity(out ObjectId entIdout Point3d basePoint)
        {
            entId = ObjectId.Null;
            basePoint = Point3d.Origin;
 
            var res = _ed.GetEntity("\nSelect entity to move:");
            if (res.Status == PromptStatus.OK )
            {
                entId = res.ObjectId;
                basePoint = res.PickedPoint;
 
                var opt = new PromptPointOptions(
                    "\nSelect base point:");
 
                var pRes = _ed.GetPoint(opt);
                if (pRes.Status== PromptStatus.OK)
                {
                    basePoint = pRes.Value;
                }
 
                return true;
            }
            else
            {
                return false;
            }
        }
 
        private void CreateMovingGhost()
        {
            ClearMovingGhost();
 
            _ghost = _entity.Clone() as Entity;
            _ghost.ColorIndex = 2;
            var mt = Matrix3d.Displacement(_basePoint.GetVectorTo(_mousePoint));
            _ghost.TransformBy(mt);
 
            _tsManager.AddTransient(
                _ghost, 
                TransientDrawingMode.DirectTopmost, 
                128, 
                new IntegerCollection());
        }
 
        private void ClearMovingGhost()
        {
            if (_ghost != null)
            {
                _tsManager.EraseTransient(_ghost, new IntegerCollection());
                _ghost.Dispose();
                _ghost = null;
            }
        }
 
        private bool GetDestinationPoint(out Point3d destPoint)
        {
            destPoint = Point3d.Origin;
            var picked = false;
 
            var opt = new PromptPointOptions(
                "Move to:");
            opt.UseBasePoint = true;
            opt.BasePoint = _basePoint;
            opt.UseDashedLine = true;
 
            _ed.PointMonitor += Editor_PointMonitor;
 
            try
            {
                var res = _ed.GetPoint(opt);
                if (res.Status == PromptStatus.OK)
                {
                    destPoint = res.Value;
                    picked = true;
                }
            }
            finally
            {
                ClearMovingGhost();
                _ed.PointMonitor -= Editor_PointMonitor;
            }
            
            return picked;
        }
 
        private void Editor_PointMonitor(object senderPointMonitorEventArgs e)
        {
            _mousePoint = e.Context.RawPoint;
            CreateMovingGhost();
 
            ObjectId id = GetHoveredEntity(e.Context.RawPoint);
            if (!id.IsNull)
            {
                // Compose custom tool tip text
                var tip = GetDefaultTooltip(id);
                e.AppendToolTipText(tip);
            }
        }
 
        private ObjectId GetHoveredEntity(Point3d mousePoint)
        {
            var entId = ObjectId.Null;
 
            var res = _ed.SelectAtPickBox(mousePoint);
            if (res.Status== PromptStatus.OK)
            {
                entId = res.Value[0].ObjectId;
            }
 
            return entId;
        }
 
        private string GetDefaultTooltip(ObjectId entId)
        {
            return $"\nMove to/close to:{entId.ObjectClass.DxfName.ToUpper()}";
        }
 
        #endregion
    }
}

We can see code for this custom jig is incredibly simple: after selecting an entity to move (and its base point for moving), we only need to surround the moving point picking call (Editor.GetPoint()) with adding/removing PointMonitor event handler. The "jig effect" (showing a ghost image of selected entity that follows mouse cursor) is done by creating Transient Graphics in the event handler.

Since PointMonitorEventArgs object provides a method AppendToolTipText() method, we can update the tool tip with whatever text we want to. Thus, if we want to get information of an entity where the mouse cursor is crossing or is near, then we need to be able to know when the mouse cursor is crossing/is near to an entity. When AutoCAD Editor is not is selecting/picking mode, PointMonitorEventArgs.Context.GetPickedEmtities() would provide a way to find out if there is entity or entities at the mouse cursor. But unfortunately, in this custom jig case, because AutoCAD Editor is waiting for picking a point, the GetPickedEntities() method will not return any entity at mouse cursor at all. So, I need to somehow be able to programmatically "select" an entity that is at mouse cursor or very near to when mouse is moving/dragging.

In one of my old article, I used cursor pick box to call Editor.SelectCrossingPolygon[Window]() to select entities. I thought I could re-use that code. Of course, in this case, the mouse cursor is displayed as "cross", not "pick box", because AutoCAD Editor is waiting for a point picking. So, here the the extension method to select entity at mouse cursor with an invisible small window in size of the regular AutoCAD selecting box.

using System;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
 
namespace JigWithTooltip
{
    public static class EditorSelectionExtension
    {
        public static PromptSelectionResult SelectAtPickBox(
           this Editor edPoint3d pickBoxCentre)
         {
            // Get pick box's size on screen
            System.Windows.Point screenPt = ed.PointToScreen(pickBoxCentre, 1);
     
            // Get pickbox's size. Note, the number obtained from
            // system variable "PICKBOX" is actually the half of
            // pickbox's width/height
            int pSize = Convert.ToInt32(Application.GetSystemVariable("PICKBOX"));
            if (pSize < 10) pSize = 10;
 
            // Define a Point3dCollection for CrossingWindow selecting
            Point3dCollection points = new Point3dCollection();
    
            System.Windows.Point p;
            Point3d pt;
    
            p = new System.Windows.Point(screenPt.X - pSizescreenPt.Y - pSize);
            pt = ed.PointToWorld(p, 1).TransformBy(ed.CurrentUserCoordinateSystem.Inverse());
            points.Add(pt);
    
            p = new System.Windows.Point(screenPt.X + pSizescreenPt.Y - pSize);
            pt = ed.PointToWorld(p, 1).TransformBy(ed.CurrentUserCoordinateSystem.Inverse());
            points.Add(pt);
    
            p = new System.Windows.Point(screenPt.X + pSizescreenPt.Y + pSize);
            pt = ed.PointToWorld(p, 1).TransformBy(ed.CurrentUserCoordinateSystem.Inverse());
            points.Add(pt);
    
            p = new System.Windows.Point(screenPt.X - pSizescreenPt.Y + pSize);
            pt = ed.PointToWorld(p, 1).TransformBy(ed.CurrentUserCoordinateSystem.Inverse());
            points.Add(pt);
    
            return ed.SelectCrossingPolygon(points);
        }
    }
}

At this point, the moving jig with tool tip prompt is completed. Here is the code to use it in a command class:

using Autodesk.AutoCAD.Runtime;
using CadApp = Autodesk.AutoCAD.ApplicationServices.Application;
 
[assemblyCommandClass(typeof(JigWithTooltip.MyCommands))]
 
namespace JigWithTooltip
{
    public class MyCommands
    {
        [CommandMethod("DoMove")]
        public static void RunMyCommand()
        {
            var dwg = CadApp.DocumentManager.MdiActiveDocument;
            var ed = dwg.Editor;
 
            try
            {
                var mover = new TooltipMovingJig(dwg);
                mover.MoveEntity();
            }
            catch (System.Exception ex)
            {
                ed.WriteMessage($"\nInitializing error:\n{ex.Message}\n");
            }
        }
    }
}

This video clip shows the custom jig effect. One would notice that how custom tool tip appears when the mouse cursor crosses or is near to another entity.

Thus far the topic 1 and 2 have been addressed. Stay tuned for topic 3 in my next post.

Update

After I posted this, and involved another discussion thread, Alexander Rivilis suggested another solution to make PointMonitorEventArgs.Context.GetPickedEntities() work while Editor is waiting for user input, that is, use Editor.TurnForcedPickOn[Off]() method pair. I went ahead to give it a try. Indeed, this solves the issue with GetPickedEntities() in the PointMonitor event handler. So, I no longer need the Editor extension method Editor.SelectAtPickBox(), which make the code simpler. Here is the updated code (changed lines are in red).

using System;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
 
namespace JigWithTooltip
{
    public class TooltipMovingJig
    {
        private Document _dwg = null;
        private Editor _ed = null;
 
        private Entity _ghost = null;
        private Entity _entity = null;
        private Point3d _basePoint = Point3d.Origin;
        private Point3d _mousePoint = Point3d.Origin;
 
        private TransientManager _tsManager = 
            TransientManager.CurrentTransientManager;
 
        public TooltipMovingJig(Document dwg)
        {
            _dwg = dwg;
            _ed = dwg.Editor;      
        }
 
        public void MoveEntity()
        {
            if (!SelectEntity(out ObjectId entIdout _basePoint)) return;
 
            _mousePoint = _basePoint;
 
            using (var tran = _dwg.TransactionManager.StartTransaction())
            {
                _entity = (Entity)tran.GetObject(entIdOpenMode.ForRead);
                _entity.Highlight();
                try
                {
                    if (GetDestinationPoint(out Point3d destPoint))
                    {
                        var mt = Matrix3d.Displacement(
                            _basePoint.GetVectorTo(destPoint));
                        _entity.UpgradeOpen();
                        _entity.TransformBy(mt);
                    }
                }
                finally
                {
                    _entity.Unhighlight();
                }
 
                tran.Commit();
            }
        }
 
        #region private methods
 
        private bool SelectEntity(out ObjectId entIdout Point3d basePoint)
        {
            entId = ObjectId.Null;
            basePoint = Point3d.Origin;
 
            var res = _ed.GetEntity("\nSelect entity to move:");
            if (res.Status == PromptStatus.OK )
            {
                entId = res.ObjectId;
                basePoint = res.PickedPoint;
 
                var opt = new PromptPointOptions(
                    "\nSelect base point:");
 
                var pRes = _ed.GetPoint(opt);
                if (pRes.Status== PromptStatus.OK)
                {
                    basePoint = pRes.Value;
                }
 
                return true;
            }
            else
            {
                return false;
            }
        }
 
        private void CreateMovingGhost()
        {
            ClearMovingGhost();
 
            _ghost = _entity.Clone() as Entity;
            _ghost.ColorIndex = 2;
            var mt = Matrix3d.Displacement(_basePoint.GetVectorTo(_mousePoint));
            _ghost.TransformBy(mt);
 
            _tsManager.AddTransient(
                _ghost, 
                TransientDrawingMode.DirectTopmost, 
                128, 
                new IntegerCollection());
        }
 
        private void ClearMovingGhost()
        {
            if (_ghost != null)
            {
                _tsManager.EraseTransient(_ghost, new IntegerCollection());
                _ghost.Dispose();
                _ghost = null;
            }
        }
 
        private bool GetDestinationPoint(out Point3d destPoint)
        {
            destPoint = Point3d.Origin;
            var picked = false;
 
            var opt = new PromptPointOptions(
                "Move to:");
            opt.UseBasePoint = true;
            opt.BasePoint = _basePoint;
            opt.UseDashedLine = true;
 
            // Set system variable "PICKBOX" to at least 10 (range 0 to 20)
            // so that mouse cursor would pick up entities easily 
            // when moveving close
            var pickBox = Convert.ToInt32(
                Application.GetSystemVariable("PICKBOX"));
            bool pickBoxChanged = false;
            if (pickBox < 10)
            {
                Application.SetSystemVariable("PICKBOX", 10);
                pickBoxChanged = true;
            }
 
            var forcedCount = _ed.TurnForcedPickOn();

            _ed.PointMonitor += Editor_PointMonitor;
 
            try
            {
                var res = _ed.GetPoint(opt);
                if (res.Status == PromptStatus.OK)
                {
                    destPoint = res.Value;
                    picked = true;
                }
            }
            finally
            {
                ClearMovingGhost();
                _ed.PointMonitor -= Editor_PointMonitor;
                var count = _ed.TurnForcedPickOff();
                while(count>forcedCount-1)
                {
                    count = _ed.TurnForcedPickOff();
                }
 
                // restore "PICKBOX" original value
                if (pickBoxChanged) 
                    Application.SetSystemVariable("PICKBOX", pickBox);
            }
            
            return picked;
        }
 
        private void Editor_PointMonitor(object senderPointMonitorEventArgs e)
        {
            _mousePoint = e.Context.RawPoint;
            CreateMovingGhost();
 
            var paths = e.Context.GetPickedEntities();
            if (paths == null || paths.Length == 0) return;
            var ids = paths[0].GetObjectIds();
            if (ids == null || ids.Length == 0) return;
 
            // Compose custom tool tip text
            var tip = GetDefaultTooltip(ids[0]);
            e.AppendToolTipText(tip);
 
            //ObjectId id = GetHoveredEntity(e.Context.RawPoint);
            //if (!id.IsNull)
            //{
            //    // Compose custom tool tip text
            //    var tip = GetDefaultTooltip(id);
            //    e.AppendToolTipText(tip);
            //}
        }
 
        private ObjectId GetHoveredEntity(Point3d mousePoint)
        {
            var entId = ObjectId.Null;
 
            var res = _ed.SelectAtPickBox(mousePoint);
            if (res.Status== PromptStatus.OK)
            {
                entId = res.Value[0].ObjectId;
            }
 
            return entId;
        }
 
        private string GetDefaultTooltip(ObjectId entId)
        {
            return $"\nMove to/close to:{entId.ObjectClass.DxfName.ToUpper()}";
        }
 
        #endregion
    }
}
















Followers

About Me

My photo
After graduating from university, I worked as civil engineer for more than 10 years. It was AutoCAD use that led me to the path of computer programming. Although I now do more generic business software development, such as enterprise system, timesheet, billing, web services..., AutoCAD related programming is always interesting me and I still get AutoCAD programming tasks assigned to me from time to time. So, AutoCAD goes, I go.