Skip to content

Commit

Permalink
Expand the grid size of the Multiply test automatically when needed
Browse files Browse the repository at this point in the history
#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.
  • Loading branch information
shallawa committed Apr 26, 2024
1 parent f1c7edb commit c73e13c
Show file tree
Hide file tree
Showing 2 changed files with 247 additions and 94 deletions.
26 changes: 26 additions & 0 deletions MotionMark/resources/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,18 +265,41 @@ Point = Utilities.createClass(
return this.x;
},

// Used when the point object is used as a size object.
set width(w)
{
this.x = w;
},

// Used when the point object is used as a size object.
get height()
{
return this.y;
},

// Used when the point object is used as a size object.
set height(h)
{
this.y = h;
},

// Used when the point object is used as a size object.
get center()
{
return new Point(this.x / 2, this.y / 2);
},

// Used when the point object is used as a size object.
area: function() {
return this.x * this.y;
},

// Used when the point object is used as a size object.
expand(width, height) {
this.x += width;
this.y += height;
},

str: function()
{
return "x = " + this.x + ", y = " + this.y;
Expand Down Expand Up @@ -320,6 +343,9 @@ Point = Utilities.createClass(
}
});

// FIXME: Add a seprate class for Size.
let Size = Point;

Utilities.extendObject(Point, {
zero: new Point(0, 0),

Expand Down
315 changes: 221 additions & 94 deletions MotionMark/tests/core/resources/multiply.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,120 +24,247 @@
*/
(function() {

var Tile = Utilities.createClass(
function(stage, coordinate)
{
this.stage = stage;
this.coordinate = coordinate;

this.roundedRect = Utilities.createElement('div', {
class: "div-" + Stage.randomInt(0, 5)
}, stage.element);

this.distance = this.coordinate.length();
this.step = Math.max(3, this.distance / 1.5);
this.rotate = Stage.randomInt(0, 359);

this.move();
this.resize();
this.hide();
}, {

move: function() {
let tileSize = this.stage.tileSize;

let location = this.stage.size.center;
location = location.add(this.coordinate.multiply(tileSize));
location = location.subtract(tileSize.multiply(0.5));

this.roundedRect.style.left = location.x + 'px';
this.roundedRect.style.top = location.y + 'px';
},

resize: function() {
let tileSize = this.stage.tileSize;

this.roundedRect.style.width = tileSize.width + 'px';
this.roundedRect.style.height = tileSize.height + 'px';
},

show: function() {
this.roundedRect.style.display = "block";
},

hide: function() {
this.roundedRect.style.display = "none";
},

backgroundColor: function() {
let influence = Math.max(.01, 1 - (this.distance * this.stage.distanceFactor));
let l = this.stage.l * Math.tan(influence);
return this.stage.hslPrefix + l + "%," + influence + ")";
},

animate: function(timestamp) {
this.rotate += this.step;
this.roundedRect.style.transform = "rotate(" + this.rotate + "deg)";
this.roundedRect.style.backgroundColor = this.backgroundColor();
}
});

var SpiralIterator = Utilities.createClass(
function(gridSize)
{
this.gridSize = gridSize;
this.current = Point.zero;
this.direction = this.directions.right;
this.size = new Size(1, 1);
this.count = 0;
}, {

directions: {
top: 0,
left: 1,
bottom: 2,
right: 3
},

moves: [
new Size(0, -1), // top
new Size(-1, 0), // left
new Size(0, +1), // bottom
new Size(+1, 0) // right
],

isDone: function() {
return this.count >= this.gridSize.area();
},

next: function() {
++this.count;

if (this.isDone())
return;

let direction = this.direction;
let move = this.moves[direction];

if (Math.abs(this.current.x) == Math.abs(this.current.y)) {
// Turn left.
direction = (direction + 1) % 4;

if (this.current.x >= 0 && this.current.y >= 0) {
if (this.size.width < Math.min(this.gridSize.width, this.gridSize.height))
this.size.expand(2, 2);
else if (this.size.width < this.gridSize.width)
++this.size.width;

move = this.moves[this.directions.right];
} else
move = this.moves[direction];
}

if (this.count < this.size.area()) {
this.current = this.current.add(move);
this.direction = direction;
return;
}

// Make a U-turn.
this.direction = (this.direction + 1) % 4;

if (this.direction == this.directions.left || this.direction == this.directions.right)
this.current = this.current.add(this.moves[this.direction].multiply(this.size.width++));
else
this.current = this.current.add(this.moves[this.direction].multiply(this.size.height++));

this.direction = (this.direction + 1) % 4;
}
});

var MultiplyStage = Utilities.createSubclass(Stage,
function()
{
Stage.call(this);
this.tiles = [];
this._offsetIndex = 0;
this.activeLength = 0;
}, {

visibleCSS: [
["display", "none", "block"]
],
totalRows: 68,
initialRowsCount: 69,
extraExpandRows: 16,

initialize: function(benchmark, options)
{
initialize: function(benchmark, options) {
Stage.prototype.initialize.call(this, benchmark, options);
var tileSize = Math.round(this.size.height / this.totalRows);
if (options.visibleCSS)
this.visibleCSS = options.visibleCSS;

// Fill the scene with elements
var x = Math.round((this.size.width - tileSize) / 2);
var y = Math.round((this.size.height - tileSize) / 2);
var tileStride = tileSize;
var direction = 0;
var spiralCounter = 2;
var nextIndex = 1;
var maxSide = Math.floor(y / tileStride) * 2 + 1;
this._centerSpiralCount = maxSide * maxSide;
for (var i = 0; i < this._centerSpiralCount; ++i) {
this._addTile(x, y, tileSize, Stage.randomInt(0, 359));

if (i == nextIndex) {
direction = (direction + 1) % 4;
spiralCounter++;
nextIndex += spiralCounter >> 1;
}
if (direction == 0)
x += tileStride;
else if (direction == 1)
y -= tileStride;
else if (direction == 2)
x -= tileStride;
else
y += tileStride;
}

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

if (Math.floor(i / maxSide) % 2 == 1)
sideX = this.size.width - sideX - tileSize + 1;
this._addTile(sideX, sideY, tileSize, Stage.randomInt(0, 359));
}
this.calculateTileSize();
this.calculateGridSize();
this.iterator = new SpiralIterator(this.tileGrid);

while (!this.iterator.isDone())
this.tiles.push(this.createTile());
},

_addTile: function(x, y, tileSize, rotateDeg)
{
var tile = Utilities.createElement("div", { class: "div-" + Stage.randomInt(0,6) }, this.element);
var halfTileSize = tileSize / 2;
tile.style.left = x + 'px';
tile.style.top = y + 'px';
tile.style.width = tileSize + 'px';
tile.style.height = tileSize + 'px';
var visibleCSS = this.visibleCSS[this.tiles.length % this.visibleCSS.length];
tile.style[visibleCSS[0]] = visibleCSS[1];

var distance = 1 / tileSize * this.size.multiply(0.5).subtract(new Point(x + halfTileSize, y + halfTileSize)).length();
this.tiles.push({
element: tile,
rotate: rotateDeg,
step: Math.max(3, distance / 1.5),
distance: distance,
active: false,
visibleCSS: visibleCSS,
});
},

complexity: function()
{
return this._offsetIndex;
calculateTileSize: function() {
let tileSide = Math.round(this.size.height / this.rowsCount);
this.tileSize = new Size(tileSide, tileSide);
},

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

animate: function()
{
var progress = this._benchmark.timestamp % 10000 / 10000;
var bounceProgress = Math.sin(2 * Math.abs( 0.5 - progress));
var l = Utilities.lerp(bounceProgress, 20, 50);
var hslPrefix = "hsla(" + Utilities.lerp(progress, 0, 360) + ",100%,";

for (var i = 0; i < this._offsetIndex; ++i) {
var tile = this.tiles[i];
tile.active = true;
tile.element.style[tile.visibleCSS[0]] = tile.visibleCSS[2];
tile.rotate += tile.step;
tile.element.style.transform = "rotate(" + tile.rotate + "deg)";

var influence = Math.max(.01, 1 - (tile.distance * this._distanceFactor));
tile.element.style.backgroundColor = hslPrefix + l * Math.tan(influence / 1.25) + "%," + influence + ")";
expandGrid: function(extraRows) {
this.rowsCount += this.extraExpandRows;

this.calculateTileSize();
this.calculateGridSize();
this.iterator.gridSize = this.tileGrid;

for (let tile of this.tiles) {
tile.move();
tile.resize();
}
},

createTile: function() {
let tile = new Tile(this, this.iterator.current);

for (var i = this._offsetIndex; i < this.tiles.length && this.tiles[i].active; ++i) {
var tile = this.tiles[i];
tile.active = false;
tile.element.style[tile.visibleCSS[0]] = tile.visibleCSS[1];
if (this.iterator.isDone())
this.expandGrid();

this.iterator.next();
return tile;
},

complexity: function() {
return this.activeLength;
},

activeTiles: function() {
return this.tiles.slice(0, this.activeLength);
},

inactiveTiles: function(end) {
return this.tiles.slice(this.activeLength, end);
},

reusableTune: function(count) {
if (count == 0)
return;

if (count < 0) {
this.activeLength = Math.max(this.activeLength + count, 0);
for (var i = this.activeLength; i < this.tiles.length; ++i)
this.tiles[i].hide();
return;
}

let inactiveTiles = this.inactiveTiles(this.activeLength + count);
for (let tile of inactiveTiles)
tile.show();

for (let i = inactiveTiles.length; i < count; ++i)
this.tiles.push(this.createTile());

this.activeLength += count;
},

reusableAnimate: function(timestamp) {
for (let tile of this.activeTiles())
tile.animate(timestamp);
},

tune: function(count) {
this.reusableTune(count);

let centerSpiralCount = this.tileGrid.height * this.tileGrid.height;
let sidePanelCount = this.tileGrid.area() - centerSpiralCount;
let activeSidePanelCount = Math.max(this.activeLength - centerSpiralCount, 0);
this.distanceFactor = 1.5 * (1 - 0.5 * activeSidePanelCount / sidePanelCount) / Math.sqrt(this.activeLength);
},

animate: function(timestamp) {
let progress = timestamp % 10000 / 10000;
let bounceProgress = Math.sin(2 * Math.abs(0.5 - progress));
this.l = Utilities.lerp(bounceProgress, 20, 50);
this.hslPrefix = "hsla(" + Utilities.lerp(progress, 0, 360) + ",100%,";

this.reusableAnimate(timestamp);
}
});

Expand Down

0 comments on commit c73e13c

Please sign in to comment.