Improved helpers

This commit is contained in:
neauoire 2020-03-26 10:35:58 +09:00
parent 12bba32b5d
commit 66c436b567
12 changed files with 1057 additions and 776 deletions

View File

@ -29,23 +29,29 @@ Project Clean Escape
Example Example
; clear screen
(clear) (clear)
; draw red square
(stroke (stroke
(rect 30 30 100 100) "red" 2) (rect 30 30 100 100) "red" 2)
; download result
(export) (export)
Helpers Helpers
Ronin helpers are keywords that facilitates adding coordinates from the canvas into your script. The currently supported helpers are `$rect`, `$pos`, `$circle`, `$line`, `$drag` and `$view`. Holding right-click while using a $helper will run the script as the mouse is injecting coordinates into the script. Paste the following script, and trace a shape in the canvas: Ronin helpers are keywords that facilitates adding coordinates from the canvas into your script. The currently supported helpers are $rect, $pos, $line, $circle & $arc. Holding right-click while using a $helper will run the script as the mouse is injecting coordinates into the script. Paste the following script, and trace a shape in the canvas:
; draw a red circle
(fill $circle "red") (fill $circle "red")
Additional helpers are also available to change parts of a shape, these are as follow: $x, $y, $xy, $wh, $a & $r. Paste the following script, and change the position and radius of a circle:
(clear)
(fill
(circle $xy $r) "red")
Extra helpers are available for various transformations, these are as follow: $drag, $view, $poly, $move & $rotate. Paste the following script, and draw the vertices of a line, press escape to stop:
(clear)
(stroke
$poly "red")
Import/Export Import/Export
To save an image in memory, open an image file with Ronin, or drag an image file on the window. You will then be able to import it by using the file image's name. If the image file is `preview.png`, you can import it as follow: To save an image in memory, open an image file with Ronin, or drag an image file on the window. You will then be able to import it by using the file image's name. If the image file is `preview.png`, you can import it as follow:

View File

@ -10,12 +10,13 @@
<link rel="stylesheet" type="text/css" href="./links/theme.css"/> <link rel="stylesheet" type="text/css" href="./links/theme.css"/>
<script type="text/javascript" src="./scripts/lib/acels.js"></script> <script type="text/javascript" src="./scripts/lib/acels.js"></script>
<script type="text/javascript" src="./scripts/lib/lisp.js"></script> <script type="text/javascript" src="./scripts/lib/lain.js"></script>
<script type="text/javascript" src="./scripts/lib/source.js"></script> <script type="text/javascript" src="./scripts/lib/source.js"></script>
<script type="text/javascript" src="./scripts/lib/theme.js"></script> <script type="text/javascript" src="./scripts/lib/theme.js"></script>
<script type="text/javascript" src="./scripts/client.js"></script> <script type="text/javascript" src="./scripts/client.js"></script>
<script type="text/javascript" src="./scripts/commander.js"></script> <script type="text/javascript" src="./scripts/commander.js"></script>
<script type="text/javascript" src="./scripts/lain(old).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/surface.js"></script> <script type="text/javascript" src="./scripts/surface.js"></script>

View File

