import FreeCAD as App import FreeCADGui as Gui import Part # ============================================================ # CONFIG # ============================================================ TARGET_X = 75 TARGET_Y = 75 TARGET_Z = 50 LAYER_THICKNESS = 2.5 GAP = 2.0 # requested PADDING = 2.0 # inside cell margin # ============================================================ # LOG # ============================================================ def log(msg): App.Console.PrintMessage(str(msg) + "\n") def warn(msg): App.Console.PrintWarning("WARNING: " + str(msg) + "\n") # ============================================================ # SELECTION # ============================================================ sel = Gui.Selection.getSelection() if not sel: raise Exception("Select a solid") shape = sel[0].Shape.copy() bb = shape.BoundBox scale = min( TARGET_X / bb.XLength, TARGET_Y / bb.YLength, TARGET_Z / bb.ZLength ) m = App.Matrix() m.scale(scale, scale, scale) shape = shape.transformGeometry(m) bb = shape.BoundBox log("Scaled OK") # ============================================================ # SECTION # ============================================================ def make_slab(z): return Part.makeBox( bb.XLength * 3, bb.YLength * 3, LAYER_THICKNESS, App.Vector(bb.XMin - bb.XLength, bb.YMin - bb.YLength, z) ) # ============================================================ # SAFE WIRE EXTRACTION # ============================================================ def extract_wires(section): if hasattr(section, "Wires") and section.Wires: return section.Wires return [] # ============================================================ # LABEL INSIDE BOX # ============================================================ def make_label(text, pos): a = App.ActiveDocument.addObject("App::Annotation", f"LBL_{text}") a.LabelText = [text] a.Position = pos return a # ============================================================ # FRAME BOX # ============================================================ def make_frame(x, y, w, h): return Part.makeBox( w, h, 1, App.Vector(x, y, 0) ) # ============================================================ # OUTPUT DOC # ============================================================ doc = App.newDocument("SLICES_V13") z = bb.ZMin i = 0 cursor_x = 0 while z <= bb.ZMax: log(f"Slice {i} Z={z}") slab = make_slab(z) section = shape.common(slab) if section.isNull(): log(" empty") z += LAYER_THICKNESS i += 1 continue wires = extract_wires(section) if not wires: warn(f"no wires slice {i}") z += LAYER_THICKNESS i += 1 continue # ======================================================== # compute cell size from slice bbox # ======================================================== sb = section.BoundBox cell_w = sb.XLength + PADDING * 2 cell_h = sb.YLength + PADDING * 2 frame = make_frame(cursor_x, 0, cell_w, cell_h) frame_obj = doc.addObject("Part::Feature", f"Frame_{i:03d}") frame_obj.Shape = frame frame_obj.ViewObject.DisplayMode = "Wireframe" # ======================================================== # place wires inside frame # ======================================================== for wi, w in enumerate(wires): obj = doc.addObject("Part::Feature", f"Slice_{i:03d}_W{wi}") obj.Shape = w # center in frame with padding obj.Placement = App.Placement( App.Vector( cursor_x + PADDING, PADDING, 0 ), App.Rotation() ) # ======================================================== # LABEL INSIDE FRAME # ======================================================== make_label( f"{i:03d}", App.Vector(cursor_x + 1, 1, 0) ) # ======================================================== # advance cursor with GAP # ======================================================== cursor_x += cell_w + GAP z += LAYER_THICKNESS i += 1 doc.recompute() Gui.ActiveDocument.ActiveView.fitAll() log("DONE V13")