blob: 7e8f69e3ff15ab15d614c98f9f7e78446a8e1158 [file] [log] [blame]
/*
*
* Copyright 2019, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TEEUI_LIBTEEUI_UTILS_H_
#define TEEUI_LIBTEEUI_UTILS_H_
#include <math.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <algorithm>
#include <initializer_list>
#include <optional>
#include <tuple>
#include <type_traits>
#include <teeui/error.h>
#include <teeui/log.h>
namespace teeui {
using std::optional;
template <typename T, size_t elements> class Array {
using array_type = T[elements];
public:
constexpr Array() : data_{} {}
constexpr Array(const T (&data)[elements]) { std::copy(data, data + elements, data_); }
constexpr Array(const std::initializer_list<uint8_t>& li) {
size_t i = 0;
for (auto& item : li) {
data_[i] = item;
++i;
if (i == elements) break;
}
for (; i < elements; ++i) {
data_[i] = {};
}
}
T* data() { return data_; }
const T* data() const { return data_; }
constexpr size_t size() const { return elements; }
T* begin() { return data_; }
T* end() { return data_ + elements; }
const T* begin() const { return data_; }
const T* end() const { return data_ + elements; }
static constexpr Array fill(const T& v) {
Array result;
for (size_t i = 0; i < elements; ++i) {
result.data_[i] = v;
}
return result;
}
private:
array_type data_;
};
template <typename T> auto bytesCast(const T& v) -> const uint8_t (&)[sizeof(T)] {
return *reinterpret_cast<const uint8_t(*)[sizeof(T)]>(&v);
}
template <typename T> auto bytesCast(T& v) -> uint8_t (&)[sizeof(T)] {
return *reinterpret_cast<uint8_t(*)[sizeof(T)]>(&v);
}
class ByteBufferProxy {
template <typename T> struct has_data {
template <typename U> static int f(const U*, const void*) { return 0; }
template <typename U> static int* f(const U* u, decltype(u->data())) { return nullptr; }
static constexpr bool value = std::is_pointer<decltype(f((T*)nullptr, ""))>::value;
};
public:
template <typename T>
ByteBufferProxy(const T& buffer, decltype(buffer.data()) = nullptr)
: data_(reinterpret_cast<const uint8_t*>(buffer.data())), size_(buffer.size()) {
static_assert(sizeof(decltype(*buffer.data())) == 1, "elements to large");
}
template <size_t size>
ByteBufferProxy(const char (&buffer)[size])
: data_(reinterpret_cast<const uint8_t*>(buffer)), size_(size - 1) {
static_assert(size > 0, "even an empty string must be 0-terminated");
}
template <size_t size>
ByteBufferProxy(const uint8_t (&buffer)[size]) : data_(buffer), size_(size) {}
ByteBufferProxy() : data_(nullptr), size_(0) {}
const uint8_t* data() const { return data_; }
size_t size() const { return size_; }
const uint8_t* begin() const { return data_; }
const uint8_t* end() const { return data_ + size_; }
private:
const uint8_t* data_;
size_t size_;
};
constexpr const uint8_t kAuthTokenKeySize = 32;
constexpr const uint8_t kHmacKeySize = kAuthTokenKeySize;
using AuthTokenKey = Array<uint8_t, kAuthTokenKeySize>;
using Hmac = AuthTokenKey;
/**
* Implementer are expected to provide an implementation with the following prototype:
* static optional<array<uint8_t, 32>> hmac256(const uint8_t key[32],
* std::initializer_list<ByteBufferProxy> buffers);
*/
template <typename Impl> class HMac {
public:
template <typename... Data>
static optional<Hmac> hmac256(const AuthTokenKey& key, const Data&... data) {
return Impl::hmac256(key, {data...});
}
};
bool operator==(const ByteBufferProxy& lhs, const ByteBufferProxy& rhs);
template <typename IntType, uint32_t byteOrder> struct choose_hton;
template <typename IntType> struct choose_hton<IntType, __ORDER_LITTLE_ENDIAN__> {
inline static IntType hton(const IntType& value) {
IntType result = {};
const unsigned char* inbytes = reinterpret_cast<const unsigned char*>(&value);
unsigned char* outbytes = reinterpret_cast<unsigned char*>(&result);
for (int i = sizeof(IntType) - 1; i >= 0; --i) {
*(outbytes++) = inbytes[i];
}
return result;
}
};
template <typename IntType> struct choose_hton<IntType, __ORDER_BIG_ENDIAN__> {
inline static IntType hton(const IntType& value) { return value; }
};
template <typename IntType> inline IntType hton(const IntType& value) {
return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
}
template <typename IntType> inline IntType ntoh(const IntType& value) {
// same operation as hton
return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
}
enum class Unit : uint8_t {
PX,
DP,
MM,
};
template <Unit unit> struct UnitT { constexpr static const Unit value = unit; };
using px = UnitT<Unit::PX>;
using dp = UnitT<Unit::DP>;
using mm = UnitT<Unit::MM>;
template <typename Unit> static constexpr const char* str = "N/A";
template <> static constexpr const char* str<px> = "px";
template <> static constexpr const char* str<dp> = "dp";
template <> static constexpr const char* str<mm> = "mm";
using DefaultNumericType = float;
namespace bits {
inline long double abs(long double v) {
return ::fabsl(v);
}
inline double abs(double v) {
return ::fabs(v);
}
inline long double ceil(long double v) {
return ::ceill(v);
}
inline double ceil(double v) {
return ::ceil(v);
}
inline long double floor(long double v) {
return ::floorl(v);
}
inline double floor(double v) {
return ::floor(v);
}
inline long double sqrt(long double v) {
return ::sqrtl(v);
}
inline double sqrt(double v) {
return ::sqrt(v);
}
inline float round(float v) {
return ::roundf(v);
}
inline long double round(long double v) {
return ::roundl(v);
}
inline double round(double v) {
return ::round(v);
}
} // namespace bits
template <typename Unit, typename Numeric = DefaultNumericType> class Coordinate;
template <typename Numeric> struct Add {
constexpr static Coordinate<px, Numeric> eval(const Coordinate<px, Numeric>& v1,
const Coordinate<px, Numeric>& v2) {
return v1 + v2;
}
};
template <typename Numeric> struct Sub {
constexpr static Coordinate<px, Numeric> eval(const Coordinate<px, Numeric>& v1,
const Coordinate<px, Numeric>& v2) {
return v1 - v2;
}
};
template <typename Numeric> struct Mul {
constexpr static Coordinate<px, Numeric> eval(const Coordinate<px, Numeric>& v1,
const Coordinate<px, Numeric>& v2) {
return v1 * v2;
}
};
template <typename Numeric> struct Div {
constexpr static Coordinate<px, Numeric> eval(const Coordinate<px, Numeric>& v1,
const Coordinate<px, Numeric>& v2) {
return v1 / v2;
}
};
template <typename Param, typename Numeric = DefaultNumericType> class context;
template <typename T1, typename T2, typename Numeric, template <typename> class Op> struct BinOp;
template <typename T1, typename T2, typename Numeric> using add = BinOp<T1, T2, Numeric, Add>;
template <typename T1, typename T2, typename Numeric> using sub = BinOp<T1, T2, Numeric, Sub>;
template <typename T1, typename T2, typename Numeric> using mul = BinOp<T1, T2, Numeric, Mul>;
template <typename T1, typename T2, typename Numeric> using div = BinOp<T1, T2, Numeric, Div>;
template <typename T1, typename T2, typename Numeric, template <typename> class Op> struct BinOp {
private:
T1 v1_;
T2 v2_;
public:
constexpr BinOp(const T1& v1, const T2& v2) : v1_(v1), v2_(v2) {}
BinOp(const BinOp&) = default;
BinOp(BinOp&&) = default;
template <typename Context> Coordinate<px, Numeric> eval(const Context& ctx) const {
Coordinate<px, Numeric> v1 = ctx = v1_;
Coordinate<px, Numeric> v2 = ctx = v2_;
return Op<Numeric>::eval(v1, v2);
}
template <typename T> constexpr add<BinOp, T, Numeric> operator+(const T& v) const {
return {*this, v};
}
template <typename T> constexpr sub<BinOp, T, Numeric> operator-(const T& v) const {
return {*this, v};
}
template <typename T> constexpr mul<BinOp, T, Numeric> operator*(const T& v) const {
return {*this, v};
}
template <typename T> constexpr div<BinOp, T, Numeric> operator/(const T& v) const {
return {*this, v};
}
};
template <typename Name, typename ParamType> struct MetaParam {};
template <typename Name, typename Unit, typename Numeric>
struct MetaParam<Name, Coordinate<Unit, Numeric>> {
template <typename T> constexpr add<MetaParam, T, Numeric> operator+(const T& v) const {
return {*this, v};
}
template <typename T> constexpr sub<MetaParam, T, Numeric> operator-(const T& v) const {
return {*this, v};
}
template <typename T> constexpr mul<MetaParam, T, Numeric> operator*(const T& v) const {
return {*this, v};
}
template <typename T> constexpr div<MetaParam, T, Numeric> operator/(const T& v) const {
return {*this, v};
}
};
template <typename Name, typename ParamType> struct Param {
ParamType param_;
Param() : param_{} {}
Param(const Param&) = default;
Param(Param&&) = default;
Param& operator=(const Param&) = default;
Param& operator=(Param&&) = default;
};
template <typename Unit, typename Numeric> class Coordinate {
Numeric value_;
public:
using unit_t = Unit;
constexpr Coordinate() : value_{} {}
constexpr Coordinate(Numeric value) : value_(value) {}
Coordinate(const Coordinate&) = default;
Coordinate(Coordinate&&) = default;
template <typename N> Coordinate(const Coordinate<Unit, N>& other) {
if constexpr (std::is_floating_point<N>::value && std::is_integral<Numeric>::value) {
value_ = bits::round(other.count());
} else {
value_ = other.count();
}
}
Coordinate& operator=(const Coordinate& rhs) = default;
Coordinate& operator=(Coordinate&& rhs) = default;
constexpr Coordinate operator-(const Coordinate& v) const { return value_ - v.value_; }
constexpr Coordinate operator+(const Coordinate& v) const { return value_ + v.value_; }
constexpr Coordinate& operator-=(const Coordinate& v) {
value_ -= v.value_;
return *this;
}
constexpr Coordinate& operator+=(const Coordinate& v) {
value_ += v.value_;
return *this;
}
constexpr Coordinate operator*(const Coordinate& v) const { return value_ * v.value_; }
constexpr Coordinate& operator*=(const Coordinate& v) {
value_ *= v.value_;
return *this;
}
constexpr Coordinate operator/(const Coordinate& v) const { return value_ / v.value_; }
constexpr Coordinate& operator/=(const Coordinate& v) {
value_ /= v.value_;
return *this;
}
constexpr Coordinate operator-() const { return -value_; }
Coordinate abs() const { return bits::abs(value_); }
Coordinate ceil() const { return bits::ceil(value_); }
Coordinate floor() const { return bits::floor(value_); }
Coordinate sqrt() const { return bits::sqrt(value_); }
constexpr bool operator==(const Coordinate& v) const { return value_ == v.value_; }
constexpr bool operator!=(const Coordinate& v) const { return !(*this == v); }
constexpr bool operator<(const Coordinate& v) const { return value_ < v.value_; }
constexpr bool operator>(const Coordinate& v) const { return v < *this; }
constexpr bool operator<=(const Coordinate& v) const { return !(v < *this); }
constexpr bool operator>=(const Coordinate& v) const { return !(*this < v); }
template <typename T> constexpr add<Coordinate, T, Numeric> operator+(const T& v) const {
return {*this, v};
}
template <typename T> constexpr sub<Coordinate, T, Numeric> operator-(const T& v) const {
return {*this, v};
}
template <typename T> constexpr mul<Coordinate, T, Numeric> operator*(const T& v) const {
return {*this, v};
}
template <typename T> constexpr div<Coordinate, T, Numeric> operator/(const T& v) const {
return {*this, v};
}
Numeric count() const { return value_; }
};
template <typename... T> struct MetaList {};
template <typename MetaParam> struct metaParam2Param;
template <typename ParamName, typename ParamType>
struct metaParam2Param<MetaParam<ParamName, ParamType>> {
using type = Param<ParamName, ParamType>;
};
template <typename MetaParam> struct metaParam2ParamType;
template <typename ParamName, typename ParamType>
struct metaParam2ParamType<MetaParam<ParamName, ParamType>> {
using type = ParamType;
};
template <typename T> struct isCoordinateType { constexpr static const bool value = false; };
template <typename Unit, typename Numeric> struct isCoordinateType<Coordinate<Unit, Numeric>> {
constexpr static const bool value = true;
};
template <typename MetaParam> struct isCoordinateParam;
template <typename ParamName, typename ParamType>
struct isCoordinateParam<MetaParam<ParamName, ParamType>> {
constexpr static const bool value = isCoordinateType<ParamType>::value;
};
template <typename T> struct isMetaParam { constexpr static const bool value = false; };
template <typename ParamName, typename ParamType>
struct isMetaParam<MetaParam<ParamName, ParamType>> {
constexpr static const bool value = true;
};
template <typename... ParamsNames, typename... ParamTypes, typename Numeric>
class context<MetaList<MetaParam<ParamsNames, ParamTypes>...>, Numeric> {
Numeric mm2px_;
Numeric dp2px_;
std::tuple<Param<ParamsNames, ParamTypes>...> params_;
class Proxy {
Numeric valuepx_;
Numeric mm2px_, dp2px_;
public:
Proxy(Numeric valuepx, Numeric mm2px, Numeric dp2px)
: valuepx_(valuepx), mm2px_(mm2px), dp2px_(dp2px) {}
Proxy(const Proxy&) = default;
Proxy(Proxy&&) = default;
operator Coordinate<px, Numeric>() const { return valuepx_; }
operator Coordinate<mm, Numeric>() const { return valuepx_ / mm2px_; }
operator Coordinate<dp, Numeric>() const { return valuepx_ / dp2px_; }
};
public:
context(Numeric mm2px, Numeric dp2px) : mm2px_(mm2px), dp2px_(dp2px) {}
context(const context&) = default;
context(context&&) = default;
context& operator=(const context&) = default;
context& operator=(context&&) = default;
template <typename MetaParam> auto& getParam() {
return std::get<typename metaParam2Param<MetaParam>::type>(params_);
}
template <typename MetaParam> const auto& getParam() const {
return std::get<typename metaParam2Param<MetaParam>::type>(params_);
}
template <typename MetaParam>
void setParam(
std::enable_if_t<isCoordinateParam<MetaParam>::value, const Coordinate<px, Numeric>>& v) {
getParam<MetaParam>().param_ = v;
}
template <typename MetaParam, typename Unit, typename N,
typename = std::enable_if_t<isCoordinateParam<MetaParam>::value>>
void setParam(const Coordinate<Unit, N>& v) {
getParam<MetaParam>().param_ = *this = v;
}
template <typename MetaParam>
void setParam(std::enable_if_t<!isCoordinateParam<MetaParam>::value,
const typename metaParam2ParamType<MetaParam>::type>& v) {
getParam<MetaParam>().param_ = v;
}
Proxy operator=(const Coordinate<px, Numeric>& rhs) const {
return {rhs.count(), mm2px_, dp2px_};
}
Proxy operator=(const Coordinate<mm, Numeric>& rhs) const {
return {rhs.count() * mm2px_, mm2px_, dp2px_};
}
Proxy operator=(const Coordinate<dp, Numeric>& rhs) const {
return {rhs.count() * dp2px_, mm2px_, dp2px_};
}
template <typename T1, typename T2, template <typename> class Op>
Proxy operator=(const BinOp<T1, T2, Numeric, Op>& rhs) const {
return {rhs.eval(*this).count(), mm2px_, dp2px_};
}
template <typename ParamName, typename ParamType>
std::enable_if_t<isCoordinateParam<MetaParam<ParamName, ParamType>>::value, Proxy>
operator=(const MetaParam<ParamName, ParamType>&) const {
return {getParam<MetaParam<ParamName, ParamType>>().param_.count(), mm2px_, dp2px_};
}
template <typename ParamName, typename ParamType>
std::enable_if_t<!isCoordinateParam<MetaParam<ParamName, ParamType>>::value, const ParamType&>
operator=(const MetaParam<ParamName, ParamType>&) const {
return getParam<MetaParam<ParamName, ParamType>>().param_;
}
template <typename T,
typename = std::enable_if_t<!(isMetaParam<T>::value || isCoordinateType<T>::value)>>
inline T&& operator=(T&& v) const {
return std::forward<T>(v);
}
};
using dps = Coordinate<dp>;
using mms = Coordinate<mm>;
using pxs = Coordinate<px>;
constexpr dps operator""_dp(long double dp) {
return dps(dp);
}
constexpr mms operator""_mm(long double mm) {
return mms(mm);
}
constexpr pxs operator""_px(long double px) {
return pxs(px);
}
constexpr dps operator""_dp(unsigned long long dp) {
return dps(dp);
}
constexpr mms operator""_mm(unsigned long long mm) {
return mms(mm);
}
constexpr pxs operator""_px(unsigned long long px) {
return pxs(px);
}
template <typename Coord> class Vec2d {
Coord x_, y_;
public:
constexpr Vec2d() : x_{}, y_{} {}
constexpr Vec2d(Coord x, Coord y) : x_(x), y_(y) {}
Vec2d(const Vec2d&) = default;
Vec2d(Vec2d&&) = default;
template <typename N>
Vec2d(const Vec2d<Coordinate<typename Coord::unit_t, N>>& other)
: x_(other.x()), y_(other.y()) {}
Vec2d& operator=(const Vec2d& rhs) = default;
Vec2d& operator=(Vec2d&& rhs) = default;
Vec2d operator-(const Vec2d& rhs) const { return Vec2d(*this) -= rhs; }
Vec2d operator+(const Vec2d& rhs) const { return Vec2d(*this) += rhs; }
Vec2d& operator-=(const Vec2d& rhs) {
x_ -= rhs.x_;
y_ -= rhs.y_;
return *this;
}
Vec2d& operator+=(const Vec2d& rhs) {
x_ += rhs.x_;
y_ += rhs.y_;
return *this;
}
Coord operator*(const Vec2d& rhs) const { return (x_ * rhs.x_ + y_ * rhs.y_).count(); }
Vec2d operator*(const Coord& f) const { return Vec2d(*this) *= f; }
Vec2d& operator*=(const Coord& f) {
x_ *= f;
y_ *= f;
return *this;
}
Vec2d operator/(const Coord& f) { return Vec2d(*this) /= f; }
Vec2d& operator/=(const Coord& f) {
x_ /= f;
y_ /= f;
return *this;
}
bool operator==(const Vec2d& rhs) const { return x_ == rhs.x() && y_ == rhs.y(); }
Coord length() const {
Coord factor = *this * *this;
return bits::sqrt(factor.count());
}
Vec2d unit() const { return Vec2d(*this) /= length(); }
Coord x() const { return x_; }
Coord y() const { return y_; }
};
#ifdef TEEUI_DO_LOG_DEBUG
template <typename Unit, typename Numeric>
std::ostream& operator<<(std::ostream& out, const Coordinate<Unit, Numeric>& p) {
out << std::setprecision(10) << p.count() << str<Unit>;
return out;
}
template <typename Coord> std::ostream& operator<<(std::ostream& out, const Vec2d<Coord>& p) {
out << "Vec2d(" << p.x() << ", " << p.y() << ")";
return out;
}
#endif
using Color = uint32_t;
template <typename Coord> using Point = Vec2d<Coord>;
Color drawLinePoint(Point<pxs> a, Point<pxs> b, Point<pxs> px_origin, Color c,
pxs width = pxs(1.0));
Color drawCirclePoint(Point<pxs> center, pxs r, Point<pxs> px_origin, Color c);
using PxPoint = Point<pxs>;
using PxVec = Vec2d<pxs>;
/*
* Computes the intersection of the lines given by ax + b and cy + d.
* The result may be empty if there is no solution.
*/
optional<PxPoint> intersect(const PxVec& a, const PxPoint& b, const PxVec& c, const PxPoint& d);
namespace bits {
static constexpr const ssize_t kIntersectEmpty = -1;
/**
* Returned by the intersect if the object is in the positive half plane of the given line.
*/
static constexpr const ssize_t kIntersectAllPositive = -2;
ssize_t intersect(const PxPoint* oBegin, const PxPoint* oEnd, const PxPoint& lineA,
const PxPoint& lineB, PxPoint* nBegin, PxPoint* nEnd);
pxs area(const PxPoint* begin, const PxPoint* end);
} // namespace bits
/**
* A ConvexObject is given by a list of 2D vertexes. Each vertex must lie on the positive half-plane
* of the line denoted by its two predecessors. A point is considered inside of the convex object
* if it is on the positive half-plane of all lines given by any two subsequent vertexes.
*
* ConvexObjects have fixed size given by the capacity template argument. The geometric object
* that they describe may have any number of vertexes between 3 and capacity.
*/
template <size_t capacity> class ConvexObject {
template <size_t other_cap> friend class ConvexObject;
protected:
PxPoint points_[capacity];
size_t fill_;
public:
ConvexObject() : fill_(0) {}
explicit constexpr ConvexObject(std::initializer_list<PxPoint> l) : fill_(0) {
if (l.size() > capacity) return;
for (const auto& p : l) {
points_[fill_++] = p;
}
}
ConvexObject(const ConvexObject& other) = default;
ConvexObject(ConvexObject&& other) = default;
ConvexObject& operator=(const ConvexObject& other) = default;
ConvexObject& operator=(ConvexObject&& other) = default;
constexpr size_t size() const { return fill_; }
constexpr const PxPoint* begin() const { return &points_[0]; }
constexpr const PxPoint* end() const { return &points_[fill_]; }
template <size_t result_cap>
optional<ConvexObject<result_cap>> intersect(const PxPoint& A, const PxPoint& B) const {
static_assert(result_cap >= capacity,
"resulting capacity must be at least as large as the original");
ConvexObject<result_cap> result;
ssize_t vCount =
bits::intersect(begin(), end(), A, B, &result.points_[0], &result.points_[result_cap]);
if (vCount == bits::kIntersectEmpty) return {};
// -2 is returned if the object is in the positive half plane of the line (may be tangent)
if (vCount == bits::kIntersectAllPositive) {
std::copy(begin(), end(), &result.points_[0]);
vCount = fill_;
}
result.fill_ = vCount;
return result;
}
template <size_t result_cap, size_t arg_cap>
optional<ConvexObject<result_cap>> intersect(const ConvexObject<arg_cap>& other) const {
return intersect<result_cap>(other.begin(), other.end());
}
template <size_t result_cap>
optional<ConvexObject<result_cap>> intersect(const PxPoint* begin, const PxPoint* end) const {
if (end - begin < 3) return {};
auto b = begin;
auto a = end - 1;
auto result = intersect<result_cap>(*a, *b);
a = b++;
while (result && b != end) {
result = result->template intersect<result_cap>(*a, *b);
a = b++;
}
return result;
}
pxs area() const { return bits::area(begin(), end()); }
void push_back(const PxPoint& p) {
if (fill_ < capacity) {
points_[fill_++] = p;
}
}
};
#ifdef TEEUI_DO_LOG_DEBUG
template <size_t capacity>
std::ostream& operator<<(std::ostream& out, const ConvexObject<capacity>& o) {
out << "ConvexObject(";
bool first = true;
for (const auto& p : o) {
if (first)
first = false;
else
out << ", ";
out << p;
}
out << ")";
return out;
}
#endif
template <typename Coord> class Box {
Point<Coord> topLeft_;
Vec2d<Coord> extend_;
public:
Box() {}
template <typename N>
Box(const Box<Coordinate<typename Coord::unit_t, N>>& other)
: topLeft_(other.topLeft()), extend_(other.extend()) {}
Box(const Coord& x, const Coord& y, const Coord& w, const Coord& h)
: topLeft_(x, y), extend_(w, h) {}
Box(const Point<Coord>& topLeft, const Vec2d<Coord>& extend)
: topLeft_(topLeft), extend_(extend) {}
bool contains(Point<Coord> p) const {
p -= topLeft_;
return p.y().count() >= 0 && p.y().count() <= extend_.y().count() && p.x().count() >= 0 &&
p.x().count() <= extend_.x().count();
}
bool contains(const Box& other) const {
auto br = bottomRight();
auto obr = other.bottomRight();
return topLeft_.x() <= other.topLeft_.x() && br.x() >= obr.x() &&
topLeft_.y() <= other.topLeft_.y() && br.y() >= obr.y();
}
bool overlaps(const Box& other) const {
auto br = bottomRight();
auto obr = other.bottomRight();
return topLeft_.x() < obr.x() && other.topLeft_.x() < br.x() && topLeft_.y() < obr.y() &&
other.topLeft_.y() < obr.y();
}
/**
* fitsInside only compares the extend of the boxes. It returns true if this box would fit
* inside the other box regardless of their absolute positions.
*/
bool fitsInside(const Box& other) const { return w() <= other.w() && h() <= other.w(); }
Point<Coord> bottomRight() const { return topLeft_ + extend_; }
Point<Coord> topLeft() const { return topLeft_; }
Vec2d<Coord> extend() const { return extend_; }
Coord x() const { return topLeft_.x(); }
Coord y() const { return topLeft_.y(); }
Coord w() const { return extend_.x(); }
Coord h() const { return extend_.y(); }
Box merge(const Box& other) const {
Coord x = std::min(topLeft_.x(), other.topLeft_.x());
Coord y = std::min(topLeft_.y(), other.topLeft_.y());
auto br = bottomRight();
auto obr = other.bottomRight();
Coord w = std::max(br.x(), obr.x()) - x;
Coord h = std::max(br.y(), obr.y()) - y;
return {x, y, w, h};
}
/**
* Returns a box that contains the this box and the given point.
*/
Box merge(const Point<Coord>& p) const {
auto br = bottomRight();
TEEUI_LOG << "A tl: " << topLeft_ << " br: " << br << " new: " << p << ENDL;
Coord x = std::min(topLeft_.x(), p.x());
Coord y = std::min(topLeft_.y(), p.y());
Coord w = std::max(br.x(), p.x()) - x;
Coord h = std::max(br.y(), p.y()) - y;
TEEUI_LOG << "B x: " << x << " y: " << y << " w: " << w << " h: " << h << ENDL;
return {x, y, w, h};
}
/**
* Returns a box that contains this box and all of the given points.
*/
Box merge(const Point<Coord>* begin, const Point<Coord>* end) const {
auto tl = topLeft();
auto br = bottomRight();
while (begin != end) {
TEEUI_LOG << "A tl: " << tl << " br: " << br << " new: " << *begin << ENDL;
tl = {std::min(tl.x(), begin->x()), std::min(tl.y(), begin->y())};
br = {std::max(br.x(), begin->x()), std::max(br.y(), begin->y())};
TEEUI_LOG << "B tl: " << tl << " br: " << br << " new: " << *begin << ENDL;
++begin;
}
return {tl, br - tl};
}
/**
* Creates a box that contains all of the given points.
*/
static Box boundingBox(const Point<Coord>* begin, const Point<Coord>* end) {
if (begin == end) return {};
Box result(*begin, {0, 0});
result.merge(begin + 1, end);
return result;
}
/*
* Translates the Box by the given offset. And returns a reference to itself
*/
Box& translateSelf(const Point<Coord>& offset) {
topLeft_ += offset;
return *this;
}
/*
* Returns copy of this box translated by offset.
*/
Box translate(const Point<Coord>& offset) const& {
Box result = *this;
result.topLeft_ += offset;
return result;
}
/*
* When called on a temporary just reuse this box.
*/
Box translate(const Point<Coord>& offset) && {
topLeft_ += offset;
return *this;
}
};
#ifdef TEEUI_DO_LOG_DEBUG
template <typename Coord> std::ostream& operator<<(std::ostream& out, const Box<Coord>& p) {
out << "Box(x: " << p.x().count() << " y: " << p.y().count() << " w: " << p.w().count()
<< " h: " << p.h().count() << ")";
return out;
}
#endif
struct PixelDrawer {
Error (*const drawPixel_)(uint32_t, uint32_t, Color, const void* priv_data);
const void* priv_data_;
Error operator()(uint32_t x, uint32_t y, Color color) const {
return drawPixel_(x, y, color, priv_data_);
}
};
template <typename Fn> struct PixelDrawerHelper {
Fn fn_;
operator PixelDrawer() const {
return {[](uint32_t x, uint32_t y, Color color, const void* priv_data) -> Error {
return reinterpret_cast<const PixelDrawerHelper*>(priv_data)->fn_(x, y, color);
},
this};
}
};
template <typename Fn> PixelDrawerHelper<Fn> makePixelDrawer(Fn fn) {
return PixelDrawerHelper<Fn>{fn};
}
template <typename Derived> struct LayoutElement {
Box<pxs> bounds_;
LayoutElement() = default;
template <typename Context>
LayoutElement(const Context& context)
: bounds_{context = Derived::pos_x, context = Derived::pos_y, context = Derived::dim_w,
context = Derived::dim_h} {}
Error draw(const PixelDrawer&) { return Error::OK; }
};
template <typename... Elements, typename Context>
std::tuple<Elements...> instantiateLayout(MetaList<Elements...>, const Context& context) {
std::tuple<Elements...> result{Elements(context)...};
return result;
}
template <typename T> struct MetaList2Layout;
template <typename... Elements> struct MetaList2Layout<MetaList<Elements...>> {
using type = std::tuple<Elements...>;
};
template <typename T> using layout_t = typename MetaList2Layout<T>::type;
template <typename... Coords>
constexpr inline std::tuple<Vec2d<Coords>...> makeConvexObject(const Vec2d<Coords>&... points) {
return {points...};
}
template <size_t capacity, typename Tuple, typename Context, size_t... I>
constexpr inline ConvexObject<capacity>
initConvexObject(const Context& context, const Tuple& outline, std::index_sequence<I...>) {
return ConvexObject<capacity>(
{PxVec(context = std::get<I>(outline).x(), context = std::get<I>(outline).y())...});
}
template <size_t capacity, typename... Points, typename Context>
constexpr inline ConvexObject<capacity> initConvexObject(const Context& context,
const std::tuple<Points...>& outline) {
return initConvexObject<capacity>(context, outline, std::index_sequence_for<Points...>{});
}
template <size_t capacity, typename Tuple, typename Context, size_t... I, size_t size>
constexpr inline void initConvexObjectArray(const Context& context,
ConvexObject<capacity> (&out)[size], const Tuple& t,
std::index_sequence<I...>) {
static_assert(sizeof...(I) <= size,
"Array to initialize must be big enough to hold all tuple elements");
[](auto...) {}((out[I] = initConvexObject<capacity>(context, std::get<I>(t)))...);
}
template <size_t capacity, typename... COs, typename Context, size_t size>
constexpr inline void initConvexObjectArray(const Context& context,
ConvexObject<capacity> (&out)[size],
const std::tuple<COs...>& t) {
initConvexObjectArray(context, out, t, std::index_sequence_for<COs...>());
}
template <typename Iterator> class Range {
Iterator begin_;
Iterator end_;
public:
Range(Iterator begin, Iterator end) : begin_(begin), end_(end) {}
const Iterator begin() const { return begin_; }
const Iterator end() const { return end_; }
};
template <typename Iterator> Range<Iterator> makeRange(Iterator begin, Iterator end) {
return {begin, end};
}
} // namespace teeui
#define Position(x, y) \
static const constexpr auto pos_x = x; \
static const constexpr auto pos_y = y
#define Dimension(w, h) \
static const constexpr auto dim_w = w; \
static const constexpr auto dim_h = h
#define BEGIN_ELEMENT(name, type, ...) \
struct name : public type<name, ##__VA_ARGS__> { \
name() = default; \
template <typename Context> \
name(const Context& context) : type<name, ##__VA_ARGS__>(context) {}
#define END_ELEMENT() }
#define DECLARE_TYPED_PARAMETER(name, type) \
struct Param_##name {}; \
using name = ::teeui::MetaParam<Param_##name, type>
#define DECLARE_PARAMETER(name) DECLARE_TYPED_PARAMETER(name, ::teeui::pxs)
#define CONSTANT(name, value) static constexpr const auto name = value
#define BOTTOM_EDGE_OF(name) (name::pos_y + name::dim_h)
#define CONVEX_OBJECT(...) makeConvexObject(__VA_ARGS__)
#define CONVEX_OBJECTS(...) std::make_tuple(__VA_ARGS__)
/**
* Creates a new Layout with the name "name" followed by a list of Layout elements as defined with
* BEGIN_ELEMENT(name).
*/
#define NEW_LAYOUT(name, ...) using name = ::teeui::MetaList<__VA_ARGS__>
#define NEW_PARAMETER_SET(name, ...) using name = ::teeui::MetaList<__VA_ARGS__>
#define LABELS(name, ...) using ::teeui::MetaList<__VA_ARGS__>
#endif // TEEUI_LIBTEEUI_UTILS_H_