Last active
March 14, 2016 14:23
-
-
Save numist/32e90c48ad51b335569e to your computer and use it in GitHub Desktop.
A function that plots (as a string) a function of (Double) -> Double
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
import Darwin // Provides: sin/cos/tan | |
// memoize function from https://gist.github.com/berkus/8a9e104f8aac5d025eb5 | |
func memoize<T: Hashable, U>( body: ( (T)->U, T )->U ) -> (T)->U { | |
var memo = Dictionary<T, U>() | |
var result: ((T)->U)! | |
result = { x in | |
if let q = memo[x] { return q } | |
let r = body(result, x) | |
memo[x] = r | |
return r | |
} | |
return result | |
} | |
func popcount(p: UInt8) -> Int { | |
var result = 0 | |
for i in 0..<8 { | |
if (p & UInt8(1 << i)) != 0 { result += 1} | |
} | |
return result | |
} | |
func plot(f: (Double) -> Double, p: (String) -> Void, window: ((Double, Double), (Double, Double)), outputSize: (Int, Int)) { | |
let chartOrigin = window.0 | |
let apogee = window.1 | |
precondition(chartOrigin.0 < apogee.0 && chartOrigin.1 < apogee.1) | |
let width = outputSize.0 | |
let height = outputSize.1 | |
precondition(width > 0 && height > 0) | |
let chartWidth = apogee.0 - chartOrigin.0 | |
let cellWidth = chartWidth / Double(outputSize.0) | |
let chartHeight = apogee.1 - chartOrigin.1 | |
let cellHeight = chartHeight / Double(outputSize.1) | |
// Compute cell center in terms of function input space | |
let g: (Int) -> Int = { px in | |
let fmx = chartOrigin.0 + Double(px) * cellWidth + (cellWidth / 2) | |
let fy = f(fmx) | |
return Int((fy - chartOrigin.1) / cellHeight) | |
} | |
let h = memoize { _, x in g(x) } | |
/* This is the only control flow path using h (and thus g, and thus f) | |
* TODO: memoization in a µC isn't really a thing; the values of `column` in calls to `cellWillDraw()` | |
* should be well-defined (with respect to `window.(0...1).0`); establish those bounds and | |
* replace cellWillDraw with a LUT | |
*/ | |
let cellWillDraw: (Int,Int) -> Bool = { column, row in | |
let l = h(column - 1) | |
let val = h(column) | |
return val == row || (row > val && row < l) || (row < val && row > l) | |
} | |
// Represent cell neighborhood with an 8-bit vector. | |
// Bit positions are arbitrarily chosen; doesn't matter as long as each direction is unique. | |
let (nw, n, ne, e, se, s, sw, w) = (UInt8(1<<0), UInt8(1<<1), UInt8(1<<2), UInt8(1<<3), UInt8(1<<4), UInt8(1<<5), UInt8(1<<6), UInt8(1<<7)) | |
let neighborBitMapForPoint: (Int, Int) -> UInt8 = { column, row in | |
var result = UInt8(0) | |
if cellWillDraw(column - 1, row + 1) { result |= nw } | |
if cellWillDraw(column , row + 1) { result |= n } | |
if cellWillDraw(column + 1, row + 1) { result |= ne } | |
if cellWillDraw(column + 1, row ) { result |= e } | |
if cellWillDraw(column + 1, row - 1) { result |= se } | |
if cellWillDraw(column , row - 1) { result |= s } | |
if cellWillDraw(column - 1, row - 1) { result |= sw } | |
if cellWillDraw(column - 1, row ) { result |= w } | |
return result | |
} | |
for row in (0..<height).reverse() { | |
for col in 0..<width { | |
if !cellWillDraw(col, row) { | |
p(" ") | |
} else { | |
switch neighborBitMapForPoint(col, row) { | |
case 0: | |
p("o") | |
case let shape where shape & (w|e) == (w|e) && shape & (n|s) == 0: | |
p("-") | |
case let shape where shape & (sw|ne) == (sw|ne) && (shape & (s|se|e) == 0 || shape & (w|nw|n) == 0): | |
p ("/") | |
case let shape where shape & (nw|se) == (nw|se) && (shape & (w|sw|s) == 0 || shape & (n|ne|e) == 0): | |
p("\\") | |
case let shape where (shape & n) != 0: | |
if (shape & (sw|s|se)) == 0 { | |
p("'") | |
} else if (shape & sw) != 0 { | |
p(";") | |
} else if (shape & se) != 0 { | |
p(":") | |
} else { | |
p("|") | |
} | |
case let shape where (shape & s) != 0: | |
if shape & (nw|n|ne) == 0 { | |
p(".") | |
} else if shape & (nw|ne) != 0 { | |
p(":") | |
} else { | |
p("|") | |
} | |
case let shape where (shape & s) == 0 && (shape & (nw|n|ne)) != 0: | |
if (shape & nw) != 0 { | |
p("`") | |
} else { | |
p("'") | |
} | |
case let shape where (shape & n) == 0 && (shape & (sw|s|se)) != 0: | |
if (shape & sw) != 0 { | |
p(",") | |
} else { | |
p(".") | |
} | |
default: | |
p("X") | |
} | |
} | |
} | |
p("\n") | |
} | |
} | |
// .----. .----. | |
// / \ / \ | |
// : \ : \ | |
// ; : ; : | |
// / : / : | |
// : \ : \ | |
// ; : ; : | |
// : : : : | |
// ; : ; : | |
// : : : : | |
// ; : ; : | |
// / : / : | |
// \ : \ : | |
// : ; : ; | |
// : : : : | |
// : ; : ; | |
// : : : : | |
// : ; : ; | |
// : / : / | |
// \ : \ : | |
// : ; : ; | |
// : / : / | |
// \ / \ / | |
// `----' `----' | |
plot({sin($0)}, p: { print($0, terminator: "") }, window: ((0, -1), (M_PI * 4, 1)), outputSize: (80, 24)) | |
// ----. .---- | |
// `. .' | |
// `. .' | |
// `. .' | |
// \ / | |
// \ / | |
// `. .' | |
// \ / | |
// \ / | |
// \ / | |
// \ / | |
// \ / | |
// \ / | |
// \ / | |
// \ / | |
// \ / | |
// \ / | |
// `. .' | |
// \ / | |
// \ / | |
// `. .' | |
// `. .' | |
// `. .' | |
// `--------' | |
plot({cos($0)}, p: { print($0, terminator: "") }, window: ((0, -1), (M_PI * 2, 1)), outputSize: (80, 24)) | |
// ; | ; | | |
// : | : | | |
// ; | ; | | |
// : | : | | |
// ; | ; | | |
// / | / | | |
// / | / | | |
// / | / | | |
// .' | .' | | |
// .-' | .-' | | |
// .-' | .-' | | |
// .--' | .--' | | |
// | .--' | .--' | |
// | .-' | .-' | |
// | .-' | .-' | |
// | .' | .' | |
// | / | / | |
// | / | / | |
// | : | : | |
// | ; | ; | |
// | : | : | |
// | ; | ; | |
// | : | : | |
// | | | | | |
plot({tan($0)}, p: { print($0, terminator: "") }, window: ((0, -4), (M_PI * 2, 4)), outputSize: (80, 24)) | |
// . . . | |
// |\ |\ |\ | |
// | \ | \ | \ | |
// | \ | \ | \ | |
// | \ | \ | \ | |
// | \ | \ | \ | |
// | \ | \ | \ | |
// | \ | \ | \ | |
// | \ | \ | \ | |
// | \ | \ | \ | |
//\ | \ | \ | | |
// \ | \ | \ | | |
// \ | \ | \ | | |
// \ | \ | \ | | |
// \ | \ | \ | | |
// \ | \ | \ | | |
// \ | \ | \ | | |
// \ | \ | \ | | |
// \ ; \ ; \ ; | |
// ` ` ` | |
let sawtooth: (Double) -> Double = { x in | |
return abs((x - Double(Int(x))) - 1) | |
} | |
plot(sawtooth, p: { print($0, terminator: "") }, window: ((0.5, 0), (3.5, 1)), outputSize: (60, 20)) | |
// ,. ,. | |
// / \ / \ | |
// / \ / \ | |
// / \ / \ | |
// / \ / \ | |
// / \ / \ | |
// / \ / \ | |
// / \ / \ | |
// / \ / \ | |
/// \ / \ | |
// \ / | |
// \ / | |
// \ / | |
// \ / | |
// \ / | |
// \ / | |
// \ / | |
// \ / | |
// \ / | |
// `' | |
let triangle: (Double) -> Double = { x in | |
let val = x - Double(Int(x)) | |
if Int(x) % 2 == 0 { | |
return val | |
} else { | |
return 1 - val | |
} | |
} | |
plot(triangle, p: { print($0, terminator: "") }, window: ((0.5, 0), (3.5, 1)), outputSize: (60, 20)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Please forgive the lack of thoughtfulness around generics and such; this is an experiment for code that's ultimately intended to allow a microcontroller to plot graphs over a serial interface.