Geometry
The geometry classes provide the building blocks for positioning and layout inside a terminal buffer. They describe sizes, positions, rectangles, and directions, and let you derive new regions from existing ones.
Using explicit geometry types keeps layout code readable and makes it easier to build structured terminal interfaces.
Details about the example output on this page
The examples below were rendered with the dedicated documentation
helper doc/tools/geometry-reference.cpp at a fixed width of
72 terminal columns. This makes it easy to regenerate the visual
output together with the code snippets.
Usage
Deriving Layout Regions from a Canvas
The geometry types are designed to make screen layout explicit and easy to follow. Instead of calculating coordinates manually, you derive smaller regions from a larger canvas and keep each intermediate rectangle named.
const auto canvas = Rectangle{2, 3, 68, 8};
const auto header = canvas.subRectangle(Anchor::TopCenter, Size{0, 2}, Margins{0, 1, 0, 1});
const auto footer = canvas.subRectangle(Anchor::BottomCenter, Size{0, 1}, Margins{0, 1, 0, 1});
const auto body = canvas.insetBy(Margins{2, 1, 1, 1});
const auto sidebar = body.subRectangle(Anchor::Left, Size{18, 0}, Margins{0, 1, 0, 0});
const auto content = body.subRectangle(Anchor::Right, Size{body.width() - 19, 0}, Margins{0});
buffer.drawFrame(canvas, FrameStyle::Double, Color{fg::BrightWhite, bg::Inherited});
buffer.drawText("Header", header, Alignment::Center, Color{fg::BrightWhite, bg::Blue});
buffer.drawText("Sidebar", sidebar, Alignment::Center, Color{fg::BrightWhite, bg::Green});
buffer.drawText("Content", content, Alignment::Center, Color{fg::BrightWhite, bg::Magenta});
buffer.drawText("Footer", footer, Alignment::Center, Color{fg::BrightWhite, bg::BrightBlack});
Passing 0 as the width or height to
Rectangle::subRectangle()
means “use the full available size on that axis”. This is especially
useful for headers, footers, and sidebars that should stretch with the
parent rectangle.
subRectangle() + insetBy() keep layouts explicit One canvas can derive headers, sidebars, content, and footers cleanly. ╔╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤header╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╤╗ ║╰┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴╯║ ║╭────────────────╮ ╭─────────────────────────────────────────────╮║ ║│ │ │ │║ ║│ sidebar │ │ content │║ ║│ │ │ │║ ║╰────────────────╯ ╰─────────────────────────────────────────────╯║ ╚══════════════════════════════footer══════════════════════════════╝
Combining, Intersecting, and Testing Rectangles
Rectangle supports union and
intersection directly. This is useful when you need to compute redraw
regions, selection overlaps, or the visible area shared by two panels.
const auto a = Rectangle{4, 2, 13, 5};
const auto b = Rectangle{11, 4, 16, 5};
if (a.overlaps(b)) {
const auto dirtyRegion = a | b;
const auto sharedRegion = a & b;
buffer.drawFrame(dirtyRegion, FrameStyle::Heavy, Color{fg::BrightMagenta, bg::Inherited});
buffer.fill(sharedRegion, Char{" ", Color{fg::Inherited, bg::BrightBlack}});
buffer.drawFrame(sharedRegion, FrameStyle::Double, Color{fg::BrightGreen, bg::Inherited});
}
Use a | b when you need the combined area covered by both
rectangles. Use a & b when you need only their shared visible area.
The inexpensive
Rectangle::overlaps()
check is often the right guard before doing extra work.
A | B A & B ┏━━━━━━━━━━━┯━━━━━━━━━┓ ┌───────────┐ ┃ A │ ┃ │ A │ ┃ ┌────┼─────────┨ │ ╔════╗─────────┐ ┃ │ │ B ┃ │ ║ ║ B │ ┠──────┼────┘ ┃ └──────╚════╝ │ ┃ │ ┃ │ │ ┗━━━━━━┷━━━━━━━━━━━━━━┛ └──────────────┘ union intersection
Aligning Smaller Content and Cropping Larger Sources
There are two closely related alignment tools:
Rectangle::alignmentOffset()computes where a smaller block should start.Rectangle::alignedSource()returns anAlignedSourcewith both the effective target and the cropped source rectangle.
const auto badgeRect = Rectangle{
panel.alignmentOffset(Size{8, 3}, Alignment::BottomRight),
Size{8, 3}};
buffer.drawFrame(badgeRect, FrameStyle::Double, Color{fg::BrightCyan, bg::Inherited});
const auto aligned = panel.insetBy(Margins{1}).alignedSource(
Rectangle{0, 0, 18, 5},
Alignment::Center);
aligned.targetRect.forEach([&](const Position pos) {
const auto sourceX = aligned.sourceRect.x1() + (pos.x() - aligned.targetRect.x1());
const auto sourceY = aligned.sourceRect.y1() + (pos.y() - aligned.targetRect.y1());
// Sample from the centered source area here.
});
This saves you from writing separate “place when smaller” and “crop when larger” branches. The same alignment value handles both cases.
alignmentOffset() places smaller content; alignedSource() crops larger ╔══════╦───────────┐ ┌──────────────────┐ ┌──────────────────┐ ║ 8x3 ║ │ │ ╔══════╗ │ │ │ ╞══════╝ │ │ ║ 8x3 ║ │ │ ╔══════╡ │ │ │ ╚══════╝ │ │ ║ 8x3 ║ └──────────────────┘ └──────────────────┘ └───────────╩══════╝ TopLeft Center BottomRight ╔══════════════════════════════════════╗ ║ 234567890123456789 ║ ║ 456789012345678901 ║ ║ 678901234567890123 ║ ╚══════════════════════════════════════╝ center crop of 18x5 source
Splitting a Rectangle into Grid Cells
Rectangle::gridCells()
divides a larger canvas into evenly spaced sub-rectangles. This is
useful for dashboards, menu grids, option panels, and any row-major
layout where each cell should stay predictable.
const auto grid = Rectangle{2, 2, 68, 8};
const auto cells = grid.gridCells(2, 3, 2, 1);
for (std::size_t index = 0; index < cells.size(); ++index) {
const auto &cell = cells[index];
buffer.drawFrame(cell, FrameStyle::Light, Color{fg::BrightWhite, bg::Inherited});
buffer.drawText(
std::format("#{} {}x{}", index, cell.width(), cell.height()),
cell,
Alignment::Center);
}
Remainder pixels are distributed to the top-left cells first, and the resulting vector is returned in row-major order from left to right, then top to bottom.
If the requested number of rows, columns, and spacing no longer fits
into the rectangle, gridCells() throws std::invalid_argument.
gridCells() distributes remainder to the top-left cells and keeps ┌────────────────────┐ ┌───────────────────┐ ┌───────────────────┐ │ #0 22x4 │ │ #1 21x4 │ │ #2 21x4 │ │ │ │ │ │ │ └────────────────────┘ └───────────────────┘ └───────────────────┘ ┌────────────────────┐ ┌───────────────────┐ ┌───────────────────┐ │ #3 22x3 │ │ #4 21x3 │ │ #5 21x3 │ └────────────────────┘ └───────────────────┘ └───────────────────┘
Walking Neighbors and Frame Perimeters
Position and
Rectangle include traversal
helpers that are useful for custom layout logic, collision checks, and
procedural drawing.
const auto center = Position{12, 5};
for (const auto pos : center.cardinalFour()) {
buffer.set(pos, Char{U'+', Color{fg::BrightYellow, bg::Inherited}});
}
for (const auto pos : center.ringEight()) {
// ringEight() returns the eight surrounding positions clockwise.
}
const auto frame = Rectangle{46, 2, 18, 7};
frame.forEachInFrame([&](const Position pos, const int index) {
buffer.set(
pos,
Char{static_cast<char32_t>(U'0' + (index % 10)), Color{fg::BrightWhite, bg::Inherited}});
});
const auto currentIndex = frame.frameIndex(Position{63, 5});
The Direction wrapper fits into
the same workflow when directions come from configuration or input,
because it can convert to and from deltas and strings.
auto cursor = Position{10, 5};
cursor += Direction::fromString("east").toDelta();
Position::ringEight() Rectangle::forEachInFrame() 012345678901234567 5 8 5+7 4 9 +X+ 3 0 3+1 2 1 1 2 098765432109876543 0..7 clockwise around X index order around frame
Deriving Bounds and Keeping Cursors in Range
Two other helpers are worth using regularly:
Rectangle::bounds()creates the smallest rectangle that contains a set of positions.Rectangle::clamp()keeps a cursor or probe position inside a rectangle.
const auto points = PositionList{
Position{5, 5},
Position{9, 3},
Position{13, 6},
Position{17, 4},
Position{11, 7},
};
const auto highlight = Rectangle::bounds(points).expandedBy(Margins{1});
const auto viewport = Rectangle{46, 2, 16, 6};
const auto rawCursor = Position{66, 8};
const auto safeCursor = viewport.clamp(rawCursor);
if (viewport.contains(safeCursor)) {
buffer.set(safeCursor, Char{"@", Color{fg::BrightGreen, bg::Inherited}});
}
This pattern works well for drag selections, hit testing, and cursor movement that should stop cleanly at the viewport boundary instead of spilling outside the drawable area.
Rectangle::bounds() Rectangle::clamp() ╔═════════════╗ ┌───viewport───┐ ║ • ║ │ │ ║ •║ │ │ ║• ║ │ │ ║ • ║ │ │ ║ • ║ └──────────────@ ╚═════════════╝ raw X derive one enclosing rectangle clamped to nearest edge
Orientation Helpers and Coordinate Values
Orientation and
Coordinate are the small glue
types that keep geometry code readable when logic needs to switch
between horizontal and vertical behavior.
auto axis = Orientation::Horizontal;
auto size = Size{24, 7};
auto cursor = Position{4, 2};
const auto primaryExtent = size.coordinate(axis);
const auto primaryOffset = cursor.coordinate(axis);
const auto crossAxis = axis.crossed();
if (crossAxis == Orientation::Vertical) {
buffer.drawText(
std::format("axis={} offset={}", primaryExtent, primaryOffset),
Rectangle{2, 2, 24, 1},
Alignment::Left);
}
The coordinate() helpers on Size
and Position let one code path
serve both axes without branching on x/y or width/height names
all the way through the implementation.
Interface
Warning
doxygenenum: Cannot find enum “erbsland::cterm::Alignment” in doxygen xml output for project “erbsland-color-term” from directory: /home/runner/work/erbsland-cpp-color-term/erbsland-cpp-color-term/_build/breathe/doxygen/erbsland-color-term/xml
-
enum class erbsland::cterm::Anchor : uint8_t
Anchor describes a location inside a rectangular area using a combination of a vertical and a horizontal component.
Vertical components occupy the lower two bits (VMask).
Horizontal components occupy the next two bits (HMask). Common composite anchors (e.g. TopLeft, Center) are provided for convenience.
Values:
-
enumerator Top
Top aligned vertically.
-
enumerator VCenter
Vertically centered.
-
enumerator Bottom
Bottom aligned vertically.
-
enumerator Left
Left aligned horizontally.
-
enumerator HCenter
Horizontally centered.
-
enumerator Right
Right aligned horizontally.
-
enumerator TopLeft
-
enumerator TopCenter
-
enumerator TopRight
-
enumerator CenterLeft
-
enumerator Center
-
enumerator CenterRight
-
enumerator BottomLeft
-
enumerator BottomCenter
-
enumerator BottomRight
-
enumerator VMask
Mask for extracting the vertical component.
-
enumerator HMask
Mask for extracting the horizontal component.
-
using erbsland::cterm::Coordinate = int
-
class Direction
A direction in a 2D grid.
Public Types
-
enum Enum
The enum for the direction.
Values:
-
enumerator None
No direction.
-
enumerator North
North.
-
enumerator NorthEast
North-east.
-
enumerator East
East.
-
enumerator SouthEast
South-east.
-
enumerator South
South.
-
enumerator SouthWest
South-west.
-
enumerator West
West.
-
enumerator NorthWest
North-west.
-
enumerator _EnumCount
The number of directions enums.
-
enumerator None
Public Functions
-
constexpr Direction() noexcept = default
Create a direction with value
None.
-
~Direction() = default
Default destructor.
-
bool contains(Direction direction) const noexcept
Test if this direction contains (lexically) another direction.
Examples:
NW contains N
NW contains W
NW contains NW
NW does not contain S
NW does not contain SW (they just overlap).
-
inline constexpr std::size_t hash() const noexcept
Get a hash for this direction.
-
Position toDelta() const noexcept
Convert this direction into a position delta.
- Returns:
The unit delta for this direction, or
(0,0)forNone.
-
std::string_view toString() const noexcept
Convert this direction into a canonical lowercase string.
- Returns:
The normalized direction name.
Public Static Functions
Public Static Attributes
-
static constexpr auto cCount = static_cast<std::size_t>(_EnumCount)
The number of directions enums.
-
enum Enum
-
class Margins
Represents margins (top, right, bottom, left) around a rectangle.
Immutable-like value type for representing margins, padding, or insets.
In UI themes, margins are parent-owned outside space, while padding is part-owned inside space.
Provides convenience constructors for uniform or symmetric margins.
Public Types
Public Functions
-
Margins() = default
Default construct with uninitialized values (useful for aggregate-style construction).
-
inline explicit constexpr Margins(const Coordinate allSides)
Construct margins with the same value on all sides.
- Parameters:
allSides – Value applied to top, right, bottom and left.
-
inline constexpr Margins(const Coordinate horizontal, const Coordinate vertical)
Construct margins with separate horizontal and vertical values.
- Parameters:
horizontal – Value applied to left and right.
vertical – Value applied to top and bottom.
-
inline constexpr Margins(const Coordinate top, const Coordinate right, const Coordinate bottom, const Coordinate left)
Construct margins with individually specified sides.
- Parameters:
top – Top margin.
right – Right margin.
bottom – Bottom margin.
left – Left margin.
-
bool operator==(const Margins &other) const noexcept = default
Equality comparison (all sides must be equal).
-
inline Margins operator-() const noexcept
Unary negation producing margins with all sides negated.
Useful for reversing an inset/expansion operation.
-
inline constexpr Coordinate operator[](const Side side) const noexcept
Get the margin at the given side.
-
inline constexpr Coordinate top() const noexcept
Get top margin.
-
inline constexpr void setTop(const Coordinate value) noexcept
Set the top margin.
-
inline constexpr Coordinate right() const noexcept
Get the right margin.
-
inline constexpr void setRight(const Coordinate value) noexcept
Set the right margin.
-
inline constexpr Coordinate bottom() const noexcept
Get the bottom margin.
-
inline constexpr void setBottom(const Coordinate value) noexcept
Set the bottom margin.
-
inline constexpr Coordinate left() const noexcept
Get the left margin.
-
inline constexpr void setLeft(const Coordinate value) noexcept
Set the left margin.
-
inline constexpr Coordinate at(const Side side) const noexcept
Get the margin at the given side.
-
inline constexpr void set(const Side side, Coordinate value) noexcept
Set the margin at the given side.
-
inline constexpr Coordinate leading(const Orientation orientation) const noexcept
Get the leading margin for an orientation.
-
inline constexpr Coordinate trailing(const Orientation orientation) const noexcept
Get the trailing margin for an orientation.
-
inline constexpr Coordinate crossLeading(const Orientation orientation) const noexcept
Get the leading cross-axis margin for an orientation.
-
inline constexpr Coordinate crossTrailing(const Orientation orientation) const noexcept
Get the trailing cross-axis margin for an orientation.
-
inline constexpr Margins horizontal() const noexcept
Get a new object with only the horizontal margins preserved.
-
inline constexpr Margins vertical() const noexcept
Get a new object with only the vertical margins preserved.
-
inline constexpr Coordinate horizontalExtent() const noexcept
Get the positive horizontal extent consumed by the margins.
-
inline constexpr Coordinate verticalExtent() const noexcept
Get the positive vertical extent consumed by the margins.
-
inline constexpr Size extent() const noexcept
Get the positive extent consumed by the margins.
- Returns:
A size containing the horizontal and vertical extents.
-
inline constexpr Coordinate extent(const Orientation orientation) const noexcept
Get the extent for the selected orientation.
- Parameters:
orientation – The orientation that selects horizontal or vertical extent.
- Returns:
horizontalExtent()forHorizontal, otherwiseverticalExtent().
-
inline constexpr Coordinate horizontalSpacing() const noexcept
Get the horizontal spacing represented by the larger positive horizontal side.
-
inline constexpr Coordinate verticalSpacing() const noexcept
Get the vertical spacing represented by the larger positive vertical side.
-
inline constexpr Size spacing() const noexcept
Get the spacing represented by the margins.
- Returns:
A size containing horizontal and vertical spacing.
-
inline constexpr Coordinate spacing(const Orientation orientation) const noexcept
Get the spacing for the selected orientation.
- Parameters:
orientation – The orientation that selects horizontal or vertical spacing.
- Returns:
horizontalSpacing()forHorizontal, otherwiseverticalSpacing().
-
inline Coordinate horizontalDelta() const noexcept
Get the horizontal delta caused by positive or negative margins.
Saturation addition limits the value to the
Coordinaterange.
-
inline Coordinate verticalDelta() const noexcept
Get the vertical delta caused by positive or negative margins.
Saturation addition limits the value to the
Coordinaterange.
-
inline auto delta(const Orientation orientation) const noexcept
Get the delta for the given axis.
-
inline constexpr Margins &expandTo(const Margins other) noexcept
Expand all sides so they are at least the matching sides in another margin set.
- Parameters:
other – The minimum margins to cover.
- Returns:
This margin set.
-
inline constexpr Margins &expandTo(const Margins other, const Orientation orientation) noexcept
Expand the margins along one axis so they are at least the matching sides in another margin set.
- Parameters:
other – The minimum margins to cover.
orientation – The axis to modify.
- Returns:
This margin set.
-
inline constexpr Margins &expandTo(const Margins other, const Side side) noexcept
Expand one side so it is at least the matching side in another margin set.
- Parameters:
other – The minimum margins to cover.
side – The side to modify.
- Returns:
This margin set.
-
inline constexpr Margins &expandPositive()
Limit all sides to zero or positive values.
- Returns:
This margin set.
-
inline constexpr Margins &limitTo(const Margins other) noexcept
Limit all sides so they are at most the matching sides in another margin set.
- Parameters:
other – The maximum margins to respect.
- Returns:
This margin set.
-
inline constexpr Margins &limitTo(const Margins other, const Orientation orientation) noexcept
Limit the margins along one axis so they are at most the matching sides in another margin set.
- Parameters:
other – The maximum margins to respect.
orientation – The axis to modify.
- Returns:
This margin set.
-
inline constexpr Margins &limitTo(const Margins other, const Side side) noexcept
Limit one side so it is at most the matching side in another margin set.
- Parameters:
other – The maximum margins to respect.
side – The side to modify.
- Returns:
This margin set.
-
inline constexpr Margins expandedWith(const Margins other) const noexcept
Create a copy expanded so all sides are at least the matching sides in another margin set.
- Parameters:
other – The minimum margins to cover.
- Returns:
The expanded margins.
-
inline constexpr Margins expandedWith(const Margins other, const Orientation orientation) const noexcept
Create a copy expanded on one axis so both sides are at least the matching sides in another margin set.
- Parameters:
other – The minimum margins to cover.
orientation – The axis to modify.
- Returns:
The expanded margins.
-
inline constexpr Margins expandedWith(const Margins other, const Side side) const noexcept
Create a copy expanded on one side so it is at least the matching side in another margin set.
- Parameters:
other – The minimum margins to cover.
side – The side to modify.
- Returns:
The expanded margins.
-
inline constexpr Margins expandedPositive() const noexcept
Create a copy expanding with all sides to zero or positive values.
-
inline constexpr Margins limitedWith(const Margins other) const noexcept
Create a copy limited so all sides are at most the matching sides in another margin set.
- Parameters:
other – The maximum margins to respect.
- Returns:
The limited margins.
-
inline constexpr Margins limitedWith(const Margins other, const Orientation orientation) const noexcept
Create a copy limited on one axis so both sides are at most the matching sides in another margin set.
- Parameters:
other – The maximum margins to respect.
orientation – The axis to modify.
- Returns:
The limited margins.
Public Static Functions
-
static inline constexpr Side leadingSide(const Orientation orientation) noexcept
Get the leading side for an orientation.
-
static inline constexpr Side trailingSide(const Orientation orientation) noexcept
Get the trailing side for an orientation.
-
static inline constexpr Side crossLeadingSide(const Orientation orientation) noexcept
Get the leading cross-axis side for an orientation.
-
static inline constexpr Side crossTrailingSide(const Orientation orientation) noexcept
Get the trailing cross-axis side for an orientation.
-
class Orientation
Represents the orientation of a layout or a direction.
Public Types
Public Functions
-
constexpr Orientation() noexcept = default
Construct the default horizontal orientation.
-
inline constexpr Orientation(const Value value) noexcept
Construct an orientation from a value.
- Parameters:
value – The orientation value.
-
bool operator==(const Orientation &other) const noexcept = default
Equality comparison.
-
bool operator!=(const Orientation &other) const noexcept = default
Inequality comparison.
-
inline constexpr Value value() const noexcept
Get the stored value.
- Returns:
The wrapped orientation value.
-
inline constexpr Orientation crossed() const noexcept
Get the crossed orientation.
- Returns:
VerticalforHorizontal, otherwiseHorizontal.
-
constexpr Orientation() noexcept = default
-
class Position
Represents a 2D integer position or vector (x, y).
Lightweight value type with default construction to (0,0).
Useful both for coordinates in a grid and for 2D vector arithmetic.
Public Functions
-
Position() = default
Default construct to (0,0).
-
inline constexpr Position(Coordinate x, Coordinate y) noexcept
Construct from explicit coordinates.
- Parameters:
x – The x-coordinate.
y – The y-coordinate.
-
bool operator==(const Position &other) const noexcept = default
Equality comparison (component-wise).
-
bool operator!=(const Position &other) const noexcept = default
Inequality comparison (component-wise).
-
inline Position operator+(const Position &other) const noexcept
Vector addition (component-wise).
- Parameters:
other – The other position to add.
- Returns:
A Position with coordinates (_x + other._x, _y + other._y).
-
inline Position operator-(const Position &other) const noexcept
Vector subtraction (component-wise).
- Parameters:
other – The other position to subtract.
- Returns:
A Position with coordinates (_x - other._x, _y - other._y).
-
inline Position &operator+=(const Position &other) noexcept
Add another position to this one in-place.
- Parameters:
other – The other position to add.
- Returns:
Reference to this position.
-
inline Position &operator-=(const Position &other) noexcept
Subtract another position from this one in-place.
- Parameters:
other – The other position to subtract.
- Returns:
Reference to this position.
-
inline constexpr Coordinate x() const noexcept
Get the x coordinate.
-
void setX(Coordinate x) noexcept
Set the x coordinate.
- Parameters:
x – New x value.
-
inline constexpr Coordinate y() const noexcept
Get the y coordinate.
-
void setY(Coordinate y) noexcept
Set the y coordinate.
- Parameters:
y – New y value.
-
inline constexpr Coordinate coordinate(const Orientation orientation) const noexcept
Get the coordinate for the selected orientation.
- Parameters:
orientation – The orientation that selects the x or y coordinate.
- Returns:
x()forOrientation::Horizontal, otherwisey().
-
inline constexpr std::size_t hash() const noexcept
Get a hash for this position.
This hash is designed to be fast and uniform for both 32-bit and 64-bit platforms. It is not only optimized to be used in a map but also as a source for pseudo-randomness.
-
Coordinate distanceTo(Position other) const noexcept
Manhattan (L1) distance to another position.
- Parameters:
other – The other position.
- Returns:
|x - other.x| + |y - other.y|.
-
inline Position componentMax(Position other) const noexcept
Component-wise maximum with another position.
- Parameters:
other – The other position.
- Returns:
A Position containing the max of each component.
-
inline Position componentMin(Position other) const noexcept
Component-wise minimum with another position.
- Parameters:
other – The other position.
- Returns:
A Position containing the min of each component.
-
inline std::array<Position, 4> cardinalFour() const noexcept
Get the four cardinal positions, relative to this one.
Order: right, down, left, up
Public Static Functions
-
static const std::array<Position, 4> &cardinalFourDeltas() noexcept
Get the four cardinal position deltas.
Order: right, down, left, up
-
class Rectangle
Axis-aligned rectangle represented by a top-left position and size.
Provides geometry utilities such as containment tests, expansion and iteration.
- Tested:
RectangleTest
Public Functions
-
Rectangle() = default
Construct an empty rectangle at (0,0).
-
inline constexpr Rectangle(Coordinate x, Coordinate y, Coordinate width, Coordinate height) noexcept
Construct from explicit position and size values.
-
inline constexpr Rectangle(Position pos, Size size) noexcept
Construct from position and size objects.
- Parameters:
pos – Top-left corner position.
size – Rectangle size.
-
inline constexpr Rectangle(const Position topLeft, const Position bottomRight) noexcept
Construct from a top-left and bottom-right (exclusive) corner position.
If the bottom right is left or above the top-left corner, the rectangle will be empty.
- Parameters:
topLeft – The top-left corner inside the new rectangle.
bottomRight – The bottom-right corner outside the new rectangle.
-
Rectangle operator|(const Rectangle &other) const noexcept
Merge two rectangles into a larger one that holds both rectangles.
-
Rectangle &operator|=(const Rectangle &other) noexcept
Expand this rectangle to include another rectangle.
- Parameters:
other – The other rectangle to merge.
- Returns:
Reference to this rectangle.
-
Rectangle operator&(const Rectangle &other) const noexcept
Intersect two rectangles to get only the overlapping part.
If the two rectangles don’t overlap, return an empty rectangle.
-
Rectangle &operator&=(const Rectangle &other) noexcept
Change this rectangle to the intersection of both rectangles.
If the two rectangles don’t overlap, its size will be (0,0).
-
inline void setPos(Position pos) noexcept
Set the top-left corner position.
- Parameters:
pos – New position value.
-
inline void setSize(Size size) noexcept
Set the size of the rectangle.
- Parameters:
size – New size value.
-
inline constexpr Coordinate x1() const noexcept
Left x-coordinate.
-
inline constexpr Coordinate y1() const noexcept
Top y-coordinate.
-
inline constexpr Coordinate x2() const noexcept
Right x-coordinate (exclusive).
-
inline constexpr Coordinate y2() const noexcept
Bottom y-coordinate (exclusive).
-
inline constexpr Position topLeft() const noexcept
Top-left corner position.
Is equivalent to
pos().
-
inline constexpr Position topRight() const noexcept
Top-right corner position (x-coordinate exclusive).
-
inline constexpr Position bottomLeft() const noexcept
Bottom-left corner position (y-coordinate exclusive).
-
inline constexpr Position bottomRight() const noexcept
Bottom-right corner position (x-coordinate exclusive, y-coordinate exclusive).
-
inline constexpr Coordinate width() const noexcept
Rectangle width.
-
inline constexpr Coordinate height() const noexcept
Rectangle height.
-
Position anchor(Anchor anchor = Anchor::TopLeft) const noexcept
Position of a given anchor within this rectangle.
- Parameters:
anchor – Anchor to query.
- Returns:
The position inside this rectangle matching the requested anchor.
-
inline Position center() const noexcept
Center position.
This is equal to the position of the
Anchor::Centeranchor.
-
bool contains(Position testedPosition) const noexcept
Check if a position is inside the rectangle.
- Parameters:
testedPosition – Position to test.
- Returns:
trueif the position lies inside the rectangle bounds.
-
bool contains(Rectangle testedRectangle) const noexcept
Checks if another rectangle fits into this one.
Only
trueif every position of the tested rectangle is inside this one.- Parameters:
testedRectangle – The rectangle to test for containment.
- Returns:
trueif the tested rectangle is fully contained within this one.
-
bool overlaps(Rectangle testedRectangle) const noexcept
Check if another rectangle overlaps this one.
Overlapping is when both rectangles share at least one position.
- Parameters:
testedRectangle – The rectangle to test for overlap.
-
bool isFrame(Position testedPosition) const noexcept
Check if a position lies on the rectangle frame.
- Parameters:
testedPosition – Position to test.
- Returns:
trueif the position lies on the outer frame of the rectangle.
-
inline constexpr std::size_t hash() const noexcept
Get a hash for this rectangle.
-
Position clamp(Position position) const noexcept
Clamp a position to this rectangle.
- Parameters:
position – The position to clamp.
- Returns:
The clamped position where (x1 <= position.x <= x2) && (y1 <= position.y <= y2)
-
Rectangle expandedBy(Margins margins) const noexcept
Create a rectangle expanded by the provided margins.
- Parameters:
margins – Margins to apply; positive values expand outward.
- Returns:
The expanded rectangle.
-
Rectangle insetBy(Margins margins) const noexcept
Create a rectangle inset by the provided margins.
- Parameters:
margins – Margins to remove from each side.
- Returns:
The inset rectangle.
-
Rectangle subRectangle(Anchor anchor, Size size, Margins margins) const noexcept
Create a sub-rectangle inside this rectangle.
- Parameters:
anchor – The anchor of the rectangle.
size – The size. Zero means full width/height.
margins – The margins around the sub rectangle.
- Returns:
The aligned sub-rectangle.
-
Position alignmentOffset(Size contentSize, Alignment alignment) const noexcept
Compute the position for content aligned inside this rectangle.
If
contentSizeis larger than this rectangle on an axis, the returned position on that axis lies beforetopLeft()on that axis.- Parameters:
contentSize – The aligned content size.
alignment – The alignment for the content.
- Returns:
The aligned content position relative to the global coordinate space.
-
AlignedSource alignedSource(Rectangle sourceRect, Alignment alignment) const noexcept
Align a source rectangle inside this rectangle and crop the larger side according to the alignment.
If the source is smaller than this rectangle on an axis, the returned target rectangle is moved inside this rectangle. If the source is larger on an axis, the returned source rectangle is cropped on that axis.
- Parameters:
sourceRect – The source rectangle before alignment and cropping.
alignment – The alignment used for placement or cropping.
- Returns:
The effective target rectangle and source rectangle after alignment.
-
int64_t frameIndex(Position testedPosition) const noexcept
Get the clockwise border index for a frame position.
The top-left corner has index
0, then the index increases clockwise around the perimeter. Degenerate rectangles with width or height1still produce a continuous index sequence.- Parameters:
testedPosition – The position on the frame.
- Returns:
The clockwise border index, or
-1if the position is not on the frame.
-
Direction frameDirection(Position testedPosition) const noexcept
Get the frame direction for a given position in this rectangle.
- Returns:
The direction of the frame at the given position, or Direction::None if the position is not on the frame.
-
std::vector<Rectangle> gridCells(int rows, int columns, Coordinate horizontalSpacing = 0, Coordinate verticalSpacing = 0) const
Divide this rectangle into equally spaced grid cells.
Each cell must be at least 1x1 in size, if this isn’t possible,
std::invalid_argumentis thrown.- Parameters:
rows – The number of rows. Minimum 1.
columns – The number of columns. Minimum 1
horizontalSpacing – The spacing between cells horizontally.
verticalSpacing – The spacing between cells vertically.
- Throws:
std::invalid_argument – if rows or columns are less than 1 or the chosen division is impossible.
- Returns:
A vector of rectangles representing the grid cells from left to right, top to bottom.
-
struct AlignedSource
Effective source and target rectangles after alignment.
-
class Size
A non-negative 2D size (width × height).
Width and height are clamped to be >= 0.
Many operations assume a grid indexed from (0,0) to (width-1,height-1).
Public Functions
-
Size() = default
Construct a zero size (0 × 0).
-
inline constexpr Size(Coordinate width, Coordinate height) noexcept
Construct a size from explicit width and height.
Negative inputs are clamped to 0.
- Parameters:
width – The desired width (clamped to >= 0).
height – The desired height (clamped to >= 0).
-
inline constexpr Size(Position pos1, Position pos2) noexcept
Construct a size from the axis-aligned distance between two positions.
Note
The order of the positions does not matter; absolute differences are used.
- Parameters:
pos1 – First position.
pos2 – Second position.
-
bool operator==(const Size &other) const noexcept = default
Equality comparison on width and height.
-
bool operator!=(const Size &other) const noexcept = default
Inequality comparison on width and height.
-
inline Size operator+(const Size &other) const noexcept
Add two sizes using saturated arithmetic.
- Parameters:
other – The size to add.
- Returns:
The component-wise sum, capped at the maximum coordinate value.
-
inline Size operator-(const Size &other) const noexcept
Subtract two sizes using saturated arithmetic.
- Parameters:
other – The size to subtract.
- Returns:
The component-wise difference, clamped to non-negative values.
-
inline Size &operator+=(const Size &other) noexcept
Add a size using saturated arithmetic.
- Parameters:
other – The size to add.
- Returns:
This size.
-
inline Size &operator-=(const Size &other) noexcept
Subtract a size using saturated arithmetic.
- Parameters:
other – The size to subtract.
- Returns:
This size.
-
inline constexpr Coordinate width() const noexcept
Get the width (>= 0).
-
inline void setWidth(Coordinate width) noexcept
Set the width.
Negative values are clamped to 0.
- Parameters:
width – New width (clamped to >= 0).
-
inline constexpr Coordinate height() const noexcept
Get the height (>= 0).
-
inline void setHeight(Coordinate height) noexcept
Set the height.
Negative values are clamped to 0.
- Parameters:
height – New height (clamped to >= 0).
-
inline constexpr Coordinate coordinate(const Orientation orientation) const noexcept
Get the size component for the selected orientation.
- Parameters:
orientation – The orientation that selects width or height.
- Returns:
width()forOrientation::Horizontal, otherwiseheight().
-
Position anchor(Anchor anchor) const noexcept
Compute a position inside the rectangle defined by this size for a given anchor.
Bottom-right resolves to (width-1, height-1), top-left to (0,0), etc.
- Parameters:
anchor – The anchor describing the target corner/edge/center.
- Returns:
The position inside the [0,width-1]×[0,height-1] grid (or (0,0) for empty dimensions).
-
Position alignmentOffset(Size contentSize, Alignment alignment) const noexcept
Compute the offset for content aligned inside this size.
If
contentSizeis larger than this size on an axis, the returned offset on that axis is negative.- Parameters:
contentSize – The aligned content size.
alignment – The alignment for the content.
- Returns:
The zero-based offset for placing the content inside this size.
-
inline constexpr bool isZero() const noexcept
Test if this size is zero.
-
inline bool fitsInto(const Size other) const noexcept
Check if this size fits completely into another size (component-wise <=).
- Parameters:
other – The candidate container size.
- Returns:
true if width <= other.width and height <= other.height.
-
inline bool isInRange(const Size minimum, const Size maximum) const noexcept
Test if the width and height of this size is in the given range.
- Parameters:
minimum – The minimum size.
maximum – The maximum size.
- Returns:
true If this size is in the range
minimum-maximum.
-
inline constexpr bool contains(const Position &pos) const noexcept
Check if a position lies strictly inside the bounds [0,width) × [0,height).
- Parameters:
pos – The position to test.
- Returns:
true if 0 <= x < width and 0 <= y < height.
-
inline Size &add(const Size other) noexcept
Add another size using saturated arithmetic.
- Parameters:
other – The size to add.
- Returns:
This size.
-
inline Size &add(const Size other, const Orientation orientation) noexcept
Add another size only on the selected axis using saturated arithmetic.
- Parameters:
other – The size to add.
orientation – The axis to modify.
- Returns:
This size.
-
inline Size &subtract(const Size other) noexcept
Subtract another size using saturated arithmetic and clamp the result to zero.
- Parameters:
other – The size to subtract.
- Returns:
This size.
-
inline Size &subtract(const Size other, const Orientation orientation) noexcept
Subtract another size only on the selected axis using saturated arithmetic and clamp the result to zero.
- Parameters:
other – The size to subtract.
orientation – The axis to modify.
- Returns:
This size.
-
inline Size &expandTo(const Size other) noexcept
Expand this size so it is at least the given size.
- Parameters:
other – The minimum size to cover.
- Returns:
This size.
-
inline Size &expandTo(const Size other, const Orientation orientation) noexcept
Expand this size so it is at least the given size on the selected axis.
- Parameters:
other – The minimum size to cover.
orientation – The axis to modify.
- Returns:
This size.
-
inline Size &limitTo(const Size other) noexcept
Limit this size so it is at most the given size.
- Parameters:
other – The maximum size to respect.
- Returns:
This size.
-
inline Size &limitTo(const Size other, const Orientation orientation) noexcept
Limit this size so it is at most the given size on the selected axis.
- Parameters:
other – The maximum size to respect.
orientation – The axis to modify.
- Returns:
This size.
-
inline Size expandedWith(const Size other) const noexcept
Create a copy expanded so it is at least the given size.
- Parameters:
other – The minimum size to cover.
- Returns:
The expanded size.
-
inline Size expandedWith(const Size other, const Orientation orientation) const noexcept
Create a copy expanded so it is at least the given size on the selected axis.
- Parameters:
other – The minimum size to cover.
orientation – The axis to modify.
- Returns:
The expanded size.
-
inline Size limitedWith(const Size other) const noexcept
Create a copy limited so it is at most the given size.
- Parameters:
other – The maximum size to respect.
- Returns:
The limited size.
-
inline Size limitedWith(const Size other, const Orientation orientation) const noexcept
Create a copy limited so it is at most the given size on the selected axis.
- Parameters:
other – The maximum size to respect.
orientation – The axis to modify.
- Returns:
The limited size.
-
inline Size clampTo(const Size minimum, const Size maximum) const noexcept
Component-wise clamp with a minimum and maximum size.
Warning
If minimum.width > maximum.width or minimum.height > maximum.height, the behavior is undefined.
- Parameters:
minimum – The minimum size.
maximum – The maximum size.
- Returns:
a size (min.width <= width <= max.width, min.height <= height <= max.height)
-
inline Position clamp(Position position) const noexcept
Clamp a position inside this size.
The resulting position is at least (0, 0) and less than (width, height).
-
inline int area() const noexcept
Compute the area (width * height).
- Returns:
The area. Note: returns 0 if either dimension is 0.