OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2012 Google Inc. | 2 * Copyright 2012 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 #include "SkIntersections.h" | 7 #include "SkIntersections.h" |
8 #include "SkOpAngle.h" | 8 #include "SkOpAngle.h" |
| 9 #include "SkOpSegment.h" |
9 #include "SkPathOpsCurve.h" | 10 #include "SkPathOpsCurve.h" |
10 #include "SkTSort.h" | 11 #include "SkTSort.h" |
11 | 12 |
12 #if DEBUG_SORT || DEBUG_SORT_SINGLE | 13 #if DEBUG_ANGLE |
13 #include "SkOpSegment.h" | 14 #include "SkString.h" |
| 15 |
| 16 static const char funcName[] = "SkOpSegment::operator<"; |
| 17 static const int bugChar = strlen(funcName) + 1; |
14 #endif | 18 #endif |
15 | 19 |
16 // FIXME: this is bogus for quads and cubics | 20 /* Angles are sorted counterclockwise. The smallest angle has a positive x and t
he smallest |
17 // if the quads and cubics' line from end pt to ctrl pt are coincident, | 21 positive y. The largest angle has a positive x and a zero y. */ |
18 // there's no obvious way to determine the curve ordering from the | |
19 // derivatives alone. In particular, if one quadratic's coincident tangent | |
20 // is longer than the other curve, the final control point can place the | |
21 // longer curve on either side of the shorter one. | |
22 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf | |
23 // may provide some help, but nothing has been figured out yet. | |
24 | 22 |
25 /*( | 23 #if DEBUG_ANGLE |
| 24 static bool CompareResult(SkString* bugOut, const char* append, bool compare
) { |
| 25 bugOut->appendf(append); |
| 26 bugOut->writable_str()[bugChar] = "><"[compare]; |
| 27 SkDebugf("%s\n", bugOut->c_str()); |
| 28 return compare; |
| 29 } |
| 30 |
| 31 #define COMPARE_RESULT(append, compare) CompareResult(&bugOut, append, compa
re) |
| 32 #else |
| 33 #define COMPARE_RESULT(append, compare) compare |
| 34 #endif |
| 35 |
| 36 bool SkOpAngle::calcSlop(double x, double y, double rx, double ry, bool* result)
const{ |
| 37 double absX = fabs(x); |
| 38 double absY = fabs(y); |
| 39 double length = absX < absY ? absX / 2 + absY : absX + absY / 2; |
| 40 int exponent; |
| 41 (void) frexp(length, &exponent); |
| 42 double epsilon = ldexp(FLT_EPSILON, exponent); |
| 43 SkPath::Verb verb = fSegment->verb(); |
| 44 SkASSERT(verb == SkPath::kQuad_Verb || verb == SkPath::kCubic_Verb); |
| 45 // FIXME: the quad and cubic factors are made up ; determine actual values |
| 46 double slop = verb == SkPath::kQuad_Verb ? 4 * epsilon : 512 * epsilon; |
| 47 double xSlop = slop; |
| 48 double ySlop = x * y < 0 ? -xSlop : xSlop; // OPTIMIZATION: use copysign / _
copysign ? |
| 49 double x1 = x - xSlop; |
| 50 double y1 = y + ySlop; |
| 51 double x_ry1 = x1 * ry; |
| 52 double rx_y1 = rx * y1; |
| 53 *result = x_ry1 < rx_y1; |
| 54 double x2 = x + xSlop; |
| 55 double y2 = y - ySlop; |
| 56 double x_ry2 = x2 * ry; |
| 57 double rx_y2 = rx * y2; |
| 58 bool less2 = x_ry2 < rx_y2; |
| 59 return *result == less2; |
| 60 } |
| 61 |
| 62 /* |
26 for quads and cubics, set up a parameterized line (e.g. LineParameters ) | 63 for quads and cubics, set up a parameterized line (e.g. LineParameters ) |
27 for points [0] to [1]. See if point [2] is on that line, or on one side | 64 for points [0] to [1]. See if point [2] is on that line, or on one side |
28 or the other. If it both quads' end points are on the same side, choose | 65 or the other. If it both quads' end points are on the same side, choose |
29 the shorter tangent. If the tangents are equal, choose the better second | 66 the shorter tangent. If the tangents are equal, choose the better second |
30 tangent angle | 67 tangent angle |
31 | 68 |
32 maybe I could set up LineParameters lazily | 69 FIXME: maybe I could set up LineParameters lazily |
33 */ | 70 */ |
34 static int simple_compare(double x, double y, double rx, double ry) { | 71 bool SkOpAngle::operator<(const SkOpAngle& rh) const { // this/lh: left-hand; r
h: right-hand |
35 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ? | 72 double y = dy(); |
36 return y < 0; | 73 double ry = rh.dy(); |
| 74 #if DEBUG_ANGLE |
| 75 SkString bugOut; |
| 76 bugOut.printf("%s _ id=%d segId=%d tStart=%1.9g tEnd=%1.9g" |
| 77 " | id=%d segId=%d tStart=%1.9g tEnd=%1.9g ", funcName, |
| 78 fID, fSegment->debugID(), fSegment->t(fStart), fSegment->t(fEnd), |
| 79 rh.fID, rh.fSegment->debugID(), rh.fSegment->t(rh.fStart), rh.fSegment->
t(rh.fEnd)); |
| 80 #endif |
| 81 double y_ry = y * ry; |
| 82 if (y_ry < 0) { // if y's are opposite signs, we can do a quick return |
| 83 return COMPARE_RESULT("1 y * ry < 0", y < 0); |
37 } | 84 } |
38 if (y == 0 && ry == 0 && x * rx < 0) { | 85 // at this point, both y's must be the same sign, or one (or both) is zero |
39 return x < rx; | 86 double x = dx(); |
| 87 double rx = rh.dx(); |
| 88 if (x * rx < 0) { // if x's are opposite signs, use y to determine first or
second half |
| 89 if (y < 0 && ry < 0) { // if y's are negative, lh x is smaller if posit
ive |
| 90 return COMPARE_RESULT("2 x_rx < 0 && y < 0 ...", x > 0); |
| 91 } |
| 92 if (y >= 0 && ry >= 0) { // if y's are zero or positive, lh x is smalle
r if negative |
| 93 return COMPARE_RESULT("3 x_rx < 0 && y >= 0 ...", x < 0); |
| 94 } |
| 95 SkASSERT((y == 0) ^ (ry == 0)); // if one y is zero and one is negative
, neg y is smaller |
| 96 return COMPARE_RESULT("4 x_rx < 0 && y == 0 ...", y < 0); |
40 } | 97 } |
41 double x_ry = x * ry; | 98 // at this point, both x's must be the same sign, or one (or both) is zero |
42 double rx_y = rx * y; | 99 if (y_ry == 0) { // if either y is zero |
43 double cmp = x_ry - rx_y; | 100 if (y + ry < 0) { // if the other y is less than zero, it must be smalle
r |
44 if (!approximately_zero(cmp)) { | 101 return COMPARE_RESULT("5 y_ry == 0 && y + ry < 0", y < 0); |
45 return cmp < 0; | 102 } |
46 } | 103 if (y + ry > 0) { // if a y is greater than zero and an x is positive,
non zero is smaller |
47 if (approximately_zero(x_ry) && approximately_zero(rx_y) | 104 return COMPARE_RESULT("6 y_ry == 0 && y + ry > 0", (x + rx > 0) ^ (y
== 0)); |
48 && !approximately_zero_squared(cmp)) { | 105 } |
49 return cmp < 0; | 106 // at this point, both y's are zero, so lines are coincident or one is d
egenerate |
50 } | 107 SkASSERT(x * rx != 0); // and a degenerate line should haven't gotten t
his far |
51 return -1; | 108 } |
52 } | 109 // see if either curve can be lengthened before trying the tangent |
53 | 110 if (fSegment->other(fEnd) != rh.fSegment // tangents not absolutely identic
al |
54 bool SkOpAngle::operator<(const SkOpAngle& rh) const { | 111 && rh.fSegment->other(rh.fEnd) != fSegment) { // and not intersecti
ng |
55 double x = dx(); | |
56 double y = dy(); | |
57 double rx = rh.dx(); | |
58 double ry = rh.dy(); | |
59 int simple = simple_compare(x, y, rx, ry); | |
60 if (simple >= 0) { | |
61 return simple; | |
62 } | |
63 // at this point, the initial tangent line is coincident | |
64 // see if edges curl away from each other | |
65 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide) | |
66 || !approximately_zero(rh.fSide))) { | |
67 // FIXME: running demo will trigger this assertion | |
68 // (don't know if commenting out will trigger further assertion or not) | |
69 // commenting it out allows demo to run in release, though | |
70 return fSide < rh.fSide; | |
71 } | |
72 // see if either curve can be lengthened and try the tangent compare again | |
73 if (/* cmp && */ (*fSpans)[fEnd].fOther != rh.fSegment // tangents not abso
lutely identical | |
74 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersect
ing | |
75 SkOpAngle longer = *this; | 112 SkOpAngle longer = *this; |
76 SkOpAngle rhLonger = rh; | 113 SkOpAngle rhLonger = rh; |
77 if (longer.lengthen() | rhLonger.lengthen()) { | 114 if ((longer.lengthen(rh) | rhLonger.lengthen(*this)) // lengthen both |
78 return longer < rhLonger; | 115 && (fUnorderable || !longer.fUnorderable) |
| 116 && (rh.fUnorderable || !rhLonger.fUnorderable)) { |
| 117 #if DEBUG_ANGLE |
| 118 bugOut.prepend(" "); |
| 119 #endif |
| 120 return COMPARE_RESULT("10 longer.lengthen(rh) ...", longer < rhLonge
r); |
79 } | 121 } |
80 } | 122 } |
81 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_z
ero(y)) | 123 if (y_ry != 0) { // if they aren't coincident, look for a stable cross produ
ct |
82 || (rh.fVerb == SkPath::kLine_Verb | 124 // at this point, y's are the same sign, neither is zero |
83 && approximately_zero(rx) && approximately_zero(ry))) { | 125 // and x's are the same sign, or one (or both) is zero |
| 126 double x_ry = x * ry; |
| 127 double rx_y = rx * y; |
| 128 if (!fComputed && !rh.fComputed) { |
| 129 if (!AlmostEqualUlps(x_ry, rx_y)) { |
| 130 return COMPARE_RESULT("7 !fComputed && !rh.fComputed", x_ry < rx
_y); |
| 131 } |
| 132 } else { |
| 133 // if the vector was a result of subdividing a curve, see if it is s
table |
| 134 bool sloppy1 = x_ry < rx_y; |
| 135 bool sloppy2 = !sloppy1; |
| 136 if ((!fComputed || calcSlop(x, y, rx, ry, &sloppy1)) |
| 137 && (!rh.fComputed || rh.calcSlop(rx, ry, x, y, &sloppy2)) |
| 138 && sloppy1 != sloppy2) { |
| 139 return COMPARE_RESULT("8 CalcSlop(x, y ...", sloppy1); |
| 140 } |
| 141 } |
| 142 } |
| 143 if (fSide * rh.fSide == 0) { |
| 144 SkASSERT(fSide + rh.fSide != 0); |
| 145 return COMPARE_RESULT("9 fSide * rh.fSide == 0 ...", fSide < rh.fSide); |
| 146 } |
| 147 // at this point, the initial tangent line is nearly coincident |
| 148 // see if edges curl away from each other |
| 149 if (fSide * rh.fSide < 0 && (!approximately_zero(fSide) || !approximately_ze
ro(rh.fSide))) { |
| 150 return COMPARE_RESULT("9b fSide * rh.fSide < 0 ...", fSide < rh.fSide); |
| 151 } |
| 152 if (fUnsortable || rh.fUnsortable) { |
| 153 // even with no solution, return a stable sort |
| 154 return COMPARE_RESULT("11 fUnsortable || rh.fUnsortable", this < &rh); |
| 155 } |
| 156 SkPath::Verb verb = fSegment->verb(); |
| 157 SkPath::Verb rVerb = rh.fSegment->verb(); |
| 158 if ((verb == SkPath::kLine_Verb && approximately_zero(y) && approximately_ze
ro(x)) |
| 159 || (rVerb == SkPath::kLine_Verb |
| 160 && approximately_zero(ry) && approximately_zero(rx))) { |
84 // See general unsortable comment below. This case can happen when | 161 // See general unsortable comment below. This case can happen when |
85 // one line has a non-zero change in t but no change in x and y. | 162 // one line has a non-zero change in t but no change in x and y. |
86 fUnsortable = true; | 163 fUnsortable = true; |
87 rh.fUnsortable = true; | 164 return COMPARE_RESULT("12 verb == SkPath::kLine_Verb ...", this < &rh); |
88 return this < &rh; // even with no solution, return a stable sort | |
89 } | 165 } |
90 if ((*rh.fSpans)[SkMin32(rh.fStart, rh.fEnd)].fTiny | 166 if (fSegment->isTiny(this) || rh.fSegment->isTiny(&rh)) { |
91 || (*fSpans)[SkMin32(fStart, fEnd)].fTiny) { | |
92 fUnsortable = true; | 167 fUnsortable = true; |
93 rh.fUnsortable = true; | 168 return COMPARE_RESULT("13 verb == fSegment->isTiny(this) ...", this < &r
h); |
94 return this < &rh; // even with no solution, return a stable sort | |
95 } | 169 } |
96 SkASSERT(fVerb >= SkPath::kQuad_Verb); | 170 SkASSERT(verb >= SkPath::kQuad_Verb); |
97 SkASSERT(rh.fVerb >= SkPath::kQuad_Verb); | 171 SkASSERT(rVerb >= SkPath::kQuad_Verb); |
98 // FIXME: until I can think of something better, project a ray from the | 172 // FIXME: until I can think of something better, project a ray from the |
99 // end of the shorter tangent to midway between the end points | 173 // end of the shorter tangent to midway between the end points |
100 // through both curves and use the resulting angle to sort | 174 // through both curves and use the resulting angle to sort |
101 // FIXME: some of this setup can be moved to set() if it works, or cached if
it's expensive | 175 // FIXME: some of this setup can be moved to set() if it works, or cached if
it's expensive |
102 double len = fTangent1.normalSquared(); | 176 double len = fTangent1.normalSquared(); |
103 double rlen = rh.fTangent1.normalSquared(); | 177 double rlen = rh.fTangent1.normalSquared(); |
104 SkDLine ray; | 178 SkDLine ray; |
105 SkIntersections i, ri; | 179 SkIntersections i, ri; |
106 int roots, rroots; | 180 int roots, rroots; |
107 bool flip = false; | 181 bool flip = false; |
108 bool useThis; | 182 bool useThis; |
109 bool leftLessThanRight = fSide > 0; | 183 bool leftLessThanRight = fSide > 0; |
110 do { | 184 do { |
111 useThis = (len < rlen) ^ flip; | 185 useThis = (len < rlen) ^ flip; |
112 const SkDCubic& part = useThis ? fCurvePart : rh.fCurvePart; | 186 const SkDCubic& part = useThis ? fCurvePart : rh.fCurvePart; |
113 SkPath::Verb partVerb = useThis ? fVerb : rh.fVerb; | 187 SkPath::Verb partVerb = useThis ? verb : rVerb; |
114 ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(p
art[1]) ? | 188 ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(p
art[1]) ? |
115 part[2] : part[1]; | 189 part[2] : part[1]; |
116 ray[1].fX = (part[0].fX + part[SkPathOpsVerbToPoints(partVerb)].fX) / 2; | 190 ray[1] = SkDPoint::Mid(part[0], part[SkPathOpsVerbToPoints(partVerb)]); |
117 ray[1].fY = (part[0].fY + part[SkPathOpsVerbToPoints(partVerb)].fY) / 2; | |
118 SkASSERT(ray[0] != ray[1]); | 191 SkASSERT(ray[0] != ray[1]); |
119 roots = (i.*CurveRay[SkPathOpsVerbToPoints(fVerb)])(fPts, ray); | 192 roots = (i.*CurveRay[SkPathOpsVerbToPoints(verb)])(fSegment->pts(), ray)
; |
120 rroots = (ri.*CurveRay[SkPathOpsVerbToPoints(rh.fVerb)])(rh.fPts, ray); | 193 rroots = (ri.*CurveRay[SkPathOpsVerbToPoints(rVerb)])(rh.fSegment->pts()
, ray); |
121 } while ((roots == 0 || rroots == 0) && (flip ^= true)); | 194 } while ((roots == 0 || rroots == 0) && (flip ^= true)); |
122 if (roots == 0 || rroots == 0) { | 195 if (roots == 0 || rroots == 0) { |
123 // FIXME: we don't have a solution in this case. The interim solution | 196 // FIXME: we don't have a solution in this case. The interim solution |
124 // is to mark the edges as unsortable, exclude them from this and | 197 // is to mark the edges as unsortable, exclude them from this and |
125 // future computations, and allow the returned path to be fragmented | 198 // future computations, and allow the returned path to be fragmented |
126 fUnsortable = true; | 199 fUnsortable = true; |
127 rh.fUnsortable = true; | 200 return COMPARE_RESULT("roots == 0 || rroots == 0", this < &rh); |
128 return this < &rh; // even with no solution, return a stable sort | |
129 } | 201 } |
130 SkASSERT(fSide != 0 && rh.fSide != 0); | 202 SkASSERT(fSide != 0 && rh.fSide != 0); |
131 SkASSERT(fSide * rh.fSide > 0); // both are the same sign | 203 SkASSERT(fSide * rh.fSide > 0); // both are the same sign |
132 SkDPoint lLoc; | 204 SkDPoint lLoc; |
133 double best = SK_ScalarInfinity; | 205 double best = SK_ScalarInfinity; |
134 #if DEBUG_SORT | 206 #if DEBUG_SORT |
135 SkDebugf("lh=%d rh=%d use-lh=%d ray={{%1.9g,%1.9g}, {%1.9g,%1.9g}} %c\n", | 207 SkDebugf("lh=%d rh=%d use-lh=%d ray={{%1.9g,%1.9g}, {%1.9g,%1.9g}} %c\n", |
136 fSegment->debugID(), rh.fSegment->debugID(), useThis, ray[0].fX, ray
[0].fY, | 208 fSegment->debugID(), rh.fSegment->debugID(), useThis, ray[0].fX, ray
[0].fY, |
137 ray[1].fX, ray[1].fY, "-+"[fSide > 0]); | 209 ray[1].fX, ray[1].fY, "-+"[fSide > 0]); |
138 #endif | 210 #endif |
(...skipping 18 matching lines...) Expand all Loading... |
157 double dist = dxy.lengthSquared(); | 229 double dist = dxy.lengthSquared(); |
158 #if DEBUG_SORT | 230 #if DEBUG_SORT |
159 SkDebugf("best=%1.9g dist=%1.9g %c=(fSide < 0) rLoc={%1.9g,%1.9g} dxy={%
1.9g,%1.9g}\n", | 231 SkDebugf("best=%1.9g dist=%1.9g %c=(fSide < 0) rLoc={%1.9g,%1.9g} dxy={%
1.9g,%1.9g}\n", |
160 best, dist, "><"[fSide < 0], rLoc.fX, rLoc.fY, dxy.fX, dxy.fY); | 232 best, dist, "><"[fSide < 0], rLoc.fX, rLoc.fY, dxy.fX, dxy.fY); |
161 #endif | 233 #endif |
162 if (best > dist) { | 234 if (best > dist) { |
163 flip = true; | 235 flip = true; |
164 break; | 236 break; |
165 } | 237 } |
166 } | 238 } |
167 #if 0 | 239 if (flip) { |
168 SkDVector lRay = lLoc - fCurvePart[0]; | 240 leftLessThanRight = !leftLessThanRight; |
169 SkDVector rRay = rLoc - fCurvePart[0]; | |
170 int rayDir = simple_compare(lRay.fX, lRay.fY, rRay.fX, rRay.fY); | |
171 SkASSERT(rayDir >= 0); | |
172 if (rayDir < 0) { | |
173 fUnsortable = true; | |
174 rh.fUnsortable = true; | |
175 return this < &rh; // even with no solution, return a stable sort | |
176 } | 241 } |
177 #endif | 242 return COMPARE_RESULT("14 leftLessThanRight", leftLessThanRight); |
178 if (flip) { | |
179 leftLessThanRight = !leftLessThanRight; | |
180 // rayDir = !rayDir; | |
181 } | |
182 #if 0 && (DEBUG_SORT || DEBUG_SORT_SINGLE) | |
183 SkDebugf("%d %c %d (fSide %c 0) loc={{%1.9g,%1.9g}, {%1.9g,%1.9g}} flip=%d r
ayDir=%d\n", | |
184 fSegment->debugID(), "><"[leftLessThanRight], rh.fSegment->debug
ID(), | |
185 "<>"[fSide > 0], lLoc.fX, lLoc.fY, rLoc.fX, rLoc.fY, flip, rayDi
r); | |
186 #endif | |
187 // SkASSERT(leftLessThanRight == (bool) rayDir); | |
188 return leftLessThanRight; | |
189 } | 243 } |
190 | 244 |
191 bool SkOpAngle::lengthen() { | 245 bool SkOpAngle::isHorizontal() const { |
| 246 return dy() == 0 && fSegment->verb() == SkPath::kLine_Verb; |
| 247 } |
| 248 |
| 249 // lengthen cannot cross opposite angle |
| 250 bool SkOpAngle::lengthen(const SkOpAngle& opp) { |
| 251 if (fSegment->other(fEnd) == opp.fSegment) { |
| 252 return false; |
| 253 } |
| 254 // FIXME: make this a while loop instead and make it as large as possible? |
192 int newEnd = fEnd; | 255 int newEnd = fEnd; |
193 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) { | 256 if (fStart < fEnd ? ++newEnd < fSegment->count() : --newEnd >= 0) { |
194 fEnd = newEnd; | 257 fEnd = newEnd; |
195 setSpans(); | 258 setSpans(); |
196 return true; | 259 return true; |
197 } | 260 } |
198 return false; | 261 return false; |
199 } | 262 } |
200 | 263 |
201 bool SkOpAngle::reverseLengthen() { | 264 void SkOpAngle::set(const SkOpSegment* segment, int start, int end) { |
202 if (fReversed) { | |
203 return false; | |
204 } | |
205 int newEnd = fStart; | |
206 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) { | |
207 fEnd = newEnd; | |
208 fReversed = true; | |
209 setSpans(); | |
210 return true; | |
211 } | |
212 return false; | |
213 } | |
214 | |
215 void SkOpAngle::set(const SkPoint* orig, SkPath::Verb verb, const SkOpSegment* s
egment, | |
216 int start, int end, const SkTDArray<SkOpSpan>& spans) { | |
217 fSegment = segment; | 265 fSegment = segment; |
218 fStart = start; | 266 fStart = start; |
219 fEnd = end; | 267 fEnd = end; |
220 fPts = orig; | |
221 fVerb = verb; | |
222 fSpans = &spans; | |
223 fReversed = false; | |
224 fUnsortable = false; | |
225 setSpans(); | 268 setSpans(); |
226 } | 269 } |
227 | 270 |
228 | |
229 void SkOpAngle::setSpans() { | 271 void SkOpAngle::setSpans() { |
230 double startT = (*fSpans)[fStart].fT; | 272 fUnorderable = false; |
231 double endT = (*fSpans)[fEnd].fT; | 273 if (fSegment->verb() == SkPath::kLine_Verb) { |
232 switch (fVerb) { | 274 fUnsortable = false; |
| 275 } else { |
| 276 // if start-1 exists and is tiny, then start pt may have moved |
| 277 int smaller = SkMin32(fStart, fEnd); |
| 278 int tinyCheck = smaller; |
| 279 while (tinyCheck > 0 && fSegment->isTiny(tinyCheck - 1)) { |
| 280 --tinyCheck; |
| 281 } |
| 282 if ((fUnsortable = smaller > 0 && tinyCheck == 0)) { |
| 283 return; |
| 284 } |
| 285 int larger = SkMax32(fStart, fEnd); |
| 286 tinyCheck = larger; |
| 287 int max = fSegment->count() - 1; |
| 288 while (tinyCheck < max && fSegment->isTiny(tinyCheck + 1)) { |
| 289 ++tinyCheck; |
| 290 } |
| 291 if ((fUnsortable = larger < max && tinyCheck == max)) { |
| 292 return; |
| 293 } |
| 294 } |
| 295 fComputed = fSegment->subDivide(fStart, fEnd, &fCurvePart); |
| 296 // FIXME: slight errors in subdivision cause sort trouble later on. As an ex
periment, try |
| 297 // rounding the curve part to float precision here |
| 298 // fCurvePart.round(fSegment->verb()); |
| 299 switch (fSegment->verb()) { |
233 case SkPath::kLine_Verb: { | 300 case SkPath::kLine_Verb: { |
234 SkDLine l = SkDLine::SubDivide(fPts, startT, endT); | |
235 // OPTIMIZATION: for pure line compares, we never need fTangent1.c | 301 // OPTIMIZATION: for pure line compares, we never need fTangent1.c |
236 fTangent1.lineEndPoints(l); | 302 fTangent1.lineEndPoints(*SkTCast<SkDLine*>(&fCurvePart)); |
237 fSide = 0; | 303 fSide = 0; |
238 } break; | 304 } break; |
239 case SkPath::kQuad_Verb: { | 305 case SkPath::kQuad_Verb: { |
240 SkDQuad& quad = *SkTCast<SkDQuad*>(&fCurvePart); | 306 SkDQuad& quad = *SkTCast<SkDQuad*>(&fCurvePart); |
241 quad = SkDQuad::SubDivide(fPts, startT, endT); | 307 fTangent1.quadEndPoints(quad); |
242 fTangent1.quadEndPoints(quad, 0, 1); | 308 fSide = -fTangent1.pointDistance(fCurvePart[2]); // not normalized -- c
ompare sign only |
243 if (dx() == 0 && dy() == 0) { | 309 if (fComputed && dx() > 0 && approximately_zero(dy())) { |
244 fTangent1.quadEndPoints(quad); | 310 SkDCubic origCurve; // can't use segment's curve in place since it m
ay be flipped |
| 311 int last = fSegment->count() - 1; |
| 312 fSegment->subDivide(fStart < fEnd ? 0 : last, fStart < fEnd ? last :
0, &origCurve); |
| 313 SkLineParameters origTan; |
| 314 origTan.quadEndPoints(*SkTCast<SkDQuad*>(&origCurve)); |
| 315 if ((fUnorderable = origTan.dx() <= 0 |
| 316 || (dy() != origTan.dy() && dy() * origTan.dy() <= 0))) { //
signs match? |
| 317 return; |
| 318 } |
245 } | 319 } |
246 fSide = -fTangent1.pointDistance(fCurvePart[2]); // not normalized -- c
ompare sign only | |
247 } break; | 320 } break; |
248 case SkPath::kCubic_Verb: { | 321 case SkPath::kCubic_Verb: { |
249 // int nextC = 2; | 322 fTangent1.cubicEndPoints(fCurvePart); |
250 fCurvePart = SkDCubic::SubDivide(fPts, startT, endT); | |
251 fTangent1.cubicEndPoints(fCurvePart, 0, 1); | |
252 if (dx() == 0 && dy() == 0) { | |
253 fTangent1.cubicEndPoints(fCurvePart, 0, 2); | |
254 // nextC = 3; | |
255 if (dx() == 0 && dy() == 0) { | |
256 fTangent1.cubicEndPoints(fCurvePart, 0, 3); | |
257 } | |
258 } | |
259 // fSide = -fTangent1.pointDistance(fCurvePart[nextC]); // compare sign on
ly | |
260 // if (nextC == 2 && approximately_zero(fSide)) { | |
261 // fSide = -fTangent1.pointDistance(fCurvePart[3]); | |
262 // } | |
263 double testTs[4]; | 323 double testTs[4]; |
264 // OPTIMIZATION: keep inflections precomputed with cubic segment? | 324 // OPTIMIZATION: keep inflections precomputed with cubic segment? |
265 int testCount = SkDCubic::FindInflections(fPts, testTs); | 325 const SkPoint* pts = fSegment->pts(); |
| 326 int testCount = SkDCubic::FindInflections(pts, testTs); |
| 327 double startT = fSegment->t(fStart); |
| 328 double endT = fSegment->t(fEnd); |
266 double limitT = endT; | 329 double limitT = endT; |
267 int index; | 330 int index; |
268 for (index = 0; index < testCount; ++index) { | 331 for (index = 0; index < testCount; ++index) { |
269 if (!between(startT, testTs[index], limitT)) { | 332 if (!between(startT, testTs[index], limitT)) { |
270 testTs[index] = -1; | 333 testTs[index] = -1; |
271 } | 334 } |
272 } | 335 } |
273 testTs[testCount++] = startT; | 336 testTs[testCount++] = startT; |
274 testTs[testCount++] = endT; | 337 testTs[testCount++] = endT; |
275 SkTQSort<double>(testTs, &testTs[testCount - 1]); | 338 SkTQSort<double>(testTs, &testTs[testCount - 1]); |
276 double bestSide = 0; | 339 double bestSide = 0; |
277 int testCases = (testCount << 1) - 1; | 340 int testCases = (testCount << 1) - 1; |
278 index = 0; | 341 index = 0; |
279 while (testTs[index] < 0) { | 342 while (testTs[index] < 0) { |
280 ++index; | 343 ++index; |
281 } | 344 } |
282 index <<= 1; | 345 index <<= 1; |
283 for (; index < testCases; ++index) { | 346 for (; index < testCases; ++index) { |
284 int testIndex = index >> 1; | 347 int testIndex = index >> 1; |
285 double testT = testTs[testIndex]; | 348 double testT = testTs[testIndex]; |
286 if (index & 1) { | 349 if (index & 1) { |
287 testT = (testT + testTs[testIndex + 1]) / 2; | 350 testT = (testT + testTs[testIndex + 1]) / 2; |
288 } | 351 } |
289 // OPTIMIZE: could avoid call for t == startT, endT | 352 // OPTIMIZE: could avoid call for t == startT, endT |
290 SkDPoint pt = dcubic_xy_at_t(fPts, testT); | 353 SkDPoint pt = dcubic_xy_at_t(pts, testT); |
291 double testSide = fTangent1.pointDistance(pt); | 354 double testSide = fTangent1.pointDistance(pt); |
292 if (fabs(bestSide) < fabs(testSide)) { | 355 if (fabs(bestSide) < fabs(testSide)) { |
293 bestSide = testSide; | 356 bestSide = testSide; |
294 } | 357 } |
295 } | 358 } |
296 fSide = -bestSide; // compare sign only | 359 fSide = -bestSide; // compare sign only |
| 360 if (fComputed && dx() > 0 && approximately_zero(dy())) { |
| 361 SkDCubic origCurve; // can't use segment's curve in place since it m
ay be flipped |
| 362 int last = fSegment->count() - 1; |
| 363 fSegment->subDivide(fStart < fEnd ? 0 : last, fStart < fEnd ? last :
0, &origCurve); |
| 364 SkLineParameters origTan; |
| 365 origTan.cubicEndPoints(origCurve); |
| 366 if ((fUnorderable = origTan.dx() <= 0)) { |
| 367 fUnsortable = fSegment->isTiny(this); |
| 368 return; |
| 369 } |
| 370 // if one is < 0 and the other is >= 0 |
| 371 if ((fUnorderable = (dy() < 0) ^ (origTan.dy() < 0))) { |
| 372 fUnsortable = fSegment->isTiny(this); |
| 373 return; |
| 374 } |
| 375 SkDCubicPair split = origCurve.chopAt(startT); |
| 376 SkLineParameters splitTan; |
| 377 splitTan.cubicEndPoints(fStart < fEnd ? split.second() : split.first
()); |
| 378 if ((fUnorderable = splitTan.dx() <= 0)) { |
| 379 fUnsortable = fSegment->isTiny(this); |
| 380 return; |
| 381 } |
| 382 // if one is < 0 and the other is >= 0 |
| 383 if ((fUnorderable = (dy() < 0) ^ (splitTan.dy() < 0))) { |
| 384 fUnsortable = fSegment->isTiny(this); |
| 385 return; |
| 386 } |
| 387 } |
297 } break; | 388 } break; |
298 default: | 389 default: |
299 SkASSERT(0); | 390 SkASSERT(0); |
300 } | 391 } |
301 fUnsortable = dx() == 0 && dy() == 0; | 392 if ((fUnsortable = approximately_zero(dx()) && approximately_zero(dy()))) { |
302 if (fUnsortable) { | |
303 return; | 393 return; |
304 } | 394 } |
305 SkASSERT(fStart != fEnd); | 395 SkASSERT(fStart != fEnd); |
306 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 t
ype macro? | 396 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 t
ype macro? |
307 for (int index = fStart; index != fEnd; index += step) { | 397 for (int index = fStart; index != fEnd; index += step) { |
308 #if 1 | 398 #if 1 |
309 const SkOpSpan& thisSpan = (*fSpans)[index]; | 399 const SkOpSpan& thisSpan = fSegment->span(index); |
310 const SkOpSpan& nextSpan = (*fSpans)[index + step]; | 400 const SkOpSpan& nextSpan = fSegment->span(index + step); |
311 if (thisSpan.fTiny || precisely_equal(thisSpan.fT, nextSpan.fT)) { | 401 if (thisSpan.fTiny || precisely_equal(thisSpan.fT, nextSpan.fT)) { |
312 continue; | 402 continue; |
313 } | 403 } |
314 fUnsortable = step > 0 ? thisSpan.fUnsortableStart : nextSpan.fUnsortabl
eEnd; | 404 fUnsortable = step > 0 ? thisSpan.fUnsortableStart : nextSpan.fUnsortabl
eEnd; |
315 #if DEBUG_UNSORTABLE | 405 #if DEBUG_UNSORTABLE |
316 if (fUnsortable) { | 406 if (fUnsortable) { |
317 SkPoint iPt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, t
hisSpan.fT); | 407 SkPoint iPt = fSegment->xyAtT(index); |
318 SkPoint ePt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, n
extSpan.fT); | 408 SkPoint ePt = fSegment->xyAtT(index + step); |
319 SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __
FUNCTION__, | 409 SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __
FUNCTION__, |
320 index, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); | 410 index, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); |
321 } | 411 } |
322 #endif | 412 #endif |
323 return; | 413 return; |
324 #else | 414 #else |
325 if ((*fSpans)[index].fUnsortableStart) { | 415 if ((*fSpans)[index].fUnsortableStart) { |
326 fUnsortable = true; | 416 fUnsortable = true; |
327 return; | 417 return; |
328 } | 418 } |
329 #endif | 419 #endif |
330 } | 420 } |
331 #if 1 | 421 #if 1 |
332 #if DEBUG_UNSORTABLE | 422 #if DEBUG_UNSORTABLE |
333 SkPoint iPt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, startT); | 423 SkPoint iPt = fSegment->xyAtT(fStart); |
334 SkPoint ePt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, endT); | 424 SkPoint ePt = fSegment->xyAtT(fEnd); |
335 SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", _
_FUNCTION__, | 425 SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", _
_FUNCTION__, |
336 fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); | 426 fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); |
337 #endif | 427 #endif |
338 fUnsortable = true; | 428 fUnsortable = true; |
339 #endif | 429 #endif |
340 } | 430 } |
OLD | NEW |