271 lines
9.4 KiB
Python
271 lines
9.4 KiB
Python
"""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()
|