This repository has been archived on 2026-05-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
instructions-clear/src/terrain.js
T

91 lines
2.7 KiB
JavaScript

import { Shape, ShapeUtils, Vector2 } from 'three'
import SAT from 'sat'
export default class Terrain {
id = crypto.randomUUID()
position = new Vector2()
relativeVertices = []
#colliders = []
#hull = null
#vertices = []
#unadjustedWaypoints = []
constructor(vertices) {
this.#vertices = vertices.map((v) => new Vector2(v.x, v.y))
if (ShapeUtils.isClockWise(this.#vertices)) {
this.#vertices.reverse()
}
this.#calculateColliders()
this.#calculatePosition()
this.#calculateRelativeVertices()
this.#calculateUnadjustedWaypoints()
}
get colliders() { return this.#colliders }
get unadjustedWaypoints() { return this.#unadjustedWaypoints }
get vertices() { return this.#vertices }
static waypointsForSide(fromVertex, toVertex, isClockwise = false) {
const from = isClockwise ? toVertex : fromVertex
const to = isClockwise ? fromVertex : toVertex
const origin = new Vector2()
const sideNormal = to.clone().sub(from).clone().normalize()
const margin = sideNormal.clone().rotateAround(origin, -3 * Math.PI / 4)
const offset = margin.clone().multiplyScalar(Math.SQRT2)
const inverseMargin = sideNormal.clone().negate().rotateAround(origin, 3 * Math.PI / 4)
const inverseOffset = inverseMargin.clone().multiplyScalar(Math.SQRT2)
return [
[margin.clone().add(from), offset],
[inverseMargin.clone().add(to), inverseOffset],
]
}
state() {
return {
...this,
}
}
#shape() {
const complexShape = new Shape()
complexShape.moveTo(this.#vertices.at(0).x, this.#vertices.at(0).y)
this.#vertices.slice(1).forEach((v) => complexShape.lineTo(v.x, v.y))
return complexShape
}
#calculateColliders() {
const points = this.#shape().extractPoints(16)
const indicesToPolygon = (indices) => {
const satPoints = [
new SAT.Vector(...points.shape[indices[0]].toArray()),
new SAT.Vector(...points.shape[indices[1]].clone().sub(points.shape[indices[0]]).toArray()),
new SAT.Vector(...points.shape[indices[2]].clone().sub(points.shape[indices[0]]).toArray()),
]
return new SAT.Polygon(satPoints[0], [new SAT.Vector(), satPoints[1], satPoints[2]])
}
this.#colliders = ShapeUtils.triangulateShape(points.shape, points.holes).map(indicesToPolygon)
}
#calculatePosition() {
this.position = this.#vertices.reduce(((sum, v) => sum.add(v)), new Vector2()).divideScalar(this.#vertices.length)
}
#calculateRelativeVertices() {
this.relativeVertices = this.#vertices.map((v) => v.clone().sub(this.position))
}
#calculateUnadjustedWaypoints() {
this.#unadjustedWaypoints = this.#vertices.map((v, i, arr) => Terrain.waypointsForSide(v, i + 1 < arr.length ? arr[i + 1] : arr[0])).flat()
}
}