diff options
Diffstat (limited to 'root/experiments')
| -rw-r--r-- | root/experiments/center_of_mass.moon | 179 | ||||
| -rw-r--r-- | root/experiments/init.moon | 26 | ||||
| -rw-r--r-- | root/experiments/tags/init.moon | 89 | ||||
| -rw-r--r-- | root/experiments/tags/tags.moon | 146 |
4 files changed, 440 insertions, 0 deletions
diff --git a/root/experiments/center_of_mass.moon b/root/experiments/center_of_mass.moon new file mode 100644 index 0000000..e5eceab --- /dev/null +++ b/root/experiments/center_of_mass.moon @@ -0,0 +1,179 @@ +import define_fileders from require 'lib.mmmfs' +Fileder = define_fileders ... + +with Fileder { + 'name: alpha': 'center_of_mass', + 'description: text/plain': "Fonts sorted by Center-of-Mass", +} + + if MODE == 'CLIENT' + import CanvasApp from require 'lib.canvasapp' + import rgb from require 'lib.color' + import article, h1, p, div, span, input, button from require 'lib.dom' + + fast = true + center = true + center_char = do + canvas = document\createElement 'canvas' + ctx = canvas\getContext '2d' + + cache = {} + + (char, font, height) -> + name = "#{char} #{height} #{font}" + return table.unpack cache[name] if cache[name] + + ctx\resetTransform! + ctx.font = "#{height}px #{font}" + width = (ctx\measureText char).width + canvas.width, canvas.height = width, height * 1.2 + + ctx.font = "#{height}px #{font}" + ctx.textBaseline = 'top' + ctx.fillStyle = rgb 0, 0, 0 + ctx\fillText char, 0, 0 + + data = ctx\getImageData 0, 0, width, height * 1.2 + + local xx, yy + if fast + loop = window\eval '(function(data) { + var xx = 0, yy = 0, n = 0; + for (var x = 0; x < data.width - 1; x++) { + for (var y = 0; y < data.height - 1; y++) { + var i = y * (data.width * 4) + x * 4; + var alpha = data.data[i + 3] / 255; + xx += x * alpha; + yy += y * alpha; + n += alpha; + } + } + + xx /= n; + yy /= n; + return [xx, yy]; + })' + res = loop nil, data + xx, yy = res[0], res[1] + else + xx, yy, n = 0, 0, 0 + for x = 0, data.width - 1 + for y = 0, data.height - 1 + i = y * (data.width * 4) + x * 4 + alpha = data.data[i + 3] / 255 + xx += x * alpha + yy += y * alpha + n += alpha + + xx /= n + yy /= n + cache[name] = { xx, yy, width } + xx, yy, width + + class CenterOfMass extends CanvasApp + width: window.innerWidth - 20 + height: 300 + new: (text, @font, @size) => + super! + @text = {} + for i = 1,#text + @add text\sub i, i + + add: (char) => + rcx, rcy, w = center_char char, @font, @size + cx, cy = w/2, @size/2 + vx, vy = 0, 0 + table.insert @text, { + :char, :rcx, :rcy, :w + :cx, :cy, :vx, :vy + } + + refresh: => + for char in *@text + char.rcx, char.rcy, char.w = center_char char.char, @font, @size + + keydown: (key) => + if key == "Backspace" or key == "Delete" + table.remove @text + elseif string.len(key) == 1 + @add key + + update: (dt) => + super dt + + ACCEL = 4 * dt + DAMPING = 8 * dt + + for char in *@text + { :rcx, :rcy, :cx, :cy, :w } = char + if not center + rcx, rcy = w/2, @size/2 + dx, dy = rcx - cx, rcy - cy + char.vx += dx * ACCEL + char.vy += dy * ACCEL + char.cx += char.vx + char.cy += char.vy + char.vx *= DAMPING + char.vy *= DAMPING + + draw: => + @ctx\clearRect 0, 0, @width, @height + + @ctx.font = "#{@size}px #{@font}" + @ctx.textBaseline = 'top' + + x, y = @size * .1, @size + for { :char, :cx, :cy, :w } in *@text + if x + w > @width + x = 0 + y += @size * 1.2 + + @ctx\fillText char, x + w/2 - cx, y - cy + x += w + + .props['moon -> mmm/dom'] = -> + _content = {} + append = (x) -> table.insert _content, x + + append h1 'Fonts aligned by Center-of-Mass' + app = CenterOfMass "Click here and type Away!", "Times New Roman", 40 + append app.canvas + app.canvas.style.backgroundColor = '#eee' + + add = => + append div { + span 'font: ', + with @font_input = input! + .type = 'text' + .value = 'Times New Roman' + with button 'set' + .onclick = (_, e) -> + app.font = @font_input.value + app\refresh! + } + + append div { + span 'size: ', + input type: 'range', min: 2, max: 120, value: 40, onchange: (_, e) -> + size = e.target.value + @size_label.innerText = size + app.size = size + app\refresh! + with @size_label = span '40' + '' + } + + append div { + span 'center characters by weight: ', + input type: 'checkbox', checked: center, onchange: (_, e) -> + center = e.target.checked + } + + append div { + span 'optimize inner loop: ', + input type: 'checkbox', checked: fast, onchange: (_, e) -> + fast = e.target.checked + } + add {} + + article _content diff --git a/root/experiments/init.moon b/root/experiments/init.moon new file mode 100644 index 0000000..0beb78c --- /dev/null +++ b/root/experiments/init.moon @@ -0,0 +1,26 @@ +import div, h3, ul, li, a from require 'lib.dom' +import define_fileders from require 'lib.mmmfs' +Fileder = define_fileders ... +require = relative ... + +Fileder { + 'name: alpha': 'experiments', + 'title: text/plain': 'various experiments', + 'moon -> mmm/dom': (path) => div { + h3 @gett 'title: text/plain', style: { 'margin-bottom': '-.5em' }, + ul for child in *@children + name = child\gett 'name: alpha' + desc = child\gett 'description: text/plain' + li { + a name, { + href: child.path, + onclick: (e) => + e\preventDefault! + BROWSER\navigate "animations | #{name}", + }, + ': ', desc + } + } + + require '.center_of_mass' +} diff --git a/root/experiments/tags/init.moon b/root/experiments/tags/init.moon new file mode 100644 index 0000000..e3198f8 --- /dev/null +++ b/root/experiments/tags/init.moon @@ -0,0 +1,89 @@ +on_client ((...) -> + require = relative ... + import add_tag, rmv_tag, Node, Hierarchy, Toggle, NamespacedToggle from require '.tags' + import ReactiveVar, append, tohtml, text, elements from require 'lib.component' + import div, form, span, h3, a, input, textarea, button from elements + + clone = (set) -> + assert set and 'table' == (type set), 'not a set' + { k,v for k,v in pairs set } + set_append = (val) -> (set) -> + with copy = clone set + copy[val] = val + set_remove = (val) -> (set) -> + with copy = clone set + copy[val] = nil + set_join = (tbl, sep=' ') -> + ret = '' + for tag in pairs tbl + ret ..= (tostring tag) .. sep + ret + + entries = div! + append entries + + class ReactiveNode extends Node + new: (...) => + super ... + @tags = ReactiveVar @tags + @_node = div { + span @name, style: { 'font-weight': 'bold' }, + @tags\map (tags) -> with div! + for tag,_ in pairs tags + \append a (text tag), href: '#', style: { + display: 'inline-block', + margin: '0 5px', + } + } + @node = tohtml @_node + + has: (tag) => @tags\get![tag] + add: (tag) => @tags\transform set_append tag + rmv: (tag) => @tags\transform set_remove tag + + rules = { + Hierarchy 'home', 'sol' + Hierarchy 'sol', 'desktop' + Hierarchy 'desktop', 'vacation' + Hierarchy 'desktop', 'documents' + NamespacedToggle 'documents', 'work', 'personal' + -- Toggle 'work', 'personal' + -- Hierarchy 'documents', 'work' + -- Hierarchy 'documents', 'personal' + } + + pictures = for i=1,10 + with node = ReactiveNode "picture#{i}.jpg" + entries\append node + add_tag node, 'vacation' + + pers = ReactiveNode 'mypersonalfile.doc' + entries\append pers + + append div do + yield = coroutine.yield + step = coroutine.wrap -> + yield "mark document" + add_tag pers, 'documents' + + yield "mark personal" + add_tag pers, 'personal' + + yield "mark work" + add_tag pers, 'work' + + yield "unmark work" + rmv_tag pers, 'work' + + yield "remove from documents" + rmv_tag pers, 'documents' + + yield false + + next_step = ReactiveVar step! + next_step\map (desc) -> + if desc + button (text desc), onclick: (e) => next_step\set step! + else + text '' +), ... diff --git a/root/experiments/tags/tags.moon b/root/experiments/tags/tags.moon new file mode 100644 index 0000000..198392e --- /dev/null +++ b/root/experiments/tags/tags.moon @@ -0,0 +1,146 @@ +join = (tbl, sep) -> + ret = '' + for tag in pairs tbl + ret ..= (tostring tag) .. sep + ret + +handlers = { + add: {} + rmv: {} +} + +class Node + new: (@name) => + @tags = {} + + inspect: => + "#{@name}: [#{join @tags, ' '}]" + + has: (tag) => @tags[tag] + add: (tag) => @tags[tag] = tag + rmv: (tag) => @tags[tag] = nil + +any = -> true +literal = (def) -> (val) -> def == val +oneof = (defs) -> (val) -> + for def in *defs + return true if def == val + false +has = (tag) -> (node) -> node\has tag + +add_tag = (node, tag) -> + return if node\has tag + node\add tag + for hand, _ in pairs handlers.add + hand\match node, tag + +rmv_tag = (node, tag) -> + return if not node\has tag + node\rmv tag + for hand, _ in pairs handlers.rmv + hand\match node, tag + +class Handler + new: (@rule, @action, match, @func) => + @args = for arg in *match + if 'string' == type arg + literal arg + elseif 'table' == type arg + oneof arg + else + arg + + match: (...) => + supplied = { ... } + assert #supplied == #@args, 'length of arguments doesnt match' + for i = 1, #supplied + return false if not @args[i] supplied[i] + + @.func @rule, ... + + name: => "#{@rule.name}:#{@action}" + +class Rule + new: (@name="#{@@__name}") => + @owned_handlers = {} + + hook: (action, ...) => + handler = Handler @, action, ... + table.insert @owned_handlers, handler + handlers[action][handler] = handler + + destroy: => + for hand in *@owned_handlers + handlers[hand.action][hand] = nil + +class Hierarchy extends Rule + new: (@parent, @child) => + super! + + -- when something is tagged with the child-tag, apply the parent tag + @hook 'add', { any, @child }, (node) => + add_tag node, @parent + + -- when child tag is removed, remove parent tag + @hook 'rmv', { any, @child }, (node) => + rmv_tag node, @parent + + -- when parent tag is removed, remove child tag + @hook 'rmv', { any, @parent }, (node) => + rmv_tag node, @child + +class Toggle extends Rule + new: (@a, @b) => + super! + + either = { @a, @b } + opposite = (tag) -> if tag == @a then @b else @a + + -- when a is added, remove b and vice-versa + @hook 'add', { any, either }, (node, tag) => + rmv_tag node, opposite tag + + -- when a is removed, add b and vice-versa + @hook 'rmv', { any, either }, (node, tag) => + add_tag node, opposite tag + +class NamespacedToggle extends Rule + new: (@ns, @a, @b) => + super! + + namespaced = has @ns + either = { @a, @b } + opposite = (tag) -> if tag == @a then @b else @a + + -- when node enters namespace, add default tag + @hook 'add', { any, @ns }, (node) => + add_tag node, @a + + -- when node leaves namespace, remove tags + @hook 'rmv', { any, @ns }, (node) => + rmv_tag node, @a + rmv_tag node, @b + + -- when a is added, remove b and vice-versa + @hook 'add', { namespaced, either }, (node, tag) => + rmv_tag node, opposite tag + + -- when a is removed, add b and vice-versa + @hook 'rmv', { namespaced, either }, (node, tag) => + add_tag node, opposite tag + +{ + :Node, + :Rule, + + :any, + :literal, + :oneof, + :has, + :add_tag, + :rmv_tag, + + :Hierarchy, + :Toggle, + :NamespacedToggle, +} |
