Skip to content

Instantly share code, notes, and snippets.

@mathdoodle
Last active August 29, 2015 14:23
Show Gist options
  • Save mathdoodle/7dd6392c92f5d95731bc to your computer and use it in GitHub Desktop.
Save mathdoodle/7dd6392c92f5d95731bc to your computer and use it in GitHub Desktop.
Geometric Algebra Introduction
{
"uuid": "1b898dc7-8971-4fc2-a04e-f750b87b6e50",
"description": "Geometric Algebra Introduction",
"dependencies": {
"angular": "1.4.0",
"davinci-mathbox": "latest",
"DomReady": "latest",
"davinci-blade": "1.1.1"
},
"operatorOverloading": false
}
<!doctype html>
<html ng-app='doodle'>
<head>
<meta charset='utf-8'/>
<link rel='stylesheet' href='//netdna.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css'>
<style>
/* STYLE-MARKER */
</style>
<script type="text/javascript" src="http://use.typekit.com/nde6wmn.js"></script>
<script type="text/javascript">try{Typekit.load();}catch(e){}</script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
"HTML-CSS": { availableFonts: ["TeX"] },
extensions: ["tex2jax.js"],
jax: ["input/TeX","output/HTML-CSS"],
tex2jax: {inlineMath: [["$","$"],["\\(","\\)"]]},
messageStyle: 'none'
});
</script>
<script type="text/javascript"
src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>
<!-- SCRIPTS-MARKER -->
</head>
<body>
<div class='container'>
<div class='page-header'>
<h1><a href='#'>Origins of Geometric Algebra</a></h1>
</div>
<div ng-controller='my-section-controller'>
<h2>Geometry as Physics ({{step+1}} / {{maxSteps}})</h2>
<!--
<label class='checkbox-inline'>
<input type='checkbox' ng-model='audio'/>Audio
</label>
<button ng-click='prev()' ng-show='true'>Back</button>
<button ng-click='next()' ng-show='true'>Next</button>
-->
<div id='info' class="transition">{{text()}}</div>
<div id='steps'>
</div>
<!-- The following div will be the parent of the canvas -->
<div id='zeno'></div>
</div>
</div>
<script>
// CODE-MARKER
</script>
</body>
</html>
var TAU = Math.PI * 2;
var HALF_TURN = Math.PI;
var QUARTER_TURN = Math.PI / 2;
var EIGHTH_TURN = Math.PI / 4;
/**
* Normalize angle so that it conforms to usual spherical coordinate conventions.
*/
function phi(angle: number): number {
return -angle;
}
/**
*
*/
function theta(angle: number): number {
return Math.PI/2 -angle;
}
/**
* This is the scope for MySectionController.
*/
interface MySectionScope extends angular.IScope {
audio: boolean;
prev(): void;
next(): void;
/**
* Returns the text for the current step.
*/
text(): string;
/**
* The step is zero-based. Be careful because I think the director is 1-based.
*/
step: number;
maxSteps: number;
/**
* Experiment with the standard duration for transitions.
*/
duration: number;
}
/**
* Define the structure that we will use for the slideshow.
*/
interface Step {
text: string;
mbox: {}[][]
}
angular.module('doodle', [])
.controller('my-section-controller', ['$scope', function($scope: MySectionScope) {
// The director is only visible within the controller. i.e. strictly private.
var director: MathBox.Director;
$scope.audio = false;
$scope.duration = 500;
$scope.step = 0;
// TODO: Can we merge the text with the slides so that it is synchronized?
var steps: Step[] = scriptBuilder($scope);
$scope.maxSteps = steps.length;
ThreeBox.preload([
'../shaders/MathBox.glsl.html'
], function(assets) {
// We want to try to put this on our own canvas, instead of spawning a new one.
// It seems that something in the stack uses the identified element as the parent.
var canvasParent = document.getElementById('zeno');
// Something about mathBox is eating the mouse events.
var mathbox = mathBox(canvasParent, {
cameraControls: false, // We don't want camera controls for a slideshow. This is actually the defauklt
cursor: false,
controlClass: undefined, // ThreeBox.OrbitControls, // Trying to see if OrbitControls is eating the mouse events.
elementResize: true,
fullscreen: true,
screenshot: true,
stats: false,
scale: 1,
}).start();
director = new MathBox.Director(mathbox, steps.map(function(step: Step){return step.mbox}));
// The Director crashes if the steps div is missing!!!
document.getElementById('steps').style.opacity = '0';
// This isn't really what we want from AngularJS.
window.addEventListener('touchstart', function (e) {
$scope.$apply(function() {
$scope.next();
});
});
window.addEventListener('keydown', function (e) {
if (e.keyCode == 38 || e.keyCode == 37) {
// This is not AngularJS triggered, so we apply to kick-off a digest cycle.
$scope.$apply(function() {
$scope.prev();
});
}
else if (e.keyCode == 40 || e.keyCode == 39) {
// This is not AngularJS triggered, so we apply to kick-off a digest cycle.
$scope.$apply(function() {
$scope.next();
});
}
else {
return;
}
});
setUp(mathbox, assets);
$scope.$apply(function() {
go(0);
});
});
/**
* This function is idempotent, so it will be smart about reaching the end.
*/
function go(moves: number) {
// Don't allow the step to go out of bounds.
var prevStep = $scope.step;
$scope.step = Math.max(0, Math.min($scope.maxSteps - 1, $scope.step + moves));
director && director.go($scope.step + 1);
if ($scope.audio && $scope.step !== prevStep && $scope.step > prevStep) {
var utterance = $scope.text();
if (utterance) {
var msg = new window['SpeechSynthesisUtterance']($scope.text())
window['speechSynthesis'].speak(msg);
}
}
}
$scope.text = function(): string {
return steps[$scope.step].text;
}
$scope.prev = function() {
go(-1);
}
$scope.next = function() {
go(+1);
}
}]);
/**
* Build the script
*/
function scriptBuilder($scope: MySectionScope): Step[] {
var e1 = blade.vectorE3(1,0,0);
var e2 = blade.vectorE3(0,1,0);
var e3 = blade.vectorE3(0,0,1);
var a = blade.vectorE3(0.2,0.5,0.4);
var b = blade.vectorE3(0.5,0.1,0.6);
var COLOR_RED = 0xff0000;
var COLOR_GREEN = 0x00ff00;
var COLOR_BLUE = 0x0000ff;
var ID_AXIS_X = 'axis-x';
var ID_AXIS_Y = 'axis-y';
var ID_AXIS_Z = 'axis-z';
var ID_PLANE_XY = 'plane-xy';
var ID_PLANE_YZ = 'plane-yz';
var ID_PLANE_ZX = 'plane-zx';
var ID_VECTOR_A = 'vector-a';
var ID_VECTOR_B = 'vector-b';
var ID_VECTOR_C = 'vector-c';
var ID_LINE_TO_XY = 'line-to-xy';
var ID_LINE_TO_YZ = 'line-to-yz';
var ID_LINE_TO_ZX = 'line-to-zx';
return [
{
text: "Use the keyboard arrow keys to step through.",
mbox: []
},
{
text: "Most Mathematics of use in physics has origins in successful attacks on physical problems.",
mbox: []
},
{
text: "Euclid's systematic formulation of Greek geometry (in 300BC) was the first comprehensive theory of the physical world.",
mbox: []
},
{
text: "Euclid began his famous book, The Elements, by identifying the objects of study. These are points, lines, circles and angles.",
mbox: []
},
{
text: "Next, he defined a small number of operations that could be performed.",
mbox: []
},
{
text: "1. A straight line segment can be drawn joining any two points.",
mbox: []
},
{
text: "2. Any straight line segment can be extended indefinitely in a straight line.",
mbox: []
},
{
text: "3. Given any straight line segment, a circle can be drawn having the segment as radius and one endpoint as center.",
mbox: []
},
{
text: "4. All right angles are congruent.",
mbox: []
},
{
text: "5. If two lines are drawn which intersect a third in such a way that the sum of the inner angles on one side is less than two right angles, then the two lines inevitably must intersect each other on that side if extended far enough.",
mbox: []
},
{
text: "We start with the xy plane.",
mbox: [
grid(ID_PLANE_XY, [0,1]),
// axis(ID_AXIS_X, 0, COLOR_RED, $scope.duration),
// axis(ID_AXIS_Y, 1, COLOR_GREEN, $scope.duration)
]
},
{
text: "Let's take a look at the plane from 3D!",
mbox: [
['animate', 'camera', {theta: theta(QUARTER_TURN * 4/5), phi: phi(QUARTER_TURN*1/3)}, {delay: 0, duration:1500}]
]
},
{
text: "We'll throw in the z axis.",
mbox: [
axis(ID_AXIS_Z, 2, COLOR_BLUE, $scope.duration)
]
},
{
text: "",
mbox: [
grid(ID_PLANE_ZX, [2, 0]),
]
},
{
text: "",
mbox: [
]
},
{
text: "",
mbox: [
grid(ID_PLANE_YZ, [1, 2]),
]
},
{
text: "",
mbox: [
remove(id(ID_PLANE_XY)),
remove(id(ID_PLANE_YZ)),
remove(id(ID_PLANE_ZX))
]
},
{
text: "",
mbox: [
fade(id(ID_AXIS_X)),
fade(id(ID_AXIS_Y)),
fade(id(ID_AXIS_Z)),
]
},
{
text: "",
mbox: [
positionVector(ID_VECTOR_A, a, COLOR_RED),
positionVector(ID_VECTOR_B, b, COLOR_GREEN)
]
},
{
text: "",
mbox: [
lineFromPositionToGridPlane(ID_LINE_TO_XY, a, e1 ^ e2),
lineFromPositionToGridPlane(ID_LINE_TO_YZ, a, e2 ^ e3),
lineFromPositionToGridPlane(ID_LINE_TO_ZX, a, e3 ^ e1)
]
}
];
}
function id(name: string): string {
return '#' + name;
}
function animate(selector: string) {
return ['animate', selector, {color: 0xCCCCCC}, {delay: 0, duration:1500}];
}
function axis(id: string, axis: number, color: number, duration: number): {}[] {
var options: MathBox.AxisOptions = {};
return ['add', 'axis', {id: id, axis: axis, color:color, ticks:0}, {duration:duration}]
}
function grid(id: string, axis: number[]): {}[] {
return ['add','grid',{id: id, axis: axis},{duration:1500}];
}
function fade(selector: string) {
return animate(selector);
}
/**
* Adds a position vector ( a vector based at the origin).
*/
function positionVector(id: string, v: {x: number; y: number; z: number}, color: number): {}[] {
var options: MathBox.VectorOptions = {};
options.id = id;
options.color = color;
options.data = [[0,0,0],[v.x, v.y, v.z]];
options.lineWidth = 4;
return ['add','vector', options, {duration:1500}];
}
function lineFromPositionToGridPlane(id: string, a: blade.Euclidean3, B: blade.Euclidean3): {}[] {
var options: MathBox.CurveOptions = {};
options.id = id;
options.n = 10;
// options.points = true;
// options.pointSize = 4;
options.line = true;
options.lineWidth = 2;
options.color = 0xc0c0c0;
options.expression = function(x: number, index: number) {
var d = projectOntoBivector(a, B);
var e = midpoint(d, a, x);
return [e.x, e.y, e.z]
};
return ['add','curve', options];
}
function projectOntoBivector(v: blade.Euclidean3, B: blade.Euclidean3) {
var unitB = B / B.norm();
var c = v << unitB;
var R = Math.cos(Math.PI/4) + unitB * Math.sin(Math.PI / 4);
return R * c * ~R;
}
function remove(id: string) {
return ['remove', id, {duration:1500}];
}
/**
* Computes a point along a line that runs through a start and finish point.
*/
function midpoint(start: blade.Euclidean3, finish: blade.Euclidean3, f: number): blade.Euclidean3 {
return start + f * (finish - start);
}
// Base plane of surface
function flat(a: number, b: number, x: number, y: number): number[] {
return [a/3, 0.001, b/3];
}
/**
* Construct the viewport, camera, and all the objects in the stage.
*/
function setUp(stage: MathBox.Stage, assets: {[name:string]:any}) {
stage
// Euler rotation of negative quarter turn about x-axis puts us into a more mathematical perspective.
.viewport({type: 'cartesian',rotation:[-QUARTER_TURN, 0, 0]})
// Unfortunately, theta and phi don't follow normal conventions.
.camera({theta: theta(0), phi: phi(-QUARTER_TURN)})
.transition(300);
}
html, body { height: 100%; }
body { margin: 0; padding: 0 }
canvas { display: block }
#info {
position: absolute;
left: 50%;
bottom: 50px;
z-index: 20;
width: 300px;
margin-left: -150px;
padding: 25px;
background: rgba(0, 0, 0, .5);
color: #fff;
font-family: "Lucida Grande", sans-serif;
font-size: 16px;
text-align: center;
border-radius: 3px;
text-shadow: 0px 1px 0px rgba(0, 0, 0, .4);
opacity: 1;
}
#info.transition {
-webkit-transition: opacity 300ms ease-in-out;
-moz-transition: opacity 300ms ease-in-out;
transition: opacity 300ms ease-in-out;
}
#info kbd {
background: #aaa;
box-shadow: 0px 1px 1px rgba(0, 0, 0, .3);
border-radius: 3px;
padding: 3px;
margin: 3px;
font-family: inherit;
}
.mathbox-label {
font-family: 'klavika-web', sans-serif;
font-weight: normal;
font-style: normal;
text-shadow:
3px 0px 1px rgb(255, 255, 255),
-3px 0px 1px rgb(255, 255, 255),
0px -3px 1px rgb(255, 255, 255),
0px 3px 1px rgb(255, 255, 255),
2px 2px 1px rgb(255, 255, 255),
-2px 2px 1px rgb(255, 255, 255),
2px -2px 1px rgb(255, 255, 255),
-2px -2px 1px rgb(255, 255, 255),
3px 2px 1px rgb(255, 255, 255),
-3px 2px 1px rgb(255, 255, 255),
3px -2px 1px rgb(255, 255, 255),
-3px -2px 1px rgb(255, 255, 255),
1px 3px 1px rgb(255, 255, 255),
-1px 3px 1px rgb(255, 255, 255),
1px -3px 1px rgb(255, 255, 255),
-1px -3px 1px rgb(255, 255, 255),
-1px -1px 1px rgb(255, 255, 255),
-1px 1px 1px rgb(255, 255, 255),
1px -1px 1px rgb(255, 255, 255),
1px 1px 1px rgb(255, 255, 255);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment