/* * \brief Geometric primitives * \author Norman Feske * \date 2006-08-05 */ /* * Copyright (C) 2006-2024 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ #ifndef _INCLUDE__UTIL__GEOMETRY_H_ #define _INCLUDE__UTIL__GEOMETRY_H_ #include #include #include #include namespace Genode { template class Point; template class Area; template class Rect; } /** * \param CT coordinate type */ template struct Genode::Point { CT x {}, y {}; /** * Operator for adding points */ Point operator + (Point const &p) const { return Point(x + p.x, y + p.y); } /** * Operator for subtracting points */ Point operator - (Point const &p) const { return Point(x - p.x, y - p.y); } /** * Operator for testing non-equality of two points */ bool operator != (Point const &p) const { return p.x != x || p.y != y; } /** * Operator for testing equality of two points */ bool operator == (Point const &p) const { return p.x == x && p.y == y; } void print(Output &out) const { auto abs = [] (auto v) { return v >= 0 ? v : -v; }; Genode::print(out, x >= 0 ? "+" : "-", abs(x), y >= 0 ? "+" : "-", abs(y)); } /** * Construct point from XML node attributes * * The XML node is expected to feature the attributes 'xpos' and 'ypos'. */ static Point from_xml(Xml_node const &node) { return Point(node.attribute_value("xpos", CT{}), node.attribute_value("ypos", CT{})); } }; /** * \param DT distance type */ template struct Genode::Area { DT w {}, h {}; bool valid() const { return w > 0 && h > 0; } size_t count() const { return size_t(w)*size_t(h); } /** * Operator for testing non-equality of two areas */ bool operator != (Area const &a) const { return a.w != w || a.h != h; } /** * Operator for testing equality of two areas */ bool operator == (Area const &a) const { return a.w == w && a.h == h; } void print(Output &out) const { Genode::print(out, w, "x", h); } /** * Construct area from XML node attributes * * The XML node is expected to feature the attributes 'width' and * 'height'. */ static Area from_xml(Xml_node const &node) { return Area(node.attribute_value("width", DT{}), node.attribute_value("height", DT{})); } }; /** * Rectangle * * A valid rectangle consists of two points wheras point 2 has higher or equal * coordinates than point 1. All other cases are threated as invalid * rectangles. * * \param CT coordinate type * \param DT distance type */ template struct Genode::Rect { using Point = Genode::Point; using Area = Genode::Area
; Point at {}; Area area {}; /** * Construct rectangle from two given points * * The x and y coordinates of p1 must not be higher than those of p2. */ static constexpr Rect compound(Point const p1, Point const p2) { if (p1.x > p2.x || p1.y > p2.y) return { /* invalid */ }; return { .at = p1, .area = { .w = DT(p2.x - p1.x + 1), .h = DT(p2.y - p1.y + 1) } }; } /** * Construct compounding rectangle of two rectangles */ static constexpr Rect compound(Rect r1, Rect r2) { return compound(Point(min(r1.x1(), r2.x1()), min(r1.y1(), r2.y1())), Point(max(r1.x2(), r2.x2()), max(r1.y2(), r2.y2()))); } /** * Construct rectangle by intersecting two rectangles */ static constexpr Rect intersect(Rect const r1, Rect const r2) { return Rect::compound(Point(max(r1.x1(), r2.x1()), max(r1.y1(), r2.y1())), Point(min(r1.x2(), r2.x2()), min(r1.y2(), r2.y2()))); } CT x1() const { return at.x; } CT y1() const { return at.y; } CT x2() const { return at.x + area.w - 1; } CT y2() const { return at.y + area.h - 1; } DT w() const { return area.w; } DT h() const { return area.h; } Point p1() const { return at; } Point p2() const { return { x2(), y2() }; } /** * Return true if rectangle area is greater than zero */ bool valid() const { return area.valid(); } /** * Return true if area fits in rectangle */ bool fits(Area const area) const { return w() >= area.w && h() >= area.h; } /** * Return true if the specified point lies within the rectangle */ bool contains(Point const p) const { return p.x >= x1() && p.x <= x2() && p.y >= y1() && p.y <= y2(); } struct Cut_remainder { Rect top, left, right, bottom; void for_each(auto const &fn) const { fn(top); fn(left); fn(right); fn(bottom); } }; /** * Cut out rectangle from rectangle * * \param r rectangle to cut out * * In the worst case (if we cut a hole into the rectangle) we get * four valid resulting rectangles. */ Cut_remainder cut(Rect r) const { /* limit the cut-out area to the actual rectangle */ r = intersect(r, *this); return { .top = compound(Point(x1(), y1()), Point(x2(), r.y1() - 1)), .left = compound(Point(x1(), r.y1()), Point(r.x1() - 1, r.y2())), .right = compound(Point(r.x2() + 1, r.y1()), Point(x2(), r.y2())), .bottom = compound(Point(x1(), r.y2() + 1), Point(x2(), y2())) }; } /** * Return position of an area when centered within the rectangle */ Point center(Area const area) const { return Point { .x = (CT(w()) - CT(area.w))/2, .y = (CT(h()) - CT(area.h))/2 } + at; } Point clamp(Point const p) const { return Point { .x = max(x1(), min(x2(), p.x)), .y = max(y1(), min(y2(), p.y)) }; } /** * Operator for testing non-equality of two rectangled */ bool operator != (Rect const &r) const { return r.at != at || r.area != area; } /** * Operator for testing equality of two rectangles */ bool operator == (Rect const &r) const { return r.at == at && r.area == area; } /** * Print rectangle coordinates * * The output has the form 'width' x 'height' +/- 'p1.x' +/- 'p1.y'. * For example, a rectange of size 15x16 as position (-13, 14) is * printed as "15x16-13+14". */ void print(Output &out) const { Genode::print(out, area, at); } /** * Construct rectangle from XML node attributes * * The XML node is expected to feature the attributes 'xpos', 'ypos'. * 'width', and 'height'. If an attribute is absent, the corresponding * value is set to 0. */ static Rect from_xml(Xml_node const &node) { return Rect(Point::from_xml(node), Area::from_xml(node)); } }; #endif /* _INCLUDE__UTIL__GEOMETRY_H_ */