Swift - `Scale Number in a Range min - max
edit/update: Xcode 11.5 • Swift 5.2
extension FloatingPoint {
func converting(from input: ClosedRange<Self>, to output: ClosedRange<Self>) -> Self {
let x = (output.upperBound - output.lowerBound) * (self - input.lowerBound)
let y = (input.upperBound - input.lowerBound)
return x / y + output.lowerBound
}
}
extension BinaryInteger {
func converting(from input: ClosedRange<Self>, to output: ClosedRange<Self>) -> Self {
let x = (output.upperBound - output.lowerBound) * (self - input.lowerBound)
let y = (input.upperBound - input.lowerBound)
return x / y + output.lowerBound
}
}
let integer = 380
let result = integer.converting(from: 10...750, to: 100...350) // 225
let double = 750.0
let result = double.converting(from: 10.1...750.0, to: 0...350) // 350
Here is the Swift 3 compatible version, which also supports generics (to not bound Rescale just for Doubles).
struct Rescale<Type : BinaryFloatingPoint> {
typealias RescaleDomain = (lowerBound: Type, upperBound: Type)
var fromDomain: RescaleDomain
var toDomain: RescaleDomain
init(from: RescaleDomain, to: RescaleDomain) {
self.fromDomain = from
self.toDomain = to
}
func interpolate(_ x: Type ) -> Type {
return self.toDomain.lowerBound * (1 - x) + self.toDomain.upperBound * x;
}
func uninterpolate(_ x: Type) -> Type {
let b = (self.fromDomain.upperBound - self.fromDomain.lowerBound) != 0 ? self.fromDomain.upperBound - self.fromDomain.lowerBound : 1 / self.fromDomain.upperBound;
return (x - self.fromDomain.lowerBound) / b
}
func rescale(_ x: Type ) -> Type {
return interpolate( uninterpolate(x) )
}
}
Example would be:
Rescale(from: (15, 85), to: (0, 100)).rescale(85)
I translated this to Swift from this answer here based on the D3 library.
struct Rescale {
var range0: Double, range1: Double, domain0: Double, domain1: Double
init( domain0: Double, domain1: Double, range0: Double, range1: Double ) {
self.range0 = range0
self.range1 = range1
self.domain0 = domain0
self.domain1 = domain1
}
func interpolate( x: Double ) -> Double {
return range0 * (1 - x) + range1 * x;
}
func uninterpolate( x: Double) -> Double {
let b: Double = (domain1 - domain0) != 0 ? domain1 - domain0 : 1 / domain1;
return (x - domain0) / b
}
func rescale( x: Double ) -> Double {
return interpolate( uninterpolate(x) )
}
}
Usage:
let scaleUp = Rescale( domain0: 15, domain1: 85, range0: 0, range1: 100 )
let scaleDown = Rescale( domain0: 0, domain1: 100, range0: 15, range1: 85 )
// scales it to 0 - 100 from the input range 15 - 85
let scaledValue = scaleUp.rescale( 85 ) // = 100
// or the other way...
let scaledValue = scaleDown.rescale( 100 ) // = 85