stbkit 0.1.0a9.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. stbkit/__main__.py +10 -0
  2. stbkit/api/__init__.py +48 -0
  3. stbkit/api/experimental/__init__.py +19 -0
  4. stbkit/api/experimental/repository/__init__.py +30 -0
  5. stbkit/api/py.typed +0 -0
  6. stbkit/api/stb_latest.py +7 -0
  7. stbkit/api/stb_v2_0_2.py +11 -0
  8. stbkit/api/stb_v2_1_1.py +9 -0
  9. stbkit/cli/__init__.py +17 -0
  10. stbkit/cli/_internal/cli_main.py +114 -0
  11. stbkit/cli/_internal/common.py +126 -0
  12. stbkit/cli/_internal/constants_exit_code.py +59 -0
  13. stbkit/cli/_internal/converter.py +693 -0
  14. stbkit/cli/_internal/diff.py +213 -0
  15. stbkit/cli/_internal/entry_points.py +25 -0
  16. stbkit/cli/_internal/global_parent.py +70 -0
  17. stbkit/cli/_internal/show_versions.py +36 -0
  18. stbkit/cli/_internal/validate.py +310 -0
  19. stbkit/cli/commands/convert.py +16 -0
  20. stbkit/cli/commands/diff.py +16 -0
  21. stbkit/cli/commands/validate.py +16 -0
  22. stbkit/cli/py.typed +0 -0
  23. stbkit/tools/__init__.py +12 -0
  24. stbkit/tools/_internal/__init__.py +5 -0
  25. stbkit/tools/_internal/constants.py +21 -0
  26. stbkit/tools/_internal/data_model/building_geometry.py +1208 -0
  27. stbkit/tools/_internal/data_model/building_geometry_web.py +106 -0
  28. stbkit/tools/_internal/data_model/element_data.py +93 -0
  29. stbkit/tools/_internal/data_model/ifc_data/entity_data.py +67 -0
  30. stbkit/tools/_internal/data_model/ifc_data/entity_names.py +32 -0
  31. stbkit/tools/_internal/data_model/shape_data.py +1089 -0
  32. stbkit/tools/_internal/diff/__init__.py +5 -0
  33. stbkit/tools/_internal/diff/common.py +50 -0
  34. stbkit/tools/_internal/diff/diff_xml.py +796 -0
  35. stbkit/tools/_internal/matrix.py +51 -0
  36. stbkit/tools/_internal/optional_dependencies/__init__.py +5 -0
  37. stbkit/tools/_internal/optional_dependencies/ifc_common.py +28 -0
  38. stbkit/tools/_internal/optional_dependencies/ifc_exporter/ifc_entity_exporter.py +473 -0
  39. stbkit/tools/_internal/optional_dependencies/ifc_exporter/ifc_profile_exporter.py +267 -0
  40. stbkit/tools/_internal/optional_dependencies/ifc_exporter/ifc_representation_exporter.py +178 -0
  41. stbkit/tools/_internal/optional_dependencies/ifc_pipeline.py +133 -0
  42. stbkit/tools/_internal/optional_dependencies/ifc_to_building_geometry.py +64 -0
  43. stbkit/tools/_internal/stb_io.py +116 -0
  44. stbkit/tools/_internal/stb_repair/_common.py +24 -0
  45. stbkit/tools/_internal/stb_repair/_stb_repair.py +197 -0
  46. stbkit/tools/_internal/stb_repair/_stb_v2_0_2_repair.py +53 -0
  47. stbkit/tools/_internal/stb_repair/_stb_v2_1_0_repair.py +109 -0
  48. stbkit/tools/_internal/stb_repair/_stb_v2_1_1_repair.py +109 -0
  49. stbkit/tools/_internal/studio_report.py +49 -0
  50. stbkit/tools/_internal/utils/atomic_writer.py +57 -0
  51. stbkit/tools/_internal/utils/float_rounding.py +49 -0
  52. stbkit/tools/_internal/utils/girder_utils.py +91 -0
  53. stbkit/tools/_internal/utils/guid_utils.py +30 -0
  54. stbkit/tools/_internal/utils/mesh_utils.py +1305 -0
  55. stbkit/tools/_internal/utils/node_utils.py +50 -0
  56. stbkit/tools/_internal/utils/unit_utils.py +23 -0
  57. stbkit/tools/_internal/vectors.py +222 -0
  58. stbkit/tools/converters/__init__.py +39 -0
  59. stbkit/tools/converters/_internal/__init__.py +5 -0
  60. stbkit/tools/converters/_internal/building_geometry_to_ply_text.py +32 -0
  61. stbkit/tools/converters/_internal/coord_converter.py +151 -0
  62. stbkit/tools/converters/_internal/element_data_to_building_geometry.py +549 -0
  63. stbkit/tools/converters/_internal/ifc_to_ply_text.py +21 -0
  64. stbkit/tools/converters/_internal/name_converter.py +23 -0
  65. stbkit/tools/converters/_internal/pipeline/__init__.py +5 -0
  66. stbkit/tools/converters/_internal/pipeline/data.py +188 -0
  67. stbkit/tools/converters/_internal/pipeline/execution.py +569 -0
  68. stbkit/tools/converters/_internal/pipeline/ply_pipeline.py +52 -0
  69. stbkit/tools/converters/_internal/pipeline/stb_pipeline.py +226 -0
  70. stbkit/tools/converters/_internal/pipeline/stb_pipeline_latest.py +39 -0
  71. stbkit/tools/converters/_internal/pipeline/step.py +139 -0
  72. stbkit/tools/converters/_internal/stb_latest_to_element_data/__init__.py +17 -0
  73. stbkit/tools/converters/_internal/stb_latest_to_element_data/_stb_latest_to_element_data.py +186 -0
  74. stbkit/tools/converters/_internal/stb_latest_to_element_data/member/member_footing.py +49 -0
  75. stbkit/tools/converters/_internal/stb_latest_to_element_data/member/member_line.py +533 -0
  76. stbkit/tools/converters/_internal/stb_latest_to_element_data/member/member_pile.py +100 -0
  77. stbkit/tools/converters/_internal/stb_latest_to_element_data/member/member_plane.py +103 -0
  78. stbkit/tools/converters/_internal/stb_latest_to_element_data/open.py +70 -0
  79. stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec.py +214 -0
  80. stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_beam_rc.py +67 -0
  81. stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_beam_s.py +97 -0
  82. stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_beam_src.py +28 -0
  83. stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_brace_s.py +49 -0
  84. stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_column_cft.py +217 -0
  85. stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_column_rc.py +41 -0
  86. stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_column_s.py +127 -0
  87. stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_column_src.py +173 -0
  88. stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_foundation.py +225 -0
  89. stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_parapet_rc.py +53 -0
  90. stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_pile_rc.py +106 -0
  91. stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_pile_s.py +100 -0
  92. stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_steel.py +432 -0
  93. stbkit/tools/converters/_internal/stb_to_building_geometry.py +44 -0
  94. stbkit/tools/converters/_internal/stb_to_ifc_data/__init__.py +5 -0
  95. stbkit/tools/converters/_internal/stb_to_ifc_data/_stb_to_ifc_data.py +59 -0
  96. stbkit/tools/converters/_internal/stb_to_ifc_data/convert_option.py +22 -0
  97. stbkit/tools/converters/_internal/stb_to_ply_text.py +22 -0
  98. stbkit/tools/py.typed +0 -0
  99. stbkit/tools/upgrade/__init__.py +13 -0
  100. stbkit/tools/upgrade/_internal/__init__.py +5 -0
  101. stbkit/tools/upgrade/_internal/common.py +205 -0
  102. stbkit/tools/upgrade/_internal/upgrade.py +228 -0
  103. stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/__init__.py +60 -0
  104. stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/hook_common.py +62 -0
  105. stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/hook_joint_beam_shape_h.py +44 -0
  106. stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/hook_sec_pile_rc.py +101 -0
  107. stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/hook_sec_pile_s.py +78 -0
  108. stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/hook_sec_slab_deck.py +56 -0
  109. stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/hook_sec_slab_rc.py +128 -0
  110. stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/post_girder.py +126 -0
  111. stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/post_open.py +110 -0
  112. stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/post_sec_beam_rc.py +227 -0
  113. stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/post_sec_beam_s.py +1153 -0
  114. stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hooks.py +22 -0
  115. stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/post.py +42 -0
  116. stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/tables.py +99 -0
  117. stbkit/tools/upgrade/_internal/v2_1_0_to_v2_1_1/__init__.py +58 -0
  118. stbkit/tools/upgrade/_internal/v2_1_0_to_v2_1_1/hooks.py +32 -0
  119. stbkit/tools/upgrade/_internal/v2_1_0_to_v2_1_1/tables.py +56 -0
  120. stbkit-0.1.0a9.dev1.dist-info/METADATA +196 -0
  121. stbkit-0.1.0a9.dev1.dist-info/RECORD +125 -0
  122. stbkit-0.1.0a9.dev1.dist-info/WHEEL +5 -0
  123. stbkit-0.1.0a9.dev1.dist-info/entry_points.txt +2 -0
  124. stbkit-0.1.0a9.dev1.dist-info/licenses/LICENSE +373 -0
  125. stbkit-0.1.0a9.dev1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1089 @@
