Skip to content

Layer Overview

Layer is a concept that holds the specification of the 4 key components: data, mark, channels and transform. A layer may be complete if all its associated components are not None, or partial if one or more component is None. A layer is created with the hkw.layer method.

l0 = hkw.layer()

Here we have created an empty layer, where all of data, mark, channels and transform are None.

An empty layer cannot be rendered because the data component is required for rendering. Fortunately, it is very easy to build on top of an existing layer with a set of overwrite functions. For example,

l1 = l0.data("shape.obj")

where l1 is a new layer created from l0 layer with the data component set to shape.obj. The method .data is an example of such overwrite functions. The other overwrite functions are .mark, .channel and .transform. These overwrite functions can be chained together based on the fluent interface design pattern.

l2 = l0.data("shape.obj").mark(hkw.mark.Point)

Note that the overwrite functions do not change the caller object (i.e. l0 in the above example). This design allows the base layer l0 to be reused over and over again. Here is a more complex example.

mesh = lagrange.io.load_mesh("shape.obj")
position_attr_name = mesh.attr_name_vertex_to_position

base = (
    hkw.layer()
    .data(mesh)
    .mark(hkw.mark.Point)
    .channel(size=0.1)
    .transform(
        hkw.transform.Filter(
            data=position_attr_name, condition=lambda p: p[0] > 0
        )
    )
)

This visualization shows all vertices of the input mesh with positive X coordinate as spheres with radius 0.1. Note that, in addition to filename, .data method can also take an actual Lagrange SurfaceMesh object.

Lastly, it is also possible to directly specify the components as arguments to hkw.layer method.

base = hkw.layer("shape.obj", mark=hkw.mark.Point)

Layer composition

In the following example, we will demonstrate the idea of layer composition.

base = hkw.layer("shape.obj")

surface_view = base.mark(hkw.mark.Surface)
point_view = base.mark(hkw.mark.Point)
edge_view = base.mark(hkw.mark.Curve)

composite_view = surface_view + point_view + edge_view

Here, surface_view is a visualization of the surface geometry, while point_view and edge_view are the visualizations of vertices and edges of the geometry. The addition operations combines all three views together to form a composite view that visualizes all three elements.

Layer comparison

While + overlays layers in the same coordinate space, the | operator places layers side by side so they can be compared.

comparison = hkw.layer("before.obj") | hkw.layer("after.obj")

This lays out the two layers in a horizontal row, automatically translating them apart so they do not overlap. The original relative scale of each layer is preserved.

The & operator is the vertical analogue of |: it stacks layers in a column instead of a row.

comparison = hkw.layer("before.obj") & hkw.layer("after.obj")

!!! note Python binds & tighter than |, so a | b & c parses as a | (b & c). Parenthesise when mixing the two operators.

For more control, use the juxtapose method, which | and & call with default settings (axis="x" and axis="y" respectively):

comparison = base.juxtapose(
    other,
    axis="y",        # lay out along Y instead of the default X
    gap=0.2,         # spacing between cells, as a fraction of the mean cell size
    normalize=True,  # scale each cell to equal size before placing
)

Each operand of | becomes one cell. Cells may themselves be composite layers, so +, |, and & combine freely:

# Compare a bare surface against the same surface with its wireframe overlaid.
surface = hkw.layer("shape.obj").mark(hkw.mark.Surface)
edges = hkw.layer("shape.obj").mark(hkw.mark.Curve)
comparison = surface | (surface + edges)

Nesting | and & builds a 2-D grid — each operator lays out its own operands along its axis, so a row of cells can be stacked over another, or several rows tiled into a matrix:

top    = a | b           # a row
grid   = (a | b) & c     # the a–b row stacked above c
matrix = (a | b) & (c | d)  # a 2x2 grid

Cells are spaced by their bounding spheres, so they never overlap — even as you rotate each cell in the interactive viewer.