By Augusto Goncalves
I’m always amazed how AutoCAD Geometry namespace can be used to perform some vector based math very easy. On this post, the goal is get all contours lines from a Tin Surface and create label group object on each of them.
The main point is the SurfaceContourLabelGroup.Create method requires a list of points. Why? Because you can pass a sequence of points where Civil 3D will draw a line (or polyline) and every time it crosses (intersect) a contour, will add a label.
Just like in the UI, we can add this SurfaceContourLabelGroup object for each contour line but, just like in the UI, we need a small line that cross the contour line. Here is where the math enters: get a point at the middle of a contour line, get the first derivative vector (that points on the direction of the contour line), rotate by +90 and by -90 degrees, finally convert to a unit vector (to make it small enough) and add on both direction to the original point on the contour line. And with that we have the 2 points we need.
[CommandMethod("testAddLabelContour")]
public static void CmdAddLabelAtContour()
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
// select a tin surface
PromptEntityOptions peo = new PromptEntityOptions(
"\nSelect a tin surface: ");
peo.SetRejectMessage("\nOnly surface");
peo.AddAllowedClass(typeof(TinSurface), true);
PromptEntityResult per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK) return;
AddLabelAtContours(per.ObjectId);
}
private static void AddLabelAtContours(ObjectId surfaceId)
{
Database db = Application.DocumentManager.MdiActiveDocument.Database;
CivilDocument civilDoc = CivilApplication.ActiveDocument;
using (Transaction trans = db.TransactionManager.StartTransaction())
{
TinSurface surface = trans.GetObject(surfaceId,
OpenMode.ForRead) as TinSurface;
// finally call our custom method to add labels
// on each polyline extracted from the surface
AddContoursLabels(surfaceId,
surface.ExtractMajorContours(SurfaceExtractionSettingsType.Plan));
AddContoursLabels(surfaceId,
surface.ExtractMinorContours(SurfaceExtractionSettingsType.Plan));
trans.Commit();
}
}
private static void AddContoursLabels(ObjectId surfaceId,
ObjectIdCollection contourIds)
{
for (int i = 0; i < contourIds.Count; i++)
{
ObjectId contourId = contourIds[i];
// Contours are lightweight Polyline objects:
Polyline contour = contourId.GetObject(OpenMode.ForWrite) as Polyline;
// get a point at the middle of the pline
Point3d pointOnContour = contour.GetPointAtParameter(0.5);
// get the first derivative on this point
Vector3d firstDerivative = contour.GetFirstDerivative(pointOnContour);
// now do some math to get one point on each side, not far
// than 0.5 units...
Point3d leftPoint = pointOnContour
.Add(firstDerivative.RotateBy(Math.PI / 2, Vector3d.ZAxis) // rotate 90o
.DivideBy(firstDerivative.Length) // make unit vector
.MultiplyBy(0.5)); // size 0.5
Point3d rightPoint = pointOnContour
.Add(firstDerivative.RotateBy(-Math.PI / 2, Vector3d.ZAxis) // rotate -90o
.DivideBy(firstDerivative.Length) // make unit vector
.MultiplyBy(0.5)); // size 0.5
// create a list of points
Point2dCollection points = new Point2dCollection();
points.Add(leftPoint.Convert2d(new Plane()));
points.Add(rightPoint.Convert2d(new Plane()));
// and create the label group
SurfaceContourLabelGroup.Create(surfaceId, points);
// and erase the pline (don't need anymore...)
contour.Erase();
}
}
As a result, a label is added at the middle (parameter 0.5) of each contour line (major and minor).