subsurface-terra 2025.1.0rc15__py3-none-any.whl → 2025.1.0rc16__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/_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 +319 -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 +478 -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.0rc16.dist-info}/METADATA +194 -194
- subsurface_terra-2025.1.0rc16.dist-info/RECORD +98 -0
- {subsurface_terra-2025.1.0rc15.dist-info → subsurface_terra-2025.1.0rc16.dist-info}/WHEEL +1 -1
- {subsurface_terra-2025.1.0rc15.dist-info → subsurface_terra-2025.1.0rc16.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.0rc16.dist-info}/top_level.txt +0 -0
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
from .csv_mesh_reader import mesh_csv_to_vertex, mesh_csv_to_cells, mesh_csv_to_attributes
|
|
2
|
-
from .dxf_reader import dxf_from_file_to_vertex, dxf_from_stream_to_vertex, DXFEntityType
|
|
3
|
-
from subsurface.core.reader_helpers.readers_data import GenericReaderFilesHelper, SupportedFormats
|
|
4
|
-
import numpy as np
|
|
5
|
-
|
|
6
|
-
__all__ = ['read_mesh_file_to_vertex', 'read_mesh_file_to_cells',
|
|
7
|
-
'read_mesh_file_to_attr', 'cells_from_delaunay']
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def read_mesh_file_to_vertex(reader_args: GenericReaderFilesHelper) -> np.ndarray:
|
|
11
|
-
if reader_args.format is SupportedFormats.CSV:
|
|
12
|
-
vertex = mesh_csv_to_vertex(reader_args.file_or_buffer, reader_args.columns_map,
|
|
13
|
-
**reader_args.pandas_reader_kwargs)
|
|
14
|
-
elif reader_args.format is SupportedFormats.DXF:
|
|
15
|
-
vertex = dxf_from_file_to_vertex(
|
|
16
|
-
file_path=reader_args.file_or_buffer,
|
|
17
|
-
entity_type=reader_args.additional_reader_kwargs.get('entity_type', DXFEntityType.ALL)
|
|
18
|
-
)
|
|
19
|
-
elif reader_args.format is SupportedFormats.DXFStream:
|
|
20
|
-
vertex = dxf_from_stream_to_vertex(reader_args.file_or_buffer)
|
|
21
|
-
else:
|
|
22
|
-
raise ValueError(f"Subsurface is not able to read the following extension: {reader_args.format}")
|
|
23
|
-
return vertex
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def read_mesh_file_to_cells(reader_args: GenericReaderFilesHelper) -> np.ndarray:
|
|
27
|
-
extension = reader_args.format
|
|
28
|
-
|
|
29
|
-
if extension == SupportedFormats.CSV:
|
|
30
|
-
cells = mesh_csv_to_cells(
|
|
31
|
-
path_to_file=reader_args.file_or_buffer,
|
|
32
|
-
columns_map=reader_args.columns_map,
|
|
33
|
-
**reader_args.pandas_reader_kwargs
|
|
34
|
-
)
|
|
35
|
-
else:
|
|
36
|
-
raise ValueError(f"Subsurface is not able to read the following extension: {extension}")
|
|
37
|
-
return cells
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def read_mesh_file_to_attr(reader_args: GenericReaderFilesHelper):
|
|
41
|
-
extension = reader_args.format
|
|
42
|
-
if extension == SupportedFormats.CSV:
|
|
43
|
-
attr = mesh_csv_to_attributes(reader_args.file_or_buffer,
|
|
44
|
-
reader_args.columns_map,
|
|
45
|
-
**reader_args.pandas_reader_kwargs)
|
|
46
|
-
else:
|
|
47
|
-
raise ValueError(f"Subsurface is not able to read the following extension: {extension}")
|
|
48
|
-
return attr
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def cells_from_delaunay(vertex):
|
|
52
|
-
import pyvista as pv
|
|
53
|
-
a = pv.PolyData(vertex)
|
|
54
|
-
b = a.delaunay_2d().faces
|
|
55
|
-
cells = b.reshape(-1, 4)[:, 1:]
|
|
56
|
-
return cells
|
|
1
|
+
from .csv_mesh_reader import mesh_csv_to_vertex, mesh_csv_to_cells, mesh_csv_to_attributes
|
|
2
|
+
from .dxf_reader import dxf_from_file_to_vertex, dxf_from_stream_to_vertex, DXFEntityType
|
|
3
|
+
from subsurface.core.reader_helpers.readers_data import GenericReaderFilesHelper, SupportedFormats
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
__all__ = ['read_mesh_file_to_vertex', 'read_mesh_file_to_cells',
|
|
7
|
+
'read_mesh_file_to_attr', 'cells_from_delaunay']
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def read_mesh_file_to_vertex(reader_args: GenericReaderFilesHelper) -> np.ndarray:
|
|
11
|
+
if reader_args.format is SupportedFormats.CSV:
|
|
12
|
+
vertex = mesh_csv_to_vertex(reader_args.file_or_buffer, reader_args.columns_map,
|
|
13
|
+
**reader_args.pandas_reader_kwargs)
|
|
14
|
+
elif reader_args.format is SupportedFormats.DXF:
|
|
15
|
+
vertex = dxf_from_file_to_vertex(
|
|
16
|
+
file_path=reader_args.file_or_buffer,
|
|
17
|
+
entity_type=reader_args.additional_reader_kwargs.get('entity_type', DXFEntityType.ALL)
|
|
18
|
+
)
|
|
19
|
+
elif reader_args.format is SupportedFormats.DXFStream:
|
|
20
|
+
vertex = dxf_from_stream_to_vertex(reader_args.file_or_buffer)
|
|
21
|
+
else:
|
|
22
|
+
raise ValueError(f"Subsurface is not able to read the following extension: {reader_args.format}")
|
|
23
|
+
return vertex
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def read_mesh_file_to_cells(reader_args: GenericReaderFilesHelper) -> np.ndarray:
|
|
27
|
+
extension = reader_args.format
|
|
28
|
+
|
|
29
|
+
if extension == SupportedFormats.CSV:
|
|
30
|
+
cells = mesh_csv_to_cells(
|
|
31
|
+
path_to_file=reader_args.file_or_buffer,
|
|
32
|
+
columns_map=reader_args.columns_map,
|
|
33
|
+
**reader_args.pandas_reader_kwargs
|
|
34
|
+
)
|
|
35
|
+
else:
|
|
36
|
+
raise ValueError(f"Subsurface is not able to read the following extension: {extension}")
|
|
37
|
+
return cells
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def read_mesh_file_to_attr(reader_args: GenericReaderFilesHelper):
|
|
41
|
+
extension = reader_args.format
|
|
42
|
+
if extension == SupportedFormats.CSV:
|
|
43
|
+
attr = mesh_csv_to_attributes(reader_args.file_or_buffer,
|
|
44
|
+
reader_args.columns_map,
|
|
45
|
+
**reader_args.pandas_reader_kwargs)
|
|
46
|
+
else:
|
|
47
|
+
raise ValueError(f"Subsurface is not able to read the following extension: {extension}")
|
|
48
|
+
return attr
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def cells_from_delaunay(vertex):
|
|
52
|
+
import pyvista as pv
|
|
53
|
+
a = pv.PolyData(vertex)
|
|
54
|
+
b = a.delaunay_2d().faces
|
|
55
|
+
cells = b.reshape(-1, 4)[:, 1:]
|
|
56
|
+
return cells
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import pandas as pd
|
|
2
|
-
from typing import Union
|
|
3
|
-
|
|
4
|
-
import numpy as np
|
|
5
|
-
import warnings
|
|
6
|
-
|
|
7
|
-
from .surface_reader import read_mesh_file_to_vertex, read_mesh_file_to_cells, cells_from_delaunay, read_mesh_file_to_attr
|
|
8
|
-
from ....core.reader_helpers.reader_unstruct import ReaderUnstructuredHelper
|
|
9
|
-
from ....core.structs import UnstructuredData
|
|
10
|
-
|
|
11
|
-
from ....core.structs.base_structures.base_structures_enum import SpecialCellCase
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def read_2d_mesh_to_unstruct(
|
|
15
|
-
reader_args: ReaderUnstructuredHelper,
|
|
16
|
-
delaunay: bool = True
|
|
17
|
-
) -> UnstructuredData:
|
|
18
|
-
|
|
19
|
-
vertex: np.ndarray = read_mesh_file_to_vertex(reader_args.reader_vertex_args)
|
|
20
|
-
cells: Union[np.ndarray, SpecialCellCase]
|
|
21
|
-
cells_attr: Union[pd.DataFrame, None] = None
|
|
22
|
-
vertex_attr: Union[pd.DataFrame, None] = None
|
|
23
|
-
if reader_args.reader_cells_args is not None:
|
|
24
|
-
cells = read_mesh_file_to_cells(reader_args.reader_cells_args)
|
|
25
|
-
elif delaunay:
|
|
26
|
-
cells = cells_from_delaunay(vertex)
|
|
27
|
-
else:
|
|
28
|
-
warnings.warn("No arguments to compute cell")
|
|
29
|
-
cells = SpecialCellCase.POINTS
|
|
30
|
-
if reader_args.reader_cells_attr_args is not None:
|
|
31
|
-
cells_attr: pd.DataFrame = read_mesh_file_to_attr(reader_args.reader_cells_attr_args)
|
|
32
|
-
if reader_args.reader_vertex_attr_args is not None:
|
|
33
|
-
vertex_attr = read_mesh_file_to_attr(reader_args.reader_vertex_attr_args)
|
|
34
|
-
|
|
35
|
-
ud = UnstructuredData.from_array(
|
|
36
|
-
vertex=vertex,
|
|
37
|
-
cells=cells,
|
|
38
|
-
cells_attr=cells_attr,
|
|
39
|
-
vertex_attr=vertex_attr,
|
|
40
|
-
)
|
|
41
|
-
return ud
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import warnings
|
|
6
|
+
|
|
7
|
+
from .surface_reader import read_mesh_file_to_vertex, read_mesh_file_to_cells, cells_from_delaunay, read_mesh_file_to_attr
|
|
8
|
+
from ....core.reader_helpers.reader_unstruct import ReaderUnstructuredHelper
|
|
9
|
+
from ....core.structs import UnstructuredData
|
|
10
|
+
|
|
11
|
+
from ....core.structs.base_structures.base_structures_enum import SpecialCellCase
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def read_2d_mesh_to_unstruct(
|
|
15
|
+
reader_args: ReaderUnstructuredHelper,
|
|
16
|
+
delaunay: bool = True
|
|
17
|
+
) -> UnstructuredData:
|
|
18
|
+
|
|
19
|
+
vertex: np.ndarray = read_mesh_file_to_vertex(reader_args.reader_vertex_args)
|
|
20
|
+
cells: Union[np.ndarray, SpecialCellCase]
|
|
21
|
+
cells_attr: Union[pd.DataFrame, None] = None
|
|
22
|
+
vertex_attr: Union[pd.DataFrame, None] = None
|
|
23
|
+
if reader_args.reader_cells_args is not None:
|
|
24
|
+
cells = read_mesh_file_to_cells(reader_args.reader_cells_args)
|
|
25
|
+
elif delaunay:
|
|
26
|
+
cells = cells_from_delaunay(vertex)
|
|
27
|
+
else:
|
|
28
|
+
warnings.warn("No arguments to compute cell")
|
|
29
|
+
cells = SpecialCellCase.POINTS
|
|
30
|
+
if reader_args.reader_cells_attr_args is not None:
|
|
31
|
+
cells_attr: pd.DataFrame = read_mesh_file_to_attr(reader_args.reader_cells_attr_args)
|
|
32
|
+
if reader_args.reader_vertex_attr_args is not None:
|
|
33
|
+
vertex_attr = read_mesh_file_to_attr(reader_args.reader_vertex_attr_args)
|
|
34
|
+
|
|
35
|
+
ud = UnstructuredData.from_array(
|
|
36
|
+
vertex=vertex,
|
|
37
|
+
cells=cells,
|
|
38
|
+
cells_attr=cells_attr,
|
|
39
|
+
vertex_attr=vertex_attr,
|
|
40
|
+
)
|
|
41
|
+
return ud
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
@@ -1,197 +1,197 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
|
|
3
|
-
from typing import Union, List
|
|
4
|
-
|
|
5
|
-
import numpy as np
|
|
6
|
-
import pandas as pd
|
|
7
|
-
|
|
8
|
-
import subsurface
|
|
9
|
-
from subsurface import optional_requirements
|
|
10
|
-
from subsurface.core.structs.unstructured_elements import TriSurf
|
|
11
|
-
from ...visualization import to_pyvista_line, to_pyvista_mesh
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def create_vertical_mesh(coords: np.ndarray, zmin: float, zmax: float):
|
|
15
|
-
"""
|
|
16
|
-
Create a 'vertical curtain' triangular mesh by extruding the line of points
|
|
17
|
-
in coords from zmin to zmax.
|
|
18
|
-
|
|
19
|
-
Parameters
|
|
20
|
-
----------
|
|
21
|
-
coords : np.ndarray
|
|
22
|
-
An Nx2 array of (x, y) points along the profile.
|
|
23
|
-
zmin : float
|
|
24
|
-
The lower z-value (bottom of the curtain).
|
|
25
|
-
zmax : float
|
|
26
|
-
The upper z-value (top of the curtain).
|
|
27
|
-
|
|
28
|
-
Returns
|
|
29
|
-
-------
|
|
30
|
-
vertices : np.ndarray
|
|
31
|
-
An array of shape (2N, 3) with xyz-coordinates for all vertices.
|
|
32
|
-
faces : np.ndarray
|
|
33
|
-
An array of shape (2*(N-1), 3) with triangle indices into `vertices`.
|
|
34
|
-
Each row is one triangle [vi, vj, vk].
|
|
35
|
-
"""
|
|
36
|
-
n = len(coords)
|
|
37
|
-
if n < 2:
|
|
38
|
-
raise ValueError("Need at least 2 points to form a mesh.")
|
|
39
|
-
|
|
40
|
-
# Create 2 * n vertices
|
|
41
|
-
# “Bottom ring”: z = zmin
|
|
42
|
-
# “Top ring”: z = zmax
|
|
43
|
-
vertices = np.zeros((2 * n, 3), dtype=float)
|
|
44
|
-
vertices[:n, 0:2] = coords
|
|
45
|
-
vertices[:n, 2] = zmin
|
|
46
|
-
vertices[n:, 0:2] = coords
|
|
47
|
-
vertices[n:, 2] = zmax
|
|
48
|
-
|
|
49
|
-
# Build faces (2 triangles per segment along the line)
|
|
50
|
-
# Bottom ring indices: 0..(n-1)
|
|
51
|
-
# Top ring indices: n..(2n-1)
|
|
52
|
-
faces = []
|
|
53
|
-
for i in range(n - 1):
|
|
54
|
-
# Triangle 1: (bottom i, bottom i+1, top i)
|
|
55
|
-
faces.append([i, i + 1, i + n])
|
|
56
|
-
# Triangle 2: (top i, bottom i+1, top i+1)
|
|
57
|
-
faces.append([i + n, i + 1, i + 1 + n])
|
|
58
|
-
|
|
59
|
-
faces = np.array(faces, dtype=np.int64)
|
|
60
|
-
return vertices, faces
|
|
61
|
-
|
|
62
|
-
@dataclass
|
|
63
|
-
class TexturedMesh:
|
|
64
|
-
unstruct: subsurface.UnstructuredData
|
|
65
|
-
struct: subsurface.StructuredData
|
|
66
|
-
origin_vector3: np.array
|
|
67
|
-
point_u_vector3: np.array
|
|
68
|
-
point_v_vector3: np.array
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def create_mesh_from_trace(linestring, zmax: Union[float, int], zmin: Union[float, int]):
|
|
72
|
-
from scipy.spatial.qhull import Delaunay
|
|
73
|
-
if isinstance(linestring, list):
|
|
74
|
-
linestring_coords = linestring
|
|
75
|
-
else: # ! For now I leave it as this because I am not sure with object geopandas returns
|
|
76
|
-
linestring_coords = linestring.coords # * Geopandas branch
|
|
77
|
-
n = len(list(linestring_coords))
|
|
78
|
-
coords = np.array([[x[0] for x in list(linestring_coords)],
|
|
79
|
-
[y[1] for y in list(linestring_coords)]]).T
|
|
80
|
-
# duplicating the line, once with z=lower and another with z=upper values
|
|
81
|
-
vertices = np.zeros((2 * n, 3))
|
|
82
|
-
vertices[:n, :2] = coords
|
|
83
|
-
vertices[:n, 2] = zmin
|
|
84
|
-
vertices[n:, :2] = coords
|
|
85
|
-
vertices[n:, 2] = zmax
|
|
86
|
-
# i+n --- i+n+1
|
|
87
|
-
# |\ |
|
|
88
|
-
# | \ |
|
|
89
|
-
# | \ |
|
|
90
|
-
# | \ |
|
|
91
|
-
# i --- i+1
|
|
92
|
-
|
|
93
|
-
tri = Delaunay(vertices[:, [0, 2]])
|
|
94
|
-
faces = tri.simplices
|
|
95
|
-
return vertices, faces
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def create_tri_surf_from_traces_texture(
|
|
99
|
-
path_to_trace,
|
|
100
|
-
path_to_texture: Union[List[str]],
|
|
101
|
-
idx=None,
|
|
102
|
-
uv=None
|
|
103
|
-
):
|
|
104
|
-
tri_surf: list[subsurface.TriSurf] = _traces_texture_to_sub_structs(
|
|
105
|
-
path_to_trace=path_to_trace,
|
|
106
|
-
path_to_texture=path_to_texture,
|
|
107
|
-
idx=idx,
|
|
108
|
-
uv=uv
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
pyvista_mesh = [to_pyvista_mesh(i) for i in tri_surf]
|
|
112
|
-
|
|
113
|
-
return tri_surf, pyvista_mesh
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def lineset_from_trace(path_to_trace, idx=None):
|
|
117
|
-
import geopandas as gpd
|
|
118
|
-
|
|
119
|
-
traces = gpd.read_file(path_to_trace)
|
|
120
|
-
traces = _select_traces_by_index(idx, traces)
|
|
121
|
-
|
|
122
|
-
mesh_list = []
|
|
123
|
-
for index, row in traces.iterrows():
|
|
124
|
-
s = len(row['geometry'].coords.xy[0])
|
|
125
|
-
vertex = np.array((*row['geometry'].coords.xy,
|
|
126
|
-
np.zeros(s))).T
|
|
127
|
-
unstruct = subsurface.UnstructuredData.from_array(vertex, 'lines')
|
|
128
|
-
ls = subsurface.LineSet(unstruct)
|
|
129
|
-
mesh_list.append(to_pyvista_line(ls, radius=10))
|
|
130
|
-
|
|
131
|
-
return mesh_list
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def _traces_texture_to_sub_structs(path_to_trace, path_to_texture, idx, uv=None) -> list[TriSurf]:
|
|
135
|
-
gpd = optional_requirements.require_geopandas()
|
|
136
|
-
imageio = optional_requirements.require_imageio()
|
|
137
|
-
|
|
138
|
-
traces = gpd.read_file(path_to_trace)
|
|
139
|
-
traces = _select_traces_by_index(idx, traces)
|
|
140
|
-
|
|
141
|
-
textured_mesh: list[TriSurf] = []
|
|
142
|
-
n = 0
|
|
143
|
-
for index, row in traces.iterrows():
|
|
144
|
-
v, e = create_mesh_from_trace(row['geometry'], row['zmax'], row['zmin'])
|
|
145
|
-
unstruct_mesh = subsurface.UnstructuredData.from_array(
|
|
146
|
-
vertex=v,
|
|
147
|
-
cells=e,
|
|
148
|
-
vertex_attr=pd.DataFrame(np.zeros((v.shape[0], 2)), columns=['u', 'v'])
|
|
149
|
-
)
|
|
150
|
-
structured_data_texture = subsurface.StructuredData.from_numpy(
|
|
151
|
-
array=np.array(imageio.v3.imread(path_to_texture[index]))
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
tri_surf = TriSurf(
|
|
155
|
-
mesh=unstruct_mesh,
|
|
156
|
-
texture=structured_data_texture,
|
|
157
|
-
texture_origin=np.array([row['geometry'].xy[0][0], row['geometry'].xy[1][0], row['zmin']]),
|
|
158
|
-
texture_point_u=np.array([row['geometry'].xy[0][-1], row['geometry'].xy[1][-1], row['zmin']]),
|
|
159
|
-
texture_point_v=np.array([row['geometry'].xy[0][0], row['geometry'].xy[1][0], row['zmax']])
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
# * Set uv
|
|
163
|
-
if uv is not None:
|
|
164
|
-
uv_item = pd.DataFrame(uv[n], columns=['u', 'v'])
|
|
165
|
-
else:
|
|
166
|
-
uv_item = get_uv_from_pyvista(tri_surf)
|
|
167
|
-
tri_surf.mesh.points_attributes = uv_item
|
|
168
|
-
|
|
169
|
-
textured_mesh.append(tri_surf)
|
|
170
|
-
n += 1
|
|
171
|
-
|
|
172
|
-
return textured_mesh
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def get_uv_from_pyvista(tri_surf: TriSurf) -> pd.DataFrame:
|
|
176
|
-
pv = optional_requirements.require_pyvista()
|
|
177
|
-
_mesh = to_pyvista_mesh(tri_surf)
|
|
178
|
-
if tri_surf.texture is None:
|
|
179
|
-
raise ValueError('unstructured_element needs texture data to be mapped.')
|
|
180
|
-
_mesh.texture_map_to_plane(
|
|
181
|
-
inplace=True,
|
|
182
|
-
origin=tri_surf.texture_origin,
|
|
183
|
-
point_u=tri_surf.texture_point_u,
|
|
184
|
-
point_v=tri_surf.texture_point_v
|
|
185
|
-
)
|
|
186
|
-
tex = pv.numpy_to_texture(tri_surf.texture.values)
|
|
187
|
-
_mesh._textures = {0: tex}
|
|
188
|
-
from vtkmodules.util.numpy_support import vtk_to_numpy
|
|
189
|
-
uv = vtk_to_numpy(_mesh.GetPointData().GetTCoords())
|
|
190
|
-
|
|
191
|
-
return pd.DataFrame(uv, columns=['u', 'v'])
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
def _select_traces_by_index(idx, traces):
|
|
195
|
-
if idx is not None:
|
|
196
|
-
traces = traces.loc[idx]
|
|
197
|
-
return traces
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from typing import Union, List
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
import subsurface
|
|
9
|
+
from subsurface import optional_requirements
|
|
10
|
+
from subsurface.core.structs.unstructured_elements import TriSurf
|
|
11
|
+
from ...visualization import to_pyvista_line, to_pyvista_mesh
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create_vertical_mesh(coords: np.ndarray, zmin: float, zmax: float):
|
|
15
|
+
"""
|
|
16
|
+
Create a 'vertical curtain' triangular mesh by extruding the line of points
|
|
17
|
+
in coords from zmin to zmax.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
coords : np.ndarray
|
|
22
|
+
An Nx2 array of (x, y) points along the profile.
|
|
23
|
+
zmin : float
|
|
24
|
+
The lower z-value (bottom of the curtain).
|
|
25
|
+
zmax : float
|
|
26
|
+
The upper z-value (top of the curtain).
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
vertices : np.ndarray
|
|
31
|
+
An array of shape (2N, 3) with xyz-coordinates for all vertices.
|
|
32
|
+
faces : np.ndarray
|
|
33
|
+
An array of shape (2*(N-1), 3) with triangle indices into `vertices`.
|
|
34
|
+
Each row is one triangle [vi, vj, vk].
|
|
35
|
+
"""
|
|
36
|
+
n = len(coords)
|
|
37
|
+
if n < 2:
|
|
38
|
+
raise ValueError("Need at least 2 points to form a mesh.")
|
|
39
|
+
|
|
40
|
+
# Create 2 * n vertices
|
|
41
|
+
# “Bottom ring”: z = zmin
|
|
42
|
+
# “Top ring”: z = zmax
|
|
43
|
+
vertices = np.zeros((2 * n, 3), dtype=float)
|
|
44
|
+
vertices[:n, 0:2] = coords
|
|
45
|
+
vertices[:n, 2] = zmin
|
|
46
|
+
vertices[n:, 0:2] = coords
|
|
47
|
+
vertices[n:, 2] = zmax
|
|
48
|
+
|
|
49
|
+
# Build faces (2 triangles per segment along the line)
|
|
50
|
+
# Bottom ring indices: 0..(n-1)
|
|
51
|
+
# Top ring indices: n..(2n-1)
|
|
52
|
+
faces = []
|
|
53
|
+
for i in range(n - 1):
|
|
54
|
+
# Triangle 1: (bottom i, bottom i+1, top i)
|
|
55
|
+
faces.append([i, i + 1, i + n])
|
|
56
|
+
# Triangle 2: (top i, bottom i+1, top i+1)
|
|
57
|
+
faces.append([i + n, i + 1, i + 1 + n])
|
|
58
|
+
|
|
59
|
+
faces = np.array(faces, dtype=np.int64)
|
|
60
|
+
return vertices, faces
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class TexturedMesh:
|
|
64
|
+
unstruct: subsurface.UnstructuredData
|
|
65
|
+
struct: subsurface.StructuredData
|
|
66
|
+
origin_vector3: np.array
|
|
67
|
+
point_u_vector3: np.array
|
|
68
|
+
point_v_vector3: np.array
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def create_mesh_from_trace(linestring, zmax: Union[float, int], zmin: Union[float, int]):
|
|
72
|
+
from scipy.spatial.qhull import Delaunay
|
|
73
|
+
if isinstance(linestring, list):
|
|
74
|
+
linestring_coords = linestring
|
|
75
|
+
else: # ! For now I leave it as this because I am not sure with object geopandas returns
|
|
76
|
+
linestring_coords = linestring.coords # * Geopandas branch
|
|
77
|
+
n = len(list(linestring_coords))
|
|
78
|
+
coords = np.array([[x[0] for x in list(linestring_coords)],
|
|
79
|
+
[y[1] for y in list(linestring_coords)]]).T
|
|
80
|
+
# duplicating the line, once with z=lower and another with z=upper values
|
|
81
|
+
vertices = np.zeros((2 * n, 3))
|
|
82
|
+
vertices[:n, :2] = coords
|
|
83
|
+
vertices[:n, 2] = zmin
|
|
84
|
+
vertices[n:, :2] = coords
|
|
85
|
+
vertices[n:, 2] = zmax
|
|
86
|
+
# i+n --- i+n+1
|
|
87
|
+
# |\ |
|
|
88
|
+
# | \ |
|
|
89
|
+
# | \ |
|
|
90
|
+
# | \ |
|
|
91
|
+
# i --- i+1
|
|
92
|
+
|
|
93
|
+
tri = Delaunay(vertices[:, [0, 2]])
|
|
94
|
+
faces = tri.simplices
|
|
95
|
+
return vertices, faces
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def create_tri_surf_from_traces_texture(
|
|
99
|
+
path_to_trace,
|
|
100
|
+
path_to_texture: Union[List[str]],
|
|
101
|
+
idx=None,
|
|
102
|
+
uv=None
|
|
103
|
+
):
|
|
104
|
+
tri_surf: list[subsurface.TriSurf] = _traces_texture_to_sub_structs(
|
|
105
|
+
path_to_trace=path_to_trace,
|
|
106
|
+
path_to_texture=path_to_texture,
|
|
107
|
+
idx=idx,
|
|
108
|
+
uv=uv
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
pyvista_mesh = [to_pyvista_mesh(i) for i in tri_surf]
|
|
112
|
+
|
|
113
|
+
return tri_surf, pyvista_mesh
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def lineset_from_trace(path_to_trace, idx=None):
|
|
117
|
+
import geopandas as gpd
|
|
118
|
+
|
|
119
|
+
traces = gpd.read_file(path_to_trace)
|
|
120
|
+
traces = _select_traces_by_index(idx, traces)
|
|
121
|
+
|
|
122
|
+
mesh_list = []
|
|
123
|
+
for index, row in traces.iterrows():
|
|
124
|
+
s = len(row['geometry'].coords.xy[0])
|
|
125
|
+
vertex = np.array((*row['geometry'].coords.xy,
|
|
126
|
+
np.zeros(s))).T
|
|
127
|
+
unstruct = subsurface.UnstructuredData.from_array(vertex, 'lines')
|
|
128
|
+
ls = subsurface.LineSet(unstruct)
|
|
129
|
+
mesh_list.append(to_pyvista_line(ls, radius=10))
|
|
130
|
+
|
|
131
|
+
return mesh_list
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _traces_texture_to_sub_structs(path_to_trace, path_to_texture, idx, uv=None) -> list[TriSurf]:
|
|
135
|
+
gpd = optional_requirements.require_geopandas()
|
|
136
|
+
imageio = optional_requirements.require_imageio()
|
|
137
|
+
|
|
138
|
+
traces = gpd.read_file(path_to_trace)
|
|
139
|
+
traces = _select_traces_by_index(idx, traces)
|
|
140
|
+
|
|
141
|
+
textured_mesh: list[TriSurf] = []
|
|
142
|
+
n = 0
|
|
143
|
+
for index, row in traces.iterrows():
|
|
144
|
+
v, e = create_mesh_from_trace(row['geometry'], row['zmax'], row['zmin'])
|
|
145
|
+
unstruct_mesh = subsurface.UnstructuredData.from_array(
|
|
146
|
+
vertex=v,
|
|
147
|
+
cells=e,
|
|
148
|
+
vertex_attr=pd.DataFrame(np.zeros((v.shape[0], 2)), columns=['u', 'v'])
|
|
149
|
+
)
|
|
150
|
+
structured_data_texture = subsurface.StructuredData.from_numpy(
|
|
151
|
+
array=np.array(imageio.v3.imread(path_to_texture[index]))
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
tri_surf = TriSurf(
|
|
155
|
+
mesh=unstruct_mesh,
|
|
156
|
+
texture=structured_data_texture,
|
|
157
|
+
texture_origin=np.array([row['geometry'].xy[0][0], row['geometry'].xy[1][0], row['zmin']]),
|
|
158
|
+
texture_point_u=np.array([row['geometry'].xy[0][-1], row['geometry'].xy[1][-1], row['zmin']]),
|
|
159
|
+
texture_point_v=np.array([row['geometry'].xy[0][0], row['geometry'].xy[1][0], row['zmax']])
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# * Set uv
|
|
163
|
+
if uv is not None:
|
|
164
|
+
uv_item = pd.DataFrame(uv[n], columns=['u', 'v'])
|
|
165
|
+
else:
|
|
166
|
+
uv_item = get_uv_from_pyvista(tri_surf)
|
|
167
|
+
tri_surf.mesh.points_attributes = uv_item
|
|
168
|
+
|
|
169
|
+
textured_mesh.append(tri_surf)
|
|
170
|
+
n += 1
|
|
171
|
+
|
|
172
|
+
return textured_mesh
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def get_uv_from_pyvista(tri_surf: TriSurf) -> pd.DataFrame:
|
|
176
|
+
pv = optional_requirements.require_pyvista()
|
|
177
|
+
_mesh = to_pyvista_mesh(tri_surf)
|
|
178
|
+
if tri_surf.texture is None:
|
|
179
|
+
raise ValueError('unstructured_element needs texture data to be mapped.')
|
|
180
|
+
_mesh.texture_map_to_plane(
|
|
181
|
+
inplace=True,
|
|
182
|
+
origin=tri_surf.texture_origin,
|
|
183
|
+
point_u=tri_surf.texture_point_u,
|
|
184
|
+
point_v=tri_surf.texture_point_v
|
|
185
|
+
)
|
|
186
|
+
tex = pv.numpy_to_texture(tri_surf.texture.values)
|
|
187
|
+
_mesh._textures = {0: tex}
|
|
188
|
+
from vtkmodules.util.numpy_support import vtk_to_numpy
|
|
189
|
+
uv = vtk_to_numpy(_mesh.GetPointData().GetTCoords())
|
|
190
|
+
|
|
191
|
+
return pd.DataFrame(uv, columns=['u', 'v'])
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _select_traces_by_index(idx, traces):
|
|
195
|
+
if idx is not None:
|
|
196
|
+
traces = traces.loc[idx]
|
|
197
|
+
return traces
|