Ladan's Personal Blog on ML and Software Development

JUCE 2D Graphics 2: Affine Transformations on Paths

Drawing a Pretty Circle

final result

In the last post, we used Path objects and clipping tricks to shade the intersection of two overlapping cricles.

In this post, I’ll take it a step further and introduce transformation operations that can be applied to Paths to efficiently draw things like the pleasant looking circle at the top of this post.

Drawing the Circle

First we create the circle, which is just a Path that can be created using the method Path::addElipse(). We then draw it like any other Path.


        /* Step 1 */
        path1.addEllipse(150, 50, 200, 200);
        
        g.strokePath(path1, juce::PathStrokeType(1.5f));

Partitioning the Circle

We’ll rely on Path again to help us partition the circle we drew.

One way to create a new Path object is to create a Rectangle. We can use squares, which are Rectangles as far as JUCE is concerned, to divide the circle through its origin. One way to do that is to create 4 square Paths, one for each quadrant of the circle. However that seems too easy and a bit boring.

Rectangles and circle

This is code for drawing the first rectangle, and shading it the color plum.

        /*step 2 */
        
        g.setColour(juce::Colours::plum);
        path2.addRectangle(150, 50, 100, 100);
        g.reduceClipRegion(path1);
        g.fillPath(path2);

And here is what it looks like:

Clip region for top left

Affine Transforms

As mentioned earlier, instead of drawing four adjacent rectangles, we’ll just use one and slide it around. The translation is an Affine transform. Affine transforms are a very cool compact linear algebra operation that encodes a variety of translation and transformations in a single matrix. JUCE allows for Paths to have an Affine transformation matrix applied to them, mapping the Path to Path.

The code below applies three different Affine translations to the first and only path we careted for the purpose of dividing the circle. Then each path is filled with a distinct color, bounded by the edge of the circle, creating 4 distinct quadrants of equal area.

        /* Step 3 */

        /* Move the square from top left to to the bottom left location using Affine transformation */
        juce::AffineTransform affine = juce::AffineTransform::translation(0, 100);

        g.setColour(juce::Colours::mediumorchid);
        path2.applyTransform(affine);
        g.fillpath(path2);


        /* Move the square from bottom left to bottom right */

        affine = juce::AffineTransform::translation(100, 0);

        g.setColour(juce::Colours::blueviolet);
        path2.applyTransform(affine);
        g.fillpath(path2);

        /* Move the square form bottom right to top right */

        affine = juce::AffineTransform::translation(0, -100);

        g.setColour(juce::Colours::lavenderblush);
        path2.applyTransform(affine);
        g.fillpath(path2);

The final result is very easy on the eyes and definitely feels spring-like.

final result

I like Affine transformations, I may write a deeper post on them, as I encountered the OpenCV2 getAffineTransform() and had to implement a similar fucntion in JUCE which did not exist. This led to reading through OpenCV2 source and finding some interesting surprises under the hood.