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

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

Issue 1037573004: cumulative pathops patch (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: fix pathopsinverse gm Created 5 years, 8 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
« no previous file with comments | « src/pathops/SkPathOpsCubic.h ('k') | src/pathops/SkPathOpsCubicSect.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 "SkGeometry.h"
7 #include "SkLineParameters.h" 8 #include "SkLineParameters.h"
8 #include "SkPathOpsCubic.h" 9 #include "SkPathOpsCubic.h"
9 #include "SkPathOpsLine.h" 10 #include "SkPathOpsLine.h"
10 #include "SkPathOpsQuad.h" 11 #include "SkPathOpsQuad.h"
11 #include "SkPathOpsRect.h" 12 #include "SkPathOpsRect.h"
12 #include "SkTSort.h" 13 #include "SkTSort.h"
13 14
14 const int SkDCubic::gPrecisionUnit = 256; // FIXME: test different values in te st framework 15 const int SkDCubic::gPrecisionUnit = 256; // FIXME: test different values in te st framework
15 16
16 // give up when changing t no longer moves point 17 // give up when changing t no longer moves point
17 // also, copy point rather than recompute it when it does change 18 // also, copy point rather than recompute it when it does change
18 double SkDCubic::binarySearch(double min, double max, double axisIntercept, 19 double SkDCubic::binarySearch(double min, double max, double axisIntercept,
19 SearchAxis xAxis) const { 20 SearchAxis xAxis) const {
20 double t = (min + max) / 2; 21 double t = (min + max) / 2;
21 double step = (t - min) / 2; 22 double step = (t - min) / 2;
22 SkDPoint cubicAtT = ptAtT(t); 23 SkDPoint cubicAtT = ptAtT(t);
23 double calcPos = (&cubicAtT.fX)[xAxis]; 24 double calcPos = (&cubicAtT.fX)[xAxis];
24 double calcDist = calcPos - axisIntercept; 25 double calcDist = calcPos - axisIntercept;
25 do { 26 do {
26 double priorT = t - step; 27 double priorT = t - step;
27 SkASSERT(priorT >= min); 28 SkASSERT(priorT >= min);
28 SkDPoint lessPt = ptAtT(priorT); 29 SkDPoint lessPt = ptAtT(priorT);
29 if (approximately_equal(lessPt.fX, cubicAtT.fX) 30 if (approximately_equal_half(lessPt.fX, cubicAtT.fX)
30 && approximately_equal(lessPt.fY, cubicAtT.fY)) { 31 && approximately_equal_half(lessPt.fY, cubicAtT.fY)) {
31 return -1; // binary search found no point at this axis intercept 32 return -1; // binary search found no point at this axis intercept
32 } 33 }
33 double lessDist = (&lessPt.fX)[xAxis] - axisIntercept; 34 double lessDist = (&lessPt.fX)[xAxis] - axisIntercept;
34 #if DEBUG_CUBIC_BINARY_SEARCH 35 #if DEBUG_CUBIC_BINARY_SEARCH
35 SkDebugf("t=%1.9g calc=%1.9g dist=%1.9g step=%1.9g less=%1.9g\n", t, cal cPos, calcDist, 36 SkDebugf("t=%1.9g calc=%1.9g dist=%1.9g step=%1.9g less=%1.9g\n", t, cal cPos, calcDist,
36 step, lessDist); 37 step, lessDist);
37 #endif 38 #endif
38 double lastStep = step; 39 double lastStep = step;
39 step /= 2; 40 step /= 2;
40 if (calcDist > 0 ? calcDist > lessDist : calcDist < lessDist) { 41 if (calcDist > 0 ? calcDist > lessDist : calcDist < lessDist) {
41 t = priorT; 42 t = priorT;
42 } else { 43 } else {
43 double nextT = t + lastStep; 44 double nextT = t + lastStep;
44 SkASSERT(nextT <= max); 45 if (nextT > max) {
46 return -1;
47 }
45 SkDPoint morePt = ptAtT(nextT); 48 SkDPoint morePt = ptAtT(nextT);
46 if (approximately_equal(morePt.fX, cubicAtT.fX) 49 if (approximately_equal_half(morePt.fX, cubicAtT.fX)
47 && approximately_equal(morePt.fY, cubicAtT.fY)) { 50 && approximately_equal_half(morePt.fY, cubicAtT.fY)) {
48 return -1; // binary search found no point at this axis interce pt 51 return -1; // binary search found no point at this axis interce pt
49 } 52 }
50 double moreDist = (&morePt.fX)[xAxis] - axisIntercept; 53 double moreDist = (&morePt.fX)[xAxis] - axisIntercept;
51 if (calcDist > 0 ? calcDist <= moreDist : calcDist >= moreDist) { 54 if (calcDist > 0 ? calcDist <= moreDist : calcDist >= moreDist) {
52 continue; 55 continue;
53 } 56 }
54 t = nextT; 57 t = nextT;
55 } 58 }
56 SkDPoint testAtT = ptAtT(t); 59 SkDPoint testAtT = ptAtT(t);
57 cubicAtT = testAtT; 60 cubicAtT = testAtT;
(...skipping 23 matching lines...) Expand all
81 void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C, double* D) { 84 void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C, double* D) {
82 *A = src[6]; // d 85 *A = src[6]; // d
83 *B = src[4] * 3; // 3*c 86 *B = src[4] * 3; // 3*c
84 *C = src[2] * 3; // 3*b 87 *C = src[2] * 3; // 3*b
85 *D = src[0]; // a 88 *D = src[0]; // a
86 *A -= *D - *C + *B; // A = -a + 3*b - 3*c + d 89 *A -= *D - *C + *B; // A = -a + 3*b - 3*c + d
87 *B += 3 * *D - 2 * *C; // B = 3*a - 6*b + 3*c 90 *B += 3 * *D - 2 * *C; // B = 3*a - 6*b + 3*c
88 *C -= 3 * *D; // C = -3*a + 3*b 91 *C -= 3 * *D; // C = -3*a + 3*b
89 } 92 }
90 93
91 bool SkDCubic::controlsContainedByEnds() const {
92 SkDVector startTan = fPts[1] - fPts[0];
93 if (startTan.fX == 0 && startTan.fY == 0) {
94 startTan = fPts[2] - fPts[0];
95 }
96 SkDVector endTan = fPts[2] - fPts[3];
97 if (endTan.fX == 0 && endTan.fY == 0) {
98 endTan = fPts[1] - fPts[3];
99 }
100 if (startTan.dot(endTan) >= 0) {
101 return false;
102 }
103 SkDLine startEdge = {{fPts[0], fPts[0]}};
104 startEdge[1].fX -= startTan.fY;
105 startEdge[1].fY += startTan.fX;
106 SkDLine endEdge = {{fPts[3], fPts[3]}};
107 endEdge[1].fX -= endTan.fY;
108 endEdge[1].fY += endTan.fX;
109 double leftStart1 = startEdge.isLeft(fPts[1]);
110 if (leftStart1 * startEdge.isLeft(fPts[2]) < 0) {
111 return false;
112 }
113 double leftEnd1 = endEdge.isLeft(fPts[1]);
114 if (leftEnd1 * endEdge.isLeft(fPts[2]) < 0) {
115 return false;
116 }
117 return leftStart1 * leftEnd1 >= 0;
118 }
119
120 bool SkDCubic::endsAreExtremaInXOrY() const { 94 bool SkDCubic::endsAreExtremaInXOrY() const {
121 return (between(fPts[0].fX, fPts[1].fX, fPts[3].fX) 95 return (between(fPts[0].fX, fPts[1].fX, fPts[3].fX)
122 && between(fPts[0].fX, fPts[2].fX, fPts[3].fX)) 96 && between(fPts[0].fX, fPts[2].fX, fPts[3].fX))
123 || (between(fPts[0].fY, fPts[1].fY, fPts[3].fY) 97 || (between(fPts[0].fY, fPts[1].fY, fPts[3].fY)
124 && between(fPts[0].fY, fPts[2].fY, fPts[3].fY)); 98 && between(fPts[0].fY, fPts[2].fY, fPts[3].fY));
125 } 99 }
126 100
101 // Do a quick reject by rotating all points relative to a line formed by
102 // a pair of one cubic's points. If the 2nd cubic's points
103 // are on the line or on the opposite side from the 1st cubic's 'odd man', the
104 // curves at most intersect at the endpoints.
105 /* if returning true, check contains true if cubic's hull collapsed, making the cubic linear
106 if returning false, check contains true if the the cubic pair have only the e nd point in common
107 */
108 bool SkDCubic::hullIntersects(const SkDCubic& c2, bool* isLinear) const {
109 bool linear = true;
110 char hullOrder[4];
111 int hullCount = convexHull(hullOrder);
112 int end1 = hullOrder[0];
113 int hullIndex = 0;
114 const SkDPoint* endPt[2];
115 endPt[0] = &fPts[end1];
116 do {
117 hullIndex = (hullIndex + 1) % hullCount;
118 int end2 = hullOrder[hullIndex];
119 endPt[1] = &fPts[end2];
120 double origX = endPt[0]->fX;
121 double origY = endPt[0]->fY;
122 double adj = endPt[1]->fX - origX;
123 double opp = endPt[1]->fY - origY;
124 int oddManMask = other_two(end1, end2);
125 int oddMan = end1 ^ oddManMask;
126 double sign = (fPts[oddMan].fY - origY) * adj - (fPts[oddMan].fX - origX ) * opp;
127 int oddMan2 = end2 ^ oddManMask;
128 double sign2 = (fPts[oddMan2].fY - origY) * adj - (fPts[oddMan2].fX - or igX) * opp;
129 if (sign * sign2 < 0) {
130 continue;
131 }
132 if (approximately_zero(sign)) {
133 sign = sign2;
134 if (approximately_zero(sign)) {
135 continue;
136 }
137 }
138 linear = false;
139 bool foundOutlier = false;
140 for (int n = 0; n < kPointCount; ++n) {
141 double test = (c2[n].fY - origY) * adj - (c2[n].fX - origX) * opp;
142 if (test * sign > 0 && !precisely_zero(test)) {
143 foundOutlier = true;
144 break;
145 }
146 }
147 if (!foundOutlier) {
148 return false;
149 }
150 endPt[0] = endPt[1];
151 end1 = end2;
152 } while (hullIndex);
153 *isLinear = linear;
154 return true;
155 }
156
127 bool SkDCubic::isLinear(int startIndex, int endIndex) const { 157 bool SkDCubic::isLinear(int startIndex, int endIndex) const {
128 SkLineParameters lineParameters; 158 SkLineParameters lineParameters;
129 lineParameters.cubicEndPoints(*this, startIndex, endIndex); 159 lineParameters.cubicEndPoints(*this, startIndex, endIndex);
130 // FIXME: maybe it's possible to avoid this and compare non-normalized 160 // FIXME: maybe it's possible to avoid this and compare non-normalized
131 lineParameters.normalize(); 161 lineParameters.normalize();
162 double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX , fPts[0].fY),
163 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPt s[3].fY);
164 double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX , fPts[0].fY),
165 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPt s[3].fY);
166 largest = SkTMax(largest, -tiniest);
132 double distance = lineParameters.controlPtDistance(*this, 1); 167 double distance = lineParameters.controlPtDistance(*this, 1);
133 if (!approximately_zero(distance)) { 168 if (!approximately_zero_when_compared_to(distance, largest)) {
134 return false; 169 return false;
135 } 170 }
136 distance = lineParameters.controlPtDistance(*this, 2); 171 distance = lineParameters.controlPtDistance(*this, 2);
137 return approximately_zero(distance); 172 return approximately_zero_when_compared_to(distance, largest);
173 }
174
175 bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) {
176 SkScalar d[3];
177 SkCubicType cubicType = SkClassifyCubic(pointsPtr, d);
178 if (cubicType == kLoop_SkCubicType) {
179 // crib code from gpu path utils that finds t values where loop self-int ersects
180 // use it to find mid of t values which should be a friendly place to ch op
181 SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]);
182 SkScalar ls = d[1] - tempSqrt;
183 SkScalar lt = 2.f * d[0];
184 SkScalar ms = d[1] + tempSqrt;
185 SkScalar mt = 2.f * d[0];
186 if (between(0, ls, lt) || between(0, ms, mt)) {
187 ls = ls / lt;
188 ms = ms / mt;
189 SkScalar smaller = SkTMax(0.f, SkTMin(ls, ms));
190 SkScalar larger = SkTMin(1.f, SkTMax(ls, ms));
191 *t = (smaller + larger) / 2;
192 return *t > 0 && *t < 1;
193 }
194 } else if (cubicType == kSerpentine_SkCubicType) {
195 SkDCubic cubic;
196 cubic.set(pointsPtr);
197 double inflectionTs[2];
198 int infTCount = cubic.findInflections(inflectionTs);
199 if (infTCount == 2) {
200 double maxCurvature[3];
201 int roots = cubic.findMaxCurvature(maxCurvature);
202 for (int index = 0; index < roots; ++index) {
203 if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1 ])) {
204 *t = maxCurvature[index];
205 return true;
206 }
207 }
208 } else if (infTCount == 1) {
209 *t = inflectionTs[0];
210 return *t > 0 && *t < 1;
211 }
212 return false;
213 }
214 return false;
138 } 215 }
139 216
140 bool SkDCubic::monotonicInY() const { 217 bool SkDCubic::monotonicInY() const {
141 return between(fPts[0].fY, fPts[1].fY, fPts[3].fY) 218 return between(fPts[0].fY, fPts[1].fY, fPts[3].fY)
142 && between(fPts[0].fY, fPts[2].fY, fPts[3].fY); 219 && between(fPts[0].fY, fPts[2].fY, fPts[3].fY);
143 } 220 }
144 221
222 void SkDCubic::otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const {
223 int offset = (int) !SkToBool(index);
224 o1Pts[0] = &fPts[offset];
225 o1Pts[1] = &fPts[++offset];
226 o1Pts[2] = &fPts[++offset];
227 }
228
145 int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept , 229 int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept ,
146 SearchAxis xAxis, double* validRoots) const { 230 SearchAxis xAxis, double* validRoots) const {
147 extrema += findInflections(&extremeTs[extrema]); 231 extrema += findInflections(&extremeTs[extrema]);
148 extremeTs[extrema++] = 0; 232 extremeTs[extrema++] = 0;
149 extremeTs[extrema] = 1; 233 extremeTs[extrema] = 1;
150 SkTQSort(extremeTs, extremeTs + extrema); 234 SkTQSort(extremeTs, extremeTs + extrema);
151 int validCount = 0; 235 int validCount = 0;
152 for (int index = 0; index < extrema; ) { 236 for (int index = 0; index < extrema; ) {
153 double min = extremeTs[index]; 237 double min = extremeTs[index];
154 double max = extremeTs[++index]; 238 double max = extremeTs[++index];
155 if (min == max) { 239 if (min == max) {
156 continue; 240 continue;
157 } 241 }
158 double newT = binarySearch(min, max, axisIntercept, xAxis); 242 double newT = binarySearch(min, max, axisIntercept, xAxis);
159 if (newT >= 0) { 243 if (newT >= 0) {
160 validRoots[validCount++] = newT; 244 validRoots[validCount++] = newT;
161 } 245 }
162 } 246 }
163 return validCount; 247 return validCount;
164 } 248 }
165 249
166 bool SkDCubic::serpentine() const {
167 #if 0 // FIXME: enabling this fixes cubicOp114 but breaks cubicOp58d and cubicO p53d
168 double tValues[2];
169 // OPTIMIZATION : another case where caching the present of cubic inflection s would be useful
170 return findInflections(tValues) > 1;
171 #endif
172 if (!controlsContainedByEnds()) {
173 return false;
174 }
175 double wiggle = (fPts[0].fX - fPts[2].fX) * (fPts[0].fY + fPts[2].fY);
176 for (int idx = 0; idx < 2; ++idx) {
177 wiggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[i dx].fY);
178 }
179 double waggle = (fPts[1].fX - fPts[3].fX) * (fPts[1].fY + fPts[3].fY);
180 for (int idx = 1; idx < 3; ++idx) {
181 waggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[i dx].fY);
182 }
183 return wiggle * waggle < 0;
184 }
185
186 // cubic roots 250 // cubic roots
187 251
188 static const double PI = 3.141592653589793; 252 static const double PI = 3.141592653589793;
189 253
190 // from SkGeometry.cpp (and Numeric Solutions, 5.6) 254 // from SkGeometry.cpp (and Numeric Solutions, 5.6)
191 int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) { 255 int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) {
192 double s[3]; 256 double s[3];
193 int realRoots = RootsReal(A, B, C, D, s); 257 int realRoots = RootsReal(A, B, C, D, s);
194 int foundRoots = SkDQuad::AddValidTs(s, realRoots, t); 258 int foundRoots = SkDQuad::AddValidTs(s, realRoots, t);
195 return foundRoots; 259 return foundRoots;
(...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after
498 dstPt->fX = fPts[endIndex].fX; 562 dstPt->fX = fPts[endIndex].fX;
499 } 563 }
500 if (fPts[endIndex].fY == fPts[ctrlIndex].fY) { 564 if (fPts[endIndex].fY == fPts[ctrlIndex].fY) {
501 dstPt->fY = fPts[endIndex].fY; 565 dstPt->fY = fPts[endIndex].fY;
502 } 566 }
503 } 567 }
504 568
505 void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d, 569 void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d,
506 double t1, double t2, SkDPoint dst[2]) const { 570 double t1, double t2, SkDPoint dst[2]) const {
507 SkASSERT(t1 != t2); 571 SkASSERT(t1 != t2);
508 #if 0
509 double ex = interp_cubic_coords(&fPts[0].fX, (t1 * 2 + t2) / 3);
510 double ey = interp_cubic_coords(&fPts[0].fY, (t1 * 2 + t2) / 3);
511 double fx = interp_cubic_coords(&fPts[0].fX, (t1 + t2 * 2) / 3);
512 double fy = interp_cubic_coords(&fPts[0].fY, (t1 + t2 * 2) / 3);
513 double mx = ex * 27 - a.fX * 8 - d.fX;
514 double my = ey * 27 - a.fY * 8 - d.fY;
515 double nx = fx * 27 - a.fX - d.fX * 8;
516 double ny = fy * 27 - a.fY - d.fY * 8;
517 /* bx = */ dst[0].fX = (mx * 2 - nx) / 18;
518 /* by = */ dst[0].fY = (my * 2 - ny) / 18;
519 /* cx = */ dst[1].fX = (nx * 2 - mx) / 18;
520 /* cy = */ dst[1].fY = (ny * 2 - my) / 18;
521 #else
522 // this approach assumes that the control points computed directly are accur ate enough 572 // this approach assumes that the control points computed directly are accur ate enough
523 SkDCubic sub = subDivide(t1, t2); 573 SkDCubic sub = subDivide(t1, t2);
524 dst[0] = sub[1] + (a - sub[0]); 574 dst[0] = sub[1] + (a - sub[0]);
525 dst[1] = sub[2] + (d - sub[3]); 575 dst[1] = sub[2] + (d - sub[3]);
526 #endif
527 if (t1 == 0 || t2 == 0) { 576 if (t1 == 0 || t2 == 0) {
528 align(0, 1, t1 == 0 ? &dst[0] : &dst[1]); 577 align(0, 1, t1 == 0 ? &dst[0] : &dst[1]);
529 } 578 }
530 if (t1 == 1 || t2 == 1) { 579 if (t1 == 1 || t2 == 1) {
531 align(3, 2, t1 == 1 ? &dst[0] : &dst[1]); 580 align(3, 2, t1 == 1 ? &dst[0] : &dst[1]);
532 } 581 }
533 if (AlmostBequalUlps(dst[0].fX, a.fX)) { 582 if (AlmostBequalUlps(dst[0].fX, a.fX)) {
534 dst[0].fX = a.fX; 583 dst[0].fX = a.fX;
535 } 584 }
536 if (AlmostBequalUlps(dst[0].fY, a.fY)) { 585 if (AlmostBequalUlps(dst[0].fY, a.fY)) {
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
576 dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4; 625 dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4;
577 dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2; 626 dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2;
578 dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2; 627 dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2;
579 dst.pts[6] = fPts[3]; 628 dst.pts[6] = fPts[3];
580 return dst; 629 return dst;
581 } 630 }
582 interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t); 631 interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t);
583 interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t); 632 interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t);
584 return dst; 633 return dst;
585 } 634 }
OLDNEW
« no previous file with comments | « src/pathops/SkPathOpsCubic.h ('k') | src/pathops/SkPathOpsCubicSect.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698