Example #1
0
// Magnitude does the classic calculation for length of a vector
// (or, distance from origin)
func Magnitude(v Vector) (float64, error) {
	if len(v) == 0 {
		return 0, errors.New("v is an empty vector")
	}
	var ǁvǁsq float64
	for i := 0; i < len(v); i++ {
		ǁvǁsq += v[i] * v[i]
	}
	return calc.ToFixed(math.Sqrt(ǁvǁsq), 5), nil
}
Example #2
0
// AngleToIntercept calculates the change in angle required from the current heading to point in direction of target.
func AngleToIntercept(pos Vector, dir𝚹 float64, target Vector) (float64, error) {
	// angle between pos and target:
	Φ, err := RelativeAngle(pos, target)
	if err != nil {
		return 0, err
	}
	// angle between unit vector (dir) and Φ:
	Ψ := Φ - dir𝚹
	if Ψ < -math.Pi {
		Ψ += 2 * math.Pi
	}
	Ψ = calc.ToFixed(Ψ, 5)
	return Ψ, nil
}
Example #3
0
// RelativeAngle – does what it says on the box.
// Only implemented in 2D currently, or, as a comparitive rotation between two points on a single plane.
func RelativeAngle(v Vector, u Vector) (float64, error) {
	if len(v) == 0 || len(u) == 0 {
		return 0, errors.New("v or u is an empty vector")
	}
	if len(v) != len(u) {
		return 0, errors.New("vector dimensions do not coincide")
	}

	dx := u[x] - v[x]
	dy := u[y] - v[y]

	Φ := math.Atan2(dy, dx)
	return calc.ToFixed(Φ, 5), nil
}
Example #4
0
// Normalise returns the normalised Vector of v
// – it's what you might call a direction vector,
// as opposed to a position vector.
// NOTE: this is not the same as a 'norm'/'normal' which is the vector
// orthogonal to a plane or surface.
func Normalise(v Vector) (Vector, error) {
	if len(v) == 0 {
		return nil, errors.New("v is an empty vector")
	}
	var norm Vector
	var ǁvǁ float64
	var err error
	for i := 0; i < len(v); i++ {
		ǁvǁ, err = Magnitude(v)
		if err != nil {
			return nil, err
		}
		norm = append(norm, calc.ToFixed(v[i]/ǁvǁ, 5))
	}
	return norm, nil
}
func TestAngleToIntercept(t *testing.T) {
	// baseline initial test:
	samplePos := Vector{0, 0}
	sampleHeading := calc.ToFixed(3*math.Pi/4, 5) //  pointing towards (-1,1), halfway between 𝛑/2 and 𝛑 radians
	sampleTarget := Vector{1, -1}
	// result should be how MUCH we have to turn one way or another:
	Ψ, _ := AngleToIntercept(samplePos, sampleHeading, sampleTarget)
	// we disregard error value from AngleToIntercept as the only possible error would be from mismatched Vector lengths or Vectors of length != 2 which do not apply here.
	want := -3.14159
	if Ψ != want {
		t.Errorf("AngleToIntercept(%v, %v, %v) == %v, want: %v", samplePos, sampleHeading, sampleTarget, Ψ, want)
	}

	sampleTarget = Vector{-1, -1} // angle of -3𝛑/4 radians relative to samplePos
	Ψ, _ = AngleToIntercept(samplePos, sampleHeading, sampleTarget)
	want = 1.57081
	if Ψ != want {
		t.Errorf("AngleToIntercept(%v, %v, %v) == %v, want: %v", samplePos, sampleHeading, sampleTarget, Ψ, want)
	}
}
Example #6
0
func absToView(p float64, d float64, n float64) (view float64) {
	view = ((p + d) / (2 * d)) * n
	view = calc.ToFixed(view, 3)
	return
}
Example #7
0
// AngleFromOrigin calculates the angle of a given vector from the origin
// relative to the x-axis of 𝐄 (the model environment)
func AngleFromOrigin(v Vector) (float64, error) {
	if len(v) != 2 {
		return 0, errors.New("vector dimension != 2")
	}
	return calc.ToFixed(math.Atan2(v[x], v[y]), 5), nil
}
Example #8
0
// UnitAngle will map any floating-point value to its angle on a unit circle.
func UnitAngle(angle float64) float64 {
	twoPi := math.Pi * 2
	return calc.ToFixed((angle - (twoPi * math.Floor(angle/twoPi))), 5)
}
Example #9
0
/*RGBDistance quantifies the value difference between two RGB structs,
returning a floating-point ratio from 0.0 to 1.0.
Multiply the returned value by100 for a percentage.
NOTE: this is a distinct concept from the distance between them as 3D vectors,
as there would be 2 other RGB for any RGB with an identical magnitude.
e.g. [1.0 0 0] [0 1.0 0] [0 0 1.0] will all have the same magnitude, but are
pure Red, pure Blue, pure Green respectively! */
func RGBDistance(c1 RGB, c2 RGB) float64 {
	red := math.Abs(c1.Red - c2.Red)
	green := math.Abs(c1.Green - c2.Green)
	blue := math.Abs(c1.Blue - c2.Blue)
	return calc.ToFixed(((red + blue + green) / 3.0), 3) // returns to 3 d.p. only
}
Example #10
0
func Test(t *testing.T) {
	rand.Seed(time.Now().UnixNano())
	uval := float64(rand.Int())
	vval := float64(rand.Int())
	wval := float64(rand.Int())
	xval := float64(rand.Int())
	yval := float64(rand.Int())
	zval := float64(rand.Int())
	randU := rand.Float64()
	randV := rand.Float64()
	randW := rand.Float64()
	randX := rand.Float64()
	randY := rand.Float64()
	randZ := rand.Float64()

	var vecAdditionTests = []struct {
		v, u, want Vector
	}{
		{Vector{1.0, 0.0}, Vector{0.0, 1.0}, Vector{1.0, 1.0}},
		{Vector{xval, yval}, Vector{wval, zval}, Vector{xval + wval, yval + zval}},
		{Vector{randX, randY}, Vector{randW, randZ},
			Vector{randX + randW, randY + randZ}},
	}

	for _, c := range vecAdditionTests {
		got, _ := VecAddition(c.v, c.u)
		if got.Equal(c.want) == false {
			t.Errorf("VecAddition(%v, %v) == %v, want %v\n", c.v, c.u, got, c.want)
		}
	}

	var vecScalarMultiplicationTests = []struct {
		v    Vector
		s    float64
		want Vector
	}{
		{Vector{1.0, 0.0}, -0.1, Vector{1.0 * -0.1, 0.0 * -0.1}},
		{Vector{xval, yval}, randX, Vector{xval * randX, yval * randX}},
		{Vector{randX, randY}, 3.3, Vector{randX * 3.3, randY * 3.3}},
	}

	for _, d := range vecScalarMultiplicationTests {
		got, _ := VecScalarMultiply(d.v, d.s)
		if got.Equal(d.want) == false {
			t.Errorf("VecScalarMultiply(%q, %v) == %q, want %q\n", d.v, d.s, got, d.want)
		}
	}

	var dotProductTests = []struct {
		v, u Vector
		want float64
	}{
		{Vector{1.0, 0.0}, Vector{0.0, 1.0}, 0.0},
		{Vector{xval, yval}, Vector{wval, zval}, (xval*wval + yval*zval)},
		{Vector{randX, randY}, Vector{randW, randZ}, (randX*randW + randY*randZ)},
	}

	for _, e := range dotProductTests {
		got, _ := DotProduct(e.v, e.u)
		if got != e.want {
			t.Errorf("DotProduct(%v, %v) == %v, want %v\n", e.v, e.u, got, e.want)
		}
	}
	/*
	  y*w - z*v
	  z*u - x*w
	  x*v - y*u
	*/
	var crossProductTests = []struct {
		v, u, want Vector
	}{
		{Vector{1.0, 2.0, 3.0}, Vector{3.0, 4.0, 5.0}, Vector{-2.0, 4.0, -2.0}},
		{Vector{xval, yval, zval}, Vector{uval, vval, wval},
			Vector{(yval*wval - zval*vval), (zval*uval - xval*wval), (xval*vval - yval*uval)}},
		{Vector{randX, randY, randZ}, Vector{randU, randV, randW},
			Vector{(randY*randW - randZ*randV), (randZ*randU - randX*randW), (randX*randV - randY*randU)}},
	}

	for _, f := range crossProductTests {
		got, _ := CrossProduct(f.v, f.u)
		if got.Equal(f.want) == false {
			t.Errorf("VecScalarMultiply(%v, %v) == %v, want %q\n", f.v, f.u, got, f.want)
		}
	}
	/*
	  // AngleFromOrigin calculates the angle of a given vector from the origin
	  // relative to the x-axis of 𝐄 (the model environment)
	  func AngleFromOrigin(v Vector) float64 {
	  	return math.Atan2(v[x], v[y])
	  }
	*/
	var angleFromOriginTests = []struct {
		v    Vector
		want float64
	}{
		{Vector{1.0, 2.0}, calc.ToFixed(math.Atan2(1.0, 2.0), 5)},
		{Vector{xval, yval}, calc.ToFixed(math.Atan2(xval, yval), 5)},
		{Vector{randW, randZ}, calc.ToFixed(math.Atan2(randW, randZ), 5)},
	}

	for _, g := range angleFromOriginTests {
		got, _ := AngleFromOrigin(g.v)
		if got != g.want {
			t.Errorf("AngleFromOrigin(%v), == %v, want %v\n", g.v, got, g.want)
		}
	}

	var relativeAngleTests = []struct {
		v, u Vector
		want float64
	}{
		{Vector{0, 0}, Vector{0, 1}, calc.ToFixed((math.Pi / 2), 5)},
		{Vector{1, 0}, Vector{-1, 0}, calc.ToFixed(math.Pi, 5)},
		{Vector{1, 0}, Vector{0, 0}, calc.ToFixed(math.Pi, 5)},
		{Vector{0, 0}, Vector{1, 0}, 0},
		{Vector{0, 1}, Vector{0, 0}, calc.ToFixed((-math.Pi / 2), 5)},
		{Vector{0, 0}, Vector{1, 1}, calc.ToFixed((math.Pi / 4), 5)},
		{Vector{0, 0}, Vector{-1, -1}, calc.ToFixed(((-3 * math.Pi) / 4), 5)},
		{Vector{0, 0}, Vector{-1, 1}, calc.ToFixed(((3 * math.Pi) / 4), 5)},
		{Vector{1, 0}, Vector{-1, 1}, 2.67795},
	}

	for _, r := range relativeAngleTests {
		got, _ := RelativeAngle(r.v, r.u)
		if got != r.want {
			t.Errorf("RelativeAngle(%v, %v) == %v, want %v\n", r.v, r.u, got, r.want)
		}
	}

	var unitAngleTests = []struct {
		a, want float64
	}{
		{2 * math.Pi, 0},
		{(2 * math.Pi) + randU, calc.ToFixed(randU, 5)},
		{(3 * math.Pi), calc.ToFixed(math.Pi, 5)},
	}

	for _, i := range unitAngleTests {
		got := UnitAngle(i.a)
		if got != i.want {
			t.Errorf("UnitAngle(%v), == %v, want %v\n", i.a, got, i.want)
		}
	}

	d0 := float64(4.35)
	r0 := float64(0.31)
	n0 := int((2.0 * d0) / (2.0 * r0)) // n0 = 14
	v0 := Vector{4.34, -4.34}
	d1 := float64(17.10189)
	r1 := float64(0.125)
	n1 := int((2 * d1) / (2 * r1)) // n1 = 136
	v1 := Vector{-3.056, 11.13}

	var translatePositionToSector2DTests = []struct {
		ed               float64
		n                int
		v                Vector
		rowWant, colWant int
	}{
		{1.0, int((2 * 1.0) / (2 * 0.2)), Vector{-0.18, 0.0}, 2, 2},
		{d0, n0, v0, 13, 13},
		{d1, n1, v1, 23, 55},
	}

	for _, j := range translatePositionToSector2DTests {
		rowGot, colGot := TranslatePositionToSector2D(j.ed, j.n, j.v)
		if rowGot != j.rowWant || colGot != j.colWant {
			t.Errorf("TranslatePositionToSector2D(%v, %v, %v) == (%v,%v), want (%v,%v)\n", j.ed, j.n, j.v, rowGot, colGot, j.rowWant, j.colWant)
		}
	}

	v2 := Vector{-0.00146, 9.13, 0.006}

	var magnitudeTests = []struct {
		v    Vector
		want float64
	}{
		{v0, 6.13769},
		{v1, 11.54193},
		{v2, 9.13000},
	}

	for _, k := range magnitudeTests {
		got, _ := Magnitude(k.v)
		if got != k.want {
			t.Errorf("Magnitude(%v) == %v, want %v\n", k.v, got, k.want)
		}
	}

	var normaliseTests = []struct {
		v    Vector
		want Vector
	}{
		{v0, Vector{0.70711, -0.70711}},
		{v1, Vector{-0.26477, 0.96431}},
		{v2, Vector{-0.00016, 1.0, 0.00066}},
	}

	for _, l := range normaliseTests {
		got, _ := Normalise(l.v)
		if got.Equal(l.want) == false {
			t.Errorf("Normalise(%v) == %v, want %v\n", l.v, got, l.want)
		}
	}
}