Skip to content

Commit

Permalink
Merge pull request #521 from JD557/rasterizer-examples
Browse files Browse the repository at this point in the history
Add vector shapes example and add helper methods
  • Loading branch information
JD557 committed Sep 7, 2024
2 parents 968a07e + c799a85 commit 6fe0c34
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 18 deletions.
93 changes: 77 additions & 16 deletions core/shared/src/main/scala/eu/joaocosta/minart/geometry/Shape.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,84 @@ trait Shape {
}

object Shape {

/** Coordinates of a point in the shape.
*
* For performance reasons, only integer coordinates are supported,
* although shapes are free to use floating point in intermediate states
* and transformations.
*/
final case class Point(x: Int, y: Int)

/** The shape of a circle.
*
* If the radius is positive, the circle's front face is facing the viewer.
* If the radius is negative, the circle's back face is facing the viewer.
*/
def circle(center: Point, radius: Int): Circle = Circle(center, radius)

/** The shape of an axis aligned rectangle.
*
* If p1 is the top left point or bottom right point, the rectangle's front face is facing the viewer.
* Otherwise, the rectangle's back face is facing the viewer.
*/
def rectangle(p1: Point, p2: Point): ConvexPolygon = ConvexPolygon(
Vector(
p1,
Point(p2.x, p1.y),
p2,
Point(p1.x, p2.y)
)
)

/** The shape of a triangle.
*
* If the points are ordered clockwise, the triangle's front face is facing the viewer.
* Otherwise, the triangle's back face is facing the viewer.
*/
def triangle(p1: Point, p2: Point, p3: Point): ConvexPolygon = ConvexPolygon(
Vector(
p1,
p2,
p3
)
)

/** The shape of an arbitrary convex polygon.
*
* If the points are ordered clockwise, the polygon's front face is facing the viewer.
* Otherwise, the polygon's back face is facing the viewer.
*
* If the points do not form a convex polygon, the behavior is undefined.
*/
def convexPolygon(p1: Point, p2: Point, p3: Point, ps: Point*): ConvexPolygon = ConvexPolygon(
Vector(
p1,
p2,
p3
) ++ ps
)

/** Face of a convex polygon.
*
* If the points are defined in clockwise order, the front face faces the viewer.
*/
enum Face {
case Front
case Back
}

// Preallocated values to avoid repeated allocations
private[geometry] val someFront = Some(Face.Front)
private[geometry] val someBack = Some(Face.Back)

private[Shape] final case class MatrixShape(matrix: Matrix, shape: Shape) extends Shape {
def knownFace: Option[Shape.Face] = shape.knownFace
def knownFace: Option[Shape.Face] = if (matrix.a * matrix.e < 0)
shape.knownFace.map {
case Face.Front => Face.Back
case Face.Back => Face.Front
}
else shape.knownFace
lazy val aabb: AxisAlignedBoundingBox = {
val xs = Vector(
matrix.applyX(shape.aabb.x1, shape.aabb.y1),
Expand All @@ -159,19 +235,4 @@ object Shape {
override def mapMatrix(matrix: Matrix) =
MatrixShape(matrix.multiply(this.matrix), shape)
}

/** Face if a convex polygon.
*
* If the points are defined in clockwise order, the front face faces the viewer.
*/
enum Face {
case Front
case Back
}

// Preallocated values to avoid repeated allocations
private[geometry] val someFront = Some(Face.Front)
private[geometry] val someBack = Some(Face.Back)

final case class Point(x: Int, y: Int)
}
92 changes: 92 additions & 0 deletions examples/snapshot/10-vector-shapes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# 10. Vector shapes

Besides surfaces, Minart also allows you to render some basic vector shapes.

## Drawing shapes

### Dependencies and imports

The relevant methods are in the `eu.joaocosta.minart.geometry` package

```scala
//> using scala "3.3.3"
//> using dep "eu.joaocosta::minart::0.6.2-SNAPSHOT"

import eu.joaocosta.minart.backend.defaults.given
import eu.joaocosta.minart.graphics.*
import eu.joaocosta.minart.geometry.*
import eu.joaocosta.minart.runtime.*
```

### Shapes

Vectorial shapes are represented by the `Shape` abstraction.

Minart already comes with some basic shapes, such as circle and convex polygons, with helper methods in the `Shape` companion object.

First, let's create a few shapes with those methods.

```scala
import eu.joaocosta.minart.geometry.Shape.Point

val triangle = Shape.triangle(Point(-16, 16), Point(0, -16), Point(16, 16))
val square = Shape.rectangle(Point(-16, -16), Point(16, 16))
val octagon = Shape.convexPolygon(
Point(-8, -16),
Point(8, -16),
Point(16, -8),
Point(16, 8),
Point(8, 16),
Point(-8, 16),
Point(-16, 8),
Point(-16, -8)
)
val circle = Shape.circle(Point(0, 0), 16)
```

### Faces

Notice that all shapes are defined with points in clockwise fashion.

All shapes have two faces: A front face and a back face.

Depending on the way they are defined (or transformed), different faces might be shown.

Minart allows you to set different colors for each face, and even no color at all!
This is helpful if, for some reason, you know you don't want to draw back faces.

### Rasterizing

Now we just need to use the `rasterize` operation, just like we did with `blit`.

In this example we will also scale our images with time, to show how the color changes when the face flips.

```scala
val frontfaceColor = Color(255, 0, 0)
val backfaceColor = Color(0, 255, 0)

def application(t: Double, canvas: Canvas): Unit = {
val scale = math.sin(t)
canvas.rasterize(triangle.scale(scale, 1.0), Some(frontfaceColor), Some(backfaceColor))(32, 32)
canvas.rasterize(square.scale(scale, 1.0), Some(frontfaceColor), Some(backfaceColor))(64, 32)
canvas.rasterize(octagon.scale(scale, 1.0), Some(frontfaceColor), Some(backfaceColor))(32, 64)
canvas.rasterize(circle.scale(scale, 1.0), Some(frontfaceColor), Some(backfaceColor))(64, 64)
}
```

### Putting it all together

```scala
val canvasSettings = Canvas.Settings(width = 128, height = 128, scale = Some(4), clearColor = Color(0, 0, 0))

AppLoop
.statefulRenderLoop((t: Double) => (canvas: Canvas) => {
canvas.clear()
application(t, canvas)
canvas.redraw()
t + 0.01
}
)
.configure(canvasSettings, LoopFrequency.hz60, 0)
.run()
```
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 10. Audio playback
# 11. Audio playback

Besides graphics and input, Minart also supports loading and playing back audio.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 11. Loading sounds
# 12. Loading sounds

Just like we did with images, we can also load audio clips from files.

Expand Down

0 comments on commit 6fe0c34

Please sign in to comment.