mirror of
https://github.com/skeeto/webgl-particles.git
synced 2026-01-18 16:00:15 +00:00
231 lines
6.5 KiB
JavaScript
231 lines
6.5 KiB
JavaScript
/*global updateCount LZString */
|
|
|
|
/**
|
|
* User interface connection to the simulation.
|
|
* @constructor
|
|
*/
|
|
function Controller(particles) {
|
|
this.particles = particles;
|
|
this.obstacle = null;
|
|
this.init();
|
|
this.mousedown = false;
|
|
|
|
var _this = this,
|
|
canvas = particles.igloo.gl.canvas;
|
|
$(canvas).on('mousemove', function(event) {
|
|
var coords = Controller.coords(event);
|
|
_this.obstacle.position[0] = coords[0];
|
|
_this.obstacle.position[1] = coords[1];
|
|
_this.obstacle.enabled = true;
|
|
particles.updateObstacles();
|
|
if (_this.mousedown) _this.place();
|
|
});
|
|
$(canvas).on('mouseout', function() {
|
|
_this.obstacle.enabled = false;
|
|
particles.updateObstacles();
|
|
_this.mousedown = false;
|
|
});
|
|
$(canvas).on('mousedown', function() {
|
|
_this.mousedown = true;
|
|
});
|
|
$(canvas).on('mouseup', function(event) {
|
|
if (event.which === 1) _this.place();
|
|
_this.mousedown = false;
|
|
});
|
|
$(window).on('keyup', function(event) {
|
|
switch (event.which) {
|
|
case 67: // c
|
|
_this.clear();
|
|
break;
|
|
case 68: // d
|
|
_this.adjust(2);
|
|
break;
|
|
case 72: // h
|
|
_this.adjust(0.5);
|
|
break;
|
|
}
|
|
});
|
|
$('.controls .increase').on('click', function() {
|
|
_this.adjust(2);
|
|
});
|
|
$('.controls .decrease').on('click', function() {
|
|
_this.adjust(0.5);
|
|
});
|
|
$('.controls .particles .color').on('change', function(event) {
|
|
var value = $(event.target).val();
|
|
_this.particles.color = Controller.parseColor(value);
|
|
});
|
|
$('.controls .reset').on('click', function() {
|
|
_this.adjust(1);
|
|
});
|
|
$('.controls .particles .size').on('input', function() {
|
|
_this.particles.size = Number($(this).val());
|
|
});
|
|
$('.controls .particles .gravity').on('input', function() {
|
|
_this.particles.gravity[1] = -Number($(this).val());
|
|
});
|
|
$('.controls .particles .wind').on('input', function() {
|
|
_this.particles.wind[0] = Number($(this).val());
|
|
});
|
|
$('.controls .particles .restitution').on('input', function() {
|
|
_this.particles.restitution = Number($(this).val());
|
|
});
|
|
$('.controls .obstacles .color').on('change', function(event) {
|
|
var value = $(event.target).val();
|
|
_this.particles.obstacleColor = Controller.parseColor(value);
|
|
});
|
|
$('.controls .clear').on('click', function() {
|
|
_this.clear();
|
|
});
|
|
$('.controls .obstacles .size').on('change', function() {
|
|
_this.obstacle.size = Number($(this).val());
|
|
_this.particles.updateObstacles();
|
|
});
|
|
$('.controls .save').on('click', function() {
|
|
var data = JSON.stringify(_this.save()),
|
|
armored = LZString.compressToBase64(data);
|
|
localStorage.snapshot = data;
|
|
$('.controls .snapshot .manual').val(armored);
|
|
});
|
|
$('.controls .restore').on('click', function() {
|
|
var data = null,
|
|
manual = $('.controls .snapshot .manual').val();
|
|
if (manual !== '') {
|
|
data = LZString.decompressFromBase64(manual);
|
|
} else {
|
|
data = JSON.parse(localStorage.snapshot);
|
|
}
|
|
_this.restore();
|
|
updateCount();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create and capture the mouse obstacle.
|
|
* @returns {Controller} this
|
|
*/
|
|
Controller.prototype.init = function() {
|
|
this.obstacle = this.particles.addObstacle([0, 0], 20);
|
|
this.obstacle.enabled = false;
|
|
this.particles.updateObstacles();
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Place a new obstacle at the mouse location.
|
|
* @returns {Controller} this
|
|
*/
|
|
Controller.prototype.place = function() {
|
|
var center = this.obstacle.position,
|
|
radius = this.obstacle.size;
|
|
this.particles.addObstacle(center.slice(0), radius);
|
|
this.particles.updateObstacles();
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Clear all obstacles.
|
|
* @returns {Controller} this
|
|
*/
|
|
Controller.prototype.clear = function() {
|
|
var size = this.obstacle.size;
|
|
this.particles.obstacles.length = 0;
|
|
this.init();
|
|
this.obstacle.size = size;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Immediately adjust the particle count.
|
|
* @param {number} factor multiplies the particle count
|
|
* @returns {Controller} this
|
|
*/
|
|
Controller.prototype.adjust = function(factor) {
|
|
this.particles.setCount(this.particles.getCount() * factor);
|
|
updateCount();
|
|
};
|
|
|
|
/**
|
|
* Captures the simulation's particle count and obstacle configuration.
|
|
* @returns {Object} the simulation save state object
|
|
*/
|
|
Controller.prototype.save = function() {
|
|
var save = {
|
|
gravity: this.particles.gravity,
|
|
wind: this.particles.wind,
|
|
size: this.particles.size,
|
|
particles: this.particles.getCount(),
|
|
obstacles: []
|
|
};
|
|
this.particles.obstacles.forEach(function(o) {
|
|
if (o.enabled) {
|
|
save.obstacles.push({
|
|
position: Controller.round(o.position),
|
|
size: o.size
|
|
});
|
|
}
|
|
});
|
|
return save;
|
|
};
|
|
|
|
/**
|
|
* Restore the simulation's state from a save object.
|
|
* @param {Object} save
|
|
* @returns {Controller} this
|
|
*/
|
|
Controller.prototype.restore = function(save) {
|
|
if (this.particles.getCount() !== save.particles) {
|
|
this.particles.setCount(save.particles);
|
|
}
|
|
this.clear();
|
|
var ps = this.particles;
|
|
ps.size = save.size;
|
|
ps.gravity = save.gravity;
|
|
ps.wind = save.wind;
|
|
save.obstacles.forEach(function(o) {
|
|
ps.addObstacle(o.position, o.size);
|
|
});
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* @param {Object} event
|
|
* @returns {Array} the simulation coodinates from the event
|
|
*/
|
|
Controller.coords = function(event) {
|
|
var $target = $(event.target),
|
|
offset = $target.offset(),
|
|
border = 1,
|
|
x = event.pageX - offset.left - border,
|
|
y = $target.height() - (event.pageY - offset.top - border);
|
|
return [x, y];
|
|
};
|
|
|
|
/**
|
|
* @param {Array|ArrayBufferView|value} value
|
|
* @param {number} [precision=4]
|
|
* @returns {Array|number} a copy of the array/value rounded to PRECISION
|
|
*/
|
|
Controller.round = function(value, precision) {
|
|
precision = precision || 4;
|
|
if ('length' in value) {
|
|
return Array.prototype.map.call(value, function (x) {
|
|
return Number(x.toPrecision(precision));
|
|
});
|
|
} else {
|
|
return Number(value.toPrecision(precision));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {string} color
|
|
* @returns {Array} a 4-element color array
|
|
*/
|
|
Controller.parseColor = function(color) {
|
|
var colors = /#(..)(..)(..)/.exec(color).slice(1).map(function(x) {
|
|
return parseInt(x, 16) / 255;
|
|
});
|
|
colors.push(1);
|
|
return colors;
|
|
};
|