blob: 7a1cc09247a49bb614ce1ebbf7ba7f7711368529 [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#ifndef SkOpContour_DEFINED
8#define SkOpContour_DEFINED
9
10#include "SkOpSegment.h"
11#include "SkTArray.h"
12
commit-bot@chromium.org0bcb8ca2014-04-25 13:42:21 +000013#if defined(SK_DEBUG) || !FORCE_RELEASE
14#include "SkThread.h"
15#endif
16
caryclark@google.com07393ca2013-04-08 11:47:37 +000017class SkIntersections;
18class SkOpContour;
19class SkPathWriter;
20
21struct SkCoincidence {
caryclark@google.com570863f2013-09-16 15:55:01 +000022 SkOpContour* fOther;
caryclark@google.com07393ca2013-04-08 11:47:37 +000023 int fSegments[2];
24 double fTs[2][2];
caryclarkdac1d172014-06-17 05:15:38 -070025 SkPoint fPts[2][2];
26 int fNearly[2];
caryclark@google.com07393ca2013-04-08 11:47:37 +000027};
28
29class SkOpContour {
30public:
31 SkOpContour() {
32 reset();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000033#if defined(SK_DEBUG) || !FORCE_RELEASE
commit-bot@chromium.org0bcb8ca2014-04-25 13:42:21 +000034 fID = sk_atomic_inc(&SkPathOpsDebug::gContourID);
caryclark@google.com07393ca2013-04-08 11:47:37 +000035#endif
36 }
37
38 bool operator<(const SkOpContour& rh) const {
39 return fBounds.fTop == rh.fBounds.fTop
40 ? fBounds.fLeft < rh.fBounds.fLeft
41 : fBounds.fTop < rh.fBounds.fTop;
42 }
43
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000044 bool addCoincident(int index, SkOpContour* other, int otherIndex,
caryclark@google.com07393ca2013-04-08 11:47:37 +000045 const SkIntersections& ts, bool swap);
46 void addCoincidentPoints();
47
48 void addCross(const SkOpContour* crosser) {
49#ifdef DEBUG_CROSS
50 for (int index = 0; index < fCrosses.count(); ++index) {
51 SkASSERT(fCrosses[index] != crosser);
52 }
53#endif
caryclark@google.comd892bd82013-06-17 14:10:36 +000054 fCrosses.push_back(crosser);
caryclark@google.com07393ca2013-04-08 11:47:37 +000055 }
56
57 void addCubic(const SkPoint pts[4]) {
58 fSegments.push_back().addCubic(pts, fOperand, fXor);
59 fContainsCurves = fContainsCubics = true;
60 }
61
62 int addLine(const SkPoint pts[2]) {
63 fSegments.push_back().addLine(pts, fOperand, fXor);
64 return fSegments.count();
65 }
66
67 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
68 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
69 }
70
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000071 bool addPartialCoincident(int index, SkOpContour* other, int otherIndex,
caryclark@google.com570863f2013-09-16 15:55:01 +000072 const SkIntersections& ts, int ptIndex, bool swap);
73
caryclark@google.com07393ca2013-04-08 11:47:37 +000074 int addQuad(const SkPoint pts[3]) {
75 fSegments.push_back().addQuad(pts, fOperand, fXor);
76 fContainsCurves = true;
77 return fSegments.count();
78 }
79
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +000080 int addT(int segIndex, SkOpContour* other, int otherIndex, const SkPoint& pt, double newT) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000081 setContainsIntercepts();
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +000082 return fSegments[segIndex].addT(&other->fSegments[otherIndex], pt, newT);
caryclark@google.com07393ca2013-04-08 11:47:37 +000083 }
84
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000085 int addSelfT(int segIndex, const SkPoint& pt, double newT) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000086 setContainsIntercepts();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000087 return fSegments[segIndex].addSelfT(pt, newT);
caryclark@google.com07393ca2013-04-08 11:47:37 +000088 }
89
caryclarkdac1d172014-06-17 05:15:38 -070090 void align(const SkOpSegment::AlignedSpan& aligned, bool swap, SkCoincidence* coincidence);
91 void alignCoincidence(const SkOpSegment::AlignedSpan& aligned,
92 SkTArray<SkCoincidence, true>* coincidences);
93
94 void alignCoincidence(const SkOpSegment::AlignedSpan& aligned) {
95 alignCoincidence(aligned, &fCoincidences);
96 alignCoincidence(aligned, &fPartialCoincidences);
97 }
98
99 void alignMultiples(SkTDArray<SkOpSegment::AlignedSpan>* aligned) {
100 int segmentCount = fSegments.count();
101 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
102 SkOpSegment& segment = fSegments[sIndex];
103 if (segment.hasMultiples()) {
104 segment.alignMultiples(aligned);
105 }
106 }
107 }
108
109 void alignTPt(int segmentIndex, const SkOpContour* other, int otherIndex,
110 bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const;
111
caryclark@google.com07393ca2013-04-08 11:47:37 +0000112 const SkPathOpsBounds& bounds() const {
113 return fBounds;
114 }
115
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000116 bool calcAngles();
caryclark630240d2014-09-19 06:33:31 -0700117 bool calcCoincidentWinding();
caryclark@google.com570863f2013-09-16 15:55:01 +0000118 void calcPartialCoincidentWinding();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000119
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000120 void checkDuplicates() {
121 int segmentCount = fSegments.count();
122 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
123 SkOpSegment& segment = fSegments[sIndex];
124 if (segment.count() > 2) {
125 segment.checkDuplicates();
126 }
127 }
128 }
129
caryclark65f55312014-11-13 06:58:52 -0800130 bool checkEnds() {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000131 if (!fContainsCurves) {
caryclark65f55312014-11-13 06:58:52 -0800132 return true;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000133 }
134 int segmentCount = fSegments.count();
135 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
136 SkOpSegment* segment = &fSegments[sIndex];
137 if (segment->verb() == SkPath::kLine_Verb) {
138 continue;
139 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000140 if (segment->done()) {
141 continue; // likely coincident, nothing to do
142 }
caryclark65f55312014-11-13 06:58:52 -0800143 if (!segment->checkEnds()) {
144 return false;
145 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000146 }
caryclark65f55312014-11-13 06:58:52 -0800147 return true;
caryclark@google.com570863f2013-09-16 15:55:01 +0000148 }
149
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000150 void checkMultiples() {
151 int segmentCount = fSegments.count();
152 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
153 SkOpSegment& segment = fSegments[sIndex];
154 if (segment.count() > 2) {
155 segment.checkMultiples();
caryclarkdac1d172014-06-17 05:15:38 -0700156 fMultiples |= segment.hasMultiples();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000157 }
158 }
159 }
160
161 void checkSmall() {
162 int segmentCount = fSegments.count();
163 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
164 SkOpSegment& segment = fSegments[sIndex];
caryclarkdac1d172014-06-17 05:15:38 -0700165 // OPTIMIZATION : skip segments that are done?
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000166 if (segment.hasSmall()) {
167 segment.checkSmall();
168 }
169 }
170 }
171
caryclark@google.com570863f2013-09-16 15:55:01 +0000172 // if same point has different T values, choose a common T
173 void checkTiny() {
174 int segmentCount = fSegments.count();
175 if (segmentCount <= 2) {
176 return;
177 }
178 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000179 SkOpSegment& segment = fSegments[sIndex];
180 if (segment.hasTiny()) {
181 segment.checkTiny();
182 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000183 }
184 }
185
caryclark@google.com07393ca2013-04-08 11:47:37 +0000186 void complete() {
187 setBounds();
188 fContainsIntercepts = false;
189 }
190
191 bool containsCubics() const {
192 return fContainsCubics;
193 }
194
195 bool crosses(const SkOpContour* crosser) const {
196 for (int index = 0; index < fCrosses.count(); ++index) {
197 if (fCrosses[index] == crosser) {
198 return true;
199 }
200 }
201 return false;
202 }
203
204 bool done() const {
205 return fDone;
206 }
207
208 const SkPoint& end() const {
209 const SkOpSegment& segment = fSegments.back();
reed@google.com277c3f82013-05-31 15:17:50 +0000210 return segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000211 }
212
caryclark@google.com07393ca2013-04-08 11:47:37 +0000213 void fixOtherTIndex() {
214 int segmentCount = fSegments.count();
215 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
216 fSegments[sIndex].fixOtherTIndex();
217 }
218 }
219
caryclarkdac1d172014-06-17 05:15:38 -0700220 bool hasMultiples() const {
221 return fMultiples;
222 }
223
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000224 void joinCoincidence() {
225 joinCoincidence(fCoincidences, false);
226 joinCoincidence(fPartialCoincidences, true);
227 }
228
caryclark@google.com07393ca2013-04-08 11:47:37 +0000229 SkOpSegment* nonVerticalSegment(int* start, int* end);
skia.committer@gmail.com32840172013-04-09 07:01:27 +0000230
caryclark@google.com07393ca2013-04-08 11:47:37 +0000231 bool operand() const {
232 return fOperand;
233 }
234
235 void reset() {
236 fSegments.reset();
237 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclarkdac1d172014-06-17 05:15:38 -0700238 fContainsCurves = fContainsCubics = fContainsIntercepts = fDone = fMultiples = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000239 }
240
caryclarkdac1d172014-06-17 05:15:38 -0700241 void resolveNearCoincidence();
242
caryclark@google.com07393ca2013-04-08 11:47:37 +0000243 SkTArray<SkOpSegment>& segments() {
244 return fSegments;
245 }
246
247 void setContainsIntercepts() {
248 fContainsIntercepts = true;
249 }
250
251 void setOperand(bool isOp) {
252 fOperand = isOp;
253 }
254
255 void setOppXor(bool isOppXor) {
256 fOppXor = isOppXor;
257 int segmentCount = fSegments.count();
258 for (int test = 0; test < segmentCount; ++test) {
259 fSegments[test].setOppXor(isOppXor);
260 }
261 }
262
263 void setXor(bool isXor) {
264 fXor = isXor;
265 }
266
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000267 void sortAngles();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000268 void sortSegments();
skia.committer@gmail.com32840172013-04-09 07:01:27 +0000269
caryclark@google.com07393ca2013-04-08 11:47:37 +0000270 const SkPoint& start() const {
271 return fSegments.front().pts()[0];
272 }
skia.committer@gmail.com32840172013-04-09 07:01:27 +0000273
caryclark@google.com07393ca2013-04-08 11:47:37 +0000274 void toPath(SkPathWriter* path) const;
skia.committer@gmail.com32840172013-04-09 07:01:27 +0000275
caryclark@google.com07393ca2013-04-08 11:47:37 +0000276 void toPartialBackward(SkPathWriter* path) const {
277 int segmentCount = fSegments.count();
278 for (int test = segmentCount - 1; test >= 0; --test) {
279 fSegments[test].addCurveTo(1, 0, path, true);
280 }
281 }
282
283 void toPartialForward(SkPathWriter* path) const {
284 int segmentCount = fSegments.count();
285 for (int test = 0; test < segmentCount; ++test) {
286 fSegments[test].addCurveTo(0, 1, path, true);
287 }
288 }
289
290 void topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY, SkOpSegment** topStart);
291 SkOpSegment* undoneSegment(int* start, int* end);
292
293 int updateSegment(int index, const SkPoint* pts) {
294 SkOpSegment& segment = fSegments[index];
295 segment.updatePts(pts);
reed@google.com277c3f82013-05-31 15:17:50 +0000296 return SkPathOpsVerbToPoints(segment.verb()) + 1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000297 }
298
299#if DEBUG_TEST
300 SkTArray<SkOpSegment>& debugSegments() {
301 return fSegments;
302 }
303#endif
304
caryclark@google.coma5e55922013-05-07 18:51:31 +0000305#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
caryclark@google.com07393ca2013-04-08 11:47:37 +0000306 void debugShowActiveSpans() {
307 for (int index = 0; index < fSegments.count(); ++index) {
308 fSegments[index].debugShowActiveSpans();
309 }
310 }
311#endif
312
313#if DEBUG_SHOW_WINDING
314 int debugShowWindingValues(int totalSegments, int ofInterest);
caryclark@google.comd892bd82013-06-17 14:10:36 +0000315 static void debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000316#endif
317
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000318 // available to test routines only
319 void dump() const;
320 void dumpAngles() const;
caryclarkdac1d172014-06-17 05:15:38 -0700321 void dumpCoincidence(const SkCoincidence& ) const;
322 void dumpCoincidences() const;
323 void dumpPt(int ) const;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000324 void dumpPts() const;
caryclarkdac1d172014-06-17 05:15:38 -0700325 void dumpSpan(int ) const;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000326 void dumpSpans() const;
327
caryclark@google.com07393ca2013-04-08 11:47:37 +0000328private:
caryclarkdac1d172014-06-17 05:15:38 -0700329 void alignPt(int index, SkPoint* point, int zeroPt) const;
330 int alignT(bool swap, int tIndex, SkIntersections* ts) const;
caryclark630240d2014-09-19 06:33:31 -0700331 bool calcCommonCoincidentWinding(const SkCoincidence& );
caryclarkdac1d172014-06-17 05:15:38 -0700332 void checkCoincidentPair(const SkCoincidence& oneCoin, int oneIdx,
333 const SkCoincidence& twoCoin, int twoIdx, bool partial);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000334 void joinCoincidence(const SkTArray<SkCoincidence, true>& , bool partial);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000335 void setBounds();
336
337 SkTArray<SkOpSegment> fSegments;
caryclark@google.comd892bd82013-06-17 14:10:36 +0000338 SkTArray<SkOpSegment*, true> fSortedSegments;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000339 int fFirstSorted;
caryclark@google.comd892bd82013-06-17 14:10:36 +0000340 SkTArray<SkCoincidence, true> fCoincidences;
caryclark@google.com570863f2013-09-16 15:55:01 +0000341 SkTArray<SkCoincidence, true> fPartialCoincidences;
caryclark@google.comd892bd82013-06-17 14:10:36 +0000342 SkTArray<const SkOpContour*, true> fCrosses;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000343 SkPathOpsBounds fBounds;
344 bool fContainsIntercepts; // FIXME: is this used by anybody?
345 bool fContainsCubics;
346 bool fContainsCurves;
347 bool fDone;
caryclarkdac1d172014-06-17 05:15:38 -0700348 bool fMultiples; // set if some segment has multiple identical intersections with other curves
caryclark@google.com07393ca2013-04-08 11:47:37 +0000349 bool fOperand; // true for the second argument to a binary operator
350 bool fXor;
351 bool fOppXor;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000352#if defined(SK_DEBUG) || !FORCE_RELEASE
353 int debugID() const { return fID; }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000354 int fID;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000355#else
356 int debugID() const { return -1; }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000357#endif
358};
359
360#endif