# Core​Graphics Geometry Primitives

Unless you were a Math Geek or an Ancient Greek, Geometry probably wasn’t your favorite subject in school. More likely, you were that kid in class who dutifully programmed all of those necessary formulæ into your TI-8X calculator to avoid rote memorization.

So for those of you who spent more time learning TI-BASIC than Euclid, here’s the cheat-sheet for how geometry works in Quartz 2D, the drawing system used by Apple platforms:

• A `CGFloat` represents a scalar quantity.
• A `CGPoint` represents a location in a two-dimensional coordinate system and is defined by `x` and `y` scalar components.
• A `CGVector` represents a change in position in 2D space and is defined by `dx` and `dy` scalar components.
• A `CGSize` represents the extent of a figure in 2D space and is defined by `width` and `height` scalar components.
• A `CGRect` represents a rectangle and is defined by an origin point (`CGPoint`) and a size (`CGSize`).
``````import CoreGraphics

let float: CGFloat = 1.0
let point = CGPoint(x: 1.0, y: 2.0)
let vector = CGVector(dx: 4.0, dy: 3.0)
let size = CGSize(width: 4.0, height: 3.0)
var rectangle = CGRect(origin: point, size: size)
``````

On iOS, the origin is located at the top-left corner of a window, so `x` and `y` values increase as they move down and to the right. macOS, by default, orients `(0, 0)` at the bottom left corner of a window, such that `y` values increase as they move up.

Every view in an iOS or macOS app has a `frame` represented by a `CGRect` value, so one would do well to learn the fundamentals of these geometric primitives.

In this week’s article, we’ll do a quick run through the APIs with which every app developer should be familiar.

## Introspection

“First, know thyself.” So goes the philosophical aphorism. And it remains practical guidance as we begin our survey of CoreGraphics API.

As structures, you can access the member values of geometric types directly through their stored properties:

``````point.x // 1.0
point.y // 2.0

size.width // 4.0
size.height // 3.0

rectangle.origin // {x 1 y 2}
rectangle.size // {w 4 h 3}
``````

You can mutate variables by reassignment or by using mutating operators like `*=` and `+=`:

``````var mutableRectangle = rectangle // {x 1 y 2 w 4 h 3}
mutableRectangle.origin.x = 7.0
mutableRectangle.size.width *= 2.0
mutableRectangle.size.height += 3.0
mutableRectangle // {x 7 y 2 w 8 h 6}
``````

For convenience, rectangles also expose `width` and `height` as top-level, computed properties; (`x` and `y` coordinates must be accessed through the intermediary `origin`):

``````rectangle.origin.x
rectangle.origin.y
rectangle.width
rectangle.height
``````

### Accessing Minimum, Median, and Maximum Values

Although a rectangle can be fully described by a location (`CGPoint`) and an extent (`CGSize`), that’s just one side of the story.

For the other 3 sides, use the built-in convenience properties to get the minimum (`min`), median (`mid`), and maximum (`max`) values in the `x` and `y` dimensions:

``````rectangle.minX // 1.0
rectangle.midX // 3.0
rectangle.maxX // 5.0

rectangle.minY // 2.0
rectangle.midY // 3.5
rectangle.maxY // 5.0
``````

#### Computing the Center of a Rectangle

It’s often useful to compute the center point of a rectangle. Although this isn’t provided by the framework SDK, you can easily extend `CGRect` to implement it using the `midX` and `midY` properties:

``````extension CGRect {
var center: CGPoint {
return CGPoint(x: midX, y: midY)
}
}
``````

## Normalization

Things can get a bit strange when you use non-integral or negative values in geometric calculations. Fortunately, CoreGraphics has just the APIs you need to keep everything in order.

### Standardizing Rectangles

We expect that a rectangle’s origin is situated at its top-left corner. However, if its size has a negative width or height, the origin could become any of the other corners instead.

For example, consider the following bizarro rectangle that extends leftwards and upwards from its origin.

``````let ǝןƃuɐʇɔǝɹ = CGRect(origin: point,
size: CGSize(width: -4.0, height: -3.0))
ǝןƃuɐʇɔǝɹ // {x 1 y 2 w -4 h -3}
``````

We can use the `standardized` property to get the equivalent rectangle with non-negative width and height. In the case of the previous example, the standardized rectangle has a width of `4` and height of `3` and is situated at the point `(-3, -1)`:

``````ǝןƃuɐʇɔǝɹ.standardized // {x -3 y -1 w 4 h 3}
``````

### Integrating Rectangles

It’s generally a good idea for all `CGRect` values to be rounded to the nearest whole point. Fractional values can cause the frame to be drawn on a pixel boundary. Because pixels are atomic units, a fractional value causes drawing to be averaged over the neighboring pixels. The result: blurry lines that don’t look great.

The `integral` property takes the `floor` each origin value and the `ceil` each size value. This ensures that your drawing code aligns on pixel boundaries crisply.

``````let blurry = CGRect(x: 0.1, y: 0.5, width: 3.3, height: 2.7)
blurry // {x 0.1 y 0.5 w 3.3 h 2.7}
blurry.integral // {x 0 y 0 w 4 h 4}
``````

## Transformations

