87 lines
4.0 KiB
Python
87 lines
4.0 KiB
Python
import hashlib
|
|
|
|
|
|
def _json_value(value):
|
|
if value is None or isinstance(value, (bool, int, float, str)):
|
|
return value
|
|
if hasattr(value, "Value") and hasattr(value, "Unit"):
|
|
return {"value": float(value.Value), "unit": str(value.Unit)}
|
|
if hasattr(value, "x") and hasattr(value, "y") and hasattr(value, "z"):
|
|
return [float(value.x), float(value.y), float(value.z)]
|
|
if hasattr(value, "Name") and hasattr(value, "TypeId"):
|
|
return {"name": str(value.Name), "typeId": str(value.TypeId)}
|
|
if isinstance(value, (list, tuple)):
|
|
return [_json_value(item) for item in value]
|
|
return str(value)
|
|
|
|
|
|
def _shape_snapshot(feature):
|
|
shape = feature.Shape
|
|
if shape.isNull():
|
|
raise RuntimeError("{} produced a null Shape".format(feature.Name))
|
|
if not shape.isValid():
|
|
raise RuntimeError("{} produced an invalid Shape".format(feature.Name))
|
|
brep = shape.exportBrepToString().encode("utf-8")
|
|
bounds = shape.BoundBox
|
|
return {
|
|
"brepSha256": hashlib.sha256(brep).hexdigest(),
|
|
"shapeType": str(shape.ShapeType),
|
|
"valid": True,
|
|
"solids": len(shape.Solids),
|
|
"faces": len(shape.Faces),
|
|
"edges": len(shape.Edges),
|
|
"vertices": len(shape.Vertexes),
|
|
"volume": float(shape.Volume),
|
|
"area": float(shape.Area),
|
|
"bounds": {
|
|
"min": [float(bounds.XMin), float(bounds.YMin), float(bounds.ZMin)],
|
|
"max": [float(bounds.XMax), float(bounds.YMax), float(bounds.ZMax)],
|
|
},
|
|
}
|
|
|
|
|
|
def capture_property_mutation(document, feature, property_name, edited_value, target=None, property_path=None):
|
|
target = target or feature
|
|
original_value = getattr(target, property_name)
|
|
before_parameter = _json_value(original_value)
|
|
|
|
document.recompute()
|
|
before_shape_object = feature.Shape.copy()
|
|
before_shape = _shape_snapshot(feature)
|
|
try:
|
|
setattr(target, property_name, edited_value)
|
|
document.recompute()
|
|
edited_parameter = _json_value(getattr(target, property_name))
|
|
if before_parameter == edited_parameter:
|
|
raise RuntimeError("{} mutation did not change {}".format(feature.TypeId, property_path or property_name))
|
|
edited_shape = _shape_snapshot(feature)
|
|
finally:
|
|
setattr(target, property_name, original_value)
|
|
document.recompute()
|
|
restored_shape_object = feature.Shape
|
|
restored_shape = _shape_snapshot(feature)
|
|
restored_parameter = _json_value(getattr(target, property_name))
|
|
|
|
if restored_parameter != before_parameter:
|
|
raise RuntimeError("{} did not restore {}".format(feature.TypeId, property_path or property_name))
|
|
restored_equivalent = bool(restored_shape_object.isEqual(before_shape_object))
|
|
geometry_equivalent = (restored_shape["shapeType"], restored_shape["solids"], restored_shape["faces"], restored_shape["edges"], restored_shape["vertices"], round(restored_shape["volume"], 7), round(restored_shape["area"], 7)) == (before_shape["shapeType"], before_shape["solids"], before_shape["faces"], before_shape["edges"], before_shape["vertices"], round(before_shape["volume"], 7), round(before_shape["area"], 7))
|
|
if not restored_equivalent and not geometry_equivalent:
|
|
raise RuntimeError("{} did not restore the original geometry".format(feature.TypeId))
|
|
|
|
return {
|
|
"schemaVersion": 1,
|
|
"familyId": str(feature.TypeId),
|
|
"featureName": str(feature.Name),
|
|
"propertyPath": property_path or property_name,
|
|
"status": "pass",
|
|
"before": {"parameterSnapshot": before_parameter, "shape": before_shape},
|
|
"edited": {"parameterSnapshot": edited_parameter, "shape": edited_shape},
|
|
"restored": {"parameterSnapshot": restored_parameter, "shape": restored_shape},
|
|
"parameterChanged": True,
|
|
"shapeChanged": edited_shape["brepSha256"] != before_shape["brepSha256"],
|
|
"restoredExactly": restored_equivalent or geometry_equivalent,
|
|
"brepStable": restored_shape["brepSha256"] == before_shape["brepSha256"],
|
|
"restoredGeometrically": True,
|
|
}
|