not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

542 lines
15 KiB

define(["sugar-web/activity/activity"], function (activity) {
// Manipulate the DOM only when it is ready.
requirejs(['domReady!'], function (doc) {
// Initialize the activity
activity.setup();
// Initialize cordova
var useragent = navigator.userAgent.toLowerCase();
var sensorButton = document.getElementById("sensor-button");
var gravityButton = document.getElementById("gravity-button");
var appleButton = document.getElementById("apple-button");
var runButton = document.getElementById("run-button");
var readyToWatch = false;
var sensorMode = true;
var newtonMode = false;
var resizeTimer = null;
if (useragent.indexOf('android') != -1 || useragent.indexOf('iphone') != -1 || useragent.indexOf('ipad') != -1 || useragent.indexOf('ipod') != -1 || useragent.indexOf('mozilla/5.0 (mobile') != -1) {
document.addEventListener('deviceready', function() {
readyToWatch = true;
}, false);
sensorButton.disabled = false;
sensorButton.classList.add('active');
} else {
sensorButton.disabled = true;
}
// Initialize the world
var body = document.getElementById("body");
var innerWidth = body.offsetWidth;
var innerHeight = body.offsetHeight;
var toolbarHeight = 55;
var outerWidth = 0; // Use to determine if items could disappear, could be 300;
var init = false;
var gravityMode = 0;
var currentType = 0;
var physicsActive = true;
Physics({ timestep: 6 }, function (world) {
// bounds of the window
var viewWidth = innerWidth
,viewportBounds = Physics.aabb(0-outerWidth, toolbarHeight, innerWidth+outerWidth, innerHeight)
,edgeBounce
,renderer
;
// let's use the pixi renderer
requirejs(['pixi'], function( PIXI ){
window.PIXI = PIXI;
// create a renderer
renderer = Physics.renderer('pixi', {
el: 'viewport'
});
// add the renderer
world.add(renderer);
// render on each step
world.on('step', function () {
world.render();
if (!init) {
init = true;
zoom();
}
if (readyToWatch) {
watchId = navigator.accelerometer.watchAcceleration(accelerationChanged, null, { frequency: 500 });
readyToWatch = false;
}
});
// add the interaction
world.add(Physics.behavior('interactive', { el: renderer.container }));
});
// constrain objects to these bounds
edgeBounce = Physics.behavior('edge-collision-detection', {
aabb: viewportBounds
,restitution: 0.2
,cof: 0.8
});
// resize events
window.addEventListener('resize', function () {
if (resizeTimer) {
clearTimeout(resizeTimer);
}
resizerTimer = setTimeout(function() {
renderer.resize(body.offsetWidth,body.offsetHeight);
// as of 0.7.0 the renderer will auto resize... so we just take the values from the renderer
viewportBounds = Physics.aabb(0-outerWidth, toolbarHeight, renderer.width+outerWidth, renderer.height);
// update the boundaries
edgeBounce.setAABB(viewportBounds);
innerWidth = body.offsetWidth;
innerHeight = body.offsetHeight;
zoom();
}, 500);
}, true);
// handle toolbar buttons
document.getElementById("box-button").addEventListener('click', function (e) {
currentType = 1;
switchToType(currentType);
}, true);
document.getElementById("circle-button").addEventListener('click', function (e) {
currentType = 0;
switchToType(currentType);
}, true);
document.getElementById("triangle-button").addEventListener('click', function (e) {
currentType = 2;
switchToType(currentType);
}, true);
document.getElementById("polygon-button").addEventListener('click', function (e) {
currentType = 3;
switchToType(currentType);
}, true);
gravityButton.addEventListener('click', function () {
setGravity((gravityMode + 1)%8);
}, true);
runButton.addEventListener('click', function () {
togglePause();
}, true);
document.getElementById("clear-button").addEventListener('click', function () {
currentType = -1;
switchToType(currentType);
}, true);
// Handle acceleration and gravity mode
sensorButton.addEventListener('click', function () {
sensorMode = !sensorMode;
if (sensorMode)
sensorButton.classList.add('active');
else
sensorButton.classList.remove('active');
}, true);
appleButton.addEventListener('click', function () {
newtonMode = !newtonMode;
if (newtonMode) {
world.remove(gravity);
world.add(newton);
appleButton.classList.add('active');
gravityButton.disabled = true;
} else {
world.remove(newton);
world.add(gravity);
appleButton.classList.remove('active');
gravityButton.disabled = false;
}
}, true);
function accelerationChanged(acceleration) {
if (!sensorMode) return;
if (acceleration.x < -4.5) {
if (acceleration.y > 4.75)
setGravity(3);
else if (acceleration.y < -4.75)
setGravity(5);
else
setGravity(4);
} else if (acceleration.x <= 4.5 && acceleration.x >= -4.5) {
if (acceleration.y > 4.75)
setGravity(2);
else if (acceleration.y < -4.75)
setGravity(6);
} else if (acceleration.x > 4.5) {
if (acceleration.y > 4.75)
setGravity(1);
else if (acceleration.y < -4.75)
setGravity(7);
else
setGravity(0);
}
}
// Save/Load world
loadWorld();
var stopButton = document.getElementById("stop-button");
stopButton.addEventListener('click', function (event) {
console.log("writing...");
saveWorld(function (error) {
if (error === null) {
console.log("write done.");
}
else {
console.log("write failed.");
}
});
});
// Force resize renderer at startup to avoid glitch margin
var initialResize = function() {
if (renderer) {
renderer.resize(body.offsetWidth,body.offsetHeight);
} else {
setTimeout(initialResize, 300);
}
};
setTimeout(initialResize, 300);
var colors = [
['0x268bd2', '0x0d394f']
,['0xc93b3b', '0x561414']
,['0xe25e36', '0x79231b']
,['0x6c71c4', '0x393f6a']
,['0x58c73c', '0x30641c']
,['0xcac34c', '0x736a2c']
];
function zoom() {
if (window.devicePixelRatio == 1) {
return;
}
var canvas = document.getElementById("viewport").children[0];
var zoom = 1.0 / window.devicePixelRatio;
canvas.style.zoom = zoom;
var useragent = navigator.userAgent.toLowerCase();
if (useragent.indexOf('chrome') == -1) {
canvas.style.MozTransform = "scale("+zoom+")";
canvas.style.MozTransformOrigin = "0 0";
}
world.wakeUpAll();
}
function random( min, max ){
return (Math.random() * (max-min) + min)|0;
}
function switchToType(newtype) {
document.getElementById("box-button").classList.remove('active');
document.getElementById("circle-button").classList.remove('active');
document.getElementById("polygon-button").classList.remove('active');
document.getElementById("triangle-button").classList.remove('active');
document.getElementById("clear-button").classList.remove('active');
if (newtype == 0) document.getElementById("circle-button").classList.add('active');
else if (newtype == 1) document.getElementById("box-button").classList.add('active');
else if (newtype == 2) document.getElementById("triangle-button").classList.add('active');
else if (newtype == 3) document.getElementById("polygon-button").classList.add('active');
else if (newtype == -1) document.getElementById("clear-button").classList.add('active');
}
function dropInBody(type, pos){
var body;
var c;
switch (type){
// add a circle
case 0:
c = colors[random(0, colors.length-1)];
body = Physics.body('circle', {
x: pos.x
,y: pos.y
,vx: random(-5, 5)/100
,radius: 40
,restitution: 0.9
,styles: {
fillStyle: c[0]
,strokeStyle: c[1]
,lineWidth: 1
,angleIndicator: c[1]
}
});
break;
// add a square
case 1:
c = colors[random(0, colors.length-1)];
var l = random(0, 70);
body = Physics.body('rectangle', {
width: 50+l
,height: 50+l
,x: pos.x
,y: pos.y
,vx: random(-5, 5)/100
,restitution: 0.9
,styles: {
fillStyle: c[0]
,strokeStyle: c[1]
,lineWidth: 1
,angleIndicator: c[1]
}
});
break;
// add a polygon
case 2:
case 3:
var s = (type == 2 ? 3 : random( 5, 10 ));
c = colors[ random(0, colors.length-1) ];
body = Physics.body('convex-polygon', {
vertices: Physics.geometry.regularPolygonVertices( s, 30 )
,x: pos.x
,y: pos.y
,vx: random(-5, 5)/100
,angle: random( 0, 2 * Math.PI )
,restitution: 0.9
,styles: {
fillStyle: c[0]
,strokeStyle: c[1]
,lineWidth: 1
,angleIndicator: c[1]
}
});
break;
}
body.treatment = "static";
world.add( body );
return body;
}
// Save world to datastore
function saveWorld(callback) {
// Build bodies list
var bodies = world.getBodies();
var objects = [];
for(var i = 0 ; i < bodies.length ; i++) {
var object = serializeObject(bodies[i]);
objects.push(object);
}
// Save to datastore
var datastoreObject = activity.getDatastoreObject();
var jsonData = JSON.stringify({world: objects});
datastoreObject.setDataAsText(jsonData);
datastoreObject.save(callback);
}
function serializeObject(body) {
var object = {};
object.type = body.geometry.name;
if (object.type == "circle") {
object.radius = body.radius;
} else if (body.geometry.name == "rectangle") {
object.width = body.view.width;
object.height = body.view.height;
} else if (body.geometry.name == "convex-polygon") {
object.vertices = body.vertices;
}
object.restitution = body.restitution;
object.styles = body.styles;
object.x = body.view.x;
object.y = body.view.y;
return object;
}
// Load world from datastore
function loadWorld(objects) {
var datastoreObject = activity.getDatastoreObject();
datastoreObject.loadAsText(function (error, metadata, data) {
var data = JSON.parse(data);
if (data == null)
return;
// Create bodies
var objects = data.world;
for(var i = 0 ; i < objects.length ; i++) {
var newBody = deserializeObject(objects[i]);
world.add(newBody);
}
});
}
function deserializeObject(savedObject) {
var newOptions = {
x: savedObject.x,
y: savedObject.y,
restitution: savedObject.restitution,
styles: savedObject.styles
};
if (savedObject.angle)
newOptions.angle = savedObject.angle;
if (savedObject.type == "circle") {
newOptions.radius = savedObject.radius;
} else if (savedObject.type == "rectangle") {
newOptions.width = savedObject.width;
newOptions.height = savedObject.height;
} else if (savedObject.type = "convex-polygon") {
newOptions.vertices = savedObject.vertices;
}
return Physics.body(savedObject.type, newOptions);
}
function setBodiesTreatmentStatic() {
var bodies = world.getBodies();
bodies.forEach(function(item, index, array) {
item.treatment = 'static';
});
}
function setBodiesTreatmentDynamic() {
var bodies = world.getBodies();
bodies.forEach(function(item, index, array) {
item.treatment = 'dynamic';
});
}
function togglePause() {
if (physicsActive) {
document.getElementById("run-button").classList.remove('running');
document.getElementById("run-button").setAttribute('title', 'Play');
setBodiesTreatmentStatic();
} else {
document.getElementById("run-button").classList.add('running');
document.getElementById("run-button").setAttribute('title', 'Pause');
Physics.util.ticker.start();
setBodiesTreatmentDynamic();
}
physicsActive = !physicsActive;
}
// Change gravity value
function setGravity(value) {
if (gravityMode == value) return;
var acc = {};
switch(value) {
case 0:
acc = { x: 0, y: 0.0004 };
break;
case 1:
acc = { x: 0.0004, y: 0.0004 };
break;
case 2:
acc = { x: 0.0004, y: 0 };
break;
case 3:
acc = { x: 0.0004, y: -0.0004 };
break;
case 4:
acc = { x: 0, y: -0.0004 };
break;
case 5:
acc = { x: -0.0004, y: -0.0004 };
break;
case 6:
acc = { x: -0.0004, y: 0 };
break;
case 7:
acc = { x: -0.0004, y: 0.0004 };
break;
}
var reverse = (window.orientation == -90 ? -1 : 1);
acc = { x: acc.x * reverse, y: acc.y * reverse };
document.getElementById("gravity-button").style.backgroundImage = "url(icons/gravity"+(reverse == -1 ? (value+4)%8 : value)+".svg)";
gravity.setAcceleration(acc);
world.wakeUpAll();
gravityMode = value;
}
// add some fun interaction
var createdBody = null;
var createdStart = null;
world.on({
'interact:poke': function( pos ){
// create body at a static place
if (currentType != -1 && pos.y > toolbarHeight) {
createdBody = dropInBody(currentType, pos);
createdStart = pos;
}
}
,'interact:move': function( pos ){
// update size of created body
if (createdBody != null) {
// compute new size
var distx = createdStart.x - pos.x;
var disty = createdStart.y - pos.y;
var distance = Math.min(Math.sqrt(Math.abs(distx*distx-disty*disty)),createdStart.y-toolbarHeight);
if (createdBody.view != null) {
// Recreate the object with new size
var object = serializeObject(createdBody);
if (object.type == "circle") {
object.radius = Math.max(40, distance);
} else if (object.type == "rectangle") {
object.width = object.height = Math.max(50, distance);
} else if (object.type = "convex-polygon") {
object.vertices = Physics.geometry.regularPolygonVertices( object.vertices.length, Math.max(30, distance));
}
world.removeBody(createdBody);
var v1 = new Physics.vector(createdStart.x, 0);
var v2 = new Physics.vector(pos.x-createdStart.x, pos.y-createdStart.y);
object.angle = -v1.angle(v2);
createdBody = deserializeObject(object);
createdBody.treatment = "static";
world.add(createdBody);
}
}
}
,'interact:release': function( pos ){
if (physicsActive) {
if (createdBody != null) {
createdBody.treatment = "dynamic";
}
world.wakeUpAll();
}
createdBody = null;
}
,'interact:grab': function ( data ) {
if (currentType == -1) {
world.remove(data.body);
}
}
});
// add things to the world
var gravity = Physics.behavior('constant-acceleration');
var newton = Physics.behavior('newtonian', { strength: .5 });
world.add([
gravity
,Physics.behavior('body-impulse-response')
,Physics.behavior('body-collision-detection')
,Physics.behavior('sweep-prune')
,edgeBounce
]);
// subscribe to ticker to advance the simulation
Physics.util.ticker.on(function( time ) {
// next step
world.step( time );
// remove bodies out of
var bodies = world.getBodies();
var limit = outerWidth / 2;
if (limit > 0) {
for(var i = 0 ; i < bodies.length ; i++) {
var body = bodies[i];
if (body.state.pos.x < 0-limit || body.state.pos.x > innerWidth+limit)
world.remove(body);
}
}
});
});
});
});