cosplay: Touhou/Houjuu Nue #4

Open
aniva wants to merge 189 commits from touhou/houjuu-nue into main
4 changed files with 78 additions and 29 deletions
Showing only changes of commit 77cc69acfb - Show all commits

View File

@ -605,12 +605,12 @@ class ElbowJoint(Model):
material: Material = Material.RESIN_TRANSPERENT material: Material = Material.RESIN_TRANSPERENT
angle_neutral: float = 20.0 angle_neutral: float = 0.0
def __post_init__(self): def __post_init__(self):
assert self.child_arm_radius > self.disk_joint.radius_housing assert self.child_arm_radius > self.disk_joint.radius_housing
assert self.parent_arm_radius > self.disk_joint.radius_housing assert self.parent_arm_radius > self.disk_joint.radius_housing
self.disk_joint.tongue_length = self.child_arm_radius - self.disk_joint.radius_disk self.disk_joint.tongue_length = self.child_arm_radius - self.disk_joint.radius_disk - self.lip_thickness / 2
assert self.disk_joint.movement_angle < self.parent_arm_angle < 360 - self.parent_arm_span assert self.disk_joint.movement_angle < self.parent_arm_angle < 360 - self.parent_arm_span
def lip(self) -> Cq.Workplane: def lip(self) -> Cq.Workplane:
@ -640,7 +640,7 @@ class ElbowJoint(Model):
# We need to ensure the disk is on the "other" side so # We need to ensure the disk is on the "other" side so
flip_x = Cq.Location((0, 0, 0), (1, 0, 0), 180) flip_x = Cq.Location((0, 0, 0), (1, 0, 0), 180)
flip_z = Cq.Location((0, 0, 0), (0, 0, 1), 180) flip_z = Cq.Location((0, 0, 0), (0, 0, 1), 180)
lip_dz = self.lip_thickness / 2 lip_dz = self.lip_thickness
result = ( result = (
Cq.Assembly() Cq.Assembly()
.add(self.lip(), name="lip", loc= .add(self.lip(), name="lip", loc=
@ -664,7 +664,7 @@ class ElbowJoint(Model):
connector = ( connector = (
Cq.Solid.makeCylinder( Cq.Solid.makeCylinder(
height=conn_h, height=conn_h,
radius=self.parent_arm_radius, radius=self.parent_arm_radius - self.lip_thickness / 2,
angleDegrees=self.parent_arm_span) angleDegrees=self.parent_arm_span)
.cut(Cq.Solid.makeCylinder( .cut(Cq.Solid.makeCylinder(
height=conn_h, height=conn_h,
@ -679,7 +679,7 @@ class ElbowJoint(Model):
(0, 0, 1), (0, 0, 1),
-self.disk_joint.tongue_span / 2 + self.angle_neutral -self.disk_joint.tongue_span / 2 + self.angle_neutral
) )
lip_dz = self.lip_thickness / 2 lip_dz = self.lip_thickness
result = ( result = (
Cq.Assembly() Cq.Assembly()
.add(self.lip(), name="lip", loc= .add(self.lip(), name="lip", loc=

View File

@ -10,6 +10,23 @@ class TestJoints(unittest.TestCase):
j = MJ.ShoulderJoint() j = MJ.ShoulderJoint()
assembly = j.assembly() assembly = j.assembly()
self.assertEqual(pairwise_intersection(assembly), []) self.assertEqual(pairwise_intersection(assembly), [])
def test_shoulder_joint_dist(self):
"""
Tests the arm radius
"""
j = MJ.ShoulderJoint()
for deflection in [0, 40, 95, 120]:
with self.subTest(deflection=deflection):
a = j.assembly(deflection=deflection)
# Axle
o = a.get_abs_location("parent_top/track?spring")
l_c1 = a.get_abs_location("parent_top/lip?conn0")
l_c2= a.get_abs_location("parent_top/lip?conn1")
v_c = 0.5 * ((l_c1 - o) + (l_c2 - o))
v_c.z = 0
self.assertAlmostEqual(v_c.Length, j.parent_lip_ext)
def test_disk_collision_0(self): def test_disk_collision_0(self):
j = MJ.DiskJoint() j = MJ.DiskJoint()
assembly = j.assembly(angle=0) assembly = j.assembly(angle=0)
@ -23,6 +40,28 @@ class TestJoints(unittest.TestCase):
assembly = j.assembly(angle=j.movement_angle) assembly = j.assembly(angle=j.movement_angle)
self.assertEqual(pairwise_intersection(assembly), []) self.assertEqual(pairwise_intersection(assembly), [])
def test_elbow_joint_dist(self):
"""
Tests the arm radius
"""
j = MJ.ElbowJoint()
for angle in [0, 10, 20, j.disk_joint.movement_angle]:
with self.subTest(angle=angle):
a = j.assembly(angle=angle)
o = a.get_abs_location("child/disk?mate_bot")
l_c1 = a.get_abs_location("child/lip?conn_top0")
l_c2 = a.get_abs_location("child/lip?conn_bot0")
v_c = 0.5 * ((l_c1 - o) + (l_c2 - o))
v_c.z = 0
self.assertAlmostEqual(v_c.Length, j.child_arm_radius)
l_p1 = a.get_abs_location("parent_upper/lip?conn_top0")
l_p2 = a.get_abs_location("parent_upper/lip?conn_bot0")
v_p = 0.5 * ((l_p1 - o) + (l_p2 - o))
v_p.z = 0
self.assertAlmostEqual(v_p.Length, j.parent_arm_radius)
class Test(unittest.TestCase): class Test(unittest.TestCase):
def test_hs_joint_parent(self): def test_hs_joint_parent(self):

View File

@ -445,23 +445,20 @@ class WingProfile(Model):
.polygon(self._mask_elbow(), mode='i') .polygon(self._mask_elbow(), mode='i')
) )
return profile return profile
def surface_s1(self, def surface_s1(self, front: bool = True) -> Cq.Workplane:
shoulder_mount_inset: float = 0,
elbow_mount_inset: float = 0,
front: bool = True) -> Cq.Workplane:
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", (shoulder_mount_inset, h), 90), ("shoulder_bot", (0, h), 90),
("shoulder_top", (shoulder_mount_inset, h + shoulder_h), 270), ("shoulder_top", (0, h + shoulder_h), 270),
] ]
h = self.elbow_height / 2 h = self.elbow_height / 2
tags_elbow = [ tags_elbow = [
("elbow_bot", ("elbow_bot",
self.elbow_to_abs(-elbow_mount_inset, 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",
self.elbow_to_abs(-elbow_mount_inset, 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()
@ -522,26 +519,23 @@ class WingProfile(Model):
.polygon(self._mask_wrist(), mode='i') .polygon(self._mask_wrist(), mode='i')
) )
return profile return profile
def surface_s2(self, def surface_s2(self, front: bool = True) -> Cq.Workplane:
elbow_mount_inset: float = 0,
wrist_mount_inset: float = 0,
front: bool = True) -> Cq.Workplane:
h = self.elbow_height / 2 h = self.elbow_height / 2
tags_elbow = [ tags_elbow = [
("elbow_bot", ("elbow_bot",
self.elbow_to_abs(elbow_mount_inset, 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",
self.elbow_to_abs(elbow_mount_inset, 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",
self.wrist_to_abs(-wrist_mount_inset, 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",
self.wrist_to_abs(-wrist_mount_inset, 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()
@ -596,14 +590,13 @@ class WingProfile(Model):
return profile return profile
def surface_s3(self, def surface_s3(self,
front: bool = True) -> Cq.Workplane: front: bool = True) -> Cq.Workplane:
wrist_mount_inset = 0
h = self.wrist_height / 2 h = self.wrist_height / 2
tags = [ tags = [
("wrist_bot", ("wrist_bot",
self.wrist_to_abs(wrist_mount_inset, 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",
self.wrist_to_abs(wrist_mount_inset, 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()
@ -629,14 +622,12 @@ class WingProfile(Model):
) )
for t in ["wrist_bot", "wrist_top"]: for t in ["wrist_bot", "wrist_top"]:
is_top = t.endswith("_top") is_top = t.endswith("_top")
is_parent = True
o = self.spacer_s3_wrist() o = self.spacer_s3_wrist()
self._assembly_insert_spacer( self._assembly_insert_spacer(
result, result,
o.generate(), o.generate(),
point_tag=t, point_tag=t,
flipped=is_top,# != is_parent, flipped=is_top,
#rotate=True,
) )
return result.solve() return result.solve()

View File

@ -11,8 +11,6 @@ import cadquery as Cq
from nhf import Role from nhf import Role
from typing import Union, Tuple, cast from typing import Union, Tuple, cast
COLOR_MARKER = Cq.Color(0, 1, 1, 1)
# Bug fixes # Bug fixes
def _subloc(self, name: str) -> Tuple[Cq.Location, str]: def _subloc(self, name: str) -> Tuple[Cq.Location, str]:
""" """
@ -37,6 +35,15 @@ def _subloc(self, name: str) -> Tuple[Cq.Location, str]:
return (rv, name_out) return (rv, name_out)
Cq.Assembly._subloc = _subloc Cq.Assembly._subloc = _subloc
### Vector arithmetic
def location_sub(self: Cq.Location, rhs: Cq.Location) -> Cq.Vector:
(x1, y1, z1), _ = self.toTuple()
(x2, y2, z2), _ = rhs.toTuple()
return Cq.Vector(x1 - x2, y1 - y2, z1 - z2)
Cq.Location.__sub__ = location_sub
### Tags
def tagPoint(self, tag: str): def tagPoint(self, tag: str):
""" """
@ -100,6 +107,8 @@ def make_arrow(size: float = 2) -> Cq.Workplane:
def to_marker_name(tag: str) -> str: def to_marker_name(tag: str) -> str:
return tag.replace("?", "__T").replace("/", "__Z") + "_marker" return tag.replace("?", "__T").replace("/", "__Z") + "_marker"
COLOR_MARKER = Cq.Color(0, 1, 1, 1)
def mark_point(self: Cq.Assembly, def mark_point(self: Cq.Assembly,
tag: str, tag: str,
size: float = 2, size: float = 2,
@ -132,6 +141,16 @@ def mark_plane(self: Cq.Assembly,
Cq.Assembly.markPlane = mark_plane Cq.Assembly.markPlane = mark_plane
def get_abs_location(self: Cq.Assembly,
tag: str) -> Cq.Location:
name, shape = self._query(tag)
loc_self = shape.location()
loc_parent, _ = self._subloc(name)
loc = loc_parent * loc_self
return loc
Cq.Assembly.get_abs_location = get_abs_location
def extrude_with_markers(sketch: Cq.Sketch, def extrude_with_markers(sketch: Cq.Sketch,
thickness: float, thickness: float,