| /* |
| * 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 "CurveUtilities.h" |
| #include "IntersectionUtilities.h" |
| |
| /* Given a cubic, find the convex hull described by the end and control points. |
| The hull may have 3 or 4 points. Cubics that degenerate into a point or line |
| are not considered. |
| |
| The hull is computed by assuming that three points, if unique and non-linear, |
| form a triangle. The fourth point may replace one of the first three, may be |
| discarded if in the triangle or on an edge, or may be inserted between any of |
| the three to form a convex quadralateral. |
| |
| The indices returned in order describe the convex hull. |
| */ |
| int convex_hull(const Cubic& cubic, char order[4]) { |
| size_t index; |
| // find top point |
| size_t yMin = 0; |
| for (index = 1; index < 4; ++index) { |
| if (cubic[yMin].y > cubic[index].y || (cubic[yMin].y == cubic[index].y |
| && cubic[yMin].x > cubic[index].x)) { |
| yMin = index; |
| } |
| } |
| order[0] = yMin; |
| int midX = -1; |
| int backupYMin = -1; |
| for (int pass = 0; pass < 2; ++pass) { |
| for (index = 0; index < 4; ++index) { |
| if (index == yMin) { |
| continue; |
| } |
| // rotate line from (yMin, index) to axis |
| // see if remaining two points are both above or below |
| // use this to find mid |
| int mask = other_two(yMin, index); |
| int side1 = yMin ^ mask; |
| int side2 = index ^ mask; |
| Cubic rotPath; |
| if (!rotate(cubic, yMin, index, rotPath)) { // ! if cbc[yMin]==cbc[idx] |
| order[1] = side1; |
| order[2] = side2; |
| return 3; |
| } |
| int sides = side(rotPath[side1].y - rotPath[yMin].y); |
| sides ^= side(rotPath[side2].y - rotPath[yMin].y); |
| if (sides == 2) { // '2' means one remaining point <0, one >0 |
| if (midX >= 0) { |
| printf("%s unexpected mid\n", __FUNCTION__); // there can be only one mid |
| } |
| midX = index; |
| } else if (sides == 0) { // '0' means both to one side or the other |
| backupYMin = index; |
| } |
| } |
| if (midX >= 0) { |
| break; |
| } |
| if (backupYMin < 0) { |
| break; |
| } |
| yMin = backupYMin; |
| backupYMin = -1; |
| } |
| if (midX < 0) { |
| midX = yMin ^ 3; // choose any other point |
| } |
| int mask = other_two(yMin, midX); |
| int least = yMin ^ mask; |
| int most = midX ^ mask; |
| order[0] = yMin; |
| order[1] = least; |
| |
| // see if mid value is on same side of line (least, most) as yMin |
| Cubic midPath; |
| if (!rotate(cubic, least, most, midPath)) { // ! if cbc[least]==cbc[most] |
| order[2] = midX; |
| return 3; |
| } |
| int midSides = side(midPath[yMin].y - midPath[least].y); |
| midSides ^= side(midPath[midX].y - midPath[least].y); |
| if (midSides != 2) { // if mid point is not between |
| order[2] = most; |
| return 3; // result is a triangle |
| } |
| order[2] = midX; |
| order[3] = most; |
| return 4; // result is a quadralateral |
| } |
| |
| /* Find the convex hull for cubics with the x-axis interval regularly spaced. |
| Cubics computed as distance functions are formed this way. |
| |
| connectTo0[0], connectTo0[1] are the point indices that cubic[0] connects to. |
| connectTo3[0], connectTo3[1] are the point indices that cubic[3] connects to. |
| |
| Returns true if cubic[1] to cubic[2] also forms part of the hull. |
| */ |
| bool convex_x_hull(const Cubic& cubic, char connectTo0[2], char connectTo3[2]) { |
| double projectedY[4]; |
| projectedY[0] = 0; |
| int index; |
| for (index = 1; index < 4; ++index) { |
| projectedY[index] = (cubic[index].y - cubic[0].y) * (3.0 / index); |
| } |
| int lower0Index = 1; |
| int upper0Index = 1; |
| for (index = 2; index < 4; ++index) { |
| if (approximately_greater_or_equal(projectedY[lower0Index], projectedY[index])) { |
| lower0Index = index; |
| } |
| if (approximately_lesser_or_equal(projectedY[upper0Index], projectedY[index])) { |
| upper0Index = index; |
| } |
| } |
| connectTo0[0] = lower0Index; |
| connectTo0[1] = upper0Index; |
| for (index = 0; index < 3; ++index) { |
| projectedY[index] = (cubic[3].y - cubic[index].y) * (3.0 / (3 - index)); |
| } |
| projectedY[3] = 0; |
| int lower3Index = 2; |
| int upper3Index = 2; |
| for (index = 1; index > -1; --index) { |
| if (approximately_greater_or_equal(projectedY[lower3Index], projectedY[index])) { |
| lower3Index = index; |
| } |
| if (approximately_lesser_or_equal(projectedY[upper3Index], projectedY[index])) { |
| upper3Index = index; |
| } |
| } |
| connectTo3[0] = lower3Index; |
| connectTo3[1] = upper3Index; |
| return (1 << lower0Index | 1 << upper0Index |
| | 1 << lower3Index | 1 << upper3Index) == 0x0F; |
| } |