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