import json import FreeCAD as App import Part import Sketcher LENGTH = 10.0 TOLERANCE = 1e-7 def line(start, end): return Part.LineSegment(App.Vector(start[0], start[1], 0), App.Vector(end[0], end[1], 0)) def add_loop(sketch, points, closed=True): segment_count = len(points) if closed else len(points) - 1 sketch.addGeometry([line(points[index], points[(index + 1) % len(points)]) for index in range(segment_count)], False) def object_status(feature): try: status_string = feature.getStatusString() except Exception: status_string = "" return { "state": [str(value) for value in feature.State], "statusString": str(status_string), } def run_case(case_id, expected, setup): document = App.newDocument("ProfileOracle_" + case_id.replace("-", "_")) try: body = document.addObject("PartDesign::Body", "Body") sketch = document.addObject("Sketcher::SketchObject", "Sketch") body.addObject(sketch) sketch.MapMode = "Deactivated" setup(sketch) document.recompute() pad = document.addObject("PartDesign::Pad", "Pad") pad.Profile = sketch pad.Length = LENGTH body.addObject(pad) recompute_result = document.recompute() shape_null = bool(pad.Shape.isNull()) volume = 0.0 if shape_null else float(pad.Shape.Volume) solids = 0 if shape_null else len(pad.Shape.Solids) accepted = not shape_null and volume > TOLERANCE and solids >= 1 observed = "accepted" if accepted else "rejected" return { "id": case_id, "expected": expected, "observed": observed, "passed": observed == expected, "recomputeResult": bool(recompute_result), "shapeNull": shape_null, "solidCount": int(solids), "volume": volume, **object_status(pad), } except Exception as error: return { "id": case_id, "expected": expected, "observed": "rejected", "passed": expected == "rejected", "errorType": type(error).__name__, "error": str(error), } finally: App.closeDocument(document.Name) OUTER = [(0, 0), (6, 0), (6, 6), (0, 6)] HOLE = [(2, 2), (2, 4), (4, 4), (4, 2)] def closed_profile(sketch): add_loop(sketch, OUTER) def multi_ring_profile(sketch): add_loop(sketch, HOLE) add_loop(sketch, OUTER) def open_profile(sketch): add_loop(sketch, OUTER, False) def self_intersecting_profile(sketch): add_loop(sketch, [(0, 0), (4, 4), (0, 4), (4, 0)]) def disjoint_profile(sketch): add_loop(sketch, OUTER) add_loop(sketch, [(8, 8), (9, 8), (9, 9), (8, 9)]) cases = [ run_case("closed", "accepted", closed_profile), run_case("multi-ring", "accepted", multi_ring_profile), run_case("open", "rejected", open_profile), run_case("self-intersecting", "accepted", self_intersecting_profile), run_case("invalid-nesting", "accepted", disjoint_profile), ] report = { "schemaVersion": 1, "baselineId": "freecad-1.1.1-partdesign-profile-oracle", "freecadVersion": ".".join(App.Version()[0:3]), "gitCommit": "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d", "length": LENGTH, "tolerance": TOLERANCE, "status": "pass" if all(case["passed"] for case in cases) else "failed", "summary": { "cases": len(cases), "passed": sum(1 for case in cases if case["passed"]), "accepted": sum(1 for case in cases if case["observed"] == "accepted"), "rejected": sum(1 for case in cases if case["observed"] == "rejected"), }, "cases": cases, } print("FREECAD_PARTDESIGN_PROFILE_ORACLE_RESULT=" + json.dumps(report, sort_keys=True))