Merge branch 'master' into convolution

This commit is contained in:
Лu Лinveгa 2019-07-22 10:38:23 +12:00 committed by GitHub
commit aea4184168
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1634 additions and 230 deletions

View File

@ -25,6 +25,8 @@ npm start
## Library ## Library
Additional functions can be found in the [includes](https://github.com/hundredrabbits/Ronin/tree/master/desktop/sources/lisp), you can also look at the [examples](https://github.com/hundredrabbits/Ronin/tree/master/examples) to see them in action.
- `(import path rect)` Imports a graphic file with format. - `(import path rect)` Imports a graphic file with format.
- `(export path ~format ~quality)` Exports a graphic file with format. - `(export path ~format ~quality)` Exports a graphic file with format.
- `(open path)` Imports a graphic file and resizes the frame. - `(open path)` Imports a graphic file and resizes the frame.
@ -38,6 +40,17 @@ npm start
- `(stroke ~shape)` Strokes a shape. - `(stroke ~shape)` Strokes a shape.
- `(fill ~rect)` Fills a shape. - `(fill ~rect)` Fills a shape.
- `(clear ~rect)` Clears a rect. - `(clear ~rect)` Clears a rect.
- `(frame)` Returns a rect of the frame.
- `(center)` Returns a position of the center of the frame.
- `(resize w h ~fit)` Resizes the canvas to target w and h, returns the rect.
- `(rescale w h)` Rescales the canvas to target ratio of w and h, returns the rect.
- `(crop rect)` Crop canvas to rect.
- `(clone a b)`
- `(theme variable ~el)`
- `(gradient [x1 y1 x2 y2] ~colors 'black'])`
- `(pixels rect fn q)`
- `(saturation pixel ~q)`
- `(contrast pixel ~q)`
- `(concat ...items)` Concat multiple strings. - `(concat ...items)` Concat multiple strings.
- `(add ...args)` Adds values. - `(add ...args)` Adds values.
- `(sub ...args)` Subtracts values. - `(sub ...args)` Subtracts values.
@ -71,17 +84,8 @@ npm start
- `(get item key)` Gets an object's parameter with name. - `(get item key)` Gets an object's parameter with name.
- `(set item key val)` Sets an object's parameter with name as value. - `(set item key val)` Sets an object's parameter with name as value.
- `(of h ...keys)` Gets object parameters with names. - `(of h ...keys)` Gets object parameters with names.
- `(frame)` Returns a rect of the frame. - `(keys item)` Returns a list of the object's keys
- `(center)` Returns a position of the center of the frame. - `(values item)` Returns a list of the object's values
- `(resize w h ~fit)` Resizes the canvas to target w and h, returns the rect.
- `(rescale w h)` Rescales the canvas to target ratio of w and h, returns the rect.
- `(crop rect)` Crop canvas to rect.
- `(clone a b)`
- `(theme variable ~el)`
- `(gradient [x1 y1 x2 y2] ~colors 'black'])`
- `(pixels rect fn q)`
- `(saturation pixel ~q)`
- `(contrast pixel ~q)`
- `(dir ~path)` Returns the content of a directory. - `(dir ~path)` Returns the content of a directory.
- `(file ~path)` Returns the content of a file. - `(file ~path)` Returns the content of a file.
- `(dirpath ~path)` Returns the path of a directory. - `(dirpath ~path)` Returns the path of a directory.
@ -93,6 +97,7 @@ npm start
- `(js)` Javascript interop. - `(js)` Javascript interop.
- `(test name a b)` - `(test name a b)`
- `(benchmark fn)` logs time taken to execute a function. - `(benchmark fn)` logs time taken to execute a function.
- `(osc ~address)` returns the last received osc msg on port 12940
## Extras ## Extras

View File

@ -1,6 +1,7 @@
const { app, BrowserWindow, webFrame, Menu } = require('electron') const { app, BrowserWindow, webFrame, Menu } = require('electron')
const path = require('path') const path = require('path')
const url = require('url') const url = require('url')
const osc = require('osc')
const shell = require('electron').shell const shell = require('electron').shell
let isShown = true let isShown = true

1262
desktop/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
"start": "electron .", "start": "electron .",
"install": "electron-rebuild",
"clean": "rm -r ~/Desktop/Ronin-darwin-x64/ ; rm -r ~/Desktop/Ronin-linux-x64/ ; rm -r ~/Desktop/Ronin-win32-x64/ ; echo 'cleaned build location'", "clean": "rm -r ~/Desktop/Ronin-darwin-x64/ ; rm -r ~/Desktop/Ronin-linux-x64/ ; rm -r ~/Desktop/Ronin-win32-x64/ ; echo 'cleaned build location'",
"build_osx": "electron-packager . Ronin --platform=darwin --arch=x64 --out ~/Desktop/ --overwrite --icon=icon.icns ; echo 'Built for OSX'", "build_osx": "electron-packager . Ronin --platform=darwin --arch=x64 --out ~/Desktop/ --overwrite --icon=icon.icns ; echo 'Built for OSX'",
"build_linux": "electron-packager . Ronin --platform=linux --arch=x64 --out ~/Desktop/ --overwrite --icon=icon.ico ; echo 'Built for LINUX'", "build_linux": "electron-packager . Ronin --platform=linux --arch=x64 --out ~/Desktop/ --overwrite --icon=icon.ico ; echo 'Built for LINUX'",
@ -16,8 +17,12 @@
"push_status": "~/butler status hundredrabbits/ronin", "push_status": "~/butler status hundredrabbits/ronin",
"push": "npm run build ; npm run push_theme ; npm run push_osx ; npm run push_linux ; npm run push_win ; npm run clean ; npm run push_status" "push": "npm run build ; npm run push_theme ; npm run push_osx ; npm run push_linux ; npm run push_win ; npm run clean ; npm run push_status"
}, },
"dependencies": {
"osc": "^2.3.1"
},
"devDependencies": { "devDependencies": {
"electron": "^5.0.6", "electron": "^5.0.6",
"electron-packager": "^13.1.1" "electron-packager": "^13.1.1",
"electron-rebuild": "^1.8.5"
} }
} }

