Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize polygon rasterization #522

Merged
merged 1 commit into from
Sep 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,29 @@ final case class AxisAlignedBoundingBox(
x <- (x1 until x2).iterator
} f(x, y)
}

object AxisAlignedBoundingBox {

/** Mutable Builder for AABBs.
*/
final class Builder() {
private var x1: Int = Int.MaxValue
private var y1: Int = Int.MaxValue
private var x2: Int = Int.MinValue
private var y2: Int = Int.MinValue

def add(x: Int, y: Int): this.type = {
if (x < x1) x1 = x
if (y < y1) y1 = y
if (x > x2) x2 = x
if (y > y2) y2 = y
this
}

def add(point: Shape.Point): this.type = add(point.x, point.y)

def result(): AxisAlignedBoundingBox =
if (x1 > x2 || y1 > y2) AxisAlignedBoundingBox(0, 0, 0, 0)
else AxisAlignedBoundingBox(x1, y1, x2 - x1, y2 - y1)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,33 @@ package eu.joaocosta.minart.geometry
* @param vertices ordered sequence of vertices.
*/
final case class ConvexPolygon(vertices: Vector[Shape.Point]) extends Shape {
require(vertices.size >= 3, "A polygon needs at least 3 vertices")

val aabb: AxisAlignedBoundingBox = {
val x1 = vertices.iterator.minBy(_.x).x
val y1 = vertices.iterator.minBy(_.y).y
val x2 = vertices.iterator.maxBy(_.x).x
val y2 = vertices.iterator.maxBy(_.y).y
AxisAlignedBoundingBox(x1, y1, x2 - x1, y2 - y1)
val size = vertices.size
require(size >= 3, "A polygon needs at least 3 vertices")

lazy val aabb: AxisAlignedBoundingBox = {
val builder = AxisAlignedBoundingBox.Builder()
vertices.foreach(builder.add)
builder.result()
}

val knownFace: Option[Shape.Face] =
faceAt(aabb.centerX, aabb.centerY)
lazy val knownFace: Option[Shape.Face] =
faceAt(vertices.head)

private def edgeFunction(x1: Int, y1: Int, x2: Int, y2: Int, x3: Int, y3: Int): Int =
(x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1)

private def edgeFunction(p1: Shape.Point, p2: Shape.Point, p3: Shape.Point): Int =
edgeFunction(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y)

private def rawWeights(x: Int, y: Int): Iterator[Int] =
(0 until vertices.size).iterator.map(idx =>
private def rawWeights(x: Int, y: Int): Iterator[Int] = {
(0 until size).iterator.map(idx =>
val current = vertices(idx)
val next = if (idx + 1 >= vertices.size) vertices(0) else vertices(idx + 1)
val next = if (idx + 1 >= size) vertices(0) else vertices(idx + 1)
edgeFunction(current.x, current.y, next.x, next.y, x, y)
)
}

private val maxWeight: Int =
private lazy val maxWeight: Int =
(vertices.tail)
.sliding(2)
.collect { case Vector(b, c) =>
Expand All @@ -47,14 +47,35 @@ final case class ConvexPolygon(vertices: Vector[Shape.Point]) extends Shape {
.sum

def faceAt(x: Int, y: Int): Option[Shape.Face] = {
val sides = rawWeights(x, y).filter(_ != 0).map(_ >= 0).distinct.toVector
if (sides.size == 1) {
if (sides.head) Shape.someFront else Shape.someBack
} else None
val it = rawWeights(x, y).filter(_ != 0).map(_ >= 0)
var res: Option[Shape.Face] = null
if (it.hasNext) {
var last = it.next()
while (it.hasNext && res != None) {
val value = it.next()
if (last != value) res = None
last = value
}
if (res == null) {
if (last) res = Shape.someFront
else res = Shape.someBack
}
}
if (res == null) None else res
}

override def contains(x: Int, y: Int): Boolean = {
rawWeights(x, y).filter(_ != 0).map(_ >= 0).distinct.size == 1
val it = rawWeights(x, y).filter(_ != 0).map(_ >= 0)
var res = true
if (it.hasNext) {
var last = it.next()
while (it.hasNext && res) {
val value = it.next()
if (last != value) res = false
last = value
}
}
res
}

/** Checks if this polygon contains another polygon.
Expand Down
Loading