000

Index Labels

Splitting Polyline/Curve With AutoCAD Civil 3D Labels Associated - Updated

.
The code shown in my previous post, as I mentioned there, only re-creates single Civil3D segment label when a polyline/curve is split. However, each segment of a split-able curve/polyline could be annotated by multiple labels (in different label styles, of course). Also, the code shown in that post does not re-store the labels in their original locations. I was able to find a few hours sitting down to update the code to cover these issues.

The solution is to search drawing to find all labels that associate to the polyline/curve to be split, and save these labels' information for later need (when re-creating labels). So, I defined this class SegmentLabelInfo:

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

namespace Civil3DLabels
{
    public enum SegmentLabelType
    {
        LineLabel=0,
        CurveLabel=1,
    }

    public class SegmentLabelInfo
    {
        public ObjectId LabelStyleId { setget; }
        public string LabelLayer { setget; }
        public Point3d LabelPosition { setget; }
        public SegmentLabelType LabelType { setget; }

        public SegmentLabelInfo()
        {
            LabelStyleId = ObjectId.Null;
            LabelLayer = "";
            LabelPosition = Point3d.Origin;
            LabelType = SegmentLabelType.LineLabel;
        }

        public bool IsOnCurve(ObjectId curveId, out double ratio)
        {
            ratio = 0.5;
            bool onCurve = false;

            Point3d pt;
            using (var tran = 
                curveId.Database.TransactionManager.StartTransaction())
            {
                var curve = (Curve)tran.GetObject(
                    curveId, OpenMode.ForRead);
                pt = curve.GetClosestPointTo(LabelPosition, false);

                //If the label anchored on this curve
                var dist = LabelPosition.DistanceTo(pt);
                onCurve = dist <= Tolerance.Global.EqualPoint;

                //the distance from curve's start point to anchor point
                dist = curve.GetDistAtPoint(pt);
                ratio = dist / curve.GetDistAtPoint(curve.EndPoint);

                tran.Commit();
            }

            return onCurve;
        }
    }
}

As the code shows, besides the 4 public properties used to store a label's information, a method IsOnCurve() is also defined to determine if the label sits on one of the segment split from the original polyline/curve; if yes, what location (distance to the start point) the label is at (output parameter ratio).

Now, here is the updated working code, where I changed the class name from PolylineSplitter to MyPolylineSplitter:

using System.Collections.Generic;
using System.Linq;
using CadApp = Autodesk.AutoCAD.ApplicationServices.Application;
using CadDb = Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using CivilApp = Autodesk.Civil.ApplicationServices.CivilApplication;
using Civil = Autodesk.Civil;
using CivilDb = Autodesk.Civil.DatabaseServices;
using Autodesk.Civil.DatabaseServices.Styles;

[assemblyCommandClass(typeof(Civil3DLabels.MyPolylineSplitter))]

namespace Civil3DLabels
{
    public class MyPolylineSplitter
    {
        private const string DXF_LABEL = "AECC_GENERAL_SEGMENT_LABEL";
 
        [CommandMethod("ExplodePoly")]
        public void DoSplit()
        {
            var dwg = CadApp.DocumentManager.MdiActiveDocument;
            var ed = dwg.Editor;
            try
            {
                Split(dwg);
            }
            catch (System.Exception ex)
            {
                ed.WriteMessage("\nError: {0}", ex.Message);
                ed.WriteMessage("\n*Cancel*");
            }

            Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
        }
        public void Split(Document dwg)
        {
            CadDb.ObjectId polyId = SelectPolyline(dwg.Editor);
            if (polyId.IsNull) return;

            //Collect all labels' information that associate with the polyline
            IEnumerable<SegmentLabelInfo> labels = FindAssociatedLabels(polyId);
            
            //Get default line/curve label style IDs
            var defaultLineLabelStyleId = CadDb.ObjectId.Null;
            var defaultCurveLabelStyleId = CadDb.ObjectId.Null;
            if (labels.Count() > 0)
            {
                GetSettingsCmdAddSegmentLabelDefaultStyles(
                    out defaultLineLabelStyleId, out defaultCurveLabelStyleId);
            }

            using (var tran = dwg.TransactionManager.StartTransaction())
            {
                var poly = (CadDb.Polyline)tran.GetObject(
                    polyId, CadDb.OpenMode.ForRead);

                //Find current space
                var curSpace = (CadDb.BlockTableRecord)tran.GetObject(
                    poly.OwnerId, CadDb.OpenMode.ForRead);

                //Newly created entities because of the splitting
                var newIds = new List<CadDb.ObjectId>();

                //Get split segments and add them into current space
                using (var ents = GetSplitCurvesFromPolyline(poly))
                {
                    if (ents.Count > 1)
                    {   
                        curSpace.UpgradeOpen();
                        foreach (CadDb.DBObject ent in ents)
                        {
                            var id = curSpace.AppendEntity(ent as CadDb.Entity);
                            tran.AddNewlyCreatedDBObject(ent, true);

                            newIds.Add(id);
                        }

                        // Erase the polyline,
                        // Associated labels would also be gone
                        poly.UpgradeOpen();
                        poly.Erase(true);
                    }
                    else
                    {
                        ents[0].Dispose();
                    }
                }

                //Add label to each newly added entities
                if (newIds.Count>0 && labels.Count()>0)
                {      
                    foreach (var entId in newIds)
                    {
                        AddSegmentLabel(
                            entId, 
                            labels, 
                            defaultLineLabelStyleId, 
                            defaultCurveLabelStyleId,
                            tran);
                    }
                }

                tran.Commit();
            }         
        }

