blob: e742a265e06a823d75ff82752e13c9ac9c8cbbfc [file] [log] [blame]
caryclark@google.com639df892012-01-10 21:46:10 +00001#include "CubicIntersection.h"
2#include "Extrema.h"
3#include "IntersectionUtilities.h"
4#include "LineParameters.h"
5
6#ifdef MAYBE_USEFUL_IN_THE_FUTURE
7static double interp_quad_coords(double a, double b, double c, double t)
8{
9 double ab = interp(a, b, t);
10 double bc = interp(b, c, t);
11 return interp(ab, bc, t);
12}
13#endif
14
15static double interp_cubic_coords(const double* src, double t)
16{
17 double ab = interp(src[0], src[2], t);
18 double bc = interp(src[2], src[4], t);
19 double cd = interp(src[4], src[6], t);
20 double abc = interp(ab, bc, t);
21 double bcd = interp(bc, cd, t);
22 return interp(abc, bcd, t);
23}
24
25static int coincident_line(const Cubic& cubic, Cubic& reduction) {
26 reduction[0] = reduction[1] = cubic[0];
27 return 1;
28}
29
30static int vertical_line(const Cubic& cubic, Cubic& reduction) {
31 double tValues[2];
32 reduction[0] = cubic[0];
33 reduction[1] = cubic[3];
34 int smaller = reduction[1].y > reduction[0].y;
35 int larger = smaller ^ 1;
36 int roots = SkFindCubicExtrema(cubic[0].y, cubic[1].y, cubic[2].y, cubic[3].y, tValues);
37 for (int index = 0; index < roots; ++index) {
38 double yExtrema = interp_cubic_coords(&cubic[0].y, tValues[index]);
39 if (reduction[smaller].y > yExtrema) {
40 reduction[smaller].y = yExtrema;
41 continue;
42 }
43 if (reduction[larger].y < yExtrema) {
44 reduction[larger].y = yExtrema;
45 }
46 }
47 return 2;
48}
49
50static int horizontal_line(const Cubic& cubic, Cubic& reduction) {
51 double tValues[2];
52 reduction[0] = cubic[0];
53 reduction[1] = cubic[3];
54 int smaller = reduction[1].x > reduction[0].x;
55 int larger = smaller ^ 1;
56 int roots = SkFindCubicExtrema(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x, tValues);
57 for (int index = 0; index < roots; ++index) {
58 double xExtrema = interp_cubic_coords(&cubic[0].x, tValues[index]);
59 if (reduction[smaller].x > xExtrema) {
60 reduction[smaller].x = xExtrema;
61 continue;
62 }
63 if (reduction[larger].x < xExtrema) {
64 reduction[larger].x = xExtrema;
65 }
66 }
67 return 2;
68}
69
70// check to see if it is a quadratic or a line
71static int check_quadratic(const Cubic& cubic, Cubic& reduction,
72 int minX, int maxX, int minY, int maxY) {
73 double dx10 = cubic[1].x - cubic[0].x;
74 double dx23 = cubic[2].x - cubic[3].x;
75 double midX = cubic[0].x + dx10 * 3 / 2;
76 if (!approximately_equal(midX - cubic[3].x, dx23 * 3 / 2)) {
77 return 0;
78 }
79 double dy10 = cubic[1].y - cubic[0].y;
80 double dy23 = cubic[2].y - cubic[3].y;
81 double midY = cubic[0].y + dy10 * 3 / 2;
82 if (!approximately_equal(midY - cubic[3].y, dy23 * 3 / 2)) {
83 return 0;
84 }
85 reduction[0] = cubic[0];
86 reduction[1].x = midX;
87 reduction[1].y = midY;
88 reduction[2] = cubic[3];
89 return 3;
90}
91
92static int check_linear(const Cubic& cubic, Cubic& reduction,
93 int minX, int maxX, int minY, int maxY) {
94 int startIndex = 0;
95 int endIndex = 3;
96 while (cubic[startIndex].approximatelyEqual(cubic[endIndex])) {
97 --endIndex;
98 if (endIndex == 0) {
99 printf("%s shouldn't get here if all four points are about equal", __FUNCTION__);
100 assert(0);
101 }
102 }
103 LineParameters lineParameters;
104 lineParameters.cubicEndPoints(cubic, startIndex, endIndex);
105 double normalSquared = lineParameters.normalSquared();
106 double distance[2]; // distance is not normalized
107 int mask = other_two(startIndex, endIndex);
108 int inner1 = startIndex ^ mask;
109 int inner2 = endIndex ^ mask;
110 lineParameters.controlPtDistance(cubic, inner1, inner2, distance);
111 double limit = normalSquared * SquaredEpsilon;
112 int index;
113 for (index = 0; index < 2; ++index) {
114 double distSq = distance[index];
115 distSq *= distSq;
116 if (distSq > limit) {
117 return 0;
118 }
119 }
120 // four are colinear: return line formed by outside
121 reduction[0] = cubic[0];
122 reduction[1] = cubic[3];
123 int sameSide1;
124 int sameSide2;
125 bool useX = cubic[maxX].x - cubic[minX].x >= cubic[maxY].y - cubic[minY].y;
126 if (useX) {
127 sameSide1 = sign(cubic[0].x - cubic[1].x) + sign(cubic[3].x - cubic[1].x);
128 sameSide2 = sign(cubic[0].x - cubic[2].x) + sign(cubic[3].x - cubic[2].x);
129 } else {
130 sameSide1 = sign(cubic[0].y - cubic[1].y) + sign(cubic[3].y - cubic[1].y);
131 sameSide2 = sign(cubic[0].y - cubic[2].y) + sign(cubic[3].y - cubic[2].y);
132 }
133 if (sameSide1 == sameSide2 && (sameSide1 & 3) != 2) {
134 return 2;
135 }
136 double tValues[2];
137 int roots;
138 if (useX) {
139 roots = SkFindCubicExtrema(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x, tValues);
140 } else {
141 roots = SkFindCubicExtrema(cubic[0].y, cubic[1].y, cubic[2].y, cubic[3].y, tValues);
142 }
143 for (index = 0; index < roots; ++index) {
144 _Point extrema;
145 extrema.x = interp_cubic_coords(&cubic[0].x, tValues[index]);
146 extrema.y = interp_cubic_coords(&cubic[0].y, tValues[index]);
147 // sameSide > 0 means mid is smaller than either [0] or [3], so replace smaller
148 int replace;
149 if (useX) {
150 if (extrema.x < cubic[0].x ^ extrema.x < cubic[3].x) {
151 continue;
152 }
153 replace = (extrema.x < cubic[0].x | extrema.x < cubic[3].x)
154 ^ cubic[0].x < cubic[3].x;
155 } else {
156 if (extrema.y < cubic[0].y ^ extrema.y < cubic[3].y) {
157 continue;
158 }
159 replace = (extrema.y < cubic[0].y | extrema.y < cubic[3].y)
160 ^ cubic[0].y < cubic[3].y;
161 }
162 reduction[replace] = extrema;
163 }
164 return 2;
165}
166
167/* food for thought:
168http://objectmix.com/graphics/132906-fast-precision-driven-cubic-quadratic-piecewise-degree-reduction-algos-2-a.html
169
170Given points c1, c2, c3 and c4 of a cubic Bezier, the points of the
171corresponding quadratic Bezier are (given in convex combinations of
172points):
173
174q1 = (11/13)c1 + (3/13)c2 -(3/13)c3 + (2/13)c4
175q2 = -c1 + (3/2)c2 + (3/2)c3 - c4
176q3 = (2/13)c1 - (3/13)c2 + (3/13)c3 + (11/13)c4
177
178Of course, this curve does not interpolate the end-points, but it would
179be interesting to see the behaviour of such a curve in an applet.
180
181--
182Kalle Rutanen
183http://kaba.hilvi.org
184
185*/
186
187// reduce to a quadratic or smaller
188// look for identical points
189// look for all four points in a line
190 // note that three points in a line doesn't simplify a cubic
191// look for approximation with single quadratic
192 // save approximation with multiple quadratics for later
193int reduceOrder(const Cubic& cubic, Cubic& reduction, ReduceOrder_Flags allowQuadratics) {
194 int index, minX, maxX, minY, maxY;
195 int minXSet, minYSet;
196 minX = maxX = minY = maxY = 0;
197 minXSet = minYSet = 0;
198 for (index = 1; index < 4; ++index) {
199 if (cubic[minX].x > cubic[index].x) {
200 minX = index;
201 }
202 if (cubic[minY].y > cubic[index].y) {
203 minY = index;
204 }
205 if (cubic[maxX].x < cubic[index].x) {
206 maxX = index;
207 }
208 if (cubic[maxY].y < cubic[index].y) {
209 maxY = index;
210 }
211 }
212 for (index = 0; index < 4; ++index) {
213 if (approximately_equal(cubic[index].x, cubic[minX].x)) {
214 minXSet |= 1 << index;
215 }
216 if (approximately_equal(cubic[index].y, cubic[minY].y)) {
217 minYSet |= 1 << index;
218 }
219 }
220 if (minXSet == 0xF) { // test for vertical line
221 if (minYSet == 0xF) { // return 1 if all four are coincident
222 return coincident_line(cubic, reduction);
223 }
224 return vertical_line(cubic, reduction);
225 }
226 if (minYSet == 0xF) { // test for horizontal line
227 return horizontal_line(cubic, reduction);
228 }
229 int result = check_linear(cubic, reduction, minX, maxX, minY, maxY);
230 if (result) {
231 return result;
232 }
233 if (allowQuadratics && (result = check_quadratic(cubic, reduction, minX, maxX, minY, maxY))) {
234 return result;
235 }
236 memcpy(reduction, cubic, sizeof(Cubic));
237 return 4;
238}