git.s-ol.nu watch-cad / b6ed370
API redesign s-ol 18 days ago
8 changed file(s) with 349 addition(s) and 213 deletion(s). Raw diff Collapse all Expand all
1 [submodule "cpml"]
2 path = cpml
3 url = https://github.com/excessive/cpml
1 { graphics: lg } = love
2
3 import vec2, bound2 from require 'cpml'
4
5 random =
6 point: ->
7 w, h = love.graphics.getDimensions!
8 vec2 w*math.random!, h*math.random!
9
10 size: ->
11 vec2 math.random! * 200 + 100, math.random! * 200 + 100
12
13 draw =
14 rect: (mode, min, max) ->
15 x, y = min\unpack!
16 w, h = (max - min)\unpack!
17 lg.setColor 1, 1, 1
18 lg.rectangle 'line', x, y, w, h
19
20 rect = bound2 min, max
21 contains = rect\contains INPUT.mouse
22 contains and (not mode or INPUT["mouse_#{mode}"] INPUT, 1)
23
24 circle: (mode, center, r) ->
25 x, y = center\unpack!
26 lg.setColor 1, 1, 1
27 lg.circle 'line', x, y, r
28
29 contains = (INPUT.mouse - center)\len! < r
30 contains and (not mode or INPUT["mouse_#{mode}"] INPUT, 1)
31
32 half_handle = vec2 15, 15
33 input =
34 dot: (init, fixed) =>
35 init or= random.point!
36
37 if fixed == true
38 return init
39
40 fixed or= x: false, y: false
41 @pos = (rawget @, 'pos') or init
42
43 if draw.rect 'held', @pos - half_handle, @pos + half_handle
44 delta = INPUT\mouse_delta!
45 @pos = @pos + delta
46
47 @pos.x = init.x if fixed.x
48 @pos.y = init.y if fixed.y
49
50 @pos
51
52 rectangle: (init={}, fixed={}) =>
53 init.min or= random.point! * 0.8
54 init.max or= init.min + random.size!
55 @last_min = (rawget @, 'last_min') or init.min
56
57 max = input.dot @max, init.max, fixed.max
58 min = input.dot @min, init.min, fixed.min
59 delta = @last_min - min
60 max += delta
61 @last_min = min
62
63 draw.rect nil, min, max
64 { :min, :max }
65
66 circle: (init={}, fixed={}) =>
67 init.center or= random.point!
68 init.radius or= math.random! * 100 + 50
69 init.tangent or= do
70 angle = math.random! * math.pi * 2
71 init.center + vec2.from_cartesian init.radius, angle
72
73 center = input.dot @c, init.center, fixed.center
74
75 local tangent, radius
76 if fixed.radius
77 radius = init.radius
78 else
79 tangent = input.dot @t, init.tangent, fixed.tangent
80 radius = (tangent - center)\len!
81
82 draw.circle nil, center, radius
83
84 { center, tangent, :radius }
85
86 selection: (init, fixed) =>
87 init or= {o for o in *OBJS when match and o.name\match init}
88
89 lg.push 'all'
90 lg.setLineWidth 5
91 for obj in *OBJS
92 selected = @selection[obj]
93 if selected
94 obj\draw 'line', orng
95
96 if obj\hit!
97 @selection[obj] = if selected then nil else obj
98
99 lg.pop!
100
101 [o for o in pairs @selection]
102
103 {
104 :random
105 :draw
106 :input
107 }
1 Submodule commit 74de1154aa32b9c7348b95faff3228512c0a2e1c
1 -- return function (state)
2 function place_in_rect(state)
3 selection = Select_objs(state.objs, '^led-')
4 rect = Select_rect(state.dest)
5
6 sx = rect.w / (#selection + 1)
7
8 x = sx
9 for i, obj in ipairs(selection) do
10 Move(obj, rect.x + x - obj.w/2, rect.y)
11 x = x + sx
12 end
13 end
14
15 function create_star(state)
16 local inner = Select_circle(state.inner)
17 local outer = Select_circle(state.outer, inner.cx, inner.cy)
18
19 cnt = 20
20 step = 2*math.pi/cnt
21 lag_x, lag_y = inner.cx + inner.r, inner.cy
22 for i = 0, 20 do
23 r = (i % 2 == 0) and inner.r or outer.r
24 x = inner.cx + math.cos(step * i) * r
25 y = inner.cy + math.sin(step * i) * r
26 obj = Object("object_"..i, x-10, y-10, 20, 20)
27 Add(obj)
28
29 love.graphics.line(lag_x, lag_y, obj:center())
30 lag_x, lag_y = obj:center()
31 end
32 end
33
34 function test(S)
35 rect = input.rectangle(S.rect)
36
37 input.circle(S.circle, { center = rect.min, radius = 100 }, { center = true, radius = true })
38 end
39
40 return test
1 print "scratch buffer is #{file}"
2 lfs = require 'lfs'
1 import Input from require 'input'
2 import State from require 'state'
3 import Session, Script from require 'session'
4
5 export ^
6
7 COMMIT = false
8 INPUT = Input!
9 STATE = State!
10 SESSION = Session Script arg[#arg]
11
12 for k, v in pairs require 'api'
13 _G[k] = v
14
15 for event in *{'keypressed', 'keyreleased', 'mousepressed', 'mousereleased'}
16 love[event] = (...) -> INPUT[event] INPUT, ...
17
18 love.draw = SESSION\frame
19
320 require 'moon.all'
421
5 love.timer = nil
22 -- Move = (obj, x, y) ->
23 -- if COMMIT
24 -- obj.x = x
25 -- obj.y = y
26 -- return
27 --
28 -- lg.setColor orng
29 -- lg.line x + obj.w/2, y + obj.h/2, obj\center!
30 -- obj = obj\clone!
31 -- obj.x = x
32 -- obj.y = y
33 -- obj\draw 'line', orng
34 --
35 -- Add = (obj) ->
36 -- if COMMIT
37 -- table.insert OBJS, obj
38 -- return
39 --
40 -- obj\draw 'line', gren
41 --
42 -- class Object
43 -- new: (@name, @x, @y, @w=60, @h=30) =>
44 --
45 -- center: =>
46 -- @x + @w/2, @y + @h/2
47 --
48 -- draw: (mode, color) =>
49 -- rect @x, @y, @w, @h, :mode, :color
50 --
51 -- clone: =>
52 -- Object "", @x, @y, @w, @h
53 --
54 -- hit: =>
55 -- mx, my = lm.getPosition!
56 -- (lm.justDown!) and @x < mx and @x + @w > mx and @y < my and @y + @h > my
657
7 { graphics: lg, mouse: lm } = love
8
9 red = { 1, 0, 0 }
10 turk = { 0, 1, 1 }
11 orng = { .6, .5, 0 }
12 gren = { 0, .8, 0 }
13
14 rect = (x, y, w, h, opts={mode: 'line'}) ->
15 lg.setColor opts.color if opts.color
16 lg.rectangle opts.mode, x, y, w, h
17
18 mx, my = lm.getPosition!
19 (lm.isDown 1) and x < mx and x + w > mx and y < my and y + h > my
20
21 export ^
22 COMMIT = false
23
24 Move = (obj, x, y) ->
25 if COMMIT
26 obj.x = x
27 obj.y = y
28 return
29
30 lg.setColor orng
31 lg.line x + obj.w/2, y + obj.h/2, obj\center!
32 obj = obj\clone!
33 obj.x = x
34 obj.y = y
35 obj\draw 'line', orng
36
37 Add = (obj) ->
38 if COMMIT
39 table.insert OBJS, obj
40 return
41
42 obj\draw 'line', gren
43
44 Select_objs = (match) =>
45 if not @selection
46 @selection = {o for o in *OBJS when match and o.name\match match}
47
48 lg.push 'all'
49 lg.setLineWidth 5
50 for obj in *OBJS
51 selected = @selection[obj]
52 if selected
53 obj\draw 'line', orng
54
55 if obj\hit!
56 @selection[obj] = if selected then nil else obj
57
58 lg.pop!
59
60 [o for o in pairs @selection]
61
62 Select_rect = =>
63 { :x, :y, :w, :h } = @rect or { x: 222, y: 200, w: 400, h: 200 }
64
65 opts = mode: 'line', color: turk
66 rect x, y, w, h, opts
67
68 if rect x, y, 30, 30, opts
69 dx, dy = lm.getDelta!
70 x += dx
71 y += dy
72 w -= dx
73 h -= dy
74 if rect x + w - 30, y + h - 30, 30, 30, opts
75 dx, dy = lm.getDelta!
76 w += dx
77 h += dy
78
79 @rect = { :x, :y, :w, :h }
80 @rect
81
82
83 Select_circle = (cx, cy) =>
84 @circ or= do
85 r = math.random! * 100 + 50
86 a = math.random! * math.pi * 2 * 50 + 50
87 hx, hy = r * math.cos(a), r * math.sin(a)
88 { cx: 150, cy: 150, :hx, :hy, :r }
89
90 { :r, :hx, :hy } = @circ
91 if cx and cy
92 @fix_center = true
93 else
94 { :cx, :cy } = @circ
95
96 lg.setColor turk
97 lg.circle 'line', cx, cy, r
98
99 opts = color: turk, mode: 'line'
100
101 if not @fix_center
102 if rect cx - 15, cy - 15, 30, 30, opts
103 dx, dy = lm.getDelta!
104 cx += dx
105 cy += dy
106
107 if rect cx + hx - 15, cy + hy - 15, 30, 30
108 dx, dy = lm.getDelta!
109 hx += dx
110 hy += dy
111
112
113 @circ = { :cx, :cy, :hx, :hy, r: math.sqrt hx*hx + hy*hy }
114 @circ
115
116 update_script = do
117 file = arg[2]
118 last_mod = 0
119 last_script = ->
120
121 wrapper = () ->
122 ok, err = pcall last_script
123
124 if ok and 'function' != type err
125 ok = false
126 err = 'script did not return a function'
127
128 if not ok
129 print "--- ERROR EXECUTING SCRIPT: ---"
130 print err
131 return
132
133 ok, err = pcall err, STATE
134 if not ok
135 print "--- ERROR EXECUTING SCRIPT: ---"
136 print err
137
138 print "watching '#{file}'..."
139 ->
140 mod = lfs.attributes file, 'modification'
141 if last_mod < mod
142 last_mod = mod
143 script, err = loadfile file
144 if script
145 last_script = script
146 else
147 print "--- ERROR PARSING SCRIPT: ---"
148 print err
149
150 wrapper
151
152
153 class Object
154 new: (@name, @x, @y, @w=60, @h=30) =>
155
156 center: =>
157 @x + @w/2, @y + @h/2
158
159 draw: (mode, color) =>
160 rect @x, @y, @w, @h, :mode, :color
161
162 clone: =>
163 Object "", @x, @y, @w, @h
164
165 hit: =>
166 mx, my = lm.getPosition!
167 (lm.justDown!) and @x < mx and @x + @w > mx and @y < my and @y + @h > my
168
169 OBJS = {
170 Object 'led-1', 50, 50
171 Object 'led-2', 50, 150
172 Object 'led-3', 150, 50
173 Object 'led-3', 250, 150
174 Object 'btn', 150, 150, 40, 40
175 }
176
177 lm.doesHit = (x, y, w, h) ->
178 mx, my = lm.getPosition!
179
180 mkstate = -> setmetatable {}, __index: (t, k) ->
181 with val = {}
182 rawset t, k, val
183 STATE = mkstate!
184
185 commit = ->
186 COMMIT = true
187 print "COMMITTING"
188 script = update_script!
189 script!
190 STATE = mkstate!
191 COMMIT = false
192
193 local last_mx, last_my, last_down
194 love.draw = ->
195 script = update_script!
196
197 for obj in *OBJS
198 obj\draw 'fill', red
199
200 script!
201
202 last_mx, last_my = lm.getPosition!
203 last_down = lm.isDown 1
204
205 love.keypressed = (key) ->
206 if key == 'return'
207 commit!
208 if key == 'escape'
209 STATE = mkstate!
210
211 lm.justDown = ->
212 (lm.isDown 1) and not last_down
213
214 lm.getDelta = ->
215 mx, my = lm.getPosition!
216 mx - last_mx, my - last_my
58 -- OBJS = {
59 -- Object 'led-1', 50, 50
60 -- Object 'led-2', 50, 150
61 -- Object 'led-3', 150, 50
62 -- Object 'led-3', 250, 150
63 -- Object 'btn', 150, 150, 40, 40
64 -- }
1 import vec2 from require 'cpml'
2
3 class Input
4 new: =>
5 @last_keys = {}
6 @keys = {}
7
8 @mouse = vec2 love.mouse.getPosition!
9 @last_mouse = @mouse\clone!
10
11 mouse_down: (button=1) => @key_down "mouse-#{button}"
12 mouse_up: (button=1) => @key_up "mouse-#{button}"
13 mouse_held: (button=1) => @key_held "mouse-#{button}"
14
15 key_down: (key) => @keys[key] and not @last_keys[key]
16 key_up: (key) => not @keys[key] and @last_keys[key]
17 key_held: (key) => @keys[key]
18
19 mouse_pos: => @mouse
20 mouse_delta: => @mouse - @last_mouse
21
22 keypressed: (key) => @keys[key] = true
23 keyreleased: (key) => @keys[key] = nil
24
25 mousepressed: (_, _, button) => @keys["mouse-#{button}"] = true
26 mousereleased: (_, _, button) => @keys["mouse-#{button}"] = nil
27
28 end_frame: =>
29 @last_mouse = @mouse
30 @mouse = vec2 love.mouse.getPosition!
31
32 @last_keys = @keys
33 @keys = {k,v for k,v in pairs @keys}
34
35 {
36 :Input
37 }
1 lfs = require 'lfs'
2
3 trace = (msg) -> debug.traceback msg, 2
4
5 class Script
6 new: (@file) =>
7 print "loading script '#{@file}'..."
8 @last_modification = 0
9 @func = -> print 'idle'
10
11 commit: =>
12 export COMMIT
13 COMMIT = true
14
15 return unless @func
16
17 ok, msg = xpcall @func, trace, STATE.state
18 if not ok
19 @error = at: 'commit', :msg
20 return
21
22 COMMIT = false
23
24 reload_and_run: =>
25 func = @reload!
26 @func = func if func
27
28 ok, msg = xpcall @func, trace, STATE.state
29 if not ok
30 @error = at: 'exec', :msg
31
32 @print_error!
33
34 print_error: =>
35 if @error
36 switch @error.at
37 when 'parse' do print "--- ERROR PARSING SCRIPT #{@file} ---"
38 when 'load' do print "--- ERROR LOADING SCRIPT: #{@file} ---"
39 when 'exec' do print "--- ERROR EXECUTING SCRIPT: #{@file} ---"
40 when 'commit' do print "--- ERROR COMMITTING SCRIPT: #{@file} ---"
41
42 print @error.msg
43
44 reload: =>
45 modification = lfs.attributes @file, 'modification'
46
47 if @last_modification < modification
48 @last_modification = modification
49 module, msg = loadfile @file
50 if not module
51 @error = at: 'parse', :msg
52 return
53
54 ok, msg = xpcall module, trace
55 if not msg
56 @error = at: 'load', :msg
57 return
58
59 @error = nil
60 msg
61
62 class Session
63 new: (@script) =>
64 @objects = {}
65
66 frame: =>
67 for obj in *@objects
68 obj\draw!
69
70 if INPUT\key_down 'escape'
71 STATE\reset!
72
73 @script\reload_and_run!
74
75 if INPUT\key_down 'space'
76 @script\commit!
77 STATE\reset!
78
79 INPUT\end_frame!
80
81 {
82 :Script
83 :Session
84 }
1 class State
2 @mt: {
3 __index: (t, k) ->
4 with v = setmetatable {}, @@mt
5 rawset t, k, v
6 }
7
8 new: =>
9 @reset!
10
11 reset: =>
12 @state = setmetatable {}, @@mt
13
14 {
15 :State
16 }