# distance to the edge of a rounded rectangle

I'm trying to figure out a formula to create a rounded rectangle... but I need to do it by finding the distance from $(0, 0)$ to the line/edge of the rectangle given the angle provided as a vector.

Ok.. so here is the formula to plot the rectangle: $\left(\frac{x}{a}\right)^n + \left(\frac{y}{b}\right)^n = 1$

In my usage a and b will both be 1. Thus the formula can be simplified into $x^n + y^n = 1$

http://www.wolframalpha.com/input/?i=x%5E4+%2B+y%5E4+%3D+1

My input will be a point in space that can represent a vector. Say it's (.5,.5). The angle of this vector is $45^\circ$ and therefore it intersects with the plotted line at a specific distance from $(0, 0)$. I don't need to know the $x$, $y$ of the plot per se. I need to know the distance to that point. If I have the x,y I can easily calculate the distance. But maybe there is a way to calculate the distance directly.

I know I have: $\left(\frac{x}{a}\right)^n + \left(\frac{y}{b}\right)^n = 1$

and I have $d = \sqrt{x^2 + y^2}$

But alone that isn't enough info for me to figure it out. I can see a pattern in there though. OH crap they're the same formula, except that $n=2$, where I want $n=4$ (or $n>4$). When $d = 1$ the square root of $1 = 1$.

I was able to solve for d like this - but I don't know if this is helping...

1 = x^n + y^n

0 = x^n + y^n - 1

d = sqr(x^2 + y^2)

0 = sqr(x^2 + y^2) - d

d = 1 + sqr(x^2 + y^2) - x^n - y^n

But still.. I don't know x and y on the plot. I'm given x,y in space and have to find the distance to the plotted line where it intersects with that vector.

Maybe rather I need some sort of intersection type formula?? Any ideas?

EDIT ----

Really what I need is to figure out the distance to the line at any given angle.

EDIT 2 --

Here are the knowns.. I have an x,y out in space. Therefore I have an angle and a distance for that vector. I also know the base size of the rounded rectangle.

If it was a circle it's easy cause the radius of a circle is constant.

My answer here might not be very mathematical in terms of equation syntax.. but I'm a programmer, not a mathematician..

It should be possible to calculate the difference between the radius of the circle (known if the object were a circle) and the "radius" at any point on the rounded rectangle.

At an angle of $\theta$ they are the same. At and angle of 90 they are the same. But at points between the two they are not.

If the radius of the circle is 1.. then you have $d = 1 + \sqrt{x^2 + y^2} - x^n - y^n$ for the "radius" or distance to the edge of the rounded rectangle.

The difference between the two is delta $d = 1 - x^n - y^n$

So if our desired rectangle is S in size instead of 1 - S is a scale factor over the 1.

If I normalize the vector $(x, y)$ and make it's length 1. Then use that x, y to figure out the delta d. I can then scale that dd by S and add it to d to get the distance at that angle.

Checking to see if it works in code.

• Why not just write that you need to calculate $\sqrt{x^2+y^2}$ for any $(x,y)$ satisfying x^n+y^n=1$? Or, at the very least, 'the distance from zero to that point' - you have saved me 20'... – automaton 3 Sep 22 '13 at 8:02 • Sorry if I didn't phrase my question correctly. I have my solution now but I won't checkmark my own answer. – badweasel Sep 23 '13 at 11:12 • Sorry for snapping at you :). I tend to be very pedantic when it comes to scientific writing... – automaton 3 Sep 23 '13 at 11:37 • It's totally cool. I'm a game coder not a mathematician, yet I use complex math all the time - like matrix math. A lot of it is above my head if I try to figure it out but I know enough to do what I need. On this one I knew what I needed, but maybe not the best way to explain it. I should have probably also put the code in to make it more clear. Thanks for your help! – badweasel Sep 23 '13 at 12:07 ## 5 Answers Let the vector be$(a,b)$. The line it is on is$y=\frac ba x$(assuming it is not vertical.) Plug this into your superellipse and get$x^n+\left(\frac ba x\right)^n=1$or$x^n= \frac 1{1+(\frac ba)^n}=\frac {a^n}{a^n+b^n}, x=\left(\frac {a^n}{a^n+b^n}\right)^{\frac 1n}$for the intersection point. The curve$x^n+y^n=1$can be parameterized by$(x(\theta),y(\theta)) = (\cos^{2/n}\theta,\sin^{2/n}\theta)$. Let your input point be$(x_*,y_*); you're looking to minimize the (square of the) distance \begin{align} d(\theta) &= \big(x_*-\cos^{2/n}\theta\big)^2 + \big(y_*-\sin^{2/n}\theta\big)^2 \\ &= x_*^2+y_*^2 - 2 \big(x_*\cos^{2/n}\theta + y_*\sin^{2/n}\theta\big)+\cos^{4/n}\theta + \sin^{4/n}\theta . \end{align} Critical points occur, after writing(x_*,y_*)=(r\cos\phi,r\sin\phi), at \begin{align} 0 &= r \left(\cos\phi \sin\theta \cos^{2/n-1}\theta - \sin\phi \cos\theta \sin^{2/n-1}\theta\right) + \sin^{4/n-1}\theta \cos\theta - \cos^{4/n-1}\theta \sin\theta . \end{align} It's easy to see why\theta=\phi$for$n=2\$ (ball case) & probably impossible to explicitly solve the eq. for any other case.

