264 lines
8.8 KiB
Python
264 lines
8.8 KiB
Python
"""Build a hydraulic rod assembly with separate sleeve and piston-rod parts."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import simplecadapi as scad
|
|
from simplecadapi import ql
|
|
|
|
|
|
OUT_DIR = Path("examples/out/hydraulic_rod_assembly")
|
|
|
|
|
|
def build_hydraulic_rod_assembly():
|
|
flange_holes = [
|
|
(0.0, 16.0),
|
|
(16.0, 0.0),
|
|
(0.0, -16.0),
|
|
(-16.0, 0.0),
|
|
]
|
|
|
|
with scad.GraphSession() as session:
|
|
barrel = scad.make_cylinder_rsolid(
|
|
radius=16.0,
|
|
height=120.0,
|
|
bottom_face_center=(-60.0, 0.0, 0.0),
|
|
axis=(1.0, 0.0, 0.0),
|
|
)
|
|
rod_gland_flange = scad.make_cylinder_rsolid(
|
|
radius=22.0,
|
|
height=12.0,
|
|
bottom_face_center=(50.0, 0.0, 0.0),
|
|
axis=(1.0, 0.0, 0.0),
|
|
)
|
|
rod_gland_nose = scad.make_cylinder_rsolid(
|
|
radius=13.0,
|
|
height=10.0,
|
|
bottom_face_center=(58.0, 0.0, 0.0),
|
|
axis=(1.0, 0.0, 0.0),
|
|
)
|
|
base_cap = scad.make_cylinder_rsolid(
|
|
radius=18.0,
|
|
height=12.0,
|
|
bottom_face_center=(-66.0, 0.0, 0.0),
|
|
axis=(1.0, 0.0, 0.0),
|
|
)
|
|
rear_eye = scad.make_cylinder_rsolid(
|
|
radius=14.0,
|
|
height=12.0,
|
|
bottom_face_center=(-80.0, -6.0, 0.0),
|
|
axis=(0.0, 1.0, 0.0),
|
|
)
|
|
rear_eye_neck = scad.make_box_rsolid(
|
|
18.0,
|
|
14.0,
|
|
16.0,
|
|
bottom_face_center=(-68.0, 0.0, -8.0),
|
|
)
|
|
sleeve_raw = scad.union_rsolid(
|
|
barrel,
|
|
rod_gland_flange,
|
|
rod_gland_nose,
|
|
base_cap,
|
|
rear_eye,
|
|
rear_eye_neck,
|
|
glue=False,
|
|
)
|
|
sleeve_solid = scad.cut_rsolid(
|
|
sleeve_raw,
|
|
scad.make_cylinder_rsolid(
|
|
radius=10.5,
|
|
height=136.0,
|
|
bottom_face_center=(-68.0, 0.0, 0.0),
|
|
axis=(1.0, 0.0, 0.0),
|
|
),
|
|
)
|
|
sleeve_solid = scad.cut_rsolid(
|
|
sleeve_solid,
|
|
scad.make_cylinder_rsolid(
|
|
radius=4.6,
|
|
height=26.0,
|
|
bottom_face_center=(-80.0, -13.0, 0.0),
|
|
axis=(0.0, 1.0, 0.0),
|
|
),
|
|
)
|
|
for y, z in flange_holes:
|
|
sleeve_solid = scad.cut_rsolid(
|
|
sleeve_solid,
|
|
scad.make_cylinder_rsolid(
|
|
radius=1.8,
|
|
height=16.0,
|
|
bottom_face_center=(48.0, y, z),
|
|
axis=(1.0, 0.0, 0.0),
|
|
),
|
|
)
|
|
|
|
piston_land_left = scad.make_cylinder_rsolid(
|
|
radius=10.0,
|
|
height=3.2,
|
|
bottom_face_center=(-6.0, 0.0, 0.0),
|
|
axis=(1.0, 0.0, 0.0),
|
|
)
|
|
piston_seal_groove = scad.make_cylinder_rsolid(
|
|
radius=9.0,
|
|
height=6.0,
|
|
bottom_face_center=(-3.0, 0.0, 0.0),
|
|
axis=(1.0, 0.0, 0.0),
|
|
)
|
|
piston_land_right = scad.make_cylinder_rsolid(
|
|
radius=10.0,
|
|
height=3.2,
|
|
bottom_face_center=(2.6, 0.0, 0.0),
|
|
axis=(1.0, 0.0, 0.0),
|
|
)
|
|
chrome_rod = scad.make_cylinder_rsolid(
|
|
radius=6.5,
|
|
height=132.0,
|
|
bottom_face_center=(3.0, 0.0, 0.0),
|
|
axis=(1.0, 0.0, 0.0),
|
|
)
|
|
rod_eye = scad.make_cylinder_rsolid(
|
|
radius=13.0,
|
|
height=9.0,
|
|
bottom_face_center=(143.0, -4.5, 0.0),
|
|
axis=(0.0, 1.0, 0.0),
|
|
)
|
|
rod_eye_neck = scad.make_box_rsolid(
|
|
20.0,
|
|
8.0,
|
|
13.0,
|
|
bottom_face_center=(130.0, 0.0, -6.5),
|
|
)
|
|
piston_rod_raw = scad.union_rsolid(
|
|
piston_land_left,
|
|
piston_seal_groove,
|
|
piston_land_right,
|
|
chrome_rod,
|
|
rod_eye,
|
|
rod_eye_neck,
|
|
glue=False,
|
|
)
|
|
rod_eye_pin_hole = scad.make_cylinder_rsolid(
|
|
radius=5.5,
|
|
height=13.0,
|
|
bottom_face_center=(143.0, -6.5, 0.0),
|
|
axis=(0.0, 1.0, 0.0),
|
|
)
|
|
piston_rod_solid = scad.cut_rsolid(piston_rod_raw, rod_eye_pin_hole)
|
|
|
|
black_oxide_steel = scad.make_material_rmaterial(
|
|
"black_oxide_steel",
|
|
name="Black oxide steel",
|
|
density=7.85e-6,
|
|
density_unit="kg/mm^3",
|
|
color=(0.10, 0.11, 0.12),
|
|
)
|
|
chrome_steel = scad.make_material_rmaterial(
|
|
"chrome_plated_steel",
|
|
name="Chrome plated steel",
|
|
density=7.85e-6,
|
|
density_unit="kg/mm^3",
|
|
color=(0.78, 0.80, 0.82),
|
|
)
|
|
|
|
sleeve_part = scad.make_part_rpart(
|
|
"outer_sleeve", sleeve_solid, name="Outer sleeve with clevis and gland"
|
|
)
|
|
sleeve_part = scad.assign_material_rpart(sleeve_part, black_oxide_steel)
|
|
sleeve_faces = ql.faces().resolve(sleeve_solid)
|
|
sleeve_end_face = None
|
|
for f in sleeve_faces:
|
|
n = f.get_normal_at()
|
|
if abs(abs(n.x) - 1.0) < 0.01 and f.get_area() < 1000.0:
|
|
sleeve_end_face = f
|
|
break
|
|
sleeve_connector = scad.make_face_connector_rconnector("slide_axis", sleeve_end_face)
|
|
sleeve_part = scad.add_connector_rpart(sleeve_part, sleeve_connector)
|
|
|
|
piston_rod_part = scad.make_part_rpart(
|
|
"piston_rod", piston_rod_solid, name="Inner piston rod with eye end"
|
|
)
|
|
piston_rod_part = scad.assign_material_rpart(piston_rod_part, chrome_steel)
|
|
rod_faces = ql.faces().resolve(piston_rod_solid)
|
|
rod_end_face = None
|
|
for f in rod_faces:
|
|
n = f.get_normal_at()
|
|
if abs(abs(n.x) - 1.0) < 0.01 and f.get_area() < 1000.0:
|
|
rod_end_face = f
|
|
break
|
|
sleeve_normal = sleeve_end_face.get_normal_at()
|
|
rod_normal = rod_end_face.get_normal_at()
|
|
rod_flip = (sleeve_normal.x * rod_normal.x) < 0
|
|
rod_connector = scad.make_face_connector_rconnector("slide_axis", rod_end_face, flip=rod_flip)
|
|
piston_rod_part = scad.add_connector_rpart(piston_rod_part, rod_connector)
|
|
|
|
hydraulic_assembly = scad.make_assembly_rassembly(
|
|
"hydraulic_rod_assembly", name="Hydraulic rod assembly"
|
|
)
|
|
hydraulic_assembly = scad.add_component_rassembly(
|
|
hydraulic_assembly,
|
|
sleeve_part,
|
|
component_id="outer_sleeve",
|
|
placement=scad.identity_placement_rplacement(),
|
|
)
|
|
hydraulic_assembly = scad.add_component_rassembly(
|
|
hydraulic_assembly,
|
|
piston_rod_part,
|
|
component_id="inner_piston_rod",
|
|
placement=scad.identity_placement_rplacement(),
|
|
)
|
|
hydraulic_assembly = scad.ground_component_rassembly(
|
|
hydraulic_assembly, "outer_sleeve"
|
|
)
|
|
hydraulic_assembly = scad.add_prismatic_constraint_rassembly(
|
|
hydraulic_assembly,
|
|
"rod_slide",
|
|
scad.make_connector_ref_rconnectorref("outer_sleeve", "slide_axis"),
|
|
scad.make_connector_ref_rconnectorref("inner_piston_rod", "slide_axis"),
|
|
drive_distance=0.0,
|
|
distance_limit=scad.make_scalar_limit_rscalarlimit(0.0, 100.0),
|
|
)
|
|
hydraulic_assembly = scad.solve_assembly_constraints_rassembly(
|
|
hydraulic_assembly
|
|
)
|
|
|
|
preview = scad.make_compound_from_assembly_rcompound(hydraulic_assembly)
|
|
model_json = scad.export_model_json(session)
|
|
|
|
return hydraulic_assembly, preview, model_json
|
|
|
|
|
|
def main() -> None:
|
|
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
|
assembly, preview, model_json = build_hydraulic_rod_assembly()
|
|
|
|
model_path = OUT_DIR / "hydraulic_rod_assembly.model.json"
|
|
step_path = OUT_DIR / "hydraulic_rod_assembly.step"
|
|
fcstd_path = OUT_DIR / "hydraulic_rod_assembly.FCStd"
|
|
model_path.write_text(model_json, encoding="utf-8")
|
|
scad.export_step(preview, str(step_path))
|
|
fcstd_status = "skipped"
|
|
try:
|
|
scad.translator.freecad_translator.translate_model_json_to_fcstd(model_json, str(fcstd_path.resolve()))
|
|
fcstd_status = str(fcstd_path)
|
|
except Exception as exc: # pragma: no cover - depends on local FreeCAD install
|
|
fcstd_status = f"skipped ({exc.__class__.__name__})"
|
|
|
|
payload = json.loads(model_json)
|
|
face_count = len(ql.faces().resolve(preview))
|
|
print("assembly", assembly.assembly_id)
|
|
print("components", assembly.component_ids())
|
|
print("preview_solids", len(preview.get_solids()))
|
|
print("preview_faces", face_count)
|
|
print("preview_volume", round(preview.get_volume(), 3))
|
|
print("graph_nodes", len(payload["graph"]["nodes"]))
|
|
print("wrote", model_path)
|
|
print("wrote", step_path)
|
|
print("fcstd", fcstd_status)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|