@ -1,80 +1,78 @@
(clear) (clear)
; ;
(open $path) (open $path 0.75)
; picker to elementary ; picker to elementary
(def unit 40) (def unit 10)
(def f (def f
(get-frame)) (get-frame))
(def pos-row-1 (def pos-row-1
(sub f:h (sub f:h 80))
(mul unit 4)))
(def pos-row-2 (def pos-row-2
(sub f:h (sub f:h 45))
(mul unit 2)))
; color picker ; color picker
(def color-1 (def color-1
(pick (pick
(guide (guide
(rect $xy unit unit)))) (rect 846 220 unit unit))))
(def color-2 (def color-2
(pick (pick
(guide (guide
(rect $xy unit unit)))) (rect 584 364 unit unit))))
(def color-3 (def color-3
(pick (pick
(guide (guide
(rect $xy unit unit)))) (rect 70 538 unit unit))))
(def color-4 (def color-4
(pick (pick
(guide (guide
(rect $xy unit unit)))) (rect 468 650 unit unit))))
(def color-5 (def color-5
(pick (pick
(guide (guide
(rect $xy unit unit)))) (rect 254 246 unit unit))))
(def color-6 (def color-6
(pick (pick
(guide (guide
(rect $xy unit unit)))) (rect 190 502 unit unit))))
(def color-7 (def color-7
(pick (pick
(guide (guide
(rect $xy unit unit)))) (rect 1084 446 unit unit))))
(def color-8 (def color-8
(pick (pick
(guide (guide
(rect $xy unit unit)))) (rect 1068 730 unit unit))))
; display ; display
(fill (fill
(circle (circle
(mul unit 2) pos-row-1 unit) color-1) (mul 20 2) pos-row-1 18) color-1)
(fill (fill
(circle (circle
(mul unit 4) pos-row-1 unit) color-2) (mul 20 4) pos-row-1 18) color-2)
(fill (fill
(circle (circle
(mul unit 6) pos-row-1 unit) color-3) (mul 20 6) pos-row-1 18) color-3)
(fill (fill
(circle (circle
(mul unit 8) pos-row-1 unit) color-4) (mul 20 8) pos-row-1 18) color-4)
(fill (fill
(circle (circle
(mul unit 3) pos-row-2 unit) color-5) (mul 20 3) pos-row-2 18) color-5)
(fill (fill
(circle (circle
(mul unit 5) pos-row-2 unit) color-6) (mul 20 5) pos-row-2 18) color-6)
(fill (fill
(circle (circle
(mul unit 7) pos-row-2 unit) color-7) (mul 20 7) pos-row-2 18) color-7)
(fill (fill
(circle (circle
(mul unit 9) pos-row-2 unit) color-8) (mul 20 9) pos-row-2 18) color-8)
; ;
(def res (def res
(add color-1:hex ":" color-2:hex ":" color-3:hex ":" color-4:hex ":" color-5:hex ":" color-6:hex ":" color-7:hex ":" color-8:hex)) (add color-1:hex ":" color-2:hex ":" color-3:hex ":" color-4:hex ":" color-5:hex ":" color-6:hex ":" color-7:hex ":" color-8:hex))

1499
index.html

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,7 @@ body { margin:0px; padding:0px; overflow:hidden; font-family:"input_mono_regular
#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 textarea { background: none; width: 100%; height: calc(100vh - 105px); resize: none; font-size: 12px;line-height: 15px; padding-right: 15px}
#ronin #wrapper #commander #status { position: absolute; bottom: 0px; line-height: 15px; height: 30px; overflow: hidden; width: calc(100% - 75px); padding-left:45px;} #ronin #wrapper #commander #status { position: absolute; bottom: 0px; line-height: 15px; height: 30px; overflow: hidden; width: calc(100% - 75px); padding-left:45px;}
#ronin #wrapper #commander #status #run { display: block; width: 26px; height: 26px; position: absolute; top: 0px; border-radius: 15px; left:0px; cursor: pointer; border:2px solid #fff; transition: background-color 250ms, border-color 250ms} #ronin #wrapper #commander #status #run { display: block; width: 26px; height: 26px; position: absolute; top: 0px; border-radius: 15px; left:0px; cursor: pointer; border:2px solid #fff; transition: background-color 250ms, border-color 250ms}
#ronin #wrapper #commander #status #run:hover { background: none }
#ronin.expand #wrapper #commander { width:100%; } #ronin.expand #wrapper #commander { width:100%; }
#ronin #surface, #ronin #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%; transition: left 250ms, opacity 250ms; opacity: 1; } #ronin #surface, #ronin #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%; transition: left 250ms, opacity 250ms; opacity: 1; }

View File

@ -6,7 +6,7 @@
/* global Commander */ /* global Commander */
/* global Surface */ /* global Surface */
/* global Library */ /* global Library */
/* global Lisp */ /* global Lain */
/* global Image */ /* global Image */
/* global requestAnimationFrame */ /* global requestAnimationFrame */
@ -21,7 +21,7 @@ function Client () {
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.lisp = new Lisp(this.library) this.lain = new Lain(this.library)
this.bindings = {} this.bindings = {}
@ -226,6 +226,7 @@ function Client () {
} }
const wh = rect.w + ' ' + rect.h const wh = rect.w + ' ' + rect.h
const d = Math.sqrt(((line.a.x - line.b.x) * (line.a.x - line.b.x)) + ((line.a.y - line.b.y) * (line.a.y - line.b.y))).toFixed(2) const d = Math.sqrt(((line.a.x - line.b.x) * (line.a.x - line.b.x)) + ((line.a.y - line.b.y) * (line.a.y - line.b.y))).toFixed(2)
const r = d
const a = Math.atan2(pos.y - line.a.y, pos.x - line.a.x).toFixed(2) const a = Math.atan2(pos.y - line.a.y, pos.x - line.a.x).toFixed(2)
const circle = { const circle = {
cx: line.a.x, cx: line.a.x,
@ -239,6 +240,6 @@ function Client () {
sa: 0, sa: 0,
ea: a ea: a
} }
return { x, y, xy, wh, d, a, line, rect, pos, size, circle, arc, type, 'is-down': type !== 'mouse-up' ? true : null } return { x, y, xy, wh, d, r, a, line, rect, pos, size, circle, arc, type, 'is-down': type !== 'mouse-up' ? true : null }
} }
} }

