diff --git a/README.md b/README.md index c953b45..1f1aa21 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,13 @@ _"All I wanted, was a quick way of resizing a few photos.."_ -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/). +The application was created to automate basic graphical tasks using a dialect of [LISP](https://en.wikipedia.org/wiki/Lisp_(programming_language)), it all went south when we started adding animation tools. You can look at these [example files](https://github.com/hundredrabbits/Ronin/tree/master/examples) to better understand how this all works. + +```lisp +; draw a red square +(stroke + (rect 30 30 100 100) 2 "red") +``` ## Install & Run @@ -31,6 +37,7 @@ npm start - `(stroke ~shape)` Strokes a shape. - `(fill ~rect)` Fills a shape. - `(clear ~rect)` Clears a rect. +- `(concat ...items)` - `(add ...args)` Adds values. - `(sub ...args)` Subtracts values. - `(mul ...args)` Multiplies values. @@ -54,7 +61,7 @@ npm start - `(or a b ...rest)` Returns true if at least one condition is true. - `(map fn arr)` - `(filter fn arr)` -- `(reduce fn arr ~acc)` +- `(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 @@ -66,7 +73,7 @@ npm start - `(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. +- `(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)` - `(clone a b)` @@ -78,12 +85,16 @@ npm start - `(echo ...args)` - `(str ...args)` - `(open path)` Imports a graphic file and resizes the frame. -- `(folder ~path)` Returns the content of a folder path. +- `(dir ~path)` Returns the content of a directory. +- `(file ~path)` Returns the content of a file +- `(dirpath ~path)` Returns the path of a directory. +- `(filepath ~path)` Returns the path of a file - `(exit ~force)` Exits Ronin. - `(time)` Returns timestamp in milliseconds. - `(animate ~play)` Toggles animation. - `(js)` Javascript interop. - `(test name a b)` +- `(benchmark fn)` logs time taken to execute a function ## Extras diff --git a/desktop/main.js b/desktop/main.js index 5201349..33934bd 100644 --- a/desktop/main.js +++ b/desktop/main.js @@ -23,7 +23,7 @@ app.on('ready', () => { }) app.win.loadURL(`file://${__dirname}/sources/index.html`) - app.inspect() + // app.inspect() app.win.on('closed', () => { win = null diff --git a/desktop/sources/index.html b/desktop/sources/index.html index d3a5353..955ce4b 100644 --- a/desktop/sources/index.html +++ b/desktop/sources/index.html @@ -45,6 +45,7 @@ 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","Toggle Commander",() => { ronin.commander.toggle(); },"CmdOrCtrl+K"); + ronin.controller.add("default","View","Expand Commander",() => { ronin.commander.toggle(true); },"CmdOrCtrl+Shift+K"); ronin.controller.add("default","Project","Run",() => { ronin.commander.run(); },"CmdOrCtrl+R"); ronin.controller.add("default","Project","Reload Run",() => { ronin.source.revert(); ronin.commander.run(); },"CmdOrCtrl+Shift+R"); ronin.controller.add("default","Project", "Animate",() => { ronin.animate(!ronin.always); },"CmdOrCtrl+Shift+T"); diff --git a/desktop/sources/links/main.css b/desktop/sources/links/main.css index 8abe5c2..4a9d7de 100644 --- a/desktop/sources/links/main.css +++ b/desktop/sources/links/main.css @@ -7,6 +7,7 @@ body { margin:0px; padding:0px; overflow:hidden; font-family:"input_mono_regular #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 - 105px); resize: none; font-size: 12px;line-height: 15px; padding-right: 15px} #ronin #wrapper #commander div#status { position: absolute; bottom: 0px; text-transform: lowercase;} +#ronin.expand #wrapper #commander { width:100%; } #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,"); background-size: 10px 10px; background-position: -4px -4px; width:100%; height:100%; left:340px; transition: left 250ms} diff --git a/desktop/sources/scripts/commander.js b/desktop/sources/scripts/commander.js index b7aae9b..95892a5 100644 --- a/desktop/sources/scripts/commander.js +++ b/desktop/sources/scripts/commander.js @@ -105,37 +105,43 @@ function Commander (ronin) { this.onMouseDown = (e) => { this.mouseDown = true - this.mouseRect.x = e.offsetX - this.mouseRect.y = e.offsetY - this.mouseRect.a.x = e.offsetX - this.mouseRect.a.y = e.offsetY + const offset = this.makeMouseOffset({ x: e.offsetX, y: e.offsetY }) + this.mouseRect.x = offset.x + this.mouseRect.y = offset.y + this.mouseRect.a.x = offset.x + this.mouseRect.a.y = offset.y this.mouseRect.t = 'pos' this.capture() this.show() } this.onMouseMove = (e) => { - if (this.mouseDown === true) { - this.mouseRect.w = e.offsetX - this.mouseRect.x - this.mouseRect.h = e.offsetY - this.mouseRect.y - this.mouseRect.b.x = e.offsetX - this.mouseRect.b.y = e.offsetY - this.commit() - } + if (this.mouseDown !== true) { return } + const offset = this.makeMouseOffset({ x: e.offsetX, y: e.offsetY }) + this.mouseRect.w = offset.x - this.mouseRect.x + this.mouseRect.h = offset.y - this.mouseRect.y + this.mouseRect.b.x = offset.x + this.mouseRect.b.y = offset.y + this.commit() } this.onMouseUp = (e) => { this.mouseDown = false - this.mouseRect.w = e.offsetX - this.mouseRect.x - this.mouseRect.h = e.offsetY - this.mouseRect.y - this.mouseRect.b.x = e.offsetX - this.mouseRect.b.y = e.offsetY + const offset = this.makeMouseOffset({ x: e.offsetX, y: e.offsetY }) + this.mouseRect.w = offset.x - this.mouseRect.x + this.mouseRect.h = offset.y - this.mouseRect.y + this.mouseRect.b.x = offset.x + this.mouseRect.b.y = offset.y this.mouseRect.t = '' this.commit() this._input.focus() ronin.surface.clearGuide() } + this.makeMouseOffset = (pos) => { + return { x: pos.x * ronin.surface.ratio, y: pos.y * ronin.surface.ratio } + } + // Injection this.cache = '' @@ -188,9 +194,9 @@ function Commander (ronin) { // Display - this.show = function () { + this.show = function (expand = false) { if (this.isVisible === true) { return } - ronin.el.className = '' + ronin.el.className = expand ? 'expand' : '' this.isVisible = true } @@ -200,9 +206,9 @@ function Commander (ronin) { this.isVisible = false } - this.toggle = function () { + this.toggle = function (expand = false) { if (this.isVisible !== true) { - this.show() + this.show(expand) } else { this.hide() } diff --git a/desktop/sources/scripts/library.js b/desktop/sources/scripts/library.js index 7bd7b3e..ea3483a 100644 --- a/desktop/sources/scripts/library.js +++ b/desktop/sources/scripts/library.js @@ -60,6 +60,12 @@ function Library (ronin) { return rect } + // Strings + + this.concat = function (...items) { + return items.reduce((acc, item) => { return `${acc}${item}` }, '') + } + // Math this.add = (...args) => { // Adds values. @@ -318,8 +324,22 @@ function Library (ronin) { this.open = async (path) => { // Imports a graphic file and resizes the frame. return ronin.surface.open(path) } + + // File System - this.folder = (path = ronin.source.path) => { // Returns the content of a folder path. + this.dir = (path = ronin.source.path) => { // Returns the content of a directory. + return fs.existsSync(path) ? fs.readdirSync(path) : [] + } + + this.file = (path = ronin.source.path) => { // Returns the content of a file + return fs.existsSync(path) ? fs.readFileSync(p, 'utf8') : '' + } + + this.dirpath = (path = ronin.source.path) => { // Returns the path of a directory. + return require('path').dirname(path) + } + + this.filepath = (path = ronin.source.path) => { // Returns the path of a file return fs.existsSync(path) ? fs.readdirSync(path) : [] } diff --git a/desktop/sources/scripts/surface.js b/desktop/sources/scripts/surface.js index 36ab155..b2ee463 100644 --- a/desktop/sources/scripts/surface.js +++ b/desktop/sources/scripts/surface.js @@ -6,7 +6,7 @@ function Surface (ronin) { this.ratio = window.devicePixelRatio // Contexts this.context = this.el.getContext('2d') - this.guide = this.el.getContext('2d') + this.guide = this._guide.getContext('2d') this.install = function (host) { host.appendChild(this.el) @@ -15,9 +15,6 @@ function Surface (ronin) { this._guide.addEventListener('mousedown', ronin.commander.onMouseDown, false) this._guide.addEventListener('mousemove', ronin.commander.onMouseMove, false) this._guide.addEventListener('mouseup', ronin.commander.onMouseUp, false) - // this.context.imageSmoothingEnabled = false - this.context.scale(this.ratio, this.ratio) - this.guide.scale(this.ratio, this.ratio) } this.start = function () { @@ -151,8 +148,8 @@ function Surface (ronin) { context.clearRect(rect.x, rect.y, rect.w, rect.h) } - this.clearGuide = function () { - this.clear(ronin.surface.getFrame(), ronin.surface.guide) + this.clearGuide = function (rect = this.getFrame(), context = this.guide) { + context.clearRect(rect.x, rect.y, rect.w, rect.h) } this.clone = function (a, b) { diff --git a/documentation.md b/documentation.md deleted file mode 100644 index 706fee5..0000000 --- a/documentation.md +++ /dev/null @@ -1,159 +0,0 @@ -# Functions - -## IO - -`(open path)` - -`(export path type quality)` - -`(draw path rect)` - -`(resize width height)` - -`(crop rect)` - -`(folder path)` - -`(exit)` - -## Logic - -`(gt a b)` check if `a` is greater than `b` - -`(lt a b)` check if `a` is lower than `b` - -`(eq a b)` check if `a` is equal to `b` - -`(and a b )` returns true if all conditions are true - -`(or a b )` returns true if at least one condition is true - -## Arrays - -`(map function array)` - -`(filter function array)` - -`(reduce function array accumulator)` - -`(len array)` - -`(first array)` - -`(last array)` - -`(rest array)` - -`(range start end step)` - -## Shapes - -`(pos x y)` - -`(size w h)` - -`(rect x y w h t)` - -`(circle x y r)` - -`(line start end)` - -`(text x y g string font)` - -`(svg data)` - -## Helpers - -`(frame)` - -`(center)` - -`(scale rect width height)` - -## Copy/Paste - -`(clone start end)` clone start `rect` into end `rect` - -`(stroke shape thickness color)` - -`(fill shape color)` - -`(clear shape)` - -## Objects - -`(get item key )` - -`(set item key val)` - -## Colors - -`(theme variable)` - -`(gradient (x1,y1,x2,y2) colors)` - -`(pixels rect function q)` - -`(saturation pixel q)` - -`(contrast pixel q)` - -## Math - -`(add ...values)` - -`(sub...values)` - -`(mul ...values)` - -`(div ...values)` - -`(mod a b)` - -`(clamp value min max)` - -`(step value step)` - -`(min a b)` - -`(max a b)` - -`(ceil value)` - -`(floor value)` - -`(sin a)` - -`(cos a)` - -`PI, TWO_PI` - -`(random)` - -`(random start end)` - -`(random max)` - -## Generics - -`(echo args)` - -`(str args)` - -`(test name value expectedValue)` - -## Livecoding - -`(time)` returns timestamp in milliseconds - -`(animate)` start animation - -`(animate false)` stop animation - -## Javascript interop - -`js` - -## Client - -`ronin` diff --git a/examples/animate.lisp b/examples/animate.lisp index ed3eadf..4adaf64 100644 --- a/examples/animate.lisp +++ b/examples/animate.lisp @@ -1,13 +1,17 @@ ; animate - ( - (clear) - (def t (sin (div (time) 100))) - - (def pos (add 200 30 (mul 30 t))) - (defn square (a) (rect a a a a)) - (stroke (square pos) 1 "red") - + (clear) + (def t + (sin + (div + (time) 100))) + (def pos + (add 200 30 + (mul 30 t))) + (defn square + (a) + (rect a a a a)) + (stroke + (square pos) 1 "red") ; set false to stop - (animate true) -) \ No newline at end of file + (animate true)) \ No newline at end of file diff --git a/examples/fs.lisp b/examples/fs.lisp new file mode 100644 index 0000000..b63101e --- /dev/null +++ b/examples/fs.lisp @@ -0,0 +1,14 @@ +; filesystem +( + ; get files + (def files + (dir + (dirpath))) + ; pick a random file + (def random-index + (floor + (random + (len files)))) + ; print random file name + (echo + (get files random-index))) \ No newline at end of file diff --git a/examples/g_spiral1.lisp b/examples/g_spiral1.lisp deleted file mode 100644 index 0fc82e6..0000000 --- a/examples/g_spiral1.lisp +++ /dev/null @@ -1,29 +0,0 @@ -; animated recusive spiral -; by @local_guru -( - (def start (get ronin "animate")) - (clear) - (defn rec - (v) - (if (gt v 0) - ((stroke - (circle - (add 300 - (mul (cos (add (div v 17) (div (time) 2000))) - (div v 2) - ) - ) - (add 300 - (mul (sin (div v 11)) - (div v 2) - ) - ) - (div v 2)) - 1 "rgba(255,255,255,0.1") - (rec (sub v 0.3)) - ) - ) - ) -(start) -(rec 300) -) diff --git a/examples/spiral.lisp b/examples/spiral.lisp new file mode 100644 index 0000000..6ca0c55 --- /dev/null +++ b/examples/spiral.lisp @@ -0,0 +1,30 @@ +; animated recusive spiral +; by @local_guru +( + (clear) + (defn rec + (v) + (if + (gt v 0) + ( + (stroke + (circle + (add 375 + (mul + (cos + (add + (div v 17) + (div + (time) 2000))) + (div v 2))) + (add 300 + (mul + (sin + (div v 11)) + (div v 2))) + (div v 2)) 1 "rgba(114,222, 194,0.1)") + (rec + (sub v 0.3))))) + ; set false to stop + (animate true) + (rec 300)) \ No newline at end of file