Slide 45 of 53

Drawing on a canvas

A chart, in the program's own units
import hive.ui

// Twelve across and a hundred down, so a month of 68 kilos is drawn 68
// long and nothing has to be turned into pixels first.
func chart(harvest: Int[]): ui.View {
	mut ui.Mark[dyn] marks = []
	for i := 0; i < len(harvest); i++ {
		if harvest bounds i {
			tall := hive.conv.itf(harvest[i])
			// A bar is placed from its middle, which is why the floor is
			// 100 and the middle of one standing on it is half a bar up.
			append(marks, ui.bar([ui.paint(ui.Tone.Good())],
				hive.conv.itf(i) + 0.5, 100.0 - tall / 2.0, 0.7, tall))
		}
	}
	append(marks, ui.words([ui.paint(ui.Tone.Muted())], 6.0, 8.0, 7.0, "kg per month"))
	return ui.canvas([
		ui.width(480),
		ui.height(200),
		ui.region(0.0, 0.0, 12.0, 100.0),
	], marks)
}

proc main(): void {
	// The same tree a window would swap in is what a page writes out.
	echo len(ui.page("Harvest", chart([31, 44, 58, 72, 68, 51])))
}
A line, a ring and a word
import hive.ui

proc main(): void {
	// A line through five places — x, y, x, y — and a ring on the last of
	// them, which is a dot and so needed no name of its own.
	trace := ui.path([ui.thick(0.15), ui.paint(ui.Tone.Good())],
		[0.0, 2.0, 1.0, 5.0, 2.0, 4.0, 3.0, 8.0, 4.0, 7.0])
	tip := ui.dot([ui.paint(ui.Tone.Good())], 4.0, 7.0, 0.2)

	// Words are centred on the place, and their height is in region units
	// too — so this one is a twelfth as tall as the plane it sits on.
	label := ui.words([ui.paint(ui.Tone.Muted())], 4.0, 8.4, 0.8, "now")

	view := ui.canvas([
		ui.width(360),
		ui.height(200),
		ui.region(-0.5, 0.0, 5.5, 10.0),
	], [trace, tip, label])

	echo ui.html(view)
}
canvas(attrs, marks) is a widget like any other and a Mark is a value like any other, so what a canvas holds is what is on it right now. There is no drawing context, no frame to schedule and nothing to clear: a chart is rebuilt from the state exactly the way the rest of the page is, and the same tree a window swaps in is what page writes out — so a chart drawn for a program is a chart served by a web server with nothing changed.There are five marks, and each one is here for the reason a shape is: it cannot be made out of the others. dot is a filled circle, bar a rectangle, path a line through a run of points, patch those same numbers filled, and words text placed on the plane. A ring is a dot with no fill, a grid is a vector of paths, an arrow is a path — and none of those needed a name of its own.Everything on a canvas is said in the program's own units. region(x, y, width, height) says which part of the plane the box shows, and a map of a circuit written in metres draws a car nine metres long nine long. paint(Tone) is a mark's colour and thick(Float) is how wide a path is drawn, in the region's units too — so a map zoomed in has thicker roads, which is what zooming in means. The box may be any shape; what is drawn keeps its own and is centred in it.Two placements are worth stating outright, because both are the sort of thing a program discovers by being half a width out. A bar is placed by its middle, like everything else on a canvas — one mark placed by its corner would be the one that is always wrong. And words are centred on the place they are given, with their height in the region's units rather than in points, because a label on a plan is as big as the thing it labels.