|
1 |
| -from typing import List, Sequence |
| 1 | +from typing import ClassVar, List, Sequence |
2 | 2 |
|
3 | 3 | import numpy as np
|
4 | 4 | import parameters as params
|
5 | 5 | from regions.region import Region
|
6 | 6 |
|
7 | 7 | import classy_blocks as cb
|
8 | 8 | from classy_blocks.base.transforms import Transformation, Translation
|
9 |
| -from classy_blocks.construct.flat.sketch import Sketch |
| 9 | +from classy_blocks.construct.point import Point |
10 | 10 | from classy_blocks.construct.shapes.round import RoundSolidShape
|
11 |
| -from classy_blocks.types import NPPointListType, NPVectorType, PointListType, PointType |
12 |
| -from classy_blocks.util import functions as f |
13 |
| - |
14 |
| - |
15 |
| -class DozenBlockDisk(Sketch): |
16 |
| - """A disk that has 3x3 inside quads and 12 outer |
17 |
| - See sketch for explanations""" |
18 |
| - |
19 |
| - layer_1_ratio = 0.25 |
20 |
| - layer_2_ratio = 0.55 |
21 |
| - |
22 |
| - face_map = [ # for creating blocks from points-by-layer |
23 |
| - [[0, 0], [0, 1], [0, 2], [0, 3]], # core: 0 |
24 |
| - [[0, 0], [1, 11], [1, 0], [1, 1]], # layer 1: 1 |
25 |
| - [[0, 0], [1, 1], [1, 2], [0, 1]], # 2 |
26 |
| - [[0, 1], [1, 2], [1, 3], [1, 4]], # 3 |
27 |
| - [[0, 1], [1, 4], [1, 5], [0, 2]], # 4 |
28 |
| - [[0, 2], [1, 5], [1, 6], [1, 7]], # 5 |
29 |
| - [[0, 2], [1, 7], [1, 8], [0, 3]], # 6 |
30 |
| - [[0, 3], [1, 8], [1, 9], [1, 10]], # 7 |
31 |
| - [[0, 3], [1, 10], [1, 11], [0, 0]], # 8 |
32 |
| - [[1, 0], [2, 0], [2, 1], [1, 1]], # layer 3: 9 |
33 |
| - [[1, 1], [2, 1], [2, 2], [1, 2]], # 10 |
34 |
| - [[1, 2], [2, 2], [2, 3], [1, 3]], # 11 |
35 |
| - [[1, 3], [2, 3], [2, 4], [1, 4]], # 12 |
36 |
| - [[1, 4], [2, 4], [2, 5], [1, 5]], # 13 |
37 |
| - [[1, 5], [2, 5], [2, 6], [1, 6]], # 14 |
38 |
| - [[1, 6], [2, 6], [2, 7], [1, 7]], # 15 |
39 |
| - [[1, 7], [2, 7], [2, 8], [1, 8]], # 16 |
40 |
| - [[1, 8], [2, 8], [2, 9], [1, 9]], # 17 |
41 |
| - [[1, 9], [2, 9], [2, 10], [1, 10]], # 18 |
42 |
| - [[1, 10], [2, 10], [2, 11], [1, 11]], # 19 |
43 |
| - [[1, 11], [2, 11], [2, 0], [1, 0]], # 20 |
| 11 | +from classy_blocks.types import NPVectorType, PointListType, PointType |
| 12 | + |
| 13 | + |
| 14 | +class NineCoreDisk(cb.MappedSketch): |
| 15 | + """A disk that has 3x3 inside quads and 12 outer; |
| 16 | + see docs/cylinder.svg for a sketch""" |
| 17 | + |
| 18 | + quads: ClassVar = [ |
| 19 | + # core |
| 20 | + [0, 1, 2, 3], # 0 |
| 21 | + # layer 1 |
| 22 | + [0, 15, 4, 5], # 1 |
| 23 | + [0, 5, 6, 1], # 2 |
| 24 | + [1, 6, 7, 8], # 3 |
| 25 | + [1, 8, 9, 2], # 4 |
| 26 | + [2, 9, 10, 11], # 5 |
| 27 | + [2, 11, 12, 3], # 6 |
| 28 | + [3, 12, 13, 14], # 7 |
| 29 | + [3, 14, 15, 0], # 8 |
| 30 | + # layer 2 |
| 31 | + [4, 16, 17, 5], # 9 |
| 32 | + [5, 17, 18, 6], # 10 |
| 33 | + [6, 18, 19, 7], # 11 |
| 34 | + [7, 19, 20, 8], # 12 |
| 35 | + [8, 20, 21, 9], # 13 |
| 36 | + [9, 21, 22, 10], # 14 |
| 37 | + [10, 22, 23, 11], # 15 |
| 38 | + [11, 23, 24, 12], # 16 |
| 39 | + [12, 24, 25, 13], # 17 |
| 40 | + [13, 25, 26, 14], # 18 |
| 41 | + [14, 26, 27, 15], # 19 |
| 42 | + [15, 27, 16, 4], # 20 |
44 | 43 | ]
|
45 | 44 |
|
46 |
| - # TODO: drop this and inherit from MappedSketch |
47 |
| - neighbours = [ # for laplacian smoothing of the inside |
48 |
| - [15, 5, 1, 3], # 0 |
49 |
| - [0, 6, 8, 2], # 1 |
50 |
| - [9, 1, 11, 3], # 2 |
51 |
| - [14, 0, 2, 12], # 3 |
52 |
| - [16, 5, 15], # 4 |
53 |
| - [4, 17, 6, 0], # 5 |
54 |
| - [18, 7, 1, 5], # 6 |
55 |
| - [6, 19, 8], # 7 |
56 |
| - [1, 7, 20, 9], # 8 |
57 |
| - [2, 8, 21, 10], # 9 |
58 |
| - [22, 11, 9], # 10 |
59 |
| - [23, 10, 2, 12], # 11 |
60 |
| - [11, 24, 13, 3], # 12 |
61 |
| - [25, 12, 14], # 13 |
62 |
| - [13, 3, 15, 26], # 14 |
63 |
| - [14, 27, 4, 0], # 15 |
64 |
| - ] |
65 |
| - |
66 |
| - def _smooth_points(self, points: NPPointListType): |
67 |
| - # A very rudimentary 2D laplacian smoothing; |
68 |
| - # to be replaced with automatic neighbour search, |
69 |
| - # removing the need for this 'neighbours' map |
70 |
| - for i, nei_indexes in enumerate(self.neighbours): |
71 |
| - nei_points = np.take(points, nei_indexes, axis=0) |
72 |
| - points[i] = np.average(nei_points, axis=0) |
73 |
| - |
74 | 45 | def __init__(self, perimeter: PointListType, center_point: PointType):
|
75 |
| - # TODO: add a class method that creates this kind of shapes from perimeter |
76 |
| - self.perimeter = np.array(perimeter) |
77 | 46 | center_point = np.asarray(center_point)
|
78 | 47 |
|
79 |
| - # calculate 3 layers of points; |
80 |
| - # 1st layer, square, 4 points |
81 |
| - # 2nd layer, 3x3 squares, 9 points |
82 |
| - # 3rd layer, 12 shell faces, 12 points |
83 |
| - layer_2 = np.array([center_point + self.layer_2_ratio * (p - center_point) for p in self.perimeter]) |
84 |
| - layer_1 = np.array([center_point + self.layer_1_ratio * (layer_2[i] - center_point) for i in (0, 3, 6, 9)]) |
| 48 | + # inner points will be determined with smoothing; |
| 49 | + # a good enough starting estimate is the center |
| 50 | + # (anything in the same plane as other points) |
| 51 | + outer_points = np.asarray(perimeter) |
| 52 | + inner_points = np.ones((16, 3)) * np.average(outer_points, axis=0) |
85 | 53 |
|
86 |
| - # Assemble a full list for smoothing: |
87 |
| - points_by_index = np.concatenate((layer_1, layer_2, self.perimeter)) |
| 54 | + positions = np.concatenate((inner_points, outer_points)) |
88 | 55 |
|
89 |
| - for _ in range(5): |
90 |
| - self._smooth_points(points_by_index) |
| 56 | + self.center_point = Point(center_point) |
91 | 57 |
|
92 |
| - # reconstruct the list back to layer_1, layer_2, layer_3 for face creation |
93 |
| - points_by_layer = [points_by_index[:4], points_by_index[4:16], points_by_index[16:]] |
| 58 | + super().__init__(positions, self.quads) |
94 | 59 |
|
95 |
| - # the first point is layer index, the second is the point within layer |
96 |
| - self._faces: List[cb.Face] = [] |
| 60 | + # smooth the inner_points (which are all invalid) into position |
| 61 | + smoother = cb.SketchSmoother(self) |
| 62 | + smoother.smooth() |
97 | 63 |
|
98 |
| - for face_indexes in self.face_map: |
99 |
| - face = cb.Face([points_by_layer[i[0]][i[1]] for i in face_indexes]) |
100 |
| - self._faces.append(face) |
| 64 | + @property |
| 65 | + def core(self) -> List[cb.Face]: |
| 66 | + return self.faces[:9] |
101 | 67 |
|
102 |
| - self.core = self._faces[:10] |
103 |
| - self.shell = self._faces[10:] |
| 68 | + @property |
| 69 | + def shell(self) -> List[cb.Face]: |
| 70 | + return self.faces[9:] |
104 | 71 |
|
| 72 | + def add_edges(self): |
105 | 73 | for face in self.shell:
|
106 |
| - face.add_edge(1, cb.Origin(center_point)) |
| 74 | + face.add_edge(1, cb.Origin(self.center_point.position)) |
107 | 75 |
|
108 | 76 | @property
|
109 |
| - def faces(self): |
110 |
| - return self._faces |
| 77 | + def parts(self): |
| 78 | + return [*super().parts, self.center_point] |
111 | 79 |
|
112 | 80 | @property
|
113 |
| - def grid(self): |
114 |
| - return [self.faces] |
| 81 | + def center(self): |
| 82 | + return self.center_point.position |
115 | 83 |
|
116 | 84 | @property
|
117 |
| - def center(self): |
118 |
| - return self.faces[0].center |
| 85 | + def grid(self): |
| 86 | + return [[self.faces[0]], self.faces[1:9], self.faces[9:]] |
119 | 87 |
|
120 | 88 | @property
|
121 | 89 | def normal(self) -> NPVectorType:
|
122 |
| - return f.unit_vector(np.cross(self.perimeter[0] - self.center, self.perimeter[1] - self.center)) |
| 90 | + return self.faces[0].normal |
123 | 91 |
|
124 | 92 | @property
|
125 | 93 | def n_segments(self):
|
126 | 94 | return 12
|
127 | 95 |
|
128 | 96 |
|
129 | 97 | class DozenBlockCylinder(RoundSolidShape):
|
130 |
| - sketch_class = DozenBlockDisk |
| 98 | + sketch_class = NineCoreDisk |
131 | 99 |
|
132 | 100 | def __init__(self, perimeter: PointListType, center_point: PointType, length: float):
|
133 |
| - sketch = DozenBlockDisk(perimeter, center_point) |
| 101 | + sketch = NineCoreDisk(perimeter, center_point) |
134 | 102 | transforms: Sequence[Transformation] = [Translation(sketch.normal * length)]
|
135 | 103 | super().__init__(sketch, transforms)
|
136 | 104 |
|
|
0 commit comments