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 "SkPathOpsCurve.h" | 9 #include "SkPathOpsCurve.h" |
10 #include "SkTSort.h" | 10 #include "SkTSort.h" |
11 | 11 |
| 12 #if DEBUG_SORT || DEBUG_SORT_SINGLE |
| 13 #include "SkOpSegment.h" |
| 14 #endif |
| 15 |
12 // FIXME: this is bogus for quads and cubics | 16 // FIXME: this is bogus for quads and cubics |
13 // if the quads and cubics' line from end pt to ctrl pt are coincident, | 17 // if the quads and cubics' line from end pt to ctrl pt are coincident, |
14 // there's no obvious way to determine the curve ordering from the | 18 // there's no obvious way to determine the curve ordering from the |
15 // derivatives alone. In particular, if one quadratic's coincident tangent | 19 // derivatives alone. In particular, if one quadratic's coincident tangent |
16 // is longer than the other curve, the final control point can place the | 20 // is longer than the other curve, the final control point can place the |
17 // longer curve on either side of the shorter one. | 21 // longer curve on either side of the shorter one. |
18 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf | 22 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf |
19 // may provide some help, but nothing has been figured out yet. | 23 // may provide some help, but nothing has been figured out yet. |
20 | 24 |
21 /*( | 25 /*( |
22 for quads and cubics, set up a parameterized line (e.g. LineParameters ) | 26 for quads and cubics, set up a parameterized line (e.g. LineParameters ) |
23 for points [0] to [1]. See if point [2] is on that line, or on one side | 27 for points [0] to [1]. See if point [2] is on that line, or on one side |
24 or the other. If it both quads' end points are on the same side, choose | 28 or the other. If it both quads' end points are on the same side, choose |
25 the shorter tangent. If the tangents are equal, choose the better second | 29 the shorter tangent. If the tangents are equal, choose the better second |
26 tangent angle | 30 tangent angle |
27 | 31 |
28 maybe I could set up LineParameters lazily | 32 maybe I could set up LineParameters lazily |
29 */ | 33 */ |
30 bool SkOpAngle::operator<(const SkOpAngle& rh) const { | 34 static int simple_compare(double x, double y, double rx, double ry) { |
31 double y = dy(); | |
32 double ry = rh.dy(); | |
33 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ? | 35 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ? |
34 return y < 0; | 36 return y < 0; |
35 } | 37 } |
36 double x = dx(); | |
37 double rx = rh.dx(); | |
38 if (y == 0 && ry == 0 && x * rx < 0) { | 38 if (y == 0 && ry == 0 && x * rx < 0) { |
39 return x < rx; | 39 return x < rx; |
40 } | 40 } |
41 double x_ry = x * ry; | 41 double x_ry = x * ry; |
42 double rx_y = rx * y; | 42 double rx_y = rx * y; |
43 double cmp = x_ry - rx_y; | 43 double cmp = x_ry - rx_y; |
44 if (!approximately_zero(cmp)) { | 44 if (!approximately_zero(cmp)) { |
45 return cmp < 0; | 45 return cmp < 0; |
46 } | 46 } |
47 if (approximately_zero(x_ry) && approximately_zero(rx_y) | 47 if (approximately_zero(x_ry) && approximately_zero(rx_y) |
48 && !approximately_zero_squared(cmp)) { | 48 && !approximately_zero_squared(cmp)) { |
49 return cmp < 0; | 49 return cmp < 0; |
50 } | 50 } |
| 51 return -1; |
| 52 } |
| 53 |
| 54 bool SkOpAngle::operator<(const SkOpAngle& rh) const { |
| 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 } |
51 // at this point, the initial tangent line is coincident | 63 // at this point, the initial tangent line is coincident |
52 // see if edges curl away from each other | 64 // see if edges curl away from each other |
53 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide) | 65 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide) |
54 || !approximately_zero(rh.fSide))) { | 66 || !approximately_zero(rh.fSide))) { |
55 // FIXME: running demo will trigger this assertion | 67 // FIXME: running demo will trigger this assertion |
56 // (don't know if commenting out will trigger further assertion or not) | 68 // (don't know if commenting out will trigger further assertion or not) |
57 // commenting it out allows demo to run in release, though | 69 // commenting it out allows demo to run in release, though |
58 return fSide < rh.fSide; | 70 return fSide < rh.fSide; |
59 } | 71 } |
60 // see if either curve can be lengthened and try the tangent compare again | 72 // see if either curve can be lengthened and try the tangent compare again |
(...skipping 25 matching lines...) Expand all Loading... |
86 // FIXME: until I can think of something better, project a ray from the | 98 // FIXME: until I can think of something better, project a ray from the |
87 // end of the shorter tangent to midway between the end points | 99 // end of the shorter tangent to midway between the end points |
88 // through both curves and use the resulting angle to sort | 100 // through both curves and use the resulting angle to sort |
89 // FIXME: some of this setup can be moved to set() if it works, or cached if
it's expensive | 101 // FIXME: some of this setup can be moved to set() if it works, or cached if
it's expensive |
90 double len = fTangent1.normalSquared(); | 102 double len = fTangent1.normalSquared(); |
91 double rlen = rh.fTangent1.normalSquared(); | 103 double rlen = rh.fTangent1.normalSquared(); |
92 SkDLine ray; | 104 SkDLine ray; |
93 SkIntersections i, ri; | 105 SkIntersections i, ri; |
94 int roots, rroots; | 106 int roots, rroots; |
95 bool flip = false; | 107 bool flip = false; |
| 108 bool useThis; |
| 109 bool leftLessThanRight = fSide > 0; |
96 do { | 110 do { |
97 bool useThis = (len < rlen) ^ flip; | 111 useThis = (len < rlen) ^ flip; |
98 const SkDCubic& part = useThis ? fCurvePart : rh.fCurvePart; | 112 const SkDCubic& part = useThis ? fCurvePart : rh.fCurvePart; |
99 SkPath::Verb partVerb = useThis ? fVerb : rh.fVerb; | 113 SkPath::Verb partVerb = useThis ? fVerb : rh.fVerb; |
100 ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(p
art[1]) ? | 114 ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(p
art[1]) ? |
101 part[2] : part[1]; | 115 part[2] : part[1]; |
102 ray[1].fX = (part[0].fX + part[partVerb].fX) / 2; | 116 ray[1].fX = (part[0].fX + part[partVerb].fX) / 2; |
103 ray[1].fY = (part[0].fY + part[partVerb].fY) / 2; | 117 ray[1].fY = (part[0].fY + part[partVerb].fY) / 2; |
104 SkASSERT(ray[0] != ray[1]); | 118 SkASSERT(ray[0] != ray[1]); |
105 roots = (i.*CurveRay[fVerb])(fPts, ray); | 119 roots = (i.*CurveRay[fVerb])(fPts, ray); |
106 rroots = (ri.*CurveRay[rh.fVerb])(rh.fPts, ray); | 120 rroots = (ri.*CurveRay[rh.fVerb])(rh.fPts, ray); |
107 } while ((roots == 0 || rroots == 0) && (flip ^= true)); | 121 } while ((roots == 0 || rroots == 0) && (flip ^= true)); |
108 if (roots == 0 || rroots == 0) { | 122 if (roots == 0 || rroots == 0) { |
109 // FIXME: we don't have a solution in this case. The interim solution | 123 // FIXME: we don't have a solution in this case. The interim solution |
110 // is to mark the edges as unsortable, exclude them from this and | 124 // is to mark the edges as unsortable, exclude them from this and |
111 // future computations, and allow the returned path to be fragmented | 125 // future computations, and allow the returned path to be fragmented |
112 fUnsortable = true; | 126 fUnsortable = true; |
113 rh.fUnsortable = true; | 127 rh.fUnsortable = true; |
114 return this < &rh; // even with no solution, return a stable sort | 128 return this < &rh; // even with no solution, return a stable sort |
115 } | 129 } |
116 SkDPoint loc; | 130 SkASSERT(fSide != 0 && rh.fSide != 0); |
| 131 SkASSERT(fSide * rh.fSide > 0); // both are the same sign |
| 132 SkDPoint lLoc; |
117 double best = SK_ScalarInfinity; | 133 double best = SK_ScalarInfinity; |
118 SkDVector dxy; | 134 #if DEBUG_SORT |
119 double dist; | 135 SkDebugf("lh=%d rh=%d use-lh=%d ray={{%1.9g,%1.9g}, {%1.9g,%1.9g}} %c\n", |
120 int index; | 136 fSegment->debugID(), rh.fSegment->debugID(), useThis, ray[0].fX, ray
[0].fY, |
121 for (index = 0; index < roots; ++index) { | 137 ray[1].fX, ray[1].fY, "-+"[fSide > 0]); |
122 loc = (*CurveDPointAtT[fVerb])(fPts, i[0][index]); | 138 #endif |
123 dxy = loc - ray[0]; | 139 for (int index = 0; index < roots; ++index) { |
124 dist = dxy.lengthSquared(); | 140 SkDPoint loc = i.pt(index); |
| 141 SkDVector dxy = loc - ray[0]; |
| 142 double dist = dxy.lengthSquared(); |
| 143 #if DEBUG_SORT |
| 144 SkDebugf("best=%1.9g dist=%1.9g loc={%1.9g,%1.9g} dxy={%1.9g,%1.9g}\n", |
| 145 best, dist, loc.fX, loc.fY, dxy.fX, dxy.fY); |
| 146 #endif |
125 if (best > dist) { | 147 if (best > dist) { |
| 148 lLoc = loc; |
126 best = dist; | 149 best = dist; |
127 } | 150 } |
128 } | 151 } |
129 for (index = 0; index < rroots; ++index) { | 152 flip = false; |
130 loc = (*CurveDPointAtT[rh.fVerb])(rh.fPts, ri[0][index]); | 153 SkDPoint rLoc; |
131 dxy = loc - ray[0]; | 154 for (int index = 0; index < rroots; ++index) { |
132 dist = dxy.lengthSquared(); | 155 rLoc = ri.pt(index); |
| 156 SkDVector dxy = rLoc - ray[0]; |
| 157 double dist = dxy.lengthSquared(); |
| 158 #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", |
| 160 best, dist, "><"[fSide < 0], rLoc.fX, rLoc.fY, dxy.fX, dxy.fY); |
| 161 #endif |
133 if (best > dist) { | 162 if (best > dist) { |
134 return fSide < 0; | 163 flip = true; |
| 164 break; |
135 } | 165 } |
136 } | 166 } |
137 return fSide > 0; | 167 #if 0 |
| 168 SkDVector lRay = lLoc - fCurvePart[0]; |
| 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 } |
| 177 #endif |
| 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; |
138 } | 189 } |
139 | 190 |
140 bool SkOpAngle::lengthen() { | 191 bool SkOpAngle::lengthen() { |
141 int newEnd = fEnd; | 192 int newEnd = fEnd; |
142 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) { | 193 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) { |
143 fEnd = newEnd; | 194 fEnd = newEnd; |
144 setSpans(); | 195 setSpans(); |
145 return true; | 196 return true; |
146 } | 197 } |
147 return false; | 198 return false; |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
280 #if 1 | 331 #if 1 |
281 #if DEBUG_UNSORTABLE | 332 #if DEBUG_UNSORTABLE |
282 SkPoint iPt = (*CurvePointAtT[fVerb])(fPts, startT); | 333 SkPoint iPt = (*CurvePointAtT[fVerb])(fPts, startT); |
283 SkPoint ePt = (*CurvePointAtT[fVerb])(fPts, endT); | 334 SkPoint ePt = (*CurvePointAtT[fVerb])(fPts, endT); |
284 SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", _
_FUNCTION__, | 335 SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", _
_FUNCTION__, |
285 fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); | 336 fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); |
286 #endif | 337 #endif |
287 fUnsortable = true; | 338 fUnsortable = true; |
288 #endif | 339 #endif |
289 } | 340 } |
OLD | NEW |