package pixelitor.utils;
import net.jafama.FastMath;
/**
* Calculates whether a point is inside or outside a blurred ellipse.
* It assumes that the outer/inner radius ratios are the same
* for the x and y radii.
*/
public class BlurredEllipse {
private final double cx;
private final double cy;
private final boolean linkedRadius;
private final double innerRadiusY;
private double innerRadius2;
private double innerRadiusX2;
private double innerRadiusY2;
private final double outerRadiusX;
private final double outerRadiusY;
private double outerRadius2;
private double outerRadiusX2;
private double outerRadiusY2;
private final double yRadiusDifference;
public BlurredEllipse(double centerX, double centerY, double innerRadiusX, double innerRadiusY, double outerRadiusX, double outerRadiusY) {
this.cx = centerX;
this.cy = centerY;
this.innerRadiusY = innerRadiusY;
this.outerRadiusX = outerRadiusX;
this.outerRadiusY = outerRadiusY;
linkedRadius = innerRadiusX == innerRadiusY;
if (linkedRadius) {
innerRadius2 = innerRadiusX * innerRadiusX;
outerRadius2 = outerRadiusX * outerRadiusX;
} else {
innerRadiusX2 = innerRadiusX * innerRadiusX;
innerRadiusY2 = innerRadiusY * innerRadiusY;
outerRadiusX2 = outerRadiusX * outerRadiusX;
outerRadiusY2 = outerRadiusY * outerRadiusY;
}
yRadiusDifference = outerRadiusY - innerRadiusY;
}
/**
* Returns 1.0 if the given coordinate is completely inside the ellipse,
* 0.0 if it is completely outside, and
* a number between 0.0 and 1.0 if it is in the blurred area
*/
public double isOutside(int x, int y) {
double dx = x - cx;
double dy = y - cy;
double dist2 = dx * dx + dy * dy;
if (linkedRadius) { // circle
if (dist2 > outerRadius2) { // outside
return 1.0;
} else if (dist2 < innerRadius2) { // innermost region
return 0.0;
} else { // between the inner and outer radius
double distance = Math.sqrt(dist2);
double ratio = (distance - innerRadiusY) / yRadiusDifference; // gives a value between 0 and 1
// double trigRatio = (FastMath.cos(ratio * Math.PI) + 1.0) / 2.0;
// 1- smooth step is faster than cosine interpolation
// http://en.wikipedia.org/wiki/Smoothstep
// http://www.wolframalpha.com/input/?i=Plot[{%281+%2B+Cos[a+*+Pi]%29%2F2%2C+1+-+3+*+a+*+a+%2B+2+*+a+*+a+*a}%2C+{a%2C+0%2C+1}]
double trigRatio = 1 + ratio * ratio * (2 * ratio - 3);
return 1.0 - trigRatio;
}
} else { // ellipsis
double dx2 = dx * dx;
double dy2 = dy * dy;
if (dy2 >= (outerRadiusY2 - (outerRadiusY2 * dx2) / outerRadiusX2)) { // outside
return 1.0;
}
if (dy2 <= (innerRadiusY2 - (innerRadiusY2 * dx2) / innerRadiusX2)) { // innermost region
return 0.0;
} else { // between the inner and outer radius
// we are on an ellipse with unknown a and b semi major/minor axes
// but we know that a/b = outerRadiusX/outerRadiusY
double ellipseDistortion = outerRadiusX / outerRadiusY;
double b = Math.sqrt(ellipseDistortion * ellipseDistortion * dy2 + dx2) / ellipseDistortion;
// now we can calculate how far away we are between the two ellipses
double ratio = (b - innerRadiusY) / yRadiusDifference; // gives a value between 0 and 1
double trigRatio = (FastMath.cos(ratio * Math.PI) + 1.0) / 2.0;
return 1.0 - trigRatio;
}
}
}
}