spatial/r2,all: harmonize r2.Vec API with r3.Vec API

Migration to the new API can be achieved with this rsc.io/rf script:

```
rf ex {
	import "gonum.org/v1/gonum/spatial/r2";
	var p,q r2.Vec;
	var f float64;

	p.Add(q) -> r2.Add(p, q);
	p.Sub(q) -> r2.Sub(p, q);
	p.Scale(f) -> r2.Scale(f, p);
	p.Dot(q) -> r2.Dot(p, q);
	p.Cross(q) -> r2.Cross(p, q);
	p.Rotate(f, q) -> r2.Rotate(p, f, q);
}
```

Updates gonum/gonum#1553.
This commit is contained in:
Sebastien Binet
2021-01-28 10:40:57 +01:00
parent 3199e478a1
commit c9092a1e6a
6 changed files with 93 additions and 41 deletions

View File

@@ -84,7 +84,7 @@ func (u *EadesR2) Update(g graph.Graph, layout LayoutR2) bool {
}
var updated bool
for i, p := range u.particles {
f := plane.ForceOn(p, u.Theta, barneshut.Gravity2).Scale(-u.Repulsion)
f := r2.Scale(-u.Repulsion, plane.ForceOn(p, u.Theta, barneshut.Gravity2))
// Prevent marginal updates that can be caused by
// floating point error when nodes are very far apart.
if math.Hypot(f.X, f.Y) > 1e-12 {
@@ -137,16 +137,16 @@ func (u *EadesR2) Update(g graph.Graph, layout LayoutR2) bool {
yidx := u.indexOf[yid]
// Apply adjacent node attraction.
v := u.particles[yidx].Coord2().Sub(u.particles[xidx].Coord2())
f := v.Scale(weight(xid, yid) * math.Log(math.Hypot(v.X, v.Y)))
v := r2.Sub(u.particles[yidx].Coord2(), u.particles[xidx].Coord2())
f := r2.Scale(weight(xid, yid)*math.Log(math.Hypot(v.X, v.Y)), v)
if math.IsInf(f.X, 0) || math.IsInf(f.Y, 0) {
return false
}
if math.Hypot(f.X, f.Y) > 1e-12 {
updated = true
}
u.forces[xidx] = u.forces[xidx].Add(f)
u.forces[yidx] = u.forces[yidx].Sub(f)
u.forces[xidx] = r2.Add(u.forces[xidx], f)
u.forces[yidx] = r2.Sub(u.forces[yidx], f)
}
}
@@ -160,7 +160,7 @@ func (u *EadesR2) Update(g graph.Graph, layout LayoutR2) bool {
}
for i, f := range u.forces {
n := u.particles[i].(eadesR2Node)
n.pos = n.pos.Add(f.Scale(rate))
n.pos = r2.Add(n.pos, r2.Scale(rate, f))
u.particles[i] = n
layout.SetCoord2(n.id, n.pos)
}

View File

@@ -37,7 +37,7 @@ func Gravity2(_, _ Particle2, m1, m2 float64, v r2.Vec) r2.Vec {
if d2 == 0 {
return r2.Vec{}
}
return v.Scale((m1 * m2) / (d2 * math.Sqrt(d2)))
return r2.Scale((m1*m2)/(d2*math.Sqrt(d2)), v)
}
// Plane implements Barnes-Hut force approximation calculations.
@@ -135,7 +135,7 @@ func (q *Plane) ForceOn(p Particle2, theta float64, f Force2) (force r2.Vec) {
m := p.Mass()
pv := p.Coord2()
for _, e := range q.Particles {
v = v.Add(f(p, e, m, e.Mass(), e.Coord2().Sub(pv)))
v = r2.Add(v, f(p, e, m, e.Mass(), r2.Sub(e.Coord2(), pv)))
}
return v
}
@@ -260,7 +260,7 @@ func (t *tile) forceOn(p Particle2, pt r2.Vec, m, theta float64, f Force2) (vect
s := ((t.bounds.Max.X - t.bounds.Min.X) + (t.bounds.Max.Y - t.bounds.Min.Y)) / 2
d := math.Hypot(pt.X-t.center.X, pt.Y-t.center.Y)
if s/d < theta || t.particle != nil {
return f(p, t.particle, m, t.mass, t.center.Sub(pt))
return f(p, t.particle, m, t.mass, r2.Sub(t.center, pt))
}
var v r2.Vec
@@ -268,7 +268,7 @@ func (t *tile) forceOn(p Particle2, pt r2.Vec, m, theta float64, f Force2) (vect
if d == nil {
continue
}
v = v.Add(d.forceOn(p, pt, m, theta, f))
v = r2.Add(v, d.forceOn(p, pt, m, theta, f))
}
return v
}

View File

@@ -427,9 +427,9 @@ func TestPlaneForceOn(t *testing.T) {
m := p.Mass()
pv := p.Coord2()
for _, e := range particles {
v = v.Add(Gravity2(p, e, m, e.Mass(), e.Coord2().Sub(pv)))
v = r2.Add(v, Gravity2(p, e, m, e.Mass(), r2.Sub(e.Coord2(), pv)))
}
moved[i] = p.Coord2().Add(v)
moved[i] = r2.Add(p.Coord2(), v)
}
plane, err := NewPlane(particles)
@@ -448,8 +448,8 @@ func TestPlaneForceOn(t *testing.T) {
calls++
return Gravity2(p1, p2, m1, m2, v)
})
pos := p.Coord2().Add(v)
d := moved[i].Sub(pos)
pos := r2.Add(p.Coord2(), v)
d := r2.Sub(moved[i], pos)
ssd += d.X*d.X + d.Y*d.Y
sd += math.Hypot(d.X, d.Y)
}

49
spatial/r2/deprecated.go Normal file
View File

@@ -0,0 +1,49 @@
// Copyright ©2021 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package r2
// TODO(sbinet): remove this file for Gonum-v0.10.0.
// Add returns the vector sum of p and q.
//
// DEPRECATED: use r2.Add.
func (p Vec) Add(q Vec) Vec {
return Add(p, q)
}
// Sub returns the vector sum of p and -q.
//
// DEPRECATED: use r2.Sub.
func (p Vec) Sub(q Vec) Vec {
return Sub(p, q)
}
// Scale returns the vector p scaled by f.
//
// DEPRECATED: use r2.Scale.
func (p Vec) Scale(f float64) Vec {
return Scale(f, p)
}
// Dot returns the dot product p·q.
//
// DEPRECATED: use r2.Dot.
func (p Vec) Dot(q Vec) float64 {
return Dot(p, q)
}
// Cross returns the cross product p×q.
//
// DEPRECATED: use r2.Cross.
func (p Vec) Cross(q Vec) float64 {
return Cross(p, q)
}
// Rotate returns a new vector, rotated by alpha around the provided point, q.
//
// DEPRECATED: use r2.Rotate.
func (p Vec) Rotate(alpha float64, q Vec) Vec {
return Rotate(p, alpha, q)
}

View File

