JUCE 2D Graphics 2: Affine Transformations on Paths
Drawing a Pretty Circle
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.
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:
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.
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.