"""Parametric 28-tooth spur gear generated with SimpleCADAPI.""" import json import sys from pathlib import Path _PROJECT_ROOT = Path(__file__).resolve().parents[4] _SIMPLECADAPI_SRC = _PROJECT_ROOT / "SimpleCADAPI" / "src" if str(_SIMPLECADAPI_SRC) not in sys.path: sys.path.insert(0, str(_SIMPLECADAPI_SRC)) import simplecadapi as scad N_TEETH = 28 MODULE_MM = 1.0 PRESSURE_ANGLE_DEG = 20.0 THICKNESS_MM = 4.0 BORE_DIAMETER_MM = 5.0 ADDENDUM_FACTOR = 1.0 CLEARANCE_FACTOR = 0.25 BACKLASH_MM = 0.0 VIEWER_SELECTED_EDGE = { "reference_id": "e344", "row_index": 343, "description": "top circular edge of the center bore", } BORE_TOP_CHAMFER_DISTANCE_MM = 3.0 EDIT_HOLE_AXIS = (0.0, 0.0, 1.0) EDIT_HOLES = [ { "diameter_mm": 2.0, "center_mm": (-2.9189440262073063, 5.47686606164935, THICKNESS_MM), }, { "diameter_mm": 2.0, "center_mm": (7.5974463825483305, -0.8433357012066764, THICKNESS_MM), }, ] EDIT_HOLE_PATTERNS = [ { "diameter_mm": 2.0, "rows": 2, "columns": 2, "pitch_x_mm": 8.0, "pitch_y_mm": 8.0, "depth": "through", "direction": "face_normal", "orientation": "surface_xy", "center_mm": (0.24688766006678975, -8.367405492677905, THICKNESS_MM), "selected_face": { "reference_id": "f5", "selector": "f5", "face_index": 2785, "row_index": 4, "normal": (0.0, 0.0, 1.0), }, }, ] EDIT_POCKETS = [ { "shape": "rectangle", "width_mm": 10.0, "height_mm": 6.0, "depth_mm": 1.0, "center_mm": (-8.717129036056448, -0.9462437095981322, THICKNESS_MM), "direction": "face_normal", "orientation": "surface_xy", "selected_face": { "reference_id": "f5", "selector": "f5", "face_index": 5428, "row_index": 4, "normal": (0.0, 0.0, 1.0), }, }, ] def hole_pattern_centers(pattern): x_center, y_center, z_center = pattern["center_mm"] x_start = x_center - (pattern["columns"] - 1) * pattern["pitch_x_mm"] / 2.0 y_start = y_center - (pattern["rows"] - 1) * pattern["pitch_y_mm"] / 2.0 return [ ( x_start + column * pattern["pitch_x_mm"], y_start + row * pattern["pitch_y_mm"], z_center, ) for row in range(pattern["rows"]) for column in range(pattern["columns"]) ] def generate() -> None: output_dir = Path(__file__).resolve().parent with scad.GraphSession() as session: gear_blank = scad.std.gear.make_spur_gear_rsolid( n_teeth=N_TEETH, module=MODULE_MM, pressure_angle=PRESSURE_ANGLE_DEG, gear_height=THICKNESS_MM, addendum_factor=ADDENDUM_FACTOR, clearance_factor=CLEARANCE_FACTOR, backlash=BACKLASH_MM, ) blank_faces = scad.ql.select(items=gear_blank.get_faces()).all() print("gear_blank_faces", len(blank_faces)) bore_radius = BORE_DIAMETER_MM / 2.0 bore = scad.make_cylinder_rsolid( radius=bore_radius, height=THICKNESS_MM + 2.0, bottom_face_center=(0.0, 0.0, -1.0), axis=(0.0, 0.0, 1.0), ) bore_top_chamfer = scad.make_cone_rsolid( bottom_radius=bore_radius, height=BORE_TOP_CHAMFER_DISTANCE_MM, top_radius=bore_radius + BORE_TOP_CHAMFER_DISTANCE_MM, bottom_face_center=( 0.0, 0.0, THICKNESS_MM - BORE_TOP_CHAMFER_DISTANCE_MM, ), axis=(0.0, 0.0, 1.0), ) edit_hole_cutters = [ scad.make_cylinder_rsolid( radius=hole["diameter_mm"] / 2.0, height=THICKNESS_MM + 2.0, bottom_face_center=(hole["center_mm"][0], hole["center_mm"][1], -1.0), axis=EDIT_HOLE_AXIS, ) for hole in EDIT_HOLES ] pattern_hole_cutters = [ scad.make_cylinder_rsolid( radius=pattern["diameter_mm"] / 2.0, height=THICKNESS_MM + 2.0, bottom_face_center=(center[0], center[1], -1.0), axis=EDIT_HOLE_AXIS, ) for pattern in EDIT_HOLE_PATTERNS for center in hole_pattern_centers(pattern) ] pocket_cutters = [ scad.make_box_rsolid( width=pocket["width_mm"], height=pocket["height_mm"], depth=pocket["depth_mm"] + 0.02, bottom_face_center=( pocket["center_mm"][0], pocket["center_mm"][1], THICKNESS_MM - pocket["depth_mm"], ), ) for pocket in EDIT_POCKETS ] print("viewer_edit_holes", len(edit_hole_cutters)) print("viewer_edit_pattern_holes", len(pattern_hole_cutters)) print("viewer_edit_pockets", len(pocket_cutters)) gear = scad.cut_rsolid( gear_blank, [ bore, bore_top_chamfer, *edit_hole_cutters, *pattern_hole_cutters, *pocket_cutters, ], skip_non_intersecting=False, ) gear = scad.apply_tag(shape=gear, tag="role.power_transmission.gear") gear = scad.apply_tag(shape=gear, tag="feature.viewer_edit_point_hole") gear = scad.apply_tag(shape=gear, tag="feature.viewer_edit_hole_pattern") gear = scad.apply_tag(shape=gear, tag="feature.viewer_edit_rectangular_pocket") gear = scad.apply_tag(shape=gear, tag="feature.viewer_selected_edge_chamfer") final_faces = scad.ql.select(items=gear.get_faces()).all() final_edges = scad.ql.select(items=gear.get_edges()).all() print("final_topology", {"faces": len(final_faces), "edges": len(final_edges)}) model_json = scad.export_model_json(session=session, indent=2) (output_dir / "gear_28t_4mm.model.json").write_text(model_json, encoding="utf-8") scad.export_step(shapes=gear, filename=str(output_dir / "gear_28t_4mm.step")) scad.export_stl(shapes=gear, filename=str(output_dir / "gear_28t_4mm.stl")) pitch_diameter_mm = MODULE_MM * N_TEETH outside_diameter_mm = MODULE_MM * (N_TEETH + 2.0 * ADDENDUM_FACTOR) root_diameter_mm = MODULE_MM * ( N_TEETH - 2.0 * (ADDENDUM_FACTOR + CLEARANCE_FACTOR) ) validation = { "n_teeth": N_TEETH, "pitch_diameter_mm": pitch_diameter_mm, "outside_diameter_mm": outside_diameter_mm, "root_diameter_mm": root_diameter_mm, "thickness_mm": THICKNESS_MM, "bore_diameter_mm": BORE_DIAMETER_MM, "viewer_selected_edge": VIEWER_SELECTED_EDGE, "bore_top_chamfer_distance_mm": BORE_TOP_CHAMFER_DISTANCE_MM, "bore_top_chamfer_top_diameter_mm": BORE_DIAMETER_MM + 2.0 * BORE_TOP_CHAMFER_DISTANCE_MM, "edit_holes": [ { "diameter_mm": hole["diameter_mm"], "center_mm": list(hole["center_mm"]), } for hole in EDIT_HOLES ], "edit_hole_count": len(EDIT_HOLES), "edit_hole_axis": list(EDIT_HOLE_AXIS), "edit_hole_patterns": [ { "diameter_mm": pattern["diameter_mm"], "rows": pattern["rows"], "columns": pattern["columns"], "pitch_x_mm": pattern["pitch_x_mm"], "pitch_y_mm": pattern["pitch_y_mm"], "depth": pattern["depth"], "direction": pattern["direction"], "orientation": pattern["orientation"], "center_mm": list(pattern["center_mm"]), "hole_centers_mm": [ list(center) for center in hole_pattern_centers(pattern) ], "selected_face": { **pattern["selected_face"], "normal": list(pattern["selected_face"]["normal"]), }, } for pattern in EDIT_HOLE_PATTERNS ], "edit_hole_pattern_count": len(EDIT_HOLE_PATTERNS), "edit_hole_pattern_hole_count": sum( pattern["rows"] * pattern["columns"] for pattern in EDIT_HOLE_PATTERNS ), "edit_pockets": [ { "shape": pocket["shape"], "width_mm": pocket["width_mm"], "height_mm": pocket["height_mm"], "depth_mm": pocket["depth_mm"], "center_mm": list(pocket["center_mm"]), "direction": pocket["direction"], "orientation": pocket["orientation"], "selected_face": { **pocket["selected_face"], "normal": list(pocket["selected_face"]["normal"]), }, } for pocket in EDIT_POCKETS ], "edit_pocket_count": len(EDIT_POCKETS), "volume_mm3": round(gear.get_volume(), 6), "faces": len(final_faces), "edges": len(final_edges), "tags": scad.list_tags(shape=gear), "graph_nodes": session.graph.node_count, } (output_dir / "validation.json").write_text( json.dumps(validation, indent=2, ensure_ascii=False) + "\n", encoding="utf-8", ) print("validation", validation) if __name__ == "__main__": generate()