Hey there JUCE team. I developed a version of Path::addRoundedRectangle() (the version that lets you choose which corners are rounded) that combines itself with Path::addQuadrilateral()
I present to you: addRoundedQuadrilateral!!
usage, as shown in the picture:
g.fillAll(Colours::white);
g.setColour( Colours::black );
Path p = addRoundedQuadrilateral(Point<float>(10,10),
Point<float>(187,29),
Point<float>(50,96),
Point<float>(62,225),
29, 47, 52, 67,
false, true, true, true);
g.strokePath(p, PathStrokeType(2));
for my use case, I just made it return a Path object. the method doesn’t operate on a path member of the path class. Easy enough to modify it to do that. I just didn’t want to bother modifying the Path class.
Here’s the code:
Path MainContentComponent::addRoundedQuadrilateral (const Point<float> p1,
const Point<float> p2,
const Point<float> p3,
const Point<float> p4,
float cs1, float cs2, float cs3, float cs4,
const bool curveTopLeft, const bool curveTopRight,
const bool curveBottomRight, const bool curveBottomLeft )
{
Path p;
Point<float> pA, pB, pC, pD;
if (curveTopLeft)
{
/*
p1 pC pD p2
O--o--o-----------o--o--O
| |
o pB o
| |
o pA o
| |
| |
| |
| |
| |
o o
| |
o o
| |
p4 O--o--o-----------o--o--O p3
*/
roundedQuadHelper(p4, p1, p2, cs1, pA, pB, pC, pD); //calculate pA,pB,pC,pD
p.startNewSubPath( pA );
p.cubicTo(pB, pC, pD);
}
else
{
p.startNewSubPath(p1);
}
if (curveTopRight)
{
/*
p1 pA pB p2
O--o--o-----------o--o--O
| |
o o pC
| |
o o pD
| |
| |
| |
| |
| |
o o
| |
o o
| |
p4 O--o--o-----------o--o--O p3
*/
roundedQuadHelper(p1, p2, p3, cs2, pA, pB, pC, pD);
p.lineTo(pA);
p.cubicTo(pB, pC, pD);
}
else
{
p.lineTo(p2);
}
if (curveBottomRight)
{
/*
p1 p2
O--o--o-----------o--o--O
| |
o o
| |
o o
| |
| |
| |
| |
| |
o o pA
| |
o o pB
| |
p4 O--o--o-----------o--o--O p3
pD pC
*/
roundedQuadHelper(p2, p3, p4, cs3, pA, pB, pC, pD);
p.lineTo(pA);
p.cubicTo(pB, pC, pD);
}
else
{
p.lineTo(p3);
}
if (curveBottomLeft)
{
/*
p1 p2
O--o--o-----------o--o--O
| |
o o
| |
o o
| |
| |
| |
| |
| |
o pD o
| |
o pC o
| |
p4 O--o--o-----------o--o--O p3
pB pA
*/
roundedQuadHelper(p3, p4, p1, cs4, pA, pB, pC, pD);
p.lineTo(pA);
p.cubicTo(pB, pC, pD);
}
else
{
p.lineTo(p4);
}
p.closeSubPath();
return p;
}
void MainContentComponent::roundedQuadHelper(const Point<float> &p1, const Point<float> &p2, const Point<float> &p3, float cs, Point<float> &pA, Point<float> &pB, Point<float> &pC, Point<float> &pD)
{
/*
p2 p3
O--o--o-----------O
| pC pD
o pB
|
o pA
|
|
|
|
|
p1 O
pA is calculated as a point on the line starting at p2 going to p1 with a distance of cs from p2
pB is calculated as a point on the line starting at p2 going to pA with a distance of cs45 from p2
pD is calculated as a point on the line starting at p2 going to p3 with a distance of cs from p2
pC is calculated as a point on the line starting at p2 going to pD with a distance of cs45 from p2
the Path is defined as:
path.startNewSubPath(pA);
path.cubicTo(pB, pC, pD);
https://math.stackexchange.com/questions/134112/find-a-point-on-a-line-segment-located-at-a-distance-d-from-one-endpoint
*/
auto denomHelper = [](const Point<float> &start, const Point<float> &end)
{
return sqrtf((start.x - end.x)*(start.x-end.x) + (start.y-end.y)*(start.y-end.y) );
};
auto pointHelper = [denomHelper](const Point<float> &start, const Point<float> &end, float d)
{
Point<float> p;
p.setX(start.x + (d * (end.x - start.x)) / denomHelper(start,end) );
p.setY(start.y + (d * (end.y - start.y)) / denomHelper(start,end) );
return p;
};
float cs_ = jmin(cs, Line<float>(p2, p1).getLength() * 0.5f);
pA = pointHelper(p2, p1, cs_); //calculate pA
pB = pointHelper(p2, pA, cs_ * 0.45f);
cs_ = jmin(cs, Line<float>(p2, p3).getLength() * 0.5f);
pD = pointHelper(p2, p3, cs_);
pC = pointHelper(p2, pD, cs_ * 0.45f);
}
Maybe it could be added to the Path class? I had a need for it in my current project.
Anyway, hopefully someone finds it useful!