diff --git a/spatial/r2/vector.go b/spatial/r2/vector.go index b7b9cd6d..3419ed55 100644 --- a/spatial/r2/vector.go +++ b/spatial/r2/vector.go @@ -30,6 +30,11 @@ func (p Vec) Scale(f float64) Vec { return p } +// Dot returns the dot product p·q. +func (p Vec) Dot(q Vec) float64 { + return p.X*q.X + p.Y*q.Y +} + // Box is a 2D bounding box. type Box struct { Min, Max Vec diff --git a/spatial/r2/vector_test.go b/spatial/r2/vector_test.go index 09081218..3ccc2c7e 100644 --- a/spatial/r2/vector_test.go +++ b/spatial/r2/vector_test.go @@ -77,3 +77,38 @@ func TestScale(t *testing.T) { }) } } + +func TestDot(t *testing.T) { + for _, test := range []struct { + u, v Vec + want float64 + }{ + {Vec{1, 2}, Vec{1, 2}, 5}, + {Vec{1, 0}, Vec{1, 0}, 1}, + {Vec{1, 0}, Vec{0, 1}, 0}, + {Vec{1, 0}, Vec{0, 1}, 0}, + {Vec{1, 1}, Vec{-1, -1}, -2}, + {Vec{1, 2}, Vec{-0.3, 0.4}, 0.5}, + } { + t.Run("", func(t *testing.T) { + { + got := test.u.Dot(test.v) + if got != test.want { + t.Fatalf( + "error: %v · %v: got=%v, want=%v", + test.u, test.v, got, test.want, + ) + } + } + { + got := test.v.Dot(test.u) + if got != test.want { + t.Fatalf( + "error: %v · %v: got=%v, want=%v", + test.v, test.u, got, test.want, + ) + } + } + }) + } +}