import json import math import FreeCAD as App import Part import Sketcher import TestSketcherApp from freecad_mutation_evidence import capture_property_mutation def version_text(): return ".".join(str(value) for value in App.Version()[:3]) def shape_result(case_id, document, feature): document.recompute() shape = feature.Shape return { "id": case_id, "typeId": feature.TypeId, "passed": not shape.isNull() and shape.isValid() and len(shape.Solids) == 1, "status": feature.getStatusString(), "solids": len(shape.Solids) if not shape.isNull() else 0, "faces": len(shape.Faces) if not shape.isNull() else 0, "volume": float(shape.Volume) if not shape.isNull() else 0.0, } def additive_loft(): document = App.newDocument("AdditiveLoftOracle") body = document.addObject("PartDesign::Body", "Body") profile = body.newObject("Sketcher::SketchObject", "Profile") TestSketcherApp.CreateRectangleSketch(profile, (0, 0), (1, 1)) section = body.newObject("Sketcher::SketchObject", "Section") section.MapMode = "FlatFace" section.AttachmentSupport = (document.XZ_Plane, [""]) document.recompute() TestSketcherApp.CreateRectangleSketch(section, (0, 1), (1, 1)) feature = body.newObject("PartDesign::AdditiveLoft", "AdditiveLoft") feature.Profile = profile feature.Sections = [section] mutation = capture_property_mutation(document, feature, "Ruled", True) result = shape_result("additive-loft", document, feature) result["mutation"] = mutation App.closeDocument(document.Name) return result def subtractive_loft(): document = App.newDocument("SubtractiveLoftOracle") body = document.addObject("PartDesign::Body", "Body") pad_sketch = body.newObject("Sketcher::SketchObject", "PadSketch") TestSketcherApp.CreateRectangleSketch(pad_sketch, (0, 0), (1, 1)) pad = body.newObject("PartDesign::Pad", "Pad") pad.Profile = pad_sketch pad.Length = 2 document.recompute() profile = body.newObject("Sketcher::SketchObject", "Profile") TestSketcherApp.CreateRectangleSketch(profile, (0, 0), (1, 1)) section = body.newObject("Sketcher::SketchObject", "Section") section.MapMode = "FlatFace" section.AttachmentSupport = (document.XZ_Plane, [""]) document.recompute() TestSketcherApp.CreateRectangleSketch(section, (0, 1), (1, 1)) edited_section = body.newObject("Sketcher::SketchObject", "EditedSection") edited_section.MapMode = "FlatFace" edited_section.AttachmentSupport = (document.XZ_Plane, [""]) document.recompute() TestSketcherApp.CreateRectangleSketch(edited_section, (0, 1), (0.75, 0.75)) feature = body.newObject("PartDesign::SubtractiveLoft", "SubtractiveLoft") feature.Profile = profile feature.Sections = [section] mutation = capture_property_mutation(document, feature, "Sections", [edited_section]) result = shape_result("subtractive-loft", document, feature) result["mutation"] = mutation App.closeDocument(document.Name) return result def pipe_sketches(document, body): profile = body.newObject("Sketcher::SketchObject", "Profile") TestSketcherApp.CreateCircleSketch(profile, (0, 0), 1) edited_profile = body.newObject("Sketcher::SketchObject", "EditedProfile") TestSketcherApp.CreateCircleSketch(edited_profile, (0, 0), 0.75) spine = body.newObject("Sketcher::SketchObject", "Spine") spine.MapMode = "FlatFace" spine.AttachmentSupport = (document.XZ_Plane, [""]) document.recompute() spine.addGeometry(Part.LineSegment(App.Vector(0, 0, 0), App.Vector(0, 1, 0)), False) spine.addConstraint(Sketcher.Constraint("Coincident", 0, 1, -1, 1)) spine.addConstraint(Sketcher.Constraint("PointOnObject", 0, 2, -2)) spine.addConstraint(Sketcher.Constraint("DistanceY", 0, 1, 0, 2, 1)) return profile, edited_profile, spine def additive_pipe(): document = App.newDocument("AdditivePipeOracle") body = document.addObject("PartDesign::Body", "Body") profile, edited_profile, spine = pipe_sketches(document, body) feature = body.newObject("PartDesign::AdditivePipe", "AdditivePipe") feature.Profile = profile feature.Spine = spine mutation = capture_property_mutation(document, feature, "Profile", edited_profile) result = shape_result("additive-pipe", document, feature) result["mutation"] = mutation result["transition"] = str(feature.Transition) App.closeDocument(document.Name) return result def subtractive_pipe(): document = App.newDocument("SubtractivePipeOracle") body = document.addObject("PartDesign::Body", "Body") profile, edited_profile, spine = pipe_sketches(document, body) pad_sketch = body.newObject("Sketcher::SketchObject", "PadSketch") TestSketcherApp.CreateRectangleSketch(pad_sketch, (-5, -5), (10, 10)) pad = body.newObject("PartDesign::Pad", "Pad") pad.Profile = pad_sketch pad.Length = 1 document.recompute() feature = body.newObject("PartDesign::SubtractivePipe", "SubtractivePipe") feature.Profile = profile feature.Spine = spine mutation = capture_property_mutation(document, feature, "Profile", edited_profile) result = shape_result("subtractive-pipe", document, feature) result["mutation"] = mutation result["transition"] = str(feature.Transition) App.closeDocument(document.Name) return result cases = [additive_loft(), subtractive_loft(), additive_pipe(), subtractive_pipe()] report = { "schemaVersion": 1, "baselineId": "freecad-1.1.1-partdesign-loft-oracle", "freecadVersion": version_text(), "gitCommit": "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d", "status": "pass" if all(case["passed"] for case in cases) else "failed", "tolerance": 1e-7, "cases": cases, "summary": {"cases": len(cases), "passed": sum(1 for case in cases if case["passed"])}, } print("FREECAD_PARTDESIGN_LOFT_RESULT=" + json.dumps(report, sort_keys=True, separators=(",", ":")))