# fc2dxf.py: export a flat FreeCAD body (sheet part) to a LINE-only DXF for dxf2rd.py. # Takes the largest planar face normal to the body's thinnest axis, writes its outline and holes # with inner loops first (so the part doesn't drop before its holes are cut), origin at the # bottom-left corner, mm. Curves are flattened to lines within 0.02 mm. Opens the .FCStd # read-only and never saves it. # # usage (FreeCAD's headless interpreter): # freecadcmd -c "import sys; sys.argv=['x','MODEL.FCStd','BodyLabel','OUT.dxf']; exec(open('fc2dxf.py').read())" # Prints the part thickness: check it matches your stock. A blind pocket (not through) can't be # cut, only engraved, so the script refuses bodies with planar faces between the two sides. import FreeCAD, sys MODEL, NAME, OUT = sys.argv[-3], sys.argv[-2], sys.argv[-1] d = FreeCAD.openDocument(MODEL) objs = d.getObjectsByLabel(NAME) assert objs, f"no object labelled {NAME!r}" s = objs[0].Shape; b = s.BoundBox dims = [b.XLength, b.YLength, b.ZLength]; t = dims.index(min(dims)) ax = [i for i in range(3) if i != t] mn = [b.XMin, b.YMin, b.ZMin] flat = [f for f in s.Faces if f.Surface.__class__.__name__ == 'Plane' and abs(abs(f.normalAt(0, 0)[t]) - 1) < 1e-6] levels = sorted({round(f.CenterOfMass[t], 3) for f in flat}) assert len(levels) == 2, f"faces at {levels}: blind pocket or step, not a through-cut sheet part" f = max(flat, key=lambda f: f.Area) loops = [] for w in f.Wires: segs = [] for e in w.OrderedEdges: pts = ([e.Vertexes[0].Point, e.Vertexes[-1].Point] if e.Curve.__class__.__name__ == 'Line' else e.discretize(Deflection=0.02)) pts = [(p[ax[0]] - mn[ax[0]], p[ax[1]] - mn[ax[1]]) for p in pts] segs += list(zip(pts, pts[1:])) xs = [p[0] for sg in segs for p in sg]; ys = [p[1] for sg in segs for p in sg] loops.append(((max(xs) - min(xs)) * (max(ys) - min(ys)), segs)) loops.sort(key=lambda l: l[0]) # inner cuts first, outline last out = ["0", "SECTION", "2", "HEADER", "9", "$INSUNITS", "70", "4", "0", "ENDSEC", "0", "SECTION", "2", "ENTITIES"] n = 0 for _, segs in loops: for (x1, y1), (x2, y2) in segs: if abs(x1 - x2) + abs(y1 - y2) < 1e-6: continue out += ["0", "LINE", "8", "CUT", "10", "%.4f" % x1, "20", "%.4f" % y1, "30", "0", "11", "%.4f" % x2, "21", "%.4f" % y2, "31", "0"]; n += 1 out += ["0", "ENDSEC", "0", "EOF"] open(OUT, 'w').write("\n".join(out) + "\n") print(f"{NAME} -> {OUT}: {n} lines, {len(loops)} loops, {dims[ax[0]]:.2f} x {dims[ax[1]]:.2f} mm, " f"thickness {dims[t]:.2f} mm") FreeCAD.closeDocument(d.Name)