Paths are a way to construct complex shapes, implemented by the Path algebra and the OpenPath ClosedPath types. A path specifies how to control a virtual pen to draw a line. There are three different commands that a path can contain:

Here's an example of a path. Notice that it has examples of all of the different components above.

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

val feather =
    .lineTo(100, 100)
    .curveTo(90, 75, 90, 25, 10, 10)
    .moveTo(100, 100)
    .curveTo(75, 90, 25, 90, 10, 10)

Drawing this path creates the output below.

A path with straight lines and bezier curves

You probably noticed this is a ClosedPath, which suggests there is also an OpenPath. This is correct. The difference is how the path ends. If a ClosedPath doesn't end at the point it started (which is the origin), it will have a straight line inserted joining the end to the start. An OpenPath will not.

Here's an example showing the difference. We create a curve, drawn as an open and as a closed path. Notice how the open path is just a curve, while the closed path has an extra line added joining the start and end points.

val open =
  OpenPath.empty.curveTo(90, 0, 100, 10, 50, 50).path.strokeColor(

val closed =
  ClosedPath.empty.curveTo(90, 0, 100, 10, 50, 50).path.strokeColor(

val curves = open.beside(closed)
A curve drawn as an open and as a closed path


ClosedPath and OpenPath are both part of doodle.core, and not tied to either algebras or Image.

You can create a path by calling the empty method on either ClosedPath or OpenPath, and then calling methods on the resulting object. This is the approach used above. You can also creates instances of PathElement and create a path from a List[PathElement].

To convert a path to a Picture you can use the path syntax method, which is demonstrated in the examples above, or use the path method on the Picture object.

To create an Image from a path, use the path method on Image. Here's an example.

import doodle.image.*

val openPath = OpenPath.empty.lineTo(100, 100)

val closedPath = ClosedPath.empty.lineTo(100, 100)

val path1 = Image.path(openPath)
val path2 = Image.path(closedPath)


There are several utilities to create common shapes. These are available as both Picture and Image constructors.

These, and other methods, also exist on OpenPath and ClosedPath. You can also create these paths as a List[PathElement] by calling the methods on PathElement.

There is also interpolatingSpline, which creates a curve that intersects a given sequence of points. Here's an example.

val points =
  for (x <- yield Point(x, x.degrees.sin * 100)

val curve = Picture.interpolatingSpline(points.toList)
A curve that intersects a sequence of points