-
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
291 additions
and
0 deletions.
There are no files selected for viewing
8 changes: 8 additions & 0 deletions
8
Examples/SwiftIOPlayground/12MoreProjects/SandSimulation/.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/SandSimulation/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 |
29 changes: 29 additions & 0 deletions
29
Examples/SwiftIOPlayground/12MoreProjects/SandSimulation/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,29 @@ | ||
// 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: "SandSimulation", | ||
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"), | ||
.package(url: "https://github.com/madmachineio/MadGraphics.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: "SandSimulation", | ||
dependencies: [ | ||
"SwiftIO", | ||
"MadBoards", | ||
// Use specific library name rather than "MadDrivers" would speed up the build procedure. | ||
.product(name: "ST7789", package: "MadDrivers"), | ||
.product(name: "LIS3DH", package: "MadDrivers"), | ||
"MadGraphics" | ||
]), | ||
] | ||
) |
176 changes: 176 additions & 0 deletions
176
Examples/SwiftIOPlayground/12MoreProjects/SandSimulation/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,176 @@ | ||
import MadGraphics | ||
|
||
struct Particle { | ||
var x: Int | ||
var y: Int | ||
var xSpeed: Float | ||
var ySpeed: Float | ||
let color: Color | ||
} | ||
|
||
struct Sand { | ||
let count = 1000 | ||
let size = 3 | ||
let canvas: Canvas | ||
var particles: [Particle] = [] | ||
|
||
let colors: [Color] = [.red, .orange, .yellow, .lime, .cyan, .blue, .purple, .magenta] | ||
var grid: [Bool] | ||
|
||
let column: Int | ||
let row: Int | ||
|
||
init(canvas: Canvas, _ acceleration: (x: Float, y: Float, z: Float)) { | ||
self.canvas = canvas | ||
column = canvas.width / size | ||
row = canvas.height / size | ||
grid = [Bool](repeating: false, count: column * row) | ||
|
||
// Draw the particles on the top of the canvas. | ||
var index = 0 | ||
|
||
while index < count { | ||
let x = index % column | ||
let y = index / column | ||
|
||
particles.append(Particle(x: x, y: y, xSpeed: 0, ySpeed: 0, color: colors.randomElement()!)) | ||
canvas.fillRectangle(at: Point(x * size, y * size), width: size, height: size, color: particles[y * column + x].color) | ||
grid[y * column + x] = true | ||
|
||
index += 1 | ||
} | ||
} | ||
|
||
mutating func update(_ acceleration: (x: Float, y: Float, z: Float)) { | ||
updateSpeed(acceleration) | ||
|
||
for i in particles.indices { | ||
var xSpeed = particles[i].xSpeed | ||
var ySpeed = particles[i].ySpeed | ||
|
||
let lastX = particles[i].x | ||
let lastY = particles[i].y | ||
|
||
var newX = particles[i].x + Int(xSpeed) | ||
var newY = particles[i].y + Int(ySpeed) | ||
|
||
// If the particle collides with a wall, bounce it. | ||
if newX > column - 1 { | ||
newX = column - 1 | ||
xSpeed /= -2 | ||
} else if newX < 0 { | ||
newX = 0 | ||
xSpeed /= -2 | ||
} | ||
|
||
if newY > row - 1 { | ||
newY = row - 1 | ||
ySpeed /= -2 | ||
} else if newY < 0 { | ||
newY = 0 | ||
ySpeed /= -2 | ||
} | ||
|
||
let lastIndex = lastY * column + lastX | ||
let newIndex = newY * column + newX | ||
|
||
// The particle moves and collide with other particle. | ||
if lastIndex != newIndex && grid[newIndex] { | ||
if abs(lastIndex - newIndex) == 1 { | ||
// The particle moves horizontally. | ||
xSpeed /= -2 | ||
newX = lastX | ||
} else if abs(lastIndex - newIndex) == column { | ||
// The particle moves vertically. | ||
ySpeed /= -2 | ||
newY = lastY | ||
} else { | ||
// If the particle moves diagonally, find an available position adjacent to the particle to place it. | ||
if abs(xSpeed) >= abs(ySpeed) { | ||
// The speed on the x-axis is greater than the speed on the y-axis. | ||
if !grid[lastY * column + newX] { | ||
// Suppress movement along the y-axis. | ||
newY = lastY | ||
ySpeed /= -2 | ||
} else if !grid[newY * column + lastX] { | ||
// Suppress movement along the x-axis. | ||
newX = lastX | ||
xSpeed /= -2 | ||
} else { | ||
// Remain still. | ||
newX = lastX | ||
newY = lastY | ||
xSpeed /= -2 | ||
ySpeed /= -2 | ||
} | ||
} else { | ||
// The speed on the x-axis is less than the speed on the y-axis. | ||
if !grid[newY * column + lastX] { | ||
// Suppress movement along the x-axis. | ||
newX = lastX | ||
xSpeed /= -2 | ||
} else if !grid[lastY * column + newX] { | ||
// Suppress movement along the y-axis. | ||
newY = lastY | ||
ySpeed /= -2 | ||
} else { | ||
// Remain still. | ||
newX = lastX | ||
newY = lastY | ||
xSpeed /= -2 | ||
ySpeed /= -2 | ||
} | ||
} | ||
} | ||
} | ||
|
||
particles[i].x = newX | ||
particles[i].y = newY | ||
particles[i].xSpeed = xSpeed | ||
particles[i].ySpeed = ySpeed | ||
grid[lastY * column + lastX] = false | ||
grid[newY * column + newX] = true | ||
canvas.fillRectangle(at: Point(lastX * size, lastY * size), width: size, height: size, color: Color.black) | ||
canvas.fillRectangle(at: Point(newX * size, newY * size), width: size, height: size, color: particles[i].color) | ||
} | ||
} | ||
|
||
mutating func updateSpeed(_ acceleration: (x: Float, y: Float, z: Float)) { | ||
var xAccel = -acceleration.x | ||
var yAccel = acceleration.y | ||
var zAccel = min(abs(acceleration.z), 1) | ||
|
||
if abs(xAccel) <= 0.1 && abs(yAccel) <= 0.1 { | ||
// Prevent the particles from moving when the board is lying on the table not completely level. | ||
for i in particles.indices { | ||
particles[i].xSpeed = 0 | ||
particles[i].ySpeed = 0 | ||
} | ||
} else { | ||
// Acceleration on z-axis simulates the effect of gravitational on the motion of the particles. | ||
// When z-axis acceleration is close to 1, sensor is flat, and gravity barely affects xy movement. | ||
// Lower z-axis acceleration means more gravity influence on xy motion. | ||
zAccel = 0.5 - zAccel / 2 | ||
xAccel -= zAccel | ||
yAccel -= zAccel | ||
|
||
//A slight random motion is added to each particle according to the z-axis acceleration. | ||
// Their speed stays below 1 to avoid overlap. | ||
// However, the rapid iteration speed creates the illusion of smooth particle movement. | ||
for i in particles.indices { | ||
var xSpeed = particles[i].xSpeed + xAccel + Float.random(in: 0...zAccel) | ||
if abs(xSpeed) > 1 { | ||
xSpeed /= abs(xSpeed) | ||
} | ||
|
||
var ySpeed = particles[i].ySpeed + yAccel + Float.random(in: 0...zAccel) | ||
if abs(ySpeed) > 1 { | ||
ySpeed /= abs(ySpeed) | ||
} | ||
|
||
particles[i].xSpeed = xSpeed | ||
particles[i].ySpeed = ySpeed | ||
} | ||
} | ||
} | ||
} |
61 changes: 61 additions & 0 deletions
61
Examples/SwiftIOPlayground/12MoreProjects/SandSimulation/Sources/SandSimulation.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,61 @@ | ||
import SwiftIO | ||
import MadBoard | ||
import ST7789 | ||
import MadGraphics | ||
import LIS3DH | ||
|
||
@main | ||
public struct SandSimulation { | ||
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) | ||
|
||
let canvas = Canvas(width: screen.width, height: screen.height) | ||
var frameBuffer = [UInt16](repeating: 0, count: canvas.width * canvas.height) | ||
|
||
// Initialize the accelerometer. | ||
let i2c = I2C(Id.I2C0) | ||
let accelerometer = LIS3DH(i2c) | ||
|
||
// Draw the sand particle. | ||
var sand = Sand(canvas: canvas, accelerometer.readXYZ()) | ||
updateDisplay(canvas: canvas, frameBuffer: &frameBuffer, screen: screen) | ||
|
||
// Update sand particle positions based on movement. | ||
while true { | ||
sand.update(accelerometer.readXYZ()) | ||
updateDisplay(canvas: canvas, frameBuffer: &frameBuffer, screen: screen) | ||
sleep(ms: 1) | ||
} | ||
} | ||
} | ||
|
||
// Get the region that needs to be updated and send data to the screen. | ||
func updateDisplay(canvas: Canvas, frameBuffer: inout [UInt16], screen: ST7789) { | ||
guard let dirty = canvas.getDirtyRect() else { | ||
return | ||
} | ||
|
||
var index = 0 | ||
let stride = canvas.width | ||
let canvasBuffer = canvas.buffer | ||
for y in dirty.y0..<dirty.y1 { | ||
for x in dirty.x0..<dirty.x1 { | ||
frameBuffer[index] = Color.getRGB565LE(canvasBuffer[y * stride + x]) | ||
index += 1 | ||
} | ||
} | ||
|
||
frameBuffer.withUnsafeBytes { ptr in | ||
screen.writeBitmap(x: dirty.x, y: dirty.y, width: dirty.width, height: dirty.height, data: ptr) | ||
} | ||
|
||
canvas.finishRefresh() | ||
} |