This notebook introduces surfaces parameterized by heightmaps.
# !pip install git+https://github.com/tum-pbs/PhiFlow@3.0
from phi.flow import *
Heightmaps can be used in 2D or 3D. They encode a grid surface made up of lines or planes, respectively. The grid vertices are equally spaced and the displacement (height) of each point is specified via a 1D or 2D tensor.
The following code creates a heightmap in 2D from a list of height values.
height = wrap([.1, .02, 0, 0, 1, .95, .8, .5, 0], spatial('x'))
bounds = Box(x=2, y=1)
heightmap = geom.Heightmap(height, bounds, max_dist=.1)
plot(heightmap)
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[2], line 4 2 bounds = Box(x=2, y=1) 3 heightmap = geom.Heightmap(height, bounds, max_dist=.1) ----> 4 plot(heightmap) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/vis/_vis.py:322, in plot(lib, row_dims, col_dims, animate, overlay, title, size, same_scale, log_dims, show_color_bar, color, alpha, err, frame_time, repeat, plt_params, max_subfigures, *fields) 320 for i, f in enumerate(fields): 321 idx = indices[pos][i] --> 322 plots.plot(f, figure, axes[pos], subplots[pos], min_val, max_val, show_color_bar, color[pos][i][idx], alpha[idx], err[idx]) 323 plots.finalize(figure) 324 LAST_FIGURE[0] = figure File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/vis/_vis_base.py:381, in PlottingLibrary.plot(self, data, figure, subplot, space, *args, **kwargs) 379 for recipe in self.recipes: 380 if recipe.can_plot(data, space): --> 381 recipe.plot(data, figure, subplot, space, *args, **kwargs) 382 return 383 raise NotImplementedError(f"No {self.name} recipe found for {data}. Recipes: {self.recipes}") File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1270, in broadcast.<locals>.broadcast_(*args, **kwargs) 1268 @wraps(function) 1269 def broadcast_(*args, **kwargs): -> 1270 return map_(function, *args, dims=dims, range=range, unwrap_scalars=unwrap_scalars, simplify=simplify, map_name=name, **kwargs) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1402, in map_(function, dims, range, unwrap_scalars, expand_results, simplify, map_name, *args, **kwargs) 1400 idx_extra_args = list(extra_args) 1401 idx_all_args = [idx_args.pop(0) if isinstance(v, Shapable) else idx_extra_args.pop(0) for v in args] -> 1402 f_output = function(*idx_all_args, **idx_kwargs, **extra_kwargs) 1403 results.append(f_output) 1404 if isinstance(results[0], tuple): File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/vis/_matplotlib/_matplotlib_plots.py:558, in Heightmap2D.plot(self, data, figure, subplot, space, min_val, max_val, show_color_bar, color, alpha, err) 556 col = _plt_col(color) 557 alpha_f = float(alpha) --> 558 if heightmap._hdim == dims[1]: # horizontal 559 if heightmap._fill_below: 560 y1, y2 = max(-1e10, float(heightmap.bounds[heightmap._hdim].lower)), y File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/geom/_heightmap.py:201, in Heightmap.__getattr__(self, item) 199 if item in self.shape: 200 return BoundDim(self, item) --> 201 raise AttributeError(f"{self.__class__.__name__} has no attribute '{item}'") AttributeError: Heightmap has no attribute '_hdim'
The heightmap surface separates the inside from the outside.
Whether the inside is below or above the height values can be set in the Heightmap constructor.
is_inside = CenteredGrid(lambda loc: heightmap.lies_inside(loc), x=100, y=100, bounds=Box(x=2, y=2))
plot(is_inside)
/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/vis/_matplotlib/_matplotlib_plots.py:167: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect. plt.tight_layout() # because subplot titles can be added after figure creation
While the inside check is always exact, the distance from the surface is an approximation.
For small distances, the face directly underneath a point will usually be closest but for farther points this is not always true.
To account for this, each face links to one other face that will be queried for larger distances.
Finding these secondary faces is performed during heightmap construction where the parameter max_dist influences which other faces are the most important to link.
Above, we set max_dist=0.1. In the following cell, we visualize the distance from the surface.
distance = CenteredGrid(lambda loc: heightmap.approximate_signed_distance(loc), x=100, y=100, bounds=Box(x=2, y=2))
plot(distance, heightmap, overlay='args')
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[4], line 1 ----> 1 distance = CenteredGrid(lambda loc: heightmap.approximate_signed_distance(loc), x=100, y=100, bounds=Box(x=2, y=2)) 2 plot(distance, heightmap, overlay='args') File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/field/_grid.py:75, in CenteredGrid(values, boundary, bounds, resolution, extrapolation, convert, **resolution_) 73 values = sample(values, elements) 74 elif callable(values): ---> 75 values = sample_function(values, elements, 'center', extrapolation) 76 else: 77 if isinstance(values, (tuple, list)) and len(values) == resolution.rank: File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/geom/_geom.py:826, in sample_function(f, elements, at, extrapolation) 824 values = math.map_s2b(f)(*pos.vector) 825 else: --> 826 values = math.map_s2b(f)(pos) 827 assert isinstance(values, math.Tensor), f"values function must return a Tensor but returned {type(values)}" 828 return values File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1216, in map_types.<locals>.retyped_f(*args, **kwargs) 1214 retyped_kwarg, input_types = forward_retype(v, input_types) 1215 retyped_kwargs[k] = retyped_kwarg -> 1216 output = f(*retyped_args, **retyped_kwargs) 1217 restored_output = reverse_retype(output, input_types) 1218 return restored_output Cell In[4], line 1, in <lambda>(loc) ----> 1 distance = CenteredGrid(lambda loc: heightmap.approximate_signed_distance(loc), x=100, y=100, bounds=Box(x=2, y=2)) 2 plot(distance, heightmap, overlay='args') File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/geom/_heightmap.py:181, in Heightmap.approximate_signed_distance(self, location) 180 def approximate_signed_distance(self, location: Tensor) -> Tensor: --> 181 return self.approximate_closest_surface(location)[0] File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/geom/_heightmap.py:135, in Heightmap.approximate_closest_surface(self, location) 133 # --- use closest face from considered --- 134 delta = math.where(projects_onto_face, proj_delta, delta_highest) --> 135 return math.at_min((distances, delta, normals, offsets, face_idx), key=abs(distances), dim=batch('consider') & instance(self).as_batch()) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_ops.py:2170, in at_min(value, key, dim) 2168 if not shape(key).only(dim): 2169 return value -> 2170 idx = argmin(key, dim) 2171 return slice_(value, idx) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_ops.py:2263, in argmin(x, dim, index_dim) 2261 multi_idx_native = choose_backend(idx_native).unravel_index(idx_native[:, 0], dims.sizes) 2262 return reshaped_tensor(multi_idx_native, [keep - broadcast, index_dim.with_size(dims.name_list)]) -> 2263 return broadcast_op(uniform_argmin, [x], broadcast) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_ops.py:1236, in broadcast_op(operation, tensors, iter_dims, no_return) 1234 iter_dims = broadcast_dims(*tensors) if iter_dims is None else iter_dims 1235 if len(iter_dims) == 0: -> 1236 return operation(*tensors) 1237 else: 1238 if isinstance(iter_dims, SHAPE_TYPES): File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_ops.py:2261, in argmin.<locals>.uniform_argmin(x) 2259 v_native = x._reshaped_native([keep - broadcast, dims]) 2260 idx_native = x.backend.argmin(v_native, 1, keepdims=True) -> 2261 multi_idx_native = choose_backend(idx_native).unravel_index(idx_native[:, 0], dims.sizes) 2262 return reshaped_tensor(multi_idx_native, [keep - broadcast, index_dim.with_size(dims.name_list)]) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/backend/_numpy_backend.py:360, in NumPyBackend.unravel_index(self, flat_index, shape) 359 def unravel_index(self, flat_index, shape): --> 360 return np.stack(np.unravel_index(flat_index, shape), -1) TypeError: 'NoneType' object cannot be interpreted as an integer
With max_dist=0.1, the values further than 0.1 away from the cliff get inaccurate distances.
Let's increase `max_dist´ to see the difference.
heightmap = geom.Heightmap(height, bounds, max_dist=1)
distance = CenteredGrid(lambda loc: heightmap.approximate_signed_distance(loc), x=100, y=100, bounds=Box(x=2, y=2))
plot(distance, heightmap, overlay='args')
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[5], line 2 1 heightmap = geom.Heightmap(height, bounds, max_dist=1) ----> 2 distance = CenteredGrid(lambda loc: heightmap.approximate_signed_distance(loc), x=100, y=100, bounds=Box(x=2, y=2)) 3 plot(distance, heightmap, overlay='args') File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/field/_grid.py:75, in CenteredGrid(values, boundary, bounds, resolution, extrapolation, convert, **resolution_) 73 values = sample(values, elements) 74 elif callable(values): ---> 75 values = sample_function(values, elements, 'center', extrapolation) 76 else: 77 if isinstance(values, (tuple, list)) and len(values) == resolution.rank: File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/geom/_geom.py:826, in sample_function(f, elements, at, extrapolation) 824 values = math.map_s2b(f)(*pos.vector) 825 else: --> 826 values = math.map_s2b(f)(pos) 827 assert isinstance(values, math.Tensor), f"values function must return a Tensor but returned {type(values)}" 828 return values File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1216, in map_types.<locals>.retyped_f(*args, **kwargs) 1214 retyped_kwarg, input_types = forward_retype(v, input_types) 1215 retyped_kwargs[k] = retyped_kwarg -> 1216 output = f(*retyped_args, **retyped_kwargs) 1217 restored_output = reverse_retype(output, input_types) 1218 return restored_output Cell In[5], line 2, in <lambda>(loc) 1 heightmap = geom.Heightmap(height, bounds, max_dist=1) ----> 2 distance = CenteredGrid(lambda loc: heightmap.approximate_signed_distance(loc), x=100, y=100, bounds=Box(x=2, y=2)) 3 plot(distance, heightmap, overlay='args') File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/geom/_heightmap.py:181, in Heightmap.approximate_signed_distance(self, location) 180 def approximate_signed_distance(self, location: Tensor) -> Tensor: --> 181 return self.approximate_closest_surface(location)[0] File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/geom/_heightmap.py:135, in Heightmap.approximate_closest_surface(self, location) 133 # --- use closest face from considered --- 134 delta = math.where(projects_onto_face, proj_delta, delta_highest) --> 135 return math.at_min((distances, delta, normals, offsets, face_idx), key=abs(distances), dim=batch('consider') & instance(self).as_batch()) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_ops.py:2170, in at_min(value, key, dim) 2168 if not shape(key).only(dim): 2169 return value -> 2170 idx = argmin(key, dim) 2171 return slice_(value, idx) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_ops.py:2263, in argmin(x, dim, index_dim) 2261 multi_idx_native = choose_backend(idx_native).unravel_index(idx_native[:, 0], dims.sizes) 2262 return reshaped_tensor(multi_idx_native, [keep - broadcast, index_dim.with_size(dims.name_list)]) -> 2263 return broadcast_op(uniform_argmin, [x], broadcast) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_ops.py:1236, in broadcast_op(operation, tensors, iter_dims, no_return) 1234 iter_dims = broadcast_dims(*tensors) if iter_dims is None else iter_dims 1235 if len(iter_dims) == 0: -> 1236 return operation(*tensors) 1237 else: 1238 if isinstance(iter_dims, SHAPE_TYPES): File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_ops.py:2261, in argmin.<locals>.uniform_argmin(x) 2259 v_native = x._reshaped_native([keep - broadcast, dims]) 2260 idx_native = x.backend.argmin(v_native, 1, keepdims=True) -> 2261 multi_idx_native = choose_backend(idx_native).unravel_index(idx_native[:, 0], dims.sizes) 2262 return reshaped_tensor(multi_idx_native, [keep - broadcast, index_dim.with_size(dims.name_list)]) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/backend/_numpy_backend.py:360, in NumPyBackend.unravel_index(self, flat_index, shape) 359 def unravel_index(self, flat_index, shape): --> 360 return np.stack(np.unravel_index(flat_index, shape), -1) TypeError: 'NoneType' object cannot be interpreted as an integer
Now larger distances are covered as well. This runs with the same number of computations but trades off accuracy closer to the surface.
We can additionally query the direction to the closest surface point as well as the corresponding normal vector.
Let's plot these vectors for random points inside (orange) and outside (red) of the heightmap.
points = Box(x=3, y=(-1, 1)).sample_uniform(instance(points=200))
sgn_dist, delta, normal, _, face_index = heightmap.approximate_closest_surface(points)
plot({'closest': [heightmap, PointCloud(points, .1 * math.vec_normalize(delta))],
'normal': [heightmap, PointCloud(points, .2 * normal)]}, overlay='list', color=[0, heightmap.lies_inside(points)*2+1])
/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_tensors.py:1155: RuntimeWarning: invalid value encountered in power result = op(n1, n2)
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[6], line 3 1 points = Box(x=3, y=(-1, 1)).sample_uniform(instance(points=200)) 2 sgn_dist, delta, normal, _, face_index = heightmap.approximate_closest_surface(points) ----> 3 plot({'closest': [heightmap, PointCloud(points, .1 * math.vec_normalize(delta))], 4 'normal': [heightmap, PointCloud(points, .2 * normal)]}, overlay='list', color=[0, heightmap.lies_inside(points)*2+1]) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/vis/_vis.py:322, in plot(lib, row_dims, col_dims, animate, overlay, title, size, same_scale, log_dims, show_color_bar, color, alpha, err, frame_time, repeat, plt_params, max_subfigures, *fields) 320 for i, f in enumerate(fields): 321 idx = indices[pos][i] --> 322 plots.plot(f, figure, axes[pos], subplots[pos], min_val, max_val, show_color_bar, color[pos][i][idx], alpha[idx], err[idx]) 323 plots.finalize(figure) 324 LAST_FIGURE[0] = figure File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/vis/_vis_base.py:381, in PlottingLibrary.plot(self, data, figure, subplot, space, *args, **kwargs) 379 for recipe in self.recipes: 380 if recipe.can_plot(data, space): --> 381 recipe.plot(data, figure, subplot, space, *args, **kwargs) 382 return 383 raise NotImplementedError(f"No {self.name} recipe found for {data}. Recipes: {self.recipes}") File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1270, in broadcast.<locals>.broadcast_(*args, **kwargs) 1268 @wraps(function) 1269 def broadcast_(*args, **kwargs): -> 1270 return map_(function, *args, dims=dims, range=range, unwrap_scalars=unwrap_scalars, simplify=simplify, map_name=name, **kwargs) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1402, in map_(function, dims, range, unwrap_scalars, expand_results, simplify, map_name, *args, **kwargs) 1400 idx_extra_args = list(extra_args) 1401 idx_all_args = [idx_args.pop(0) if isinstance(v, Shapable) else idx_extra_args.pop(0) for v in args] -> 1402 f_output = function(*idx_all_args, **idx_kwargs, **extra_kwargs) 1403 results.append(f_output) 1404 if isinstance(results[0], tuple): File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/vis/_matplotlib/_matplotlib_plots.py:558, in Heightmap2D.plot(self, data, figure, subplot, space, min_val, max_val, show_color_bar, color, alpha, err) 556 col = _plt_col(color) 557 alpha_f = float(alpha) --> 558 if heightmap._hdim == dims[1]: # horizontal 559 if heightmap._fill_below: 560 y1, y2 = max(-1e10, float(heightmap.bounds[heightmap._hdim].lower)), y File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/geom/_heightmap.py:201, in Heightmap.__getattr__(self, item) 199 if item in self.shape: 200 return BoundDim(self, item) --> 201 raise AttributeError(f"{self.__class__.__name__} has no attribute '{item}'") AttributeError: Heightmap has no attribute '_hdim'
The grid orientation is determined by which spatial dimension is not part of the grid. E.g. to create a vertical heightmap, the displacement values should be listed along the y direction.
height = wrap([.1, .02, 0, 0, 1, .95, .8, .5, 0], spatial('y'))
bounds = Box(x=1, y=2)
plot(geom.Heightmap(height, bounds, max_dist=.1))
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[7], line 3 1 height = wrap([.1, .02, 0, 0, 1, .95, .8, .5, 0], spatial('y')) 2 bounds = Box(x=1, y=2) ----> 3 plot(geom.Heightmap(height, bounds, max_dist=.1)) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/vis/_vis.py:322, in plot(lib, row_dims, col_dims, animate, overlay, title, size, same_scale, log_dims, show_color_bar, color, alpha, err, frame_time, repeat, plt_params, max_subfigures, *fields) 320 for i, f in enumerate(fields): 321 idx = indices[pos][i] --> 322 plots.plot(f, figure, axes[pos], subplots[pos], min_val, max_val, show_color_bar, color[pos][i][idx], alpha[idx], err[idx]) 323 plots.finalize(figure) 324 LAST_FIGURE[0] = figure File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/vis/_vis_base.py:381, in PlottingLibrary.plot(self, data, figure, subplot, space, *args, **kwargs) 379 for recipe in self.recipes: 380 if recipe.can_plot(data, space): --> 381 recipe.plot(data, figure, subplot, space, *args, **kwargs) 382 return 383 raise NotImplementedError(f"No {self.name} recipe found for {data}. Recipes: {self.recipes}") File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1270, in broadcast.<locals>.broadcast_(*args, **kwargs) 1268 @wraps(function) 1269 def broadcast_(*args, **kwargs): -> 1270 return map_(function, *args, dims=dims, range=range, unwrap_scalars=unwrap_scalars, simplify=simplify, map_name=name, **kwargs) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1402, in map_(function, dims, range, unwrap_scalars, expand_results, simplify, map_name, *args, **kwargs) 1400 idx_extra_args = list(extra_args) 1401 idx_all_args = [idx_args.pop(0) if isinstance(v, Shapable) else idx_extra_args.pop(0) for v in args] -> 1402 f_output = function(*idx_all_args, **idx_kwargs, **extra_kwargs) 1403 results.append(f_output) 1404 if isinstance(results[0], tuple): File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/vis/_matplotlib/_matplotlib_plots.py:558, in Heightmap2D.plot(self, data, figure, subplot, space, min_val, max_val, show_color_bar, color, alpha, err) 556 col = _plt_col(color) 557 alpha_f = float(alpha) --> 558 if heightmap._hdim == dims[1]: # horizontal 559 if heightmap._fill_below: 560 y1, y2 = max(-1e10, float(heightmap.bounds[heightmap._hdim].lower)), y File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/geom/_heightmap.py:201, in Heightmap.__getattr__(self, item) 199 if item in self.shape: 200 return BoundDim(self, item) --> 201 raise AttributeError(f"{self.__class__.__name__} has no attribute '{item}'") AttributeError: Heightmap has no attribute '_hdim'
Adding a third dimension z yields a 2D heightmap in 3D space.
Here, we create height values from a function, but the rest stays the same.
bounds = Box(x=2, y=2, z=1)
height = CenteredGrid(lambda pos: math.exp(-math.vec_squared(pos-1) * 3), 0, bounds['x,y'], x=10, y=10).values
heightmap = geom.Heightmap(height, bounds, max_dist=.1)
show(heightmap)
/tmp/ipykernel_2909/959595594.py:2: DeprecationWarning: phiml.math.vec_squared is deprecated in favor of phiml.math.squared_norm height = CenteredGrid(lambda pos: math.exp(-math.vec_squared(pos-1) * 3), 0, bounds['x,y'], x=10, y=10).values
Again, we can sample directions and distances from the surface.
points = bounds.sample_uniform(instance(points=200))
sgn_dist, delta, normal, _, face_index = heightmap.approximate_closest_surface(points)
show({'closest': [heightmap, PointCloud(points, .1 * math.vec_normalize(delta))],
'normal': [heightmap, PointCloud(points, .2 * normal)]}, overlay='list', color=[0, heightmap.lies_inside(points)*2+1])
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[9], line 3 1 points = bounds.sample_uniform(instance(points=200)) 2 sgn_dist, delta, normal, _, face_index = heightmap.approximate_closest_surface(points) ----> 3 show({'closest': [heightmap, PointCloud(points, .1 * math.vec_normalize(delta))], 4 'normal': [heightmap, PointCloud(points, .2 * normal)]}, overlay='list', color=[0, heightmap.lies_inside(points)*2+1]) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/vis/_vis.py:58, in show(lib, row_dims, col_dims, animate, overlay, title, size, same_scale, log_dims, show_color_bar, color, alpha, err, frame_time, repeat, plt_params, max_subfigures, *fields) 56 kwargs = locals() 57 del kwargs['fields'] ---> 58 fig = plot(*fields, **kwargs) 59 plots = get_plots_by_figure(fig) 60 if isinstance(fig, Tensor): File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/vis/_vis.py:322, in plot(lib, row_dims, col_dims, animate, overlay, title, size, same_scale, log_dims, show_color_bar, color, alpha, err, frame_time, repeat, plt_params, max_subfigures, *fields) 320 for i, f in enumerate(fields): 321 idx = indices[pos][i] --> 322 plots.plot(f, figure, axes[pos], subplots[pos], min_val, max_val, show_color_bar, color[pos][i][idx], alpha[idx], err[idx]) 323 plots.finalize(figure) 324 LAST_FIGURE[0] = figure File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/vis/_vis_base.py:381, in PlottingLibrary.plot(self, data, figure, subplot, space, *args, **kwargs) 379 for recipe in self.recipes: 380 if recipe.can_plot(data, space): --> 381 recipe.plot(data, figure, subplot, space, *args, **kwargs) 382 return 383 raise NotImplementedError(f"No {self.name} recipe found for {data}. Recipes: {self.recipes}") File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/vis/_dash/_plotly_plots.py:325, in VectorCloud3D.plot(self, data, figure, subplot, space, min_val, max_val, show_color_bar, color, alpha, err) 323 x, y, z = math.reshaped_numpy(data.points.vector[dims], [vector, data.shape.non_channel]) 324 u, v, w = math.reshaped_numpy(data.values.vector[dims], [vector, extra_channels, data.shape.non_channel]) --> 325 figure.add_cone(x=x.flatten(), y=y.flatten(), z=z.flatten(), u=u.flatten(), v=v.flatten(), w=w.flatten(), 326 colorscale=colorscale, 327 sizemode='raw', anchor='tail', 328 row=row, col=col, opacity=float(alpha)) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/plotly/graph_objs/_figure.py:4659, in Figure.add_cone(self, anchor, autocolorscale, cauto, cmax, cmid, cmin, coloraxis, colorbar, colorscale, customdata, customdatasrc, hoverinfo, hoverinfosrc, hoverlabel, hovertemplate, hovertemplatefallback, hovertemplatesrc, hovertext, hovertextsrc, ids, idssrc, legend, legendgroup, legendgrouptitle, legendrank, legendwidth, lighting, lightposition, meta, metasrc, name, opacity, reversescale, scene, showlegend, showscale, sizemode, sizeref, stream, text, textsrc, u, uhoverformat, uid, uirevision, usrc, v, vhoverformat, visible, vsrc, w, whoverformat, wsrc, x, xhoverformat, xsrc, y, yhoverformat, ysrc, z, zhoverformat, zsrc, row, col, **kwargs) 4284 """ 4285 Add a new Cone trace 4286 (...) 4655 Figure 4656 """ 4657 from plotly.graph_objs import Cone -> 4659 new_trace = Cone( 4660 anchor=anchor, 4661 autocolorscale=autocolorscale, 4662 cauto=cauto, 4663 cmax=cmax, 4664 cmid=cmid, 4665 cmin=cmin, 4666 coloraxis=coloraxis, 4667 colorbar=colorbar, 4668 colorscale=colorscale, 4669 customdata=customdata, 4670 customdatasrc=customdatasrc, 4671 hoverinfo=hoverinfo, 4672 hoverinfosrc=hoverinfosrc, 4673 hoverlabel=hoverlabel, 4674 hovertemplate=hovertemplate, 4675 hovertemplatefallback=hovertemplatefallback, 4676 hovertemplatesrc=hovertemplatesrc, 4677 hovertext=hovertext, 4678 hovertextsrc=hovertextsrc, 4679 ids=ids, 4680 idssrc=idssrc, 4681 legend=legend, 4682 legendgroup=legendgroup, 4683 legendgrouptitle=legendgrouptitle, 4684 legendrank=legendrank, 4685 legendwidth=legendwidth, 4686 lighting=lighting, 4687 lightposition=lightposition, 4688 meta=meta, 4689 metasrc=metasrc, 4690 name=name, 4691 opacity=opacity, 4692 reversescale=reversescale, 4693 scene=scene, 4694 showlegend=showlegend, 4695 showscale=showscale, 4696 sizemode=sizemode, 4697 sizeref=sizeref, 4698 stream=stream, 4699 text=text, 4700 textsrc=textsrc, 4701 u=u, 4702 uhoverformat=uhoverformat, 4703 uid=uid, 4704 uirevision=uirevision, 4705 usrc=usrc, 4706 v=v, 4707 vhoverformat=vhoverformat, 4708 visible=visible, 4709 vsrc=vsrc, 4710 w=w, 4711 whoverformat=whoverformat, 4712 wsrc=wsrc, 4713 x=x, 4714 xhoverformat=xhoverformat, 4715 xsrc=xsrc, 4716 y=y, 4717 yhoverformat=yhoverformat, 4718 ysrc=ysrc, 4719 z=z, 4720 zhoverformat=zhoverformat, 4721 zsrc=zsrc, 4722 **kwargs, 4723 ) 4724 return self.add_trace(new_trace, row=row, col=col) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/plotly/graph_objs/_cone.py:2251, in Cone.__init__(self, arg, anchor, autocolorscale, cauto, cmax, cmid, cmin, coloraxis, colorbar, colorscale, customdata, customdatasrc, hoverinfo, hoverinfosrc, hoverlabel, hovertemplate, hovertemplatefallback, hovertemplatesrc, hovertext, hovertextsrc, ids, idssrc, legend, legendgroup, legendgrouptitle, legendrank, legendwidth, lighting, lightposition, meta, metasrc, name, opacity, reversescale, scene, showlegend, showscale, sizemode, sizeref, stream, text, textsrc, u, uhoverformat, uid, uirevision, usrc, v, vhoverformat, visible, vsrc, w, whoverformat, wsrc, x, xhoverformat, xsrc, y, yhoverformat, ysrc, z, zhoverformat, zsrc, **kwargs) 2249 self._set_property("coloraxis", arg, coloraxis) 2250 self._set_property("colorbar", arg, colorbar) -> 2251 self._set_property("colorscale", arg, colorscale) 2252 self._set_property("customdata", arg, customdata) 2253 self._set_property("customdatasrc", arg, customdatasrc) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/plotly/basedatatypes.py:4403, in BasePlotlyType._set_property(self, name, arg, provided) 4397 def _set_property(self, name, arg, provided): 4398 """ 4399 Initialize a property of this object using the provided value 4400 or a value popped from the arguments dictionary. If neither 4401 is available, do not set the property. 4402 """ -> 4403 _set_property_provided_value(self, name, arg, provided) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/plotly/basedatatypes.py:398, in _set_property_provided_value(obj, name, arg, provided) 396 val = provided if provided is not None else val 397 if val is not None: --> 398 obj[name] = val File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/plotly/basedatatypes.py:4932, in BasePlotlyType.__setitem__(self, prop, value) 4928 self._set_array_prop(prop, value) 4930 # ### Handle simple property ### 4931 else: -> 4932 self._set_prop(prop, value) 4933 else: 4934 # Make sure properties dict is initialized 4935 self._init_props() File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/plotly/basedatatypes.py:5276, in BasePlotlyType._set_prop(self, prop, val) 5274 return 5275 else: -> 5276 raise err 5278 # val is None 5279 # ----------- 5280 if val is None: 5281 # Check if we should send null update File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/plotly/basedatatypes.py:5271, in BasePlotlyType._set_prop(self, prop, val) 5268 validator = self._get_validator(prop) 5270 try: -> 5271 val = validator.validate_coerce(val) 5272 except ValueError as err: 5273 if self._skip_invalid: File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/_plotly_utils/basevalidators.py:1650, in ColorscaleValidator.validate_coerce(self, v) 1645 v = [ 1646 [e[0], ColorValidator.perform_validate_coerce(e[1])] for e in v 1647 ] 1649 if not v_valid: -> 1650 self.raise_invalid_val(v) 1652 return v File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/_plotly_utils/basevalidators.py:312, in BaseValidator.raise_invalid_val(self, v, inds) 309 for i in inds: 310 name += "[" + str(i) + "]" --> 312 raise ValueError( 313 """ 314 Invalid value of type {typ} received for the '{name}' property of {pname} 315 Received value: {v} 316 317 {valid_clr_desc}""".format( 318 name=name, 319 pname=self.parent_name, 320 typ=type_str(v), 321 v=repr(v), 322 valid_clr_desc=self.description(), 323 ) 324 ) ValueError: Invalid value of type 'builtins.list' received for the 'colorscale' property of cone Received value: [[0, ['rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)']], [1, ['rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)']]] The 'colorscale' property is a colorscale and may be specified as: - A list of colors that will be spaced evenly to create the colorscale. Many predefined colorscale lists are included in the sequential, diverging, and cyclical modules in the plotly.colors package. - A list of 2-element lists where the first element is the normalized color level value (starting at 0 and ending at 1), and the second item is a valid color string. (e.g. [[0, 'green'], [0.5, 'red'], [1.0, 'rgb(0, 0, 255)']]) - One of the following named colorscales: ['aggrnyl', 'agsunset', 'algae', 'amp', 'armyrose', 'balance', 'blackbody', 'bluered', 'blues', 'blugrn', 'bluyl', 'brbg', 'brwnyl', 'bugn', 'bupu', 'burg', 'burgyl', 'cividis', 'curl', 'darkmint', 'deep', 'delta', 'dense', 'earth', 'edge', 'electric', 'emrld', 'fall', 'geyser', 'gnbu', 'gray', 'greens', 'greys', 'haline', 'hot', 'hsv', 'ice', 'icefire', 'inferno', 'jet', 'magenta', 'magma', 'matter', 'mint', 'mrybm', 'mygbm', 'oranges', 'orrd', 'oryel', 'oxy', 'peach', 'phase', 'picnic', 'pinkyl', 'piyg', 'plasma', 'plotly3', 'portland', 'prgn', 'pubu', 'pubugn', 'puor', 'purd', 'purp', 'purples', 'purpor', 'rainbow', 'rdbu', 'rdgy', 'rdpu', 'rdylbu', 'rdylgn', 'redor', 'reds', 'solar', 'spectral', 'speed', 'sunset', 'sunsetdark', 'teal', 'tealgrn', 'tealrose', 'tempo', 'temps', 'thermal', 'tropic', 'turbid', 'turbo', 'twilight', 'viridis', 'ylgn', 'ylgnbu', 'ylorbr', 'ylorrd']. Appending '_r' to a named colorscale reverses it.
Like all Geometry objects, heightmaps can be merged using the union function.
If we only pass Heightmap objects with the same grid resolution, ΦFlow will automatically vectorize computations so that the latency does not increase.
We can use this to create complex obstacles with detailed surfaces on all sides or to create cavities.
height = (1 - math.linspace(-1, 1, spatial(x=10)) ** 2) ** .5
upper_heightmap = geom.Heightmap(height, Box(x=(-1, 1), y=None), max_dist=.1, fill_below=False, extrapolation=0)
lower_heightmap = geom.Heightmap(-height, Box(x=(-1, 1), y=None), max_dist=.1, fill_below=True, extrapolation=0)
heightmap = union(lower_heightmap, upper_heightmap)
points = Box(x=(-2, 2), y=(-2, 2)).sample_uniform(instance(points=200))
sgn_dist, delta, normal, _, face_index = heightmap.approximate_closest_surface(points)
show({'closest': [heightmap, PointCloud(points, .1 * math.vec_normalize(delta))],
'normal': [heightmap, PointCloud(points, .2 * normal)]}, overlay='list', color=[0, heightmap.lies_inside(points)*2+1])
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[10], line 7 4 heightmap = union(lower_heightmap, upper_heightmap) 6 points = Box(x=(-2, 2), y=(-2, 2)).sample_uniform(instance(points=200)) ----> 7 sgn_dist, delta, normal, _, face_index = heightmap.approximate_closest_surface(points) 8 show({'closest': [heightmap, PointCloud(points, .1 * math.vec_normalize(delta))], 9 'normal': [heightmap, PointCloud(points, .2 * normal)]}, overlay='list', color=[0, heightmap.lies_inside(points)*2+1]) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/geom/_heightmap.py:111, in Heightmap.approximate_closest_surface(self, location) 109 def approximate_closest_surface(self, location: Tensor) -> Tuple[Tensor, Tensor, Tensor, Tensor, Tensor]: 110 grid_bounds = math.i2b(self.grid_bounds) --> 111 faces = math.i2b(self.face_cache) 112 cell_idx = cell_index(location, grid_bounds, self.resolution, clip=True) 113 # --- gather face infos at projected cell --- File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/functools.py:998, in cached_property.__get__(self, instance, owner) 996 val = cache.get(self.attrname, _NOT_FOUND) 997 if val is _NOT_FOUND: --> 998 val = self.func(instance) 999 try: 1000 cache[self.attrname] = val File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/geom/_heightmap.py:77, in Heightmap.face_cache(self) 75 @cached_property 76 def face_cache(self): ---> 77 proj_faces = build_faces(self) 78 with numpy.errstate(divide='ignore', invalid='ignore'): 79 secondary_idx = math.map(find_most_important_neighbor, proj_faces, self.dx, self.resolution, self.hdim, self.fill_below, self.max_dist, dims=instance, unwrap_scalars=False) File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/geom/_heightmap.py:219, in build_faces(heightmap) 217 height = heightmap.height 218 flat_space = spatial(height).name_list --> 219 pos = heightmap.vertices 220 center = math.neighbor_mean(pos) 221 face_slopes = {} File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phi/geom/_heightmap.py:89, in Heightmap.vertices(self) 87 space = self.vector.item_names 88 pos = self.grid_bounds.local_to_global(math.meshgrid(spatial(self.height)) / self.resolution) ---> 89 vert = stack({dim: self.height if dim == hdim else pos[dim] for dim in space}, channel('vector')) 90 return vert File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/phiml/math/_magic_ops.py:148, in stack(values, dim, expand_values, simplify, layout_non_matching, **kwargs) 146 from ._tensors import layout 147 return layout(values, dim) --> 148 raise ValueError(f"Non-batch dims must match but got: {v0_dims} and {s.non_batch.names}. Manually expand tensors or set expand_values=True") 149 # --- Add missing dims --- 150 if expand_values: ValueError: Non-batch dims must match but got: {'x'} and ('union', 'x'). Manually expand tensors or set expand_values=True
Here, we set extrapolation=0 to extend the heightmap beyond its bounds.
Consequently, points with x < -1 or x > 1 count as inside the mesh and are colored red correspondingly.