Getting Started With Paper.js: Paths and Geometry

In my previous tutorial, I covered the installation process and project hierarchy in Paper.js. This time I will teach you about paths, segments, and their manipulation. This will enable you to create complex shapes using the library. After that, I would like to cover a few basic geometric principles that Paper.js is based on.

Working With Paths

Paths in Paper.js are represented by a sequence of segments that are connected by curves. A segment is basically a point and its two handles, which define the location and direction of the curves. Not defining segment handles results in straight lines instead of curves.

Once you define a new path using the new Path() constructor, you can add segments to it with the help of the path.add(segment) function. As this function supports multiple arguments, you can also add many segments at once. Let's say you want to insert a new segment at a specific index inside an existing path. You can do so by using the path.insert(index, segment) function. Similarly, to remove a segment at a specific index, you can use the path.removeSegment(index) function. Both these functions use zero-based indexing. This implies that using path.removeSegment(3) will remove the fourth segment. You can close all the paths you draw with the path.closed property. It will join the first and last segments of the path together.

Till now all our paths had straight lines. To create curved paths without specifying handles for each segment, you can use the path.smooth() function. This function calculates the optimal values for handles of all the segments in a path in such a way that the curve that goes through them is smooth. The segments themselves don't change their location, and if you have specified handle values for any of the segments, those values will be ignored. The code below uses all the functions and properties we discussed to create four paths, two of which are curved.

First, we create a new path and then add segments to it. Using path.insert(3, new Point(180, 110)) inserts a new segment in place of the fourth one and moves the fourth segment to the fifth position. I have set fullySelected to true to show you all points and handles for each curve. For the second path, I have used the path.smooth() function to make the curve smooth. Removing the fourth segment using cPath.removeSegment(3) gives us our original shape without any index-based insertions. You can verify this by commenting out aPath.insert(3, new Point(180, 110)); in this CodePen demo. This is the final result of all our manipulations up to this point:

Predefined Shapes

Paper.js supports some basic shapes out of the box. For instance, to create a circle, you can simply use the new Path.Circle(center, radius) constructor. Similarly, you can use the new Path.Rectangle(rect) constructor to create a rectangle. You can either specify the top-left and bottom-right corners or specify the top-left corner and the size of the rectangle. To draw a rectangle with rounded corners, you can use the new Path.RoundedRectangle(rect, size) constructor where the size parameter determines the size of rounded corners.

If you want to create an n-sided regular polygon, you can do so by using the new Path.RegularPolygon(center, numSides, radius) constructor. The parameter center determines the center of our polygon, and radius determines the radius of our polygon.

The code below will generate all the shapes that we just discussed.

The first rectangle that we create is based on co-ordinate points. The next one uses the first point to determine the top-left corner of our rectangle and then uses the size value to plot the rest of the points. In the third rectangle, we have additionally specified a radius for our rectangle. The first radius parameter decides the horizontal curvature, and the second parameter determines the vertical curvature.

The last two shapes just use the RegularPolygon constructor to create a triangle and a dodecagon. The embedded demo below shows the result of our code.

Simplifying and Flattening Paths

There are two ways to create a circle. The first one is to create a lot of segments without any handles and place them closely together. This way, even though they will be connected by a straight line, the overall shape will still be closer to a circle. The second way is to use just four segments with appropriate values for their handles. This can save a lot of memory and still give us the desired results. 

Most of the time we can remove quite a few segments from a path without significant changes in its shape. The library provides a simple path.simplify([tolerance]) function to achieve this result. The tolerance parameter is optional. It is used to specify the maximum distance to which the path-simplifying algorithm can deviate from its original path. It is set to 2.5 by default. If you set the parameter to a higher value, the final curve will be a bit smoother, with fewer segment points, but the deviation could be significant. Similarly, a lower value will result in very little deviation but include many more segments.

You can also convert the curves in a path to straight lines using the path.flatten(maxDistance) function. While flattening a path, the library tries to keep the distance between segments as close to maxDistance as possible.

In the code above, I first created a polygon using the RegularPolygon function discussed above. I have intentionally set the selected property to true so that all the segments from these paths are visible. Then I cloned the second polygon from the first one and used the simplify function on it. This reduced the number of segments to just five.

In the third polygon, I have set the tolerance parameter to a higher value. This further reduces the number of segments. You can see that all the paths still have the same basic shape. In the final path, I have used the flatten(maxDistance) function to flatten our curve. The algorithm tries to keep the shape as close to the original one as possible while still respecting the maxDistance constraint. Here is the final result:

Geometry and Mathematics

Paper.js has some basic data types like Point, Size, and Rectangle to describe geometric attributes of graphical items. They are abstract representations of geometric values like location or dimension. A point just describes a two-dimensional location, and size describes abstract dimensions in two-dimensional space. The rectangle here signifies an area enclosed by the top-left point, its width and its height. It is different from the rectangular path that we discussed earlier. Unlike the path, it is not an item. You can read more about them all in this Paper.js tutorial.

You can perform basic mathematical operations—addition, subtraction, multiplication, and division—on points and sizes. All the operations below are valid:

Besides these basic operations, you can also perform some rounding operations or generate random values for points and sizes. Consider the following examples:

The random() function generates random values between 0 and 1. You can multiply them with a Point or Size object of appropriate value to get the desired results.

This concludes the basic mathematics that you need to be familiar with to create something useful with Paper.js.

Final Thoughts

After following this tutorial, you should be able to create a variety of paths and shapes, flatten curves, or simplify complex paths. Now you also have a basic understanding of various mathematical operations that you can perform with Paper.js. By combining all that you have learnt in this and the previous tutorial of the series, you should be able to create complex polygons on different layers and blend them together. You should also be able to insert and remove segments from a path to get a desired shape.

If you’re looking for additional JavaScript resources to study or to use in your work, check out what we have available in the Envato marketplace.

If you have any questions regarding this tutorial, please let me know in the comments.



Related Articles