Mercurial > code > home > repos > light9
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) |