cosplay: Touhou/Houjuu Nue #4

Open
aniva wants to merge 189 commits from touhou/houjuu-nue into main
3 changed files with 72 additions and 68 deletions
Showing only changes of commit 6d72749c9b - Show all commits

36
nhf/parts/planar.py Normal file
View File

@ -0,0 +1,36 @@
"""
Operations on planar geometry (usually used for laser cutting parts)
"""
import math
from typing import Tuple
import cadquery as Cq
def extrude_with_markers(
sketch: Cq.Sketch,
thickness: float,
tags: list[Tuple[str, Cq.Location]],
reverse: bool = False):
"""
Extrudes a sketch and place tags on the sketch for mating.
Each tag is of the format `(name, loc)`, where the (must be 2d) location's
angle is specifies in degrees counterclockwise from +X. Two marks are
generated for each `name`, "{name}" for the location (with normal) and
"{name}_dir" for the directrix specified by the angle.
This simulates a process of laser cutting and bonding (for wood and acrylic)
"""
result = (
Cq.Workplane('XY')
.placeSketch(sketch)
.extrude(thickness)
)
plane = result.faces("<Z" if reverse else ">Z").workplane()
sign = -1 if reverse else 1
for tag, p in tags:
(x, y), angle = p.to2d()
theta = sign * math.radians(angle)
direction = (math.cos(theta), math.sin(theta), 0)
plane.moveTo(x, sign * y).tagPlane(tag)
plane.moveTo(x, sign * y).tagPlane(f"{tag}_dir", direction)
return result

View File

