Take a step back in history with the archives of PragPub magazine. The Pragmatic Programmers hope you’ll find that learning about the past can help you make better decisions for the future.

FROM THE ARCHIVES OF PRAGPUB MAGAZINE DECEMBER 2016

Concatenate Got Your Tongue?

A Swift Puzzle by Erica Sadun

4 min readMar 11, 2022

--

📝Author’s Note: Since writing, the details of the Swift language use of Core Graphics have changed. The underlying math and matrices remain an interesting area to explore.

Erica’s book is Swift Style. Here she poses a Swift puzzle for the reader.

https://pragprog.com/newsletter/
https://pragprog.com/newsletter/

Quick quiz time! Given these two transforms:

let translation = CGAffineTransform(translationX: 5, y: 10)
let rotation = CGAffineTransform(rotationAngle: CGFloat(Double.pi) / 6)

Consider the following assignments:

let a = rotation.concatenating(translation) 
let b = translation.concatenating(rotation)
let c = rotation.translatedBy(x: 5, y: 10)
let d = translation.rotated(byDegrees: CGFloat(Double.pi) / 6)

❔ Can you tell me which of these outcomes match each other before peeking at the answer?

Photo by Cara Fuller on Unsplash

To provide a little buffer between here and there, let me remind you about what a basic affine transform looks like:

For translation, the tx and ty entries specify the offsets for x and y: translation:

When rotating, the abcd slots are filled by cos(theta), sin(theta), -sin(theta), and cos(theta):
rotation:

Multiplying translation by rotation gives you this:

And multiplying rotation by translation gives you this:

The results are identical except in the (tx, ty) offset slots.

Okay, ready with your answers? If you guessed a/d and b/c, you’re right. As a basic rule of thumb, x.concatenating(y) is going to be the same as y.performing(x), where the performing call is rotated(by:), translatedBy(x:,y:), or scaledBy(x, y).

Concatenating a transform simply multiplies one transform by another: T1 x T2. However, performing a transformation (rotated, translated, scaled) gives you T2 x T1. If you hop into the module declarations, the answers are there to see in the hipster retro documentation:

/* Translate `t' by `(tx, ty)' and return the result: 
t' = [ 1 0 0 1 tx ty ] * t */
@available(iOS 2.0, *)
public func translatedBy(x tx: CGFloat, y ty: CGFloat) -> CGAffineTransform
/* Scale `t' by `(sx, sy)' and return the result:
t' = [ sx 0 0 sy 0 0 ] * t */
@available(iOS 2.0, *)
public func scaledBy(x sx: CGFloat, y sy: CGFloat) -> CGAffineTransform
/* Rotate `t' by `angle' radians and return the result:
t' = [ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ] * t */
@available(iOS 2.0, *)
public func rotated(by angle: CGFloat) -> CGAffineTransform

Here are the results, printed from a Swift playground, just to confirm that the behavior is, in fact, exactly as documented:

a
CGAffineTransform(a: 0.866025403784439, b: 0.5, c: -0.5,
d: 0.866025403784439, tx: 5.0, ty: 10.0)
b
CGAffineTransform(a: 0.866025403784439, b: 0.5, c: -0.5,
d: 0.866025403784439, tx: -0.669872981077805, ty: 11.1602540378444)
c
CGAffineTransform(a: 0.866025403784439, b: 0.5, c: -0.5,
d: 0.866025403784439, tx: -0.669872981077805, ty:
11.1602540378444)
d
CGAffineTransform(a: 0.866025403784439, b: 0.5, c: -0.5,
d: 0.866025403784439, tx: 5.0, ty: 10.0)

I will leave my rants about the absurd and inconsistent naming, caps, argument labels, and initializers for another day.

About the Author

Erica Sadun is the author of Swift Style. When not writing, she’s a full-time parent of geeks who are brushing up on their world domination skills. According to her academic dosimeter, she’s acquired more education than any self-respecting person might consider wise. She enjoys deep diving into technology and has written, co-written, and contributed to dozens of books about computing and digital media. Sadun has blogged at TUAW, Ars Technica, O’Reilly, and Lifehacker and written for Make magazine. Follow her on Twitter and check her blog to keep up with news and ebook announcements.

--

--

PragPub
The Pragmatic Programmers

The Pragmatic Programmers bring you archives from PragPub, a magazine on web and mobile development (by editor Michael Swaine, of Dr. Dobb’s Journal fame).