Merge branch 'master' of github.com:hundredrabbits/Ronin
This commit is contained in:
commit
533c4d3f27
81
README.md
81
README.md
@ -1,22 +1,89 @@
|
|||||||
# Ronin
|
# Ronin
|
||||||
|
|
||||||
_All I wanted, was a way of resizing photos._
|
_"All I wanted, was a quick way of resizing a few photos.."_
|
||||||
|
|
||||||
Ronin is a LISP repl to create generative graphics currently under development. You can follow the daily progress on [Mastodon](https://merveilles.town/@neauoire/). Until we have documented the library, you can find a list of available functions [here](https://github.com/hundredrabbits/Ronin/blob/master/desktop/sources/scripts/library.js).
|
Ronin is a [LISP](https://en.wikipedia.org/wiki/Lisp_(programming_language)) repl to create generative graphics currently under development. You can follow the daily progress on [Mastodon](https://merveilles.town/@neauoire/).
|
||||||
|
|
||||||
<img src='https://raw.githubusercontent.com/hundredrabbits/Ronin/master/PREVIEW.jpg' width='600'/>
|
## Install & Run
|
||||||
|
|
||||||
## Electron Build
|
You can download [builds](https://hundredrabbits.itch.io/ronin) for **OSX, Windows and Linux**, or if you wish to build it yourself, follow these steps:
|
||||||
|
|
||||||
```
|
```
|
||||||
cd desktop
|
git clone https://github.com/hundredrabbits/Ronin.git
|
||||||
|
cd Ronin/desktop/
|
||||||
npm install
|
npm install
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
<img src='https://raw.githubusercontent.com/hundredrabbits/Ronin/master/PREVIEW.jpg' width='600'/>
|
||||||
|
|
||||||
Documentation is accessible [here](./documentation.md)
|
## Library
|
||||||
|
|
||||||
|
- `(import path rect)` Imports a graphic file with format.
|
||||||
|
- `(export path ~format ~quality)` Exports a graphic file with format.
|
||||||
|
- `(pos x y ~t)` Returns a position shape.
|
||||||
|
- `(size w h ~t)` Returns a size shape.
|
||||||
|
- `(rect x y w h ~t)` Returns a rect shape.
|
||||||
|
- `(circle x y r ~t)` Returns a circle shape.
|
||||||
|
- `(line a b ~t)` Returns a line shape.
|
||||||
|
- `(text x y g s ~f ~t)` Returns a text shape.
|
||||||
|
- `(svg d ~t)` Returns a svg shape.
|
||||||
|
- `(stroke ~shape)` Strokes a shape.
|
||||||
|
- `(fill ~rect)` Fills a shape.
|
||||||
|
- `(clear ~rect)` Clears a rect.
|
||||||
|
- `(add ...args)` Adds values.
|
||||||
|
- `(sub ...args)` Subtracts values.
|
||||||
|
- `(mul ...args)` Multiplies values.
|
||||||
|
- `(div ...args)` Divides values.
|
||||||
|
- `(mod a b)` Returns the modulo of a and b.
|
||||||
|
- `(clamp val min max)` Clamps a value between min and max.
|
||||||
|
- `(step val step)`
|
||||||
|
- `(min)`
|
||||||
|
- `(max)`
|
||||||
|
- `(ceil)`
|
||||||
|
- `(floor)`
|
||||||
|
- `(sin)`
|
||||||
|
- `(cos)`
|
||||||
|
- `(PI)`
|
||||||
|
- `(TWO_PI)`
|
||||||
|
- `(random ...args)`
|
||||||
|
- `(gt a b)` Returns true if a is greater than b, else false.
|
||||||
|
- `(lt a b)` Returns true if a is less than b, else false.
|
||||||
|
- `(eq a b)` Returns true if a is equal to b, else false.
|
||||||
|
- `(and a b ...rest)` Returns true if all conditions are true.
|
||||||
|
- `(or a b ...rest)` Returns true if at least one condition is true.
|
||||||
|
- `(map fn arr)`
|
||||||
|
- `(filter fn arr)`
|
||||||
|
- `(reduce fn arr ~acc)`
|
||||||
|
- `(len item)` Returns the length of a list.
|
||||||
|
- `(first arr)` Returns the first item of a list.
|
||||||
|
- `(last arr)` Returns the last
|
||||||
|
- `(rest [_ ...arr])`
|
||||||
|
- `(range start end ~step)`
|
||||||
|
- `(get item key)` Gets an object's parameter with name.
|
||||||
|
- `(set item key val)` Sets an object's parameter with name as value.
|
||||||
|
- `(of h ...keys)`
|
||||||
|
- `(frame)` Returns a rect of the frame.
|
||||||
|
- `(center)` Returns a position of the center of the frame.
|
||||||
|
- `(scale rect w h)`
|
||||||
|
- `(resize w h)` 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)`
|
||||||
|
- `(clone a b)`
|
||||||
|
- `(theme variable ~el)`
|
||||||
|
- `(gradient [x1 y1 x2 y2] ~colors 'black'])`
|
||||||
|
- `(pixels rect fn q)`
|
||||||
|
- `(saturation pixel ~q)`
|
||||||
|
- `(contrast pixel ~q)`
|
||||||
|
- `(echo ...args)`
|
||||||
|
- `(str ...args)`
|
||||||
|
- `(open path)` Imports a graphic file and resizes the frame.
|
||||||
|
- `(folder ~path)` Returns the content of a folder path.
|
||||||
|
- `(exit ~force)` Exits Ronin.
|
||||||
|
- `(time)` Returns timestamp in milliseconds.
|
||||||
|
- `(animate ~play)` Toggles animation.
|
||||||
|
- `(js)` Javascript interop.
|
||||||
|
- `(test name a b)`
|
||||||
|
|
||||||
## Extras
|
## Extras
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
ronin.controller.addRole('default', 'Edit', 'paste')
|
ronin.controller.addRole('default', 'Edit', 'paste')
|
||||||
ronin.controller.addRole('default', 'Edit', 'delete')
|
ronin.controller.addRole('default', 'Edit', 'delete')
|
||||||
ronin.controller.addRole('default', 'Edit', 'selectall')
|
ronin.controller.addRole('default', 'Edit', 'selectall')
|
||||||
|
ronin.controller.add("default","Edit","Re-Indent",() => { ronin.commander.reindent() },"CmdOrCtrl+Shift+I")
|
||||||
ronin.controller.add("default","View","Zoom In",() => { ronin.modZoom(0.25) },"CmdOrCtrl+=")
|
ronin.controller.add("default","View","Zoom In",() => { ronin.modZoom(0.25) },"CmdOrCtrl+=")
|
||||||
ronin.controller.add("default","View","Zoom Out",() => { ronin.modZoom(-0.25) },"CmdOrCtrl+-")
|
ronin.controller.add("default","View","Zoom Out",() => { ronin.modZoom(-0.25) },"CmdOrCtrl+-")
|
||||||
ronin.controller.add("default","View","Zoom Reset",() => { ronin.modZoom(1,true) },"CmdOrCtrl+0")
|
ronin.controller.add("default","View","Zoom Reset",() => { ronin.modZoom(1,true) },"CmdOrCtrl+0")
|
||||||
|
@ -5,12 +5,12 @@ body { margin:0px; padding:0px; overflow:hidden; font-family:"input_mono_regular
|
|||||||
#ronin { height: calc(100vh - 60px); width:calc(100vw - 60px); -webkit-app-region: drag; padding: 30px;overflow: hidden; }
|
#ronin { height: calc(100vh - 60px); width:calc(100vw - 60px); -webkit-app-region: drag; padding: 30px;overflow: hidden; }
|
||||||
#ronin #wrapper { overflow: hidden; position: relative; }
|
#ronin #wrapper { overflow: hidden; position: relative; }
|
||||||
#ronin #wrapper #commander { z-index: 9000;position: relative;width: 310px;height: calc(100vh - 60px);-webkit-app-region: no-drag;padding-right: 30px;transition: margin-left 250ms;}
|
#ronin #wrapper #commander { z-index: 9000;position: relative;width: 310px;height: calc(100vh - 60px);-webkit-app-region: no-drag;padding-right: 30px;transition: margin-left 250ms;}
|
||||||
#ronin #wrapper #commander textarea { background: none; width: 100%; height: calc(100vh - 80px); resize: none; font-size: 12px;line-height: 15px; padding-right: 15px}
|
#ronin #wrapper #commander textarea { background: none; width: 100%; height: calc(100vh - 105px); resize: none; font-size: 12px;line-height: 15px; padding-right: 15px}
|
||||||
#ronin #wrapper #commander div#status { position: absolute; bottom: 0px; }
|
#ronin #wrapper #commander div#status { position: absolute; bottom: 0px; text-transform: lowercase;}
|
||||||
#ronin.hidden #wrapper #commander { margin-left:-331px; }
|
#ronin.hidden #wrapper #commander { margin-left:-331px; }
|
||||||
|
|
||||||
#ronin canvas#surface,#ronin canvas#guide { position: absolute; top:0px; -webkit-user-select: none;-webkit-app-region: no-drag; background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20'><circle cx='10' cy='10' r='1' fill='%23555'></circle></svg>"); background-size: 10px 10px; background-position: -4px -4px; width:100%; height:100%; left:340px; transition: left 250ms}
|
#ronin canvas#surface,#ronin canvas#guide { position: absolute; top:0px; -webkit-user-select: none;-webkit-app-region: no-drag; background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20'><circle cx='10' cy='10' r='1' fill='%23555'></circle></svg>"); background-size: 10px 10px; background-position: -4px -4px; width:100%; height:100%; left:340px; transition: left 250ms}
|
||||||
#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:0px; }
|
#ronin.hidden canvas#surface, #ronin.hidden canvas#guide { left:10px; }
|
@ -4,18 +4,28 @@ function Commander (ronin) {
|
|||||||
this._input = document.createElement('textarea')
|
this._input = document.createElement('textarea')
|
||||||
this._status = document.createElement('div')
|
this._status = document.createElement('div')
|
||||||
this._status.id = 'status'
|
this._status.id = 'status'
|
||||||
|
this._log = document.createElement('div')
|
||||||
|
this._log.id = 'log'
|
||||||
|
this._source = document.createElement('div')
|
||||||
|
this._source.id = 'source'
|
||||||
|
this._docs = document.createElement('div')
|
||||||
|
this._docs.id = 'help'
|
||||||
this.isVisible = true
|
this.isVisible = true
|
||||||
|
|
||||||
this.install = function (host) {
|
this.install = function (host) {
|
||||||
this.el.appendChild(this._input)
|
this.el.appendChild(this._input)
|
||||||
|
this._status.appendChild(this._log)
|
||||||
|
this._status.appendChild(this._source)
|
||||||
|
this._status.appendChild(this._docs)
|
||||||
this.el.appendChild(this._status)
|
this.el.appendChild(this._status)
|
||||||
host.appendChild(this.el)
|
host.appendChild(this.el)
|
||||||
|
|
||||||
this._input.addEventListener('input', this.onInput)
|
this._input.addEventListener('input', this.onInput)
|
||||||
|
this._input.addEventListener('click', this.onClick)
|
||||||
|
this.docs.install()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.start = function () {
|
this.start = function () {
|
||||||
this._status.textContent = 'Idle. (zoom 100%)'
|
this.setStatus('Ready.')
|
||||||
this._input.focus()
|
this._input.focus()
|
||||||
this.run()
|
this.run()
|
||||||
this.hide()
|
this.hide()
|
||||||
@ -33,21 +43,59 @@ function Commander (ronin) {
|
|||||||
this.run()
|
this.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.reindent = function () {
|
||||||
|
let val = this._input.value.replace(/\n/g, '').replace(/ +(?= )/g, '').replace(/\( \(/g, '((').replace(/\) \)/g, '))').trim()
|
||||||
|
let depth = 0
|
||||||
|
for (let i = 0; i < val.length; i++) {
|
||||||
|
const c = val.charAt(i)
|
||||||
|
if (c === '(') { depth++ } else if (c === ')') { depth-- }
|
||||||
|
if (c === ';') {
|
||||||
|
const indent = '\n' + (' '.repeat(depth))
|
||||||
|
val = val.insert(indent, i)
|
||||||
|
i += indent.length
|
||||||
|
}
|
||||||
|
if (c === '(') {
|
||||||
|
const indent = '\n' + (' '.repeat(depth - 1))
|
||||||
|
val = val.insert(indent, i)
|
||||||
|
i += indent.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._input.value = val.trim()
|
||||||
|
}
|
||||||
|
|
||||||
this.setStatus = function (msg) {
|
this.setStatus = function (msg) {
|
||||||
if (!msg) { return }
|
// Logs
|
||||||
this._status.textContent = `${(msg + '').substr(0, 40)}`
|
if (msg && msg !== this._log.textContent) {
|
||||||
|
this._log.textContent = `${msg}`
|
||||||
|
console.log(msg)
|
||||||
|
}
|
||||||
|
// Source
|
||||||
|
const _source = `${ronin.source} ${this._input.value.split('\n').length} lines`
|
||||||
|
if (_source !== this._source.textContent) {
|
||||||
|
this._source.textContent = _source
|
||||||
|
}
|
||||||
|
// Docs
|
||||||
|
const _docs = this.docs.print(this.getLastfn())
|
||||||
|
if (_docs !== this._docs.textContent) {
|
||||||
|
this._docs.textContent = `${_docs}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.update = function () {
|
this.update = function () {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onInput = function () {
|
this.onInput = () => {
|
||||||
|
this.setStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getQuery = function () {
|
this.onClick = () => {
|
||||||
|
this.setStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getLastfn = function () {
|
||||||
|
const pos = this._input.value.substr(0, this._input.selectionStart).lastIndexOf('(')
|
||||||
|
return this._input.value.substr(pos).split(' ')[0].replace(/\(/g, '').replace(/\)/g, '').trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mouse
|
// Mouse
|
||||||
@ -62,7 +110,6 @@ function Commander (ronin) {
|
|||||||
this.mouseRect.a.x = e.offsetX
|
this.mouseRect.a.x = e.offsetX
|
||||||
this.mouseRect.a.y = e.offsetY
|
this.mouseRect.a.y = e.offsetY
|
||||||
this.mouseRect.t = 'pos'
|
this.mouseRect.t = 'pos'
|
||||||
this._status.textContent = `${this.mouseRect.x},${this.mouseRect.y} ${this.mouseRect.w},${this.mouseRect.h}`
|
|
||||||
this.capture()
|
this.capture()
|
||||||
this.show()
|
this.show()
|
||||||
}
|
}
|
||||||
@ -73,7 +120,6 @@ function Commander (ronin) {
|
|||||||
this.mouseRect.h = e.offsetY - this.mouseRect.y
|
this.mouseRect.h = e.offsetY - this.mouseRect.y
|
||||||
this.mouseRect.b.x = e.offsetX
|
this.mouseRect.b.x = e.offsetX
|
||||||
this.mouseRect.b.y = e.offsetY
|
this.mouseRect.b.y = e.offsetY
|
||||||
this._status.textContent = `${this.mouseRect.x},${this.mouseRect.y} ${this.mouseRect.w},${this.mouseRect.h}`
|
|
||||||
this.commit()
|
this.commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,7 +131,6 @@ function Commander (ronin) {
|
|||||||
this.mouseRect.b.x = e.offsetX
|
this.mouseRect.b.x = e.offsetX
|
||||||
this.mouseRect.b.y = e.offsetY
|
this.mouseRect.b.y = e.offsetY
|
||||||
this.mouseRect.t = ''
|
this.mouseRect.t = ''
|
||||||
this._status.textContent = `${this.mouseRect.x},${this.mouseRect.y} ${this.mouseRect.w},${this.mouseRect.h}`
|
|
||||||
this.commit()
|
this.commit()
|
||||||
this._input.focus()
|
this._input.focus()
|
||||||
ronin.surface.clearGuide()
|
ronin.surface.clearGuide()
|
||||||
@ -162,4 +207,44 @@ function Commander (ronin) {
|
|||||||
this.hide()
|
this.hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Docs micro-module
|
||||||
|
|
||||||
|
this.docs = {
|
||||||
|
dict: {},
|
||||||
|
load: function () {
|
||||||
|
const fs = require('fs')
|
||||||
|
const path = require('path')
|
||||||
|
const p = path.join(__dirname, 'scripts/', 'library.js')
|
||||||
|
if (!fs.existsSync(p)) { console.warn('Docs', 'File does not exist: ' + p); return }
|
||||||
|
const lines = fs.readFileSync(p, 'utf8').split('\n').filter((line) => { return line.substr(0, 7) === ' this.' })
|
||||||
|
return lines.map((line) => { return line.trim().substr(5).trim() })
|
||||||
|
},
|
||||||
|
install: function (payload = this.load()) {
|
||||||
|
for (const id in payload) {
|
||||||
|
const name = payload[id].substr(0, payload[id].indexOf(' = '))
|
||||||
|
const parent = payload[id].substr(payload[id].indexOf(' = ')).match(/\(([^)]+)\)/)
|
||||||
|
const params = parent ? parent[1].split(',').map((word) => { return word.indexOf(' = ') > -1 ? '~' + (word.split(' = ')[0]).trim() : word.trim() }) : []
|
||||||
|
const note = payload[id].indexOf('// ') > -1 ? payload[id].split('//')[1].trim() : ''
|
||||||
|
this.dict[name] = { note, params }
|
||||||
|
if (params.length < 1) { console.warn('Docs', 'Missing params for ' + name) }
|
||||||
|
if (note === '') { console.warn('Docs', 'Missing note for ' + name) }
|
||||||
|
}
|
||||||
|
console.log('Docs', `Loaded ${Object.keys(this.dict).length} functions.`)
|
||||||
|
console.log(this.toMarkdown())
|
||||||
|
},
|
||||||
|
toMarkdown: function () {
|
||||||
|
return Object.keys(this.dict).reduce((acc, item, key) => {
|
||||||
|
const example = `${item} ${this.dict[item].params.reduce((acc, item) => {
|
||||||
|
return `${acc}${item} `
|
||||||
|
}, '').trim()}`
|
||||||
|
return `${acc}- \`(${example.trim()})\` ${this.dict[item].note}\n`
|
||||||
|
}, '')
|
||||||
|
},
|
||||||
|
print: function (name) {
|
||||||
|
return this.dict[name] ? `(${name} ${this.dict[name].params.reduce((acc, item) => { return `${acc}${item} ` }, '').trim()})` : ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String.prototype.insert = function (s, i) { return [this.slice(0, i), `${s}`, this.slice(i)].join('') }
|
||||||
}
|
}
|
||||||
|
@ -1,278 +1,88 @@
|
|||||||
function Library (ronin) {
|
function Library (ronin) {
|
||||||
console.log(ronin)
|
this.import = async (path, rect) => { // Imports a graphic file with format.
|
||||||
this.open = async (path) => {
|
|
||||||
return ronin.surface.open(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.export = (path, type = 'image/png', quality = 1.0) => {
|
|
||||||
if (!path) { console.warn('Missing export path'); return path }
|
|
||||||
var dataUrl = ronin.surface.el.toDataURL(type, quality)
|
|
||||||
const data = dataUrl.replace(/^data:image\/png;base64,/, '')
|
|
||||||
fs.writeFileSync(path, data, 'base64')
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
this.draw = async (path, rect) => {
|
|
||||||
const img = new Image()
|
const img = new Image()
|
||||||
img.src = path
|
img.src = path
|
||||||
return ronin.surface.draw(img, rect)
|
return ronin.surface.draw(img, rect)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resize = async (w = 1, h = 1) => {
|
this.export = (path, format = 'image/png', quality = 1.0) => { // Exports a graphic file with format.
|
||||||
const rect = w <= 1 || h <= 1 ? { x: 0, y: 0, w: this.frame().w * w, h: this.frame().h * h } : { x: 0, y: 0, w, h }
|
if (!path) { console.warn('Missing export path'); return path }
|
||||||
const a = document.createElement('img')
|
var dataUrl = ronin.surface.el.toDataURL(format, quality)
|
||||||
const b = document.createElement('img')
|
const data = dataUrl.replace(/^data:image\/png;base64,/, '')
|
||||||
a.src = ronin.surface.el.toDataURL()
|
fs.writeFileSync(path, data, 'base64')
|
||||||
ronin.surface.resizeImage(a, b)
|
return path
|
||||||
ronin.surface.resize(rect, true)
|
|
||||||
return ronin.surface.draw(b, rect)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.crop = async (rect) => {
|
|
||||||
return ronin.surface.crop(rect)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.folder = (path = ronin.source.path) => {
|
|
||||||
return fs.existsSync(path) ? fs.readdirSync(path) : []
|
|
||||||
}
|
|
||||||
|
|
||||||
this.exit = () => {
|
|
||||||
ronin.source.quit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logic
|
|
||||||
|
|
||||||
this.gt = (a, b) => {
|
|
||||||
return a > b
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lt = (a, b) => {
|
|
||||||
return a < b
|
|
||||||
}
|
|
||||||
|
|
||||||
this.eq = (a, b) => {
|
|
||||||
return a === b
|
|
||||||
}
|
|
||||||
|
|
||||||
this.and = (a, b, ...rest) => {
|
|
||||||
let args = [a, b].concat(rest)
|
|
||||||
for (let i = 0; i < args.length; i++) {
|
|
||||||
if (!args[i]) {
|
|
||||||
return args[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return args[args.length - 1]
|
|
||||||
}
|
|
||||||
|
|
||||||
this.or = (a, b, ...rest) => {
|
|
||||||
let args = [a, b].concat(rest)
|
|
||||||
for (let i = 0; i < args.length; i++) {
|
|
||||||
if (args[i]) {
|
|
||||||
return args[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return args[args.length - 1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arrays
|
|
||||||
|
|
||||||
this.map = async (fn, arr) => {
|
|
||||||
return Promise.all(arr.map(fn))
|
|
||||||
}
|
|
||||||
|
|
||||||
this.filter = (fn, arr) => {
|
|
||||||
const list = Array.from(arr)
|
|
||||||
return Promise.all(list.map((element, index) => fn(element, index, list)))
|
|
||||||
.then(result => {
|
|
||||||
return list.filter((_, index) => {
|
|
||||||
return result[index]
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
this.reduce = async (fn, arr, acc) => {
|
|
||||||
const length = arr.length
|
|
||||||
let result = acc === undefined ? subject[0] : acc
|
|
||||||
for (let i = acc === undefined ? 1 : 0; i < length; i++) {
|
|
||||||
result = await fn(result, arr[i], i, arr)
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.len = (item) => {
|
|
||||||
return item.length
|
|
||||||
}
|
|
||||||
|
|
||||||
this.first = (arr) => {
|
|
||||||
return arr[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
this.last = (arr) => {
|
|
||||||
return arr[arr.length - 1]
|
|
||||||
}
|
|
||||||
|
|
||||||
this.rest = ([_, ...arr]) => {
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
|
|
||||||
this.range = (start, end, step = 1) => {
|
|
||||||
let arr = []
|
|
||||||
if (step > 0) {
|
|
||||||
for (let i = start; i <= end; i += step) {
|
|
||||||
arr.push(i)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (let i = start; i >= end; i += step) {
|
|
||||||
arr.push(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shapes
|
// Shapes
|
||||||
|
|
||||||
this.pos = (x, y, t = 'pos') => {
|
this.pos = (x, y, t = 'pos') => { // Returns a position shape.
|
||||||
return { x, y, t }
|
return { x, y, t }
|
||||||
}
|
}
|
||||||
|
|
||||||
this.size = (w, h, t = 'size') => {
|
this.size = (w, h, t = 'size') => { // Returns a size shape.
|
||||||
return { w, h, t }
|
return { w, h, t }
|
||||||
}
|
}
|
||||||
|
|
||||||
this.rect = (x, y, w, h, t = 'rect') => {
|
this.rect = (x, y, w, h, t = 'rect') => { // Returns a rect shape.
|
||||||
return { x, y, w, h, t }
|
return { x, y, w, h, t }
|
||||||
}
|
}
|
||||||
|
|
||||||
this.circle = (x, y, r, t = 'circle') => {
|
this.circle = (x, y, r, t = 'circle') => { // Returns a circle shape.
|
||||||
return { x, y, r, t }
|
return { x, y, r, t }
|
||||||
}
|
}
|
||||||
|
|
||||||
this.line = (a, b, t = 'line') => {
|
this.line = (a, b, t = 'line') => { // Returns a line shape.
|
||||||
return { a, b, t }
|
return { a, b, t }
|
||||||
}
|
}
|
||||||
|
|
||||||
this.text = (x, y, g, s, f = 'Arial', t = 'text') => {
|
this.text = (x, y, g, s, f = 'Arial', t = 'text') => { // Returns a text shape.
|
||||||
return { x, y, g, s, f, t }
|
return { x, y, g, s, f, t }
|
||||||
}
|
}
|
||||||
|
|
||||||
this.svg = (d, t = 'svg') => {
|
this.svg = (d, t = 'svg') => { // Returns a svg shape.
|
||||||
return { d, t }
|
return { d, t }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helpers
|
// Actions
|
||||||
|
|
||||||
this.frame = () => {
|
this.stroke = (shape = this.frame(), thickness, color) => { // Strokes a shape.
|
||||||
return ronin.surface.getFrame()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.center = () => {
|
|
||||||
const rect = this.frame()
|
|
||||||
return this.pos(rect.w / 2, rect.h / 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.scale = (rect, w, h) => {
|
|
||||||
return { x: rect.x, y: rect.y, w: rect.w * w, h: rect.h * h }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy/Paste
|
|
||||||
|
|
||||||
this.clone = (a, b) => {
|
|
||||||
ronin.surface.clone(a, b)
|
|
||||||
return [a, b]
|
|
||||||
}
|
|
||||||
|
|
||||||
this.stroke = (shape = this.frame(), thickness, color) => {
|
|
||||||
ronin.surface.stroke(shape, thickness, color)
|
ronin.surface.stroke(shape, thickness, color)
|
||||||
return shape
|
return shape
|
||||||
}
|
}
|
||||||
|
|
||||||
this.fill = (rect = this.frame(), color) => {
|
this.fill = (rect = this.frame(), color) => { // Fills a shape.
|
||||||
ronin.surface.fill(rect, color)
|
ronin.surface.fill(rect, color)
|
||||||
return rect
|
return rect
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clear = (rect = this.frame()) => {
|
this.clear = (rect = this.frame()) => { // Clears a rect.
|
||||||
ronin.surface.clear(rect)
|
ronin.surface.clear(rect)
|
||||||
return rect
|
return rect
|
||||||
}
|
}
|
||||||
|
|
||||||
this.get = (item, key) => {
|
|
||||||
return item[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set = (item, key, val) => {
|
|
||||||
item[key] = val
|
|
||||||
return item[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Should remove (of) for (get)?
|
|
||||||
|
|
||||||
this.of = (h, ...keys) => {
|
|
||||||
return keys.reduce((acc, key) => {
|
|
||||||
return acc[key]
|
|
||||||
}, h)
|
|
||||||
}
|
|
||||||
|
|
||||||
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]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Math
|
// Math
|
||||||
|
|
||||||
this.add = (...args) => {
|
this.add = (...args) => { // Adds values.
|
||||||
return args.reduce((sum, val) => sum + val)
|
return args.reduce((sum, val) => sum + val)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sub = (...args) => {
|
this.sub = (...args) => { // Subtracts values.
|
||||||
return args.reduce((sum, val) => sum - val)
|
return args.reduce((sum, val) => sum - val)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mul = (...args) => {
|
this.mul = (...args) => { // Multiplies values.
|
||||||
return args.reduce((sum, val) => sum * val)
|
return args.reduce((sum, val) => sum * val)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.div = (...args) => {
|
this.div = (...args) => { // Divides values.
|
||||||
return args.reduce((sum, val) => sum / val)
|
return args.reduce((sum, val) => sum / val)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mod = (a, b) => {
|
this.mod = (a, b) => { // Returns the modulo of a and b.
|
||||||
return a % b
|
return a % b
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clamp = (val, min, max) => {
|
this.clamp = (val, min, max) => { // Clamps a value between min and max.
|
||||||
return Math.min(max, Math.max(min, val))
|
return Math.min(max, Math.max(min, val))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,7 +117,194 @@ function Library (ronin) {
|
|||||||
return Math.random()
|
return Math.random()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generics
|
// Logic
|
||||||
|
|
||||||
|
this.gt = (a, b) => { // Returns true if a is greater than b, else false.
|
||||||
|
return a > b
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lt = (a, b) => { // Returns true if a is less than b, else false.
|
||||||
|
return a < b
|
||||||
|
}
|
||||||
|
|
||||||
|
this.eq = (a, b) => { // Returns true if a is equal to b, else false.
|
||||||
|
return a === b
|
||||||
|
}
|
||||||
|
|
||||||
|
this.and = (a, b, ...rest) => { // Returns true if all conditions are true.
|
||||||
|
let args = [a, b].concat(rest)
|
||||||
|
for (let i = 0; i < args.length; i++) {
|
||||||
|
if (!args[i]) {
|
||||||
|
return args[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args[args.length - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.or = (a, b, ...rest) => { // Returns true if at least one condition is true.
|
||||||
|
let args = [a, b].concat(rest)
|
||||||
|
for (let i = 0; i < args.length; i++) {
|
||||||
|
if (args[i]) {
|
||||||
|
return args[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args[args.length - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrays
|
||||||
|
|
||||||
|
this.map = async (fn, arr) => {
|
||||||
|
return Promise.all(arr.map(fn))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.filter = (fn, arr) => {
|
||||||
|
const list = Array.from(arr)
|
||||||
|
return Promise.all(list.map((element, index) => fn(element, index, list)))
|
||||||
|
.then(result => {
|
||||||
|
return list.filter((_, index) => {
|
||||||
|
return result[index]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reduce = async (fn, arr, acc) => {
|
||||||
|
const length = arr.length
|
||||||
|
let result = acc === undefined ? subject[0] : acc
|
||||||
|
for (let i = acc === undefined ? 1 : 0; i < length; i++) {
|
||||||
|
result = await fn(result, arr[i], i, arr)
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.len = (item) => { // Returns the length of a list.
|
||||||
|
return item.length
|
||||||
|
}
|
||||||
|
|
||||||
|
this.first = (arr) => { // Returns the first item of a list.
|
||||||
|
return arr[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.last = (arr) => { // Returns the last
|
||||||
|
return arr[arr.length - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.rest = ([_, ...arr]) => {
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
this.range = (start, end, step = 1) => {
|
||||||
|
let arr = []
|
||||||
|
if (step > 0) {
|
||||||
|
for (let i = start; i <= end; i += step) {
|
||||||
|
arr.push(i)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = start; i >= end; i += step) {
|
||||||
|
arr.push(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Objects
|
||||||
|
|
||||||
|
this.get = (item, key) => { // Gets an object's parameter with name.
|
||||||
|
return item[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set = (item, key, val) => { // Sets an object's parameter with name as value.
|
||||||
|
item[key] = val
|
||||||
|
return item[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.of = (h, ...keys) => {
|
||||||
|
return keys.reduce((acc, key) => {
|
||||||
|
return acc[key]
|
||||||
|
}, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.scale = (rect, w, h) => {
|
||||||
|
return { x: rect.x, y: rect.y, w: rect.w * w, h: rect.h * h }
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resize = async (w, h) => { // 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()
|
||||||
|
ronin.surface.resizeImage(a, b)
|
||||||
|
ronin.surface.resize(rect, true)
|
||||||
|
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()
|
||||||
|
ronin.surface.resizeImage(a, b)
|
||||||
|
ronin.surface.resize(rect, true)
|
||||||
|
return ronin.surface.draw(b, rect)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.crop = async (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]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Misc
|
||||||
|
|
||||||
this.echo = (...args) => {
|
this.echo = (...args) => {
|
||||||
ronin.log(args)
|
ronin.log(args)
|
||||||
@ -318,29 +315,36 @@ function Library (ronin) {
|
|||||||
return args.reduce((acc, val) => { return acc + val }, '')
|
return args.reduce((acc, val) => { return acc + val }, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
this.test = (name, a, b) => {
|
this.open = async (path) => { // Imports a graphic file and resizes the frame.
|
||||||
if (Array.isArray(a)) {
|
return ronin.surface.open(path)
|
||||||
// TODO: make testing more solid
|
|
||||||
a = a.toString()
|
|
||||||
b = b.toString()
|
|
||||||
}
|
}
|
||||||
if (a !== b) {
|
|
||||||
|
this.folder = (path = ronin.source.path) => { // Returns the content of a folder path.
|
||||||
|
return fs.existsSync(path) ? fs.readdirSync(path) : []
|
||||||
|
}
|
||||||
|
|
||||||
|
this.exit = (force = false) => { // Exits Ronin.
|
||||||
|
ronin.source.quit(force)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.time = () => { // Returns timestamp in milliseconds.
|
||||||
|
return Date.now
|
||||||
|
}
|
||||||
|
|
||||||
|
this.animate = (play = true) => { // Toggles animation.
|
||||||
|
ronin.animate(play)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.js = () => { // Javascript interop.
|
||||||
|
return window
|
||||||
|
}
|
||||||
|
|
||||||
|
this.test = (name, a, b) => {
|
||||||
|
if (`${a}` !== `${b}`) {
|
||||||
console.warn('failed ' + name, a, b)
|
console.warn('failed ' + name, a, b)
|
||||||
} else {
|
} else {
|
||||||
console.log('passed ' + name, a)
|
console.log('passed ' + name, a)
|
||||||
}
|
}
|
||||||
return a === b
|
return a === b
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client
|
|
||||||
this.ronin = ronin
|
|
||||||
|
|
||||||
// Livecoding
|
|
||||||
this.time = Date.now
|
|
||||||
|
|
||||||
this.animate = (b = true) => ronin.animate(b)
|
|
||||||
|
|
||||||
// javascript interop
|
|
||||||
this.js = window
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,8 @@ function Lisp (input, lib) {
|
|||||||
|
|
||||||
const special = {
|
const special = {
|
||||||
include: (input, context) => {
|
include: (input, context) => {
|
||||||
const p = input[1].value
|
if (!input[1].value || !fs.existsSync(input[1].value)) { console.warn('Source', input[1].value); return [] }
|
||||||
if (!fs.existsSync(p)) { console.warn('Source', p); return [] }
|
const file = fs.readFileSync(input[1].value, { encoding: 'utf-8' })
|
||||||
const file = fs.readFileSync(p, { encoding: 'utf-8' })
|
|
||||||
return interpret(this.parse(file), context)
|
return interpret(this.parse(file), context)
|
||||||
},
|
},
|
||||||
let: function (input, context) {
|
let: function (input, context) {
|
||||||
@ -34,29 +33,21 @@ function Lisp (input, lib) {
|
|||||||
},
|
},
|
||||||
def: function (input, context) {
|
def: function (input, context) {
|
||||||
const identifier = input[1].value
|
const identifier = input[1].value
|
||||||
const value = (input[2].type === TYPES.string) ? input[3] : input[2]
|
const value = input[2].type === TYPES.string && input[3] ? input[3] : input[2]
|
||||||
if (input[2].type === TYPES.string) {
|
|
||||||
// docstring
|
|
||||||
console.log(input[2].value)
|
|
||||||
}
|
|
||||||
context.scope[identifier] = interpret(value, context)
|
context.scope[identifier] = interpret(value, context)
|
||||||
return value
|
return value
|
||||||
},
|
},
|
||||||
defn: function (input, context) {
|
defn: function (input, context) {
|
||||||
const identifier = input[1].value
|
const fnName = input[1].value
|
||||||
const argumentNames = (input[2].type === TYPES.string) ? input[3] : input[2]
|
const fnParams = input[2].type === TYPES.string && input[3] ? input[3] : input[2]
|
||||||
const functionBody = (input[2].type === TYPES.string) ? input[4] : input[3]
|
const fnBody = input[2].type === TYPES.string && input[4] ? input[4] : input[3]
|
||||||
if (input[2].type === TYPES.string) {
|
context.scope[fnName] = async function () {
|
||||||
// docstring
|
|
||||||
console.log(input[2].value)
|
|
||||||
}
|
|
||||||
context.scope[identifier] = async function () {
|
|
||||||
const lambdaArguments = arguments
|
const lambdaArguments = arguments
|
||||||
const lambdaScope = argumentNames.reduce(function (acc, x, i) {
|
const lambdaScope = fnParams.reduce(function (acc, x, i) {
|
||||||
acc[x.value] = lambdaArguments[i]
|
acc[x.value] = lambdaArguments[i]
|
||||||
return acc
|
return acc
|
||||||
}, {})
|
}, {})
|
||||||
return interpret(functionBody, new Context(lambdaScope, context))
|
return interpret(fnBody, new Context(lambdaScope, context))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
lambda: function (input, context) {
|
lambda: function (input, context) {
|
||||||
|
@ -15,7 +15,6 @@ function Ronin () {
|
|||||||
this.el.id = 'ronin'
|
this.el.id = 'ronin'
|
||||||
|
|
||||||
this.theme = new Theme(defaultTheme)
|
this.theme = new Theme(defaultTheme)
|
||||||
|
|
||||||
this.source = new Source(this)
|
this.source = new Source(this)
|
||||||
this.commander = new Commander(this)
|
this.commander = new Commander(this)
|
||||||
this.surface = new Surface(this)
|
this.surface = new Surface(this)
|
||||||
@ -52,7 +51,6 @@ function Ronin () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.log = function (...msg) {
|
this.log = function (...msg) {
|
||||||
console.log(...msg)
|
|
||||||
this.commander.setStatus(msg.reduce((acc, val) => { return acc + val + ' ' }, ''))
|
this.commander.setStatus(msg.reduce((acc, val) => { return acc + val + ' ' }, ''))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ function Source (ronin) {
|
|||||||
console.log('Source', 'Make a new file..')
|
console.log('Source', 'Make a new file..')
|
||||||
this.path = null
|
this.path = null
|
||||||
ronin.surface.clear()
|
ronin.surface.clear()
|
||||||
|
ronin.log(`New file.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.open = function () {
|
this.open = function () {
|
||||||
@ -57,6 +58,7 @@ function Source (ronin) {
|
|||||||
if (quitAfter === true) {
|
if (quitAfter === true) {
|
||||||
app.exit()
|
app.exit()
|
||||||
}
|
}
|
||||||
|
ronin.log(`Writing file.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.read = function (loc = this.path) {
|
this.read = function (loc = this.path) {
|
||||||
@ -65,6 +67,7 @@ function Source (ronin) {
|
|||||||
console.log('Source', 'Reading ' + loc)
|
console.log('Source', 'Reading ' + loc)
|
||||||
this.path = loc
|
this.path = loc
|
||||||
this.load(fs.readFileSync(this.path, 'utf8'))
|
this.load(fs.readFileSync(this.path, 'utf8'))
|
||||||
|
ronin.log(`Reading file.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.run = function () {
|
this.run = function () {
|
||||||
@ -75,8 +78,8 @@ function Source (ronin) {
|
|||||||
ronin.commander._input.value = data
|
ronin.commander._input.value = data
|
||||||
}
|
}
|
||||||
|
|
||||||
this.quit = function () {
|
this.quit = function (force = false) {
|
||||||
if (this.hasChanges() === true) {
|
if (this.hasChanges() === true && force === false) {
|
||||||
this.verify()
|
this.verify()
|
||||||
} else {
|
} else {
|
||||||
app.exit()
|
app.exit()
|
||||||
@ -144,7 +147,7 @@ function Source (ronin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.toString = function () {
|
this.toString = function () {
|
||||||
return this.path ? this.name() : 'unsaved'
|
return this.path ? this.name() + '.lisp' : 'unsaved'
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDifferent (a, b) {
|
function isDifferent (a, b) {
|
||||||
|
@ -61,11 +61,11 @@ function Surface (ronin) {
|
|||||||
context.closePath()
|
context.closePath()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.linearGradient = function(x1, y1, x2, y2, colors, context = this.context) {
|
this.linearGradient = function (x1, y1, x2, y2, colors, context = this.context) {
|
||||||
const gradient = context.createLinearGradient(x1, y1, x2, y2)
|
const gradient = context.createLinearGradient(x1, y1, x2, y2)
|
||||||
const step = 1/(colors.length - 1)
|
const step = 1 / (colors.length - 1)
|
||||||
colors.forEach((color,i) => {
|
colors.forEach((color, i) => {
|
||||||
gradient.addColorStop(i*step, color)
|
gradient.addColorStop(i * step, color)
|
||||||
})
|
})
|
||||||
return gradient
|
return gradient
|
||||||
}
|
}
|
||||||
@ -159,6 +159,8 @@ function Surface (ronin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.resize = function (size, fit = false) {
|
this.resize = function (size, fit = false) {
|
||||||
|
const frame = this.getFrame()
|
||||||
|
if (frame.w === size.w && frame.h === size.h) { return }
|
||||||
console.log('Surface', `Resize: ${size.w}x${size.h}`)
|
console.log('Surface', `Resize: ${size.w}x${size.h}`)
|
||||||
this.el.width = size.w
|
this.el.width = size.w
|
||||||
this.el.height = size.h
|
this.el.height = size.h
|
||||||
@ -173,10 +175,14 @@ function Surface (ronin) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.getFrame = function () {
|
||||||
|
return { x: 0, y: 0, w: this.el.width, h: this.el.height, t: 'rect' }
|
||||||
|
}
|
||||||
|
|
||||||
this.fitWindow = function (size) {
|
this.fitWindow = function (size) {
|
||||||
const win = require('electron').remote.getCurrentWindow()
|
const win = require('electron').remote.getCurrentWindow()
|
||||||
const pad = { w: ronin.commander.isVisible === true ? 400 : 60, h: 60 }
|
const pad = { w: ronin.commander.isVisible === true ? 400 : 60, h: 60 }
|
||||||
win.setSize(size.w + pad.w, size.h + pad.h, false)
|
win.setSize(size.w + pad.w, size.h + pad.h, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.maximize = function () {
|
this.maximize = function () {
|
||||||
@ -191,10 +197,6 @@ function Surface (ronin) {
|
|||||||
ronin.log(`resize ${f.w}x${f.h}`)
|
ronin.log(`resize ${f.w}x${f.h}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getFrame = function () {
|
|
||||||
return { x: 0, y: 0, w: this.el.width, h: this.el.height, t: 'rect' }
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getCrop = function (rect) {
|
this.getCrop = function (rect) {
|
||||||
const newCanvas = document.createElement('canvas')
|
const newCanvas = document.createElement('canvas')
|
||||||
newCanvas.width = rect.w
|
newCanvas.width = rect.w
|
||||||
@ -203,7 +205,7 @@ function Surface (ronin) {
|
|||||||
return newCanvas
|
return newCanvas
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resizeImage = function (src, dst, type = 'image/jpeg', quality = 0.92) {
|
this.resizeImage = function (src, dst, type = 'image/png', quality = 1.0) {
|
||||||
const tmp = new Image()
|
const tmp = new Image()
|
||||||
let canvas
|
let canvas
|
||||||
let context
|
let context
|
||||||
|
9
examples/basics.lisp
Normal file
9
examples/basics.lisp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
; basics
|
||||||
|
(
|
||||||
|
; define a variable
|
||||||
|
(def a 25)
|
||||||
|
(echo a)
|
||||||
|
|
||||||
|
; define a function
|
||||||
|
(defn add-two (a) (add 2 a))
|
||||||
|
(echo (add-two 4)))
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
; Draw photo
|
; Draw photo
|
||||||
|
|
||||||
(draw
|
(import
|
||||||
"../static/crystal.jpg"
|
"../static/crystal.jpg"
|
||||||
(rect 0 0 400 400))
|
(rect 0 0 400 400))
|
||||||
|
|
||||||
|
@ -1,36 +1,57 @@
|
|||||||
; guides file
|
; guides
|
||||||
|
(
|
||||||
((clear)
|
(clear)
|
||||||
(stroke (frame) 1 "red")
|
(stroke
|
||||||
|
(frame) 1 "red")
|
||||||
(stroke
|
(stroke
|
||||||
(line
|
(line
|
||||||
(pos 0 0)
|
(pos 0 0)
|
||||||
(pos (of (frame) "w") (of (frame) "h")))
|
(pos
|
||||||
1 "red")
|
(of
|
||||||
|
(frame) "w")
|
||||||
|
(of
|
||||||
|
(frame) "h"))) 1 "red")
|
||||||
(stroke
|
(stroke
|
||||||
(line
|
(line
|
||||||
(pos (of (frame) "w") 0)
|
(pos
|
||||||
(pos 0 (of (frame) "h")))
|
(of
|
||||||
1 "red")
|
(frame) "w") 0)
|
||||||
|
(pos 0
|
||||||
|
(of
|
||||||
|
(frame) "h"))) 1 "red")
|
||||||
(stroke
|
(stroke
|
||||||
(line
|
(line
|
||||||
(pos (div (of (frame) "w") 2) 0)
|
(pos
|
||||||
(pos (div (of (frame) "w") 2) (of (frame) "h")))
|
(div
|
||||||
1 "red")
|
(of
|
||||||
|
(frame) "w") 2) 0)
|
||||||
|
(pos
|
||||||
|
(div
|
||||||
|
(of
|
||||||
|
(frame) "w") 2)
|
||||||
|
(of
|
||||||
|
(frame) "h"))) 1 "red")
|
||||||
(stroke
|
(stroke
|
||||||
(line
|
(line
|
||||||
(pos 0 (div (of (frame) "h") 2))
|
(pos 0
|
||||||
(pos (div (of (frame) "w") 2) (of (frame) "h")))
|
(div
|
||||||
1 "#72dec2")
|
(of
|
||||||
|
(frame) "h") 2))
|
||||||
|
(pos
|
||||||
|
(div
|
||||||
|
(of
|
||||||
|
(frame) "w") 2)
|
||||||
|
(of
|
||||||
|
(frame) "h"))) 1 "#72dec2")
|
||||||
(stroke
|
(stroke
|
||||||
(line
|
(line
|
||||||
(pos (div (of (frame) "w") 2) 0)
|
(pos
|
||||||
(pos (of (frame) "w") (div (of (frame) "h") 2)))
|
(div
|
||||||
1 "#72dec2")
|
(of
|
||||||
)
|
(frame) "w") 2) 0)
|
||||||
|
(pos
|
||||||
|
(of
|
||||||
|
(frame) "w")
|
||||||
|
(div
|
||||||
|
(of
|
||||||
|
(frame) "h") 2))) 1 "#72dec2"))
|
4
examples/include.lisp
Normal file
4
examples/include.lisp
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
; include
|
||||||
|
(
|
||||||
|
(include "../examples/recursive.lisp")
|
||||||
|
(echo line-color))
|
@ -1,12 +1,7 @@
|
|||||||
; pixels
|
; pixels
|
||||||
|
|
||||||
(
|
(
|
||||||
(clear)
|
(clear)
|
||||||
(draw
|
(import "../../PREVIEW.jpg"
|
||||||
"../../PREVIEW.jpg"
|
|
||||||
(frame))
|
(frame))
|
||||||
(pixels
|
(pixels
|
||||||
(rect 0 0 500 500)
|
(rect 0 0 500 500) saturation 0.5))
|
||||||
saturation
|
|
||||||
0.5)
|
|
||||||
)
|
|
@ -1,21 +1,17 @@
|
|||||||
; random
|
; random
|
||||||
|
|
||||||
(
|
(
|
||||||
(clear)
|
(clear)
|
||||||
|
|
||||||
(defn place
|
(defn place
|
||||||
(rec)
|
(rec)
|
||||||
(if (gt rec 0)
|
(if
|
||||||
|
(gt rec 0)
|
||||||
(
|
(
|
||||||
(draw "../static/crystal.jpg"
|
(import "../static/crystal.jpg"
|
||||||
(rect
|
(rect
|
||||||
(random 200)
|
(random 200)
|
||||||
(random 200)
|
(random 200)
|
||||||
(random 200)
|
(random 200)
|
||||||
(random 200)))
|
(random 200)))
|
||||||
(place (sub rec 1))
|
(place
|
||||||
))
|
(sub rec 1)))))
|
||||||
)
|
(place 30))
|
||||||
|
|
||||||
(place 30)
|
|
||||||
)
|
|
@ -1,17 +1,16 @@
|
|||||||
; recursive
|
; recursive
|
||||||
|
|
||||||
(
|
(
|
||||||
(clear)
|
(clear)
|
||||||
(def line-color "red")
|
|
||||||
(defn rec
|
(defn rec
|
||||||
(v)
|
(v)
|
||||||
(if (gt v 0)
|
(if
|
||||||
((stroke (circle
|
(gt v 0)
|
||||||
|
(
|
||||||
|
(stroke
|
||||||
|
(circle
|
||||||
(mul 5 v)
|
(mul 5 v)
|
||||||
(mul 5 v)
|
(mul 5 v)
|
||||||
(mul 5 v)) 1 line-color)
|
(mul 5 v)) 1 "red")
|
||||||
(rec (sub v 5))))
|
(rec
|
||||||
)
|
(sub v 5)))))
|
||||||
|
(rec 100))
|
||||||
(rec 100)
|
|
||||||
)
|
|
@ -1,7 +1,5 @@
|
|||||||
; resize
|
; resize
|
||||||
|
|
||||||
(
|
(
|
||||||
(clear)
|
(clear)
|
||||||
(open "../../PREVIEW.jpg")
|
(open "../../PREVIEW.jpg")
|
||||||
(resize 0.5 0.5)
|
(resize 0.5 0.5))
|
||||||
)
|
|
@ -1,4 +0,0 @@
|
|||||||
(
|
|
||||||
(include "../examples/recursive.lisp")
|
|
||||||
(echo line-color)
|
|
||||||
)
|
|
@ -1,5 +1,8 @@
|
|||||||
((fill
|
(
|
||||||
(svg "M255,60 L255,60 L135,180 L75,60 L195,210 L120,225 L105,225 L165,255 L225,195 L255,135 L285,150") "white")
|
(clear)
|
||||||
|
; ronin path
|
||||||
(stroke
|
(stroke
|
||||||
(svg "M405,15 L405,15 L150,150 L195,90 L240,135 L120,195 L75,90 L135,165 L120,225 L90,240 L60,210 L90,150 L255,180 L285,180 L285,165 ") "pink"))
|
(svg "M60,60 L195,60 A45,45 0 0,1 240,105 A45,45 0 0,1 195,150 L60,150 M195,150 A45,45 0 0,1 240,195 L240,240 ") 2 "white")
|
||||||
|
; outline
|
||||||
|
(stroke
|
||||||
|
(svg "M15,15 L15,15 L285,15 L285,285 L15,285 Z") 1 "#555"))
|
@ -1,9 +1,12 @@
|
|||||||
((clear)
|
; theme
|
||||||
|
(
|
||||||
|
(clear)
|
||||||
(def col
|
(def col
|
||||||
(lambda
|
(lambda
|
||||||
(i)
|
(i)
|
||||||
(of
|
(of
|
||||||
((theme "f_high")
|
(
|
||||||
|
(theme "f_high")
|
||||||
(theme "f_med")
|
(theme "f_med")
|
||||||
(theme "f_low")
|
(theme "f_low")
|
||||||
(theme "f_inv")
|
(theme "f_inv")
|
||||||
@ -15,14 +18,19 @@
|
|||||||
(def rec
|
(def rec
|
||||||
(lambda
|
(lambda
|
||||||
(v i)
|
(v i)
|
||||||
(if (gt v 0)
|
(if
|
||||||
((fill
|
(gt v 0)
|
||||||
|
(
|
||||||
|
(fill
|
||||||
(circle
|
(circle
|
||||||
(add
|
(add
|
||||||
(div (of (frame) "w") 1.6)
|
(div
|
||||||
|
(of
|
||||||
|
(frame) "w") 1.6)
|
||||||
(mul 1.5 v))
|
(mul 1.5 v))
|
||||||
(mul 10 v)
|
(mul 10 v)
|
||||||
(mul v (div v 5)))
|
(mul v
|
||||||
|
(div v 5)))
|
||||||
(col i))
|
(col i))
|
||||||
(rec
|
(rec
|
||||||
(sub v 3)
|
(sub v 3)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user