RosettaCodeData/Task/B-zier-curves-Intersections/Java/b-zier-curves-intersections...

146 lines
4.8 KiB
Java

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public final class BezierCurveIntersection {
public static void main(String[] aArgs) {
QuadCurve vertical = new QuadCurve( new QuadSpline(-1.0, 0.0, 1.0), new QuadSpline(0.0, 10.0, 0.0) );
// QuadCurve vertical represents the Bezier curve having control points (-1, 0), (0, 10) and (1, 0)
QuadCurve horizontal = new QuadCurve( new QuadSpline(2.0, -8.0, 2.0), new QuadSpline(1.0, 2.0, 3.0) );
// QuadCurve horizontal represents the Bezier curve having control points (2, 1), (-8, 2) and (2, 3)
System.out.println("The points of intersection are:");
List<Point> intersects = findIntersects(vertical, horizontal);
for ( Point intersect : intersects ) {
System.out.println(String.format("%s%9.6f%s%9.6f%s", "( ", intersect.aX, ", ", intersect.aY, " )"));
}
}
private static List<Point> findIntersects(QuadCurve aP, QuadCurve aQ) {
List<Point> result = new ArrayList<Point>();
Stack<QuadCurve> stack = new Stack<QuadCurve>();
stack.push(aP);
stack.push(aQ);
while ( ! stack.isEmpty() ) {
QuadCurve pp = stack.pop();
QuadCurve qq = stack.pop();
List<Object> objects = testIntersection(pp, qq);
final boolean accepted = (boolean) objects.get(0);
final boolean excluded = (boolean) objects.get(1);
Point intersect = (Point) objects.get(2);
if ( accepted ) {
if ( ! seemsToBeDuplicate(result, intersect) ) {
result.add(intersect);
}
} else if ( ! excluded ) {
QuadCurve p0 = new QuadCurve();
QuadCurve q0 = new QuadCurve();
QuadCurve p1 = new QuadCurve();
QuadCurve q1 = new QuadCurve();
subdivideQuadCurve(pp, 0.5, p0, p1);
subdivideQuadCurve(qq, 0.5, q0, q1);
stack.addAll(List.of( p0, q0, p0, q1, p1, q0, p1, q1 ));
}
}
return result;
}
private static boolean seemsToBeDuplicate(List<Point> aIntersects, Point aPoint) {
for ( Point intersect : aIntersects ) {
if ( Math.abs(intersect.aX - aPoint.aX) < SPACING && Math.abs(intersect.aY - aPoint.aY) < SPACING ) {
return true;
}
}
return false;
}
private static List<Object> testIntersection(QuadCurve aP, QuadCurve aQ) {
final double pxMin = Math.min(Math.min(aP.x.c0, aP.x.c1), aP.x.c2);
final double pyMin = Math.min(Math.min(aP.y.c0, aP.y.c1), aP.y.c2);
final double pxMax = Math.max(Math.max(aP.x.c0, aP.x.c1), aP.x.c2);
final double pyMax = Math.max(Math.max(aP.y.c0, aP.y.c1), aP.y.c2);
final double qxMin = Math.min(Math.min(aQ.x.c0, aQ.x.c1), aQ.x.c2);
final double qyMin = Math.min(Math.min(aQ.y.c0, aQ.y.c1), aQ.y.c2);
final double qxMax = Math.max(Math.max(aQ.x.c0, aQ.x.c1), aQ.x.c2);
final double qyMax = Math.max(Math.max(aQ.y.c0, aQ.y.c1), aQ.y.c2);
boolean accepted = false;
boolean excluded = true;
Point intersect = new Point(0.0, 0.0);
if ( rectanglesOverlap(pxMin, pyMin, pxMax, pyMax, qxMin, qyMin, qxMax, qyMax) ) {
excluded = false;
final double xMin = Math.max(pxMin, qxMin);
final double xMax = Math.min(pxMax, pxMax);
if ( xMax - xMin <= TOLERANCE ) {
final double yMin = Math.max(pyMin, qyMin);
final double yMax = Math.min(pyMax, qyMax);
if ( yMax - yMin <= TOLERANCE ) {
accepted = true;
intersect = new Point(0.5 * ( xMin + xMax), 0.5 * ( yMin + yMax));
}
}
}
return List.of( accepted, excluded, intersect );
}
private static boolean rectanglesOverlap(double aXa0, double aYa0, double aXa1, double aYa1,
double aXb0, double aYb0, double aXb1, double aYb1) {
return aXb0 <= aXa1 && aXa0 <= aXb1 && aYb0 <= aYa1 && aYa0 <= aYb1;
}
private static void subdivideQuadCurve(QuadCurve aQ, double aT, QuadCurve aU, QuadCurve aV) {
subdivideQuadSpline(aQ.x, aT, aU.x, aV.x);
subdivideQuadSpline(aQ.y, aT, aU.y, aV.y);
}
// de Casteljau's algorithm
private static void subdivideQuadSpline(QuadSpline aQ, double aT, QuadSpline aU, QuadSpline aV) {
final double s = 1.0 - aT;
aU.c0 = aQ.c0;
aV.c2 = aQ.c2;
aU.c1 = s * aQ.c0 + aT * aQ.c1;
aV.c1 = s * aQ.c1 + aT * aQ.c2;
aU.c2 = s * aU.c1 + aT * aV.c1;
aV.c0 = aU.c2;
}
private static record Point(double aX, double aY) {}
private static class QuadSpline {
public QuadSpline(double aC0, double aC1, double aC2) {
c0 = aC0; c1 = aC1; c2 = aC2;
}
public QuadSpline() {
this(0.0, 0.0, 0.0);
}
private double c0, c1, c2;
}
private static class QuadCurve {
public QuadCurve(QuadSpline aX, QuadSpline aY) {
x = aX; y = aY;
}
public QuadCurve() {
this( new QuadSpline(), new QuadSpline() );
}
private QuadSpline x, y;
}
private static final double TOLERANCE = 0.000_000_1;
private static final double SPACING = 10 * TOLERANCE;
}