1
+ # Copyright 2025-2026 TAISEI CORPORATION
2
+ # SPDX-License-Identifier: MPL-2.0
3
+ # This Source Code Form is subject to the terms of the Mozilla Public
4
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ # file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
+
7
+ import math
8
+ from abc import ABCMeta, abstractmethod
9
+ from dataclasses import dataclass, field
10
+ from enum import Enum
11
+ from typing import Any, Final, Self
12
+
13
+ from ..._internal.constants import UNKNOWN_ELEMENT_SIZE_MM
14
+ from ..utils.unit_utils import degree_to_radian
15
+ from ..vectors import Vector2d
16
+
17
+
18
+ def rotate_and_offset_points(
19
+ points: list[Vector2d], angle_degree: float | None, offset: Vector2d | None
20
+ ) -> list[Vector2d]:
21
+ if angle_degree is None and offset is None:
22
+ return points
23
+ angle_degree = angle_degree or 0.0
24
+ offset = offset or Vector2d.zero()
25
+ angle: float = degree_to_radian(angle_degree)
26
+ cos_a: float = math.cos(angle)
27
+ sin_a: float = math.sin(angle)
28
+ rotated_and_offset_points = [
29
+ Vector2d(x * cos_a - y * sin_a + offset.x, x * sin_a + y * cos_a + offset.y)
30
+ for x, y in points
31
+ ]
32
+ return rotated_and_offset_points
33
+
34
+
35
+ def diagram_core(points: list[Vector2d]) -> Vector2d:
36
+ """図心を計算する(多角形の重心)"""
37
+ if not points or len(points) < 3:
38
+ raise ValueError("3点以上の多角形が必要です")
39
+ area: float = 0.0
40
+ cx: float = 0.0
41
+ cy: float = 0.0
42
+ n: int = len(points)
43
+ for i in range(n):
44
+ x0, y0 = points[i].x, points[i].y
45
+ x1, y1 = points[(i + 1) % n].x, points[(i + 1) % n].y
46
+ cross: float = x0 * y1 - x1 * y0
47
+ area += cross
48
+ cx += (x0 + x1) * cross
49
+ cy += (y0 + y1) * cross
50
+ area *= 0.5
51
+ if abs(area) < 1e-12:
52
+ raise ValueError("面積がゼロです")
53
+ cx /= 6.0 * area
54
+ cy /= 6.0 * area
55
+ return Vector2d(cx, cy)
56
+
57
+
58
+ class ShapePosition(Enum):
59
+ BOTTOM_CENTER = "中央下"
60
+ BOTTOM_LEFT = "左下"
61
+ BOTTOM_RIGHT = "右下"
62
+ CENTER_CENTER = "中央"
63
+ CENTER_LEFT = "左中央"
64
+ CENTER_RIGHT = "右中央"
65
+ TOP_CENTER = "中央上"
66
+ TOP_LEFT = "左上"
67
+ TOP_RIGHT = "右上"
68
+ DIAGRAM_CORE = "図心"
69
+
70
+
71
+ class SlotsMergeMeta(ABCMeta):
72
+ def __new__(
73
+ mcs,
74
+ name: str,
75
+ bases: tuple[type, ...],
76
+ namespace: dict[str, Any],
77
+ **kwargs: Any,
78
+ ) -> Any:
79
+ # クラス生成
80
+ cls = super().__new__(mcs, name, bases, namespace)
81
+ # すべての親クラスの __slots__ を集める
82
+ slots: list[str] = []
83
+ for base in bases:
84
+ base_slots = getattr(base, "__slots__", ())
85
+ if isinstance(base_slots, str):
86
+ base_slots = (base_slots,)
87
+ slots.extend(base_slots)
88
+ # dataclassのフィールド名を取得
89
+ if hasattr(cls, "__annotations__"):
90
+ dataclass_fields = cls.__annotations__.keys()
91
+ slots.extend(dataclass_fields)
92
+ # 重複を除去し、tupleに変換
93
+ slots_tuple: tuple[str, ...] = tuple(dict.fromkeys(slots))
94
+ # __slots__をクラスに設定
95
+ cls.__slots__ = slots_tuple # type: ignore
96
+ return cls
97
+
98
+
99
+ @dataclass(eq=False, order=False, kw_only=True)
100
+ class Shape(metaclass=SlotsMergeMeta):
101
+ rotate_degree: float | None = None
102
+ offset: Vector2d | None = None
103
+
104
+ # オーバーロード必須メソッド
105
+
106
+ @abstractmethod
107
+ def get_out_point2ds(self) -> list[Vector2d]:
108
+ """座標は形状に外接する長方形の中心点を原点とする"""
109
+
110
+ # 任意でオーバーロードするメソッド
111
+
112
+ def get_in_point2ds(self) -> list[Vector2d] | None:
113
+ """座標は形状に外接する長方形の中心点を原点とする"""
114
+ return None
115
+
116
+ def get_position_point(self, position: ShapePosition) -> Vector2d:
117
+ match position:
118
+ case ShapePosition.TOP_LEFT:
119
+ return Vector2d(self._x_min, self._y_max)
120
+ case ShapePosition.TOP_RIGHT:
121
+ return Vector2d(self._x_max, self._y_max)
122
+ case ShapePosition.BOTTOM_LEFT:
123
+ return Vector2d(self._x_min, self._y_min)
124
+ case ShapePosition.BOTTOM_RIGHT:
125
+ return Vector2d(self._x_max, self._y_min)
126
+ case ShapePosition.TOP_CENTER:
127
+ return Vector2d(self._x_center, self._y_max)
128
+ case ShapePosition.BOTTOM_CENTER:
129
+ return Vector2d(self._x_center, self._y_min)
130
+ case ShapePosition.CENTER_LEFT:
131
+ return Vector2d(self._x_min, self._y_center)
132
+ case ShapePosition.CENTER_RIGHT:
133
+ return Vector2d(self._x_max, self._y_center)
134
+ case ShapePosition.CENTER_CENTER:
135
+ return Vector2d(self._x_center, self._y_center)
136
+ case ShapePosition.DIAGRAM_CORE:
137
+ core_point: Vector2d = diagram_core(self.get_out_point2ds())
138
+ in_point_2ds: list[Vector2d] | None = self.get_in_point2ds()
139
+ if in_point_2ds:
140
+ core_point -= diagram_core(in_point_2ds)
141
+ return core_point
142
+ case _:
143
+ raise AssertionError(f"{position}:不明な位置指定です")
144
+
145
+ # 基本的にオーバーロード不要メソッド
146
+
147
+ def get_out_point2ds_with_rotate_and_offset(self) -> list[Vector2d]:
148
+ return rotate_and_offset_points(
149
+ self.get_out_point2ds(), self.rotate_degree, self.offset
150
+ )
151
+
152
+ def get_in_point2ds_with_rotate_and_offset(self) -> list[Vector2d] | None:
153
+ in_points = self.get_in_point2ds()
154
+ if in_points is None:
155
+ return None
156
+ return rotate_and_offset_points(in_points, self.rotate_degree, self.offset)
157
+
158
+ # プライベートプロパティ
159
+
160
+ @property
161
+ def _x_min(self) -> float:
162
+ return min(point.x for point in self.get_out_point2ds())
163
+
164
+ @property
165
+ def _x_max(self) -> float:
166
+ return max(point.x for point in self.get_out_point2ds())
167
+
168
+ @property
169
+ def _y_min(self) -> float:
170
+ return min(point.y for point in self.get_out_point2ds())
171
+
172
+ @property
173
+ def _y_max(self) -> float:
174
+ return max(point.y for point in self.get_out_point2ds())
175
+
176
+ @property
177
+ def _x_center(self) -> float:
178
+ return (self._x_min + self._x_max) / 2.0
179
+
180
+ @property
181
+ def _y_center(self) -> float:
182
+ return (self._y_min + self._y_max) / 2.0
183
+
184
+
185
+ @dataclass(repr=False, eq=False, order=False)
186
+ class ShapePair:
187
+ start: Shape
188
+ end: Shape | None = None
189
+
190
+
191
+ @dataclass(eq=False, order=False, kw_only=True)
192
+ class ShapeAngle(Shape):
193
+ name: str
194
+ a: float
195
+ b: float
196
+ t1: float
197
+ t2: float
198
+ r1: float = 0.0
199
+ r2: float = 0.0
200
+
201
+ def get_out_point2ds(self) -> list[Vector2d]:
202
+ result: list[Vector2d] = [
203
+ Vector2d(-self.b / 2.0, -self.a / 2.0),
204
+ Vector2d(-self.b / 2.0 + self.t1, -self.a / 2.0),
205
+ Vector2d(-self.b / 2.0 + self.t1, self.a / 2.0 - self.t2),
206
+ Vector2d(self.b / 2.0, self.a / 2.0 - self.t2),
207
+ Vector2d(self.b / 2.0, self.a / 2.0),
208
+ Vector2d(-self.b / 2.0, self.a / 2.0),
209
+ ]
210
+ return result
211
+
212
+ def get_position_point(self, position: ShapePosition) -> Vector2d:
213
+ if self.rotate_degree is None and self.offset is None:
214
+ match position:
215
+ case ShapePosition.BOTTOM_CENTER:
216
+ return Vector2d(0.0, -self.a / 2.0)
217
+ case ShapePosition.BOTTOM_LEFT:
218
+ return Vector2d(-self.b / 2.0, -self.a / 2.0)
219
+ case ShapePosition.BOTTOM_RIGHT:
220
+ return Vector2d(self.b / 2.0, -self.a / 2.0)
221
+ case ShapePosition.CENTER_CENTER:
222
+ return Vector2d.zero()
223
+ case ShapePosition.CENTER_LEFT:
224
+ return Vector2d(-self.b / 2.0, 0.0)
225
+ case ShapePosition.CENTER_RIGHT:
226
+ return Vector2d(self.b / 2.0, 0.0)
227
+ case ShapePosition.TOP_CENTER:
228
+ return Vector2d(0.0, self.a / 2.0)
229
+ case ShapePosition.TOP_LEFT:
230
+ return Vector2d(-self.b / 2.0, self.a / 2.0)
231
+ case ShapePosition.TOP_RIGHT:
232
+ return Vector2d(self.b / 2.0, self.a / 2.0)
233
+ case ShapePosition.DIAGRAM_CORE:
234
+ raise NotImplementedError(
235
+ "Angleの図心の計算はサポートされていません"
236
+ )
237
+ else:
238
+ return super().get_position_point(position)
239
+
240
+
241
+ @dataclass(eq=False, order=False, kw_only=True)
242
+ class ShapeArbitrary(Shape):
243
+ out_points: list[Vector2d] = field(default_factory=list)
244
+ in_points: list[list[Vector2d]] = field(default_factory=list)
245
+
246
+ def get_out_point2ds(self) -> list[Vector2d]:
247
+ return self.out_points
248
+
249
+ def get_in_point2ds(self) -> list[Vector2d] | None:
250
+ if self.in_points:
251
+ return self.in_points[0]
252
+ else:
253
+ return None
254
+
255
+ def get_position_point(self, position: ShapePosition) -> Vector2d:
256
+ if position == ShapePosition.DIAGRAM_CORE:
257
+ core_point: Vector2d = diagram_core(self.out_points)
258
+ for in_point_2ds in self.in_points:
259
+ core_point -= diagram_core(in_point_2ds)
260
+ return core_point
261
+ return super().get_position_point(position)
262
+
263
+
264
+ @dataclass(eq=False, order=False, kw_only=True)
265
+ class ShapeBox(Shape):
266
+ name: str
267
+ a: float
268
+ b: float
269
+ t: float
270
+ r: float
271
+
272
+ def get_out_point2ds(self) -> list[Vector2d]:
273
+ result: list[Vector2d] = [
274
+ Vector2d(-self.b / 2.0, -self.a / 2.0),
275
+ Vector2d(self.b / 2.0, -self.a / 2.0),
276
+ Vector2d(self.b / 2.0, self.a / 2.0),
277
+ Vector2d(-self.b / 2.0, self.a / 2.0),
278
+ ]
279
+ return result
280
+
281
+ def get_position_point(self, position: ShapePosition) -> Vector2d:
282
+ match position:
283
+ case ShapePosition.BOTTOM_CENTER:
284
+ return Vector2d(0.0, -self.a / 2.0)
285
+ case ShapePosition.BOTTOM_LEFT:
286
+ return Vector2d(-self.b / 2.0, -self.a / 2.0)
287
+ case ShapePosition.BOTTOM_RIGHT:
288
+ return Vector2d(self.b / 2.0, -self.a / 2.0)
289
+ case ShapePosition.CENTER_LEFT:
290
+ return Vector2d(-self.b / 2.0, 0.0)
291
+ case ShapePosition.CENTER_RIGHT:
292
+ return Vector2d(self.b / 2.0, 0.0)
293
+ case ShapePosition.TOP_CENTER:
294
+ return Vector2d(0.0, self.a / 2.0)
295
+ case ShapePosition.TOP_LEFT:
296
+ return Vector2d(-self.b / 2.0, self.a / 2.0)
297
+ case ShapePosition.TOP_RIGHT:
298
+ return Vector2d(self.b / 2.0, self.a / 2.0)
299
+ case ShapePosition.CENTER_CENTER | ShapePosition.DIAGRAM_CORE:
300
+ return Vector2d.zero()
301
+
302
+
303
+ @dataclass(eq=False, order=False, kw_only=True)
304
+ class ShapeBuildBox(Shape):
305
+ name: str
306
+ a: float
307
+ b: float
308
+ t1: float
309
+ t2: float
310
+
311
+ def get_out_point2ds(self) -> list[Vector2d]:
312
+ result: list[Vector2d] = [
313
+ Vector2d(-self.b / 2.0, -self.a / 2.0),
314
+ Vector2d(self.b / 2.0, -self.a / 2.0),
315
+ Vector2d(self.b / 2.0, self.a / 2.0),
316
+ Vector2d(-self.b / 2.0, self.a / 2.0),
317
+ ]
318
+ return result
319
+
320
+ def get_in_point2ds(self) -> list[Vector2d]:
321
+ result: list[Vector2d] = [
322
+ Vector2d(-self.b / 2.0 + self.t1, -self.a / 2.0 + self.t2),
323
+ Vector2d(self.b / 2.0 - self.t1, -self.a / 2.0 + self.t2),
324
+ Vector2d(self.b / 2.0 - self.t1, self.a / 2.0 - self.t2),
325
+ Vector2d(-self.b / 2.0 + self.t1, self.a / 2.0 - self.t2),
326
+ ]
327
+ return result
328
+
329
+ def get_position_point(self, position: ShapePosition) -> Vector2d:
330
+ match position:
331
+ case ShapePosition.BOTTOM_CENTER:
332
+ return Vector2d(0.0, -self.a / 2.0)
333
+ case ShapePosition.BOTTOM_LEFT:
334
+ return Vector2d(-self.b / 2.0, -self.a / 2.0)
335
+ case ShapePosition.BOTTOM_RIGHT:
336
+ return Vector2d(self.b / 2.0, -self.a / 2.0)
337
+ case ShapePosition.CENTER_LEFT:
338
+ return Vector2d(-self.b / 2.0, 0.0)
339
+ case ShapePosition.CENTER_RIGHT:
340
+ return Vector2d(self.b / 2.0, 0.0)
341
+ case ShapePosition.TOP_CENTER:
342
+ return Vector2d(0.0, self.a / 2.0)
343
+ case ShapePosition.TOP_LEFT:
344
+ return Vector2d(-self.b / 2.0, self.a / 2.0)
345
+ case ShapePosition.TOP_RIGHT:
346
+ return Vector2d(self.b / 2.0, self.a / 2.0)
347
+ case ShapePosition.CENTER_CENTER | ShapePosition.DIAGRAM_CORE:
348
+ return Vector2d.zero()
349
+
350
+
351
+ @dataclass(eq=False, order=False, kw_only=True)
352
+ class ShapeChannel(Shape):
353
+ name: str
354
+ a: float
355
+ b: float
356
+ t1: float
357
+ t2: float
358
+ r1: float = 0.0
359
+ r2: float = 0.0
360
+
361
+ def get_out_point2ds(self) -> list[Vector2d]:
362
+ result: list[Vector2d] = [
363
+ Vector2d(-self.b / 2.0, -self.a / 2.0),
364
+ Vector2d(self.b / 2.0, -self.a / 2.0),
365
+ Vector2d(self.b / 2.0, -self.a / 2.0 + self.t2),
366
+ Vector2d(-self.b / 2.0 + self.t1, -self.a / 2.0 + self.t2),
367
+ Vector2d(-self.b / 2.0 + self.t1, self.a / 2.0 - self.t2),
368
+ Vector2d(self.b / 2.0, self.a / 2.0 - self.t2),
369
+ Vector2d(self.b / 2.0, self.a / 2.0),
370
+ Vector2d(-self.b / 2.0, self.a / 2.0),
371
+ ]
372
+ return result
373
+
374
+ def get_position_point(self, position: ShapePosition) -> Vector2d:
375
+ match position:
376
+ case ShapePosition.BOTTOM_CENTER:
377
+ return Vector2d(0.0, -self.a / 2.0)
378
+ case ShapePosition.BOTTOM_LEFT:
379
+ return Vector2d(-self.b / 2.0, -self.a / 2.0)
380
+ case ShapePosition.BOTTOM_RIGHT:
381
+ return Vector2d(self.b / 2.0, -self.a / 2.0)
382
+ case ShapePosition.CENTER_CENTER:
383
+ return Vector2d.zero()
384
+ case ShapePosition.CENTER_LEFT:
385
+ return Vector2d(-self.b / 2.0, 0.0)
386
+ case ShapePosition.CENTER_RIGHT:
387
+ return Vector2d(self.b / 2.0, 0.0)
388
+ case ShapePosition.TOP_CENTER:
389
+ return Vector2d(0.0, self.a / 2.0)
390
+ case ShapePosition.TOP_LEFT:
391
+ return Vector2d(-self.b / 2.0, self.a / 2.0)
392
+ case ShapePosition.TOP_RIGHT:
393
+ return Vector2d(self.b / 2.0, self.a / 2.0)
394
+ case ShapePosition.DIAGRAM_CORE:
395
+ raise NotImplementedError("Channelの図心の計算はサポートされていません")
396
+
397
+
398
+ @dataclass(eq=False, order=False, kw_only=True)
399
+ class ShapeCircle(Shape):
400
+ name: str
401
+ d: float
402
+ number_of_divisions: int = 12
403
+
404
+ def get_out_point2ds(self) -> list[Vector2d]:
405
+ n_div: int = max(self.number_of_divisions, 3)
406
+ unit_angle: float = math.pi * 2.0 / n_div
407
+ r: float = self.d / 2.0
408
+ result: list[Vector2d] = [
409
+ Vector2d(
410
+ r * math.cos(i * unit_angle),
411
+ r * math.sin(i * unit_angle),
412
+ )
413
+ for i in range(n_div)
414
+ ]
415
+ return result
416
+
417
+ def get_position_point(self, position: ShapePosition) -> Vector2d:
418
+ i: int
419
+ match position:
420
+ case ShapePosition.CENTER_CENTER | ShapePosition.DIAGRAM_CORE:
421
+ return Vector2d.zero()
422
+ case ShapePosition.BOTTOM_CENTER:
423
+ i = 6
424
+ case ShapePosition.BOTTOM_LEFT:
425
+ i = 5
426
+ case ShapePosition.BOTTOM_RIGHT:
427
+ i = 7
428
+ case ShapePosition.CENTER_LEFT:
429
+ i = 4
430
+ case ShapePosition.CENTER_RIGHT:
431
+ i = 0
432
+ case ShapePosition.TOP_CENTER:
433
+ i = 2
434
+ case ShapePosition.TOP_LEFT:
435
+ i = 1
436
+ case ShapePosition.TOP_RIGHT:
437
+ i = 3
438
+ unit_angle: float = math.pi * 2.0 / 8.0
439
+ r: float = self.d / 2.0
440
+ return Vector2d(
441
+ r * math.cos(i * unit_angle),
442
+ r * math.sin(i * unit_angle),
443
+ )
444
+
445
+
446
+ @dataclass(eq=False, order=False, kw_only=True)
447
+ class ShapeCrossH(Shape):
448
+ name: str
449
+ h_h: float
450
+ h_b: float
451
+ h_tw: float
452
+ h_tf: float
453
+ i_h: float
454
+ i_b: float
455
+ i_tw: float
456
+ i_tf: float
457
+ i_offset_x: float = 0.0
458
+ h_offset_y: float = 0.0
459
+
460
+ @property
461
+ def _x_max(self) -> float:
462
+ return self.h_h / 2.0
463
+
464
+ @property
465
+ def _x_min(self) -> float:
466
+ return -self.h_h / 2.0
467
+
468
+ @property
469
+ def _x_center(self) -> float:
470
+ return 0.0
471
+
472
+ @property
473
+ def _y_max(self) -> float:
474
+ return self.i_h / 2.0
475
+
476
+ @property
477
+ def _y_min(self) -> float:
478
+ return -self.i_h / 2.0
479
+
480
+ @property
481
+ def _y_center(self) -> float:
482
+ return 0.0
483
+
484
+ def get_out_point2ds(self) -> list[Vector2d]:
485
+ return [
486
+ Vector2d(-self.i_b / 2.0 + self.i_offset_x, self._y_min),
487
+ Vector2d(self.i_b / 2.0 + self.i_offset_x, self._y_min),
488
+ Vector2d(self.i_b / 2.0 + self.i_offset_x, self._y_min + self.i_tf),
489
+ Vector2d(self.i_tw / 2.0 + self.i_offset_x, self._y_min + self.i_tf),
490
+ Vector2d(
491
+ self.i_tw / 2.0 + self.i_offset_x, -self.h_tw / 2.0 + self.h_offset_y
492
+ ),
493
+ Vector2d(self._x_max - self.h_tf, -self.h_tw / 2.0 + self.h_offset_y),
494
+ Vector2d(self._x_max - self.h_tf, -self.h_b / 2.0 + self.h_offset_y),
495
+ Vector2d(self._x_max, -self.h_b / 2.0 + self.h_offset_y),
496
+ Vector2d(self._x_max, self.h_b / 2.0 + self.h_offset_y),
497
+ Vector2d(self._x_max - self.h_tf, self.h_b / 2.0 + self.h_offset_y),
498
+ Vector2d(self._x_max - self.h_tf, self.h_tw / 2.0 + self.h_offset_y),
499
+ Vector2d(
500
+ self.i_tw / 2.0 + self.i_offset_x, self.h_tw / 2.0 + self.h_offset_y
501
+ ),
502
+ Vector2d(self.i_tw / 2.0 + self.i_offset_x, self._y_max - self.i_tf),
503
+ Vector2d(self.i_b / 2.0 + self.i_offset_x, self._y_max - self.i_tf),
504
+ Vector2d(self.i_b / 2.0 + self.i_offset_x, self._y_max),
505
+ Vector2d(-self.i_b / 2.0 + self.i_offset_x, self._y_max),
506
+ Vector2d(-self.i_b / 2.0 + self.i_offset_x, self._y_max - self.i_tf),
507
+ Vector2d(-self.i_tw / 2.0 + self.i_offset_x, self._y_max - self.i_tf),
508
+ Vector2d(
509
+ -self.i_tw / 2.0 + self.i_offset_x, self.h_tw / 2.0 + self.h_offset_y
510
+ ),
511
+ Vector2d(self._x_min + self.h_tf, self.h_tw / 2.0 + self.h_offset_y),
512
+ Vector2d(self._x_min + self.h_tf, self.h_b / 2.0 + self.h_offset_y),
513
+ Vector2d(self._x_min, self.h_b / 2.0 + self.h_offset_y),
514
+ Vector2d(self._x_min, -self.h_b / 2.0 + self.h_offset_y),
515
+ Vector2d(self._x_min + self.h_tf, -self.h_b / 2.0 + self.h_offset_y),
516
+ Vector2d(self._x_min + self.h_tf, -self.h_tw / 2.0 + self.h_offset_y),
517
+ Vector2d(
518
+ -self.i_tw / 2.0 + self.i_offset_x, -self.h_tw / 2.0 + self.h_offset_y
519
+ ),
520
+ Vector2d(-self.i_tw / 2.0 + self.i_offset_x, self._y_min + self.i_tf),
521
+ Vector2d(-self.i_b / 2.0 + self.i_offset_x, self._y_min + self.i_tf),
522
+ ]
523
+
524
+
525
+ @dataclass(eq=False, order=False, kw_only=True)
526
+ class ShapeEquilateralTriangle(Shape):
527
+ name: str
528
+ width_base: float
529
+ width_chamfer: float = 0.0
530
+
531
+ def get_out_point2ds(self) -> list[Vector2d]:
532
+ if self.width_chamfer == 0.0:
533
+ return [
534
+ Vector2d(self._x_min, self._y_min),
535
+ Vector2d(self._x_max, self._y_min),
536
+ Vector2d(self._x_center, self._y_max),
537
+ ]
538
+ else:
539
+ height_chamfer: float = (math.sqrt(3.0) / 2.0) * self.width_chamfer
540
+ return [
541
+ Vector2d(-self.width_base / 2.0, self._y_min),
542
+ Vector2d(self.width_base / 2.0, self._y_min),
543
+ Vector2d(
544
+ self._x_max,
545
+ self._y_min + height_chamfer,
546
+ ),
547
+ Vector2d(self.width_chamfer / 2.0, self._y_max),
548
+ Vector2d(-self.width_chamfer / 2.0, self._y_max),
549
+ Vector2d(
550
+ self._x_min,
551
+ self._y_min + height_chamfer,
552
+ ),
553
+ ]
554
+
555
+ @property
556
+ def _x_max(self) -> float:
557
+ if self.width_chamfer == 0.0:
558
+ return self.width_base / 2.0
559
+ else:
560
+ return self.width_base / 2.0 + self.width_chamfer / 2.0
561
+
562
+ @property
563
+ def _x_min(self) -> float:
564
+ return -self._x_max
565
+
566
+ @property
567
+ def _x_center(self) -> float:
568
+ return 0.0
569
+
570
+ @property
571
+ def _y_max(self) -> float:
572
+ return math.sqrt(3.0) / 2.0 * (self.width_base + self.width_chamfer) / 2.0
573
+
574
+ @property
575
+ def _y_min(self) -> float:
576
+ return -self._y_max
577
+
578
+ @property
579
+ def _y_center(self) -> float:
580
+ return 0.0
581
+
582
+
583
+ @dataclass(eq=False, order=False, kw_only=True)
584
+ class ShapeH(Shape):
585
+ name: str
586
+ a: float
587
+ b: float
588
+ t1: float
589
+ t2: float
590
+ r: float
591
+
592
+ def get_out_point2ds(self) -> list[Vector2d]:
593
+ result: list[Vector2d] = [
594
+ Vector2d(-self.b / 2.0, -self.a / 2.0),
595
+ Vector2d(self.b / 2.0, -self.a / 2.0),
596
+ Vector2d(self.b / 2.0, -self.a / 2.0 + self.t2),
597
+ Vector2d(self.t1 / 2.0, -self.a / 2.0 + self.t2),
598
+ Vector2d(self.t1 / 2.0, self.a / 2.0 - self.t2),
599
+ Vector2d(self.b / 2.0, self.a / 2.0 - self.t2),
600
+ Vector2d(self.b / 2.0, self.a / 2.0),
601
+ Vector2d(-self.b / 2.0, self.a / 2.0),
602
+ Vector2d(-self.b / 2.0, self.a / 2.0 - self.t2),
603
+ Vector2d(-self.t1 / 2.0, self.a / 2.0 - self.t2),
604
+ Vector2d(-self.t1 / 2.0, -self.a / 2.0 + self.t2),
605
+ Vector2d(-self.b / 2.0, -self.a / 2.0 + self.t2),
606
+ ]
607
+ return result
608
+
609
+ def get_position_point(self, position: ShapePosition) -> Vector2d:
610
+ match position:
611
+ case ShapePosition.CENTER_CENTER | ShapePosition.DIAGRAM_CORE:
612
+ return Vector2d.zero()
613
+ case ShapePosition.BOTTOM_CENTER:
614
+ return Vector2d(0.0, -self.a / 2.0)
615
+ case ShapePosition.BOTTOM_LEFT:
616
+ return Vector2d(-self.b / 2.0, -self.a / 2.0)
617
+ case ShapePosition.BOTTOM_RIGHT:
618
+ return Vector2d(self.b / 2.0, -self.a / 2.0)
619
+ case ShapePosition.CENTER_LEFT:
620
+ return Vector2d(-self.b / 2.0, 0.0)
621
+ case ShapePosition.CENTER_RIGHT:
622
+ return Vector2d(self.b / 2.0, 0.0)
623
+ case ShapePosition.TOP_CENTER:
624
+ return Vector2d(0.0, self.a / 2.0)
625
+ case ShapePosition.TOP_LEFT:
626
+ return Vector2d(-self.b / 2.0, self.a / 2.0)
627
+ case ShapePosition.TOP_RIGHT:
628
+ return Vector2d(self.b / 2.0, self.a / 2.0)
629
+
630
+
631
+ @dataclass(eq=False, order=False, kw_only=True)
632
+ class ShapeLipC(Shape):
633
+ name: str
634
+ h: float
635
+ a: float
636
+ c: float
637
+ t: float
638
+
639
+ def get_out_point2ds(self) -> list[Vector2d]:
640
+ result: list[Vector2d] = [
641
+ Vector2d(-self.a / 2.0, -self.h / 2.0),
642
+ Vector2d(self.a / 2.0, -self.h / 2.0),
643
+ Vector2d(self.a / 2.0, -self.h / 2.0 + self.c),
644
+ Vector2d(self.a / 2.0 - self.t, -self.h / 2.0 + self.c),
645
+ Vector2d(self.a / 2.0 - self.t, -self.h / 2.0 + self.t),
646
+ Vector2d(-self.a / 2.0 + self.t, -self.h / 2.0 + self.t),
647
+ Vector2d(-self.a / 2.0 + self.t, self.h / 2.0 - self.t),
648
+ Vector2d(self.a / 2.0 - self.t, self.h / 2.0 - self.t),
649
+ Vector2d(self.a / 2.0 - self.t, self.h / 2.0 - self.c),
650
+ Vector2d(self.a / 2.0, self.h / 2.0 - self.c),
651
+ Vector2d(self.a / 2.0, self.h / 2.0),
652
+ Vector2d(-self.a / 2.0, self.h / 2.0),
653
+ ]
654
+ return result
655
+
656
+ def get_position_point(self, position: ShapePosition) -> Vector2d:
657
+ match position:
658
+ case ShapePosition.BOTTOM_CENTER:
659
+ return Vector2d(0.0, -self.h / 2.0)
660
+ case ShapePosition.BOTTOM_LEFT:
661
+ return Vector2d(-self.a / 2.0, -self.h / 2.0)
662
+ case ShapePosition.BOTTOM_RIGHT:
663
+ return Vector2d(self.a / 2.0, -self.h / 2.0)
664
+ case ShapePosition.CENTER_CENTER:
665
+ return Vector2d.zero()
666
+ case ShapePosition.CENTER_LEFT:
667
+ return Vector2d(-self.a / 2.0, 0.0)
668
+ case ShapePosition.CENTER_RIGHT:
669
+ return Vector2d(self.a / 2.0, 0.0)
670
+ case ShapePosition.TOP_CENTER:
671
+ return Vector2d(0.0, self.h / 2.0)
672
+ case ShapePosition.TOP_LEFT:
673
+ return Vector2d(-self.a / 2.0, self.h / 2.0)
674
+ case ShapePosition.TOP_RIGHT:
675
+ return Vector2d(self.a / 2.0, self.h / 2.0)
676
+ case ShapePosition.DIAGRAM_CORE:
677
+ raise NotImplementedError("LipCの図心の計算はサポートされていません")
678
+
679
+
680
+ @dataclass(eq=False, order=False, kw_only=True)
681
+ class ShapeOctagon(Shape):
682
+ name: str
683
+ width_x: float
684
+ width_y: float
685
+ width_chamfer_bottom_left_x: float = 0.0
686
+ width_chamfer_bottom_left_y: float = 0.0
687
+ width_chamfer_bottom_right_x: float = 0.0
688
+ width_chamfer_bottom_right_y: float = 0.0
689
+ width_chamfer_top_right_x: float = 0.0
690
+ width_chamfer_top_right_y: float = 0.0
691
+ width_chamfer_top_left_x: float = 0.0
692
+ width_chamfer_top_left_y: float = 0.0
693
+
694
+ def get_out_point2ds(self) -> list[Vector2d]:
695
+ result: list[Vector2d] = []
696
+ if (
697
+ self.width_chamfer_bottom_left_x == 0.0
698
+ or self.width_chamfer_bottom_left_y == 0.0
699
+ ):
700
+ result.append(Vector2d(self._x_min, self._y_min))
701
+ else:
702
+ result.append(
703
+ Vector2d(self._x_min + self.width_chamfer_bottom_left_x, self._y_min)
704
+ )
705
+ if (
706
+ self.width_chamfer_bottom_right_x == 0.0
707
+ or self.width_chamfer_bottom_right_y == 0.0
708
+ ):
709
+ result.append(Vector2d(self._x_max, self._y_min))
710
+ else:
711
+ result.append(
712
+ Vector2d(self._x_max - self.width_chamfer_bottom_right_x, self._y_min)
713
+ )
714
+ result.append(
715
+ Vector2d(self._x_max, self._y_min + self.width_chamfer_bottom_right_y)
716
+ )
717
+ if (
718
+ self.width_chamfer_top_right_x == 0.0
719
+ or self.width_chamfer_top_right_y == 0.0
720
+ ):
721
+ result.append(Vector2d(self._x_max, self._y_max))
722
+ else:
723
+ result.append(
724
+ Vector2d(self._x_max, self._y_max - self.width_chamfer_top_right_y)
725
+ )
726
+ result.append(
727
+ Vector2d(self._x_max - self.width_chamfer_top_right_x, self._y_max)
728
+ )
729
+ if self.width_chamfer_top_left_x == 0.0 or self.width_chamfer_top_left_y == 0.0:
730
+ result.append(Vector2d(self._x_min, self._y_max))
731
+ else:
732
+ result.append(
733
+ Vector2d(self._x_min + self.width_chamfer_top_left_x, self._y_max)
734
+ )
735
+ result.append(
736
+ Vector2d(self._x_min, self._y_max - self.width_chamfer_top_left_y)
737
+ )
738
+ if (
739
+ self.width_chamfer_bottom_left_x == 0.0
740
+ or self.width_chamfer_bottom_left_y == 0.0
741
+ ):
742
+ pass
743
+ else:
744
+ result.append(
745
+ Vector2d(self._x_min, self._y_min + self.width_chamfer_bottom_left_y)
746
+ )
747
+ return result
748
+
749
+ @property
750
+ def _x_min(self) -> float:
751
+ return -self.width_x / 2.0
752
+
753
+ @property
754
+ def _x_max(self) -> float:
755
+ return self.width_x / 2.0
756
+
757
+ @property
758
+ def _x_center(self) -> float:
759
+ return 0.0
760
+
761
+ @property
762
+ def _y_min(self) -> float:
763
+ return -self.width_y / 2.0
764
+
765
+ @property
766
+ def _y_max(self) -> float:
767
+ return self.width_y / 2.0
768
+
769
+ @property
770
+ def _y_center(self) -> float:
771
+ return 0.0
772
+
773
+
774
+ @dataclass(eq=False, order=False, kw_only=True)
775
+ class ShapePipe(Shape):
776
+ name: str
777
+ d: float
778
+ t: float
779
+ number_of_divisions: int = 12
780
+
781
+ def get_out_point2ds(self) -> list[Vector2d]:
782
+ n_div: int = max(self.number_of_divisions, 3)
783
+ unit_angle: float = math.pi * 2.0 / n_div
784
+ r: float = self.d / 2.0
785
+ result: list[Vector2d] = [
786
+ Vector2d(
787
+ r * math.cos(i * unit_angle),
788
+ r * math.sin(i * unit_angle),
789
+ )
790
+ for i in range(n_div)
791
+ ]
792
+ return result
793
+
794
+ def get_in_point2ds(self) -> list[Vector2d]:
795
+ n_div: int = max(self.number_of_divisions, 3)
796
+ unit_angle: float = math.pi * 2.0 / n_div
797
+ r = self.d / 2.0 - self.t
798
+ result: list[Vector2d] = [
799
+ Vector2d(
800
+ r * math.cos(i * unit_angle),
801
+ r * math.sin(i * unit_angle),
802
+ )
803
+ for i in range(n_div)
804
+ ]
805
+ return result
806
+
807
+ def get_position_point(self, position: ShapePosition) -> Vector2d:
808
+ i: int
809
+ match position:
810
+ case ShapePosition.CENTER_CENTER | ShapePosition.DIAGRAM_CORE:
811
+ return Vector2d.zero()
812
+ case ShapePosition.BOTTOM_CENTER:
813
+ i = 6
814
+ case ShapePosition.BOTTOM_LEFT:
815
+ i = 5
816
+ case ShapePosition.BOTTOM_RIGHT:
817
+ i = 7
818
+ case ShapePosition.CENTER_LEFT:
819
+ i = 4
820
+ case ShapePosition.CENTER_RIGHT:
821
+ i = 0
822
+ case ShapePosition.TOP_CENTER:
823
+ i = 2
824
+ case ShapePosition.TOP_LEFT:
825
+ i = 1
826
+ case ShapePosition.TOP_RIGHT:
827
+ i = 3
828
+ unit_angle: float = math.pi * 2.0 / 8.0
829
+ r: float = self.d / 2.0
830
+ return Vector2d(
831
+ r * math.cos(i * unit_angle),
832
+ r * math.sin(i * unit_angle),
833
+ )
834
+
835
+
836
+ @dataclass(eq=False, order=False, kw_only=True)
837
+ class ShapeRectangle(Shape):
838
+ name: str
839
+ width_x: float
840
+ width_y: float
841
+
842
+ def get_out_point2ds(self) -> list[Vector2d]:
843
+ result: list[Vector2d] = [
844
+ Vector2d(-self.width_x / 2.0, -self.width_y / 2.0),
845
+ Vector2d(self.width_x / 2.0, -self.width_y / 2.0),
846
+ Vector2d(self.width_x / 2.0, self.width_y / 2.0),
847
+ Vector2d(-self.width_x / 2.0, self.width_y / 2.0),
848
+ ]
849
+ return result
850
+
851
+ def get_position_point(self, position: ShapePosition) -> Vector2d:
852
+ match position:
853
+ case ShapePosition.CENTER_CENTER | ShapePosition.DIAGRAM_CORE:
854
+ return Vector2d.zero()
855
+ case ShapePosition.BOTTOM_CENTER:
856
+ return Vector2d(0.0, -self.width_y / 2.0)
857
+ case ShapePosition.BOTTOM_LEFT:
858
+ return Vector2d(-self.width_x / 2.0, -self.width_y / 2.0)
859
+ case ShapePosition.BOTTOM_RIGHT:
860
+ return Vector2d(self.width_x / 2.0, -self.width_y / 2.0)
861
+ case ShapePosition.CENTER_LEFT:
862
+ return Vector2d(-self.width_x / 2.0, 0.0)
863
+ case ShapePosition.CENTER_RIGHT:
864
+ return Vector2d(self.width_x / 2.0, 0.0)
865
+ case ShapePosition.TOP_CENTER:
866
+ return Vector2d(0.0, self.width_y / 2.0)
867
+ case ShapePosition.TOP_LEFT:
868
+ return Vector2d(-self.width_x / 2.0, self.width_y / 2.0)
869
+ case ShapePosition.TOP_RIGHT:
870
+ return Vector2d(self.width_x / 2.0, self.width_y / 2.0)
871
+
872
+
873
+ @dataclass(eq=False, order=False, kw_only=True)
874
+ class ShapeRightTriangle(Shape):
875
+ """直角三角形
876
+
877
+ 左下が直角とする"""
878
+
879
+ name: str
880
+ width_x: float
881
+ width_y: float
882
+ chamfer_x: float = 0.0
883
+ chamfer_y: float = 0.0
884
+
885
+ def get_out_point2ds(self) -> list[Vector2d]:
886
+ result: list[Vector2d] = [
887
+ Vector2d(-self.width_x / 2.0, -self.width_y / 2.0),
888
+ Vector2d(self.width_x / 2.0, -self.width_y / 2.0),
889
+ ]
890
+ if self.chamfer_y > 0.0:
891
+ result.append(
892
+ Vector2d(self.width_x / 2.0, -self.width_y / 2.0 + self.chamfer_y)
893
+ )
894
+ if self.chamfer_x > 0.0:
895
+ result.append(
896
+ Vector2d(-self.width_x / 2.0 + self.chamfer_x, self.width_y / 2.0)
897
+ )
898
+ result.append(
899
+ Vector2d(-self.width_x / 2.0, self.width_y / 2.0),
900
+ )
901
+ return result
902
+
903
+ @property
904
+ def _x_max(self) -> float:
905
+ return self.width_x / 2.0
906
+
907
+ @property
908
+ def _x_min(self) -> float:
909
+ return -self.width_x / 2.0
910
+
911
+ @property
912
+ def _x_center(self) -> float:
913
+ return 0.0
914
+
915
+ @property
916
+ def _y_max(self) -> float:
917
+ return self.width_y / 2.0
918
+
919
+ @property
920
+ def _y_min(self) -> float:
921
+ return -self.width_y / 2.0
922
+
923
+ @property
924
+ def _y_center(self) -> float:
925
+ return 0.0
926
+
927
+
928
+ @dataclass(eq=False, order=False, kw_only=True)
929
+ class ShapeT(Shape):
930
+ name: str
931
+ a: float
932
+ b: float
933
+ t1: float
934
+ t2: float
935
+ r: float
936
+
937
+ def get_out_point2ds(self) -> list[Vector2d]:
938
+ result: list[Vector2d] = [
939
+ Vector2d(-self.t1 / 2.0, -self.a / 2.0),
940
+ Vector2d(self.t1 / 2.0, -self.a / 2.0),
941
+ Vector2d(self.t1 / 2.0, self.a / 2.0 - self.t2),
942
+ Vector2d(self.b / 2.0, self.a / 2.0 - self.t2),
943
+ Vector2d(self.b / 2.0, self.a / 2.0),
944
+ Vector2d(-self.b / 2.0, self.a / 2.0),
945
+ Vector2d(-self.b / 2.0, self.a / 2.0 - self.t2),
946
+ Vector2d(-self.t1 / 2.0, self.a / 2.0 - self.t2),
947
+ ]
948
+ return result
949
+
950
+ def get_position_point(self, position: ShapePosition) -> Vector2d:
951
+ match position:
952
+ case ShapePosition.BOTTOM_CENTER:
953
+ return Vector2d(0.0, -self.a / 2.0)
954
+ case ShapePosition.BOTTOM_LEFT:
955
+ return Vector2d(-self.b / 2.0, -self.a / 2.0)
956
+ case ShapePosition.BOTTOM_RIGHT:
957
+ return Vector2d(self.b / 2.0, -self.a / 2.0)
958
+ case ShapePosition.CENTER_CENTER:
959
+ return Vector2d.zero()
960
+ case ShapePosition.CENTER_LEFT:
961
+ return Vector2d(-self.b / 2.0, 0.0)
962
+ case ShapePosition.CENTER_RIGHT:
963
+ return Vector2d(self.b / 2.0, 0.0)
964
+ case ShapePosition.TOP_CENTER:
965
+ return Vector2d(0.0, self.a / 2.0)
966
+ case ShapePosition.TOP_LEFT:
967
+ return Vector2d(-self.b / 2.0, self.a / 2.0)
968
+ case ShapePosition.TOP_RIGHT:
969
+ return Vector2d(self.b / 2.0, self.a / 2.0)
970
+ case ShapePosition.DIAGRAM_CORE:
971
+ raise NotImplementedError("T形鋼の図心の計算はサポートされていません")
972
+
973
+
974
+ @dataclass(eq=False, order=False, kw_only=True)
975
+ class ShapeTSrc(Shape):
976
+ name: str
977
+ h_h: float
978
+ h_b: float
979
+ h_tw: float
980
+ h_tf: float
981
+ t_h: float
982
+ t_b: float
983
+ t_tw: float
984
+ t_tf: float
985
+ t_offset_x: float = 0.0
986
+
987
+ @property
988
+ def _x_max(self) -> float:
989
+ return self.h_h / 2.0
990
+
991
+ @property
992
+ def _x_min(self) -> float:
993
+ return -self.h_h / 2.0
994
+
995
+ @property
996
+ def _x_center(self) -> float:
997
+ return 0.0
998
+
999
+ @property
1000
+ def _y_max(self) -> float:
1001
+ return (self.t_h + self.h_b / 2.0) / 2.0
1002
+
1003
+ @property
1004
+ def _y_min(self) -> float:
1005
+ return -self._y_max
1006
+
1007
+ @property
1008
+ def _y_center(self) -> float:
1009
+ return 0.0
1010
+
1011
+ def get_out_point2ds(self) -> list[Vector2d]:
1012
+ return [
1013
+ Vector2d(-self.t_b / 2.0 + self.t_offset_x, self._y_min),
1014
+ Vector2d(self.t_b / 2.0 + self.t_offset_x, self._y_min),
1015
+ Vector2d(self.t_b / 2.0 + self.t_offset_x, self._y_min + self.t_tf),
1016
+ Vector2d(self.t_tw / 2.0 + self.t_offset_x, self._y_min + self.t_tf),
1017
+ Vector2d(
1018
+ self.t_tw / 2.0 + self.t_offset_x,
1019
+ self._y_max - self.h_b / 2.0 - self.h_tw / 2.0,
1020
+ ),
1021
+ Vector2d(
1022
+ self._x_max - self.h_tf, self._y_max - self.h_b / 2.0 - self.h_tw / 2.0
1023
+ ),
1024
+ Vector2d(self._x_max - self.h_tf, self._y_max - self.h_b),
1025
+ Vector2d(self._x_max, self._y_max - self.h_b),
1026
+ Vector2d(self._x_max, self._y_max),
1027
+ Vector2d(self._x_max - self.h_tf, self._y_max),
1028
+ Vector2d(
1029
+ self._x_max - self.h_tf, self._y_max - self.h_b / 2.0 + self.h_tw / 2.0
1030
+ ),
1031
+ Vector2d(
1032
+ self._x_min + self.h_tf, self._y_max - self.h_b / 2.0 + self.h_tw / 2.0
1033
+ ),
1034
+ Vector2d(self._x_min + self.h_tf, self._y_max),
1035
+ Vector2d(self._x_min, self._y_max),
1036
+ Vector2d(self._x_min, self._y_max - self.h_b),
1037
+ Vector2d(self._x_min + self.h_tf, self._y_max - self.h_b),
1038
+ Vector2d(
1039
+ self._x_min + self.h_tf, self._y_max - self.h_b / 2.0 - self.h_tw / 2.0
1040
+ ),
1041
+ Vector2d(
1042
+ -self.t_tw / 2.0 + self.t_offset_x,
1043
+ self._y_max - self.h_b / 2.0 - self.h_tw / 2.0,
1044
+ ),
1045
+ Vector2d(-self.t_tw / 2.0 + self.t_offset_x, self._y_min + self.t_tf),
1046
+ Vector2d(-self.t_b / 2.0 + self.t_offset_x, self._y_min + self.t_tf),
1047
+ ]
1048
+
1049
+
1050
+ class ShapeUnknown(Shape):
1051
+ _instance: Shape | None = None
1052
+
1053
+ def __new__(cls) -> Self:
1054
+ if cls._instance is None:
1055
+ cls._instance = ShapeRectangle(
1056
+ name="Unknown",
1057
+ width_x=UNKNOWN_ELEMENT_SIZE_MM,
1058
+ width_y=UNKNOWN_ELEMENT_SIZE_MM,
1059
+ )
1060
+ return cls._instance # type:ignore[return-value]
1061
+
1062
+ def get_out_point2ds(self) -> list[Vector2d]:
1063
+ if self._instance is None:
1064
+ raise RuntimeError("UnknownShape instance is not initialized.")
1065
+ return self._instance.get_out_point2ds()
1066
+
1067
+
1068
+ class ShapeUndefined(Shape):
1069
+ _instance: Shape | None = None
1070
+
1071
+ def __new__(cls) -> Any:
1072
+ if cls._instance is None:
1073
+ cls._instance = ShapeRectangle(
1074
+ name="Undefined",
1075
+ width_x=UNKNOWN_ELEMENT_SIZE_MM,
1076
+ width_y=UNKNOWN_ELEMENT_SIZE_MM,
1077
+ )
1078
+ return cls._instance
1079
+
1080
+ def get_out_point2ds(self) -> list[Vector2d]:
1081
+ if self._instance is None:
1082
+ raise RuntimeError("UndefinedShape instance is not initialized.")
1083
+ return self._instance.get_out_point2ds()
1084
+
1085
+
1086
+ SHAPE_PAIR_UNKNOWN: Final[ShapePair] = ShapePair(start=ShapeUnknown(), end=None)
1087
+ SHAPE_PAIRS_UNKNOWN: Final[list[ShapePair]] = [SHAPE_PAIR_UNKNOWN]
1088
+ SHAPE_PAIR_UNDEFINED: Final[ShapePair] = ShapePair(start=ShapeUndefined(), end=None)
1089
+ SHAPE_PAIRS_UNDEFINED: Final[list[ShapePair]] = [SHAPE_PAIR_UNDEFINED]