Category talk:Wren-vector: Difference between revisions

Content added Content deleted
(→‎Source code: Bug fix.)
(→‎Source code: Numerous changes most notably renaming Vector class to Vector2 and adding a generic n-dimensional Vector class.)
Line 3:
<syntaxhighlight lang="ecmascript">/* Module "vector.wren" */
 
/*
/* Vector represents a two dimensional geometric vector in the plane. */
Vector represents a vector in n-dimensional Euclidean space.
For two or three dimensional geometric vectors, the specialist
classes Vector2 or Vector3 should normally be used instead
as they have greater functionality including support for
construction using coordinate systems other than Cartesian.
*/
class Vector {
// GetsReturns ora setszero theVector defaultof angledimension unit'n'.
static useDegreeszero(n) { __useDegVector.new([0] * n) }
static useDegrees=(v) { (v is Bool) ? __useDeg = v : Fiber.abort("Invalid argument.") }
// Returns a zero Vector.
static zero { Vector.new(0, 0) }
 
// Returns v1 * v2 or v2 * v1 depending on which order the arguments are presented.
Line 19 ⟶ 21:
}
 
// Efficiently sums a list of vectorsVectors of the same dimension.
static sumAll(vectorsVectors) {
if (!(vectorsVectors is List) || vectorsVectors.count == 0 || !(vectorsVectors[0] is Vector)) {
Fiber.abort("Argument must be a non-empty list of vectorsVectors of the same dimension.")
}
if (Vectors.count == 1) return Vectors[0].copy()
var n = Vectors[0].n
var si = List.filled(n, 0)
for (i in 0...n) {
si[i] = Vectors.map { |v| v[i] }.reduce { |acc, e| acc + e }
}
return Vector.new(si)
}
 
// Constructs a Vector from a non-empty list of cartesian coordinates.
construct new(a) {
if (!(a is List) || a.count == 0 || !(a[0] is Num)) {
Fiber.abort("Argument must be a non-empty list of numbers.")
}
_a = a.toList
_n = a.count // save dimension as a separate field
}
 
// Self-evident properties.
n { _n }
 
[i] {
if (!(i is Num) || !(i.isInteger) || i < 0 || i >= _n) {
Fiber.abort("Index must be an integer between 0 and %(_n - 1) inclusive.")
}
return _a[i]
}
 
[i]=(v) {
if (!(i is Num) || !(i.isInteger) || i < 0 || i >= _n) {
Fiber.abort("Index must be an integer between 0 and %(_n - 1) inclusive.")
}
if (!(v is Num)) Fiber.abort("Value must be a number.")
return _a[i] = v
}
 
square { a.map { |v| v * v }.reduce { |acc, e| acc + e } }
length { square.sqrt }
manhattan { a.map { |v| v.abs }.reduce { |acc, e| acc + e } }
 
// Returns a unit Vector with the same direction as this one.
unit {
if (length == 0) return Vector.zero(_n)
return Vector.new(a.map { |v| v / length }.toList)
}
 
// Basic operations.
 
-{ Vector.new(a.map { |v| -v }.toList) }
 
+(other) {
if (other is Num) {
return Vector.new((0..._n).map { |i| this[i] + other }.toList)
}
if (!(other is Vector) || _n != other.n) {
Fiber.abort("Other must be a Vector of dimension %(_n).")
}
return Vector.new((0..._n).map { |i| this[i] + other[i] }.toList)
}
 
-(other) { this + (-other) }
 
*(n) {
if (!(n is Num)) Fiber.abort("n must be a number.")
return Vector.new(a.map { |v| v * n }.toList)
}
 
/(n) {
if (!(n is Num)) Fiber.abort("n must be a number.")
return Vector.new(a.map { |v| v / n }.toList)
}
 
==(other) {
if (!(other is Vector || _n != other.n)) {
Fiber.abort("Other must be a Vector of dimension %(_n).")
}
return (0..._n).all { |i| this[i] == other[i] }
}
 
!=(other) { !(this == other) }
 
// Returns the dot product of this and another Vector of the same dimension.
dot(other) {
if (!(other is Vector) || _n != other.n) {
Fiber.abort("Other must be a Vector of dimension %(_n).")
}
return (0..._n).map { |i| this[i] * other[i] }.reduce { |acc, e| acc + e }
}
 
// Returns whether or not this Vector is perpendicular to another one.
isPerpTo(other) { this.dot(other) == 0 }
 
