Skip to content

Commit

Permalink
Fix/add madgraphics demo (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
andy0808 authored Dec 5, 2024
1 parent 0c2359b commit 7fb92b2
Show file tree
Hide file tree
Showing 41 changed files with 2,076 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
28 changes: 28 additions & 0 deletions Examples/SwiftIOPlayground/13MoreProjects/Fireworks/Package.mmp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# This is a MadMachine project file in TOML format
# This file contains parameters that cannot be managed by SwiftPM
# Editing this file will alter the behavior of the build/download process
# Project files within dependent libraries will be IGNORED

# Specify the board name below
# Supported boards are listed as follows
# "SwiftIOBoard"
# "SwiftIOMicro"
board = "SwiftIOMicro"

# Specify the target triple below
# Supported architectures are listed as follows
# "thumbv7em-unknown-none-eabi"
# "thumbv7em-unknown-none-eabihf"
# "armv7em-none-none-eabi"
triple = "armv7em-none-none-eabi"

# Enable or disable hardware floating-point support below
# If your code involves significant floating-point calculations, please set it to 'true'
hard-float = true

# Enable or disable float register below
# If your code involves significant floating-point calculations, please set it to 'true'
float-abi = false

# Reserved for future use
version = 1
28 changes: 28 additions & 0 deletions Examples/SwiftIOPlayground/13MoreProjects/Fireworks/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "Fireworks",
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/madmachineio/SwiftIO.git", branch: "develop"),
.package(url: "https://github.com/madmachineio/MadBoards.git", branch: "develop"),
.package(url: "https://github.com/madmachineio/MadDrivers.git", branch: "develop"),
.package(url: "https://github.com/madmachineio/CFreeType", from: "2.13.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.executableTarget(
name: "Fireworks",
dependencies: [
"SwiftIO",
"MadBoards",
// Use specific library name rather than "MadDrivers" would speed up the build procedure.
.product(name: "ST7789", package: "MadDrivers"),
"CFreeType"
]),
]
)
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import MadGraphics
import ST7789
import SwiftIO

