wolfhece 2.0.14__py3-none-any.whl → 2.0.16__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.
- wolfhece/PyDraw.py +9 -9
- wolfhece/Results2DGPU.py +5 -1
- wolfhece/opengl/__init__.py +0 -0
- wolfhece/opengl/gl_utils.py +1544 -0
- wolfhece/opengl/py3d.py +1665 -0
- wolfhece/opengl/tile_packer.py +352 -0
- wolfhece/shaders/quad_frag_shader.glsl +6 -2
- wolfhece/shaders/quad_geom_shader.glsl +8 -3
- wolfhece/shaders/quadpos_frag_shader.glsl +12 -0
- wolfhece/shaders/quadpos_geom_shader.glsl +76 -0
- wolfhece/shaders/simple_fragment_shader.glsl +4 -1
- wolfhece/wolf_array.py +9 -5
- wolfhece/wolfresults_2D.py +14 -12
- {wolfhece-2.0.14.dist-info → wolfhece-2.0.16.dist-info}/METADATA +3 -1
- {wolfhece-2.0.14.dist-info → wolfhece-2.0.16.dist-info}/RECORD +18 -12
- {wolfhece-2.0.14.dist-info → wolfhece-2.0.16.dist-info}/WHEEL +0 -0
- {wolfhece-2.0.14.dist-info → wolfhece-2.0.16.dist-info}/entry_points.txt +0 -0
- {wolfhece-2.0.14.dist-info → wolfhece-2.0.16.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,352 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
import logging
|
3
|
+
from math import sqrt, ceil
|
4
|
+
import numpy as np
|
5
|
+
|
6
|
+
class TilePackingMode(Enum):
|
7
|
+
REGULAR = 1
|
8
|
+
#SHUFFLED = 2
|
9
|
+
TRANSPARENT = 3 # For debugging: set it to disable the tile packing.
|
10
|
+
|
11
|
+
class TilePacker:
|
12
|
+
"""
|
13
|
+
A class that packs an array in tiles and provides an indirection map
|
14
|
+
|
15
|
+
After the initialization phase based on the "NAP" array, the class provides two methods:
|
16
|
+
- shuffle_and_pack_array
|
17
|
+
- unpack_and_deshuffle_array
|
18
|
+
"""
|
19
|
+
|
20
|
+
|
21
|
+
def __init__(self, nap: np.ndarray, tile_size: int, mode: TilePackingMode = TilePackingMode.REGULAR):
|
22
|
+
"""
|
23
|
+
Initialize the tile indirection system.
|
24
|
+
|
25
|
+
The motivation is that usually, due to the shape of our rivers, the computation
|
26
|
+
domain (a matrix) is mostly empty. Therefore we don't use the memory very
|
27
|
+
wisely if we keep a plain rectangular domain to store a riverbed.
|
28
|
+
|
29
|
+
What we do is:
|
30
|
+
- cut the computation domain in tiles. A tile is either active or inactive.
|
31
|
+
An active tile is one that contains meshes which are marke active in the NAP map.
|
32
|
+
- translate all the active tiles so that they are close together ("packing")
|
33
|
+
|
34
|
+
Of course, once the tiles are packed, the whole hydro computation must still continue
|
35
|
+
to work. So each time the hydro computation looks for a mesh at (i,j), we must
|
36
|
+
translate those coordinates to the "packed" domain. So in this function we also
|
37
|
+
compute an "indirection" map that tells us where a (t_i, t_j) tile falls in the
|
38
|
+
packed domain.
|
39
|
+
|
40
|
+
nap: the NAP array.
|
41
|
+
"""
|
42
|
+
self._tile_size = tile_size
|
43
|
+
self._mode = mode
|
44
|
+
self._height, self._width = nap.shape
|
45
|
+
|
46
|
+
# Save the NAP for reuse later on (in our case, saving it to disk in the ResultStore)
|
47
|
+
# ATTENTION don't build upon it because it eats a lot of memory. It's just here
|
48
|
+
# as a cheap placeholder.
|
49
|
+
# FIXME Later on one should store it directly from the sim to the disk
|
50
|
+
self._original_nap = nap
|
51
|
+
|
52
|
+
nap = self._pad_array_to_tiles(nap)
|
53
|
+
|
54
|
+
nb_tiles_x, nb_tiles_y = self.size_in_tiles()
|
55
|
+
|
56
|
+
# Here we cut the original NAP array into tiles.
|
57
|
+
# (I(ve tried to do it with reshape, but it seems to not being able to do the
|
58
|
+
# job. E.g. cut a 4x4 array in 2x2 tiles: np.arange(16).reshape(4,4).T.reshape(2,2,2,2) fail...)
|
59
|
+
# In the end we get a (nb tiles x, nb tiles y) array of which
|
60
|
+
# each element is a (_tile_size, _tile_size) tile array.
|
61
|
+
# To understand the stride trick, do this:
|
62
|
+
# - read the shape parameter as the shape you *want*.
|
63
|
+
# - read the strides as strides over the flattened array. The first
|
64
|
+
# two strides are to choose the tile. The other two strides are
|
65
|
+
# to enumerate the elements inside a tile.
|
66
|
+
|
67
|
+
TS = self._tile_size
|
68
|
+
|
69
|
+
# # ATTENTION stride_tricks comes with tons of gotcha's !
|
70
|
+
# # array item size (in bytes)
|
71
|
+
# ais = nap.dtype.itemsize
|
72
|
+
# tiled = np.lib.stride_tricks.as_strided(
|
73
|
+
# np.ascontiguousarray(nap), # This one is really necessary (see warning in side_tricks documentation)
|
74
|
+
# shape=((nap.shape[0]//TS,nap.shape[1]//TS,TS,TS)),
|
75
|
+
# strides=(nap.shape[1]*TS*ais, TS*ais, # choose tile
|
76
|
+
# nap.shape[1]*ais, 1*ais), # enumerate meshes inside tile
|
77
|
+
# writeable=False)
|
78
|
+
# # Determine if a tile has some active meshes inside it
|
79
|
+
# # We do that by summing the NAP values inside each tile.
|
80
|
+
# tiles_sums = np.sum(tiled, axis=(2,3))
|
81
|
+
|
82
|
+
# Replacing the above lines by more numpy-ish code.
|
83
|
+
tiles_sums = np.sum(nap.reshape(nap.shape[0]//TS,TS,nap.shape[1]//TS,TS).swapaxes(1,2), axis=(2,3))
|
84
|
+
|
85
|
+
active_tiles = np.zeros_like(tiles_sums, np.uint32)
|
86
|
+
|
87
|
+
assert active_tiles.shape == (nb_tiles_y,nb_tiles_x), f"{active_tiles.shape} ?"
|
88
|
+
|
89
|
+
active_tiles[tiles_sums > 0] = 1
|
90
|
+
|
91
|
+
# Numbering active cells (0 == inactive, >= 1 == active)
|
92
|
+
|
93
|
+
# Note that we have an edge case.
|
94
|
+
|
95
|
+
# This case occurs when we compute meshes located on the border of a
|
96
|
+
# tile of which the neighbouring border tile (call it n_t) is not
|
97
|
+
# active. When we compute that mesh, we usually have a look at its
|
98
|
+
# neighbours. In this edge case, one of the neighbours in question (call
|
99
|
+
# it n_m) will be inside the neighbouring tile (n_t) which is inactive.
|
100
|
+
# Since this tile is inactive, it will not be part of the "packed tiles"
|
101
|
+
# and thus has no representation whatsoever. Therefore, the values
|
102
|
+
# h,qx,qy,bathy,... of n_m ill be unknown.
|
103
|
+
|
104
|
+
# On a regular (non packed) computation domain it's not a problem
|
105
|
+
# because we put default/neutral values (such as water height == 0) in
|
106
|
+
# inactive meshes. So when we look outside the domain, we get these safe
|
107
|
+
# values.
|
108
|
+
|
109
|
+
# Once tiles are packed, as we have seen, we may reach out to a not
|
110
|
+
# existing, or worse, random tile. To avoid that, we choose to have an
|
111
|
+
# "emtpy" tile and number it zero. Any lookup outside the regular active
|
112
|
+
# tiles will fall on the empty tile. Adding a tile imply that if we have
|
113
|
+
# N active tiles in the computation domain we'll store N+1 tiles. So we
|
114
|
+
# have to make enough room to allow that.
|
115
|
+
|
116
|
+
self._active_tiles_ndx = np.nonzero(active_tiles) # Only indices (don't multiply by tile_size)
|
117
|
+
self._nb_active_tiles = len(self._active_tiles_ndx[0])
|
118
|
+
active_tiles[self._active_tiles_ndx] = np.arange(1, self._nb_active_tiles+1) #+1 for the "empy" tile
|
119
|
+
|
120
|
+
# Now transforming the numbers of the numbered cells in coordinates
|
121
|
+
|
122
|
+
# Indir will map a tile index (ty,tx) into its indirected x (indir[ty,tx,0]) and y ([ty,tx,1])
|
123
|
+
# coordinates
|
124
|
+
# Note : dtype can not be changed because it's used in the OpenGL shader code or you have to modify the shader code too.
|
125
|
+
indir = np.zeros( (nb_tiles_y,nb_tiles_x,2), dtype=np.uint16)
|
126
|
+
|
127
|
+
used_tiles = self._nb_active_tiles + 1
|
128
|
+
# pack tiles in a square or almost in a square
|
129
|
+
self._packed_nb_tiles_x = int(sqrt(used_tiles))
|
130
|
+
self._packed_nb_tiles_y = int(ceil(used_tiles / self._packed_nb_tiles_x))
|
131
|
+
indir[:,:,0] = active_tiles % self._packed_nb_tiles_x
|
132
|
+
indir[:,:,1] = active_tiles // self._packed_nb_tiles_x
|
133
|
+
|
134
|
+
# Convert to mesh position (this spares a multiplication
|
135
|
+
# in the shader code)
|
136
|
+
indir *= self._tile_size
|
137
|
+
|
138
|
+
# When shuffling arrays, it should be faster to read directly
|
139
|
+
# the active cells.
|
140
|
+
|
141
|
+
if self._mode == TilePackingMode.TRANSPARENT:
|
142
|
+
# For debugging purpose: create some "harmless" indirection maps.
|
143
|
+
# This is basically a transparent map
|
144
|
+
nb_tiles_x, nb_tiles_y = self.size_in_tiles()
|
145
|
+
self._packed_nb_tiles_x, self._packed_nb_tiles_y = nb_tiles_x, nb_tiles_y
|
146
|
+
|
147
|
+
xs = np.repeat( np.atleast_2d( np.arange(nb_tiles_x)), nb_tiles_y, axis=0)
|
148
|
+
ys = np.repeat( np.atleast_2d( np.arange(nb_tiles_y)), nb_tiles_x, axis=0).T
|
149
|
+
indir = np.zeros( (nb_tiles_y,nb_tiles_x,2), dtype=np.uint16)
|
150
|
+
indir[:,:,0] = xs
|
151
|
+
indir[:,:,1] = ys
|
152
|
+
indir *= self._tile_size
|
153
|
+
|
154
|
+
self._active_tiles_ndx = None
|
155
|
+
self._nb_active_tiles = None
|
156
|
+
|
157
|
+
#indir = np.roll(indir, shift=nbx//4, axis = 1)
|
158
|
+
#indir = np.roll(indir, shift=nby//4, axis = 0)
|
159
|
+
|
160
|
+
#print(indir)
|
161
|
+
self._tile_indirection_map = indir
|
162
|
+
|
163
|
+
# FIXME : why not a property ?
|
164
|
+
def tile_indirection_map(self):
|
165
|
+
return self._tile_indirection_map
|
166
|
+
|
167
|
+
# FIXME : why not a property ?
|
168
|
+
def mode(self) -> TilePackingMode:
|
169
|
+
return self._mode
|
170
|
+
|
171
|
+
# FIXME : why not a property ?
|
172
|
+
def packed_size(self):
|
173
|
+
""" Size of the arrays after padding them and packing them in tiles,
|
174
|
+
expressed in meshes. Size is a (width, height) tuple.
|
175
|
+
|
176
|
+
Note that this size can be very different than the actual computation
|
177
|
+
domain size.
|
178
|
+
"""
|
179
|
+
if self._mode != TilePackingMode.TRANSPARENT:
|
180
|
+
return (self._packed_nb_tiles_x * self._tile_size,
|
181
|
+
self._packed_nb_tiles_y * self._tile_size)
|
182
|
+
else:
|
183
|
+
return (self._width, self._height)
|
184
|
+
|
185
|
+
# FIXME : why not a property ?
|
186
|
+
def packed_size_in_tiles(self):
|
187
|
+
""" Size of the arrays after padding them and packing them in tiles,
|
188
|
+
expressed in tiles. Size is a (width, height) tuple.
|
189
|
+
|
190
|
+
Note that this size can be very different than the actual computation
|
191
|
+
domain size.
|
192
|
+
"""
|
193
|
+
return (self._packed_nb_tiles_x,
|
194
|
+
self._packed_nb_tiles_y)
|
195
|
+
|
196
|
+
# FIXME : why not a property ?
|
197
|
+
def size_in_tiles(self):
|
198
|
+
"""
|
199
|
+
Size of the (original, non packed, non tiled) computation domain, in
|
200
|
+
tiles. Not that we count full tiles. So if one dimension of the domain
|
201
|
+
is not a multiple of the tile size, then we round one tile up.
|
202
|
+
|
203
|
+
Size is a (width, height) tuple.
|
204
|
+
"""
|
205
|
+
return ((self._width +self._tile_size-1) // self._tile_size,
|
206
|
+
(self._height+self._tile_size-1) // self._tile_size)
|
207
|
+
|
208
|
+
# FIXME : why not a property ?
|
209
|
+
def tile_size(self) -> int:
|
210
|
+
""" The tile size. Note that tiles are squared.
|
211
|
+
"""
|
212
|
+
return self._tile_size
|
213
|
+
|
214
|
+
# FIXME : why not a property ?
|
215
|
+
def active_tiles_ndx(self):
|
216
|
+
return self._active_tiles_ndx
|
217
|
+
|
218
|
+
def unpack_and_deshuffle_array(self, a: np.ndarray) -> np.ndarray:
|
219
|
+
""" De-shuffle and un-pad an array that was shuffled and padded.
|
220
|
+
"""
|
221
|
+
psw, psh = self.packed_size()
|
222
|
+
assert a.shape[0] == psh and a.shape[1] == psw, \
|
223
|
+
f"Seems like the array you gave is not shuffled/padded. Its shape is {a.shape}. " \
|
224
|
+
f"I was expecting something with a shape like ({psh},{psw},...)"
|
225
|
+
|
226
|
+
if self._mode == TilePackingMode.TRANSPARENT:
|
227
|
+
return a
|
228
|
+
|
229
|
+
s = list(a.shape)
|
230
|
+
s[0], s[1] = self._height, self._width
|
231
|
+
r = np.zeros( tuple(s), dtype=a.dtype )
|
232
|
+
|
233
|
+
for tile_pos in zip(self._active_tiles_ndx[0], self._active_tiles_ndx[1]):
|
234
|
+
|
235
|
+
j, i = tile_pos
|
236
|
+
|
237
|
+
if (i+1)*self._tile_size > self._width:
|
238
|
+
tw = self._tile_size - ((i+1)*self._tile_size - self._width)
|
239
|
+
else:
|
240
|
+
tw = self._tile_size
|
241
|
+
|
242
|
+
if (j+1)*self._tile_size > self._height:
|
243
|
+
th = self._tile_size - ((j+1)*self._tile_size - self._height)
|
244
|
+
else:
|
245
|
+
th = self._tile_size
|
246
|
+
|
247
|
+
dest_slice_i = slice(i*self._tile_size, i*self._tile_size + tw)
|
248
|
+
dest_slice_j = slice(j*self._tile_size, j*self._tile_size + th)
|
249
|
+
|
250
|
+
tc = self._tile_indirection_map[j,i,:]
|
251
|
+
source_slice_i = slice(tc[0], tc[0]+tw)
|
252
|
+
source_slice_j = slice(tc[1], tc[1]+th)
|
253
|
+
|
254
|
+
r[dest_slice_j, dest_slice_i, ...] = a[source_slice_j, source_slice_i, ...]
|
255
|
+
|
256
|
+
return r
|
257
|
+
|
258
|
+
|
259
|
+
def _unpad_array(self, a: np.ndarray) -> np.ndarray:
|
260
|
+
""" Undo `_pad_array_to_tiles`.
|
261
|
+
"""
|
262
|
+
ntx, nty = self.size_in_tiles()
|
263
|
+
assert a.shape[0] == nty*self._tile_size, "Seems like the array you gave is not padded"
|
264
|
+
assert a.shape[1] == ntx*self._tile_size, "Seems like the array you gave is not padded"
|
265
|
+
return a[0:self._height, 0:self._width]
|
266
|
+
|
267
|
+
|
268
|
+
def _pad_array_to_tiles(self, a: np.ndarray) -> np.ndarray:
|
269
|
+
""" Make an array fit in a given number of tiles (on x and y axis).
|
270
|
+
After this, the array's dimensions are multiple of the tile_size.
|
271
|
+
"""
|
272
|
+
|
273
|
+
assert a.shape[0] == self._height, "The array seems to have nothing to do with a computation domain"
|
274
|
+
assert a.shape[1] == self._width, "The array seems to have nothing to do with a computation domain"
|
275
|
+
ntx, nty = self.size_in_tiles()
|
276
|
+
mesh_to_add_on_y = nty*self._tile_size - a.shape[0]
|
277
|
+
mesh_to_add_on_x = ntx*self._tile_size - a.shape[1]
|
278
|
+
assert mesh_to_add_on_y >= 0, "Your array is too tall (or there's something wrong in the tiles)"
|
279
|
+
assert mesh_to_add_on_x >= 0, "Your array is too wide (or there's something wrong in the tiles)"
|
280
|
+
|
281
|
+
if len(a.shape) == 3:
|
282
|
+
return np.pad(a, [(0,mesh_to_add_on_y), (0,mesh_to_add_on_x), (0,0)])
|
283
|
+
elif len(a.shape) == 2:
|
284
|
+
return np.pad(a, [(0,mesh_to_add_on_y), (0,mesh_to_add_on_x)])
|
285
|
+
else:
|
286
|
+
raise Exception(f"Array shape {a.shape} is not not supported")
|
287
|
+
|
288
|
+
|
289
|
+
def shuffle_and_pack_array(self, a: np.ndarray, neutral_values = None) -> np.ndarray:
|
290
|
+
""" Reorganize an array by moving tiles around to
|
291
|
+
follow the ordering given by `self._tile_indirection_map`
|
292
|
+
The array is resized in order to be just as large as
|
293
|
+
needed to hold the active tiles plus the "empty" tile.
|
294
|
+
|
295
|
+
`neutral_values`: value to fill the empty tile with.
|
296
|
+
"""
|
297
|
+
|
298
|
+
if self._mode == TilePackingMode.TRANSPARENT:
|
299
|
+
return a
|
300
|
+
|
301
|
+
logging.debug(f"Packing {a.shape}")
|
302
|
+
|
303
|
+
# Padding to avoid tricky computations over "incomplete" tiles.
|
304
|
+
a = self._pad_array_to_tiles(a)
|
305
|
+
|
306
|
+
packed_shape = list(a.shape) # Preserve the third dimension, if any.
|
307
|
+
packed_shape[0] = self._packed_nb_tiles_y * self._tile_size
|
308
|
+
packed_shape[1] = self._packed_nb_tiles_x * self._tile_size
|
309
|
+
|
310
|
+
# Clearing non used tiles because they're acutally use while computing
|
311
|
+
# max step size ('cos that computation doesn't use the indirection mechanism)
|
312
|
+
|
313
|
+
# FIXME We clear too much, only the last row of tiles and the "neutral" tile
|
314
|
+
# should be cleared.
|
315
|
+
|
316
|
+
# The array containing the active tiles, packed.
|
317
|
+
if neutral_values is None:
|
318
|
+
packed_tiles = np.zeros(tuple(packed_shape), dtype=a.dtype)
|
319
|
+
else:
|
320
|
+
packed_tiles = np.empty(tuple(packed_shape), dtype=a.dtype)
|
321
|
+
packed_tiles[:,:,...] = neutral_values
|
322
|
+
|
323
|
+
# There's the 0-th tile which is meant to be neutral. So we clear it
|
324
|
+
# because 0 is mostly neutral (it depends on what `a` (the input array)
|
325
|
+
# represents. If it's h,qx,qy then it's neutral, but for bathymetry it
|
326
|
+
# may be different.
|
327
|
+
|
328
|
+
# FIXME For the moment, I believe that if h,qx,qy then, the mesh is
|
329
|
+
# neutral, regardless of the other params such as bathymetry.
|
330
|
+
|
331
|
+
if neutral_values is None:
|
332
|
+
packed_tiles[0:self._tile_size, 0:self._tile_size, ...] = 0
|
333
|
+
else:
|
334
|
+
packed_tiles[0:self._tile_size, 0:self._tile_size, ...] = neutral_values
|
335
|
+
|
336
|
+
# Go over all NAP-active tiles and pack each of them.
|
337
|
+
for tile_coord in zip(self._active_tiles_ndx[0], self._active_tiles_ndx[1]):
|
338
|
+
|
339
|
+
j,i = tile_coord
|
340
|
+
source_i = slice(i*self._tile_size, (i+1)*self._tile_size)
|
341
|
+
source_j = slice(j*self._tile_size, (j+1)*self._tile_size)
|
342
|
+
|
343
|
+
# Remember that the active tiles are numbered 1-based. The 0-th tile
|
344
|
+
# is the "neutral value" tile (used to represent out of domain, neutral data)
|
345
|
+
tc = self._tile_indirection_map[j,i,:]
|
346
|
+
dest_i = slice(tc[0], tc[0]+self._tile_size)
|
347
|
+
dest_j = slice(tc[1], tc[1]+self._tile_size)
|
348
|
+
|
349
|
+
#logging.trace(f"{a.shape} -> {packed_shape}: {dest_i}, {dest_j}")
|
350
|
+
packed_tiles[dest_j, dest_i, ...] = a[source_j, source_i, ...]
|
351
|
+
|
352
|
+
return packed_tiles
|
@@ -6,8 +6,10 @@ uniform float sunIntensity;
|
|
6
6
|
in vec4 gl_FragCoord ;
|
7
7
|
in vec3 Normal;
|
8
8
|
in vec3 ourColor;
|
9
|
+
in vec3 ourCoord;
|
9
10
|
|
10
|
-
out vec4
|
11
|
+
layout(location=0) out vec4 FragColor;
|
12
|
+
layout(location=1) out vec4 CoordColor;
|
11
13
|
|
12
14
|
void main() {
|
13
15
|
// Calculate the direction from the fragment position to the sun position
|
@@ -26,5 +28,7 @@ void main() {
|
|
26
28
|
// Apply the diffuse and specular intensities to the fragment color
|
27
29
|
vec3 diffuseColor = lightIntensity * ourColor.rgb;
|
28
30
|
vec3 specularColor = specularIntensity * vec3(1.0, 1.0, 1.0);
|
29
|
-
|
31
|
+
FragColor = vec4(diffuseColor + specularColor, 1.0);
|
32
|
+
CoordColor = vec4(ourCoord, 1.);
|
33
|
+
|
30
34
|
}
|
@@ -9,7 +9,10 @@ uniform float dx;
|
|
9
9
|
uniform float dy;
|
10
10
|
uniform float origx;
|
11
11
|
uniform float origy;
|
12
|
+
uniform float width;
|
13
|
+
uniform float height;
|
12
14
|
uniform mat4 mvp;
|
15
|
+
uniform int idx;
|
13
16
|
|
14
17
|
uniform sampler1D colorPalette; // Texture de gradient représentant la palette de couleurs
|
15
18
|
|
@@ -19,6 +22,7 @@ uniform float colorValues[256]; // Tableau de couleurs de la palette
|
|
19
22
|
out vec3 ourColor;
|
20
23
|
out vec3 Normal; // Normale du fragment dans l'espace du monde
|
21
24
|
out vec4 FragPos; // Position du fragment dans l'espace du monde
|
25
|
+
out vec3 ourCoord;
|
22
26
|
|
23
27
|
vec3 mapColor(float zValue) {
|
24
28
|
float zColor = 0.0;
|
@@ -63,14 +67,15 @@ void horizontal(){
|
|
63
67
|
float halfdx = dx/2.0;
|
64
68
|
float halfdy = dy/2.0;
|
65
69
|
|
66
|
-
// vec2 texCoord = vec2(((gl_in[0].gl_Position.x - origx) / dx ), ((gl_in[0].gl_Position.y - origy) / dy));
|
67
|
-
// float zValue = texture(zText, texCoord).r * zScale;
|
68
|
-
|
69
70
|
float zValue = get_value(zText, gl_in[0].gl_Position.xy);
|
70
71
|
float zColor = 0.0;
|
71
72
|
|
72
73
|
vec3 color = mapColor(zValue);
|
73
74
|
|
75
|
+
float posx = (gl_in[0].gl_Position.x - origx) / dx ;// /width;
|
76
|
+
float posy = (gl_in[0].gl_Position.y - origy) / dy ;// /height;
|
77
|
+
ourCoord = vec3(posx, posy, idx);
|
78
|
+
|
74
79
|
gl_Position = mvp * vec4(gl_in[0].gl_Position.x - halfdx, gl_in[0].gl_Position.y - halfdy, zValue, 1.0);
|
75
80
|
ourColor = color;
|
76
81
|
Normal = vec3(0.0, 0.0, 1.0);
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#version 460 core
|
2
|
+
|
3
|
+
layout (points) in;
|
4
|
+
layout (triangle_strip, max_vertices = 20) out;
|
5
|
+
|
6
|
+
uniform sampler2DRect zText;
|
7
|
+
uniform float zScale;
|
8
|
+
uniform float dx;
|
9
|
+
uniform float dy;
|
10
|
+
uniform float origx;
|
11
|
+
uniform float origy;
|
12
|
+
uniform mat4 mvp;
|
13
|
+
|
14
|
+
uniform sampler1D colorPalette; // Texture de gradient représentant la palette de couleurs
|
15
|
+
|
16
|
+
uniform int paletteSize; // Taille de la palette
|
17
|
+
uniform float colorValues[256]; // Tableau de couleurs de la palette
|
18
|
+
|
19
|
+
out vec3 ourColor;
|
20
|
+
out vec4 FragPos; // Position du fragment dans l'espace du monde
|
21
|
+
|
22
|
+
|
23
|
+
float get_value(sampler2DRect tex, vec2 pos) {
|
24
|
+
|
25
|
+
float posx = (pos.x - origx) / dx;
|
26
|
+
float posy = (pos.y - origy) / dy;
|
27
|
+
|
28
|
+
vec2 texCoord = vec2(posx, posy);
|
29
|
+
|
30
|
+
float zValue = texture(tex, texCoord).r * zScale;
|
31
|
+
return zValue;
|
32
|
+
}
|
33
|
+
|
34
|
+
void horizontal(){
|
35
|
+
|
36
|
+
float halfdx = dx/2.0;
|
37
|
+
float halfdy = dy/2.0;
|
38
|
+
|
39
|
+
float zValue = get_value(zText, gl_in[0].gl_Position.xy);
|
40
|
+
float zColor = 0.0;
|
41
|
+
|
42
|
+
// float posx = (gl_in[0].gl_Position.x - origx) / dx;
|
43
|
+
// float posy = (gl_in[0].gl_Position.y - origy) / dy;
|
44
|
+
float posx = 1.;
|
45
|
+
float posy = 1.;
|
46
|
+
|
47
|
+
gl_Position = mvp * vec4(gl_in[0].gl_Position.x - halfdx, gl_in[0].gl_Position.y - halfdy, zValue, 1.0);
|
48
|
+
ourColor = vec3(1.,0.,0.);
|
49
|
+
FragPos = vec4(gl_in[0].gl_Position.x - halfdx, gl_in[0].gl_Position.y - halfdy, zValue, 1.0);
|
50
|
+
EmitVertex();
|
51
|
+
|
52
|
+
gl_Position = mvp * vec4(gl_in[0].gl_Position.x + halfdx, gl_in[0].gl_Position.y - halfdy, zValue, 1.0);
|
53
|
+
ourColor = vec3(0.,1.,0.);
|
54
|
+
FragPos = vec4(gl_in[0].gl_Position.x + halfdx, gl_in[0].gl_Position.y - halfdy, zValue, 1.0);
|
55
|
+
EmitVertex();
|
56
|
+
|
57
|
+
gl_Position = mvp * vec4(gl_in[0].gl_Position.x - halfdx, gl_in[0].gl_Position.y + halfdy, zValue, 1.0);
|
58
|
+
ourColor = vec3(0.,0.,1.);
|
59
|
+
FragPos = vec4(gl_in[0].gl_Position.x - halfdx, gl_in[0].gl_Position.y + halfdy, zValue, 1.0);
|
60
|
+
EmitVertex();
|
61
|
+
|
62
|
+
gl_Position = mvp * vec4(gl_in[0].gl_Position.x + halfdx, gl_in[0].gl_Position.y + halfdy, zValue, 1.0);
|
63
|
+
ourColor = vec3(1.,0.,0.);
|
64
|
+
FragPos = vec4(gl_in[0].gl_Position.x + halfdx, gl_in[0].gl_Position.y + halfdy, zValue, 1.0);
|
65
|
+
EmitVertex();
|
66
|
+
|
67
|
+
EndPrimitive();
|
68
|
+
|
69
|
+
}
|
70
|
+
|
71
|
+
|
72
|
+
void main() {
|
73
|
+
|
74
|
+
horizontal();
|
75
|
+
|
76
|
+
}
|
wolfhece/wolf_array.py
CHANGED
@@ -3552,17 +3552,20 @@ class WolfArray(Element_To_Draw, header_wolf):
|
|
3552
3552
|
if id(cur.mypal) != id(self.mypal):
|
3553
3553
|
cur.mypal = self.mypal
|
3554
3554
|
|
3555
|
-
def filter_inundation(self,
|
3555
|
+
def filter_inundation(self, epsilon:float = None, mask:np.ndarray = None):
|
3556
3556
|
"""
|
3557
3557
|
Apply filter on array :
|
3558
3558
|
- mask data below eps
|
3559
3559
|
- mask data outisde linkedvec
|
3560
3560
|
"""
|
3561
|
-
|
3561
|
+
if epsilon is not None:
|
3562
|
+
self.array[np.where(self.array<epsilon)] = self.nullvalue
|
3563
|
+
elif mask is not None:
|
3564
|
+
self.array.data[mask] = self.nullvalue
|
3562
3565
|
|
3563
3566
|
idx_nan = np.where(np.isnan(self.array))
|
3564
3567
|
if len(idx_nan[0])>0:
|
3565
|
-
self.array[idx_nan] =
|
3568
|
+
self.array[idx_nan] = self.nullvalue
|
3566
3569
|
self.array.mask[idx_nan] = True
|
3567
3570
|
logging.warning(_('NaN values found in the array'))
|
3568
3571
|
for i,j in zip(idx_nan[0],idx_nan[1]):
|
@@ -4551,7 +4554,8 @@ class WolfArray(Element_To_Draw, header_wolf):
|
|
4551
4554
|
|
4552
4555
|
if not self.loaded and self.filename != '':
|
4553
4556
|
# if not loaded, load it
|
4554
|
-
self.
|
4557
|
+
self.read_all()
|
4558
|
+
# self.read_data()
|
4555
4559
|
if self.masknull:
|
4556
4560
|
self.mask_data(self.nullvalue)
|
4557
4561
|
|
@@ -5625,7 +5629,7 @@ class WolfArray(Element_To_Draw, header_wolf):
|
|
5625
5629
|
self.rgb[pond<0,i] = self.rgb[pond<0,i] * (1.+pond[pond<0]) - pmin[pond<0,i] * pond[pond<0]
|
5626
5630
|
self.rgb[pond>0,i] = self.rgb[pond>0,i] * (1.-pond[pond>0]) + pmax[pond>0,i] * pond[pond>0]
|
5627
5631
|
|
5628
|
-
self.rgb[self.array.mask] = [1., 1., 1.,
|
5632
|
+
self.rgb[self.array.mask] = [1., 1., 1., 0.]
|
5629
5633
|
|
5630
5634
|
if self.myops is not None:
|
5631
5635
|
# update the wx
|
wolfhece/wolfresults_2D.py
CHANGED
@@ -353,7 +353,7 @@ class Props_Res_2D(wx.Frame):
|
|
353
353
|
Toolssizer = wx.BoxSizer(wx.VERTICAL)
|
354
354
|
hbox = wx.BoxSizer(wx.HORIZONTAL)
|
355
355
|
self._label_nullvalue = wx.StaticText(self._tools,label=_('Epsilon'))
|
356
|
-
self._txt_nullvalue = wx.TextCtrl(self._tools,value=str(self._parent.
|
356
|
+
self._txt_nullvalue = wx.TextCtrl(self._tools,value=str(self._parent.epsilon), style=wx.TE_CENTER)
|
357
357
|
self._txt_nullvalue.SetToolTip(_('Epsilon value under which the value is not plotted'))
|
358
358
|
hbox.Add(self._label_nullvalue, 0, wx.EXPAND|wx.ALL)
|
359
359
|
hbox.Add(self._txt_nullvalue, 1, wx.EXPAND|wx.ALL)
|
@@ -621,7 +621,7 @@ class Props_Res_2D(wx.Frame):
|
|
621
621
|
if self._parent.nullvalue == np.nan:
|
622
622
|
self._txt_nullvalue.Value = 'nan'
|
623
623
|
else :
|
624
|
-
self._txt_nullvalue.Value = str(self._parent.
|
624
|
+
self._txt_nullvalue.Value = str(self._parent.epsilon)
|
625
625
|
|
626
626
|
self.update_palette()
|
627
627
|
|
@@ -634,8 +634,8 @@ class Props_Res_2D(wx.Frame):
|
|
634
634
|
else:
|
635
635
|
newnull = float(newnull)
|
636
636
|
|
637
|
-
if self._parent.
|
638
|
-
self._parent.
|
637
|
+
if self._parent.epsilon != newnull:
|
638
|
+
self._parent.epsilon = newnull
|
639
639
|
self._parent.read_oneresult(self._parent.current_result)
|
640
640
|
self._parent.set_currentview()
|
641
641
|
|
@@ -1417,7 +1417,8 @@ class OneWolfResult:
|
|
1417
1417
|
- mask data below eps
|
1418
1418
|
- mask data outisde linkedvec
|
1419
1419
|
"""
|
1420
|
-
self.
|
1420
|
+
mask = self.waterdepth.array < self.epsilon
|
1421
|
+
self._current.filter_inundation(mask = mask)
|
1421
1422
|
|
1422
1423
|
def set_current(self,which):
|
1423
1424
|
self._which_current = which
|
@@ -1568,6 +1569,7 @@ class OneWolfResult:
|
|
1568
1569
|
|
1569
1570
|
self._current.linkedvec = self.linkedvec
|
1570
1571
|
self._current.idx = self.idx
|
1572
|
+
self._current.nullvalue = self.waterdepth.nullvalue
|
1571
1573
|
|
1572
1574
|
@property
|
1573
1575
|
def min_field_size(self):
|
@@ -2032,14 +2034,14 @@ class Wolfresults_2D(Element_To_Draw):
|
|
2032
2034
|
|
2033
2035
|
return x>=xmin and x<=xmax and y>=ymin and y<=ymax
|
2034
2036
|
|
2035
|
-
@property
|
2036
|
-
def nullvalue(self):
|
2037
|
-
|
2037
|
+
# @property
|
2038
|
+
# def nullvalue(self):
|
2039
|
+
# return self.epsilon
|
2038
2040
|
|
2039
|
-
@nullvalue.setter
|
2040
|
-
def nullvalue(self, value):
|
2041
|
-
|
2042
|
-
|
2041
|
+
# @nullvalue.setter
|
2042
|
+
# def nullvalue(self, value):
|
2043
|
+
# self._epsilon_default = value
|
2044
|
+
# self.epsilon = self._epsilon_default
|
2043
2045
|
|
2044
2046
|
@property
|
2045
2047
|
def alpha(self):
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: wolfhece
|
3
|
-
Version: 2.0.
|
3
|
+
Version: 2.0.16
|
4
4
|
Author-email: Stéphane Champailler <stephane.champailler@uliege.be>, Pierre Archambeau <pierre.archambeau@uliege.be>
|
5
5
|
Project-URL: Homepage, https://uee.uliege.be/hece
|
6
6
|
Project-URL: Issues, https://uee.uliege.be/hece
|
@@ -51,6 +51,8 @@ Requires-Dist: xarray
|
|
51
51
|
Requires-Dist: rasterio
|
52
52
|
Requires-Dist: h5py
|
53
53
|
Requires-Dist: basemap
|
54
|
+
Requires-Dist: exif
|
55
|
+
Requires-Dist: pyglm
|
54
56
|
|
55
57
|
Ce paquet contient l'interface graphique Python du logiciel WOLF (HECE - ULiège) de même que plusieurs outils de traitements topographique, hydraulique et hydrologique.
|
56
58
|
|