2021-09-08 20:08:57 +10:00
|
|
|
package geom
|
2021-09-05 17:22:46 +10:00
|
|
|
|
2021-09-07 13:14:42 +10:00
|
|
|
import "image"
|
2021-09-05 17:22:46 +10:00
|
|
|
|
2021-09-23 10:25:01 +10:00
|
|
|
// Projector types can be used to project 3D coordinates into 2D. It only
|
|
|
|
// supports projecting Z into a 2D offset (i.e. not a general projection).
|
2021-09-17 16:47:18 +10:00
|
|
|
type Projector interface {
|
|
|
|
// Sign returns a {-1, 0, 1}-valued 2D vector pointing in the direction that
|
|
|
|
// positive Z values are projected to.
|
|
|
|
Sign() image.Point
|
2021-09-23 10:25:01 +10:00
|
|
|
// Project converts a Z coordinate to a 2D offset.
|
2021-09-19 15:51:35 +10:00
|
|
|
Project(int) image.Point
|
|
|
|
}
|
|
|
|
|
|
|
|
// Project is shorthand for π.Project(p.Z).Add(p.XY()).
|
|
|
|
func Project(π Projector, p Int3) image.Point {
|
|
|
|
return π.Project(p.Z).Add(p.XY())
|
2021-09-17 16:47:18 +10:00
|
|
|
}
|
|
|
|
|
2021-09-23 10:13:05 +10:00
|
|
|
// ElevationProjection throws away Z.
|
|
|
|
type ElevationProjection struct{}
|
|
|
|
|
|
|
|
// Sign returns the zero point.
|
|
|
|
func (ElevationProjection) Sign() image.Point { return image.Point{} }
|
|
|
|
|
|
|
|
// Project returns the zero point.
|
|
|
|
func (ElevationProjection) Project(int) image.Point { return image.Point{} }
|
|
|
|
|
|
|
|
// SimpleProjection projects Z onto Y only.
|
|
|
|
type SimpleProjection struct{}
|
|
|
|
|
|
|
|
// Sign returns (0, 1).
|
|
|
|
func (SimpleProjection) Sign() image.Point { return image.Pt(0, 1) }
|
|
|
|
|
|
|
|
// Project returns (0, z).
|
|
|
|
func (SimpleProjection) Project(z int) image.Point { return image.Pt(0, z) }
|
|
|
|
|
|
|
|
// Projection uses two floats to define a custom projection.
|
2021-09-16 10:28:58 +10:00
|
|
|
type Projection struct{ X, Y float64 }
|
|
|
|
|
2021-09-23 10:13:05 +10:00
|
|
|
func (π Projection) Sign() image.Point {
|
2021-09-17 16:47:18 +10:00
|
|
|
return image.Pt(int(FSign(π.X)), int(FSign(π.Y)))
|
|
|
|
}
|
|
|
|
|
2021-09-19 15:51:35 +10:00
|
|
|
// Project returns (z*π.X, z*π.Y).
|
|
|
|
func (π Projection) Project(z int) image.Point {
|
2021-09-16 10:28:58 +10:00
|
|
|
return image.Pt(
|
2021-09-19 15:51:35 +10:00
|
|
|
int(π.X*float64(z)),
|
|
|
|
int(π.Y*float64(z)),
|
2021-09-16 10:28:58 +10:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-09-23 10:13:05 +10:00
|
|
|
// IntProjection uses two integers to define a custom projection.
|
2021-09-07 14:00:50 +10:00
|
|
|
// It is designed for projecting Z onto X and Y with integer fractions as would
|
2021-09-19 15:51:35 +10:00
|
|
|
// be used in e.g. a diametric projection (IntProjection{X:0, Y:2}).
|
2021-09-07 13:14:42 +10:00
|
|
|
type IntProjection image.Point
|
2021-09-05 17:22:46 +10:00
|
|
|
|
2021-09-17 16:47:18 +10:00
|
|
|
func (π IntProjection) Sign() image.Point { return image.Point(π) }
|
|
|
|
|
2021-09-19 15:51:35 +10:00
|
|
|
// Project returns (z/π.X, z/π.Y), unless π.X or π.Y are 0, in which case that
|
|
|
|
// component is zero
|
|
|
|
func (π IntProjection) Project(z int) image.Point {
|
2021-09-07 13:14:42 +10:00
|
|
|
/*
|
|
|
|
Dividing is used because there's little reason for an isometric
|
|
|
|
projection in a game to exaggerate the Z position.
|
2021-09-05 17:22:46 +10:00
|
|
|
|
2021-09-09 19:11:25 +10:00
|
|
|
Integers are used to preserve "pixel perfect" calculation in case you
|
|
|
|
are making the next Celeste.
|
2021-09-07 13:14:42 +10:00
|
|
|
*/
|
2021-09-19 15:51:35 +10:00
|
|
|
var q image.Point
|
2021-09-07 13:14:42 +10:00
|
|
|
if π.X != 0 {
|
2021-09-19 15:51:35 +10:00
|
|
|
q.X = z / π.X
|
2021-09-07 13:14:42 +10:00
|
|
|
}
|
|
|
|
if π.Y != 0 {
|
2021-09-19 15:51:35 +10:00
|
|
|
q.Y = z / π.Y
|
2021-09-07 13:14:42 +10:00
|
|
|
}
|
|
|
|
return q
|
2021-09-05 17:22:46 +10:00
|
|
|
}
|