View File

@ -9,6 +9,8 @@
<script type="text/javascript" src="scripts/surface.js"></script> <script type="text/javascript" src="scripts/surface.js"></script>
<script type="text/javascript" src="scripts/lisp.js"></script> <script type="text/javascript" src="scripts/lisp.js"></script>
<script type="text/javascript" src="scripts/library.js"></script> <script type="text/javascript" src="scripts/library.js"></script>
<script type="text/javascript" src="scripts/osc.js"></script>
<link rel="stylesheet" type="text/css" href="links/reset.css"/> <link rel="stylesheet" type="text/css" href="links/reset.css"/>
<link rel="stylesheet" type="text/css" href="links/fonts.css"/> <link rel="stylesheet" type="text/css" href="links/fonts.css"/>
<link rel="stylesheet" type="text/css" href="links/main.css"/> <link rel="stylesheet" type="text/css" href="links/main.css"/>
@ -18,10 +20,10 @@
<body> <body>
<script type="text/javascript"> <script type="text/javascript">
const {dialog,app} = require('electron').remote; const {dialog,app} = require('electron').remote;
const fs = require('fs'); const fs = require('fs')
const app_path = app.getAppPath(); const ronin = new Ronin()
const ronin = new Ronin();
ronin.controller = new Controller(); ronin.controller = new Controller()
ronin.controller.add("default","*","About",() => { require('electron').shell.openExternal('https://github.com/hundredrabbits/Dotgrid'); },"CmdOrCtrl+,"); ronin.controller.add("default","*","About",() => { require('electron').shell.openExternal('https://github.com/hundredrabbits/Dotgrid'); },"CmdOrCtrl+,");
ronin.controller.add("default","*","Fullscreen",() => { app.toggleFullscreen() },"CmdOrCtrl+Enter"); ronin.controller.add("default","*","Fullscreen",() => { app.toggleFullscreen() },"CmdOrCtrl+Enter");
ronin.controller.add("default","*","Hide",() => { app.toggleVisible() },"CmdOrCtrl+H"); ronin.controller.add("default","*","Hide",() => { app.toggleVisible() },"CmdOrCtrl+H");
@ -53,9 +55,10 @@
ronin.controller.add("default","Theme","Reset Theme",() => { ronin.theme.reset() },"CmdOrCtrl+Shift+Backspace") ronin.controller.add("default","Theme","Reset Theme",() => { ronin.theme.reset() },"CmdOrCtrl+Shift+Backspace")
ronin.controller.addSpacer('default', 'Theme', 'Download') ronin.controller.addSpacer('default', 'Theme', 'Download')
ronin.controller.add("default","Theme","Download Themes..",() => { require('electron').shell.openExternal('https://github.com/hundredrabbits/Themes') }) ronin.controller.add("default","Theme","Download Themes..",() => { require('electron').shell.openExternal('https://github.com/hundredrabbits/Themes') })
ronin.controller.commit(); ronin.controller.commit()
ronin.install(document.body);
window.addEventListener('load', () => { ronin.start(); }) ronin.install(document.body)
window.addEventListener('load', () => { ronin.start() })
</script> </script>
</body> </body>
</html> </html>

View File

@ -14,4 +14,4 @@ body { margin:0px; padding:0px; overflow:hidden; font-family:"input_mono_regular
#ronin canvas#guide { background:none; } #ronin canvas#guide { background:none; }
#ronin canvas#surface { border-radius: 2px } #ronin canvas#surface { border-radius: 2px }
#ronin.hidden canvas#surface, #ronin.hidden canvas#guide { left:10px; } #ronin.hidden canvas#surface, #ronin.hidden canvas#guide { left:0px; }

View File

@ -0,0 +1,34 @@
;
(echo "Loading prelude.lisp")
; translate
(defn translate
(r p)
(clone r
(rect
(of p :x)
(of p :y)
(of r :w)
(of r :h))))
; times
(defn times
(v f)
(
(f v)
(if
(gt v 1)
(times
(sub v 1) f))))
; convert deg to radians
(defn deg-rad
(deg)
(mul deg
(div PI 180)))
; position on a circle from angle
(defn circle-pos
(cx cy r a) {:x
(add cx
(mul r
(cos a))) :y
(add cy
(mul r
(sin a)))})

View File

@ -31,11 +31,21 @@ function Commander (ronin) {
this.hide() this.hide()
} }
this.cache = ''
this.run = (txt = this._input.value) => { this.run = (txt = this._input.value) => {
if (txt.indexOf('$') > -1) { ronin.log('Present: $'); return } if (txt.indexOf('$') > -1) { ronin.log('Present: $'); return }
const inter = new Lisp(txt, ronin.library) this.cache = txt
inter.toPixels() if (ronin.always !== true) {
ronin.always === true && requestAnimationFrame(() => this.run(txt)) ronin.surface.maximize()
ronin.interpreter.run(this.cache)
}
}
this.loop = () => {
ronin.surface.maximize()
ronin.interpreter.run(this.cache)
ronin.always === true && requestAnimationFrame(() => this.loop())
} }
this.load = function (txt) { this.load = function (txt) {
@ -47,6 +57,10 @@ function Commander (ronin) {
this.reindent = function () { this.reindent = function () {
let val = this._input.value.replace(/\n/g, '').replace(/ +(?= )/g, '').replace(/\( \(/g, '((').replace(/\) \)/g, '))').trim() let val = this._input.value.replace(/\n/g, '').replace(/ +(?= )/g, '').replace(/\( \(/g, '((').replace(/\) \)/g, '))').trim()
let depth = 0 let depth = 0
if (val.split('(').length !== val.split(')').length) {
ronin.log('Uneven number of parens.')
return
}
for (let i = 0; i < val.length; i++) { for (let i = 0; i < val.length; i++) {
const c = val.charAt(i) const c = val.charAt(i)
if (c === '(') { depth++ } else if (c === ')') { depth-- } if (c === '(') { depth++ } else if (c === ')') { depth-- }
@ -68,7 +82,7 @@ function Commander (ronin) {
// Logs // Logs
if (msg && msg !== this._log.textContent) { if (msg && msg !== this._log.textContent) {
this._log.textContent = `${msg}` this._log.textContent = `${msg}`
console.log(msg) // console.log(msg)
} }
// Source // Source
const _source = `${ronin.source} ${this._input.value.split('\n').length} lines` const _source = `${ronin.source} ${this._input.value.split('\n').length} lines`

View File

@ -7,7 +7,7 @@ function Library (ronin) {
this.export = (path, format = 'image/png', quality = 1.0) => { // Exports a graphic file with format. this.export = (path, format = 'image/png', quality = 1.0) => { // Exports a graphic file with format.
if (!path) { console.warn('Missing export path'); return path } if (!path) { console.warn('Missing export path'); return path }
var dataUrl = ronin.surface.el.toDataURL(format, quality) const dataUrl = ronin.surface.el.toDataURL(format, quality)
const data = dataUrl.replace(/^data:image\/png;base64,/, '').replace(/^data:image\/jpeg;base64,/, '') const data = dataUrl.replace(/^data:image\/png;base64,/, '').replace(/^data:image\/jpeg;base64,/, '')
fs.writeFileSync(path, data, 'base64') fs.writeFileSync(path, data, 'base64')
return path return path
@ -31,8 +31,8 @@ function Library (ronin) {
return { x, y, w, h, t } return { x, y, w, h, t }
} }
this.circle = (x, y, r, t = 'circle') => { // Returns a circle shape. this.circle = (cx, cy, r, t = 'circle') => { // Returns a circle shape.
return { x, y, r, t } return { cx, cy, r, t }
} }
this.line = (a, b, t = 'line') => { // Returns a line shape. this.line = (a, b, t = 'line') => { // Returns a line shape.
@ -64,6 +64,83 @@ function Library (ronin) {
return rect return rect
} }
// Frame
this.frame = () => { // Returns a rect of the frame.
return ronin.surface.getFrame()
}
this.center = () => { // Returns a position of the center of the frame.
const rect = this.frame()
return this.pos(rect.w / 2, rect.h / 2)
}
this.resize = async (w, h, fit = true) => { // Resizes the canvas to target w and h, returns the rect.
const rect = { x: 0, y: 0, w, h }
const a = document.createElement('img')
const b = document.createElement('img')
a.src = ronin.surface.el.toDataURL()
await ronin.surface.resizeImage(a, b)
ronin.surface.resize(rect, fit)
return ronin.surface.draw(b, rect)
}
this.rescale = async (w, h) => { // Rescales the canvas to target ratio of w and h, returns the rect.
const rect = { x: 0, y: 0, w: this.frame().w * w, h: this.frame().h * h }
const a = document.createElement('img')
const b = document.createElement('img')
a.src = ronin.surface.el.toDataURL()
await ronin.surface.resizeImage(a, b)
ronin.surface.resize(rect, true)
return ronin.surface.draw(b, rect)
}
this.crop = async (rect) => { // Crop canvas to rect.
return ronin.surface.crop(rect)
}
this.clone = (a, b) => {
ronin.surface.clone(a, b)
return [a, b]
}
this.theme = (variable, el = document.documentElement) => {
// ex. (theme "f_main") -> :root { --f_main: "#fff" }
return getComputedStyle(el).getPropertyValue(`--${variable}`)
}
// Gradients
this.gradient = ([x1, y1, x2, y2], colors = ['white', 'black']) => {
return ronin.surface.linearGradient(x1, y1, x2, y2, colors)
}
// Pixels
this.pixels = (rect, fn, q) => {
const img = ronin.surface.context.getImageData(0, 0, rect.w, rect.h)
for (let i = 0, loop = img.data.length; i < loop; i += 4) {
const pixel = { r: img.data[i], g: img.data[i + 1], b: img.data[i + 2], a: img.data[i + 3] }
const processed = fn(pixel, q)
img.data[i] = processed[0]
img.data[i + 1] = processed[1]
img.data[i + 2] = processed[2]
img.data[i + 3] = processed[3]
}
ronin.surface.context.putImageData(img, 0, 0)
return rect
}
this.saturation = (pixel, q = 1) => {
const color = 0.2126 * pixel.r + 0.7152 * pixel.g + 0.0722 * pixel.b
return [(color * (1 - q)) + (pixel.r * q), (color * (1 - q)) + (pixel.g * q), (color * (1 - q)) + (pixel.b * q), pixel.a]
}
this.contrast = (pixel, q = 1) => {
const intercept = 128 * (1 - q)
return [pixel.r * q + intercept, pixel.g * q + intercept, pixel.b * q + intercept, pixel.a]
}
// Strings // Strings
this.concat = function (...items) { // Concat multiple strings. this.concat = function (...items) { // Concat multiple strings.
@ -233,78 +310,12 @@ function Library (ronin) {
}, h) }, h)
} }
// Frame this.keys = (item) => { // Returns a list of the object's keys
return Object.keys(item)
this.frame = () => { // Returns a rect of the frame.
return ronin.surface.getFrame()
} }
this.center = () => { // Returns a position of the center of the frame. this.values = (item) => { // Returns a list of the object's values
const rect = this.frame() return Object.values(item)
return this.pos(rect.w / 2, rect.h / 2)
}
this.resize = async (w, h, fit = true) => { // Resizes the canvas to target w and h, returns the rect.
const rect = { x: 0, y: 0, w, h }
const a = document.createElement('img')
const b = document.createElement('img')
a.src = ronin.surface.el.toDataURL()
await ronin.surface.resizeImage(a, b)
ronin.surface.resize(rect, fit)
return ronin.surface.draw(b, rect)
}
this.rescale = async (w, h) => { // Rescales the canvas to target ratio of w and h, returns the rect.
const rect = { x: 0, y: 0, w: this.frame().w * w, h: this.frame().h * h }
const a = document.createElement('img')
const b = document.createElement('img')
a.src = ronin.surface.el.toDataURL()
await ronin.surface.resizeImage(a, b)
ronin.surface.resize(rect, true)
return ronin.surface.draw(b, rect)
}
this.crop = async (rect) => { // Crop canvas to rect.
return ronin.surface.crop(rect)
}
this.clone = (a, b) => {
ronin.surface.clone(a, b)
return [a, b]
}
this.theme = (variable, el = document.documentElement) => {
// ex. (theme "f_main") -> :root { --f_main: "#fff" }
return getComputedStyle(el).getPropertyValue(`--${variable}`)
}
// Gradients
this.gradient = ([x1, y1, x2, y2], colors = ['white', 'black']) => {
return ronin.surface.linearGradient(x1, y1, x2, y2, colors)
}
// Pixels
this.pixels = (rect, fn, q) => {
const img = ronin.surface.context.getImageData(rect.x, rect.y, rect.w, rect.h)
for (let i = 0, loop = img.data.length; i < loop; i += 4) {
const pixel = { r: img.data[i], g: img.data[i + 1], b: img.data[i + 2], a: img.data[i + 3] }
const processed = fn(pixel, q)
img.data.set(processed, i);
}
ronin.surface.context.putImageData(img, rect.x, rect.y)
return rect
}
this.saturation = (pixel, q = 1) => {
const color = 0.2126 * pixel.r + 0.7152 * pixel.g + 0.0722 * pixel.b * (1 - q)
return [color + (pixel.r * q), color + (pixel.g * q), color + (pixel.b * q), pixel.a]
}
this.contrast = (pixel, q = 1) => {
const intercept = 128 * (1 - q)
return [pixel.r * q + intercept, pixel.g * q + intercept, pixel.b * q + intercept, pixel.a]
} }
// Convolve // Convolve
@ -370,6 +381,16 @@ function Library (ronin) {
return args return args
} }
this.table = (arg) => {
console.table(arg)
return arg
}
this.debug = (arg) => {
console.log(arg)
return arg
}
this.time = (rate = 1) => { // Returns timestamp in milliseconds. this.time = (rate = 1) => { // Returns timestamp in milliseconds.
return (Date.now() * rate) return (Date.now() * rate)
} }
@ -397,4 +418,10 @@ function Library (ronin) {
console.log(`time taken: ${Date.now() - start}ms`) console.log(`time taken: ${Date.now() - start}ms`)
return result return result
} }
// IO
this.osc = (path) => { // Returns the latest osc message at path
return path ? ronin.osc.msg[path] : ronin.osc.msg
}
} }