struct Firework {
var particle: Particle
var sparks: [Spark] = []

var exploded = false

let color: Color
let size = 2

// Create a firework at the bottom of the screen.
init(color: Color, maxWidth: Int) {
self.color = color

let x = Array(0..<(maxWidth - 1)).shuffled().randomElement()!
particle = Particle(pos: Point(x: x, y: maxWidth - 1))
}

// Update firework's position.
// If it explodes, it will return true to indicate it's time to play sound.
mutating func update(_ layer: Layer) -> Bool {
if exploded {
for spark in sparks {
layer.draw() { canvas in
canvas.fillCircle(at: spark.pos, radius: size, data: Color.black.rawValue)
}
}

updateSparks()

for spark in sparks {
let color = Color.blend(foreground: color.rawValue, background: Color.black.rawValue, mask: spark.lifespan)
layer.draw() { canvas in
canvas.fillCircle(at: spark.pos, radius: size, data: color.rawValue)
}
}
} else {
layer.draw() { canvas in
canvas.fillCircle(at: particle.pos, radius: size, data: Color.black.rawValue)
}

let playSound = updateParticle()

layer.draw() { canvas in
canvas.fillCircle(at: particle.pos, radius: size, data: color.rawValue)
}
return playSound
}

return false
}

// Update sparks' position and speed over time.
mutating func updateSparks() {
for i in sparks.indices.reversed() {
sparks[i].update()
// If
if sparks[i].done() {
sparks.remove(at: i)
}
}
}

// Update particle's position and speed over time.
mutating func updateParticle() -> Bool {
particle.update()
if particle.willExplode() {
exploded = true
explode()
return true
}

return false
}

// Generate firework sparks after explosion.
mutating func explode() {
for _ in 0..<100 {
sparks.append(Spark(pos: particle.pos))
}
}

func done() -> Bool {
return exploded && sparks.count == 0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import SwiftIO
import MadBoard
import ST7789
import MadGraphics

// Use lock to protect data from simultaneous access by multiple threads.
let i2sLock = Mutex()
// Whether the speaker will play sound.
var playSound = false

@main
public struct Fireworks {
public static func main() {
// Initialize the SPI pin and the digital pins for the LCD.
let bl = DigitalOut(Id.D2)
let rst = DigitalOut(Id.D12)
let dc = DigitalOut(Id.D13)
let cs = DigitalOut(Id.D5)
let spi = SPI(Id.SPI0, speed: 30_000_000)

// Initialize the LCD using the pins above. Rotate the screen to keep the original at the upper left.
let screen = ST7789(spi: spi, cs: cs, dc: dc, rst: rst, bl: bl, rotation: .angle90)
var screenBuffer = [UInt16](repeating: 0, count: screen.width * screen.width)
var frameBuffer = [UInt32](repeating: 0, count: screen.width * screen.width)

var colorIndex = 0
let colors: [Color] = [
.pink, .red, .lime, .blue, .cyan,
.purple, .magenta, .orange, .yellow
]

let layer = Layer(at: Point.zero, anchorPoint: UnitPoint.zero, width: screen.width, height: screen.height)


let font = Font(path: "/lfs/Resources/Fonts/Roboto-Regular.ttf", pointSize: 10, dpi: 220)
let text1 = TextLayer(at: Point(x: layer.bounds.size.halfWidth, y: 40), anchorPoint: UnitPoint.center, string: "Happy", font: font, foregroundColor: Color.red)
let text2 = TextLayer(at: Point(x: layer.bounds.size.halfWidth, y: 80), anchorPoint: UnitPoint.center, string: "Christmas", font: font, foregroundColor: Color.red)

layer.append(text1)
layer.append(text2)

var fireworks: [Firework] = []
var exploded = false

createThread(
name: "play_sound",
priority: 3,
stackSize: 1024 * 64,
soundThread
)

sleep(ms: 10)

while true {
if Int.random(in: 0..<100) < 10 {
fireworks.append(Firework(color: colors[colorIndex], maxWidth: layer.bounds.width))
// Update firwork's color.
colorIndex += 1
if colorIndex == colors.count {
colorIndex = 0
}
}

var i = 0
while i < fireworks.count {
if fireworks[i].update(layer) {
exploded = true
}

// If a all sparks of a firework disppear, remove the firework.
if fireworks[i].done() {
fireworks.remove(at: i)
} else {
i += 1
}
}

// If any firework has exploded, update the global variable.
if exploded {
i2sLock.lock()
playSound = true
i2sLock.unlock()

exploded = false
}

layer.render(into: &frameBuffer, output: &screenBuffer, transform: Color.getRGB565LE) { dirty, data in
screen.writeBitmap(x: dirty.x, y: dirty.y, width: dirty.width, height: dirty.height, data: data)
}

sleep(ms: 10)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import MadGraphics

// The firework before explosion.
struct Particle {
var pos: Point
var velocity: (x: Float, y: Float)
var acceleration: (x: Float, y: Float)

// Create a particle with a random velocity and acceleration.
init(pos: Point) {
self.pos = pos
velocity = (0, Float.random(in: (-14.0)...(-10.0)))
acceleration = (0, Float.random(in: 0.3..<0.4))
}

// Update the particle's position and velocity over time.
mutating func update() {
pos.x += Int(velocity.x)
pos.y += Int(velocity.y)

velocity.x += acceleration.x
velocity.y += acceleration.y
}

// Whether the particle reaches its maximum height.
func willExplode() -> Bool {
return velocity.y > 0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import SwiftIO
import MadBoard

func soundThread(_ a: UnsafeMutableRawPointer?, _ b: UnsafeMutableRawPointer?, _ c: UnsafeMutableRawPointer?) -> () {
let speaker = I2S(Id.I2S0, rate: 44_100)

// Read sound data from file.
let sound = readSoundData(from: "/lfs/Resources/Sounds/boom.wav")
var startPlay = false

while true {
sleep(ms: 100)

// Check the global variable to see if the speaker need to play sound.
i2sLock.lock()
if playSound {
startPlay = true
}
i2sLock.unlock()

if startPlay {
speaker.write(sound)
startPlay = false

// Update the global variable.
i2sLock.lock()
playSound = false
i2sLock.unlock()
}
}

func readSoundData(from path: String) -> [UInt8] {
let headerSize = 0x2C
var buffer = [UInt8]()

do {
let file = try FileDescriptor.open(path)
try file.seek(offset: 0, from: FileDescriptor.SeekOrigin.end)
let size = try file.tell() - headerSize

buffer = [UInt8](repeating: 0, count: size)
try file.read(fromAbsoluteOffest: headerSize, into: &buffer, count: size)
try file.close()
} catch {
print("File \(path) handle error: \(error)")
return []
}

return buffer
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import MadGraphics

// The firework after explosion.
struct Spark {
var pos: Point
var velocity: (x: Float, y: Float)
var acceleration: (x: Float, y: Float)
// How long the spark shows on the screen.
var lifespan: UInt8 = 255

// Create a particle with a random velocity and acceleration.
// Its initial position is the maximum height of the firework particle.
init(pos: Point) {
self.pos = pos

// The speed of the spark at a random direction.
velocity.x = Float(Array(-100..<100).shuffled().randomElement()!) / 50
velocity.y = Float(Array(-100..<100).shuffled().randomElement()!) / 50
velocity.x *= Float.random(in: 1...4)
velocity.y *= Float.random(in: 1...4)

acceleration = (0, Float.random(in: 0.3..<0.4))
}

// Update the particle's position and velocity over time.
mutating func update() {
velocity.x *= 0.8
velocity.y *= 0.8
lifespan -= 5

pos.x += Int(velocity.x)
pos.y += Int(velocity.y)

velocity.x += acceleration.x
velocity.y += acceleration.y
}

// Whether the spark will disappear.
func done() -> Bool {
return lifespan <= 0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
Loading

0 comments on commit 7fb92b2

Please sign in to comment.