Skip to content

Commit

Permalink
Add Hilbert curve example
Browse files Browse the repository at this point in the history
  • Loading branch information
Ines333 committed Apr 30, 2024
1 parent b53c157 commit ce34801
Show file tree
Hide file tree
Showing 5 changed files with 230 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
17 changes: 17 additions & 0 deletions Examples/SwiftIOPlayground/12MoreProjects/HilbertCurve/Package.mmp
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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: "HilbertCurve",
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"),
.package(url: "https://github.com/apple/swift-numerics", from: "1.0.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: "HilbertCurve",
dependencies: [
"SwiftIO",
"MadBoards",
// Use specific library name rather than "MadDrivers" would speed up the build procedure.
.product(name: "MadDrivers", package: "MadDrivers"),
"MadGraphics",
.product(name: "RealModule", package: "swift-numerics"),
]),
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import MadGraphics

struct Hilbert {
// The order of the Hilbert curve.
let order: Int
// The points of the Hilbert curve.
var points = [Point]()

let size: Int
let total: Int

// The four corners of a unit square.
let unitSquarePoints = [Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0)]

// Generate a Hilbert curve with a specified order.
init(order: Int, canvas: Canvas) {
self.order = order

// Total number of points in the curve.
size = Int(Float.pow(2, order))
total = size * size
for i in 0..<total {
points.append(hilbert(i))
}
}

// Generate the points of the Hilbert curve with the given index.
func hilbert(_ i: Int) -> Point {
var i = i
// One of the four corners of the unit square, which serves as the initial point.
var point = unitSquarePoints[i % 4]

// Update the point iteratively.
for j in 1..<order {
let lastPoint = point
let offset = Int(Float.pow(2, j))
i /= 4

// Update the point based on its corresponding quadrant.
switch i % 4 {
case 0:
point.x = lastPoint.y
point.y = lastPoint.x
case 1:
point.y += offset
case 2:
point.x += offset
point.y += offset
case 3:
point.x = offset - 1 - lastPoint.y
point.y = offset - 1 - lastPoint.x
point.x += offset
default: break
}
}

return point
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import SwiftIO
import MadBoard
import ST7789
import MadGraphics
import RealModule

@main
public struct HilbertCurve {
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)

// Canvas used to draw the Hilbert curve.
let canvas = Canvas(width: 128, height: 128)
var frameBuffer = [UInt16](repeating: 0, count: canvas.width * canvas.height)

var colorIndex = 0
let colors: [Color] = [
.red, .orange, .yellow, .lime, .blue,
Color(UInt32(0x4B0082)), Color(UInt32(0x9400D3))
]

let minOrder = 1
let maxOrder = 6
var order = minOrder
var increaseOrder = true

var hilbert = Hilbert(order: order, canvas: canvas)
// Scale and position each point to fit the canvas.
var length: Int { canvas.width / hilbert.size }

var pointIndex = 1

drawBorder()

while true {
if pointIndex == hilbert.total {
// Generate a new Hilbert curve.
order = increaseOrder ? order + 1 : order - 1

if order == maxOrder {
increaseOrder = false
} else if order == minOrder {
increaseOrder = true
}

hilbert = Hilbert(order: order, canvas: canvas)
// Clear the canvas.
canvas.fillRectangle(at: Point(0, 0), width: canvas.width, height: canvas.height, color: Color.black)
drawBorder()

colorIndex = 0
pointIndex = 1

sleep(ms: 500)
} else {
// Draw a single line each time.
drawLine(pointIndex)

colorIndex = (pointIndex / 4) % colors.count
pointIndex += 1

sleep(ms: 2)
}

updateDisplay(canvas: canvas, frameBuffer: &frameBuffer, screen: screen)
}

// Connect the given point with its preceding point on the curve.
func drawLine(_ index: Int) {
let x1 = hilbert.points[index - 1].x * length + length / 2
let y1 = hilbert.points[index - 1].y * length + length / 2
let x2 = hilbert.points[index].x * length + length / 2
let y2 = hilbert.points[index].y * length + length / 2

canvas.drawLine(from: Point(x1, y1), to: Point(x2, y2), color: colors[colorIndex])
}

// Outline the canvas with a border.
func drawBorder() {
canvas.drawLine(from: Point(0, 0), to: Point(canvas.width - 1, 0), color: Color.silver)
canvas.drawLine(from: Point(0, canvas.height - 1), to: Point(canvas.width - 1, canvas.height - 1), color: Color.silver)
canvas.drawLine(from: Point(0, 0), to: Point(0, canvas.height - 1), color: Color.silver)
canvas.drawLine(from: Point(canvas.width - 1, 0), to: Point(canvas.width - 1, canvas.height - 1), color: Color.silver)
}

// 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()
}
}
}

0 comments on commit ce34801

Please sign in to comment.