C3D-CURBNORM · Normalize curb layer to Polylines done
Converts the mixed Line/Polyline3d content on SS-V-ROAD-CURB into joined 2D Polylines (chains Lines end-to-end, flattens Polyline3d). Skips <3ft noise and >500ft boundary lines. Prep for the 0.5/2.0ft curb offsets.
C# payload
// STEP 1 of curb work: normalize SS-V-ROAD-CURB to Polylines.
// The layer currently holds a mix of Line / Polyline / Polyline3d. Convert Lines and
// Polyline3d into joined 2D Polylines so the next step (0.5' and 2.0' offsets toward the
// road) has one consistent entity type to work with.
// Chains Lines end-to-end into runs; flattens Polyline3d to Polyline at its average Z.
// Skips runs shorter than 3ft (survey noise) and longer than 500ft (site boundary lines,
// not curb). Leaves existing Polylines alone.
// Host owns Tx/ModelSpace - no using/Commit here.
const string LAYER = "SS-V-ROAD-CURB";
const double MINLEN = 3.0, MAXLEN = 500.0, TOL = 0.02;
var lineIds = new List<ObjectId>();
var p3Ids = new List<ObjectId>();
foreach (ObjectId id in ModelSpace)
{
var e = Tx.GetObject(id, OpenMode.ForRead) as Autodesk.AutoCAD.DatabaseServices.Entity;
if (e == null || e.Layer != LAYER) continue;
if (e is Line) lineIds.Add(id);
else if (e is Polyline3d) p3Ids.Add(id);
}
Log($"Found {lineIds.Count} Lines, {p3Ids.Count} Polyline3d on '{LAYER}'.");
// ---- chain Lines into runs ----
var segs = new List<(Point2d a, Point2d b, ObjectId id, double len)>();
foreach (var id in lineIds)
{
var ln = (Line)Tx.GetObject(id, OpenMode.ForRead);
segs.Add((new Point2d(ln.StartPoint.X, ln.StartPoint.Y),
new Point2d(ln.EndPoint.X, ln.EndPoint.Y), id, ln.Length));
}
bool Near(Point2d p, Point2d q) => p.GetDistanceTo(q) < TOL;
var used = new bool[segs.Count];
var runs = new List<List<Point2d>>();
var consumed = new List<ObjectId>();
for (int i = 0; i < segs.Count; i++)
{
if (used[i]) continue;
used[i] = true;
var run = new List<Point2d> { segs[i].a, segs[i].b };
var mine = new List<ObjectId> { segs[i].id };
bool grew = true;
while (grew)
{
grew = false;
for (int j = 0; j < segs.Count; j++)
{
if (used[j]) continue;
if (Near(segs[j].a, run[run.Count-1])) { run.Add(segs[j].b); used[j]=true; mine.Add(segs[j].id); grew=true; }
else if (Near(segs[j].b, run[run.Count-1])) { run.Add(segs[j].a); used[j]=true; mine.Add(segs[j].id); grew=true; }
else if (Near(segs[j].a, run[0])) { run.Insert(0,segs[j].b); used[j]=true; mine.Add(segs[j].id); grew=true; }
else if (Near(segs[j].b, run[0])) { run.Insert(0,segs[j].a); used[j]=true; mine.Add(segs[j].id); grew=true; }
}
}
double L = 0;
for (int k = 1; k < run.Count; k++) L += run[k].GetDistanceTo(run[k-1]);
if (L < MINLEN || L > MAXLEN) { Log($" skip run {run.Count}v {L:F1}ft (outside {MINLEN}-{MAXLEN})"); continue; }
runs.Add(run);
consumed.AddRange(mine);
}
int made = 0;
foreach (var run in runs)
{
var pl = new Polyline();
for (int i = 0; i < run.Count; i++) pl.AddVertexAt(i, run[i], 0, 0, 0);
pl.Layer = LAYER;
ModelSpace.AppendEntity(pl);
Tx.AddNewlyCreatedDBObject(pl, true);
made++;
}
foreach (var id in consumed)
{
var w = (Autodesk.AutoCAD.DatabaseServices.Entity)Tx.GetObject(id, OpenMode.ForWrite);
w.Erase();
}
Log($"Lines -> {made} Polylines (consumed {consumed.Count} Lines).");
// ---- flatten Polyline3d to Polyline ----
int conv = 0;
foreach (var id in p3Ids)
{
var p3 = (Polyline3d)Tx.GetObject(id, OpenMode.ForRead);
var vs = new List<Point3d>();
foreach (ObjectId vid in p3)
{
var v = Tx.GetObject(vid, OpenMode.ForRead) as PolylineVertex3d;
if (v != null) vs.Add(v.Position);
}
if (vs.Count < 2) continue;
double L = 0;
for (int i = 1; i < vs.Count; i++) L += vs[i].DistanceTo(vs[i-1]);
if (L < MINLEN || L > MAXLEN) continue;
var pl = new Polyline();
for (int i = 0; i < vs.Count; i++) pl.AddVertexAt(i, new Point2d(vs[i].X, vs[i].Y), 0, 0, 0);
double z = 0; foreach (var v in vs) z += v.Z; pl.Elevation = z / vs.Count;
pl.Layer = LAYER;
ModelSpace.AppendEntity(pl);
Tx.AddNewlyCreatedDBObject(pl, true);
var w = (Autodesk.AutoCAD.DatabaseServices.Entity)Tx.GetObject(id, OpenMode.ForWrite);
w.Erase();
conv++;
}
Log($"Polyline3d -> {conv} Polylines.");
int total = 0;
foreach (ObjectId id in ModelSpace)
{
var e = Tx.GetObject(id, OpenMode.ForRead) as Autodesk.AutoCAD.DatabaseServices.Entity;
if (e != null && e.Layer == LAYER && e is Polyline) total++;
}
Log($"'{LAYER}' now has {total} Polylines.");
Result
Log
Found 126 Lines, 25 Polyline3d on 'SS-V-ROAD-CURB'. skip run 2v 770.8ft (outside 3-500) skip run 2v 773.4ft (outside 3-500) skip run 2v 1.6ft (outside 3-500) skip run 2v 1.0ft (outside 3-500) skip run 2v 1.0ft (outside 3-500) skip run 2v 0.7ft (outside 3-500) skip run 2v 0.0ft (outside 3-500) Lines -> 21 Polylines (consumed 119 Lines). Polyline3d -> 25 Polylines. 'SS-V-ROAD-CURB' now has 63 Polylines.
Notes
What worked, what didn't, job-specific gotchas — flagged notes feed the recipes.
No notes yet.