 # New operators for • and × questions

As soon as I saw 2D vectors, I had to do the whole chapter in 3D vectors. Adding z: 0 to the book examples was negligible extra work and now I have a Vector struct I will use.

Mattt Thompson was kind enough to blog how to add operators to Swift, making it possible to express A • B and A × B directly in my code instead of the more awkward dot() and cross() functions.

When declaring the • operator, at first, I declared it with left associativity, but then on reflection, A • B • C is undefined; A • B returns a scalar, giving scalar • C. I changed the declaration to omit associativity. Am I doing this correctly?

Also, I’ve declared precedence 150, putting • and × in the Multiplicative group. Comments? Objections?

The code is broken into three parts, introductory comments, the vector structure and related functions, with the third part containing verification code.

Introductory comments:

[code]// Hillegass, A., Preble, A., Chandler, N.
// Cocoa Programming For OS X, 5th Edition
// Chapter 3, Structures and Classes
// iBooks, p 114, print, p 41

/*

Challenge: Vector angle, iBooks p 152

Authors have been working with 2 dimensional vectors; the Vector struct below defines
3 dimensional vectors. The authors suggest implementing a computed variable angle that
returns atan2(y,x). More is needed when working with veectors in three dimensions.

It turns out that there are many 3 dimensional coordinate systems; we need to explicitly define
the wolrd in which we work.

We will start with the corner of an imaginary room; the positive x axis extends downward and to
the left. The positive y axis extends to the right. The positive z axis extends upward. This results
in a right handed coordinate system.

In a spherical coordinate system, many possibilities also exist. The use of (r, θ, φ) to denote radial
distance, inclination (or elevation), and azimuth, respectively, is common practice in physics, and is
specified by ISO standard 80000-2 :2009, and earlier in ISO 31-11 .

However, some authors (including mathematicians) use φ for inclination (or elevation) and θ for azimuth.

We will follow the conventions below for a vector originating at the origin (O) and ending at a point P:

r: Radial distance from origin to point P.
theta: Polar angle (or inclination) from z axis to line segment OP.
phi: Azimuth angle from the x axis to a projecction of the vector on the xy plane.

r >= 0
theta 0 <= theta <= π, 0° <= theta <= 180°
phi 0 <= phi <= 2π, 0° <= phi <= 360°

The Vector structure accepts and returns angles in radians

Conversions:

r = sqrt(xx + yy + z*z)
theta = acos(z/r)
phi = atan2(y/x)

x = r * sin(theta) * cos(phi)
y = r * sin(theta) * sin(phi)
z = r * cos(phi)

*/

/*
The function acos() returns the first angle that matches its parameter; this angle might not
match the direction of the vector. theta as returned by acos() function may need to be adjusted
to point within the octant containing the vector, matching the direction of the vector.

Likewise, phi might need adjustment.

To identify each octant, it is convenient to give its list of signs.

Octant Adjustment to theta (radians, degrees) Adjustment to phi (radians, degrees)

xyz

+++ 0, 0° 0, 0°
-++ 0, 0° 0, 0°
–+ 0, 0° 2π, 360°
±+ 0, 0° 2π, 360°

+± 0, 0° 0, 0°
-± 0, 0° 0, 0°
— 0, 0° 2π, 360°
±- 0, 0° 2π, 360°

*/

/*

Sidebar: Importance of defining coordinate systems and units - a 193 million USD question

In December, 1998, NASA launched the Mars Climate Orbiter mission; the mission was lost on arrival at Mars in
September 1999. Investigation determined that one team was working with imperial units; the other mission teams
were working in metric units. The discrepancy in units was not discovered until after mission loss.

http://mars.jpl.nasa.gov/msp98/news/mco991110.html

*/[/code]

Vector structure and related functions:

[code]let pi = M_PI
let piOver2 = M_PI_2
let twoPi = M_PI * 2.0

struct Vector: Printable {
var x: Double
var y: Double
var z: Double

``````var description: String {
return "(\(x), \(y), \(z))"
}

var r: Double {
return sqrt(x*x + y*y + z*z)
}

var theta: Double {
return acos(z / r)
}

var phi: Double {
// if y < 0, add 2π to result; octants --+, +-+, ---, +--
return atan2(y, x) + ((y < 0.0) ? twoPi : 0.0)
}

init() {
self.init(x: 0, y: 0, z: 0)
}

init(x: Double, y: Double, z: Double) {
self.x = x
self.y = y
self.z = z
}

init(r: Double, theta: Double, phi: Double) {
self.init(x: r * sin(theta) * cos(phi),
y: r * sin(theta) * sin(phi),
z: r * cos(theta))
}

func vectorByAddingVector(vector: Vector) -> Vector {
return Vector(x: x + vector.x,
y: y + vector.y,
z: z + vector.z)
}
``````

}

// useful overloads and functions

// vector + vector

func + (left: Vector, right: Vector) -> Vector {
return left.vectorByAddingVector(right)
}

// vector - vector

func - (left: Vector, right: Vector) -> Vector {
return left + (-1.0 * right)
}

// vector * Double

func * (left: Vector, right: Double) -> Vector {
return Vector(x: left.x * right, y: left.y * right, z: left.z * right)
}

// Double * Vector

func * (left: Double, right: Vector) -> Vector {
return right * left
}

// vector • vector

// infix operator • { associativity left precedence 150 }

infix operator • { precedence 150 }
func • (left: Vector, right: Vector) -> Double {
return (left.x * right.x + left.y * right.y + left.z * right.z)
}

