2021-08-31 15:16:56 +10:00
|
|
|
package engine
|
|
|
|
|
2021-09-02 13:42:44 +10:00
|
|
|
import "strconv"
|
|
|
|
|
|
|
|
// Point3 is a en element of int^3.
|
|
|
|
type Point3 struct {
|
|
|
|
X, Y, Z int
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pt3(x, y, z) is shorthand for Point3{x, y, z}.
|
|
|
|
func Pt3(x, y, z int) Point3 {
|
|
|
|
return Point3{x, y, z}
|
|
|
|
}
|
|
|
|
|
|
|
|
// String returns a string representation of p like "(3,4,5)".
|
|
|
|
func (p Point3) String() string {
|
|
|
|
return "(" + strconv.Itoa(p.X) + "," + strconv.Itoa(p.Y) + "," + strconv.Itoa(p.Z) + ")"
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add performs vector addition.
|
|
|
|
func (p Point3) Add(q Point3) Point3 {
|
|
|
|
return Point3{p.X + q.X, p.Y + q.Y, p.Z + q.Z}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sub performs vector subtraction.
|
|
|
|
func (p Point3) Sub(q Point3) Point3 {
|
|
|
|
return p.Add(q.Neg())
|
|
|
|
}
|
|
|
|
|
|
|
|
// CMul performs componentwise multiplication.
|
|
|
|
func (p Point3) CMul(q Point3) Point3 {
|
|
|
|
return Point3{p.X * q.X, p.Y * q.Y, p.Z * q.Z}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mul performs scalar multiplication.
|
|
|
|
func (p Point3) Mul(k int) Point3 {
|
|
|
|
return Point3{p.X * k, p.Y * k, p.Z * k}
|
|
|
|
}
|
|
|
|
|
|
|
|
// CDiv performs componentwise division.
|
|
|
|
func (p Point3) CDiv(q Point3) Point3 {
|
|
|
|
return Point3{p.X / q.X, p.Y / q.Y, p.Z / q.Z}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Div performs scalar division by k.
|
|
|
|
func (p Point3) Div(k int) Point3 {
|
|
|
|
return Point3{p.X / k, p.Y / k, p.Z / k}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Neg returns the vector pointing in the opposite direction.
|
|
|
|
func (p Point3) Neg() Point3 {
|
|
|
|
return Point3{-p.X, -p.Y, -p.Z}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Coord returns the components of the vector.
|
|
|
|
func (p Point3) Coord() (x, y, z int) {
|
|
|
|
return p.X, p.Y, p.Z
|
|
|
|
}
|
|
|
|
|
2021-08-31 15:16:56 +10:00
|
|
|
// Box describes an axis-aligned rectangular prism.
|
|
|
|
type Box struct {
|
2021-09-02 13:42:44 +10:00
|
|
|
Min, Max Point3
|
|
|
|
}
|
|
|
|
|
|
|
|
// String returns a string representation of b like "(3,4,5)-(6,5,8)".
|
|
|
|
func (b Box) String() string {
|
|
|
|
return b.Min.String() + "-" + b.Max.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Empty reports whether the box contains no points.
|
|
|
|
func (b Box) Empty() bool {
|
|
|
|
return b.Min.X >= b.Max.X || b.Min.Y >= b.Max.Y || b.Min.Z >= b.Max.Z
|
|
|
|
}
|
|
|
|
|
|
|
|
// Eq reports whether b and c contain the same set of points. All empty boxes
|
|
|
|
// are considered equal.
|
|
|
|
func (b Box) Eq(c Box) bool {
|
|
|
|
return b == c || b.Empty() && c.Empty()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Overlaps reports whether b and c have non-empty intersection.
|
|
|
|
func (b Box) Overlaps(c Box) bool {
|
|
|
|
return !b.Empty() && !c.Empty() &&
|
|
|
|
b.Min.X < c.Max.X && c.Min.X < b.Max.X &&
|
|
|
|
b.Min.Y < c.Max.Y && c.Min.Y < b.Max.Y &&
|
|
|
|
b.Min.Z < c.Max.Z && c.Min.Z < b.Max.Z
|
|
|
|
}
|
|
|
|
|
|
|
|
// Size returns b's width, height, and depth.
|
|
|
|
func (b Box) Size() Point3 {
|
|
|
|
return b.Max.Sub(b.Min)
|
2021-08-31 15:16:56 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
// IsoProjection translates an integer 3D coordinate into an integer 2D
|
|
|
|
// coordinate.
|
|
|
|
type IsoProjection struct {
|
|
|
|
ZX, ZY int
|
|
|
|
}
|
|
|
|
|
|
|
|
// Project projects a 3D coordinate into 2D.
|
|
|
|
// If ZX = 0, x is unchanged; similarly for ZY and y.
|
2021-09-01 15:57:02 +10:00
|
|
|
// Otherwise, x projects to x + z/ZX and y projects to y + z/ZY.
|
|
|
|
// Dividing is used because there's little reason for an isometric projection
|
2021-08-31 15:18:21 +10:00
|
|
|
// in a game to exaggerate the Z position, and integers are used to preserve
|
|
|
|
// "pixel perfect" calculation in case you are making the next Celeste.
|
2021-08-31 15:16:56 +10:00
|
|
|
func (π IsoProjection) Project(x, y, z int) (xp, yp int) {
|
2021-08-31 15:22:44 +10:00
|
|
|
// I'm using the π character because I'm a maths wanker
|
2021-08-31 15:16:56 +10:00
|
|
|
xp, yp = x, y
|
|
|
|
if π.ZX != 0 {
|
|
|
|
xp += z / π.ZX
|
|
|
|
}
|
|
|
|
if π.ZY != 0 {
|
|
|
|
yp += z / π.ZY
|
|
|
|
}
|
|
|
|
return xp, yp
|
|
|
|
}
|