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,549 @@
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 uuid
8
+ from collections.abc import Sequence
9
+ from dataclasses import dataclass, field
10
+ from uuid import UUID
11
+
12
+ from stbkit.core.stb_reporting import Code, Phase, Reporter
13
+
14
+ from stbkit.tools._internal.vectors import Vector2d, Vector3d
15
+
16
+ from ..._internal.data_model.building_geometry import (
17
+ BuildingGeometry,
18
+ ElementFlags,
19
+ ElementKind,
20
+ SurfaceLoopType,
21
+ )
22
+ from ..._internal.data_model.element_data import (
23
+ ElementData,
24
+ ElementLine,
25
+ ElementLineSrc,
26
+ ElementPlane,
27
+ ElementType,
28
+ Node,
29
+ )
30
+ from ..._internal.data_model.shape_data import Shape, ShapePair, ShapePosition
31
+ from ..._internal.utils.mesh_utils import create_mesh
32
+ from .coord_converter import (
33
+ axis_vector_plate,
34
+ to_global_coord,
35
+ to_global_coord_plate,
36
+ to_plate_element_coord,
37
+ )
38
+
39
+
40
+ @dataclass
41
+ class _LocalMesh:
42
+ vertices: list[Vector3d] = field(default_factory=list)
43
+ faces: list[Sequence[int]] = field(default_factory=list)
44
+
45
+ def add_vertices_and_faces(
46
+ self, vertices: Sequence[Vector3d], faces: Sequence[Sequence[int]]
47
+ ) -> None:
48
+ offset: int = len(self.vertices)
49
+ self.vertices.extend(vertices)
50
+ for face in faces:
51
+ self.faces.append([f + offset for f in face])
52
+
53
+
54
+ def create_local_line_meshes(
55
+ shapes: Sequence[ShapePair],
56
+ axis_position: ShapePosition,
57
+ lengths: Sequence[float],
58
+ ) -> list[_LocalMesh]:
59
+ if not shapes:
60
+ raise AssertionError("形状が設定されていません")
61
+ if len(shapes) != len(lengths):
62
+ raise AssertionError("形状と長さの数が異なります")
63
+
64
+ meshes: list[_LocalMesh] = []
65
+ start_length = 0.0
66
+ for shape, length in zip(shapes, lengths, strict=True):
67
+ shape_end: Shape = shape.end if shape.end else shape.start
68
+ start_point2ds: list[Vector2d] = (
69
+ shape.start.get_out_point2ds_with_rotate_and_offset()
70
+ )
71
+ end_point2ds: list[Vector2d] = (
72
+ shape_end.get_out_point2ds_with_rotate_and_offset()
73
+ )
74
+ start_in_point2ds: list[Vector2d] | None = (
75
+ shape.start.get_in_point2ds_with_rotate_and_offset()
76
+ )
77
+ end_in_point2ds: list[Vector2d] | None = (
78
+ shape_end.get_in_point2ds_with_rotate_and_offset()
79
+ )
80
+ index_start: int = 0
81
+ index_end: int = index_start + len(start_point2ds)
82
+ if axis_position != ShapePosition.CENTER_CENTER:
83
+ offset_start: Vector2d = shape.start.get_position_point(axis_position)
84
+ start_point2ds = [point - offset_start for point in start_point2ds]
85
+ offset_end: Vector2d = shape_end.get_position_point(axis_position)
86
+ end_point2ds = [point - offset_end for point in end_point2ds]
87
+
88
+ if len(start_point2ds) != len(end_point2ds):
89
+ raise AssertionError("断面のStartとEndの頂点数が異なります")
90
+
91
+ n: int = len(start_point2ds)
92
+ vertices: list[Vector3d] = [
93
+ point.to_vector3d(start_length) for point in start_point2ds
94
+ ]
95
+ vertices.extend(
96
+ point.to_vector3d(start_length + length) for point in end_point2ds
97
+ )
98
+ faces: list[Sequence[int]] = []
99
+ if (
100
+ start_in_point2ds
101
+ and end_in_point2ds
102
+ and len(start_in_point2ds) == len(end_in_point2ds)
103
+ and len(start_point2ds) == len(start_in_point2ds)
104
+ ):
105
+ vertices.extend(
106
+ point.to_vector3d(start_length) for point in start_in_point2ds
107
+ )
108
+ vertices.extend(
109
+ point.to_vector3d(start_length + length) for point in end_in_point2ds
110
+ )
111
+ index_in_start: int = index_end + len(end_point2ds)
112
+ index_in_end: int = index_in_start + len(start_in_point2ds)
113
+ for i in range(len(start_in_point2ds)):
114
+ faces.append(
115
+ [
116
+ (i + 1) % n + index_in_start,
117
+ i + index_in_start,
118
+ i + index_in_end,
119
+ (i + 1) % n + index_in_end,
120
+ ]
121
+ )
122
+ faces.append(
123
+ [
124
+ i + index_start,
125
+ (i + 1) % n + index_start,
126
+ (i + 1) % n + index_in_start,
127
+ i + index_in_start,
128
+ ]
129
+ )
130
+ faces.append(
131
+ [
132
+ (i + 1) % n + index_end,
133
+ i + index_end,
134
+ i + index_in_end,
135
+ (i + 1) % n + index_in_end,
136
+ ]
137
+ )
138
+ else:
139
+ faces.append(list(range(n)))
140
+ faces.append(list(range(2 * n - 1, n - 1, -1)))
141
+
142
+ for i in range(n):
143
+ faces.append(
144
+ [
145
+ i,
146
+ (i + 1) % n,
147
+ (i + 1) % n + n,
148
+ i + n,
149
+ ]
150
+ )
151
+
152
+ meshes.append(_LocalMesh(vertices=vertices, faces=faces))
153
+ start_length += length
154
+
155
+ return meshes
156
+
157
+
158
+ def _triangulate_faces(mesh: _LocalMesh) -> _LocalMesh:
159
+ result_vertices: list[Vector3d] = list(mesh.vertices)
160
+ result_faces: list[Sequence[int]] = []
161
+
162
+ for face in mesh.faces:
163
+ match len(face):
164
+ case 0 | 1 | 2:
165
+ raise ValueError("頂点数が3未満の面は三角形分割できません")
166
+ case 3:
167
+ result_faces.append(list(face))
168
+ case _:
169
+ face_vertices: list[Vector3d] = [mesh.vertices[index] for index in face]
170
+ face_vertices_element: list[Vector2d] = [
171
+ Vector2d(v.x, v.y) for v in to_plate_element_coord(face_vertices)
172
+ ]
173
+ _, new_faces, _, _ = create_mesh(face_vertices_element)
174
+ result_faces.extend(new_faces)
175
+ return _LocalMesh(vertices=result_vertices, faces=result_faces)
176
+
177
+
178
+ def element_line_to_element_mesh(
179
+ element: ElementLine, *, allow_polygons: bool, reporter: Reporter
180
+ ) -> _LocalMesh | None:
181
+ if not element.shapes or not element.point_start or not element.point_end:
182
+ reporter.error(
183
+ "形状が設定されていません",
184
+ code=Code.UNKNOWN_SHAPE,
185
+ phase=Phase.CONVERT_GEOMETRY,
186
+ )
187
+ return None
188
+ if not element.element_type:
189
+ reporter.error(
190
+ "要素タイプが設定されていません",
191
+ code=Code.UNKNOWN_SHAPE,
192
+ phase=Phase.CONVERT_GEOMETRY,
193
+ )
194
+ return None
195
+ if len(element.shapes) != len(element.lengths):
196
+ reporter.error(
197
+ "形状と長さの数が異なります",
198
+ code=Code.UNKNOWN_SHAPE,
199
+ phase=Phase.CONVERT_GEOMETRY,
200
+ )
201
+ return None
202
+ local_meshes: list[_LocalMesh] = create_local_line_meshes(
203
+ element.shapes,
204
+ element.axis_position,
205
+ element.lengths,
206
+ )
207
+ mesh: _LocalMesh = _LocalMesh()
208
+ for local_mesh in local_meshes:
209
+ global_vertices: list[Vector3d] = to_global_coord(
210
+ local_mesh.vertices,
211
+ element.point_start,
212
+ element.point_end,
213
+ element.angle,
214
+ element.element_type,
215
+ )
216
+ mesh.add_vertices_and_faces(
217
+ [point.mm_to_m() for point in global_vertices],
218
+ local_mesh.faces,
219
+ )
220
+ if allow_polygons:
221
+ return mesh
222
+
223
+ try:
224
+ return _triangulate_faces(mesh)
225
+ except ValueError as e:
226
+ reporter.error(
227
+ f"線要素の多角形の三角形分割に失敗しました: {e}",
228
+ code=Code.UNEXPECTED_ERROR,
229
+ phase=Phase.CONVERT_GEOMETRY,
230
+ )
231
+ return None
232
+
233
+
234
+ def element_line_src_to_element_meshes(
235
+ element: ElementLineSrc, *, allow_polygons: bool, reporter: Reporter
236
+ ) -> tuple[_LocalMesh | None, _LocalMesh | None]:
237
+ if element.rc is None or element.steel is None:
238
+ reporter.error(
239
+ "RCまたはS形状が設定されていません",
240
+ code=Code.UNKNOWN_SHAPE,
241
+ phase=Phase.CONVERT_GEOMETRY,
242
+ )
243
+ return None, None
244
+ mesh_rc: _LocalMesh | None = element_line_to_element_mesh(
245
+ element.rc, allow_polygons=allow_polygons, reporter=reporter
246
+ )
247
+ mesh_s: _LocalMesh | None = element_line_to_element_mesh(
248
+ element.steel, allow_polygons=allow_polygons, reporter=reporter
249
+ )
250
+ return mesh_rc, mesh_s
251
+
252
+
253
+ def element_plane_to_element_mesh(
254
+ element: ElementPlane, *, allow_polygons: bool, reporter: Reporter
255
+ ) -> _LocalMesh | None:
256
+ if not element.points:
257
+ raise AssertionError("要素の頂点が設定されていません")
258
+ thickness: float = element.thickness if element.thickness else 10
259
+ # 表面
260
+ out_points2d: list[Vector2d] = [
261
+ Vector2d(v.x, v.y) for v in to_plate_element_coord(element.points)
262
+ ]
263
+ in_points2d: list[list[Vector2d]] = element.opens or []
264
+ try:
265
+ vertices2d, faces, new_out_points, new_in_points = create_mesh(
266
+ out_points2d, in_points2d
267
+ )
268
+ except ValueError as e:
269
+ reporter.error(
270
+ f"平面要素のメッシュ化に失敗しました: {e}",
271
+ code=Code.UNEXPECTED_ERROR,
272
+ phase=Phase.CONVERT_GEOMETRY,
273
+ )
274
+ return None
275
+ mesh: _LocalMesh = _LocalMesh()
276
+ vertices3d: list[Vector3d] = to_global_coord_plate(
277
+ [v.to_vector3d(z=thickness) for v in vertices2d], element.points
278
+ )
279
+ mesh.add_vertices_and_faces([v.mm_to_m() for v in vertices3d], faces)
280
+ # 裏面
281
+ faces_rev = [list(reversed(face)) for face in faces]
282
+ vertices3d_back: list[Vector3d] = to_global_coord_plate(
283
+ [v.to_vector3d(z=0.0) for v in vertices2d], element.points
284
+ )
285
+ mesh.add_vertices_and_faces(
286
+ [v.mm_to_m() for v in vertices3d_back],
287
+ faces_rev,
288
+ )
289
+
290
+ _vx, _vy, _vz = axis_vector_plate(element.points)
291
+
292
+ # 側面
293
+ side_faces: list[list[int]] = (
294
+ [[0, 1, 2, 3]] if allow_polygons else [[0, 1, 2], [0, 2, 3]]
295
+ )
296
+
297
+ if new_out_points is not None:
298
+ if len(new_out_points) == 1:
299
+ out_points2d = new_out_points[0]
300
+ else:
301
+ raise NotImplementedError("未実装です")
302
+ if new_in_points is not None:
303
+ in_points2d = new_in_points
304
+ # 側面内側
305
+ for tmp_in_point2ds in in_points2d:
306
+ tmp_in_point2ds = tmp_in_point2ds + [tmp_in_point2ds[0]]
307
+ for i in range(len(tmp_in_point2ds) - 1):
308
+ p0 = tmp_in_point2ds[i]
309
+ p1 = tmp_in_point2ds[i + 1]
310
+ p0_3d_top = to_global_coord_plate(
311
+ [p0.to_vector3d(z=thickness)], element.points
312
+ )[0]
313
+ p1_3d_top = to_global_coord_plate(
314
+ [p1.to_vector3d(z=thickness)], element.points
315
+ )[0]
316
+ p0_3d_bottom = to_global_coord_plate(
317
+ [p0.to_vector3d(z=0.0)], element.points
318
+ )[0]
319
+ p1_3d_bottom = to_global_coord_plate(
320
+ [p1.to_vector3d(z=0.0)], element.points
321
+ )[0]
322
+ mesh.add_vertices_and_faces(
323
+ [
324
+ p0_3d_top.mm_to_m(),
325
+ p1_3d_top.mm_to_m(),
326
+ p1_3d_bottom.mm_to_m(),
327
+ p0_3d_bottom.mm_to_m(),
328
+ ],
329
+ side_faces,
330
+ )
331
+ # 側面外側
332
+ out_points2d = out_points2d + [out_points2d[0]]
333
+ for i in range(len(out_points2d) - 1):
334
+ p0 = out_points2d[i]
335
+ p1 = out_points2d[i + 1]
336
+ p0_3d_top = to_global_coord_plate(
337
+ [p0.to_vector3d(z=thickness)], element.points
338
+ )[0]
339
+ p1_3d_top = to_global_coord_plate(
340
+ [p1.to_vector3d(z=thickness)], element.points
341
+ )[0]
342
+ p0_3d_bottom = to_global_coord_plate([p0.to_vector3d(z=0.0)], element.points)[0]
343
+ p1_3d_bottom = to_global_coord_plate([p1.to_vector3d(z=0.0)], element.points)[0]
344
+ mesh.add_vertices_and_faces(
345
+ [
346
+ p0_3d_top.mm_to_m(),
347
+ p1_3d_top.mm_to_m(),
348
+ p1_3d_bottom.mm_to_m(),
349
+ p0_3d_bottom.mm_to_m(),
350
+ ],
351
+ side_faces,
352
+ )
353
+
354
+ return mesh
355
+
356
+
357
+ def _get_element_source_type(
358
+ element: ElementLine | ElementLineSrc | ElementPlane,
359
+ ) -> str:
360
+ path: str | None = element.path
361
+ if path:
362
+ source_type = path.rsplit("/", 1)[-1].partition("[")[0]
363
+ if source_type:
364
+ return source_type
365
+
366
+ element_type: ElementType | None = element.element_type
367
+ return element_type.value if element_type is not None else ""
368
+
369
+
370
+ def _element_kind_from_type(element_type: ElementType | None) -> ElementKind:
371
+ if element_type is None:
372
+ return ElementKind.OTHER
373
+ return ElementKind[element_type.name]
374
+
375
+
376
+ _OFFSET_AXIS_TYPES: frozenset[ElementType] = frozenset(
377
+ {
378
+ ElementType.PILE,
379
+ ElementType.FOUNDATION_COLUMN,
380
+ ElementType.FOOTING,
381
+ ElementType.PARAPET,
382
+ }
383
+ )
384
+ """Line描画の際にoffsetを考慮しないと見えない部材
385
+ - 杭は基準節点が1つのため、1つの柱に複数の杭がある場合offsetで表現しないと重なって見えない
386
+ - 基礎柱、フーチングは基準節点が1つであり、点になってしまうため、offsetした位置で表現する
387
+ - パラペットは基本的に梁と節点がかぶるため、重なってしまうのでoffsetで表現する
388
+ """
389
+
390
+
391
+ def _line_points(
392
+ element: ElementLine | ElementLineSrc,
393
+ node_infos: dict[int, Node],
394
+ ) -> tuple[Vector3d, Vector3d] | None:
395
+ node_ids: list[int] = element.node_ids
396
+ if element.element_type not in _OFFSET_AXIS_TYPES and len(node_ids) == 2:
397
+ node_start: Node | None = node_infos.get(node_ids[0])
398
+ node_end: Node | None = node_infos.get(node_ids[1])
399
+ if node_start is not None and node_end is not None:
400
+ return node_start.position, node_end.position
401
+ line: ElementLine | None = (
402
+ element
403
+ if isinstance(element, ElementLine)
404
+ else (element.rc if element.rc is not None else element.steel)
405
+ )
406
+ if line is None or line.point_start is None or line.point_end is None:
407
+ return None
408
+ return line.point_start, line.point_end
409
+
410
+
411
+ def _plane_points(
412
+ element: ElementPlane,
413
+ ) -> list[tuple[SurfaceLoopType, list[Vector3d]]] | None:
414
+ node_points: list[Vector3d] | None = element.node_points
415
+ if node_points is None or len(node_points) < 3:
416
+ return None
417
+
418
+ loops: list[tuple[SurfaceLoopType, list[Vector3d]]] = [
419
+ (SurfaceLoopType.OUTER, [point.mm_to_m() for point in node_points])
420
+ ]
421
+ for open_point2ds in element.opens:
422
+ if len(open_point2ds) < 3:
423
+ continue
424
+ open_point3ds: list[Vector3d] = to_global_coord_plate(
425
+ [point.to_vector3d(z=0.0) for point in open_point2ds],
426
+ node_points,
427
+ )
428
+ loops.append(
429
+ (SurfaceLoopType.HOLE, [point.mm_to_m() for point in open_point3ds])
430
+ )
431
+ return loops
432
+
433
+
434
+ def _add_element_info(
435
+ geom: BuildingGeometry,
436
+ element_index: int,
437
+ element: ElementLine | ElementLineSrc | ElementPlane,
438
+ node_infos: dict[int, Node],
439
+ node_indices: dict[int, int],
440
+ ) -> None:
441
+ node_relation: list[int] = [
442
+ node_indices[node_id] for node_id in element.node_ids if node_id in node_indices
443
+ ]
444
+ if node_relation:
445
+ geom.set_element_nodes(element_index, node_relation)
446
+
447
+ if isinstance(element, ElementPlane):
448
+ loops = _plane_points(element)
449
+ if loops is not None:
450
+ geom.add_element_plane_shape(element_index, loops)
451
+ return
452
+
453
+ axis_points = _line_points(element, node_infos)
454
+ if axis_points is not None:
455
+ point_start, point_end = axis_points
456
+ geom.add_element_line_shape(
457
+ element_index,
458
+ start=point_start.mm_to_m(),
459
+ end=point_end.mm_to_m(),
460
+ )
461
+
462
+
463
+ def element_data_to_building_geometry(
464
+ elements: ElementData,
465
+ *,
466
+ allow_polygons: bool,
467
+ reporter: Reporter,
468
+ node_infos: dict[int, Node] | None = None,
469
+ ) -> BuildingGeometry:
470
+
471
+ geom: BuildingGeometry = BuildingGeometry()
472
+ node_indices: dict[int, int] = {}
473
+ if node_infos is not None:
474
+ node_indices = {
475
+ node_id: geom.add_node(
476
+ node.position.mm_to_m(),
477
+ source_id=str(node_id),
478
+ guid=node.guid,
479
+ xpath=node.xpath,
480
+ )
481
+ for node_id, node in node_infos.items()
482
+ }
483
+
484
+ for element in elements:
485
+ path: str | None = element.path
486
+ guid: UUID = element.guid if element.guid else uuid.uuid4()
487
+ element_kind: ElementKind = _element_kind_from_type(element.element_type)
488
+
489
+ source_type: str = _get_element_source_type(element)
490
+ # SRCのクリック検出は外側のRCで行う。そのためRCを先に登録する。
491
+ is_composite: bool = isinstance(element, ElementLineSrc)
492
+ index: int = geom.add_element(
493
+ kind=element_kind,
494
+ guid=guid,
495
+ xpath=path,
496
+ name=element.name,
497
+ source_type=source_type,
498
+ flags=(
499
+ ElementFlags.IS_COMPOSITE_CONCRETE
500
+ if is_composite
501
+ else ElementFlags.NONE
502
+ ),
503
+ )
504
+ if node_infos is not None:
505
+ _add_element_info(
506
+ geom,
507
+ index,
508
+ element,
509
+ node_infos,
510
+ node_indices,
511
+ )
512
+ match element:
513
+ case ElementLineSrc():
514
+ mesh_rc, mesh_s = element_line_src_to_element_meshes(
515
+ element,
516
+ allow_polygons=allow_polygons,
517
+ reporter=reporter,
518
+ )
519
+ if mesh_rc:
520
+ geom.add_element_mesh(index, mesh_rc.vertices, mesh_rc.faces)
521
+ if mesh_s:
522
+ # SはRCと同じGUID・XPathを持つ別要素とする。
523
+ # 節点関連はRCにのみ持たせて二重描画を防ぐ
524
+ steel_index: int = geom.add_element(
525
+ kind=element_kind,
526
+ guid=guid,
527
+ xpath=path,
528
+ name=element.name,
529
+ source_type=source_type,
530
+ flags=ElementFlags.IS_COMPOSITE_STEEL,
531
+ )
532
+ geom.add_element_mesh(steel_index, mesh_s.vertices, mesh_s.faces)
533
+ case ElementLine():
534
+ mesh: _LocalMesh | None = element_line_to_element_mesh(
535
+ element,
536
+ allow_polygons=allow_polygons,
537
+ reporter=reporter,
538
+ )
539
+ if mesh:
540
+ geom.add_element_mesh(index, mesh.vertices, mesh.faces)
541
+ case ElementPlane():
542
+ mesh = element_plane_to_element_mesh(
543
+ element,
544
+ allow_polygons=allow_polygons,
545
+ reporter=reporter,
546
+ )
547
+ if mesh:
548
+ geom.add_element_mesh(index, mesh.vertices, mesh.faces)
549
+ return geom
@@ -0,0 +1,21 @@
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
+ from typing import TYPE_CHECKING
8
+
9
+ from ..._internal.data_model.building_geometry import BuildingGeometry
10
+ from ..._internal.optional_dependencies.ifc_to_building_geometry import (
11
+ ifc_to_building_geometry,
12
+ )
13
+ from .building_geometry_to_ply_text import building_geometry_to_ply_text
14
+
15
+ if TYPE_CHECKING:
16
+ import ifcopenshell.file
17
+
18
+
19
+ def ifc_to_ply_text(ifc_file: "ifcopenshell.file") -> str:
20
+ geom: BuildingGeometry = ifc_to_building_geometry(ifc_file)
21
+ return building_geometry_to_ply_text(geom)
@@ -0,0 +1,23 @@
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
+ from stbkit.core._internal.name_converter import private_field_name
8
+ from stbkit.core.data_model.common import StBridgeElement
9
+
10
+
11
+ def stb_element_to_ifc_name(stb_element: StBridgeElement) -> str:
12
+ result: list[str] = []
13
+ p_name_id: str = private_field_name("id")
14
+ if hasattr(stb_element, p_name_id):
15
+ id = getattr(stb_element, p_name_id)
16
+ if id:
17
+ result.append(str(id))
18
+ p_name_name: str = private_field_name("name")
19
+ if hasattr(stb_element, p_name_name):
20
+ name = getattr(stb_element, p_name_name)
21
+ if name:
22
+ result.append(str(name))
23
+ return "_".join(result)
@@ -0,0 +1,5 @@
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/.