1 /*
2 *
3 * Copyright 2019, The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include <teeui/utils.h>
19
20 namespace teeui {
21
operator ==(const ByteBufferProxy & lhs,const ByteBufferProxy & rhs)22 bool operator==(const ByteBufferProxy& lhs, const ByteBufferProxy& rhs) {
23 if (lhs.size() == rhs.size()) {
24 auto lhsi = lhs.begin();
25 auto rhsi = rhs.begin();
26 while (lhsi != lhs.end()) {
27 if (*lhsi++ != *rhsi++) return false;
28 }
29 }
30 return true;
31 }
32
33 constexpr const DefaultNumericType kEpsilon = 0.000001;
34 constexpr const DefaultNumericType kHalfSqrt2 = 0.70710678118;
35
pixelLineIntersect(Point<pxs> line,pxs dist,Color c)36 Color pixelLineIntersect(Point<pxs> line, pxs dist, Color c) {
37 TEEUI_LOG << "Line: " << line << " Dist: " << dist;
38 bool more_than_half = dist < 0.0;
39 TEEUI_LOG << " " << more_than_half;
40
41 Color intensity = 0;
42 if (dist.abs() < kEpsilon) {
43 intensity = 0x80;
44 TEEUI_LOG << " half covered";
45 } else if (dist.abs() >= kHalfSqrt2) {
46 intensity = more_than_half ? 0xff : 0;
47 TEEUI_LOG << (more_than_half ? " fully covered" : " not covered");
48 } else {
49 auto dist_vec = line * dist;
50 TEEUI_LOG << " vec " << dist_vec;
51 dist_vec = Point<pxs>(dist_vec.x().abs(), dist_vec.y().abs());
52 TEEUI_LOG << " vec " << dist_vec;
53 if (dist_vec.x() < dist_vec.y()) {
54 dist_vec = Point<pxs>(dist_vec.y(), dist_vec.x());
55 }
56 auto a0 = dist_vec.x();
57 auto a1 = -dist_vec.y();
58 pxs area(.0);
59 if (a1 > -kEpsilon) {
60 TEEUI_LOG << " X";
61 area = a0;
62 } else {
63 Point<pxs> Q(a1 * (a1 + pxs(.5)) / a0 + a0, pxs(-.5));
64 if (Q.x() >= pxs(.5)) {
65 // line does not intersect our pixel.
66 intensity = more_than_half ? 0xff : 0;
67 TEEUI_LOG << (more_than_half ? " fully covered (2)" : " not covered(2)");
68 } else {
69 TEEUI_LOG << " partially covered";
70 Point<pxs> P(pxs(.5), a1 - a0 * (pxs(.5) - a0) / a1);
71 TEEUI_LOG << " P: " << P << " Q: " << Q;
72 Point<pxs> R = P - Q;
73 TEEUI_LOG << " R: " << R;
74 area = R.x() * R.y() * pxs(.5);
75 if (R.y() > 1.0) {
76 auto r = R.y() - pxs(1.0);
77 area -= r * R.x() * ((r) / R.y()) * pxs(.5);
78 }
79 }
80 }
81 if (more_than_half) {
82 area = pxs(1.0) - area;
83 }
84 TEEUI_LOG << " area: " << area;
85 intensity = area.count() * 0xff;
86 }
87 TEEUI_LOG << ENDL;
88 return intensity << 24 | (c & 0xffffff);
89 }
90
drawLinePoint(Point<pxs> a,Point<pxs> b,Point<pxs> px_origin,Color c,pxs width)91 Color drawLinePoint(Point<pxs> a, Point<pxs> b, Point<pxs> px_origin, Color c, pxs width) {
92 auto line = a - b;
93 auto len = line.length();
94 auto l = line / len;
95 auto seg = l * (px_origin - b);
96 auto dist = 0_px;
97 if (seg < 0_px) {
98 // line = px_origin - b;
99 // dist = line.length();
100 // line /= dist;
101 // dist -= width;
102 return 0;
103 } else if (seg > len) {
104 // line = px_origin - a;
105 // dist = line.length();
106 // line /= dist;
107 // dist -= width;
108 return 0;
109 } else {
110 line = Point<pxs>(-line.y(), line.x()) / len;
111 dist = (line * (px_origin - a)).abs() - width + .5_px;
112 }
113
114 return pixelLineIntersect(line, dist, c);
115 }
116
drawCirclePoint(Point<pxs> center,pxs r,Point<pxs> px_origin,Color c)117 Color drawCirclePoint(Point<pxs> center, pxs r, Point<pxs> px_origin, Color c) {
118 auto line = px_origin - center;
119 auto dist = line.length() - r;
120
121 return pixelLineIntersect(line.unit(), dist, c);
122 }
123
124 /*
125 * Computes the intersection of the lines given by ax + b and cy + d.
126 * The result may be empty if there is no solution.
127 */
intersect(const PxVec & a,const PxPoint & b,const PxVec & c,const PxPoint & d)128 optional<PxPoint> intersect(const PxVec& a, const PxPoint& b, const PxVec& c, const PxPoint& d) {
129 pxs y = 0.0;
130 PxVec g = b - d;
131 if (a.x().abs() < kEpsilon) {
132 if (c.x().abs() < kEpsilon || a.y() < kEpsilon) {
133 return {};
134 } else {
135 y = g.x() / c.x();
136 }
137 } else {
138 pxs f = a.y() / a.x();
139 pxs h = f * c.x() - c.y();
140 if (h.abs() < kEpsilon) {
141 return {};
142 } else {
143 y = (f * g.x() - g.y()) / h;
144 }
145 }
146 return c * y + d;
147 }
148
149 namespace bits {
150
rotate90(const VectorType & in)151 template <typename VectorType> inline VectorType rotate90(const VectorType& in) {
152 return {-in.y(), in.x()};
153 }
154
intersect(const PxPoint * oBegin,const PxPoint * oEnd,const PxPoint & lineA,const PxPoint & lineB,PxPoint * nBegin,PxPoint * nEnd)155 ssize_t intersect(const PxPoint* oBegin, const PxPoint* oEnd, const PxPoint& lineA,
156 const PxPoint& lineB, PxPoint* nBegin, PxPoint* nEnd) {
157
158 auto line = lineB - lineA;
159 if (oBegin == oEnd) return kIntersectEmpty;
160 auto b = oBegin;
161 auto a = b;
162 ++b;
163 auto nCur = nBegin;
164 unsigned int intersections_found = 0;
165 // inside indicates if we are inside the new convex object.
166 // If we happen to transition from inside to inside, we know that we where wrong and
167 // reset the output object. But if we were on the inside we have the full new object once
168 // we have traveled around the old object once.
169 bool inside = true;
170
171 auto processSegment = [&](const PxVec& a, const PxVec& b) -> bool {
172 auto segment = b - a;
173 if (auto p = intersect(line, lineA, segment, a)) {
174 auto seg_len = segment.length();
175 auto aDist = (segment * (*p - a)) / seg_len;
176 if (aDist >= 0.0 && aDist < segment.length()) {
177 ++intersections_found;
178 // The line vector points toward the negative half plain of segment.
179 // This means we are entering the resulting convex object.
180 bool enter = rotate90(segment) * line < 0;
181 if (enter && inside) {
182 // if we are entering the object, but we thought we are already inside, we
183 // forget all previous points, because we were wrong.
184 if (intersections_found < 2) {
185 // Only do after we found the first intersection. Other cases are likely
186 // duplications due to rounding errors.
187 nCur = nBegin;
188 }
189 }
190 TEEUI_LOG << *p << " inside: " << inside << " enter: " << enter << ENDL;
191 inside = enter;
192 // an intersection of the new line and a segment is always part of the resulting
193 // object.
194 if (nCur == nEnd) {
195 TEEUI_LOG << "error out of space 1" << ENDL;
196 return false;
197 }
198 if (aDist > 0.0 || enter) {
199 TEEUI_LOG << "add P: " << *p << ENDL;
200 *nCur++ = *p;
201 }
202 }
203 }
204 if (nCur == nEnd) {
205 TEEUI_LOG << "error out of space 2" << ENDL;
206 return false;
207 }
208 if (inside) {
209 TEEUI_LOG << "add B: " << b << ENDL;
210 *nCur++ = b;
211 }
212 return true;
213 };
214
215 while (b != oEnd) {
216 if (!processSegment(*a, *b)) return kIntersectEmpty;
217 a = b++;
218 }
219 if (!processSegment(*a, *oBegin)) return kIntersectEmpty;
220
221 TEEUI_LOG << "intersections found: " << intersections_found << ENDL;
222 // handle tangents and disjunct case
223 if (intersections_found < 2) {
224 // find a point that is not on the line
225 // if there is at most one intersection, all points of the object are on the same half
226 // plane or on the line.
227 a = oBegin;
228 pxs d;
229 do {
230 d = rotate90(line) * (*a - lineA);
231 if (++a == oEnd) {
232 TEEUI_LOG << "error no point with distance > 0" << ENDL;
233 return kIntersectEmpty;
234 }
235 } while (d == 0.0);
236
237 if (d > 0) {
238 // positive half plane
239 return kIntersectAllPositive;
240 } else {
241 // negative half plane
242 TEEUI_LOG << "egative half plane" << ENDL;
243 return kIntersectEmpty;
244 }
245 }
246
247 return nCur - nBegin;
248 }
249
area(const PxPoint * begin,const PxPoint * end)250 pxs area(const PxPoint* begin, const PxPoint* end) {
251 if (end - begin < 3) return 0.0;
252 auto o = *begin;
253 auto a = begin;
254 ++a;
255 auto b = a;
256 ++b;
257 pxs result = 0;
258 while (b != end) {
259 auto x = *a - o;
260 auto y = *b - o;
261 result += x.x() * y.y() - x.y() * y.x();
262 a = b;
263 ++b;
264 }
265 result /= 2;
266 return result;
267 }
268
269 } // namespace bits
270
271 } // namespace teeui
272