Skip to content

Commit c73e13c

Browse files
committed
Expand the grid size of the Multiply test automatically when needed
#52 Enlarge the grid size and reduce the tile size when a larger complexity is needed to lower the frame rate. Refactor the spiral iterator into a separate class. Keep calling its next() method to move to the next cell. When its isDone() returns true enlarge the iterator grid size and resize the already created tiles to fit in the new grid size. Add a new class for the roundedRect tile called "Tile". This class can handle the location, size and animation of the Tile.
1 parent f1c7edb commit c73e13c

File tree

2 files changed

+247
-94
lines changed

2 files changed

+247
-94
lines changed

MotionMark/resources/extensions.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,18 +265,41 @@ Point = Utilities.createClass(
265265
return this.x;
266266
},
267267

268+
// Used when the point object is used as a size object.
269+
set width(w)
270+
{
271+
this.x = w;
272+
},
273+
268274
// Used when the point object is used as a size object.
269275
get height()
270276
{
271277
return this.y;
272278
},
273279

280+
// Used when the point object is used as a size object.
281+
set height(h)
282+
{
283+
this.y = h;
284+
},
285+
274286
// Used when the point object is used as a size object.
275287
get center()
276288
{
277289
return new Point(this.x / 2, this.y / 2);
278290
},
279291

292+
// Used when the point object is used as a size object.
293+
area: function() {
294+
return this.x * this.y;
295+
},
296+
297+
// Used when the point object is used as a size object.
298+
expand(width, height) {
299+
this.x += width;
300+
this.y += height;
301+
},
302+
280303
str: function()
281304
{
282305
return "x = " + this.x + ", y = " + this.y;
@@ -320,6 +343,9 @@ Point = Utilities.createClass(
320343
}
321344
});
322345

