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 { set; get; } public string LabelLayer { set; get; } public Point3d LabelPosition { set; get; } public SegmentLabelType LabelType { set; get; } 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; [assembly: CommandClass(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.
IMPRESSED WITH SUCH A GOOD CONTENT!!
ReplyDeleteVERY INTERESTING
GREAT WORK
CAD to BIM conversion India