I do not use Civil3D, but thought I know what the question is asking for. So, I decided to dig out that piece of code and do a bit enhancement, so that the temporary TransientGraphics can be drawn dynamically when the mouse cursor moves along the polyline.
I though I would like the code to do this:
1. User selects a polyline;
2. User decides the start point of the tracing. Polyline's start point is default, but user can choose to pick a point on the polyline;
3. Set an allowed offset, so that the mouse cursor does not have to move along the polyline exactly on top of the polyline;
4. The temporary TransientGraphics' color/lineweight can be set easily.
The code materializes above goals are actually quite simple.
Here is the class PolylineTracer:
using System; using System.Collections.Generic; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.GraphicsInterface; using CadApp = Autodesk.AutoCAD.ApplicationServices.Application; using CadDb = Autodesk.AutoCAD.DatabaseServices; namespace TraceAlongPolyline { public class PolylineTracer : IDisposable { private Document _dwg; private Editor _ed; private TransientManager _tsManager; private CadDb.Polyline _polyline = null; private CadDb.Polyline _drawable = null; private Point3d _startPoint; private double _allowedOffset = 1.0; private int _colorIndex = 2; private LineWeight _lineWeight = LineWeight.LineWeight050; public PolylineTracer(Document dwg) { _dwg = dwg; _ed = dwg.Editor; _tsManager = TransientManager.CurrentTransientManager; } public void Dispose() { ClearTransientGraphics(); } public void Trace( int colorIndex=2, LineWeight lineweight=LineWeight.LineWeight050) { _colorIndex = colorIndex; _lineWeight = lineweight; ObjectId selectedId = SelectPolyline(); if (selectedId.IsNull) { _ed.WriteMessage("\n*Cancel*"); return; } using (Transaction tran = _dwg.TransactionManager.StartTransaction()) { _polyline = (CadDb.Polyline) tran.GetObject(selectedId, OpenMode.ForRead); _allowedOffset = _polyline.Length / 50.0; _polyline.Highlight(); try { if (SelectTraceStartPoint()) { int showLineWeight = Convert.ToInt32( CadApp.GetSystemVariable("LWDISPLAY")); CadApp.SetSystemVariable("LWDISPLAY", 1); try { _ed.PointMonitor += Editor_PointMonitor; PromptPointOptions opt = new PromptPointOptions( "\nClick on the polyline for path length:"); opt.AllowNone = true; opt.Keywords.Add("eXit"); opt.Keywords.Default = "eXit"; PromptPointResult res = _ed.GetPoint(opt); if (res.Status == PromptStatus.OK) { if (_drawable != null) { _ed.WriteMessage( "\nLength of picked path: {0}", _drawable.Length); } } } finally { _ed.PointMonitor -= Editor_PointMonitor; CadApp.SetSystemVariable( "LWDISPLAY", showLineWeight); } } } finally { _polyline.Unhighlight(); } tran.Commit(); } } #region private methods private ObjectId SelectPolyline() { PromptEntityOptions opt = new PromptEntityOptions( "\nSelect a polyline:"); opt.SetRejectMessage("\nInvalid selection: not a polyline."); opt.AddAllowedClass(typeof(CadDb.Polyline), true); PromptEntityResult res = _ed.GetEntity(opt); if (res.Status == PromptStatus.OK) return res.ObjectId; else return ObjectId.Null; } private bool SelectTraceStartPoint() { while (true) { PromptPointOptions opt = new PromptPointOptions( "\nTrace starts at -> " + "pick a point on the selected polyline, " + "or use polyline's start point:"); opt.AllowNone = true; opt.Keywords.Add("Start point"); opt.Keywords.Default = "Start point"; PromptPointResult res = _ed.GetPoint(opt); if (res.Status == PromptStatus.OK || res.Status == PromptStatus.Keyword) { if (res.Status == PromptStatus.Keyword) { _startPoint = _polyline.StartPoint; return true; } else { Point3d pt = _ed.Snap("NEA", res.Value); if (IsPointOnPolyline(pt, _polyline)) { _startPoint = pt; return true; } } return true; } else { return false; } } } private bool IsPointOnPolyline(Point3d pt, CadDb.Polyline poly) { Point3d closest = poly.GetClosestPointTo(pt, false); return IsTheSamePoint(closest, pt); } private bool IsTheSamePoint(Point3d pt1, Point3d pt2) { double dist = pt1.DistanceTo(pt2); return dist <= Tolerance.Global.EqualPoint; } #endregion #region private methods: draw transientgraphics along the polyline private void Editor_PointMonitor( object sender, PointMonitorEventArgs e) { ClearTransientGraphics(); //Current mouse cursor point Point3d pt = e.Context.RawPoint; //Closest point on the polyline Point3d closest = _polyline.GetClosestPointTo(pt, false); //If the mouse cursor is to far off the polyline if (pt.DistanceTo(closest) > _allowedOffset) return; //Draw TransientGraphics DrawTransientGraphics(closest); double l = _drawable.Length; string tip = string.Format("Traced length: {0}", l); e.AppendToolTipText(tip); } private void ClearTransientGraphics() { if (_drawable!=null) { _tsManager.EraseTransient(_drawable, new IntegerCollection()); _drawable.Dispose(); _drawable = null; } } private void DrawTransientGraphics(Point3d endPoint) { List<Point2d> vertices = CollectVerticesFromPolyline(endPoint); _drawable = CreatePolylineFromPoints(vertices); _tsManager.AddTransient( _drawable, TransientDrawingMode.DirectTopmost, 128, new IntegerCollection()); } private List<Point2d> CollectVerticesFromPolyline(Point3d endPoint) { List<Point2d> lst = new List<Point2d>(); double startDistance = _polyline.GetDistAtPoint(_startPoint); double endDistance = _polyline.GetDistAtPoint(endPoint); lst.Add(new Point2d(_startPoint.X, _startPoint.Y)); bool stop = false; for (int i = 1; i < _polyline.NumberOfVertices; i++) { Point3d vertex = _polyline.GetPoint3dAt(i); double dist = _polyline.GetDistAtPoint(vertex); if (dist>startDistance) { if (dist<endDistance) { lst.Add(new Point2d(vertex.X, vertex.Y)); } else { lst.Add(new Point2d(endPoint.X, endPoint.Y)); stop = true; } } if (stop) break; } return lst; } private CadDb.Polyline CreatePolylineFromPoints(List<Point2d> points) { CadDb.Polyline poly = new CadDb.Polyline(points.Count); int i = 0; foreach (var p in points) { poly.AddVertexAt(i, p, 0.0, 0.0, 0.0); i++; } poly.ColorIndex = _colorIndex; poly.LineWeight = _lineWeight; return poly; } #endregion } }
Here is the command method to run it:
using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Runtime; using CadApp = Autodesk.AutoCAD.ApplicationServices.Application; [assembly: CommandClass(typeof(TraceAlongPolyline.MyCadCommands))] namespace TraceAlongPolyline { public class MyCadCommands { [CommandMethod("TracePoly")] public static void RunMyCadCommand() { Document dwg = CadApp.DocumentManager.MdiActiveDocument; Editor ed = dwg.Editor; try { using (PolylineTracer tracer=new PolylineTracer(dwg)) { tracer.Trace(); } } catch (System.Exception ex) { ed.WriteMessage("\nError: {0}\n{1}", ex.Message, ex.StackTrace); } finally { Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt(); } } } }
This video clip shows how the code works.