Position supports current coordinate types. (#576)

This commit is contained in:
danhale-git 2020-07-11 23:11:26 +01:00 committed by GitHub
parent 3aab0515cf
commit fa0814e0b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 123 additions and 21 deletions

View File

@ -13,13 +13,21 @@ type Position struct {
}
// NewPosition creates a new Position at the given float64 world position.
func NewPosition(x, y float64) *Position {
p := &Position{NewVector(x, y)}
func NewPosition(x, y float64) Position {
p := Position{NewVector(x, y)}
p.checkValues()
return p
}
// EntityPosition returns a Position struct based on the given entity spawn point.
// The value given should be the one set in d2mapstamp.Stamp.Entities:
// (tileOffsetX*5)+object.X, (tileOffsetY*5)+object.Y
// TODO: This probably doesn't support positions in between sub tiles so will only be suitable for spawning entities from map generation, not for multiplayer syncing.
func EntityPosition(x, y int) Position {
return NewPosition(float64(x)/5, float64(y)/5)
}
// Set sets this position to the given x and y values.
func (p *Position) Set(x, y float64) {
p.x, p.y = x, y
@ -37,36 +45,47 @@ func (p *Position) checkValues() {
}
// World is the position, where 1 = one map tile.
// unused
func (p *Position) World() *Vector {
return &p.Vector
}
// Tile is the tile position, always a whole number.
// Tile is the tile position, always a whole number. (tileX, tileY)
func (p *Position) Tile() *Vector {
c := p.World().Clone()
return c.Floor()
}
// TileOffset is the offset from the tile position, always < 1.
// unused
func (p *Position) TileOffset() *Vector {
c := p.World().Clone()
return c.Subtract(p.Tile())
}
// SubWorld is the position, where 5 = one map tile.
// SubWorld is the position, where 5 = one map tile. (locationX, locationY)
func (p *Position) SubWorld() *Vector {
c := p.World().Clone()
return c.Scale(subTilesPerTile)
}
// SubTile is the tile position in sub tiles, always a multiple of 5.
// unused
func (p *Position) SubTile() *Vector {
c := p.Tile().Clone()
return c.Scale(subTilesPerTile)
return p.Tile().Scale(subTilesPerTile)
}
// SubTileOffset is the offset from the sub tile position in sub tiles, always < 1.
// unused
func (p *Position) SubTileOffset() *Vector {
c := p.SubWorld().Clone()
return c.Subtract(p.SubTile())
return p.SubWorld().Subtract(p.SubTile())
}
// TODO: understand this and maybe improve/remove it
// SubTileOffset() + 1. It's used for rendering. It seems to always do this:
// v.offsetX+int((v.subcellX-v.subcellY)*16),
// v.offsetY+int(((v.subcellX+v.subcellY)*8)-5),
// ^ Maybe similar to Viewport.OrthoToWorld? (subCellX, subCellY)
func (p *Position) SubCell() *Vector {
return p.SubTileOffset().AddScalar(1)
}

View File

@ -1,12 +1,49 @@
package d2vector
import (
"math"
"math/rand"
"testing"
)
func validate(t *testing.T, original, got, want, unchanged Vector) {
func TestEntityPosition(t *testing.T) {
x, y := rand.Intn(1000), rand.Intn(1000)
pos := EntityPosition(x, y)
locX, locY := float64(x), float64(y)
// old coordinate values Position equivalent
locationX := locX // .SubWord().X()
locationY := locY // .SubWord().Y()
tileX := float64(x / 5) // .Tile().X()
tileY := float64(y / 5) // .Tile().Y()
subcellX := 1 + math.Mod(locX, 5) // .SubCell().X()
subcellY := 1 + math.Mod(locY, 5) // .SubCell().Y()
want := NewVector(tileX, tileY)
got := pos.Tile()
if !got.Equals(want) {
t.Errorf("world position should match old value: got %s: want %s", got, want)
}
want = NewVector(subcellX, subcellY)
got = pos.SubCell()
if !got.Equals(want) {
t.Errorf("sub cell position should match old value: got %s: want %s", got, want)
}
want = NewVector(locationX, locationY)
got = pos.SubWorld()
if !got.Equals(want) {
t.Errorf("sub tile position should match old value: got %s: want %s", got, want)
}
}
func validate(description string, t *testing.T, original, got, want, unchanged Vector) {
if !got.EqualsApprox(want) {
t.Errorf("want %s: got %s", want, got)
t.Errorf("%s: want %s: got %s", description, want, got)
}
if !original.EqualsApprox(unchanged) {
@ -20,7 +57,7 @@ func TestTile(t *testing.T) {
want := NewVector(1, 1)
unchanged := NewVector(1.6, 1.6)
validate(t, p.Vector, *got, want, unchanged)
validate("tile position", t, p.Vector, *got, want, unchanged)
}
func TestTileOffset(t *testing.T) {
@ -29,7 +66,7 @@ func TestTileOffset(t *testing.T) {
want := NewVector(0.6, 0.6)
unchanged := NewVector(1.6, 1.6)
validate(t, p.Vector, *got, want, unchanged)
validate("tile offset", t, p.Vector, *got, want, unchanged)
}
func TestSubWorld(t *testing.T) {
@ -38,7 +75,7 @@ func TestSubWorld(t *testing.T) {
want := NewVector(5, 5)
unchanged := NewVector(1, 1)
validate(t, p.Vector, *got, want, unchanged)
validate("sub tile world position", t, p.Vector, *got, want, unchanged)
}
func TestSubTile(t *testing.T) {
@ -47,7 +84,7 @@ func TestSubTile(t *testing.T) {
want := NewVector(5, 5)
unchanged := NewVector(1, 1)
validate(t, p.Vector, *got, want, unchanged)
validate("sub tile with offset", t, p.Vector, *got, want, unchanged)
}
func TestSubTileOffset(t *testing.T) {
@ -56,5 +93,5 @@ func TestSubTileOffset(t *testing.T) {
want := NewVector(0.5, 0.5)
unchanged := NewVector(1.1, 1.1)
validate(t, p.Vector, *got, want, unchanged)
validate("offset from sub tile", t, p.Vector, *got, want, unchanged)
}

View File

@ -20,6 +20,16 @@ func NewVector(x, y float64) Vector {
return Vector{x, y}
}
// X returns the x value of this vector.
func (v *Vector) X() float64 {
return v.x
}
// Y returns the y value of this vector.
func (v *Vector) Y() float64 {
return v.y
}
// Equals returns true if the float64 values of this vector are exactly equal to the given Vector.
func (v *Vector) Equals(o Vector) bool {
return v.x == o.x && v.y == o.y
@ -85,6 +95,14 @@ func (v *Vector) Add(o *Vector) *Vector {
return v
}
// AddScalar the given vector to this vector.
func (v *Vector) AddScalar(s float64) *Vector {
v.x += s
v.y += s
return v
}
// Subtract the given vector from this vector.
func (v *Vector) Subtract(o *Vector) *Vector {
v.x -= o.x
@ -117,6 +135,14 @@ func (v *Vector) Divide(o *Vector) *Vector {
return v
}
// DivideScalar divides this vector by the given float64 value.
func (v *Vector) DivideScalar(s float64) *Vector {
v.x /= s
v.y /= s
return v
}
// Abs sets the vector to it's absolute (positive) equivalent.
func (v *Vector) Abs() *Vector {
xm, ym := 1.0, 1.0

View File

@ -154,15 +154,25 @@ func TestClamp(t *testing.T) {
}
func TestAdd(t *testing.T) {
v := NewVector(1, 1)
add := NewVector(0.5, 0.5)
want := NewVector(1.5, 1.5)
v := NewVector(1, 2)
add := NewVector(0.5, 3)
want := NewVector(1.5, 5)
got := v.Clone()
got.Add(&add)
evaluateVector(fmt.Sprintf("add %s to %s", add, v), want, got, t)
}
func TestAddScalar(t *testing.T) {
v := NewVector(1, -1)
add := 0.5
want := NewVector(1.5, -0.5)
got := v.Clone()
got.AddScalar(add)
evaluateVector(fmt.Sprintf("add %.2f to %s", add, v), want, got, t)
}
func TestSubtract(t *testing.T) {
v := NewVector(1, 1)
subtract := NewVector(0.6, 0.6)
@ -184,15 +194,25 @@ func TestMultiply(t *testing.T) {
}
func TestDivide(t *testing.T) {
v := NewVector(1, 1)
divide := NewVector(2, 2)
want := NewVector(0.5, 0.5)
v := NewVector(1, 8)
divide := NewVector(2, 4)
want := NewVector(0.5, 2)
got := v.Clone()
got.Divide(&divide)
evaluateVector(fmt.Sprintf("divide %s by %s", v, divide), want, got, t)
}
func TestDivideScalar(t *testing.T) {
v := NewVector(1, 2)
divide := 2.0
want := NewVector(0.5, 1.0)
got := v.Clone()
got.DivideScalar(divide)
evaluateVector(fmt.Sprintf("divide %s by %.2f", v, divide), want, got, t)
}
func TestScale(t *testing.T) {
v := NewVector(2, 3)
want := NewVector(4, 6)