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 1 height = wrap([.1, .02, 0, 0, 1, .95, .8, .5, 0], spatial('x')) 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.13/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.13/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.13/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1285, in broadcast.<locals>.broadcast_(*args, **kwargs) 1283 @wraps(function) 1284 def broadcast_(*args, **kwargs): -> 1285 return map_(function, *args, dims=dims, range=range, unwrap_scalars=unwrap_scalars, simplify=simplify, map_name=name, **kwargs) File /opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1417, in map_(function, dims, range, unwrap_scalars, expand_results, simplify, map_name, *args, **kwargs) 1415 idx_extra_args = list(extra_args) 1416 idx_all_args = [idx_args.pop(0) if isinstance(v, Shapable) else idx_extra_args.pop(0) for v in args] -> 1417 f_output = function(*idx_all_args, **idx_kwargs, **extra_kwargs) 1418 results.append(f_output) 1419 if isinstance(results[0], tuple): File /opt/hostedtoolcache/Python/3.12.13/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.13/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.13/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')
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[4], line 2 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.13/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.13/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.13/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1285, in broadcast.<locals>.broadcast_(*args, **kwargs) 1283 @wraps(function) 1284 def broadcast_(*args, **kwargs): -> 1285 return map_(function, *args, dims=dims, range=range, unwrap_scalars=unwrap_scalars, simplify=simplify, map_name=name, **kwargs) File /opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1417, in map_(function, dims, range, unwrap_scalars, expand_results, simplify, map_name, *args, **kwargs) 1415 idx_extra_args = list(extra_args) 1416 idx_all_args = [idx_args.pop(0) if isinstance(v, Shapable) else idx_extra_args.pop(0) for v in args] -> 1417 f_output = function(*idx_all_args, **idx_kwargs, **extra_kwargs) 1418 results.append(f_output) 1419 if isinstance(results[0], tuple): File /opt/hostedtoolcache/Python/3.12.13/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.13/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'
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')
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[5], line 3 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.13/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.13/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.13/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1285, in broadcast.<locals>.broadcast_(*args, **kwargs) 1283 @wraps(function) 1284 def broadcast_(*args, **kwargs): -> 1285 return map_(function, *args, dims=dims, range=range, unwrap_scalars=unwrap_scalars, simplify=simplify, map_name=name, **kwargs) File /opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1417, in map_(function, dims, range, unwrap_scalars, expand_results, simplify, map_name, *args, **kwargs) 1415 idx_extra_args = list(extra_args) 1416 idx_all_args = [idx_args.pop(0) if isinstance(v, Shapable) else idx_extra_args.pop(0) for v in args] -> 1417 f_output = function(*idx_all_args, **idx_kwargs, **extra_kwargs) 1418 results.append(f_output) 1419 if isinstance(results[0], tuple): File /opt/hostedtoolcache/Python/3.12.13/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.13/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'
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])
--------------------------------------------------------------------------- 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.13/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.13/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.13/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1285, in broadcast.<locals>.broadcast_(*args, **kwargs) 1283 @wraps(function) 1284 def broadcast_(*args, **kwargs): -> 1285 return map_(function, *args, dims=dims, range=range, unwrap_scalars=unwrap_scalars, simplify=simplify, map_name=name, **kwargs) File /opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1417, in map_(function, dims, range, unwrap_scalars, expand_results, simplify, map_name, *args, **kwargs) 1415 idx_extra_args = list(extra_args) 1416 idx_all_args = [idx_args.pop(0) if isinstance(v, Shapable) else idx_extra_args.pop(0) for v in args] -> 1417 f_output = function(*idx_all_args, **idx_kwargs, **extra_kwargs) 1418 results.append(f_output) 1419 if isinstance(results[0], tuple): File /opt/hostedtoolcache/Python/3.12.13/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.13/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.13/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.13/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.13/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1285, in broadcast.<locals>.broadcast_(*args, **kwargs) 1283 @wraps(function) 1284 def broadcast_(*args, **kwargs): -> 1285 return map_(function, *args, dims=dims, range=range, unwrap_scalars=unwrap_scalars, simplify=simplify, map_name=name, **kwargs) File /opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/phiml/math/_functional.py:1417, in map_(function, dims, range, unwrap_scalars, expand_results, simplify, map_name, *args, **kwargs) 1415 idx_extra_args = list(extra_args) 1416 idx_all_args = [idx_args.pop(0) if isinstance(v, Shapable) else idx_extra_args.pop(0) for v in args] -> 1417 f_output = function(*idx_all_args, **idx_kwargs, **extra_kwargs) 1418 results.append(f_output) 1419 if isinstance(results[0], tuple): File /opt/hostedtoolcache/Python/3.12.13/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.13/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_3044/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.13/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.13/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.13/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.13/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.13/x64/lib/python3.12/site-packages/plotly/graph_objs/_figure.py:4661, 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) 4657 Figure 4658 """ 4659 from plotly.graph_objs import Cone 4660 -> 4661 new_trace = Cone( 4662 anchor=anchor, 4663 autocolorscale=autocolorscale, 4664 cauto=cauto, File /opt/hostedtoolcache/Python/3.12.13/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.13/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.13/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.13/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.13/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.13/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.13/x64/lib/python3.12/site-packages/_plotly_utils/basevalidators.py:1661, in ColorscaleValidator.validate_coerce(self, v) 1656 v = [ 1657 [e[0], ColorValidator.perform_validate_coerce(e[1])] for e in v 1658 ] 1660 if not v_valid: -> 1661 self.raise_invalid_val(v) 1663 return v File /opt/hostedtoolcache/Python/3.12.13/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(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(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(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(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(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(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(255, 127, 14)', 'rgb(214, 39, 40)', '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(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(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(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(214, 39, 40)', '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(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(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', '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(214, 39, 40)', '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(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(255, 127, 14)', '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(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(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(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(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(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(255, 127, 14)', 'rgb(255, 127, 14)', 'rgb(255, 127, 14)']], [1, ['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(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(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(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(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(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(255, 127, 14)', 'rgb(214, 39, 40)', '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(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(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(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(214, 39, 40)', '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(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(255, 127, 14)', 'rgb(214, 39, 40)', 'rgb(214, 39, 40)', '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(214, 39, 40)', '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(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(255, 127, 14)', '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(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(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(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(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(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(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 3 lower_heightmap = geom.Heightmap(-height, Box(x=(-1, 1), y=None), max_dist=.1, fill_below=True, extrapolation=0) 4 heightmap = union(lower_heightmap, upper_heightmap) 5 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.13/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.13/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.13/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.13/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.13/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.13/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.