'use strict';
var Base = require('./Base');
var Colors = Base.Colors;
/**
* Initialices rendering and animate entities.
*
* @class Render
* @constructor
* @module CrowdSimApp
* @submodule Render
* @param {Canvas} canvas
* @param {Number} w width
* @param {Number} h height
* @param {Object} options
*/
var Render = function(canvas, w, h, options) {
this.options = Lazy(options).defaults(Render.defaults).toObject();
// create a renderer instance.
this._renderer = PIXI.autoDetectRenderer(w, h);
this._renderer.backgroundColor = this.options.backgroundColor;
this._renderer.autoResize = true;
// add the renderer view element to the DOM
canvas.appendChild(this._renderer.view);
// create root container
this._stage = new PIXI.Container();
this._stage.scale.x = this.options.scale;
this._stage.scale.y = this.options.scale; // 10pix ~ 1m
// create agents container
this._worldContainer = new PIXI.Container();
if (this.options.useParticle) {
this._agentsContainer = new PIXI.ParticleContainer(this.options.maxAgents, {
scale: true,
position: true,
rotation: true,
uvs: true,
alpha: true
});
} else {
this._agentsContainer = new PIXI.Container();
}
this._stage.addChild(this._agentsContainer);
this._stage.addChild(this._worldContainer);
if (this.options.debug) {
this._debugContainer = new PIXI.Container();
this._stage.addChild(this._debugContainer);
}
};
/**
* Initialice render.
*
* @method init
* @param {Array} textures coordinates for create Pixi.Textures.
* @param {Array} events to receive world callbacks
*/
Render.prototype.init = function(textures, events) {
var baseTextures = PIXI.Texture.fromImage(textures.file),
a = textures.agent,
w = textures.wall,
p = textures.path;
Render.Agent.texture = new PIXI.Texture(baseTextures, new PIXI.Rectangle(a[0], a[1], a[2], a[3]));
Render.Wall.texture = new PIXI.Texture(baseTextures, new PIXI.Rectangle(w[0], w[1], w[2], w[3]));
Render.Path.texture = new PIXI.Texture(baseTextures, new PIXI.Rectangle(p[0], p[1], p[2], p[3]));
this._worldContainer.removeChildren();
this._agentsContainer.removeChildren();
// init default containers
Render.Agent.container = this._agentsContainer;
Render.Agent.debugContainer = this._debugContainer;
Render.Context.container = this._worldContainer;
Render.Group.container = this._worldContainer;
Render.Wall.container = this._worldContainer;
Render.Path.container = this._worldContainer;
// to draw everything
//App._renderOnce();
this.onPreRender = events.onPreRender;
this.onPostRender = events.onPostRender;
// wire Entity events
Render.Entity.mouseover = events.mouseover;
Render.Entity.mouseout = events.mouseout;
Render.Entity.mousedown = events.mousedown;
Render.Entity.mouseup = events.mouseup;
Render.Entity.mousemove = events.mousemove;
// axis
var graphicsAux = new PIXI.Graphics();
graphicsAux.lineStyle(0.2, Colors.Helpers);
// x
var length = 5;
graphicsAux.moveTo(0, 0);
graphicsAux.lineTo(length, 0);
graphicsAux.moveTo(length - 1, -1);
graphicsAux.lineTo(length, 0);
graphicsAux.lineTo(length - 1, 1);
// y
graphicsAux.moveTo(0, 0);
graphicsAux.lineTo(0, length);
graphicsAux.moveTo(-1, length - 1);
graphicsAux.lineTo(0, length);
graphicsAux.lineTo(1, length - 1);
// for temporary graphics
this._graphicsHelper = new PIXI.Graphics();
this._worldContainer.addChild(this._graphicsHelper);
this._worldContainer.addChild(graphicsAux);
this._stop = false;
this.animate();
};
/**
* Start rendering loop.
*
* @method start
*/
Render.prototype.start = function() {
this._stop = false;
this.animate();
};
/**
* Rendering loop.
*
* @method animate
*/
Render.prototype.animate = function() {
if (this.onPreRender) {
this.onPreRender();
}
if (this.world) {
if (this.world.changesNumber() > 0 || this.world.freeze()) {
// jump renders when no changes happened yet
var entities = this.world.entities;
// render/refresh entities
for (var prop in entities) {
Lazy(entities[prop]).each(function(a) {
if (a.view) { a.view.render(); }
});
}
var agents = this.world.getAgents();
for (var i in agents) {
agents[i].view.render();
}
}
}
// render the stage
this._renderer.render(this._stage);
if (!this._stop) {
requestAnimationFrame(this.animate.bind(this));
}
if (this.onPostRender) {
this.onPostRender();
}
};
/**
* Stop rendering loop.
*
* @method stop
*/
Render.prototype.stop = function() {
this._stop = true;
};
/**
* Set current rendered world.
*
* @method setWorld
* @param {World} world
*/
Render.prototype.setWorld = function(world) {
this.world = world;
};
/**
* Resize rendering stage.
*
* @method resize
* @param {Number} w
* @param {Number} h
*/
Render.prototype.resize = function(w, h) {
this._renderer.resize(w,h);
};
/**
* Draw a helper line from 0 to 1.
*
* @method drawHelperLine
* @param {Number} x0
* @param {Number} y0
* @param {Number} x1
* @param {Number} y1
*/
Render.prototype.drawHelperLine = function(x0, y0, x1, y1) {
this._graphicsHelper.clear();
if (x0) {
this._graphicsHelper.clear();
this._graphicsHelper.lineStyle(0.2, Colors.Helpers);
this._graphicsHelper.moveTo(x0, y0);
this._graphicsHelper.lineTo(x1, y1);
}
};
/**
* Zoom-in, zoom-out stage.
*
* @method zoom
* @param {Number} scale
* @param {Number} x center of zoom
* @param {Number} y center of zoom
*/
Render.prototype.zoom = function(scale, x, y) {
scale = scale > 0 ? 1.1 : 0.9;
var currentWorldPos = this.screenToWorld(x, y);
this._stage.scale.x = this._stage.scale.x * scale;
this._stage.scale.y = this._stage.scale.y * scale;
var newScreenPos = this.worldToScreen(currentWorldPos.x, currentWorldPos.y);
this._stage.x -= (newScreenPos.x - x) ;
this._stage.y -= (newScreenPos.y - y) ;
};
/**
* Pan view of stage.
*
* @method pan
* @param {Number} dx displacement x
* @param {Number} dy displacement y
*/
Render.prototype.pan = function(dx, dy) {
this._stage.x += dx;
this._stage.y += dy;
};
/**
* Get stage width.
*
* @method getWidth
* @return {Number} width
*/
Render.prototype.getWidth = function() {
return this._stage.width;
};
/**
* Get stage height.
*
* @method getHeight
* @return {Number} height
*/
Render.prototype.getHeight = function() {
return this._stage.height;
};
/**
* Convert screen coordinates to world coordinates.
*
* @method screenToWorld
* @param {Number} x
* @param {Number} y
* @return {Object} world coordinate {x:x,y:y}
*/
Render.prototype.screenToWorld = function(x, y) {
return {x: (x - this._stage.x) / this._stage.scale.x,
y: (y - this._stage.y) / this._stage.scale.y};
};
/**
* Convert world coordinates to screen coordinates.
*
* @method worldToScreen
* @param {Number} x
* @param {Number} y
* @return {Object} screen coordinate {x:x,y:y}
*/
Render.prototype.worldToScreen = function(x, y) {
return {x: x * this._stage.scale.x + this._stage.x,
y: y * this._stage.scale.y + this._stage.y};
};
Render.Agent = require('./Agent');
Render.Entity = require('./Entity');
Render.Group = require('./Group');
Render.Context = require('./Context');
Render.Path = require('./Path');
Render.Wall = require('./Wall');
Render.Joint = require('./Joint');
Render.defaults = {
backgroundColor: 0,
useParticle: true,
scale: 10,
mxAgents: 5000, // to init particle container
debug: false,
};
module.exports = Render;