Skip to content

Commit

Permalink
move isolayers to dedicated component
Browse files Browse the repository at this point in the history
  • Loading branch information
chrishavlin committed Jul 9, 2024
1 parent 80612c0 commit f02a935
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 70 deletions.
10 changes: 7 additions & 3 deletions examples/amr_volume_rendering_isocontours.py
Original file line number Diff line number Diff line change
@@ -1,15 +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)
sg.components[0].render_method = "isocontours"

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


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

rc.run()
13 changes: 6 additions & 7 deletions yt_idv/scene_components/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@
from yt_idv.gui_support import add_popup_help
from yt_idv.opengl_support import TransferFunctionTexture
from yt_idv.scene_components.base_component import SceneComponent
from yt_idv.scene_components.isolayers import Isolayers
from yt_idv.scene_data.block_collection import BlockCollection
from yt_idv.shader_objects import component_shaders


class BlockRendering(SceneComponent, Isolayers):
class BlockRendering(SceneComponent):
"""
A class that renders block data. It may do this in one of several ways,
including mesh outline. This allows us to render a single collection of
Expand Down Expand Up @@ -63,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 Expand Up @@ -121,9 +125,6 @@ def render_gui(self, imgui, renderer, scene):
changed = changed or _
_ = add_popup_help(imgui, "The normal vector of the slicing plane.")
changed = changed or _
elif self.render_method == "isocontours":
_ = self._render_isolayer_inputs(imgui)
changed = changed or _

return changed

Expand Down Expand Up @@ -153,5 +154,3 @@ def _set_uniforms(self, scene, shader_program):
shader_program._set_uniform("tf_log", float(self.tf_log))
shader_program._set_uniform("slice_normal", np.array(self.slice_normal))
shader_program._set_uniform("slice_position", np.array(self.slice_position))
if self.render_method == "isocontours":
self._set_iso_uniforms(shader_program)
163 changes: 114 additions & 49 deletions yt_idv/scene_components/isolayers.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
import numpy as np
import traitlets
from OpenGL import GL

from yt_idv.gui_support import add_popup_help
from yt_idv.opengl_support import TransferFunctionTexture
from yt_idv.scene_components.base_component import SceneComponent
from yt_idv.scene_data.block_collection import BlockCollection


class Isolayers(traitlets.HasTraits):
class Isolayers(SceneComponent):
name = "block_isocontours"
render_method = "isocontours"

data = traitlets.Instance(BlockCollection)
iso_tol_is_pct = traitlets.Bool(True) # if True, the tolerance is a fraction
iso_log = traitlets.Bool(True) # if True, iso values are base 10 exponents
iso_tolerance = traitlets.List() # the tolerance for finding isocontours
iso_layers = traitlets.List() # the target values for isocontours
iso_layers_alpha = traitlets.List() # the transparency of isocontours
box_width = traitlets.CFloat(0.1)
sample_factor = traitlets.CFloat(1.0)
transfer_function = traitlets.Instance(TransferFunctionTexture)
tf_min = traitlets.CFloat(0.0)
tf_max = traitlets.CFloat(1.0)
tf_log = traitlets.Bool(True)

@traitlets.observe("iso_log")
def _switch_iso_log(self, change):
Expand All @@ -29,63 +43,95 @@ def _switch_iso_log(self, change):
new_iso_layers = [np.log10(iso_val) for iso_val in self.iso_layers]
self.iso_layers = new_iso_layers

@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:
val = (self.data.min_val + self.data.max_val) / 2.0
if self.iso_log:
val = np.log10(val)
self.iso_layers.append(val)
self.iso_tolerance.append(0.0)
self.iso_layers_alpha.append(1.0)

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)))
@traitlets.default("iso_layers")
def _default_iso_layer(self):
val = (self.data.min_val + self.data.max_val) / 2.0
if self.iso_log:
val = np.log10(val)
return [
val,
]

@traitlets.default("iso_tolerance")
def _default_iso_layer_tol(self):
return [
0.0,
]

@traitlets.default("iso_layers_alpha")
def _default_iso_layers_alpha(self):
return [
1.0,
]

def _set_uniforms(self, scene, shader_program):

shader_program._set_uniform("iso_num_layers", int(len(self.iso_layers)))
isolayervals = self._get_sanitized_iso_layers()
p._set_uniform("iso_layers_min", isolayervals[0])
p._set_uniform("iso_layers_max", isolayervals[1])
shader_program._set_uniform("iso_layers_min", isolayervals[0])
shader_program._set_uniform("iso_layers_max", isolayervals[1])
avals = np.zeros((32,), dtype="float32")
avals[: len(self.iso_layers)] = np.array(self.iso_layers_alpha)
p._set_uniform("iso_alphas", avals)

def _render_isolayer_inputs(self, imgui) -> bool:
shader_program._set_uniform("iso_alphas", avals)

shader_program._set_uniform("box_width", self.box_width)
shader_program._set_uniform("sample_factor", self.sample_factor)
shader_program._set_uniform("ds_tex", np.array([0, 0, 0, 0, 0, 0]))
shader_program._set_uniform("bitmap_tex", 1)
shader_program._set_uniform("tf_tex", 2)
shader_program._set_uniform("tf_min", self.tf_min)
shader_program._set_uniform("tf_max", self.tf_max)
shader_program._set_uniform("tf_log", float(self.tf_log))

def render_gui(self, imgui, renderer, scene):
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.next_column()
_, self.iso_tol_is_pct = imgui.checkbox(
"set tolerance in %", 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()
_, 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.next_column()
_, self.iso_tol_is_pct = imgui.checkbox(
"set tolerance in %", 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.iso_tolerance.append(0.0)
_ = self._construct_isolayer_table(imgui)
changed = changed or _

_ = super().render_gui(imgui, renderer, scene)
changed = changed or _

return changed

def _construct_isolayer_table(self, imgui) -> bool:
imgui.columns(4, "iso_layers_cols", False)
if len(self.iso_layers) > 0:
# column text headers
imgui.text("Value")
imgui.next_column()
imgui.text("Tolerance")
imgui.next_column()
imgui.text("alpha")
imgui.next_column()
imgui.text("delete")
imgui.next_column()

i = 0
changed = False
while i < len(self.iso_layers):
_, self.iso_layers[i] = imgui.input_float(
f"Layer {i + 1}",
f"##layer_{i}",
self.iso_layers[i],
flags=imgui.INPUT_TEXT_ENTER_RETURNS_TRUE,
)
Expand All @@ -94,7 +140,7 @@ def _construct_isolayer_table(self, imgui) -> bool:

imgui.next_column()
_, self.iso_tolerance[i] = imgui.input_float(
f"tol {i}",
f"##tol_{i}",
self.iso_tolerance[i],
flags=imgui.INPUT_TEXT_ENTER_RETURNS_TRUE,
)
Expand All @@ -103,17 +149,16 @@ def _construct_isolayer_table(self, imgui) -> bool:

imgui.next_column()
_, self.iso_layers_alpha[i] = imgui.input_float(
f"alpha {i}",
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)
if imgui.button(f"X##remove_{i}"):
self._remove_layer(i)
i -= 1
_ = True
changed = changed or _
Expand All @@ -123,6 +168,11 @@ def _construct_isolayer_table(self, imgui) -> bool:

return changed

def _remove_layer(self, layer_id):
self.iso_layers.pop(layer_id)
self.iso_layers_alpha.pop(layer_id)
self.iso_tolerance.pop(layer_id)

@property
def _iso_layer_array(self):
iso_vals = np.asarray(self.iso_layers, dtype="float32")
Expand Down Expand Up @@ -164,3 +214,18 @@ def _get_sanitized_iso_tol(self):
else:
final_tol = tol
return final_tol

@traitlets.default("transfer_function")
def _default_transfer_function(self):
tf = TransferFunctionTexture(data=np.ones((256, 1, 4), dtype="u1") * 255)
return tf

def draw(self, scene, program):
each = self.data.vertex_array.each
GL.glEnable(GL.GL_CULL_FACE)
GL.glCullFace(GL.GL_BACK)
with self.transfer_function.bind(target=2):
for tex_ind, tex, bitmap_tex in self.data.viewpoint_iter(scene.camera):
with tex.bind(target=0):
with bitmap_tex.bind(target=1):
GL.glDrawArrays(GL.GL_POINTS, tex_ind * each, each)
14 changes: 8 additions & 6 deletions yt_idv/shaders/shaderlist.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -207,18 +207,20 @@ component_shaders:
first_fragment: transfer_function
second_vertex: passthrough
second_fragment: passthrough
isocontours:
description: Isocontours
slice:
description: Slice
first_vertex: grid_position
first_geometry: grid_expand
first_fragment: isocontour
first_fragment: slice_sample
second_vertex: passthrough
second_fragment: apply_colormap
slice:
description: Slice
block_isocontours:
default_value: isocontours
isocontours:
description: Isocontours
first_vertex: grid_position
first_geometry: grid_expand
first_fragment: slice_sample
first_fragment: isocontour
second_vertex: passthrough
second_fragment: apply_colormap
octree_block_rendering:
Expand Down
14 changes: 9 additions & 5 deletions yt_idv/tests/test_yt_idv.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import yt_idv
from yt_idv import shader_objects
from yt_idv.scene_components.curves import CurveCollectionRendering, CurveRendering
from yt_idv.scene_components.isolayers import Isolayers
from yt_idv.scene_data.curve import CurveCollection, CurveData


Expand Down Expand Up @@ -125,11 +126,14 @@ def test_annotate_text(osmesa_empty, image_store):


def test_isocontour_functionality(osmesa_fake_amr, image_store):
osmesa_fake_amr.scene.components[0].render_method = "isocontours"
osmesa_fake_amr.scene.components[0].iso_log = False
osmesa_fake_amr.scene.components[0].iso_tol_is_pct = False
osmesa_fake_amr.scene.components[0].iso_tolerance[0] = 1
osmesa_fake_amr.scene.components[0].iso_layers[0] = -29
sc = osmesa_fake_amr.scene
iso = Isolayers(data=sc.components[0].data)
sc.components.append(iso)

sc.components[1].iso_log = False
sc.components[1].iso_tol_is_pct = False
sc.components[1].iso_tolerance[0] = 1
sc.components[1].iso_layers[0] = -29
image_store(osmesa_fake_amr)


Expand Down

0 comments on commit f02a935

Please sign in to comment.