Skip to content

Instantly share code, notes, and snippets.

@mathdoodle
Last active July 7, 2020 19:16
Show Gist options
  • Save mathdoodle/07760feb161200d7dd35fe3219a229a2 to your computer and use it in GitHub Desktop.
Save mathdoodle/07760feb161200d7dd35fe3219a229a2 to your computer and use it in GitHub Desktop.
Diagram3D
/**
* Displays an exception by writing it to a <pre> element.
*/
export default function displayError(e: any) {
const stderr = <HTMLPreElement>document.getElementById('error')
stderr.style.color = "#FF0000"
stderr.innerHTML = `${e}`
}
<!doctype html>
<html>
<head>
<!-- STYLES-MARKER -->
<style>
/* STYLE-MARKER */
</style>
<script src='https://jspm.io/[email protected]'></script>
<!-- SHADERS-MARKER -->
<!-- SCRIPTS-MARKER -->
</head>
<body>
<div id='container'>
<canvas id='canvas3D'></canvas>
<canvas id='canvas2D' width='500' height='500'></canvas>
</div>
<pre id='error'></pre>
<script>
// CODE-MARKER
</script>
<script>
System.import('./index.js')
</script>
</body>
</html>
import { e1, e2, e3/*, zero*/ } from './math';
import {
Engine,
Capability,
Scene,
Facet, PerspectiveCamera, DirectionalLight, TrackballControls, Box, Color, Arrow, HTMLScriptsMaterial, Mesh, Diagram3D
} from 'davinci-eight'
// Comment out the following line to use the standard window.requestAnimationFrame
import requestAnimationFrame from './requestAnimationFrame';
import WireCube from './WireCube';
import Overlay from './Overlay';
/**
* Wrapper around the WebGLRenderingContext providing the ContextManager interface.
*/
const engine = new Engine('canvas3D')
.size(500, 500)
.clearColor(0.1, 0.1, 0.1, 1.0)
.enable(Capability.DEPTH_TEST);
/**
* A collection of objects that can be rendered with a single draw method call.
*/
const scene = new Scene(engine)
/**
* Rendering information that applies to all objects.
*/
const ambients: Facet[] = []
/**
* Provides the viewing point and perspective transformation.
*/
const camera = new PerspectiveCamera()
camera.eye.copy(e3).scale(5)
ambients.push(camera)
/**
* Provides a light color and direction for Lambert shading.
*/
const dirLight = new DirectionalLight()
ambients.push(dirLight)
/**
* Controls the camera by accumulating mouse movements then moving and rotating the camera.
*/
const trackball = new TrackballControls(camera, window)
trackball.subscribe(engine.canvas)
trackball.noPan = true;
// Create drawables such as Arrow, Box, Curve, Grid, Sphere, Cylinder.
// Add them to the scene here...
const box = new Box(engine, { color: Color.green })
box.width = 0.6
box.height = 0.6
box.depth = 0.6
// scene.add(box)
const arrowA = new Arrow(engine);
arrowA.color = Color.red
arrowA.axis = e1
scene.add(arrowA);
const arrowB = new Arrow(engine);
arrowB.color = Color.blue
arrowB.axis = e2
scene.add(arrowB);
const geometry = new WireCube(engine)
const material = new HTMLScriptsMaterial(engine, ['line-vs', 'line-fs'], [], document)
const wireCube = new Mesh(geometry, material, engine)
scene.add(wireCube)
const stats = new Stats()
document.body.appendChild(stats.domElement)
// const diagram = new Diagram3D('canvas2D', camera);
const diagram = new Overlay('canvas2D', camera);
/**
* Animates the scene.
*/
const animate = function(_timestamp: number) {
stats.begin()
trackball.update()
dirLight.direction.copy(camera.look).sub(camera.eye)
engine.clear()
diagram.clear();
diagram.fillText("a", arrowA.axis);
diagram.fillText("b", arrowB.axis);
scene.render(ambients)
stats.end()
requestAnimationFrame(animate)
}
requestAnimationFrame(animate)
varying highp vec4 vColor;
void main(void) {
gl_FragColor = vColor;
}
attribute vec3 aPosition;
uniform vec3 uColor;
uniform float uOpacity;
uniform mat4 uModel;
uniform mat4 uProjection;
uniform mat4 uView;
varying highp vec4 vColor;
void main(void) {
gl_Position = uProjection * uView * uModel * vec4(aPosition, 1.0);
vColor = vec4(uColor, uOpacity);
}
import { Geometric3 } from 'davinci-eight'
export const e1 = Geometric3.e1();
export const e2 = Geometric3.e2();
export const e3 = Geometric3.e3();
export const zero = Geometric3.zero();
import { PerspectiveCamera, VectorE3, VectorE2, Geometric3 } from 'davinci-eight'
export default class Overlay {
public ctx: CanvasRenderingContext2D;
constructor(_canvas: string, private camera: PerspectiveCamera) {
const canvasElement = <HTMLCanvasElement> document.getElementById('canvas2D');
this.ctx = canvasElement.getContext('2d') as CanvasRenderingContext2D;
this.ctx.strokeStyle = "#FFFFFF";
this.ctx.fillStyle = '#ffffff';
this.ctx.font = '24px Helvetica';
}
clear(): void {
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
}
fillText(text: string, X: VectorE3, maxWidth?: number) {
const coords = this.canvasCoords(X);
this.ctx.fillText(text, coords.x, coords.y, maxWidth);
}
beginPath() {
this.ctx.beginPath();
}
moveTo(X: VectorE3) {
const coords = this.canvasCoords(X);
this.ctx.moveTo(coords.x, coords.y);
}
lineTo(X: VectorE3) {
const coords = this.canvasCoords(X);
this.ctx.lineTo(coords.x, coords.y);
}
stroke() {
this.ctx.stroke();
}
private canvasCoords(X: VectorE3): VectorE2 {
const camera = this.camera;
const cameraCoords = view(Geometric3.fromVector(X), camera.eye, camera.look, camera.up);
const N = camera.near;
const F = camera.far;
const θ = camera.fov;
const aspect = camera.aspect;
const canonCoords = perspective(cameraCoords, N, F, θ, aspect);
const x = (canonCoords.x + 1) * this.ctx.canvas.width / 2;
const y = (canonCoords.y - 1) * -this.ctx.canvas.height / 2;
return { x, y }
}
}
/**
* View transformation converts world coordinates to camera frame coordinates.
*/
function view(X: Geometric3, eye: Geometric3, look: Geometric3, up: Geometric3): VectorE3 {
const n = (eye - look).normalize();
const u = up.clone().cross(n).normalize();
const v = n.clone().cross(u);
const du = - eye | u;
const dv = - eye | v;
const dn = - eye | n;
const x = (X | u + du).a;
const y = (X | v + dv).a;
const z = (X | n + dn).a;
return { x, y, z };
}
/**
* Perspective transformation projects camera coordinates onto the near plane.
*/
function perspective(X: VectorE3, N: number, F: number, fov: number, aspect: number): VectorE3 {
const t = N * Math.tan(fov / 2);
const b = -t;
const r = aspect * t;
const l = -r;
const x = ((2 * N) * X.x + (r + l) * X.z) / (-X.z * (r - l));
const y = ((2 * N) * X.y + (t + b) * X.z) / (-X.z * (t - b));
const z = (-(F + N) * X.z - 2 * F * N) / (-X.z * (F - N))
return { x, y, z };
}
{
"description": "Diagram3D",
"dependencies": {
"stats.js": "0.16.0",
"davinci-eight": "7.4.4"
},
"operatorOverloading": true,
"name": "copy-of-copy-of-eight-starter-template",
"version": "0.1.0",
"keywords": [
"EIGHT",
"Overlay",
"2D",
"WebGL",
"3D"
],
"author": "David Geo Holmes"
}
import displayError from './displayError'
/**
* Catches exceptions thrown in the animation callback and displays them.
* This function will have a slight performance impact owing to the try...catch statement.
* This function may be bypassed for production use by using window.requestAnimationFrame directly.
*/
export default function requestAnimationFrame(callback: FrameRequestCallback): number {
const wrapper: FrameRequestCallback = function(time: number) {
try {
callback(time)
}
catch(e) {
displayError(e)
}
}
return window.requestAnimationFrame(wrapper)
}
body { margin: 0; }
#container {
position: relative;
}
#canvas3D {
position: absolute;
left: 0px;
top: 0px;
width: 500px;
height: 500px;
}
#canvas2D {
position: absolute;
left: 0px;
top: 0px;
z-index: 10;
width: 500px;
height: 500px;
/**
* Allow events to go to the other elements
*/
pointer-events: none;
}
#stats { position: absolute; top: 0; left: 0; }
{
"allowJs": true,
"checkJs": true,
"declaration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"jsx": "react",
"module": "system",
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"preserveConstEnums": true,
"removeComments": false,
"skipLibCheck": true,
"sourceMap": true,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"target": "es5",
"traceResolution": true
}
import { Geometry, Engine, Material, Usage, DataType, BeginMode } from 'davinci-eight'
/**
* A Geometry for rendering a cube made from lines.
*/
export default class WireCube implements Geometry {
private buffer: WebGLBuffer;
public data: Float32Array;
private material: Material | null = null;
/**
*
*/
public invalid = true;
constructor(private engine: Engine) {
const gl = engine.gl;
const size = 1;
const L = size / 2;
this.data = new Float32Array([
-L, -L, -L, +L, -L, -L,
-L, +L, -L, +L, +L, -L,
-L, -L, +L, +L, -L, +L,
-L, +L, +L, +L, +L, +L,
-L, +L, +L, -L, +L, -L,
+L, +L, +L, +L, +L, -L,
-L, -L, +L, -L, -L, -L,
+L, -L, +L, +L, -L, -L,
-L, -L, -L, -L, +L, -L,
+L, -L, -L, +L, +L, -L,
-L, -L, +L, -L, +L, +L,
+L, -L, +L, +L, +L, +L
]);
this.buffer = gl.createBuffer() as WebGLBuffer;
}
bind(material: Material): void {
this.material = material;
const gl = this.engine.gl;
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
}
unbind(_material: Material): void {
this.material = null
const gl = this.engine.gl;
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
draw(): void {
const gl = this.engine.gl;
const aPosition = (this.material as Material).getAttribLocation('aPosition');
if (this.invalid) {
gl.bufferData(gl.ARRAY_BUFFER, this.data, Usage.STATIC_DRAW);
this.invalid = false;
}
gl.vertexAttribPointer(aPosition, 3, DataType.FLOAT, true, 0, 0);
gl.enableVertexAttribArray(aPosition);
gl.drawArrays(BeginMode.LINES, 0, 24);
gl.disableVertexAttribArray(aPosition);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment