[orx-shapes] Add RectangleGrid abstraction and update related utility functions
This commit is contained in:
@@ -6,6 +6,18 @@ import kotlin.jvm.JvmName
|
||||
import kotlin.math.round
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* Represents a rectangular grid structure composed of lists of [Rectangle] instances.
|
||||
*
|
||||
* This class provides a convenient abstraction for working with 2D grids,
|
||||
* where each cell is represented by a [Rectangle]. The class delegates
|
||||
* to the underlying grid structure, which is implemented as a list of lists,
|
||||
* allowing it to be used interchangeably with other list-based data structures.
|
||||
*
|
||||
* @property grid The 2D list structure containing [Rectangle] elements.
|
||||
*/
|
||||
class RectangleGrid(val grid: List<List<Rectangle>>) : List<List<Rectangle>> by grid
|
||||
|
||||
/**
|
||||
* Divides a rectangle into a grid of sub-rectangles with irregular spacing,
|
||||
* based on the specified column and row weights. Optionally, margins can be
|
||||
@@ -30,7 +42,7 @@ fun Rectangle.irregularGrid(
|
||||
rowWeights: List<Double>,
|
||||
marginX: Double = 0.0,
|
||||
marginY: Double = 0.0,
|
||||
): List<List<Rectangle>> {
|
||||
): RectangleGrid {
|
||||
|
||||
val columnWeight = columnWeights.sum()
|
||||
val rowWeight = rowWeights.sum()
|
||||
@@ -56,7 +68,7 @@ fun Rectangle.irregularGrid(
|
||||
}
|
||||
result.add(row)
|
||||
}
|
||||
return result
|
||||
return RectangleGrid(result)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +112,7 @@ fun Rectangle.grid(
|
||||
minMarginY: Double = 0.0,
|
||||
gutterX: Double = 0.0,
|
||||
gutterY: Double = 0.0
|
||||
): List<List<Rectangle>> {
|
||||
): RectangleGrid {
|
||||
|
||||
val availableWidth = (width - minMarginX * 2).coerceAtLeast(0.0)
|
||||
val availableHeight = (height - minMarginY * 2).coerceAtLeast(0.0)
|
||||
@@ -112,7 +124,7 @@ fun Rectangle.grid(
|
||||
val rows = round((availableHeight + gutterY) / cellSpaceY).toInt()
|
||||
|
||||
if (columns == 0 || rows == 0) {
|
||||
return emptyList()
|
||||
return RectangleGrid(emptyList())
|
||||
}
|
||||
|
||||
val totalGutterWidth = gutterX * (columns - 1).coerceAtLeast(0)
|
||||
@@ -132,7 +144,7 @@ fun Rectangle.grid(
|
||||
cellWidth, cellHeight
|
||||
)
|
||||
}
|
||||
}
|
||||
}.let { RectangleGrid(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,14 +155,14 @@ fun Rectangle.grid(
|
||||
*
|
||||
* @return A new 2D list of rectangles where rows and columns are swapped.
|
||||
*/
|
||||
fun List<List<Rectangle>>.transpose(): List<List<Rectangle>> {
|
||||
fun RectangleGrid.transpose(): RectangleGrid {
|
||||
val columns = MutableList<MutableList<Rectangle>>(this[0].size) { mutableListOf() }
|
||||
for (row in this) {
|
||||
for ((index, column) in row.withIndex()) {
|
||||
columns[index].add(column)
|
||||
}
|
||||
}
|
||||
return columns
|
||||
return RectangleGrid(columns)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,7 +173,7 @@ fun List<List<Rectangle>>.transpose(): List<List<Rectangle>> {
|
||||
* @param y The row index in the two-dimensional list.
|
||||
* @return The [Rectangle] at the specified indices (x, y).
|
||||
*/
|
||||
operator fun List<List<Rectangle>>.get(x: Int, y: Int): Rectangle = this[y][x]
|
||||
operator fun RectangleGrid.get(x: Int, y: Int): Rectangle = this[y][x]
|
||||
|
||||
|
||||
/**
|
||||
@@ -171,7 +183,7 @@ operator fun List<List<Rectangle>>.get(x: Int, y: Int): Rectangle = this[y][x]
|
||||
* @param y The index of the row from which the sublist is retrieved.
|
||||
* @return A sublist of [Rectangle] objects within the specified range of columns from the specified row.
|
||||
*/
|
||||
operator fun List<List<Rectangle>>.get(xRange: IntRange, y: Int): List<Rectangle> = this[y].slice(xRange)
|
||||
operator fun RectangleGrid.get(xRange: IntRange, y: Int): List<Rectangle> = this[y].slice(xRange)
|
||||
|
||||
/**
|
||||
* Retrieves a list of rectangles at a specific x-coordinate for a range of y-coordinates
|
||||
@@ -181,7 +193,7 @@ operator fun List<List<Rectangle>>.get(xRange: IntRange, y: Int): List<Rectangle
|
||||
* @param yRange The range of y-coordinates (indices of the outer list) to retrieve rectangles from.
|
||||
* @return A list of rectangles corresponding to the specified x-coordinate and y-coordinate range.
|
||||
*/
|
||||
operator fun List<List<Rectangle>>.get(x: Int, yRange: IntRange): List<Rectangle> = yRange.map { y -> this[y][x] }
|
||||
operator fun RectangleGrid.get(x: Int, yRange: IntRange): List<Rectangle> = yRange.map { y -> this[y][x] }
|
||||
|
||||
/**
|
||||
* Retrieves a subgrid from a 2D list of [Rectangle]s based on the specified ranges.
|
||||
@@ -190,8 +202,8 @@ operator fun List<List<Rectangle>>.get(x: Int, yRange: IntRange): List<Rectangle
|
||||
* @param yRange The range of y indices to include in the subgrid.
|
||||
* @return A 2D list containing the elements of the subgrid specified by the ranges.
|
||||
*/
|
||||
operator fun List<List<Rectangle>>.get(xRange: IntRange, yRange: IntRange): List<List<Rectangle>> =
|
||||
yRange.map { y -> xRange.map { x -> this[y][x] } }
|
||||
operator fun RectangleGrid.get(xRange: IntRange, yRange: IntRange): RectangleGrid =
|
||||
RectangleGrid(yRange.map { y -> xRange.map { x -> this[y][x] } })
|
||||
|
||||
|
||||
/**
|
||||
@@ -230,13 +242,13 @@ fun List<List<Rectangle>>.uniform(random: Random): Rectangle {
|
||||
* @param random An instance of Random used to generate random coordinates and dimensions for the block.
|
||||
* @return A 2D list of rectangles representing the randomly extracted uniform block.
|
||||
*/
|
||||
fun List<List<Rectangle>>.uniformBlock(
|
||||
fun RectangleGrid.uniformBlock(
|
||||
minWidth: Int = 1,
|
||||
maxWidth: Int = this[0].size,
|
||||
minHeight: Int = 1,
|
||||
maxHeight: Int = this.size,
|
||||
random: Random = Random.Default
|
||||
): List<List<Rectangle>> {
|
||||
): RectangleGrid {
|
||||
require(minWidth > 0) { "Minimum width must be greater than zero." }
|
||||
require(minHeight > 0) { "Minimum height must be greater than zero." }
|
||||
require(minWidth <= maxWidth) { "Minimum width must be less than or equal to maximum width." }
|
||||
@@ -277,14 +289,14 @@ fun List<List<Rectangle>>.row(index: Int): List<Rectangle> = this[index]
|
||||
* @param height The height of the block, specifying the number of rows to include.
|
||||
* @return A 2D list of rectangles representing the extracted block.
|
||||
*/
|
||||
fun List<List<Rectangle>>.block(x: Int, y: Int, width: Int, height: Int): List<List<Rectangle>> {
|
||||
fun RectangleGrid.block(x: Int, y: Int, width: Int, height: Int): RectangleGrid {
|
||||
require(x + width <= this[0].size) { "Width of block exceeds bounds of the original 2D list." }
|
||||
require(y + height <= this.size) { "Height of block exceeds bounds of the original 2D list." }
|
||||
require(width > 0) { "Width of block must be greater than zero." }
|
||||
require(height > 0) { "Height of block must be greater than zero." }
|
||||
require(x >= 0) { "X coordinate of block must be non-negative." }
|
||||
require(y >= 0) { "Y coordinate of block must be non-negative." }
|
||||
return this[x..<x + width, y..<y + height]
|
||||
return RectangleGrid(this[x..<x + width, y..<y + height])
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -296,7 +308,7 @@ fun List<List<Rectangle>>.block(x: Int, y: Int, width: Int, height: Int): List<L
|
||||
* @param n The number of columns to drop from each inner list. Must be non-negative.
|
||||
* @return A new 2D list of Rectangles with the first n columns removed.
|
||||
*/
|
||||
fun List<List<Rectangle>>.dropColumns(n: Int): List<List<Rectangle>> = map { it.drop(n) }
|
||||
fun RectangleGrid.dropColumns(n: Int): RectangleGrid = RectangleGrid(map { it.drop(n) })
|
||||
|
||||
/**
|
||||
* Removes the last `n` columns from each row (inner list) within a two-dimensional list.
|
||||
@@ -304,7 +316,7 @@ fun List<List<Rectangle>>.dropColumns(n: Int): List<List<Rectangle>> = map { it.
|
||||
* @param n The number of columns to drop from the end of each inner list.
|
||||
* @return A new two-dimensional list with the last `n` columns removed from each row.
|
||||
*/
|
||||
fun List<List<Rectangle>>.dropLastColumns(n: Int): List<List<Rectangle>> = map { it.dropLast(n) }
|
||||
fun RectangleGrid.dropLastColumns(n: Int): RectangleGrid = RectangleGrid(map { it.dropLast(n) })
|
||||
|
||||
/**
|
||||
* Selects the first `n` columns from each row in a 2D list of `Rectangle` objects.
|
||||
@@ -313,7 +325,7 @@ fun List<List<Rectangle>>.dropLastColumns(n: Int): List<List<Rectangle>> = map {
|
||||
* all elements of that row are returned.
|
||||
* @return A new 2D list containing the first `n` columns from each row of the original list.
|
||||
*/
|
||||
fun List<List<Rectangle>>.takeColumns(n: Int): List<List<Rectangle>> = map { it.take(n) }
|
||||
fun RectangleGrid.takeColumns(n: Int): RectangleGrid = RectangleGrid(map { it.take(n) })
|
||||
|
||||
/**
|
||||
* Returns a new list where each sub-list contains only the last `n` elements of the original sub-list.
|
||||
@@ -321,7 +333,7 @@ fun List<List<Rectangle>>.takeColumns(n: Int): List<List<Rectangle>> = map { it.
|
||||
* @param n The number of elements to retain from the end of each sub-list.
|
||||
* @return A list containing sub-lists that include the last `n` elements of each original sub-list.
|
||||
*/
|
||||
fun List<List<Rectangle>>.takeLastColumns(n: Int): List<List<Rectangle>> = map { it.takeLast(n) }
|
||||
fun RectangleGrid.takeLastColumns(n: Int): RectangleGrid = RectangleGrid(map { it.takeLast(n) })
|
||||
|
||||
/**
|
||||
* Slices the specified range of columns from each row of a two-dimensional list.
|
||||
@@ -329,7 +341,7 @@ fun List<List<Rectangle>>.takeLastColumns(n: Int): List<List<Rectangle>> = map {
|
||||
* @param range The range of column indices to slice from each row.
|
||||
* @return A new list containing sublists with columns sliced from the input range.
|
||||
*/
|
||||
fun List<List<Rectangle>>.sliceColumns(range: IntRange): List<List<Rectangle>> = map { it.slice(range) }
|
||||
fun RectangleGrid.sliceColumns(range: IntRange): RectangleGrid = RectangleGrid(map { it.slice(range) })
|
||||
|
||||
/**
|
||||
* Selects specific columns from a two-dimensional list of rectangles.
|
||||
@@ -338,4 +350,4 @@ fun List<List<Rectangle>>.sliceColumns(range: IntRange): List<List<Rectangle>> =
|
||||
* @param indices The collection of column indices to retain in each inner list.
|
||||
* @return A new two-dimensional list of rectangles with only the selected columns.
|
||||
*/
|
||||
fun List<List<Rectangle>>.sliceColumns(indices: Iterable<Int>): List<List<Rectangle>> = map { it.slice(indices) }
|
||||
fun RectangleGrid.sliceColumns(indices: Iterable<Int>): RectangleGrid = RectangleGrid(map { it.slice(indices) })
|
||||
Reference in New Issue
Block a user