Drawing objects#
This section explains how to render and interact with objects in HelloTriangle.
Which objects can be rendered?#
You can render both Mesh and Coords objects.
For meshes, all supported element types can be rendered (see Element types).
Objects are drawn using the draw() function. You can set a color for the
whole mesh with the color parameter, or use the style parameter for
fine-grained control over individual representations:
draw(mymesh, name="mymesh", style={"faces": {"color": "red"}})
draw(mycoords, name="mycoords", style={"points": {"color": "black"}})
Note
If you do not specify a name, a random one will be assigned automatically.
The name will appear in the Visualization Settings panel of the GUI.
Your script may contain multiple draw() statements — all corresponding objects will appear in the 3D viewer.
Choosing colors#
The color parameter is a convenient shorthand: it sets the same color on all
representations (faces, lines, and points, depending on the mesh):
draw(mymesh, name="mymesh", color="red")
For representation-specific colors — or any property beyond color — use the
style parameter described below.
Color values are passed as strings. The following formats are all accepted:
Hexadecimal:
"#990000"Named colors:
"skyblue"(any of the 140 X11 color names)RGB values:
"rgb(255, 0, 0)"or"rgb(100%, 0%, 0%)"HSL values:
"hsl(0, 100%, 50%)"
Rendering scalar fields#
HelloTriangle meshes can contain fields (per-node or per-element) imported from files or added programmatically. These can be visualized directly in the 3D viewer.
Example: adding a nodal field and rendering it
rect = shapes.rectangle(div=1, eltype='quad4')
rect.add_field([0.0, 0.5, 1.0, 2.0], type="node", name="temp")
draw(rect, field='temp')
Field-based coloring is automatically applied when field is specified in
draw().
The types of fields apart from nodal (vertex) fields are:
Element fields (per-element/face data): Use
type="elem"when adding the field.Nodal element fields (per-element-per-node data): Use
type="elemn"when adding the field.
When rendering, HelloTriangle will automatically select the appropriate representation
Advanced styling with the style parameter#
For full control over mesh appearance, use the style parameter with a dictionary.
Pass a sub-dict for each representation you want to style — "faces", "lines",
or "points" — and set properties within it.
Example:
For 2D (surface) and 3D (volumetric) meshes, you can separately control faces, edges (lines), and vertices (points):
# Different colors for faces, edges, and points
draw(mymesh, name="mesh", style={
"faces": {
"color": "#00ff00",
"opacity": 0.9,
"metalness": 0.5,
"roughness": 0.5
},
"lines": {
"color": "#000000",
"width": 2.0,
"opacity": 1.0
},
"points": {
"color": "#ff0000",
"size": 3.0,
"opacity": 1.0
}
})
Available style properties:
Faces (
facesdict):color: Face coloropacity: Face transparencymetalness: Float from 0.0 (non-metallic) to 1.0 (metallic)roughness: Float from 0.0 (smooth) to 1.0 (rough)visible: Boolean to show/hide facestransparencyMode: String, one of"default","simple", or"dithered"colorSource:"solid"or"field"renderOrder: Integer draw order (see Fixing flicker and z-fighting between overlapping objects below)depthOffset: Integer depth bias to resolve overlapping/coincident faces (see Fixing flicker and z-fighting between overlapping objects below)
Lines/Edges (
linesdict):color: Line coloropacity: Line transparencywidth: Line width (float)visible: Boolean to show/hide edgestransparencyMode: String,"default"or"simple"colorSource:"solid"or"field"renderOrder: Integer draw order (see Fixing flicker and z-fighting between overlapping objects below)
Points/Vertices (
pointsdict):color: Point coloropacity: Point transparencysize: Point size (float)visible: Boolean to show/hide pointstransparencyMode: String,"default"or"simple"colorSource:"solid"or"field"renderOrder: Integer draw order (see Fixing flicker and z-fighting between overlapping objects below)
Hiding a mesh entirely
The top-level "hidden" key suspends the whole object from the scene without discarding its
style settings:
draw(mymesh, name="mesh", style={"hidden": True})
This is distinct from setting "visible": False on individual representations (faces,
lines, points). Those flags control which representations are active; hidden operates
at the object level and temporarily overrides all of them at once — the same toggle exposed in the
Visualization Settings panel as the eye icon for each object. Removing "hidden" (or
setting it to False) restores the object with its representations exactly as configured.
Note
Transparency modes:
"default": Standard transparency with depth writing (best for faces)"simple": Disables depth writing when transparent (best for edges/points)"dithered": Alpha hashing for order-independent transparency (faces only)
Fixing flicker and z-fighting between overlapping objects#
When a script draws several objects that occupy the same space — a liquid mesh sitting inside a transparent container, for example — you can run into two distinct visual glitches. They look similar but need different fixes.
An object flickers or disappears as you orbit the camera
This happens with nested transparent objects (e.g. liquid fully enclosed by a transparent container). The viewer normally decides transparent draw order automatically, sorting objects back-to-front by their distance from the camera each frame — but for objects with near-identical centers, that distance comparison is unstable, so which one draws first (and therefore which is visible) can flip as you orbit.
By default, objects already draw in the same order as their draw()
calls in the script, which resolves this for most scenes without any extra
work. If you need to force a specific stacking regardless of script order,
set renderOrder explicitly — higher values draw later, i.e. on top:
draw(container, name="container", style={"faces": {"opacity": 0.35}})
draw(liquid, name="liquid", style={"faces": {"color": "#1e88e5", "renderOrder": 1}})
renderOrder is available on faces, lines, and points.
A speckled or dithered pattern flickers where two surfaces touch
This happens when two faces from different objects are genuinely
coincident — occupying the exact same surface, like a liquid’s surface
flush against the inside wall of its container. renderOrder does not fix
this: it only changes which object draws first, not which one is judged
closer to the camera at that shared surface, so the two still flicker
against each other pixel-by-pixel.
For this case, set depthOffset on the faces sub-dict of whichever
object should reliably win and render in front — higher values render more
in front:
draw(container, name="container", style={"faces": {"opacity": 0.35}})
draw(liquid, name="liquid", style={
"faces": {"color": "#1e88e5", "depthOffset": 1}
})
depthOffset is faces-only and has no automatic default — unlike
renderOrder, it must be set explicitly on the object(s) you know overlap.
A small integer (1 is usually enough) resolves the flicker; there is
normally no need to go higher.
Note
Most scripts never need renderOrder or depthOffset — they only
matter when two or more drawn objects occupy the same physical space.
Objects that merely sit near each other, or touch only at a shared edge,
render correctly with no extra styling.
Restyling or hiding an object after drawing it#
draw() returns a handle to the object it just added. Use it to restyle,
hide, or show that object later in the script — after pick_points(), for
example, once you know which object should stay the visible one:
aorta = draw(anatomy, name="aorta")
inlet, outlet = pick_points("Click the inlet and outlet")
draw(extend_artery(anatomy, inlet, outlet), name="extension", color="red")
aorta.set_style({"faces": {"opacity": 0.3}})
set_style() deep-merges into the object’s current style — the same
semantics as the style parameter at draw time, so properties you do not
mention (color, metalness, other representations, …) are left as they were.
hide() and show() are shorthand for setting the top-level "hidden"
key described above.
If the handle returned by draw() is not in scope — a different function,
or a later part of a long script — resolve it by name with get_drawn():
get_drawn("aorta").set_style({"faces": {"opacity": 0.3}})
get_drawn() raises a clear error if no object with that name has been
drawn yet. Restyling or hiding an object never counts as redrawing it — the
name stays taken, and drawing again with the same name is still an error.
Animating meshes#
Pass a list of frames to draw() to produce an animated object in the 3D viewer.
The viewer shows playback controls (play/pause, scrubber, speed buttons, loop mode) as soon as
any frame has finished loading, so you can start exploring the animation while later frames are
still downloading.
Evenly-spaced frames (simplest — frames get integer times 0, 1, 2, …):
frames = [mesh_at_t0, mesh_at_t1, mesh_at_t2]
draw(frames, name="my_animation", style={"faces": {"color": "red"}})
Explicit physical times (when the time steps matter for display):
times = [0.0, 0.1, 0.25]
frames = [mesh_at_t0, mesh_at_t1, mesh_at_t2]
draw(frames, times=times, name="my_animation")
Animated field visualization#
Field coloring works the same way as for static meshes — pass field= to draw()
and ensure every frame’s mesh carries a field with that name. The colormap range is
automatically derived from the global min/max across all loaded frames and updates
progressively as frames arrive:
data = load("mysimulation.ht5")
frames = []
times = []
for i, t in enumerate(data.times):
mesh = data.get_mesh("PART-1", frame=i)
frames.append(mesh)
times.append(t)
draw(frames, times=times, name="PART-1", field="S_VON_MISES")
GUI options#
After running a script with one or more draw() calls, all rendered objects will appear in the Visualization Settings panel on the right-hand side of the screen.
From this panel you can:
Interactively change object colors
Adjust transparency
For 2D (surface) and 3D (volumetric) meshes, separately control the appearance of faces, edges, and vertices
Copy the current style as Python code to paste into your script
Note
When you re-run a script, any manual changes made in the visualization settings panel will be reset.
To preserve your styling, copy the style from the visualization panel and add it to your script using the style parameter.
Scene-level settings#
Use set_background() and set_camera() to configure scene-level properties.
These settings apply to the entire 3D viewer, not individual objects.
# Set background color
set_background("#f0f0f0")
# Set camera type and position
set_camera({"type": "orthographic"})
set_camera({"type": "perspective", "position": [10, 5, 20], "target": [0, 0, 0]})
# Draw your meshes
draw(mymesh, name="mesh", style={"faces": {"color": "red"}})
set_background(color)
Sets the scene background color. Accepts any CSS color value:
set_background("white")
set_background("#1a1a2e")
set_background("lightblue")
set_camera(config)
Sets the camera type and optional position. Pass a dictionary with any of these keys:
"type":"perspective"(default) or"orthographic""position":[x, y, z]— world-space camera position"target":[x, y, z]— look-at point (default[0, 0, 0])"up":[x, y, z]— up vector (default[0, 1, 0])"fov": field of view in degrees (perspective only)"zoom": zoom multiplier (orthographic only)
set_camera({"type": "perspective", "fov": 60})
set_camera({"type": "orthographic", "zoom": 1.5})
Note
set_background() and set_camera() are typically placed at the beginning of a script.
Settings defined in code take precedence over any manual adjustments made in the visualization panel.
When a setting is removed from the script on rerun, any previously saved manual adjustment for that
setting is restored.
The older set_scene() function (e.g. set_scene(background="#f0f0f0", camera={"type": "orthographic"}))
is still supported for backwards compatibility, but the dedicated functions are preferred.