@@ -12,38 +12,41 @@ type Vec struct {
}
// Add returns the vector sum of p and q.
func (p Vec) Add(q Vec) Vec {
p.X += q.X
p.Y += q.Y
return p
func Add(p, q Vec) Vec {
return Vec{
X: p.X + q.X,
Y: p.Y + q.Y,
}
}
// Sub returns the vector sum of p and -q.
func (p Vec) Sub(q Vec) Vec {
p.X -= q.X
p.Y -= q.Y
return p
func Sub(p, q Vec) Vec {
return Vec{
X: p.X - q.X,
Y: p.Y - q.Y,
}
}
// Scale returns the vector p scaled by f.
func (p Vec) Scale(f float64) Vec {
p.X *= f
p.Y *= f
return p
func Scale(f float64, p Vec) Vec {
return Vec{
X: f * p.X,
Y: f * p.Y,
}
}
// Dot returns the dot product p·q.
func (p Vec) Dot(q Vec) float64 {
func Dot(p, q Vec) float64 {
return p.X*q.X + p.Y*q.Y
}
// Cross returns the cross product p×q.
func (p Vec) Cross(q Vec) float64 {
func Cross(p, q Vec) float64 {
return p.X*q.Y - p.Y*q.X
}
// Rotate returns a new vector, rotated by alpha around the provided point, q.
func (p Vec) Rotate(alpha float64, q Vec) Vec {
func Rotate(p Vec, alpha float64, q Vec) Vec {
return NewRotation(alpha, q).Rotate(p)
}
@@ -65,12 +68,12 @@ func Unit(p Vec) Vec {
if p.X == 0 && p.Y == 0 {
return Vec{X: math.NaN(), Y: math.NaN()}
}
return p.Scale(1 / Norm(p))
return Scale(1/Norm(p), p)
}
// Cos returns the cosine of the opening angle between p and q.
func Cos(p, q Vec) float64 {
return p.Dot(q) / (Norm(p) * Norm(q))
return Dot(p, q) / (Norm(p) * Norm(q))
}
// Box is a 2D bounding box.
@@ -98,11 +101,11 @@ func (r Rotation) Rotate(p Vec) Vec {
if r.isIdentity() {
return p
}
o := p.Sub(r.p)
return Vec{
o := Sub(p, r.p)
return Add(Vec{
X: (o.X*r.cos - o.Y*r.sin),
Y: (o.X*r.sin + o.Y*r.cos),
}.Add(r.p)
}, r.p)
}
func (r Rotation) isIdentity() bool {

View File

@@ -22,7 +22,7 @@ func TestAdd(t *testing.T) {
{Vec{1, -3}, Vec{1, -6}, Vec{2, -9}},
{Vec{1, 2}, Vec{-1, -2}, Vec{}},
} {
got := test.v1.Add(test.v2)
got := Add(test.v1, test.v2)
if got != test.want {
t.Errorf(
"error: %v + %v: got=%v, want=%v",
@@ -43,7 +43,7 @@ func TestSub(t *testing.T) {
{Vec{1, -3}, Vec{1, -6}, Vec{0, 3}},
{Vec{1, 2}, Vec{1, 2}, Vec{}},
} {
got := test.v1.Sub(test.v2)
got := Sub(test.v1, test.v2)
if got != test.want {
t.Errorf(
"error: %v - %v: got=%v, want=%v",
@@ -67,7 +67,7 @@ func TestScale(t *testing.T) {
{2, Vec{1, -3}, Vec{2, -6}},
{10, Vec{1, 2}, Vec{10, 20}},
} {
got := test.v.Scale(test.a)
got := Scale(test.a, test.v)
if got != test.want {
t.Errorf(
"error: %v * %v: got=%v, want=%v",
@@ -89,7 +89,7 @@ func TestDot(t *testing.T) {
{Vec{1, 2}, Vec{-0.3, 0.4}, 0.5},
} {
{
got := test.u.Dot(test.v)
got := Dot(test.u, test.v)
if got != test.want {
t.Errorf(
"error: %v · %v: got=%v, want=%v",
@@ -98,7 +98,7 @@ func TestDot(t *testing.T) {
}
}
{
got := test.v.Dot(test.u)
got := Dot(test.v, test.u)
if got != test.want {
t.Errorf(
"error: %v · %v: got=%v, want=%v",
@@ -120,7 +120,7 @@ func TestCross(t *testing.T) {
{Vec{1, 2}, Vec{-4, 5}, 13},
{Vec{1, 2}, Vec{2, 3}, -1},
} {
got := test.v1.Cross(test.v2)
got := Cross(test.v1, test.v2)
if got != test.want {
t.Errorf(
"error: %v × %v = %v, want %v",
@@ -236,7 +236,7 @@ func TestRotate(t *testing.T) {
{Vec{2, 2}, Vec{1, 1}, math.Pi, Vec{0, 0}},
{Vec{2, 2}, Vec{2, 0}, math.Pi, Vec{2, -2}},
} {
got := test.v.Rotate(test.alpha, test.q)
got := Rotate(test.v, test.alpha, test.q)
if !vecApproxEqual(got, test.want) {
t.Errorf(
"rotate(%v, %v, %v)= %v, want=%v",