comparison web/paint/paint-elements.coffee @ 2376:4556eebe5d73

topdir reorgs; let pdm have its src/ dir; separate vite area from light9/
author drewp@bigasterisk.com
date Sun, 12 May 2024 19:02:10 -0700
parents light9/web/paint/paint-elements.coffee@7fe81130b735
children
comparison
equal deleted inserted replaced
2375:623836db99af 2376:4556eebe5d73
1 log = debug('paint')
2 debug.enable('paint')
3
4 class Painting
5 constructor: (@svg) ->
6 @strokes = []
7
8 setSize: (@size) ->
9
10 startStroke: (pos, color) ->
11 stroke = new Stroke(pos, color, @size)
12 stroke.appendElem(@svg)
13 @strokes.push(stroke)
14 return stroke
15
16 hover: (pos) ->
17 @clear()
18 s = @startStroke(pos, '#ffffff', @size)
19 r = .02
20 steps = 5
21 for ang in [0..steps]
22 ang = 6.28 * ang / steps
23 s.move([pos[0] + r * Math.sin(ang), pos[1] + 1.5 * r * Math.cos(ang)])
24
25 getDoc: ->
26 {strokes: @strokes}
27
28 clear: ->
29 s.removeElem() for s in @strokes
30 @strokes = []
31
32 class Stroke
33 constructor: (pos, @color, @size) ->
34 @path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
35 @path.setAttributeNS(null, 'd', "M #{pos[0]*@size[0]} #{pos[1]*@size[1]}")
36 @pts = [pos]
37 @lastPos = pos
38
39 appendElem: (parent) ->
40 parent.appendChild(@path)
41
42 removeElem: ->
43 @path.remove()
44
45 move: (pos) ->
46 if Math.hypot(pos[0] - @lastPos[0], pos[1] - @lastPos[1]) < .02
47 return
48 @path.attributes.d.value += " L #{pos[0]*@size[0]} #{pos[1]*@size[1]}"
49 @pts.push(pos)
50 @lastPos = pos
51
52 finish: () ->
53
54 Polymer
55 is: "light9-paint-canvas"
56 behaviors: [ Polymer.IronResizableBehavior ]
57 listeners: 'iron-resize': 'onResize'
58 properties: {
59 bg: { type: String },
60 tool: { type: String, value: 'hover' },
61 painting: { type: Object } # output
62 }
63 ready: ->
64 @painting = new Painting(@$.paint)
65 @onResize()
66 @$.paint.addEventListener('mousedown', @onDown.bind(@))
67 @$.paint.addEventListener('mousemove', @onMove.bind(@))
68 @$.paint.addEventListener('mouseup', @onUp.bind(@))
69 @$.paint.addEventListener('touchstart', @onDown.bind(@))
70 @$.paint.addEventListener('touchmove', @onMove.bind(@))
71 @$.paint.addEventListener('touchend', @onUp.bind(@))
72
73 @hover = _.throttle((ev) =>
74 @painting.hover(@evPos(ev))
75 @scopeSubtree(@$.paint)
76 @fire('paintingChanged', @painting)
77 , 100)
78
79 evPos: (ev) ->
80 px = (if ev.touches?.length? then [Math.round(ev.touches[0].clientX),
81 Math.round(ev.touches[0].clientY)] else [ev.x, ev.y])
82 return [px[0] / @size[0], px[1] / @size[1]]
83
84 onClear: () ->
85 @painting.clear()
86 @fire('paintingChanged', @painting)
87
88 onDown: (ev) ->
89 switch @tool
90 when "hover"
91 @onMove(ev)
92 when "paint"
93 # if it's on an existing one, do selection
94 @currentStroke = @painting.startStroke(@evPos(ev), '#aaaaaa')
95 @scopeSubtree(@$.paint)
96
97 onMove: (ev) ->
98 switch @tool
99 when "hover"
100 @hover(ev)
101
102 when "paint"
103 # ..or move selection
104 return unless @currentStroke
105 @currentStroke.move(@evPos(ev))
106
107 onUp: (ev) ->
108 return unless @currentStroke
109 @currentStroke.finish()
110 @currentStroke = null
111
112 @notifyPath('painting.strokes.length') # not working
113 @fire('paintingChanged', @painting)
114
115 onResize: (ev) ->
116 @size = [@$.parent.offsetWidth, @$.parent.offsetHeight]
117 @$.paint.attributes.viewBox.value = "0 0 #{@size[0]} #{@size[1]}"
118 @painting.setSize(@size)
119
120
121 Polymer
122 is: "light9-simulation"
123 properties: {
124 graph: { type: Object }
125 layers: { type: Object }
126 solution: { type: Object }
127 }
128 listeners: [
129 "onLayers(layers)"
130 ]
131 ready: ->
132 null
133 onLayers: (layers) ->
134 log('upd', layers)
135
136
137 Polymer
138 is: "light9-device-settings",
139 properties: {
140 graph: { type: Object }
141 subj: {type: String, notify: true},
142 label: {type: String, notify: true},
143 attrs: {type: Array, notify: true},
144 },
145 observers: [
146 'onSubj(graph, subj)'
147 ]
148 ready: ->
149 @label = "aura2"
150 @attrs = [
151 {attr: 'rx', val: .03},
152 {attr: 'color', val: '#ffe897'},
153 ]
154 onSubj: (graph, @subj) ->
155 graph.runHandler(@loadAttrs.bind(@), "loadAttrs #{@subj}")
156 loadAttrs: ->
157 U = (x) => @graph.Uri(x)
158 @attrs = []
159 for s in @graph.objects(U(@subj), U(':setting'))
160 attr = @graph.uriValue(s, U(':deviceAttr'))
161 attrLabel = @graph.stringValue(attr, U('rdfs:label'))
162 @attrs.push({attr: attrLabel, val: @settingValue(s)})
163 @attrs = _.sortBy(@attrs, 'attr')
164
165 settingValue: (s) ->
166 U = (x) => @graph.Uri(x)
167 for pred in [U(':value'), U(':scaledValue')]
168 try
169 return @graph.stringValue(s, pred)
170 catch
171 null
172 try
173 return @graph.floatValue(s, pred)
174 catch
175 null
176 throw new Error("no value for #{s}")
177
178 Polymer
179 is: "light9-paint"
180 properties: {
181 painting: { type: Object }
182 client: { type: Object }
183 graph: { type: Object }
184 }
185
186 ready: () ->
187 # couldn't make it work to bind to painting's notifyPath events
188 @$.canvas.addEventListener('paintingChanged', @paintingChanged.bind(@))
189 @$.solve.addEventListener('response', @onSolve.bind(@))
190
191 @clientSendThrottled = _.throttle(@client.send.bind(@client), 60)
192 @bestMatchPending = false
193
194 paintingChanged: (ev) ->
195 U = (x) => @graph.Uri(x)
196
197 @painting = ev.detail
198 @$.solve.body = JSON.stringify(@painting.getDoc())
199 #@$.solve.generateRequest()
200
201 @$.bestMatches.body = JSON.stringify({
202 painting: @painting.getDoc(),
203 devices: [
204 U('dev:aura1'), U('dev:aura2'), U('dev:aura3'), U('dev:aura4'), U('dev:aura5'),
205 U('dev:q1'), U('dev:q2'), U('dev:q3'),
206 ]})
207
208 send = =>
209 @$.bestMatches.generateRequest().completes.then (r) =>
210 @clientSendThrottled(r.response.settings)
211 if @bestMatchPending
212 @bestMatchPending = false
213 send()
214
215 if @$.bestMatches.loading
216 @bestMatchPending = true
217 else
218 send()
219
220 onSolve: (response) ->
221 U = (x) => @graph.Uri(x)
222
223 sample = @$.solve.lastResponse.bestMatch.uri
224 settingsList = []
225 for s in @graph.objects(sample, U(':setting'))
226 try
227 v = @graph.floatValue(s, U(':value'))
228 catch
229 v = @graph.stringValue(s, U(':scaledValue'))
230 row = [@graph.uriValue(s, U(':device')), @graph.uriValue(s, U(':deviceAttr')), v]
231 settingsList.push(row)
232 @client.send(settingsList)