1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2012 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci#include "src/core/SkPathPriv.h"
8cb93a386Sopenharmony_ci#include "src/core/SkTSort.h"
9cb93a386Sopenharmony_ci#include "src/pathops/SkPathOpsBounds.h"
10cb93a386Sopenharmony_ci#include "src/pathops/SkPathOpsConic.h"
11cb93a386Sopenharmony_ci#include "src/pathops/SkPathOpsCubic.h"
12cb93a386Sopenharmony_ci#include "src/pathops/SkPathOpsLine.h"
13cb93a386Sopenharmony_ci#include "src/pathops/SkPathOpsQuad.h"
14cb93a386Sopenharmony_ci#include "src/pathops/SkPathOpsTSect.h"
15cb93a386Sopenharmony_ci#include "src/pathops/SkReduceOrder.h"
16cb93a386Sopenharmony_ci#include "tests/PathOpsTestCommon.h"
17cb93a386Sopenharmony_ci
18cb93a386Sopenharmony_ci#include <utility>
19cb93a386Sopenharmony_ci
20cb93a386Sopenharmony_cistatic double calc_t_div(const SkDCubic& cubic, double precision, double start) {
21cb93a386Sopenharmony_ci    const double adjust = sqrt(3.) / 36;
22cb93a386Sopenharmony_ci    SkDCubic sub;
23cb93a386Sopenharmony_ci    const SkDCubic* cPtr;
24cb93a386Sopenharmony_ci    if (start == 0) {
25cb93a386Sopenharmony_ci        cPtr = &cubic;
26cb93a386Sopenharmony_ci    } else {
27cb93a386Sopenharmony_ci        // OPTIMIZE: special-case half-split ?
28cb93a386Sopenharmony_ci        sub = cubic.subDivide(start, 1);
29cb93a386Sopenharmony_ci        cPtr = &sub;
30cb93a386Sopenharmony_ci    }
31cb93a386Sopenharmony_ci    const SkDCubic& c = *cPtr;
32cb93a386Sopenharmony_ci    double dx = c[3].fX - 3 * (c[2].fX - c[1].fX) - c[0].fX;
33cb93a386Sopenharmony_ci    double dy = c[3].fY - 3 * (c[2].fY - c[1].fY) - c[0].fY;
34cb93a386Sopenharmony_ci    double dist = sqrt(dx * dx + dy * dy);
35cb93a386Sopenharmony_ci    double tDiv3 = precision / (adjust * dist);
36cb93a386Sopenharmony_ci    double t = SkDCubeRoot(tDiv3);
37cb93a386Sopenharmony_ci    if (start > 0) {
38cb93a386Sopenharmony_ci        t = start + (1 - start) * t;
39cb93a386Sopenharmony_ci    }
40cb93a386Sopenharmony_ci    return t;
41cb93a386Sopenharmony_ci}
42cb93a386Sopenharmony_ci
43cb93a386Sopenharmony_cistatic bool add_simple_ts(const SkDCubic& cubic, double precision, SkTArray<double, true>* ts) {
44cb93a386Sopenharmony_ci    double tDiv = calc_t_div(cubic, precision, 0);
45cb93a386Sopenharmony_ci    if (tDiv >= 1) {
46cb93a386Sopenharmony_ci        return true;
47cb93a386Sopenharmony_ci    }
48cb93a386Sopenharmony_ci    if (tDiv >= 0.5) {
49cb93a386Sopenharmony_ci        ts->push_back(0.5);
50cb93a386Sopenharmony_ci        return true;
51cb93a386Sopenharmony_ci    }
52cb93a386Sopenharmony_ci    return false;
53cb93a386Sopenharmony_ci}
54cb93a386Sopenharmony_ci
55cb93a386Sopenharmony_cistatic void addTs(const SkDCubic& cubic, double precision, double start, double end,
56cb93a386Sopenharmony_ci        SkTArray<double, true>* ts) {
57cb93a386Sopenharmony_ci    double tDiv = calc_t_div(cubic, precision, 0);
58cb93a386Sopenharmony_ci    double parts = ceil(1.0 / tDiv);
59cb93a386Sopenharmony_ci    for (double index = 0; index < parts; ++index) {
60cb93a386Sopenharmony_ci        double newT = start + (index / parts) * (end - start);
61cb93a386Sopenharmony_ci        if (newT > 0 && newT < 1) {
62cb93a386Sopenharmony_ci            ts->push_back(newT);
63cb93a386Sopenharmony_ci        }
64cb93a386Sopenharmony_ci    }
65cb93a386Sopenharmony_ci}
66cb93a386Sopenharmony_ci
67cb93a386Sopenharmony_cistatic void toQuadraticTs(const SkDCubic* cubic, double precision, SkTArray<double, true>* ts) {
68cb93a386Sopenharmony_ci    SkReduceOrder reducer;
69cb93a386Sopenharmony_ci    int order = reducer.reduce(*cubic, SkReduceOrder::kAllow_Quadratics);
70cb93a386Sopenharmony_ci    if (order < 3) {
71cb93a386Sopenharmony_ci        return;
72cb93a386Sopenharmony_ci    }
73cb93a386Sopenharmony_ci    double inflectT[5];
74cb93a386Sopenharmony_ci    int inflections = cubic->findInflections(inflectT);
75cb93a386Sopenharmony_ci    SkASSERT(inflections <= 2);
76cb93a386Sopenharmony_ci    if (!cubic->endsAreExtremaInXOrY()) {
77cb93a386Sopenharmony_ci        inflections += cubic->findMaxCurvature(&inflectT[inflections]);
78cb93a386Sopenharmony_ci        SkASSERT(inflections <= 5);
79cb93a386Sopenharmony_ci    }
80cb93a386Sopenharmony_ci    SkTQSort<double>(inflectT, inflectT + inflections);
81cb93a386Sopenharmony_ci    // OPTIMIZATION: is this filtering common enough that it needs to be pulled out into its
82cb93a386Sopenharmony_ci    // own subroutine?
83cb93a386Sopenharmony_ci    while (inflections && approximately_less_than_zero(inflectT[0])) {
84cb93a386Sopenharmony_ci        memmove(inflectT, &inflectT[1], sizeof(inflectT[0]) * --inflections);
85cb93a386Sopenharmony_ci    }
86cb93a386Sopenharmony_ci    int start = 0;
87cb93a386Sopenharmony_ci    int next = 1;
88cb93a386Sopenharmony_ci    while (next < inflections) {
89cb93a386Sopenharmony_ci        if (!approximately_equal(inflectT[start], inflectT[next])) {
90cb93a386Sopenharmony_ci            ++start;
91cb93a386Sopenharmony_ci        ++next;
92cb93a386Sopenharmony_ci            continue;
93cb93a386Sopenharmony_ci        }
94cb93a386Sopenharmony_ci        memmove(&inflectT[start], &inflectT[next], sizeof(inflectT[0]) * (--inflections - start));
95cb93a386Sopenharmony_ci    }
96cb93a386Sopenharmony_ci
97cb93a386Sopenharmony_ci    while (inflections && approximately_greater_than_one(inflectT[inflections - 1])) {
98cb93a386Sopenharmony_ci        --inflections;
99cb93a386Sopenharmony_ci    }
100cb93a386Sopenharmony_ci    SkDCubicPair pair;
101cb93a386Sopenharmony_ci    if (inflections == 1) {
102cb93a386Sopenharmony_ci        pair = cubic->chopAt(inflectT[0]);
103cb93a386Sopenharmony_ci        int orderP1 = reducer.reduce(pair.first(), SkReduceOrder::kNo_Quadratics);
104cb93a386Sopenharmony_ci        if (orderP1 < 2) {
105cb93a386Sopenharmony_ci            --inflections;
106cb93a386Sopenharmony_ci        } else {
107cb93a386Sopenharmony_ci            int orderP2 = reducer.reduce(pair.second(), SkReduceOrder::kNo_Quadratics);
108cb93a386Sopenharmony_ci            if (orderP2 < 2) {
109cb93a386Sopenharmony_ci                --inflections;
110cb93a386Sopenharmony_ci            }
111cb93a386Sopenharmony_ci        }
112cb93a386Sopenharmony_ci    }
113cb93a386Sopenharmony_ci    if (inflections == 0 && add_simple_ts(*cubic, precision, ts)) {
114cb93a386Sopenharmony_ci        return;
115cb93a386Sopenharmony_ci    }
116cb93a386Sopenharmony_ci    if (inflections == 1) {
117cb93a386Sopenharmony_ci        pair = cubic->chopAt(inflectT[0]);
118cb93a386Sopenharmony_ci        addTs(pair.first(), precision, 0, inflectT[0], ts);
119cb93a386Sopenharmony_ci        addTs(pair.second(), precision, inflectT[0], 1, ts);
120cb93a386Sopenharmony_ci        return;
121cb93a386Sopenharmony_ci    }
122cb93a386Sopenharmony_ci    if (inflections > 1) {
123cb93a386Sopenharmony_ci        SkDCubic part = cubic->subDivide(0, inflectT[0]);
124cb93a386Sopenharmony_ci        addTs(part, precision, 0, inflectT[0], ts);
125cb93a386Sopenharmony_ci        int last = inflections - 1;
126cb93a386Sopenharmony_ci        for (int idx = 0; idx < last; ++idx) {
127cb93a386Sopenharmony_ci            part = cubic->subDivide(inflectT[idx], inflectT[idx + 1]);
128cb93a386Sopenharmony_ci            addTs(part, precision, inflectT[idx], inflectT[idx + 1], ts);
129cb93a386Sopenharmony_ci        }
130cb93a386Sopenharmony_ci        part = cubic->subDivide(inflectT[last], 1);
131cb93a386Sopenharmony_ci        addTs(part, precision, inflectT[last], 1, ts);
132cb93a386Sopenharmony_ci        return;
133cb93a386Sopenharmony_ci    }
134cb93a386Sopenharmony_ci    addTs(*cubic, precision, 0, 1, ts);
135cb93a386Sopenharmony_ci}
136cb93a386Sopenharmony_ci
137cb93a386Sopenharmony_civoid CubicToQuads(const SkDCubic& cubic, double precision, SkTArray<SkDQuad, true>& quads) {
138cb93a386Sopenharmony_ci    SkTArray<double, true> ts;
139cb93a386Sopenharmony_ci    toQuadraticTs(&cubic, precision, &ts);
140cb93a386Sopenharmony_ci    if (ts.count() <= 0) {
141cb93a386Sopenharmony_ci        SkDQuad quad = cubic.toQuad();
142cb93a386Sopenharmony_ci        quads.push_back(quad);
143cb93a386Sopenharmony_ci        return;
144cb93a386Sopenharmony_ci    }
145cb93a386Sopenharmony_ci    double tStart = 0;
146cb93a386Sopenharmony_ci    for (int i1 = 0; i1 <= ts.count(); ++i1) {
147cb93a386Sopenharmony_ci        const double tEnd = i1 < ts.count() ? ts[i1] : 1;
148cb93a386Sopenharmony_ci        SkDRect bounds;
149cb93a386Sopenharmony_ci        bounds.setBounds(cubic);
150cb93a386Sopenharmony_ci        SkDCubic part = cubic.subDivide(tStart, tEnd);
151cb93a386Sopenharmony_ci        SkDQuad quad = part.toQuad();
152cb93a386Sopenharmony_ci        if (quad[1].fX < bounds.fLeft) {
153cb93a386Sopenharmony_ci            quad[1].fX = bounds.fLeft;
154cb93a386Sopenharmony_ci        } else if (quad[1].fX > bounds.fRight) {
155cb93a386Sopenharmony_ci            quad[1].fX = bounds.fRight;
156cb93a386Sopenharmony_ci        }
157cb93a386Sopenharmony_ci        if (quad[1].fY < bounds.fTop) {
158cb93a386Sopenharmony_ci            quad[1].fY = bounds.fTop;
159cb93a386Sopenharmony_ci        } else if (quad[1].fY > bounds.fBottom) {
160cb93a386Sopenharmony_ci            quad[1].fY = bounds.fBottom;
161cb93a386Sopenharmony_ci        }
162cb93a386Sopenharmony_ci        quads.push_back(quad);
163cb93a386Sopenharmony_ci        tStart = tEnd;
164cb93a386Sopenharmony_ci    }
165cb93a386Sopenharmony_ci}
166cb93a386Sopenharmony_ci
167cb93a386Sopenharmony_civoid CubicPathToQuads(const SkPath& cubicPath, SkPath* quadPath) {
168cb93a386Sopenharmony_ci    quadPath->reset();
169cb93a386Sopenharmony_ci    SkDCubic cubic;
170cb93a386Sopenharmony_ci    SkTArray<SkDQuad, true> quads;
171cb93a386Sopenharmony_ci    for (auto [verb, pts, w] : SkPathPriv::Iterate(cubicPath)) {
172cb93a386Sopenharmony_ci        switch (verb) {
173cb93a386Sopenharmony_ci            case SkPathVerb::kMove:
174cb93a386Sopenharmony_ci                quadPath->moveTo(pts[0].fX, pts[0].fY);
175cb93a386Sopenharmony_ci                continue;
176cb93a386Sopenharmony_ci            case SkPathVerb::kLine:
177cb93a386Sopenharmony_ci                quadPath->lineTo(pts[1].fX, pts[1].fY);
178cb93a386Sopenharmony_ci                break;
179cb93a386Sopenharmony_ci            case SkPathVerb::kQuad:
180cb93a386Sopenharmony_ci                quadPath->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
181cb93a386Sopenharmony_ci                break;
182cb93a386Sopenharmony_ci            case SkPathVerb::kCubic:
183cb93a386Sopenharmony_ci                quads.reset();
184cb93a386Sopenharmony_ci                cubic.set(pts);
185cb93a386Sopenharmony_ci                CubicToQuads(cubic, cubic.calcPrecision(), quads);
186cb93a386Sopenharmony_ci                for (int index = 0; index < quads.count(); ++index) {
187cb93a386Sopenharmony_ci                    SkPoint qPts[2] = {
188cb93a386Sopenharmony_ci                        quads[index][1].asSkPoint(),
189cb93a386Sopenharmony_ci                        quads[index][2].asSkPoint()
190cb93a386Sopenharmony_ci                    };
191cb93a386Sopenharmony_ci                    quadPath->quadTo(qPts[0].fX, qPts[0].fY, qPts[1].fX, qPts[1].fY);
192cb93a386Sopenharmony_ci                }
193cb93a386Sopenharmony_ci                break;
194cb93a386Sopenharmony_ci            case SkPathVerb::kClose:
195cb93a386Sopenharmony_ci                 quadPath->close();
196cb93a386Sopenharmony_ci                break;
197cb93a386Sopenharmony_ci            default:
198cb93a386Sopenharmony_ci                SkDEBUGFAIL("bad verb");
199cb93a386Sopenharmony_ci                return;
200cb93a386Sopenharmony_ci        }
201cb93a386Sopenharmony_ci    }
202cb93a386Sopenharmony_ci}
203cb93a386Sopenharmony_ci
204cb93a386Sopenharmony_civoid CubicPathToSimple(const SkPath& cubicPath, SkPath* simplePath) {
205cb93a386Sopenharmony_ci    simplePath->reset();
206cb93a386Sopenharmony_ci    SkDCubic cubic;
207cb93a386Sopenharmony_ci    for (auto [verb, pts, w] : SkPathPriv::Iterate(cubicPath)) {
208cb93a386Sopenharmony_ci        switch (verb) {
209cb93a386Sopenharmony_ci            case SkPathVerb::kMove:
210cb93a386Sopenharmony_ci                simplePath->moveTo(pts[0].fX, pts[0].fY);
211cb93a386Sopenharmony_ci                continue;
212cb93a386Sopenharmony_ci            case SkPathVerb::kLine:
213cb93a386Sopenharmony_ci                simplePath->lineTo(pts[1].fX, pts[1].fY);
214cb93a386Sopenharmony_ci                break;
215cb93a386Sopenharmony_ci            case SkPathVerb::kQuad:
216cb93a386Sopenharmony_ci                simplePath->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
217cb93a386Sopenharmony_ci                break;
218cb93a386Sopenharmony_ci            case SkPathVerb::kCubic: {
219cb93a386Sopenharmony_ci                cubic.set(pts);
220cb93a386Sopenharmony_ci                double tInflects[2];
221cb93a386Sopenharmony_ci                int inflections = cubic.findInflections(tInflects);
222cb93a386Sopenharmony_ci                if (inflections > 1 && tInflects[0] > tInflects[1]) {
223cb93a386Sopenharmony_ci                    using std::swap;
224cb93a386Sopenharmony_ci                    swap(tInflects[0], tInflects[1]);
225cb93a386Sopenharmony_ci                }
226cb93a386Sopenharmony_ci                double lo = 0;
227cb93a386Sopenharmony_ci                for (int index = 0; index <= inflections; ++index) {
228cb93a386Sopenharmony_ci                    double hi = index < inflections ? tInflects[index] : 1;
229cb93a386Sopenharmony_ci                    SkDCubic part = cubic.subDivide(lo, hi);
230cb93a386Sopenharmony_ci                    SkPoint cPts[3];
231cb93a386Sopenharmony_ci                    cPts[0] = part[1].asSkPoint();
232cb93a386Sopenharmony_ci                    cPts[1] = part[2].asSkPoint();
233cb93a386Sopenharmony_ci                    cPts[2] = part[3].asSkPoint();
234cb93a386Sopenharmony_ci                    simplePath->cubicTo(cPts[0].fX, cPts[0].fY, cPts[1].fX, cPts[1].fY,
235cb93a386Sopenharmony_ci                            cPts[2].fX, cPts[2].fY);
236cb93a386Sopenharmony_ci                    lo = hi;
237cb93a386Sopenharmony_ci                }
238cb93a386Sopenharmony_ci                break;
239cb93a386Sopenharmony_ci            }
240cb93a386Sopenharmony_ci            case SkPathVerb::kClose:
241cb93a386Sopenharmony_ci                 simplePath->close();
242cb93a386Sopenharmony_ci                break;
243cb93a386Sopenharmony_ci            default:
244cb93a386Sopenharmony_ci                SkDEBUGFAIL("bad verb");
245cb93a386Sopenharmony_ci                return;
246cb93a386Sopenharmony_ci        }
247cb93a386Sopenharmony_ci    }
248cb93a386Sopenharmony_ci}
249cb93a386Sopenharmony_ci
250cb93a386Sopenharmony_cibool ValidBounds(const SkPathOpsBounds& bounds) {
251cb93a386Sopenharmony_ci    if (SkScalarIsNaN(bounds.fLeft)) {
252cb93a386Sopenharmony_ci        return false;
253cb93a386Sopenharmony_ci    }
254cb93a386Sopenharmony_ci    if (SkScalarIsNaN(bounds.fTop)) {
255cb93a386Sopenharmony_ci        return false;
256cb93a386Sopenharmony_ci    }
257cb93a386Sopenharmony_ci    if (SkScalarIsNaN(bounds.fRight)) {
258cb93a386Sopenharmony_ci        return false;
259cb93a386Sopenharmony_ci    }
260cb93a386Sopenharmony_ci    return !SkScalarIsNaN(bounds.fBottom);
261cb93a386Sopenharmony_ci}
262cb93a386Sopenharmony_ci
263cb93a386Sopenharmony_cibool ValidConic(const SkDConic& conic) {
264cb93a386Sopenharmony_ci    for (int index = 0; index < SkDConic::kPointCount; ++index) {
265cb93a386Sopenharmony_ci        if (!ValidPoint(conic[index])) {
266cb93a386Sopenharmony_ci            return false;
267cb93a386Sopenharmony_ci        }
268cb93a386Sopenharmony_ci    }
269cb93a386Sopenharmony_ci    if (SkDoubleIsNaN(conic.fWeight)) {
270cb93a386Sopenharmony_ci        return false;
271cb93a386Sopenharmony_ci    }
272cb93a386Sopenharmony_ci    return true;
273cb93a386Sopenharmony_ci}
274cb93a386Sopenharmony_ci
275cb93a386Sopenharmony_cibool ValidCubic(const SkDCubic& cubic) {
276cb93a386Sopenharmony_ci    for (int index = 0; index < 4; ++index) {
277cb93a386Sopenharmony_ci        if (!ValidPoint(cubic[index])) {
278cb93a386Sopenharmony_ci            return false;
279cb93a386Sopenharmony_ci        }
280cb93a386Sopenharmony_ci    }
281cb93a386Sopenharmony_ci    return true;
282cb93a386Sopenharmony_ci}
283cb93a386Sopenharmony_ci
284cb93a386Sopenharmony_cibool ValidLine(const SkDLine& line) {
285cb93a386Sopenharmony_ci    for (int index = 0; index < 2; ++index) {
286cb93a386Sopenharmony_ci        if (!ValidPoint(line[index])) {
287cb93a386Sopenharmony_ci            return false;
288cb93a386Sopenharmony_ci        }
289cb93a386Sopenharmony_ci    }
290cb93a386Sopenharmony_ci    return true;
291cb93a386Sopenharmony_ci}
292cb93a386Sopenharmony_ci
293cb93a386Sopenharmony_cibool ValidPoint(const SkDPoint& pt) {
294cb93a386Sopenharmony_ci    if (SkDoubleIsNaN(pt.fX)) {
295cb93a386Sopenharmony_ci        return false;
296cb93a386Sopenharmony_ci    }
297cb93a386Sopenharmony_ci    return !SkDoubleIsNaN(pt.fY);
298cb93a386Sopenharmony_ci}
299cb93a386Sopenharmony_ci
300cb93a386Sopenharmony_cibool ValidPoints(const SkPoint* pts, int count) {
301cb93a386Sopenharmony_ci    for (int index = 0; index < count; ++index) {
302cb93a386Sopenharmony_ci        if (SkScalarIsNaN(pts[index].fX)) {
303cb93a386Sopenharmony_ci            return false;
304cb93a386Sopenharmony_ci        }
305cb93a386Sopenharmony_ci        if (SkScalarIsNaN(pts[index].fY)) {
306cb93a386Sopenharmony_ci            return false;
307cb93a386Sopenharmony_ci        }
308cb93a386Sopenharmony_ci    }
309cb93a386Sopenharmony_ci    return true;
310cb93a386Sopenharmony_ci}
311cb93a386Sopenharmony_ci
312cb93a386Sopenharmony_cibool ValidQuad(const SkDQuad& quad) {
313cb93a386Sopenharmony_ci    for (int index = 0; index < 3; ++index) {
314cb93a386Sopenharmony_ci        if (!ValidPoint(quad[index])) {
315cb93a386Sopenharmony_ci            return false;
316cb93a386Sopenharmony_ci        }
317cb93a386Sopenharmony_ci    }
318cb93a386Sopenharmony_ci    return true;
319cb93a386Sopenharmony_ci}
320cb93a386Sopenharmony_ci
321cb93a386Sopenharmony_cibool ValidVector(const SkDVector& v) {
322cb93a386Sopenharmony_ci    if (SkDoubleIsNaN(v.fX)) {
323cb93a386Sopenharmony_ci        return false;
324cb93a386Sopenharmony_ci    }
325cb93a386Sopenharmony_ci    return !SkDoubleIsNaN(v.fY);
326cb93a386Sopenharmony_ci}
327