1/*
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 {
18    SkOpContour* fContours[2];
19    int fSegments[2];
20    double fTs[2][2];
21    SkPoint fPts[2];
22};
23
24class SkOpContour {
25public:
26    SkOpContour() {
27        reset();
28#if DEBUG_DUMP
29        fID = ++gContourID;
30#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
39    void addCoincident(int index, SkOpContour* other, int otherIndex,
40                       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
49        fCrosses.push_back(crosser);
50    }
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
66    int addQuad(const SkPoint pts[3]) {
67        fSegments.push_back().addQuad(pts, fOperand, fXor);
68        fContainsCurves = true;
69        return fSegments.count();
70    }
71
72    int addT(int segIndex, SkOpContour* other, int otherIndex, const SkPoint& pt, double newT) {
73        setContainsIntercepts();
74        return fSegments[segIndex].addT(&other->fSegments[otherIndex], pt, newT);
75    }
76
77    int addSelfT(int segIndex, SkOpContour* other, int otherIndex, const SkPoint& pt, double newT) {
78        setContainsIntercepts();
79        return fSegments[segIndex].addSelfT(&other->fSegments[otherIndex], pt, newT);
80    }
81
82    int addUnsortableT(int segIndex, SkOpContour* other, int otherIndex, bool start,
83                       const SkPoint& pt, double newT) {
84        return fSegments[segIndex].addUnsortableT(&other->fSegments[otherIndex], start, pt, newT);
85    }
86
87    const SkPathOpsBounds& bounds() const {
88        return fBounds;
89    }
90
91    void calcCoincidentWinding();
92
93    void checkEnds() {
94        if (!fContainsCurves) {
95            return;
96        }
97        int segmentCount = fSegments.count();
98        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
99            SkOpSegment* segment = &fSegments[sIndex];
100            if (segment->verb() == SkPath::kLine_Verb) {
101                continue;
102            }
103            fSegments[sIndex].checkEnds();
104        }
105    }
106
107    void complete() {
108        setBounds();
109        fContainsIntercepts = false;
110    }
111
112    bool containsCubics() const {
113        return fContainsCubics;
114    }
115
116    bool crosses(const SkOpContour* crosser) const {
117        for (int index = 0; index < fCrosses.count(); ++index) {
118            if (fCrosses[index] == crosser) {
119                return true;
120            }
121        }
122        return false;
123    }
124
125    bool done() const {
126        return fDone;
127    }
128
129    const SkPoint& end() const {
130        const SkOpSegment& segment = fSegments.back();
131        return segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
132    }
133
134    void findTooCloseToCall() {
135        int segmentCount = fSegments.count();
136        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
137            fSegments[sIndex].findTooCloseToCall();
138        }
139    }
140
141    void fixOtherTIndex() {
142        int segmentCount = fSegments.count();
143        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
144            fSegments[sIndex].fixOtherTIndex();
145        }
146    }
147
148    SkOpSegment* nonVerticalSegment(int* start, int* end);
149
150    bool operand() const {
151        return fOperand;
152    }
153
154    void reset() {
155        fSegments.reset();
156        fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
157        fContainsCurves = fContainsCubics = fContainsIntercepts = fDone = false;
158    }
159
160    SkTArray<SkOpSegment>& segments() {
161        return fSegments;
162    }
163
164    void setContainsIntercepts() {
165        fContainsIntercepts = true;
166    }
167
168    void setOperand(bool isOp) {
169        fOperand = isOp;
170    }
171
172    void setOppXor(bool isOppXor) {
173        fOppXor = isOppXor;
174        int segmentCount = fSegments.count();
175        for (int test = 0; test < segmentCount; ++test) {
176            fSegments[test].setOppXor(isOppXor);
177        }
178    }
179
180    void setXor(bool isXor) {
181        fXor = isXor;
182    }
183
184    void sortSegments();
185
186    const SkPoint& start() const {
187        return fSegments.front().pts()[0];
188    }
189
190    void toPath(SkPathWriter* path) const;
191
192    void toPartialBackward(SkPathWriter* path) const {
193        int segmentCount = fSegments.count();
194        for (int test = segmentCount - 1; test >= 0; --test) {
195            fSegments[test].addCurveTo(1, 0, path, true);
196        }
197    }
198
199    void toPartialForward(SkPathWriter* path) const {
200        int segmentCount = fSegments.count();
201        for (int test = 0; test < segmentCount; ++test) {
202            fSegments[test].addCurveTo(0, 1, path, true);
203        }
204    }
205
206    void topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY, SkOpSegment** topStart);
207    SkOpSegment* undoneSegment(int* start, int* end);
208
209    int updateSegment(int index, const SkPoint* pts) {
210        SkOpSegment& segment = fSegments[index];
211        segment.updatePts(pts);
212        return SkPathOpsVerbToPoints(segment.verb()) + 1;
213    }
214
215#if DEBUG_TEST
216    SkTArray<SkOpSegment>& debugSegments() {
217        return fSegments;
218    }
219#endif
220
221#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
222    void debugShowActiveSpans() {
223        for (int index = 0; index < fSegments.count(); ++index) {
224            fSegments[index].debugShowActiveSpans();
225        }
226    }
227#endif
228
229#if DEBUG_SHOW_WINDING
230    int debugShowWindingValues(int totalSegments, int ofInterest);
231    static void debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList);
232#endif
233
234private:
235    void setBounds();
236
237    SkTArray<SkOpSegment> fSegments;
238    SkTArray<SkOpSegment*, true> fSortedSegments;
239    int fFirstSorted;
240    SkTArray<SkCoincidence, true> fCoincidences;
241    SkTArray<const SkOpContour*, true> fCrosses;
242    SkPathOpsBounds fBounds;
243    bool fContainsIntercepts;  // FIXME: is this used by anybody?
244    bool fContainsCubics;
245    bool fContainsCurves;
246    bool fDone;
247    bool fOperand;  // true for the second argument to a binary operator
248    bool fXor;
249    bool fOppXor;
250#if DEBUG_DUMP
251    int fID;
252#endif
253};
254
255#endif
256