Creating Visualizations
Visualizations in Chartreuse are built from small components. The most important components are:
- Layouts, which define a way to layout data. For example, a layout can be a line or curve, or a scatterplot.
- Layers, which associate a layout with data, and metadata like a name and a scale.
- Plots, which consist of one or more layers, and metadata like axes and title.
So creating a plot consists of creating each of the above components and combining them together. There are some shortcuts that simplify common cases.
Here's an example, taken from the Quick Start. We start with some data, which is always needed for a visualization.
import doodle.core.Point
import scala.util.Random
val data =
List.fill(100)(Point(Random.nextGaussian(), Random.nextGaussian()))
Now we create a Layout
, the first component described above.
import chartreuse.*
import chartreuse.layout.Scatter
val layout = Scatter.default[Point]
The next component is a Layer
, which associates the layout and the data.
The easiest way to create a Layer
is with the toLayer
method on Layout
.
val layer = layout.toLayer(data)
If we want to set metadata we call methods on Layer
, such as withLabel
to give a name to this layer, or withScale
to use a scale different to the normal linear scale.
Now we can create a Plot
. We can convert a Layer
to a Plot
using the toPlot
method.
val plot = layer.toPlot
Builders and Constructors
Builder methods are any method starting with
with
, such aswithLabel
onLayer
; orfor
, such asforThemeable
, onLayout
.
These methods are used to set metadata.
A method beginning with with
will accept a new value for metadata.
So withLabel
will allow you to change the label of a Layer
by passing it the new label.
The example below shows the effect of changing the label of the Layer
we created above.
layer.label
// res0: String = "Layer Label"
layer.withLabel("The New Label").label
// res1: String = "The New Label"
A method beginning with for
accepts a function that is passed the current value of the metdata, and returns the updated value.
This allows you to update just the values of interest within a nested object.
The example below shows how we can modify just the strokeColor
of a Layout's
themeable values using forThemeable
.
import doodle.core.Color
layout
.forThemeable(themeable => themeable.withStrokeColor(Themeable.Override(Some(Color.chartreuse))))
Builder methods always return a modified copy of the object they're called on, so it's always safe to call a builder method even if you used a component in another place. This is a core part of Chartreuse's design philosophy, as described in Core Concepts
In the example above we used toLayer
and toPlot
to convert types. These are convenience methods.
You can, for example, construct a Plot
by calling its constructor but it's much simpler to type .
and follow the auto-complete to turn a Layer
into a Plot
.
Most types have other convenience constructors on their companion object.
For example, you usually don't construct a Scatter
layout by calling it's constructor.
Instead you call the Scatter.default
convenience constructor, and then perhaps change the few metadata settings using the builder methods.