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:

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:

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      
                                                                        

Interface

enum class erbsland::cterm::Alignment : uint8_t

Alignment of text or graphics in a box.

Values:

enumerator Left

Aligned to the left edge of the box.

enumerator HCenter

Aligned to the horizontal center of the box.

enumerator Right

Aligned to the right edge of the box.

enumerator Top

Aligned to the top edge of the box.

enumerator VCenter

Aligned to the vertical center of the box.

enumerator Bottom

Aligned to the bottom edge of the box.

enumerator TopLeft

Aligned to the top-left corner of the box.

enumerator TopCenter

Aligned to the top-center of the box.

enumerator TopRight

Aligned to the top-right corner of the box.

enumerator CenterLeft

Aligned to the center-left of the box.

enumerator Center

Aligned to the center of the box.

enumerator CenterRight

Aligned to the center-right of the box.

enumerator BottomLeft

Aligned to the bottom-left corner of the box.

enumerator BottomCenter

Aligned to the bottom-center of the box.

enumerator BottomRight

Aligned to the bottom-right corner of the box.

enumerator HorizontalMask

Mask for extracting the horizontal component.

enumerator VerticalMask

Mask for extracting the vertical component.

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.

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.

Public Functions

constexpr Direction() noexcept = default

Create a direction with value None.

inline constexpr Direction(const Enum value) noexcept

Create a direction from an enum value.

~Direction() = default

Default destructor.

Direction(const Direction&) = default

Default copy constructor.

Direction &operator=(const Direction&) = default

Default copy assignment.

inline Direction &operator=(const Enum value) noexcept

Assign an enum value.

inline constexpr operator Enum() const noexcept

Convert to the enum value.

constexpr bool operator==(const Direction&) const noexcept = default

Compare two directions.

inline constexpr bool operator==(const Enum value) const noexcept

Compare with an enum value.

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) for None.

std::string_view toString() const noexcept

Convert this direction into a canonical lowercase string.

Returns:

The normalized direction name.

Public Static Functions

static Direction fromDelta(Position delta) noexcept

Convert a position delta into a direction.

Only tests the signs of the x and y value in the given position.

Parameters:

delta – The position delta to convert.

Returns:

The direction for the given delta, or None if the delta is zero.

static Direction fromString(std::string_view text)

Parse a direction from text.

Accepts empty text, abbreviations and normalized names.

Parameters:

text – The direction text.

Returns:

The parsed direction.

Public Static Attributes

static constexpr auto cCount = static_cast<std::size_t>(_EnumCount)

The number of directions enums.

Friends

inline friend constexpr bool operator==(const Enum value, const Direction &direction) noexcept

Compare an enum value with a direction.

class Margins

Represents margins (top, right, bottom, left) around a rectangle.

  • Immutable-like value type for representing padding or insets.

  • Provides convenience constructors for uniform or symmetric margins.

Public Functions

Margins() = default

Default construct with uninitialized values (useful for aggregate-style construction).

inline explicit constexpr Margins(const int allSides)

Construct margins with the same value on all sides.

Parameters:

allSides – Value applied to top, right, bottom and left.

inline constexpr Margins(const int horizontal, const int 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 int top, const int right, const int bottom, const int 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).

bool operator!=(const Margins &other) const noexcept = default

Inequality comparison.

inline Margins operator-() const noexcept

Unary negation producing margins with all sides negated.

Useful for reversing an inset/expansion operation.

inline constexpr int top() const noexcept

Get top margin.

inline constexpr int right() const noexcept

Get the right margin.

inline constexpr int bottom() const noexcept

Get the bottom margin.

inline constexpr int left() const noexcept

Get the left margin.

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).

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).

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).

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.

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 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|.

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.

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

template<typename Fn>
uint32_t cardinalFourBitmask(Fn fn) const noexcept

Create a bitmask testing the four cardinal positions.

Parameters:

fn – The function to test each cardinal delta position, relative to this one.

inline std::array<Position, 8U> ringEight() const noexcept

Get the eight positions that form a ring around this position.

Clockwise order: 0:E, 1:SE, 2:S, 3:SW, 4:W, 5:NW, 6:N, 7:NE

Returns:

An array with all the eight positions.

Public Static Functions

