C3D-0355 · Deep drawing index: dump every annotation object (dims/text/labels/leaders) to JSON [C3D-INDEX] error
Read-only. Walks model space and every layout, writing each dimension, MText, DBText, label and leader to C:\Civil3DControl\index\<drawing>.annotations.json - handle, kind, space, layer, style, displayed text, and geometry. For dimensions it records the measurement, text position/rotation and the extension/dim-line points so 'is the number inside the arrows or along the line' is answerable without re-reading. Echoes all dimensions in the log.
C# payload
// DEEP DRAWING INDEX (read-only). Every annotation object in model space and every layout,
// written as JSON to the plugin box so Claude can grep it instead of guessing.
//
// Per object: handle, kind, owning space, text, layer, style, and the geometry that answers
// "is the number inside the arrows or along the line" - for dimensions that means the dim-line
// endpoints, the text position, and the measurement.
var civ = Autodesk.Civil.ApplicationServices.CivilApplication.ActiveDocument;
Log("drawing: " + Db.Filename);
string J(string s) {
if (s == null) return "";
var sb = new System.Text.StringBuilder();
foreach (char c in s) {
if (c == '"' || c == '\') sb.Append('\').Append(c);
else if (c == '\n') sb.Append("\n");
else if (c == '\r') { }
else if (c == '\t') sb.Append(' ');
else sb.Append(c);
}
return sb.ToString();
}
var rows = new List<string>();
int nDim=0, nText=0, nLabel=0, nLeader=0, nOther=0;
System.Action<ObjectId,string> handle = (id, space) => {
var o = Tx.GetObject(id, OpenMode.ForRead);
var ent = o as Autodesk.AutoCAD.DatabaseServices.Entity;
if (ent == null) return;
string kind = o.GetType().Name;
string layer = ent.Layer;
var dim = o as Dimension;
var mt = o as MText;
var dt = o as DBText;
// classify - only annotation-ish things
bool isDim = dim != null;
bool isTxt = mt != null || dt != null;
bool isLbl = kind.IndexOf("Label", StringComparison.OrdinalIgnoreCase) >= 0;
bool isLdr = (o is Leader) || (o is MLeader);
if (!isDim && !isTxt && !isLbl && !isLdr) return;
string text = "";
if (dim != null) text = dim.DimensionText ?? ""; // "" means the measured value is shown
else if (mt != null) text = mt.Contents;
else if (dt != null) text = dt.TextString;
else {
// labels/leaders: explode to read what they display
try {
var col = new DBObjectCollection(); ent.Explode(col);
foreach (DBObject p in col) {
var m2 = p as MText; var d2 = p as DBText;
if (m2 != null) text += " " + m2.Contents;
else if (d2 != null) text += " " + d2.TextString;
}
text = text.Trim();
} catch { }
}
string style = "";
string geom = "";
if (dim != null) {
try {
var st = (DimStyleTableRecord)Tx.GetObject(dim.DimensionStyle, OpenMode.ForRead);
style = st.Name;
} catch { }
// measurement + placement
double meas = 0; try { meas = dim.Measurement; } catch { }
Point3d tp = dim.TextPosition;
// dim-line endpoints depend on the concrete type
string ends = "";
var ad = o as AlignedDimension;
var rd = o as RotatedDimension;
if (ad != null) ends = string.Format("\"xline1\":[{0:F3},{1:F3}],\"xline2\":[{2:F3},{3:F3}],\"dimline\":[{4:F3},{5:F3}]",
ad.XLine1Point.X, ad.XLine1Point.Y, ad.XLine2Point.X, ad.XLine2Point.Y, ad.DimLinePoint.X, ad.DimLinePoint.Y);
else if (rd != null) ends = string.Format("\"xline1\":[{0:F3},{1:F3}],\"xline2\":[{2:F3},{3:F3}],\"dimline\":[{4:F3},{5:F3}],\"rot\":{6:F4}",
rd.XLine1Point.X, rd.XLine1Point.Y, rd.XLine2Point.X, rd.XLine2Point.Y, rd.DimLinePoint.X, rd.DimLinePoint.Y, rd.Rotation);
geom = string.Format("\"measurement\":{0:F4},\"textpos\":[{1:F3},{2:F3}],\"textrot\":{3:F4},{4}",
meas, tp.X, tp.Y, dim.TextRotation, ends);
nDim++;
} else {
Point3d p = mt != null ? mt.Location : (dt != null ? dt.Position : Point3d.Origin);
double rot = mt != null ? mt.Rotation : (dt != null ? dt.Rotation : 0);
double h = mt != null ? mt.TextHeight : (dt != null ? dt.Height : 0);
geom = string.Format("\"pos\":[{0:F3},{1:F3}],\"rot\":{2:F4},\"height\":{3:F3}", p.X, p.Y, rot, h);
if (isTxt) nText++; else if (isLbl) nLabel++; else nLeader++;
}
string disp = text.Length > 120 ? text.Substring(0,120) : text;
rows.Add(string.Format(
"{{\"h\":\"{0}\",\"kind\":\"{1}\",\"space\":\"{2}\",\"layer\":\"{3}\",\"style\":\"{4}\",\"text\":\"{5}\",{6}}}",
ent.Handle, kind, space, J(layer), J(style), J(disp), geom));
};
// model space
var ms = (BlockTableRecord)Tx.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(Db), OpenMode.ForRead);
foreach (ObjectId id in ms) handle(id, "model");
// every layout
var dict = (DBDictionary)Tx.GetObject(Db.LayoutDictionaryId, OpenMode.ForRead);
foreach (DBDictionaryEntry de in dict) {
if (de.Key == "Model") continue;
var lay = (Layout)Tx.GetObject(de.Value, OpenMode.ForRead);
var btr = (BlockTableRecord)Tx.GetObject(lay.BlockTableRecordId, OpenMode.ForRead);
foreach (ObjectId id in btr) handle(id, lay.LayoutName);
}
// write it to the plugin box
string dir = "C:" + ((char)92) + "Civil3DControl" + ((char)92) + "index";
System.IO.Directory.CreateDirectory(dir);
string baseName = System.IO.Path.GetFileNameWithoutExtension(Db.Filename);
string outPath = System.IO.Path.Combine(dir, baseName + ".annotations.json");
var sb2 = new System.Text.StringBuilder();
sb2.Append("{\"drawing\":\"").Append(J(Db.Filename)).Append("\",\"count\":").Append(rows.Count).Append(",\"items\":[\r\n");
for (int i = 0; i < rows.Count; i++) { sb2.Append(rows[i]); if (i < rows.Count-1) sb2.Append(","); sb2.Append("\r\n"); }
sb2.Append("]}\r\n");
System.IO.File.WriteAllText(outPath, sb2.ToString(), new System.Text.UTF8Encoding(false));
Log("WROTE " + outPath + " (" + new System.IO.FileInfo(outPath).Length + " bytes)");
Log(string.Format("indexed: {0} dimensions, {1} text, {2} labels, {3} leaders = {4} total",
nDim, nText, nLabel, nLeader, rows.Count));
// echo the dimensions so we can see them here immediately
Log("");
Log("--- dimensions ---");
foreach (var r in rows) if (r.Contains("\"measurement\"")) Log(" " + r);
Result
Notes
What worked, what didn't, job-specific gotchas — flagged notes feed the recipes.
No notes yet.