// Returns the distance between this and another Vector.
dist(other) {
if (!(other is Vector) || _n != other.n) {
Fiber.abort("Other must be a Vector of dimension %(_n).")
}
return (0..._n).map { |i| this[i] - other[i] }.reduce(0) { |acc, e| acc + e * e }.sqrt
}
 
// Returns a copy of this Vector.
copy() { Vector.new(_a) }
 
// Returns the cartesian coordinates of this Vector as a list.
toCartesian { _a.toList }
 
// Returns a string representation of this Vector in cartesian coordinates.
toString { "(" + _a.join(", ") + ")" }
}
 
/* Vector2 represents a two dimensional geometric vector in the plane. */
class Vector2 {
// Gets or sets the default angle unit.
static useDegrees { __useDeg }
static useDegrees=(v) { (v is Bool) ? __useDeg = v : Fiber.abort("Invalid argument.") }
 
// Returns a zero Vector2.
static zero { Vector2.new(0, 0) }
 
// Returns v1 * v2 or v2 * v1 depending on which order the arguments are presented.
static scale(v1, v2) {
if ((v1 is Vector2) && (v2 is Num)) return v1 * v2
if ((v1 is Num) && (v2 is Vector2)) return v2 * v1
Fiber.abort("One argument must be a Vector2 and the other a number.")
}
 
// Efficiently sums a list of vector2s.
static sumAll(vector2s) {
if (!(vector2s is List) || vector2s.count == 0 || !(vector2s[0] is Vector2)) {
Fiber.abort("Argument must be a non-empty list of vector2s.")
}
if (vectorsvector2s.count == 1) return vectorsvector2s[0].copy()
var sx = vector2s.map { |v| v.x }.reduce { |acc, x| acc + x }
if (vectors.count == 2) return vectors[0] + vectors[1]
var sxsy = vectorsvector2s.map { |v| v.xy }.reduce { |acc, xy| acc + xy }
return Vector2.new(sx, sy)
var sy = vectors.map { |v| v.y }.reduce { |acc, y| acc + y }
return Vector.new(sx, sy)
}
 
// Constructs a VectorVector2 from polar coordinates.
static fromPolar(r, theta) {
if (!(r is Num) || !(theta is Num)) Fiber.abort("Arguments must both be numbers.")
Line 38 ⟶ 170:
}
 
// Constructs a VectorVector2 from cartesian coordinates.
construct new(x, y) {
if (!(x is Num) || !(y is Num)) Fiber.abort("Arguments must both be numbers.")
Line 63 ⟶ 195:
}
 
// Returns a VectorVector2 of the same length which is perpendicular to this one.
perp { VectorVector2.new(-_y, x) }
 
// Returns a unit VectorVector2 with the same direction as this one.
unit {
if (length == 0) return VectorVector2.new(0, 0)
return VectorVector2.new(_x/length, _y/length)
}
 
// Basic operations.
 
-{ VectorVector2.new(-_x, -_y) }
 
-(other) { this + (-other) }
 
+(other) {
if (!(other is Vector)Num) Fiberreturn Vector2.abortnew("Other_x must+ beother, a_y Vector."+ other)
if (!(other is Vector2)) Fiber.abort("Other must be a Vector2 or a scalar.")
return Vector.new(_x + other.x, _y + other.y)
return Vector2.new(_x + other.x, _y + other.y)
}
 
-(other) { this + (-other) }
 
*(n) {
if (!(n is Num)) Fiber.abort("n must be a number.")
return VectorVector2.new(_x * n, _y * n)
}
 
/(n) {
if (!(n is Num)) Fiber.abort("n must be a number.")
return VectorVector2.new(_x / n, _y / n)
}
 
==(other) {
if (!(other is VectorVector2)) Fiber.abort("Other must be a VectorVector2.")
return x == other.x && _y == other.y
}
Line 100 ⟶ 233:
!=(other) { !(this == other) }
 
// Returns the dot product of this and another VectorVector2.
dot(other) {
if (!(other is VectorVector2)) Fiber.abort("Other must be a VectorVector2.")
return _x * other.x + _y * other.y
}
 
// Returns whether or not this VectorVector2 is perpendicular to another one.
isPerpTo(other) { this.dot(other) == 0 }
if (!(other is Vector)) Fiber.abort("Other must be a Vector.")
return this.dot(other) == 0
}
 
// Returns the angle between this VectorVector2 and another one.
angleBetween(other) {
if (!(other is Vector)) Fiber.abort("Other must be a Vector.")
var v = (this.dot(other)/(length * other.length)).acos
if (__useDeg) v = v * 180 / Num.pi
Line 120 ⟶ 249:
}
 
