enum SamplingMode { case cumulative // e.g., distance, energy case instantaneous // e.g., heart rate, altitude } struct Point: Identifiable, Equatable, Hashable { var x, y: Double var id: Double { x } /** Interpolate the y value at an x coordinate toward another point. */ func interpolate(to other: Point, at location: Double) -> Double { let totalX = other.x - x if totalX == 0 { return (y + other.y) / 2 } let diffX = location - x if diffX == 0 { return y } let ratio = diffX / totalX return y + (other.y - y) * ratio } static let zero = Point(x: 0, y: 0) } extension Array where Element == Point { func resample(numberOfSamples: Int, minX: Double? = nil, maxX: Double? = nil, mode: SamplingMode) -> [Point] { guard count >= 2, numberOfSamples > 0 else { return [] } let firstX = minX ?? first!.x let lastX = maxX ?? last!.x let totalDuration = lastX - firstX guard totalDuration > 0 else { return [] } let interval = totalDuration / Double(numberOfSamples) var result: [Point] = .init(repeating: .zero, count: numberOfSamples) var currentIndex = 0 var current = self[0] for i in 0.. 0 ? accumulated / accumulatedDuration : current.y } } return result } func resample( numberOfSamples: Int, minX: Double? = nil, maxX: Double? = nil, mode: SamplingMode ) -> [Double] { guard count >= 2, numberOfSamples > 0 else { return [] } let minX = minX ?? first!.x let maxX = maxX ?? last!.x let totalDuration = maxX - minX guard totalDuration > 0 else { return [] } let interval = totalDuration / Double(numberOfSamples) var result = [Double](repeating: 0.0, count: numberOfSamples) var currentIndex = 0 var current = self[0] for i in 0.. 0 ? accumulated / accumulatedDuration : current.y } result[i] = value } return result } } extension Sequence { /// Resamples any sequence of elements into evenly spaced intervals. /// /// - Parameters: /// - numberOfSamples: Number of output points /// - xSelector: Closure to get the independent variable (e.g., time) /// - ySelector: Closure to get the dependent variable (e.g., distance, heart rate) /// - mode: .cumulative or .instantaneous /// - Returns: Array of Points with evenly spaced `x` and averaged `y` func resample( numberOfSamples: Int, x: (Element) -> Double, y: (Element) -> Double, mode: SamplingMode ) -> [Point] { let points = self.map { Point(x: x($0), y: y($0)) } return points.resample(numberOfSamples: numberOfSamples, mode: mode) } func resample( numberOfSamples: Int, minX: Double? = nil, maxX: Double? = nil, x: (Element) -> Double, y: (Element) -> Double, mode: SamplingMode ) -> [Double] { let points = self.map { Point(x: x($0), y: y($0)) } return points.resample( numberOfSamples: numberOfSamples, minX: minX, maxX: maxX, mode: mode) } }