Diagonal ColourGradient?

I need the effect of a diagonal ColourGradient.

Is this supported in the toolkit?

If not, what would an efficient way to achieve the effect be?

Obviously I could “just” draw 800x400=320,000 points but I’m guessing that’d be slow.

But what about filling in an array and using it as a pixmap or something?

That is supported by setting the two points along that the gradient is aligned.
See the docs for ColourGradient

ColourGradient grad (juce::Colours::black, getLocalBounds().getBottomLeft(), juce::Colours::white, getLocalBounds().getTopRight());

Will be diagonal

1 Like

Thanks! Got it working. Hmmm, I see what it’s doing but its not quite the gradient I had in mind…
In effect, it’s drawing a line between the two points, then finding, for any point on the rectangle, a normal, perpendicular to that line. So for a tall thin rectangle, both top corners are so nearly the same it might as well be a vertical gradient. Instead, I’m looking for one corner to be light, one to be dark, and the other two (even in short wide, or tall thin) rectangles to be half-way between.

I suppose Juce is limited to what the underlying OS offers so that’s that. Still, just in case you know how to do something similar, the effect I’m looking for is as if you do this gradient on a square (such that two corners are half-way between the start and end colors), and then copy that square to a screen area with a different ratio. I think with XWindows I could load a Pixmap, then copy to the Pixmap to screen coordinates with dissimilar ratio, compressing or distorting it. Granted it wouldn’t be pretty with a photo, but when the source is just a gradient it should look fine. Any idea if Juce gives you off-screen canvases and a way to copy them onto screen and have them resize to fit?

It’s just a little math involved. I understand your expectation is only met for squares, but not for other rectangles.

To get the other two corners at the half lightness you need to calculate the points on the perpendicular axis yourself. juce::Point has handy methods for that.

auto diagonale = (getLocalBounds().getTopLeft() - getLocalBounds().getBottomRight()).toFloat();
auto length = diagonale.getDistanceFromOrigin();
auto perpendicular = diagonale.rotatedAboutOrigin (juce::degreesToRadians (90.0f));

juce::ColourGradient grad (juce::Colours::black, perpendicular + diagonale * (0.5f / length),
                           juce::Colours::white, perpendicular - diagonale * (0.5f / length), false); 

I think that is just what math allows with those requirements.

EDIT: I just realise the length is probably not correct, you are looking for the height of the tilted enclosing rectangle…
But you get the idea

EDIT 2:
The length is the height of the triangle:

auto length = diagonale.getDistanceFromOrigin();
auto perpendicular = diagonale.rotatedAboutOrigin (juce::degreesToRadians (90.0f)) / length;
auto height = float (getWidth() * getHeight()) / length;

juce::ColourGradient grad (juce::Colours::black, perpendicular * height,
                           juce::Colours::white, perpendicular * -height, false); 
1 Like

OK Daniel, I’ve read your proposal a couple more times. Frankly I don’t quite get it… but it does exactly what I needed, so big thanks! (BTW if you want to edit your answer for the purposes of future readers, grad() needs a fifth parameter: isRadial = false…)

Good shout, I’ll fix that.

The math is like that:

  • For the corners to have the same colour, the gradient needs to be aligned in perpendicular direction
  • The remaining two corners are in the same distance from the diagonale, because it’s a rectangle
  • Because it’s a rectangle, you can calculate the distance from corner to the diagonale by
    a * b / c where the right angle is between a and b

There’s a LOT of things that are confusing…

First, trying to follow this with graph paper is error-prone because in Juce graphics (common with Win32 and X-Windows) the TOP is 0, and positive is DOWN.

Second, the calculation for “diagonale” gives a negative X/Y value, not the positive I was mentally expecting.

Third, I was wondering where the “origin” was and thanks to “auto” I didn’t know these types were points, I thought they might be line segments :smiley: So now I see that if we’re storing a vector in a data type called point, then distance to origin is vector length. Simple enough–but not obvious at first (I’ve only had about 4 hours programming with Juce so I don’t know any of these methods etc.).

Fourth, what you call “perpendicular” is a unit vector, that you then scale to “height”, which is the distance from the top right point to that “diagonale”. (And distance from “diagonale” to the bottom left point.)

And fifth, I had no idea you could get that height simply by dividing the area by the diagonal length. How did you think of that?!

So in short, grad is an infinite sheet with a gradiated band aligned with that diagonal, and the width of this band is just enough to cover the upper-left and lower-right corners.

Now I wanted to have “white” top left, “black” bottom right. But that flipped some signs and not others, flipping to -90, etc. I ended up building the grad with points (0,0) and height * perpendicular * 2. (After about 12 tries!)

Thanks again Daniel. I have to say the Juce documentation is basic at best, but the forum support is excellent.