gitstack

grindlemire/go-tui code browser

3.8 KB Go 152 lines 2026-01-31 · d1f6635 raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package layout

// Size represents dimensions without position.
type Size struct {
	Width, Height int
}

// Rect represents a rectangle with integer coordinates.
// X and Y are the top-left corner; Width and Height are dimensions.
type Rect struct {
	X, Y          int
	Width, Height int
}

// NewRect creates a new Rect with the given position and dimensions.
func NewRect(x, y, width, height int) Rect {
	return Rect{X: x, Y: y, Width: width, Height: height}
}

// Right returns the x-coordinate of the right edge (exclusive).
func (r Rect) Right() int {
	return r.X + r.Width
}

// Bottom returns the y-coordinate of the bottom edge (exclusive).
func (r Rect) Bottom() int {
	return r.Y + r.Height
}

// IsEmpty returns true if the rectangle has zero or negative area.
func (r Rect) IsEmpty() bool {
	return r.Width <= 0 || r.Height <= 0
}

// Area returns the area of the rectangle.
func (r Rect) Area() int {
	if r.Width <= 0 || r.Height <= 0 {
		return 0
	}
	return r.Width * r.Height
}

// Contains returns true if the point (x, y) is inside the rectangle.
// Points on the left and top edges are inside; points on the right and bottom edges are outside.
func (r Rect) Contains(x, y int) bool {
	return x >= r.X && x < r.Right() && y >= r.Y && y < r.Bottom()
}

// ContainsRect returns true if the other rectangle is fully contained within this rectangle.
func (r Rect) ContainsRect(other Rect) bool {
	if other.IsEmpty() {
		return true
	}
	if r.IsEmpty() {
		return false
	}
	return other.X >= r.X && other.Y >= r.Y &&
		other.Right() <= r.Right() && other.Bottom() <= r.Bottom()
}

// Inset returns a new Rect inset by the given Edges.
// Positive values shrink the rectangle; negative values expand it.
func (r Rect) Inset(edges Edges) Rect {
	return Rect{
		X:      r.X + edges.Left,
		Y:      r.Y + edges.Top,
		Width:  r.Width - edges.Left - edges.Right,
		Height: r.Height - edges.Top - edges.Bottom,
	}
}

// Outset returns a new Rect expanded outward by the given Edges.
// Positive values expand the rectangle; negative values shrink it.
func (r Rect) Outset(edges Edges) Rect {
	return Rect{
		X:      r.X - edges.Left,
		Y:      r.Y - edges.Top,
		Width:  r.Width + edges.Left + edges.Right,
		Height: r.Height + edges.Top + edges.Bottom,
	}
}

// Translate returns a new Rect moved by (dx, dy).
func (r Rect) Translate(dx, dy int) Rect {
	return Rect{X: r.X + dx, Y: r.Y + dy, Width: r.Width, Height: r.Height}
}

// Intersect returns the intersection of two rectangles.
// If the rectangles don't overlap, returns an empty Rect.
func (r Rect) Intersect(other Rect) Rect {
	x := max(r.X, other.X)
	y := max(r.Y, other.Y)
	right := min(r.Right(), other.Right())
	bottom := min(r.Bottom(), other.Bottom())

	width := right - x
	height := bottom - y

	if width <= 0 || height <= 0 {
		return Rect{}
	}

	return Rect{X: x, Y: y, Width: width, Height: height}
}

// Union returns the smallest rectangle that contains both rectangles.
// If either rectangle is empty, returns the other rectangle.
func (r Rect) Union(other Rect) Rect {
	if r.IsEmpty() {
		return other
	}
	if other.IsEmpty() {
		return r
	}

	x := min(r.X, other.X)
	y := min(r.Y, other.Y)
	right := max(r.Right(), other.Right())
	bottom := max(r.Bottom(), other.Bottom())

	return Rect{X: x, Y: y, Width: right - x, Height: bottom - y}
}

// Intersects returns true if the two rectangles overlap.
// Touching edges do not count as overlapping.
func (r Rect) Intersects(other Rect) bool {
	return !r.Intersect(other).IsEmpty()
}

// Clamp constrains a point to be within the rectangle bounds.
// Returns the clamped (x, y) coordinates.
func (r Rect) Clamp(x, y int) (int, int) {
	if r.IsEmpty() {
		return r.X, r.Y
	}

	// Clamp x to [r.X, r.Right()-1]
	if x < r.X {
		x = r.X
	} else if x >= r.Right() {
		x = r.Right() - 1
	}

	// Clamp y to [r.Y, r.Bottom()-1]
	if y < r.Y {
		y = r.Y
	} else if y >= r.Bottom() {
		y = r.Bottom() - 1
	}

	return x, y
}