cosplay: Touhou/Houjuu Nue #4
|
@ -55,7 +55,7 @@ class WingProfile(Model):
|
||||||
movement_angle=55,
|
movement_angle=55,
|
||||||
),
|
),
|
||||||
hole_diam=4.0,
|
hole_diam=4.0,
|
||||||
angle_neutral=15.0,
|
angle_neutral=30.0,
|
||||||
actuator=LINEAR_ACTUATOR_50,
|
actuator=LINEAR_ACTUATOR_50,
|
||||||
flexor_offset_angle=0,
|
flexor_offset_angle=0,
|
||||||
flip=False,
|
flip=False,
|
||||||
|
@ -92,7 +92,9 @@ class WingProfile(Model):
|
||||||
elbow_height: float
|
elbow_height: float
|
||||||
wrist_bot_loc: Cq.Location
|
wrist_bot_loc: Cq.Location
|
||||||
wrist_height: float
|
wrist_height: float
|
||||||
elbow_rotate: float = -5.0
|
elbow_rotate: float = 10.0
|
||||||
|
elbow_joint_overlap_median: float = 0.3
|
||||||
|
wrist_joint_overlap_median: float = 0.5
|
||||||
wrist_rotate: float = -30.0
|
wrist_rotate: float = -30.0
|
||||||
# Position of the elbow axle with 0 being bottom and 1 being top (flipped on the left side)
|
# Position of the elbow axle with 0 being bottom and 1 being top (flipped on the left side)
|
||||||
elbow_axle_pos: float = 0.5
|
elbow_axle_pos: float = 0.5
|
||||||
|
@ -428,6 +430,9 @@ class WingProfile(Model):
|
||||||
"""
|
"""
|
||||||
Generates profile from shoulder and above. Subclass should implement
|
Generates profile from shoulder and above. Subclass should implement
|
||||||
"""
|
"""
|
||||||
|
@target(name="profile-s2-bridge", kind=TargetKind.DXF)
|
||||||
|
def profile_s2_bridge(self) -> Optional[Cq.Sketch]:
|
||||||
|
return None
|
||||||
@target(name="profile-s3-extra", kind=TargetKind.DXF)
|
@target(name="profile-s3-extra", kind=TargetKind.DXF)
|
||||||
def profile_s3_extra(self) -> Optional[Cq.Sketch]:
|
def profile_s3_extra(self) -> Optional[Cq.Sketch]:
|
||||||
"""
|
"""
|
||||||
|
@ -460,73 +465,44 @@ class WingProfile(Model):
|
||||||
for p in points
|
for p in points
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
def _child_joint_extension_profile(
|
|
||||||
self,
|
|
||||||
axle_loc: Cq.Location,
|
|
||||||
radius: float,
|
|
||||||
angle_span: float,
|
|
||||||
bot: bool = False) -> Cq.Sketch:
|
|
||||||
"""
|
|
||||||
Creates a sector profile which accomodates extension
|
|
||||||
"""
|
|
||||||
sign = -1 if bot else 1
|
|
||||||
axle_loc = axle_loc * Cq.Location.rot2d(-90 if bot else 90)
|
|
||||||
loc_h = Cq.Location.from2d(radius, 0)
|
|
||||||
start = axle_loc * loc_h
|
|
||||||
mid = axle_loc * Cq.Location.rot2d(-sign * angle_span/2) * loc_h
|
|
||||||
end = axle_loc * Cq.Location.rot2d(-sign * angle_span) * loc_h
|
|
||||||
return (
|
|
||||||
Cq.Sketch()
|
|
||||||
.segment(
|
|
||||||
axle_loc.to2d_pos(),
|
|
||||||
start.to2d_pos(),
|
|
||||||
)
|
|
||||||
.arc(
|
|
||||||
start.to2d_pos(),
|
|
||||||
mid.to2d_pos(),
|
|
||||||
end.to2d_pos(),
|
|
||||||
)
|
|
||||||
.segment(
|
|
||||||
end.to2d_pos(),
|
|
||||||
axle_loc.to2d_pos(),
|
|
||||||
)
|
|
||||||
.assemble()
|
|
||||||
)
|
|
||||||
|
|
||||||
def _parent_joint_extension_profile(
|
def _joint_extension_cut_polygon(
|
||||||
self,
|
self,
|
||||||
loc_axle: Cq.Location,
|
|
||||||
loc_bot: Cq.Location,
|
loc_bot: Cq.Location,
|
||||||
loc_top: Cq.Location,
|
loc_top: Cq.Location,
|
||||||
|
height: float,
|
||||||
angle_span: float,
|
angle_span: float,
|
||||||
bot: bool = True
|
axle_pos: float,
|
||||||
|
bot: bool = True,
|
||||||
|
child: bool = False,
|
||||||
|
overestimate: float = 1.2,
|
||||||
|
median: float = 0.5,
|
||||||
) -> Cq.Sketch:
|
) -> Cq.Sketch:
|
||||||
"""
|
"""
|
||||||
Generates a sector-like profile on the child side of a panel to
|
A cut polygon to accomodate for joint extensions
|
||||||
accomodate for joint rotation
|
|
||||||
"""
|
"""
|
||||||
sign = -1 if bot else 1
|
loc_ext = loc_bot if bot else loc_top
|
||||||
|
|
||||||
loc_tip = loc_top if bot else loc_bot
|
loc_tip = loc_top if bot else loc_bot
|
||||||
loc_arc_right = loc_bot if bot else loc_top
|
theta = math.radians(angle_span * (median if child else 1 - median))
|
||||||
loc_rel_arc_right = loc_axle.inverse * loc_arc_right
|
y_sign = -1 if bot else 1
|
||||||
loc_arc_left = loc_axle * Cq.Location.rot2d(sign * angle_span) * loc_rel_arc_right
|
sign = -1 if child else 1
|
||||||
loc_arc_middle = loc_axle * Cq.Location.rot2d(sign * angle_span / 2) * loc_rel_arc_right
|
dh = axle_pos * height * (overestimate - 1)
|
||||||
|
loc_left = loc_ext * Cq.Location.from2d(0, y_sign * dh)
|
||||||
|
loc_right = loc_left * Cq.Location.from2d(sign * height * overestimate * axle_pos * math.tan(theta), 0)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
Cq.Sketch()
|
Cq.Sketch()
|
||||||
.segment(
|
.segment(
|
||||||
loc_tip.to2d_pos(),
|
loc_tip.to2d_pos(),
|
||||||
loc_arc_right.to2d_pos(),
|
loc_left.to2d_pos(),
|
||||||
)
|
|
||||||
.arc(
|
|
||||||
loc_arc_right.to2d_pos(),
|
|
||||||
loc_arc_middle.to2d_pos(),
|
|
||||||
loc_arc_left.to2d_pos(),
|
|
||||||
)
|
)
|
||||||
.segment(
|
.segment(
|
||||||
|
loc_left.to2d_pos(),
|
||||||
|
loc_right.to2d_pos(),
|
||||||
|
)
|
||||||
|
.segment(
|
||||||
|
loc_right.to2d_pos(),
|
||||||
loc_tip.to2d_pos(),
|
loc_tip.to2d_pos(),
|
||||||
loc_arc_left.to2d_pos(),
|
|
||||||
)
|
)
|
||||||
.assemble()
|
.assemble()
|
||||||
)
|
)
|
||||||
|
@ -597,10 +573,22 @@ class WingProfile(Model):
|
||||||
|
|
||||||
@target(name="profile-s1", kind=TargetKind.DXF)
|
@target(name="profile-s1", kind=TargetKind.DXF)
|
||||||
def profile_s1(self) -> Cq.Sketch:
|
def profile_s1(self) -> Cq.Sketch:
|
||||||
|
cut_poly = self._joint_extension_cut_polygon(
|
||||||
|
loc_bot=self.elbow_bot_loc,
|
||||||
|
loc_top=self.elbow_top_loc,
|
||||||
|
height=self.elbow_height,
|
||||||
|
angle_span=self.elbow_joint.motion_span,
|
||||||
|
axle_pos=self.elbow_axle_pos,
|
||||||
|
bot=not self.elbow_joint.flip,
|
||||||
|
median=self.elbow_joint_overlap_median,
|
||||||
|
child=False,
|
||||||
|
).reset().polygon(self._mask_elbow(), mode='a')
|
||||||
profile = (
|
profile = (
|
||||||
self.profile()
|
self.profile()
|
||||||
.reset()
|
.reset()
|
||||||
.polygon(self._mask_elbow(), mode='i')
|
.push([self.elbow_axle_loc.to2d_pos()])
|
||||||
|
.each(lambda _: cut_poly, mode='i')
|
||||||
|
#.polygon(self._mask_elbow(), mode='i')
|
||||||
)
|
)
|
||||||
return profile
|
return profile
|
||||||
def surface_s1(self, front: bool = True) -> Cq.Workplane:
|
def surface_s1(self, front: bool = True) -> Cq.Workplane:
|
||||||
|
@ -686,21 +674,47 @@ class WingProfile(Model):
|
||||||
|
|
||||||
@target(name="profile-s2", kind=TargetKind.DXF)
|
@target(name="profile-s2", kind=TargetKind.DXF)
|
||||||
def profile_s2(self) -> Cq.Sketch:
|
def profile_s2(self) -> Cq.Sketch:
|
||||||
|
# Calculates `(profile - (E - JE)) * (W + JW)`
|
||||||
|
cut_elbow = (
|
||||||
|
Cq.Sketch()
|
||||||
|
.polygon(self._mask_elbow())
|
||||||
|
.reset()
|
||||||
|
.boolean(self._joint_extension_cut_polygon(
|
||||||
|
loc_bot=self.elbow_bot_loc,
|
||||||
|
loc_top=self.elbow_top_loc,
|
||||||
|
height=self.elbow_height,
|
||||||
|
angle_span=self.elbow_joint.motion_span,
|
||||||
|
axle_pos=self.elbow_axle_pos,
|
||||||
|
bot=not self.elbow_joint.flip,
|
||||||
|
median=self.elbow_joint_overlap_median,
|
||||||
|
child=True,
|
||||||
|
), mode='s')
|
||||||
|
)
|
||||||
|
cut_wrist = (
|
||||||
|
Cq.Sketch()
|
||||||
|
.polygon(self._mask_wrist())
|
||||||
|
)
|
||||||
|
if self.flip:
|
||||||
|
poly = self._joint_extension_cut_polygon(
|
||||||
|
loc_bot=self.wrist_bot_loc,
|
||||||
|
loc_top=self.wrist_top_loc,
|
||||||
|
height=self.wrist_height,
|
||||||
|
angle_span=self.wrist_joint.motion_span,
|
||||||
|
axle_pos=self.wrist_axle_pos,
|
||||||
|
bot=not self.wrist_joint.flip,
|
||||||
|
median=self.wrist_joint_overlap_median,
|
||||||
|
child=False,
|
||||||
|
)
|
||||||
|
cut_wrist = (
|
||||||
|
cut_wrist
|
||||||
|
.reset()
|
||||||
|
.boolean(poly, mode='a')
|
||||||
|
)
|
||||||
profile = (
|
profile = (
|
||||||
self.profile()
|
self.profile()
|
||||||
.reset()
|
.reset()
|
||||||
.polygon(self._mask_elbow(), mode='s')
|
.boolean(cut_elbow, mode='s')
|
||||||
.reset()
|
.boolean(cut_wrist, mode='i')
|
||||||
.polygon(self._mask_wrist(), mode='i')
|
|
||||||
.reset()
|
|
||||||
.push([self.elbow_axle_loc])
|
|
||||||
.each(lambda loc: self._parent_joint_extension_profile(
|
|
||||||
loc,
|
|
||||||
self.elbow_bot_loc,
|
|
||||||
self.elbow_top_loc,
|
|
||||||
self.elbow_joint.motion_span,
|
|
||||||
bot=not self.flip,
|
|
||||||
), mode='a')
|
|
||||||
)
|
)
|
||||||
return profile
|
return profile
|
||||||
def surface_s2(self, front: bool = True) -> Cq.Workplane:
|
def surface_s2(self, front: bool = True) -> Cq.Workplane:
|
||||||
|
@ -724,33 +738,10 @@ class WingProfile(Model):
|
||||||
profile = self.profile_s2()
|
profile = self.profile_s2()
|
||||||
tags = tags_elbow + tags_wrist
|
tags = tags_elbow + tags_wrist
|
||||||
return extrude_with_markers(profile, self.panel_thickness, tags, reverse=front)
|
return extrude_with_markers(profile, self.panel_thickness, tags, reverse=front)
|
||||||
@target(name="profile-s2-bridge", kind=TargetKind.DXF)
|
def surface_s2_bridge(self, front: bool = True) -> Optional[Cq.Workplane]:
|
||||||
def profile_s2_bridge(self) -> Cq.Workplane:
|
|
||||||
# FIXME: Leave some margin here so we can glue the panels
|
|
||||||
|
|
||||||
# Generates the extension profile, which is required on both sides
|
|
||||||
profile = self._child_joint_extension_profile(
|
|
||||||
axle_loc=self.wrist_axle_loc,
|
|
||||||
radius=self.wrist_height * (0.5 if self.flip else 1),
|
|
||||||
angle_span=self.wrist_joint.motion_span,
|
|
||||||
bot=self.flip,
|
|
||||||
)
|
|
||||||
# Generates the contraction (cut) profile. only required on the left
|
|
||||||
if self.flip:
|
|
||||||
extra = (
|
|
||||||
self.profile()
|
|
||||||
.reset()
|
|
||||||
.push([self.wrist_axle_loc])
|
|
||||||
.each(self._wrist_joint_retract_cut_polygon, mode='i')
|
|
||||||
)
|
|
||||||
profile = (
|
|
||||||
profile
|
|
||||||
.push([self.wrist_axle_loc])
|
|
||||||
.each(lambda _: extra, mode='a')
|
|
||||||
)
|
|
||||||
return profile
|
|
||||||
def surface_s2_bridge(self, front: bool = True) -> Cq.Workplane:
|
|
||||||
profile = self.profile_s2_bridge()
|
profile = self.profile_s2_bridge()
|
||||||
|
if profile is None:
|
||||||
|
return None
|
||||||
loc_wrist = Cq.Location.rot2d(self.wrist_rotate) * self.wrist_joint.parent_arm_loc()
|
loc_wrist = Cq.Location.rot2d(self.wrist_rotate) * self.wrist_joint.parent_arm_loc()
|
||||||
tags = [
|
tags = [
|
||||||
("wrist_bot", self.wrist_axle_loc * loc_wrist *
|
("wrist_bot", self.wrist_axle_loc * loc_wrist *
|
||||||
|
@ -796,15 +787,25 @@ class WingProfile(Model):
|
||||||
material=self.mat_panel, role=self.role_panel)
|
material=self.mat_panel, role=self.role_panel)
|
||||||
.constrain("front@faces@>Z", "back@faces@<Z", "Point",
|
.constrain("front@faces@>Z", "back@faces@<Z", "Point",
|
||||||
param=self.s1_thickness)
|
param=self.s1_thickness)
|
||||||
.addS(self.surface_s2_bridge(front=True), name="bridge_front",
|
|
||||||
material=self.mat_panel, role=self.role_panel)
|
|
||||||
.constrain("front?wrist_bot", "bridge_front?wrist_bot", "Plane")
|
|
||||||
.constrain("front?wrist_top", "bridge_front?wrist_top", "Plane")
|
|
||||||
.addS(self.surface_s2_bridge(front=False), name="bridge_back",
|
|
||||||
material=self.mat_panel, role=self.role_panel)
|
|
||||||
.constrain("back?wrist_bot", "bridge_back?wrist_bot", "Plane")
|
|
||||||
.constrain("back?wrist_top", "bridge_back?wrist_top", "Plane")
|
|
||||||
)
|
)
|
||||||
|
bridge_front = self.surface_s2_bridge(front=True)
|
||||||
|
bridge_back = self.surface_s2_bridge(front=False)
|
||||||
|
if bridge_front:
|
||||||
|
(
|
||||||
|
result
|
||||||
|
.addS(bridge_front, name="bridge_front",
|
||||||
|
material=self.mat_panel, role=self.role_panel)
|
||||||
|
.constrain("front?wrist_bot", "bridge_front?wrist_bot", "Plane")
|
||||||
|
.constrain("front?wrist_top", "bridge_front?wrist_top", "Plane")
|
||||||
|
)
|
||||||
|
if bridge_back:
|
||||||
|
(
|
||||||
|
result
|
||||||
|
.addS(bridge_back, name="bridge_back",
|
||||||
|
material=self.mat_panel, role=self.role_panel)
|
||||||
|
.constrain("back?wrist_bot", "bridge_back?wrist_bot", "Plane")
|
||||||
|
.constrain("back?wrist_top", "bridge_back?wrist_top", "Plane")
|
||||||
|
)
|
||||||
for o, t in [
|
for o, t in [
|
||||||
(self.spacer_s2_elbow(), "elbow_bot"),
|
(self.spacer_s2_elbow(), "elbow_bot"),
|
||||||
(self.spacer_s2_elbow(), "elbow_top"),
|
(self.spacer_s2_elbow(), "elbow_top"),
|
||||||
|
@ -825,10 +826,28 @@ class WingProfile(Model):
|
||||||
|
|
||||||
@target(name="profile-s3", kind=TargetKind.DXF)
|
@target(name="profile-s3", kind=TargetKind.DXF)
|
||||||
def profile_s3(self) -> Cq.Sketch:
|
def profile_s3(self) -> Cq.Sketch:
|
||||||
|
cut_wrist = (
|
||||||
|
Cq.Sketch()
|
||||||
|
.polygon(self._mask_wrist())
|
||||||
|
)
|
||||||
|
if self.flip:
|
||||||
|
poly = self._joint_extension_cut_polygon(
|
||||||
|
loc_bot=self.wrist_bot_loc,
|
||||||
|
loc_top=self.wrist_top_loc,
|
||||||
|
height=self.wrist_height,
|
||||||
|
angle_span=self.wrist_joint.motion_span,
|
||||||
|
axle_pos=self.wrist_axle_pos,
|
||||||
|
bot=not self.wrist_joint.flip,
|
||||||
|
median=self.wrist_joint_overlap_median,
|
||||||
|
child=True,
|
||||||
|
)
|
||||||
|
cut_wrist = (
|
||||||
|
cut_wrist
|
||||||
|
.boolean(poly, mode='s')
|
||||||
|
)
|
||||||
profile = (
|
profile = (
|
||||||
self.profile()
|
self.profile()
|
||||||
.reset()
|
.boolean(cut_wrist, mode='s')
|
||||||
.polygon(self._mask_wrist(), mode='s')
|
|
||||||
)
|
)
|
||||||
return profile
|
return profile
|
||||||
def surface_s3(self,
|
def surface_s3(self,
|
||||||
|
@ -1029,7 +1048,6 @@ class WingR(WingProfile):
|
||||||
|
|
||||||
# Underapproximate the wrist tangent angle to leave no gaps on the blade
|
# Underapproximate the wrist tangent angle to leave no gaps on the blade
|
||||||
blade_wrist_approx_tangent_angle: float = 40.0
|
blade_wrist_approx_tangent_angle: float = 40.0
|
||||||
blade_overlap_arrow_height: float = 5.0
|
|
||||||
# Some overlap needed to glue the two sides
|
# Some overlap needed to glue the two sides
|
||||||
blade_overlap_angle: float = -1
|
blade_overlap_angle: float = -1
|
||||||
blade_hole_angle: float = 3
|
blade_hole_angle: float = 3
|
||||||
|
@ -1116,6 +1134,74 @@ class WingR(WingProfile):
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def _child_joint_extension_profile(
|
||||||
|
self,
|
||||||
|
axle_loc: Cq.Location,
|
||||||
|
radius: float,
|
||||||
|
angle_span: float,
|
||||||
|
bot: bool = False) -> Cq.Sketch:
|
||||||
|
"""
|
||||||
|
Creates a sector profile which accomodates extension
|
||||||
|
"""
|
||||||
|
# leave some margin for gluing
|
||||||
|
margin = 5
|
||||||
|
sign = -1 if bot else 1
|
||||||
|
axle_loc = axle_loc * Cq.Location.rot2d(-90 if bot else 90)
|
||||||
|
loc_h = Cq.Location.from2d(radius, 0)
|
||||||
|
loc_offset = axle_loc * Cq.Location.from2d(0, margin)
|
||||||
|
start = axle_loc * loc_h
|
||||||
|
mid = axle_loc * Cq.Location.rot2d(-sign * angle_span/2) * loc_h
|
||||||
|
end = axle_loc * Cq.Location.rot2d(-sign * angle_span) * loc_h
|
||||||
|
return (
|
||||||
|
Cq.Sketch()
|
||||||
|
.segment(
|
||||||
|
loc_offset.to2d_pos(),
|
||||||
|
start.to2d_pos(),
|
||||||
|
)
|
||||||
|
.arc(
|
||||||
|
start.to2d_pos(),
|
||||||
|
mid.to2d_pos(),
|
||||||
|
end.to2d_pos(),
|
||||||
|
)
|
||||||
|
.segment(
|
||||||
|
end.to2d_pos(),
|
||||||
|
axle_loc.to2d_pos(),
|
||||||
|
)
|
||||||
|
.segment(
|
||||||
|
axle_loc.to2d_pos(),
|
||||||
|
loc_offset.to2d_pos(),
|
||||||
|
)
|
||||||
|
.assemble()
|
||||||
|
)
|
||||||
|
|
||||||
|
@target(name="profile-s2-bridge", kind=TargetKind.DXF)
|
||||||
|
def profile_s2_bridge(self) -> Cq.Sketch:
|
||||||
|
"""
|
||||||
|
This extension profile is required to accomodate the awkward shaped
|
||||||
|
joint next to the scythe
|
||||||
|
"""
|
||||||
|
# Generates the extension profile, which is required on both sides
|
||||||
|
profile = self._child_joint_extension_profile(
|
||||||
|
axle_loc=self.wrist_axle_loc,
|
||||||
|
radius=self.wrist_height,
|
||||||
|
angle_span=self.wrist_joint.motion_span,
|
||||||
|
bot=self.flip,
|
||||||
|
)
|
||||||
|
# Generates the contraction (cut) profile. only required on the left
|
||||||
|
if self.flip:
|
||||||
|
extra = (
|
||||||
|
self.profile()
|
||||||
|
.reset()
|
||||||
|
.push([self.wrist_axle_loc])
|
||||||
|
.each(self._wrist_joint_retract_cut_polygon, mode='i')
|
||||||
|
)
|
||||||
|
profile = (
|
||||||
|
profile
|
||||||
|
.push([self.wrist_axle_loc])
|
||||||
|
.each(lambda _: extra, mode='a')
|
||||||
|
)
|
||||||
|
return profile
|
||||||
|
|
||||||
def profile_s3_extra(self) -> Cq.Sketch:
|
def profile_s3_extra(self) -> Cq.Sketch:
|
||||||
"""
|
"""
|
||||||
Implements the blade part on Nue's wing
|
Implements the blade part on Nue's wing
|
||||||
|
@ -1123,7 +1209,7 @@ class WingR(WingProfile):
|
||||||
left_bot_loc = self.arrow_bot_loc * Cq.Location.rot2d(-1)
|
left_bot_loc = self.arrow_bot_loc * Cq.Location.rot2d(-1)
|
||||||
hole_bot_loc = self.arrow_bot_loc * Cq.Location.rot2d(self.blade_hole_angle)
|
hole_bot_loc = self.arrow_bot_loc * Cq.Location.rot2d(self.blade_hole_angle)
|
||||||
right_bot_loc = self.arrow_bot_loc * Cq.Location.rot2d(self.blade_angle)
|
right_bot_loc = self.arrow_bot_loc * Cq.Location.rot2d(self.blade_angle)
|
||||||
h_loc = Cq.Location.from2d(0, self.arrow_height + self.blade_overlap_arrow_height)
|
h_loc = Cq.Location.from2d(0, self.arrow_height)
|
||||||
|
|
||||||
# Law of sines, uses the triangle of (wrist_bot_loc, arrow_bot_loc, ?)
|
# Law of sines, uses the triangle of (wrist_bot_loc, arrow_bot_loc, ?)
|
||||||
theta_wp = math.radians(90 - self.blade_wrist_approx_tangent_angle)
|
theta_wp = math.radians(90 - self.blade_wrist_approx_tangent_angle)
|
||||||
|
@ -1202,6 +1288,9 @@ class WingL(WingProfile):
|
||||||
elbow_axle_pos: float = 0.4
|
elbow_axle_pos: float = 0.4
|
||||||
wrist_axle_pos: float = 0.5
|
wrist_axle_pos: float = 0.5
|
||||||
|
|
||||||
|
elbow_joint_overlap_median: float = 0.5
|
||||||
|
wrist_joint_overlap_median: float = 0.5
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
assert self.wrist_height <= self.shoulder_joint.height
|
assert self.wrist_height <= self.shoulder_joint.height
|
||||||
self.wrist_bot_loc = self.wrist_bot_loc.with_angle_2d(self.wrist_angle)
|
self.wrist_bot_loc = self.wrist_bot_loc.with_angle_2d(self.wrist_angle)
|
||||||
|
|
|
@ -100,6 +100,15 @@ def flip_y(self: Cq.Location) -> Cq.Location:
|
||||||
return Cq.Location.from2d(x, -y, -a)
|
return Cq.Location.from2d(x, -y, -a)
|
||||||
Cq.Location.flip_y = flip_y
|
Cq.Location.flip_y = flip_y
|
||||||
|
|
||||||
|
def boolean(self: Cq.Sketch, obj, **kwargs) -> Cq.Sketch:
|
||||||
|
return (
|
||||||
|
self
|
||||||
|
.reset()
|
||||||
|
.push([(0, 0)])
|
||||||
|
.each(lambda _: obj, **kwargs)
|
||||||
|
)
|
||||||
|
Cq.Sketch.boolean = boolean
|
||||||
|
|
||||||
### Tags
|
### Tags
|
||||||
|
|
||||||
def tagPoint(self, tag: str):
|
def tagPoint(self, tag: str):
|
||||||
|
|
Loading…
Reference in New Issue