From 17e10a59ee2401fb5fb948cab06ead2d57805b3c Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Thu, 20 Feb 2025 23:56:37 +0100 Subject: [PATCH] [orx-shapes] Add utilities for sub-list operations in Rectangle grids Introduced functions for slicing, dropping, and selecting columns in 2D rectangle grids, including `uniformBlock` for random sub-block extraction. --- .../kotlin/primitives/RectangleGrid.kt | 106 +++++++++++++++++- 1 file changed, 104 insertions(+), 2 deletions(-) diff --git a/orx-shapes/src/commonMain/kotlin/primitives/RectangleGrid.kt b/orx-shapes/src/commonMain/kotlin/primitives/RectangleGrid.kt index 9504c9ec..b9132959 100644 --- a/orx-shapes/src/commonMain/kotlin/primitives/RectangleGrid.kt +++ b/orx-shapes/src/commonMain/kotlin/primitives/RectangleGrid.kt @@ -59,7 +59,6 @@ fun Rectangle.irregularGrid( return result } - /** * Splits [Rectangle] into a grid of [Rectangle]s * @param columns the number of columns in the resulting grid @@ -221,6 +220,37 @@ fun List>.uniform(random: Random): Rectangle { return this.random(random).random(random) } +/** + * Extracts a uniform random sub-block of rectangles from a 2D list within the specified constraints. + * + * @param minWidth The minimum width (number of columns) of the block. Defaults to 1. + * @param maxWidth The maximum width (number of columns) of the block. Defaults to the width of the original 2D list. + * @param minHeight The minimum height (number of rows) of the block. Defaults to 1. + * @param maxHeight The maximum height (number of rows) of the block. Defaults to the height of the original 2D list. + * @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>.uniformBlock( + minWidth: Int = 1, + maxWidth: Int = this[0].size, + minHeight: Int = 1, + maxHeight: Int = this.size, + random: Random = Random.Default +): List> { + 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." } + require(minHeight <= maxHeight) { "Minimum height must be less than or equal to maximum height." } + require(maxWidth <= this[0].size + 1) { "Maximum width (=$maxWidth) must be less than or equal to the width of the original 2D list + 1 (=${this[0].size})." } + require(maxHeight <= this.size + 1) { "Maximum height (=$maxHeight) must be less than or equal the height of the original 2D list + 1 (=${this.size})." } + val width = random.nextInt(minWidth, maxWidth) + val height = random.nextInt(minHeight, maxHeight) + + val x = random.nextInt(0, this[0].size - width + 1) + val y = random.nextInt(0, this.size - height + 1) + return block(x, y, width, height) +} + /** * Retrieves a column of rectangles from a 2D list of rectangles. * @@ -236,4 +266,76 @@ fun List>.column(index: Int): List = this.map { it[in * @param index The index of the row to retrieve. Must be in the valid range of indices for the list. * @return A list of `Rectangle` objects representing the row at the given index. */ -fun List>.row(index: Int): List = this[index] \ No newline at end of file +fun List>.row(index: Int): List = this[index] + +/** + * Extracts a sub-block from a 2D list of rectangles based on the specified coordinates and dimensions. + * + * @param x The horizontal starting index of the block. + * @param y The vertical starting index of the block. + * @param width The width of the block, specifying the number of columns to include. + * @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>.block(x: Int, y: Int, width: Int, height: Int): List> { + 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..>.dropColumns(n: Int): List> = map { it.drop(n) } + +/** + * Removes the last `n` columns from each row (inner list) within a two-dimensional list. + * + * @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>.dropLastColumns(n: Int): List> = map { it.dropLast(n) } + +/** + * Selects the first `n` columns from each row in a 2D list of `Rectangle` objects. + * + * @param n The number of columns to select from each row. If a row has fewer than `n` elements, + * 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>.takeColumns(n: Int): List> = map { it.take(n) } + +/** + * Returns a new list where each sub-list contains only the last `n` elements of the original sub-list. + * + * @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>.takeLastColumns(n: Int): List> = map { it.takeLast(n) } + +/** + * Slices the specified range of columns from each row of a two-dimensional list. + * + * @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>.sliceColumns(range: IntRange): List> = map { it.slice(range) } + +/** + * Selects specific columns from a two-dimensional list of rectangles. + * The method slices each inner list based on the provided column indices. + * + * @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>.sliceColumns(indices: Iterable): List> = map { it.slice(indices) } \ No newline at end of file