Last active
April 7, 2017 22:10
-
-
Save larrybotha/6af6847199719c41e90452ff78982d85 to your computer and use it in GitHub Desktop.
Two.js Renderer for PhysicsJS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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