Skip to content

Commit

Permalink
polygon: Move polygon rel. fn's to new file
Browse files Browse the repository at this point in the history
  • Loading branch information
johannes-wolf committed Jul 19, 2024
1 parent b69f81f commit 6b9fa2b
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 40 deletions.
43 changes: 8 additions & 35 deletions src/draw/projection.typ
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#import "/src/matrix.typ"
#import "/src/drawable.typ"
#import "/src/util.typ"
#import "/src/polygon.typ"

// Get an orthographic view matrix for 3 angles
#let ortho-matrix(x, y, z) = matrix.mul-mat(
Expand All @@ -20,6 +21,7 @@
(0, 0, 0, 1),
)

// Sort drawables by median or max segment z-value
#let _sort-by-distance(drawables) = {
return drawables.sorted(key: d => {
let z = none
Expand All @@ -35,41 +37,12 @@
})
}

#let _calc-polygon-area(points) = {
let a = 0 // Signed area: 1/2 sum_i=0^n-1 x_i*y_i+1 - x_i+1*y_i
let n = points.len()
let (cx, cy) = (0, 0)
for i in range(0, n) {
let (x0, y0, _) = points.at(i)
let (x1, y1, _) = points.at(calc.rem(i + 1, n))
cx += (x0 + x1) * (x0 * y1 - x1 * y0)
cy += (y0 + y1) * (x0 * y1 - x1 * y0)
a += x0 * y1 - x1 * y0
}
return .5 * a
}

// Compute the face order by computing the face
// area. Curves get sampled to a polygon.
#let _compute-face-order(d, samples: 10) = {
import "/src/bezier.typ": cubic-point
let points = ()
for ((kind, ..pts)) in d.segments {
if kind == "cubic" {
pts = range(0, samples).map(t => {
cubic-point(..pts, t / (samples - 1))
})
}
points += pts
}
return _calc-polygon-area(points) <= 0
}

#let _filter-cw-faces(drawables, invert: false) = {
// Filter out all clock-wise polygons, or if `invert` is true,
// all counter clock-wise ones.
#let _filter-cw-faces(drawables, mode: "cw") = {
return drawables.filter(d => {
let a = _compute-face-order(d)
let b = invert
return (a and not b) or (not a and b)
let poly = polygon.from-segments(d.segments)
poly.first() != poly.last() or polygon.winding-order(poly) == mode
})
}

Expand All @@ -93,7 +66,7 @@
if cull-face != none {
assert(cull-face in ("cw", "ccw"),
message: "cull-face must be none, cw or ccw.")
drawables = _filter-cw-faces(drawables, invert: cull-face == "ccw")
drawables = _filter-cw-faces(drawables, mode: cull-face)
}
if sorted {
drawables = _sort-by-distance(drawables)
Expand Down
59 changes: 59 additions & 0 deletions src/polygon.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/// Returns a list of polygon points from
/// a list of segments.
///
/// Cubic segments get linearized by sampling.
///
/// - segment (list): List of segments
/// - samples (int): Number of samples
/// -> List of vectors
#let from-segments(segments, samples: 10) = {
import "/src/bezier.typ": cubic-point
let poly = ()
for ((kind, ..pts)) in segments {
if kind == "cubic" {
poly += range(0, samples).map(t => {
cubic-point(..pts, t / (samples - 1))
})
} else {
poly += pts
}
}
return poly
}

/// Computes the signed area of a 2D polygon.
///
/// The formula used is the following:
/// $ 1/2 sum_i=0^n-1 x_i*y_i+1 - x_i+1*y_i $
///
/// - points (list): List of Vectors of dimension >= 2
/// -> Signed area
#let signed-area(points) = {
let a = 0
let n = points.len()
let (cx, cy) = (0, 0)
for i in range(0, n) {
let (x0, y0, ..) = points.at(i)
let (x1, y1, ..) = points.at(calc.rem(i + 1, n))
cx += (x0 + x1) * (x0 * y1 - x1 * y0)
cy += (y0 + y1) * (x0 * y1 - x1 * y0)
a += x0 * y1 - x1 * y0
}
return .5 * a
}

/// Returns the winding order of a 2D polygon
/// by using it's signed area.
///
/// - point (list): List of polygon points
/// -> "ccw" (counter clock-wise) or "cw" (clock-wise) or none
#let winding-order(points) = {
let area = signed-area(points)
if area > 0 {
"cw"
} else if area < 0 {
"ccw"
} else {
none
}
}
Binary file modified tests/projection/ortho/ref/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 5 additions & 5 deletions tests/projection/ortho/test.typ
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,17 @@
#test-case({
import draw: *
ortho(x: 0deg, y: 0deg, cull-face: "cw", {
rect((-1, -1), (1, 1))
circle((0,0))
line((-1, -1), (1, -1), (1, 1), (-1, 1), close: true)
line((-1,-1), (1,-1), (0,1), close: true)
})
})

// Nothing visible
#test-case({
import draw: *
ortho(x: 0deg, y: 0deg, cull-face: "cw", {
rect((-1, -1), (1, 1))
rotate(y: 91deg)
circle((0,0))
line((-1, -1), (1, -1), (1, 1), (-1, 1), close: true)
rotate(y: 120deg)
line((-1,-1), (1,-1), (0,1), close: true)
})
})

0 comments on commit 6b9fa2b

Please sign in to comment.