feat: add candidate FreeCAD naming SDK and attachment oracles
Build and verify the candidate-only FreeCAD naming bridge and OCCT worker path, including three-stage StringHasher restoration. Add Datum, ShapeBinder, attachment-mode, and PartDesign structure oracles plus offline SDK build plans and CI boundary checks.
This commit is contained in:
182
scripts/freecad-attachment-mode-oracle.py
Normal file
182
scripts/freecad-attachment-mode-oracle.py
Normal file
@@ -0,0 +1,182 @@
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import FreeCAD as App
|
||||
|
||||
|
||||
def version_text():
|
||||
return ".".join(str(value) for value in App.Version()[:3])
|
||||
|
||||
|
||||
def placement_json(placement):
|
||||
axis = placement.Rotation.Axis
|
||||
base = placement.Base
|
||||
return {
|
||||
"position": [float(base.x), float(base.y), float(base.z)],
|
||||
"axis": [float(axis.x), float(axis.y), float(axis.z)],
|
||||
"angleDegrees": float(placement.Rotation.Angle * 180.0 / 3.141592653589793),
|
||||
}
|
||||
|
||||
|
||||
def support_json(value):
|
||||
result = []
|
||||
for obj, sub_elements in value:
|
||||
subs = [sub_elements] if isinstance(sub_elements, str) else list(sub_elements)
|
||||
result.append({
|
||||
"document": obj.Document.Name,
|
||||
"object": obj.Name,
|
||||
"subElements": [str(sub) for sub in subs if str(sub)],
|
||||
})
|
||||
return result
|
||||
|
||||
|
||||
def engine_json(obj):
|
||||
attacher = obj.Attacher
|
||||
implemented = list(attacher.ImplementedModes)
|
||||
return {
|
||||
"typeId": obj.TypeId,
|
||||
"attacherType": attacher.AttacherType,
|
||||
"completeModeList": list(attacher.CompleteModeList),
|
||||
"implementedModes": implemented,
|
||||
"modeInfo": {
|
||||
mode: {
|
||||
"modeIndex": int(attacher.getModeInfo(mode)["ModeIndex"]),
|
||||
"referenceCombinations": attacher.getModeInfo(mode)["ReferenceCombinations"],
|
||||
}
|
||||
for mode in implemented
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def object_state(obj):
|
||||
suggestion = obj.Attacher.suggestModes()
|
||||
return {
|
||||
"typeId": obj.TypeId,
|
||||
"mapMode": str(obj.MapMode),
|
||||
"support": support_json(obj.AttachmentSupport),
|
||||
"placement": placement_json(obj.Placement),
|
||||
"state": [str(value) for value in obj.State],
|
||||
"status": str(obj.getStatusString()),
|
||||
"positionBySupport": bool(obj.positionBySupport()),
|
||||
"suggestion": {
|
||||
"message": suggestion["message"],
|
||||
"bestFitMode": suggestion["bestFitMode"],
|
||||
"allApplicableModes": suggestion["allApplicableModes"],
|
||||
"referenceTypes": suggestion["references_Types"],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
document = App.newDocument("AttachmentModeOracle")
|
||||
source_body = document.addObject("PartDesign::Body", "SourceBody")
|
||||
source_box = source_body.newObject("PartDesign::AdditiveBox", "SourceBox")
|
||||
source_box.Length = 4
|
||||
source_box.Width = 5
|
||||
source_box.Height = 6
|
||||
document.recompute()
|
||||
|
||||
plane_body = document.addObject("PartDesign::Body", "PlaneBody")
|
||||
datum_plane = plane_body.newObject("PartDesign::Plane", "DatumPlane")
|
||||
datum_plane.AttachmentSupport = [(source_box, "Face1")]
|
||||
datum_plane.MapMode = "FlatFace"
|
||||
|
||||
line_body = document.addObject("PartDesign::Body", "LineBody")
|
||||
datum_line = line_body.newObject("PartDesign::Line", "DatumLine")
|
||||
datum_line.AttachmentSupport = [(source_box, ("Vertex1", "Vertex2"))]
|
||||
datum_line.MapMode = "TwoPointLine"
|
||||
|
||||
point_body = document.addObject("PartDesign::Body", "PointBody")
|
||||
datum_point = point_body.newObject("PartDesign::Point", "DatumPoint")
|
||||
datum_point.AttachmentSupport = [(source_box, "Vertex1")]
|
||||
datum_point.MapMode = "Vertex"
|
||||
|
||||
sketch_body = document.addObject("PartDesign::Body", "SketchBody")
|
||||
sketch = sketch_body.newObject("Sketcher::SketchObject", "Sketch")
|
||||
sketch.AttachmentSupport = [(source_box, "Face1")]
|
||||
sketch.MapMode = "FlatFace"
|
||||
|
||||
invalid_plane_body = document.addObject("PartDesign::Body", "InvalidPlaneBody")
|
||||
invalid_plane = invalid_plane_body.newObject("PartDesign::Plane", "InvalidPlane")
|
||||
invalid_plane.AttachmentSupport = [(source_box, "Vertex1")]
|
||||
invalid_plane.MapMode = "Vertex"
|
||||
|
||||
invalid_line_body = document.addObject("PartDesign::Body", "InvalidLineBody")
|
||||
invalid_line = invalid_line_body.newObject("PartDesign::Line", "InvalidLine")
|
||||
invalid_line.AttachmentSupport = [(source_box, "Face1")]
|
||||
invalid_line.MapMode = "FlatFace"
|
||||
|
||||
invalid_point_body = document.addObject("PartDesign::Body", "InvalidPointBody")
|
||||
invalid_point = invalid_point_body.newObject("PartDesign::Point", "InvalidPoint")
|
||||
invalid_point.AttachmentSupport = [(source_box, "Face1")]
|
||||
invalid_point.MapMode = "FlatFace"
|
||||
|
||||
document.recompute()
|
||||
engine_objects = {
|
||||
"plane": datum_plane,
|
||||
"line": datum_line,
|
||||
"point": datum_point,
|
||||
"sketch": sketch,
|
||||
}
|
||||
engines = {name: engine_json(obj) for name, obj in engine_objects.items()}
|
||||
success_initial = {name: object_state(obj) for name, obj in engine_objects.items()}
|
||||
failures = {
|
||||
obj.Name: {
|
||||
"typeId": obj.TypeId,
|
||||
"mapMode": str(obj.MapMode),
|
||||
"support": support_json(obj.AttachmentSupport),
|
||||
"state": [str(value) for value in obj.State],
|
||||
"status": str(obj.getStatusString()),
|
||||
}
|
||||
for obj in [invalid_plane, invalid_line, invalid_point]
|
||||
}
|
||||
|
||||
source_box.Height = 9
|
||||
document.recompute()
|
||||
success_edited = {name: object_state(obj) for name, obj in engine_objects.items()}
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix="freecad-attachment-mode-") as temp_dir:
|
||||
path = os.path.join(temp_dir, "AttachmentModeOracle.FCStd")
|
||||
document.saveAs(path)
|
||||
App.closeDocument(document.Name)
|
||||
reopened = App.openDocument(path)
|
||||
reopened.recompute()
|
||||
roundtrip = {
|
||||
name: object_state(reopened.getObject(object_name))
|
||||
for name, object_name in {
|
||||
"plane": "DatumPlane",
|
||||
"line": "DatumLine",
|
||||
"point": "DatumPoint",
|
||||
"sketch": "Sketch",
|
||||
}.items()
|
||||
}
|
||||
roundtrip_failures = {
|
||||
object_name: {
|
||||
"mapMode": str(reopened.getObject(object_name).MapMode),
|
||||
"state": [str(value) for value in reopened.getObject(object_name).State],
|
||||
"status": str(reopened.getObject(object_name).getStatusString()),
|
||||
}
|
||||
for object_name in ["InvalidPlane", "InvalidLine", "InvalidPoint"]
|
||||
}
|
||||
App.closeDocument(reopened.Name)
|
||||
|
||||
all_modes = engines["plane"]["completeModeList"]
|
||||
implemented_union = sorted({mode for engine in engines.values() for mode in engine["implementedModes"]})
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"baselineId": "freecad-1.1.1-attachment-mode-oracle",
|
||||
"freecadVersion": version_text(),
|
||||
"gitCommit": "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d",
|
||||
"status": "pass",
|
||||
"tolerance": 1e-7,
|
||||
"registry": {
|
||||
"modeCount": len(all_modes),
|
||||
"modes": all_modes,
|
||||
"implementedUnionCount": len(implemented_union),
|
||||
"unimplementedModes": [mode for mode in all_modes if mode not in implemented_union],
|
||||
},
|
||||
"engines": engines,
|
||||
"success": {"initial": success_initial, "edited": success_edited, "roundtrip": roundtrip},
|
||||
"failures": {"initial": failures, "roundtrip": roundtrip_failures},
|
||||
}
|
||||
print("FREECAD_ATTACHMENT_MODE_RESULT=" + json.dumps(report, sort_keys=True, separators=(",", ":")))
|
||||
Reference in New Issue
Block a user