Using Algebras

This section gives recipes for using Doodle's algebras, targetting one backend or working across multiple backends. You should be familiar with the concepts behind Doodle to understand everything here.

Using a Single Backend

Using a single backend should be straightforward:

  1. Import the Doodle core, syntax, and the backend you're using.
  2. Construct pictures using the methods on the Picture object.
  3. Compose Pictures using the methods provided by the syntax imports.

For example, the following imports are used for the Java2D backend:

import doodle.java2d.*
import doodle.core.*
import doodle.syntax.all.*

Given these imports we can create pictures. To construct atomic elements we call the constructors on Picture.

val smallCircle = Picture.circle(100)
val largeSquare = Picture.square(200)

When we have some pictures we can compose them using the methods provided by the syntax import.

val composition =
  smallCircle
    .fillColor(Color.crimson)
    .noStroke
    .on(largeSquare.fillColor(Color.midnightBlue).strokeWidth(5))

Naturally we might want to draw our creation. For this we need another import

import cats.effect.unsafe.implicits.global

Now we can call draw

composition.draw()

producing the masterpiece shown below.

A crimson circle on a midnight blue square

As we've seen, using the algebras is mostly as simple as using Image. The only issue you might see is that the error messages can be a bit harder to interpret.

Using Backend Specific Features

Using backend specific features follows the same pattern as using a single backend. Here's an example using the Bitmap algebra, which is currently only supported by the java2d backend. (The bitmap used is the Old God created by Kevin Chaloux and released under Creative Commons.)

The first step is, as before, to import the Doodle core, Java2D backend, syntax extensions, and the Cats Effect runtime.

import doodle.core.*
import doodle.java2d.*
import doodle.syntax.all.*
import cats.effect.unsafe.implicits.global

Now we can go about creating our picture, using the read method to load a bitmap image.

val redCircle = Picture.circle(100).strokeColor(Color.red)
val twoRedCircles = redCircle.beside(redCircle)
val oldGod = Picture.read("old-god.png")

We can combine this bitmap value with other pictures in the usual way.

twoRedCircles.above(oldGod)

We can then draw it using the draw method, which produces the output shown below.

Double suns rising over the Old God

Creating Cross-Backend Pictures

Targeting multiple backends requires a little bit more work than working with a single backend. We can't use the usual constructor methods on a Picture object, as those methods target a specific backend and we want to work across multiple backends. However, every constructor method has a syntax equivalent that we can call. So to create a circle we can write

circle(100)

Once we know how to do this, everything proceeds as before. Here's a complete example.

val redCircle = circle(100).strokeColor(Color.red)
val rad = text("Doodle is rad")
  
val picture = rad.on(redCircle)

We can then draw the picture as before. In this case we get the output below.

Doodle is rad, and so is tagless final style

Using Raw Algebras

We never need to call methods on algebras directly. Doodle provides the Picture abstraction and lots of syntax to avoid this. However, if some reason you did want to use algebras directly here is how to do it. Understanding this does help a bit in understanding the utilities that Doodle provides to avoid using algebras directly.

To use algebras directly, write a method with a parameter that is the algebras that you need. For example, if we were to write a simple program using Layout, Style, and Shape we might write the following.

import doodle.algebra.*

// Two red circles beside each other
def twoRedCircles[Alg <: Layout & Style & Shape](algebra: Alg): algebra.Drawing[Unit] = {
  val redCircle = algebra.strokeColor(algebra.circle(100), Color.red)
  
  algebra.beside(redCircle, redCircle)
}

This is not a convenient way to write code, so don't do it unless you have a good reason.

Picture→