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,51 @@
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 dataclasses import dataclass
8
+
9
+ from .vectors import Vector3d
10
+
11
+ type _Matrix4x4Tuple = tuple[
12
+ tuple[float, float, float, float],
13
+ tuple[float, float, float, float],
14
+ tuple[float, float, float, float],
15
+ tuple[float, float, float, float],
16
+ ]
17
+
18
+
19
+ @dataclass(frozen=True, slots=True)
20
+ class RotationMatrix:
21
+ """回転行列
22
+ x_axis,y_axis,z_axisは正規直行基底"""
23
+
24
+ origin: Vector3d
25
+ x_axis: Vector3d
26
+ y_axis: Vector3d
27
+ z_axis: Vector3d
28
+
29
+ def to_global_point(self, point: Vector3d) -> Vector3d:
30
+ return (
31
+ self.origin
32
+ + point.x * self.x_axis
33
+ + point.y * self.y_axis
34
+ + point.z * self.z_axis
35
+ )
36
+
37
+ def to_local_point(self, point: Vector3d) -> Vector3d:
38
+ delta: Vector3d = point - self.origin
39
+ return Vector3d(
40
+ delta.dot(self.x_axis),
41
+ delta.dot(self.y_axis),
42
+ delta.dot(self.z_axis),
43
+ )
44
+
45
+ def to_matrix_tuple(self) -> _Matrix4x4Tuple:
46
+ return (
47
+ (self.x_axis.x, self.y_axis.x, self.z_axis.x, self.origin.x),
48
+ (self.x_axis.y, self.y_axis.y, self.z_axis.y, self.origin.y),
49
+ (self.x_axis.z, self.y_axis.z, self.z_axis.z, self.origin.z),
50
+ (0.0, 0.0, 0.0, 1.0),
51
+ )
@@ -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/.
@@ -0,0 +1,28 @@
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 typing import Never
8
+
9
+ _IFCOPENSHELL_IMPORT_ERROR_MESSAGE = (
10
+ "ifcopenshellがインストールされていません。IFCファイルの機能を利用するにはifcopenshellをインストールしてください。\n"
11
+ "インストール方法: \npip install ifcopenshell\n"
12
+ "または\npip install stbkit[ifc]"
13
+ )
14
+
15
+
16
+ def raise_import_error(e: ImportError) -> Never:
17
+ if e.name == "ifcopenshell" or (
18
+ e.name is not None and e.name.startswith("ifcopenshell.")
19
+ ):
20
+ raise ImportError(_IFCOPENSHELL_IMPORT_ERROR_MESSAGE) from e
21
+ raise ImportError(f"予期しないImportErrorが発生しました: {e}") from e
22
+
23
+
24
+ def ensure_ifcopenshell() -> None:
25
+ try:
26
+ import ifcopenshell # noqa: F401
27
+ except ImportError as e:
28
+ raise_import_error(e)
@@ -0,0 +1,473 @@
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 Protocol
8
+ from uuid import UUID
9
+
10
+ import ifcopenshell.api.aggregate
11
+ import ifcopenshell.api.context
12
+ import ifcopenshell.api.geometry
13
+ import ifcopenshell.api.project
14
+ import ifcopenshell.api.root
15
+ import ifcopenshell.api.unit
16
+ import ifcopenshell.file
17
+ import numpy as np
18
+ from ifcopenshell import entity_instance
19
+ from numpy.typing import NDArray
20
+ from stbkit.core._internal.transform.guid_tools import generate_uuid
21
+ from stbkit.core.stb_exceptions import ZeroLengthMemberError
22
+ from stbkit.core.stb_reporting import Code, Phase, Reporter, get_reporter
23
+
24
+ from ....converters._internal import coord_converter
25
+ from ...constants import UNKNOWN_ELEMENT_SIZE_MM
26
+ from ...data_model.element_data import (
27
+ ElementLine,
28
+ ElementLineSrc,
29
+ ElementPlane,
30
+ ElementType,
31
+ )
32
+ from ...data_model.ifc_data.entity_data import (
33
+ IfcBuilding,
34
+ IfcBuildingStorey,
35
+ IfcModel,
36
+ IfcProject,
37
+ IfcSite,
38
+ )
39
+ from ...data_model.ifc_data.entity_names import EntityName
40
+ from ...matrix import RotationMatrix
41
+ from ...utils.float_rounding import round_float
42
+ from ...utils.guid_utils import to_ifc_guid
43
+ from ...vectors import Vector2d, Vector3d
44
+ from .ifc_representation_exporter import IfcRepresentationExporter
45
+
46
+
47
+ def _rotation_matrix_to_ndarray(
48
+ matrix: RotationMatrix,
49
+ *,
50
+ round_digits: int | None,
51
+ round_digits_mm: int | None,
52
+ ) -> NDArray[np.float64]:
53
+ result: NDArray[np.float64] = np.array(matrix.to_matrix_tuple(), dtype=np.float64)
54
+ if result.shape != (4, 4):
55
+ raise AssertionError(f"4x4行列ではありません: {result.shape}")
56
+ if round_digits is None and round_digits_mm is None:
57
+ return result
58
+ rounded: NDArray[np.float64] = result.copy()
59
+ if round_digits is not None:
60
+ for i in range(3):
61
+ for j in range(3):
62
+ rounded[i, j] = round_float(
63
+ rounded[i, j],
64
+ round_digits=round_digits,
65
+ )
66
+ if round_digits_mm is not None:
67
+ round_digits_m: int = round_digits_mm + 3
68
+ rounded[0, 3] = round_float(rounded[0, 3], round_digits=round_digits_m)
69
+ rounded[1, 3] = round_float(rounded[1, 3], round_digits=round_digits_m)
70
+ rounded[2, 3] = round_float(rounded[2, 3], round_digits=round_digits_m)
71
+ return rounded
72
+
73
+
74
+ class _HasGuid(Protocol):
75
+ guid: UUID | None
76
+
77
+
78
+ def _unit_sort_key(unit: entity_instance) -> tuple[str, str]:
79
+ """単位を固定順序へ並べるためのkey"""
80
+ return (unit.is_a(), str(getattr(unit, "UnitType", "")))
81
+
82
+
83
+ def _set_guid(ifc_entity: entity_instance, obj: _HasGuid) -> None:
84
+ if obj.guid is None:
85
+ obj.guid = generate_uuid()
86
+ if obj.guid:
87
+ ifc_entity.GlobalId = to_ifc_guid(obj.guid)
88
+
89
+
90
+ class IfcEntityExporter:
91
+ def __init__(
92
+ self,
93
+ *,
94
+ model: ifcopenshell.file | None = None,
95
+ reporter: Reporter | None = None,
96
+ round_digits: int | None = None,
97
+ round_digits_mm: int | None = None,
98
+ ) -> None:
99
+ self._model: ifcopenshell.file | None = model
100
+ self._body: entity_instance | None = None
101
+ self._round_digits: int | None = round_digits
102
+ self._round_digits_mm: int | None = round_digits_mm
103
+ self.reporter: Reporter = reporter or get_reporter()
104
+ self.representation_exporter: IfcRepresentationExporter = (
105
+ IfcRepresentationExporter(
106
+ model,
107
+ reporter=self.reporter,
108
+ round_digits_mm=round_digits_mm,
109
+ )
110
+ )
111
+
112
+ @property
113
+ def model(self) -> ifcopenshell.file:
114
+ if self._model is None:
115
+ raise AssertionError("modelが初期化されていません")
116
+ return self._model
117
+
118
+ @property
119
+ def body(self) -> entity_instance:
120
+ if self._body is None:
121
+ raise AssertionError("bodyが初期化されていません")
122
+ return self._body
123
+
124
+ @body.setter
125
+ def body(self, value: entity_instance) -> None:
126
+ self._body = value
127
+ self.representation_exporter.body = value
128
+
129
+ def create_entity_plane_element(
130
+ self,
131
+ plane_element: ElementPlane,
132
+ ) -> entity_instance:
133
+ if not plane_element.points:
134
+ raise AssertionError(f"{plane_element.name}にpointsが設定されていません")
135
+ if len(plane_element.points) < 3:
136
+ raise AssertionError(f"{plane_element.name}のpointsの数が3以下です")
137
+ if plane_element.element_type is None:
138
+ raise AssertionError(
139
+ f"{plane_element.name}にelement_typeが設定されていません"
140
+ )
141
+ plate_element: entity_instance = ifcopenshell.api.root.create_entity(
142
+ self.model,
143
+ ifc_class=IfcEntityExporter.to_entity_name(
144
+ plane_element.element_type
145
+ ).value,
146
+ name=plane_element.name,
147
+ )
148
+ _set_guid(plate_element, plane_element)
149
+ thickness: float | None = plane_element.thickness
150
+ if thickness is None:
151
+ self.reporter.warning(
152
+ f"要素[{plane_element.name}]にthicknessが設定されていないため、厚さ{UNKNOWN_ELEMENT_SIZE_MM}mmで出力します。",
153
+ code=Code.UNKNOWN_SHAPE,
154
+ phase=Phase.CONVERT_IFC,
155
+ )
156
+ thickness = UNKNOWN_ELEMENT_SIZE_MM
157
+
158
+ representation = self.representation_exporter.get_plate_representation(
159
+ thickness=thickness,
160
+ opens=plane_element.opens,
161
+ points=plane_element.points,
162
+ )
163
+
164
+ ifcopenshell.api.geometry.assign_representation(
165
+ self.model, product=plate_element, representation=representation
166
+ )
167
+ matrix = _rotation_matrix_to_ndarray(
168
+ coord_converter.rotation_matrix_plate(
169
+ [p.mm_to_m() for p in plane_element.points]
170
+ ),
171
+ round_digits=self._round_digits,
172
+ round_digits_mm=self._round_digits_mm,
173
+ )
174
+ ifcopenshell.api.geometry.edit_object_placement(
175
+ self.model, product=plate_element, matrix=matrix
176
+ )
177
+ return plate_element
178
+
179
+ def create_entity_element_line(
180
+ self,
181
+ element_line: ElementLine,
182
+ ) -> entity_instance | None:
183
+ if not element_line.lengths:
184
+ self.reporter.warning(
185
+ f"要素[{element_line.name}]にlengthsが設定されていないため、IFCエクスポートをスキップしました。",
186
+ code=Code.ZERO_LENGTH_MEMBER,
187
+ phase=Phase.CONVERT_IFC,
188
+ )
189
+ return None
190
+ if not element_line.point_start:
191
+ raise AssertionError(
192
+ f"{element_line.name}にpoint_startが設定されていません"
193
+ )
194
+ if not element_line.point_end:
195
+ raise AssertionError(f"{element_line.name}にpoint_endが設定されていません")
196
+ if element_line.element_type is None:
197
+ raise AssertionError(
198
+ f"{element_line.name}にelement_typeが設定されていません"
199
+ )
200
+ line_element: entity_instance = ifcopenshell.api.root.create_entity(
201
+ self.model,
202
+ ifc_class=IfcEntityExporter.to_entity_name(element_line.element_type).value,
203
+ name=element_line.name,
204
+ )
205
+ _set_guid(line_element, element_line)
206
+ point_start: Vector3d = element_line.point_start
207
+ point_end: Vector3d = element_line.point_end
208
+ if element_line.shapes:
209
+ representation = self.representation_exporter.get_line_representation(
210
+ shapes=element_line.shapes,
211
+ axis_position=element_line.axis_position,
212
+ lengths=element_line.lengths,
213
+ )
214
+
215
+ if self.representation_exporter.is_extrusion(element_line.shapes):
216
+ offset: Vector2d = Vector2d.zero() - element_line.shapes[
217
+ 0
218
+ ].start.get_position_point(element_line.axis_position)
219
+ if element_line.shapes[0].start.offset:
220
+ offset += element_line.shapes[0].start.offset
221
+ _, vy, vz = coord_converter.axis_vector_by_element_type(
222
+ element_line.point_start,
223
+ element_line.point_end,
224
+ element_line.angle,
225
+ element_line.element_type,
226
+ )
227
+ point_start += offset.x * vy + offset.y * vz
228
+ point_end += offset.x * vy + offset.y * vz
229
+
230
+ else:
231
+ representation = (
232
+ self.representation_exporter.get_line_unknown_representation(
233
+ element_line.lengths
234
+ )
235
+ )
236
+ ifcopenshell.api.geometry.assign_representation(
237
+ self.model, product=line_element, representation=representation
238
+ )
239
+ matrix = _rotation_matrix_to_ndarray(
240
+ coord_converter.rotation_matrix_by_element_type(
241
+ point_start.mm_to_m(),
242
+ point_end.mm_to_m(),
243
+ element_line.angle,
244
+ element_line.element_type,
245
+ ),
246
+ round_digits=self._round_digits,
247
+ round_digits_mm=self._round_digits_mm,
248
+ )
249
+ ifcopenshell.api.geometry.edit_object_placement(
250
+ self.model, product=line_element, matrix=matrix
251
+ )
252
+ return line_element
253
+
254
+ def create_entity_element_line_src(
255
+ self,
256
+ line_element_src: ElementLineSrc,
257
+ ) -> entity_instance:
258
+ element_assembly: entity_instance = ifcopenshell.api.root.create_entity(
259
+ self.model,
260
+ ifc_class=EntityName.IFC_ELEMENT_ASSEMBLY.value,
261
+ name=line_element_src.name,
262
+ )
263
+ _set_guid(element_assembly, line_element_src)
264
+
265
+ items: list[entity_instance] = []
266
+ if line_element_src.steel:
267
+ item_steel: entity_instance | None = self.create_entity_element_line(
268
+ line_element_src.steel
269
+ )
270
+ if item_steel is not None:
271
+ items.append(item_steel)
272
+ if line_element_src.rc:
273
+ item_rc: entity_instance | None = self.create_entity_element_line(
274
+ line_element_src.rc
275
+ )
276
+ if item_rc is not None:
277
+ items.append(item_rc)
278
+ # ifcopenshellのassign_objectは呼ぶたびに集合演算を行うため処理時間がかかり順序も安定しなくなる。
279
+ # そのため、参照関係はここで1度だけ作る。
280
+ if items:
281
+ self.model.create_entity(
282
+ EntityName.IFC_REL_AGGREGATES.value,
283
+ GlobalId=to_ifc_guid(generate_uuid()),
284
+ RelatingObject=element_assembly,
285
+ RelatedObjects=items,
286
+ )
287
+ return element_assembly
288
+
289
+ def create_entity_element(
290
+ self,
291
+ element: ElementLine | ElementPlane | ElementLineSrc,
292
+ ) -> entity_instance | None:
293
+ try:
294
+ match element:
295
+ case ElementLine() as line_element:
296
+ return self.create_entity_element_line(line_element)
297
+ case ElementPlane() as plane_element:
298
+ return self.create_entity_plane_element(plane_element)
299
+ case ElementLineSrc() as line_element_src:
300
+ return self.create_entity_element_line_src(line_element_src)
301
+ case _:
302
+ raise AssertionError(f"不明な要素タイプです: {type(element)}")
303
+ except ZeroLengthMemberError:
304
+ self.reporter.warning(
305
+ f"要素'{element.name}'の始端と終端が同じ位置のため、IFCエクスポートをスキップしました。",
306
+ code=Code.ZERO_LENGTH_MEMBER,
307
+ phase=Phase.CONVERT_IFC,
308
+ )
309
+ return None
310
+
311
+ def create_entity_building_storey(
312
+ self,
313
+ ifc_building_storey: IfcBuildingStorey,
314
+ ) -> entity_instance:
315
+ building_storey: entity_instance = ifcopenshell.api.root.create_entity(
316
+ self.model,
317
+ ifc_class=EntityName.IFC_BUILDING_STOREY.value,
318
+ name=ifc_building_storey.name,
319
+ )
320
+ _set_guid(building_storey, ifc_building_storey)
321
+ n_element: int = len(ifc_building_storey.elements)
322
+ self.reporter.info(
323
+ message=f"---要素のIFCエンティティ変換処理({n_element}件)---",
324
+ code=Code.PROGRESS_INFO,
325
+ phase=Phase.CONVERT_IFC,
326
+ )
327
+ elements: list[entity_instance] = []
328
+ batch_size: int = 1000
329
+ for i, element in enumerate(ifc_building_storey.elements, 1):
330
+ e = self.create_entity_element(element)
331
+ if e is not None:
332
+ elements.append(e)
333
+ if i % batch_size == 0 or i == n_element:
334
+ self.reporter.info(
335
+ message=f"---要素のIFCエンティティ変換処理({i}/{n_element}件)---",
336
+ code=Code.PROGRESS_INFO,
337
+ phase=Phase.CONVERT_IFC,
338
+ )
339
+ n_element = len(elements)
340
+ self.reporter.info(
341
+ message=f"---要素のIFCエンティティ配置処理({n_element}件)---",
342
+ code=Code.PROGRESS_INFO,
343
+ phase=Phase.CONVERT_IFC,
344
+ )
345
+ # ifcopenshellのassign_containerは、呼ぶたびに集合演算を行うため処理時間がかかり順序も安定しなくなる。
346
+ # そのため、参照関係はここで1度だけ作る。
347
+ if elements:
348
+ self.model.create_entity(
349
+ EntityName.IFC_REL_CONTAINED_IN_SPATIAL_STRUCTURE.value,
350
+ GlobalId=to_ifc_guid(generate_uuid()),
351
+ RelatedElements=elements,
352
+ RelatingStructure=building_storey,
353
+ )
354
+ return building_storey
355
+
356
+ def create_entity_ifc_building(self, ifc_building: IfcBuilding) -> entity_instance:
357
+ building: entity_instance = ifcopenshell.api.root.create_entity(
358
+ self.model,
359
+ ifc_class=EntityName.IFC_BUILDING.value,
360
+ name=ifc_building.name,
361
+ )
362
+ _set_guid(building, ifc_building)
363
+ building_storeys: list[entity_instance] = [
364
+ self.create_entity_building_storey(building_storey)
365
+ for building_storey in ifc_building.building_storeys
366
+ ]
367
+ rel: entity_instance | None = ifcopenshell.api.aggregate.assign_object(
368
+ self.model, relating_object=building, products=building_storeys
369
+ )
370
+ if rel is not None:
371
+ rel.GlobalId = to_ifc_guid(generate_uuid())
372
+ return building
373
+
374
+ def create_entity_ifc_site(self, ifc_site: IfcSite) -> entity_instance:
375
+ site: entity_instance = ifcopenshell.api.root.create_entity(
376
+ self.model, ifc_class=EntityName.IFC_SITE.value, name=ifc_site.name
377
+ )
378
+ if not ifc_site.building:
379
+ raise AssertionError("buildingが設定されていません")
380
+ building: entity_instance = self.create_entity_ifc_building(ifc_site.building)
381
+ _set_guid(site, ifc_site)
382
+ rel: entity_instance | None = ifcopenshell.api.aggregate.assign_object(
383
+ self.model, relating_object=site, products=[building]
384
+ )
385
+ if rel is not None:
386
+ rel.GlobalId = to_ifc_guid(generate_uuid())
387
+ return site
388
+
389
+ def create_entity_project(self, ifc_project: IfcProject) -> entity_instance:
390
+ project: entity_instance = ifcopenshell.api.root.create_entity(
391
+ self.model, ifc_class=EntityName.IFC_PROJECT.value, name=ifc_project.name
392
+ )
393
+ _set_guid(project, ifc_project)
394
+ ifcopenshell.api.unit.assign_unit(self.model)
395
+ # assign_unitはsetを経由するため並びが実行ごとに変わるため、順序を固定する
396
+ units = project.UnitsInContext
397
+ if units is not None:
398
+ units.Units = sorted(units.Units, key=_unit_sort_key)
399
+ context: entity_instance = ifcopenshell.api.context.add_context(
400
+ self.model, context_type="Model"
401
+ )
402
+ self.body = ifcopenshell.api.context.add_context(
403
+ self.model,
404
+ context_type="Model",
405
+ context_identifier="Body",
406
+ target_view="MODEL_VIEW",
407
+ parent=context,
408
+ )
409
+ if not ifc_project.site:
410
+ raise AssertionError("siteが設定されていません")
411
+ site: entity_instance = self.create_entity_ifc_site(ifc_project.site)
412
+ rel: entity_instance | None = ifcopenshell.api.aggregate.assign_object(
413
+ self.model, relating_object=project, products=[site]
414
+ )
415
+ if rel is not None:
416
+ rel.GlobalId = to_ifc_guid(generate_uuid())
417
+ return project
418
+
419
+ @staticmethod
420
+ def ifc_model_to_file(
421
+ ifc_model: IfcModel,
422
+ *,
423
+ reporter: Reporter,
424
+ round_digits: int | None = None,
425
+ round_digits_mm: int | None = None,
426
+ ) -> ifcopenshell.file:
427
+ reporter.info(
428
+ "---IFCジオメトリデータ->IFC Entityデータ変換処理開始---",
429
+ code=Code.PROGRESS_INFO,
430
+ phase=Phase.CONVERT_IFC,
431
+ )
432
+ model: ifcopenshell.file = ifcopenshell.api.project.create_file()
433
+ exporter: IfcEntityExporter = IfcEntityExporter(
434
+ model=model,
435
+ reporter=reporter,
436
+ round_digits=round_digits,
437
+ round_digits_mm=round_digits_mm,
438
+ )
439
+
440
+ if not ifc_model.project:
441
+ raise AssertionError("projectが設定されていません")
442
+ exporter.create_entity_project(ifc_model.project)
443
+ reporter.info(
444
+ "---IFCジオメトリデータ->IFC Entityデータ変換処理終了---",
445
+ code=Code.PROGRESS_INFO,
446
+ phase=Phase.CONVERT_IFC,
447
+ )
448
+ return model
449
+
450
+ @staticmethod
451
+ def to_entity_name(element_type: ElementType) -> EntityName:
452
+ match element_type:
453
+ case (
454
+ ElementType.GIRDER
455
+ | ElementType.BEAM
456
+ | ElementType.STRIP_FOOTING
457
+ | ElementType.PARAPET
458
+ ):
459
+ return EntityName.IFC_BEAM
460
+ case ElementType.COLUMN | ElementType.POST | ElementType.FOUNDATION_COLUMN:
461
+ return EntityName.IFC_COLUMN
462
+ case ElementType.BRACE:
463
+ return EntityName.IFC_MEMBER
464
+ case ElementType.WALL:
465
+ return EntityName.IFC_WALL
466
+ case ElementType.SLAB:
467
+ return EntityName.IFC_SLAB
468
+ case ElementType.FOOTING:
469
+ return EntityName.IFC_FOOTING
470
+ case ElementType.PILE:
471
+ return EntityName.IFC_PILE
472
+ case _:
473
+ raise AssertionError(f"不明なElementTypeです: {element_type}")