← Inbox

C3D-0311 · 260610 Tara Blvd: centre each category tag in the box it is already in done

Each tag only slides to its OWN boxs centre - no reassignment, nothing created or erased. The apparent large off-centre offsets were an attachment artifact: every tag was BottomLeft so its insertion point sat at the text corner. Sets MiddleCenter attachment, box-centre location, height 0.33, rotation along the boxs long axis, no forced wrap. Tags with no containing box keep their position and only get the height set. Fixture name labels are untouched.

Script file
C3D-0311.cs
Target
260610 - 6736 Tara Blvd R2.dwg · model space
Author
claude
Approved
yes · auto-auth
Timeout
60s

C# payload

// Centre each category tag in the box it is ALREADY in. Nothing is created, erased, or moved to
// a different box - each tag only slides to its own box's centre.
//
// Why the last read looked so far "off centre": every tag is BottomLeft-attached, so its
// insertion point is the corner of the text, not its middle. On a 16.5 ft tall shelf a
// bottom-anchored vertical label legitimately reads dy = -7.98. Switching to MiddleCenter and
// placing the insertion point at the box centre is the actual centring operation.
//
// RULES:
//   - only tags whose insertion point is INSIDE a minimal fixture rectangle are touched
//   - tags with no box are left exactly as the user placed them
//   - when two tags share one box they are offset slightly along the long axis so neither is
//     hidden, still inside the box
//   - height forced to 0.33; rotation follows the box's long axis
//   - fixture NAME labels (chips, candy, drinks, wine, snacks, ...) are left alone; only the
//     category tags are re-centred
const double H = 0.33;
string[] CATS = { "Fresh Fruits", "Fresh Uncooked Meats", "Dairy", "Canned Foods", "CANNED FOODS",
                  "Frozen Foods", "Dry groceries", "Non-Alcoholic", "dairy", "meats" };

var ms = (BlockTableRecord)Tx.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(Db), OpenMode.ForWrite);

// ---- minimal fixture rectangles
var hseg = new List<double[]>(); var vseg = new List<double[]>();
foreach (ObjectId id in ms) {
    var ln = Tx.GetObject(id, OpenMode.ForRead) as Line;
    if (ln == null || !ln.Layer.Equals("FIXTURE", StringComparison.OrdinalIgnoreCase)) continue;
    double x1=Math.Round(ln.StartPoint.X,3), y1=Math.Round(ln.StartPoint.Y,3);
    double x2=Math.Round(ln.EndPoint.X,3),   y2=Math.Round(ln.EndPoint.Y,3);
    if (Math.Abs(y1-y2)<0.01) hseg.Add(new double[]{y1,Math.Min(x1,x2),Math.Max(x1,x2)});
    else if (Math.Abs(x1-x2)<0.01) vseg.Add(new double[]{x1,Math.Min(y1,y2),Math.Max(y1,y2)});
}
Func<double,double,double,bool> hasH = (y,xa,xb) => { foreach (var s in hseg) if (Math.Abs(s[0]-y)<0.02 && s[1]<=xa+0.02 && s[2]>=xb-0.02) return true; return false; };
Func<double,double,double,bool> hasV = (x,ya,yb) => { foreach (var s in vseg) if (Math.Abs(s[0]-x)<0.02 && s[1]<=ya+0.02 && s[2]>=yb-0.02) return true; return false; };
var xs=new List<double>(); foreach(var s in vseg) if(!xs.Exists(v=>Math.Abs(v-s[0])<0.02)) xs.Add(s[0]);
var ys=new List<double>(); foreach(var s in hseg) if(!ys.Exists(v=>Math.Abs(v-s[0])<0.02)) ys.Add(s[0]);
xs.Sort(); ys.Sort();
var all=new List<double[]>();
for(int i=0;i<xs.Count;i++) for(int j=i+1;j<xs.Count;j++)
for(int k=0;k<ys.Count;k++) for(int l=k+1;l<ys.Count;l++){
    double x0=xs[i],x1b=xs[j],y0=ys[k],y1b=ys[l];
    if(x1b-x0<0.5||y1b-y0<0.5) continue;
    if(!hasH(y0,x0,x1b)||!hasH(y1b,x0,x1b)||!hasV(x0,y0,y1b)||!hasV(x1b,y0,y1b)) continue;
    all.Add(new double[]{x0,y0,x1b,y1b});
}
var boxes=new List<double[]>();
foreach(var b in all){
    bool bigger=false;
    foreach(var o in all){ if(o==b) continue;
        if(o[0]>=b[0]-0.01&&o[1]>=b[1]-0.01&&o[2]<=b[2]+0.01&&o[3]<=b[3]+0.01 &&
           ((o[2]-o[0])*(o[3]-o[1])) < ((b[2]-b[0])*(b[3]-b[1]))-0.01){ bigger=true; break; } }
    if(!bigger) boxes.Add(b);
}
Log("fixture boxes: " + boxes.Count);

// ---- which tags are in which box (by CURRENT insertion point only - no reassignment)
var perBox = new Dictionary<int, List<ObjectId>>();
var untouched = new List<ObjectId>();
foreach (ObjectId id in ms) {
    var mt = Tx.GetObject(id, OpenMode.ForRead) as MText;
    if (mt == null) continue;
    string c = mt.Contents.Replace("\n"," ").Replace(@"\P"," ").Trim();
    bool isCat = false;
    foreach (var k in CATS) if (c.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0) { isCat = true; break; }
    if (!isCat) continue;
    double px=mt.Location.X, py=mt.Location.Y;
    int hit=-1;
    for(int i=0;i<boxes.Count;i++){ var b=boxes[i];
        if(px>=b[0]-0.05&&px<=b[2]+0.05&&py>=b[1]-0.05&&py<=b[3]+0.05){ hit=i; break; } }
    if (hit < 0) { untouched.Add(id); continue; }
    if (!perBox.ContainsKey(hit)) perBox[hit] = new List<ObjectId>();
    perBox[hit].Add(id);
}