@ -12,6 +12,7 @@ from nhf.build import Model, TargetKind, target, assembly, submodel
from nhf.parts.box import box_with_centre_holes, MountingBox, Hole from nhf.parts.box import box_with_centre_holes, MountingBox, Hole
from nhf.parts.joints import HirthJoint from nhf.parts.joints import HirthJoint
from nhf.touhou.houjuu_nue.joints import ShoulderJoint, ElbowJoint, DiskJoint from nhf.touhou.houjuu_nue.joints import ShoulderJoint, ElbowJoint, DiskJoint
from nhf.parts.planar import extrude_with_markers
import nhf.utils import nhf.utils
@dataclass(kw_only=True) @dataclass(kw_only=True)
@ -300,13 +301,13 @@ class WingProfile(Model):
c, s = math.cos(-theta), math.sin(-theta) c, s = math.cos(-theta), math.sin(-theta)
tags = [ tags = [
# transforms [axle_dist, -sw/2] about the centre (tip_x, tip_y - sw/2) # transforms [axle_dist, -sw/2] about the centre (tip_x, tip_y - sw/2)
("shoulder", ( ("shoulder", Cq.Location.from2d(
self.shoulder_tip_x + axle_dist * c + (-sw/2) * s, self.shoulder_tip_x + axle_dist * c + (-sw/2) * s,
self.shoulder_tip_y - sw / 2 - axle_dist * s + (-sw/2) * c), self.shoulder_tip_y - sw / 2 - axle_dist * s + (-sw/2) * c,
self.shoulder_joint.angle_neutral), self.shoulder_joint.angle_neutral)),
("base", (base_dx, base_dy), 90), ("base", Cq.Location.from2d(base_dx, base_dy, 90)),
] ]
result = nhf.utils.extrude_with_markers( result = extrude_with_markers(
self.profile_s0(), self.profile_s0(),
self.panel_thickness, self.panel_thickness,
tags, tags,
@ -453,21 +454,21 @@ class WingProfile(Model):
shoulder_h = self.shoulder_joint.child_height shoulder_h = self.shoulder_joint.child_height
h = (self.shoulder_joint.height - shoulder_h) / 2 h = (self.shoulder_joint.height - shoulder_h) / 2
tags_shoulder = [ tags_shoulder = [
("shoulder_bot", (0, h), 90), ("shoulder_bot", Cq.Location.from2d(0, h, 90)),
("shoulder_top", (0, h + shoulder_h), 270), ("shoulder_top", Cq.Location.from2d(0, h + shoulder_h, 270)),
] ]
h = self.elbow_height / 2 h = self.elbow_height / 2
tags_elbow = [ tags_elbow = [
("elbow_bot", ("elbow_bot", Cq.Location.from2d(
self.elbow_to_abs(-self.elbow_joint.parent_arm_radius, h - self.elbow_h2), *self.elbow_to_abs(-self.elbow_joint.parent_arm_radius, h - self.elbow_h2),
self.elbow_angle + 0), self.elbow_angle + 0)),
("elbow_top", ("elbow_top", Cq.Location.from2d(
self.elbow_to_abs(-self.elbow_joint.parent_arm_radius, h + self.elbow_h2), *self.elbow_to_abs(-self.elbow_joint.parent_arm_radius, h + self.elbow_h2),
self.elbow_angle + 0), self.elbow_angle + 0)),
] ]
profile = self.profile_s1() profile = self.profile_s1()
tags = tags_shoulder + tags_elbow tags = tags_shoulder + tags_elbow
return nhf.utils.extrude_with_markers( return extrude_with_markers(
profile, self.panel_thickness, tags, reverse=front) profile, self.panel_thickness, tags, reverse=front)
@submodel(name="spacer-s1-shoulder") @submodel(name="spacer-s1-shoulder")
def spacer_s1_shoulder(self) -> MountingBox: def spacer_s1_shoulder(self) -> MountingBox:
@ -526,25 +527,25 @@ class WingProfile(Model):
def surface_s2(self, front: bool = True) -> Cq.Workplane: def surface_s2(self, front: bool = True) -> Cq.Workplane:
h = self.elbow_height / 2 h = self.elbow_height / 2
tags_elbow = [ tags_elbow = [
("elbow_bot", ("elbow_bot", Cq.Location.from2d(
self.elbow_to_abs(self.elbow_joint.child_arm_radius, h - self.elbow_h2), *self.elbow_to_abs(self.elbow_joint.child_arm_radius, h - self.elbow_h2),
self.elbow_angle + 180), self.elbow_angle + 180)),
("elbow_top", ("elbow_top", Cq.Location.from2d(
self.elbow_to_abs(self.elbow_joint.child_arm_radius, h + self.elbow_h2), *self.elbow_to_abs(self.elbow_joint.child_arm_radius, h + self.elbow_h2),
self.elbow_angle + 180), self.elbow_angle + 180)),
] ]
h = self.wrist_height / 2 h = self.wrist_height / 2
tags_wrist = [ tags_wrist = [
("wrist_bot", ("wrist_bot", Cq.Location.from2d(
self.wrist_to_abs(-self.wrist_joint.parent_arm_radius, h - self.wrist_h2), *self.wrist_to_abs(-self.wrist_joint.parent_arm_radius, h - self.wrist_h2),
self.wrist_angle), self.wrist_angle)),
("wrist_top", ("wrist_top", Cq.Location.from2d(
self.wrist_to_abs(-self.wrist_joint.parent_arm_radius, h + self.wrist_h2), *self.wrist_to_abs(-self.wrist_joint.parent_arm_radius, h + self.wrist_h2),
self.wrist_angle), self.wrist_angle)),
] ]
profile = self.profile_s2() profile = self.profile_s2()
tags = tags_elbow + tags_wrist tags = tags_elbow + tags_wrist
return nhf.utils.extrude_with_markers(profile, self.panel_thickness, tags, reverse=front) return extrude_with_markers(profile, self.panel_thickness, tags, reverse=front)
@submodel(name="spacer-s2-elbow") @submodel(name="spacer-s2-elbow")
def spacer_s2_elbow(self) -> MountingBox: def spacer_s2_elbow(self) -> MountingBox:
return self.spacer_of_joint( return self.spacer_of_joint(
@ -596,15 +597,15 @@ class WingProfile(Model):
front: bool = True) -> Cq.Workplane: front: bool = True) -> Cq.Workplane:
h = self.wrist_height / 2 h = self.wrist_height / 2
tags = [ tags = [
("wrist_bot", ("wrist_bot", Cq.Location.from2d(
self.wrist_to_abs(self.wrist_joint.child_arm_radius, h - self.wrist_h2), *self.wrist_to_abs(self.wrist_joint.child_arm_radius, h - self.wrist_h2),
self.wrist_angle + 180), self.wrist_angle + 180)),
("wrist_top", ("wrist_top", Cq.Location.from2d(
self.wrist_to_abs(self.wrist_joint.child_arm_radius, h + self.wrist_h2), *self.wrist_to_abs(self.wrist_joint.child_arm_radius, h + self.wrist_h2),
self.wrist_angle + 180), self.wrist_angle + 180)),
] ]
profile = self.profile_s3() profile = self.profile_s3()
return nhf.utils.extrude_with_markers(profile, self.panel_thickness, tags, reverse=front) return extrude_with_markers(profile, self.panel_thickness, tags, reverse=front)
@submodel(name="spacer-s3-wrist") @submodel(name="spacer-s3-wrist")
def spacer_s3_wrist(self) -> MountingBox: def spacer_s3_wrist(self) -> MountingBox:
return self.spacer_of_joint( return self.spacer_of_joint(

View File

@ -1,9 +1,5 @@
""" """
Marking utilities for `Cq.Workplane` Utility functions for cadquery objects
Adds the functions to `Cq.Workplane`:
1. `tagPoint`
2. `tagPlane`
""" """
import math import math
import functools import functools
@ -170,32 +166,3 @@ def get_abs_location(self: Cq.Assembly,
return loc return loc
Cq.Assembly.get_abs_location = get_abs_location Cq.Assembly.get_abs_location = get_abs_location
def extrude_with_markers(sketch: Cq.Sketch,
thickness: float,
tags: list[Tuple[str, Tuple[float, float], float]],
reverse: bool = False):
"""
Extrudes a sketch and place tags on the sketch for mating.
Each tag is of the format `(name, (x, y), angle)`, where the angle is
specifies in degrees counterclockwise from +X. Two marks are generated for
each `name`, "{name}" for the location (with normal) and "{name}_dir" for
the directrix specified by the angle.
This simulates a process of laser cutting and bonding (for wood and acrylic)
"""
result = (
Cq.Workplane('XY')
.placeSketch(sketch)
.extrude(thickness)
)
plane = result.faces("<Z" if reverse else ">Z").workplane()
sign = -1 if reverse else 1
for tag, (px, py), angle in tags:
theta = sign * math.radians(angle)
direction = (math.cos(theta), math.sin(theta), 0)
plane.moveTo(px, sign * py).tagPlane(tag)
plane.moveTo(px, sign * py).tagPlane(f"{tag}_dir", direction)
return result