While it’s possible to mutate a rectangle by performing member-wise operations on its origin and size, the CoreGraphics framework offers better solutions by way of the APIs discussed below.

### Translating Rectangles

Translation describes the geometric operation of moving a shape from one location to another.

Use the `offsetBy` method (or `CGRectOffset` function in Objective-C) to translate a rectangle’s origin by a specified `x` and `y` distance.

``````rectangle.offsetBy(dx: 2.0, dy: 2.0) // {x 3 y 4 w 4 h 3}
``````

Consider using this method whenever you shift a rectangle’s position. Not only does it save a line of code, but it more semantically represents intended operation than manipulating the origin values individually.

### Contracting and Expanding Rectangles

Other common transformations for rectangles include contraction and expansion around a center point. The `insetBy(dx:dy:)` method can accomplish both.

When passed a positive value for either component, this method returns a rectangle that shrinks by the specified amount from each side as computed from the center point. For example, when inset by `1.0` horizontally (`dy = 0.0`), a rectangle originating at `(1, 2)` with a width of `4` and `height` equal to `3`, produces a new rectangle originating at `(2, 2)` with width equal to `2` and height equal to `3`. Which is to say: the result of insetting a rectangle by `1` point horizontally is a rectangle whose `width` is `2` points smaller than the original.

``````rectangle // {x 1 y 2 w 4 h 3}
rectangle.insetBy(dx: 1.0, dy: 0.0) // {x 2 y 2 w 2 h 3}
``````

When passed a negative value for either component, the rectangle grows by that amount from each side. When passed a non-integral value, this method may produce a rectangle with non-integral components.

``````rectangle.insetBy(dx: -1.0, dy: 0.0) // {x 0 y 2 w 6 h 3}
rectangle.insetBy(dx: 0.5, dy: 0.0) // {x 1.5 y 2 w 3 h 3}
``````

## Identities and Special Values

Points, sizes, and rectangles each have a `zero` property, which defines the identity value for each respective type:

``````CGPoint.zero // {x 0 y 0}
CGSize.zero // {w 0 h 0}
CGRect.zero // {x 0 y 0 w 0 h 0}
``````

Swift shorthand syntax allows you to pass `.zero` directly as an argument for methods and initializers, such as `CGRect.init(origin:size:)`:

``````let square = CGRect(origin: .zero,
size: CGSize(width: 4.0, height: 4.0))
``````

`CGRect` has two additional special values: `infinite` and `null`:

``````CGRect.infinite // {x -∞ y -∞ w +∞ h +∞}
CGRect.null // {x +∞ y +∞ w 0 h 0}
``````

`CGRect.null` is conceptually similar to `NSNotFound`, in that it represents the absence of an expected value, and does so using the largest representable number to exclude all other values.

`CGRect.infinite` has even more interesting properties, as it intersects with all points and rectangles, contains all rectangles, and its union with any rectangle is itself.

``````CGRect.infinite.contains(any point) // true
CGRect.infinite.intersects(any other rectangle) // true
CGRect.infinite.union(any other rectangle) // CGRect.infinite
``````

Use `isInfinite` to determine whether a rectangle is, indeed, infinite.

``````CGRect.infinite.isInfinite // true
``````

But to fully appreciate why these values exist and how they’re used, let’s talk about geometric relationships:

## Relationships

Up until this point, we’ve been dealing with geometries in isolation. To round out our discussion, let’s consider what’s possible when evaluating two or more rectangles.

### Intersection

Two rectangles intersect if they overlap. Their intersection is the smallest rectangle that encompasses all points contained by both rectangles.

In Swift, you can use the `intersects(_:)` and `intersection(_:)` methods to efficiently compute the intersection of two `CGRect` values:

``````let square = CGRect(origin: .zero,
size: CGSize(width: 4.0, height: 4.0))
square // {x 0 y 0 w 4 h 4}

rectangle.intersects(square) // true
rectangle.intersection(square) // {x 1 y 2 w 3 h 2}
``````

If two rectangles don’t intersect, the `intersection(_:)` method produces `CGRect.null`:

``````rectangle.intersects(.zero) // false
rectangle.intersection(.zero) // CGRect.null
``````

### Union

The union of two rectangles is the smallest rectangle that encompasses all of the points contained by either rectangle.

In Swift, the aptly-named `union(_:)` method does just this for two `CGRect` values:

``````rectangle.union(square) // {x 0 y 0 w 5 h 5}
``````

So what if you didn’t pay attention in Geometry class — this is the real world. And in the real world, you have `CGGeometry.h` and all of the types and functions it provides.

Know it well, and you’ll be on your way to discovering great new user interfaces in your apps. Do a good enough job with that, and you may encounter the best arithmetic problem of all: adding up all the money you’ve made with your awesome new app. Mathematical!

NSMutableHipster

Questions? Corrections? Issues and pull requests are always welcome.

This article uses Swift version 5.0 and was last reviewed on April 22, 2019. Find status information for all articles on the status page.

Written by Mattt

Mattt (@mattt) is a writer and developer in Portland, Oregon.

Next Article

# Optional, throws, Result, async/await

An exploration of error handling in Swift: then, now, and soon.