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/pathops/SkPathOpsCubic.h"
8cb93a386Sopenharmony_ci
9cb93a386Sopenharmony_cistatic bool rotate(const SkDCubic& cubic, int zero, int index, SkDCubic& rotPath) {
10cb93a386Sopenharmony_ci    double dy = cubic[index].fY - cubic[zero].fY;
11cb93a386Sopenharmony_ci    double dx = cubic[index].fX - cubic[zero].fX;
12cb93a386Sopenharmony_ci    if (approximately_zero(dy)) {
13cb93a386Sopenharmony_ci        if (approximately_zero(dx)) {
14cb93a386Sopenharmony_ci            return false;
15cb93a386Sopenharmony_ci        }
16cb93a386Sopenharmony_ci        rotPath = cubic;
17cb93a386Sopenharmony_ci        if (dy) {
18cb93a386Sopenharmony_ci            rotPath[index].fY = cubic[zero].fY;
19cb93a386Sopenharmony_ci            int mask = other_two(index, zero);
20cb93a386Sopenharmony_ci            int side1 = index ^ mask;
21cb93a386Sopenharmony_ci            int side2 = zero ^ mask;
22cb93a386Sopenharmony_ci            if (approximately_equal(cubic[side1].fY, cubic[zero].fY)) {
23cb93a386Sopenharmony_ci                rotPath[side1].fY = cubic[zero].fY;
24cb93a386Sopenharmony_ci            }
25cb93a386Sopenharmony_ci            if (approximately_equal(cubic[side2].fY, cubic[zero].fY)) {
26cb93a386Sopenharmony_ci                rotPath[side2].fY = cubic[zero].fY;
27cb93a386Sopenharmony_ci            }
28cb93a386Sopenharmony_ci        }
29cb93a386Sopenharmony_ci        return true;
30cb93a386Sopenharmony_ci    }
31cb93a386Sopenharmony_ci    for (int i = 0; i < 4; ++i) {
32cb93a386Sopenharmony_ci        rotPath[i].fX = cubic[i].fX * dx + cubic[i].fY * dy;
33cb93a386Sopenharmony_ci        rotPath[i].fY = cubic[i].fY * dx - cubic[i].fX * dy;
34cb93a386Sopenharmony_ci    }
35cb93a386Sopenharmony_ci    return true;
36cb93a386Sopenharmony_ci}
37cb93a386Sopenharmony_ci
38cb93a386Sopenharmony_ci
39cb93a386Sopenharmony_ci// Returns 0 if negative, 1 if zero, 2 if positive
40cb93a386Sopenharmony_cistatic int side(double x) {
41cb93a386Sopenharmony_ci    return (x > 0) + (x >= 0);
42cb93a386Sopenharmony_ci}
43cb93a386Sopenharmony_ci
44cb93a386Sopenharmony_ci/* Given a cubic, find the convex hull described by the end and control points.
45cb93a386Sopenharmony_ci   The hull may have 3 or 4 points. Cubics that degenerate into a point or line
46cb93a386Sopenharmony_ci   are not considered.
47cb93a386Sopenharmony_ci
48cb93a386Sopenharmony_ci   The hull is computed by assuming that three points, if unique and non-linear,
49cb93a386Sopenharmony_ci   form a triangle. The fourth point may replace one of the first three, may be
50cb93a386Sopenharmony_ci   discarded if in the triangle or on an edge, or may be inserted between any of
51cb93a386Sopenharmony_ci   the three to form a convex quadralateral.
52cb93a386Sopenharmony_ci
53cb93a386Sopenharmony_ci   The indices returned in order describe the convex hull.
54cb93a386Sopenharmony_ci*/
55cb93a386Sopenharmony_ciint SkDCubic::convexHull(char order[4]) const {
56cb93a386Sopenharmony_ci    size_t index;
57cb93a386Sopenharmony_ci    // find top point
58cb93a386Sopenharmony_ci    size_t yMin = 0;
59cb93a386Sopenharmony_ci    for (index = 1; index < 4; ++index) {
60cb93a386Sopenharmony_ci        if (fPts[yMin].fY > fPts[index].fY || (fPts[yMin].fY == fPts[index].fY
61cb93a386Sopenharmony_ci                && fPts[yMin].fX > fPts[index].fX)) {
62cb93a386Sopenharmony_ci            yMin = index;
63cb93a386Sopenharmony_ci        }
64cb93a386Sopenharmony_ci    }
65cb93a386Sopenharmony_ci    order[0] = yMin;
66cb93a386Sopenharmony_ci    int midX = -1;
67cb93a386Sopenharmony_ci    int backupYMin = -1;
68cb93a386Sopenharmony_ci    for (int pass = 0; pass < 2; ++pass) {
69cb93a386Sopenharmony_ci        for (index = 0; index < 4; ++index) {
70cb93a386Sopenharmony_ci            if (index == yMin) {
71cb93a386Sopenharmony_ci                continue;
72cb93a386Sopenharmony_ci            }
73cb93a386Sopenharmony_ci            // rotate line from (yMin, index) to axis
74cb93a386Sopenharmony_ci            // see if remaining two points are both above or below
75cb93a386Sopenharmony_ci            // use this to find mid
76cb93a386Sopenharmony_ci            int mask = other_two(yMin, index);
77cb93a386Sopenharmony_ci            int side1 = yMin ^ mask;
78cb93a386Sopenharmony_ci            int side2 = index ^ mask;
79cb93a386Sopenharmony_ci            SkDCubic rotPath;
80cb93a386Sopenharmony_ci            if (!rotate(*this, yMin, index, rotPath)) { // ! if cbc[yMin]==cbc[idx]
81cb93a386Sopenharmony_ci                order[1] = side1;
82cb93a386Sopenharmony_ci                order[2] = side2;
83cb93a386Sopenharmony_ci                return 3;
84cb93a386Sopenharmony_ci            }
85cb93a386Sopenharmony_ci            int sides = side(rotPath[side1].fY - rotPath[yMin].fY);
86cb93a386Sopenharmony_ci            sides ^= side(rotPath[side2].fY - rotPath[yMin].fY);
87cb93a386Sopenharmony_ci            if (sides == 2) { // '2' means one remaining point <0, one >0
88cb93a386Sopenharmony_ci                if (midX >= 0) {
89cb93a386Sopenharmony_ci                    // one of the control points is equal to an end point
90cb93a386Sopenharmony_ci                    order[0] = 0;
91cb93a386Sopenharmony_ci                    order[1] = 3;
92cb93a386Sopenharmony_ci                    if (fPts[1] == fPts[0] || fPts[1] == fPts[3]) {
93cb93a386Sopenharmony_ci                        order[2] = 2;
94cb93a386Sopenharmony_ci                        return 3;
95cb93a386Sopenharmony_ci                    }
96cb93a386Sopenharmony_ci                    if (fPts[2] == fPts[0] || fPts[2] == fPts[3]) {
97cb93a386Sopenharmony_ci                        order[2] = 1;
98cb93a386Sopenharmony_ci                        return 3;
99cb93a386Sopenharmony_ci                    }
100cb93a386Sopenharmony_ci                    // one of the control points may be very nearly but not exactly equal --
101cb93a386Sopenharmony_ci                    double dist1_0 = fPts[1].distanceSquared(fPts[0]);
102cb93a386Sopenharmony_ci                    double dist1_3 = fPts[1].distanceSquared(fPts[3]);
103cb93a386Sopenharmony_ci                    double dist2_0 = fPts[2].distanceSquared(fPts[0]);
104cb93a386Sopenharmony_ci                    double dist2_3 = fPts[2].distanceSquared(fPts[3]);
105cb93a386Sopenharmony_ci                    double smallest1distSq = std::min(dist1_0, dist1_3);
106cb93a386Sopenharmony_ci                    double smallest2distSq = std::min(dist2_0, dist2_3);
107cb93a386Sopenharmony_ci                    if (approximately_zero(std::min(smallest1distSq, smallest2distSq))) {
108cb93a386Sopenharmony_ci                        order[2] = smallest1distSq < smallest2distSq ? 2 : 1;
109cb93a386Sopenharmony_ci                        return 3;
110cb93a386Sopenharmony_ci                    }
111cb93a386Sopenharmony_ci                }
112cb93a386Sopenharmony_ci                midX = index;
113cb93a386Sopenharmony_ci            } else if (sides == 0) { // '0' means both to one side or the other
114cb93a386Sopenharmony_ci                backupYMin = index;
115cb93a386Sopenharmony_ci            }
116cb93a386Sopenharmony_ci        }
117cb93a386Sopenharmony_ci        if (midX >= 0) {
118cb93a386Sopenharmony_ci            break;
119cb93a386Sopenharmony_ci        }
120cb93a386Sopenharmony_ci        if (backupYMin < 0) {
121cb93a386Sopenharmony_ci            break;
122cb93a386Sopenharmony_ci        }
123cb93a386Sopenharmony_ci        yMin = backupYMin;
124cb93a386Sopenharmony_ci        backupYMin = -1;
125cb93a386Sopenharmony_ci    }
126cb93a386Sopenharmony_ci    if (midX < 0) {
127cb93a386Sopenharmony_ci        midX = yMin ^ 3; // choose any other point
128cb93a386Sopenharmony_ci    }
129cb93a386Sopenharmony_ci    int mask = other_two(yMin, midX);
130cb93a386Sopenharmony_ci    int least = yMin ^ mask;
131cb93a386Sopenharmony_ci    int most = midX ^ mask;
132cb93a386Sopenharmony_ci    order[0] = yMin;
133cb93a386Sopenharmony_ci    order[1] = least;
134cb93a386Sopenharmony_ci
135cb93a386Sopenharmony_ci    // see if mid value is on same side of line (least, most) as yMin
136cb93a386Sopenharmony_ci    SkDCubic midPath;
137cb93a386Sopenharmony_ci    if (!rotate(*this, least, most, midPath)) { // ! if cbc[least]==cbc[most]
138cb93a386Sopenharmony_ci        order[2] = midX;
139cb93a386Sopenharmony_ci        return 3;
140cb93a386Sopenharmony_ci    }
141cb93a386Sopenharmony_ci    int midSides = side(midPath[yMin].fY - midPath[least].fY);
142cb93a386Sopenharmony_ci    midSides ^= side(midPath[midX].fY - midPath[least].fY);
143cb93a386Sopenharmony_ci    if (midSides != 2) {  // if mid point is not between
144cb93a386Sopenharmony_ci        order[2] = most;
145cb93a386Sopenharmony_ci        return 3; // result is a triangle
146cb93a386Sopenharmony_ci    }
147cb93a386Sopenharmony_ci    order[2] = midX;
148cb93a386Sopenharmony_ci    order[3] = most;
149cb93a386Sopenharmony_ci    return 4; // result is a quadralateral
150cb93a386Sopenharmony_ci}
151