// Returns the distance between this and another VectorVector2.
dist(other) {
if (!(other is VectorVector2)) Fiber.abort("Other must be a VectorVector2.")
var d1 = _x - other.x
var d2 = _y - other.y
Line 128 ⟶ 257:
}
 
// Returns a copy of this VectorVector2.
copy() { VectorVector2.new(_x, _y) }
 
// Returns the cartesian coordinates of this VectorVector2 as a list.
toCartesian { [_x, _y] }
 
// Returns the polar coordinates of this VectorVector2 as a list.
toPolar { !__useDeg ? [radius, theta] : [radius, theta * 180 / Num.pi] }
 
// ReturnsConverts athis stringVector2 representationto ofa thisgeneric Vector in cartesian coordinates.
toVector { Vector.new(toCartesian) }
 
// Returns a string representation of this Vector2 in cartesian coordinates.
toString { "(%(_x), %(_y))" }
}
Line 163 ⟶ 295:
}
if (vector3s.count == 1) return vector3s[0].copy()
if (vector3s.count == 2) return vector3s[0] + vector3s[1]
var sx = vector3s.map { |v| v.x }.reduce { |acc, x| acc + x }
var sy = vector3s.map { |v| v.y }.reduce { |acc, y| acc + y }
Line 239 ⟶ 370:
 
+(other) {
if (!(other is Vector3)Num) Fiberreturn Vector3.abortnew("Other_x must+ beother, a_y Vector3."+ other, _z + other)
if (!(other is Vector3)) Fiber.abort("Other must be a Vector3 or a scalar.")
return Vector3.new(_x + other.x, _y + other.y, _z + other.z)
}
Line 279 ⟶ 411:
 
// Returns the scalar triple product of this and two other Vector3s.
scalarTripleProd(other1, other2) { this.dot(other1.cross(other2)) }
if (!(other1 is Vector3) || !(other2 is Vector3)) {
Fiber.abort("Arguments must both be Vector3s.")
}
return this.dot(other1.cross(other2))
}
 
// Returns the vector triple product of this and two other Vector3s.
vectorTripleProd(other1, other2) { this.cross(other1.cross(other2)) }
if (!(other1 is Vector3) || !(other2 is Vector3)) {
Fiber.abort("Arguments must both be Vector3s.")
}
return this.cross(other1.cross(other2))
}
 
// Returns the scalar quadruple product of this and three other Vector3s.
scalarQuodProd(other1, other2, other3) { this.cross(other1).dot(other2.cross(other3)) }
if (!(other1 is Vector3) || !(other2 is Vector3) || !(other3 is Vector3)) {
Fiber.abort("Arguments must all be Vector3s.")
}
return this.cross(other1).dot(other2.cross(other3))
}
 
// Returns the vector quadruple product of this and three other Vector3s.
vectorQuodProd(other1, other2, other3) { this.cross(other1).cross(other2.cross(other3)) }
 
if (!(other1 is Vector3) || !(other2 is Vector3) || !(other3 is Vector3)) {
// Returns whether or not this Vector3 is perpendicular to another one.
Fiber.abort("Arguments must all be Vector3s.")
isPerpTo(other) { this.dot(other) == 0 }
return this.cross(other1).cross(other2.cross(other3))
}
 
// Returns the angle between this Vector3 and another one.
angleBetween(other) {
if (!(other is Vector3)) Fiber.abort("Other must be a Vector3.")
var v = (this.dot(other)/(length * other.length)).acos
if (__useDeg) v = v * 180 / Num.pi
Line 320 ⟶ 434:
// Returns the distance between this and another Vector3.
dist(other) {
if (!(other is VectorVector3)) Fiber.abort("Other must be a Vector3.")
var d1 = _x - other.x
var d2 = _y - other.y
Line 334 ⟶ 448:
 
// Returns the cylindrical coordinates of this Vector3 as a list.
toCylindrical { !__useDeg ? [radius, theta, _z] : [radius, theta * 180 / Num.pi , _z]}
 
// Returns the spherical coordinates of this Vector3 as a list.
toSpherical { !__useDeg ? [length, theta, phi] : [length, theta * 180 /Num.pi, phi * 180 / Num.pi] }
 
// Converts this Vector3 to a generic Vector.
toVector { Vector.new(toCartesian) }
 
// Returns a string representation of this Vector3 in cartesian coordinates.
Line 344 ⟶ 461:
 
// Set initial angle unit defaults (radians).
VectorVector2.useDegrees = false
Vector3.useDegrees = false</syntaxhighlight>
 
var Vector2 = Vector // alias for Vector class</syntaxhighlight>