When working on code where I’m doing a lot of calculation in geometric spaces such as layout, animation, or color, I often find myself using the concept of a floating point *interval*: a closed range on the floating point number line. However, the existing `ClosedRange`

requires its bounds to be ordered, which makes it less than useful for geometric calculations where a coordinates can travel or be interpolated in either direction.

I have developed a pure Swift type, `Interval`

along with an operator to construct such intervals that I have found very useful, and think others might find it useful too. It might be worth considering including in the Standard Library.

- The Swift package including unit tests is available here.
- The discussion of this on the Swift Forums is here.

## Definition

The basic type is defined as follows:

```
public struct Interval<T: FloatingPoint> : Equatable, Hashable {
public typealias Bound = T
public let a: Bound
public let b: Bound
public init(_ a: Bound, _ b: Bound) {
self.a = a
self.b = b
}
}
```

## Interval Formation Operator

In addition, a new *interval formation operator* is introduced:

```
infix operator .. : RangeFormationPrecedence
public func .. <T>(left: T, right: T) -> Interval<T> {
Interval(left, right)
}
```

This operator, like the range formation operators, makes it easy to create Intervals in code:

```
let i = 0..100
let j = 100..3.14
```

In practice I have not found any trouble keeping the new operator `..`

and the existing range formation operators `...`

and `..<`

distinct in my mind.

## Geometric Queries

Unlike `ClosedRange`

, `a`

and `b`

can be in any order. This means this type has to deal with the various cases that entails, and thus it also provides computed attributes like:

`isAscending` |
is `a` < `b` ? |

`isDescending` |
is `a` > `b` ? |

`isEmpty` |
is `a` == `b` ? This is different from `ClosedRange` , which always returns `true` . |

`reversed` |
`Interval(b, a)` |

`normalized` |
returns Interval with values ordered so `isAscending` is `true` |

`min` |
`min(a, b)` |

`max` |
`max(a, b)` |

Because `Interval`

is designed to be used with geometric calculations, it contains a number of geometric operators:

`extent` |
`b - a` |

`contains` |
Does `self` contain a particular coordinate? |

`contains` |
Does `self` fully contain another `Interval` ? |

`intersects` |
Does `self` intersect (overlap) another `Interval` ? |

`intersection` |
Returns an `Interval` which is the overlapping part of `self` and another `Interval` , or `nil` if they don’t intersect. |

`union` |
Returns an `Interval` subtending the greatest extents of `self` and another interval. |

## Linear Interpolation

One place `Interval`

shines is when you need to do linear interpolation. An extension on `FloatingPoint`

makes every one of these types interpolable into and out of normalized unit spaces and between spaces.

Interpolation is not clamped, so interpolating a value outside the interval `0..1`

to another interval results in extrapolation.

```
extension FloatingPoint {
/// The value linearly interpolated from the unit interval `0..1` to the interval `a..b`.
public func interpolated(to i: Interval<Self>) -> Self
/// The value linearly interpolated from the interval `a..b` into the unit interval `0..1`.
public func interpolated(from i: Interval<Self>) -> Self
/// The value linearly interpolated from the interval `i1` to the interval `i2`.
public func interpolated(from i1: Interval<Self>, to i2: Interval<Self>) -> Self
}
```

Here is an example of using `Interval`

to interpolate between two colors:

```
import Interval
struct Color : CustomStringConvertible {
let r, g, b: Double
private func f(_ n: Double) -> String { return String(format: "%.2f", n) }
var description: String { "(r: \(f(r)), g: \(f(g)), b: \(f(b)))" }
func interpolated(to other: Color, at t: Double) -> Color {
Color(
r: t.interpolated(to: r..other.r),
g: t.interpolated(to: g..other.g),
b: t.interpolated(to: b..other.b)
)
}
}
let darkTurquoise = Color(r: 0, g: 0.8, b: 0.81)
let salmon = Color(r: 0.98, g: 0.5, b: 0.45)
for t in stride(from: 0.0, to: 1.1, by: 0.1) {
let c = darkTurquoise.interpolated(to: salmon, at: t)
print(String(format: "%.1f", t) + ": " + c.description)
}
```

When run, the output below is produced. Notice that the `r`

value is increasing while `g`

and `b`

are decreasing.

```
0.0: (r: 0.00, g: 0.80, b: 0.81)
0.1: (r: 0.10, g: 0.77, b: 0.77)
0.2: (r: 0.20, g: 0.74, b: 0.74)
0.3: (r: 0.29, g: 0.71, b: 0.70)
0.4: (r: 0.39, g: 0.68, b: 0.67)
0.5: (r: 0.49, g: 0.65, b: 0.63)
0.6: (r: 0.59, g: 0.62, b: 0.59)
0.7: (r: 0.69, g: 0.59, b: 0.56)
0.8: (r: 0.78, g: 0.56, b: 0.52)
0.9: (r: 0.88, g: 0.53, b: 0.49)
1.0: (r: 0.98, g: 0.50, b: 0.45)
```

## Interoperability with ClosedRange

To provide interoperability with `ClosedRange`

, conversion constructors are provided. When converting from `Interval`

to `ClosedRange`

, the bounds are normalized so `a`

<= `b`

.

```
extension Interval {
public init(_ r: ClosedRange<Bound>)
}
extension ClosedRange where Bound: FloatingPoint {
public init(_ i: Interval<Bound>)
}
```

## Interoperability with Random Number Generation

Extensions on `Float`

, `Double`

, and `CGFloat`

provide the ability to produce random numbers in an interval.

```
extension Double {
public static func random(in interval: Interval<Self>) -> Self
public static func random<T: RandomNumberGenerator>(in interval: Interval<Self>, using generator: inout T) -> Self
}
```