6
6
from gempy .core .data .grid_modules import RegularGrid
7
7
from gempy .optional_dependencies import require_gempy_viewer
8
8
9
+ from skimage import measure
10
+
9
11
PLOT = True
10
12
13
+ def marching_cubes (block , elements , spacing , extent ):
14
+ """
15
+ Extract the surface meshes using marching cubes
16
+ Args:
17
+ block (np.array): The block to extract the surface meshes from.
18
+ elements (list): IDs of unique structural elements in model
19
+ spacing (tuple): The spacing between grid points in the block.
20
+
21
+ Returns:
22
+ mc_vertices (list): Vertices of the surface meshes.
23
+ mc_edges (list): Edges of the surface meshes.
24
+ """
25
+
26
+ # Extract the surface meshes using marching cubes
27
+ mc_vertices = []
28
+ mc_edges = []
29
+ for i in range (0 , len (elements )):
30
+ verts , faces , _ , _ = measure .marching_cubes (block , i ,
31
+ spacing = spacing )
32
+ mc_vertices .append (verts + [extent [0 ], extent [2 ], extent [4 ]])
33
+ mc_edges .append (faces )
34
+ return mc_vertices , mc_edges
35
+
11
36
12
37
def test_marching_cubes_implementation ():
13
38
model = gp .generate_example_model (ExampleModel .COMBINATION , compute_model = False )
@@ -25,7 +50,7 @@ def test_marching_cubes_implementation():
25
50
reset = True
26
51
)
27
52
28
- model .interpolation_options .evaluation_options .mesh_extraction = False # * Not extracting the mesh with dual contouring
53
+ model .interpolation_options .evaluation_options .mesh_extraction = False # * Not extracting the mesh with dual contouring
29
54
gp .compute_model (model )
30
55
31
56
# Assert
@@ -35,19 +60,121 @@ def test_marching_cubes_implementation():
35
60
36
61
assert arrays .scalar_field_matrix .shape == (3 , 8_000 ) # * 3 surfaces, 8000 points
37
62
38
- # TODO: Code to extract the mesh with marching cubes
63
+ # TODO: Maybe to complicated because it includes accounting for faults, multiple elements in groups
64
+ # and transformation to real coordinates
65
+
66
+ # Empty lists to store vertices and edges
67
+ mc_vertices = []
68
+ mc_edges = []
69
+
70
+ # Boolean list of fault groups
71
+ faults = model .structural_frame .group_is_fault
72
+
73
+ # MC for faults, directly on fault block not on scalar field
74
+ if faults is not None :
75
+ # TODO: This should also use the scalar fields probably
76
+ for i in np .unique (model .solutions .raw_arrays .fault_block )[:- 1 ]:
77
+ fault_block = model .solutions .raw_arrays .fault_block .reshape (model .grid .regular_grid .resolution )
78
+ verts , faces , _ , _ = measure .marching_cubes (fault_block ,
79
+ i ,
80
+ spacing = (model .grid .regular_grid .dx ,
81
+ model .grid .regular_grid .dy ,
82
+ model .grid .regular_grid .dz ))
83
+ mc_vertices .append (verts + [model .grid .regular_grid .extent [0 ],
84
+ model .grid .regular_grid .extent [2 ],
85
+ model .grid .regular_grid .extent [4 ]])
86
+ mc_edges .append (faces )
87
+ else :
88
+ pass
89
+
90
+ # Extract scalar field values for elements
91
+ scalar_values = model .solutions .raw_arrays .scalar_field_at_surface_points
92
+
93
+ # Get indices of non fault elements
94
+ if faults is not None :
95
+ false_indices = [i for i , fault in enumerate (faults ) if not fault ]
96
+ else :
97
+ false_indices = np .arange (len (model .structural_frame .structural_groups ))
98
+
99
+ # Extract marching cubes for non fault elements
100
+ for idx in false_indices :
101
+
102
+ # Get correct scalar field for structural group
103
+ scalar_field = model .solutions .raw_arrays .scalar_field_matrix [idx ].reshape (model .grid .regular_grid .resolution )
104
+
105
+ # Extract marching cubes for each scalar value for all elements of a group
106
+ for i in range (len (scalar_values [idx ])):
107
+ verts , faces , _ , _ = measure .marching_cubes (scalar_field , scalar_values [idx ][i ],
108
+ spacing = (model .grid .regular_grid .dx ,
109
+ model .grid .regular_grid .dy ,
110
+ model .grid .regular_grid .dz ))
111
+
112
+ mc_vertices .append (verts + [model .grid .regular_grid .extent [0 ],
113
+ model .grid .regular_grid .extent [2 ],
114
+ model .grid .regular_grid .extent [4 ]])
115
+ mc_edges .append (faces )
116
+
117
+ # Reorder everything correctly if faults exist
118
+ # TODO: All of the following is just complicated code to reorder the elements to match the order of the elements
119
+ # in the structural frame, probably unnecessary in gempy strucuture
120
+ #
121
+ # if faults is not None:
122
+ #
123
+ # # TODO: This is a very convoluted way to get a boolean list of faults per element
124
+ # bool_list = np.zeros(4, dtype=bool)
125
+ # for i in range(len(model.structural_frame.structural_groups)):
126
+ # print(i)
127
+ # if model.structural_frame.group_is_fault[i]:
128
+ # for j in range(len(model.structural_frame.structural_groups[i].elements)):
129
+ # bool_list[i + j] = True
130
+ # if not model.structural_frame.group_is_fault[i]:
131
+ # for k in range(len(model.structural_frame.structural_groups[i].elements)):
132
+ # bool_list[i + k] = False
133
+ #
134
+ # true_count = sum(bool_list)
135
+ #
136
+ # # Split arr_list into two parts
137
+ # true_elements_vertices = mc_vertices[:true_count]
138
+ # false_elements_vertices = mc_vertices[true_count:]
139
+ # true_elements_edges = mc_edges[:true_count]
140
+ # false_elements_edges = mc_edges[true_count:]
141
+ #
142
+ # # Create a new list to store reordered elements
143
+ # mc_vertices = []
144
+ # mc_edges = []
145
+ #
146
+ # # Iterator for both true and false elements
147
+ # true_idx, false_idx = 0, 0
148
+ #
149
+ # # Populate reordered_list based on bool_list
150
+ # for is_true in bool_list:
151
+ # if is_true:
152
+ # mc_vertices.append(true_elements_vertices[true_idx] + [model.grid.regular_grid.extent[0],
153
+ # model.grid.regular_grid.extent[2],
154
+ # model.grid.regular_grid.extent[4]])
155
+ # mc_edges.append(true_elements_edges[true_idx])
156
+ # true_idx += 1
157
+ # else:
158
+ # mc_vertices.append(false_elements_vertices[false_idx] + [model.grid.regular_grid.extent[0],
159
+ # model.grid.regular_grid.extent[2],
160
+ # model.grid.regular_grid.extent[4]])
161
+ # mc_edges.append(false_elements_edges[false_idx])
162
+ # false_idx += 1
39
163
40
164
if PLOT :
41
165
gpv = require_gempy_viewer ()
42
- gtv : gpv .GemPyToVista = gpv .plot_3d (model , show_data = True , image = True )
166
+ # gtv: gpv.GemPyToVista = gpv.plot_3d(model, show_data=True, image=True)
43
167
import pyvista as pv
44
- pyvista_plotter : pv .Plotter = gtv .p
45
-
46
- foo = np .empty (0 ) # TODO: use the marching cubes algorithm to add mesh to the gempy plot
47
- # Add the mesh to the plot
48
- pyvista_plotter .add_mesh (
49
- foo ,
50
- show_edges = True ,
51
- color = 'white' ,
52
- opacity = 0.5 ,
53
- )
168
+ # pyvista_plotter: pv.Plotter = gtv.p
169
+
170
+ # TODO: This opens interactive window as of now
171
+ pyvista_plotter = pv .Plotter ()
172
+
173
+ # Add the meshes to the plot
174
+ for i in range (len (mc_vertices )):
175
+ pyvista_plotter .add_mesh (
176
+ pv .PolyData (mc_vertices [i ],
177
+ np .insert (mc_edges [i ], 0 , 3 , axis = 1 ).ravel ()),
178
+ color = 'blue' )
179
+
180
+ pyvista_plotter .show ()
0 commit comments