Skip to content

Instantly share code, notes, and snippets.

@scottfister
Created July 27, 2021 19:44
Show Gist options
  • Save scottfister/5eb35e07624ddf84590707b21e37d151 to your computer and use it in GitHub Desktop.
Save scottfister/5eb35e07624ddf84590707b21e37d151 to your computer and use it in GitHub Desktop.
Swift Modulate Function
public extension FloatingPoint {
typealias ModulationRange = (lowerBound: Self, upperBound: Self)
/// Translates the given floating point between two ranges.
/// Limit defaults to true. If limit is set to false, the value returned can be outside of the "to" range.
///
/// Using the Modulate function
/// ----------------------------------------------------------
/// Say you'd like to transform a view's scale from `1.0` to `0.45` depending on some other factor, like scroll amount.
/// The `from` could be `0...120` and the `to` would be`1.0...0.45`, which would mean at `0` content offset, the transform would be `1.0`
/// and at `120` the transform would be `0.45`. The modulate function will interpolate the values in between.
///
/// let offset: CGFloat = 60
/// offset.modulate(from: (0, 120), to: (1.0, 0.45))
/// // returns 0.825
///
/// - Parameter limit: Whether to clamp the return value to the `to` range.
/// - Returns: A new scalar interpolated from the ranges.
func modulate(from: ModulationRange, to: ModulationRange, limit: Bool = true) -> Self {
let result = to.lowerBound + ((self - from.lowerBound) / (from.upperBound - from.lowerBound)) * (to.upperBound - to.lowerBound)
if limit {
if to.lowerBound < to.upperBound {
if result < to.lowerBound { return to.lowerBound }
if result > to.upperBound { return to.upperBound }
} else {
if result > to.lowerBound { return to.lowerBound }
if result < to.upperBound { return to.upperBound }
}
}
return result
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment