π.Project -> geom.Project(π,

This commit is contained in:
Josh Deprez 2021-09-19 15:51:35 +10:00
parent c533ee63f7
commit ed78ef3d2e
6 changed files with 26 additions and 22 deletions

View file

@ -83,7 +83,7 @@ func (b *Billboard) String() string {
// Transform returns a translation by the projected position. // Transform returns a translation by the projected position.
func (b *Billboard) Transform() (opts ebiten.DrawImageOptions) { func (b *Billboard) Transform() (opts ebiten.DrawImageOptions) {
opts.GeoM.Translate(geom.CFloat( opts.GeoM.Translate(geom.CFloat(
b.game.Projection.Project(b.Pos), geom.Project(b.game.Projection, b.Pos),
)) ))
return opts return opts
} }

View file

@ -41,7 +41,7 @@ func (c *Camera) PointAt(centre geom.Int3, zoom float64) {
// Special sauce: if Child has a BoundingRect, make some adjustments // Special sauce: if Child has a BoundingRect, make some adjustments
bnd, ok := c.Child.(BoundingRecter) bnd, ok := c.Child.(BoundingRecter)
if !ok { if !ok {
c.Centre = c.game.Projection.Project(centre) c.Centre = geom.Project(c.game.Projection, centre)
c.Zoom = zoom c.Zoom = zoom
return return
} }
@ -63,7 +63,7 @@ func (c *Camera) PointAt(centre geom.Int3, zoom float64) {
// Camera frame currently Rectangle{ centre ± (screen/(2*zoom)) }. // Camera frame currently Rectangle{ centre ± (screen/(2*zoom)) }.
sw2, sh2 := geom.CFloat(c.game.ScreenSize.Div(2)) sw2, sh2 := geom.CFloat(c.game.ScreenSize.Div(2))
swz, shz := int(sw2/zoom), int(sh2/zoom) swz, shz := int(sw2/zoom), int(sh2/zoom)
cent := c.game.Projection.Project(centre) cent := geom.Project(c.game.Projection, centre)
if cent.X-swz < br.Min.X { if cent.X-swz < br.Min.X {
cent.X = br.Min.X + swz cent.X = br.Min.X + swz
} }

View file

@ -212,7 +212,7 @@ func (p *Prism) String() string {
// Transform returns a translation by the projected position. // Transform returns a translation by the projected position.
func (p *Prism) Transform() (opts ebiten.DrawImageOptions) { func (p *Prism) Transform() (opts ebiten.DrawImageOptions) {
opts.GeoM.Translate(geom.CFloat( opts.GeoM.Translate(geom.CFloat(
p.m.game.Projection.Project(p.pos), geom.Project(p.m.game.Projection, p.pos),
)) ))
return opts return opts
} }

View file

@ -67,7 +67,8 @@ func (s *Sprite) Transform() (opts ebiten.DrawImageOptions) {
// Reaching into Actor for a reference to Game so I don't have to // Reaching into Actor for a reference to Game so I don't have to
// implement Prepare in this file, but writing this long comment // implement Prepare in this file, but writing this long comment
// providing exposition... // providing exposition...
s.Actor.game.Projection.Project(s.Actor.Pos).Add(s.DrawOffset), geom.Project(s.Actor.game.Projection, s.Actor.Pos).
Add(s.DrawOffset),
)) ))
return opts return opts
} }

View file

@ -80,7 +80,7 @@ func (b Box) BoundingRect(π Projector) image.Rectangle {
// Back returns an image.Rectangle representing the back of the box, using // Back returns an image.Rectangle representing the back of the box, using
// the given projection π. // the given projection π.
func (b Box) Back(π Projector) image.Rectangle { func (b Box) Back(π Projector) image.Rectangle {
p := π.Project(Int3{0, 0, b.Min.Z}) p := π.Project(b.Min.Z)
return image.Rectangle{ return image.Rectangle{
Min: b.Min.XY().Add(p), Min: b.Min.XY().Add(p),
Max: b.Max.XY().Add(p), Max: b.Max.XY().Add(p),
@ -90,7 +90,7 @@ func (b Box) Back(π Projector) image.Rectangle {
// Front returns an image.Rectangle representing the front of the box, using // Front returns an image.Rectangle representing the front of the box, using
// the given projection π. // the given projection π.
func (b Box) Front(π Projector) image.Rectangle { func (b Box) Front(π Projector) image.Rectangle {
p := π.Project(Int3{0, 0, b.Max.Z}) p := π.Project(b.Max.Z)
return image.Rectangle{ return image.Rectangle{
Min: b.Min.XY().Add(p), Min: b.Min.XY().Add(p),
Max: b.Max.XY().Add(p), Max: b.Max.XY().Add(p),

View file

@ -7,8 +7,13 @@ type Projector interface {
// Sign returns a {-1, 0, 1}-valued 2D vector pointing in the direction that // Sign returns a {-1, 0, 1}-valued 2D vector pointing in the direction that
// positive Z values are projected to. // positive Z values are projected to.
Sign() image.Point Sign() image.Point
// Project projects a 3D point into 2D. // Project projects a Z coordinate into 2D offset.
Project(Int3) image.Point 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())
} }
// Projection uses floats to define a projection. // Projection uses floats to define a projection.
@ -18,26 +23,24 @@ func (π Projection) Sign() (s image.Point) {
return image.Pt(int(FSign(π.X)), int(FSign(π.Y))) return image.Pt(int(FSign(π.X)), int(FSign(π.Y)))
} }
// Project performs a parallel projection of a 3D coordiante into 2D. // Project returns (z*π.X, z*π.Y).
// x projects to (x + z*π.X), and y to (y + z*π.Y) func (π Projection) Project(z int) image.Point {
func (π Projection) Project(p Int3) image.Point {
return image.Pt( return image.Pt(
p.X+int(π.X*float64(p.Z)), int(π.X*float64(z)),
p.Y+int(π.Y*float64(p.Z)), int(π.Y*float64(z)),
) )
} }
// IntProjection holds an integer projection definition. // IntProjection holds an integer projection definition.
// It is designed for projecting Z onto X and Y with integer fractions as would // It is designed for projecting Z onto X and Y with integer fractions as would
// be used in e.g. a diametric projection (IntProjection{X:0, Y:-2}). // be used in e.g. a diametric projection (IntProjection{X:0, Y:2}).
type IntProjection image.Point type IntProjection image.Point
func (π IntProjection) Sign() image.Point { return image.Point(π) } func (π IntProjection) Sign() image.Point { return image.Point(π) }
// Project performs an integer parallel projection of a 3D coordinate into 2D. // Project returns (z/π.X, z/π.Y), unless π.X or π.Y are 0, in which case that
// If π.X = 0, the x returned is p.X; similarly for π.Y and y. // component is zero
// Otherwise, x projects to x + z/π.X and y projects to y + z/π.Y. func (π IntProjection) Project(z int) image.Point {
func (π IntProjection) Project(p Int3) image.Point {
/* /*
Dividing is used because there's little reason for an isometric Dividing is used because there's little reason for an isometric
projection in a game to exaggerate the Z position. projection in a game to exaggerate the Z position.
@ -45,12 +48,12 @@ func (π IntProjection) Project(p Int3) image.Point {
Integers are used to preserve "pixel perfect" calculation in case you Integers are used to preserve "pixel perfect" calculation in case you
are making the next Celeste. are making the next Celeste.
*/ */
q := p.XY() var q image.Point
if π.X != 0 { if π.X != 0 {
q.X += p.Z / π.X q.X = z / π.X
} }
if π.Y != 0 { if π.Y != 0 {
q.Y += p.Z / π.Y q.Y = z / π.Y
} }
return q return q
} }