Thursday, February 24, 2022

Where The Tangent Point Is - Show It When User Moves Mouse

Frequent visitors here must have noticed that many of my articles talk about Transient Grphics. It is one of my favorite AutoCAD .NET API features. You see, if you develop AutoCAD application to help user interact with AutoCAD easier, more efficiently, CAD users would appreciate if your application provides as much visual assistance as possible. A while ago, a question posted in .NET API forum about finding tangent point of a curve from a point, I though it would be an interesting exercise to write a bit of code to give user visual feedback of potential tangent point when mouse is dragged.

The visual part, obviously by using Transient Graphics, is rather simple, as long as the tangent point is calculated correctly. The calculation can be purely done with trigonometric methods/formula. However, since I need to use AutoCAD entities to generate transient image, I took a easier way of using AutoCAD objects for the calculation. One may argue that using trigonometric math would use much less computer power then using AutoCAD object for such calculation. But considering how the code runs: while moving mouse to show the visual effect, AutoCAD is basically waiting for user to decide his/here input and doing nothing else. So, the extra computing power burden would not be an issue, IMO.

Anyway, here is the code:

using System;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
 
namespace TangentTrail
{
    public class TangentFinder : IDisposable
    {
        private readonly Document _dwg;
        private readonly Editor _ed;
        private CircularArc3d _circularArc = null;
        private TransientManager tsManager = TransientManager.CurrentTransientManager;
 
        private Point3d? _tangent1 = null;
        private Point3d? _tangent2 = null;
 
        private Line _ghostLine1 = null;
        private Line _ghostLine2 = null;
        private DBPoint _ghostPoint1 = null;
        private DBPoint _ghostPoint2 = null;
 
        private readonly int _colorIndex;
        private bool _transientAdded = false;
 
        public TangentFinder(Document dwgint ghostColorIndex=1)
        {
            _dwg= dwg;
            _ed = _dwg.Editor;
            _colorIndex = ghostColorIndex;
        }
 
