Skip to content

Commit

Permalink
libktx#479: add math Shape2D destructure extensions and MapRenderer u…
Browse files Browse the repository at this point in the history
…se extension
  • Loading branch information
Quillraven committed Mar 24, 2024
1 parent 671f7e5 commit 2eac905
Show file tree
Hide file tree
Showing 14 changed files with 400 additions and 1 deletion.
6 changes: 6 additions & 0 deletions math/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ used to quickly determine which velocity or force is greater. Note that length *
much faster to calculate and yields the same results in most cases.
- `dot` infix function allows to calculate the dot product of 2 vectors.
- `x` infix function allows to calculate the cross product of 2 vectors.
- `Rectangle` and `Ellipse` instances can be destructed to four float variables in one step with
`val (x, y, w, h) = rectangle/ellipse` syntax thanks to `component1()`, `component2()`, `component3()` and `component4()` operator methods.
- `Circle` instances can be destructed to three float variables in one step with
`val (x, y, radius) = circle` syntax thanks to `component1()`, `component2()` and `component3()` operator methods.
- `Polygon` and `Polyline` instances can be destructed to two float variables in one step with
`val (x, y) = polygon/polyline` syntax thanks to `component1()` and `component2()` operator methods.

Note that since `Shape2D` has `contains(Vector2)` method, `in` operator can be used for any `Shape2D` implementation
(like `Rectangle`, `Ellipse` or `Circle`). For example, given `vec: Vector2` and `rect: Rectangle` variables, you can
Expand Down
21 changes: 21 additions & 0 deletions math/src/main/kotlin/ktx/math/circle.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ktx.math

import com.badlogic.gdx.math.Circle

/**
* Operator function that allows to deconstruct this circle.
* @return X component.
*/
operator fun Circle.component1() = this.x

/**
* Operator function that allows to deconstruct this circle.
* @return Y component.
*/
operator fun Circle.component2() = this.y

/**
* Operator function that allows to deconstruct this circle.
* @return Radius component.
*/
operator fun Circle.component3() = this.radius
27 changes: 27 additions & 0 deletions math/src/main/kotlin/ktx/math/ellipse.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ktx.math

import com.badlogic.gdx.math.Ellipse

/**
* Operator function that allows to deconstruct this ellipse.
* @return X component.
*/
operator fun Ellipse.component1() = this.x

/**
* Operator function that allows to deconstruct this ellipse.
* @return Y component.
*/
operator fun Ellipse.component2() = this.y

/**
* Operator function that allows to deconstruct this ellipse.
* @return Width component.
*/
operator fun Ellipse.component3() = this.width

/**
* Operator function that allows to deconstruct this ellipse.
* @return Height component.
*/
operator fun Ellipse.component4() = this.height
15 changes: 15 additions & 0 deletions math/src/main/kotlin/ktx/math/polygon.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ktx.math

import com.badlogic.gdx.math.Polygon

/**
* Operator function that allows to deconstruct this polygon.
* @return X component.
*/
operator fun Polygon.component1() = this.x

/**
* Operator function that allows to deconstruct this polygon.
* @return Y component.
*/
operator fun Polygon.component2() = this.y
15 changes: 15 additions & 0 deletions math/src/main/kotlin/ktx/math/polyline.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ktx.math

import com.badlogic.gdx.math.Polyline

/**
* Operator function that allows to deconstruct this polyline.
* @return X component.
*/
operator fun Polyline.component1() = this.x

/**
* Operator function that allows to deconstruct this polyline.
* @return Y component.
*/
operator fun Polyline.component2() = this.y
27 changes: 27 additions & 0 deletions math/src/main/kotlin/ktx/math/rectangle.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ktx.math

import com.badlogic.gdx.math.Rectangle

/**
* Operator function that allows to deconstruct this rectangle.
* @return X component.
*/
operator fun Rectangle.component1() = this.x

