Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Isolayer updates #140

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions examples/amr_volume_rendering_isocontours.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import yt

import yt_idv
from yt_idv.scene_components.isolayers import Isolayers

ds = yt.load_sample("IsolatedGalaxy")

rc = yt_idv.render_context(height=800, width=800, gui=True)
sg = rc.add_scene(ds, "density", no_ghost=True)

iso = Isolayers(data=sg.components[0].data)
sg.components.append(iso)


# default behavior will treat these values as base-10 exponents
sg.components[1].iso_layers[0] = -27.0
sg.components[1].iso_tolerance[0] = 1.2 # tolerance in percent

rc.run()
178 changes: 0 additions & 178 deletions yt_idv/scene_components/base_component.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import numpy as np
import traitlets
from OpenGL import GL

Expand Down Expand Up @@ -29,11 +28,6 @@ class SceneComponent(traitlets.HasTraits):
priority = traitlets.CInt(0)
visible = traitlets.Bool(True)
use_db = traitlets.Bool(False) # use depth buffer
iso_tolerance = traitlets.CFloat(-1) # the tolerance for finding isocontours
iso_tol_is_pct = traitlets.Bool(False) # if True, the tolerance is a fraction
iso_log = traitlets.Bool(True) # if True, iso values are base 10 exponents
iso_layers = traitlets.List() # the target values for isocontours
iso_layers_alpha = traitlets.List() # the transparency of isocontours
display_bounds = traitlets.Tuple(
traitlets.CFloat(),
traitlets.CFloat(),
Expand Down Expand Up @@ -109,28 +103,8 @@ def render_gui(self, imgui, renderer, scene):
_ = add_popup_help(imgui, "Click to reset the colorbounds of the current view.")
changed = changed or _

if self.render_method == "isocontours":
_ = self._render_isolayer_inputs(imgui)
changed = changed or _

return changed

@traitlets.observe("iso_log")
def _switch_iso_log(self, change):
# if iso_log, then the user is setting 10**x, otherwise they are setting
# x directly. So when toggling this checkbox we convert the existing
# values between the two forms.
if change["old"]:
# if True, we were taking the log, but now are not:
self.iso_tolerance = 10**self.iso_tolerance
new_iso_layers = [10**iso_val for iso_val in self.iso_layers]
self.iso_layers = new_iso_layers
else:
# we were not taking the log but now we are, so convert to the exponent
self.iso_tolerance = np.log10(self.iso_tolerance)
new_iso_layers = [np.log10(iso_val) for iso_val in self.iso_layers]
self.iso_layers = new_iso_layers

@traitlets.default("display_name")
def _default_display_name(self):
return self.name
Expand All @@ -149,14 +123,6 @@ def _change_render_method(self, change):
self.colormap_vertex = new_combo["second_vertex"]
self.colormap_fragment = new_combo["second_fragment"]

@traitlets.observe("render_method")
def _add_initial_isolayer(self, change):
# this adds an initial isocontour entry when the render method
# switches to isocontours and if there are no layers yet.
if change["new"] == "isocontours" and len(self.iso_layers) == 0:
self.iso_layers.append(0.0)
self.iso_layers_alpha.append(1.0)

@traitlets.default("fb")
def _fb_default(self):
return Framebuffer()
Expand Down Expand Up @@ -253,18 +219,6 @@ def program2(self):
self._program2_invalid = False
return self._program2

def _set_iso_uniforms(self, p):
# these could be handled better by watching traits.
p._set_uniform("iso_num_layers", int(len(self.iso_layers)))
isolayervals = self._get_sanitized_iso_layers()
p._set_uniform("iso_layers", isolayervals)
p._set_uniform("iso_layer_tol", self._get_sanitized_iso_tol())
avals = np.zeros((32,), dtype="float32")
avals[: len(self.iso_layers)] = np.array(self.iso_layers_alpha)
p._set_uniform("iso_alphas", avals)
p._set_uniform("iso_min", float(self.data.min_val))
p._set_uniform("iso_max", float(self.data.max_val))

def run_program(self, scene):
# Store this info, because we need to render into a framebuffer that is the
# right size.
Expand All @@ -276,8 +230,6 @@ def run_program(self, scene):
with self.program1.enable() as p:
scene.camera._set_uniforms(scene, p)
self._set_uniforms(scene, p)
if self.render_method == "isocontours":
self._set_iso_uniforms(p)
with self.data.vertex_array.bind(p):
self.draw(scene, p)

Expand Down Expand Up @@ -306,65 +258,6 @@ def run_program(self, scene):
def draw(self, scene, program):
raise NotImplementedError

def _get_sanitized_iso_layers(self, normalize=True):
# returns an array of the isocontour layer values, padded with 0s out
# to max number of contours (32).
iso_vals = np.asarray(self.iso_layers)
if self.iso_log:
iso_vals = 10**iso_vals

if normalize:
iso_vals = self.data._normalize_by_min_max(iso_vals)

full_array = np.zeros(32, dtype="float32")
full_array[: len(self.iso_layers)] = iso_vals
return full_array

def _get_sanitized_iso_tol(self):
# isocontour selection conditions:
#
# absolute difference
# d - c <= eps
# or percent difference
# (d - c) / c * 100 <= eps_pct
#
# where d is a raw data value, c is the target isocontour, eps
# is an absolute difference, eps_f is a percent difference
#
# The data textures available on the shaders are normalized values:
# d_ = (d - min) / (max - min)
# where max and min are the global min and max values across the entire
# volume (e.g., over all blocks, not within a block)
#
# So in terms of normalized values, the absoulte difference condition
# becomes
# d_ - c_ <= eps / (max - min)
# where c_ is the target value normalized in the same way as d_.
#
# And the percent difference becomes
# (d_ - c_) * (max - min) / c * 100 <= eps_pct
# or
# d_ - c_ <= eps_pct / 100 * c / (max - min)
# so that the allowed tolerance is a function of the raw target value
# and so will vary with each layer.

if self.iso_log:
# the tol value is an exponent, convert
tol = 10 ** float(self.iso_tolerance)
else:
tol = float(self.iso_tolerance)
# always normalize tolerance
tol = tol / self.data.val_range

if self.iso_tol_is_pct:
# tolerance depends on the layer value
tol = tol * 0.01
raw_layers = self._get_sanitized_iso_layers(normalize=False)
final_tol = raw_layers * tol
else:
final_tol = np.full((32,), tol, dtype="float32")
return final_tol

def _recompile_shader(self) -> bool:
# removes existing shaders, invalidates shader programs
shaders = (
Expand All @@ -381,77 +274,6 @@ def _recompile_shader(self) -> bool:
self._program1_invalid = self._program2_invalid = True
return True

def _render_isolayer_inputs(self, imgui) -> bool:
changed = False
if imgui.tree_node("Isocontours"):
_, self.iso_log = imgui.checkbox("set exponent", self.iso_log)
_ = add_popup_help(
imgui, "If checked, will treat isocontour values as base-10 exponents."
)
changed = changed or _

imgui.columns(2, "iso_tol_cols", False)

_, self.iso_tolerance = imgui.input_float(
"tol",
self.iso_tolerance,
flags=imgui.INPUT_TEXT_ENTER_RETURNS_TRUE,
)
_ = add_popup_help(imgui, "The tolerance for selecting an isocontour.")
changed = changed or _

imgui.next_column()
_, self.iso_tol_is_pct = imgui.checkbox("%", self.iso_tol_is_pct)
_ = add_popup_help(imgui, "If checked, the tolerance is a percent.")
changed = changed or _
imgui.columns(1)

if imgui.button("Add Layer"):
if len(self.iso_layers) < 32:
changed = True
self.iso_layers.append(0.0)
self.iso_layers_alpha.append(1.0)
_ = self._construct_isolayer_table(imgui)
changed = changed or _
imgui.tree_pop()
return changed

def _construct_isolayer_table(self, imgui) -> bool:
imgui.columns(3, "iso_layers_cols", False)

i = 0
changed = False
while i < len(self.iso_layers):
_, self.iso_layers[i] = imgui.input_float(
f"Layer {i + 1}",
self.iso_layers[i],
flags=imgui.INPUT_TEXT_ENTER_RETURNS_TRUE,
)
_ = add_popup_help(imgui, "The value of the isocontour layer.")
changed = changed or _

imgui.next_column()
_, self.iso_layers_alpha[i] = imgui.input_float(
f"alpha {i}",
self.iso_layers_alpha[i],
flags=imgui.INPUT_TEXT_ENTER_RETURNS_TRUE,
)
_ = add_popup_help(imgui, "The opacity of the isocontour layer.")
changed = changed or _

imgui.next_column()
if imgui.button("Remove##rl" + str(i + 1)):
self.iso_layers.pop(i)
self.iso_layers_alpha.pop(i)
i -= 1
_ = True
changed = changed or _
imgui.next_column()
i += 1
imgui.columns(1)

return changed

def _reset_cmap_bounds(self):
data = self.fb.data
if self.use_db:
Expand Down
5 changes: 5 additions & 0 deletions yt_idv/scene_components/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ def render_gui(self, imgui, renderer, scene):
gp = GridPositions(grid_list=grids)
scene.data_objects.append(gp)
scene.components.append(GridOutlines(data=gp))
if imgui.button("Add Isocontours"):
from .isolayers import Isolayers

iso = Isolayers(data=self.data)
scene.components.append(iso)
if self.render_method == "transfer_function":
# Now for the transfer function stuff
imgui.image_button(
Expand Down
Loading