Skip to content

Commit

Permalink
Bug Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Lakr233 committed Aug 17, 2024
1 parent f5235cc commit e6c22db
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 79 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ import MetalKit
import ColorfulX

let view = AnimatedMulticolorGradientView()
view.setColors(color, interpolationEnabled: false)
view.setColors(color, animated: false)
view.speed = speed
view.transitionDuration = transitionDuration
view.noise = noise
Expand Down
82 changes: 55 additions & 27 deletions Sources/ColorfulX/AnimatedMulticolorGradientView+Speckle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,36 @@ public extension AnimatedMulticolorGradientView {
struct Speckle {
public var enabled: Bool

public var targetColor: ColorVector
public var previousColor: ColorVector
public var targetColor: ColorVector {
didSet { assert(targetColor.space == .lab) }
}

public var previousColor: ColorVector {
didSet { assert(targetColor.space == .lab) }
}

public var transitionProgress: SpringInterpolation
public var position: SpringInterpolation2D

public var isTransitionCompleted: Bool {
transitionProgress.context.currentPos >= 1
}

public var color: ColorVector {
let progress = transitionProgress.context.currentPos
return previousColor.lerp(to: targetColor, percent: progress)
}

public init(
enabled: Bool = false,
targetColor: ColorVector = .init(space: .rgb),
previousColor: ColorVector = .init(space: .rgb),
targetColor: ColorVector = .init(space: .lab),
previousColor: ColorVector = .init(space: .lab),
transitionProgress: Double = 1,
position: SpringInterpolation2D = .init()
) {
assert(targetColor.space == .lab)
assert(previousColor.space == .lab)

self.enabled = enabled
self.targetColor = targetColor
self.previousColor = previousColor
Expand All @@ -41,34 +59,44 @@ public extension AnimatedMulticolorGradientView {
}
}

extension AnimatedMulticolorGradientView.Speckle {
var colorVector: ColorVector {
let progress = transitionProgress.context.currentPos
return previousColor.lerp(to: targetColor, percent: progress)
}
}

public extension AnimatedMulticolorGradientView {
func setColors(_ colors: [ColorVector], interpolationEnabled: Bool = true, repeatToFillColorSlots: Bool = true) {
func setColors(_ colors: [ColorVector], animated: Bool = true, repeats: Bool = true) {
var colors = colors
if colors.isEmpty { colors.append(.init(v: .zero, space: .rgb)) }
if colors.isEmpty { colors.append(.init(v: .zero, space: .lab)) }
colors = colors.map { $0.color(in: .lab) }

let endingIndex = repeatToFillColorSlots ? Uniforms.COLOR_SLOT : min(colors.count, Uniforms.COLOR_SLOT)
guard endingIndex > 0 else { return }

alteringSpeckles { speckles in
for idx in 0 ..< endingIndex {
var read = speckles[idx]
let color: ColorVector = colors[idx % colors.count]
guard read.targetColor != color else { continue }
let interpolationEnabled = interpolationEnabled && read.enabled
read.enabled = true
read.targetColor = color
read.previousColor = interpolationEnabled ? read.colorVector : color
read.transitionProgress.setCurrent(interpolationEnabled ? 0 : 1, 0)
speckles[idx] = read
let endingIndex = repeats
? Uniforms.COLOR_SLOT
: min(colors.count, Uniforms.COLOR_SLOT)

alteringSpeckleByIteratingValues { speckle, idx in
guard idx < endingIndex else {
speckle.enabled = false
return
}

speckle.enabled = true

let newColor: ColorVector = colors[idx % colors.count]
guard speckle.targetColor != newColor else { return }

if animated {
speckle.previousColor = speckle.color
speckle.targetColor = newColor
speckle.transitionProgress.setCurrent(0)
} else {
speckle.previousColor = newColor
speckle.targetColor = newColor
speckle.transitionProgress.setCurrent(1)
}
}
}
}

extension SpringInterpolation2D {
var distanceToTarget: CGFloat {
let dx = x.context.currentPos - x.context.targetPos
let dy = y.context.currentPos - y.context.targetPos
return sqrt(dx * dx + dy * dy)
}
}
20 changes: 10 additions & 10 deletions Sources/ColorfulX/AnimatedMulticolorGradientView+SwiftUI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public struct AnimatedMulticolorGradientViewRepresentable {
@Binding var renderScale: Double
@Binding var pauseRender: Bool

let repeatToFillColorSlots: Bool
let repeats: Bool

public init(
color: Binding<[ColorVector]>,
Expand All @@ -29,7 +29,7 @@ public struct AnimatedMulticolorGradientViewRepresentable {
frameLimit: Binding<Int> = .constant(0),
renderScale: Binding<Double> = .constant(1),
pauseRender: Binding<Bool> = .constant(false),
repeatToFillColorSlots: Bool = true
repeats: Bool = true
) {
_color = color
_speed = speed
Expand All @@ -40,17 +40,17 @@ public struct AnimatedMulticolorGradientViewRepresentable {
_renderScale = renderScale
_pauseRender = pauseRender

self.repeatToFillColorSlots = repeatToFillColorSlots
self.repeats = repeats
}

public func updatePropertyToView(_ view: AnimatedMulticolorGradientView) {
public func updatePropertyToView(_ view: AnimatedMulticolorGradientView, initialSetup: Bool) {
view.frameLimit = frameLimit
view.metalLink?.scaleFactor = renderScale

view.setColors(
color,
interpolationEnabled: transitionSpeed > 0,
repeatToFillColorSlots: repeatToFillColorSlots
animated: transitionSpeed > 0 && !initialSetup,
repeats: repeats
)
view.speed = speed
view.bias = bias
Expand All @@ -66,12 +66,12 @@ public struct AnimatedMulticolorGradientViewRepresentable {
extension AnimatedMulticolorGradientViewRepresentable: UIViewRepresentable {
public func makeUIView(context _: Context) -> AnimatedMulticolorGradientView {
let view = AnimatedMulticolorGradientView()
updatePropertyToView(view)
updatePropertyToView(view, initialSetup: true)
return view
}

public func updateUIView(_ uiView: AnimatedMulticolorGradientView, context _: Context) {
updatePropertyToView(uiView)
updatePropertyToView(uiView, initialSetup: false)
}
}
#endif
Expand All @@ -82,12 +82,12 @@ public struct AnimatedMulticolorGradientViewRepresentable {
extension AnimatedMulticolorGradientViewRepresentable: NSViewRepresentable {
public func makeNSView(context _: Context) -> AnimatedMulticolorGradientView {
let view = AnimatedMulticolorGradientView()
updatePropertyToView(view)
updatePropertyToView(view, initialSetup: true)
return view
}

public func updateNSView(_ nsView: AnimatedMulticolorGradientView, context _: Context) {
updatePropertyToView(nsView)
updatePropertyToView(nsView, initialSetup: false)
}
}
#endif
63 changes: 29 additions & 34 deletions Sources/ColorfulX/AnimatedMulticolorGradientView+Update.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,51 +16,46 @@ extension AnimatedMulticolorGradientView {
}

func initializeRenderParameters() {
var rand = randomLocationPair()
for idx in 0 ..< speckles.count {
rand = randomLocationPair()
alteringSpeckles { speckles in
speckles[idx].position.setCurrent(.init(x: rand.x, y: rand.y))
rand = randomLocationPair()
speckles[idx].position.setTarget(.init(x: rand.x, y: rand.y))
}
alteringSpeckleByIteratingValues { speckle in
let rand = randomLocationPair()
speckle.transitionProgress.setTarget(1)
speckle.position.setCurrent(.init(x: rand.x, y: rand.y))
speckle.position.setTarget(.init(x: rand.x, y: rand.y))
}
}

func updateRenderParameters(deltaTime: Double) {
print("[*] render parameter update at \(obtainCurrentTimestamp())")

defer { renderInputWasModified = false }

let moveDelta = deltaTime * speed * 0.5 // just slow down

var points: [MulticolorGradientView.Parameters.ColorStop]?
alteringSpeckles { speckles in
for idx in 0 ..< speckles.count where speckles[idx].enabled {
if speckles[idx].transitionProgress.context.currentPos < 1 {
speckles[idx].transitionProgress.update(withDeltaTime: deltaTime * transitionSpeed)
}
if moveDelta > 0 {
speckles[idx].position.update(withDeltaTime: moveDelta)
alteringSpeckleByIteratingValues { speckle in
if speckle.transitionProgress.context.currentPos < 1 {
speckle.transitionProgress.update(withDeltaTime: deltaTime * transitionSpeed)
}

let pos_x = speckles[idx].position.x.context.currentPos
let tar_x = speckles[idx].position.x.context.targetPos
let pos_y = speckles[idx].position.y.context.currentPos
let tar_y = speckles[idx].position.y.context.targetPos
if abs(pos_x - tar_x) < 0.125 || abs(pos_y - tar_y) < 0.125 {
let rand = randomLocationPair()
speckles[idx].position.setTarget(.init(x: rand.x, y: rand.y))
}
}
if moveDelta > 0 {
speckle.position.update(withDeltaTime: moveDelta)
}

points = speckles
.filter(\.enabled)
.map { .init(
color: $0.colorVector,
position: .init(
x: $0.position.x.context.currentPos,
y: $0.position.y.context.currentPos
)
) }
if speckle.position.distanceToTarget < 50 {
let rand = randomLocationPair()
speckle.position.setTarget(.init(x: rand.x, y: rand.y))
}
}

var points: [MulticolorGradientView.Parameters.ColorStop]?

alteringSpeckles { speckles in
points = speckles.filter(\.enabled).map { .init(
color: $0.color,
position: .init(
x: $0.position.x.context.currentPos,
y: $0.position.y.context.currentPos
)
) }
}
guard let points else { return }

Expand Down
17 changes: 15 additions & 2 deletions Sources/ColorfulX/AnimatedMulticolorGradientView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ private let SPRING_CONFIG = SpringInterpolation.Configuration(
angularFrequency: 1.5,
dampingRatio: 0.2,
threshold: 0.001,
stopWhenHitTarget: true
stopWhenHitTarget: false
)
private let SPRING_ENGINE = SpringInterpolation2D(SPRING_CONFIG)
private let defaultFrameRate: Int = 60
Expand Down Expand Up @@ -106,7 +106,7 @@ open class AnimatedMulticolorGradientView: MulticolorGradientView {
func isColorTransitionCompleted() -> Bool {
speckles
.filter(\.enabled)
.allSatisfy { $0.transitionProgress.context.currentPos >= 1 }
.allSatisfy(\.isTransitionCompleted)
}

@inline(__always)
Expand All @@ -127,6 +127,7 @@ open class AnimatedMulticolorGradientView: MulticolorGradientView {
}

override func render() {
print("[*] render at \(obtainCurrentTimestamp())")
super.render()
lastRenderExecution = obtainCurrentTimestamp()
}
Expand All @@ -142,4 +143,16 @@ open class AnimatedMulticolorGradientView: MulticolorGradientView {
callback(&speckles)
specklesAccessLock.unlock()
}

func alteringSpeckleByIteratingValues(_ callback: (inout Speckle) -> Void) {
alteringSpeckleByIteratingValues { speckle, _ in callback(&speckle) }
}

func alteringSpeckleByIteratingValues(_ callback: (inout Speckle, _ idx: Int) -> Void) {
specklesAccessLock.lock()
for idx in 0 ..< speckles.count {
callback(&speckles[idx], idx)
}
specklesAccessLock.unlock()
}
}
8 changes: 4 additions & 4 deletions Sources/ColorfulX/ColorfulView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public struct ColorfulView: View {
@Binding var renderScale: Double
@Binding var pauseRender: Bool

let repeatToFillColorSlots: Bool
let repeats: Bool

public init(
color: Binding<[Color]>,
Expand All @@ -29,7 +29,7 @@ public struct ColorfulView: View {
frameLimit: Binding<Int> = .constant(0),
renderScale: Binding<Double> = .constant(1.0),
pauseRender: Binding<Bool> = .constant(false),
repeatToFillColorSlots: Bool = true
repeats: Bool = true
) {
_color = color
_speed = speed
Expand All @@ -40,7 +40,7 @@ public struct ColorfulView: View {
_renderScale = renderScale
_pauseRender = pauseRender

self.repeatToFillColorSlots = repeatToFillColorSlots
self.repeats = repeats
}

public var body: some View {
Expand All @@ -55,7 +55,7 @@ public struct ColorfulView: View {
frameLimit: $frameLimit,
renderScale: $renderScale,
pauseRender: $pauseRender,
repeatToFillColorSlots: repeatToFillColorSlots
repeats: repeats
)
}
}
7 changes: 6 additions & 1 deletion Sources/ColorfulX/MulticolorGradientView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import MetalKit

open class MulticolorGradientView: MetalView {
public var parameters: Parameters = .init() {
didSet { if oldValue != parameters { needsRender = true } }
didSet {
if oldValue != parameters {
print("parameters: \(parameters)")
needsRender = true
}
}
}

private var needsRender: Bool = false
Expand Down

0 comments on commit e6c22db

Please sign in to comment.