subsurface-terra 2025.1.0rc15__py3-none-any.whl → 2025.1.0rc17__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.
- subsurface/__init__.py +31 -31
- subsurface/_version.py +34 -21
- subsurface/api/__init__.py +13 -13
- subsurface/api/interfaces/__init__.py +3 -3
- subsurface/api/interfaces/stream.py +136 -136
- subsurface/api/reader/read_wells.py +78 -78
- subsurface/core/geological_formats/boreholes/_combine_trajectories.py +117 -117
- subsurface/core/geological_formats/boreholes/_map_attrs_to_survey.py +236 -234
- subsurface/core/geological_formats/boreholes/_survey_to_unstruct.py +163 -163
- subsurface/core/geological_formats/boreholes/boreholes.py +140 -140
- subsurface/core/geological_formats/boreholes/collars.py +26 -26
- subsurface/core/geological_formats/boreholes/survey.py +86 -86
- subsurface/core/geological_formats/fault.py +47 -47
- subsurface/core/reader_helpers/reader_unstruct.py +11 -11
- subsurface/core/reader_helpers/readers_data.py +130 -130
- subsurface/core/reader_helpers/readers_wells.py +13 -13
- subsurface/core/structs/__init__.py +3 -3
- subsurface/core/structs/base_structures/__init__.py +2 -2
- subsurface/core/structs/base_structures/_aux.py +69 -0
- subsurface/core/structs/base_structures/_liquid_earth_mesh.py +121 -121
- subsurface/core/structs/base_structures/_unstructured_data_constructor.py +70 -70
- subsurface/core/structs/base_structures/base_structures_enum.py +6 -6
- subsurface/core/structs/base_structures/structured_data.py +282 -282
- subsurface/core/structs/base_structures/unstructured_data.py +338 -319
- subsurface/core/structs/structured_elements/octree_mesh.py +10 -10
- subsurface/core/structs/structured_elements/structured_grid.py +59 -59
- subsurface/core/structs/structured_elements/structured_mesh.py +9 -9
- subsurface/core/structs/unstructured_elements/__init__.py +3 -3
- subsurface/core/structs/unstructured_elements/line_set.py +72 -72
- subsurface/core/structs/unstructured_elements/point_set.py +43 -43
- subsurface/core/structs/unstructured_elements/tetrahedron_mesh.py +35 -35
- subsurface/core/structs/unstructured_elements/triangular_surface.py +62 -62
- subsurface/core/utils/utils_core.py +38 -38
- subsurface/modules/reader/__init__.py +13 -13
- subsurface/modules/reader/faults/faults.py +80 -80
- subsurface/modules/reader/from_binary.py +46 -46
- subsurface/modules/reader/mesh/_GOCAD_mesh.py +82 -82
- subsurface/modules/reader/mesh/_trimesh_reader.py +447 -447
- subsurface/modules/reader/mesh/csv_mesh_reader.py +53 -53
- subsurface/modules/reader/mesh/dxf_reader.py +177 -177
- subsurface/modules/reader/mesh/glb_reader.py +30 -30
- subsurface/modules/reader/mesh/mx_reader.py +232 -232
- subsurface/modules/reader/mesh/obj_reader.py +53 -53
- subsurface/modules/reader/mesh/omf_mesh_reader.py +43 -43
- subsurface/modules/reader/mesh/surface_reader.py +56 -56
- subsurface/modules/reader/mesh/surfaces_api.py +41 -41
- subsurface/modules/reader/profiles/__init__.py +3 -3
- subsurface/modules/reader/profiles/profiles_core.py +197 -197
- subsurface/modules/reader/read_netcdf.py +38 -38
- subsurface/modules/reader/topography/__init__.py +7 -7
- subsurface/modules/reader/topography/topo_core.py +100 -100
- subsurface/modules/reader/volume/read_grav3d.py +447 -428
- subsurface/modules/reader/volume/read_volume.py +327 -230
- subsurface/modules/reader/volume/segy_reader.py +105 -105
- subsurface/modules/reader/volume/seismic.py +173 -173
- subsurface/modules/reader/volume/volume_utils.py +43 -43
- subsurface/modules/reader/wells/DEP/__init__.py +43 -43
- subsurface/modules/reader/wells/DEP/_well_files_reader.py +167 -167
- subsurface/modules/reader/wells/DEP/_wells_api.py +61 -61
- subsurface/modules/reader/wells/DEP/_welly_reader.py +180 -180
- subsurface/modules/reader/wells/DEP/pandas_to_welly.py +212 -212
- subsurface/modules/reader/wells/_read_to_df.py +57 -57
- subsurface/modules/reader/wells/read_borehole_interface.py +148 -148
- subsurface/modules/reader/wells/wells_utils.py +68 -68
- subsurface/modules/tools/mocking_aux.py +104 -104
- subsurface/modules/visualization/__init__.py +2 -2
- subsurface/modules/visualization/to_pyvista.py +320 -320
- subsurface/modules/writer/to_binary.py +12 -12
- subsurface/modules/writer/to_rex/common.py +78 -78
- subsurface/modules/writer/to_rex/data_struct.py +74 -74
- subsurface/modules/writer/to_rex/gempy_to_rexfile.py +791 -791
- subsurface/modules/writer/to_rex/material_encoder.py +44 -44
- subsurface/modules/writer/to_rex/mesh_encoder.py +152 -152
- subsurface/modules/writer/to_rex/to_rex.py +115 -115
- subsurface/modules/writer/to_rex/utils.py +15 -15
- subsurface/optional_requirements.py +116 -116
- {subsurface_terra-2025.1.0rc15.dist-info → subsurface_terra-2025.1.0rc17.dist-info}/METADATA +194 -194
- subsurface_terra-2025.1.0rc17.dist-info/RECORD +99 -0
- {subsurface_terra-2025.1.0rc15.dist-info → subsurface_terra-2025.1.0rc17.dist-info}/WHEEL +1 -1
- {subsurface_terra-2025.1.0rc15.dist-info → subsurface_terra-2025.1.0rc17.dist-info}/licenses/LICENSE +203 -203
- subsurface_terra-2025.1.0rc15.dist-info/RECORD +0 -98
- {subsurface_terra-2025.1.0rc15.dist-info → subsurface_terra-2025.1.0rc17.dist-info}/top_level.txt +0 -0
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
from typing import Union, Callable
|
|
2
|
-
|
|
3
|
-
import numpy as np
|
|
4
|
-
import pandas as pd
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def mesh_csv_to_vertex(path_to_file: str, columns_map: Union[None, Callable, dict, pd.Series] = None,
|
|
8
|
-
**reader_kwargs) -> np.ndarray:
|
|
9
|
-
data = pd.read_csv(path_to_file, **reader_kwargs)
|
|
10
|
-
if columns_map is not None: map_columns_names(columns_map, data)
|
|
11
|
-
return get_vertices_from_df(data)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def mesh_csv_to_cells(path_to_file: str, columns_map: Union[None, Callable, dict, pd.Series] = None,
|
|
15
|
-
**reader_kwargs) -> np.ndarray:
|
|
16
|
-
data = pd.read_csv(path_to_file, **reader_kwargs)
|
|
17
|
-
if columns_map is not None: map_columns_names(columns_map, data)
|
|
18
|
-
return get_cells_from_df(data)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def mesh_csv_to_attributes(path_to_file: str,
|
|
22
|
-
columns_map: Union[None, Callable, dict, pd.Series] = None,
|
|
23
|
-
**reader_kwargs) -> pd.DataFrame:
|
|
24
|
-
data = pd.read_csv(path_to_file, **reader_kwargs)
|
|
25
|
-
if columns_map is not None:
|
|
26
|
-
map_columns_names(columns_map, data)
|
|
27
|
-
return data
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def get_cells_from_df(data):
|
|
31
|
-
try:
|
|
32
|
-
cells = data[['e1', 'e2', 'e3']].dropna().astype('int').values
|
|
33
|
-
except KeyError:
|
|
34
|
-
raise KeyError('Columns e1, e2, and e3 must be present in the data set. Use'
|
|
35
|
-
'columns_map to map other names')
|
|
36
|
-
return cells
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def get_vertices_from_df(data):
|
|
40
|
-
try:
|
|
41
|
-
vertex = data[['x', 'y', 'z']].values
|
|
42
|
-
except KeyError:
|
|
43
|
-
raise KeyError('Columns x, y, and z must be present in the data set. Use'
|
|
44
|
-
'columns_map to map other names')
|
|
45
|
-
return vertex
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def map_columns_names(columns_map: Union[Callable, dict, pd.Series], data: pd.DataFrame):
|
|
49
|
-
data.columns = data.columns.map(columns_map)
|
|
50
|
-
if data.columns.isin(['x', 'y', 'z']).any() is False:
|
|
51
|
-
raise AttributeError('At least x, y, z must be passed to `columns_map`')
|
|
52
|
-
|
|
53
|
-
return data.columns
|
|
1
|
+
from typing import Union, Callable
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def mesh_csv_to_vertex(path_to_file: str, columns_map: Union[None, Callable, dict, pd.Series] = None,
|
|
8
|
+
**reader_kwargs) -> np.ndarray:
|
|
9
|
+
data = pd.read_csv(path_to_file, **reader_kwargs)
|
|
10
|
+
if columns_map is not None: map_columns_names(columns_map, data)
|
|
11
|
+
return get_vertices_from_df(data)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def mesh_csv_to_cells(path_to_file: str, columns_map: Union[None, Callable, dict, pd.Series] = None,
|
|
15
|
+
**reader_kwargs) -> np.ndarray:
|
|
16
|
+
data = pd.read_csv(path_to_file, **reader_kwargs)
|
|
17
|
+
if columns_map is not None: map_columns_names(columns_map, data)
|
|
18
|
+
return get_cells_from_df(data)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def mesh_csv_to_attributes(path_to_file: str,
|
|
22
|
+
columns_map: Union[None, Callable, dict, pd.Series] = None,
|
|
23
|
+
**reader_kwargs) -> pd.DataFrame:
|
|
24
|
+
data = pd.read_csv(path_to_file, **reader_kwargs)
|
|
25
|
+
if columns_map is not None:
|
|
26
|
+
map_columns_names(columns_map, data)
|
|
27
|
+
return data
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_cells_from_df(data):
|
|
31
|
+
try:
|
|
32
|
+
cells = data[['e1', 'e2', 'e3']].dropna().astype('int').values
|
|
33
|
+
except KeyError:
|
|
34
|
+
raise KeyError('Columns e1, e2, and e3 must be present in the data set. Use'
|
|
35
|
+
'columns_map to map other names')
|
|
36
|
+
return cells
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_vertices_from_df(data):
|
|
40
|
+
try:
|
|
41
|
+
vertex = data[['x', 'y', 'z']].values
|
|
42
|
+
except KeyError:
|
|
43
|
+
raise KeyError('Columns x, y, and z must be present in the data set. Use'
|
|
44
|
+
'columns_map to map other names')
|
|
45
|
+
return vertex
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def map_columns_names(columns_map: Union[Callable, dict, pd.Series], data: pd.DataFrame):
|
|
49
|
+
data.columns = data.columns.map(columns_map)
|
|
50
|
+
if data.columns.isin(['x', 'y', 'z']).any() is False:
|
|
51
|
+
raise AttributeError('At least x, y, z must be passed to `columns_map`')
|
|
52
|
+
|
|
53
|
+
return data.columns
|
|
@@ -1,177 +1,177 @@
|
|
|
1
|
-
import pathlib
|
|
2
|
-
import warnings
|
|
3
|
-
from enum import Flag
|
|
4
|
-
from typing import TextIO, Union
|
|
5
|
-
|
|
6
|
-
import numpy as np
|
|
7
|
-
|
|
8
|
-
from .... import optional_requirements
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class DXFEntityType(Flag):
|
|
12
|
-
"""Decides which entity types should be extracted."""
|
|
13
|
-
FACE3D = 2 ** 0 # Extract only 3DFACE
|
|
14
|
-
POLYLINE = 2 ** 1 # Extract only POLYLINE
|
|
15
|
-
ALL = FACE3D | POLYLINE # Extract all entity types
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def dxf_from_file_to_vertex(
|
|
19
|
-
file_path: Union[str, pathlib.Path],
|
|
20
|
-
entity_type: DXFEntityType = DXFEntityType.ALL,
|
|
21
|
-
) -> np.ndarray:
|
|
22
|
-
"""
|
|
23
|
-
Extract vertices from a DXF file, according to the chosen entity_type.
|
|
24
|
-
|
|
25
|
-
:param file_path: Path to the DXF file.
|
|
26
|
-
:param entity_type: Controls which entity types to extract.
|
|
27
|
-
:return: Unique vertex array [N, 3].
|
|
28
|
-
"""
|
|
29
|
-
ezdxf = optional_requirements.require_ezdxf()
|
|
30
|
-
dataset = ezdxf.readfile(file_path)
|
|
31
|
-
return _extract_vertices_from_dataset(dataset, entity_type)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def dxf_from_stream_to_vertex(
|
|
35
|
-
stream: TextIO,
|
|
36
|
-
entity_type: DXFEntityType = DXFEntityType.ALL,
|
|
37
|
-
) -> np.ndarray:
|
|
38
|
-
"""
|
|
39
|
-
Extract vertices from a DXF stream, according to the chosen entity_type.
|
|
40
|
-
|
|
41
|
-
:param stream: A file-like object containing the DXF data.
|
|
42
|
-
:param entity_type: Controls which entity types to extract.
|
|
43
|
-
:return: Unique vertex array [N, 3].
|
|
44
|
-
"""
|
|
45
|
-
ezdxf = optional_requirements.require_ezdxf()
|
|
46
|
-
dataset = ezdxf.read(stream)
|
|
47
|
-
return _extract_vertices_from_dataset(dataset, entity_type)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def dxf_file_to_unstruct_input(
|
|
51
|
-
file: Union[str, pathlib.Path],
|
|
52
|
-
) -> tuple[np.ndarray, np.ndarray, np.ndarray, dict]:
|
|
53
|
-
"""
|
|
54
|
-
Extract unstructured-mesh-like data (vertex, cells, cell_attr_int, cell_attr_map)
|
|
55
|
-
from a DXF file using only 3DFACE entities.
|
|
56
|
-
|
|
57
|
-
:param file: Path to the DXF file.
|
|
58
|
-
:return: (vertex, cells, cell_attr_int, cell_attr_map)
|
|
59
|
-
"""
|
|
60
|
-
ezdxf = optional_requirements.require_ezdxf()
|
|
61
|
-
dataset = ezdxf.readfile(file)
|
|
62
|
-
vertex, cells, cell_attr_int, cell_attr_map = _dxf_dataset_to_unstruct_input(dataset)
|
|
63
|
-
|
|
64
|
-
if vertex.size == 0:
|
|
65
|
-
raise ValueError("The DXF file does not contain any 3DFACE entities.")
|
|
66
|
-
|
|
67
|
-
return vertex, cells, cell_attr_int, cell_attr_map
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def dxf_stream_to_unstruct_input(
|
|
71
|
-
stream: TextIO,
|
|
72
|
-
) -> tuple[np.ndarray, np.ndarray, np.ndarray, dict]:
|
|
73
|
-
"""
|
|
74
|
-
Extract unstructured-mesh-like data (vertex, cells, cell_attr_int, cell_attr_map)
|
|
75
|
-
from a DXF stream using only 3DFACE entities.
|
|
76
|
-
|
|
77
|
-
:param stream: A file-like object containing the DXF data.
|
|
78
|
-
:return: (vertex, cells, cell_attr_int, cell_attr_map)
|
|
79
|
-
"""
|
|
80
|
-
ezdxf = optional_requirements.require_ezdxf()
|
|
81
|
-
dataset = ezdxf.read(stream)
|
|
82
|
-
return _dxf_dataset_to_unstruct_input(dataset)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def _extract_vertices_from_dataset(
|
|
86
|
-
dataset,
|
|
87
|
-
entity_type: DXFEntityType = DXFEntityType.ALL
|
|
88
|
-
) -> np.ndarray:
|
|
89
|
-
"""
|
|
90
|
-
Collect unique vertices from the dataset's modelspace.
|
|
91
|
-
The entity_type flag dictates which entities are extracted.
|
|
92
|
-
"""
|
|
93
|
-
vertices = []
|
|
94
|
-
|
|
95
|
-
for e in dataset.modelspace():
|
|
96
|
-
match e.dxftype():
|
|
97
|
-
# 3DFACE
|
|
98
|
-
case "3DFACE" if DXFEntityType.FACE3D in entity_type:
|
|
99
|
-
# A 3DFACE entity typically has three corners: e[0], e[1], e[2]
|
|
100
|
-
# Sometimes it can have a fourth corner e[3], which might be equal to e[2].
|
|
101
|
-
# Adjust accordingly if needed:
|
|
102
|
-
vertices.extend([e[0], e[1], e[2]])
|
|
103
|
-
# If you'd like to handle the potential 4th corner,
|
|
104
|
-
# you could do: vertices.append(e[3]) if e[3] != e[2] else None
|
|
105
|
-
|
|
106
|
-
# POLYLINE
|
|
107
|
-
case "POLYLINE" if DXFEntityType.POLYLINE in entity_type:
|
|
108
|
-
for v in e.vertices:
|
|
109
|
-
x, y, z = v.dxf.location.xyz
|
|
110
|
-
vertices.append([x, y, z])
|
|
111
|
-
|
|
112
|
-
# Other / unsupported
|
|
113
|
-
case _:
|
|
114
|
-
# If it doesn't match the chosen entity type(s), we skip or warn.
|
|
115
|
-
# But if you'd prefer to only warn when an entity isn't recognized at all,
|
|
116
|
-
# you can do so here:
|
|
117
|
-
warnings.warn(f"Entity type '{e.dxftype()}' not extracted (flag: {entity_type}).")
|
|
118
|
-
continue
|
|
119
|
-
|
|
120
|
-
# Convert to numpy array and ensure uniqueness
|
|
121
|
-
vertices = np.array(vertices)
|
|
122
|
-
if vertices.size == 0:
|
|
123
|
-
return vertices
|
|
124
|
-
return np.unique(vertices, axis=0)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
def _dxf_dataset_to_unstruct_input(dataset: 'ezdxf.drawing.Drawing') -> tuple[np.ndarray, np.ndarray, np.ndarray, dict]:
|
|
128
|
-
"""
|
|
129
|
-
Build unstructured-mesh-like data from 3DFACE entities in a dataset:
|
|
130
|
-
- vertex coordinates
|
|
131
|
-
- connectivity in 'cells' array
|
|
132
|
-
- cell attributes in both integer-coded and mapping (string->int) forms
|
|
133
|
-
|
|
134
|
-
"""
|
|
135
|
-
"""
|
|
136
|
-
Build unstructured-mesh-like data from 3DFACE entities in a dataset:
|
|
137
|
-
- vertex coordinates
|
|
138
|
-
- connectivity in 'cells' array
|
|
139
|
-
- cell attributes in both integer-coded and mapping (string->int) forms
|
|
140
|
-
|
|
141
|
-
Returns (cell_attr_int, cell_attr_map, cells, vertex).
|
|
142
|
-
"""
|
|
143
|
-
|
|
144
|
-
def _map_cell_attr_strings_to_integers(cell_attr):
|
|
145
|
-
"""
|
|
146
|
-
Map string layer names (or other string attributes) to integer IDs, starting from 1.
|
|
147
|
-
"""
|
|
148
|
-
unique_values = np.unique(cell_attr)
|
|
149
|
-
sorted_unique_values = sorted(unique_values)
|
|
150
|
-
|
|
151
|
-
# Create mapping from string values to integers (start at 1, 2, 3, ...)
|
|
152
|
-
value_to_int_mapping = {
|
|
153
|
-
str(value): index + 1 for index, value in enumerate(sorted_unique_values)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
cell_attr_int = np.array([value_to_int_mapping[value] for value in cell_attr])
|
|
157
|
-
return cell_attr_int, value_to_int_mapping
|
|
158
|
-
|
|
159
|
-
vertex_list = []
|
|
160
|
-
cell_attr = []
|
|
161
|
-
|
|
162
|
-
for e in dataset.modelspace():
|
|
163
|
-
match e.dxftype():
|
|
164
|
-
case "3DFACE":
|
|
165
|
-
# Typically 3 corners, but can have 4th corner repeated
|
|
166
|
-
vertex_list.extend([e[0], e[1], e[2]])
|
|
167
|
-
cell_attr.append(e.dxf.get("layer"))
|
|
168
|
-
case _:
|
|
169
|
-
# We ignore other entities in unstructured input
|
|
170
|
-
continue
|
|
171
|
-
|
|
172
|
-
vertices = np.array(vertex_list)
|
|
173
|
-
# For each triple (3DFACE), build a cell (triangle)
|
|
174
|
-
cells = np.arange(0, vertices.shape[0]).reshape(-1, 3)
|
|
175
|
-
|
|
176
|
-
cell_attr_int, cell_attr_map = _map_cell_attr_strings_to_integers(cell_attr)
|
|
177
|
-
return vertices, cells, cell_attr_int, cell_attr_map
|
|
1
|
+
import pathlib
|
|
2
|
+
import warnings
|
|
3
|
+
from enum import Flag
|
|
4
|
+
from typing import TextIO, Union
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from .... import optional_requirements
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DXFEntityType(Flag):
|
|
12
|
+
"""Decides which entity types should be extracted."""
|
|
13
|
+
FACE3D = 2 ** 0 # Extract only 3DFACE
|
|
14
|
+
POLYLINE = 2 ** 1 # Extract only POLYLINE
|
|
15
|
+
ALL = FACE3D | POLYLINE # Extract all entity types
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def dxf_from_file_to_vertex(
|
|
19
|
+
file_path: Union[str, pathlib.Path],
|
|
20
|
+
entity_type: DXFEntityType = DXFEntityType.ALL,
|
|
21
|
+
) -> np.ndarray:
|
|
22
|
+
"""
|
|
23
|
+
Extract vertices from a DXF file, according to the chosen entity_type.
|
|
24
|
+
|
|
25
|
+
:param file_path: Path to the DXF file.
|
|
26
|
+
:param entity_type: Controls which entity types to extract.
|
|
27
|
+
:return: Unique vertex array [N, 3].
|
|
28
|
+
"""
|
|
29
|
+
ezdxf = optional_requirements.require_ezdxf()
|
|
30
|
+
dataset = ezdxf.readfile(file_path)
|
|
31
|
+
return _extract_vertices_from_dataset(dataset, entity_type)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def dxf_from_stream_to_vertex(
|
|
35
|
+
stream: TextIO,
|
|
36
|
+
entity_type: DXFEntityType = DXFEntityType.ALL,
|
|
37
|
+
) -> np.ndarray:
|
|
38
|
+
"""
|
|
39
|
+
Extract vertices from a DXF stream, according to the chosen entity_type.
|
|
40
|
+
|
|
41
|
+
:param stream: A file-like object containing the DXF data.
|
|
42
|
+
:param entity_type: Controls which entity types to extract.
|
|
43
|
+
:return: Unique vertex array [N, 3].
|
|
44
|
+
"""
|
|
45
|
+
ezdxf = optional_requirements.require_ezdxf()
|
|
46
|
+
dataset = ezdxf.read(stream)
|
|
47
|
+
return _extract_vertices_from_dataset(dataset, entity_type)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def dxf_file_to_unstruct_input(
|
|
51
|
+
file: Union[str, pathlib.Path],
|
|
52
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray, dict]:
|
|
53
|
+
"""
|
|
54
|
+
Extract unstructured-mesh-like data (vertex, cells, cell_attr_int, cell_attr_map)
|
|
55
|
+
from a DXF file using only 3DFACE entities.
|
|
56
|
+
|
|
57
|
+
:param file: Path to the DXF file.
|
|
58
|
+
:return: (vertex, cells, cell_attr_int, cell_attr_map)
|
|
59
|
+
"""
|
|
60
|
+
ezdxf = optional_requirements.require_ezdxf()
|
|
61
|
+
dataset = ezdxf.readfile(file)
|
|
62
|
+
vertex, cells, cell_attr_int, cell_attr_map = _dxf_dataset_to_unstruct_input(dataset)
|
|
63
|
+
|
|
64
|
+
if vertex.size == 0:
|
|
65
|
+
raise ValueError("The DXF file does not contain any 3DFACE entities.")
|
|
66
|
+
|
|
67
|
+
return vertex, cells, cell_attr_int, cell_attr_map
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def dxf_stream_to_unstruct_input(
|
|
71
|
+
stream: TextIO,
|
|
72
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray, dict]:
|
|
73
|
+
"""
|
|
74
|
+
Extract unstructured-mesh-like data (vertex, cells, cell_attr_int, cell_attr_map)
|
|
75
|
+
from a DXF stream using only 3DFACE entities.
|
|
76
|
+
|
|
77
|
+
:param stream: A file-like object containing the DXF data.
|
|
78
|
+
:return: (vertex, cells, cell_attr_int, cell_attr_map)
|
|
79
|
+
"""
|
|
80
|
+
ezdxf = optional_requirements.require_ezdxf()
|
|
81
|
+
dataset = ezdxf.read(stream)
|
|
82
|
+
return _dxf_dataset_to_unstruct_input(dataset)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _extract_vertices_from_dataset(
|
|
86
|
+
dataset,
|
|
87
|
+
entity_type: DXFEntityType = DXFEntityType.ALL
|
|
88
|
+
) -> np.ndarray:
|
|
89
|
+
"""
|
|
90
|
+
Collect unique vertices from the dataset's modelspace.
|
|
91
|
+
The entity_type flag dictates which entities are extracted.
|
|
92
|
+
"""
|
|
93
|
+
vertices = []
|
|
94
|
+
|
|
95
|
+
for e in dataset.modelspace():
|
|
96
|
+
match e.dxftype():
|
|
97
|
+
# 3DFACE
|
|
98
|
+
case "3DFACE" if DXFEntityType.FACE3D in entity_type:
|
|
99
|
+
# A 3DFACE entity typically has three corners: e[0], e[1], e[2]
|
|
100
|
+
# Sometimes it can have a fourth corner e[3], which might be equal to e[2].
|
|
101
|
+
# Adjust accordingly if needed:
|
|
102
|
+
vertices.extend([e[0], e[1], e[2]])
|
|
103
|
+
# If you'd like to handle the potential 4th corner,
|
|
104
|
+
# you could do: vertices.append(e[3]) if e[3] != e[2] else None
|
|
105
|
+
|
|
106
|
+
# POLYLINE
|
|
107
|
+
case "POLYLINE" if DXFEntityType.POLYLINE in entity_type:
|
|
108
|
+
for v in e.vertices:
|
|
109
|
+
x, y, z = v.dxf.location.xyz
|
|
110
|
+
vertices.append([x, y, z])
|
|
111
|
+
|
|
112
|
+
# Other / unsupported
|
|
113
|
+
case _:
|
|
114
|
+
# If it doesn't match the chosen entity type(s), we skip or warn.
|
|
115
|
+
# But if you'd prefer to only warn when an entity isn't recognized at all,
|
|
116
|
+
# you can do so here:
|
|
117
|
+
warnings.warn(f"Entity type '{e.dxftype()}' not extracted (flag: {entity_type}).")
|
|
118
|
+
continue
|
|
119
|
+
|
|
120
|
+
# Convert to numpy array and ensure uniqueness
|
|
121
|
+
vertices = np.array(vertices)
|
|
122
|
+
if vertices.size == 0:
|
|
123
|
+
return vertices
|
|
124
|
+
return np.unique(vertices, axis=0)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _dxf_dataset_to_unstruct_input(dataset: 'ezdxf.drawing.Drawing') -> tuple[np.ndarray, np.ndarray, np.ndarray, dict]:
|
|
128
|
+
"""
|
|
129
|
+
Build unstructured-mesh-like data from 3DFACE entities in a dataset:
|
|
130
|
+
- vertex coordinates
|
|
131
|
+
- connectivity in 'cells' array
|
|
132
|
+
- cell attributes in both integer-coded and mapping (string->int) forms
|
|
133
|
+
|
|
134
|
+
"""
|
|
135
|
+
"""
|
|
136
|
+
Build unstructured-mesh-like data from 3DFACE entities in a dataset:
|
|
137
|
+
- vertex coordinates
|
|
138
|
+
- connectivity in 'cells' array
|
|
139
|
+
- cell attributes in both integer-coded and mapping (string->int) forms
|
|
140
|
+
|
|
141
|
+
Returns (cell_attr_int, cell_attr_map, cells, vertex).
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
def _map_cell_attr_strings_to_integers(cell_attr):
|
|
145
|
+
"""
|
|
146
|
+
Map string layer names (or other string attributes) to integer IDs, starting from 1.
|
|
147
|
+
"""
|
|
148
|
+
unique_values = np.unique(cell_attr)
|
|
149
|
+
sorted_unique_values = sorted(unique_values)
|
|
150
|
+
|
|
151
|
+
# Create mapping from string values to integers (start at 1, 2, 3, ...)
|
|
152
|
+
value_to_int_mapping = {
|
|
153
|
+
str(value): index + 1 for index, value in enumerate(sorted_unique_values)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
cell_attr_int = np.array([value_to_int_mapping[value] for value in cell_attr])
|
|
157
|
+
return cell_attr_int, value_to_int_mapping
|
|
158
|
+
|
|
159
|
+
vertex_list = []
|
|
160
|
+
cell_attr = []
|
|
161
|
+
|
|
162
|
+
for e in dataset.modelspace():
|
|
163
|
+
match e.dxftype():
|
|
164
|
+
case "3DFACE":
|
|
165
|
+
# Typically 3 corners, but can have 4th corner repeated
|
|
166
|
+
vertex_list.extend([e[0], e[1], e[2]])
|
|
167
|
+
cell_attr.append(e.dxf.get("layer"))
|
|
168
|
+
case _:
|
|
169
|
+
# We ignore other entities in unstructured input
|
|
170
|
+
continue
|
|
171
|
+
|
|
172
|
+
vertices = np.array(vertex_list)
|
|
173
|
+
# For each triple (3DFACE), build a cell (triangle)
|
|
174
|
+
cells = np.arange(0, vertices.shape[0]).reshape(-1, 3)
|
|
175
|
+
|
|
176
|
+
cell_attr_int, cell_attr_map = _map_cell_attr_strings_to_integers(cell_attr)
|
|
177
|
+
return vertices, cells, cell_attr_int, cell_attr_map
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import io
|
|
2
|
-
from typing import Union
|
|
3
|
-
|
|
4
|
-
from ....core.structs import TriSurf
|
|
5
|
-
from ._trimesh_reader import load_with_trimesh, trimesh_to_unstruct, TriMeshTransformations
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def load_gltf_with_trimesh(path_to_glb: Union[str | io.BytesIO], coordinate_system: TriMeshTransformations) -> TriSurf:
|
|
9
|
-
"""
|
|
10
|
-
load_obj_with_trimesh(path_to_glb, plot=False)
|
|
11
|
-
|
|
12
|
-
Loads a 3D object file in .glb format using trimesh, processes it, and converts it into
|
|
13
|
-
a subsurface TriSurf object for further analysis or usage. Optionally, it allows
|
|
14
|
-
plotting the loaded object.
|
|
15
|
-
|
|
16
|
-
Parameters
|
|
17
|
-
----------
|
|
18
|
-
path_to_glb : str
|
|
19
|
-
Path to the .glb file containing the 3D object.
|
|
20
|
-
plot : bool, optional
|
|
21
|
-
A flag indicating whether the loaded 3D object should be plotted. Defaults to False.
|
|
22
|
-
|
|
23
|
-
Returns
|
|
24
|
-
-------
|
|
25
|
-
subsurface.TriSurf
|
|
26
|
-
A TriSurf object representing the processed 3D surface geometry.
|
|
27
|
-
"""
|
|
28
|
-
trimesh = load_with_trimesh(path_to_glb, file_type="glb", coordinate_system=coordinate_system, plot=False)
|
|
29
|
-
trisurf = trimesh_to_unstruct(trimesh)
|
|
30
|
-
return trisurf
|
|
1
|
+
import io
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
from ....core.structs import TriSurf
|
|
5
|
+
from ._trimesh_reader import load_with_trimesh, trimesh_to_unstruct, TriMeshTransformations
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def load_gltf_with_trimesh(path_to_glb: Union[str | io.BytesIO], coordinate_system: TriMeshTransformations) -> TriSurf:
|
|
9
|
+
"""
|
|
10
|
+
load_obj_with_trimesh(path_to_glb, plot=False)
|
|
11
|
+
|
|
12
|
+
Loads a 3D object file in .glb format using trimesh, processes it, and converts it into
|
|
13
|
+
a subsurface TriSurf object for further analysis or usage. Optionally, it allows
|
|
14
|
+
plotting the loaded object.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
path_to_glb : str
|
|
19
|
+
Path to the .glb file containing the 3D object.
|
|
20
|
+
plot : bool, optional
|
|
21
|
+
A flag indicating whether the loaded 3D object should be plotted. Defaults to False.
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
subsurface.TriSurf
|
|
26
|
+
A TriSurf object representing the processed 3D surface geometry.
|
|
27
|
+
"""
|
|
28
|
+
trimesh = load_with_trimesh(path_to_glb, file_type="glb", coordinate_system=coordinate_system, plot=False)
|
|
29
|
+
trisurf = trimesh_to_unstruct(trimesh)
|
|
30
|
+
return trisurf
|