If you work w/ Lagrange multipliers, you need to consider

$$D(x,y,\lambda) = (x-x_*)^2 + (y-y_*)^2+ 2\lambda \big(x^n+y^n-1\big) .$$

The critical points satisfy the system

\begin{align} x + n \lambda x^{n-1} & = x_* \\ y + n \lambda y^{n-1} & = y_* \\ x^n + y^n & = 1 \end{align}

Same comment as above - no solution I can see.

I solved the problem by using my second approach above. Again.. not sure what the actual single equation would be cause I'm not a mathematician.

Known values are an x,y vector from 0,0 to a point in space, and the desired radius if the shape were a circle.

1 = sqrt(x^2 + y^2) is a circle

1 = sqrt(xb^n + yb^n) is my rounded rectangle, also known as a superellipse.

h = sqrt(x^2 + y^2) can be scaled on either side to get the same result. As sqrt((xS)^2 + (yS)^2) = S * sqrt(x^2 + y^2). Therefore we can scale h or x & y and get the same result,

And when h = 1, you can skip the sqrt part as the sqrt(1) = 1. That formula becomes 1=xx+yy. Same goes for the xb^n + yb^n.

So.. by taking my vector (x,y) and normalizing it to a length of 1, I can figure out what the distance to the point in the super ellipse would be at a scale of 1. Then subtract the known distance of the normalized vector (which is 1), and get a delta d at that vector angle at a scale of 1. Then scale it up to whatever size the target distance is and add it to that target distance.

There are some other tweaks in my formula to simplify it and avoid the use of sqrt for speed. I can't give you the complete explanation, but this was what I was looking for...

vec2 vector = normalize (coordinate);

float deltaD = 1.5 - (vector.xvector.xvector.xvector.x + vector.yvector.yvector.yvector.y)/4.0;

Just based on your final post, it seems that you were doing this mathematics to achieve a blur. If that is indeed the case, you'd be much better off with a convolution with a gaussian kernel. Despite being faster, it would also produce much nicer results. (It's a very standard blurring method).

• No it was used to create a vignette, which did have a soft edge. But no Gaussian is needed because the amount of vignette is determined by the same distance formula. – badweasel Jul 10 '15 at 5:12

For what its worth here is a solution using Qt that finds the intersecting point between a line starting at the center of a rounding rectangle to some place outside of the shape. You have to specify the Rectangle, Radius of the rounded edges and a point B which is outside of the rounded rectangle.

