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.
- stbkit/__main__.py +10 -0
- stbkit/api/__init__.py +48 -0
- stbkit/api/experimental/__init__.py +19 -0
- stbkit/api/experimental/repository/__init__.py +30 -0
- stbkit/api/py.typed +0 -0
- stbkit/api/stb_latest.py +7 -0
- stbkit/api/stb_v2_0_2.py +11 -0
- stbkit/api/stb_v2_1_1.py +9 -0
- stbkit/cli/__init__.py +17 -0
- stbkit/cli/_internal/cli_main.py +114 -0
- stbkit/cli/_internal/common.py +126 -0
- stbkit/cli/_internal/constants_exit_code.py +59 -0
- stbkit/cli/_internal/converter.py +693 -0
- stbkit/cli/_internal/diff.py +213 -0
- stbkit/cli/_internal/entry_points.py +25 -0
- stbkit/cli/_internal/global_parent.py +70 -0
- stbkit/cli/_internal/show_versions.py +36 -0
- stbkit/cli/_internal/validate.py +310 -0
- stbkit/cli/commands/convert.py +16 -0
- stbkit/cli/commands/diff.py +16 -0
- stbkit/cli/commands/validate.py +16 -0
- stbkit/cli/py.typed +0 -0
- stbkit/tools/__init__.py +12 -0
- stbkit/tools/_internal/__init__.py +5 -0
- stbkit/tools/_internal/constants.py +21 -0
- stbkit/tools/_internal/data_model/building_geometry.py +1208 -0
- stbkit/tools/_internal/data_model/building_geometry_web.py +106 -0
- stbkit/tools/_internal/data_model/element_data.py +93 -0
- stbkit/tools/_internal/data_model/ifc_data/entity_data.py +67 -0
- stbkit/tools/_internal/data_model/ifc_data/entity_names.py +32 -0
- stbkit/tools/_internal/data_model/shape_data.py +1089 -0
- stbkit/tools/_internal/diff/__init__.py +5 -0
- stbkit/tools/_internal/diff/common.py +50 -0
- stbkit/tools/_internal/diff/diff_xml.py +796 -0
- stbkit/tools/_internal/matrix.py +51 -0
- stbkit/tools/_internal/optional_dependencies/__init__.py +5 -0
- stbkit/tools/_internal/optional_dependencies/ifc_common.py +28 -0
- stbkit/tools/_internal/optional_dependencies/ifc_exporter/ifc_entity_exporter.py +473 -0
- stbkit/tools/_internal/optional_dependencies/ifc_exporter/ifc_profile_exporter.py +267 -0
- stbkit/tools/_internal/optional_dependencies/ifc_exporter/ifc_representation_exporter.py +178 -0
- stbkit/tools/_internal/optional_dependencies/ifc_pipeline.py +133 -0
- stbkit/tools/_internal/optional_dependencies/ifc_to_building_geometry.py +64 -0
- stbkit/tools/_internal/stb_io.py +116 -0
- stbkit/tools/_internal/stb_repair/_common.py +24 -0
- stbkit/tools/_internal/stb_repair/_stb_repair.py +197 -0
- stbkit/tools/_internal/stb_repair/_stb_v2_0_2_repair.py +53 -0
- stbkit/tools/_internal/stb_repair/_stb_v2_1_0_repair.py +109 -0
- stbkit/tools/_internal/stb_repair/_stb_v2_1_1_repair.py +109 -0
- stbkit/tools/_internal/studio_report.py +49 -0
- stbkit/tools/_internal/utils/atomic_writer.py +57 -0
- stbkit/tools/_internal/utils/float_rounding.py +49 -0
- stbkit/tools/_internal/utils/girder_utils.py +91 -0
- stbkit/tools/_internal/utils/guid_utils.py +30 -0
- stbkit/tools/_internal/utils/mesh_utils.py +1305 -0
- stbkit/tools/_internal/utils/node_utils.py +50 -0
- stbkit/tools/_internal/utils/unit_utils.py +23 -0
- stbkit/tools/_internal/vectors.py +222 -0
- stbkit/tools/converters/__init__.py +39 -0
- stbkit/tools/converters/_internal/__init__.py +5 -0
- stbkit/tools/converters/_internal/building_geometry_to_ply_text.py +32 -0
- stbkit/tools/converters/_internal/coord_converter.py +151 -0
- stbkit/tools/converters/_internal/element_data_to_building_geometry.py +549 -0
- stbkit/tools/converters/_internal/ifc_to_ply_text.py +21 -0
- stbkit/tools/converters/_internal/name_converter.py +23 -0
- stbkit/tools/converters/_internal/pipeline/__init__.py +5 -0
- stbkit/tools/converters/_internal/pipeline/data.py +188 -0
- stbkit/tools/converters/_internal/pipeline/execution.py +569 -0
- stbkit/tools/converters/_internal/pipeline/ply_pipeline.py +52 -0
- stbkit/tools/converters/_internal/pipeline/stb_pipeline.py +226 -0
- stbkit/tools/converters/_internal/pipeline/stb_pipeline_latest.py +39 -0
- stbkit/tools/converters/_internal/pipeline/step.py +139 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/__init__.py +17 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/_stb_latest_to_element_data.py +186 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/member/member_footing.py +49 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/member/member_line.py +533 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/member/member_pile.py +100 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/member/member_plane.py +103 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/open.py +70 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec.py +214 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_beam_rc.py +67 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_beam_s.py +97 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_beam_src.py +28 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_brace_s.py +49 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_column_cft.py +217 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_column_rc.py +41 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_column_s.py +127 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_column_src.py +173 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_foundation.py +225 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_parapet_rc.py +53 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_pile_rc.py +106 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_pile_s.py +100 -0
- stbkit/tools/converters/_internal/stb_latest_to_element_data/section/sec_steel.py +432 -0
- stbkit/tools/converters/_internal/stb_to_building_geometry.py +44 -0
- stbkit/tools/converters/_internal/stb_to_ifc_data/__init__.py +5 -0
- stbkit/tools/converters/_internal/stb_to_ifc_data/_stb_to_ifc_data.py +59 -0
- stbkit/tools/converters/_internal/stb_to_ifc_data/convert_option.py +22 -0
- stbkit/tools/converters/_internal/stb_to_ply_text.py +22 -0
- stbkit/tools/py.typed +0 -0
- stbkit/tools/upgrade/__init__.py +13 -0
- stbkit/tools/upgrade/_internal/__init__.py +5 -0
- stbkit/tools/upgrade/_internal/common.py +205 -0
- stbkit/tools/upgrade/_internal/upgrade.py +228 -0
- stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/__init__.py +60 -0
- stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/hook_common.py +62 -0
- stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/hook_joint_beam_shape_h.py +44 -0
- stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/hook_sec_pile_rc.py +101 -0
- stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/hook_sec_pile_s.py +78 -0
- stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/hook_sec_slab_deck.py +56 -0
- stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/hook_sec_slab_rc.py +128 -0
- stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/post_girder.py +126 -0
- stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/post_open.py +110 -0
- stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/post_sec_beam_rc.py +227 -0
- stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hook_funcs/post_sec_beam_s.py +1153 -0
- stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/hooks.py +22 -0
- stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/post.py +42 -0
- stbkit/tools/upgrade/_internal/v2_0_2_to_v2_1_0/tables.py +99 -0
- stbkit/tools/upgrade/_internal/v2_1_0_to_v2_1_1/__init__.py +58 -0
- stbkit/tools/upgrade/_internal/v2_1_0_to_v2_1_1/hooks.py +32 -0
- stbkit/tools/upgrade/_internal/v2_1_0_to_v2_1_1/tables.py +56 -0
- stbkit-0.1.0a9.dev1.dist-info/METADATA +196 -0
- stbkit-0.1.0a9.dev1.dist-info/RECORD +125 -0
- stbkit-0.1.0a9.dev1.dist-info/WHEEL +5 -0
- stbkit-0.1.0a9.dev1.dist-info/entry_points.txt +2 -0
- stbkit-0.1.0a9.dev1.dist-info/licenses/LICENSE +373 -0
- 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
|