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(x*x + y*y + 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://unicodelookup.com/#×/1

// 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]