#include <QApplication>
#include <QDebug>
#include <QGraphicsItem>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QLine>
#include <QPainterPath>
#include <QPoint>
#include <QRect>
#include <limits>
#include <math.h>

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool circleLineIntersectPoint( double aRadius, QPointF aA, QPointF aB, QPointF aC, QPointF &aIntersection)
{
bool tReturn = true; // false = not intersection point, true == insersected
aIntersection = QPointF();
QPointF tD;
tD.setX(aB.x() - aA.x());
tD.setY(aB.y() - aA.y());
if ((tD.x() == 0) && (tD.y() == 0))
{
// A and B are the same points, no way to calculate intersection
tReturn = false;
return tReturn;
}

double tDL = (tD.x() * tD.x() + tD.y() * tD.y());
double tT = ((aC.x() - aA.x()) * tD.x() + (aC.y() - aA.y()) * tD.y()) / tDL;

// point on a line nearest to circle center
QPointF tNearest;
tNearest.setX(aA.x() + tT * tD.x());
tNearest.setY(aA.y() + tT * tD.y());

QLineF tLine(tNearest.x(), tNearest.y(), aC.x(), aC.y());
double tDist = tLine.length();

{
// line segment touches circle; one intersection point
aIntersection = tNearest;

if (tT < 0 || tT > 1)
{
// intersection point is not actually within line segment
tReturn = false;
}
}
{
// two possible intersection points

double tDT = sqrt(aRadius * aRadius - tDist * tDist) / sqrt(tDL);

// intersection point nearest to A
//    double tT1 = tT - tDT;
//    QPointF tI1;
//    tI1.setX(aA.x() + tT1 * tD.x());
//    tI1.setY(aA.y() + tT1 * tD.y());
//    if (tT1 < 0 || tT1 > 1)
//    {
//      // intersection point is not actually within line segment
//      tReturn = false;
//    }

//    if(tReturn)  // intersection point farthest from A
//    {
double tT2 = tT + tDT;
QPointF tI2;
tI2.setX(aA.x() + tT2 * tD.x());
tI2.setY(aA.y() + tT2 * tD.y());
if (tT2 < 0 || tT2 > 1)
{
// intersection point is not actually within line segment
tReturn = false;
}
else
aIntersection = tI2;
//    }
}
else
{
// no intersection
tReturn = false;
}

return tReturn;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int rectangleLineIntersectPoint(QRectF aRect, QPointF aB, QPointF &aPoint)
{
// Define a line from the center of the rectangle to point B
QLineF tLine(aRect.center(), aB);

QPointF tReturn;
int tType = 0;

// See if the line intersects the top line of the rectangle
QLineF tTopLine(aRect.topLeft(), aRect.topRight());
if(QLineF::BoundedIntersection == tTopLine.intersect(tLine, &aPoint)) tType = 1; // Top Line
else
{
// See if the line intersects the right line of the rectangle
QLineF tRightLine (aRect.topRight(), aRect.bottomRight());
if(QLineF::BoundedIntersection == tRightLine.intersect(tLine, &aPoint)) tType = 2; // Right Line
else
{
// See if the line intersects the bottom line of the rectangle
QLineF tBottomLine(aRect.bottomLeft(), aRect.bottomRight());
if(QLineF::BoundedIntersection == tBottomLine.intersect(tLine, &aPoint)) tType = 3; // Bottom Line
else
{
// See if the line intersects the left line of the rectangle
QLineF tLeftLine(aRect.topLeft(), aRect.bottomLeft());
if(QLineF::BoundedIntersection == tLeftLine.intersect(tLine, &aPoint)) tType = 4; // Left Line
else
tType = 0; // This should never happen since the center of the line is the center of the rect
}
}
}
return tType;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool roundedRectLineIntersectionPoint(double aRadius, QRectF aRect, QPointF aB, QPointF &aPoint)
{
bool tReturn = true; // False = not intersection point

// Compute the rectangle to line intersection point
int tType = rectangleLineIntersectPoint(aRect, aB, aPoint);

//  Based on the type figure out if we need to compute the intersection with
//  rounded part of the rounded rectangle
QPointF tC; // Center of the circle that makes up the rounded part
bool tInRoundedEdge = true;
switch(tType)
{
case 0: tReturn = false; tInRoundedEdge = false; break;

// Top Line
case 1: if(aPoint.x() <= aRect.left() + aRadius) // Top Left
else
if(aPoint.x() >= aRect.right() - aRadius) // Top Right
else
tInRoundedEdge = false;
break;

// Right Line
case 2: if(aPoint.y() <= aRect.top() + aRadius) // Top right
else
if(aPoint.y() >= aRect.bottom() - aRadius) // Bottom Right
else
tInRoundedEdge = false;
break;
// Bottom Line
case 3: if(aPoint.x() <= aRect.left() + aRadius) // Bottom Left
else
if(aPoint.x() >= aRect.right() - aRadius) // Bottom Right
else
tInRoundedEdge = false;
break;
// Left Line
case 4: if(aPoint.y() <= aRect.top() + aRadius) // Top Left
else
if(aPoint.y() >= aRect.bottom() - aRadius) // Bottom left
else
tInRoundedEdge = false;
break;
}

if(tReturn && tInRoundedEdge)
tReturn = circleLineIntersectPoint( aRadius, aRect.center(), aB, tC, aPoint);

return tReturn;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
QApplication app(argc, argv);

QGraphicsView view;
QGraphicsScene *scene = new QGraphicsScene;
view.setScene(scene);

QRectF tRect(-300, -150, 600, 300);
double tLineLength = 800.0;

QPainterPath tPath;

QPointF tPoint;
for(double tIndex = 0; tIndex < 360; tIndex+=1)
{
double tX = tLineLength * sin(tIndex * kDegToRad);
double tY = tLineLength * cos(tIndex * kDegToRad);