about summary refs log tree commit diff stats
path: root/app/assets/javascripts/svg.js
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/svg.js')
-rw-r--r--app/assets/javascripts/svg.js422
1 files changed, 422 insertions, 0 deletions
diff --git a/app/assets/javascripts/svg.js b/app/assets/javascripts/svg.js new file mode 100644 index 0000000..c103b94 --- /dev/null +++ b/app/assets/javascripts/svg.js
@@ -0,0 +1,422 @@
1namespace(function() {
2
3window.createElement = function(type) {
4 return document.createElementNS('http://www.w3.org/2000/svg', type)
5}
6
7window.drawSymbol = function(params, customMechanics) {
8 var svg = createElement('svg')
9 svg.setAttribute('viewBox', '0 0 ' + params.width + ' ' + params.height)
10 if (!params.x) params.x = 0
11 if (!params.y) params.y = 0
12 drawSymbolWithSvg(svg, params, customMechanics)
13 return svg
14}
15
16window.drawSymbolWithSvg = function(svg, params, customMechanics) {
17 if (params.type == 'square') square(svg, params)
18 else if (params.type == 'dot') dot(svg, params)
19 else if (params.type == 'gap') gap(svg, params)
20 else if (params.type == 'star') star(svg, params)
21 else if (params.type == 'poly') poly(svg, params)
22 else if (params.type == 'ylop') ylop(svg, params)
23 else if (params.type == 'nega') nega(svg, params)
24 else if (params.type == 'nonce') { /* Do nothing */ }
25 else if (params.type == 'triangle') triangle(svg, params)
26 else if (params.type == 'crayon') crayon(svg, params)
27 else if (params.type == 'start') start(svg, params)
28 else if (params.type == 'end') end(svg, params)
29 else if (params.type == 'drag') drag(svg, params)
30 else if (params.type == 'plus') plus(svg, params)
31 else if (params.type == 'minus') minus(svg, params)
32 else if (params.type == 'bridge' && customMechanics) bridge(svg, params)
33 else if (params.type == 'arrow' && customMechanics) arrow(svg, params)
34 else if (params.type == 'sizer' && customMechanics) sizer(svg, params)
35 else {console.error('Cannot draw unknown SVG type: ' + params.type)}
36}
37
38function square(svg, params) {
39 var rect = createElement('rect')
40 svg.appendChild(rect)
41 rect.setAttribute('width', 28)
42 rect.setAttribute('height', 28)
43 rect.setAttribute('x', params.width/2-14 + params.x)
44 rect.setAttribute('y', params.height/2-14 + params.y)
45 rect.setAttribute('rx', 7)
46 rect.setAttribute('ry', 7)
47 rect.setAttribute('fill', params.color)
48 rect.setAttribute('class', params.class)
49}
50
51function star(svg, params) {
52 var poly = createElement('polygon')
53 svg.appendChild(poly)
54 var points = [
55 '-10.5 -10.5', // Top left
56 '-9.5 -4',
57 '-15 0',
58 '-9.5 4',
59 '-10.5 10.5', // Bottom left
60 '-4 9.5',
61 '0 15',
62 '4 9.5',
63 '10.5 10.5', // Bottom right
64 '9.5 4',
65 '15 0',
66 '9.5 -4',
67 '10.5 -10.5', // Top right
68 '4, -9.5',
69 '0 -15',
70 '-4 -9.5',
71 ]
72 poly.setAttribute('transform', 'translate(' + (params.width/2 + params.x) + ', ' + (params.height/2 + params.y) + ')')
73 poly.setAttribute('points', points.join(', '))
74 poly.setAttribute('fill', params.color)
75 poly.setAttribute('class', params.class)
76}
77
78function poly(svg, params) {
79 if (params.polyshape === 0) return
80 var size = 10 // Side length of individual squares in the polyomino
81 var space = 4 // Gap between squares in the polyomino
82 var polyomino = window.polyominoFromPolyshape(params.polyshape)
83
84 var bounds = {'xmin':0, 'xmax':0, 'ymin':0, 'ymax':0}
85 for (var i=0; i<polyomino.length; i++) {
86 var pos = polyomino[i]
87 bounds.xmin = Math.min(bounds.xmin, pos.x)
88 bounds.xmax = Math.max(bounds.xmax, pos.x)
89 bounds.ymin = Math.min(bounds.ymin, pos.y)
90 bounds.ymax = Math.max(bounds.ymax, pos.y)
91 }
92 var offset = (size+space)/2 // Offset between elements to create the gap
93 var centerX = (params.width - size - offset * (bounds.xmax + bounds.xmin)) / 2 + params.x
94 var centerY = (params.height - size - offset * (bounds.ymax + bounds.ymin)) / 2 + params.y
95
96 for (var i=0; i<polyomino.length; i++) {
97 var pos = polyomino[i]
98 if (pos.x%2 !== 0 || pos.y%2 !== 0) continue
99 var rect = createElement('rect')
100 rect.style.pointerEvents = 'none'
101 var transform = 'translate(' + (centerX + pos.x*offset) + ', ' + (centerY + pos.y*offset) + ')'
102 if (window.isRotated(params.polyshape)) {
103 // -30 degree rotation around the midpoint of the square
104 transform = 'rotate(-30, ' + (params.width/2 + params.x) + ', ' + (params.height/2 + params.y) + ') ' + transform
105 }
106 rect.setAttribute('transform', transform)
107 rect.setAttribute('height', size)
108 rect.setAttribute('width', size)
109 rect.setAttribute('fill', params.color)
110 rect.setAttribute('class', params.class)
111 svg.appendChild(rect)
112 }
113}
114
115function ylop(svg, params) {
116 if (params.polyshape === 0) return
117 var size = 12 // Side length of individual squares in the polyomino
118 var space = 2 // Gap between squares in the polyomino
119 var polyomino = window.polyominoFromPolyshape(params.polyshape)
120
121 var bounds = {'xmin':0, 'xmax':0, 'ymin':0, 'ymax':0}
122 for (var i=0; i<polyomino.length; i++) {
123 var pos = polyomino[i]
124 bounds.xmin = Math.min(bounds.xmin, pos.x)
125 bounds.xmax = Math.max(bounds.xmax, pos.x)
126 bounds.ymin = Math.min(bounds.ymin, pos.y)
127 bounds.ymax = Math.max(bounds.ymax, pos.y)
128 }
129 var offset = (size+space)/2 // Offset between elements to create the gap
130 var centerX = (params.width - size - offset * (bounds.xmax + bounds.xmin)) / 2 + params.x
131 var centerY = (params.height - size - offset * (bounds.ymax + bounds.ymin)) / 2 + params.y
132
133 for (var i=0; i<polyomino.length; i++) {
134 var pos = polyomino[i]
135 if (pos.x%2 !== 0 || pos.y%2 !== 0) continue
136 var poly = createElement('polygon')
137 poly.style.pointerEvents = 'none'
138 var points = [
139 '0 0', '12 0', '12 12', '0 12', '0 3',
140 '3 3', '3 9', '9 9', '9 3', '0 3',
141 ]
142 poly.setAttribute('points', points.join(', '))
143 var transform = 'translate(' + (centerX + pos.x*offset) + ', ' + (centerY + pos.y*offset) + ')'
144 if (window.isRotated(params.polyshape)) {
145 // -30 degree rotation around the midpoint of the square
146 transform = 'rotate(-30, ' + (params.width/2 + params.x) + ', ' + (params.height/2 + params.y) + ') ' + transform
147 }
148 poly.setAttribute('transform', transform)
149 poly.setAttribute('fill', params.color)
150 poly.setAttribute('class', params.class)
151 svg.appendChild(poly)
152 }
153}
154
155function nega(svg, params) {
156 var poly = createElement('polygon')
157 svg.appendChild(poly)
158 var points = [
159 '2.9 -2',
160 '2.9 -10.4',
161 '-2.9 -10.4',
162 '-2.9 -2',
163 '-10.2 2.2',
164 '-7.3 7.2',
165 '0 3',
166 '7.3 7.2',
167 '10.2 2.2',
168 ]
169 poly.setAttribute('transform', 'translate(' + (params.width/2 + params.x) + ', ' + (params.height/2 + params.y) + ')')
170 poly.setAttribute('points', points.join(', '))
171 poly.setAttribute('fill', params.color)
172 poly.setAttribute('class', params.class)
173}
174
175var triangleDistributions = [
176 [],
177 [1],
178 [2],
179 [3],
180 [2, 2],
181 [2, 3],
182 [3, 3],
183 [2, 3, 2],
184 [3, 2, 3],
185 [3, 3, 3]
186]
187
188function triangle(svg, params) {
189 var distribution = triangleDistributions[params.count]
190 var high = distribution.length
191 for (var y=0; y<high; y++) {
192 var wide = distribution[y]
193 for (var x=0; x<wide; x++) {
194 var poly = createElement('polygon')
195 svg.appendChild(poly)
196 var Xcoord = params.x + params.width/2 + 11*(2*x - wide + 1)
197 var Ycoord = params.y + params.height/2 + 10*(2*y - high + 1)
198 poly.setAttribute('transform', 'translate(' + Xcoord + ', ' + Ycoord + ')')
199 poly.setAttribute('points', '0 -8, -8 6, 8 6')
200 poly.setAttribute('fill', params.color)
201 poly.setAttribute('class', params.class)
202 }
203 }
204}
205
206function crayon(svg, params) {
207 var height = params.height
208
209 var poly = createElement('polygon')
210 svg.appendChild(poly)
211 var points = [
212 '0 ' + (height/2),
213 (height/2) + ' 0',
214 (height/2) + ' ' + height,
215 ]
216 poly.setAttribute('points', points.join(', '))
217 poly.setAttribute('fill', params.color)
218 var txt = createElement('text')
219 svg.appendChild(txt)
220 txt.setAttribute('fill', window.TEXT_COLOR)
221 txt.setAttribute('transform', 'translate(' + (height/2 + 10) + ', ' + (height/2 + 6) + ')')
222 txt.textContent = params.text
223}
224
225function start(svg, params) {
226 var circ = createElement('circle')
227 svg.appendChild(circ)
228 circ.setAttribute('r', 24)
229 circ.setAttribute('fill', window.FOREGROUND)
230 circ.setAttribute('cx', params.height/2 + params.x)
231 circ.setAttribute('cy', params.width/2 + params.y)
232}
233
234function end(svg, params) {
235 var rect = createElement('rect')
236 svg.appendChild(rect)
237 rect.setAttribute('width', 24)
238 rect.setAttribute('height', 24)
239 rect.setAttribute('fill', window.FOREGROUND)
240 rect.setAttribute('x', params.height/2 - 12 + params.x)
241 rect.setAttribute('y', params.width/2 - 12 + params.y)
242
243 var circ = createElement('circle')
244 svg.appendChild(circ)
245 circ.setAttribute('r', 12)
246 circ.setAttribute('fill', window.FOREGROUND)
247 circ.setAttribute('cx', params.height/2 + params.x)
248 circ.setAttribute('cy', params.width/2 + params.y)
249
250 if (params.dir === 'left') {
251 rect.setAttribute('x', parseInt(rect.getAttribute('x'), 10) - 12)
252 circ.setAttribute('cx', parseInt(circ.getAttribute('cx'), 10) - 24)
253 } else if (params.dir === 'right') {
254 rect.setAttribute('x', parseInt(rect.getAttribute('x'), 10) + 12)
255 circ.setAttribute('cx', parseInt(circ.getAttribute('cx'), 10) + 24)
256 } else if (params.dir === 'top') {
257 rect.setAttribute('y', parseInt(rect.getAttribute('y'), 10) - 12)
258 circ.setAttribute('cy', parseInt(circ.getAttribute('cy'), 10) - 24)
259 } else if (params.dir === 'bottom') {
260 rect.setAttribute('y', parseInt(rect.getAttribute('y'), 10) + 12)
261 circ.setAttribute('cy', parseInt(circ.getAttribute('cy'), 10) + 24)
262 } else {
263 console.error('Endpoint direction not defined!', JSON.stringify(params))
264 }
265}
266
267function dot(svg, params) {
268 var hex = createElement('polygon')
269 svg.appendChild(hex)
270 hex.setAttribute('points', '5.2 9, 10.4 0, 5.2 -9, -5.2 -9, -10.4 0, -5.2 9')
271 hex.setAttribute('transform', 'translate(' + (params.width/2 + params.x) + ', ' + (params.height/2 + params.y) + ')')
272 hex.setAttribute('fill', params.color)
273 hex.setAttribute('class', params.class)
274 hex.setAttribute('stroke', params.stroke)
275 hex.setAttribute('stroke-width', params.strokeWidth)
276 hex.setAttribute('style', 'pointer-events:none;')
277}
278
279function gap(svg, params) {
280 if (!params.rot) params.rot = 0
281 var centerX = params.height/2 + params.x
282 var centerY = params.width/2 + params.y
283 var rotate = function(degrees) {return 'rotate(' + degrees + ', ' + centerX + ', ' + centerY + ')'}
284
285 var rect = createElement('rect')
286 svg.appendChild(rect)
287 rect.setAttribute('width', 32)
288 rect.setAttribute('height', 24)
289 rect.setAttribute('fill', window.FOREGROUND)
290 rect.setAttribute('transform', rotate(90 * params.rot))
291 rect.setAttribute('x', centerX - 40)
292 rect.setAttribute('y', centerY - 12)
293 rect.setAttribute('shape-rendering', 'crispedges')
294
295 var rect = createElement('rect')
296 svg.appendChild(rect)
297 rect.setAttribute('width', 32)
298 rect.setAttribute('height', 24)
299 rect.setAttribute('fill', window.FOREGROUND)
300 rect.setAttribute('transform', rotate(90 * params.rot))
301 rect.setAttribute('x', centerX + 9)
302 rect.setAttribute('y', centerY - 12)
303 rect.setAttribute('shape-rendering', 'crispedges')
304}
305
306function drag(svg, params) {
307 if (!params.rot) params.rot = 0
308
309 for (var i=0; i<6; i++) {
310 for (var j=0; j<2; j++) {
311 var rect = createElement('rect')
312 svg.appendChild(rect)
313 rect.setAttribute('width', 2)
314 rect.setAttribute('height', 2)
315 if (params.rot === 0) {
316 rect.setAttribute('x', i*4)
317 rect.setAttribute('y', j*4)
318 } else {
319 rect.setAttribute('y', i*4)
320 rect.setAttribute('x', j*4)
321 }
322 rect.setAttribute('fill', window.PAGE_BACKGROUND)
323 }
324 }
325}
326
327function plus(svg, params) {
328 var verti = createElement('rect')
329 svg.appendChild(verti)
330 verti.setAttribute('x', params.width/2 - 1)
331 verti.setAttribute('y', 3)
332 verti.setAttribute('width', 2)
333 verti.setAttribute('height', params.height - 6)
334 verti.setAttribute('fill', window.TEXT_COLOR)
335 minus(svg, params)
336}
337
338function minus(svg, params) {
339 var horiz = createElement('rect')
340 svg.appendChild(horiz)
341 horiz.setAttribute('x', 3)
342 horiz.setAttribute('y', params.height/2 - 1)
343 horiz.setAttribute('width', params.width - 6)
344 horiz.setAttribute('height', 2)
345 horiz.setAttribute('fill', window.TEXT_COLOR)
346}
347
348function bridge(svg, params) {
349 var poly = createElement('polygon')
350 svg.appendChild(poly)
351 var points = [
352 '-10.58 14.56',
353 '-17.12 -5.56',
354 '0 -18',
355 '17.12 -5.56',
356 '10.58 14.56',
357 '5.29 7.28',
358 '8.56 -2.78',
359 '0 -9',
360 '-8.56 -2.78',
361 '-5.29 7.28',
362 ]
363 poly.setAttribute('transform', 'translate(' + (params.width/2 + params.x) + ', ' + (params.height/2 + params.y) + ')')
364 poly.setAttribute('points', points.join(', '))
365 poly.setAttribute('fill', params.color)
366 poly.setAttribute('class', params.class)
367}
368
369function arrow(svg, params) {
370 if (!params.rot) params.rot = 0
371
372 var centerX = params.height/2 + params.x
373 var centerY = params.width/2 + params.y
374 var rotate = function(degrees) {return 'rotate(' + degrees + ', ' + centerX + ', ' + centerY + ')'}
375
376 var rect = createElement('rect')
377 svg.appendChild(rect)
378 rect.setAttribute('width', 8)
379 rect.setAttribute('height', 46)
380 rect.setAttribute('fill', params.color)
381 rect.setAttribute('class', params.class)
382 rect.setAttribute('transform', rotate(45 * params.rot))
383 rect.setAttribute('x', centerX - 4)
384 rect.setAttribute('y', centerY - 22)
385
386 for (var i=0; i<params.count; i++) {
387 var arrowhead = createElement('polygon')
388 svg.appendChild(arrowhead)
389 var points = [
390 '-24 -15',
391 '-21.4 -8.6',
392 '0 -19',
393 '21.4 -8.6',
394 '24 -15',
395 '0 -27',
396 ]
397 var transform = rotate(45 * params.rot)
398 transform += ' translate(' + (params.width/2 + params.x) + ', ' + (params.height/2 + params.y + i*12) + ')'
399 arrowhead.setAttribute('transform', transform)
400 arrowhead.setAttribute('points', points.join(', '))
401 arrowhead.setAttribute('fill', params.color)
402 arrowhead.setAttribute('class', params.class)
403 }
404}
405
406function sizer(svg, params) {
407 var path = createElement('path')
408 svg.appendChild(path)
409 path.setAttribute('d',
410 'M -24 0 ' +
411 'a 24 24 0 0 0 24 -24 ' +
412 'a 24 24 0 0 0 24 24 ' +
413 'a 24 24 0 0 0 -24 24 ' +
414 'a 24 24 0 0 0 -24 -24 ' +
415 'z'
416 )
417 path.setAttribute('fill', params.color)
418 path.setAttribute('class', params.class)
419 path.setAttribute('transform', 'translate(' + (params.width/2 + params.x) + ', ' + (params.height/2 + params.y) + ')')
420}
421
422})