vidartf / unray

Jupyter Widgets for volume rendering of functions on unstructured tetrahedral meshes.
BSD 3-Clause "New" or "Revised" License
14 stars 4 forks source link

Restructure and clean up data flow #4

Closed martinal closed 7 years ago

martinal commented 7 years ago

I have some ideas on paper, will write it up later. This can also be a testing ground for generic data/encoding/plot/figure widgets to extract into a separate library. To be discussed!

martinal commented 7 years ago

One draft of widget based user interface:

#  maybe it is nice to have a mesh class
mesh = Mesh (cells, coordinates )

#  or maybe it can be passed as any other array:
f = Data(array)

# array may be same object, forced upload anyway
f.update(array)

# explicit control of data naming and sharing
group = DataGroup()
group.add(fname, f)

group.update(fname, array)  # calls f.update(array)

group.add(name, array)  # shortcut, wraps array in Data

encoding1 = Encoding()
encoding1.add (name, desc)
encoding1.add ("channel name", { field: "data name", ...})
encoding1.update(name, desc)
encoding1.update((name, "field"), "other data name")

# create some plot configurations
surf = Surface(mesh, data, encoding1)
xray = XRay(mesh, data, encoding2)

# setup figure
f = Figure(axes, [surf, xray], renderer=pythreejs.Renderer(...), camera=...)
martinal commented 7 years ago

Another attempt, dropping the explicit data dict and encoding dict and using widget object references instead of names. Trying to mimic some of the bqplot interface here.

class Mesh(Widget):
    celltype = Unicode("tetrahedron").tag(sync=True)
    cells = Array().tag(sync=True)
    coordinates = Array().tag(sync=True)

class Field(Widget):
    mesh = Instance(Mesh).tag(sync=True)
    space = Unicode("P1").tag(sync=True)
    values = Array().tag(sync=True)

class ScalarLUT(Widget):
    values = Array().tag(sync=True)
    interpolation = Unicode("linear").tag(sync=True)

class ColorLUT(Widget):
    values = Array().tag(sync=True)
    interpolation = Unicode("linear").tag(sync=True)

class Indicators(Widget):
    values = Array().tag(sync=True)
    dim = CInt().tag(sync=True)
# Unstructured mesh
mesh = Mesh(cells=cells, coordinates=coordinates)

# Cellwise constant field over mesh
field0 = Field(mesh=mesh, values=values, space="P0")
field0.update(new_values)

# Cellwise continuous linear field over mesh
field1 = Field(mesh=mesh, values=values, space="P1")
field1.update(new_values)

# Cellwise discontinuous linear field over mesh
field2 = Field(mesh=mesh, values=values, space="D1")
field2.update(new_values)

# Cell indicator values over mesh
ind3 = Indicators(mesh=mesh, values=values, dim=3)
ind3.update(new_values)

# Facet indicator values over mesh
ind2 = Indicators(mesh=mesh, values=values, dim=2)
ind2.update(new_values)

# Lookup tables
lut0 = ScalarLUT(values=[0.2, 0.4, 0.7, 1.0], interpolation="nearest")
lut1 = ScalarLUT(values=[0, 1], interpolation="linear")
lut2 = ColorLUT(values=[[1,0,0], [0,0,1]], interpolation="linear")

# Plot configurations can be packaged in dicts
surf_encoding = {
    "color": { "field": field0, "lut": lut0 },
    "wireframe": { "enable": true, "width": 0.05, "color": "black" },
    "restrict": { "indicators": ind3, "enabled_value": 7 },
}
# But the encoding is really just passed as keyword arguments
surf = Surface(mesh, **surf_encoding)
xray = XRay(mesh, density=field1)

# Setup figure
axes = [...]
marks = [surf, xray]  # bqplot calls this marks, from data visualization literature
fig = Figure(axes, marks,
              # Allow passing renderer and camera but create these by default if not provided:
              renderer=pythreejs.Renderer(...), camera=pythreejs.Camera(...))
display(fig)
martinal commented 7 years ago

@vidartf pushed a notebook with some further refinement of the above to this branch: https://github.com/martinal/jupyter-unray/tree/widget-redesign

martinal commented 7 years ago

Current setup is similar to the above. Needs some iteration, fixes, and polish, but closing this unspecific issue.