spatial/r{2,3}: add Norm and Norm2
MODIFY
spatial/r2/vector.go
spatial/r2/vector.go
package r2
+import "math"
+
// Vec is a 2D vector.
type Vec struct {
X, Y float64
@@ -40,6 +42,18 @@ func (p Vec) Cross(q Vec) float64 {
return p.X*q.Y - p.Y*q.X
}
+// Norm returns the Euclidean norm of p
+// |p| = sqrt(p_x^2 + p_y^2).
+func Norm(p Vec) float64 {
+ return math.Hypot(p.X, p.Y)
+}
+
+// Norm returns the Euclidean squared norm of p
+// |p|^2 = p_x^2 + p_y^2.
+func Norm2(p Vec) float64 {
+ return p.X*p.X + p.Y*p.Y
+}
+
// Box is a 2D bounding box.
type Box struct {
Min, Max Vec

MODIFY
spatial/r2/vector_test.go
spatial/r2/vector_test.go
package r2
-import "testing"
+import (
+ "math"
+ "testing"
+)
func TestAdd(t *testing.T) {
for _, test := range []struct {
@@ -135,3 +138,46 @@ func TestCross(t *testing.T) {
})
}
}
+
+func TestNorm(t *testing.T) {
+ for _, test := range []struct {
+ v Vec
+ want float64
+ }{
+ {Vec{0, 0}, 0},
+ {Vec{0, 1}, 1},
+ {Vec{1, 1}, math.Sqrt2},
+ {Vec{1, 2}, math.Sqrt(5)},
+ {Vec{3, -4}, 5},
+ {Vec{1, 1e-16}, 1},
+ {Vec{4.3145006366056343748277397783556100978621924913975e-196, 4.3145006366056343748277397783556100978621924913975e-196}, 6.101625315155041e-196},
+ } {
+ t.Run("", func(t *testing.T) {
+ if got, want := Norm(test.v), test.want; got != want {
+ t.Fatalf("|%v| = %v, want %v", test.v, got, want)
+ }
+ })
+ }
+}
+
+func TestNorm2(t *testing.T) {
+ for _, test := range []struct {
+ v Vec
+ want float64
+ }{
+ {Vec{0, 0}, 0},
+ {Vec{0, 1}, 1},
+ {Vec{1, 1}, 2},
+ {Vec{1, 2}, 5},
+ {Vec{3, -4}, 25},
+ {Vec{1, 1e-16}, 1},
+ // This will underflow and return zero.
+ {Vec{4.3145006366056343748277397783556100978621924913975e-196, 4.3145006366056343748277397783556100978621924913975e-196}, 0},
+ } {
+ t.Run("", func(t *testing.T) {
+ if got, want := Norm2(test.v), test.want; got != want {
+ t.Fatalf("|%v|^2 = %v, want %v", test.v, got, want)
+ }
+ })
+ }
+}

MODIFY
spatial/r3/vector.go
spatial/r3/vector.go
package r3
+import "math"
+
// Vec is a 3D vector.
type Vec struct {
X, Y, Z float64
@@ -47,6 +49,18 @@ func (p Vec) Cross(q Vec) Vec {
}
}
+// Norm returns the Euclidean norm of p
+// |p| = sqrt(p_x^2 + p_y^2 + p_z^2).
+func Norm(p Vec) float64 {
+ return math.Hypot(p.X, math.Hypot(p.Y, p.Z))
+}
+
+// Norm returns the Euclidean squared norm of p
+// |p|^2 = p_x^2 + p_y^2 + p_z^2.
+func Norm2(p Vec) float64 {
+ return p.X*p.X + p.Y*p.Y + p.Z*p.Z
+}
+
// Box is a 3D bounding box.
type Box struct {
Min, Max Vec

MODIFY
spatial/r3/vector_test.go
spatial/r3/vector_test.go
@@ -135,3 +135,44 @@ func TestCross(t *testing.T) {
})
}
}
+
+func TestNorm(t *testing.T) {
+ for _, test := range []struct {
+ v Vec
+ want float64
+ }{
+ {Vec{0, 0, 0}, 0},
+ {Vec{0, 1, 0}, 1},
+ {Vec{3, -4, 12}, 13},
+ {Vec{1, 1e-16, 1e-32}, 1},
+ {Vec{-0, 4.3145006366056343748277397783556100978621924913975e-196, 4.3145006366056343748277397783556100978621924913975e-196}, 6.101625315155041e-196},
+ } {
+ t.Run("", func(t *testing.T) {
+ if got, want := Norm(test.v), test.want; got != want {
+ t.Fatalf("|%v| = %v, want %v", test.v, got, want)
+ }
+ })
+ }
+}
+
+func TestNorm2(t *testing.T) {
+ for _, test := range []struct {
+ v Vec
+ want float64
+ }{
+ {Vec{0, 0, 0}, 0},
+ {Vec{0, 1, 0}, 1},
+ {Vec{1, 1, 1}, 3},
+ {Vec{1, 2, 3}, 14},
+ {Vec{3, -4, 12}, 169},
+ {Vec{1, 1e-16, 1e-32}, 1},
+ // This will underflow and return zero.
+ {Vec{-0, 4.3145006366056343748277397783556100978621924913975e-196, 4.3145006366056343748277397783556100978621924913975e-196}, 0},
+ } {
+ t.Run("", func(t *testing.T) {
+ if got, want := Norm2(test.v), test.want; got != want {
+ t.Fatalf("|%v|^2 = %v, want %v", test.v, got, want)
+ }
+ })
+ }
+}

spatial/r{2,3}: add Norm and Norm2