Skip to content

Instantly share code, notes, and snippets.

@larrybotha
Last active April 7, 2017 22:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save larrybotha/6af6847199719c41e90452ff78982d85 to your computer and use it in GitHub Desktop.
Save larrybotha/6af6847199719c41e90452ff78982d85 to your computer and use it in GitHub Desktop.
Two.js Renderer for PhysicsJS

Two.js Renderer for PhysicsJS

A starting point for a Two.js renderer for PhysicsJS.

// has a dependency on PhysicsJS and Two.js
Physics.renderer('Two', function(parent) {
if (!document) {
// must be in node environment
return {};
}
const metaStyles = {
alignment: 'left',
size: 12,
};
const defaults = {
metaEl: null,
offset: { x: 0, y: 0 },
styles: {
'point': {},
'circle' : {},
'rectangle' : {},
'convex-polygon' : {}
}
};
return {
// extended
init(options) {
const self = this;
let el;
if (typeof Two === 'undefined') {
throw "Two.js not present - cannot continue";
}
parent.init.call(this, options);
this.options.defaults(defaults, true);
this.options.onChange(function() {
self.options.offset = new Physics.vector(self.options.offset);
});
this.options(options, true);
this.two = new Two({
width: options.width,
height: options.height,
type: options.type || Two.Types.svg,
});
this.meta = {};
this.two.appendTo(this.el || document.body);
if (this.options.autoResize) {
this.resize();
} else {
this.resize(this.options.width, this.options.height);
}
},
createView(geometry, styles, parent) {
const aabb = geometry.aabb();
const hw = aabb.hw + Math.abs(aabb.x);
const hh = aabb.hh + Math.abs(aabb.y);
const name = geometry.name;
let view = null;
styles = styles || this.options.styles[name] || this.options.styles.circle || {};
if (name === 'circle') {
view = this.createCircle(0, 0, geometry.radius, styles);
} else if (name === 'rectangle') {
view = this.createRect(-geometry.width/2, -geometry.height/2, geometry.width, geometry.height, styles);
} else if (name === 'convex-polygon') {
view = this.createPolygon(geometry.vertices, geometry.options.pathParams, styles);
} else if (name === 'compound') {
view = this.createCompound(geometry.children, styles);
} else {
view = this.createCircle(0, 0, 1, styles);
}
if (parent) parent.add(view);
return view;
},
// extended
resize(width, height) {
parent.resize.call(this, width, height);
this.two.width = width;
this.two.height= height;
},
// extended
connect(world) {
world.on('add:body', this.attach, this);
world.on('remove:body', this.detach, this);
},
// extended
disconnect(world) {
world.off('add:body', this.attach, this);
world.off('remove:body', this.detach, this);
},
/**
* TwoRenderer#detach(data) -> this
* - data (Two.Path|Object): Graphics object or event data (`data.body`)
*
* Event callback to detach a child from the stage
**/
detach(data) {
// interpred data as either dom node or event data
const el = (data instanceof Two.Path && data) || (data.body && data.body.view);
if (el) this.two.remove(el);
return this;
},
/**
* TwoRenderer#attach(data) -> this
* - data (Two.Path|Object): Graphics object or event data (`data.body`)
*
* Event callback to attach a child to the stage
**/
attach(data) {
// interpred data as either dom node or event data
const el = (data instanceof Two.Path && data) || (data.body && data.body.view);
if (el) this.two.add(el);
return this;
},
/**
* TwoRenderer#drawBody(body, view)
* - body (Body): The body to draw
* - view (DisplayObject): The pixi display object
*
* Draw a Two.js path
**/
drawBody(body, view) {
const pos = body.state.pos;
const v = body.state.vel;
const os = body.offset;
const t = this._interpolateTime || 0;
let x;
let y;
let ang;
// interpolate positions
x = pos._[0] + v._[0] * t;
y = pos._[1] + v._[1] * t;
ang = body.state.angular.pos + body.state.angular.vel * t;
view.translation.set(pos.x, pos.y);
view.rotation = ang;
},
// extended
render(bodies, meta) {
parent.render.call(this, bodies, meta);
if (!this.two.width) {
this.two.width = this.options.width;
this.two.height = this.options.height;
}
this.two.update();
},
/**
* TwoRenderer#setStyles(path, styles) -> Two.Path
* - path (Two.Path): The path object to set styles on
* - styles (Object): The styles configuration
* + (Two.Path): A path object
*
* Set styles on a Two path
**/
setStyles(path, styles) {
Object.keys(styles).map(key => path[key] = styles[key]);
return path;
},
/**
* TwoRenderer#createCompound(children) -> Two.Group
* - children (Array): An array of bodies to be added to a group
* - styles (Object): An object defining the styles to apply to all children
* + (Two.Group): A Two.Group object
*
* Create a Two.Group where multiple bodies / paths can be added to
**/
createCompound(children, styles) {
const group = this.two.makeGroup();
children.map(child => {
const childView = this.createView(child.g, styles, group);
childView.translation.set(child.pos.x, child.pos.y);
childView.rotation = child.angle;
});
return group;
},
/**
* TwoRenderer#createCircle(x, y, r, styles) -> Two.Circle
* - x (Number): The x coord
* - y (Number): The y coord
* - r (Number): The circle radius
* - props (Object): The styles configuration
* + (Two.Path): A graphic object representing a circle.
*
* Create a circle for use in PIXI stage
**/
createCircle(x, y, r, styles) {
let circle = this.two.makeCircle(x, y, r);
circle = this.setStyles(circle, styles);
return circle;
},
/**
* TwoRenderer#createRect(x, y, width, height, styles) -> Two.Rectangle
* - x (Number): The x coord
* - y (Number): The y coord
* - width (Number): The rectangle width
* - height (Number): The rectangle height
* - styles (Object): The styles configuration
* + (Two.Rectangle): A Two rectangle.
*
* Create a rectangle for use in Two.js
**/
createRect(x, y, width, height, styles) {
let rect = this.two.makeRectangle(x, y, width, height);
rect = this.setStyles(rect, styles);
return rect;
},
/**
* TwoRenderer#createPolygon(verts, styles) -> Two.Path
* - verts (Array): Array of [[Vectorish]] vertices
* - styles (Object): The styles configuration
* + (Two.Path): A graphic object representing a polygon.
*
* Create a polygon for use in PIXI stage
**/
createPolygon(verts, pathParams, styles) {
const anchors = verts.map((vert, i) => {
const { lx, ly, rx, ry, command } = pathParams.anchors[i];
return new Two.Anchor(vert.x, vert.y, lx, ly, rx, ry, command);
});
const path = this.two.makePath(anchors, pathParams.open);
this.setStyles(path, styles);
return path;
},
/**
* TwoRenderer#createLine(from, to, styles) -> Two.Path
* - from (Vectorish): Starting point
* - to (Vectorish): Ending point
* - styles (Object): The styles configuration
* + (Two.Path): A graphic object representing a polygon.
*
* Create a line for use in PIXI stage
**/
createLine(from, to, styles) {
let line = this.two.makeLine(from.x, from.y, to.x, to.y);
line = this.setStyles(line, styles);
return line;
},
// extended
drawMeta(meta) {
if (!this.meta.loaded) {
const offset = 5;
this.meta.fps = new Two.Text(
`FPS: ${meta.fps.toFixed(2)}`, offset,
metaStyles.size + offset
);
this.setStyles(this.meta.fps, metaStyles);
this.meta.ipf = new Two.Text(
`IPF: ${meta.ipf}`, offset,
(metaStyles.size * 2) + offset
);
this.setStyles(this.meta.ipf, metaStyles);
this.two.add(this.meta.fps);
this.two.add(this.meta.ipf);
this.meta.loaded = true;
} else {
this.meta.fps.value = 'FPS: ' + meta.fps.toFixed(2);
this.meta.ipf.value = 'IPF: ' + meta.ipf;
}
},
};
});
// extend convex polygon to allow Two.Polygon params to be passed
Physics.body('convex-polygon', function(parent) {
var defaults = {};
return {
// extended
init: function( options ){
// call parent init method
parent.init.call(this, options);
options = Physics.util.extend({}, defaults, options);
this.geometry = Physics.geometry('convex-polygon', {
vertices: options.vertices,
pathParams: options.pathParams,
});
this.recalc();
},
};
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment