import json import os import FreeCAD as App import Part def make_feature_shape(type_id, properties): document = App.newDocument("GoldenPrimitive") try: feature = document.addObject(type_id, "Primitive") for name, value in properties.items(): setattr(feature, name, value) document.recompute() return feature.Shape.copy() finally: App.closeDocument(document.Name) def make_shape(operation): kind = operation["type"] if kind == "box": shape = Part.makeBox(operation["length"], operation["width"], operation["height"]) elif kind == "cylinder": shape = Part.makeCylinder(operation["radius"], operation["height"], App.Vector(0, 0, 0), App.Vector(0, 0, 1), operation["angle"]) elif kind == "sphere": shape = Part.makeSphere(operation["radius"]) elif kind == "cone": shape = Part.makeCone(operation["radius1"], operation["radius2"], operation["height"], App.Vector(0, 0, 0), App.Vector(0, 0, 1), operation["angle"]) elif kind == "ellipsoid": shape = make_feature_shape("Part::Ellipsoid", { "Radius1": operation["radius1"], "Radius2": operation["radius2"], "Radius3": operation["radius3"], "Angle1": operation["angle1"], "Angle2": operation["angle2"], "Angle3": operation["angle3"], }) elif kind == "torus": shape = make_feature_shape("Part::Torus", { "Radius1": operation["radius1"], "Radius2": operation["radius2"], "Angle1": operation["angle1"], "Angle2": operation["angle2"], "Angle3": operation["angle3"], }) elif kind == "prism": shape = make_feature_shape("Part::Prism", { "Polygon": operation["polygon"], "Circumradius": operation["circumradius"], "Height": operation["height"], "FirstAngle": operation["firstAngle"], "SecondAngle": operation["secondAngle"], }) elif kind == "wedge": shape = make_feature_shape("Part::Wedge", { "Xmin": operation["xmin"], "Ymin": operation["ymin"], "Zmin": operation["zmin"], "Z2min": operation["z2min"], "X2min": operation["x2min"], "Xmax": operation["xmax"], "Ymax": operation["ymax"], "Zmax": operation["zmax"], "Z2max": operation["z2max"], "X2max": operation["x2max"], }) elif kind == "helix": shape = make_feature_shape("Part::Helix", { "Pitch": operation["pitch"], "Height": operation["height"], "Radius": operation["radius"], "Angle": operation["angle"], "SegmentLength": operation["segmentLength"], }) elif kind in ("cut", "fuse", "common"): base = make_shape(operation["base"]) tool = make_shape(operation["tool"]) shape = base.cut(tool) if kind == "cut" else base.fuse(tool) if kind == "fuse" else base.common(tool) else: raise ValueError("Unsupported golden operation: " + kind) placement_value = operation.get("placement") if placement_value: placement = App.Placement() placement.Base = App.Vector(*(placement_value.get("translation") or [0, 0, 0])) rotation = placement_value.get("rotation") if rotation: placement.Rotation = App.Rotation(App.Vector(*rotation["axis"]), rotation["angle"]) shape = shape.copy() shape.Placement = placement return shape def vector(x, y, z): return [float(x), float(y), float(z)] scenario_path = os.environ.get("FREECAD_GOLDEN_SCENARIO") if not scenario_path: raise RuntimeError("FREECAD_GOLDEN_SCENARIO is required") with open(scenario_path, "r", encoding="utf-8") as scenario_file: scenario = json.load(scenario_file) shape = make_shape(scenario["operation"]) box = shape.BoundBox version = App.Version() result = { "schemaVersion": 1, "fixtureId": scenario["id"], "freecadVersion": ".".join(str(value) for value in version[:3]), "shapeType": shape.ShapeType, "isNull": shape.isNull(), "isValid": shape.isValid(), "solids": len(shape.Solids), "faces": len(shape.Faces), "edges": len(shape.Edges), "vertices": len(shape.Vertexes), "volume": float(shape.Volume), "area": float(shape.Area), "boundingBox": { "min": vector(box.XMin, box.YMin, box.ZMin), "max": vector(box.XMax, box.YMax, box.ZMax), }, } print("FREECAD_GOLDEN_RESULT=" + json.dumps(result, sort_keys=True, separators=(",", ":")))