Skip to content

Commit

Permalink
Easings (#97)
Browse files Browse the repository at this point in the history
  • Loading branch information
finnvoor authored Oct 24, 2024
1 parent 59da2b3 commit b0e54b4
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 33 deletions.
3 changes: 2 additions & 1 deletion .swiftformat
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
--ifdef no-indent
--funcattributes same-line
--typeattributes same-line
--varattributes same-line
--storedvarattrs same-line
--computedvarattrs same-line
--ranges no-space
--header strip
--selfrequired log,debug,info,notice,warning,trace,error,critical,fault
2 changes: 2 additions & 0 deletions Sources/PlaydateKit/Core/Display.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public enum Display {
case fourTimes = 4
case eightTimes = 8

// MARK: Public

public var next: Scale {
switch self {
case .oneTimes: .twoTimes
Expand Down
128 changes: 128 additions & 0 deletions Sources/PlaydateKit/Core/Easing.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
public enum Easing {
case inSine
case outSine
case inOutSine
case inQuad
case outQuad
case inOutQuad
case inCubic
case outCubic
case inOutCubic
case inQuart
case outQuart
case inOutQuart
case inQuint
case outQuint
case inOutQuint
case inExpo
case outExpo
case inOutExpo
case inCirc
case outCirc
case inOutCirc
case inBack
case outBack
case inOutBack
case inElastic
case outElastic
case inOutElastic
case inBounce
case outBounce
case inOutBounce

// MARK: Public

public func ease(_ value: Float, duration: Float = 1, scale: ClosedRange<Float> = 0...1) -> Float {
var x = value.scaled(from: 0...duration, to: 0...1)
.clamped(to: 0...1)
let ease = {
switch self {
case .inSine:
return 1 - cosf((x * Float.pi) / 2)
case .outSine:
return sinf((x * Float.pi) / 2)
case .inOutSine:
return -(cosf(Float.pi * x) - 1) / 2
case .inQuad:
return x * x
case .outQuad:
return 1 - (1 - x) * (1 - x)
case .inOutQuad:
return x < 0.5 ? 2 * x * x : 1 - powf(-2 * x + 2, 2) / 2
case .inCubic:
return x * x * x
case .outCubic:
return 1 - powf(1 - x, 3)
case .inOutCubic:
return x < 0.5 ? 4 * x * x * x : 1 - powf(-2 * x + 2, 3) / 2
case .inQuart:
return x * x * x * x
case .outQuart:
return 1 - powf(1 - x, 4)
case .inOutQuart:
return x < 0.5 ? 8 * x * x * x * x : 1 - powf(-2 * x + 2, 4) / 2
case .inQuint:
return x * x * x * x * x
case .outQuint:
return 1 - powf(1 - x, 5)
case .inOutQuint:
return x < 0.5 ? 16 * x * x * x * x * x : 1 - powf(-2 * x + 2, 5) / 2
case .inExpo:
return x == 0 ? 0 : powf(2, 10 * x - 10)
case .outExpo:
return x == 1 ? 1 : 1 - powf(2, -10 * x)
case .inOutExpo:
return x == 0 ? 0 : x == 1 ? 1 : x < 0.5 ? powf(2, 20 * x - 10) / 2 : 2 - powf(2, -20 * x + 10) / 2
case .inCirc:
return 1 - sqrtf(1 - powf(x, 2))
case .outCirc:
return sqrtf(1 - powf(x - 1, 2))
case .inOutCirc:
return x < 0.5 ? (1 - sqrtf(1 - powf(2 * x, 2))) / 2 : (sqrtf(1 - powf(-2 * x + 2, 2)) + 1) / 2
case .inBack:
let c1: Float = 1.70158
let c3: Float = c1 + 1
return c3 * x * x * x - c1 * x * x
case .outBack:
let c1: Float = 1.70158
let c3: Float = c1 + 1
return 1 + c3 * powf(x - 1, 3) + c1 * powf(x - 1, 2)
case .inOutBack:
let c1: Float = 1.70158
let c2: Float = c1 * 1.525
return x < 0.5 ? (powf(2 * x, 2) * ((c2 + 1) * 2 * x - c2)) / 2 : (powf(2 * x - 2, 2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2
case .inElastic:
let c4: Float = (2 * Float.pi) / 3
return x == 0 ? 0 : x == 1 ? 1 : -powf(2, 10 * x - 10) * sinf((x * 10 - 10.75) * c4)
case .outElastic:
let c4: Float = (2 * Float.pi) / 3
return x == 0 ? 0 : x == 1 ? 1 : powf(2, -10 * x) * sinf((x * 10 - 0.75) * c4) + 1
case .inOutElastic:
let c5: Float = (2 * Float.pi) / 4.5
return x == 0 ? 0 : x == 1 ? 1 : x < 0.5 ? -(powf(2, 20 * x - 10) * sinf((20 * x - 11.125) * c5)) / 2 : (powf(2, -20 * x + 10) * sinf((20 * x - 11.125) * c5)) / 2 + 1
case .inBounce:
return 1 - Easing.outBounce.ease(1 - x)
case .outBounce:
let n1: Float = 7.5625
let d1: Float = 2.75

if x < 1 / d1 {
return n1 * x * x
} else if x < 2 / d1 {
x -= 1.5 / d1
return n1 * x * x + 0.75
} else if x < 2.5 / d1 {
x -= 2.25 / d1
return n1 * x * x + 0.9375
} else {
x -= 2.625 / d1
return n1 * x * x + 0.984375
}
case .inOutBounce:
return x < 0.5 ? (1 - Easing.outBounce.ease(1 - 2 * x)) / 2 : (1 + Easing.outBounce.ease(2 * x - 1)) / 2
}
}()

return ease.scaled(from: 0...1, to: scale)
}
}
4 changes: 2 additions & 2 deletions Sources/PlaydateKit/Core/Graphics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,8 @@ public enum Graphics {
}

/// Returns the height of the font.
public var height: UInt8 {
graphics.getFontHeight.unsafelyUnwrapped(pointer)
public var height: Int {
Int(graphics.getFontHeight.unsafelyUnwrapped(pointer))
}

/// Returns the width of the given `text` in the font.
Expand Down
63 changes: 33 additions & 30 deletions Sources/PlaydateKit/Core/UI/UI.swift
Original file line number Diff line number Diff line change
@@ -1,35 +1,16 @@
public enum UI {
public final class CrankIndicator {
private var lastScale: Display.Scale?
private var bubbleX: Int = 0
private var bubbleY: Int = 0
private var bubbleWidth: Int = 0
private var bubbleHeight: Int = 0
private var bubbleImage: Graphics.Bitmap?

private var lastTime: CUnsignedInt = 0
private var currentFrame: Int = 1
private var currentScale: Display.Scale?

private var crankIndicatorY: Int = 0

private var bubbleFlip: Graphics.Bitmap.Flip = .unflipped
private var textOffset = 76

private var crankFrames: Graphics.BitmapTable?
private var frameCount = 0

private var textFrameImage: Graphics.Bitmap?
private var textFrameCount = 14

public var clockwise: Bool
// MARK: Lifecycle

public init(clockwise: Bool = true) {
self.clockwise = clockwise
}

public func draw(xOffset: Int = 0, yOffset: Int = 0) throws(Playdate.Error) {
// MARK: Public

public var clockwise: Bool

public func draw(xOffset: Int = 0, yOffset: Int = 0) throws(Playdate.Error) {
var xOffset = xOffset
var yOffset = yOffset

Expand Down Expand Up @@ -90,12 +71,11 @@ public enum UI {
Graphics.drawBitmap(
frame,
at: Point(
x: (bubbleX + xOffset + (textOffset - frameWidth) / 2),
y: (bubbleY + yOffset + (bubbleHeight - frameHeight) / 2)
x: bubbleX + xOffset + (textOffset - frameWidth) / 2,
y: bubbleY + yOffset + (bubbleHeight - frameHeight) / 2
)
)
} else {

// draw text
if let textFrameImage {
let (textWidth, textHeight, _) = textFrameImage.getData(mask: nil, data: nil)
Expand All @@ -105,8 +85,8 @@ public enum UI {
Graphics.drawBitmap(
textFrameImage,
at: Point(
x: (bubbleX + xOffset + (textOffset - textWidth) / 2),
y: (bubbleY + yOffset + (bubbleHeight - textHeight) / 2)
x: bubbleX + xOffset + (textOffset - textWidth) / 2,
y: bubbleY + yOffset + (bubbleHeight - textHeight) / 2
)
)
}
Expand Down Expand Up @@ -134,8 +114,31 @@ public enum UI {
currentFrame = 1
}

private func loadImages(for scale: Display.Scale) throws(Playdate.Error) {
// MARK: Private

private var lastScale: Display.Scale?
private var bubbleX: Int = 0
private var bubbleY: Int = 0
private var bubbleWidth: Int = 0
private var bubbleHeight: Int = 0
private var bubbleImage: Graphics.Bitmap?

private var lastTime: CUnsignedInt = 0
private var currentFrame: Int = 1
private var currentScale: Display.Scale?

private var crankIndicatorY: Int = 0

private var bubbleFlip: Graphics.Bitmap.Flip = .unflipped
private var textOffset = 76

private var crankFrames: Graphics.BitmapTable?
private var frameCount = 0

private var textFrameImage: Graphics.Bitmap?
private var textFrameCount = 14

private func loadImages(for scale: Display.Scale) throws(Playdate.Error) {
lastTime = 0
currentFrame = 1
currentScale = Display.scale
Expand Down
12 changes: 12 additions & 0 deletions Sources/PlaydateKit/Utils/Comparable+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
public extension Comparable {
func clamped(to range: ClosedRange<Self>) -> Self {
max(min(self, range.upperBound), range.lowerBound)
}
}

public extension Comparable where Self: FloatingPoint {
func scaled(from inputRange: ClosedRange<Self>, to outputRange: ClosedRange<Self>) -> Self {
(self - inputRange.lowerBound) * (outputRange.upperBound - outputRange.lowerBound) /
(inputRange.upperBound - inputRange.lowerBound) + outputRange.lowerBound
}
}

0 comments on commit b0e54b4

Please sign in to comment.