feat: 完善 STEP 编辑器最小系统和稳定性
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
DEFAULT_OUTPUT = PROJECT_ROOT / "assets" / "models" / "cube_10mm.step"
|
||||
|
||||
|
||||
def write_cube_step(path: Path, size: float = 10.0) -> None:
|
||||
entities: list[str] = []
|
||||
|
||||
def add(text: str) -> int:
|
||||
index = len(entities) + 1
|
||||
entities.append(f"#{index}={text};")
|
||||
return index
|
||||
|
||||
def ref(index: int) -> str:
|
||||
return f"#{index}"
|
||||
|
||||
def refs(indices) -> str:
|
||||
return ",".join(ref(index) for index in indices)
|
||||
|
||||
app_ctx = add("APPLICATION_CONTEXT('automotive_design')")
|
||||
add(f"APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,{ref(app_ctx)})")
|
||||
prod_ctx = add(f"PRODUCT_CONTEXT('',{ref(app_ctx)},'mechanical')")
|
||||
product = add(
|
||||
"PRODUCT('CUBE_10MM','CUBE_10MM','Simple 10 mm cube generated for edge editing tests',"
|
||||
f"({ref(prod_ctx)}))"
|
||||
)
|
||||
formation = add(
|
||||
f"PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','generated',{ref(product)},.NOT_KNOWN.)"
|
||||
)
|
||||
pd_ctx = add(f"PRODUCT_DEFINITION_CONTEXT('part definition',{ref(app_ctx)},'design')")
|
||||
product_def = add(f"PRODUCT_DEFINITION('design','',{ref(formation)},{ref(pd_ctx)})")
|
||||
product_shape = add(f"PRODUCT_DEFINITION_SHAPE('','',{ref(product_def)})")
|
||||
length_unit = add("(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.))")
|
||||
angle_unit = add("(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.))")
|
||||
solid_angle_unit = add("(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT())")
|
||||
uncertainty = add(
|
||||
f"UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.E-6),{ref(length_unit)},"
|
||||
"'distance_accuracy_value','')"
|
||||
)
|
||||
geom_context = add(
|
||||
"(GEOMETRIC_REPRESENTATION_CONTEXT(3) "
|
||||
f"GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT(({ref(uncertainty)})) "
|
||||
f"GLOBAL_UNIT_ASSIGNED_CONTEXT(({refs([length_unit, angle_unit, solid_angle_unit])})) "
|
||||
"REPRESENTATION_CONTEXT('3D Context',''))"
|
||||
)
|
||||
|
||||
point_ids: dict[str, int] = {}
|
||||
for name, xyz in {
|
||||
"P1": (0.0, 0.0, 0.0),
|
||||
"P2": (size, 0.0, 0.0),
|
||||
"P3": (size, size, 0.0),
|
||||
"P4": (0.0, size, 0.0),
|
||||
"P5": (0.0, 0.0, size),
|
||||
"P6": (size, 0.0, size),
|
||||
"P7": (size, size, size),
|
||||
"P8": (0.0, size, size),
|
||||
}.items():
|
||||
point_ids[name] = add("CARTESIAN_POINT('',({:.6f},{:.6f},{:.6f}))".format(*xyz))
|
||||
|
||||
directions = {
|
||||
"+X": add("DIRECTION('',(1.000000,0.000000,0.000000))"),
|
||||
"-X": add("DIRECTION('',(-1.000000,0.000000,0.000000))"),
|
||||
"+Y": add("DIRECTION('',(0.000000,1.000000,0.000000))"),
|
||||
"-Y": add("DIRECTION('',(0.000000,-1.000000,0.000000))"),
|
||||
"+Z": add("DIRECTION('',(0.000000,0.000000,1.000000))"),
|
||||
"-Z": add("DIRECTION('',(0.000000,0.000000,-1.000000))"),
|
||||
}
|
||||
world_axis = add(
|
||||
f"AXIS2_PLACEMENT_3D('',{ref(point_ids['P1'])},{ref(directions['+Z'])},{ref(directions['+X'])})"
|
||||
)
|
||||
|
||||
vertex_ids = {
|
||||
name.replace("P", "V"): add(f"VERTEX_POINT('',{ref(point_id)})")
|
||||
for name, point_id in point_ids.items()
|
||||
}
|
||||
|
||||
edge_specs = {
|
||||
"E1": ("V1", "V2", "P1", "+X"),
|
||||
"E2": ("V2", "V3", "P2", "+Y"),
|
||||
"E3": ("V3", "V4", "P3", "-X"),
|
||||
"E4": ("V4", "V1", "P4", "-Y"),
|
||||
"E5": ("V5", "V6", "P5", "+X"),
|
||||
"E6": ("V6", "V7", "P6", "+Y"),
|
||||
"E7": ("V7", "V8", "P7", "-X"),
|
||||
"E8": ("V8", "V5", "P8", "-Y"),
|
||||
"E9": ("V1", "V5", "P1", "+Z"),
|
||||
"E10": ("V2", "V6", "P2", "+Z"),
|
||||
"E11": ("V3", "V7", "P3", "+Z"),
|
||||
"E12": ("V4", "V8", "P4", "+Z"),
|
||||
}
|
||||
edge_ids: dict[str, int] = {}
|
||||
for name, (start, end, start_point, direction) in edge_specs.items():
|
||||
vector = add(f"VECTOR('',{ref(directions[direction])},1.000000)")
|
||||
line = add(f"LINE('',{ref(point_ids[start_point])},{ref(vector)})")
|
||||
edge_ids[name] = add(
|
||||
f"EDGE_CURVE('{name}',{ref(vertex_ids[start])},{ref(vertex_ids[end])},{ref(line)},.T.)"
|
||||
)
|
||||
|
||||
def oriented(edge_name: str, same: bool) -> int:
|
||||
orientation = ".T." if same else ".F."
|
||||
return add(f"ORIENTED_EDGE('',*,*,{ref(edge_ids[edge_name])},{orientation})")
|
||||
|
||||
def make_plane(origin_point: str, normal: str, ref_dir: str) -> int:
|
||||
axis = add(
|
||||
f"AXIS2_PLACEMENT_3D('',{ref(point_ids[origin_point])},"
|
||||
f"{ref(directions[normal])},{ref(directions[ref_dir])})"
|
||||
)
|
||||
return add(f"PLANE('',{ref(axis)})")
|
||||
|
||||
faces: list[int] = []
|
||||
face_specs = [
|
||||
("BOTTOM_Z0", [("E4", False), ("E3", False), ("E2", False), ("E1", False)], "P1", "-Z", "+X"),
|
||||
("TOP_Z10", [("E5", True), ("E6", True), ("E7", True), ("E8", True)], "P5", "+Z", "+X"),
|
||||
("FRONT_Y0", [("E1", True), ("E10", True), ("E5", False), ("E9", False)], "P1", "-Y", "+X"),
|
||||
("BACK_Y10", [("E12", True), ("E7", False), ("E11", False), ("E3", True)], "P4", "+Y", "+X"),
|
||||
("LEFT_X0", [("E9", True), ("E8", False), ("E12", False), ("E4", True)], "P1", "-X", "+Y"),
|
||||
("RIGHT_X10", [("E2", True), ("E11", True), ("E6", False), ("E10", False)], "P2", "+X", "+Y"),
|
||||
]
|
||||
for name, loop_edges, plane_origin, normal, ref_dir in face_specs:
|
||||
loop = add(f"EDGE_LOOP('',({refs(oriented(edge, same) for edge, same in loop_edges)}))")
|
||||
bound = add(f"FACE_OUTER_BOUND('',{ref(loop)},.T.)")
|
||||
plane = make_plane(plane_origin, normal, ref_dir)
|
||||
faces.append(add(f"ADVANCED_FACE('{name}',({ref(bound)}),{ref(plane)},.T.)"))
|
||||
|
||||
closed_shell = add(f"CLOSED_SHELL('',({refs(faces)}))")
|
||||
solid = add(f"MANIFOLD_SOLID_BREP('CUBE_10MM',{ref(closed_shell)})")
|
||||
shape_rep = add(
|
||||
f"ADVANCED_BREP_SHAPE_REPRESENTATION('',({refs([world_axis, solid])}),{ref(geom_context)})"
|
||||
)
|
||||
add(f"SHAPE_DEFINITION_REPRESENTATION({ref(product_shape)},{ref(shape_rep)})")
|
||||
add(f"PRODUCT_RELATED_PRODUCT_CATEGORY('part','',({ref(product)}))")
|
||||
|
||||
header = "\n".join(
|
||||
[
|
||||
"ISO-10303-21;",
|
||||
"HEADER;",
|
||||
"FILE_DESCRIPTION(('Simple 10 mm cube generated by Python for edge edit tests'),'2;1');",
|
||||
(
|
||||
f"FILE_NAME('{path.name}','{datetime.now().isoformat(timespec='seconds')}',"
|
||||
"('Codex'),('OpenAI'),'Python ASCII STEP generator','python-occt','');"
|
||||
),
|
||||
"FILE_SCHEMA(('AUTOMOTIVE_DESIGN_CC2'));",
|
||||
"ENDSEC;",
|
||||
"DATA;",
|
||||
]
|
||||
)
|
||||
path.write_text(header + "\n" + "\n".join(entities) + "\nENDSEC;\nEND-ISO-10303-21;\n", encoding="utf-8")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Generate a simple STEP cube for editor tests.")
|
||||
parser.add_argument("output", nargs="?", default=str(DEFAULT_OUTPUT), help="Output STEP file path.")
|
||||
parser.add_argument("--size", type=float, default=10.0, help="Cube edge length in millimeters.")
|
||||
args = parser.parse_args()
|
||||
|
||||
output = Path(args.output)
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
write_cube_step(output, args.size)
|
||||
print(f"wrote {output.resolve()} ({output.stat().st_size} bytes)")
|
||||
print(f"cube bounds: 0,0,0 to {args.size:g},{args.size:g},{args.size:g} mm")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user