/* * Copyright 2012 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "CurveIntersection.h" #include "Extrema.h" #include "IntersectionUtilities.h" #include "LineParameters.h" static double interp_quad_coords(double a, double b, double c, double t) { double ab = interp(a, b, t); double bc = interp(b, c, t); return interp(ab, bc, t); } static int coincident_line(const Quadratic& quad, Quadratic& reduction) { reduction[0] = reduction[1] = quad[0]; return 1; } static int vertical_line(const Quadratic& quad, Quadratic& reduction) { double tValue; reduction[0] = quad[0]; reduction[1] = quad[2]; int smaller = reduction[1].y > reduction[0].y; int larger = smaller ^ 1; if (findExtrema(quad[0].y, quad[1].y, quad[2].y, &tValue)) { double yExtrema = interp_quad_coords(quad[0].y, quad[1].y, quad[2].y, tValue); if (reduction[smaller].y > yExtrema) { reduction[smaller].y = yExtrema; } else if (reduction[larger].y < yExtrema) { reduction[larger].y = yExtrema; } } return 2; } static int horizontal_line(const Quadratic& quad, Quadratic& reduction) { double tValue; reduction[0] = quad[0]; reduction[1] = quad[2]; int smaller = reduction[1].x > reduction[0].x; int larger = smaller ^ 1; if (findExtrema(quad[0].x, quad[1].x, quad[2].x, &tValue)) { double xExtrema = interp_quad_coords(quad[0].x, quad[1].x, quad[2].x, tValue); if (reduction[smaller].x > xExtrema) { reduction[smaller].x = xExtrema; } else if (reduction[larger].x < xExtrema) { reduction[larger].x = xExtrema; } } return 2; } static int check_linear(const Quadratic& quad, Quadratic& reduction, int minX, int maxX, int minY, int maxY) { int startIndex = 0; int endIndex = 2; while (quad[startIndex].approximatelyEqual(quad[endIndex])) { --endIndex; if (endIndex == 0) { printf("%s shouldn't get here if all four points are about equal", __FUNCTION__); assert(0); } } if (!isLinear(quad, startIndex, endIndex)) { return 0; } // four are colinear: return line formed by outside reduction[0] = quad[0]; reduction[1] = quad[2]; int sameSide; bool useX = quad[maxX].x - quad[minX].x >= quad[maxY].y - quad[minY].y; if (useX) { sameSide = sign(quad[0].x - quad[1].x) + sign(quad[2].x - quad[1].x); } else { sameSide = sign(quad[0].y - quad[1].y) + sign(quad[2].y - quad[1].y); } if ((sameSide & 3) != 2) { return 2; } double tValue; int root; if (useX) { root = findExtrema(quad[0].x, quad[1].x, quad[2].x, &tValue); } else { root = findExtrema(quad[0].y, quad[1].y, quad[2].y, &tValue); } if (root) { _Point extrema; extrema.x = interp_quad_coords(quad[0].x, quad[1].x, quad[2].x, tValue); extrema.y = interp_quad_coords(quad[0].y, quad[1].y, quad[2].y, tValue); // sameSide > 0 means mid is smaller than either [0] or [2], so replace smaller int replace; if (useX) { if (extrema.x < quad[0].x ^ extrema.x < quad[2].x) { return 2; } replace = (extrema.x < quad[0].x | extrema.x < quad[2].x) ^ (quad[0].x < quad[2].x); } else { if (extrema.y < quad[0].y ^ extrema.y < quad[2].y) { return 2; } replace = (extrema.y < quad[0].y | extrema.y < quad[2].y) ^ (quad[0].y < quad[2].y); } reduction[replace] = extrema; } return 2; } bool isLinear(const Quadratic& quad, int startIndex, int endIndex) { LineParameters lineParameters; lineParameters.quadEndPoints(quad, startIndex, endIndex); // FIXME: maybe it's possible to avoid this and compare non-normalized lineParameters.normalize(); double distance = lineParameters.controlPtDistance(quad); return approximately_zero(distance); } // reduce to a quadratic or smaller // look for identical points // look for all four points in a line // note that three points in a line doesn't simplify a cubic // look for approximation with single quadratic // save approximation with multiple quadratics for later int reduceOrder(const Quadratic& quad, Quadratic& reduction) { int index, minX, maxX, minY, maxY; int minXSet, minYSet; minX = maxX = minY = maxY = 0; minXSet = minYSet = 0; for (index = 1; index < 3; ++index) { if (quad[minX].x > quad[index].x) { minX = index; } if (quad[minY].y > quad[index].y) { minY = index; } if (quad[maxX].x < quad[index].x) { maxX = index; } if (quad[maxY].y < quad[index].y) { maxY = index; } } for (index = 0; index < 3; ++index) { if (AlmostEqualUlps(quad[index].x, quad[minX].x)) { minXSet |= 1 << index; } if (AlmostEqualUlps(quad[index].y, quad[minY].y)) { minYSet |= 1 << index; } } if (minXSet == 0x7) { // test for vertical line if (minYSet == 0x7) { // return 1 if all four are coincident return coincident_line(quad, reduction); } return vertical_line(quad, reduction); } if (minYSet == 0xF) { // test for horizontal line return horizontal_line(quad, reduction); } int result = check_linear(quad, reduction, minX, maxX, minY, maxY); if (result) { return result; } memcpy(reduction, quad, sizeof(Quadratic)); return 3; }