static const std::array<Position, 4> &cardinalFourDeltas() noexcept

Get the four cardinal position deltas.

Order: right, down, left, up

static const std::array<Position, 8U> &ringEightDeltas() noexcept

Get the eight deltas that form a ring around this position.

Clockwise order: 0:E, 1:SE, 2:S, 3:SW, 4:W, 5:NW, 6:N, 7:NE

static inline Position minimum() noexcept

Get the point with the minimum coordinates.

static inline Position maximum() noexcept

Get the point with the maximum coordinates.

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.

Parameters:
  • x – X-coordinate of the top-left corner.

  • y – Y-coordinate of the top-left corner.

  • widthRectangle width.

  • heightRectangle height.

inline constexpr Rectangle(Position pos, Size size) noexcept

Construct from position and size objects.

Parameters:
  • pos – Top-left corner position.

  • sizeRectangle 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.

bool operator==(const Rectangle &other) const noexcept = default

Compare two rectangles.

bool operator!=(const Rectangle &other) const noexcept = default

Compare two rectangles.

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 constexpr Position pos() const noexcept

Get the top-left corner position.

inline void setPos(Position pos) noexcept

Set the top-left corner position.

Parameters:

pos – New position value.

inline constexpr Size size() const noexcept

Get the size of the rectangle.

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::Center anchor.

bool contains(Position testedPosition) const noexcept

Check if a position is inside the rectangle.

Parameters:

testedPositionPosition to test.

Returns:

true if the position lies inside the rectangle bounds.

bool contains(Rectangle testedRectangle) const noexcept

Checks if another rectangle fits into this one.

Only true if every position of the tested rectangle is inside this one.

Parameters:

testedRectangle – The rectangle to test for containment.

Returns:

true if 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:

testedPositionPosition to test.

Returns:

true if 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:

marginsMargins 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:

marginsMargins 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 contentSize is larger than this rectangle on an axis, the returned position on that axis lies before topLeft() 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 height 1 still produce a continuous index sequence.

Parameters:

testedPosition – The position on the frame.

Returns:

The clockwise border index, or -1 if 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.

auto gridCells(int rows, int columns, Coordinate horizontalSpacing = 0, Coordinate verticalSpacing = 0) const -> std::vector<Rectangle>

Divide this rectangle into equally spaced grid cells.

Each cell must be at least 1x1 in size, if this isn’t possible, std::invalid_argument is 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.

template<typename Fn>
void forEach(Fn fn) const

Call a function for each position contained in the rectangle.

Template Parameters:

Fn – A callable with signature void(Position). The function definition must be void fn(Position pos).

template<typename Fn>
void forEachInFrame(Fn fn) const

Call a function for each position around the frame, clockwise with index.

Template Parameters:

Fn – A callable with signature void(Position, int). The function definition must be void fn(Position pos, int index).

Public Static Functions

static Rectangle bounds(const PositionList &positions) noexcept

Get the bounds from the given positions.

Parameters:

positions – The positions to get the bounds from.

Returns:

A rectangle that contains all positions.

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.

Parameters:

other – The size to add.

Returns:

The component-wise sum.

inline Size operator-(const Size &other) const noexcept

Subtract two sizes.

Parameters:

other – The size to subtract.

Returns:

The component-wise difference, clamped to non-negative values by the constructor.

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).

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 contentSize is 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 componentMax(const Size other) const noexcept

Component-wise maximum with another size.

Parameters:

other – Other size.

Returns:

A size whose width is max(this.width, other.width) and height is max(this.height, other.height).

inline Size componentMin(const Size other) const noexcept

Component-wise minimum with another size.

Parameters:

other – Other size.

Returns:

A size whose width is min(this.width, other.width) and height is min(this.height, other.height).

inline Size componentClamp(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.

inline constexpr std::size_t index(const Position &pos) const noexcept

Convert a 2D position to a row-major linear index.

Parameters:

pos – The position. Behavior is undefined if not contained by this size.

Returns:

y * width + x.

template<typename Fn>
void forEach(Fn fn) const

Visit all positions inside the size in row-major order.

Template Parameters:

Fn – A callable taking Position.

Parameters:

fn – The function to invoke for each Position (x from 0..width-1, y from 0..height-1).