Split project into frontend and backend
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,190 @@
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
import math
|
||||
|
||||
import cadquery as cq
|
||||
|
||||
|
||||
# All dimensions are millimetres. The main body is a revolved radial section.
|
||||
INNER_BOTTOM_RADIUS = 60.0
|
||||
INNER_STEP_RADIUS = 57.5
|
||||
INNER_SHOULDER_RADIUS = 70.0
|
||||
INNER_WALL_RADIUS = 79.0
|
||||
BOTTOM_OUTER_RADIUS = 77.5
|
||||
BODY_OUTER_RADIUS = 82.5
|
||||
TOP_OUTER_RADIUS = 85.0
|
||||
BASE_MIN_Z = -18.0
|
||||
BASE_MAX_Z = 92.0
|
||||
BASE_HEIGHT = BASE_MAX_Z - BASE_MIN_Z
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class HousingScale:
|
||||
radial_scale: float = 1.0
|
||||
axial_scale: float = 1.0
|
||||
source: str = "default_template_scale"
|
||||
|
||||
|
||||
def scaled_dimensions(scale: HousingScale) -> dict[str, float]:
|
||||
return {
|
||||
"inner_bottom_radius": INNER_BOTTOM_RADIUS * scale.radial_scale,
|
||||
"inner_step_radius": INNER_STEP_RADIUS * scale.radial_scale,
|
||||
"inner_shoulder_radius": INNER_SHOULDER_RADIUS * scale.radial_scale,
|
||||
"inner_wall_radius": INNER_WALL_RADIUS * scale.radial_scale,
|
||||
"bottom_outer_radius": BOTTOM_OUTER_RADIUS * scale.radial_scale,
|
||||
"body_outer_radius": BODY_OUTER_RADIUS * scale.radial_scale,
|
||||
"top_outer_radius": TOP_OUTER_RADIUS * scale.radial_scale,
|
||||
"min_z": BASE_MIN_Z * scale.axial_scale,
|
||||
"max_z": BASE_MAX_Z * scale.axial_scale,
|
||||
"height": BASE_HEIGHT * scale.axial_scale,
|
||||
"radial_scale": scale.radial_scale,
|
||||
"axial_scale": scale.axial_scale,
|
||||
"source": scale.source,
|
||||
}
|
||||
|
||||
|
||||
def scale_from_motor(
|
||||
*,
|
||||
motor_radial_diameter_mm: float,
|
||||
motor_axis_length_mm: float,
|
||||
radial_clearance_mm: float = 14.0,
|
||||
reducer_axial_allowance_mm: float = 42.0,
|
||||
min_radial_scale: float = 0.45,
|
||||
max_radial_scale: float = 1.50,
|
||||
min_axial_scale: float = 0.55,
|
||||
max_axial_scale: float = 1.80,
|
||||
) -> HousingScale:
|
||||
target_inner_radius = motor_radial_diameter_mm / 2.0 + radial_clearance_mm
|
||||
radial_scale = target_inner_radius / INNER_WALL_RADIUS
|
||||
target_height = motor_axis_length_mm + reducer_axial_allowance_mm
|
||||
axial_scale = target_height / BASE_HEIGHT
|
||||
return HousingScale(
|
||||
radial_scale=max(min_radial_scale, min(max_radial_scale, radial_scale)),
|
||||
axial_scale=max(min_axial_scale, min(max_axial_scale, axial_scale)),
|
||||
source="computed_from_motor_bbox",
|
||||
)
|
||||
|
||||
|
||||
def _polar_points(radius: float, start_degrees: float, count: int):
|
||||
return [
|
||||
(
|
||||
radius * math.cos(math.radians(start_degrees + 360.0 * index / count)),
|
||||
radius * math.sin(math.radians(start_degrees + 360.0 * index / count)),
|
||||
)
|
||||
for index in range(count)
|
||||
]
|
||||
|
||||
|
||||
def _radial_point(radius: float, angle_degrees: float, z: float) -> cq.Vector:
|
||||
angle = math.radians(angle_degrees)
|
||||
return cq.Vector(radius * math.cos(angle), radius * math.sin(angle), z)
|
||||
|
||||
|
||||
def _radial_direction(angle_degrees: float) -> cq.Vector:
|
||||
angle = math.radians(angle_degrees)
|
||||
return cq.Vector(math.cos(angle), math.sin(angle), 0.0)
|
||||
|
||||
|
||||
def build_body(scale: HousingScale = HousingScale()):
|
||||
rd = scale.radial_scale
|
||||
zd = scale.axial_scale
|
||||
|
||||
def r(value: float) -> float:
|
||||
return value * rd
|
||||
|
||||
def z(value: float) -> float:
|
||||
return value * zd
|
||||
|
||||
# Exact circular-arc midpoints for the two revolved outer blends.
|
||||
lower_arc_mid = (r(78.8397459621556), z(-8.66025403784439))
|
||||
upper_arc_mid = (r(83.1458565330652), z(71.9211556282713))
|
||||
|
||||
radial_section = (
|
||||
cq.Workplane("XZ")
|
||||
.moveTo(r(INNER_BOTTOM_RADIUS), z(-18.0))
|
||||
.lineTo(r(BOTTOM_OUTER_RADIUS), z(-18.0))
|
||||
.lineTo(r(BOTTOM_OUTER_RADIUS), z(-13.6602540378))
|
||||
.threePointArc(lower_arc_mid, (r(BODY_OUTER_RADIUS), z(-5.0)))
|
||||
.lineTo(r(BODY_OUTER_RADIUS), z(68.3856217223))
|
||||
.threePointArc(upper_arc_mid, (r(TOP_OUTER_RADIUS), z(75.0)))
|
||||
.lineTo(r(TOP_OUTER_RADIUS), z(92.0))
|
||||
.lineTo(r(INNER_WALL_RADIUS), z(92.0))
|
||||
.lineTo(r(INNER_WALL_RADIUS), z(0.0))
|
||||
.lineTo(r(INNER_SHOULDER_RADIUS), z(0.0))
|
||||
.lineTo(r(INNER_SHOULDER_RADIUS), z(-3.0))
|
||||
.lineTo(r(INNER_STEP_RADIUS), z(-3.0))
|
||||
.lineTo(r(INNER_STEP_RADIUS), z(-5.0))
|
||||
.lineTo(r(INNER_BOTTOM_RADIUS), z(-5.0))
|
||||
.close()
|
||||
)
|
||||
return radial_section.revolve(360.0, (0.0, 0.0), (0.0, 1.0))
|
||||
|
||||
|
||||
def build(scale: HousingScale = HousingScale()):
|
||||
rd = scale.radial_scale
|
||||
zd = scale.axial_scale
|
||||
|
||||
def r(value: float) -> float:
|
||||
return value * rd
|
||||
|
||||
def z(value: float) -> float:
|
||||
return value * zd
|
||||
|
||||
part = build_body(scale)
|
||||
|
||||
# Ten Ø5 blind drilled holes, equally spaced on a Ø135 pitch circle.
|
||||
drill_tip_height = z(1.5021515475689)
|
||||
for x, y in _polar_points(r(67.5), 18.0, 10):
|
||||
straight_hole = cq.Solid.makeCylinder(
|
||||
r(2.5), z(10.0), cq.Vector(x, y, z(-18.0)), cq.Vector(0.0, 0.0, 1.0)
|
||||
)
|
||||
drill_tip = cq.Solid.makeCone(
|
||||
r(2.5),
|
||||
0.0,
|
||||
drill_tip_height,
|
||||
cq.Vector(x, y, z(-8.0)),
|
||||
cq.Vector(0.0, 0.0, 1.0),
|
||||
)
|
||||
part = part.cut(straight_hole).cut(drill_tip)
|
||||
|
||||
# Four Ø2.5 blind holes opening at z=0, with 118-degree drill tips.
|
||||
small_tip_height = z(0.75107577378445)
|
||||
for x, y in _polar_points(r(71.875), 0.0, 4):
|
||||
straight_hole = cq.Solid.makeCylinder(
|
||||
r(1.25), z(4.24892422621555), cq.Vector(x, y, z(0.0)), cq.Vector(0.0, 0.0, -1.0)
|
||||
)
|
||||
drill_tip = cq.Solid.makeCone(
|
||||
r(1.25),
|
||||
0.0,
|
||||
small_tip_height,
|
||||
cq.Vector(x, y, z(-4.24892422621555)),
|
||||
cq.Vector(0.0, 0.0, -1.0),
|
||||
)
|
||||
part = part.cut(straight_hole).cut(drill_tip)
|
||||
|
||||
# Eight radial Ø6.6 holes with 90-degree countersinks around the top rim.
|
||||
for angle in range(0, 360, 45):
|
||||
direction = _radial_direction(float(angle))
|
||||
straight_hole = cq.Solid.makeCylinder(
|
||||
r(3.3), r(3.9), _radial_point(r(78.0), float(angle), z(85.1)), direction
|
||||
)
|
||||
countersink = cq.Solid.makeCone(
|
||||
r(3.3),
|
||||
r(6.4),
|
||||
r(3.1),
|
||||
_radial_point(r(81.9), float(angle), z(85.1)),
|
||||
direction,
|
||||
)
|
||||
part = part.cut(straight_hole).cut(countersink)
|
||||
|
||||
return part
|
||||
|
||||
|
||||
def export_scaled(output: Path, scale: HousingScale) -> None:
|
||||
cq.exporters.export(build(scale), str(output))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
output = Path(__file__).resolve().with_name("gear_housing.step")
|
||||
export_scaled(output, HousingScale())
|
||||
print(output)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user