Skip to content

Instantly share code, notes, and snippets.

@aheze
Created December 31, 2023 19:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aheze/c9b80aa9bf8e4e82c3637800e48e114e to your computer and use it in GitHub Desktop.
Save aheze/c9b80aa9bf8e4e82c3637800e48e114e to your computer and use it in GitHub Desktop.
public struct GraphDomains {
public var xDomain: ClosedRange<Double>
public var yDomain: ClosedRange<Double>
}
func getStride(domains: GraphDomains) -> (xStride: [Double], yStride: [Double])? {
guard let boundsSize else {
return nil
}
var xCanvasStep: Double
var yCanvasStep: Double
if useAlignmentSquare {
if boundsSize.height > boundsSize.width {
xCanvasStep = Self.calculateCanvasStep(chartVisibleDomain: domains.xDomain.length, numberOfStepsInGraph: numberOfStepsInGraph.0)
yCanvasStep = xCanvasStep
} else {
yCanvasStep = Self.calculateCanvasStep(chartVisibleDomain: domains.yDomain.length, numberOfStepsInGraph: numberOfStepsInGraph.1)
xCanvasStep = yCanvasStep
}
} else {
// calculate separately
xCanvasStep = Self.calculateCanvasStep(chartVisibleDomain: domains.xDomain.length, numberOfStepsInGraph: numberOfStepsInGraph.0)
yCanvasStep = Self.calculateCanvasStep(chartVisibleDomain: domains.yDomain.length, numberOfStepsInGraph: numberOfStepsInGraph.1)
}
let xStart = Self.roundSpanMagnitudeUp(domains.xDomain.lowerBound, toNearest: xCanvasStep)
let xEnd = Self.roundSpanMagnitudeUp(domains.xDomain.upperBound, toNearest: xCanvasStep)
let yStart = Self.roundSpanMagnitudeUp(domains.yDomain.lowerBound, toNearest: yCanvasStep)
let yEnd = Self.roundSpanMagnitudeUp(domains.yDomain.upperBound, toNearest: yCanvasStep)
let markStrideX = Array(stride(from: xStart, through: xEnd, by: xCanvasStep))
let markStrideY = Array(stride(from: yStart, through: yEnd, by: yCanvasStep))
return (markStrideX, markStrideY)
}
static func calculateCanvasStep(chartVisibleDomain: Double, numberOfStepsInGraph: Int) -> Double {
let graphCanvasStep = roundToKeyNumber(chartVisibleDomain / Double(numberOfStepsInGraph))
return graphCanvasStep
}
// Round up to the nearest step (to make sure the grid lines have a step at 0 exactly)
static func roundSpanMagnitudeUp(_ number: Double, toNearest step: Double) -> Double {
if number < 0 {
return -roundSpanPositiveUp(-number, toNearest: step)
} else {
return roundSpanPositiveUp(number, toNearest: step)
}
}
// round a number up to a step, for the first graph line
// for positive numbers only
static func roundSpanPositiveUp(_ number: Double, toNearest step: Double) -> Double {
// Ensure step is positive and non-zero to avoid division by zero or negative step issues
guard step > 0 else { return number }
let factor = ceil(number / step)
return factor * step
}
// for the graph
static func roundToKeyNumber(_ number: Double) -> Double {
if number <= 0 { return 0 } // Handle non-positive numbers
let (scale, normalizedNumber): (Double, Double) = {
if number < 1 {
let digitCount = ceil(-log10(number))
let scale = pow(10, -digitCount)
let normalizedNumber = number / scale
return (scale, normalizedNumber)
} else {
let scale = pow(10, floor(log10(number)))
let normalizedNumber = number / scale
return (scale, normalizedNumber)
}
}()
let roundedNormalized: Double
switch normalizedNumber {
case ..<1.5:
roundedNormalized = 1
case ..<3.5:
roundedNormalized = 2
case ..<7.5:
roundedNormalized = 5
default:
roundedNormalized = 10
}
return roundedNormalized * scale
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment