diff --git a/nhf/build.py b/nhf/build.py index 106577c..05e4aaa 100644 --- a/nhf/build.py +++ b/nhf/build.py @@ -214,7 +214,7 @@ class Submodel: def write_to(self, obj, path: str): x = self._method(obj) assert isinstance(x, Model), f"Unexpected type: {type(x)}" - x.build_all(path) + x.build_all(path, prefix=False) @classmethod def methods(cls, subject): @@ -271,11 +271,17 @@ class Model: total += 1 return total - def build_all(self, output_dir: Union[Path, str] = "build", verbose=1): + def build_all( + self, + output_dir: Union[Path, str] = "build", + prefix: bool = True, + verbose=1): """ Build all targets in this model and write the results to file """ output_dir = Path(output_dir) + if prefix: + output_dir = output_dir / self.name targets = Target.methods(self) for t in targets.values(): file_name = t.file_name diff --git a/nhf/materials.py b/nhf/materials.py index bc172c3..e931403 100644 --- a/nhf/materials.py +++ b/nhf/materials.py @@ -84,6 +84,7 @@ class Material(Enum): ACRYLIC_TRANSLUSCENT = 1.18, _color('ivory2', 0.8) ACRYLIC_TRANSPARENT = 1.18, _color('ghostwhite', 0.5) STEEL_SPRING = 7.8, _color('gray', 0.8) + METAL_BRASS = 8.5, _color('gold1', 0.8) def __init__(self, density: float, color: Cq.Color): self.density = density @@ -116,6 +117,9 @@ def add_with_material_role( Cq.Assembly.addS = add_with_material_role def color_by_material(self: Cq.Assembly) -> Cq.Assembly: + """ + Set colours in an assembly by material + """ for _, a in self.traverse(): if KEY_MATERIAL not in a.metadata: continue @@ -123,6 +127,9 @@ def color_by_material(self: Cq.Assembly) -> Cq.Assembly: return self Cq.Assembly.color_by_material = color_by_material def color_by_role(self: Cq.Assembly, avg: bool = True) -> Cq.Assembly: + """ + Set colours in an assembly by role + """ for _, a in self.traverse(): if KEY_ROLE not in a.metadata: continue diff --git a/nhf/touhou/__init__.py b/nhf/touhou/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nhf/touhou/shiki_eiki/__init__.py b/nhf/touhou/shiki_eiki/__init__.py new file mode 100644 index 0000000..c97eff5 --- /dev/null +++ b/nhf/touhou/shiki_eiki/__init__.py @@ -0,0 +1,40 @@ +from dataclasses import dataclass, field +import cadquery as Cq +from nhf.build import Model, TargetKind, target, assembly, submodel +import nhf.touhou.shiki_eiki.rod as MR +import nhf.touhou.shiki_eiki.crown as MC +import nhf.touhou.shiki_eiki.epaulette as ME +import nhf.utils + +@dataclass +class Parameters(Model): + + rod: MR.Rod = field(default_factory=lambda: MR.Rod()) + crown: MC.Crown = field(default_factory=lambda: MC.Crown()) + epaulette_ze: ME.Epaulette = field(default_factory=lambda: ME.Epaulette(side="ze")) + epaulette_hi: ME.Epaulette = field(default_factory=lambda: ME.Epaulette(side="hi")) + + def __post_init__(self): + super().__init__(name="shiki-eiki") + + @submodel(name="rod") + def submodel_rod(self) -> Model: + return self.rod + @submodel(name="crown") + def submodel_crown(self) -> Model: + return self.crown + @submodel(name="epaulette_ze") + def submodel_epaulette_ze(self) -> Model: + return self.epaulette_ze + @submodel(name="epaulette_hi") + def submodel_epaulette_hi(self) -> Model: + return self.epaulette_hi + + +if __name__ == '__main__': + import sys + + p = Parameters() + if len(sys.argv) == 1: + p.build_all() + sys.exit(0) diff --git a/nhf/touhou/shiki_eiki/crown.py b/nhf/touhou/shiki_eiki/crown.py new file mode 100644 index 0000000..3ae1dcc --- /dev/null +++ b/nhf/touhou/shiki_eiki/crown.py @@ -0,0 +1,388 @@ +import math +from dataclasses import dataclass, field +import cadquery as Cq +from nhf import Material, Role +from nhf.build import Model, target, assembly, TargetKind +import nhf.utils + +@dataclass +class Crown(Model): + + facets: int = 5 + # Lower circumference + base_circ: float = 538.0 + # Upper circumference + tilt_circ: float = 640.0 + height: float = 120.0 + + margin: float = 10.0 + + thickness: float = 0.4 # 26 Gauge + + material: Material = Material.METAL_BRASS + + def __post_init__(self): + super().__init__(name="crown") + + assert self.tilt_circ > self.base_circ + assert self.facet_width_upper / 2 > self.height / 2, "Top angle must be > 90 degrees" + + @property + def facet_width_lower(self): + return self.base_circ / self.facets + @property + def facet_width_upper(self): + return self.tilt_circ / self.facets + + def profile_base(self) -> Cq.Sketch: + # Generate the pentagonal shape + + dx_l = self.facet_width_lower + dx_u = self.facet_width_upper + dy = self.height + return ( + Cq.Sketch() + .polygon([ + (dx_l/2, 0), + (dx_u/2, dy/2), + (0, dy), + (-dx_u/2, dy/2), + (-dx_l/2, 0), + ]) + ) + + @target(name="side", kind=TargetKind.DXF) + def profile_side(self) -> Cq.Sketch: + dy = self.facet_width_upper * 0.1 + x_side = self.facet_width_upper + y_tip = self.height - self.margin + + eye = ( + Cq.Sketch() + .segment( + (0, y_tip), + (dy, y_tip - dy), + ) + .segment( + (0, y_tip), + (-dy, y_tip - dy), + ) + .bezier([ + (dy, y_tip - dy), + (0, y_tip - dy/2), + (0, y_tip - dy/2), + (-dy, y_tip - dy), + ]) + .assemble() + ) + return ( + self.profile_base() + .boolean(eye, mode='s') + ) + + @target(name="dot", kind=TargetKind.DXF) + def profile_dot(self) -> Cq.Sketch: + return ( + Cq.Sketch() + .circle(self.margin / 2) + ) + + @target(name="front", kind=TargetKind.DXF) + def profile_front(self) -> Cq.Sketch: + dx_l = self.facet_width_lower + dx_u = self.facet_width_upper + dy = self.height + + window_length = dy / 5 + window_height = self.margin / 2 + window = ( + Cq.Sketch() + .rect(window_length, window_height) + ) + window_p1 = Cq.Location.from2d( + dx_u/2 - self.margin - window_length * 0.4, + dy/2 + self.margin/2, + math.degrees(math.atan2(dy/2, -dx_u/2)), + ) + window_p2 = Cq.Location.from2d( + dx_l/2 - self.margin + window_length * 0.15, + window_length/2 + self.margin, + math.degrees(math.atan2(dy/2, (dx_u-dx_l)/2)), + ) + + # Carve the scale + z = dy * 1/64 # "Pen" Thickness + scale_pan_x = dx_l / 2 * 0.6 + scale_pan_y = dy / 2 * 0.7 + pan_dx = dx_l * 1/4 + pan_dy = dy * 1/16 + + scale_pan = ( + Cq.Sketch() + .arc( + (- pan_dx/2, pan_dy), + (0, 0), + (+ pan_dx/2, pan_dy), + ) + .segment( + (+pan_dx/2, pan_dy), + (+pan_dx/2 - z, pan_dy), + ) + .arc( + (-pan_dx/2 + z, pan_dy), + (0, z), + (+pan_dx/2 - z, pan_dy), + ) + .segment( + (-pan_dx/2, pan_dy), + (-pan_dx/2 + z, pan_dy), + ) + .assemble() + ) + loc_scale_pan = Cq.Location.from2d(scale_pan_x, scale_pan_y) + loc_scale_pan2 = Cq.Location.from2d(-scale_pan_x, scale_pan_y) + + scale_base_y = dy / 2 * 0.36 + scale_base_x = dx_l / 10 + assert scale_base_y < scale_pan_y + assert scale_base_x < scale_pan_x + + scale_body = ( + Cq.Sketch() + .arc( + (scale_pan_x, scale_pan_y), + (0, scale_base_y), + (-scale_pan_x, scale_pan_y), + ) + .segment( + (-scale_pan_x, scale_pan_y), + (-scale_pan_x+z, scale_pan_y+z), + ) + .arc( + (scale_pan_x - z, scale_pan_y+z), + (0, scale_base_y + z), + (-scale_pan_x + z, scale_pan_y+z), + ) + .segment( + (scale_pan_x, scale_pan_y), + (scale_pan_x-z, scale_pan_y+z), + ) + .assemble() + .polygon([ + (scale_base_x, scale_base_y + z/2), + (scale_base_x, self.margin), + (scale_base_x-z, self.margin), + (scale_base_x-z, scale_base_y-z), + + (-scale_base_x+z, scale_base_y-z), + (-scale_base_x+z, self.margin), + (-scale_base_x, self.margin), + (-scale_base_x, scale_base_y + z/2), + ], mode='a') + ) + + # Needle + needle_y_top = dy - self.margin + needle_y_mid = dy * 0.7 + needle_dx = scale_base_x * 2 + y_shoulder = needle_y_mid - z * 2 + needle = ( + Cq.Sketch() + .segment( + (0, needle_y_mid), + (z, y_shoulder), + ) + .segment( + (z, y_shoulder), + (z, scale_base_y), + ) + .segment( + (z, scale_base_y), + (-z, scale_base_y), + ) + .segment( + (-z, y_shoulder), + (-z, scale_base_y), + ) + .segment( + (-z, y_shoulder), + (0, needle_y_mid), + ) + .assemble() + ) + z2 = z * 2 + y1 = needle_y_mid + z2 + needle_head = ( + Cq.Sketch() + .segment( + (z, needle_y_mid), + (z, y1), + ) + .segment( + (-z, needle_y_mid), + (-z, y1), + ) + # Outer edge + .bezier([ + (0, needle_y_top), + (0, (needle_y_top + needle_y_mid)/2), + (needle_dx, (needle_y_top + needle_y_mid)/2), + (z, needle_y_mid), + ]) + .bezier([ + (0, needle_y_top), + (0, (needle_y_top + needle_y_mid)/2), + (-needle_dx, (needle_y_top + needle_y_mid)/2), + (-z, needle_y_mid), + ]) + # Inner edge + .bezier([ + (0, needle_y_top - z2), + (0, (needle_y_top + needle_y_mid)/2), + (needle_dx-z2*2, (needle_y_top + needle_y_mid)/2), + (z, y1), + ]) + .bezier([ + (0, needle_y_top - z2), + (0, (needle_y_top + needle_y_mid)/2), + (-needle_dx+z2*2, (needle_y_top + needle_y_mid)/2), + (-z, y1), + ]) + .assemble() + ) + + return ( + self.profile_base() + .boolean(window.moved(window_p1), mode='s') + .boolean(window.moved(window_p1.flip_x()), mode='s') + .boolean(window.moved(window_p2), mode='s') + .boolean(window.moved(window_p2.flip_x()), mode='s') + .boolean(scale_pan.moved(loc_scale_pan), mode='s') + .boolean(scale_pan.moved(loc_scale_pan2), mode='s') + .boolean(scale_body, mode='s') + .boolean(needle, mode='s') + .boolean(needle_head, mode='s') + .clean() + ) + + @target(name="side-guard", kind=TargetKind.DXF) + def profile_side_guard(self) -> Cq.Sketch: + dx = self.facet_width_lower / 2 + dy = self.height + + # Main control points + p_mid = Cq.Location.from2d(0, 0.5 * dy) + p_mid_v = Cq.Location.from2d(10/57 * dx, 0) + p_top1 = Cq.Location.from2d(0.408 * dx, 5/24 * dy) + p_top1_v = Cq.Location.from2d(0.13 * dx, 0) + p_top2 = Cq.Location.from2d(0.737 * dx, 0.255 * dy) + p_top2_c1 = p_top2 * Cq.Location.from2d(-0.105 * dx, 0.033 * dy) + p_top2_c2 = p_top2 * Cq.Location.from2d(-0.053 * dx, -0.09 * dy) + p_top3 = Cq.Location.from2d(0.929 * dx, 0.145 * dy) + p_top3_v = Cq.Location.from2d(0.066 * dx, 0.033 * dy) + p_top4 = Cq.Location.from2d(0.85 * dx, 0.374 * dy) + p_top4_v = Cq.Location.from2d(-0.053 * dx, 0.008 * dy) + p_top5 = Cq.Location.from2d(0.54 * dx, 0.349 * dy) + p_top5_c1 = p_top5 * Cq.Location.from2d(0.103 * dx, 0.017 * dy) + p_top5_c2 = p_top5 * Cq.Location.from2d(0.158 * dx, 0.034 * dy) + p_base_c = Cq.Location.from2d(1.245 * dx, 0.55 * dy) + p_base = Cq.Location.from2d(dx, 0) + + bezier_groups = [ + [ + p_base, + p_base_c, + p_top5_c2, + p_top5, + ], + [ + p_top5, + p_top5_c1, + p_top4 * p_top4_v, + p_top4, + ], + [ + p_top4, + p_top4 * p_top4_v.inverse.scale(4), + p_top3 * p_top3_v, + p_top3, + ], + [ + p_top3, + p_top3 * p_top3_v.inverse, + p_top2_c2, + p_top2, + ], + [ + p_top2, + p_top2_c1, + p_top1 * p_top1_v, + p_top1, + ], + [ + p_top1, + p_top1 * p_top1_v.inverse, + p_mid * p_mid_v, + p_mid, + ], + ] + sketch = ( + Cq.Sketch() + .segment( + p_base.to2d_pos(), + p_base.flip_x().to2d_pos(), + ) + ) + for bezier_group in bezier_groups: + sketch = ( + sketch + .bezier([p.to2d_pos() for p in bezier_group]) + .bezier([p.flip_x().to2d_pos() for p in bezier_group]) + ) + return sketch.assemble() + + def assembly(self) -> Cq.Assembly: + front = ( + Cq.Workplane('XY') + .placeSketch(self.profile_front()) + .extrude(self.thickness) + ) + side = ( + Cq.Workplane('XY') + .placeSketch(self.profile_side()) + .extrude(self.thickness) + ) + side_guard = ( + Cq.Workplane('XY') + .placeSketch(self.profile_side_guard()) + .extrude(self.thickness) + ) + assembly = ( + Cq.Assembly() + .addS( + front, + name="front", + material=self.material, + role=Role.DECORATION, + ) + ) + for i, pos in enumerate([-2, -1, 1, 2]): + x = self.facet_width_upper * pos + assembly = ( + assembly + .addS( + side, + name=f"side{i}", + material=self.material, + role=Role.DECORATION, + loc=Cq.Location.from2d(x, 0), + ) + .addS( + side_guard, + name=f"guard{i}", + material=self.material, + role=Role.DECORATION, + loc=Cq.Location(x, 0, self.thickness), + ) + ) + return assembly diff --git a/nhf/touhou/shiki_eiki/epaulette-hi.dxf b/nhf/touhou/shiki_eiki/epaulette-hi.dxf new file mode 100644 index 0000000..e7a188b --- /dev/null +++ b/nhf/touhou/shiki_eiki/epaulette-hi.dxf @@ -0,0 +1,1464 @@ + 0 +SECTION + 2 +HEADER + 9 +$ACADVER + 1 +AC1014 + 9 +$HANDSEED + 5 +FFFF + 9 +$INSUNITS + 70 + 4 + 9 +$MEASUREMENT + 70 + 1 + 0 +ENDSEC + 0 +SECTION + 2 +TABLES + 0 +TABLE + 2 +VPORT + 5 +8 +330 +0 +100 +AcDbSymbolTable + 70 + 4 + 0 +VPORT + 5 +2E +330 +8 +100 +AcDbSymbolTableRecord +100 +AcDbViewportTableRecord + 2 +*ACTIVE + 70 + 0 + 10 +0.0 + 20 +0.0 + 11 +1.0 + 21 +1.0 + 12 +210.0 + 22 +148.5 + 13 +0.0 + 23 +0.0 + 14 +10.0 + 24 +10.0 + 15 +10.0 + 25 +10.0 + 16 +0.0 + 26 +0.0 + 36 +1.0 + 17 +0.0 + 27 +0.0 + 37 +0.0 + 40 +341.0 + 41 +1.24 + 42 +50.0 + 43 +0.0 + 44 +0.0 + 50 +0.0 + 51 +0.0 + 71 + 0 + 72 + 100 + 73 + 1 + 74 + 3 + 75 + 0 + 76 + 0 + 77 + 0 + 78 + 0 + 0 +ENDTAB + 0 +TABLE + 2 +LTYPE + 5 +5 +330 +0 +100 +AcDbSymbolTable + 70 + 1 + 0 +LTYPE + 5 +14 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +BYBLOCK + 70 + 0 + 3 + + 72 + 65 + 73 + 0 + 40 +0.0 + 0 +LTYPE + 5 +15 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +BYLAYER + 70 + 0 + 3 + + 72 + 65 + 73 + 0 + 40 +0.0 + 0 +LTYPE + 5 +16 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +CONTINUOUS + 70 + 0 + 3 +Solid line + 72 + 65 + 73 + 0 + 40 +0.0 + 0 +ENDTAB + 0 +TABLE + 2 +LAYER + 5 +2 +100 +AcDbSymbolTable + 70 +5 + 0 +LAYER + 5 +50 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +0 + 70 +0 + 6 +CONTINUOUS + 0 +LAYER + 5 +51 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +Outline + 70 +0 + 6 +CONTINUOUS + 0 +LAYER + 5 +52 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +Cut + 70 +0 + 6 +CONTINUOUS + 0 +LAYER + 5 +53 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +Ze + 70 +0 + 6 +CONTINUOUS + 0 +LAYER + 5 +54 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +Hi + 70 +0 + 6 +CONTINUOUS + 0 +ENDTAB + 0 +TABLE + 2 +STYLE + 5 +3 +330 +0 +100 +AcDbSymbolTable + 70 + 1 + 0 +STYLE + 5 +11 +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbTextStyleTableRecord + 2 +STANDARD + 70 + 0 + 40 +0.0 + 41 +1.0 + 50 +0.0 + 71 + 0 + 42 +2.5 + 3 +txt + 4 + + 0 +ENDTAB + 0 +TABLE + 2 +VIEW + 5 +6 +330 +0 +100 +AcDbSymbolTable + 70 + 0 + 0 +ENDTAB + 0 +TABLE + 2 +UCS + 5 +7 +330 +0 +100 +AcDbSymbolTable + 70 + 0 + 0 +ENDTAB + 0 +TABLE + 2 +APPID + 5 +9 +330 +0 +100 +AcDbSymbolTable + 70 + 2 + 0 +APPID + 5 +12 +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +ACAD + 70 + 0 + 0 +ENDTAB + 0 +TABLE + 2 +DIMSTYLE + 5 +A +330 +0 +100 +AcDbSymbolTable + 70 + 1 + 0 +DIMSTYLE +105 +27 +330 +A +100 +AcDbSymbolTableRecord +100 +AcDbDimStyleTableRecord + 2 +ISO-25 + 70 + 0 + 3 + + 4 + + 5 + + 6 + + 7 + + 40 +1.0 + 41 +2.5 + 42 +0.625 + 43 +3.75 + 44 +1.25 + 45 +0.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 +140 +2.5 +141 +2.5 +142 +0.0 +143 +0.03937007874016 +144 +1.0 +145 +0.0 +146 +1.0 +147 +0.625 + 71 + 0 + 72 + 0 + 73 + 0 + 74 + 0 + 75 + 0 + 76 + 0 + 77 + 1 + 78 + 8 +170 + 0 +171 + 3 +172 + 1 +173 + 0 +174 + 0 +175 + 0 +176 + 0 +177 + 0 +178 + 0 +270 + 2 +271 + 2 +272 + 2 +273 + 2 +274 + 3 +340 +11 +275 + 0 +280 + 0 +281 + 0 +282 + 0 +283 + 0 +284 + 8 +285 + 0 +286 + 0 +287 + 3 +288 + 0 + 0 +ENDTAB + 0 +TABLE + 2 +BLOCK_RECORD + 5 +1 +330 +0 +100 +AcDbSymbolTable + 70 + 1 + 0 +BLOCK_RECORD + 5 +1F +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*MODEL_SPACE + 0 +BLOCK_RECORD + 5 +1B +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*PAPER_SPACE + 0 +ENDTAB + 0 +ENDSEC + 0 +SECTION + 2 +BLOCKS + 0 +BLOCK + 5 +20 +330 +1F +100 +AcDbEntity + 8 +0 +100 +AcDbBlockBegin + 2 +*MODEL_SPACE + 70 + 0 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 3 +*MODEL_SPACE + 1 + + 0 +ENDBLK + 5 +21 +330 +1F +100 +AcDbEntity + 8 +0 +100 +AcDbBlockEnd + 0 +BLOCK + 5 +1C +330 +1B +100 +AcDbEntity + 67 + 1 + 8 +0 +100 +AcDbBlockBegin + 2 +*PAPER_SPACE + 1 + + 0 +ENDBLK + 5 +1D +330 +1B +100 +AcDbEntity + 67 + 1 + 8 +0 +100 +AcDbBlockEnd + 0 +ENDSEC + 0 +SECTION + 2 +ENTITIES + 0 +SPLINE + 5 +100 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +50.000049 + 20 +99.867708 + 30 +0.0 + 10 +22.458851 + 20 +99.867699 + 30 +0.0 + 10 +0.132301 + 20 +77.541149 + 30 +0.0 + 10 +0.132292 + 20 +49.999951 + 30 +0.0 + 0 +SPLINE + 5 +101 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +0.132292 + 20 +49.999951 + 30 +0.0 + 10 +0.132302 + 20 +22.458753 + 30 +0.0 + 10 +22.458851 + 20 +0.132204 + 30 +0.0 + 10 +50.000049 + 20 +0.132194 + 30 +0.0 + 0 +SPLINE + 5 +102 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +50.000049 + 20 +0.132194 + 30 +0.0 + 10 +77.541247 + 20 +0.132204 + 30 +0.0 + 10 +99.867796 + 20 +22.458753 + 30 +0.0 + 10 +99.867806 + 20 +49.999951 + 30 +0.0 + 0 +SPLINE + 5 +103 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +99.867806 + 20 +49.999951 + 30 +0.0 + 10 +99.867796 + 20 +77.541149 + 30 +0.0 + 10 +77.541247 + 20 +99.867698 + 30 +0.0 + 10 +50.000049 + 20 +99.867708 + 30 +0.0 + 0 +SPLINE + 5 +104 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +50.000049 + 20 +94.880933 + 30 +0.0 + 10 +74.787127 + 20 +94.880924 + 30 +0.0 + 10 +94.881021 + 20 +74.787029 + 30 +0.0 + 10 +94.881030 + 20 +49.999951 + 30 +0.0 + 0 +SPLINE + 5 +105 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +94.881030 + 20 +49.999951 + 30 +0.0 + 10 +94.881021 + 20 +25.212873 + 30 +0.0 + 10 +74.787127 + 20 +5.118979 + 30 +0.0 + 10 +50.000049 + 20 +5.118970 + 30 +0.0 + 0 +SPLINE + 5 +106 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +50.000049 + 20 +5.118970 + 30 +0.0 + 10 +25.212971 + 20 +5.118979 + 30 +0.0 + 10 +5.119076 + 20 +25.212873 + 30 +0.0 + 10 +5.119067 + 20 +49.999951 + 30 +0.0 + 0 +SPLINE + 5 +107 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +5.119067 + 20 +49.999951 + 30 +0.0 + 10 +5.119076 + 20 +74.787029 + 30 +0.0 + 10 +25.212971 + 20 +94.880924 + 30 +0.0 + 10 +50.000049 + 20 +94.880933 + 30 +0.0 + 0 +LWPOLYLINE + 5 +108 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbPolyline + 90 +1 + 70 +1 + 10 +50.000049 + 20 +99.867708 + 30 +0.0 + 0 +LWPOLYLINE + 5 +109 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbPolyline + 90 +1 + 70 +1 + 10 +50.000049 + 20 +94.880933 + 30 +0.0 + 0 +LWPOLYLINE + 5 +10a +100 +AcDbEntity + 8 +Hi + 62 +5 +100 +AcDbPolyline + 90 +16 + 70 +1 + 10 +54.786837 + 20 +80.000187 + 30 +0.0 + 10 +54.786837 + 20 +20.000232 + 30 +0.0 + 10 +59.999955 + 20 +20.000232 + 30 +0.0 + 10 +59.999955 + 20 +28.943872 + 30 +0.0 + 10 +79.708830 + 20 +28.943872 + 30 +0.0 + 10 +79.708830 + 20 +37.224504 + 30 +0.0 + 10 +59.999955 + 20 +37.224504 + 30 +0.0 + 10 +59.999955 + 20 +45.859635 + 30 +0.0 + 10 +79.708830 + 20 +45.859635 + 30 +0.0 + 10 +79.708830 + 20 +54.140267 + 30 +0.0 + 10 +59.999955 + 20 +54.140267 + 30 +0.0 + 10 +59.999955 + 20 +62.775399 + 30 +0.0 + 10 +79.708830 + 20 +62.775399 + 30 +0.0 + 10 +79.708830 + 20 +71.056030 + 30 +0.0 + 10 +59.999955 + 20 +71.056030 + 30 +0.0 + 10 +59.999955 + 20 +80.000187 + 30 +0.0 + 0 +LWPOLYLINE + 5 +10b +100 +AcDbEntity + 8 +Hi + 62 +5 +100 +AcDbPolyline + 90 +16 + 70 +1 + 10 +45.245359 + 20 +20.000023 + 30 +0.0 + 10 +45.245359 + 20 +79.999978 + 30 +0.0 + 10 +40.032241 + 20 +79.999978 + 30 +0.0 + 10 +40.032241 + 20 +71.056338 + 30 +0.0 + 10 +20.323366 + 20 +71.056338 + 30 +0.0 + 10 +20.323366 + 20 +62.775706 + 30 +0.0 + 10 +40.032241 + 20 +62.775706 + 30 +0.0 + 10 +40.032241 + 20 +54.140575 + 30 +0.0 + 10 +20.323366 + 20 +54.140575 + 30 +0.0 + 10 +20.323366 + 20 +45.859943 + 30 +0.0 + 10 +40.032241 + 20 +45.859943 + 30 +0.0 + 10 +40.032241 + 20 +37.224811 + 30 +0.0 + 10 +20.323366 + 20 +37.224811 + 30 +0.0 + 10 +20.323366 + 20 +28.944180 + 30 +0.0 + 10 +40.032241 + 20 +28.944180 + 30 +0.0 + 10 +40.032241 + 20 +20.000023 + 30 +0.0 + 0 +ENDSEC + 0 +SECTION + 2 +OBJECTS + 0 +DICTIONARY + 5 +C +330 +0 +100 +AcDbDictionary + 3 +ACAD_GROUP +350 +D + 3 +ACAD_MLINESTYLE +350 +17 + 0 +DICTIONARY + 5 +D +330 +C +100 +AcDbDictionary + 0 +DICTIONARY + 5 +1A +330 +C +100 +AcDbDictionary + 0 +DICTIONARY + 5 +17 +330 +C +100 +AcDbDictionary + 3 +STANDARD +350 +18 + 0 +DICTIONARY + 5 +19 +330 +C +100 +AcDbDictionary + 0 +ENDSEC + 0 +EOF diff --git a/nhf/touhou/shiki_eiki/epaulette-ze.dxf b/nhf/touhou/shiki_eiki/epaulette-ze.dxf new file mode 100644 index 0000000..cc43962 --- /dev/null +++ b/nhf/touhou/shiki_eiki/epaulette-ze.dxf @@ -0,0 +1,2426 @@ + 0 +SECTION + 2 +HEADER + 9 +$ACADVER + 1 +AC1014 + 9 +$HANDSEED + 5 +FFFF + 9 +$INSUNITS + 70 + 4 + 9 +$MEASUREMENT + 70 + 1 + 0 +ENDSEC + 0 +SECTION + 2 +TABLES + 0 +TABLE + 2 +VPORT + 5 +8 +330 +0 +100 +AcDbSymbolTable + 70 + 4 + 0 +VPORT + 5 +2E +330 +8 +100 +AcDbSymbolTableRecord +100 +AcDbViewportTableRecord + 2 +*ACTIVE + 70 + 0 + 10 +0.0 + 20 +0.0 + 11 +1.0 + 21 +1.0 + 12 +210.0 + 22 +148.5 + 13 +0.0 + 23 +0.0 + 14 +10.0 + 24 +10.0 + 15 +10.0 + 25 +10.0 + 16 +0.0 + 26 +0.0 + 36 +1.0 + 17 +0.0 + 27 +0.0 + 37 +0.0 + 40 +341.0 + 41 +1.24 + 42 +50.0 + 43 +0.0 + 44 +0.0 + 50 +0.0 + 51 +0.0 + 71 + 0 + 72 + 100 + 73 + 1 + 74 + 3 + 75 + 0 + 76 + 0 + 77 + 0 + 78 + 0 + 0 +ENDTAB + 0 +TABLE + 2 +LTYPE + 5 +5 +330 +0 +100 +AcDbSymbolTable + 70 + 1 + 0 +LTYPE + 5 +14 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +BYBLOCK + 70 + 0 + 3 + + 72 + 65 + 73 + 0 + 40 +0.0 + 0 +LTYPE + 5 +15 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +BYLAYER + 70 + 0 + 3 + + 72 + 65 + 73 + 0 + 40 +0.0 + 0 +LTYPE + 5 +16 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +CONTINUOUS + 70 + 0 + 3 +Solid line + 72 + 65 + 73 + 0 + 40 +0.0 + 0 +ENDTAB + 0 +TABLE + 2 +LAYER + 5 +2 +100 +AcDbSymbolTable + 70 +5 + 0 +LAYER + 5 +50 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +0 + 70 +0 + 6 +CONTINUOUS + 0 +LAYER + 5 +51 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +Outline + 70 +0 + 6 +CONTINUOUS + 0 +LAYER + 5 +52 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +Cut + 70 +0 + 6 +CONTINUOUS + 0 +LAYER + 5 +53 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +Ze + 70 +0 + 6 +CONTINUOUS + 0 +LAYER + 5 +54 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +Hi + 70 +0 + 6 +CONTINUOUS + 0 +ENDTAB + 0 +TABLE + 2 +STYLE + 5 +3 +330 +0 +100 +AcDbSymbolTable + 70 + 1 + 0 +STYLE + 5 +11 +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbTextStyleTableRecord + 2 +STANDARD + 70 + 0 + 40 +0.0 + 41 +1.0 + 50 +0.0 + 71 + 0 + 42 +2.5 + 3 +txt + 4 + + 0 +ENDTAB + 0 +TABLE + 2 +VIEW + 5 +6 +330 +0 +100 +AcDbSymbolTable + 70 + 0 + 0 +ENDTAB + 0 +TABLE + 2 +UCS + 5 +7 +330 +0 +100 +AcDbSymbolTable + 70 + 0 + 0 +ENDTAB + 0 +TABLE + 2 +APPID + 5 +9 +330 +0 +100 +AcDbSymbolTable + 70 + 2 + 0 +APPID + 5 +12 +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +ACAD + 70 + 0 + 0 +ENDTAB + 0 +TABLE + 2 +DIMSTYLE + 5 +A +330 +0 +100 +AcDbSymbolTable + 70 + 1 + 0 +DIMSTYLE +105 +27 +330 +A +100 +AcDbSymbolTableRecord +100 +AcDbDimStyleTableRecord + 2 +ISO-25 + 70 + 0 + 3 + + 4 + + 5 + + 6 + + 7 + + 40 +1.0 + 41 +2.5 + 42 +0.625 + 43 +3.75 + 44 +1.25 + 45 +0.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 +140 +2.5 +141 +2.5 +142 +0.0 +143 +0.03937007874016 +144 +1.0 +145 +0.0 +146 +1.0 +147 +0.625 + 71 + 0 + 72 + 0 + 73 + 0 + 74 + 0 + 75 + 0 + 76 + 0 + 77 + 1 + 78 + 8 +170 + 0 +171 + 3 +172 + 1 +173 + 0 +174 + 0 +175 + 0 +176 + 0 +177 + 0 +178 + 0 +270 + 2 +271 + 2 +272 + 2 +273 + 2 +274 + 3 +340 +11 +275 + 0 +280 + 0 +281 + 0 +282 + 0 +283 + 0 +284 + 8 +285 + 0 +286 + 0 +287 + 3 +288 + 0 + 0 +ENDTAB + 0 +TABLE + 2 +BLOCK_RECORD + 5 +1 +330 +0 +100 +AcDbSymbolTable + 70 + 1 + 0 +BLOCK_RECORD + 5 +1F +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*MODEL_SPACE + 0 +BLOCK_RECORD + 5 +1B +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*PAPER_SPACE + 0 +ENDTAB + 0 +ENDSEC + 0 +SECTION + 2 +BLOCKS + 0 +BLOCK + 5 +20 +330 +1F +100 +AcDbEntity + 8 +0 +100 +AcDbBlockBegin + 2 +*MODEL_SPACE + 70 + 0 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 3 +*MODEL_SPACE + 1 + + 0 +ENDBLK + 5 +21 +330 +1F +100 +AcDbEntity + 8 +0 +100 +AcDbBlockEnd + 0 +BLOCK + 5 +1C +330 +1B +100 +AcDbEntity + 67 + 1 + 8 +0 +100 +AcDbBlockBegin + 2 +*PAPER_SPACE + 1 + + 0 +ENDBLK + 5 +1D +330 +1B +100 +AcDbEntity + 67 + 1 + 8 +0 +100 +AcDbBlockEnd + 0 +ENDSEC + 0 +SECTION + 2 +ENTITIES + 0 +SPLINE + 5 +100 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +50.000049 + 20 +99.867708 + 30 +0.0 + 10 +22.458851 + 20 +99.867699 + 30 +0.0 + 10 +0.132301 + 20 +77.541149 + 30 +0.0 + 10 +0.132292 + 20 +49.999951 + 30 +0.0 + 0 +SPLINE + 5 +101 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +0.132292 + 20 +49.999951 + 30 +0.0 + 10 +0.132302 + 20 +22.458753 + 30 +0.0 + 10 +22.458851 + 20 +0.132204 + 30 +0.0 + 10 +50.000049 + 20 +0.132194 + 30 +0.0 + 0 +SPLINE + 5 +102 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +50.000049 + 20 +0.132194 + 30 +0.0 + 10 +77.541247 + 20 +0.132204 + 30 +0.0 + 10 +99.867796 + 20 +22.458753 + 30 +0.0 + 10 +99.867806 + 20 +49.999951 + 30 +0.0 + 0 +SPLINE + 5 +103 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +99.867806 + 20 +49.999951 + 30 +0.0 + 10 +99.867796 + 20 +77.541149 + 30 +0.0 + 10 +77.541247 + 20 +99.867698 + 30 +0.0 + 10 +50.000049 + 20 +99.867708 + 30 +0.0 + 0 +SPLINE + 5 +104 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +50.000049 + 20 +94.880933 + 30 +0.0 + 10 +74.787127 + 20 +94.880924 + 30 +0.0 + 10 +94.881021 + 20 +74.787029 + 30 +0.0 + 10 +94.881030 + 20 +49.999951 + 30 +0.0 + 0 +SPLINE + 5 +105 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +94.881030 + 20 +49.999951 + 30 +0.0 + 10 +94.881021 + 20 +25.212873 + 30 +0.0 + 10 +74.787127 + 20 +5.118979 + 30 +0.0 + 10 +50.000049 + 20 +5.118970 + 30 +0.0 + 0 +SPLINE + 5 +106 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +50.000049 + 20 +5.118970 + 30 +0.0 + 10 +25.212971 + 20 +5.118979 + 30 +0.0 + 10 +5.119076 + 20 +25.212873 + 30 +0.0 + 10 +5.119067 + 20 +49.999951 + 30 +0.0 + 0 +SPLINE + 5 +107 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +5.119067 + 20 +49.999951 + 30 +0.0 + 10 +5.119076 + 20 +74.787029 + 30 +0.0 + 10 +25.212971 + 20 +94.880924 + 30 +0.0 + 10 +50.000049 + 20 +94.880933 + 30 +0.0 + 0 +LWPOLYLINE + 5 +108 +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbPolyline + 90 +1 + 70 +1 + 10 +50.000049 + 20 +99.867708 + 30 +0.0 + 0 +SPLINE + 5 +109 +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +50.000049 + 20 +87.354777 + 30 +0.0 + 10 +31.310151 + 20 +87.344028 + 30 +0.0 + 10 +15.504624 + 20 +73.521864 + 30 +0.0 + 10 +13.002824 + 20 +55.000163 + 30 +0.0 + 0 +LWPOLYLINE + 5 +10a +100 +AcDbEntity + 8 +Outline + 62 +7 +100 +AcDbPolyline + 90 +1 + 70 +1 + 10 +50.000049 + 20 +94.880933 + 30 +0.0 + 0 +SPLINE + 5 +10b +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +86.997274 + 20 +55.000163 + 30 +0.0 + 10 +84.495474 + 20 +73.521864 + 30 +0.0 + 10 +68.689947 + 20 +87.344028 + 30 +0.0 + 10 +50.000049 + 20 +87.354777 + 30 +0.0 + 0 +LWPOLYLINE + 5 +10c +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbPolyline + 90 +6 + 70 +0 + 10 +13.002824 + 20 +55.000163 + 30 +0.0 + 10 +15.549438 + 20 +55.000163 + 30 +0.0 + 10 +18.019055 + 20 +55.000163 + 30 +0.0 + 10 +81.984143 + 20 +55.000163 + 30 +0.0 + 10 +83.422298 + 20 +55.000163 + 30 +0.0 + 10 +86.997274 + 20 +55.000163 + 30 +0.0 + 0 +SPLINE + 5 +10d +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +50.000049 + 20 +82.374202 + 30 +0.0 + 10 +58.239794 + 20 +82.362203 + 30 +0.0 + 10 +66.164913 + 20 +79.208825 + 30 +0.0 + 10 +72.160453 + 20 +73.556653 + 30 +0.0 + 0 +LWPOLYLINE + 5 +10e +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbPolyline + 90 +1 + 70 +1 + 10 +50.000049 + 20 +87.354777 + 30 +0.0 + 0 +SPLINE + 5 +10f +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +27.842745 + 20 +73.556653 + 30 +0.0 + 10 +33.837505 + 20 +79.208092 + 30 +0.0 + 10 +41.761375 + 20 +82.361414 + 30 +0.0 + 10 +50.000049 + 20 +82.374202 + 30 +0.0 + 0 +LWPOLYLINE + 5 +110 +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbPolyline + 90 +2 + 70 +0 + 10 +72.160453 + 20 +73.556653 + 30 +0.0 + 10 +27.842745 + 20 +73.556653 + 30 +0.0 + 0 +LWPOLYLINE + 5 +111 +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbPolyline + 90 +1 + 70 +1 + 10 +50.000049 + 20 +82.374202 + 30 +0.0 + 0 +SPLINE + 5 +112 +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +76.278031 + 20 +68.798287 + 30 +0.0 + 10 +78.258337 + 20 +66.042028 + 30 +0.0 + 10 +79.792920 + 20 +62.991520 + 30 +0.0 + 10 +80.825557 + 20 +59.758529 + 30 +0.0 + 0 +LWPOLYLINE + 5 +113 +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbPolyline + 90 +2 + 70 +0 + 10 +23.725167 + 20 +68.798287 + 30 +0.0 + 10 +76.278031 + 20 +68.798287 + 30 +0.0 + 0 +SPLINE + 5 +114 +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +19.177641 + 20 +59.758529 + 30 +0.0 + 10 +20.210278 + 20 +62.991520 + 30 +0.0 + 10 +21.744861 + 20 +66.042028 + 30 +0.0 + 10 +23.725167 + 20 +68.798287 + 30 +0.0 + 0 +LWPOLYLINE + 5 +115 +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbPolyline + 90 +2 + 70 +0 + 10 +80.825557 + 20 +59.758529 + 30 +0.0 + 10 +19.177641 + 20 +59.758529 + 30 +0.0 + 0 +LWPOLYLINE + 5 +116 +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbPolyline + 90 +1 + 70 +1 + 10 +23.725167 + 20 +68.798287 + 30 +0.0 + 0 +SPLINE + 5 +117 +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +21.704102 + 20 +26.193136 + 30 +0.0 + 10 +28.753604 + 20 +17.878949 + 30 +0.0 + 10 +39.099541 + 20 +13.081227 + 30 +0.0 + 10 +50.000049 + 20 +13.071456 + 30 +0.0 + 0 +SPLINE + 5 +118 +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +50.000049 + 20 +13.071456 + 30 +0.0 + 10 +58.459978 + 20 +13.097594 + 30 +0.0 + 10 +66.657490 + 20 +16.011070 + 30 +0.0 + 10 +73.236357 + 20 +21.329867 + 30 +0.0 + 0 +LWPOLYLINE + 5 +119 +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbPolyline + 90 +2 + 70 +0 + 10 +30.468424 + 20 +34.957458 + 30 +0.0 + 10 +21.704102 + 20 +26.193136 + 30 +0.0 + 0 +SPLINE + 5 +11a +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +70.147139 + 20 +24.747229 + 30 +0.0 + 10 +64.432180 + 20 +20.158004 + 30 +0.0 + 10 +57.329500 + 20 +17.642998 + 30 +0.0 + 10 +50.000049 + 20 +17.613298 + 30 +0.0 + 0 +SPLINE + 5 +11b +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +50.000049 + 20 +17.613298 + 30 +0.0 + 10 +42.031806 + 20 +17.643491 + 30 +0.0 + 10 +34.403936 + 20 +20.612407 + 30 +0.0 + 10 +28.529008 + 20 +25.870158 + 30 +0.0 + 0 +LWPOLYLINE + 5 +11c +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbPolyline + 90 +2 + 70 +0 + 10 +73.236357 + 20 +21.329867 + 30 +0.0 + 10 +70.147139 + 20 +24.747229 + 30 +0.0 + 0 +LWPOLYLINE + 5 +11d +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbPolyline + 90 +3 + 70 +0 + 10 +28.529008 + 20 +25.870158 + 30 +0.0 + 10 +34.042367 + 20 +31.383516 + 30 +0.0 + 10 +30.468424 + 20 +34.957458 + 30 +0.0 + 0 +SPLINE + 5 +11e +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +13.231234 + 20 +45.000258 + 30 +0.0 + 10 +13.474885 + 20 +43.310754 + 30 +0.0 + 10 +13.834717 + 20 +41.640056 + 30 +0.0 + 10 +14.308171 + 20 +40.000047 + 30 +0.0 + 0 +LWPOLYLINE + 5 +11f +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbPolyline + 90 +3 + 70 +0 + 10 +53.049475 + 20 +45.027128 + 30 +0.0 + 10 +53.049475 + 20 +45.000258 + 30 +0.0 + 10 +13.231234 + 20 +45.000258 + 30 +0.0 + 0 +SPLINE + 5 +120 +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbSpline + 70 +8 + 71 +3 + 72 +8 + 73 +4 + 74 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +0 + 40 +1 + 40 +1 + 40 +1 + 40 +1 + 10 +85.691927 + 20 +40.000045 + 30 +0.0 + 10 +86.165381 + 20 +41.640054 + 30 +0.0 + 10 +86.525213 + 20 +43.310752 + 30 +0.0 + 10 +86.768864 + 20 +45.000256 + 30 +0.0 + 0 +LWPOLYLINE + 5 +121 +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbPolyline + 90 +10 + 70 +0 + 10 +14.308171 + 20 +40.000047 + 30 +0.0 + 10 +53.049475 + 20 +40.000047 + 30 +0.0 + 10 +53.049475 + 20 +24.633024 + 30 +0.0 + 10 +58.103430 + 20 +24.633024 + 30 +0.0 + 10 +58.103430 + 20 +32.302840 + 30 +0.0 + 10 +73.684908 + 20 +32.302840 + 30 +0.0 + 10 +73.684908 + 20 +37.356795 + 30 +0.0 + 10 +58.103430 + 20 +37.356795 + 30 +0.0 + 10 +58.103430 + 20 +40.000045 + 30 +0.0 + 10 +85.691927 + 20 +40.000045 + 30 +0.0 + 0 +LWPOLYLINE + 5 +122 +100 +AcDbEntity + 8 +Ze + 62 +3 +100 +AcDbPolyline + 90 +4 + 70 +0 + 10 +86.768864 + 20 +45.000256 + 30 +0.0 + 10 +58.103430 + 20 +45.000256 + 30 +0.0 + 10 +58.103430 + 20 +45.027126 + 30 +0.0 + 10 +53.049475 + 20 +45.027128 + 30 +0.0 + 0 +ENDSEC + 0 +SECTION + 2 +OBJECTS + 0 +DICTIONARY + 5 +C +330 +0 +100 +AcDbDictionary + 3 +ACAD_GROUP +350 +D + 3 +ACAD_MLINESTYLE +350 +17 + 0 +DICTIONARY + 5 +D +330 +C +100 +AcDbDictionary + 0 +DICTIONARY + 5 +1A +330 +C +100 +AcDbDictionary + 0 +DICTIONARY + 5 +17 +330 +C +100 +AcDbDictionary + 3 +STANDARD +350 +18 + 0 +DICTIONARY + 5 +19 +330 +C +100 +AcDbDictionary + 0 +ENDSEC + 0 +EOF diff --git a/nhf/touhou/shiki_eiki/epaulette.py b/nhf/touhou/shiki_eiki/epaulette.py new file mode 100644 index 0000000..6150c64 --- /dev/null +++ b/nhf/touhou/shiki_eiki/epaulette.py @@ -0,0 +1,36 @@ +import math +from dataclasses import dataclass, field +from pathlib import Path +import cadquery as Cq +from nhf import Material, Role +from nhf.build import Model, target, assembly +import nhf.utils + +@dataclass +class Epaulette(Model): + + side: str + diam: float = 100.0 + thickness_brass: float = 0.4 # 26 Gauge + thickness_fabric: float = 0.3 + material: Material = Material.METAL_BRASS + + def __post_init__(self): + super().__init__(name=f"epaulette-{self.side}") + + def surface(self) -> Cq.Solid: + path = Path(__file__).resolve().parent / f"epaulette-{self.side}.dxf" + return ( + Cq.importers.importDXF(path).wires().toPending().extrude(self.thickness_brass) + ) + def assembly(self) -> Cq.Assembly: + assembly = ( + Cq.Assembly() + .addS( + self.surface(), + name="surface", + material=self.material, + role=Role.DECORATION, + ) + ) + return assembly diff --git a/nhf/touhou/shiki_eiki/rod.py b/nhf/touhou/shiki_eiki/rod.py new file mode 100644 index 0000000..cbf1f61 --- /dev/null +++ b/nhf/touhou/shiki_eiki/rod.py @@ -0,0 +1,587 @@ +import math +from dataclasses import dataclass, field +from typing import Tuple +import cadquery as Cq +from nhf import Material, Role +from nhf.build import Model, target, assembly, TargetKind +import nhf.utils + +@dataclass +class Rod(Model): + + width: float = 120.0 + length: float = 550.0 + length_tip: float = 100.0 + width_tail: float = 60.0 + margin: float = 10.0 + + thickness_top: float = 25.4 / 8 + # The side which has mounted hinges must be thicker + thickness_side: float = 25.4 / 4 + + height_internal: float = 30.0 + + material_shell: Material = Material.WOOD_BIRCH + + # Considering the glyph on the top ... + + # counted from middle to the bottom + fac_bar_top: float = 0.1 + # counted from bottom to top + fac_window_tsumi_bot: float = 0.63 + fac_window_tsumi_top: float = 0.88 + + fac_window_footer_bot: float = 0.36 + fac_window_footer_top: float = 0.6 + + # Considering the side ... + hinge_plate_pos: list[float] = field(default_factory=lambda: [0.1, 0.9]) + hinge_plate_length: float = 30.0 + hinge_hole_diam: float = 2.5 + # Hole distance to axis + hinge_hole_axis_dist: float = 12.5 / 2 + # Distance between holes + hinge_hole_sep: float = 15.89 + + # Consider the reference objects + ref_object_width: float = 50.0 + ref_object_length: float = 50.0 + + def __post_init__(self): + super().__init__(name="rod") + self.loc_core = Cq.Location.from2d(self.length - self.length_tip, 0) + assert self.length_tip * 2 < self.length + #assert self.fac_bar_top + self.fac_window_tsumi_top < 1 + assert self.fac_window_tsumi_bot < self.fac_window_tsumi_top + + @property + def length_tail(self): + return self.length - self.length_tip + + @property + def _reduced_tip_x(self): + return self.length_tip - self.margin + @property + def _reduced_y(self): + return self.width / 2 - self.margin + @property + def _reduced_tail_y(self): + return self.width_tail / 2 - self.margin + + def profile_points(self) -> list[Tuple[str, Tuple[float, float]]]: + """ + Points in polygon line order, labaled + """ + return [ + ("tip", (self.length, 0)), + ("mid_r", (self.length - self.length_tip, self.width/2)), + ("bot_r", (0, self.width_tail / 2)), + ("bot_l", (0, -self.width_tail / 2)), + ("mid_l", (self.length - self.length_tip, -self.width/2)), + ] + + def _window_tip(self) -> Cq.Sketch: + dxh = self._reduced_tip_x + dy = self._reduced_y + return ( + Cq.Sketch() + .segment( + (dxh, 0), + (dxh / 2, dy / 2), + ) + .bezier([ + (dxh / 2, dy / 2), + (dxh * 0.6, dy * 0.4), + (dxh * 0.6, -dy * 0.4), + (dxh / 2, -dy / 2), + ]) + .segment( + (dxh, 0), + ) + .assemble() + .moved(self.loc_core.to2d_pos()) + ) + def _window_eye(self, refl: bool = False) -> Cq.Sketch: + + sign = -1 if refl else 1 + dxh = self._reduced_tip_x + xm = dxh * 0.45 + dy = sign * self._reduced_y + fac = 0.05 + + p1 = Cq.Location.from2d(xm, sign * self.margin / 2) + p2 = Cq.Location.from2d(dxh * 0.1, sign * self.margin / 2) + p3 = Cq.Location.from2d(dxh * 0.15, dy * 0.55) + p4 = Cq.Location.from2d(dxh * 0.4, dy * 0.45) + d4 = Cq.Location.from2d(dxh * fac, -dy * fac) + + return ( + Cq.Sketch() + .segment( + p1.to2d_pos(), + p2.to2d_pos(), + ) + .bezier([ + p2.to2d_pos(), + (p2 * Cq.Location.from2d(0, dy * fac)).to2d_pos(), + (p3 * Cq.Location.from2d(-dxh * fac, -dy * fac)).to2d_pos(), + p3.to2d_pos(), + ]) + .bezier([ + p3.to2d_pos(), + (p3 * Cq.Location.from2d(0, dy * fac)).to2d_pos(), + (p4 * d4.inverse).to2d_pos(), + p4.to2d_pos(), + ]) + .bezier([ + p4.to2d_pos(), + (p4 * d4).to2d_pos(), + (p1 * Cq.Location.from2d(0, dy * fac)).to2d_pos(), + p1.to2d_pos(), + ]) + .assemble() + .moved(self.loc_core.to2d_pos()) + ) + + def _window_bar(self) -> Cq.Sketch(): + dxh = self._reduced_tip_x + dy = self._reduced_y + dyt = self._reduced_tail_y + dxt = self.length_tail + + ext_fac = self.fac_bar_top + + p_corner = Cq.Location.from2d(0, dy) + p_top = Cq.Location.from2d(0.3 * dxh, 0.7 * dy) + p_bot = Cq.Location.from2d(-ext_fac * dxt, dy + ext_fac * (dyt - dy)) + p_top_int = p_corner * Cq.Location.from2d(.05 * dxh, -.2 * dy) + p_top_ctrl = Cq.Location.from2d(0, .3 * dy) + p_bot_int = p_corner * Cq.Location.from2d(-.15 * dxh, -.2 * dy) + p_bot_ctrl = Cq.Location.from2d(-.25 * dxh, .3 * dy) + + return ( + Cq.Sketch() + .segment( + p_corner.to2d_pos(), + p_top.to2d_pos(), + ) + .segment(p_top_int.to2d_pos()) + .bezier([ + p_top_int.to2d_pos(), + p_top_ctrl.to2d_pos(), + p_top_ctrl.flip_y().to2d_pos(), + p_top_int.flip_y().to2d_pos(), + ]) + .segment(p_top.flip_y().to2d_pos()) + .segment(p_corner.flip_y().to2d_pos()) + .segment(p_bot.flip_y().to2d_pos()) + .segment(p_bot_int.flip_y().to2d_pos()) + .bezier([ + p_bot_int.flip_y().to2d_pos(), + p_bot_ctrl.flip_y().to2d_pos(), + p_bot_ctrl.to2d_pos(), + p_bot_int.to2d_pos(), + ]) + .segment(p_bot.to2d_pos()) + .segment(p_corner.to2d_pos()) + .assemble() + .moved(self.loc_core.to2d_pos()) + ) + + def _window_tsumi(self) -> Cq.Sketch: + dx = (self.fac_window_tsumi_top - self.fac_window_tsumi_bot) * self.length_tail + dy = 2 * self._reduced_y * 0.8 + loc = Cq.Location(self.fac_window_tsumi_bot * self.length_tail, 0) + + # Construction of the top part of the kanji + + dx_top = dx * 0.3 + x_top = dx - dx_top / 2 + dy_top = dy + dy_eye = dy * 0.2 + dy_border = (dy_top - 3 * dy_eye) / 4 + # The skip must follow 3 * eye + 4 * border = dy_top + y_skip = dy_eye + dy_border + + # Construction of the bottom part + x_bot = dx * 0.65 + y3 = dy * 0.4 + y2 = dy * 0.2 + y1 = dy * 0.1 + # x/y-centers of the legs + x_leg0 = x_bot / 14 + dx_leg = x_bot / 7 + y_leg = (y3 + y1) / 2 + + return ( + Cq.Sketch() + .push([(x_top, 0)]) + .rect(dx_top, dy_top) + .push([ + (x_top, -y_skip), + (x_top, 0), + (x_top, y_skip), + ]) + .rect(dx_top / 3, dy_eye, mode='s') + + # Construct the two sides + .push([ + (x_bot / 2, (y2 + y1) / 2), + (x_bot / 2, -(y2 + y1) / 2), + ]) + .rect(x_bot, y2 - y1, mode='a') + .push([ + (x_leg0 + dx_leg, y_leg), + (x_leg0 + 3 * dx_leg, y_leg), + (x_leg0 + 5 * dx_leg, y_leg), + (x_leg0 + dx_leg, -y_leg), + (x_leg0 + 3 * dx_leg, -y_leg), + (x_leg0 + 5 * dx_leg, -y_leg), + ]) + .rect(dx_leg, y3 - y1, mode='a') + + .moved(loc) + ) + + def _window_footer(self) -> Cq.Sketch: + x_bot = self.fac_window_footer_bot * self.length_tail + dx = (self.fac_window_footer_top - self.fac_window_footer_bot) * self.length_tail + loc = Cq.Location(x_bot, 0) + + dy = self._reduced_y * 0.8 + + # eyes + eye_y2 = dy * .5 + eye_y1 = dy * .2 + eye_width = eye_y2 - eye_y1 + eye_x = dx - eye_width / 2 + + # bar polygon + bar_x0 = dx * 0.65 + bar_dx = dx * 0.1 + bar_x1 = bar_x0 + bar_dx + bar_x2 = bar_x0 + bar_dx * 2 + bar_x3 = bar_x0 + bar_dx * 3 + bar_y1 = dy * .75 + assert bar_y1 > eye_y2 + bar_y2 = dy * .9 + assert bar_y1 < bar_y2 + + # Construction of the cross + cross_dx = dx * 0.7 / math.sqrt(2) + cross_dy = dy * 0.2 + + cross = ( + Cq.Sketch() + .rect(cross_dx, cross_dy) + .rect(cross_dy, cross_dx, mode='a') + .moved(Cq.Location.from2d(dx * 0.5, 0, 45)) + ) + return ( + Cq.Sketch() + # eyes + .push([ + (eye_x, (eye_y1 + eye_y2)/2), + (eye_x, -(eye_y1 + eye_y2)/2), + ]) + .rect(eye_width, eye_width, mode='a') + # middle bar + .push([(0,0)]) + .polygon([ + (bar_x1, bar_y1), + (bar_x0, bar_y1), + (bar_x0, bar_y2), + (bar_x3, bar_y2), + (bar_x3, bar_y1), + (bar_x2, bar_y1), + + (bar_x2, -bar_y1), + (bar_x3, -bar_y1), + (bar_x3, -bar_y2), + (bar_x0, -bar_y2), + (bar_x0, -bar_y1), + (bar_x1, -bar_y1), + ], mode='a') + # cross + .boolean(cross, mode='a') + + #.push([(0,0)]) + #.rect(10, 10) + + .moved(loc) + ) + + @target(name="bottom", kind=TargetKind.DXF) + def profile_bottom(self) -> Cq.Sketch: + return ( + Cq.Sketch() + .polygon([p for _, p in self.profile_points()]) + ) + + @target(name="top", kind=TargetKind.DXF) + def profile_top(self) -> Cq.Sketch: + return ( + self.profile_bottom() + .boolean(self._window_tip(), mode='s') + .boolean(self._window_eye(True), mode='s') + .boolean(self._window_eye(False), mode='s') + .boolean(self._window_bar(), mode='s') + .boolean(self._window_tsumi(), mode='s') + .boolean(self._window_footer(), mode='s') + ) + + def surface_top(self) -> Cq.Workplane: + return ( + Cq.Workplane('XY') + .placeSketch(self.profile_top()) + .extrude(self.thickness_top) + ) + + def surface_bottom(self) -> Cq.Workplane: + surface = ( + Cq.Workplane('XY') + .placeSketch(self.profile_bottom()) + .extrude(self.thickness_top) + ) + plane = surface.faces(">Z").workplane() + + for (name, p) in self.profile_points(): + plane.moveTo(*p).tagPlane(name) + + return surface + + # Properties of the side surfaces + + @property + def length_edge_tip(self): + return math.sqrt(self.length_tip ** 2 + (self.width / 2) ** 2) + @property + def length_edge_tail(self): + dw = (self.width - self.width_tail) / 2 + return math.sqrt(self.length_tail ** 2 + dw ** 2) + @property + def tip_incident_angle(self): + """ + Angle (measuring from vertical) at which the tip edge pieces must be + sanded in order to make them not collide into each other. + """ + return math.atan2(self.length_tip, self.width / 2) + @property + def shoulder_incident_angle(self) -> float: + angle_tip = math.atan2(self.width / 2, self.length_tip) + angle_tail = math.atan2((self.width - self.width_tail) / 2, self.length_tail) + return (angle_tip + angle_tail) / 2 + + @target(name="ref-tip") + def ref_tip(self) -> Cq.Workplane: + angle = self.tip_incident_angle + w = self.ref_object_width + drop = math.sin(angle) * w + profile = ( + Cq.Sketch() + .polygon([ + (0, 0), + (0, w), + (w, w), + (w - drop, 0), + ]) + ) + return ( + Cq.Workplane() + .placeSketch(profile) + .extrude(self.ref_object_length) + ) + @target(name="ref-shoulder") + def ref_shoulder(self) -> Cq.Workplane: + angle = self.shoulder_incident_angle + w = self.ref_object_width + drop = math.sin(angle) * w + profile = ( + Cq.Sketch() + .polygon([ + (0, 0), + (0, w), + (w, w), + (w - drop, 0), + ]) + ) + return ( + Cq.Workplane() + .placeSketch(profile) + .extrude(self.ref_object_length) + ) + + @target(name="side-tip-2x", kind=TargetKind.DXF) + def profile_side_tip(self): + l = self.length_edge_tip + w = self.height_internal + return ( + Cq.Sketch() + .push([(l/2, w/2)]) + .rect(l, w) + ) + @target(name="side-tail", kind=TargetKind.DXF) + def profile_side_tail(self): + """ + Plain side 2 with no hinge + """ + l = self.length_edge_tail + w = self.height_internal + return ( + Cq.Sketch() + .push([(l/2, w/2)]) + .rect(l, w) + ) + @target(name="side-hinge-plate", kind=TargetKind.DXF) + def profile_side_hinge_plate(self): + l = self.hinge_plate_length + w = self.height_internal / 2 + return ( + Cq.Sketch() + .push([(0, w/2)]) + .rect(l, w) + .push([ + (self.hinge_hole_sep / 2, self.hinge_hole_axis_dist), + (-self.hinge_hole_sep / 2, self.hinge_hole_axis_dist), + ]) + .circle(self.hinge_hole_diam / 2, mode='s') + ) + @target(name="side-tail-hinged", kind=TargetKind.DXF) + def profile_side_tail_hinged(self): + """ + Plain side 2 with no hinge + """ + l = self.length_edge_tail + w = self.height_internal + + # Holes for hinge + plate_pos = [ + (t * l, w * 3/4) for t in self.hinge_plate_pos + ] + hole_pos = [ + (self.hinge_hole_sep / 2, self.hinge_hole_axis_dist), + (-self.hinge_hole_sep / 2, self.hinge_hole_axis_dist), + ] + return ( + self.profile_side_tail() + .push(plate_pos) + .rect(self.hinge_plate_length, w/2, mode='s') + .push([ + (hx + px, w/2 - hy) + for hx, hy in hole_pos + for px, _ in plate_pos + ]) + .circle(self.hinge_hole_diam / 2, mode='s') + ) + @target(name="side-bot", kind=TargetKind.DXF) + def profile_side_bot(self): + l = self.width_tail - self.thickness_side * 2 + w = self.height_internal + return ( + Cq.Sketch() + .rect(l, w) + ) + + def surface_side_tip(self): + result = ( + Cq.Workplane('XY') + .placeSketch(self.profile_side_tip()) + .extrude(self.thickness_side) + ) + plane = result.faces(">Y").workplane() + plane.moveTo(0, 0).tagPlane("bot") + plane.moveTo(-self.length_edge_tip, 0).tagPlane("top") + return result + def surface_side_tail(self): + result = ( + Cq.Workplane('XY') + .placeSketch(self.profile_side_tail()) + .extrude(self.thickness_side) + ) + plane = result.faces(">Y").workplane() + plane.moveTo(0, 0).tagPlane("bot") + plane.moveTo(-self.length_edge_tail, 0).tagPlane("top") + return result + def surface_side_tail_hinged(self): + result = ( + Cq.Workplane('XY') + .placeSketch(self.profile_side_tail_hinged()) + .extrude(self.thickness_side) + ) + plane = result.faces(">Y").workplane() + plane.moveTo(0, 0).tagPlane("bot") + plane.moveTo(-self.length_edge_tail, 0).tagPlane("top") + return result + def surface_side_bot(self): + result = ( + Cq.Workplane('XY') + .placeSketch(self.profile_side_bot()) + .extrude(self.thickness_side) + ) + plane = result.faces(">Y").workplane() + plane.moveTo(self.width_tail / 2, 0).tagPlane("bot") + plane.moveTo(-self.width_tail / 2, 0).tagPlane("top") + return result + + @assembly() + def assembly(self) -> Cq.Assembly: + a = ( + Cq.Assembly() + .addS( + self.surface_top(), + name="top", + material=self.material_shell, + role=Role.STRUCTURE | Role.DECORATION + ) + .constrain("top", "Fixed") + .addS( + self.surface_bottom(), + name="bottom", + material=self.material_shell, + role=Role.STRUCTURE, + loc=Cq.Location(0, 0, -self.thickness_top - self.height_internal) + ) + .constrain("bottom", "Fixed") + .addS( + self.surface_side_tip(), + name="side_tip_l", + material=self.material_shell, + role=Role.STRUCTURE, + ) + .constrain("bottom?tip", "side_tip_l?top", "Plane") + .constrain("bottom?mid_l", "side_tip_l?bot", "Plane") + .addS( + self.surface_side_tip(), + name="side_tip_r", + material=self.material_shell, + role=Role.STRUCTURE, + ) + .constrain("bottom?tip", "side_tip_r?bot", "Plane") + .constrain("bottom?mid_r", "side_tip_r?top", "Plane") + .addS( + self.surface_side_tail(), + name="side_tail_l", + material=self.material_shell, + role=Role.STRUCTURE, + ) + .constrain("bottom?mid_l", "side_tail_l?top", "Plane") + .constrain("bottom?bot_l", "side_tail_l?bot", "Plane") + .addS( + self.surface_side_tail_hinged(), + name="side_tail_r", + material=self.material_shell, + role=Role.STRUCTURE, + ) + .constrain("bottom?mid_r", "side_tail_r?bot", "Plane") + .constrain("bottom?bot_r", "side_tail_r?top", "Plane") + .addS( + self.surface_side_bot(), + name="side_bot", + material=self.material_shell, + role=Role.STRUCTURE, + ) + .constrain("bottom?bot_l", "side_bot?top", "Plane") + .constrain("bottom?bot_r", "side_bot?bot", "Plane") + .solve() + ) + return a diff --git a/nhf/touhou/shiki_eiki/zehi.svg b/nhf/touhou/shiki_eiki/zehi.svg new file mode 100644 index 0000000..cfa56e0 --- /dev/null +++ b/nhf/touhou/shiki_eiki/zehi.svg @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nhf/utils.py b/nhf/utils.py index 13469fd..0c83db7 100644 --- a/nhf/utils.py +++ b/nhf/utils.py @@ -1,13 +1,11 @@ """ Utility functions for cadquery objects """ -import functools -import math -from typing import Optional +import functools, math +from typing import Optional, Union, Tuple, cast import cadquery as Cq from cadquery.occ_impl.solver import ConstraintSpec from nhf import Role -from typing import Union, Tuple, cast from nhf.materials import KEY_ITEM, KEY_MATERIAL # Bug fixes @@ -55,6 +53,11 @@ def is2d(self: Cq.Location) -> bool: return z == 0 and rx == 0 and ry == 0 Cq.Location.is2d = is2d +def scale(self: Cq.Location, fac: float) -> bool: + (x, y, z), (rx, ry, rz) = self.toTuple() + return Cq.Location(x*fac, y*fac, z*fac, rx, ry, rz) +Cq.Location.scale = scale + def to2d(self: Cq.Location) -> Tuple[Tuple[float, float], float]: """ Returns position and angle @@ -93,17 +96,24 @@ Cq.Location.with_angle_2d = with_angle_2d def flip_x(self: Cq.Location) -> Cq.Location: (x, y), a = self.to2d() - return Cq.Location.from2d(-x, y, 90 - a) + return Cq.Location.from2d(-x, y, 180 - a) Cq.Location.flip_x = flip_x def flip_y(self: Cq.Location) -> Cq.Location: (x, y), a = self.to2d() return Cq.Location.from2d(x, -y, -a) Cq.Location.flip_y = flip_y -def boolean(self: Cq.Sketch, obj, **kwargs) -> Cq.Sketch: +def boolean( + self: Cq.Sketch, + obj: Union[Cq.Face, Cq.Sketch, Cq.Compound], + **kwargs) -> Cq.Sketch: + """ + Performs Boolean operation between a sketch and a sketch-like object + """ return ( self .reset() + # Has to be 0, 0. Translation doesn't work. .push([(0, 0)]) .each(lambda _: obj, **kwargs) )