Compare commits

..

9 Commits

3 changed files with 375 additions and 193 deletions

View File

@ -1,37 +0,0 @@
import nhf.touhou.yasaka_kanako.mirror as MM
import nhf.touhou.yasaka_kanako.onbashira as MO
import nhf.touhou.yasaka_kanako.shimenawa as MS
from nhf.build import Model, TargetKind, target, assembly, submodel
import nhf.utils
from dataclasses import dataclass, field
import cadquery as Cq
@dataclass
class Parameters(Model):
mirror: MM.Mirror = field(default_factory=lambda: MM.Mirror())
onbashira: MO.Onbashira = field(default_factory=lambda: MO.Onbashira())
shimenawa: MS.Shimenawa = field(default_factory=lambda: MS.Shimenawa())
def __post_init__(self):
super().__init__(name="yasaka-kanako")
@submodel(name="mirror")
def submodel_mirror(self) -> Model:
return self.mirror
@submodel(name="onbashira")
def submodel_onbashira(self) -> Model:
return self.onbashira
@submodel(name="shimenawa")
def submodel_shimenawa(self) -> Model:
return self.shimenawa
if __name__ == '__main__':
import sys
p = Parameters()
if len(sys.argv) == 1:
p.build_all()
sys.exit(0)

View File

@ -0,0 +1,37 @@
from .mirror import Mirror
from .onbashira import Onbashira
from .shimenawa import Shimenawa
from nhf.build import Model, TargetKind, target, assembly, submodel
import nhf.utils
from dataclasses import dataclass, field
import cadquery as Cq
@dataclass
class Parameters(Model):
mirror: Mirror = field(default_factory=Mirror)
onbashira: Onbashira = field(default_factory=Onbashira)
shimenawa: Shimenawa = field(default_factory=Shimenawa)
def __post_init__(self):
super().__init__(name="yasaka-kanako")
@submodel(name="mirror")
def submodel_mirror(self) -> Model:
return self.mirror
@submodel(name="onbashira")
def submodel_onbashira(self) -> Model:
return self.onbashira
@submodel(name="shimenawa")
def submodel_shimenawa(self) -> Model:
return self.shimenawa
if __name__ == '__main__':
import sys
p = Parameters()
if len(sys.argv) == 1:
p.build_all()
sys.exit(0)

View File

