| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- //
- // ARK-OS Splash Animation (Wayland/4K Ready)
- //
- // Renders a high-definition, dynamic 4K animation of the ARK atom.
- //
- import Foundation
- // Core parameters (dynamically scaled in drawSwiftSplash)
- var NUC_R: Double = 28.0
- var INNER_R: Double = 120.0
- var OUTER_R: Double = 200.0
- var ELEC_R: Double = 12.0
- var RING_T: Double = 2.0
- public var scrW: Int = 1920
- public var scrH: Int = 1080
- public var bpp: Int = 4
- public var lineLen: Int = 1920 * 4
- // The Wayland client handle for the splash screen
- var splashGraphics: ArkGraphics!
- var renderBuffer: UnsafeMutablePointer<UInt8>!
- public func emitFrame() {
- splashGraphics.pan()
- usleep(16000) // basic ~60fps pacing
- }
- func presentScreen() {
- emitFrame()
- }
- func clearScreen(r: UInt8, g: UInt8, b: UInt8) {
- if renderBuffer == nil { return }
- let count = scrW * scrH * bpp
- for i in stride(from: 0, to: count, by: bpp) {
- renderBuffer[i] = b
- renderBuffer[i+1] = g
- renderBuffer[i+2] = r
- if bpp == 4 { renderBuffer[i+3] = 255 }
- }
- }
- func drawRoundedRect(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8, a: UInt8, radius: Int) {
- if renderBuffer == nil { return }
- for i in max(0, y)..<min(scrH, y+h) {
- let rowBase = i * lineLen
- for j in max(0, x)..<min(scrW, x+w) {
- let off = rowBase + j * bpp
- renderBuffer[off] = b
- renderBuffer[off+1] = g
- renderBuffer[off+2] = r
- if bpp == 4 { renderBuffer[off+3] = a }
- }
- }
- }
- // Draw filled disk with anti-aliasing (proper premultiplied alpha)
- func diskAA(_ p: UnsafeMutablePointer<UInt8>, _ cx: Double, _ cy: Double, _ r: Double, _ r_col: UInt8, _ g_col: UInt8, _ b_col: UInt8, _ alpha: UInt8) {
- let y0 = max(0, Int(cy - r - 2))
- let y1 = min(scrH - 1, Int(cy + r + 2))
- let x0 = max(0, Int(cx - r - 2))
- let x1 = min(scrW - 1, Int(cx + r + 2))
-
- let rInnerSq = (r - 0.5) * (r - 0.5)
- let rOuter = r + 0.5
- let rOuterSq = rOuter * rOuter
-
- let fVal = Double(alpha) / 255.0
- let preR = Double(r_col) * fVal
- let preG = Double(g_col) * fVal
- let preB = Double(b_col) * fVal
-
- for y in y0...y1 {
- let dy = Double(y) - cy
- let dySq = dy * dy
- let rowBase = y * lineLen
- for x in x0...x1 {
- let dx = Double(x) - cx
- let distSq = dx * dx + dySq
- if distSq < rInnerSq {
- let off = rowBase + x * bpp
- let invA = 1.0 - fVal
- p[off] = UInt8(min(255, preB + Double(p[off]) * invA))
- p[off+1] = UInt8(min(255, preG + Double(p[off+1]) * invA))
- p[off+2] = UInt8(min(255, preR + Double(p[off+2]) * invA))
- if bpp == 4 {
- p[off+3] = UInt8(min(255, fVal * 255.0 + Double(p[off+3]) * invA))
- }
- } else if distSq < rOuterSq {
- let dist = sqrt(distSq)
- let edgeAlpha = fVal * (rOuter - dist)
- let invA = 1.0 - edgeAlpha
- let eB = Double(b_col) * edgeAlpha
- let eG = Double(g_col) * edgeAlpha
- let eR = Double(r_col) * edgeAlpha
-
- let off = rowBase + x * bpp
- p[off] = UInt8(min(255, eB + Double(p[off]) * invA))
- p[off+1] = UInt8(min(255, eG + Double(p[off+1]) * invA))
- p[off+2] = UInt8(min(255, eR + Double(p[off+2]) * invA))
- if bpp == 4 {
- p[off+3] = UInt8(min(255, edgeAlpha * 255.0 + Double(p[off+3]) * invA))
- }
- }
- }
- }
- }
- // Draw ring outline with anti-aliasing
- func ringAA(_ p: UnsafeMutablePointer<UInt8>, _ cx: Double, _ cy: Double, _ r: Double, _ t: Double, _ val: UInt8) {
- let scan = r + t + 2
- let y0 = max(0, Int(cy - scan))
- let y1 = min(scrH - 1, Int(cy + scan))
- let x0 = max(0, Int(cx - scan))
- let x1 = min(scrW - 1, Int(cx + scan))
-
- let rInMinSq = (r - t - 0.5) * (r - t - 0.5)
- let rInMaxSq = (r - t + 0.5) * (r - t + 0.5)
- let rOutMinSq = (r + t - 0.5) * (r + t - 0.5)
- let rOutMaxSq = (r + t + 0.5) * (r + t + 0.5)
-
- let tOuter = t + 0.5
- let fVal = Double(val) / 255.0
- let c = 255.0 * fVal // We draw white rings
-
- for y in y0...y1 {
- let dy = Double(y) - cy
- let dySq = dy * dy
- let rowBase = y * lineLen
- for x in x0...x1 {
- let dx = Double(x) - cx
- let d = dx * dx + dySq
- if d >= rInMaxSq && d <= rOutMinSq {
- let off = rowBase + x * bpp
- let invA = 1.0 - fVal
- p[off] = UInt8(min(255, c + Double(p[off]) * invA))
- p[off+1] = UInt8(min(255, c + Double(p[off+1]) * invA))
- p[off+2] = UInt8(min(255, c + Double(p[off+2]) * invA))
- if bpp == 4 {
- p[off+3] = UInt8(min(255, fVal * 255.0 + Double(p[off+3]) * invA))
- }
- } else if (d >= rInMinSq && d < rInMaxSq) || (d > rOutMinSq && d <= rOutMaxSq) {
- let dist = sqrt(d)
- let diff = abs(dist - r)
- let edgeA = fVal * (tOuter - diff)
- if edgeA > 0 {
- let invA = 1.0 - edgeA
- let eC = 255.0 * edgeA
- let off = rowBase + x * bpp
- p[off] = UInt8(min(255, eC + Double(p[off]) * invA))
- p[off+1] = UInt8(min(255, eC + Double(p[off+1]) * invA))
- p[off+2] = UInt8(min(255, eC + Double(p[off+2]) * invA))
- if bpp == 4 {
- p[off+3] = UInt8(min(255, edgeA * 255.0 + Double(p[off+3]) * invA))
- }
- }
- }
- }
- }
- }
- // Particle System for ArkText
- struct ArkTextDot {
- var x: Double, y: Double
- var targetX: Double, targetY: Double
- }
- func drawSwiftSplash() {
- print("ui_daemon: Animating Splash Screen in 4K Wayland mode...")
-
- // Initialize the Wayland client for the splash screen
- splashGraphics = ArkGraphics()
- if !splashGraphics.isConnected {
- print("ui_daemon: FATAL - Could not connect splash to Wayland server")
- return
- }
-
- // Extract the raw buffer pointer from ArkGraphics (via memory reflection or a temporary workaround)
- // Wait, ArkGraphics doesn't expose `buffer` publicly. Let's make it public!
- renderBuffer = splashGraphics.buffer
- scrW = splashGraphics.screenWidth
- scrH = splashGraphics.screenHeight
- lineLen = scrW * bpp
-
- // Scale parameters based on resolution (assuming 1920x1080 baseline)
- let scale = Double(scrW) / 1920.0
- NUC_R = 28.0 * scale
- INNER_R = 120.0 * scale
- OUTER_R = 200.0 * scale
- ELEC_R = 12.0 * scale
- RING_T = 3.0 * scale
-
- let startCx = Double(scrW) / 2.0
- let startCy = Double(scrH) / 2.0
- // Frame timing via compositor
- func emitFrame() {
- presentScreen()
- }
- // Phase 1: Spawn & Accelerate (45 frames)
- for frame in 0..<45 {
- clearScreen(r: 0, g: 0, b: 0)
- let t = Double(frame) / 44.0
- let easeOut = 1.0 - pow(1.0 - t, 3.0)
- let curInnerR = INNER_R * easeOut
- let curOuterR = OUTER_R * easeOut
- let curElR = max(1.0, ELEC_R * easeOut)
- let ringBright = UInt8(90.0 * easeOut) // Brighter rings
-
- diskAA(renderBuffer, startCx, startCy, NUC_R, 255, 255, 255, 255)
- if curInnerR > NUC_R { ringAA(renderBuffer, startCx, startCy, curInnerR, RING_T, ringBright) }
- if curOuterR > NUC_R { ringAA(renderBuffer, startCx, startCy, curOuterR, RING_T, ringBright) }
-
- // Quadrupled the rotation speed (ease-in so it smoothly starts spinning)
- let angle = pow(t, 2.0) * 8.0
- for i in 0..<2 {
- let a = angle + Double(i) * Double.pi
- diskAA(renderBuffer, startCx + cos(a) * curInnerR, startCy + sin(a) * curInnerR, curElR, 255, 255, 255, 255)
- }
- for i in 0..<2 {
- let a = -angle * 0.7 + Double(i) * Double.pi
- diskAA(renderBuffer, startCx + cos(a) * curOuterR, startCy + sin(a) * curOuterR, curElR, 255, 255, 255, 255)
- }
- emitFrame()
- }
- // Phase 2: Fast Stable Orbiting (120 frames for multiple full rotations)
- for frame in 0..<120 {
- clearScreen(r: 0, g: 0, b: 0)
- // 8.0 rads from previous + 0.3 rads per frame = fast spinning!
- let angle = 8.0 + Double(frame) * 0.3
-
- diskAA(renderBuffer, startCx, startCy, NUC_R, 255, 255, 255, 255)
- ringAA(renderBuffer, startCx, startCy, INNER_R, RING_T, 90)
- ringAA(renderBuffer, startCx, startCy, OUTER_R, RING_T, 90)
-
- for i in 0..<2 {
- let a = angle + Double(i) * Double.pi
- diskAA(renderBuffer, startCx + cos(a) * INNER_R, startCy + sin(a) * INNER_R, ELEC_R, 255, 255, 255, 255)
- }
- for i in 0..<2 {
- let a = -angle * 0.7 + Double(i) * Double.pi
- diskAA(renderBuffer, startCx + cos(a) * OUTER_R, startCy + sin(a) * OUTER_R, ELEC_R, 255, 255, 255, 255)
- }
- emitFrame()
- }
- // Phase 3: Deceleration (60 frames)
- let baseAngle = 8.0 + (120.0 * 0.3)
- var currentSpeed = 0.3
- var currentAngle = baseAngle
- for _ in 0..<60 {
- clearScreen(r: 0, g: 0, b: 0)
- currentSpeed *= 0.93 // Slower decay for smoother stop
- currentAngle += currentSpeed
-
- diskAA(renderBuffer, startCx, startCy, NUC_R, 255, 255, 255, 255)
- ringAA(renderBuffer, startCx, startCy, INNER_R, RING_T, 90)
- ringAA(renderBuffer, startCx, startCy, OUTER_R, RING_T, 90)
-
- for i in 0..<2 {
- let a = currentAngle + Double(i) * Double.pi
- diskAA(renderBuffer, startCx + cos(a) * INNER_R, startCy + sin(a) * INNER_R, ELEC_R, 255, 255, 255, 255)
- }
- for i in 0..<2 {
- let a = -currentAngle * 0.7 + Double(i) * Double.pi
- diskAA(renderBuffer, startCx + cos(a) * OUTER_R, startCy + sin(a) * OUTER_R, ELEC_R, 255, 255, 255, 255)
- }
- emitFrame()
- }
- // Phase 4: Slide to Top-Left (40 frames)
- let endAngleInner = currentAngle
- let endAngleOuter = -currentAngle * 0.7
- let targetCx = 180.0 * scale
- let targetCy = 180.0 * scale
-
- for frame in 0..<40 {
- clearScreen(r: 0, g: 0, b: 0)
- let t = Double(frame) / 39.0
- let easeOut = 1.0 - pow(1.0 - t, 3.0)
- let cx = startCx + (targetCx - startCx) * easeOut
- let cy = startCy + (targetCy - startCy) * easeOut
-
- diskAA(renderBuffer, cx, cy, NUC_R, 255, 255, 255, 255)
- ringAA(renderBuffer, cx, cy, INNER_R, RING_T, 90)
- ringAA(renderBuffer, cx, cy, OUTER_R, RING_T, 90)
-
- for i in 0..<2 {
- let a = endAngleInner + Double(i) * Double.pi
- diskAA(renderBuffer, cx + cos(a) * INNER_R, cy + sin(a) * INNER_R, ELEC_R, 255, 255, 255, 255)
- }
- for i in 0..<2 {
- let a = endAngleOuter + Double(i) * Double.pi
- diskAA(renderBuffer, cx + cos(a) * OUTER_R, cy + sin(a) * OUTER_R, ELEC_R, 255, 255, 255, 255)
- }
- emitFrame()
- }
- // Phase 5: Electron Glows Blue (30 frames)
- let glowingIndex = 0
- let glowingAngle = endAngleInner + Double(glowingIndex) * Double.pi
- let glowEx = targetCx + cos(glowingAngle) * INNER_R
- let glowEy = targetCy + sin(glowingAngle) * INNER_R
-
- for frame in 0..<30 {
- clearScreen(r: 0, g: 0, b: 0)
- let t = Double(frame) / 29.0
-
- diskAA(renderBuffer, targetCx, targetCy, NUC_R, 255, 255, 255, 255)
- ringAA(renderBuffer, targetCx, targetCy, INNER_R, RING_T, 90)
- ringAA(renderBuffer, targetCx, targetCy, OUTER_R, RING_T, 90)
-
- // Non-glowing electron
- let a2 = endAngleInner + Double.pi
- diskAA(renderBuffer, targetCx + cos(a2) * INNER_R, targetCy + sin(a2) * INNER_R, ELEC_R, 255, 255, 255, 255)
-
- for i in 0..<2 {
- let a = endAngleOuter + Double(i) * Double.pi
- diskAA(renderBuffer, targetCx + cos(a) * OUTER_R, targetCy + sin(a) * OUTER_R, ELEC_R, 255, 255, 255, 255)
- }
-
- // Glowing electron
- let glowRad = 20.0 * scale * t
- if glowRad > 0 {
- // Blue glow aura
- diskAA(renderBuffer, glowEx, glowEy, ELEC_R + glowRad, 30, 144, 255, UInt8(180.0 * t))
- }
- // Core changes to bright blue
- let coreR = UInt8(255 - 200 * t)
- let coreG = UInt8(255 - 100 * t)
- diskAA(renderBuffer, glowEx, glowEy, ELEC_R, coreR, coreG, 255, 255)
-
- emitFrame()
- }
- // Phase 6: Drop and Roll to "Get Started" (50 frames)
- let btnW = 300.0 * scale
- let btnH = 80.0 * scale
- let btnX = Double(scrW) / 2.0 - btnW / 2.0
- let btnY = Double(scrH) - 200.0 * scale - btnH
-
- for frame in 0..<50 {
- clearScreen(r: 0, g: 0, b: 0)
- let t = Double(frame) / 49.0
- let easeIn = t * t
-
- // Draw static atom
- diskAA(renderBuffer, targetCx, targetCy, NUC_R, 255, 255, 255, 255)
- ringAA(renderBuffer, targetCx, targetCy, INNER_R, RING_T, 90)
- ringAA(renderBuffer, targetCx, targetCy, OUTER_R, RING_T, 90)
- let a2 = endAngleInner + Double.pi
- diskAA(renderBuffer, targetCx + cos(a2) * INNER_R, targetCy + sin(a2) * INNER_R, ELEC_R, 255, 255, 255, 255)
- for i in 0..<2 {
- let a = endAngleOuter + Double(i) * Double.pi
- diskAA(renderBuffer, targetCx + cos(a) * OUTER_R, targetCy + sin(a) * OUTER_R, ELEC_R, 255, 255, 255, 255)
- }
-
- // Falling & morphing electron
- let curEx = glowEx + (btnX + btnW/2 - glowEx) * t
- // Bounce effect for Y
- let bounceT = abs(sin(t * Double.pi * 1.5)) * (1.0 - t)
- let curEy = glowEy + (btnY + btnH/2 - glowEy) * easeIn - (100.0 * scale * bounceT)
-
- // Morph from circle to rounded rect
- let curW = (ELEC_R * 2.0) + (btnW - (ELEC_R * 2.0)) * t
- let curH = (ELEC_R * 2.0) + (btnH - (ELEC_R * 2.0)) * t
- let curRad = Int((ELEC_R) + (Double(btnH/2) - ELEC_R) * t)
-
- drawRoundedRect(x: Int(curEx - curW/2), y: Int(curEy - curH/2), w: Int(curW), h: Int(curH), r: 30, g: 144, b: 255, a: 255, radius: curRad)
-
- emitFrame()
- }
-
- // Phase 7: White Dots Assemble "Welcome to ARK-OS!" (60 frames)
- // Create text points by sampling a grid inside the text area
- var dots: [ArkTextDot] = []
- let textW = 800.0 * scale
- let textH = 100.0 * scale
- let textX = Double(scrW)/2.0 - textW/2.0
- let textY = Double(scrH)/2.0 - textH/2.0
-
- // Create some random dots that represent "text"
- for _ in 0..<2000 {
- let ty = Double.random(in: 0..<textH)
- let tx = Double.random(in: 0..<textW)
- // Just rough bounds to look like text assembling
- if sin(tx * 0.05) * cos(ty * 0.1) > 0.2 {
- let spawnX = (Double.random(in: 0...1) > 0.5) ? -200.0 : Double(scrW) + 200.0
- let spawnY = Double.random(in: 0..<Double(scrH))
- dots.append(ArkTextDot(x: spawnX, y: spawnY, targetX: textX + tx, targetY: textY + ty))
- }
- }
-
- for frame in 0..<60 {
- clearScreen(r: 0, g: 0, b: 0)
- let t = Double(frame) / 59.0
- let _ = 1.0 - pow(1.0 - t, 3.0)
-
- // Static background
- diskAA(renderBuffer, targetCx, targetCy, NUC_R, 255, 255, 255, 255)
- ringAA(renderBuffer, targetCx, targetCy, INNER_R, RING_T, 90)
- ringAA(renderBuffer, targetCx, targetCy, OUTER_R, RING_T, 90)
- let a2 = endAngleInner + Double.pi
- diskAA(renderBuffer, targetCx + cos(a2) * INNER_R, targetCy + sin(a2) * INNER_R, ELEC_R, 255, 255, 255, 255)
- for i in 0..<2 {
- let a = endAngleOuter + Double(i) * Double.pi
- diskAA(renderBuffer, targetCx + cos(a) * OUTER_R, targetCy + sin(a) * OUTER_R, ELEC_R, 255, 255, 255, 255)
- }
- drawRoundedRect(x: Int(btnX), y: Int(btnY), w: Int(btnW), h: Int(btnH), r: 30, g: 144, b: 255, a: 255, radius: Int(btnH/2))
-
- // Assemble dots
- for i in 0..<dots.count {
- dots[i].x = dots[i].x + (dots[i].targetX - dots[i].x) * 0.1
- dots[i].y = dots[i].y + (dots[i].targetY - dots[i].y) * 0.1
-
- let px = Int(dots[i].x)
- let py = Int(dots[i].y)
- if px >= 0 && px < scrW && py >= 0 && py < scrH {
- let off = py * lineLen + px * bpp
- renderBuffer[off] = 255
- renderBuffer[off+1] = 255
- renderBuffer[off+2] = 255
- if bpp == 4 { renderBuffer[off+3] = 255 }
-
- // Make dots slightly larger for 4K
- if scale > 1.5 {
- if px+1 < scrW {
- renderBuffer[off+bpp] = 255; renderBuffer[off+bpp+1] = 255; renderBuffer[off+bpp+2] = 255; if bpp == 4 { renderBuffer[off+bpp+3] = 255 }
- }
- if py+1 < scrH {
- let off2 = (py+1) * lineLen + px * bpp
- renderBuffer[off2] = 255; renderBuffer[off2+1] = 255; renderBuffer[off2+2] = 255; if bpp == 4 { renderBuffer[off2+3] = 255 }
- }
- }
- }
- }
-
- emitFrame()
- }
- }
|