blob: 5b92a4b071b999d9adfad3d8f89a8ad025b75f38 [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
13class SkIntersections;
14class SkOpContour;
15class SkPathWriter;
16
17struct SkCoincidence {
caryclark@google.com570863f2013-09-16 15:55:01 +000018 SkOpContour* fOther;
caryclark@google.com07393ca2013-04-08 11:47:37 +000019 int fSegments[2];
20 double fTs[2][2];
21 SkPoint fPts[2];
22};
23
24class SkOpContour {
25public:
26 SkOpContour() {
27 reset();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000028#if defined(SK_DEBUG) || !FORCE_RELEASE
caryclark@google.com570863f2013-09-16 15:55:01 +000029 fID = ++SkPathOpsDebug::gContourID;
caryclark@google.com07393ca2013-04-08 11:47:37 +000030#endif
31 }
32
33 bool operator<(const SkOpContour& rh) const {
34 return fBounds.fTop == rh.fBounds.fTop
35 ? fBounds.fLeft < rh.fBounds.fLeft
36 : fBounds.fTop < rh.fBounds.fTop;
37 }
38
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000039 bool addCoincident(int index, SkOpContour* other, int otherIndex,
caryclark@google.com07393ca2013-04-08 11:47:37 +000040 const SkIntersections& ts, bool swap);
41 void addCoincidentPoints();
42
43 void addCross(const SkOpContour* crosser) {
44#ifdef DEBUG_CROSS
45 for (int index = 0; index < fCrosses.count(); ++index) {
46 SkASSERT(fCrosses[index] != crosser);
47 }
48#endif
caryclark@google.comd892bd82013-06-17 14:10:36 +000049 fCrosses.push_back(crosser);
caryclark@google.com07393ca2013-04-08 11:47:37 +000050 }
51
52 void addCubic(const SkPoint pts[4]) {
53 fSegments.push_back().addCubic(pts, fOperand, fXor);
54 fContainsCurves = fContainsCubics = true;
55 }
56
57 int addLine(const SkPoint pts[2]) {
58 fSegments.push_back().addLine(pts, fOperand, fXor);
59 return fSegments.count();
60 }
61
62 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
63 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
64 }
65
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000066 bool addPartialCoincident(int index, SkOpContour* other, int otherIndex,
caryclark@google.com570863f2013-09-16 15:55:01 +000067 const SkIntersections& ts, int ptIndex, bool swap);
68
caryclark@google.com07393ca2013-04-08 11:47:37 +000069 int addQuad(const SkPoint pts[3]) {
70 fSegments.push_back().addQuad(pts, fOperand, fXor);
71 fContainsCurves = true;
72 return fSegments.count();
73 }
74
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +000075 int addT(int segIndex, SkOpContour* other, int otherIndex, const SkPoint& pt, double newT) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000076 setContainsIntercepts();
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +000077 return fSegments[segIndex].addT(&other->fSegments[otherIndex], pt, newT);
caryclark@google.com07393ca2013-04-08 11:47:37 +000078 }
79
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000080 int addSelfT(int segIndex, const SkPoint& pt, double newT) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000081 setContainsIntercepts();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000082 return fSegments[segIndex].addSelfT(pt, newT);
caryclark@google.com07393ca2013-04-08 11:47:37 +000083 }
84
caryclark@google.com07393ca2013-04-08 11:47:37 +000085 const SkPathOpsBounds& bounds() const {
86 return fBounds;
87 }
88
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000089 bool calcAngles();
caryclark@google.com07393ca2013-04-08 11:47:37 +000090 void calcCoincidentWinding();
caryclark@google.com570863f2013-09-16 15:55:01 +000091 void calcPartialCoincidentWinding();
caryclark@google.com07393ca2013-04-08 11:47:37 +000092
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000093 void checkDuplicates() {
94 int segmentCount = fSegments.count();
95 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
96 SkOpSegment& segment = fSegments[sIndex];
97 if (segment.count() > 2) {
98 segment.checkDuplicates();
99 }
100 }
101 }
102
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000103 void checkEnds() {
104 if (!fContainsCurves) {
105 return;
106 }
107 int segmentCount = fSegments.count();
108 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
109 SkOpSegment* segment = &fSegments[sIndex];
110 if (segment->verb() == SkPath::kLine_Verb) {
111 continue;
112 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000113 if (segment->done()) {
114 continue; // likely coincident, nothing to do
115 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000116 segment->checkEnds();
117 }
118 }
119
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000120 void checkMultiples() {
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.checkMultiples();
126 }
127 }
128 }
129
130 void checkSmall() {
131 int segmentCount = fSegments.count();
132 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
133 SkOpSegment& segment = fSegments[sIndex];
134 if (segment.hasSmall()) {
135 segment.checkSmall();
136 }
137 }
138 }
139
caryclark@google.com570863f2013-09-16 15:55:01 +0000140 // if same point has different T values, choose a common T
141 void checkTiny() {
142 int segmentCount = fSegments.count();
143 if (segmentCount <= 2) {
144 return;
145 }
146 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000147 SkOpSegment& segment = fSegments[sIndex];
148 if (segment.hasTiny()) {
149 segment.checkTiny();
150 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000151 }
152 }
153
caryclark@google.com07393ca2013-04-08 11:47:37 +0000154 void complete() {
155 setBounds();
156 fContainsIntercepts = false;
157 }
158
159 bool containsCubics() const {
160 return fContainsCubics;
161 }
162
163 bool crosses(const SkOpContour* crosser) const {
164 for (int index = 0; index < fCrosses.count(); ++index) {
165 if (fCrosses[index] == crosser) {
166 return true;
167 }
168 }
169 return false;
170 }
171
172 bool done() const {
173 return fDone;
174 }
175
176 const SkPoint& end() const {
177 const SkOpSegment& segment = fSegments.back();
reed@google.com277c3f82013-05-31 15:17:50 +0000178 return segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000179 }
180
caryclark@google.com07393ca2013-04-08 11:47:37 +0000181 void fixOtherTIndex() {
182 int segmentCount = fSegments.count();
183 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
184 fSegments[sIndex].fixOtherTIndex();
185 }
186 }
187
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000188 void joinCoincidence() {
189 joinCoincidence(fCoincidences, false);
190 joinCoincidence(fPartialCoincidences, true);
191 }
192
caryclark@google.com07393ca2013-04-08 11:47:37 +0000193 SkOpSegment* nonVerticalSegment(int* start, int* end);
skia.committer@gmail.com32840172013-04-09 07:01:27 +0000194
caryclark@google.com07393ca2013-04-08 11:47:37 +0000195 bool operand() const {
196 return fOperand;
197 }
198
199 void reset() {
200 fSegments.reset();
201 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
202 fContainsCurves = fContainsCubics = fContainsIntercepts = fDone = false;
203 }
204
205 SkTArray<SkOpSegment>& segments() {
206 return fSegments;
207 }
208
209 void setContainsIntercepts() {
210 fContainsIntercepts = true;
211 }
212
213 void setOperand(bool isOp) {
214 fOperand = isOp;
215 }
216
217 void setOppXor(bool isOppXor) {
218 fOppXor = isOppXor;
219 int segmentCount = fSegments.count();
220 for (int test = 0; test < segmentCount; ++test) {
221 fSegments[test].setOppXor(isOppXor);
222 }
223 }
224
225 void setXor(bool isXor) {
226 fXor = isXor;
227 }
228
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000229 void sortAngles();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000230 void sortSegments();
skia.committer@gmail.com32840172013-04-09 07:01:27 +0000231
caryclark@google.com07393ca2013-04-08 11:47:37 +0000232 const SkPoint& start() const {
233 return fSegments.front().pts()[0];
234 }
skia.committer@gmail.com32840172013-04-09 07:01:27 +0000235
caryclark@google.com07393ca2013-04-08 11:47:37 +0000236 void toPath(SkPathWriter* path) const;
skia.committer@gmail.com32840172013-04-09 07:01:27 +0000237
caryclark@google.com07393ca2013-04-08 11:47:37 +0000238 void toPartialBackward(SkPathWriter* path) const {
239 int segmentCount = fSegments.count();
240 for (int test = segmentCount - 1; test >= 0; --test) {
241 fSegments[test].addCurveTo(1, 0, path, true);
242 }
243 }
244
245 void toPartialForward(SkPathWriter* path) const {
246 int segmentCount = fSegments.count();
247 for (int test = 0; test < segmentCount; ++test) {
248 fSegments[test].addCurveTo(0, 1, path, true);
249 }
250 }
251
252 void topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY, SkOpSegment** topStart);
253 SkOpSegment* undoneSegment(int* start, int* end);
254
255 int updateSegment(int index, const SkPoint* pts) {
256 SkOpSegment& segment = fSegments[index];
257 segment.updatePts(pts);
reed@google.com277c3f82013-05-31 15:17:50 +0000258 return SkPathOpsVerbToPoints(segment.verb()) + 1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000259 }
260
261#if DEBUG_TEST
262 SkTArray<SkOpSegment>& debugSegments() {
263 return fSegments;
264 }
265#endif
266
caryclark@google.coma5e55922013-05-07 18:51:31 +0000267#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
caryclark@google.com07393ca2013-04-08 11:47:37 +0000268 void debugShowActiveSpans() {
269 for (int index = 0; index < fSegments.count(); ++index) {
270 fSegments[index].debugShowActiveSpans();
271 }
272 }
273#endif
274
275#if DEBUG_SHOW_WINDING
276 int debugShowWindingValues(int totalSegments, int ofInterest);
caryclark@google.comd892bd82013-06-17 14:10:36 +0000277 static void debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000278#endif
279
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000280 // available to test routines only
281 void dump() const;
282 void dumpAngles() const;
283 void dumpPts() const;
284 void dumpSpans() const;
285
caryclark@google.com07393ca2013-04-08 11:47:37 +0000286private:
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000287 void calcCommonCoincidentWinding(const SkCoincidence& );
288 void joinCoincidence(const SkTArray<SkCoincidence, true>& , bool partial);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000289 void setBounds();
290
291 SkTArray<SkOpSegment> fSegments;
caryclark@google.comd892bd82013-06-17 14:10:36 +0000292 SkTArray<SkOpSegment*, true> fSortedSegments;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000293 int fFirstSorted;
caryclark@google.comd892bd82013-06-17 14:10:36 +0000294 SkTArray<SkCoincidence, true> fCoincidences;
caryclark@google.com570863f2013-09-16 15:55:01 +0000295 SkTArray<SkCoincidence, true> fPartialCoincidences;
caryclark@google.comd892bd82013-06-17 14:10:36 +0000296 SkTArray<const SkOpContour*, true> fCrosses;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000297 SkPathOpsBounds fBounds;
298 bool fContainsIntercepts; // FIXME: is this used by anybody?
299 bool fContainsCubics;
300 bool fContainsCurves;
301 bool fDone;
302 bool fOperand; // true for the second argument to a binary operator
303 bool fXor;
304 bool fOppXor;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000305#if defined(SK_DEBUG) || !FORCE_RELEASE
306 int debugID() const { return fID; }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000307 int fID;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000308#else
309 int debugID() const { return -1; }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000310#endif
311};
312
313#endif