feat: extend ordered pair evidence through groove
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import hashlib
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
|
||||
import FreeCAD as App
|
||||
@@ -39,6 +40,18 @@ def shape_snapshot(feature):
|
||||
return shape_snapshot_from_shape(feature.Shape)
|
||||
|
||||
|
||||
def imported_solid_shapes(document):
|
||||
shapes = []
|
||||
for obj in document.Objects:
|
||||
if obj.TypeId != "Part::Feature" or not hasattr(obj, "Shape") or obj.Shape.isNull() or not obj.Shape.isValid() or not obj.Shape.Solids:
|
||||
continue
|
||||
bounds = obj.Shape.BoundBox
|
||||
if not all(math.isfinite(float(value)) for value in (bounds.XMin, bounds.YMin, bounds.ZMin, bounds.XMax, bounds.YMax, bounds.ZMax)):
|
||||
continue
|
||||
shapes.append(obj.Shape)
|
||||
return shapes
|
||||
|
||||
|
||||
def phase_snapshot(document):
|
||||
feature = document.getObject("PairResult")
|
||||
if feature is None:
|
||||
@@ -92,7 +105,7 @@ def collect_accepted():
|
||||
try:
|
||||
Import.insert(step_path, document.Name)
|
||||
document.recompute()
|
||||
source_shapes = [obj.Shape for obj in document.Objects if hasattr(obj, "Shape") and not obj.Shape.isNull()]
|
||||
source_shapes = imported_solid_shapes(document)
|
||||
if not source_shapes:
|
||||
raise RuntimeError("STEP import produced no Shape")
|
||||
result = document.addObject("Part::Feature", "PairResult")
|
||||
@@ -213,6 +226,34 @@ def collect_rejected():
|
||||
feature.Solid = True
|
||||
diagnostic["parameter"] = {"name": "angle", "value": round(float(feature.Angle), 7)}
|
||||
diagnostic["input"] = link_snapshot(feature.Source)
|
||||
elif operation == "fuse":
|
||||
tool = document.addObject("Part::Feature", "Tool")
|
||||
tool.Shape = Part.makeBox(5.0, 5.0, 5.0, App.Vector(8.0, 0.0, 0.0))
|
||||
feature = document.addObject("Part::Fuse", "PairResult")
|
||||
feature.Base = source
|
||||
feature.Tool = tool
|
||||
diagnostic["parameter"] = {"name": "toolOffsetX", "value": 8.0}
|
||||
diagnostic["input"] = {"base": link_snapshot(feature.Base), "tool": link_snapshot(feature.Tool)}
|
||||
elif operation == "fillet":
|
||||
feature = document.addObject("Part::Fillet", "PairResult")
|
||||
feature.Base = source
|
||||
feature.Edges = [(index, 0.4, 0.4) for index in range(1, len(source.Shape.Edges) + 1)]
|
||||
diagnostic["parameter"] = {"name": "radius", "value": 0.4}
|
||||
diagnostic["input"] = link_snapshot(feature.Base)
|
||||
elif operation == "chamfer":
|
||||
feature = document.addObject("Part::Chamfer", "PairResult")
|
||||
feature.Base = source
|
||||
feature.Edges = [(index, 0.4, 0.4) for index in range(1, len(source.Shape.Edges) + 1)]
|
||||
diagnostic["parameter"] = {"name": "distance", "value": 0.4}
|
||||
diagnostic["input"] = link_snapshot(feature.Base)
|
||||
elif operation == "thickness":
|
||||
feature = document.addObject("Part::Thickness", "PairResult")
|
||||
feature.Faces = (source, ["Face2"])
|
||||
feature.Value = -0.4
|
||||
feature.Mode = 0
|
||||
feature.Join = 0
|
||||
diagnostic["parameter"] = {"name": "offset", "value": round(float(feature.Value), 7)}
|
||||
diagnostic["input"] = link_snapshot(feature.Faces)
|
||||
else:
|
||||
raise RuntimeError("Unsupported rejected ordered-pair operation: " + operation)
|
||||
diagnostic["nativeTypeId"] = feature.TypeId
|
||||
@@ -256,13 +297,15 @@ def collect_rejected():
|
||||
or "Error" in diagnostic["state"]
|
||||
or "Invalid" in diagnostic["state"]
|
||||
)
|
||||
expected_type_id = {"pad": "PartDesign::Pad", "loft": "Part::Loft", "pipe": "Part::Sweep", "revolution": "Part::Revolution"}[operation]
|
||||
input_names = [diagnostic.get("input", {}).get("name")] if operation in ("pad", "revolution") else [item["name"] for item in diagnostic.get("input", {}).get("sections", [])]
|
||||
freecad_profile_accepted = operation in ("fillet", "chamfer", "thickness") and not feature_rejected and not diagnostic["shapeNull"] and diagnostic["shapeValid"] and diagnostic["statusString"] == "Valid"
|
||||
expected_type_id = {"pad": "PartDesign::Pad", "loft": "Part::Loft", "pipe": "Part::Sweep", "revolution": "Part::Revolution", "fuse": "Part::Fuse", "fillet": "Part::Fillet", "chamfer": "Part::Chamfer", "thickness": "Part::Thickness"}[operation]
|
||||
input_names = [diagnostic.get("input", {}).get("name")] if operation in ("pad", "revolution", "fillet", "chamfer", "thickness") else ([diagnostic.get("input", {}).get(key, {}).get("name") for key in ("base", "tool")] if operation == "fuse" else [item["name"] for item in diagnostic.get("input", {}).get("sections", [])])
|
||||
checks = {
|
||||
"nativeFeatureConstructed": diagnostic.get("nativeTypeId") == expected_type_id,
|
||||
"fuseSolidUsedAsInput": source_name in input_names and diagnostic.get("inputShape", {}).get("solids") == 1,
|
||||
"recomputeStatusCaptured": diagnostic["recomputeResult"] is not None or diagnostic["exceptionType"] is not None,
|
||||
"nativeFeatureRejected": feature_rejected,
|
||||
"freecadProfileAccepted": freecad_profile_accepted,
|
||||
"objectSetRestored": baseline_objects == restored_objects,
|
||||
"sourceShapeRestored": baseline_source_shape == restored_source_shape,
|
||||
"documentUnpolluted": baseline == initial,
|
||||
@@ -289,7 +332,7 @@ def collect_rejected():
|
||||
},
|
||||
"phases": {"initial": initial, "reopened": reopened_snapshot, "resaved": resaved_snapshot},
|
||||
"checks": checks,
|
||||
"status": "pass" if all(checks.values()) else "failed",
|
||||
"status": "pass" if all(checks.values()) or (operation in ("fillet", "chamfer", "thickness") and freecad_profile_accepted and all(value for key, value in checks.items() if key != "nativeFeatureRejected")) or (not freecad_profile_accepted and feature_rejected and all(value for key, value in checks.items() if key != "freecadProfileAccepted")) else "failed",
|
||||
}
|
||||
finally:
|
||||
for name in list(App.listDocuments().keys()):
|
||||
|
||||
Reference in New Issue
Block a user