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,569 @@
|
|
|
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
|
+
import hashlib
|
|
8
|
+
import itertools
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
import shutil
|
|
12
|
+
from collections.abc import Callable, Sequence
|
|
13
|
+
from enum import StrEnum
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
from uuid import uuid4
|
|
17
|
+
|
|
18
|
+
from stbkit.core import stb_io
|
|
19
|
+
from stbkit.core.data_model.common import StBridgeRoot
|
|
20
|
+
from stbkit.core.stb_exceptions import SchemaError
|
|
21
|
+
from stbkit.core.stb_io._internal.serializer import _raw_dumps
|
|
22
|
+
from stbkit.core.stb_reporting import Reporter, get_reporter
|
|
23
|
+
|
|
24
|
+
from stbkit.tools._internal.constants import DEBUG_DUMP_DIR_ENV
|
|
25
|
+
|
|
26
|
+
from ...._internal.utils.atomic_writer import (
|
|
27
|
+
write_text_stream_with_temp_file,
|
|
28
|
+
write_text_with_temp_file,
|
|
29
|
+
write_with_temp_file,
|
|
30
|
+
)
|
|
31
|
+
from ..building_geometry_to_ply_text import building_geometry_to_ply_text
|
|
32
|
+
from .data import (
|
|
33
|
+
_Data,
|
|
34
|
+
_DataFormat,
|
|
35
|
+
_DataFormatSet,
|
|
36
|
+
_DataKind,
|
|
37
|
+
_FileData,
|
|
38
|
+
_StbVersion,
|
|
39
|
+
_TextData,
|
|
40
|
+
)
|
|
41
|
+
from .ply_pipeline import _PlyData, _StbToPlyStep, save_ply_data
|
|
42
|
+
from .stb_pipeline import (
|
|
43
|
+
StbAssignGuidsStep,
|
|
44
|
+
StbData,
|
|
45
|
+
StbDumpsStep,
|
|
46
|
+
StbFileData,
|
|
47
|
+
StbLoadStep,
|
|
48
|
+
StbRepairStep,
|
|
49
|
+
StbTextData,
|
|
50
|
+
StbToVersionStep,
|
|
51
|
+
)
|
|
52
|
+
from .stb_pipeline_latest import StbToLatestStep
|
|
53
|
+
from .step import (
|
|
54
|
+
_compose_steps,
|
|
55
|
+
_ExecutableStep,
|
|
56
|
+
_execute_step,
|
|
57
|
+
_ExecutionContext,
|
|
58
|
+
_ExecutionResult,
|
|
59
|
+
_NoOpStep,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ConvertResultFormat(StrEnum):
|
|
64
|
+
FILE = "file"
|
|
65
|
+
TEXT = "text"
|
|
66
|
+
FILE_OR_TEXT = "file_or_text"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class _UnsupportedConversionError(NotImplementedError):
|
|
70
|
+
"""パイプラインに変換ルートが登録されていない場合の例外"""
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _short_name_for_dump(name: str, *, max_length: int = 64) -> str:
|
|
74
|
+
cleaned: str = re.sub(r"[^A-Za-z0-9._-]+", "_", name).strip("._-") or "unknown"
|
|
75
|
+
if len(cleaned) <= max_length:
|
|
76
|
+
return cleaned
|
|
77
|
+
hash: str = hashlib.sha1(cleaned.encode("utf-8")).hexdigest()[:12]
|
|
78
|
+
length = max_length - (len(hash) + 1)
|
|
79
|
+
if length <= 0:
|
|
80
|
+
return hash
|
|
81
|
+
return f"{cleaned[:length]}_{hash}"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _debug_dump_path_for_test(data: StbData[Any]) -> Path | None:
|
|
85
|
+
debug_dump_dir = os.getenv(DEBUG_DUMP_DIR_ENV)
|
|
86
|
+
if not debug_dump_dir:
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
test_name: str = os.getenv("PYTEST_CURRENT_TEST", "unknown_test")
|
|
90
|
+
node_id: str = test_name.split(" ", maxsplit=1)[0]
|
|
91
|
+
short_test_name: str = _short_name_for_dump(node_id.rsplit("::", maxsplit=1)[-1])
|
|
92
|
+
stb_version: _StbVersion = data.stb_version or _StbVersion.UNSPECIFIED
|
|
93
|
+
model_name: str = _short_name_for_dump(f"object_stb_{stb_version.value}")
|
|
94
|
+
file_name: str = f"{model_name}_{short_test_name}_{uuid4().hex}_error.stb"
|
|
95
|
+
return Path(debug_dump_dir).expanduser() / file_name
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _normalize_format_set(
|
|
99
|
+
data_format: _DataFormat | _DataFormatSet,
|
|
100
|
+
*,
|
|
101
|
+
kind: _DataKind,
|
|
102
|
+
stb_version: _StbVersion | None = None,
|
|
103
|
+
) -> _DataFormatSet:
|
|
104
|
+
if isinstance(data_format, _DataFormatSet):
|
|
105
|
+
return data_format
|
|
106
|
+
return _DataFormatSet(
|
|
107
|
+
format=data_format,
|
|
108
|
+
kind=kind,
|
|
109
|
+
stb_version=stb_version if data_format == _DataFormat.STB else None,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def to_text(
|
|
114
|
+
data: _Data,
|
|
115
|
+
reporter: Reporter,
|
|
116
|
+
) -> str:
|
|
117
|
+
if isinstance(data, _TextData):
|
|
118
|
+
return data.text
|
|
119
|
+
if isinstance(data, _FileData):
|
|
120
|
+
with open(data.path, encoding=data.encoding) as f:
|
|
121
|
+
return f.read()
|
|
122
|
+
if data.format == _DataFormat.STB and isinstance(data, StbData):
|
|
123
|
+
try:
|
|
124
|
+
return stb_io.dumps(data.value, reporter=reporter)
|
|
125
|
+
except SchemaError:
|
|
126
|
+
debug_dump_path = _debug_dump_path_for_test(data)
|
|
127
|
+
if debug_dump_path is not None:
|
|
128
|
+
stb_error: str = _raw_dumps(
|
|
129
|
+
data.value, reporter=reporter, _strict=False
|
|
130
|
+
)
|
|
131
|
+
try:
|
|
132
|
+
debug_dump_path.parent.mkdir(parents=True, exist_ok=True)
|
|
133
|
+
write_text_with_temp_file(
|
|
134
|
+
debug_dump_path,
|
|
135
|
+
stb_error,
|
|
136
|
+
encoding="utf-8",
|
|
137
|
+
)
|
|
138
|
+
except OSError as error:
|
|
139
|
+
reporter.warning(
|
|
140
|
+
"デバッグダンプの保存に失敗しました: "
|
|
141
|
+
f"path={debug_dump_path} error={error}"
|
|
142
|
+
)
|
|
143
|
+
raise
|
|
144
|
+
if data.format == _DataFormat.PLY and isinstance(data, _PlyData):
|
|
145
|
+
return building_geometry_to_ply_text(data.value)
|
|
146
|
+
if data.format == _DataFormat.IFC:
|
|
147
|
+
from ...._internal.optional_dependencies import ifc_pipeline
|
|
148
|
+
|
|
149
|
+
if isinstance(data, ifc_pipeline.IfcData):
|
|
150
|
+
return data.value.to_string()
|
|
151
|
+
raise ValueError(f"テキスト化できないデータ形式です: {type(data)}")
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def save_data(
|
|
155
|
+
data: _Data,
|
|
156
|
+
path: Path,
|
|
157
|
+
reporter: Reporter,
|
|
158
|
+
encoding: str = "utf-8",
|
|
159
|
+
) -> None:
|
|
160
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
161
|
+
if isinstance(data, _FileData):
|
|
162
|
+
if data.path.absolute() != path.absolute():
|
|
163
|
+
|
|
164
|
+
def _copy_to_temp(temporary_path: Path) -> None:
|
|
165
|
+
shutil.copyfile(data.path, temporary_path)
|
|
166
|
+
|
|
167
|
+
write_with_temp_file(path, _copy_to_temp)
|
|
168
|
+
return
|
|
169
|
+
if isinstance(data, _TextData):
|
|
170
|
+
write_text_with_temp_file(path, data.text, encoding=encoding)
|
|
171
|
+
return
|
|
172
|
+
if data.format == _DataFormat.STB and isinstance(data, StbData):
|
|
173
|
+
write_text_stream_with_temp_file(
|
|
174
|
+
path,
|
|
175
|
+
lambda stream: stb_io.dump(data.value, stream, reporter=reporter),
|
|
176
|
+
encoding=encoding,
|
|
177
|
+
)
|
|
178
|
+
return
|
|
179
|
+
if data.format == _DataFormat.IFC:
|
|
180
|
+
from ...._internal.optional_dependencies import ifc_pipeline
|
|
181
|
+
|
|
182
|
+
if isinstance(data, ifc_pipeline.IfcData):
|
|
183
|
+
write_with_temp_file(
|
|
184
|
+
path, lambda temporary_path: data.value.write(temporary_path)
|
|
185
|
+
)
|
|
186
|
+
return
|
|
187
|
+
if data.format == _DataFormat.PLY and isinstance(data, _PlyData):
|
|
188
|
+
write_with_temp_file(
|
|
189
|
+
path,
|
|
190
|
+
lambda temporary_path: save_ply_data(data, temporary_path),
|
|
191
|
+
)
|
|
192
|
+
return
|
|
193
|
+
raise ValueError(f"保存できないデータ形式です: {type(data)}")
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def open_data(
|
|
197
|
+
path: Path,
|
|
198
|
+
data_format: _DataFormat | _DataFormatSet,
|
|
199
|
+
reporter: Reporter,
|
|
200
|
+
*,
|
|
201
|
+
stb_version: _StbVersion | None = None,
|
|
202
|
+
encoding: str | None = None,
|
|
203
|
+
enable_repair: bool = False,
|
|
204
|
+
max_size: int | None = None,
|
|
205
|
+
) -> _Data:
|
|
206
|
+
format_set = _normalize_format_set(
|
|
207
|
+
data_format,
|
|
208
|
+
kind=_DataKind.OBJECT,
|
|
209
|
+
stb_version=stb_version,
|
|
210
|
+
)
|
|
211
|
+
match format_set.format:
|
|
212
|
+
case _DataFormat.STB:
|
|
213
|
+
stb_file: StbFileData = StbFileData(
|
|
214
|
+
path,
|
|
215
|
+
stb_version=format_set.stb_version or _StbVersion.UNSPECIFIED,
|
|
216
|
+
encoding=encoding,
|
|
217
|
+
)
|
|
218
|
+
result = _execute_step(
|
|
219
|
+
StbLoadStep(),
|
|
220
|
+
stb_file,
|
|
221
|
+
context=_ExecutionContext(reporter=reporter, max_size=max_size),
|
|
222
|
+
)
|
|
223
|
+
data: StbData[StBridgeRoot] = result.output
|
|
224
|
+
if enable_repair:
|
|
225
|
+
repaired = _execute_step(
|
|
226
|
+
StbRepairStep[StBridgeRoot](),
|
|
227
|
+
data,
|
|
228
|
+
context=_ExecutionContext(reporter=reporter),
|
|
229
|
+
)
|
|
230
|
+
data = repaired.output
|
|
231
|
+
return data
|
|
232
|
+
case _DataFormat.IFC:
|
|
233
|
+
from ...._internal.optional_dependencies import ifc_pipeline
|
|
234
|
+
|
|
235
|
+
return ifc_pipeline.open_ifc_data(path)
|
|
236
|
+
case _:
|
|
237
|
+
raise _UnsupportedConversionError(
|
|
238
|
+
f"読み込みできないデータ形式です: {format_set.format}"
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def open_text_data(
|
|
243
|
+
text: str,
|
|
244
|
+
data_format: _DataFormat | _DataFormatSet,
|
|
245
|
+
reporter: Reporter,
|
|
246
|
+
*,
|
|
247
|
+
stb_version: _StbVersion | None = None,
|
|
248
|
+
enable_repair: bool = False,
|
|
249
|
+
max_size: int | None = None,
|
|
250
|
+
) -> _Data:
|
|
251
|
+
format_set = _normalize_format_set(
|
|
252
|
+
data_format,
|
|
253
|
+
kind=_DataKind.OBJECT,
|
|
254
|
+
stb_version=stb_version,
|
|
255
|
+
)
|
|
256
|
+
if format_set.format == _DataFormat.STB:
|
|
257
|
+
stb_text: StbTextData = StbTextData(
|
|
258
|
+
text,
|
|
259
|
+
stb_version=format_set.stb_version or _StbVersion.UNSPECIFIED,
|
|
260
|
+
)
|
|
261
|
+
result = _execute_step(
|
|
262
|
+
StbLoadStep(),
|
|
263
|
+
stb_text,
|
|
264
|
+
context=_ExecutionContext(reporter=reporter, max_size=max_size),
|
|
265
|
+
)
|
|
266
|
+
data: StbData[StBridgeRoot] = result.output
|
|
267
|
+
if enable_repair:
|
|
268
|
+
repaired = _execute_step(
|
|
269
|
+
StbRepairStep[StBridgeRoot](),
|
|
270
|
+
data,
|
|
271
|
+
context=_ExecutionContext(reporter=reporter),
|
|
272
|
+
)
|
|
273
|
+
data = repaired.output
|
|
274
|
+
return data
|
|
275
|
+
if format_set.format == _DataFormat.IFC:
|
|
276
|
+
from ...._internal.optional_dependencies import ifc_pipeline
|
|
277
|
+
|
|
278
|
+
return ifc_pipeline.loads_ifc_data(text)
|
|
279
|
+
raise _UnsupportedConversionError(
|
|
280
|
+
f"テキストから読み込みできないデータ形式です: {format_set.format}"
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _get_direct_step(
|
|
285
|
+
input_format: _DataFormatSet,
|
|
286
|
+
output_format: _DataFormatSet,
|
|
287
|
+
*,
|
|
288
|
+
assign_guids: bool = False,
|
|
289
|
+
) -> _ExecutableStep[Any, Any]:
|
|
290
|
+
"""中間形式を含まない1区間分の変換ステップ"""
|
|
291
|
+
if (
|
|
292
|
+
input_format.format == _DataFormat.STB
|
|
293
|
+
and output_format.format == _DataFormat.STB
|
|
294
|
+
):
|
|
295
|
+
if input_format.kind in (_DataKind.FILE, _DataKind.TEXT) and (
|
|
296
|
+
output_format.kind == _DataKind.OBJECT
|
|
297
|
+
):
|
|
298
|
+
loaded_format = _DataFormatSet(
|
|
299
|
+
_DataFormat.STB,
|
|
300
|
+
_DataKind.OBJECT,
|
|
301
|
+
stb_version=_StbVersion.UNSPECIFIED,
|
|
302
|
+
)
|
|
303
|
+
return _compose_steps(
|
|
304
|
+
f"{input_format} -> {output_format}",
|
|
305
|
+
(
|
|
306
|
+
StbLoadStep(),
|
|
307
|
+
_get_direct_step(
|
|
308
|
+
loaded_format,
|
|
309
|
+
output_format,
|
|
310
|
+
assign_guids=assign_guids,
|
|
311
|
+
),
|
|
312
|
+
),
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
if input_format.kind == _DataKind.OBJECT and (
|
|
316
|
+
output_format.kind == _DataKind.TEXT
|
|
317
|
+
):
|
|
318
|
+
object_output = _DataFormatSet(
|
|
319
|
+
_DataFormat.STB,
|
|
320
|
+
_DataKind.OBJECT,
|
|
321
|
+
stb_version=output_format.stb_version,
|
|
322
|
+
)
|
|
323
|
+
return _compose_steps(
|
|
324
|
+
f"{input_format} -> {output_format}",
|
|
325
|
+
(
|
|
326
|
+
_get_direct_step(
|
|
327
|
+
input_format,
|
|
328
|
+
object_output,
|
|
329
|
+
assign_guids=assign_guids,
|
|
330
|
+
),
|
|
331
|
+
StbDumpsStep(),
|
|
332
|
+
),
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
if (
|
|
336
|
+
input_format.kind == _DataKind.OBJECT
|
|
337
|
+
and output_format.kind == _DataKind.OBJECT
|
|
338
|
+
):
|
|
339
|
+
steps: list[_ExecutableStep[Any, Any]] = []
|
|
340
|
+
output_version = output_format.stb_version or _StbVersion.UNSPECIFIED
|
|
341
|
+
if output_version == _StbVersion.LATEST:
|
|
342
|
+
if input_format.stb_version != _StbVersion.LATEST:
|
|
343
|
+
steps.append(StbToLatestStep())
|
|
344
|
+
elif output_version != _StbVersion.UNSPECIFIED and (
|
|
345
|
+
input_format.stb_version != output_version
|
|
346
|
+
):
|
|
347
|
+
steps.append(StbToVersionStep(output_version))
|
|
348
|
+
if assign_guids:
|
|
349
|
+
steps.append(StbAssignGuidsStep())
|
|
350
|
+
return _compose_steps(
|
|
351
|
+
f"{input_format} -> {output_format}",
|
|
352
|
+
steps,
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
if input_format == output_format:
|
|
356
|
+
return _NoOpStep()
|
|
357
|
+
|
|
358
|
+
if (
|
|
359
|
+
input_format.format == _DataFormat.STB
|
|
360
|
+
and output_format.format == _DataFormat.IFC
|
|
361
|
+
and input_format.kind == _DataKind.OBJECT
|
|
362
|
+
and output_format.kind == _DataKind.OBJECT
|
|
363
|
+
):
|
|
364
|
+
from ...._internal.optional_dependencies import ifc_pipeline
|
|
365
|
+
|
|
366
|
+
steps = []
|
|
367
|
+
if input_format.stb_version != _StbVersion.LATEST:
|
|
368
|
+
steps.append(StbToLatestStep())
|
|
369
|
+
if assign_guids:
|
|
370
|
+
steps.append(StbAssignGuidsStep())
|
|
371
|
+
steps.append(ifc_pipeline.stb_latest_to_ifc_step())
|
|
372
|
+
return _compose_steps("STB to IFC", steps)
|
|
373
|
+
|
|
374
|
+
if (
|
|
375
|
+
input_format.format == _DataFormat.IFC
|
|
376
|
+
and output_format.format == _DataFormat.PLY
|
|
377
|
+
and input_format.kind == _DataKind.OBJECT
|
|
378
|
+
and output_format.kind == _DataKind.OBJECT
|
|
379
|
+
):
|
|
380
|
+
from ...._internal.optional_dependencies import ifc_pipeline
|
|
381
|
+
|
|
382
|
+
return ifc_pipeline.IfcDataToPlyStep()
|
|
383
|
+
|
|
384
|
+
if (
|
|
385
|
+
input_format.format == _DataFormat.STB
|
|
386
|
+
and output_format.format == _DataFormat.PLY
|
|
387
|
+
and input_format.kind == _DataKind.OBJECT
|
|
388
|
+
and output_format.kind == _DataKind.OBJECT
|
|
389
|
+
):
|
|
390
|
+
steps = []
|
|
391
|
+
if input_format.stb_version != _StbVersion.LATEST:
|
|
392
|
+
steps.append(StbToLatestStep())
|
|
393
|
+
if assign_guids:
|
|
394
|
+
steps.append(StbAssignGuidsStep())
|
|
395
|
+
steps.append(_StbToPlyStep())
|
|
396
|
+
return _compose_steps("STB to PLY", steps)
|
|
397
|
+
|
|
398
|
+
raise _UnsupportedConversionError(
|
|
399
|
+
f"変換ルートが未実装です: {input_format} -> {output_format}"
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def get_step(
|
|
404
|
+
input_format: _DataFormatSet,
|
|
405
|
+
output_format: _DataFormatSet,
|
|
406
|
+
*,
|
|
407
|
+
route_formats: Sequence[_DataFormatSet] | None = None,
|
|
408
|
+
assign_guids: bool = False,
|
|
409
|
+
) -> _ExecutableStep[Any, Any]:
|
|
410
|
+
"""入力・中間・出力形式から実行可能な変換ステップを組み立てる。"""
|
|
411
|
+
if not route_formats:
|
|
412
|
+
return _get_direct_step(
|
|
413
|
+
input_format,
|
|
414
|
+
output_format,
|
|
415
|
+
assign_guids=assign_guids,
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
waypoints = [input_format, *route_formats, output_format]
|
|
419
|
+
steps: list[_ExecutableStep[Any, Any]] = []
|
|
420
|
+
guid_pending = assign_guids
|
|
421
|
+
last_index = len(waypoints) - 2
|
|
422
|
+
for index, (source, target) in enumerate(itertools.pairwise(waypoints)):
|
|
423
|
+
apply_guid = bool(
|
|
424
|
+
guid_pending
|
|
425
|
+
and source.format == _DataFormat.STB
|
|
426
|
+
and (target.format != _DataFormat.STB or index == last_index)
|
|
427
|
+
)
|
|
428
|
+
steps.append(
|
|
429
|
+
_get_direct_step(
|
|
430
|
+
source,
|
|
431
|
+
target,
|
|
432
|
+
assign_guids=apply_guid,
|
|
433
|
+
)
|
|
434
|
+
)
|
|
435
|
+
if apply_guid:
|
|
436
|
+
guid_pending = False
|
|
437
|
+
|
|
438
|
+
return _compose_steps(
|
|
439
|
+
f"{input_format} -> {output_format}",
|
|
440
|
+
steps,
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def execute_conversion(
|
|
445
|
+
input_data: _Data,
|
|
446
|
+
output_format: _DataFormatSet,
|
|
447
|
+
*,
|
|
448
|
+
route_formats: Sequence[_DataFormatSet] | None = None,
|
|
449
|
+
reporter: Reporter | None = None,
|
|
450
|
+
assign_guids: bool = False,
|
|
451
|
+
output_enable_repair: bool = False,
|
|
452
|
+
on_step_completed: Callable[[str, _Data], None] | None = None,
|
|
453
|
+
work_dir: Path | None = None,
|
|
454
|
+
keep_temporary_files: bool = False,
|
|
455
|
+
ifc_round_digits: int | None = None,
|
|
456
|
+
ifc_round_digits_mm: int | None = None,
|
|
457
|
+
) -> _ExecutionResult[_Data]:
|
|
458
|
+
reporter = reporter or get_reporter()
|
|
459
|
+
step = get_step(
|
|
460
|
+
input_data.format_set,
|
|
461
|
+
output_format,
|
|
462
|
+
route_formats=route_formats,
|
|
463
|
+
assign_guids=assign_guids,
|
|
464
|
+
)
|
|
465
|
+
if output_enable_repair and output_format.format == _DataFormat.STB:
|
|
466
|
+
step = _compose_steps(
|
|
467
|
+
"Conversion with Repair",
|
|
468
|
+
(step, StbRepairStep()),
|
|
469
|
+
)
|
|
470
|
+
context = _ExecutionContext(
|
|
471
|
+
work_dir=work_dir,
|
|
472
|
+
keep_temporary_files=keep_temporary_files,
|
|
473
|
+
reporter=reporter,
|
|
474
|
+
on_step_completed=on_step_completed,
|
|
475
|
+
ifc_round_digits=ifc_round_digits,
|
|
476
|
+
ifc_round_digits_mm=ifc_round_digits_mm,
|
|
477
|
+
)
|
|
478
|
+
return _execute_step(step, input_data, context=context)
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def cleanup_paths(paths: Sequence[Path]) -> None:
|
|
482
|
+
"""パイプラインが生成した一時ファイル・ディレクトリを削除する"""
|
|
483
|
+
for path in reversed(tuple(dict.fromkeys(paths))):
|
|
484
|
+
if path.is_dir():
|
|
485
|
+
shutil.rmtree(path, ignore_errors=True)
|
|
486
|
+
else:
|
|
487
|
+
path.unlink(missing_ok=True)
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def convert(
|
|
491
|
+
input_path: Path,
|
|
492
|
+
input_format: _DataFormat,
|
|
493
|
+
output_format: _DataFormat,
|
|
494
|
+
result_format: ConvertResultFormat,
|
|
495
|
+
*,
|
|
496
|
+
input_stb_version: _StbVersion = _StbVersion.UNSPECIFIED,
|
|
497
|
+
output_stb_version: _StbVersion | None = None,
|
|
498
|
+
route_formats: Sequence[_DataFormat | _DataFormatSet] | None = None,
|
|
499
|
+
output_path: Path | None = None,
|
|
500
|
+
reporter: Reporter | None = None,
|
|
501
|
+
input_encoding: str | None = None,
|
|
502
|
+
input_enable_repair: bool = False,
|
|
503
|
+
output_enable_repair: bool = False,
|
|
504
|
+
assign_guids: bool = False,
|
|
505
|
+
keep_temporary_files: bool = False,
|
|
506
|
+
ifc_round_digits: int | None = None,
|
|
507
|
+
ifc_round_digits_mm: int | None = None,
|
|
508
|
+
) -> str | Path:
|
|
509
|
+
reporter = reporter or get_reporter()
|
|
510
|
+
if output_format == _DataFormat.STB:
|
|
511
|
+
output_stb_version = output_stb_version or _StbVersion.LATEST
|
|
512
|
+
else:
|
|
513
|
+
output_stb_version = None
|
|
514
|
+
|
|
515
|
+
input_format_set = _DataFormatSet(
|
|
516
|
+
input_format,
|
|
517
|
+
_DataKind.OBJECT,
|
|
518
|
+
stb_version=input_stb_version if input_format == _DataFormat.STB else None,
|
|
519
|
+
)
|
|
520
|
+
output_format_set = _DataFormatSet(
|
|
521
|
+
output_format,
|
|
522
|
+
_DataKind.OBJECT,
|
|
523
|
+
stb_version=output_stb_version if output_format == _DataFormat.STB else None,
|
|
524
|
+
)
|
|
525
|
+
normalized_routes = [
|
|
526
|
+
route
|
|
527
|
+
if isinstance(route, _DataFormatSet)
|
|
528
|
+
else _DataFormatSet(route, _DataKind.OBJECT)
|
|
529
|
+
for route in (route_formats or ())
|
|
530
|
+
]
|
|
531
|
+
|
|
532
|
+
input_data = open_data(
|
|
533
|
+
input_path,
|
|
534
|
+
input_format_set,
|
|
535
|
+
reporter,
|
|
536
|
+
encoding=input_encoding,
|
|
537
|
+
enable_repair=input_enable_repair,
|
|
538
|
+
)
|
|
539
|
+
result = execute_conversion(
|
|
540
|
+
input_data,
|
|
541
|
+
output_format_set,
|
|
542
|
+
route_formats=normalized_routes,
|
|
543
|
+
reporter=reporter,
|
|
544
|
+
assign_guids=assign_guids,
|
|
545
|
+
output_enable_repair=output_enable_repair,
|
|
546
|
+
keep_temporary_files=keep_temporary_files,
|
|
547
|
+
ifc_round_digits=ifc_round_digits,
|
|
548
|
+
ifc_round_digits_mm=ifc_round_digits_mm,
|
|
549
|
+
)
|
|
550
|
+
try:
|
|
551
|
+
if result_format in (
|
|
552
|
+
ConvertResultFormat.TEXT,
|
|
553
|
+
ConvertResultFormat.FILE_OR_TEXT,
|
|
554
|
+
):
|
|
555
|
+
return to_text(
|
|
556
|
+
result.output,
|
|
557
|
+
reporter,
|
|
558
|
+
)
|
|
559
|
+
if result_format == ConvertResultFormat.FILE:
|
|
560
|
+
if output_path is None:
|
|
561
|
+
raise ValueError(
|
|
562
|
+
"output_path must be specified when result_format is FILE"
|
|
563
|
+
)
|
|
564
|
+
save_data(result.output, output_path, reporter)
|
|
565
|
+
return output_path
|
|
566
|
+
raise NotImplementedError(f"未実装の結果形式です: {result_format}")
|
|
567
|
+
finally:
|
|
568
|
+
if not keep_temporary_files:
|
|
569
|
+
cleanup_paths(result.cleanup_paths)
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
|
|
9
|
+
from stbkit.core.stb_reporting import NullReporter
|
|
10
|
+
|
|
11
|
+
from ...._internal.data_model.building_geometry import BuildingGeometry
|
|
12
|
+
from ..building_geometry_to_ply_text import building_geometry_to_ply_text
|
|
13
|
+
from ..element_data_to_building_geometry import element_data_to_building_geometry
|
|
14
|
+
from ..stb_latest_to_element_data import stb_to_element_data
|
|
15
|
+
from .data import _DataFormat, _ObjectData
|
|
16
|
+
from .stb_pipeline_latest import StbLatestData
|
|
17
|
+
from .step import _ExecutableStep, _ExecutionContext, _ExecutionResult
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class _PlyData(_ObjectData[BuildingGeometry]):
|
|
21
|
+
def __init__(self, value: BuildingGeometry) -> None:
|
|
22
|
+
super().__init__(format=_DataFormat.PLY, value=value)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def save_ply_data(data: _PlyData, path: Path) -> None:
|
|
26
|
+
ply_text: str = building_geometry_to_ply_text(data.value)
|
|
27
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
28
|
+
f.write(ply_text)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class _StbToPlyStep(_ExecutableStep[StbLatestData, _PlyData]):
|
|
32
|
+
name = "STB -> PLY"
|
|
33
|
+
|
|
34
|
+
def execute(
|
|
35
|
+
self, input: StbLatestData, *, context: _ExecutionContext
|
|
36
|
+
) -> _ExecutionResult[_PlyData]:
|
|
37
|
+
if not isinstance(input, StbLatestData):
|
|
38
|
+
raise TypeError(f"StBridgeデータではありません: {type(input)}")
|
|
39
|
+
elements = stb_to_element_data(
|
|
40
|
+
input.value,
|
|
41
|
+
reporter=context.reporter
|
|
42
|
+
if context.reporter is not None
|
|
43
|
+
else NullReporter(),
|
|
44
|
+
)
|
|
45
|
+
mesh_data = element_data_to_building_geometry(
|
|
46
|
+
elements,
|
|
47
|
+
allow_polygons=True,
|
|
48
|
+
reporter=context.reporter
|
|
49
|
+
if context.reporter is not None
|
|
50
|
+
else NullReporter(),
|
|
51
|
+
)
|
|
52
|
+
return _ExecutionResult(_PlyData(mesh_data))
|