Drawing to the Screen
Drawing a picture to the screen is usually the end goal of using Doodle.
The usual way to draw a Picture
is by calling the draw
method. Using the Java2D backend will produce output in a window, while the SVG backend can produce output inside a web page.
The examples below use the Java2D backend, but the general principles work with other backends.
Imports
You need the normal imports to do anything with Doodle. Here are the imports for Picture
and the JVM backend.
import doodle.core.*
import doodle.syntax.all.*
import doodle.java2d.*
import cats.effect.unsafe.implicits.global
For some of the examples below we also need
import cats.effect.{IO, Resource}
Drawing
The normal way to draw output is by calling the draw
method.
val picture = Picture.circle(100).strokeColor(Color.crimson)
picture.draw()
When using the Java2D, the output will appear in separate window.
As explained in the concepts chapter, Doodle has the concept of a frame and a canvas. A canvas is an area where a picture can be drawn, and a frame describes how to create a canvas. If you don't explicitly tell Doodle what frame you want, it uses a default. The default has a white background, is a little bit larger than the picture being drawn, and centers the picture in the middle of it.
The Java2D Frame allows you specify the size of the window, a background color, and more. Here's an example where we change the size and background color.
val frame =
Frame.default.withSize(300, 300).withBackground(Color.midnightBlue)
Once we have a frame, we can pass it to drawWithFrame
.
picture.drawWithFrame(frame)
Sometimes you'll want to draw several pictures on the same canvas. By repeatedly drawing to the same canvas we can, for example, create animations. To do this we need to work with the underling IO
and Resource
types that the conveniences above hide.
Drawing with IO and Resource
It can be useful to convert a Picture[A]
to an IO[A]
. This could be because the picture is part of a larger program using IO
, you are working with an Resource[IO, Canvas]
, or you want to access the A
value that is discarded by the draw
variants discussed above. The drawToIO
method does this conversion.
picture.drawToIO()
There are also variants drawWithFrameToIO
and drawWithCanvasToIO
.
picture.drawWithFrameToIO(frame)
Using drawWithCanvasToIO
requires we get hold of a Canvas
. We call the canvas()
method on a Frame
to do so. This returns a Resource[IO, Canvas]
.
val canvas: Resource[IO, Canvas] = frame.canvas()
When we have a Resource[IO, Canvas]
we can use
it to access the Canvas
it manages. This is the idiomatic way to work with a Resource[IO, Canvas]
.
canvas.use(c => picture.drawWithCanvasToIO(c))
Once you have an IO
, you can run it in the usual way as part of an IOApp
, with unsafeRunSync
, or one of the other methods.