@ -1,9 +1,10 @@
from nhf.build import Model, TargetKind, target, assembly, submodel
from nhf.materials import Role, Material
import nhf.utils
from nhf.parts.fasteners import FlatHeadBolt, HexNut, Washer
from nhf.parts.electronics import ArduinoUnoR3, BatteryBox18650
import nhf.utils
import unittest
from typing import Optional, Union
import math
from dataclasses import dataclass, field
@ -274,7 +275,7 @@ class Onbashira(Model):
chamber_side_length: float = 400.0
chamber_side_width_ex: float = 20.0
# Circular hole to hold a switch
chamber_front_switch_diam: float = 20.0
hatch_switch_diam: float = 20.0
# Dimensions of gun barrels
barrel_diam: float = 25.4 * 1.5
@ -348,8 +349,15 @@ class Onbashira(Model):
motor_coupler_conn_dx: float = 30.0
motor_coupler_wall_thickness: float = 5.0
motor_coupler_inner_gap: float = 1.0
turning_bar_tilt: float = 12.0
turning_bar_hole_dy: float = 20.0
turning_bar_parent_hole_diam_extra: float = 1.0
turning_bar_child_hole_diam: float = 4.0
turning_bar_width: float = 15.0
electronic_mount_dx: float = 50.0
turning_bar_height: float = 30.0
electronics_panel_width_ratio: float = 0.7
electronics_mount_dx: float = 40.0
material_side: Material = Material.WOOD_BIRCH
material_bearing: Material = Material.PLASTIC_PLA
@ -438,6 +446,14 @@ class Onbashira(Model):
"""
return self.chamber_side_width / 2 / math.tan(math.radians(self.angle_side / 2))
def interior_length(self, section_length: float) -> float:
"""
Given the outside length of a section, calculate the length between the
mount holes on the angle joints on the two ends of it.
"""
z2 = self.angle_joint_gap - self.angle_joint_flange_thickness
return section_length + z2
@target(name="sanding-block")
def sanding_block(self) -> Cq.Workplane:
# Dihedral angle / 2
@ -1259,14 +1275,8 @@ class Onbashira(Model):
### Electronics ###
@property
def turning_bar_hole_dy(self) -> float:
"""
Distance between centre of mounting holes in the turning bar and top of
the side panels.
"""
panel_to_mount = self.angle_joint_flange_thickness / 2 - self.angle_joint_gap / 2
return panel_to_mount + self.turning_bar_width / 2
def turning_bar_parent_hole_diam(self) -> float:
return BOLT_COMMON.diam_thread + self.turning_bar_parent_hole_diam_extra
@target(name="turning-bar")
def turning_bar(self) -> Cq.Workplane:
"""
@ -1276,6 +1286,39 @@ class Onbashira(Model):
_, dx = self.angle_joint_bind_pos.to2d_pos()
t = 8
w = self.turning_bar_width
h = self.turning_bar_height
flange = Cq.Solid.makeBox(
length=w,
width=t,
height=w/2,
).moved(-w/2, 0, 0) + Cq.Solid.makeCylinder(
radius=w/2,
height=t,
pnt=(0, 0, 0),
dir=(0, 1, 0),
) - Cq.Solid.makeCylinder(
radius=self.turning_bar_parent_hole_diam/2,
height=w*2,
pnt=(0, -w, 0),
dir=(0, 1, 0),
)
flange = flange.moved(0, -t, -w/2-h)
leg = (
Cq.Workplane('YZ')
.sketch()
.polygon([
(0, -h),
(-t, -h),
(-t-self.turning_bar_tilt, 0),
(-self.turning_bar_tilt, 0),
])
.finalize()
.extrude(w)
.translate((-w/2, 0, 0))
.union(flange)
.val()
)
result = (
Cq.Workplane()
.box(
@ -1284,50 +1327,47 @@ class Onbashira(Model):
height=t,
centered=(True, True, False)
)
.translate((0, -w/2-self.turning_bar_tilt, -t))
)
flange = Cq.Solid.makeBox(
length=w,
width=t,
height=w/2,
).moved(-w/2, -t, -w/2) + Cq.Solid.makeCylinder(
radius=w/2,
height=t,
pnt=(0, -t, -w/2),
dir=(0, 1, 0),
)
remover = Cq.Solid.makeCylinder(
radius=BOLT_COMMON.diam_thread/2,
holeC = Cq.Solid.makeCylinder(
radius=self.turning_bar_child_hole_diam/2,
height=w,
)
removerf = Cq.Solid.makeCylinder(
radius=BOLT_COMMON.diam_thread/2,
height=w*2,
pnt=(0, -w, -w/2),
dir=(0, 1, 0),
)
dxe = self.electronic_mount_dx
dxe = self.electronics_mount_dx
result = (
result
+ flange.moved(dx, w/2, 0)
+ flange.moved(-dx, w/2, 0)
- remover.moved(dxe, 0, 0)
- remover.moved(-dxe, 0, 0)
- removerf.moved(dx, 0, 0)
- removerf.moved(-dx, 0, 0)
+ leg.moved(dx, 0, 0)
+ leg.moved(-dx, 0, 0)
- holeC.moved(dxe, -self.turning_bar_hole_dy, -w)
- holeC.moved(-dxe, -self.turning_bar_hole_dy, -w)
)
result.tagAbsolute("holeBO1", (dx, w/2, -w/2), direction="+Y")
result.tagAbsolute("holeBO2", (-dx, w/2, -w/2), direction="+Y")
result.tagAbsolute("holeMO1", (dxe, 0, t))
result.tagAbsolute("holeMO2", (-dxe, 0, t))
result.tagAbsolute("holeBO1", (dx, 0, -w/2-h), direction="+Y")
result.tagAbsolute("holeBO2", (-dx, 0, -w/2-h), direction="+Y")
result.tagAbsolute("holeMO1", (dxe, -self.turning_bar_hole_dy, 0))
result.tagAbsolute("holeMO2", (-dxe, -self.turning_bar_hole_dy, 0))
return result
def profile_electronics_panel(self) -> Cq.Sketch:
"""
Generic electronics panel
"""
hole_dx = self.electronics_mount_dx
# Distance between the holes
y = self.interior_length(self.side_length3) - self.turning_bar_hole_dy * 2
l = y + 15
w = self.side_width * self.electronics_panel_width_ratio
return (
Cq.Sketch()
.rect(l, w)
.rect(y, hole_dx * 2, mode="c", tag="corner")
.vertices(tag="corner")
.circle(self.turning_bar_child_hole_diam/2, mode="s")
.reset()
)
@target(name="electronics-panel1", kind=TargetKind.DXF)
def profile_electronics_panel1(self) -> Cq.Sketch:
hole_dy = self.turning_bar_hole_dy
hole_dx = self.electronic_mount_dx
l = self.side_length3 - hole_dy * 2 + 12
y = self.side_length3 - hole_dy * 2
w = self.side_width
# Distance between the holes
controller_holes = [
self.controller_loc * Cq.Location.from2d(*h).flip_y()
for h in self.controller.holes
@ -1338,12 +1378,7 @@ class Onbashira(Model):
for loc in self.battery_box_locs
]
profile = (
Cq.Sketch()
.rect(l, w)
.rect(y, hole_dx * 2, mode="c", tag="corner")
.vertices(tag="corner")
.circle(BOLT_COMMON.diam_thread/2, mode="s")
.reset()
self.profile_electronics_panel()
.push([
h.to2d_pos() for h in controller_holes
] + [
@ -1354,21 +1389,20 @@ class Onbashira(Model):
return profile
def electronics_panel1(self) -> Cq.Workplane:
hole_dy = self.turning_bar_hole_dy
hole_dx = self.electronic_mount_dx
l = self.side_length3
hole_dx = self.electronics_mount_dx
y = self.interior_length(self.side_length3) - self.turning_bar_hole_dy * 2
l = y + 15
t = self.side_thickness
result = (
Cq.Workplane()
.placeSketch(self.profile_electronics_panel1())
.extrude(t)
)
x = l/2 - hole_dy
for side, z, d in [("T", t, "+Z"), ("B", 0, "-Z")]:
result.tagAbsolute(f"holeLP{side}", (-x, hole_dx, z), direction=d)
result.tagAbsolute(f"holeLS{side}", (-x, -hole_dx, z), direction=d)
result.tagAbsolute(f"holeRP{side}", (x, -hole_dx, z), direction=d)
result.tagAbsolute(f"holeRS{side}", (x, hole_dx, z), direction=d)
result.tagAbsolute(f"holeLP{side}", (-y/2, hole_dx, z), direction=d)
result.tagAbsolute(f"holeLS{side}", (-y/2, -hole_dx, z), direction=d)
result.tagAbsolute(f"holeRP{side}", (y/2, -hole_dx, z), direction=d)
result.tagAbsolute(f"holeRS{side}", (y/2, hole_dx, z), direction=d)
for (i, h) in enumerate(self.controller.holes):
loc = self.controller_loc * Cq.Location.from2d(*h).flip_y()
hx, hy = loc.to2d_pos()
@ -1451,6 +1485,84 @@ class Onbashira(Model):
)
return a.solve()
@target(name="electronics-panel2", kind=TargetKind.DXF)
def profile_electronics_panel2(self) -> Cq.Sketch:
n = 15
h_hole = 10
y = self.interior_length(self.side_length3) - self.turning_bar_hole_dy * 2
w_hole = self.side_width * self.electronics_panel_width_ratio * 0.75
profile = (
self.profile_electronics_panel()
.rarray(y / n, 0, n, 1)
.slot(w_hole, h_hole, mode="s", angle=90)
)
return profile
def electronics_panel2(self) -> Cq.Workplane:
hole_dx = self.electronics_mount_dx
y = self.interior_length(self.side_length3) - self.turning_bar_hole_dy * 2
l = y + 15
t = self.side_thickness
result = (
Cq.Workplane()
.placeSketch(self.profile_electronics_panel2())
.extrude(t)
)
for side, z, d in [("T", t, "+Z"), ("B", 0, "-Z")]:
result.tagAbsolute(f"holeLP{side}", (-y/2, hole_dx, z), direction=d)
result.tagAbsolute(f"holeLS{side}", (-y/2, -hole_dx, z), direction=d)
result.tagAbsolute(f"holeRP{side}", (y/2, -hole_dx, z), direction=d)
result.tagAbsolute(f"holeRS{side}", (y/2, hole_dx, z), direction=d)
return result
@assembly()
def assembly_electronics2(self) -> Cq.Assembly:
name_barL = "barL"
name_barR = "barR"
name_panel = "panel"
a = (
Cq.Assembly()
.addS(
self.turning_bar(),
name=name_barL,
material=self.material_brace,
role=Role.STRUCTURE,
)
.addS(
self.turning_bar(),
name=name_barR,
material=self.material_brace,
role=Role.STRUCTURE,
)
.addS(
self.electronics_panel2(),
name=name_panel,
material=self.material_auxiliary,
role=Role.STRUCTURE,
)
.constrain(
f"{name_panel}?holeLPB",
f"{name_barL}?holeMO1",
"Plane"
)
.constrain(
f"{name_panel}?holeLSB",
f"{name_barL}?holeMO2",
"Plane"
)
.constrain(
f"{name_panel}?holeRPB",
f"{name_barR}?holeMO1",
"Plane"
)
.constrain(
f"{name_panel}?holeRSB",
f"{name_barR}?holeMO2",
"Plane"
)
)
return a.solve()
### Side Panels
@target(name="front-bracket")
@ -1476,7 +1588,7 @@ class Onbashira(Model):
.translate((0, 0, -self.front_bracket_depth/2))
)
hole_subtractor = Cq.Solid.makeCylinder(
radius=BOLT_COMMON.diam_thread/2,
radius=(BOLT_COMMON.diam_thread + 2)/2,
height=self.bulk_radius,
dir=(1, 0, 0)
)
@ -1757,7 +1869,7 @@ class Onbashira(Model):
angle=shift,
mode="c", tag="bolt")
.vertices(tag="bolt")
.circle(self.rotor_bind_bolt_diam/2, mode="s")
.circle(BOLT_COMMON.diam_thread/2, mode="s")
)
def chamber_back(self) -> Cq.Workplane:
sketch = self.profile_chamber_back()
@ -1789,10 +1901,11 @@ class Onbashira(Model):
)
return a
@target(name="chamber-front", kind=TargetKind.DXF)
def profile_chamber_front(self) -> Cq.Sketch:
@target(name="hatch", kind=TargetKind.DXF)
def profile_hatch(self) -> Cq.Sketch:
"""
Front chamber must allow access to the electronics section
Front chamber must allow access to the electronics section. This is the
wall sitting on the 3rd ring which connects to the previous section.
"""
l = self.side_width
h = self.side_width
@ -1802,18 +1915,18 @@ class Onbashira(Model):
.reset()
.rect(l, h, mode="s")
.push([
(l/2 + gap + self.chamber_front_switch_diam/2, 0)
(l/2 + gap + self.hatch_switch_diam/2, 0)
])
.circle(self.chamber_front_switch_diam/2, mode="s")
.circle(self.hatch_switch_diam/2, mode="s")
.reset()
.push([
(0, h/2 + gap),
(0, -h/2 - gap),
(gap, h/2 + gap),
(gap, -h/2 - gap),
])
.rect(l/4, gap, mode="s")
)
def chamber_front(self) -> Cq.Sketch:
sketch = self.profile_chamber_front()
def hatch(self) -> Cq.Sketch:
sketch = self.profile_hatch()
result = (
Cq.Workplane()
.placeSketch(sketch)
@ -1848,7 +1961,7 @@ class Onbashira(Model):
h = self.angle_joint_flange_thickness
# drill hole
cyl = Cq.Solid.makeCylinder(
radius=self.rotor_bind_bolt_diam/2,
radius=BOLT_COMMON.diam_thread/2,
height=h,
pnt=(ri * math.cos(th), ri * math.sin(th), -h/2),
)
@ -2104,7 +2217,7 @@ class Onbashira(Model):
- hole_negative.moved(p1r) \
- hole_negative.moved(p2r)
# Mark the absolute locations of the mount points
dr = self.bulk_radius + self.angle_joint_thickness
dr = self.chamber_bulk_radius
dr0 = self.bulk_radius
for i, (x, y) in enumerate(self.angle_joint_bolt_position):
py = dy + y
@ -2252,7 +2365,6 @@ class Onbashira(Model):
result.tagAbsolute(f"holeRSM{i}", locrot * Cq.Location(dr0, -x, -py), direction="-X")
# Generate the flange geometry
flange = self.angle_joint_flange()
result = result + self.angle_joint_flange()
th = math.pi / self.n_side
ri = self.angle_joint_bind_radius
@ -2340,6 +2452,42 @@ class Onbashira(Model):
self.assembly_ring(self.angle_joint()),
name="ring1",
)
name_handle1 = "handle1_1"
a = a.addS(
self.handle(),
name=name_handle1,
material=self.material_brace,
role=Role.HANDLE,
)
name_handle2 = "handle1_2"
a = a.addS(
self.handle(),
name=name_handle2,
material=self.material_brace,
role=Role.HANDLE,
)
# Handle constrain
for ih, (_x, _y) in enumerate(self.angle_joint_bolt_position):
a = a.constrain(
f"{name_handle1}?holeLPI{ih}",
f"ring1/side3?holeLPO{ih}",
"Plane",
)
a = a.constrain(
f"{name_handle1}?holeRPI{ih}",
f"ring1/side3?holeRPO{ih}",
"Plane",
)
a = a.constrain(
f"{name_handle2}?holeLPI{ih}",
f"ring1/side5?holeLPO{ih}",
"Plane",
)
a = a.constrain(
f"{name_handle2}?holeRPI{ih}",
f"ring1/side5?holeRPO{ih}",
"Plane",
)
if has_part(parts, "section2"):
a = a.add(
self.assembly_section(length=self.side_length2, hasFrontHole=True, hasBackHole=True),
@ -2351,42 +2499,6 @@ class Onbashira(Model):
name="ring2",
)
name_handle1 = "handle2_1"
a = a.addS(
self.handle(),
name=name_handle1,
material=self.material_brace,
role=Role.HANDLE,
)
name_handle2 = "handle2_2"
a = a.addS(
self.handle(),
name=name_handle2,
material=self.material_brace,
role=Role.HANDLE,
)
# Handle constrain
for ih, (x, y) in enumerate(self.angle_joint_bolt_position):
a = a.constrain(
f"{name_handle1}?holeLPI{ih}",
f"ring2/side2?holeLPO{ih}",
"Plane",
)
a = a.constrain(
f"{name_handle1}?holeRPI{ih}",
f"ring2/side2?holeRPO{ih}",
"Plane",
)
a = a.constrain(
f"{name_handle2}?holeLPI{ih}",
f"ring2/side4?holeLPO{ih}",
"Plane",
)
a = a.constrain(
f"{name_handle2}?holeRPI{ih}",
f"ring2/side4?holeRPO{ih}",
"Plane",
)
if has_part(parts, "section3"):
a = a.add(
self.assembly_section(length=self.side_length3, hasFrontHole=True, hasBackHole=True),
@ -2397,6 +2509,42 @@ class Onbashira(Model):
self.assembly_ring(self.angle_joint_chamber_front()),
name="ring3",
)
name_handle1 = "handle3_1"
a = a.addS(
self.handle(),
name=name_handle1,
material=self.material_brace,
role=Role.HANDLE,
)
name_handle2 = "handle3_2"
a = a.addS(
self.handle(),
name=name_handle2,
material=self.material_brace,
role=Role.HANDLE,
)
# Handle constrain
for ih, (x, y) in enumerate(self.angle_joint_bolt_position):
a = a.constrain(
f"{name_handle1}?holeLPI{ih}",
f"ring3/side1?holeLPO{ih}",
"Plane",
)
a = a.constrain(
f"{name_handle1}?holeLSI{ih}",
f"ring3/side0?holeLSO{ih}",
"Plane",
)
a = a.constrain(
f"{name_handle2}?holeLPI{ih}",
f"ring3/side3?holeLPO{ih}",
"Plane",
)
a = a.constrain(
f"{name_handle2}?holeLSI{ih}",
f"ring3/side2?holeLSO{ih}",
"Plane",
)
if has_part(parts, "chamber"):
a = a.add(
self.assembly_chamber(),
@ -2414,13 +2562,39 @@ class Onbashira(Model):
material=self.material_side,
role=Role.STRUCTURE | Role.DECORATION,
)
if has_part(parts, "chamber_front"):
for i in range(self.n_side):
name_bolt = f"chamber_back{i}boltFPI{i}"
a = a.addS(
BOLT_COMMON.generate(),
name=name_bolt,
material=self.material_fastener,
role=Role.CONNECTION,
)
a = a.constrain(
f"chamber_back?holeF{i}",
f"{name_bolt}?root",
"Plane",
)
if has_part(parts, "hatch"):
a = a.addS(
self.chamber_front(),
name="chamber_front",
self.hatch(),
name="hatch",
material=self.material_side,
role=Role.STRUCTURE | Role.DECORATION,
)
for i in range(self.n_side):
name_bolt = f"hatch{i}boltFPI{i}"
a = a.addS(
BOLT_COMMON.generate(),
name=name_bolt,
material=self.material_fastener,
role=Role.CONNECTION,
)
a = a.constrain(
f"hatch?holeF{i}",
f"{name_bolt}?root",
"Plane",
)
if has_part(parts, "motor"):
a = a.add(self.assembly_motor(), name="motor")
if has_part(parts, "machine"):
@ -2428,6 +2602,8 @@ class Onbashira(Model):
if has_part(parts, "electronics1"):
a = a.add(self.assembly_electronics1(), name="electronics1")
a = a.constrain("electronics1/controller", "Fixed")
if has_part(parts, "electronics2"):
a = a.add(self.assembly_electronics2(), name="electronics2")
if has_part(parts, ["electronics1", "ring3"]):
a = a.constrain(
f"electronics1/barL?holeBO2",
@ -2439,8 +2615,18 @@ class Onbashira(Model):
f"ring3/side2?holeStatorL",
"Plane",
)
if has_part(parts, ["electronics2", "ring3"]):
a = a.constrain(
f"electronics2/barL?holeBO2",
f"ring3/side3?holeStatorL",
"Plane",
)
a = a.constrain(
f"electronics2/barL?holeBO1",
f"ring3/side4?holeStatorL",
"Plane",
)
# FIXME: Filter
if has_part(parts, ["motor", "ring2"]):
for i in range(self.n_side // 2):
j = self.n_side // 2 - 1 - i
@ -2454,27 +2640,34 @@ class Onbashira(Model):
# f"ring2/side{i*2+1}?holeStatorL",
# "Plane",
#)
if parts:
return a.solve()
for i in range(self.n_side):
j = (i + 1) % self.n_side
ir = (self.n_side - i) % self.n_side
coupler_name = f"stator_coupler{i}"
a = a.addS(
self.stator_coupler(),
name=coupler_name,
material=self.material_brace,
role=Role.STRUCTURE,
)
a = a.constrain(
f"{coupler_name}?holeOB",
f"ring1/side{i}?holeStatorR",
"Plane",
)
a = a.constrain(
f"{coupler_name}?holeIF",
f"machine/stator1?holeF{ir}",
"Plane",
)
if has_part(parts, "ring1") or has_part(parts, "machine"):
coupler_name = f"stator_coupler{i}"
a = a.addS(
self.stator_coupler(),
name=coupler_name,
material=self.material_brace,
role=Role.STRUCTURE,
)
if has_part(parts, "ring1"):
a = a.constrain(
f"{coupler_name}?holeOB",
f"ring1/side{i}?holeStatorR",
"Plane",
)
if has_part(parts, "machine"):
a = a.constrain(
f"{coupler_name}?holeIF",
f"machine/stator1?holeF{ir}",
"Plane",
)
#name_bolt =f"stator_outer_bolt{i}"
#a = a.addS(
@ -2489,31 +2682,7 @@ class Onbashira(Model):
# "Plane",
#)
name_bolt =f"chamber_back{i}boltFPI{i}"
a = a.addS(
BOLT_COMMON.generate(),
name=name_bolt,
material=self.material_fastener,
role=Role.CONNECTION,
)
a = a.constrain(
f"chamber_back?holeF{i}",
f"{name_bolt}?root",
"Plane",
)
name_bolt =f"chamber_front{i}boltFPI{i}"
a = a.addS(
BOLT_COMMON.generate(),
name=name_bolt,
material=self.material_fastener,
role=Role.CONNECTION,
)
a = a.constrain(
f"chamber_front?holeF{i}",
f"{name_bolt}?root",
"Plane",
)
for ih in range(len(self.angle_joint_bolt_position)):
a = a.constrain(
f"chamber/side{i}?holeFPI{ih}",
@ -2532,7 +2701,7 @@ class Onbashira(Model):
)
a = a.constrain(
f"ring3/side{i}?holeStatorR",
f"chamber_front?holeB{i}",
f"hatch?holeB{i}",
"Plane",
)
@ -2559,5 +2728,18 @@ class Onbashira(Model):
f"{nc}/side{i}?holeRSM{ih}",
"Plane",
)
return a.solve()
class TestOnbashira(unittest.TestCase):
def test_collision(self):
from nhf.checks import pairwise_intersection
o = Onbashira()
a = o.assembly()
self.assertEqual(pairwise_intersection(a), [])
if __name__ == '__main__':
unittest.main()