        #region private methods

        private CadDb.ObjectId SelectPolyline(Editor ed)
        {
            var opt = new PromptEntityOptions(
                "\nSelect a polyline:");
            opt.SetRejectMessage("\nInvalid: not a polyline.");
            opt.AddAllowedClass(typeof(CadDb.Polyline), true);

            var res = ed.GetEntity(opt);
            if (res.Status==PromptStatus.OK)
            {
                return res.ObjectId;
            }
            else
            {
                return CadDb.ObjectId.Null;
            }
        }

        private CadDb.DBObjectCollection GetSplitCurvesFromPolyline(
            CadDb.Polyline poly)
        {
            var points = new Point3dCollection();
            for (int i = 0; i < poly.NumberOfVertices; i++)
            {
                points.Add(poly.GetPoint3dAt(i));
            }

            var dbObjects = poly.GetSplitCurves(points);

            return dbObjects;
        }

        private IEnumerable<SegmentLabelInfo> FindAssociatedLabels(
            CadDb.ObjectId polyId)
        {
            var labels = new List<SegmentLabelInfo>();

            CadDb.Database db = polyId.Database;

            using (var tran = db.TransactionManager.StartTransaction())
            {
                var ent = (CadDb.Entity)tran.GetObject(
                    polyId, CadDb.OpenMode.ForRead);
                var space = (CadDb.BlockTableRecord)tran.GetObject(
                    ent.OwnerId, CadDb.OpenMode.ForRead);

                foreach (CadDb.ObjectId id in space)
                {
                    if (id.ObjectClass.DxfName.ToUpper() == DXF_LABEL)
                    {
                        var label = tran.GetObject(
                            id, CadDb.OpenMode.ForRead) as CivilDb.Label;
                        if (label != null && label.FeatureId==polyId)
                        {
                            var info = new SegmentLabelInfo()
                            {
                                LabelStyleId = label.StyleId,
                                LabelLayer = label.Layer,
                                LabelPosition = label.AnchorInfo.Location
                            };

                            // Since each label uses either line label style or curve label style
                            // We test if the label style is line label style.
                            // if not, the label style must be curve label style
                            var lineStyles =
                                CivilApp.ActiveDocument.Styles.LabelStyles.GeneralLineLabelStyles;
                            info.LabelType = IsSegmentLabelStyle(label.StyleId, lineStyles, tran) ?
                                SegmentLabelType.LineLabel : SegmentLabelType.CurveLabel;

                            labels.Add(info);
                        }
                    }
                }

                tran.Commit();
            }

            return labels;
        }

        private void AddSegmentLabel(
            CadDb.ObjectId entId, 
            IEnumerable<SegmentLabelInfo> labels,
            CadDb.ObjectId defaultLineLabelStyleId, 
            CadDb.ObjectId defaultCurveLabelStyleId, 
            CadDb.Transaction tran )
        {
            foreach (var labelInfo in labels)
            {
                double ratio;
                if (labelInfo.IsOnCurve(entId, out ratio))
                {
                    var lineLabel = labelInfo.LabelType == SegmentLabelType.LineLabel ?
                        labelInfo.LabelStyleId : defaultLineLabelStyleId;
                    var curveLabel = labelInfo.LabelType == SegmentLabelType.CurveLabel ?
                        labelInfo.LabelStyleId : defaultCurveLabelStyleId;

                    //Add label to the segment
                    var id = CivilDb.GeneralSegmentLabel.Create(
                        entId, ratio, lineLabel, curveLabel);
                    var label = (CivilDb.Label)tran.GetObject(
                        id, CadDb.OpenMode.ForRead);
                    if (label.Layer.ToUpper()!=labelInfo.LabelLayer.ToUpper())
                    {
                        label.UpgradeOpen();
                        label.Layer = labelInfo.LabelLayer;
                    }
                }
            }
        }