View File

@ -30,21 +30,23 @@ function Commander (client) {
this._input.onkeydown = (e) => { this._input.onkeydown = (e) => {
if (e.keyCode === 9 || e.which === 9) { e.preventDefault(); this.inject(' ') } if (e.keyCode === 9 || e.which === 9) { e.preventDefault(); this.inject(' ') }
} }
client.surface.maximize()
} }
this.start = function () { this.start = function () {
this.setStatus('Ready.')
this.load(this.splash)
this.show() this.show()
this._input.value = this.splash
setTimeout(() => { this.run() }, 1000)
this.setStatus('Ready.')
} }
this.run = (txt = this._input.value) => { this.run = (txt = this._input.value) => {
if (this._input.value.indexOf('$') > -1) { txt = this.clean(txt) } if (this._input.value.indexOf('$') > -1) { txt = this.clean(txt) }
client.bindings = {} client.bindings = {}
if (this._input.value.trim() === '') { if (this._input.value.trim() === '') {
client.surface.maximize()
} }
client.lisp.run(txt) client.lain.run(`(${txt})`)
this.feedback() this.feedback()
} }
@ -180,7 +182,7 @@ function Commander (client) {
if (word === 'line') { return `(line ${shape.a.x} ${shape.a.y} ${shape.b.x} ${shape.b.y})` } if (word === 'line') { return `(line ${shape.a.x} ${shape.a.y} ${shape.b.x} ${shape.b.y})` }
if (word === 'circle') { return `(circle ${shape.cx} ${shape.cy} ${shape.r})` } if (word === 'circle') { return `(circle ${shape.cx} ${shape.cy} ${shape.r})` }
if (word === 'arc') { return `(arc ${shape.cx} ${shape.cy} ${shape.r} ${shape.sa} ${shape.ea})` } if (word === 'arc') { return `(arc ${shape.cx} ${shape.cy} ${shape.r} ${shape.sa} ${shape.ea})` }
if (word === 'x' || word === 'y' || word === 'xy' || word === 'wh' || word === 'a') { return `${shape}` } if (word === 'x' || word === 'y' || word === 'xy' || word === 'wh' || word === 'a' || word === 'r') { return `${shape}` }
return '' return ''
} }
@ -240,7 +242,7 @@ function Commander (client) {
const name = this.getCurrentFunction() const name = this.getCurrentFunction()
const fn = client.library[name] const fn = client.library[name]
if (!fn) { return } if (!fn) { return }
const fnString = fn.toString().replace('async ', '') const fnString = fn.toString()
if (fnString.indexOf(') => {') < 0) { return } if (fnString.indexOf(') => {') < 0) { return }
const fnParams = fnString.split(') => {')[0].substr(1).split(',').reduce((acc, item) => { return `${acc}${item.indexOf('=') > -1 ? '~' + item.split('=')[0].trim() : item} ` }, '').trim() const fnParams = fnString.split(') => {')[0].substr(1).split(',').reduce((acc, item) => { return `${acc}${item.indexOf('=') > -1 ? '~' + item.split('=')[0].trim() : item} ` }, '').trim()
return `(${(name + ' ' + fnParams).trim()})` return `(${(name + ' ' + fnParams).trim()})`
@ -248,17 +250,14 @@ function Commander (client) {
// Splash // Splash
this.splash = ` this.splash = `; Ronin v2.50
; Ronin v2.40
(clear)
(def logo-path "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 ") (def logo-path "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 ")
(def pos-x ;
(mul frame:c 0.25)) (clear)
(def pos-y (resize 600 600)
(sub frame:m 150))
(stroke (stroke
(svg pos-x pos-y logo-path) theme:f_high 5) (svg 140 140 logo-path) "black" 7)`
`
function insert (str, add, i) { function insert (str, add, i) {
return [str.slice(0, i), `${add}`, str.slice(i)].join('') return [str.slice(0, i), `${add}`, str.slice(i)].join('')

145
scripts/lib/lain.js Normal file
View File

@ -0,0 +1,145 @@
'use strict'
// In the real world, it didnt matter if I was there or not.
// When I realized that, I was no longer afraid of losing my body.
function Lain (lib = {}) {
const TYPES = { identifier: 0, number: 1, string: 2, bool: 3, symbol: 4 }
const Context = function (scope, parent) {
this.scope = scope
this.parent = parent
this.get = function (identifier) {
if (identifier in this.scope) {
return this.scope[identifier]
} else if (this.parent !== undefined) {
return this.parent.get(identifier)
}
}
}
const special = {
let: function (input, context) {
const letContext = input[1].reduce(function (acc, x) {
acc.scope[x[0].value] = interpret(x[1], context)
return acc
}, new Context({}, context))
return interpret(input[2], letContext)
},
def: function (input, context) {
if (input.length !== 3) { console.warn('Lain', 'Invalid definition.'); return }
const identifier = input[1].host ? input[1].host : input[1].value
if (input[1].host) {
if (!context.scope[identifier]) { context.scope[identifier] = {} }
context.scope[identifier][input[1].value] = interpret(input[2], context)
return context.scope[identifier][input[1].value]
}
context.scope[identifier] = interpret(input[2], context)
return context.scope[identifier]
},
defn: function (input, context) {
const identifier = input[1].value
if (context.scope[identifier]) { console.warn('Lain', `Redefining function: ${identifier}`) }
const fnParams = input[2].type === TYPES.string && input[3] ? input[3] : input[2]
const fnBody = input[2].type === TYPES.string && input[4] ? input[4] : input[3]
context.scope[identifier] = function () {
const lambdaArguments = arguments
const lambdaScope = fnParams.reduce(function (acc, x, i) {
acc[x.value] = lambdaArguments[i]
return acc
}, {})
return interpret(fnBody, new Context(lambdaScope, context))
}
},
λ: function (input, context) {
return function () {
const lambdaArguments = arguments
const lambdaScope = input[1].reduce(function (acc, x, i) {
acc[x.value] = lambdaArguments[i]
return acc
}, {})
return interpret(input[2], new Context(lambdaScope, context))
}
},
if: function (input, context) {
return interpret(input[1], context) ? interpret(input[2], context) : input[3] ? interpret(input[3], context) : []
}
}
const interpretList = function (input, context) {
if (input.length > 0 && input[0].value in special) {
return special[input[0].value](input, context)
}
const list = []
for (let i = 0; i < input.length; i++) {
if (input[i].type === TYPES.symbol) {
if (input[i].host) {
const host = context.get(input[i].host)
if (host) {
list.push(host[input[i].value])
}
} else {
list.push(obj => obj[input[i].value])
}
} else {
list.push(interpret(input[i], context))
}
}
return list[0] instanceof Function ? list[0].apply(undefined, list.slice(1)) : list
}
const interpret = function (input, context) {
if (!input) { console.warn('Lain', context.scope); return null }
if (context === undefined) {
return interpret(input, new Context(lib))
} else if (input instanceof Array) {
return interpretList(input, context)
} else if (input.type === TYPES.identifier) {
return context.get(input.value)
} else if (input.type === TYPES.number || input.type === TYPES.symbol || input.type === TYPES.string || input.type === TYPES.bool) {
return input.value
}
}
const categorize = function (input) {
if (!isNaN(parseFloat(input))) {
return { type: TYPES.number, value: parseFloat(input) }
} else if (input[0] === '"' && input.slice(-1) === '"') {
return { type: TYPES.string, value: input.slice(1, -1) }
} else if (input[0] === ':') {
return { type: TYPES.symbol, value: input.slice(1) }
} else if (input.indexOf(':') > 0) {
return { type: TYPES.symbol, host: input.split(':')[0], value: input.split(':')[1] }
} else if (input === 'true' || input === 'false') {
return { type: TYPES.bool, value: input === 'true' }
} else {
return { type: TYPES.identifier, value: input }
}
}
const parenthesize = function (input, list) {
if (list === undefined) { return parenthesize(input, []) }
const token = input.shift()
if (token === undefined) {
return list.pop()
} else if (token === '(') {
list.push(parenthesize(input, []))
return parenthesize(input, list)
} else if (token === ')') {
return list
} else {
return parenthesize(input, list.concat(categorize(token)))
}
}
const tokenize = function (input) {
const i = input.replace(/^[\s]*;.*\n?/gm, '').split('"')
return i.map(function (x, i) {
return i % 2 === 0 ? x.replace(/\(/g, ' ( ').replace(/\)/g, ' ) ') : x.replace(/ /g, '!ws!')
}).join('"').trim().split(/\s+/).map(function (x) { return x.replace(/!ws!/g, ' ') })
}
this.run = (input) => {
return interpret(parenthesize(tokenize(input)))
}
}

View File

@ -86,7 +86,7 @@ function Theme (client) {
this.active[key] = hex this.active[key] = hex
} }
this.read = (key) => { this.get = (key) => {
return this.active[key] return this.active[key]
} }

View File

@ -5,22 +5,23 @@
function Library (client) { function Library (client) {
// IO // IO
this.open = async (name, scale = 1) => { // Import a graphic and scale canvas to fit. this.open = (name, scale = 1) => { // Import a graphic and scale canvas to fit.
const img = client.cache.get(name) const img = client.cache.get(name)
if (!img) { client.log('No data for ' + name); return } if (!img) { client.log('No data for ' + name); return }
const rect = this.rect(0, 0, img.width * scale, img.height * scale) const rect = this.rect(0, 0, img.width * scale, img.height * scale)
await this.resize(rect.w, rect.h).then(this.import(name, rect)) this.resize(rect.w, rect.h)
this.import(name, rect)
return rect return rect
} }
this.import = async (name, shape, alpha = 1) => { // Imports a graphic file with format. this.import = (name, shape, alpha = 1) => { // Imports a graphic file with format.
const img = client.cache.get(name) const img = client.cache.get(name)
if (!img) { client.log('No data for ' + name); return } if (!img) { client.log('No data for ' + name); return }
client.surface.draw(img, shape, alpha) client.surface.draw(img, shape, alpha)
return shape || this.rect(0, 0, img.width, img.height) return shape || this.rect(0, 0, img.width, img.height)
} }
this.export = async (name = 'ronin', type = 'image/png', quality = 1.0) => { // Exports a graphic file with format. this.export = (name = 'ronin', type = 'image/png', quality = 1.0) => { // Exports a graphic file with format.
const ext = type === 'image/png' ? name + '.png' : name + '.jpg' const ext = type === 'image/png' ? name + '.png' : name + '.jpg'
client.source.write(name, ext, client.surface.el.toDataURL(type, 1.0), type) client.source.write(name, ext, client.surface.el.toDataURL(type, 1.0), type)
} }
@ -79,36 +80,36 @@ function Library (client) {
// Frame // Frame
this.resize = async (w = client.surface.bounds().w, h = client.surface.bounds().h, fit = true) => { // Resizes the canvas to target w and h, returns the rect. this.resize = (w = client.surface.bounds().w, h = client.surface.bounds().h, fit = true) => { // Resizes the canvas to target w and h, returns the rect.
if (w === this['get-frame']().w && h === this['get-frame']().h) { return } if (w === this['get-frame']().w && h === this['get-frame']().h) { return }
const rect = { x: 0, y: 0, w, h } const rect = { x: 0, y: 0, w, h }
const a = document.createElement('img') const a = document.createElement('img')
const b = document.createElement('img') const b = document.createElement('img')
a.src = client.surface.el.toDataURL() a.src = client.surface.el.toDataURL()
await client.surface.resizeImage(a, b) client.surface.resizeImage(a, b)
client.surface.resize(rect, fit) client.surface.resize(rect, fit)
return client.surface.draw(b, rect) return client.surface.draw(b, rect)
} }
this.rescale = async (w = 1, h) => { // Rescales the canvas to target ratio of w and h, returns the rect. this.rescale = (w = 1, h) => { // Rescales the canvas to target ratio of w and h, returns the rect.
const rect = { x: 0, y: 0, w: this['get-frame']().w * w, h: this['get-frame']().h * (h || w) } const rect = { x: 0, y: 0, w: this['get-frame']().w * w, h: this['get-frame']().h * (h || w) }
const a = document.createElement('img') const a = document.createElement('img')
const b = document.createElement('img') const b = document.createElement('img')
a.src = client.surface.el.toDataURL() a.src = client.surface.el.toDataURL()
await client.surface.resizeImage(a, b) client.surface.resizeImage(a, b)
client.surface.resize(rect, true) client.surface.resize(rect, true)
return client.surface.draw(b, rect) return client.surface.draw(b, rect)
} }
this.crop = async (rect = this['get-frame']()) => { // Crop canvas to rect. this.crop = (rect = this['get-frame']()) => { // Crop canvas to rect.
return client.surface.crop(rect) return client.surface.crop(rect)
} }
this.copy = async (rect = this['get-frame']()) => { // Copy a section of the canvas. this.copy = (rect = this['get-frame']()) => { // Copy a section of the canvas.
return client.surface.copy(rect) return client.surface.copy(rect)
} }
this.paste = async (copy, rect = this['get-frame']()) => { // Paste a section of the canvas. this.paste = (copy, rect = this['get-frame']()) => { // Paste a section of the canvas.
return client.surface.paste(copy, rect) return client.surface.paste(copy, rect)
} }
@ -123,6 +124,7 @@ function Library (client) {
} }
this.view = (a, b) => { // View a part of the canvas. this.view = (a, b) => { // View a part of the canvas.
if (!a || !b) { return }
this.guide({ a: { x: a.x, y: a.y }, b: { x: b.x, y: b.y } }) this.guide({ a: { x: a.x, y: a.y }, b: { x: b.x, y: b.y } })
this.guide(a) this.guide(a)
this.guide(b) this.guide(b)
@ -142,8 +144,8 @@ function Library (client) {
return this.color(this.floor(sum[0] / count), this.floor(sum[1] / count), this.floor(sum[2] / count)) return this.color(this.floor(sum[0] / count), this.floor(sum[1] / count), this.floor(sum[2] / count))
} }
this.orient = async (deg = 0) => { // Orient canvas with angle in degrees. this.orient = (deg = 0) => { // Orient canvas with angle in degrees.
const copy = await this.copy() const copy = this.copy()
const frame = this['get-frame']() const frame = this['get-frame']()
const mode = Math.floor(deg / 90) % 4 const mode = Math.floor(deg / 90) % 4
const offset = { x: [0, 0, -frame.w, -frame.w], y: [0, -frame.h, -frame.h, 0] } const offset = { x: [0, 0, -frame.w, -frame.w], y: [0, -frame.h, -frame.h, 0] }
@ -157,8 +159,8 @@ function Library (client) {
} }
this.mirror = { // Mirror canvas, methods: `x`, `y`. this.mirror = { // Mirror canvas, methods: `x`, `y`.
x: async (j = 0) => { x: (j = 0) => {
const copy = await this.copy() const copy = this.copy()
const frame = this['get-frame']() const frame = this['get-frame']()
client.surface.context.save() client.surface.context.save()
client.surface.context.translate(frame.w, 0) client.surface.context.translate(frame.w, 0)
@ -166,8 +168,8 @@ function Library (client) {
client.surface.context.drawImage(copy, 0, 0) client.surface.context.drawImage(copy, 0, 0)
client.surface.context.restore() client.surface.context.restore()
}, },
y: async (j = 0) => { y: (j = 0) => {
const copy = await this.copy() const copy = this.copy()
const frame = this['get-frame']() const frame = this['get-frame']()
client.surface.context.save() client.surface.context.save()
client.surface.context.translate(0, frame.h) client.surface.context.translate(0, frame.h)
@ -240,12 +242,12 @@ function Library (client) {
// Pixels // Pixels
this.pixels = async (fn, q = 1, rect = this['get-frame']()) => { this.pixels = (fn, q = 1, rect = this['get-frame']()) => {
if (!fn) { console.warn('Unknown function'); return rect } if (!fn) { console.warn('Unknown function'); return rect }
const img = client.surface.context.getImageData(rect.x, rect.y, rect.w, rect.h) const img = client.surface.context.getImageData(rect.x, rect.y, rect.w, rect.h)
for (let i = 0, loop = img.data.length; i < loop; i += 4) { for (let i = 0, loop = img.data.length; i < loop; i += 4) {
const pixel = [img.data[i], img.data[i + 1], img.data[i + 2], img.data[i + 3]] const pixel = [img.data[i], img.data[i + 1], img.data[i + 2], img.data[i + 3]]
const processed = await fn(pixel, q) const processed = fn(pixel, q)
img.data[i] = this.clamp(parseInt(processed[0]), 0, 255) img.data[i] = this.clamp(parseInt(processed[0]), 0, 255)
img.data[i + 1] = this.clamp(parseInt(processed[1]), 0, 255) img.data[i + 1] = this.clamp(parseInt(processed[1]), 0, 255)
img.data[i + 2] = this.clamp(parseInt(processed[2]), 0, 255) img.data[i + 2] = this.clamp(parseInt(processed[2]), 0, 255)
@ -412,10 +414,10 @@ function Library (client) {
// Arrays // Arrays
this.each = async (arr, fn) => { // Run a function for each element in a list. this.each = (arr, fn) => { // Run a function for each element in a list.
for (let i = 0; i < arr.length; i++) { for (let i = 0; i < arr.length; i++) {
const arg = arr[i] const arg = arr[i]
await fn(arg) fn(arg)
} }
} }
@ -433,11 +435,11 @@ function Library (client) {
}) })
} }
this.reduce = async (arr, fn, acc) => { this.reduce = (arr, fn, acc) => {
const length = arr.length const length = arr.length
let result = acc === undefined ? arr[0] : acc let result = acc === undefined ? arr[0] : acc
for (let i = acc === undefined ? 1 : 0; i < length; i++) { for (let i = acc === undefined ? 1 : 0; i < length; i++) {
result = await fn(result, arr[i], i, arr) result = fn(result, arr[i], i, arr)
} }
return result return result
} }
@ -589,9 +591,9 @@ function Library (client) {
return a === b return a === b
} }
this.benchmark = async (fn) => { // Logs time taken to execute a function. this.benchmark = (fn) => { // Logs time taken to execute a function.
const start = Date.now() const start = Date.now()
const result = await fn() const result = fn()
console.log(`time taken: ${Date.now() - start}ms`) console.log(`time taken: ${Date.now() - start}ms`)
return result return result
} }

View File

@ -174,24 +174,21 @@ function Surface (client) {
// IO // IO
this.draw = function (img, shape = this.getFrame(), alpha = 1) { this.draw = function (img, shape = this.getFrame(), alpha = 1) {
return new Promise(resolve => { this.context.globalAlpha = alpha
this.context.globalAlpha = alpha if (isLine(shape)) {
if (isLine(shape)) { this.context.drawImage(img, shape.a.x, shape.a.y, shape.b.x - shape.a.x, shape.b.y - shape.a.y)
this.context.drawImage(img, shape.a.x, shape.a.y, shape.b.x - shape.a.x, shape.b.y - shape.a.y) } else if (isRect(shape)) {
} else if (isRect(shape)) { const fit = fitRect({ w: img.width, h: img.height }, { w: shape.w, h: shape.h })
const fit = fitRect({ w: img.width, h: img.height }, { w: shape.w, h: shape.h }) this.context.drawImage(img, shape.x, shape.y, fit.w, fit.h)
this.context.drawImage(img, shape.x, shape.y, fit.w, fit.h) } else if (isCircle(shape)) {
} else if (isCircle(shape)) { const side = Math.sqrt(Math.pow(shape.r, 2) / 2)
const side = Math.sqrt(Math.pow(shape.r, 2) / 2) const rect = { x: shape.cx - (side), y: shape.cy - (side), w: side * 2, h: side * 2 }
const rect = { x: shape.cx - (side), y: shape.cy - (side), w: side * 2, h: side * 2 } const fit = fitRect({ w: img.width, h: img.height }, { w: rect.w, h: rect.h })
const fit = fitRect({ w: img.width, h: img.height }, { w: rect.w, h: rect.h }) this.context.drawImage(img, rect.x, rect.y, fit.w, fit.h)
this.context.drawImage(img, rect.x, rect.y, fit.w, fit.h) } else {
} else { this.context.drawImage(img, shape.x, shape.y, img.width, img.height)
this.context.drawImage(img, shape.x, shape.y, img.width, img.height) }
} this.context.globalAlpha = 1
this.context.globalAlpha = 1
resolve()
})
} }
this.crop = function (rect) { this.crop = function (rect) {