Simon Hunt

GUI -- Reworking sample view to be more interesting.

...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
15 */ 15 */
16 16
17 /* 17 /*
18 - Alternate Sample module file to illustrate framework integration. 18 + Sample module file to illustrate framework integration.
19 19
20 @author Simon Hunt 20 @author Simon Hunt
21 */ 21 */
...@@ -23,9 +23,24 @@ ...@@ -23,9 +23,24 @@
23 (function (onos) { 23 (function (onos) {
24 'use strict'; 24 'use strict';
25 25
26 - var svg; 26 + var pi = Math.PI,
27 - 27 + svg,
28 - 28 + dotG,
29 + nCircles = 12,
30 + circleData = [],
31 + dotId = 0,
32 + angle = 360 / nCircles,
33 + baseAngle = -90 - angle,
34 + groupRadius = 120,
35 + dotRadius = 24,
36 + dotMoveMs = 800,
37 + dotAppearMs = 300,
38 + dotEase = 'elastic',
39 + colorScale = d3.scale.linear()
40 + .domain([-pi/2, 2*pi/4, 3*pi/2])
41 + .range(['green', 'goldenrod', 'blue']);
42 +
43 + // set the size of the SVG layer to match that of the view
29 function sizeSvg(view) { 44 function sizeSvg(view) {
30 svg.attr({ 45 svg.attr({
31 width: view.width(), 46 width: view.width(),
...@@ -33,63 +48,169 @@ ...@@ -33,63 +48,169 @@
33 }); 48 });
34 } 49 }
35 50
36 - // NOTE: view is a view-token data structure:
37 - // {
38 - // vid: 'view-id',
39 - // nid: 'nav-id',
40 - // $div: ... // d3 selection of dom view div.
41 - // }
42 -
43 // gets invoked only the first time the view is loaded 51 // gets invoked only the first time the view is loaded
44 function preload(view, ctx) { 52 function preload(view, ctx) {
53 + // prepare our SVG layer...
45 svg = view.$div.append('svg'); 54 svg = view.$div.append('svg');
46 sizeSvg(view); 55 sizeSvg(view);
56 + dotG = svg.append('g').attr('id', 'dots');
47 } 57 }
48 58
59 + // gets invoked just before our view is loaded
49 function reset(view) { 60 function reset(view) {
50 - // clear our svg of all objects 61 + // clear dot group and reset circle data
51 - svg.html(''); 62 + dotG.html('');
63 + circleData = [];
64 + // also clear text, if any
65 + svg.selectAll('text').remove();
52 } 66 }
53 67
54 - function load(view, ctx) { 68 + function updateCirclePositions(view, addNew) {
55 - var fill = 'red', 69 + var w = view.width(),
56 - stroke = 'black', 70 + h = view.height(),
57 - ctxText = ctx ? 'Context is "' + ctx + '"' : 'No Context'; 71 + ox = w / 2,
72 + oy = h / 2;
73 +
74 + // reposition existing dots
75 + circleData.forEach(function (c, i) {
76 + var inc = addNew ? 1 : 0,
77 + theta = ((i + inc) * angle + baseAngle) * pi/180,
78 + dx = Math.cos(theta) * groupRadius,
79 + dy = Math.sin(theta) * groupRadius,
80 + x = ox + dx,
81 + y = oy + dy;
82 + if (!addNew && i === 0) {
83 + x = ox;
84 + y = oy;
85 + }
86 + c.cx = x;
87 + c.cy = y;
88 + c.rgb = colorScale(theta);
89 + });
90 +
91 + if (addNew) {
92 + // introduce a new dot
93 + circleData.unshift({
94 + cx: ox,
95 + cy: oy,
96 + id: dotId++
97 + });
98 + }
99 +
100 + // +1 to account for the circle in the center..
101 + if (circleData.length > nCircles + 1) {
102 + circleData.splice(nCircles + 1, 1);
103 + }
104 + }
58 105
59 - svg.append('circle') 106 + function doCircles(view) {
107 + var ox = view.width() / 2,
108 + oy = view.height() / 2,
109 + stroke = 'black',
110 + fill = 'red',
111 + hoverFill = 'magenta';
112 +
113 + // move existing circles, and add a new one
114 + updateCirclePositions(view, true);
115 +
116 + var circ = dotG.selectAll('circle')
117 + .data(circleData, function (d) { return d.id; });
118 +
119 + // operate on existing elements
120 + circ.on('mouseover', null)
121 + .on('mouseout', null)
122 + .on('click', null)
123 + .transition()
124 + .duration(dotMoveMs)
125 + .ease(dotEase)
60 .attr({ 126 .attr({
61 - cx: view.width() / 2, 127 + cx: function (d) { return d.cx; },
62 - cy: view.height() / 2, 128 + cy: function (d) { return d.cy; }
63 - r: 30
64 }) 129 })
65 .style({ 130 .style({
66 - fill: fill, 131 + cursor: 'default',
67 - stroke: stroke, 132 + fill: function (d) { return d.rgb; }
68 - 'stroke-width': 3.5
69 }); 133 });
70 134
71 - svg.append('text') 135 + // operate on entering elements
72 - .text(ctxText) 136 + circ.enter()
137 + .append('circle')
73 .attr({ 138 .attr({
74 - x: 20, 139 + cx: function (d) { return d.cx; },
75 - y: '1.5em' 140 + cy: function (d) { return d.cy; },
141 + r: 0
76 }) 142 })
77 .style({ 143 .style({
78 - fill: 'darkgreen', 144 + fill: fill,
79 - 'font-size': '20pt' 145 + stroke: stroke,
80 - }); 146 + 'stroke-width': 3.5,
147 + cursor: 'pointer',
148 + opacity: 0
149 + })
150 + .on('mouseover', function (d) {
151 + d3.select(this).style('fill', hoverFill);
152 + })
153 + .on('mouseout', function (d) {
154 + d3.select(this).style('fill', fill);
155 + })
156 + .on('click', function (d) {
157 + setTimeout(function() {
158 + doCircles(view, true);
159 + }, 10);
160 + })
161 + .transition()
162 + .delay(dotMoveMs)
163 + .duration(dotAppearMs)
164 + .attr('r', dotRadius)
165 + .style('opacity', 1);
166 +
167 + // operate on exiting elements
168 + circ.exit()
169 + .transition()
170 + .duration(750)
171 + .style('opacity', 0)
172 + .attr({
173 + cx: ox,
174 + cy: oy,
175 + r: groupRadius - dotRadius
176 + })
177 + .remove();
178 + }
179 +
180 + function load(view, ctx) {
181 + var ctxText = ctx ? 'Context is "' + ctx + '"' : '';
182 +
183 + // display our view context
184 + if (ctxText) {
185 + svg.append('text')
186 + .text(ctxText)
187 + .attr({
188 + x: 20,
189 + y: '1.5em'
190 + })
191 + .style({
192 + fill: 'darkgreen',
193 + 'font-size': '20pt'
194 + });
195 + }
196 +
197 + doCircles(view);
81 } 198 }
82 199
83 function resize(view, ctx) { 200 function resize(view, ctx) {
84 sizeSvg(view); 201 sizeSvg(view);
85 - svg.selectAll('circle') 202 + updateCirclePositions(view);
86 - .attr({ 203 +
87 - cx: view.width() / 2, 204 + // move exiting dots into new positions, relative to view size
88 - cy: view.height() / 2 205 + var circ = dotG.selectAll('circle')
206 + .data(circleData, function (d) { return d.id; });
207 + circ.attr({
208 + cx: function (d) { return d.cx; },
209 + cy: function (d) { return d.cy; }
89 }); 210 });
90 } 211 }
91 212
92 - // == register views here, with links to lifecycle callbacks 213 + // == register our view here, with links to lifecycle callbacks
93 214
94 onos.ui.addView('sample', { 215 onos.ui.addView('sample', {
95 preload: preload, 216 preload: preload,
...@@ -98,5 +219,4 @@ ...@@ -98,5 +219,4 @@
98 resize: resize 219 resize: resize
99 }); 220 });
100 221
101 -
102 }(ONOS)); 222 }(ONOS));
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
15 */ 15 */
16 16
17 /* 17 /*
18 - Sample module file to illustrate framework integration. 18 + Alternate sample module file to illustrate framework integration.
19 19
20 @author Simon Hunt 20 @author Simon Hunt
21 */ 21 */
...@@ -33,13 +33,6 @@ ...@@ -33,13 +33,6 @@
33 }); 33 });
34 } 34 }
35 35
36 - // NOTE: view is a view-token data structure:
37 - // {
38 - // vid: 'view-id',
39 - // nid: 'nav-id',
40 - // $div: ... // d3 selection of dom view div.
41 - // }
42 -
43 // gets invoked only the first time the view is loaded 36 // gets invoked only the first time the view is loaded
44 function preload(view, ctx) { 37 function preload(view, ctx) {
45 svg = view.$div.append('svg'); 38 svg = view.$div.append('svg');
...@@ -52,8 +45,8 @@ ...@@ -52,8 +45,8 @@
52 } 45 }
53 46
54 function load(view, ctx) { 47 function load(view, ctx) {
55 - var fill = 'blue', 48 + var fill = 'teal',
56 - stroke = 'grey'; 49 + stroke = 'black';
57 50
58 svg.append('circle') 51 svg.append('circle')
59 .attr({ 52 .attr({
...@@ -64,7 +57,8 @@ ...@@ -64,7 +57,8 @@
64 .style({ 57 .style({
65 fill: fill, 58 fill: fill,
66 stroke: stroke, 59 stroke: stroke,
67 - 'stroke-width': 3.5 60 + 'stroke-width': 1.5,
61 + opacity: 0.5
68 }); 62 });
69 } 63 }
70 64
......