Merge branch 'main' into touhou/shiki-eiki
This commit is contained in:
commit
6b0b604ae1
|
@ -1,6 +1,7 @@
|
||||||
# Cosplay
|
# Cosplay
|
||||||
|
|
||||||
This is the design repository for NorCal Hakkero Factory No. 1.
|
This is the design repository for NorCal Hakkero Factory No. 1, where we use
|
||||||
|
parametric CAD to make cosplay props.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
@ -15,6 +16,12 @@ and this should succeed
|
||||||
python3 -c "import nhf"
|
python3 -c "import nhf"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To visualize an object, create a file `visualize.py`, and run `cq-editor`:
|
||||||
|
|
||||||
|
``` sh
|
||||||
|
python3 -m cq_editor visualize.py
|
||||||
|
```
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
Run all tests with
|
Run all tests with
|
||||||
|
|
|
@ -90,7 +90,7 @@ class Target:
|
||||||
x = (
|
x = (
|
||||||
Cq.Workplane()
|
Cq.Workplane()
|
||||||
.add(x._faces)
|
.add(x._faces)
|
||||||
.add(x._wires)
|
.add(x.wires)
|
||||||
.add(x._edges)
|
.add(x._edges)
|
||||||
)
|
)
|
||||||
assert isinstance(x, Cq.Workplane)
|
assert isinstance(x, Cq.Workplane)
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import cadquery as Cq
|
||||||
|
from nhf import Material, Role
|
||||||
|
from nhf.build import Model, target, assembly, TargetKind, submodel
|
||||||
|
from nhf.parts.box import MountingBox, Hole
|
||||||
|
from nhf.parts.electronics import ArduinoUnoR3
|
||||||
|
import nhf.utils
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LightPanel(Model):
|
||||||
|
|
||||||
|
# Dimensions of the base panel
|
||||||
|
length: float = 300.0
|
||||||
|
width: float = 200.0
|
||||||
|
|
||||||
|
attach_height: float = 20.0
|
||||||
|
attach_diam: float = 8.0
|
||||||
|
attach_depth: float = 12.7
|
||||||
|
|
||||||
|
grid_height: float = 20.0
|
||||||
|
grid_top_height: float = 5.0
|
||||||
|
# Distance from grid to edge
|
||||||
|
grid_margin: float = 20.0
|
||||||
|
# Number of holes in each row of the grid
|
||||||
|
grid_holes: int = 9
|
||||||
|
grid_layers: int = 6
|
||||||
|
grid_hole_width: float = 15.0
|
||||||
|
|
||||||
|
base_thickness: float = 25.4/16
|
||||||
|
grid_thickness: float = 25.4/4
|
||||||
|
base_material: Material = Material.WOOD_BIRCH
|
||||||
|
grid_material: Material = Material.ACRYLIC_TRANSPARENT
|
||||||
|
|
||||||
|
controller: ArduinoUnoR3 = ArduinoUnoR3()
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
assert self.grid_holes >= 2
|
||||||
|
super().__init__(name="light-panel")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def grid_spacing_y(self) -> float:
|
||||||
|
return (self.width - 2 * self.grid_margin - self.grid_thickness) / (self.grid_layers - 1)
|
||||||
|
|
||||||
|
@target(name="grid", kind=TargetKind.DXF)
|
||||||
|
def grid_profile(self):
|
||||||
|
w = self.length - self.grid_margin * 2
|
||||||
|
h = self.grid_height + self.grid_top_height
|
||||||
|
|
||||||
|
# The width of one hole (w0) satisfies
|
||||||
|
# n * w0 + (n+1) t = w
|
||||||
|
# where t is the thickness of the edge
|
||||||
|
n = self.grid_holes
|
||||||
|
w0 = self.grid_hole_width
|
||||||
|
t = (w - n * w0) / (n + 1)
|
||||||
|
# The spacing is such that the first and last holes are a distance `margin`
|
||||||
|
# away from the edges, so it satisfies
|
||||||
|
# t + w0/2 + (n-1) * s + w0/2 + t = w
|
||||||
|
step = (w - t*2 - w0) / (n - 1)
|
||||||
|
return (
|
||||||
|
Cq.Sketch()
|
||||||
|
.push([(0, h/2)])
|
||||||
|
.rect(w, h)
|
||||||
|
.push([
|
||||||
|
(i * step + t + w0/2 - w/2, self.grid_height/2)
|
||||||
|
for i in range(0, n)
|
||||||
|
])
|
||||||
|
.rect(w0, self.grid_height, mode='s')
|
||||||
|
)
|
||||||
|
|
||||||
|
def grid(self) -> Cq.Workplane:
|
||||||
|
return (
|
||||||
|
Cq.Workplane('XY')
|
||||||
|
.placeSketch(self.grid_profile())
|
||||||
|
.extrude(self.grid_thickness)
|
||||||
|
)
|
||||||
|
|
||||||
|
@submodel(name="base")
|
||||||
|
def base(self) -> MountingBox:
|
||||||
|
xshift = self.length / 2 - self.controller.length - self.grid_margin / 2
|
||||||
|
yshift = self.grid_margin / 2
|
||||||
|
holes = [
|
||||||
|
Hole(
|
||||||
|
x=x + xshift, y=y + yshift,
|
||||||
|
diam=self.controller.hole_diam,
|
||||||
|
tag=f"controller_conn{i}",
|
||||||
|
)
|
||||||
|
for i, (x, y) in enumerate(self.controller.holes)
|
||||||
|
]
|
||||||
|
return MountingBox(
|
||||||
|
holes=holes,
|
||||||
|
hole_diam=self.controller.hole_diam,
|
||||||
|
length=self.length,
|
||||||
|
width=self.width,
|
||||||
|
centred=(True, False),
|
||||||
|
thickness=self.base_thickness,
|
||||||
|
)
|
||||||
|
|
||||||
|
@target(name="attachment")
|
||||||
|
def attachment(self) -> Cq.Workplane:
|
||||||
|
l = self.length / 2
|
||||||
|
w = self.width / 2
|
||||||
|
return (
|
||||||
|
Cq.Workplane('XY')
|
||||||
|
.box(
|
||||||
|
l, w, self.attach_height,
|
||||||
|
centered=(True, True, False),
|
||||||
|
)
|
||||||
|
.faces(">Z")
|
||||||
|
.hole(self.attach_diam, self.attach_depth)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def assembly(self) -> Cq.Assembly:
|
||||||
|
assembly = (
|
||||||
|
Cq.Assembly()
|
||||||
|
.addS(
|
||||||
|
self.base().generate(),
|
||||||
|
name="base",
|
||||||
|
role=Role.STRUCTURE,
|
||||||
|
material=self.base_material,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# Grid thickness t is fixed, so the spacing of the grid satisfies
|
||||||
|
# margin + t + (n-1) * spacing + margin = width
|
||||||
|
spacing = self.grid_spacing_y
|
||||||
|
shift = self.grid_margin + self.grid_thickness / 2
|
||||||
|
for i in range(self.grid_layers):
|
||||||
|
assembly = assembly.addS(
|
||||||
|
self.grid(),
|
||||||
|
name=f"grid_{i}",
|
||||||
|
role=Role.STRUCTURE,
|
||||||
|
material=self.grid_material,
|
||||||
|
loc=Cq.Location(0, spacing * i + shift, self.base_thickness, 90, 0, 0),
|
||||||
|
)
|
||||||
|
return assembly
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
|
||||||
|
p = LightPanel()
|
||||||
|
print(p.grid_spacing_y)
|
||||||
|
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
p.build_all()
|
||||||
|
sys.exit(0)
|
File diff suppressed because it is too large
Load Diff
|
@ -8,13 +8,21 @@ readme = "README.md"
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.10"
|
python = "^3.10"
|
||||||
cadquery = {git = "https://github.com/CadQuery/cadquery.git"}
|
cadquery = {git = "https://github.com/CadQuery/cadquery.git"}
|
||||||
build123d = "^0.5.0"
|
#build123d = "^0.5.0"
|
||||||
numpy = "^1.26.4"
|
numpy = ">=2,<3"
|
||||||
colorama = "^0.4.6"
|
colorama = "^0.4.6"
|
||||||
|
|
||||||
# cadquery dependency
|
# cadquery dependency
|
||||||
multimethod = "^1.12"
|
multimethod = "^1.12"
|
||||||
scipy = "^1.14.0"
|
scipy = "^1.14.0"
|
||||||
|
typish = "^1.9.3"
|
||||||
|
|
||||||
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
cq-editor = {git = "https://github.com/CadQuery/CQ-editor.git"}
|
||||||
|
pyqt5 = "^5.15.11"
|
||||||
|
logbook = "^1.8.0"
|
||||||
|
spyder = "^5"
|
||||||
|
pyqtgraph = "^0.13.7"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core"]
|
requires = ["poetry-core"]
|
||||||
|
|
Loading…
Reference in New Issue