From 07d90e9681fb7c0165e630a004f7b558708b0df9 Mon Sep 17 00:00:00 2001 From: danhale-git <36298392+danhale-git@users.noreply.github.com> Date: Sun, 5 Jul 2020 00:25:53 +0100 Subject: [PATCH] Position struct for managing world coordinates (#540) * Fixed nil pointer in Copy() * Position added Added Floor() and String() methods to Vector. Also added Position which declares an embedded Vector2 and returns various forms of it. * Position tests improved --- d2common/d2interface/vector.go | 2 + d2common/d2math/d2vector/position.go | 56 ++++++++++++++++++++ d2common/d2math/d2vector/position_test.go | 62 +++++++++++++++++++++++ d2common/d2math/d2vector/vector.go | 30 ++++++++--- d2common/d2math/d2vector/vector_test.go | 33 ++++++++++++ 5 files changed, 176 insertions(+), 7 deletions(-) create mode 100644 d2common/d2math/d2vector/position.go create mode 100644 d2common/d2math/d2vector/position_test.go create mode 100644 d2common/d2math/d2vector/vector_test.go diff --git a/d2common/d2interface/vector.go b/d2common/d2interface/vector.go index 574c2e8d..8eb965f6 100644 --- a/d2common/d2interface/vector.go +++ b/d2common/d2interface/vector.go @@ -40,4 +40,6 @@ type Vector interface { Reflect(normal Vector) Vector Mirror(axis Vector) Vector Rotate(delta *big.Float) Vector + Floor() Vector + String() string } diff --git a/d2common/d2math/d2vector/position.go b/d2common/d2math/d2vector/position.go new file mode 100644 index 00000000..ce3d6fde --- /dev/null +++ b/d2common/d2math/d2vector/position.go @@ -0,0 +1,56 @@ +package d2vector + +import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" + +// Position is a vector in world space. The stored +// value is the one returned by Position.World() +type Position struct { + d2interface.Vector +} + +// NewPosition creates a new Position at the given +// float64 world position. +func NewPosition(x, y float64) *Position { + return &Position{New(x, y)} +} + +// World is the position, where 1 = one map +// tile. +func (p *Position) World() d2interface.Vector { + return p.Vector +} + +// Tile is the tile position, always a whole +// number. +func (p *Position) Tile() d2interface.Vector { + c := p.World().Clone() + return c.Floor() +} + +// TileOffset is the offset from the tile position, +// always < 1. +func (p *Position) TileOffset() d2interface.Vector { + c := p.World().Clone() + return c.Subtract(p.Tile()) +} + +// SubWorld is the position, where 5 = one map +// tile. +func (p *Position) SubWorld() d2interface.Vector { + c := p.World().Clone() + return c.Multiply(New(5, 5)) +} + +// SubTile is the tile position in sub tiles, +// always a multiple of 5. +func (p *Position) SubTile() d2interface.Vector { + c := p.Tile().Clone() + return c.Multiply(New(5, 5)) +} + +// SubTileOffset is the offset from the sub tile +// position in sub tiles, always < 1. +func (p *Position) SubTileOffset() d2interface.Vector { + c := p.SubWorld().Clone() + return c.Subtract(p.SubTile()) +} diff --git a/d2common/d2math/d2vector/position_test.go b/d2common/d2math/d2vector/position_test.go new file mode 100644 index 00000000..3e55de2f --- /dev/null +++ b/d2common/d2math/d2vector/position_test.go @@ -0,0 +1,62 @@ +package d2vector + +import ( + "testing" + + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" +) + +func validate(t *testing.T, original, got, want, unchanged d2interface.Vector) { + if !got.FuzzyEquals(want) { + t.Errorf("want %s: got %s", want, got) + } + + if !original.FuzzyEquals(unchanged) { + t.Errorf("Position value %s was incorrectly changed to %s when calling this method", unchanged, original) + } +} + +func TestTile(t *testing.T) { + p := NewPosition(1.6, 1.6) + got := p.Tile() + want := New(1, 1) + unchanged := New(1.6, 1.6) + + validate(t, p, got, want, unchanged) +} + +func TestTileOffset(t *testing.T) { + p := NewPosition(1.6, 1.6) + got := p.TileOffset() + want := New(0.6, 0.6) + unchanged := New(1.6, 1.6) + + validate(t, p, got, want, unchanged) +} + +func TestSubWorld(t *testing.T) { + p := NewPosition(1, 1) + got := p.SubWorld() + want := New(5, 5) + unchanged := New(1, 1) + + validate(t, p, got, want, unchanged) +} + +func TestSubTile(t *testing.T) { + p := NewPosition(1, 1) + got := p.SubTile() + want := New(5, 5) + unchanged := New(1, 1) + + validate(t, p, got, want, unchanged) +} + +func TestSubTileOffset(t *testing.T) { + p := NewPosition(1.1, 1.1) + got := p.SubTileOffset() + want := New(0.5, 0.5) + unchanged := New(1.1, 1.1) + + validate(t, p, got, want, unchanged) +} diff --git a/d2common/d2math/d2vector/vector.go b/d2common/d2math/d2vector/vector.go index 25ce9af7..42efd8da 100644 --- a/d2common/d2math/d2vector/vector.go +++ b/d2common/d2math/d2vector/vector.go @@ -2,6 +2,7 @@ package d2vector import ( + "fmt" "math" "math/big" @@ -64,7 +65,7 @@ func (v *Vector2) Unmarshal(buf []byte) error { // Clone creates a copy of this Vector func (v *Vector2) Clone() d2interface.Vector { - result := &Vector2{} + result := New(0, 0) result.Copy(v) return result @@ -359,32 +360,47 @@ func (v *Vector2) Rotate(angle *big.Float) d2interface.Vector { return v } +// Floor rounds the vector down to the nearest whole numbers. +func (v *Vector2) Floor() d2interface.Vector { + var xi, yi big.Int + v.x.Int(&xi) + v.y.Int(&yi) + v.X().SetInt(&xi) + v.Y().SetInt(&yi) + + return v +} + +func (v *Vector2) String() string { + return fmt.Sprintf("Vector2{%s, %s}", v.x.Text('f', 5), v.y.Text('f', 5)) +} + // Up returns a new vector (0, 1) -func Up() *Vector2 { +func Up() d2interface.Vector { return New(0, 1) } // Down returns a new vector (0, -1) -func Down() *Vector2 { +func Down() d2interface.Vector { return New(0, -1) } // Right returns a new vector (1, 0) -func Right() *Vector2 { +func Right() d2interface.Vector { return New(1, 0) } // Left returns a new vector (-1, 0) -func Left() *Vector2 { +func Left() d2interface.Vector { return New(-1, 0) } // One returns a new vector (1, 1) -func One() *Vector2 { +func One() d2interface.Vector { return New(1, 1) } // Zero returns a new vector (0, 0) -func Zero() *Vector2 { +func Zero() d2interface.Vector { return New(0, 0) } diff --git a/d2common/d2math/d2vector/vector_test.go b/d2common/d2math/d2vector/vector_test.go new file mode 100644 index 00000000..f370dd6d --- /dev/null +++ b/d2common/d2math/d2vector/vector_test.go @@ -0,0 +1,33 @@ +package d2vector + +import "testing" + +func TestClone(t *testing.T) { + v := New(1, 1) + want := New(1, 1) + got := v.Clone() + + if !got.Equals(want) { + t.Errorf("wanted %s: got %s", want, got) + } +} + +func TestAbs(t *testing.T) { + v := New(-1, -1) + want := New(1, 1) + got := v.Abs() + + if !got.Equals(want) { + t.Errorf("wanted %s: got %s", want, got) + } +} + +func TestFloor(t *testing.T) { + v := New(1.6, 1.6) + + want := New(1, 1) + + if !v.Floor().Equals(want) { + t.Errorf("want %s: got %s", want, v) + } +}