int centred = 0;
foreach (var kv in perBox) {
    var b = boxes[kv.Key];
    double x0=b[0], y0=b[1], x1=b[2], y1=b[3];
    double bw=x1-x0, bh=y1-y0, cx=(x0+x1)/2, cy=(y0+y1)/2;
    bool vertical = bh > bw;
    int n = kv.Value.Count;
    for (int i = 0; i < n; i++) {
        var mt = (MText)Tx.GetObject(kv.Value[i], OpenMode.ForWrite);
        double ox = 0, oy = 0;
        if (n > 1) {   // share the box: nudge along the long axis, staying inside
            double span = (vertical ? bh : bw) * 0.5;
            double t = (i - (n-1)/2.0) / n;
            if (vertical) oy = span * t; else ox = span * t;
        }
        mt.TextHeight = H;
        mt.Attachment = AttachmentPoint.MiddleCenter;
        mt.Rotation = vertical ? Math.PI/2 : 0.0;
        mt.Width = 0;                       // no forced wrap - let it run its natural length
        mt.Location = new Point3d(cx + ox, cy + oy, 0);
        centred++;
        var back = (MText)Tx.GetObject(kv.Value[i], OpenMode.ForRead);
        Log(string.Format("  [{0}] box{1} {2:F2}x{3:F2} -> ({4:F3},{5:F3}) rot={6:F0} \"{7}\"",
            back.Handle, kv.Key, bw, bh, back.Location.X, back.Location.Y,
            back.Rotation*180/Math.PI, back.Contents.Replace("\n"," ")));
    }
}
foreach (var id in untouched) {
    var mt = (MText)Tx.GetObject(id, OpenMode.ForWrite);
    mt.TextHeight = H;                      // height only; position left as the user set it
    var back = (MText)Tx.GetObject(id, OpenMode.ForRead);
    Log(string.Format("  [{0}] no box - left at ({1:F2},{2:F2}), h={3:F2} \"{4}\"",
        back.Handle, back.Location.X, back.Location.Y, H, back.Contents.Replace("\n"," ")));
}

Log("");
Log(string.Format("centred {0} tags in their own boxes, {1} left in place (height set to {2:F2})",
    centred, untouched.Count, H));
Ed.Regen();

Result

Status
success
Drawing file
C:\Users\sanja\OneDrive\_SurveyDisco\260610 - 6736 Tara Blvd, Jonesboro, GA 30236, USA\260610 - 6736 Tara Blvd R2.dwg
Message
Executed C3D-0311 (0 entities touched).
Entities
Duration
407 ms
Civil 3D
25.0.0.0

Log

fixture boxes: 19
  [21BE7] box9 1.50x16.50 -> (2261079.677,1391833.943) rot=90 "Dry groceries and baked goods"
  [21BE8] box11 1.50x16.50 -> (2261081.177,1391833.943) rot=90 "Dry groceries and baked goods"
  [21BE9] box8 4.00x1.00 -> (2261080.427,1391842.693) rot=0 "Dry groceries and\P baked goods"
  [21BEC] box18 1.50x8.50 -> (2261089.177,1391837.943) rot=90 "Non-Alcoholic beverages"
  [21BED] box15 1.50x16.50 -> (2261087.677,1391833.943) rot=90 "Dry groceries and baked goods"
  [21BEF] box4 3.00x2.00 -> (2261072.427,1391824.693) rot=0 "Dry groceries and\Pbaked goods"
  [21BF0] box10 3.00x2.00 -> (2261080.427,1391824.693) rot=0 "Fresh Fruits &\P Vegetables"
  [21BF4] box7 2.50x3.00 -> (2261074.177,1391847.693) rot=90 "Frozen Foods"
  [21C06] box5 1.50x12.50 -> (2261073.177,1391831.943) rot=90 "CANNED FOODS"
  [21C99] box16 3.00x2.00 -> (2261088.427,1391824.693) rot=0 "Dry groceries and\Pbaked goods"
  [21CD6] box0 4.00x3.00 -> (2261067.427,1391816.693) rot=0 "dairy"
  [21CDE] box2 3.00x3.00 -> (2261070.927,1391816.693) rot=0 "meats"
  [21CED] box6 1.50x4.00 -> (2261073.177,1391840.193) rot=90 "Dry groceries \Pand baked goods"
  [21CFB] box14 4.00x1.00 -> (2261088.427,1391842.693) rot=0 "Dry groceries and\P baked goods"
  [21D30] box12 4.00x3.00 -> (2261085.427,1391847.693) rot=0 "Dry groceries and\P baked goods"
  [21D40] box13 4.00x2.00 -> (2261085.427,1391850.193) rot=0 "Non-Alcoholic beverages"
  [21BEA] no box - left at (2261070.43,1391842.19), h=0.33 "Dry groceries and\P baked goods"
  [21BF2] no box - left at (2261065.13,1391818.59), h=0.33 "Frozen Foods"
  [21BF6] no box - left at (2261097.73,1391845.04), h=0.33 "Non-Alcoholic beverages"
  [21CB9] no box - left at (2261063.73,1391838.39), h=0.33 "Frozen Foods"
  [21CC1] no box - left at (2261065.13,1391825.44), h=0.33 "dairy"

centred 16 tags in their own boxes, 5 left in place (height set to 0.33)

Notes

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

No notes yet.