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,267 @@
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 ifcopenshell.util.shape_builder
8
+ from ifcopenshell import entity_instance, file
9
+
10
+ from ...data_model.ifc_data.entity_names import EntityName
11
+ from ...data_model.shape_data import (
12
+ Shape,
13
+ ShapeArbitrary,
14
+ ShapeBox,
15
+ ShapeBuildBox,
16
+ ShapeChannel,
17
+ ShapeCircle,
18
+ ShapeH,
19
+ ShapeLipC,
20
+ ShapePipe,
21
+ ShapeRectangle,
22
+ ShapeT,
23
+ )
24
+ from ...utils.float_rounding import round_float, round_vector2d
25
+
26
+ PROFILE_TYPE_AREA: str = "AREA"
27
+
28
+
29
+ class IfcProfileExporter:
30
+ def __init__(
31
+ self,
32
+ model: file | None = None,
33
+ body: entity_instance | None = None,
34
+ *,
35
+ round_digits_mm: int | None = None,
36
+ ) -> None:
37
+ self._model: file | None = model
38
+ self._body: entity_instance | None = body
39
+ self._round_digits_mm: int | None = round_digits_mm
40
+
41
+ @property
42
+ def model(self) -> file:
43
+ if self._model is None:
44
+ raise AssertionError("modelが初期化されていません")
45
+ return self._model
46
+
47
+ @property
48
+ def body(self) -> entity_instance:
49
+ if self._body is None:
50
+ raise AssertionError("bodyが初期化されていません")
51
+ return self._body
52
+
53
+ @body.setter
54
+ def body(self, value: entity_instance) -> None:
55
+ self._body = value
56
+
57
+ def get_profile(self, shape: Shape) -> entity_instance:
58
+ """プロファイルを取得する
59
+
60
+ IFCに山形鋼のプロファイルは存在するが、
61
+ ST-Bridgeと基準軸方向が異なる。
62
+ そのため、プロファイルを使うと回転角がST-Bridgeと変わってしまい、対応がわかりにくくなるため、
63
+ 山形鋼については任意形状(Arbitary)で設定する。
64
+ """
65
+ match shape:
66
+ case ShapeArbitrary():
67
+ return self.get_profile_arbitrary(shape)
68
+ case ShapeBox():
69
+ return self.get_profile_box(shape)
70
+ case ShapeBuildBox():
71
+ return self.get_profile_build_box(shape)
72
+ case ShapeChannel():
73
+ return self.get_profile_channel(shape)
74
+ case ShapeCircle():
75
+ return self.get_profile_circle(shape)
76
+ case ShapeH():
77
+ return self.get_profile_h(shape)
78
+ case ShapeLipC():
79
+ return self.get_profile_lip_c(shape)
80
+ case ShapePipe():
81
+ return self.get_profile_pipe(shape)
82
+ case ShapeRectangle():
83
+ return self.get_profile_rectangle(shape)
84
+ case ShapeT():
85
+ return self.get_profile_t(shape)
86
+ case _:
87
+ return self.get_profile_base(shape)
88
+
89
+ def get_profile_base(self, shape: Shape) -> entity_instance:
90
+ builder = ifcopenshell.util.shape_builder.ShapeBuilder(self.model)
91
+ outer_curve = builder.polyline(
92
+ [
93
+ round_vector2d(point, round_digits=self._round_digits_mm)
94
+ for point in shape.get_out_point2ds()
95
+ ],
96
+ closed=True,
97
+ )
98
+ in_point2ds = shape.get_in_point2ds()
99
+ inner_curves: list[entity_instance] = []
100
+ if in_point2ds:
101
+ inner_curve = builder.polyline(
102
+ [
103
+ round_vector2d(point, round_digits=self._round_digits_mm)
104
+ for point in in_point2ds
105
+ ],
106
+ closed=True,
107
+ )
108
+ inner_curves.append(inner_curve)
109
+ profile = builder.profile(
110
+ outer_curve, inner_curves=inner_curves, name="Arbitrary"
111
+ )
112
+ return profile
113
+
114
+ def get_profile_arbitrary(self, shape: ShapeArbitrary) -> entity_instance:
115
+ builder = ifcopenshell.util.shape_builder.ShapeBuilder(self.model)
116
+ outer_curve = builder.polyline(
117
+ [
118
+ round_vector2d(point, round_digits=self._round_digits_mm)
119
+ for point in shape.get_out_point2ds()
120
+ ],
121
+ closed=True,
122
+ )
123
+ inner_curves: list[entity_instance] = []
124
+ in_point_2ds = shape.in_points
125
+ if in_point_2ds:
126
+ for in_points in in_point_2ds:
127
+ inner_curve = builder.polyline(
128
+ [
129
+ round_vector2d(point, round_digits=self._round_digits_mm)
130
+ for point in in_points
131
+ ],
132
+ closed=True,
133
+ )
134
+ inner_curves.append(inner_curve)
135
+ profile = builder.profile(
136
+ outer_curve, inner_curves=inner_curves, name="Arbitrary"
137
+ )
138
+ return profile
139
+
140
+ def get_profile_box(self, shape: ShapeBox) -> entity_instance:
141
+ profile: entity_instance = self.model.create_entity(
142
+ EntityName.IFC_RECTANGLE_HOLLOW_PROFILE_DEF.value,
143
+ ProfileName=shape.name,
144
+ ProfileType=PROFILE_TYPE_AREA,
145
+ XDim=round_float(shape.b, round_digits=self._round_digits_mm),
146
+ YDim=round_float(shape.a, round_digits=self._round_digits_mm),
147
+ WallThickness=round_float(shape.t, round_digits=self._round_digits_mm),
148
+ InnerFilletRadius=round_float(
149
+ max(0.0, shape.r - shape.t),
150
+ round_digits=self._round_digits_mm,
151
+ ),
152
+ OuterFilletRadius=round_float(
153
+ shape.r,
154
+ round_digits=self._round_digits_mm,
155
+ ),
156
+ )
157
+ return profile
158
+
159
+ def get_profile_build_box(self, shape: ShapeBuildBox) -> entity_instance:
160
+ if shape.t1 == shape.t2:
161
+ profile: entity_instance = self.model.create_entity(
162
+ EntityName.IFC_RECTANGLE_HOLLOW_PROFILE_DEF.value,
163
+ ProfileName=shape.name,
164
+ ProfileType=PROFILE_TYPE_AREA,
165
+ XDim=round_float(shape.b, round_digits=self._round_digits_mm),
166
+ YDim=round_float(shape.a, round_digits=self._round_digits_mm),
167
+ WallThickness=round_float(
168
+ shape.t1,
169
+ round_digits=self._round_digits_mm,
170
+ ),
171
+ InnerFilletRadius=0.0,
172
+ OuterFilletRadius=0.0,
173
+ )
174
+ return profile
175
+ else:
176
+ return self.get_profile_base(shape)
177
+
178
+ def get_profile_channel(self, shape: ShapeChannel) -> entity_instance:
179
+ profile: entity_instance = self.model.create_entity(
180
+ EntityName.IFC_U_SHAPE_PROFILE_DEF.value,
181
+ ProfileName=shape.name,
182
+ ProfileType=PROFILE_TYPE_AREA,
183
+ Depth=round_float(shape.a, round_digits=self._round_digits_mm),
184
+ FlangeWidth=round_float(shape.b, round_digits=self._round_digits_mm),
185
+ WebThickness=round_float(shape.t1, round_digits=self._round_digits_mm),
186
+ FlangeThickness=round_float(
187
+ shape.t2,
188
+ round_digits=self._round_digits_mm,
189
+ ),
190
+ FilletRadius=round_float(shape.r1, round_digits=self._round_digits_mm),
191
+ EdgeRadius=round_float(shape.r2, round_digits=self._round_digits_mm),
192
+ FlangeSlope=0,
193
+ )
194
+ return profile
195
+
196
+ def get_profile_circle(self, shape: ShapeCircle) -> entity_instance:
197
+ profile: entity_instance = self.model.create_entity(
198
+ EntityName.IFC_CIRCLE_PROFILE_DEF.value,
199
+ ProfileName=shape.name,
200
+ ProfileType=PROFILE_TYPE_AREA,
201
+ Radius=round_float(shape.d / 2.0, round_digits=self._round_digits_mm),
202
+ )
203
+ return profile
204
+
205
+ def get_profile_h(self, shape: ShapeH) -> entity_instance:
206
+ profile: entity_instance = self.model.create_entity(
207
+ EntityName.IFC_I_SHAPE_PROFILE_DEF.value,
208
+ ProfileName=shape.name,
209
+ ProfileType=PROFILE_TYPE_AREA,
210
+ OverallWidth=round_float(shape.b, round_digits=self._round_digits_mm),
211
+ OverallDepth=round_float(shape.a, round_digits=self._round_digits_mm),
212
+ WebThickness=round_float(shape.t1, round_digits=self._round_digits_mm),
213
+ FlangeThickness=round_float(
214
+ shape.t2,
215
+ round_digits=self._round_digits_mm,
216
+ ),
217
+ FilletRadius=round_float(shape.r, round_digits=self._round_digits_mm),
218
+ )
219
+ return profile
220
+
221
+ def get_profile_pipe(self, shape: ShapePipe) -> entity_instance:
222
+ profile: entity_instance = self.model.create_entity(
223
+ EntityName.IFC_CIRCLE_HOLLOW_PROFILE_DEF.value,
224
+ ProfileName=shape.name,
225
+ ProfileType=PROFILE_TYPE_AREA,
226
+ Radius=round_float(shape.d / 2.0, round_digits=self._round_digits_mm),
227
+ WallThickness=round_float(shape.t, round_digits=self._round_digits_mm),
228
+ )
229
+ return profile
230
+
231
+ def get_profile_rectangle(self, shape: ShapeRectangle) -> entity_instance:
232
+ profile: entity_instance = self.model.create_entity(
233
+ EntityName.IFC_RECTANGLE_PROFILE_DEF.value,
234
+ ProfileName=shape.name,
235
+ ProfileType=PROFILE_TYPE_AREA,
236
+ XDim=round_float(shape.width_x, round_digits=self._round_digits_mm),
237
+ YDim=round_float(shape.width_y, round_digits=self._round_digits_mm),
238
+ )
239
+ return profile
240
+
241
+ def get_profile_t(self, shape: ShapeT) -> entity_instance:
242
+ profile: entity_instance = self.model.create_entity(
243
+ EntityName.IFC_T_SHAPE_PROFILE_DEF.value,
244
+ ProfileName=shape.name,
245
+ ProfileType=PROFILE_TYPE_AREA,
246
+ Depth=round_float(shape.b, round_digits=self._round_digits_mm),
247
+ FlangeWidth=round_float(shape.a, round_digits=self._round_digits_mm),
248
+ WebThickness=round_float(shape.t1, round_digits=self._round_digits_mm),
249
+ FlangeThickness=round_float(
250
+ shape.t2,
251
+ round_digits=self._round_digits_mm,
252
+ ),
253
+ FilletRadius=round_float(shape.r, round_digits=self._round_digits_mm),
254
+ )
255
+ return profile
256
+
257
+ def get_profile_lip_c(self, shape: ShapeLipC) -> entity_instance:
258
+ profile: entity_instance = self.model.create_entity(
259
+ EntityName.IFC_C_SHAPE_PROFILE_DEF.value,
260
+ ProfileName=shape.name,
261
+ ProfileType=PROFILE_TYPE_AREA,
262
+ Depth=round_float(shape.h, round_digits=self._round_digits_mm),
263
+ Width=round_float(shape.a, round_digits=self._round_digits_mm),
264
+ WallThickness=round_float(shape.t, round_digits=self._round_digits_mm),
265
+ Girth=round_float(shape.c, round_digits=self._round_digits_mm),
266
+ )
267
+ return profile
@@ -0,0 +1,178 @@
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 ifcopenshell.api.geometry
8
+ from ifcopenshell import entity_instance, file
9
+ from ifcopenshell.util.shape_builder import SequenceOfVectors
10
+ from stbkit.core.stb_reporting import Reporter
11
+
12
+ from ....converters._internal import coord_converter
13
+ from ....converters._internal.element_data_to_building_geometry import (
14
+ create_local_line_meshes,
15
+ )
16
+ from ...data_model.shape_data import (
17
+ ShapeArbitrary,
18
+ ShapePair,
19
+ ShapePosition,
20
+ ShapeRectangle,
21
+ )
22
+ from ...utils.float_rounding import round_float, round_vector2d, round_vector3d
23
+ from ...utils.unit_utils import mm_to_m
24
+ from ...vectors import Vector2d, Vector3d
25
+ from .ifc_profile_exporter import IfcProfileExporter
26
+
27
+
28
+ class IfcRepresentationExporter:
29
+ def __init__(
30
+ self,
31
+ model: file | None = None,
32
+ body: entity_instance | None = None,
33
+ *,
34
+ reporter: Reporter,
35
+ round_digits_mm: int | None = None,
36
+ ) -> None:
37
+ self._model: file | None = model
38
+ self._body: entity_instance | None = body
39
+ self._reporter: Reporter = reporter
40
+ self._round_digits_mm: int | None = round_digits_mm
41
+ self.profile_exporter: IfcProfileExporter = IfcProfileExporter(
42
+ model,
43
+ body,
44
+ round_digits_mm=round_digits_mm,
45
+ )
46
+
47
+ @property
48
+ def model(self) -> file:
49
+ if self._model is None:
50
+ raise AssertionError("modelが初期化されていません")
51
+ return self._model
52
+
53
+ @property
54
+ def body(self) -> entity_instance:
55
+ if self._body is None:
56
+ raise AssertionError("bodyが初期化されていません")
57
+ return self._body
58
+
59
+ @body.setter
60
+ def body(self, value: entity_instance) -> None:
61
+ self._body = value
62
+ self.profile_exporter.body = value
63
+
64
+ @classmethod
65
+ def is_extrusion(cls, shapes: list[ShapePair]) -> bool:
66
+ return (
67
+ len(shapes) == 1
68
+ and (shapes[0].end is None or shapes[0].end == shapes[0].start)
69
+ and shapes[0].start.rotate_degree is None
70
+ )
71
+
72
+ def get_line_representation(
73
+ self,
74
+ shapes: list[ShapePair],
75
+ axis_position: ShapePosition,
76
+ lengths: list[float],
77
+ ) -> entity_instance:
78
+ round_digits_m = (
79
+ None if self._round_digits_mm is None else self._round_digits_mm + 3
80
+ )
81
+ if not shapes:
82
+ raise AssertionError("形状が設定されていません")
83
+ if len(shapes) != len(lengths):
84
+ raise AssertionError("形状と長さの数が異なります")
85
+ if self.is_extrusion(shapes):
86
+ profile: entity_instance = self.profile_exporter.get_profile(
87
+ shapes[0].start
88
+ )
89
+ representation: entity_instance = (
90
+ ifcopenshell.api.geometry.add_profile_representation(
91
+ self.model,
92
+ context=self.body,
93
+ profile=profile,
94
+ depth=round_float(
95
+ mm_to_m(lengths[0]),
96
+ round_digits=round_digits_m,
97
+ ),
98
+ )
99
+ )
100
+ else:
101
+ points_list: list[SequenceOfVectors] = []
102
+ faces_list: list[list[list[int]]] = []
103
+ for mesh in create_local_line_meshes(
104
+ shapes,
105
+ axis_position,
106
+ lengths,
107
+ ):
108
+ points_list.append(
109
+ [
110
+ round_vector3d(
111
+ point.mm_to_m(),
112
+ round_digits=round_digits_m,
113
+ ).to_tuple()
114
+ for point in mesh.vertices
115
+ ]
116
+ )
117
+ faces_list.append([list(face) for face in mesh.faces])
118
+ representation = ifcopenshell.api.geometry.add_mesh_representation(
119
+ self.model, context=self.body, vertices=points_list, faces=faces_list
120
+ )
121
+ return representation
122
+
123
+ def get_line_unknown_representation(self, lengths: list[float]) -> entity_instance:
124
+ shape: ShapeRectangle = ShapeRectangle(name="unknown", width_x=10, width_y=10)
125
+ return self.get_line_representation(
126
+ shapes=[ShapePair(shape)],
127
+ axis_position=ShapePosition.CENTER_CENTER,
128
+ lengths=lengths,
129
+ )
130
+
131
+ def get_plate_representation(
132
+ self,
133
+ thickness: float | None,
134
+ opens: list[list[Vector2d]] | None,
135
+ points: list[Vector3d],
136
+ ) -> entity_instance:
137
+ if len(points) < 3:
138
+ raise AssertionError("外形の頂点数が足りません")
139
+ if thickness is None:
140
+ raise AssertionError("厚さが設定されていません")
141
+ round_digits_m = (
142
+ None if self._round_digits_mm is None else self._round_digits_mm + 3
143
+ )
144
+ shape: ShapeArbitrary = ShapeArbitrary()
145
+ shape.out_points.extend(
146
+ Vector2d(
147
+ rounded_point.x,
148
+ rounded_point.y,
149
+ )
150
+ for item in coord_converter.to_plate_element_coord(points)
151
+ for rounded_point in [
152
+ round_vector3d(item, round_digits=self._round_digits_mm)
153
+ ]
154
+ )
155
+ shape.in_points = (
156
+ [
157
+ [round_vector2d(p, round_digits=self._round_digits_mm) for p in open]
158
+ for open in opens
159
+ ]
160
+ if opens is not None
161
+ else []
162
+ )
163
+ profile = self.profile_exporter.get_profile(shape)
164
+ representation = ifcopenshell.api.geometry.add_profile_representation(
165
+ self.model,
166
+ context=self.body,
167
+ profile=profile,
168
+ depth=round_float(
169
+ mm_to_m(thickness),
170
+ round_digits=round_digits_m,
171
+ ),
172
+ )
173
+ return representation
174
+
175
+ def get_unknown_plate_representation(
176
+ self, points: list[Vector3d]
177
+ ) -> entity_instance:
178
+ return self.get_plate_representation(thickness=10, opens=None, points=points)
@@ -0,0 +1,133 @@
1
+ # Copyright 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 pathlib import Path
8
+ from typing import TYPE_CHECKING, cast
9
+
10
+ from stbkit.api import stb_latest
11
+
12
+ from ...converters._internal.pipeline.data import _DataFormat, _ObjectData
13
+ from ...converters._internal.pipeline.ply_pipeline import _PlyData
14
+ from ...converters._internal.pipeline.stb_pipeline_latest import StbLatestData
15
+ from ...converters._internal.pipeline.step import (
16
+ _compose_steps,
17
+ _ExecutableStep,
18
+ _ExecutionContext,
19
+ _ExecutionResult,
20
+ )
21
+ from .ifc_common import ensure_ifcopenshell, raise_import_error
22
+
23
+ if TYPE_CHECKING:
24
+ from ifcopenshell import file as IfcFile
25
+
26
+ from ..data_model.ifc_data.entity_data import IfcModel
27
+ else:
28
+ type IfcFile = object
29
+ type IfcModel = object
30
+
31
+
32
+ class IfcIntermediateData(_ObjectData[IfcModel]):
33
+ def __init__(self, value: IfcModel) -> None:
34
+ super().__init__(format=_DataFormat.IFC, value=value)
35
+
36
+
37
+ class IfcData(_ObjectData[IfcFile]):
38
+ def __init__(self, value: IfcFile) -> None:
39
+ ensure_ifcopenshell()
40
+ super().__init__(format=_DataFormat.IFC, value=value)
41
+
42
+
43
+ def open_ifc_data(path: Path) -> IfcData:
44
+ try:
45
+ import ifcopenshell
46
+
47
+ return IfcData(ifcopenshell.open(str(path)))
48
+ except ImportError as e:
49
+ raise_import_error(e)
50
+
51
+
52
+ def loads_ifc_data(text: str) -> IfcData:
53
+ try:
54
+ import ifcopenshell
55
+
56
+ return IfcData(ifcopenshell.file.from_string(text))
57
+ except ImportError as e:
58
+ raise_import_error(e)
59
+
60
+
61
+ class StbLatestToIfcIntermediateStep(
62
+ _ExecutableStep[StbLatestData, IfcIntermediateData]
63
+ ):
64
+ name = "STB -> IFC中間データ"
65
+
66
+ def execute(
67
+ self,
68
+ data: StbLatestData,
69
+ *,
70
+ context: _ExecutionContext,
71
+ ) -> _ExecutionResult[IfcIntermediateData]:
72
+ from ...converters._internal.stb_to_ifc_data import _stb_to_ifc_data
73
+
74
+ stb: stb_latest.StBridge = data.value
75
+ if not isinstance(stb, stb_latest.StBridge):
76
+ raise TypeError(f"ST-Bridgeデータではありません: {type(stb)}")
77
+ ifc_model: IfcModel = _stb_to_ifc_data.stb_to_ifc_data(
78
+ stb,
79
+ reporter=context.reporter,
80
+ )
81
+ return _ExecutionResult(output=IfcIntermediateData(ifc_model))
82
+
83
+
84
+ class IfcModelToIfcDataStep(_ExecutableStep[IfcIntermediateData, IfcData]):
85
+ name = "IFC中間データ -> IFC"
86
+
87
+ def execute(
88
+ self,
89
+ data: IfcIntermediateData,
90
+ *,
91
+ context: _ExecutionContext,
92
+ ) -> _ExecutionResult[IfcData]:
93
+ from stbkit.core.stb_reporting import get_reporter
94
+
95
+ ensure_ifcopenshell()
96
+ from .ifc_exporter.ifc_entity_exporter import (
97
+ IfcEntityExporter,
98
+ )
99
+
100
+ reporter = context.reporter or get_reporter()
101
+ ifc_file = IfcEntityExporter.ifc_model_to_file(
102
+ data.value,
103
+ reporter=reporter,
104
+ round_digits=context.ifc_round_digits,
105
+ round_digits_mm=context.ifc_round_digits_mm,
106
+ )
107
+ return _ExecutionResult(output=IfcData(ifc_file))
108
+
109
+
110
+ class IfcDataToPlyStep(_ExecutableStep[IfcData, _PlyData]):
111
+ name = "IFC -> PLY"
112
+
113
+ def execute(
114
+ self,
115
+ data: IfcData,
116
+ *,
117
+ context: _ExecutionContext,
118
+ ) -> _ExecutionResult[_PlyData]:
119
+ from .ifc_to_building_geometry import ifc_to_building_geometry
120
+
121
+ mesh_data = ifc_to_building_geometry(data.value)
122
+ return _ExecutionResult(output=_PlyData(mesh_data))
123
+
124
+
125
+ def stb_latest_to_ifc_step() -> _ExecutableStep[StbLatestData, IfcData]:
126
+ step = _compose_steps(
127
+ "STB -> IFC",
128
+ (
129
+ StbLatestToIfcIntermediateStep(),
130
+ IfcModelToIfcDataStep(),
131
+ ),
132
+ )
133
+ return cast(_ExecutableStep[StbLatestData, IfcData], step)
@@ -0,0 +1,64 @@
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 collections.abc import Sequence
8
+ from typing import TYPE_CHECKING
9
+ from uuid import UUID
10
+
11
+ from ..data_model.building_geometry import BuildingGeometry
12
+ from ..utils.guid_utils import from_ifc_guid
13
+ from .ifc_common import ensure_ifcopenshell
14
+
15
+ if TYPE_CHECKING:
16
+ import ifcopenshell.file
17
+
18
+
19
+ def ifc_to_building_geometry(ifc_file: "ifcopenshell.file") -> BuildingGeometry:
20
+ ensure_ifcopenshell()
21
+ import ifcopenshell
22
+ import ifcopenshell.geom
23
+ import ifcopenshell.util.shape
24
+ from ifcopenshell.ifcopenshell_wrapper import (
25
+ BRep,
26
+ Serialization,
27
+ Triangulation,
28
+ )
29
+
30
+ # ジオメトリの設定
31
+ settings = ifcopenshell.geom.settings() # type: ignore
32
+ settings.set("use-world-coords", True)
33
+ building_geometry: BuildingGeometry = BuildingGeometry()
34
+
35
+ for element in ifc_file.by_type("IfcProduct"):
36
+ if element.is_a("IfcOpeningElement"):
37
+ continue
38
+ try:
39
+ shape = ifcopenshell.geom.create_shape(settings, element)
40
+ except RuntimeError:
41
+ continue
42
+ if not hasattr(shape, "geometry"):
43
+ continue
44
+ geometry = shape.geometry
45
+ vertices: list[Sequence[float]] = []
46
+
47
+ if isinstance(geometry, (BRep, Triangulation, Serialization)):
48
+ global_id: str | None = (
49
+ element.GlobalId if hasattr(element, "GlobalId") else None
50
+ )
51
+ guid: UUID | None = from_ifc_guid(global_id) if global_id else None
52
+ index: int = building_geometry.add_element(
53
+ guid=guid,
54
+ source_type=str(element.is_a()),
55
+ source_id=str(element.id()),
56
+ name=getattr(element, "Name", None),
57
+ )
58
+ vertices.extend(ifcopenshell.util.shape.get_vertices(geometry)) # type:ignore
59
+ tmp_faces: list[Sequence[int]] = [
60
+ item
61
+ for item in ifcopenshell.util.shape.get_faces(geometry) # type: ignore
62
+ ]
63
+ building_geometry.add_element_mesh(index, vertices, tmp_faces)
64
+ return building_geometry