Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: src/pathops/SkOpAngle.cpp

Issue 15338003: path ops -- rewrite angle sort (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/pathops/SkOpAngle.h ('k') | src/pathops/SkOpSegment.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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 }
OLDNEW
« no previous file with comments | « src/pathops/SkOpAngle.h ('k') | src/pathops/SkOpSegment.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698