diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2019-11-01 20:04:18 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2019-11-01 20:04:18 +0000 |
| commit | 5934b80f09c1c13fa4698e59a77d0c51b7c7b357 (patch) | |
| tree | 265742939a732b06a224d870026b22e1154add71 | |
| parent | API redesign (diff) | |
| download | watch-cad-5934b80f09c1c13fa4698e59a77d0c51b7c7b357.tar.gz watch-cad-5934b80f09c1c13fa4698e59a77d0c51b7c7b357.zip | |
make things work
| -rw-r--r-- | api.moon | 118 | ||||
| m--------- | cpml | 0 | ||||
| -rw-r--r-- | examples/demo.moon | 7 | ||||
| -rw-r--r-- | init.moon | 2 | ||||
| -rw-r--r-- | input.moon | 5 | ||||
| -rw-r--r-- | session.moon | 3 | ||||
| -rw-r--r-- | spec/state_spec.moon | 62 | ||||
| -rw-r--r-- | state.moon | 58 |
8 files changed, 209 insertions, 46 deletions
@@ -1,6 +1,7 @@ { graphics: lg } = love import vec2, bound2 from require 'cpml' +import is_once, is_live, are_live, Once from require 'state' random = point: -> @@ -11,7 +12,19 @@ random = vec2 math.random! * 200 + 100, math.random! * 200 + 100 draw = - rect: (mode, min, max) -> + cross: (pos) -> + hs = vec2 8, 8 + mx, my = (pos - hs)\unpack! + Mx, My = (pos + hs)\unpack! + + lg.setColor 1, 1, 1 + lg.line mx, my, Mx, My + lg.line mx, My, Mx, my + + contains = (INPUT.mouse - pos)\len2! < hs\len2! + INPUT\mouse_event! if contains + + rect: (min, max) -> x, y = min\unpack! w, h = (max - min)\unpack! lg.setColor 1, 1, 1 @@ -19,69 +32,88 @@ draw = rect = bound2 min, max contains = rect\contains INPUT.mouse - contains and (not mode or INPUT["mouse_#{mode}"] INPUT, 1) + INPUT\mouse_event! if contains - circle: (mode, center, r) -> + circle: (center, r) -> x, y = center\unpack! lg.setColor 1, 1, 1 lg.circle 'line', x, y, r contains = (INPUT.mouse - center)\len! < r - contains and (not mode or INPUT["mouse_#{mode}"] INPUT, 1) + INPUT\mouse_event! if contains half_handle = vec2 15, 15 input = - dot: (init, fixed) => - init or= random.point! + point: (fixed={}) => + if vec = is_once fixed + fixed = { + x: Once vec.x + y: Once vec.y + } - if fixed == true - return init - - fixed or= x: false, y: false - @pos = (rawget @, 'pos') or init + init = random.point! + @store 'x', fixed.x, init.x + @store 'y', fixed.y, init.y + pos = vec2 @x, @y - if draw.rect 'held', @pos - half_handle, @pos + half_handle - delta = INPUT\mouse_delta! - @pos = @pos + delta + -- both values are 'live', no UI necessary + if are_live fixed.x, fixed.y + draw.cross pos + return pos - @pos.x = init.x if fixed.x - @pos.y = init.y if fixed.y + hh = half_handle\clone! + hh.x /= 2 if is_live fixed.x + hh.y /= 2 if is_live fixed.y - @pos + @init 'drag', false + if 'down' == draw.rect pos - hh, pos + hh + @drag = true - rectangle: (init={}, fixed={}) => - init.min or= random.point! * 0.8 - init.max or= init.min + random.size! - @last_min = (rawget @, 'last_min') or init.min + if @drag + @drag = false if INPUT\mouse_up! - max = input.dot @max, init.max, fixed.max - min = input.dot @min, init.min, fixed.min - delta = @last_min - min - max += delta - @last_min = min + pos += INPUT\mouse_delta! - draw.rect nil, min, max - { :min, :max } + @store 'x', pos.x unless is_live fixed.x + @store 'y', pos.y unless is_live fixed.y - circle: (init={}, fixed={}) => - init.center or= random.point! - init.radius or= math.random! * 100 + 50 - init.tangent or= do - angle = math.random! * math.pi * 2 - init.center + vec2.from_cartesian init.radius, angle - - center = input.dot @c, init.center, fixed.center + vec2 @x, @y - local tangent, radius - if fixed.radius - radius = init.radius + point_relative_to: (other, fixed) => + assert (is_live other), "other needs to be live!" + + @init 'last_other', other + @init 'last', fixed or random.point! + delta = other - @last_other + + val = if delta\len2! > 0 and not is_live fixed + input.point @, @last + delta else - tangent = input.dot @t, init.tangent, fixed.tangent - radius = (tangent - center)\len! + input.point @, fixed or Once @last + + @store 'last_other', other + @store 'last', val - draw.circle nil, center, radius + val + + rectangle: (fixed={}) => + min = input.point @min, fixed.min or Once random.point! * 0.8 + max = input.point_relative_to @max, min, fixed.max or Once min + random.size! + + draw.rect min, max + { :min, :max } - { center, tangent, :radius } + circle: (fixed={}) => + local tangent + center = input.point @center, fixed.center + radius = (is_live fixed.radius) or do + radius = (is_once fixed.radius) or math.random! * 100 + 50 + init_tangent = vec2.from_cartesian radius, math.random! * 2 * math.pi + tangent = input.point_relative_to @tangent, center, fixed.tangent or Once center + init_tangent + center\dist tangent + + draw.circle center, radius + { :center, :tangent, :radius } selection: (init, fixed) => init or= {o for o in *OBJS when match and o.name\match init} diff --git a/cpml b/cpml -Subproject 74de1154aa32b9c7348b95faff3228512c0a2e1 +Subproject 6c4991511228758da7a0d9f647adefa5632e890 diff --git a/examples/demo.moon b/examples/demo.moon new file mode 100644 index 0000000..cb2531f --- /dev/null +++ b/examples/demo.moon @@ -0,0 +1,7 @@ +import vec2 from require 'cpml' + +=> + a = input.point @point, x: 200 + rect = input.rectangle @rect, min: a + (vec2 30, 0), max: { y: 200 } + + circle = input.circle @circ, center: rect.min, tangent: rect.max @@ -17,8 +17,6 @@ for event in *{'keypressed', 'keyreleased', 'mousepressed', 'mousereleased'} love.draw = SESSION\frame -require 'moon.all' - -- Move = (obj, x, y) -> -- if COMMIT -- obj.x = x @@ -12,6 +12,11 @@ class Input mouse_up: (button=1) => @key_up "mouse-#{button}" mouse_held: (button=1) => @key_held "mouse-#{button}" + mouse_event: (button=1) => + return 'down' if @mouse_down button + return 'up' if @mouse_up button + return 'held' if @mouse_held button + key_down: (key) => @keys[key] and not @last_keys[key] key_up: (key) => not @keys[key] and @last_keys[key] key_held: (key) => @keys[key] diff --git a/session.moon b/session.moon index 219a1c9..53780a0 100644 --- a/session.moon +++ b/session.moon @@ -1,4 +1,5 @@ lfs = require 'lfs' +moon = require 'moonscript.base' trace = (msg) -> debug.traceback msg, 2 @@ -46,7 +47,7 @@ class Script if @last_modification < modification @last_modification = modification - module, msg = loadfile @file + module, msg = if @file\match '%.moon$' then moon.loadfile @file else loadfile @file if not module @error = at: 'parse', :msg return diff --git a/spec/state_spec.moon b/spec/state_spec.moon new file mode 100644 index 0000000..b2085c5 --- /dev/null +++ b/spec/state_spec.moon @@ -0,0 +1,62 @@ +import Once, State from require 'state' + +describe "Once", -> + it "stores a value", -> + a = Once 3 + assert.is.equal 3, a.value + + it "can test for itself", -> + assert.is_true Once.is_once Once 3 + assert.is_true Once.is_once Once 2 + assert.is_true Once.is_once Once nil + assert.is_true Once.is_once Once { a: 3 } + + it "can test other types", -> + assert.is_falsy Once.is_once nil + assert.is_falsy Once.is_once 3 + assert.is_falsy Once.is_once "once impostor" + assert.is_falsy Once.is_once { a: 3 } + +describe "State", -> + local S + before_each -> + state = State! + S = state.state + + it "can be indexed infinitely", -> + assert.is_table S.a.b.c.d + + describe ":init", -> + it "stores default until changed", -> + S\init 'test', 2 + assert.is.equal 2, S.test + + S.test = 4 + assert.is.equal 4, S.test + + S\init 'test', 2 + assert.is.equal 4, S.test + + describe ":store", -> + it "stores defaults until changed", -> + S\store 'test', nil, 3 + assert.is.equal 3, S.test + + S\store 'test', nil, 4 + assert.is.equal 3, S.test + + it "follows live inputs", -> + S\store 'test', 3, 'nope' + assert.is.equal 3, S.test + + S\store 'test', 4, 'nope' + assert.is.equal 4, S.test + + it "lets Once() inputs override defaults", -> + S\store 'test', (Once 3), 'nope' + assert.is.equal 3, S.test + + S\store 'test', 'changed' + + S\store 'test', (Once 4), 'nope' + assert.is.equal 'changed', S.test @@ -1,10 +1,65 @@ +unpack = unpack or table.unpack + +class Once + new: (@value) => + + @is_once: (val) -> + return unless val + return unless 'table' == type val + val.__class == @@ + +all = (f) -> (...) -> + values = for i = 1, select '#', ... + val = f select i, ... + return unless val + val + + unpack values + +is_once = (val) -> (Once.is_once val) and val.value +is_live = (val) -> (not is_once val) and val + +are_once = all is_once +are_live = all is_live + class State @mt: { __index: (t, k) -> + if v = @[k] + return v + with v = setmetatable {}, @@mt rawset t, k, v } + -- initialize state under 'key' with 'default' unless already set + init: (key, default) => + if val = is_once default + default = val + rawset @, key, (rawget @, key) or default + + -- store and return a piece of state under 'key'. + -- the current and stored value is the first of the following: + -- * 'fixed', if it is a 'live' value + -- * current state, if set + -- * 'fixed', if it is a 'once' value + -- * 'default' + store: (key, fixed, default) => + state = rawget @, key + once = Once.is_once fixed + + next_state = if is_live fixed + fixed + elseif state + state + elseif once + fixed.value + else + default + + with next_state + rawset @, key, next_state + new: => @reset! @@ -12,5 +67,8 @@ class State @state = setmetatable {}, @@mt { + :is_once, :are_once + :is_live, :are_live + :Once :State } |
