A vector-drawing surface returned by datumhue.draw.new. Hand it to canvas:mount(opts) to mount as a UI element, or call the primitive constructors to add drawables.
Texture sampling mode for this canvas wherever it is drawn (a UI mount, canvas:image, a shader channel). Set nearest to keep pixel art crisp when the canvas is scaled up; the default smooths.
Draw an arc: a pie slice when filled, a curve when stroked.
Example
-- Filled, it is a pie slice: here all but the quarter facing +x.
canvas:arc({
radius = 60,
start_angle = math.pi / 4,
end_angle = 7 * math.pi / 4,
color = color("#dbbc7f"),
})
canvas:text({
pos = vec2(-100, 20),
text = "Drawn text wraps at its max width",
font_size = 20,
max_width = 200,
align = "center",
color = color("#7fbbb3"),
})
Draw a Sprite (from image:sprite / atlas:sprite) onto this canvas as a textured quad (physics, shaders, and canvas:point_query work on it as on any primitive). The returned primitive's cell property repoints the atlas cell to animate. To draw a whole image or another render target, use canvas:image.
Create a tilemap that draws onto this canvas from an Atlas. Returns a TileMap; set cells with tilemap:set(x, y, index) (atlas cell index) and read them with tilemap:get(x, y).
Example
-- A cell of the built-in font is its character code minus 32.local font = datumhue.image.font()
local map = canvas:tilemap(font, { columns = 5, rows = 1 })
for x, code inipairs({ string.byte("HELLO", 1, -1) }) do
map:set(x - 1, 0, code - 32)
end
Remove all primitives on the canvas (hand-drawn shapes, text, images, and chart marks/axes/legends). Plot areas survive but their rendered content is gone.
DrawCanvas:remove
method
DrawCanvas:remove()
Remove the canvas and everything it hosts — primitives, chart content, plot areas, and any UI element mounting it — freeing its render target. Afterwards every other method and property of the handle raises, and calling remove again does nothing. Other handles still holding the texture (a shader channel, a material) keep the last rendered frame.
DrawCanvas:resize
method
DrawCanvas:resize(width: number,height: number)
Resize the canvas render texture. Existing primitives survive (world-space coordinates); any UI element displaying the canvas picks up the new size.
Drive this canvas's camera from the mouse: drag to pan, scroll to zoom (around the cursor). Replaces any prior pan/zoom config on the canvas; call disable_pan_zoom to stop.
Convert element-relative cursor pixels (Y-down) to draw coordinates (Y-up), accounting for camera pan/zoom and the canvas's on-screen placement (letterbox / scale). Returns nil if the canvas is invalid.
Example
-- The element's top-left pixel is the canvas's top-left corner.print(canvas:screen_to_world(vec2(0, 0))) --> vec2(-200.0, 150.0)
Spawn an invisible physics-only body on the canvas. Returns a handle with all physics methods but no visual representation; it takes the add_body() options, and with nothing drawn, omitted sizes are 1 and an omitted radius is 0.5.
Example
-- An invisible floor along the bottom edge.local floor = canvas:body({
pos = vec2(0, -140),
type = "static",
shape = "rect",
width = 400,
height = 20,
})
Create a path builder for arbitrary shapes with bezier curves. The returned builder accepts chainable :move_to / :line_to / :quad_to / :cubic_to / :close calls and is committed with :fill(...) or :stroke(...).
Return the topmost primitive whose drawn shape covers point (canvas-local world space — pipe a cursor through canvas:screen_to_world first), or nil if none. Tests the rendered geometry: a filled shape is hit on its interior, a stroked shape on its outline. Hidden primitives, text, and groups are not hit; for a primitive inside a group, walk .parent to reach the group.
Example
local dot = canvas:circle({ radius = 20 })
-- A new shape can be hit from the next frame on.datumhue.time.wait_frames(1)
print(canvas:point_query(vec2(5, 5)) == dot) --> trueprint(canvas:point_query(vec2(100, 100))) --> nil
Every primitive whose drawn shape covers point (canvas-local world space), topmost first; empty when nothing is hit. Same geometry rules as point_query.
Example
local back = canvas:circle({ radius = 40 })
local front = canvas:circle({ pos = vec3(0, 0, 1), radius = 20 })
datumhue.time.wait_frames(1)
local hits = canvas:point_query_all(vec2(0, 0))
print(#hits, hits[1] == front, hits[2] == back) --> 2 true true
Mount this render target as a UI image element. Returns the new UiElement, or nil if the app has no UI.
Example
canvas:circle({ radius = 40, color = color("#a7c080") })
-- A second view of the same canvas, letterboxed into its box. It-- repeats the first, so it is decorative: screen readers pass over it.
canvas:mount({
width = 240,
height = 80,
fit = "contain",
decorative = true,
})
How the texture maps into the element box; aspect-preserving modes center in the box. "sharp" draws with its own shader, so it takes no material; neither "integer" nor "sharp" takes auto_size. "integer" scales a draw canvas's or scene's size in UI px, so on one whose texels are finer than that (a canvas without pixel_perfect on a HiDPI display, a scene) a texel covers a fraction of the multiple. Pair it with the source's filter set to "nearest".
auto_size
boolean
false
For a draw canvas or scene: resize it to the element's size in UI px whenever the element resizes, so what it draws keeps its size in UI px instead of scaling with the element. A scene renders at its own size, so this also keeps one sharp. Raises on a plain image, whose resolution is fixed (use fit).
accessible_label
string
the image has no accessible name
Accessible name for assistive technology. Images carry no text, so unlike text-bearing widgets they derive no name of their own. Set it on images that convey meaning, and decorative on the rest.
decorative
boolean
false
Keeps the image out of what assistive technology reads, as alt="" does on the web: a screen reader passes over it. Set it on an image that only decorates, such as art beside text that says who or what it shows. Setting both this and accessible_label raises, and so does naming the image later.
Carve a PlotArea out of this canvas for charting. Its primitives draw in fixed layers, back to front: the background, grid lines, area fills, lines, bars (with histograms, heatmaps and box plots), points, reference rules and bands, text annotations, axes and the title, then legends. No option sets a z; within a layer, a later primitive draws over an earlier one. Removing the canvas removes the area. Raises if the canvas was removed.
Example
-- A plot area over the right half of the canvas.local plot = canvas:chart({
pos = vec3(0, -150, 0),
width = 200,
height = 300,
margin = { left = 30, bottom = 20 },
background = color("#7fbbb3"),
})
Carve a rows x cols grid of PlotAreas out of this canvas for faceting / subplots, returned row-major from the top-left. Pass a shared x_scale / y_scale handle to link the cells' axes. Raises if the canvas was removed.