feat: Movement span on disk joint
This commit is contained in:
parent
d8a62d3352
commit
2aeeaae061
|
@ -15,6 +15,7 @@ class Role(Enum):
|
|||
|
||||
# Parent and child components in a load bearing joint
|
||||
PARENT = _color('blue4', 0.6)
|
||||
CASING = _color('dodgerblue3', 0.6)
|
||||
CHILD = _color('darkorange2', 0.6)
|
||||
DAMPING = _color('springgreen', 0.5)
|
||||
STRUCTURE = _color('gray', 0.4)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Optional, Tuple
|
||||
import cadquery as Cq
|
||||
from nhf import Role
|
||||
from nhf.build import Model, target
|
||||
|
@ -25,8 +26,12 @@ class DiskJoint(Model):
|
|||
spring_thickness: float = 1.3
|
||||
spring_height: float = 6.5
|
||||
spring_tail_length: float = 45.0
|
||||
spring_angle: float = 90.0
|
||||
spring_angle_shift: float = 0
|
||||
|
||||
# Spring angle at 0 degrees of movement
|
||||
spring_angle: float = 30.0
|
||||
# Angle at which the spring exerts no torque
|
||||
spring_angle_neutral: float = 90.0
|
||||
spring_angle_shift: float = 30
|
||||
wall_inset: float = 2.0
|
||||
|
||||
# Angular span of movement
|
||||
|
@ -55,15 +60,22 @@ class DiskJoint(Model):
|
|||
)
|
||||
|
||||
@property
|
||||
def total_thickness(self):
|
||||
def neutral_movement_angle(self) -> Optional[float]:
|
||||
a = self.spring_angle_neutral - self.spring_angle
|
||||
if 0 <= a and a <= self.movement_angle:
|
||||
return a
|
||||
return None
|
||||
|
||||
@property
|
||||
def total_thickness(self) -> float:
|
||||
return self.housing_thickness * 2 + self.disk_thickness
|
||||
|
||||
@property
|
||||
def opening_span(self):
|
||||
def opening_span(self) -> float:
|
||||
return self.movement_angle + self.tongue_span
|
||||
|
||||
@property
|
||||
def housing_upper_carve_offset(self):
|
||||
def housing_upper_carve_offset(self) -> float:
|
||||
return self.housing_thickness + self.disk_thickness - self.spring_height
|
||||
|
||||
@property
|
||||
|
@ -147,8 +159,7 @@ class DiskJoint(Model):
|
|||
result = result.cut(
|
||||
self
|
||||
.wall()
|
||||
.mirror("XY")
|
||||
.located(Cq.Location((0, 0, self.housing_thickness + self.disk_thickness)))
|
||||
.located(Cq.Location((0, 0, self.disk_thickness - self.wall_inset)))
|
||||
#.rotate((0, 0, 0), (1, 0, 0), 180)
|
||||
#.located(Cq.Location((0, 0, self.disk_thickness + self.housing_thickness)))
|
||||
)
|
||||
|
@ -165,7 +176,7 @@ class DiskJoint(Model):
|
|||
width=self.spring_thickness,
|
||||
height=self.housing_thickness
|
||||
).located(Cq.Location((0, self.radius_spring_internal, 0))))
|
||||
).rotate((0, 0, 0), (0, 0, 1), self.spring_angle - self.spring_angle_shift)
|
||||
).rotate((0, 0, 0), (0, 0, 1), 180 + self.spring_angle - self.spring_angle_shift)
|
||||
result = (
|
||||
Cq.Workplane('XY')
|
||||
.cylinder(
|
||||
|
@ -192,7 +203,8 @@ class DiskJoint(Model):
|
|||
result = result.union(tube)
|
||||
wall = (
|
||||
self.wall()
|
||||
.rotate((0, 0, 0), (1, 0, 0), 180)
|
||||
.rotate((0, 0, 0), (0, 0, 1), self.tongue_span)
|
||||
.mirror("XY")
|
||||
.located(Cq.Location((0, 0, self.disk_thickness + self.housing_thickness + self.wall_inset)))
|
||||
)
|
||||
result = (
|
||||
|
@ -202,18 +214,47 @@ class DiskJoint(Model):
|
|||
)
|
||||
return result
|
||||
|
||||
def add_constraints(self,
|
||||
assembly: Cq.Assembly,
|
||||
housing_lower: str,
|
||||
housing_upper: str,
|
||||
disk: str,
|
||||
angle: Tuple[float, float, float] = (0, 0, 0),
|
||||
) -> Cq.Assembly:
|
||||
"""
|
||||
The angle supplied must be perpendicular to the disk normal.
|
||||
"""
|
||||
(
|
||||
assembly
|
||||
.constrain(f"{disk}?mate_bot", f"{housing_lower}?mate", "Plane")
|
||||
.constrain(f"{disk}?mate_top", f"{housing_upper}?mate", "Plane")
|
||||
.constrain(f"{housing_lower}?dir", f"{housing_upper}?dir", "Axis")
|
||||
.constrain(f"{disk}?dir", "FixedRotation", angle)
|
||||
)
|
||||
|
||||
def assembly(self) -> Cq.Assembly:
|
||||
|
||||
def assembly(self, angle: Optional[float] = 0) -> Cq.Assembly:
|
||||
if angle is None:
|
||||
angle = self.movement_angle
|
||||
if angle is None:
|
||||
angle = 0
|
||||
else:
|
||||
assert 0 <= angle <= self.movement_angle
|
||||
result = (
|
||||
Cq.Assembly()
|
||||
.add(self.disk(), name="disk", color=Role.CHILD.color)
|
||||
.add(self.housing_lower(), name="housing_lower", color=Role.PARENT.color)
|
||||
.add(self.housing_upper(), name="housing_upper", color=Role.PARENT.color)
|
||||
.constrain("disk?mate_bot", "housing_lower?mate", "Plane")
|
||||
.constrain("disk?mate_top", "housing_upper?mate", "Plane")
|
||||
.solve()
|
||||
.add(self.housing_upper(), name="housing_upper", color=Role.CASING.color)
|
||||
.constrain("housing_lower", "Fixed")
|
||||
)
|
||||
return result
|
||||
self.add_constraints(
|
||||
result,
|
||||
housing_lower="housing_lower",
|
||||
housing_upper="housing_upper",
|
||||
disk="disk",
|
||||
angle=(0, 0, angle),
|
||||
)
|
||||
return result.solve()
|
||||
|
||||
if __name__ == '__main__':
|
||||
p = DiskJoint()
|
||||
|
|
|
@ -1,8 +1,24 @@
|
|||
import unittest
|
||||
import cadquery as Cq
|
||||
import nhf.touhou.houjuu_nue as M
|
||||
import nhf.touhou.houjuu_nue.parts as MP
|
||||
from nhf.checks import pairwise_intersection
|
||||
|
||||
class TestDiskJoint(unittest.TestCase):
|
||||
|
||||
def test_collision_0(self):
|
||||
j = MP.DiskJoint()
|
||||
assembly = j.assembly(angle=0)
|
||||
self.assertEqual(pairwise_intersection(assembly), [])
|
||||
def test_collision_mid(self):
|
||||
j = MP.DiskJoint()
|
||||
assembly = j.assembly(angle=j.movement_angle / 2)
|
||||
self.assertEqual(pairwise_intersection(assembly), [])
|
||||
def test_collision_max(self):
|
||||
j = MP.DiskJoint()
|
||||
assembly = j.assembly(angle=j.movement_angle)
|
||||
self.assertEqual(pairwise_intersection(assembly), [])
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
def test_hs_joint_parent(self):
|
||||
|
|
Loading…
Reference in New Issue