/**
* Operator function that allows to deconstruct this rectangle.
* @return Y component.
*/
operator fun Rectangle.component2() = this.y

/**
* Operator function that allows to deconstruct this rectangle.
* @return Width component.
*/
operator fun Rectangle.component3() = this.width

/**
* Operator function that allows to deconstruct this rectangle.
* @return Height component.
*/
operator fun Rectangle.component4() = this.height
20 changes: 20 additions & 0 deletions math/src/test/kotlin/ktx/math/CircleTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ktx.math

import com.badlogic.gdx.math.Circle
import org.junit.Assert.assertEquals
import org.junit.Test

class CircleTest {

@Test
fun `should destructure Circle`() {
val circle = Circle(1f, 2f, 3f)

val (x, y, radius) = circle

assertEquals(1f, x)
assertEquals(2f, y)
assertEquals(3f, radius)
}

}
21 changes: 21 additions & 0 deletions math/src/test/kotlin/ktx/math/EllipseTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ktx.math

import com.badlogic.gdx.math.Ellipse
import org.junit.Assert.assertEquals
import org.junit.Test

class EllipseTest {

@Test
fun `should destructure Ellipse`() {
val ellipse = Ellipse(1f, 2f, 3f, 4f)

val (x, y, w, h) = ellipse

assertEquals(1f, x)
assertEquals(2f, y)
assertEquals(3f, w)
assertEquals(4f, h)
}

}
19 changes: 19 additions & 0 deletions math/src/test/kotlin/ktx/math/PolygonTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ktx.math

import com.badlogic.gdx.math.Polygon
import org.junit.Assert.assertEquals
import org.junit.Test

class PolygonTest {

@Test
fun `should destructure Polygon`() {
val polygon = Polygon().apply { setPosition(1f, 2f) }

val (x, y) = polygon

assertEquals(1f, x)
assertEquals(2f, y)
}

}
19 changes: 19 additions & 0 deletions math/src/test/kotlin/ktx/math/PolylineTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ktx.math

import com.badlogic.gdx.math.Polyline
import org.junit.Assert.assertEquals
import org.junit.Test

class PolylineTest {

@Test
fun `should destructure Polyline`() {
val polyline = Polyline().apply { setPosition(1f, 2f) }

val (x, y) = polyline

assertEquals(1f, x)
assertEquals(2f, y)
}

}
21 changes: 21 additions & 0 deletions math/src/test/kotlin/ktx/math/RectangleTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ktx.math

import com.badlogic.gdx.math.Rectangle
import org.junit.Assert.assertEquals
import org.junit.Test

class RectangleTest {

@Test
fun `should destructure Rectangle`() {
val rect = Rectangle(1f, 2f, 3f, 4f)

val (x, y, w, h) = rect

assertEquals(1f, x)
assertEquals(2f, y)
assertEquals(3f, w)
assertEquals(4f, h)
}

}
33 changes: 32 additions & 1 deletion tiled/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

### Why?

libGDX brings its own set of Tiled map utilities, including loading and handling of maps exported from the editor.
LibGDX brings its own set of Tiled map utilities, including loading and handling of maps exported from the editor.
However, the API contains many wrapped non-standard collections, which makes accessing the loaded maps cumbersome.
With Kotlin's reified types and extension methods, the Tiled API can be significantly improved.

Expand Down Expand Up @@ -83,6 +83,17 @@ a certain function on them.

`isEmpty` and `isNotEmpty` extension method to check if the specific collection is empty or not.

### `BatchTiledMapRenderer`

`use` extension method to call `beginRender()` and `endRender()` automatically before
your render logic.

**Careful**: Since `beginRender()` and `endRender()` are protected methods, they are
not called directly by the `use` extension. Instead, their current implementation of the
`BatchTiledMapRenderer` is duplicated inside this extension. This means, that if you have your own
custom `BatchTiledMapRenderer` that overrides those two methods then this extension will
not call your custom `beginRender()` and `endRender()`.

