Last active
August 8, 2016 02:56
-
-
Save mathdoodle/817aace27e05804fc2833709e33630e9 to your computer and use it in GitHub Desktop.
Local Physics Basics
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
const FLOATS_PER_VERTEX = 3; | |
const BYTES_PER_FLOAT = 4; | |
const STRIDE = BYTES_PER_FLOAT * FLOATS_PER_VERTEX; | |
function primitive(): EIGHT.Primitive { | |
const aPosition: EIGHT.Attribute = { | |
values: [ | |
[0,+0.1,0, 0,-0.1,0, -0.1,0,0, +0.1,0,0], // center | |
[0,+1,0, -1,-1,0], // LHS | |
[0,+1,0, +1,-1,0], // RHS | |
[-1,-1,0, +1,-1,0] // BASE | |
].reduce(function(a,b){return a.concat(b)}), | |
size: 3, | |
type: EIGHT.DataType.FLOAT | |
}; | |
const result: EIGHT.Primitive = { | |
mode: EIGHT.BeginMode.LINES, | |
attributes: { | |
} | |
}; | |
result.attributes['aPosition'] = aPosition; | |
return result; | |
} | |
/** | |
* The geometry of the Bug is static so we use the conventional | |
* approach based upon GeometryArrays | |
*/ | |
class BugGeometry extends EIGHT.GeometryArrays { | |
private w = 1 | |
private h = 1 | |
private d = 1 | |
constructor(private contextManager: EIGHT.ContextManager) { | |
super(primitive(), contextManager); | |
} | |
getPrincipalScale(name: string): number { | |
switch (name) { | |
case 'width': { | |
return this.w | |
} | |
case 'height': { | |
return this.h | |
} | |
case 'depth': { | |
return this.d | |
} | |
default: { | |
throw new Error(`getPrincipalScale(${name}): name is not a principal scale property.`) | |
} | |
} | |
} | |
setPrincipalScale(name: string, value: number): void { | |
switch (name) { | |
case 'width': { | |
this.w = value | |
} | |
break | |
case 'height': { | |
this.h = value | |
} | |
break | |
case 'depth': { | |
this.d = value | |
} | |
break | |
default: { | |
throw new Error(`setPrinciplaScale(${name}): name is not a principal scale property.`) | |
} | |
} | |
this.setScale(this.w, this.h, this.d) | |
} | |
} | |
export default class Bug extends EIGHT.RigidBody { | |
constructor(contextManager: EIGHT.ContextManager) { | |
super(new BugGeometry(contextManager), new EIGHT.LineMaterial(void 0, contextManager), contextManager, {x:0,y:0,z:1}) | |
this.height = 1; | |
this.width = 1; | |
} | |
get width() { | |
return this.getPrincipalScale('width'); | |
} | |
set width(width: number) { | |
this.setPrincipalScale('width', width); | |
} | |
/** | |
* | |
*/ | |
get height() { | |
return this.getPrincipalScale('height'); | |
} | |
set height(height: number) { | |
this.setPrincipalScale('height', height); | |
} | |
} |
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
import Steppable from './Steppable'; | |
import Stepper from './Stepper'; | |
export default class Forward implements Steppable { | |
constructor(private body: EIGHT.RigidBody, private distance: number) { | |
} | |
stepper(): Stepper { | |
return new ForwardStepper(this.body, this.distance); | |
} | |
} | |
class ForwardStepper implements Stepper { | |
private todo = true; | |
constructor(private body: EIGHT.RigidBody, private distance: number) { | |
} | |
hasNext(): boolean { | |
return this.todo; | |
} | |
next(): void { | |
if (this.todo) { | |
// We're assuming that P is a heading unit vector. | |
// We need to change out the model representation. | |
this.body.X.add(this.body.P, this.distance); | |
this.todo = false; | |
} | |
} | |
} |
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<!-- STYLES-MARKER --> | |
<style> | |
/* STYLE-MARKER */ | |
</style> | |
<script src='https://jspm.io/system.js'></script> | |
<!-- SHADERS-MARKER --> | |
<!-- SCRIPTS-MARKER --> | |
</head> | |
<body> | |
<canvas id='my-canvas'></canvas> | |
<script> | |
// CODE-MARKER | |
</script> | |
<script> | |
System.import('./index.js') | |
</script> | |
</body> | |
</html> |
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
import Bug from './Bug'; | |
import Forward from './Forward' | |
import Repeat from './Repeat' | |
import Rotate from './Rotate' | |
import Steppable from './Steppable'; | |
import SteppableList from './SteppableList'; | |
import Track from './Track'; | |
import World from './World' | |
const e1 = EIGHT.Geometric3.e1() | |
const e2 = EIGHT.Geometric3.e2() | |
const e3 = EIGHT.Geometric3.e3() | |
/** | |
* The space will have a natural scale of [-SIZE, SIZE]. | |
*/ | |
const SIZE = 2; | |
/** | |
* A brave new World. | |
*/ | |
const world = new World(SIZE) | |
const body = new Bug(world.engine) | |
body.height = 0.1 | |
body.width = 0.0618 | |
body.color = new EIGHT.Color(0.8984, 0.1133, 0.3711) | |
const track = new Track(world.engine); | |
// const gridXY = world.createGridXY({segments: 20, min: -SIZE/2, max: +SIZE/2}) | |
// gridXY.color = new EIGHT.Color(0.4, 0.4, 0.4) | |
world.reset() | |
world.planView() | |
/** | |
* Use to control the speed of the animation. | |
* Roughly the number of frames used to execute a motion step. | |
*/ | |
const N = 40; | |
const list = new SteppableList() | |
list.add(new Repeat(new Forward(body, 1 / N), N)) | |
list.add(new Repeat(new Rotate(body, e1 ^ e2, Math.PI/(2*N)), N)) | |
const program = new Repeat(list, 4) | |
let stepper = program.stepper(); | |
const animate = function() { | |
world.beginFrame() | |
if (world.time === 0) { | |
// Initialize | |
body.X.scale(0) | |
body.P.copy(e2).normalize() | |
track.erase() | |
track.addPoint(body.X.x, body.X.y, body.X.z) | |
// We also need a new stepper. | |
stepper = program.stepper() | |
} | |
if (world.running && stepper.hasNext()) { | |
// We'll count steps. | |
// All that matters is that time moves on from zero so we can reset. | |
world.time = world.time + 1; | |
stepper.next(); | |
track.addPoint(body.X.x, body.X.y, body.X.z) | |
} | |
body.render(world.ambients) | |
track.render(world.ambients) | |
world.draw() | |
// This call keeps the animation going. | |
requestAnimationFrame(animate) | |
} | |
// This call starts the animation. | |
requestAnimationFrame(animate) |
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
{ | |
"description": "Local Physics Basics", | |
"dependencies": { | |
"DomReady": "1.0.0", | |
"jasmine": "2.4.1", | |
"davinci-eight": "2.245.0", | |
"dat-gui": "0.5.0", | |
"stats.js": "0.16.0" | |
}, | |
"name": "copy-of-Vector Modeling Problem Framework", | |
"version": "0.1.0", | |
"keywords": [ | |
"EIGHT", | |
"project", | |
"Getting", | |
"Started", | |
"WebGL" | |
], | |
"operatorOverloading": true, | |
"author": "David Geo Holmes" | |
} |
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
import Steppable from './Steppable'; | |
import Stepper from './Stepper'; | |
export default class Repeat implements Steppable { | |
constructor(private steppable: Steppable, private N: number) { | |
} | |
stepper(): Stepper { | |
return new RepeatStepper(this.steppable, this.N); | |
} | |
} | |
class RepeatStepper implements Stepper { | |
private i = 0; | |
private stepper: Stepper; | |
constructor(private steppable: Steppable, private N: number) { | |
this.stepper = steppable.stepper(); | |
} | |
hasNext() { | |
if (this.stepper.hasNext()) { | |
return true; | |
} | |
else { | |
if (this.i < this.N - 1) { | |
this.i++; | |
this.stepper = this.steppable.stepper(); | |
return this.hasNext(); | |
} | |
else { | |
return false; | |
} | |
} | |
} | |
next(): void { | |
if (this.hasNext()) { | |
this.stepper.next() | |
} | |
} | |
} |
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
import Steppable from './Steppable' | |
import Stepper from './Stepper' | |
/** | |
* Scratch variable to avoid creating temporary objects. | |
*/ | |
const R = EIGHT.Geometric3.one() | |
export default class Rotate implements Steppable { | |
constructor(private body: EIGHT.RigidBody, private B: EIGHT.Geometric3, private θ: number) { | |
} | |
stepper(): Stepper { | |
return new RotateStepper(this.body, this.B, this.θ); | |
} | |
} | |
class RotateStepper implements Stepper { | |
private todo = true; | |
constructor(private body: EIGHT.RigidBody, private B: EIGHT.Geometric3, private θ: number) { | |
} | |
hasNext() { | |
return this.todo; | |
} | |
next(): void { | |
if (this.todo) { | |
this.todo = false; | |
R.rotorFromGeneratorAngle(this.B, this.θ); | |
this.body.P.rotate(R); | |
this.body.R = R * this.body.R; | |
} | |
} | |
} |
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
import Stepper from './Stepper'; | |
/** | |
* Capable of being traversed or controlled by steps. | |
*/ | |
interface Steppable { | |
stepper(): Stepper; | |
} | |
export default Steppable; |
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
import Steppable from './Steppable'; | |
import Stepper from './Stepper'; | |
export default class SteppableList implements Steppable { | |
private steppables: Steppable[] = []; | |
constructor() { | |
} | |
stepper(): Stepper { | |
return new StepperList(this.steppables.map(function(steppable){return steppable.stepper();})); | |
} | |
add(steppable: Steppable): void { | |
this.steppables.push(steppable); | |
} | |
} | |
class StepperList implements Stepper { | |
private i = 0; | |
private N: number; | |
constructor(private steppers: Stepper[]) { | |
this.N = steppers.length; | |
} | |
hasNext(): boolean { | |
if (this.i < this.N) { | |
if (this.steppers[this.i].hasNext()) { | |
return true; | |
} | |
else { | |
this.i++; | |
return this.hasNext(); | |
} | |
} | |
else { | |
return false; | |
} | |
} | |
next(): void { | |
if (this.hasNext()) { | |
this.steppers[this.i].next(); | |
} | |
} | |
} | |
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
/** | |
* A device that moves or rotates in a series of small discrete steps. | |
*/ | |
interface Stepper { | |
hasNext(): boolean; | |
next(): void; | |
} | |
export default Stepper; |
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
body { | |
background-color: white; | |
} |
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<!-- STYLES-MARKER --> | |
<style> | |
/* STYLE-MARKER */ | |
</style> | |
<script src='https://jspm.io/system.js'></script> | |
<!-- SCRIPTS-MARKER --> | |
</head> | |
<body> | |
<script> | |
// CODE-MARKER | |
</script> | |
<script> | |
System.import('./tests.js') | |
</script> | |
</body> | |
</html> |
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
import Vector3 from './Vector3.spec' | |
window['jasmine'] = jasmineRequire.core(jasmineRequire) | |
jasmineRequire.html(window['jasmine']) | |
const env = jasmine.getEnv() | |
const jasmineInterface = jasmineRequire.interface(window['jasmine'], env) | |
extend(window, jasmineInterface) | |
const htmlReporter = new jasmine.HtmlReporter({ | |
env: env, | |
getContainer: function() { return document.body }, | |
createElement: function() { return document.createElement.apply(document, arguments) }, | |
createTextNode: function() { return document.createTextNode.apply(document, arguments) }, | |
timer: new jasmine.Timer() | |
}) | |
env.addReporter(htmlReporter) | |
DomReady.ready(function() { | |
htmlReporter.initialize() | |
describe("Vector3", Vector3) | |
env.execute() | |
}) | |
/* | |
* Helper function for extending the properties on objects. | |
*/ | |
export default function extend<T>(destination: T, source: any): T { | |
for (let property in source) { | |
destination[property] = source[property] | |
} | |
return destination | |
} |
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
const FLOATS_PER_VERTEX = 3; | |
const BYTES_PER_FLOAT = 4; | |
const STRIDE = BYTES_PER_FLOAT * FLOATS_PER_VERTEX; | |
/** | |
* | |
*/ | |
class LineGeometry implements EIGHT.Geometry { | |
scaling = EIGHT.Matrix4.one(); | |
private data: Float32Array; | |
private count = 0; | |
private N = 2; | |
private dirty = true; | |
private vbo: EIGHT.VertexBuffer; | |
private refCount = 1; | |
private contextProvider: EIGHT.ContextProvider; | |
constructor(private contextManager: EIGHT.ContextManager) { | |
this.data = new Float32Array(this.N * FLOATS_PER_VERTEX); | |
this.vbo = new EIGHT.VertexBuffer(contextManager); | |
} | |
bind(material: EIGHT.Material): LineGeometry { | |
if (this.dirty) { | |
this.vbo.bufferData(this.data, EIGHT.Usage.DYNAMIC_DRAW); | |
this.dirty = false; | |
} | |
this.vbo.bind(); | |
const aPosition = material.getAttrib('aPosition'); | |
aPosition.config(FLOATS_PER_VERTEX, EIGHT.DataType.FLOAT, true, STRIDE, 0); | |
aPosition.enable(); | |
return this; | |
} | |
unbind(material: EIGHT.Material): LineGeometry { | |
const aPosition = material.getAttrib('aPosition'); | |
aPosition.disable(); | |
this.vbo.unbind() | |
return this; | |
} | |
draw(material: EIGHT.Material): LineGeometry { | |
// console.log(`LineGeometry.draw(${this.i})`) | |
this.contextProvider.gl.drawArrays(EIGHT.BeginMode.LINE_STRIP, 0, this.count); | |
return this; | |
} | |
getPrincipalScale(name: string): number { | |
throw new Error("LineGeometry.getPrincipalScale"); | |
} | |
hasPrincipalScale(name: string): boolean { | |
throw new Error("LineGeometry.hasPrincipalScale"); | |
} | |
setPrincipalScale(name: string, value: number): void { | |
throw new Error("LineGeometry.setPrincipalScale"); | |
} | |
contextFree(contextProvider: EIGHT.ContextProvider): void { | |
this.vbo.contextFree(contextProvider); | |
} | |
contextGain(contextProvider: EIGHT.ContextProvider): void { | |
this.contextProvider = contextProvider; | |
this.vbo.contextGain(contextProvider); | |
} | |
contextLost(): void { | |
this.vbo.contextLost(); | |
} | |
addRef(): number { | |
this.refCount++; | |
return this.refCount; | |
} | |
release(): number { | |
this.refCount--; | |
if (this.refCount === 0) { | |
// Clean Up | |
} | |
return this.refCount; | |
} | |
addPoint(x: number, y: number, z: number): void { | |
if (this.count === this.N) { | |
this.N = this.N * 2; | |
const temp = new Float32Array(this.N * FLOATS_PER_VERTEX); | |
temp.set(this.data) | |
this.data = temp; | |
} | |
const offset = this.count * FLOATS_PER_VERTEX; | |
this.data[offset + 0] = x; | |
this.data[offset + 1] = y; | |
this.data[offset + 2] = z; | |
this.count++; | |
this.dirty = true; | |
} | |
erase(): void { | |
this.count = 0; | |
} | |
} | |
export default class Track extends EIGHT.Mesh { | |
constructor(contextManager: EIGHT.ContextManager) { | |
super(new LineGeometry(contextManager), new EIGHT.LineMaterial(void 0, contextManager), contextManager) | |
} | |
addPoint(x: number, y: number, z: number): void { | |
const geometry = <LineGeometry>this.geometry; | |
geometry.addPoint(x, y, z); | |
geometry.release(); | |
} | |
erase(): void { | |
const geometry = <LineGeometry>this.geometry; | |
geometry.erase(); | |
geometry.release(); | |
} | |
} |
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
import Vector3 from './Vector3'; | |
export default function() { | |
describe("constructor", function() { | |
const x = Math.random(); | |
const y = Math.random(); | |
const z = Math.random(); | |
const v = new Vector3(x, y, z); | |
it("should initialize x-coordinate", function() { | |
expect(v.x).toBe(x) | |
}) | |
}) | |
} |
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
export default class Vector3 { | |
constructor(public x = 0, public y = 0, public z = 0) { | |
} | |
} |
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
const origin = EIGHT.Geometric3.vector(0, 0, 0) | |
const e1 = EIGHT.Geometric3.vector(1, 0, 0) | |
const e2 = EIGHT.Geometric3.vector(0, 1, 0) | |
const e3 = EIGHT.Geometric3.vector(0, 0, 1) | |
export default class World { | |
public engine: EIGHT.Engine; | |
private scene: EIGHT.Scene; | |
public ambients: EIGHT.Facet[] = []; | |
private camera: EIGHT.PerspectiveCamera; | |
private trackball: EIGHT.TrackballControls; | |
private dirLight: EIGHT.DirectionalLight; | |
private gui: dat.GUI; | |
/** | |
* An flag that determines whether the simulation should move forward. | |
*/ | |
public running = false; | |
/** | |
* Universal Newtonian Time. | |
*/ | |
public time = 0; | |
/** | |
* Creates a new Worls containg a WebGL canvas, a camera, lighting, | |
* and controllers. | |
*/ | |
constructor(private size: number) { | |
this.engine = new EIGHT.Engine('my-canvas') | |
.size(500, 500) | |
.clearColor(0.1, 0.1, 0.1, 1.0) | |
.enable(EIGHT.Capability.DEPTH_TEST); | |
this.engine.gl.lineWidth(1) | |
this.scene = new EIGHT.Scene(this.engine); | |
this.camera = new EIGHT.PerspectiveCamera(); | |
this.ambients.push(this.camera) | |
this.dirLight = new EIGHT.DirectionalLight(); | |
this.ambients.push(this.dirLight) | |
this.trackball = new EIGHT.TrackballControls(this.camera, window) | |
// Subscribe to mouse events from the canvas. | |
this.trackball.subscribe(this.engine.canvas) | |
this.gui = new dat.GUI({name: 'Yahoo'}); | |
const simFolder = this.gui.addFolder("Simulation") | |
simFolder.add(this, 'start'); | |
simFolder.add(this, 'stop'); | |
simFolder.add(this, 'reset'); | |
simFolder.open(); | |
const cameraFolder = this.gui.addFolder("Camera") | |
cameraFolder.add(this, 'planView'); | |
cameraFolder.add(this, 'sideView'); | |
cameraFolder.open(); | |
this.sideView(); | |
} | |
/** | |
* This method should be called at the beginning of an animation frame. | |
* It performs the following tasks: | |
* 1. Clears the graphics output. | |
* 2. Updates the camera based upon movements of the mouse. | |
* 3. Aligns the directional light with the viewing direction. | |
*/ | |
beginFrame(): void { | |
this.engine.clear(); | |
// Update the camera based upon mouse events received. | |
this.trackball.update(); | |
// Keep the directional light pointing in the same direction as the camera. | |
this.dirLight.direction.copy(this.camera.look).sub(this.camera.eye) | |
} | |
/** | |
* This method should be called after objects have been moved. | |
*/ | |
draw(): void { | |
this.scene.draw(this.ambients); | |
} | |
/** | |
* Puts the simulation into the running state. | |
*/ | |
start(): void { | |
this.running = true | |
} | |
stop(): void { | |
this.running = false | |
} | |
/** | |
* Resets the universal time property back to zero. | |
*/ | |
reset(): void { | |
this.running = false | |
this.time = 0 | |
} | |
planView(): void { | |
this.camera.eye.copy(e3).normalize().scale(this.size * 1.4) | |
this.camera.look.copy(origin) | |
this.camera.up.copy(e2) | |
} | |
sideView(): void { | |
this.camera.eye.sub2(e3, e2).normalize().scale(this.size * 1.6) | |
this.camera.look.copy(origin) | |
this.camera.up.copy(e3) | |
} | |
/** | |
* Convenience method for creating a grid in the xy-plane. | |
*/ | |
createGridXY(options: { | |
segments?: number; | |
min?: number; | |
max?: number; | |
} = {}): EIGHT.Grid { | |
const grid = new EIGHT.Grid({ | |
uSegments: options.segments, | |
uMin: options.min, | |
uMax: options.max, | |
vSegments: options.segments, | |
vMin: options.min, | |
vMax: options.max | |
}); | |
this.scene.add(grid); | |
return grid; | |
} | |
createBox(): EIGHT.Box { | |
const box = new EIGHT.Box(); | |
this.scene.add(box); | |
return box; | |
} | |
/** | |
* Convenience method for creating a sphere. | |
*/ | |
createSphere(options: {radius?: number} = {}): EIGHT.Sphere { | |
const sphere = new EIGHT.Sphere({radius: options.radius}); | |
this.scene.add(sphere); | |
return sphere; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment