← Inbox

C3D-0134 · IPF to centerline dimensions (test the recipe) [IPF-DIMS] done

Draws an AlignedDimension from every IPF point to the closest point on the nearest centerline - the perpendicular foot on a segment, clamped, taking the global minimum across all CL polylines. Skips pins that already carry a dimension so the two hand-drawn examples are untouched and a re-run adds nothing.

Script file
C3D-0134.cs
Target
active document · model space
Author
claude
Approved
yes · reviewer
Timeout
180s

C# payload

// IPF -> centerline AlignedDimensions, per recipe ipf-to-centerline-dims.
//
// For every IPF point: compute the perpendicular foot on EVERY segment of EVERY centerline
// polyline, clamped to the segment, and take the global minimum. Draw an AlignedDimension from
// the pin to that foot. Matches the user's own two dims (pt103 -> 26.291 exactly).
//
// AlignedDimension - NOT MText/Leader - so the size is managed from paper space.
// Idempotent: a pin that already has a dimension starting or ending within 0.5 ft of it is skipped,
// so the user's two hand-drawn dims are left alone and a re-run adds nothing.
var ms = (BlockTableRecord)Tx.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(Db), OpenMode.ForWrite);
Log("drawing: " + Db.Filename);

const string DIM_LAYER = "0";
const string DIM_STYLE = "Standard";

// centerlines
var cls = new List<Point2d[]>();
foreach (ObjectId id in ms) {
    var pl = Tx.GetObject(id, OpenMode.ForRead) as Polyline;
    if (pl == null) continue;
    string u = pl.Layer.ToUpper();
    if (u.IndexOf("CNTR") < 0 && u.IndexOf("-CL") < 0) continue;
    var v = new Point2d[pl.NumberOfVertices];
    for (int i = 0; i < pl.NumberOfVertices; i++) v[i] = pl.GetPoint2dAt(i);
    cls.Add(v);
    Log("centerline [" + pl.Handle + "] " + pl.Layer + " verts=" + v.Length);
}
if (cls.Count == 0) { Log("ABORT: no centerline polylines found"); return; }

// existing dimension endpoints, so hand-drawn ones are not duplicated
var taken = new List<Point3d>();
foreach (ObjectId id in ms) {
    var ad = Tx.GetObject(id, OpenMode.ForRead) as AlignedDimension;
    if (ad == null) continue;
    taken.Add(ad.XLine1Point); taken.Add(ad.XLine2Point);
}
Log("existing aligned dimensions endpoints: " + taken.Count);

int made = 0, skipped = 0;
foreach (ObjectId id in ms) {
    var cp = Tx.GetObject(id, OpenMode.ForRead) as CogoPoint;
    if (cp == null) continue;
    string d = "";
    try { d = (cp.RawDescription ?? "").ToUpper(); } catch { }
    if (d.IndexOf("IPF") < 0) continue;

    var P = new Point2d(cp.Easting, cp.Northing);

    bool already = false;
    foreach (var t in taken)
        if (Math.Sqrt(Math.Pow(t.X - P.X, 2) + Math.Pow(t.Y - P.Y, 2)) < 0.5) { already = true; break; }
    if (already) { skipped++; Log("  pt " + cp.PointNumber + " already dimensioned - skipped"); continue; }

    double best = 1e18; Point2d foot = P;
    foreach (var poly in cls) {
        for (int i = 0; i < poly.Length - 1; i++) {
            var A = poly[i]; var B = poly[i+1];
            double vx = B.X - A.X, vy = B.Y - A.Y, L2 = vx*vx + vy*vy;
            double t = L2 > 0 ? ((P.X - A.X)*vx + (P.Y - A.Y)*vy) / L2 : 0.0;
            if (t < 0) t = 0; if (t > 1) t = 1;
            var Q = new Point2d(A.X + t*vx, A.Y + t*vy);
            double dd = Math.Sqrt(Math.Pow(P.X - Q.X, 2) + Math.Pow(P.Y - Q.Y, 2));
            if (dd < best) { best = dd; foot = Q; }
        }
    }

    // dimension line offset to the side, at the midpoint, so the text clears the linework
    double mx = (P.X + foot.X) / 2.0, my = (P.Y + foot.Y) / 2.0;
    double dx = foot.X - P.X, dy = foot.Y - P.Y, len = Math.Sqrt(dx*dx + dy*dy);
    double nx = -dy/len, ny = dx/len;
    var dimLine = new Point3d(mx + nx*6.0, my + ny*6.0, 0.0);

    var ad2 = new AlignedDimension(
        new Point3d(P.X, P.Y, 0.0),
        new Point3d(foot.X, foot.Y, 0.0),
        dimLine, "", Db.Dimstyle);
    ad2.Layer = DIM_LAYER;
    ms.AppendEntity(ad2);
    Tx.AddNewlyCreatedDBObject(ad2, true);
    made++;
    Log(String.Format("  pt {0} \"{1}\"  ->  foot {2:F3},{3:F3}   dist {4:F4}",
        cp.PointNumber, cp.RawDescription, foot.X, foot.Y, best));
}

Log("");
Log("dimensions created: " + made + "   skipped (already had one): " + skipped);

int total = 0;
foreach (ObjectId id in ms) {
    var ad = Tx.GetObject(id, OpenMode.ForRead) as AlignedDimension;
    if (ad != null && !ad.IsErased) total++;
}
Log("aligned dimensions in model space: " + total + " (verified)");
Ed.Regen();

Result

Status
success
Drawing file
C:\Users\sanja\OneDrive\_SurveyDisco\260802 - 4082 Sweetbriar Ln, Forest Park, GA 30297, USA\260802 - 4082 Sweetbriar Ln.dwg
Message
Executed C3D-0134 (0 entities touched).
Entities
Duration
456 ms
Civil 3D
25.0.0.0

Log

drawing: C:\Users\sanja\OneDrive\_SurveyDisco\260802 - 4082 Sweetbriar Ln, Forest Park, GA 30297, USA\260802 - 4082 Sweetbriar Ln.dwg
centerline [20AD7] SS-V-ROAD-CNTR verts=3
centerline [20AD8] SS-V-ROAD-CNTR verts=8
existing aligned dimensions endpoints: 4
  pt 103 already dimensioned - skipped
  pt 102 "IPF 1\2 RB"  ->  foot 2238168.389,1325137.542   dist 24.4407
  pt 101 "IPF 5\8 RB DISTRB"  ->  foot 2238369.251,1325137.012   dist 92.8418
  pt 100 already dimensioned - skipped

dimensions created: 2   skipped (already had one): 2
aligned dimensions in model space: 4 (verified)

Notes

What worked, what didn't, job-specific gotchas — flagged notes feed the recipes.

No notes yet.