Merge branch 'master' into feature/osc

This commit is contained in:
ngradwohl
2019-07-21 04:04:04 +02:00
13 changed files with 178 additions and 151 deletions

View File

@@ -20,10 +20,10 @@
<body>
<script type="text/javascript">
const {dialog,app} = require('electron').remote;
const fs = require('fs');
const app_path = app.getAppPath();
const ronin = new Ronin();
ronin.controller = new Controller();
const fs = require('fs')
const ronin = new Ronin()
ronin.controller = new Controller()
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","*","Hide",() => { app.toggleVisible() },"CmdOrCtrl+H");
@@ -55,9 +55,10 @@
ronin.controller.add("default","Theme","Reset Theme",() => { ronin.theme.reset() },"CmdOrCtrl+Shift+Backspace")
ronin.controller.addSpacer('default', 'Theme', 'Download')
ronin.controller.add("default","Theme","Download Themes..",() => { require('electron').shell.openExternal('https://github.com/hundredrabbits/Themes') })
ronin.controller.commit();
ronin.install(document.body);
window.addEventListener('load', () => { ronin.start(); })
ronin.controller.commit()
ronin.install(document.body)
window.addEventListener('load', () => { ronin.start() })
</script>
</body>
</html>

View File

@@ -1,11 +1,20 @@
;
(echo "Loading prelude.lisp")
; translate
(defn translate
(r p)
(clone
r
(rect
(of p :x)
(of p :y)
(of r :w)
(of r :h))))
(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))))

View File

@@ -33,8 +33,7 @@ function Commander (ronin) {
this.run = (txt = this._input.value) => {
if (txt.indexOf('$') > -1) { ronin.log('Present: $'); return }
const inter = new Lisp(txt, ronin.library)
inter.toPixels()
ronin.interpreter.run(txt)
ronin.always === true && requestAnimationFrame(() => this.run(txt))
}
@@ -47,6 +46,10 @@ function Commander (ronin) {
this.reindent = function () {
let val = this._input.value.replace(/\n/g, '').replace(/ +(?= )/g, '').replace(/\( \(/g, '((').replace(/\) \)/g, '))').trim()
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++) {
const c = val.charAt(i)
if (c === '(') { depth++ } else if (c === ')') { depth-- }

View File

@@ -64,6 +64,83 @@ function Library (ronin) {
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
this.concat = function (...items) { // Concat multiple strings.
@@ -241,83 +318,6 @@ function Library (ronin) {
return Object.values(item)
}
// 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]
}
// File System
this.dir = (path = this.dirpath()) => { // Returns the content of a directory.
@@ -375,8 +375,11 @@ function Library (ronin) {
// osc
this.osc = () => { // Returns a rect of the frame.
return ronin.getOsc()
this.osc = (...args) => { // Returns a rect of the frame.
if (args.length >= 1) {
return ronin.getOsc()[args[0]]
} else {
return ronin.getOsc();
}
}
}

View File

@@ -1,14 +1,15 @@
'use strict'
function Lisp (input, lib) {
function Lisp (lib = {}, includes = []) {
console.log(includes)
const path = require('path')
const fs = require('fs')
const TYPES = { identifier: 0, number: 1, string: 2, bool: 3 }
const Context = function (scope, parent) {
this.scope = scope
this.parent = parent
this.get = function (identifier) {
if (identifier in this.scope) {
return this.scope[identifier]
@@ -20,7 +21,7 @@ function Lisp (input, lib) {
const special = {
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' })
return interpret(this.parse(`(${file})`), context)
},
@@ -69,7 +70,7 @@ function Lisp (input, lib) {
__fn: function (input, context) {
return async function () {
const lambdaArguments = arguments
const keys = [...new Set(input.slice(2).flat(100).filter(i =>
const keys = [...new Set(input.slice(2).flat(100).filter(i =>
i.type === TYPES.identifier &&
i.value[0] === '%'
).map(x => x.value).sort())]
@@ -82,8 +83,8 @@ function Lisp (input, lib) {
},
__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)
for (let i = 1; i < input.length; i += 2) {
obj[await interpret(input[i], context)] = await interpret(input[i + 1], context)
}
return obj
}
@@ -101,7 +102,7 @@ function Lisp (input, lib) {
}
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) {
return interpret(input, new Context(lib))
@@ -152,27 +153,35 @@ function Lisp (input, lib) {
}
const tokenize = function (input) {
const i = input.replace(/^\;.*\n?/gm, '').split('"')
return i.map(function (x, i) {
return i % 2 === 0 ?
x.replace(/\(/g, ' ( ')
.replace(/\)/g, ' ) ')
.replace(/' \( /g, ' \'( ') // '()
.replace(/\{/g, ' { ') // {}
.replace(/\}/g, ' } ') // {}
: x.replace(/ /g, '!whitespace!')
const i = input.replace(/^\;.*\n?/gm, '').replace('λ', 'lambda ').split('"')
return i.map(function (x, i) {
return i % 2 === 0
? x.replace(/\(/g, ' ( ')
.replace(/\)/g, ' ) ')
.replace(/' \( /g, ' \'( ') // '()
.replace(/\{/g, ' { ') // {}
.replace(/\}/g, ' } ') // {}
: x.replace(/ /g, '!whitespace!')
})
.join('"').trim().split(/\s+/)
.map(function (x) { return x.replace(/!whitespace!/g, ' ') })
.join('"').trim().split(/\s+/)
.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) {
return parenthesize(tokenize(input))
}
this.toPixels = async function () {
this.run = async function (input) {
return interpret(this.parse(`(
(include "./sources/lisp/prelude.lisp")
${input})`))
${this.inc()}
${input})`))
}
}

View File

@@ -15,13 +15,12 @@ function Osc (ronin) {
udpPort.on("message", this.onOscMsg)
udpPort.open();
this.ronin.log("osc started")
ronin.log("osc started")
}
this.onOscMsg = (oscMsg, timeTag, info) => {
this.oscMsg = oscMsg;
this.ronin.log("An OSC message just arrived!", oscMsg)
this.ronin.log("Remote info is: ", info);
this.oscMsg[oscMsg.address] = oscMsg;
ronin.log("An OSC message just arrived!", oscMsg)
ronin.log("Remote info is: ", info);
}
}

View File

@@ -11,6 +11,8 @@ function Ronin () {
b_inv: '#ffb545'
}
this.includes = ['prelude']
this.el = document.createElement('div')
this.el.id = 'ronin'
@@ -19,9 +21,8 @@ function Ronin () {
this.commander = new Commander(this)
this.surface = new Surface(this)
this.library = new Library(this)
this.interpreter = new Lisp(this.library, this.includes)
this.osc = new Osc(this)
this.oscMsg = {}
// Parameters
this.always = false

View File

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