        private bool IsSegmentLabelStyle(
            CadDb.ObjectId styleId, 
            IEnumerable<CadDb.ObjectId> styleCollection, 
            CadDb.Transaction tran)
        {
            bool found = false;

            foreach (CadDb.ObjectId id in styleCollection)
            {
                if (styleId == id)
                {
                    found = true;
                }
                else
                {
                    var style = (LabelStyle)tran.GetObject(
                        id, CadDb.OpenMode.ForRead);
                    if (style.ChildrenCount > 0)
                    {
                        var styleIds = new List<CadDb.ObjectId>();
                        for (int i = 0; i < style.ChildrenCount; i++)
                        {
                            styleIds.Add(style[i]);
                        }

                        //Recursive call
                        found =
                            IsSegmentLabelStyle(styleId, styleIds, tran);
                    }
                }

                if (found) break;
            }

            return found;
        }

        private void GetSettingsCmdAddSegmentLabelDefaultStyles(
            out CadDb.ObjectId lineLabelStyleId,
            out CadDb.ObjectId curveLabelStyleId)
        {
            var settings = CivilApp.ActiveDocument.Settings;
            var cmdSettings = 
                settings.GetSettings<Civil.Settings.SettingsCmdAddSegmentLabel>();

            lineLabelStyleId = cmdSettings.Styles.LineLabelStyleId.Value;
            curveLabelStyleId = cmdSettings.Styles.CurveLabelStyleId.Value;
        }
        
        #endregion
    }
}

The polyline to be split in this video clip is annotated with 2 labels for each of its segment; some of the labels are on different layers (in different colors); Also, the labels' anchor point (location) on the segment have been dragged randomly along the segment. After execute command "ExplodePoly", all labels look like unchanged, even though they are all re-created.

Blog Archive

Labels

3D Modeling 3D Sketch Inventor AI Design AI in Manufacturing AI Tools Architecture Artificial Intelligence AutoCAD AutoCAD advice AutoCAD Basics AutoCAD Beginners AutoCAD Civil3D AutoCAD commands AutoCAD efficiency AutoCAD features AutoCAD File Management AutoCAD Layer AutoCAD learning AutoCAD print settings AutoCAD productivity AutoCAD Teaching AutoCAD Techniques AutoCAD tips AutoCAD training. AutoCAD tricks AutoCAD Tutorial AutoCAD workflow AutoCAD Xref Autodesk Autodesk 2025 Autodesk AI Tools Autodesk AutoCAD Autodesk Fusion 360 Autodesk Inventor Autodesk Inventor Frame Generator Autodesk Inventor iLogic Autodesk Recap Autodesk Revit Autodesk Software Autodesk Video Automation Automation Tutorial Basic Commands Basics Beginner Beginner Tips BIM BIM Implementation Block Editor ByLayer CAD comparison CAD Design CAD File Size Reduction CAD line thickness CAD Optimization CAD Productivity CAD software clean CAD file cleaning command Cloud Collaboration command abbreviations Construction Technology Contraints Create resizable blocks CTB STB Data Reference Data Shortcut design software Design Workflow Digital Design Digital Twin Drafting Standards Drawing Automation Dref Dynamic Block Dynamic Block AutoCAD Dynamic Blocks Dynamic doors Dynamic windows eco design editing commands energy efficiency Engineering Engineering Design Engineering Innovation Engineering Technology engineering tools Excel Express Tools External Reference Fast Structural Design Fusion 360 Generative Design green building Grips heavy CAD file Heavy CAD Files iLogic Industry 4.0 Insight Inventor API Inventor Drawing Template Inventor Frame Generator Inventor Graphics Issues Inventor IDW Inventor Tips Keyboard Shortcuts Learn AutoCAD Machine Learning in CAD maintenance command Management Manufacturing Innovation Metal Structure ObjectARX .NET API Organization OVERKILL OVERKILL AutoCAD Palette PDF Plot Style AutoCAD Practice Drawing Printing Quality professional printing Professional Tips PTC Creo PURGE PURGE AutoCAD ReCap reduce CAD file size Resizable Block Revit Revit Best Practices Revit Workflow Ribbon screen shortcut keys Shortcuts Siemens NX Sketch Small Firms Smart Block Smart Factory SolidWorks Steel Structure Design sustainability Sustainable Manufacturing toolbar Tutorial User Interface (UI) Workbook Workspace XLS Xref