### Usage examples

#### General
Expand Down Expand Up @@ -259,6 +270,26 @@ if (map.layers.isNotEmpty()) {
}
```

Using the `use` extension function to render background layers:

```kotlin
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.maps.tiled.TiledMap
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer

val tiledMap = TiledMap()
val renderer = OrthogonalTiledMapRenderer(tiledMap)
val camera = OrthographicCamera()
val bgdLayers: List<TiledMapTileLayer> = tiledMap.layers
.filter { it.name.startsWith("bgd") }
.filterIsInstance<TiledMapTileLayer>()

renderer.use(camera) { mapRenderer ->
bgdLayers.forEach { mapRenderer.renderTileLayer(it) }
}
```

#### Additional documentation

- [Official Tiled website.](https://www.mapeditor.org/)
Expand Down
74 changes: 74 additions & 0 deletions tiled/src/main/kotlin/ktx/tiled/batchTiledMapRenderer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package ktx.tiled

import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.maps.tiled.renderers.BatchTiledMapRenderer
import com.badlogic.gdx.maps.tiled.tiles.AnimatedTiledMapTile
import com.badlogic.gdx.math.Matrix4
import com.badlogic.gdx.math.Rectangle

/**
* Automatically calls [BatchTiledMapRenderer.beginRender] and [BatchTiledMapRenderer.endRender].
* @param camera A camera to set on the renderer before [BatchTiledMapRenderer.beginRender].
* @param block inlined. Executed after [BatchTiledMapRenderer.beginRender] and before [BatchTiledMapRenderer.endRender].
*/
inline fun <T : BatchTiledMapRenderer> T.use(
camera: OrthographicCamera,
block: (T) -> Unit
) {
this.setView(camera)
AnimatedTiledMapTile.updateAnimationBaseTime()
this.batch.begin()

block(this)

this.batch.end()
}

/**
* Automatically calls [BatchTiledMapRenderer.beginRender] and [BatchTiledMapRenderer.endRender].
* @param projection A projection matrix to set on the renderer before [BatchTiledMapRenderer.beginRender].
* @param x map render boundary x value.
* @param y map render boundary y value.
* @param width map render boundary width value.
* @param height map render boundary height value.
* @param block inlined. Executed after [BatchTiledMapRenderer.beginRender] and before [BatchTiledMapRenderer.endRender].
*/
inline fun <T : BatchTiledMapRenderer> T.use(
projection: Matrix4,
x: Float, y: Float,
width: Float, height: Float,
block: (T) -> Unit
) {
this.setView(projection, x, y, width, height)
AnimatedTiledMapTile.updateAnimationBaseTime()
this.batch.begin()

block(this)

this.batch.end()
}

/**
* Automatically calls [BatchTiledMapRenderer.beginRender] and [BatchTiledMapRenderer.endRender].
* @param projection A projection matrix to set on the renderer before [BatchTiledMapRenderer.beginRender].
* @param mapBoundary map render boundary.
* @param block inlined. Executed after [BatchTiledMapRenderer.beginRender] and before [BatchTiledMapRenderer.endRender].
*/
inline fun <T : BatchTiledMapRenderer> T.use(
projection: Matrix4,
mapBoundary: Rectangle,
block: (T) -> Unit
) = this.use(projection, mapBoundary.x, mapBoundary.y, mapBoundary.width, mapBoundary.height, block)

/**
* Automatically calls [BatchTiledMapRenderer.beginRender] and [BatchTiledMapRenderer.endRender].
* @param block inlined. Executed after [BatchTiledMapRenderer.beginRender] and before [BatchTiledMapRenderer.endRender].
*/
inline fun <T : BatchTiledMapRenderer> T.use(block: (T) -> Unit) {
AnimatedTiledMapTile.updateAnimationBaseTime()
this.batch.begin()

block(this)

this.batch.end()
}
Loading

0 comments on commit 2eac905

Please sign in to comment.