View File

@ -1,14 +1,14 @@
'use strict' 'use strict'
function Lisp (input, lib) { function Lisp (lib = {}, includes = []) {
const path = require('path') const path = require('path')
const fs = require('fs') const fs = require('fs')
const TYPES = { identifier: 0, number: 1, string: 2, bool: 3 } const TYPES = { identifier: 0, number: 1, string: 2, bool: 3 }
const Context = function (scope, parent) { const Context = function (scope, parent) {
this.scope = scope this.scope = scope
this.parent = parent this.parent = parent
this.get = function (identifier) { this.get = function (identifier) {
if (identifier in this.scope) { if (identifier in this.scope) {
return this.scope[identifier] return this.scope[identifier]
@ -20,9 +20,9 @@ function Lisp (input, lib) {
const special = { const special = {
include: (input, context) => { include: (input, context) => {
if (!input[1].value || !fs.existsSync(input[1].value)) { console.warn('Source', input[1].value); return [] } if (!input[1].value || !fs.existsSync(input[1].value)) { console.warn('Lisp', 'No file: ' + input[1].value); return [] }
const file = fs.readFileSync(input[1].value, { encoding: 'utf-8' }) const file = fs.readFileSync(input[1].value, { encoding: 'utf-8' })
return interpret(this.parse(file), context) return interpret(this.parse(`(${file})`), context)
}, },
let: function (input, context) { let: function (input, context) {
const letContext = input[1].reduce(function (acc, x) { const letContext = input[1].reduce(function (acc, x) {
@ -60,21 +60,32 @@ function Lisp (input, lib) {
return interpret(input[2], new Context(lambdaScope, context)) return interpret(input[2], new Context(lambdaScope, context))
} }
}, },
__fn: function (input, context) {
return async function () {
const lambdaArguments = arguments
const lambdaScope = [].reduce(function (acc, x, i) {
acc[x.value] = lambdaArguments[i]
return acc
}, {})
return interpret(input.slice(1), new Context(lambdaScope, context))
}
},
if: async function (input, context) { if: async function (input, context) {
if (await interpret(input[1], context)) { if (await interpret(input[1], context)) {
return interpret(input[2], context) return interpret(input[2], context)
} }
return input[3] ? interpret(input[3], context) : [] return input[3] ? interpret(input[3], context) : []
},
__fn: function (input, context) {
return async function () {
const lambdaArguments = arguments
const keys = [...new Set(input.slice(2).flat(100).filter(i =>
i.type === TYPES.identifier &&
i.value[0] === '%'
).map(x => x.value).sort())]
const lambdaScope = keys.reduce(function (acc, x, i) {
acc[x] = lambdaArguments[i]
return acc
}, {})
return interpret(input.slice(1), new Context(lambdaScope, context))
}
},
__obj: async function (input, context) {
const obj = {}
for (let i = 1; i < input.length; i += 2) {
obj[await interpret(input[i], context)] = await interpret(input[i + 1], context)
}
return obj
} }
} }
@ -90,7 +101,7 @@ function Lisp (input, lib) {
} }
const interpret = async function (input, context) { const interpret = async function (input, context) {
if (!input) { console.warn('error', context.scope); return null } if (!input) { console.warn('Lisp', 'error', context.scope); return null }
if (context === undefined) { if (context === undefined) {
return interpret(input, new Context(lib)) return interpret(input, new Context(lib))
@ -108,6 +119,8 @@ function Lisp (input, lib) {
return { type: TYPES.number, value: parseFloat(input) } return { type: TYPES.number, value: parseFloat(input) }
} else if (input[0] === '"' && input.slice(-1) === '"') { } else if (input[0] === '"' && input.slice(-1) === '"') {
return { type: TYPES.string, value: input.slice(1, -1) } return { type: TYPES.string, value: input.slice(1, -1) }
} else if (input[0] === ':') {
return { type: TYPES.string, value: input.slice(1) }
} else if (input === 'true' || input === 'false') { } else if (input === 'true' || input === 'false') {
return { type: TYPES.bool, value: input === 'true' } return { type: TYPES.bool, value: input === 'true' }
} else { } else {
@ -124,10 +137,14 @@ function Lisp (input, lib) {
input.unshift('__fn') input.unshift('__fn')
list.push(parenthesize(input, [])) list.push(parenthesize(input, []))
return parenthesize(input, list) return parenthesize(input, list)
} else if (token === '{') {
input.unshift('__obj')
list.push(parenthesize(input, []))
return parenthesize(input, list)
} else if (token === '(') { } else if (token === '(') {
list.push(parenthesize(input, [])) list.push(parenthesize(input, []))
return parenthesize(input, list) return parenthesize(input, list)
} else if (token === ')') { } else if (token === ')' || token === '}') {
return list return list
} else { } else {
return parenthesize(input, list.concat(categorize(token))) return parenthesize(input, list.concat(categorize(token)))
@ -135,21 +152,35 @@ function Lisp (input, lib) {
} }
const tokenize = function (input) { const tokenize = function (input) {
const i = input.replace(/^\;.*\n?/gm, '').split('"') const i = input.replace(/^\;.*\n?/gm, '').replace('λ', 'lambda ').split('"')
return i.map(function (x, i) { return i.map(function (x, i) {
return i % 2 === 0 ? return i % 2 === 0
x.replace(/\(/g, ' ( ').replace(/\)/g, ' ) ').replace(/' \( /g, ' \'( ') ? x.replace(/\(/g, ' ( ')
.replace(/\)/g, ' ) ')
.replace(/' \( /g, ' \'( ') // '()
.replace(/\{/g, ' { ') // {}
.replace(/\}/g, ' } ') // {}
: x.replace(/ /g, '!whitespace!') : x.replace(/ /g, '!whitespace!')
}) })
.join('"').trim().split(/\s+/) .join('"').trim().split(/\s+/)
.map(function (x) { return x.replace(/!whitespace!/g, ' ') }) .map(function (x) { return x.replace(/!whitespace!/g, ' ') })
}
this.inc = function () {
return includes.reduce((acc, item) => {
const p = path.join(__dirname, `lisp/${item}.lisp`)
if (!fs.existsSync(p)) { console.warn('Lisp', `Missing include: ${p}`); return acc }
return `${acc}(include "${p}") `
}, '')
} }
this.parse = function (input) { this.parse = function (input) {
return parenthesize(tokenize(`(${input})`)) return parenthesize(tokenize(input))
} }
this.toPixels = async function () { this.run = async function (input) {
return interpret(this.parse(input)) return interpret(this.parse(`(
${this.inc()}
${input})`))
} }
} }

View File

@ -0,0 +1,23 @@
'use strict'
function Osc (ronin) {
const osc = require('osc')
this.msg = {}
this.start = function () {
const udpPort = new osc.UDPPort({
localAddress: '0.0.0.0',
localPort: 49162,
metadata: true
})
udpPort.on('message', this.onMsg)
udpPort.open()
ronin.log('OSC', 'Started.')
}
this.onMsg = (msg, timeTag, info) => {
this.msg[msg.address] = msg.args
// ronin.log(`${info.address}:${info.port} > ${msg.args}`, info)
}
}

View File

@ -11,6 +11,8 @@ function Ronin () {
b_inv: '#ffb545' b_inv: '#ffb545'
} }
this.includes = ['prelude']
this.el = document.createElement('div') this.el = document.createElement('div')
this.el.id = 'ronin' this.el.id = 'ronin'
@ -19,7 +21,8 @@ function Ronin () {
this.commander = new Commander(this) this.commander = new Commander(this)
this.surface = new Surface(this) this.surface = new Surface(this)
this.library = new Library(this) this.library = new Library(this)
this.interpreter = new Lisp(this.library, this.includes)
this.osc = new Osc(this)
// Parameters // Parameters
this.always = false this.always = false
@ -43,7 +46,7 @@ function Ronin () {
this.source.start() this.source.start()
this.commander.start() this.commander.start()
this.surface.start() this.surface.start()
console.log('Ronin', 'Started') this.osc.start()
} }
this.reset = function () { this.reset = function () {
@ -61,7 +64,7 @@ function Ronin () {
this.animate = (b = true) => { this.animate = (b = true) => {
if (this.always === b) { return } if (this.always === b) { return }
this.always = b this.always = b
this.commander.run() this.commander.loop()
} }
// Zoom // Zoom

View File

@ -126,12 +126,6 @@ function Source (ronin) {
return `${str}` return `${str}`
} }
this.locate = function (name) {
if (!this.path) { return }
const loc = path.join(this.folder(), name)
return fs.existsSync(loc) ? loc : null
}
// Etc // Etc
this.name = function () { this.name = function () {

View File

@ -100,7 +100,7 @@ function Surface (ronin) {
} }
this.traceCircle = function (circle, context) { this.traceCircle = function (circle, context) {
context.arc(circle.x, circle.y, circle.r, 0, 2 * Math.PI) context.arc(circle.cx, circle.cy, circle.r, 0, 2 * Math.PI)
} }
this.traceText = function (text, context) { this.traceText = function (text, context) {
@ -185,7 +185,7 @@ function Surface (ronin) {
} }
this.maximize = function () { this.maximize = function () {
this.resize({ x: 0, y: 0, w: (window.innerWidth * this.ratio) - 60, h: (window.innerHeight * this.ratio) - 60, t: 'rect' }) this.resize({ x: 0, y: 0, w: ((window.innerWidth - 60) * this.ratio), h: ((window.innerHeight - 60) * this.ratio), t: 'rect' })
} }
this.onResize = function () { this.onResize = function () {

View File

@ -1,17 +1,50 @@
; animate ;
(clear) (clear)
(def t ;
(sin (def seg-count 50)
(div ;
(time) 100))) (def frame-middle
(def pos (div
(add 200 30 (of
(mul 30 t))) (frame) :h) 2))
(defn square ;
(a) (def seg-width
(rect a a a a)) (div
(stroke (of
(square pos) 1 "red") (frame) :w) seg-count))
; set false to stop ;
(animate true) (defn elevation
(i)
(add
(mul
(sin
(add
(time 0.001)
(div i 5)))
(div
(of
(frame) :h) 5)) frame-middle))
;
(defn draw-dash
(i)
(
(def x
(mul
(sub i 1) seg-width))
(def y
(elevation i))
(stroke
(line
(pos x
(elevation
(sub i 1)))
(pos
(add x seg-width)
(elevation i))) 4
(gradient
(50 0
(of
(frame) :w) 0)
("rgba(255,255,255,0)" "white" "#72dec2" "red")))))
;
(times seg-count draw-dash)

View File

@ -1,25 +1,26 @@
(echo (map (lambda (a) (add a 1)) (1 2 3))) (echo (map '(add %1 1) (1 2 3)))
(echo (first (1 2 3))) (echo (first (1 2 3)))
(echo (rest (1 2 3))) (echo (rest (1 2 3)))
(echo (echo
(filter (filter
(lambda (a) (eq 0 (mod a 2))) '(eq 0 (mod %1 2))
(1 2 3 4 5)) (1 2 3 4 5))
) )
(clear) (clear)
(map (lambda (a) (map
(stroke (rect (mul a 30) 20 50 50) '(stroke (rect (mul a 30) 20 50 50)
1 "red")) 1 "red")
(range 0 20 5)) (range 0 20 5))
(map (lambda (a) (map
(stroke '(stroke
(rect (rect
(mul a 10) (mul %1 10)
(add 50 (mul (sin a) 40)) (add 250 (mul (sin %1) 250))
a (mul 4 %1)
(add 20 (mul (cos a) 50))) 1 "red")) (add 20 (mul (cos %1) 50))) 1 "red")
(range 0 200 5)) (range 0 200 5))

View File

@ -7,3 +7,7 @@
; define a function ; define a function
(defn add-two (a) (add 2 a)) (defn add-two (a) (add 2 a))
(echo (add-two 4)) (echo (add-two 4))
; use a lambda
(times 5
(λ (a) (echo (concat "time:" a))))

View File

@ -35,14 +35,14 @@
(test "range simple" (range 0 4) (0 1 2 3 4)) (test "range simple" (range 0 4) (0 1 2 3 4))
(test "range with step" (range 0 4 2) (0 2 4)) (test "range with step" (range 0 4 2) (0 2 4))
(test "range with negative step" (range 0 -4 -1) (0 -1 -2 -3 -4)) (test "range with negative step" (range 0 -4 -1) (0 -1 -2 -3 -4))
(test "map" (map (lambda (a) (add 1 a)) (1 2 3)) (2 3 4)) (test "map" (map (λ (a) (add 1 a)) (1 2 3)) (2 3 4))
(test "filter" (filter (lambda (a) (eq 0 (mod a 2))) (2 3 4 5 6)) (2 4 6)) (test "filter" (filter (λ (a) (eq 0 (mod a 2))) (2 3 4 5 6)) (2 4 6))
(test "reduce" (reduce (lambda (acc val) (add acc val)) (1 2 3) 4) 10) (test "reduce" (reduce (λ (acc val) (add acc val)) (1 2 3) 4) 10)
; Scope ; Scope
(def aaa 123) (def aaa 123)
(def addOne (lambda (a) (add a 1))) (def addOne (λ (a) (add a 1)))
(test "def - value" aaa 123) (test "def - value" aaa 123)
(test "def - func" (addOne 4) 5) (test "def - func" (addOne 4) 5)
(defn addTwo (a) (add 2 a)) (defn addTwo (a) (add 2 a))

View File

@ -16,7 +16,7 @@
(defn dejong (r a b c d) (defn dejong (r a b c d)
(reduce (reduce
(lambda (acc val) (λ (acc val)
(first ( (first (
(_dejong (first acc) (last acc) a b c d) (_dejong (first acc) (last acc) a b c d)
))) )))

View File

@ -5,12 +5,11 @@
(defn glitch (defn glitch
(rec) (rec)
(if (gt rec 1) (if (gt rec 1)
((clone (
(rect (random 400) (random 400) 2 2) (translate
(rect (random 400) (random 400) (rect (random 400) (random 400) (random 10) (random 10))
(random 10) (random 30))) (pos (random 400) (random 400)))
(glitch (sub rec 1)))) (glitch (sub rec 1)))))
)
(import (import
"../static/crystal.jpg" "../static/crystal.jpg"

View File

@ -1,6 +1 @@
( (echo (map '(add %1 2) (4 5 6))
(echo (map '(add 1 2) (4 5 6))
)

15
examples/objects.lisp Normal file
View File

@ -0,0 +1,15 @@
; objects
(test "symbols" :a "a")
(def ob {:a "fantastic" :b 2})
((of (js) :console :log) ob)
(echo (of ob :a))
(echo (keys ob))
(echo (values ob))
(set ob :a 4)
(echo (of ob :a))

1
examples/orca.lisp Normal file
View File

@ -0,0 +1 @@
(debug (of (osc "/a") :0 :value))

11
examples/osc.lisp Normal file
View File

@ -0,0 +1,11 @@
(clear)
(def x (of (osc "/test") "args" "0" "value"))
(def y (of (osc "/test") "args" "1" "value"))
(def r (of (osc "/test") "args" "2" "value"))
(fill
(circle x y r)
"red")
(stroke
(circle x y r)
5 "white")

37
examples/stars.lisp Normal file
View File

@ -0,0 +1,37 @@
; stars
(clear)
;
(defn draw-spoke
(cx cy r a)
(
(stroke
(line
(pos cx cy)
(circle-pos cx cy r a)) 2 "white")))
;
(defn draw-star
(cx cy r c)
(
(times c
(λ
(i)
(
(draw-spoke cx cy r
(deg-rad
(mul i
(div 360 c)))))))))
; main
(times 100
(λ
()
(
(draw-star
(random 100
(of
(frame) :w))
(random 100
(of
(frame) :h))
(random 10 100)
(floor
(random 3 32))))))

View File

@ -2,7 +2,7 @@
(clear) (clear)
(def col (def col
(lambda (λ
(i) (i)
(of (of
( (
@ -16,7 +16,7 @@
(theme "b_inv")) (theme "b_inv"))
(mod i 8)))) (mod i 8))))
(def rec (def rec
(lambda (λ
(v i) (v i)
(if (if
(gt v 0) (gt v 0)