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.