blob: b03eb46828ec0a14128fa17b4e39834a21931fb3 [file] [log] [blame]
caryclark@google.com639df892012-01-10 21:46:10 +00001#include "CubicIntersection.h"
2#include "IntersectionUtilities.h"
3
4/* Given a cubic, find the convex hull described by the end and control points.
5 The hull may have 3 or 4 points. Cubics that degenerate into a point or line
6 are not considered.
7
8 The hull is computed by assuming that three points, if unique and non-linear,
9 form a triangle. The fourth point may replace one of the first three, may be
10 discarded if in the triangle or on an edge, or may be inserted between any of
11 the three to form a convex quadralateral.
12
13 The indices returned in order describe the convex hull.
14*/
15int convex_hull(const Cubic& cubic, char order[4]) {
16 size_t index;
17 // find top point
18 size_t yMin = 0;
19 for (index = 1; index < 4; ++index) {
20 if (cubic[yMin].y > cubic[index].y || (cubic[yMin].y == cubic[index].y
21 && cubic[yMin].x > cubic[index].x)) {
22 yMin = index;
23 }
24 }
25 order[0] = yMin;
26 int midX = -1;
27 int backupYMin = -1;
28 for (int pass = 0; pass < 2; ++pass) {
29 for (index = 0; index < 4; ++index) {
30 if (index == yMin) {
31 continue;
32 }
33 // rotate line from (yMin, index) to axis
34 // see if remaining two points are both above or below
35 // use this to find mid
36 int mask = other_two(yMin, index);
37 int side1 = yMin ^ mask;
38 int side2 = index ^ mask;
39 Cubic rotPath;
40 if (!rotate(cubic, yMin, index, rotPath)) { // ! if cbc[yMin]==cbc[idx]
41 order[1] = side1;
42 order[2] = side2;
43 return 3;
44 }
45 int sides = side(rotPath[side1].y - rotPath[yMin].y);
46 sides ^= side(rotPath[side2].y - rotPath[yMin].y);
47 if (sides == 2) { // '2' means one remaining point <0, one >0
48 if (midX >= 0) {
49 printf("%s unexpected mid\n", __FUNCTION__); // there can be only one mid
50 }
51 midX = index;
52 } else if (sides == 0) { // '0' means both to one side or the other
53 backupYMin = index;
54 }
55 }
56 if (midX >= 0) {
57 break;
58 }
59 if (backupYMin < 0) {
60 break;
61 }
62 yMin = backupYMin;
63 backupYMin = -1;
64 }
65 if (midX < 0) {
66 midX = yMin ^ 3; // choose any other point
67 }
68 int mask = other_two(yMin, midX);
69 int least = yMin ^ mask;
70 int most = midX ^ mask;
71 order[0] = yMin;
72 order[1] = least;
73
74 // see if mid value is on same side of line (least, most) as yMin
75 Cubic midPath;
76 if (!rotate(cubic, least, most, midPath)) { // ! if cbc[least]==cbc[most]
77 order[2] = midX;
78 return 3;
79 }
80 int midSides = side(midPath[yMin].y - midPath[least].y);
81 midSides ^= side(midPath[midX].y - midPath[least].y);
82 if (midSides != 2) { // if mid point is not between
83 order[2] = most;
84 return 3; // result is a triangle
85 }
86 order[2] = midX;
87 order[3] = most;
88 return 4; // result is a quadralateral
89}
90
91/* Find the convex hull for cubics with the x-axis interval regularly spaced.
92 Cubics computed as distance functions are formed this way.
93
94 connectTo0[0], connectTo0[1] are the point indices that cubic[0] connects to.
95 connectTo3[0], connectTo3[1] are the point indices that cubic[3] connects to.
96
97 Returns true if cubic[1] to cubic[2] also forms part of the hull.
98*/
99bool convex_x_hull(const Cubic& cubic, char connectTo0[2], char connectTo3[2]) {
100 double projectedY[4];
101 projectedY[0] = 0;
102 int index;
103 for (index = 1; index < 4; ++index) {
104 projectedY[index] = (cubic[index].y - cubic[0].y) * (3.0 / index);
105 }
106 int lower0Index = 1;
107 int upper0Index = 1;
108 for (index = 2; index < 4; ++index) {
109 if (approximately_greater(projectedY[lower0Index], projectedY[index])) {
110 lower0Index = index;
111 }
112 if (approximately_lesser(projectedY[upper0Index], projectedY[index])) {
113 upper0Index = index;
114 }
115 }
116 connectTo0[0] = lower0Index;
117 connectTo0[1] = upper0Index;
118 for (index = 0; index < 3; ++index) {
119 projectedY[index] = (cubic[3].y - cubic[index].y) * (3.0 / (3 - index));
120 }
121 projectedY[3] = 0;
122 int lower3Index = 2;
123 int upper3Index = 2;
124 for (index = 1; index > -1; --index) {
125 if (approximately_greater(projectedY[lower3Index], projectedY[index])) {
126 lower3Index = index;
127 }
128 if (approximately_lesser(projectedY[upper3Index], projectedY[index])) {
129 upper3Index = index;
130 }
131 }
132 connectTo3[0] = lower3Index;
133 connectTo3[1] = upper3Index;
134 return (1 << lower0Index | 1 << upper0Index
135 | 1 << lower3Index | 1 << upper3Index) == 0x0F;
136}
137