// thanks to Mattt Thompson for showing how to define new Swift operators
// http://nshipster.com/swift-operators/

// vector x vector

func cross(a: Vector, b: Vector) -> Vector {
return a × b
}

infix operator × { associativity left precedence 150 }
func × (a: Vector, b: Vector) -> Vector {
return Vector(x: a.y * b.z - a.z * b.y,
y: a.z * b.x - a.x * b.z,
z: a.x * b.y - a.y * b.x)
}

// to enter ×
// 1) Select Unicode Hex Input from the Keyboard menu.
// 2) Hold down Option -; while holding down these keys, type 00D7

// http://www.poynton.com/notes/misc/mac-unicode-hex-input.html
// Thanks to Charles Poynton for showing how to use Unicode Hex Input[/code]

Verification code:

[code]func showAngles(vector: Vector) {
var r = vector.r
var theta = vector.theta
var thetaDeg = theta / pi * 180.0
var phi = vector.phi
var phiDeg = phi / pi * 180.0

``````println("for vector = \(vector)")
println("r = \(r)")
println("theta = \(theta) radians, \(thetaDeg)°")
println("phi = \(phi) radians, \(phiDeg)°")
println()
``````

}

// +++

showAngles(Vector(x: 1.0, y: 1.0, z: 1.0))

// -++

showAngles(Vector(x: -1.0, y: 1.0, z: 1.0))

// --+

showAngles(Vector(x: -1.0, y: -1.0, z: 1.0))

// ±+

showAngles(Vector(x: 1.0, y: -1.0, z: 1.0))

// +±

showAngles(Vector(x: 1.0, y: 1.0, z: -1.0))

// -±

showAngles(Vector(x: -1.0, y: 1.0, z: -1.0))

// —

showAngles(Vector(x: -1.0, y: -1.0, z: -1.0))

// ±-

showAngles(Vector(x: 1.0, y: -1.0, z: -1.0))

let rThetaPhi1 = Vector(r: sqrt(3.0), theta: 0.955316618124509, phi: pi / 4.0) // +++
let rThetaPhi2 = Vector(r: sqrt(3.0), theta: 0.955316618124509, phi: 3.0 * pi / 4.0) // -++
let rThetaPhi3 = Vector(r: sqrt(3.0), theta: 0.955316618124509, phi: 5.0 * pi / 4.0) // --+
let rThetaPhi4 = Vector(r: sqrt(3.0), theta: 0.955316618124509, phi: 7.0 * pi / 4.0) // ±+
let rThetaPhi5 = Vector(r: sqrt(3.0), theta: 2.18627603546528, phi: pi / 4.0) // +±
let rThetaPhi6 = Vector(r: sqrt(3.0), theta: 2.18627603546528, phi: 3.0 * pi / 4.0) // -±
let rThetaPhi7 = Vector(r: sqrt(3.0), theta: 2.18627603546528, phi: 5.0 * pi / 4.0) // —
let rThetaPhi8 = Vector(r: sqrt(3.0), theta: 2.18627603546528, phi: 7.0 * pi / 4.0) // ±-

// vector calculations
// verified with Vector Calcualtor at http://wims.unice.fr/wims/wims.cgi
//
// or at http://onlinemschool.com/math/assistance/vector/multiply1/

let vector123 = Vector(x: 1.0, y: 2.0, z: 3.0)
let vector456 = Vector(x: 4.0, y: 5.0, z: 6.0)

let dot123456 = vector123 • vector456

let x123456 = vector123 × vector456
let x123456v2 = cross(vector123, vector456)

let x456123 = vector456 × vector123
let xAnother = vector123 × vector123[/code]

Nicely complete – thank you for sharing!

Are you on Yosemite? I ask because the instructions from Mr. Poynton appear to need updating.

For me anyway, to see the Unicode Hex Input option, from System Preferences > Keyboard > Input Sources, I had to:

[ol]
[li]Press + to add a language[/li]
[li]Select Others from the bottom of the language list in the left pane[/li]
[li]Select Unicode Hex Input from the right pane[/li][/ol]
If you enjoy the Unicode Hex Input, you may have even more fun with this list of codes.

Or, to go back to simpler times, select “Show Character Viewer” from the keyboard menu, and search for “multiplication” (as U+00D7 is named the Multiplication Sign ).

And yes, I first searched for “cross”. But while it did return something that LOOKED like the cross product operator, it was totally different (“Box Drawings Light Diagonal Cross”, U+2573).

Thank you, gc3182, but as first posted, the code is incomplete. I have a screen-page of comments that precede the vector definition. There are many choices for a three dimensional coordinate system and the comments answer the \$193 million question regarding the conventions chosen regarding phi and theta. Since this is a chapter relating to physics, this vector structure conforms to standards used by the physics world. Mathematicians could easily tweak this into a vector structure that conforms to their world.

I initially omitted the comments for fear of overwhelming the thread. But the more I think about it, the post is incomplete without the comments. Also missing is another screenful of code that I used to verify the vector structure. So I’ve edited the post to include both the comments and introductory code.

I’m not on Yosemite. You are referring to an online programming community? Thanks for the table of Unicode characters; I will use it. gc3182, have you tried contacting Charles Poynton directly? He gives his email on http://poynton.com.

Thanks for the gentle nudge; I’ve emailed Mr. Poynton with the updated technique.

Thank you also for updating the code with the additional comments; I rather enjoyed them. (My degree was in Mathematics…but that was a few decades ago.)