        public Point3d? TangentPoint1 => _tagent1;
        public Point3d? TangentPoint2 => _tagent2;
        public bool DragForTangent()         {             if (!SelectCurve()) return false;             var pdMode = _dwg.Database.Pdmode;             try             {                 _dwg.Database.Pdmode = 34;                 _ed.PointMonitor += Editor_PointMonitor;                 var res = _ed.GetPoint(                     "\nSelect point to form a tangent line:");                 if (res.Status== PromptStatus.OK &&                      (_tangent1.HasValue || _tangent2.HasValue))                 {                     return true;                 }                 else                 {                     return false;                 }             }             finally             {                 _ed.PointMonitor-=Editor_PointMonitor;                 _dwg.Database.Pdmode = pdMode;             }         }         public void Dispose()         {             ClearGhost();         }         #region private methods         private bool SelectCurve()         {             var opt = new PromptEntityOptions(                 "\nSelect an ARC or a CIRCLE:");             opt.SetRejectMessage("\nInvalid: must be an ARC or a CIRCLE!");             opt.AddAllowedClass(typeof(Arc), true);             opt.AddAllowedClass(typeof(Circle), true);             var res = _ed.GetEntity(opt);             if (res.Status== PromptStatus.OK)             {                 using (var tran = new OpenCloseTransaction())                 {                     var curve = (Curve)tran.GetObject(res.ObjectId, OpenMode.ForRead);                     _circularArc = curve.GetGeCurve() as CircularArc3d;                 }                 return true;             }             else             {                 return false;             }         }         private void Editor_PointMonitor(object sender, PointMonitorEventArgs e)         {             ClearGhost();             var pt = e.Context.RawPoint;                          CalculateTangentPoint(                 _circularArc, pt, out _tangent1, out _tangent2);             if (_tangent1.HasValue)             {                 ShowGhost(_ghostLine1, _ghostPoint1, pt, _tangent1.Value);             }             if (_tangent2.HasValue)             {                 ShowGhost(_ghostLine2, _ghostPoint2, pt, _tangent2.Value);             }         }         private void ClearGhost()         {             ClearGhost(_ghostLine1);             ClearGhost(_ghostLine2);             ClearGhost(_ghostPoint1);             ClearGhost(_ghostPoint2);         }         private void ClearGhost(Drawable ghost)         {             if (ghost != null)             {                 tsManager.EraseTransient(ghost, new IntegerCollection());                 ghost.Dispose();             }         }         private void ShowGhost(             Line ghostLine, DBPoint ghostPoint,              Point3d startPoint, Point3d endPoint)         {             if (ghostLine != null && !ghostLine.IsDisposed)             {                 ghostLine.Dispose();             }             ghostLine = null;             if (ghostPoint != null && !ghostPoint.IsDisposed)             {                 ghostPoint.Dispose();             }             ghostPoint = null;             ghostLine = new Line();             ghostLine.ColorIndex = _colorIndex;                          ghostLine.StartPoint = startPoint;             ghostLine.EndPoint = endPoint;             tsManager.AddTransient(                 ghostLine,                  TransientDrawingMode.DirectTopmost,                  128,                  new IntegerCollection());             ghostPoint = new DBPoint(endPoint);             ghostPoint.ColorIndex = _colorIndex;             tsManager.AddTransient(                 ghostPoint,                 TransientDrawingMode.DirectTopmost,                 128,                 new IntegerCollection());         }         private void CalculateTangentPoint(             CircularArc3d arc, Point3d point,             out Point3d? tangent1out Point3d? tangent2)         {             tangent1 = null;             tangent2 = null;             var dist = point.DistanceTo(arc.Center);             if (dist < arc.Radius) return;             var angle = Math.Acos(arc.Radius / dist);             using (var line = new Line(arc.Center, point))             {                 var angle1 = line.Angle + angle;                 var angle2 = line.Angle - angle;                 using (var circle=new Circle(arc.Center, arc.Normal, arc.Radius))                 {                     var arcLen = angle1 * arc.Radius;                     var pt = circle.GetPointAtDist(arcLen);                     if (_circularArc.IsOn(pt))                     {                         tangent1 = pt;                     }                     arcLen = angle2 * arc.Radius;                     pt = circle.GetPointAtDist(arcLen);                     if (_circularArc.IsOn(pt))                     {                         tangent2 = pt;                     }                 }             }         }         #endregion     } }

The CommandClass/Method to run it:

using Autodesk.AutoCAD.Runtime;
using CadApp = Autodesk.AutoCAD.ApplicationServices.Application;
 
[assembly: CommandClass(typeof(TangentTrail.MyCommands))]
 
namespace TangentTrail
{
    public class MyCommands 
    {
        [CommandMethod("GetTangent")]
        public static void GetTangent()
        {
            var dwg = CadApp.DocumentManager.MdiActiveDocument;
            var ed = dwg.Editor;
 
            using (var tangentTool = new TangentFinder(dwg))
            {
                if (tangentTool.DragForTangent())
                {
 
                }
            }
        }
 
    }
}


The video below shows the code in action:







5 comments:

  1. We are one of the authorized person for bus branding in Chennai metropolitan.Across Tamilnadu. Bus Back Panel Advertising in Chennai, MTC - Chennai City Bus Advertisement, Bus Advertising Agency, Bus Ad Agency, Bus branding in Tamil Nadu, Bus back advertising size, Bus back advertising Chennai, Bus back advertising cost in Chennai, Government bus advertising, Bus advertising rates in Chennai

    Bus Ad Agency Chennai

    ReplyDelete
  2. I read a lot of your and Kean's articles on transient graphics (and Jig for that matter), and I struggle with certain aspects trying to apply them to my own scenarios. I haven't found a good reference for the inputs. Is there one? Civil3D has a good reference, but is there one for vanilla AutoCAD? This is what I use for Civil3D:

    http://docs.autodesk.com/CIV3D/2019/ENU/API_Reference_Guide/index.html>API Reference Guide

    ReplyDelete
  3. I use the same documentation as you could see (those downloaded with ObjectARX SDK, or the online ones, as you referred to). Yes, there is developer reference for vanilla AutoCAD, either online y Autodesk, or you can download AutoCAD ObjectARX SDK, which includes developer reference documents in its "docs" folder. But again, in terms of transient graphics, there is no mode details in the documents that you can find other than what you have already seeing in the code samples I or other posted.

    ReplyDelete
  4. Chennai is a thriving city with a sizable population. As a result, there is a high demand for public transportation, which is further met by buses. You can build brand awareness and aggressively target the masses with Bus Ads Agency in Chennai.

    ReplyDelete
  5. Hi/Bonjour

    Very good work, thanks for sharing knowledge, it’s really interesting.
    A little problem: the videos do not work in full screen

    Amicalement

    ReplyDelete