Merge pull request #3 from hundredrabbits/master

sync
This commit is contained in:
Nikolaus Gradwohl 2019-07-20 12:12:03 +02:00 committed by GitHub
commit 3d4268fce0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 125 additions and 229 deletions

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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,<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}

View File

@ -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()
}

View File

@ -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) : []
}

View File

@ -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) {

View File

@ -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 <c d...>)` returns true if all conditions are true
`(or a b <cd...>)` 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 <keys>)`
`(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`

View File

@ -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)
)
(animate true))

14
examples/fs.lisp Normal file
View File

@ -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)))

View File

@ -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)
)

30
examples/spiral.lisp Normal file
View File

@ -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))