346+
// FIXME: Add a seprate class for Size.
347+
let Size = Point;
348+
323349
Utilities.extendObject(Point, {
324350
zero: new Point(0, 0),
325351

MotionMark/tests/core/resources/multiply.js

Lines changed: 221 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -24,120 +24,247 @@
2424
*/
2525
(function() {
2626

27+
var Tile = Utilities.createClass(
28+
function(stage, coordinate)
29+
{
30+
this.stage = stage;
31+
this.coordinate = coordinate;
32+
33+
this.roundedRect = Utilities.createElement('div', {
34+
class: "div-" + Stage.randomInt(0, 5)
35+
}, stage.element);
36+
37+
this.distance = this.coordinate.length();
38+
this.step = Math.max(3, this.distance / 1.5);
39+
this.rotate = Stage.randomInt(0, 359);
40+
41+
this.move();
42+
this.resize();
43+
this.hide();
44+
}, {
45+
46+
move: function() {
47+
let tileSize = this.stage.tileSize;
48+
49+
let location = this.stage.size.center;
50+
location = location.add(this.coordinate.multiply(tileSize));
51+
location = location.subtract(tileSize.multiply(0.5));
52+
53+
this.roundedRect.style.left = location.x + 'px';
54+
this.roundedRect.style.top = location.y + 'px';
55+
},
56+
57+
resize: function() {
58+
let tileSize = this.stage.tileSize;
59+
60+
this.roundedRect.style.width = tileSize.width + 'px';
61+
this.roundedRect.style.height = tileSize.height + 'px';
62+
},
63+
64+
show: function() {
65+
this.roundedRect.style.display = "block";
66+
},
67+
68+
hide: function() {
69+
this.roundedRect.style.display = "none";
70+
},
71+
72+
backgroundColor: function() {
73+
let influence = Math.max(.01, 1 - (this.distance * this.stage.distanceFactor));
74+
let l = this.stage.l * Math.tan(influence);
75+
return this.stage.hslPrefix + l + "%," + influence + ")";
76+
},
77+
78+
animate: function(timestamp) {
79+
this.rotate += this.step;
80+
this.roundedRect.style.transform = "rotate(" + this.rotate + "deg)";
81+
this.roundedRect.style.backgroundColor = this.backgroundColor();
82+
}
83+
});
84+
85+
var SpiralIterator = Utilities.createClass(
86+
function(gridSize)
87+
{
88+
this.gridSize = gridSize;
89+
this.current = Point.zero;
90+
this.direction = this.directions.right;
91+
this.size = new Size(1, 1);
92+
this.count = 0;
93+
}, {
94+
95+
directions: {
96+
top: 0,
97+
left: 1,
98+
bottom: 2,
99+
right: 3
100+
},
101+
102+
moves: [
103+
new Size(0, -1), // top
104+
new Size(-1, 0), // left
105+
new Size(0, +1), // bottom
106+
new Size(+1, 0) // right
107+
],
108+
109+
isDone: function() {
110+
return this.count >= this.gridSize.area();
111+
},
112+
113+
next: function() {
114+
++this.count;
115+
116+
if (this.isDone())
117+
return;
118+
119+
let direction = this.direction;
120+
let move = this.moves[direction];
121+
122+
if (Math.abs(this.current.x) == Math.abs(this.current.y)) {
123+
// Turn left.
124+
direction = (direction + 1) % 4;
125+
126+
if (this.current.x >= 0 && this.current.y >= 0) {
127+
if (this.size.width < Math.min(this.gridSize.width, this.gridSize.height))
128+
this.size.expand(2, 2);
129+
else if (this.size.width < this.gridSize.width)
130+
++this.size.width;
131+
132+
move = this.moves[this.directions.right];
133+
} else
134+
move = this.moves[direction];
135+
}
136+
137+
if (this.count < this.size.area()) {
138+
this.current = this.current.add(move);
139+
this.direction = direction;
140+
return;
141+
}
142+
143+
// Make a U-turn.
144+
this.direction = (this.direction + 1) % 4;
145+
146+
if (this.direction == this.directions.left || this.direction == this.directions.right)
147+
this.current = this.current.add(this.moves[this.direction].multiply(this.size.width++));
148+
else
149+
this.current = this.current.add(this.moves[this.direction].multiply(this.size.height++));
150+
151+
this.direction = (this.direction + 1) % 4;
152+
}
153+
});
154+
27155
var MultiplyStage = Utilities.createSubclass(Stage,
28156
function()
29157
{
30158
Stage.call(this);
31159
this.tiles = [];
32-
this._offsetIndex = 0;
160+
this.activeLength = 0;
33161
}, {
34162

35-
visibleCSS: [
36-
["display", "none", "block"]
37-
],
38-
totalRows: 68,
163+
initialRowsCount: 69,
164+
extraExpandRows: 16,
39165

40-
initialize: function(benchmark, options)
41-
{
166+
initialize: function(benchmark, options) {
42167
Stage.prototype.initialize.call(this, benchmark, options);
43-
var tileSize = Math.round(this.size.height / this.totalRows);
44-
if (options.visibleCSS)
45-
this.visibleCSS = options.visibleCSS;
46-
47-
// Fill the scene with elements
48-
var x = Math.round((this.size.width - tileSize) / 2);
49-
var y = Math.round((this.size.height - tileSize) / 2);
50-
var tileStride = tileSize;
51-
var direction = 0;
52-
var spiralCounter = 2;
53-
var nextIndex = 1;
54-
var maxSide = Math.floor(y / tileStride) * 2 + 1;
55-
this._centerSpiralCount = maxSide * maxSide;
56-
for (var i = 0; i < this._centerSpiralCount; ++i) {
57-
this._addTile(x, y, tileSize, Stage.randomInt(0, 359));
58-
59-
if (i == nextIndex) {
60-
direction = (direction + 1) % 4;
61-
spiralCounter++;
62-
nextIndex += spiralCounter >> 1;
63-
}
64-
if (direction == 0)
65-
x += tileStride;
66-
else if (direction == 1)
67-
y -= tileStride;
68-
else if (direction == 2)
69-
x -= tileStride;
70-
else
71-
y += tileStride;
72-
}
73168

74-
this._sidePanelCount = maxSide * Math.floor((this.size.width - x) / tileStride) * 2;
75-
for (var i = 0; i < this._sidePanelCount; ++i) {
76-
var sideX = x + Math.floor(Math.floor(i / maxSide) / 2) * tileStride;
77-
var sideY = y - tileStride * (i % maxSide);
169+
this.rowsCount = this.initialRowsCount;
78170

79-
if (Math.floor(i / maxSide) % 2 == 1)
80-
sideX = this.size.width - sideX - tileSize + 1;
81-
this._addTile(sideX, sideY, tileSize, Stage.randomInt(0, 359));
82-
}
171+
this.calculateTileSize();
172+
this.calculateGridSize();
173+
this.iterator = new SpiralIterator(this.tileGrid);
174+
175+
while (!this.iterator.isDone())
176+
this.tiles.push(this.createTile());
83177
},
84178

85-
_addTile: function(x, y, tileSize, rotateDeg)
86-
{
87-
var tile = Utilities.createElement("div", { class: "div-" + Stage.randomInt(0,6) }, this.element);
88-
var halfTileSize = tileSize / 2;
89-
tile.style.left = x + 'px';
90-
tile.style.top = y + 'px';
91-
tile.style.width = tileSize + 'px';
92-
tile.style.height = tileSize + 'px';
93-
var visibleCSS = this.visibleCSS[this.tiles.length % this.visibleCSS.length];
94-
tile.style[visibleCSS[0]] = visibleCSS[1];
95-
96-
var distance = 1 / tileSize * this.size.multiply(0.5).subtract(new Point(x + halfTileSize, y + halfTileSize)).length();
97-
this.tiles.push({
98-
element: tile,
99-
rotate: rotateDeg,
100-
step: Math.max(3, distance / 1.5),
101-
distance: distance,
102-
active: false,
103-
visibleCSS: visibleCSS,
104-
});
105-
},
106-
107-
complexity: function()
108-
{
109-
return this._offsetIndex;
179+
calculateTileSize: function() {
180+
let tileSide = Math.round(this.size.height / this.rowsCount);
181+
this.tileSize = new Size(tileSide, tileSide);
110182
},
111183

112-
tune: function(count)
113-
{
114-
this._offsetIndex = Math.max(0, Math.min(this._offsetIndex + count, this.tiles.length));
115-
this._distanceFactor = 1.5 * (1 - 0.5 * Math.max(this._offsetIndex - this._centerSpiralCount, 0) / this._sidePanelCount) / Math.sqrt(this._offsetIndex);
184+
calculateGridSize: function() {
185+
let columnsCount = Math.floor(this.size.width / this.tileSize.width);
186+
if (columnsCount % 2 == 0)
187+
--columnsCount;
188+
this.tileGrid = new Size(columnsCount, this.rowsCount);
116189
},
117190

118-
animate: function()
119-
{
120-
var progress = this._benchmark.timestamp % 10000 / 10000;
121-
var bounceProgress = Math.sin(2 * Math.abs( 0.5 - progress));
122-
var l = Utilities.lerp(bounceProgress, 20, 50);
123-
var hslPrefix = "hsla(" + Utilities.lerp(progress, 0, 360) + ",100%,";
124-
125-
for (var i = 0; i < this._offsetIndex; ++i) {
126-
var tile = this.tiles[i];
127-
tile.active = true;
128-
tile.element.style[tile.visibleCSS[0]] = tile.visibleCSS[2];
129-
tile.rotate += tile.step;
130-
tile.element.style.transform = "rotate(" + tile.rotate + "deg)";
131-
132-
var influence = Math.max(.01, 1 - (tile.distance * this._distanceFactor));
133-
tile.element.style.backgroundColor = hslPrefix + l * Math.tan(influence / 1.25) + "%," + influence + ")";
191+
expandGrid: function(extraRows) {
192+
this.rowsCount += this.extraExpandRows;
193+
194+
this.calculateTileSize();
195+
this.calculateGridSize();
196+
this.iterator.gridSize = this.tileGrid;
197+
198+
for (let tile of this.tiles) {
199+
tile.move();
200+
tile.resize();
134201
}
202+
},
203+
204+
createTile: function() {
205+
let tile = new Tile(this, this.iterator.current);
135206

136-
for (var i = this._offsetIndex; i < this.tiles.length && this.tiles[i].active; ++i) {
137-
var tile = this.tiles[i];
138-
tile.active = false;
139-
tile.element.style[tile.visibleCSS[0]] = tile.visibleCSS[1];
207+
if (this.iterator.isDone())
208+
this.expandGrid();
209+
210+
this.iterator.next();
211+
return tile;
212+
},
213+
214+
complexity: function() {
215+
return this.activeLength;
216+
},
217+
218+
activeTiles: function() {
219+
return this.tiles.slice(0, this.activeLength);
220+
},
221+
222+
inactiveTiles: function(end) {
223+
return this.tiles.slice(this.activeLength, end);
224+
},
225+
226+
reusableTune: function(count) {
227+
if (count == 0)
228+
return;
229+
230+
if (count < 0) {
231+
this.activeLength = Math.max(this.activeLength + count, 0);
232+
for (var i = this.activeLength; i < this.tiles.length; ++i)
233+
this.tiles[i].hide();
234+
return;
140235
}
236+
237+
let inactiveTiles = this.inactiveTiles(this.activeLength + count);
238+
for (let tile of inactiveTiles)
239+
tile.show();
240+
241+
for (let i = inactiveTiles.length; i < count; ++i)
242+
this.tiles.push(this.createTile());
243+
244+
this.activeLength += count;
245+
},
246+
247+
reusableAnimate: function(timestamp) {
248+
for (let tile of this.activeTiles())
249+
tile.animate(timestamp);
250+
},
251+
252+
tune: function(count) {
253+
this.reusableTune(count);
254+
255+
let centerSpiralCount = this.tileGrid.height * this.tileGrid.height;
256+
let sidePanelCount = this.tileGrid.area() - centerSpiralCount;
257+
let activeSidePanelCount = Math.max(this.activeLength - centerSpiralCount, 0);
258+
this.distanceFactor = 1.5 * (1 - 0.5 * activeSidePanelCount / sidePanelCount) / Math.sqrt(this.activeLength);
259+
},
260+
261+
animate: function(timestamp) {
262+
let progress = timestamp % 10000 / 10000;
263+
let bounceProgress = Math.sin(2 * Math.abs(0.5 - progress));
264+
this.l = Utilities.lerp(bounceProgress, 20, 50);
265+
this.hslPrefix = "hsla(" + Utilities.lerp(progress, 0, 360) + ",100%,";
266+
267+
this.reusableAnimate(timestamp);
141268
}
142269
});
143270

0 commit comments

Comments
 (0)