-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
288 additions
and
0 deletions.
There are no files selected for viewing
8 changes: 8 additions & 0 deletions
8
Examples/SwiftIOPlayground/12MoreProjects/FallingSand/.gitignore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
17 changes: 17 additions & 0 deletions
17
Examples/SwiftIOPlayground/12MoreProjects/FallingSand/Package.mmp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# This is a MadMachine project file in TOML format | ||
# This file holds those parameters that could not be managed by SwiftPM | ||
# Edit this file would change the behavior of the building/downloading procedure | ||
# Those project files in the dependent libraries would be IGNORED | ||
|
||
# Specify the board name below | ||
# There are "SwiftIOBoard" and "SwiftIOMicro" now | ||
board = "SwiftIOMicro" | ||
|
||
# Specifiy the target triple below | ||
# There are "thumbv7em-unknown-none-eabi" and "thumbv7em-unknown-none-eabihf" now | ||
# If your code use significant floating-point calculation, | ||
# plz set it to "thumbv7em-unknown-none-eabihf" | ||
triple = "thumbv7em-unknown-none-eabi" | ||
|
||
# Reserved for future use | ||
version = 1 |
26 changes: 26 additions & 0 deletions
26
Examples/SwiftIOPlayground/12MoreProjects/FallingSand/Package.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// 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: "FallingSand", | ||
dependencies: [ | ||
// Dependencies declare other packages that this package depends on. | ||
.package(url: "https://github.com/madmachineio/SwiftIO.git", branch: "main"), | ||
.package(url: "https://github.com/madmachineio/MadBoards.git", branch: "main"), | ||
.package(url: "https://github.com/madmachineio/MadDrivers.git", branch: "main"), | ||
], | ||
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: "FallingSand", | ||
dependencies: [ | ||
"SwiftIO", | ||
"MadBoards", | ||
// Use specific library name rather than "MadDrivers" would speed up the build procedure. | ||
.product(name: "ST7789", package: "MadDrivers") | ||
]), | ||
] | ||
) |
198 changes: 198 additions & 0 deletions
198
Examples/SwiftIOPlayground/12MoreProjects/FallingSand/Sources/Sand.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
import SwiftIO | ||
import ST7789 | ||
typealias Point = (x: Int, y: Int) | ||
|
||
struct Sand { | ||
let screen: ST7789 | ||
// Keep the size greater than 1. | ||
let sandSize: Int = 4 | ||
let row: Int | ||
let column: Int | ||
|
||
// The entire screen is made up of grids. | ||
// When a new sand particle appears, its color fills the corresponding grid. | ||
// This array stores the color value for each grid. | ||
var gridColors: [[UInt16]] | ||
|
||
// The x coordinate of the cursor used to add new sand. | ||
var cursorPos = 0 | ||
var lastCursorPos = 0 | ||
|
||
// Store the index to get current color value from the `colors` array. | ||
var colorIndex = 0 | ||
let colors = Colors.colors565 | ||
|
||
// Duration in milliseconds. | ||
let colorChangeDuration = 10000 | ||
|
||
// Timestamp used to update display. | ||
var lastColorChangeTime: Int64 = 0 | ||
|
||
// Initialize the screen with all grids defaulting to black and display the cursor. | ||
init(screen: ST7789, cursor: AnalogIn) { | ||
self.screen = screen | ||
|
||
row = screen.height / sandSize | ||
column = screen.width / sandSize | ||
|
||
// Initially, as there's no sand, all grids are set to black. | ||
gridColors = [[UInt16]](repeating: [UInt16](repeating: 0, count: column), count: row) | ||
|
||
// Read the potentiometer value and draw the cursor accordingly in the corresponding position. | ||
let cursorPos = getCursorPos(cursor: cursor) | ||
screen.drawEmptyRect(at: (cursorPos * sandSize, 0), width: sandSize, height: sandSize, stroke: 1, color: 0xFFFF) | ||
|
||
lastCursorPos = cursorPos | ||
lastColorChangeTime = getSystemUptimeInMilliseconds() | ||
} | ||
|
||
// Update the position of the cursor and sand particles over time. | ||
mutating func update(cursor: AnalogIn) { | ||
updateCursor(cursor: cursor) | ||
|
||
// Update the color for newly added sand particles. | ||
let current = getSystemUptimeInMilliseconds() | ||
if current - lastColorChangeTime >= colorChangeDuration { | ||
colorIndex += 1 | ||
if colorIndex == colors.count { | ||
colorIndex = 0 | ||
} | ||
lastColorChangeTime = current | ||
} | ||
|
||
updateSand() | ||
} | ||
|
||
// Update the cursor's position as you rotate the potentiometer. | ||
mutating func updateCursor(cursor: AnalogIn) { | ||
cursorPos = getCursorPos(cursor: cursor) | ||
|
||
if cursorPos != lastCursorPos { | ||
screen.drawRect(at: (lastCursorPos * sandSize, 0), width: sandSize, height: sandSize, color: 0) | ||
screen.drawEmptyRect(at: (cursorPos * sandSize, 0), width: sandSize, height: sandSize, stroke: 1, color: 0xFFFF) | ||
lastCursorPos = cursorPos | ||
} | ||
} | ||
|
||
// Calculate the cursor's position based on the potentiometer's reading. | ||
func getCursorPos(cursor: AnalogIn) -> Int { | ||
var values: Float = 0 | ||
|
||
for _ in 0..<10 { | ||
values += cursor.readPercentage() | ||
} | ||
|
||
var x = values * Float(row - 1) / 10 | ||
x.round(.toNearestOrAwayFromZero) | ||
return Int(x) | ||
} | ||
|
||
// Move the sand particles down. | ||
mutating func updateSand() { | ||
for y in (0..<gridColors.count-1).reversed() { | ||
for x in gridColors[y].indices { | ||
let color = gridColors[y][x] | ||
if color > 0 { | ||
// If the grid below the particle is black, fill this grid with the sand color. | ||
if gridColors[y+1][x] == 0 { | ||
gridColors[y+1][x] = color | ||
gridColors[y][x] = 0 | ||
|
||
screen.drawRect(at: (x * sandSize, y * sandSize), width: sandSize, height: sandSize, color: 0) | ||
screen.drawRect(at: (x * sandSize, (y + 1) * sandSize), width: sandSize, height: sandSize, color: color) | ||
} else { | ||
// If the grid below is unavailable, the particle moves randomly to the left or right. | ||
let left = Bool.random() | ||
if left && x > 0 && gridColors[y+1][x-1] == 0 { | ||
gridColors[y+1][x-1] = color | ||
gridColors[y][x] = 0 | ||
|
||
screen.drawRect(at: (x * sandSize, y * sandSize), width: sandSize, height: sandSize, color: 0) | ||
screen.drawRect(at: ((x - 1) * sandSize, (y + 1) * sandSize), width: sandSize, height: sandSize, color: color) | ||
} else if !left && x < gridColors[y].count - 1 && gridColors[y+1][x+1] == 0 { | ||
gridColors[y+1][x+1] = color | ||
gridColors[y][x] = 0 | ||
|
||
screen.drawRect(at: (x * sandSize, y * sandSize), width: sandSize, height: sandSize, color: 0) | ||
screen.drawRect(at: ((x + 1) * sandSize, (y + 1) * sandSize), width: sandSize, height: sandSize, color: color) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Randomly add new sand particles below the cursor, then update the colors of the corresponding grids. | ||
mutating func drawNewSand() { | ||
// Define the area of the new particles. | ||
let extent = 4 | ||
|
||
for y in 1...extent { | ||
for x in -extent/2...extent/2 { | ||
if Bool.random() { | ||
let newSandPos: Point = (cursorPos + x, y) | ||
if newSandPos.x < column && newSandPos.x >= 0 { | ||
gridColors[newSandPos.y][newSandPos.x] = colors[colorIndex] | ||
screen.drawRect( | ||
at: (newSandPos.x * sandSize, newSandPos.y * sandSize), | ||
width: sandSize, | ||
height: sandSize, | ||
color: colors[colorIndex] | ||
) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
||
struct Colors { | ||
static let red: UInt32 = 0xFF0000 | ||
static let orange: UInt32 = 0xFF7F00 | ||
static let yellow: UInt32 = 0xFFFF00 | ||
static let green: UInt32 = 0x00FF00 | ||
static let blue: UInt32 = 0x0000FF | ||
static let indigo: UInt32 = 0x4B0082 | ||
static let violet: UInt32 = 0x9400D3 | ||
static let colors888 = [red, orange, yellow, green, blue, indigo, violet] | ||
// Get 16bit color data. | ||
static let colors565: [UInt16] = colors888.map { getRGB565BE($0) } | ||
|
||
// The screen needs RGB565 color data, so change color data from UInt32 to UInt16. | ||
// Besides, the board uses little endian format, so the bytes are swapped. | ||
static func getRGB565BE(_ color: UInt32) -> UInt16 { | ||
return UInt16(((color & 0xF80000) >> 8) | ((color & 0xFC00) >> 5) | ((color & 0xF8) >> 3)).byteSwapped | ||
} | ||
} | ||
|
||
extension ST7789 { | ||
func drawRect(at point: Point, width: Int, height: Int, color: UInt16) { | ||
var buffer = [UInt16](repeating:color, count: width * height) | ||
for py in 0..<height { | ||
for px in 0..<width { | ||
buffer[py * width + px] = color | ||
} | ||
} | ||
|
||
buffer.withUnsafeBytes { | ||
screen.writeBitmap(x: point.x, y: point.y, width: width, height: height, data: $0) | ||
} | ||
} | ||
|
||
func drawEmptyRect(at point: Point, width: Int, height: Int, stroke: Int, color: UInt16) { | ||
for w in 0..<width { | ||
for line in 0..<stroke { | ||
writePixel(x: point.x + w, y: point.y + line, color: color) | ||
writePixel(x: point.x + w, y: point.y + height - stroke + line, color: color) | ||
} | ||
} | ||
|
||
for h in stroke..<height-stroke { | ||
for line in 0..<stroke { | ||
writePixel(x: point.x + line, y: point.y + h, color: color) | ||
writePixel(x: point.x + width - stroke + line, y: point.y + h, color: color) | ||
} | ||
} | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
Examples/SwiftIOPlayground/12MoreProjects/FallingSand/Sources/main.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import SwiftIO | ||
import MadBoard | ||
import ST7789 | ||
|
||
|
||
// 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) | ||
|
||
let cursor = AnalogIn(Id.A0) | ||
let button = DigitalIn(Id.D1) | ||
var pressCount = 0 | ||
|
||
var sand = Sand(screen: screen, cursor: cursor) | ||
|
||
while true { | ||
// Add more sand particles if the button is been pressed. | ||
if pressCount > 10 { | ||
sand.drawNewSand() | ||
pressCount = 0 | ||
} | ||
|
||
if button.read() { | ||
pressCount += 1 | ||
} else { | ||
pressCount = 0 | ||
} | ||
|
||
sleep(ms: 5) | ||
|
||
// Update the position of sand and cursor over time. | ||
sand.update(cursor: cursor) | ||
} |