GUI -- Experimental Transform.
Change-Id: Iebf31a32707f2736b22102b6d4620db3489fe252
Showing
1 changed file
with
460 additions
and
0 deletions
web/gui/src/main/webapp/_sdh/oblique.html
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<!-- | ||
3 | + Testing transformations for transitioning between overhead and | ||
4 | + perspective projections of two layers. | ||
5 | + | ||
6 | + @author Simon Hunt | ||
7 | + --> | ||
8 | +<html> | ||
9 | +<head> | ||
10 | + <meta charset="utf-8"> | ||
11 | + <title>Layer Transformations</title> | ||
12 | + | ||
13 | + <script src="../tp/d3.js"></script> | ||
14 | + <script src="../tp/topojson.v1.min.js"></script> | ||
15 | + <script src="../tp/jquery-2.1.1.min.js"></script> | ||
16 | + | ||
17 | + <style> | ||
18 | + html, | ||
19 | + body { | ||
20 | + background-color: #ccc; | ||
21 | + font-family: Arial, Helvetica, sans-serif; | ||
22 | + font-size: 9pt; | ||
23 | + } | ||
24 | + | ||
25 | + svg { | ||
26 | + position: absolute; | ||
27 | + background-color: #fff; | ||
28 | + top: 30px; | ||
29 | + left: 60px; | ||
30 | + } | ||
31 | + | ||
32 | + svg text { | ||
33 | + font-size: 3pt; | ||
34 | + } | ||
35 | + | ||
36 | + </style> | ||
37 | +</head> | ||
38 | +<body> | ||
39 | + <svg width="1000px" height="600px" viewBox="0 0 160 120"></svg> | ||
40 | + | ||
41 | + <script> | ||
42 | + (function (){ | ||
43 | + | ||
44 | + // Configuration... | ||
45 | + var w = 160, | ||
46 | + h = 120, | ||
47 | + time = 1500; | ||
48 | + | ||
49 | + var pktData = [ | ||
50 | + [20,60,'a'], | ||
51 | + [60,20,'b'], | ||
52 | + [100,20,'c'], | ||
53 | + [140,60,'d'], | ||
54 | + [100,100,'e'], | ||
55 | + [60,100,'f'], | ||
56 | + [20,20,'w'], | ||
57 | + [140,20,'x'], | ||
58 | + [20,100,'y'], | ||
59 | + [140,100,'z'] | ||
60 | + ], | ||
61 | + optData = [ | ||
62 | + [40,40,'p'], | ||
63 | + [120,40,'q'], | ||
64 | + [120,80,'r'], | ||
65 | + [40,80,'s'], | ||
66 | + [20,20,'j'], | ||
67 | + [140,20,'k'], | ||
68 | + [20,100,'l'], | ||
69 | + [140,100,'m'] | ||
70 | + ], | ||
71 | + linkData = [ | ||
72 | + ['a','p'], | ||
73 | + ['p','b'], | ||
74 | + ['b','c'], | ||
75 | + ['c','q'], | ||
76 | + ['q','d'], | ||
77 | + ['d','r'], | ||
78 | + ['r','e'], | ||
79 | + ['e','f'], | ||
80 | + ['f','s'], | ||
81 | + ['s','a'], | ||
82 | + ['s','q'], | ||
83 | + ['p','r'], | ||
84 | + ['b','f'], | ||
85 | + ['c','e'], | ||
86 | + ['w','j'], | ||
87 | + ['x','k'], | ||
88 | + ['z','m'], | ||
89 | + ['y','l'] | ||
90 | + ]; | ||
91 | + | ||
92 | + // Transform parameters | ||
93 | + var tf = { | ||
94 | + tt: -.7, // x skew y factor | ||
95 | + xsk: -35, // x skew angle | ||
96 | + ysc: 0.5, // y scale | ||
97 | + ytr: 50, // y translate | ||
98 | + pad: 5 | ||
99 | + }, | ||
100 | + rectFill = { | ||
101 | + pkt: 'rgba(130,130,170,0.3)', | ||
102 | + opt: 'rgba(170,130,170,0.3)' | ||
103 | + }; | ||
104 | + | ||
105 | + // Internal state... | ||
106 | + var nodes = [], | ||
107 | + links = [], | ||
108 | + overhead = true, | ||
109 | + xffn; | ||
110 | + | ||
111 | + // D3/DOM magic... | ||
112 | + var svg = d3.select('svg'), | ||
113 | + nodeG, | ||
114 | + linkG, | ||
115 | + node, | ||
116 | + link, | ||
117 | + force, | ||
118 | + pktLayer, | ||
119 | + optLayer; | ||
120 | + | ||
121 | + | ||
122 | + // General functions ... | ||
123 | + function isF(f) { | ||
124 | + return $.isFunction(f) ? f : null; | ||
125 | + } | ||
126 | + | ||
127 | + function translate(x,y) { | ||
128 | + return 'translate(' + x + ',' + y + ')'; | ||
129 | + } | ||
130 | + | ||
131 | + function scale(x,y) { | ||
132 | + return 'scale(' + x + ',' + y + ')'; | ||
133 | + } | ||
134 | + function skewX(x) { | ||
135 | + return 'skewX(' + x + ')'; | ||
136 | + } | ||
137 | + | ||
138 | + | ||
139 | + // Key Bindings... | ||
140 | + var keyHandler = { | ||
141 | + T: transform | ||
142 | + }; | ||
143 | + | ||
144 | + function whatKey(code) { | ||
145 | + switch (code) { | ||
146 | + case 13: return 'enter'; | ||
147 | + case 16: return 'shift'; | ||
148 | + case 17: return 'ctrl'; | ||
149 | + case 18: return 'alt'; | ||
150 | + case 27: return 'esc'; | ||
151 | + case 32: return 'space'; | ||
152 | + case 37: return 'leftArrow'; | ||
153 | + case 38: return 'upArrow'; | ||
154 | + case 39: return 'rightArrow'; | ||
155 | + case 40: return 'downArrow'; | ||
156 | + case 91: return 'cmdLeft'; | ||
157 | + case 93: return 'cmdRight'; | ||
158 | + case 187: return 'equals'; | ||
159 | + case 189: return 'dash'; | ||
160 | + case 191: return 'slash'; | ||
161 | + default: | ||
162 | + if ((code >= 48 && code <= 57) || | ||
163 | + (code >= 65 && code <= 90)) { | ||
164 | + return String.fromCharCode(code); | ||
165 | + } else if (code >= 112 && code <= 123) { | ||
166 | + return 'F' + (code - 111); | ||
167 | + } | ||
168 | + return '.'; | ||
169 | + } | ||
170 | + } | ||
171 | + | ||
172 | + function keyIn() { | ||
173 | + var event = d3.event, | ||
174 | + keyCode = event.keyCode, | ||
175 | + key = whatKey(keyCode), | ||
176 | + fn = isF(keyHandler[key]); | ||
177 | + if (fn) { | ||
178 | + fn(key, keyCode, event); | ||
179 | + } | ||
180 | + } | ||
181 | + | ||
182 | + // Key events.... | ||
183 | + function transform() { | ||
184 | + overhead = !overhead; | ||
185 | + if (overhead) { | ||
186 | + toOverhead(); | ||
187 | + } else { | ||
188 | + toOblique(); | ||
189 | + } | ||
190 | + } | ||
191 | + | ||
192 | + function toOverhead() { | ||
193 | + xffn = null; | ||
194 | + hidePlane(pktLayer); | ||
195 | + hidePlane(optLayer); | ||
196 | + transitionNodes(); | ||
197 | + } | ||
198 | + | ||
199 | + function padBox(box, p) { | ||
200 | + box.x -= p; | ||
201 | + box.y -= p; | ||
202 | + box.width += p*2; | ||
203 | + box.height += p*2; | ||
204 | + } | ||
205 | + | ||
206 | + function toOblique() { | ||
207 | + var box = nodeG.node().getBBox(); | ||
208 | + padBox(box, tf.pad); | ||
209 | + | ||
210 | + xffn = function (xy, dir) { | ||
211 | + var x = xy.x + xy.y*tf.tt, | ||
212 | + y = xy.y*tf.ysc + tf.ysc*tf.ytr*dir; | ||
213 | + return { x: x, y: y}; | ||
214 | + }; | ||
215 | + | ||
216 | + showPlane(pktLayer, box, -1); | ||
217 | + showPlane(optLayer, box, 1); | ||
218 | + transitionNodes(); | ||
219 | + } | ||
220 | + | ||
221 | + function transitionNodes() { | ||
222 | + // note: turn off force layout while transitioning.. if it is on | ||
223 | +// force.stop(); | ||
224 | + | ||
225 | + if (xffn) { | ||
226 | + nodes.forEach(function (d) { | ||
227 | + var dir = d.type === 'pkt' ? -1 : 1, | ||
228 | + oldxy = {x: d.x, y: d.y}, | ||
229 | + coords = xffn(oldxy, dir); | ||
230 | + d.oldxy = oldxy; | ||
231 | + d.x = coords.x; | ||
232 | + d.y = coords.y; | ||
233 | + }); | ||
234 | + } else { | ||
235 | + nodes.forEach(function (d) { | ||
236 | + d.x = d.oldxy.x; | ||
237 | + d.y = d.oldxy.y; | ||
238 | + delete d.oldxy; | ||
239 | + }); | ||
240 | + } | ||
241 | + | ||
242 | + nodeG.selectAll('.node') | ||
243 | + .transition() | ||
244 | + .duration(time) | ||
245 | + .attr({ | ||
246 | + transform: function (d) { | ||
247 | + return translate(d.x, d.y); | ||
248 | + } | ||
249 | + }); | ||
250 | + | ||
251 | + linkG.selectAll('.link') | ||
252 | + .transition() | ||
253 | + .duration(time) | ||
254 | + .attr({ | ||
255 | + x1: function (d) { return d.source.x; }, | ||
256 | + y1: function (d) { return d.source.y; }, | ||
257 | + x2: function (d) { return d.target.x; }, | ||
258 | + y2: function (d) { return d.target.y; } | ||
259 | + }); | ||
260 | + } | ||
261 | + | ||
262 | + function showPlane(layer, box, dir) { | ||
263 | + layer.select('rect') | ||
264 | + .attr(box) | ||
265 | + .attr('opacity', 0) | ||
266 | + .transition() | ||
267 | + .duration(time) | ||
268 | + .attr('opacity', 1) | ||
269 | + .attr('transform', obliqueXform(dir)); | ||
270 | + } | ||
271 | + | ||
272 | + function hidePlane(layer) { | ||
273 | + var rect = layer.select('rect'); | ||
274 | + rect.transition() | ||
275 | + .duration(time) | ||
276 | + .attr('opacity', 0) | ||
277 | + .attr('transform', overheadXform()); | ||
278 | + | ||
279 | + } | ||
280 | + | ||
281 | + function obliqueXform(dir) { | ||
282 | + return scale(1, tf.ysc) + translate(0, dir * tf.ytr) + skewX(tf.xsk); | ||
283 | + } | ||
284 | + | ||
285 | + | ||
286 | + function overheadXform() { | ||
287 | + return skewX(0) + translate(0,0) + scale(1,1); | ||
288 | + } | ||
289 | + | ||
290 | + // Nodes and Links... | ||
291 | + function prepareNodes() { | ||
292 | + var hw = w/2, | ||
293 | + hh = h/2; | ||
294 | + | ||
295 | + function addNode(t, d) { | ||
296 | + nodes.push({ | ||
297 | + type: t, | ||
298 | + x: d[0] - hw, | ||
299 | + y: d[1] - hh, | ||
300 | + id: d[2], | ||
301 | + fixed: true | ||
302 | + }); | ||
303 | + } | ||
304 | + | ||
305 | + optData.forEach(function (d) { | ||
306 | + addNode('opt', d); | ||
307 | + }); | ||
308 | + pktData.forEach(function (d) { | ||
309 | + addNode('pkt', d); | ||
310 | + }); | ||
311 | + } | ||
312 | + | ||
313 | + function findNode(id) { | ||
314 | + for (var i=0,n=nodes.length; i<n; i++) { | ||
315 | + if (nodes[i].id === id) { | ||
316 | + return nodes[i]; | ||
317 | + } | ||
318 | + } | ||
319 | + return null; | ||
320 | + } | ||
321 | + | ||
322 | + function prepareLinks() { | ||
323 | + linkData.forEach(function (d) { | ||
324 | + var src = d[0], | ||
325 | + dst = d[1]; | ||
326 | + links.push({ | ||
327 | + id: src + '-' + dst, | ||
328 | + source: findNode(src), | ||
329 | + target: findNode(dst) | ||
330 | + }); | ||
331 | + }); | ||
332 | + | ||
333 | + } | ||
334 | + | ||
335 | + function updateNodes() { | ||
336 | + node = nodeG.selectAll('.node') | ||
337 | + .data(nodes, function (d) { return d.id; }); | ||
338 | + | ||
339 | + var entering = node.enter() | ||
340 | + .append('g').attr({ | ||
341 | + id: function (d) { return d.id; }, | ||
342 | + 'class': function (d) { return 'node ' + d.type; } | ||
343 | + }); | ||
344 | + | ||
345 | + entering.each(function (d) { | ||
346 | + var el = d3.select(this); | ||
347 | + d.el = el; | ||
348 | + | ||
349 | + el.append('rect').attr({ | ||
350 | + width: 5, | ||
351 | + height: 5, | ||
352 | + fill: function (d) { | ||
353 | + return d.type === 'pkt' ? '#669' : '#969'; | ||
354 | + }, | ||
355 | + rx: 1, | ||
356 | + transform: 'translate(-2.5,-2.5)' | ||
357 | + }); | ||
358 | + el.append('text') | ||
359 | + .text(d.id) | ||
360 | + .attr({ | ||
361 | + dy: '0.9em', | ||
362 | + 'text-anchor': 'middle', | ||
363 | + transform: 'translate(0,-2.5)', | ||
364 | + fill: 'white' | ||
365 | + }); | ||
366 | + }); | ||
367 | + } | ||
368 | + | ||
369 | + function updateLinks() { | ||
370 | + link = linkG.selectAll('.link') | ||
371 | + .data(links, function (d) { return d.id; }); | ||
372 | + | ||
373 | + var entering = link.enter() | ||
374 | + .append('line').attr({ | ||
375 | + id: function (d) { return d.id; }, | ||
376 | + class: 'link', | ||
377 | + stroke: '#888', | ||
378 | + 'stroke-width': 0.4, | ||
379 | + opacity: 0.7 | ||
380 | + }); | ||
381 | + | ||
382 | + entering.each(function (d) { | ||
383 | + d.el = d3.select(this); | ||
384 | + | ||
385 | + }); | ||
386 | + } | ||
387 | + | ||
388 | + function update() { | ||
389 | + updateNodes(); | ||
390 | + updateLinks(); | ||
391 | + } | ||
392 | + | ||
393 | + var ntick = 0; | ||
394 | + function tick() { | ||
395 | + console.log('tick ' + (++ntick)); | ||
396 | + node.attr({ | ||
397 | + transform: function (d) { return translate(d.x, d.y); } | ||
398 | + }); | ||
399 | + | ||
400 | + link.attr({ | ||
401 | + x1: function (d) { return d.source.x; }, | ||
402 | + y1: function (d) { return d.source.y; }, | ||
403 | + x2: function (d) { return d.target.x; }, | ||
404 | + y2: function (d) { return d.target.y; } | ||
405 | + }); | ||
406 | + } | ||
407 | + | ||
408 | + function setOrigin(/*varargs*/) { | ||
409 | + var i, n, g; | ||
410 | + for (i= 0,n=arguments.length; i< n; i++) { | ||
411 | + g = arguments[i]; | ||
412 | + g.attr('transform', translate(w/2, h/2)); | ||
413 | + } | ||
414 | + } | ||
415 | + | ||
416 | + function initLayers() { | ||
417 | + optLayer.attr('class', 'layer').append('rect') | ||
418 | + .attr('fill', rectFill.opt); | ||
419 | + pktLayer.attr('class', 'layer').append('rect') | ||
420 | + .attr('fill', rectFill.pkt); | ||
421 | + } | ||
422 | + | ||
423 | + function init() { | ||
424 | + svg.append('text') | ||
425 | + .text('Press the "T" key....') | ||
426 | + .attr({ dy: '1.2em', fill: '#999'}) | ||
427 | + .style('font-size', '2.4pt') | ||
428 | + .style('font-style', 'italic'); | ||
429 | + | ||
430 | + optLayer = svg.append('g').attr('id', 'optLayer'); | ||
431 | + pktLayer = svg.append('g').attr('id', 'pktLayer'); | ||
432 | + linkG = svg.append('g').attr('id', 'links'); | ||
433 | + nodeG = svg.append('g').attr('id', 'nodes'); | ||
434 | + | ||
435 | + setOrigin(optLayer, pktLayer, linkG, nodeG); | ||
436 | + | ||
437 | + node = nodeG.selectAll('.node'); | ||
438 | + link = linkG.selectAll('.link'); | ||
439 | + | ||
440 | + initLayers(); | ||
441 | + prepareNodes(); | ||
442 | + prepareLinks(); | ||
443 | + | ||
444 | + force = d3.layout.force() | ||
445 | + .size([w,h]) | ||
446 | + .nodes(nodes) | ||
447 | + .links(links) | ||
448 | + .gravity(0.4) | ||
449 | + .friction(0.7) | ||
450 | + .on('tick', tick); | ||
451 | + update(); | ||
452 | + tick(); | ||
453 | + d3.select('body').on('keydown', keyIn); | ||
454 | + } | ||
455 | + | ||
456 | + init(); | ||
457 | + })(); | ||
458 | + </script> | ||
459 | +</body> | ||
460 | +</html> |
-
Please register or login to post a comment