| Index: third_party/WebKit/ManualTests/compositor-worker/physics/physics.js
|
| diff --git a/third_party/WebKit/ManualTests/compositor-worker/physics/physics.js b/third_party/WebKit/ManualTests/compositor-worker/physics/physics.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..8b0b1e625ef1f36dffdc1190e228cca8db6fede5
|
| --- /dev/null
|
| +++ b/third_party/WebKit/ManualTests/compositor-worker/physics/physics.js
|
| @@ -0,0 +1,489 @@
|
| +(function(scope) {
|
| + "use strict";
|
| +
|
| + scope.isMain = function() {
|
| + return scope.window;
|
| + };
|
| +
|
| + var spheres = [];
|
| + var foils = [];
|
| + var gravity = 0.022;
|
| + var numBlurElements = 1;
|
| + var epsilon = 0.01;
|
| + var viewportHeight = 0;
|
| + var logo = null;
|
| + var janking = false;
|
| + var logoAngle = 0;
|
| + var logoSpeed = 5;
|
| +
|
| + function lerp(a, b, t) {
|
| + return (1 - t) * a + t * b;
|
| + }
|
| +
|
| + function setPosition(sphere, dt) {
|
| + var dx = sphere.dx + (sphere.vx * dt);
|
| + var dy = sphere.dy + (sphere.vy * dt);
|
| + for (var i = 0; i < numBlurElements; ++i) {
|
| + var t = i / (numBlurElements - 1 || 1);
|
| + var lx = lerp(sphere.dx, dx, 1 - t);
|
| + var ly = lerp(sphere.dy, dy, 1 - t);
|
| + if (isMain()) {
|
| + sphere.blurElements[i].style.transform =
|
| + "translate3d(" + lx + "px, " + ly + "px, 0px)";
|
| + } else {
|
| + var transform = sphere.blurElements[i].transform;
|
| + transform.m41 = lx;
|
| + transform.m42 = ly;
|
| + sphere.blurElements[i].transform = transform;
|
| + }
|
| + }
|
| + sphere.dx = dx;
|
| + sphere.dy = dy;
|
| + }
|
| +
|
| + function updatePositions(dt) {
|
| + for (var i = 0; i < spheres.length; ++i)
|
| + setPosition(spheres[i], dt);
|
| + }
|
| +
|
| + function applyGravity(dt) {
|
| + for (var i = 0; i < spheres.length; ++i)
|
| + spheres[i].vy += gravity * dt;
|
| + }
|
| +
|
| + var re = /matrix\(.*, ([0-9\.]+)\)/;
|
| + function getAnimatedPosition(foil) {
|
| + if (isMain()) {
|
| + var match = window.getComputedStyle(foil.actor, null).webkitTransform.match(re);
|
| + return match ? parseFloat(match[1]) : 0;
|
| + } else {
|
| + return foil.actor.transform.m42;
|
| + }
|
| + }
|
| +
|
| + function signedDistanceToLine(x1, y1, nx, ny, cx, cy) {
|
| + return (cx - x1) * nx + (cy - y1) * ny;
|
| + }
|
| +
|
| + function normalizeBinormalComponent(component) {
|
| + if (component < epsilon && component > -epsilon)
|
| + return 0;
|
| + if (component < 0)
|
| + return -1;
|
| + return 1;
|
| + }
|
| +
|
| + function doAABBoxesIntersect(x1, y1, w1, h1, x2, y2, w2, h2) {
|
| + if (x1 > x2 + w2)
|
| + return false;
|
| +
|
| + if (x2 > x1 + w1)
|
| + return false;
|
| +
|
| + if (y1 > y2 + h2)
|
| + return false;
|
| +
|
| + if (y2 > y1 + h1)
|
| + return false;
|
| +
|
| + return true;
|
| + }
|
| +
|
| + function collideWithLine(x1, y1, x2, y2, nx, ny, vx, vy, sphere) {
|
| + var cx = sphere.dx + sphere.radius;
|
| + var cy = sphere.dy + sphere.radius;
|
| +
|
| + var normalDotVelocity = nx * sphere.vx + ny * sphere.vy;
|
| + if (normalDotVelocity > 0)
|
| + return false;
|
| +
|
| + var distanceToLine = signedDistanceToLine(x1, y1, nx, ny, cx, cy);
|
| + if (Math.abs(distanceToLine) > sphere.radius)
|
| + return false;
|
| +
|
| + var binormX = normalizeBinormalComponent(x2 - x1);
|
| + var binormY = normalizeBinormalComponent(y2 - y1);
|
| +
|
| + var distanceToBoundary1 = signedDistanceToLine(x1, y1, binormX, binormY, cx, cy);
|
| + if (distanceToBoundary1 < 0)
|
| + return false;
|
| +
|
| + var distanceToBoundary2 = signedDistanceToLine(x2, y2, -binormX, -binormY, cx, cy);
|
| + if (distanceToBoundary2 < 0)
|
| + return false;
|
| +
|
| + // We may have passed through the line. Let's fix that now.
|
| + var correction = sphere.radius - distanceToLine;
|
| + sphere.dx += correction * nx;
|
| + sphere.dy += correction * ny;
|
| +
|
| + // Update velocity.
|
| + if (Math.abs(nx) > epsilon)
|
| + sphere.vx = vx - sphere.vx;
|
| +
|
| + if (Math.abs(ny) > epsilon)
|
| + sphere.vy = vy - sphere.vy;
|
| +
|
| + return true;
|
| + }
|
| +
|
| + function collideWithSphere(a, b) {
|
| + if (!doAABBoxesIntersect(
|
| + a.dx, a.dy, a.radius * 2, a.radius * 2,
|
| + b.dx, b.dy, b.radius * 2, b.radius * 2))
|
| + return false;
|
| +
|
| + var cax = a.dx + a.radius;
|
| + var cay = a.dy + a.radius;
|
| + var cbx = b.dx + b.radius;
|
| + var cby = b.dy + b.radius;
|
| +
|
| + var deltaX = cax - cbx;
|
| + var deltaY = cay - cby;
|
| + var distance2 = deltaX * deltaX + deltaY * deltaY;
|
| +
|
| + var minDistance2 = a.radius + b.radius;
|
| + minDistance2 = minDistance2 * minDistance2;
|
| + if (distance2 > minDistance2)
|
| + return false;
|
| +
|
| + var futureDeltaX = (cax + a.vx * epsilon) - (cbx + b.vx * epsilon);
|
| + var futureDeltaY = (cay + a.vy * epsilon) - (cby + b.vy * epsilon);
|
| + var futureDistance2 = futureDeltaX * futureDeltaX + futureDeltaY * futureDeltaY;
|
| +
|
| + // If we're not getting closer, bail.
|
| + if (futureDistance2 > distance2)
|
| + return false;
|
| +
|
| + var distance = Math.sqrt(distance2);
|
| + deltaX /= distance;
|
| + deltaY /= distance;
|
| +
|
| + var p = 2 * (a.vx * deltaX + a.vy * deltaY - b.vx * deltaX - b.vy * deltaY);
|
| + p /= (a.mass + b.mass);
|
| +
|
| + a.vx = a.vx - p * b.mass * deltaX;
|
| + a.vy = a.vy - p * b.mass * deltaY;
|
| + b.vx = b.vx + p * a.mass * deltaX;
|
| + b.vy = b.vy + p * a.mass * deltaY;
|
| +
|
| + // Now we may be intersecting. Let's fix that.
|
| + var deltaDistance = Math.sqrt(minDistance2) - distance;
|
| + var correction = deltaDistance * 0.5;
|
| + a.dx += correction * deltaX;
|
| + a.dy += correction * deltaY;
|
| + b.dx -= correction * deltaX;
|
| + b.dy -= correction * deltaY;
|
| +
|
| + return true;
|
| + }
|
| +
|
| + function collideWithFoil(sphere, foil) {
|
| + if (!doAABBoxesIntersect(
|
| + sphere.dx, sphere.dy, sphere.radius * 2, sphere.radius * 2,
|
| + foil.dx, foil.dy, foil.width, foil.height))
|
| + return;
|
| +
|
| + var corners = [
|
| + [ foil.dx, foil.dy ],
|
| + [ foil.dx + foil.width, foil.dy ],
|
| + [ foil.dx + foil.width, foil.dy + foil.height ],
|
| + [ foil.dx, foil.dy + foil.height ]
|
| + ];
|
| +
|
| + for (var i = 0; i < corners.length; ++i) {
|
| + var corner0 = corners[i];
|
| + var corner1 = corners[(i + 1) % corners.length];
|
| + var corner2 = corners[(i + 2) % corners.length];
|
| +
|
| + var nx = normalizeBinormalComponent(corner1[0] - corner2[0]);
|
| + var ny = normalizeBinormalComponent(corner1[1] - corner2[1]);
|
| +
|
| + if (collideWithLine(corner0[0], corner0[1], corner1[0], corner1[1], nx, ny, foil.vx, foil.vy, sphere))
|
| + return;
|
| + }
|
| +
|
| + var corner = Object();
|
| + corner.radius = 0.1;
|
| + corner.mass = 50000; // really big.
|
| +
|
| + for (var i = 0; i < corners.length; ++i) {
|
| + corner.dx = corners[i][0];
|
| + corner.dy = corners[i][1];
|
| + corner.vx = foil.vx;
|
| + corner.vy = foil.vy;
|
| + if (collideWithSphere(sphere, corner))
|
| + return;
|
| + }
|
| + }
|
| +
|
| + function updateFoilProperties(foil) {
|
| + var dy = getAnimatedPosition(foil) + foil.top;
|
| + foil.vy = dy - foil.dy;
|
| + foil.dy = dy;
|
| + }
|
| +
|
| + function processCollisions() {
|
| + for (var i = 0; i < foils.length; ++i)
|
| + updateFoilProperties(foils[i]);
|
| + for (var i = 0; i < spheres.length; ++i) {
|
| + for (var j = 0; j < foils.length; ++j)
|
| + collideWithFoil(spheres[i], foils[j]);
|
| + for (var j = i + 1; j < spheres.length; ++j)
|
| + collideWithSphere(spheres[i], spheres[j]);
|
| + }
|
| + }
|
| +
|
| + function resetSpheres() {
|
| + for (var i = 0; i < spheres.length; ++i) {
|
| + var sphere = spheres[i];
|
| + var padding = 400;
|
| + if (sphere.dy > viewportHeight + padding) {
|
| + sphere.dx = sphere.initialX;
|
| + sphere.dy = sphere.initialY - 0.5 * viewportHeight;
|
| + sphere.vy = 0;
|
| + sphere.vx *= epsilon;
|
| + if (Math.abs(sphere.vx) > 100)
|
| + sphere.vx = 0;
|
| + }
|
| + }
|
| + }
|
| +
|
| + function updateLogo() {
|
| + logoAngle += logoSpeed;
|
| + while (logoAngle > 360)
|
| + logoAngle -= 360;
|
| +
|
| + logo.style.webkitTransform = "translateZ(0) rotate(" + logoAngle + "deg)";
|
| + }
|
| +
|
| + scope.tick = function(timestamp) {
|
| + var dt = 1.0;
|
| + console.log("tick: " + timestamp);
|
| + if (scope.lastTimestamp) {
|
| + dt = (timestamp - scope.lastTimestamp) * 60;
|
| + }
|
| + scope.lastTimestamp = timestamp;
|
| +
|
| + if (isMain()) {
|
| + updateLogo();
|
| + } else {
|
| + updatePositions(dt);
|
| + processCollisions();
|
| + updatePositions(dt);
|
| + applyGravity(dt);
|
| + resetSpheres();
|
| + }
|
| + scope.requestAnimationFrame(tick);
|
| + }
|
| +
|
| + function createFoil(dx, dy, width, height, delay) {
|
| + var foilActor = document.createElement("div");
|
| + foilActor.className = "foil";
|
| + foilActor.style.left = dx + "px";
|
| + foilActor.style.top = dy + "px";
|
| + foilActor.style.width = width;
|
| + foilActor.style.height = height;
|
| + foilActor.style.webkitAnimation = "foil-motion 1s " + delay + "ms infinite alternate";
|
| + document.body.appendChild(foilActor);
|
| +
|
| + var foil = new Object();
|
| + foil.actor = foilActor;
|
| + foil.left = dx;
|
| + foil.top = dy;
|
| + foil.width = width;
|
| + foil.height = height;
|
| + foil.dx = foil.left;
|
| + foil.dy = foil.top;
|
| + foil.vx = 0;
|
| + foil.vy = 0;
|
| +
|
| + return foil;
|
| + }
|
| +
|
| + function busySleep(duration) {
|
| + var start = new Date();
|
| + while ((new Date()).getTime() - start.getTime() < duration);
|
| + }
|
| +
|
| + function doJank() {
|
| + busySleep(500);
|
| + if (janking)
|
| + window.setTimeout(doJank, 500);
|
| + }
|
| +
|
| + function startJank() {
|
| + var startButton = document.getElementById("startJank");
|
| + startButton.style.webkitAnimation = "flip-hide 1s 1 forwards";
|
| +
|
| + var stopButton = document.getElementById("stopJank");
|
| + stopButton.style.webkitAnimation = "flip-show 1s 1 forwards";
|
| +
|
| + janking = true;
|
| +
|
| + window.setTimeout(doJank, 16);
|
| + }
|
| +
|
| + function stopJank() {
|
| + var startButton = document.getElementById("startJank");
|
| + startButton.style.webkitAnimation = "flip-show 1s 1 forwards";
|
| +
|
| + var stopButton = document.getElementById("stopJank");
|
| + stopButton.style.webkitAnimation = "flip-hide 1s 1 forwards";
|
| +
|
| + janking = false;
|
| + }
|
| +
|
| + function toggle() {
|
| + if (janking)
|
| + stopJank();
|
| + else
|
| + startJank();
|
| + }
|
| +
|
| + function createSphere(radius, topColor, bottomColor, dx, dy) {
|
| + var blurElements = [];
|
| + for (var i = 0; i < numBlurElements; ++i) {
|
| + var blurElement = document.createElement("div");
|
| + blurElement.className = "actor";
|
| + blurElement.style.background = "-webkit-gradient(linear, left top,"
|
| + + "left bottom, color-stop(0%," + topColor + "), color-stop(100%,"
|
| + + bottomColor + "))";
|
| + blurElement.style.width = (radius * 2) + "px";
|
| + blurElement.style.height = (radius * 2) + "px";
|
| + blurElement.style.borderRadius = radius + "px";
|
| + document.body.appendChild(blurElement);
|
| + var t = i / (numBlurElements - 1 || 1);
|
| + blurElement.style.opacity = lerp(0.4, 0.1, t);
|
| + blurElements.push(blurElement);
|
| + }
|
| +
|
| + var sphere = new Object();
|
| + sphere.blurElements = blurElements;
|
| + sphere.radius = radius;
|
| + sphere.vx = 0;
|
| + sphere.vy = 0;
|
| + sphere.dx = sphere.initialX = dx;
|
| + sphere.dy = sphere.initialY = dy;
|
| + sphere.mass = radius * radius * Math.PI;
|
| +
|
| + setPosition(sphere, dx, dy);
|
| +
|
| + spheres.push(sphere);
|
| + }
|
| +
|
| + function setupSpheres() {
|
| + var widths = [ 50, 10, 12, 30, 25, 10, 40, 10, 15, 10 ];
|
| + var colors = [
|
| + [ "rgba(53,53,255,1)", "rgba(28,41,127,1)" ],
|
| + [ "rgba(255,53,53,1)", "rgba(181,41,41,1)" ],
|
| + [ "rgba(8,160,0,1)", "rgba(24,102,23,1)" ],
|
| + [ "rgba(219,219,0,1)", "rgba(178,173,41,1)" ]
|
| + ];
|
| + var top = 100;
|
| + var left = 200;
|
| +
|
| + var numSpheres = 80;
|
| + for (var i = 0; i < numSpheres; ++i) {
|
| + var radius = widths[i % widths.length];
|
| + var colorPair = colors[i % colors.length];
|
| + var noise = i % 2 == 0 ? -5 : 5;
|
| + var dx = left - radius + noise;
|
| + var dy = top - radius * 2;
|
| +
|
| + createSphere(radius, colorPair[0], colorPair[1], dx, dy);
|
| +
|
| + top -= (widths[i % widths.length] * 2) + 30;
|
| + if (i == Math.floor(numSpheres/3)) {
|
| + top = 500;
|
| + left = 650;
|
| + } else if (i == Math.floor((numSpheres*2)/3)) {
|
| + top = 200;
|
| + left = 950;
|
| + }
|
| + }
|
| + }
|
| +
|
| + function setupFoils() {
|
| + var dxs = [ 50, 500, 700 ];
|
| + var dys = [ 150, 600, 300 ];
|
| + var delays = [ 0, 500, 250 ];
|
| + for (var i = 0; i < 3; ++i)
|
| + foils.push(createFoil(dxs[i], dys[i], 300, 30, delays[i]));
|
| + }
|
| +
|
| + function setupScene() {
|
| + logo = document.getElementById("logo");
|
| + viewportHeight = document.documentElement.clientHeight;
|
| +
|
| + var container = document.querySelector(".container");
|
| + container.addEventListener("mousedown", function(e) { e.preventDefault(); });
|
| + container.addEventListener("click", toggle);
|
| +
|
| + setupFoils();
|
| + setupSpheres();
|
| + }
|
| +
|
| + scope.initWorker = function() {
|
| + self.onmessage = function(e) {
|
| + spheres = e.data.spheres;
|
| + foils = e.data.foils;
|
| + viewportHeight = e.data.viewportHeight;
|
| + requestAnimationFrame(tick);
|
| + }
|
| + };
|
| +
|
| + scope.initMain = function() {
|
| + setupScene();
|
| + scope.worker = new CompositorWorker("physics.js");
|
| +
|
| + var message = {
|
| + 'spheres': [],
|
| + 'foils': [],
|
| + 'viewportHeight': viewportHeight
|
| + };
|
| +
|
| + for (var i = 0; i < spheres.length; ++i) {
|
| + var sphere = spheres[i];
|
| + var sphereProxy = {
|
| + radius: sphere.radius,
|
| + vx: sphere.vx,
|
| + vy: sphere.vy,
|
| + dx: sphere.dx,
|
| + dy: sphere.dy,
|
| + initialX: sphere.initialX,
|
| + initialY: sphere.initialY,
|
| + mass:sphere.mass,
|
| + blurElements: []
|
| + };
|
| + for (var j = 0; j < sphere.blurElements.length; ++j)
|
| + sphereProxy.blurElements.push(new CompositorProxy(sphere.blurElements[j], ['transform']));
|
| + message.spheres.push(sphereProxy);
|
| + }
|
| +
|
| + for (var i = 0; i < foils.length; ++i) {
|
| + var foil = foils[i];
|
| + var foilProxy = {
|
| + left: foil.left,
|
| + top: foil.top,
|
| + width:foil.width,
|
| + height:foil.height,
|
| + dx:foil.dx,
|
| + dy:foil.dy,
|
| + vx:foil.vx,
|
| + vy:foil.vy,
|
| + actor:new CompositorProxy(foil.actor, ['transform'])
|
| + }
|
| + message.foils.push(foilProxy);
|
| + }
|
| +
|
| + worker.postMessage(message);
|
| + requestAnimationFrame(tick);
|
| + };
|
| +
|
| + if (isMain())
|
| + initMain();
|
| + else
|
| + initWorker();
|
| +})(self);
|
| +
|
|
|