Test of C2 in Versor.js by Wesley Smith and Jason Merrill
Last active
December 19, 2019 03:47
-
-
Save syntagmatic/6901553 to your computer and use it in GitHub Desktop.
Versor.js Test
This file contains 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> | |
<title>Circle through three points</title> | |
<script src="versor.js"></script> | |
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
<link type="text/css" rel="stylesheet" href="style.css"/> | |
<style type="text/css"> | |
svg { | |
background-color: rgba(0, 0, 0, 0.02); | |
width: 960px; | |
height: 500px; | |
} | |
circle { | |
stroke: #055; | |
fill: none; | |
stroke-opacity: .5; | |
stroke-width: 0.05; | |
} | |
.p1 { | |
fill: #f11; | |
} | |
.p2 { | |
fill: #17f; | |
} | |
.p3 { | |
fill: #1f7; | |
} | |
</style> | |
<div id="GA"></div> | |
<script> | |
var C2 = versor.create({ | |
metric:[1, 1, 1, -1], | |
types: [ | |
{ name:"Vec2", bases:["e1", "e2"] }, | |
{ name:"Biv2", bases:["e12"] }, | |
{ name:"Pss", bases:["e1234"] }, | |
{ name:"Rot", bases:["s", "e12"] }, | |
{ name:"Pnt", bases:["e1", "e2", "e3", "e4"], dual:true }, | |
{ name:"Par", bases:["e12", "e13", "e14", "e23", "e24", "e34"] }, | |
{ name:"Dll", bases:["e1", "e2", "e4"], dual:true }, | |
{ name:"Lin", bases:["e134", "e234", "e124"] }, | |
{ name:"Flp", bases:["e14", "e24", "e34"] }, | |
{ name:"Drv", bases:["e14", "e24"] }, | |
{ name:"Tnv", bases:["e13", "e23"] }, | |
{ name:"Dil", bases:["s", "e34"] }, | |
{ name:"Trs", bases:["s", "e14", "e24"] }, | |
{ name:"Mot", bases:["s", "e12", "e14", "e24"] }, | |
{ name:"Bst", bases:["s", "e12", "e13", "e14", "e23", "e24", "e34"] } | |
], | |
conformal:true | |
}); | |
document.addEventListener("DOMContentLoaded", function(evt) { | |
var Ori = C2.e3(1); | |
var Inf = C2.e4(1); | |
var Pss = C2.e1234(1); | |
function cosh(v) { | |
return (Math.exp(v) + Math.exp(-v))*0.5; | |
} | |
function sinh(v) { | |
return (Math.exp(v) - Math.exp(-v))*0.5; | |
} | |
var Ro = { | |
point: function(x, y) { | |
return C2.Pnt(x, y, 1, (x*x+y*y)*0.5); | |
}, | |
ipoint: function(x, y) { | |
return C2.Pnt(x, y, -1, (x*x+y*y)*0.5); | |
}, | |
circle: function(x, y, r) { | |
var s = Ro.point(x, y); | |
var r2 = r*r; | |
if(r > 0) s[3] -= 0.5*r2; | |
else s[3] += 0.5*r2; | |
return s; | |
}, | |
size: function(a) { | |
var v1 = Inf.ip(a); | |
var v2 = a.gp(a.involute()).gp(v1.gp(v1).inverse()); | |
return a.isdual() ? -v2[0] : v2[0]; | |
}, | |
cen: function(a) { | |
var v = Inf.ip(a); | |
return C2.Pnt(a.gp(Inf).gp(a).div(v.gp(v).gp(-2))); | |
}, | |
// squared distance | |
sqd: function(a, b) { | |
return -a.ip(b)[0]; | |
}, | |
// distance | |
dst: function(a, b) { | |
return Math.sqrt(Math.abs(Ro.sqd(a, b))); | |
}, | |
split: function(pp) { | |
var r = Ro.dst(pp, pp); | |
var dlp = C2.e4(-1).ip(pp); | |
var bstA = C2.Bst(pp); | |
var bstB = C2.Bst(pp); | |
bstA[0] -= r; | |
bstB[0] += r; | |
var pA = C2.Pnt(bstA.div(dlp)); | |
var pB = C2.Pnt(bstB.div(dlp)); | |
return [pA, pB]; | |
} | |
}; | |
var Fl = { | |
dir: function(a) { | |
return a.isdual() ? | |
C2.e4(-1).op(a) : | |
C2.e4(-1).ip(a); | |
}, | |
loc: function(a, p) { | |
if(a.isdual()) return C2.Pnt(p.op(a).div(a)); | |
else return C2.Pnt(p.ip(a).div(a)); | |
} | |
}; | |
var Op = { | |
trs: function(x, y) { | |
return C2.Trs(1, 0.5*x, 0.5*y); | |
}, | |
bst: function(pp) { | |
var norm; | |
var sz = pp.ip(pp)[0]; | |
var cn, sn; | |
if(sz < 0) { | |
norm = Math.sqrt(-sz); | |
cn = cosh(norm); | |
sn = -sinh(norm); | |
} | |
else if(sz > 0) { | |
norm = Math.sqrt(sz); | |
cn = cosh(norm); | |
sn = -sinh(norm); | |
} | |
else { | |
cn = 1; | |
sn = -1; | |
} | |
var res = C2.Bst(pp.gp(sn)); | |
res[0] = cn; | |
return res; | |
} | |
}; | |
function dual(a) { | |
return a.gp(Pss); | |
} | |
function normalizePoint(p) { | |
return p.gp(1/p[2]); | |
} | |
function dist(p1, p2) { | |
var dx = p1[0]-p2[0]; | |
var dy = p1[1]-p2[1]; | |
return Math.sqrt(dx*dx+dy*dy); | |
} | |
function circleRadius(c) { | |
return Math.sqrt(Math.abs(Ro.size(c, true))); | |
} | |
function circleX(c) { | |
return Ro.cen(c)[0]; | |
} | |
function circleY(c) { | |
return Ro.cen(c)[1]; | |
} | |
var N=40; | |
var scale=20; | |
var w=600; | |
var h=600; | |
var svg = d3.select("#GA").append("svg:svg") | |
.attr("width", w) | |
.attr("height", h); | |
function canvasToWorld(p) { | |
return [ | |
(p[0]-w/2)/scale, | |
(p[1]-h/2)/-scale | |
]; | |
} | |
// draw a C2 circle in an SVG using D3 | |
function d3Circle(sel) { | |
return sel.attr("r", circleRadius) | |
.attr("cx", circleX) | |
.attr("cy", circleY); | |
} | |
var p1 = Ro.point(1, 1); | |
var p2 = Ro.point(-5, 0); | |
var p3 = Ro.point(0, 5); | |
var circleThrough = p1.op(p2).op(p3); | |
var drag = (function() { | |
var dragPoint; | |
var drag = d3.behavior.drag() | |
.on('dragstart', function(){ | |
var pos = canvasToWorld(d3.mouse(this)); | |
var smallestDist = 3/scale; | |
dragPoint = undefined; | |
var thisDist = dist(pos, p1); | |
if (thisDist < smallestDist) { | |
dragPoint = "p1"; | |
smallestDist = thisDist; | |
} | |
thisDist = dist(pos, p2); | |
if (thisDist < smallestDist) { | |
dragPoint = "p2"; | |
smallestDist = thisDist; | |
} | |
thisDist = dist(pos, p3); | |
if (thisDist < smallestDist) { | |
dragPoint = "p3"; | |
smallestDist = thisDist; | |
} | |
}) | |
.on('drag', function(){ | |
if (!dragPoint) return; | |
var pos = canvasToWorld(d3.mouse(this)); | |
switch (dragPoint) { | |
case "p1": p1 = Ro.point(pos[0], pos[1]); break; | |
case "p2": p2 = Ro.point(pos[0], pos[1]); break; | |
case "p3": p3 = Ro.point(pos[0], pos[1]); break; | |
} | |
var circleThrough = p1.op(p2).op(p3); | |
var sel; | |
sel = g.selectAll(".generator").data([p1, p2, p3]); | |
sel.attr("cx", function(d) { return d[0]; }) | |
.attr("cy", function(d) { return d[1]; }); | |
sel = g.selectAll(".elements").data([circleThrough]); | |
d3Circle(sel); | |
}); | |
return drag; | |
})(); | |
svg.call(drag); | |
g = svg.append("svg:g") | |
.attr("transform", "translate("+(w/2)+", "+(h/2)+") scale("+scale+", "+(-scale)+")"); | |
var sel = g.selectAll(".elements").data([circleThrough]) | |
.enter().append("svg:circle") | |
.attr("class", "elements"); | |
d3Circle(sel); | |
g.selectAll(".generator").data([p1, p2, p3]) | |
.enter().append("svg:circle") | |
.attr("class", function(d, i) { return "generator p"+(i+1); }) | |
.attr("cx", function(d) { return d[0]; }) | |
.attr("cy", function(d) { return d[1]; }) | |
.attr("r", function(d) { return 3/scale; }); | |
}, false); | |
</script> | |
</html> |
This file contains 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
/* | |
CAST (use basis) | |
.sp product | |
*/ | |
var versor = function() { | |
var foreach = function(t, f) { | |
for(var i=0; i < t.length; ++i) f(t[i], i); | |
} | |
/* Data structure representing a blade (coordinate + scale factor) | |
b - bitwise representation of coordinate | |
wt - scale factor | |
*/ | |
var blade = function(b, wt) { | |
return { id:b, w:wt }; | |
} | |
var type = function(key, bases, name) { | |
return { key:key, bases:bases, name:name, generated:false, dual:false }; | |
} | |
var classname = function(name) { | |
return "_"+name; | |
} | |
/* Calculate the grade of a coordinate | |
b - bitwise representation of coordinate | |
*/ | |
var grade = function(b) { | |
var n = 0; | |
while(b != 0) { | |
if( (b&1)==1 ) n += 1; | |
b >>= 1; | |
} | |
return n; | |
} | |
/* Calculate the sign of the product of two coordinates | |
a - bitwise representation of coordinate | |
b - bitwise representation of coordinate | |
*/ | |
var sign = function(a, b) { | |
var n = a>>1; | |
var sum = 0; | |
while(n != 0) { | |
sum += grade(n&b) | |
n >>= 1; | |
} | |
if((sum&1)==0) return 1; | |
else return -1; | |
} | |
/* Calculate the product between two coordinates | |
a - bitwise representation of coordinate | |
b - bitwise representation of coordinate | |
returns a blade | |
*/ | |
var product = function(a, b) { | |
var res = a^b; | |
var s = sign(a, b); | |
return blade(res, s); | |
} | |
/* Calculate the outer product between two coordinates | |
a - bitwise representation of coordinate | |
b - bitwise representation of coordinate | |
returns a blade | |
*/ | |
var outer = function(a, b) { | |
if((a&b)!=0) return blade(0, 0); | |
else return product(a, b); | |
} | |
var involute = function(x) { | |
var g = grade(x); | |
var n = Math.pow(-1, g); | |
return blade(x, n); | |
} | |
var reverse = function(x) { | |
var g = grade(x); | |
var n = Math.pow(-1, (g*(g-1)/2.0)); | |
return blade(x, n); | |
} | |
var conjugate = function(x) { | |
var g = grade(x); | |
var n = Math.pow(-1, (g*(g+1)/2.0)); | |
return blade(x, n); | |
} | |
/* Calculate the name of a coordinate | |
b - bitwise representation of coordinate | |
*/ | |
var basisString = function(b) { | |
var n=0; | |
var res = ""; | |
while(b != 0) { | |
n += 1; | |
if((b&1) == 1) res += n; | |
b >>= 1; | |
} | |
if(n > 0) return "e"+res; | |
else return "s"; | |
} | |
var basisBit = function(name) { | |
if(name == "s") return 0; | |
var x = 0; | |
var lastn = parseInt(name.substr(name.length-1)); | |
for(var i=lastn; i > 0; --i) { | |
x <<= 1; | |
if(name.search(i) >= 0) x += 1; | |
} | |
return x; | |
} | |
var basisBits = function(bases) { | |
var ids = []; | |
for(var i=0; i < bases.length; ++i) { | |
ids[i] = basisBit(bases[i]); | |
} | |
return ids; | |
} | |
var basisNames = function(ty) { | |
ty.sort(function(a, b) { | |
return (a<b) ? -1 : 1; | |
}); | |
var coords = []; | |
for(var i=0; i < ty.length; ++i) { | |
coords[i] = basisString(ty[i]) | |
} | |
return coords; | |
} | |
var keyCheck = function(k1, k2) { | |
if(k1.length != k2.length) return false; | |
for(var i=0; i < k1.length; ++i) { | |
if(k1[i] != k2[i]) return false; | |
} | |
return true; | |
} | |
var order = function(c) { | |
var tblades = []; | |
for(var i in c) { | |
tblades[tblades.length] = parseInt(i); | |
} | |
tblades.sort(function(a, b) { | |
return (a<b) ? -1 : 1; | |
}); | |
return { | |
blades: tblades, | |
inst: c | |
}; | |
} | |
var compress = function(x) { | |
var tally = {}; | |
// collect like terms | |
for(var i=0; i < x.length; ++i) { | |
var iv = x[i]; | |
if(tally[iv.id]) { | |
tally[iv.id].w += iv.w; | |
} | |
else { | |
tally[iv.id] = blade(iv.id, iv.w); | |
} | |
} | |
var res = []; | |
for(var id in tally) { | |
var iv = tally[id]; | |
if(iv.w != 0) { | |
res.push(iv); | |
} | |
} | |
return res; | |
} | |
var printLines = function(text, from, to) { | |
var lines = text.match(/^.*((\r\n|\n|\r)|$)/gm); | |
from = from || 0; | |
to = to || lines.length; | |
for(var i=from; i < to; ++i) { | |
console.log((i+1)+"\t"+lines[i].substr(0, lines[i].length-1)); | |
} | |
} | |
/* Representation of a GA space | |
*/ | |
var Space = function(props) { | |
props = props || {}; | |
props.metric = props.metric || [1, 1, 1]; | |
props.types = props.types || []; | |
props.binops = props.binops || []; | |
this.metric = props.metric; | |
this.basis = this.buildBasis(); | |
this.types = this.buildTypes(); | |
if(props.conformal) { | |
this.values = this.buildConformalValues(); | |
this.products = this.buildConformal(); | |
} | |
else { | |
this.products = this.buildEuclidean(); | |
} | |
this.subspaces = this.buildSubspaces(); | |
this.registerSubspaces(); | |
this.createTypes(props.types); | |
this.api = this.generate(props); | |
for(var name in this.api.constructors) { | |
this[name] = this.api.constructors[name]; | |
} | |
this.initialized = true; | |
} | |
Space.prototype.generate = function(props) { | |
var binopCode = this.generateBinops(props.binops); | |
var typeCode = this.generateRegisteredTypes(); | |
var typeCodeAliases = {}; | |
for(var name in typeCode) { | |
var ty = this.types[name]; | |
if(ty.alias && name == ty.alias) { | |
typeCodeAliases[name] = typeCode[name]; | |
} | |
} | |
var functionBody = ["var api = { classes:{}, constructors:{} };"]; | |
for(var name in typeCode) { | |
if(!typeCodeAliases[name]) { | |
var code = typeCode[name]; | |
functionBody.push([ | |
code, | |
"api.constructors."+name+" = "+name+";", | |
"api.classes."+name+" = "+classname(name)+";" | |
].join("\n") | |
); | |
if(this.types[name].alias) { | |
var aliasName = this.types[name].alias; | |
var aliasCode = typeCodeAliases[aliasName]; | |
functionBody.push([ | |
aliasCode, | |
"api.constructors."+aliasName+" = "+aliasName+";", | |
"api.classes."+aliasName+" = "+classname(aliasName)+";" | |
].join("\n") | |
); | |
} | |
} | |
} | |
functionBody = functionBody.concat(binopCode); | |
functionBody.push("return api;"); | |
var f = new Function("space", functionBody.join("\n\n")); | |
return f(this); | |
} | |
Space.prototype.metricProduct = function(a, b) { | |
var tmp = product(a, b); | |
var bs = a&b; | |
var i = 1; | |
while(bs != 0) { | |
if((bs&1) == 1) tmp.w *= this.metric[i-1]; | |
bs >>= 1; | |
++i; | |
} | |
return tmp; | |
} | |
Space.prototype.metricInner = function(a, b) { | |
var tmp = this.metricProduct(a, b); | |
var g = grade(b) - grade(a); | |
if(grade(a) > grade(b) || grade(tmp.id) != g) { | |
return blade(0, 0); | |
} | |
else { | |
return tmp; | |
} | |
} | |
/* Create a key capable of representing all coordinates in a metric | |
b - (optional) bitwise representation of coordinate | |
*/ | |
Space.prototype.key = function(b) { | |
var nkeys = Math.ceil(this.basis.length/32) | |
var key = []; | |
for(var i=0; i < nkeys; ++i) key[i] = 0; | |
if(b != undefined) { | |
var k = Math.ceil((b+1)/32); | |
var shft = (b+1) - 32*(k-1); | |
key[k-1] = 1<<shft-1; | |
} | |
return key; | |
} | |
Space.prototype.basesKey = function(bases) { | |
var key = this.key(); | |
for(var i=0; i < bases.length; ++i) { | |
var b = bases[i]; | |
var ty = this.types[basisString(b)]; | |
for(var k=0; k < ty.key.length; ++k) { | |
key[k] = key[k] | ty.key[k]; | |
} | |
} | |
return key; | |
} | |
/* Construct the bitwise representation for the coordinate basis | |
*/ | |
Space.prototype.buildBasis = function() { | |
// initialize with the scalar | |
var basis = [0]; | |
var basisMap = {0:true}; | |
// build the coordinate blades (e1, e2, e3, ...) | |
var nb = 1; | |
for(var i=0; i < this.metric.length; ++i) { | |
basis[basis.length] = nb; | |
basisMap[nb] = true; | |
nb <<= 1; | |
} | |
// build the bivectors (e12, e23, ...) | |
for(var i=0; i < basis.length; ++i) { | |
for(var j=0; j < basis.length; ++j) { | |
if(i!=j) { | |
var r = outer(basis[i], basis[j]); | |
if((r.id!=0) && !basisMap[r.id]) { | |
basis[basis.length] = r.id; | |
basisMap[r.id] = true; | |
} | |
} | |
} | |
} | |
// sort the basis by grade | |
basis.sort(function(a, b) { | |
var l = grade(a)-1/a; | |
var r = grade(b)-1/b; | |
return (l<r) ? -1 : 1; | |
}); | |
return basis; | |
} | |
Space.prototype.buildTypes = function() { | |
var types = {}; | |
for(var i=0; i < this.basis.length; ++i) { | |
var b = this.basis[i]; | |
var key = this.key(b); | |
var name = basisString(b); | |
types[name] = type(key, [b], name); | |
} | |
return types; | |
} | |
Space.prototype.bladeTable = function() { | |
var S = {}; | |
for(var i=0; i < this.basis.length; ++i) { | |
var b = this.basis[i]; | |
var name = basisString(b); | |
S[b] = { | |
id: name, | |
basis: b, | |
gp: {}, op: {}, ip: {} | |
}; | |
} | |
return S | |
} | |
// Check For presence of Minkowskian Basis | |
Space.prototype.checkMink = function(x) { | |
var v = x & this.values.eplane; | |
if((v == 0) || (v == this.values.eplane)) { | |
return false; | |
} | |
else { | |
return true | |
} | |
} | |
Space.prototype.buildEuclidean = function() { | |
var S = this.bladeTable(); | |
for(var i=0; i < this.basis.length; ++i) { | |
var iv = this.basis[i]; | |
for(var j=0; j < this.basis.length; ++j) { | |
var jv = this.basis[j]; | |
var gp = this.metricProduct(iv, jv); | |
var op = outer(iv, jv); | |
var ip = this.metricInner(iv, jv); | |
S[iv].gp[jv] = [gp]; | |
S[iv].op[jv] = [op]; | |
S[iv].ip[jv] = [ip]; | |
S[iv].involute = involute(iv); | |
S[iv].reverse = reverse(iv); | |
S[iv].conjugate = conjugate(iv); | |
} | |
} | |
return S; | |
} | |
// Push into e+.e- Minkowskian diagonal metric from a null basis (for calculating metric products) | |
Space.prototype.pushMink = function(x) { | |
if((x&this.values.no)==this.values.no) { | |
var t = x^this.values.no; | |
return [ | |
blade(t^this.values.ep, 0.5), | |
blade(t^this.values.em, 0.5) | |
]; | |
} | |
else if((x&this.values.ni)==this.values.ni) { | |
var t = x^this.values.ni; | |
return [ | |
blade(t^this.values.ep, -1), | |
blade(t^this.values.em, 1) | |
]; | |
} | |
} | |
// Pop back into degenerate null basis from nondegenerate Minkowskian (after xor-ing) | |
Space.prototype.popMink = function(x) { | |
if((x&this.values.ep)==this.values.ep) { | |
var t = x^this.values.ep; | |
return [ | |
blade(t^this.values.no, 1), | |
blade(t^this.values.ni, -0.5) | |
]; | |
} | |
else if((x&this.values.em)==this.values.em) { | |
var t = x^this.values.em; | |
return [ | |
blade(t^this.values.no, 1), | |
blade(t^this.values.ni, 0.5) | |
]; | |
} | |
} | |
Space.prototype.accumMink = function(blades) { | |
var res = []; | |
for(var i=0; i < blades.length; ++i) { | |
var iv = blades[i]; | |
if(this.checkMink(iv.id)) { | |
var minkBlades = this.popMink(iv.id); | |
for(var j=0; j < minkBlades.length; ++j) { | |
var jv = minkBlades[j]; | |
jv.w *= iv.w; | |
} | |
res = res.concat(minkBlades); | |
} | |
else { | |
res.push(iv); | |
} | |
} | |
return res; | |
} | |
Space.prototype.buildConformalBinop = function(S, iv, jv) { | |
// get list of blades in minkowskian (diagonal) metric | |
var tmpA = this.checkMink(iv) ? this.pushMink(iv) : [blade(iv, 1)]; | |
var tmpB = this.checkMink(jv) ? this.pushMink(jv) : [blade(jv, 1)]; | |
var gpTally = []; | |
var opTally = []; | |
var ipTally = []; | |
for(var a=0; a < tmpA.length; ++a) { | |
var av = tmpA[a]; | |
for(var b=0; b < tmpB.length; ++b) { | |
var bv = tmpB[b]; | |
// calculate products in mink metric | |
var gp = this.metricProduct(av.id, bv.id); | |
var op = outer(av.id, bv.id); | |
var ip = this.metricInner(av.id, bv.id); | |
// push onto tally stack | |
gpTally.push(blade(gp.id, gp.w*av.w*bv.w)); | |
opTally.push(blade(op.id, op.w*av.w*bv.w)); | |
ipTally.push(blade(ip.id, ip.w*av.w*bv.w)); | |
} | |
} | |
var gpPop = this.accumMink(compress(gpTally)); | |
var opPop = this.accumMink(compress(opTally)); | |
var ipPop = this.accumMink(compress(ipTally)); | |
S[iv].gp[jv] = compress(gpPop); | |
S[iv].op[jv] = compress(opPop); | |
S[iv].ip[jv] = compress(ipPop); | |
} | |
Space.prototype.buildConformalValues = function() { | |
var no = 1<<(this.metric.length-2); | |
var ni = 1<<(this.metric.length-1); | |
return { | |
no: no, | |
ni: ni, | |
ep: no, | |
em: ni, | |
eplane: no|ni | |
} | |
} | |
Space.prototype.buildConformal = function() { | |
var S = this.bladeTable(); | |
for(var i=0; i < this.basis.length; ++i) { | |
var ib = this.basis[i]; | |
S[ib].involute = involute(ib) | |
S[ib].reverse = reverse(ib) | |
S[ib].conjugate = conjugate(ib) | |
for(var j=0; j < this.basis.length; ++j) { | |
var jb = this.basis[j]; | |
this.buildConformalBinop(S, ib, jb) | |
} | |
} | |
return S | |
} | |
var _subspaceNames = ["Vec", "Biv", "Tri", "Quad", "Penta", "Hexa", "Hepta", "Octo"]; | |
Space.prototype.buildSubspaces = function() { | |
var subspaces = []; | |
for(var i=0; i < this.metric.length; ++i) { | |
subspaces[i] = { | |
name: _subspaceNames[i], | |
bases: [] | |
}; | |
} | |
for(var i=0; i < this.basis.length; ++i) { | |
var b = this.basis[i]; | |
var g = grade(b); | |
if(g > 0) { | |
var bases = subspaces[g-1].bases; | |
bases[bases.length] = b; | |
} | |
} | |
return subspaces; | |
} | |
Space.prototype.registerSubspaces = function() { | |
for(var i=0; i < this.subspaces.length; ++i) { | |
var iv = this.subspaces[i]; | |
this.types[iv.name] = type(this.basesKey(iv.bases), iv.bases, iv.name); | |
} | |
} | |
Space.prototype.aliasType = function(oty, nty) { | |
this.types[nty] = this.types[oty]; | |
this.types[oty].alias = nty; | |
//this.types[oty].alias = nty | |
/*delete this.types[oty]; | |
// rename subspace if necessary | |
for(var i=0; i < this.subspaces.length; ++i) { | |
var subs = this.subspaces[i]; | |
if(subs.name == oty) { | |
subs.name = nty | |
break; | |
} | |
} | |
*/ | |
} | |
Space.prototype.createType = function(bases, name, aliasExisting) { | |
var key = this.basesKey(bases); | |
for(var tyname in this.types) { | |
var ty = this.types[tyname]; | |
if(keyCheck(key, ty.key)) { | |
if(aliasExisting) { | |
this.aliasType(tyname, name) | |
return name; | |
} | |
else { | |
return tyname; | |
} | |
} | |
} | |
this.types[name] = type(key, bases, name); | |
return name; | |
} | |
Space.prototype.productList = function(bases1, bases2, opname) { | |
var tally = []; | |
// fetch table pairs of values in types | |
var idx = 0 | |
for(var i=0; i < bases1.length; ++i) { | |
var iv = bases1[i]; | |
for(var j=0; j < bases2.length; ++j) { | |
var jv = bases2[j]; | |
var prod = this.products[iv][opname][jv] | |
for(var k=0; k < prod.length; ++k) { | |
var instruction = { | |
a: i, b: j, | |
ida: basisString(iv), | |
idb: basisString(jv), | |
r: prod[k] | |
}; | |
tally[idx] = instruction; | |
idx++; | |
} | |
} | |
} | |
var combined = {}; | |
// check for similar ids in the tally, or if weight is 0 | |
for(var i=0; i < tally.length; ++i) { | |
var instruction = tally[i]; | |
if(instruction.r.w == 0) continue; | |
var b = instruction.r.id; | |
if(combined[b]) { | |
var instructions = combined[b]; | |
instructions[instructions.length] = instruction; | |
} | |
else { | |
combined[b] = [instruction]; | |
} | |
} | |
return order(combined); | |
} | |
Space.prototype.generateType = function(name) { | |
var ty = this.types[name]; | |
var coords = basisNames(ty.bases); | |
var getfields = []; | |
var setfields = []; | |
foreach(coords, function(v, i) { | |
getfields[i] = "this["+i+"]"; | |
setfields[i] = getfields[i]+" = "+v; | |
}); | |
var model = { | |
name: name, | |
classname: classname(name), | |
parameters: coords.join(", "), | |
coords: coords, | |
getfields: getfields.join(", "), | |
setfields: setfields, | |
isdual: ty.dual, | |
}; | |
var create = [ | |
"var "+model.name+" = function("+model.parameters+") {", | |
"\treturn new "+model.classname+"("+model.parameters+");", | |
"}" | |
].join("\n"); | |
var def = [ | |
"var "+model.classname+" = function("+model.parameters+") {", | |
"\tthis.type = \""+model.name+"\";", | |
"\tif(typeof "+coords[0]+" == \"object\") {", | |
"\t\tthis.cast("+coords[0]+");", | |
"\t}", | |
"\telse {", | |
model.setfields.join("\n"), | |
"\t}", | |
"};", | |
"", | |
model.classname+".prototype._cast = {};", | |
model.classname+".prototype._ip = {};", | |
model.classname+".prototype._op = {};", | |
model.classname+".prototype._gp = {};", | |
model.classname+".prototype._add = {};", | |
model.classname+".prototype._sub = {};", | |
"", | |
model.classname+".prototype.inverse = function() {", | |
"\tvar rev = this.reverse();", | |
"\tvar sca = this.gp(rev)[0];", | |
"\treturn rev.gp(1/sca);", | |
"}", | |
"", | |
model.classname+".prototype.ip = function(b) {", | |
"\tif(!this._ip[b.type]) {", | |
"\t\tspace.createBinop('ip', this.type, b.type);", | |
"\t}", | |
"\treturn this._ip[b.type].call(this, b);", | |
"}", | |
"", | |
model.classname+".prototype.op = function(b) {", | |
"\tif(!this._op[b.type]) {", | |
"\t\tspace.createBinop('op', this.type, b.type);", | |
"\t}", | |
"\treturn this._op[b.type].call(this, b);", | |
"}", | |
"", | |
model.classname+".prototype.gp = function(b) {", | |
"\tif(typeof b == \"number\") {", | |
"\t\tb = space.s(b);", | |
"\t}", | |
"\tif(!this._gp[b.type]) {", | |
"\t\tspace.createBinop('gp', this.type, b.type);", | |
"\t}", | |
"\treturn this._gp[b.type].call(this, b);", | |
"}", | |
"", | |
model.classname+".prototype.sp = function(b) {", | |
"\tvar v = this.inverse().gp(b).gp(this);", | |
"\treturn "+"new b.__proto__.constructor(v);", | |
"}", | |
"", | |
model.classname+".prototype.div = function(b) {", | |
"\treturn this.gp(b.inverse());", | |
"}", | |
"", | |
model.classname+".prototype.add = function(b) {", | |
"\tif(typeof b == \"number\") {", | |
"\t\tb = space.s(b);", | |
"\t}", | |
"\tif(!this._add[b.type]) {", | |
"\t\tspace.createAffineOp('add', this.type, b.type);", | |
"\t}", | |
"\treturn this._add[b.type].call(this, b);", | |
"}", | |
"", | |
model.classname+".prototype.sub = function(b) {", | |
"\tif(typeof b == \"number\") {", | |
"\t\tb = space.s(b);", | |
"\t}", | |
"\tif(!this._sub[b.type]) {", | |
"\t\tspace.createAffineOp('sub', this.type, b.type);", | |
"\t}", | |
"\treturn this._sub[b.type].call(this, b);", | |
"}", | |
"", | |
model.classname+".prototype.toArray = function() {", | |
"\treturn ["+model.getfields+"];", | |
"}", | |
"", | |
model.classname+".prototype.toString = function() {", | |
"\treturn \""+model.name+"(\" + this.toArray().join(\", \") + \")\";", | |
"}", | |
model.classname+".prototype.cast = function(b) {", | |
"\tif(!this._cast[b.type]) {", | |
"\t\tspace.createCast(this.type, b.type);", | |
"\t}", | |
"\treturn this._cast[b.type].call(this, b);", | |
"}", | |
"", | |
model.classname+".prototype.isdual = function() {", | |
"\treturn "+model.isdual+";", | |
"}", | |
"", | |
].join("\n"); | |
var code = [def]; | |
code.push(this.generateUnop("reverse", name)); | |
code.push(this.generateUnop("involute", name)); | |
code.push(this.generateUnop("conjugate", name)); | |
code.push(create); | |
ty.generated = true; | |
return code.join("\n\n"); | |
} | |
Space.prototype.createCast = function(toName, fromName) { | |
var toTy = this.types[toName] | |
var fromTy = this.types[fromName] | |
var fromCoordMap = {} | |
foreach(fromTy.bases, function(v, i) { | |
fromCoordMap[v] = i; | |
}); | |
var ops = []; | |
foreach(toTy.bases, function(v, i) { | |
var src; | |
if(typeof fromCoordMap[v] == "number") src = "b["+fromCoordMap[v]+"]"; | |
else src = "0" | |
ops[i] = "this["+i+"] = "+src+";" | |
}); | |
var model = { | |
classname: classname(toName), | |
fromTy:fromName, | |
ops: ops.join("\n") | |
}; | |
var code = [ | |
model.classname+".prototype._cast."+model.fromTy+" = function(b) {", | |
model.ops, | |
"};" | |
].join("\n"); | |
var f = new Function(classname(toName), code); | |
f(this.api.classes[toName]); | |
} | |
Space.prototype.generateUnop = function(opname, tyname) { | |
var ty = this.types[tyname] | |
var coords = basisNames(ty.bases); | |
var _this = this; | |
var ops = []; | |
foreach(ty.bases, function(v, i) { | |
var blade = _this.products[v][opname]; | |
ops[i] = ((blade.w>0) ? "" : "-") + "this["+i+"]"; | |
}); | |
var model = { | |
classname: classname(tyname), | |
opname: opname, | |
ops: ops.join(", ") | |
}; | |
return [ | |
model.classname+".prototype."+model.opname+" = function() {", | |
"\treturn new "+model.classname+"("+model.ops+");", | |
"};" | |
].join("\n"); | |
} | |
Space.prototype.binopResultType = function(opname, tyname1, tyname2) { | |
var ty1 = this.types[tyname1]; | |
var ty2 = this.types[tyname2]; | |
var op = this.productList(ty1.bases, ty2.bases, opname); | |
var tynameRes | |
if(op.blades.length == 0) { | |
tynameRes = "s"; | |
} | |
else { | |
tynameRes = this.createType(op.blades, tyname1+tyname2+"_"+opname, false); | |
} | |
return tynameRes; | |
} | |
Space.prototype.generateBinop = function(opname, tyname1, tyname2) { | |
var ty1 = this.types[tyname1] | |
var ty2 = this.types[tyname2] | |
var op = this.productList(ty1.bases, ty2.bases, opname); | |
var tynameRes | |
if(op.blades.length == 0) { | |
tynameRes = "s"; | |
} | |
else { | |
tynameRes = this.createType(op.blades, tyname1+tyname2+"_"+opname, false); | |
} | |
var tyRes = this.types[tynameRes]; | |
if(!tyRes) { | |
console.log("ERROR: gentype " + tyname1+tyname2+"_"+opname, op.blades); | |
} | |
else if(this.initialized && !tyRes.generated) { | |
// TODO: consolidate this with the generate() function | |
var code = this.generateType(tynameRes); | |
var functionBody = ["var api = { classes:{}, constructors:{} };"]; | |
functionBody.push([ | |
code, | |
"api.constructors."+tynameRes+" = "+tynameRes+";", | |
"api.classes."+tynameRes+" = "+classname(tynameRes)+";" | |
].join("\n") | |
); | |
functionBody.push("return api;"); | |
var f = new Function("space", functionBody.join("\n\n")); | |
var api = f(this); | |
for(var name in api.classes) { | |
this.api.classes[name] = api.classes[name]; | |
} | |
for(var name in api.constructors) { | |
this.api.constructors[name] = api.constructors[name]; | |
} | |
} | |
var ops = []; | |
if(op.blades.length == 0) { | |
ops[0] = "0"; | |
} | |
else { | |
for(var i=0; i < op.blades.length; ++i) { | |
var blade = op.blades[i]; | |
var inst = op.inst[blade]; | |
var instbops = []; | |
for(var j=0; j < inst.length; ++j) { | |
var instop = inst[j]; | |
var bop = "this["+instop.a+"]*b["+instop.b+"]"; | |
if(instop.r.w < 0) bop = "-"+bop; | |
instbops.push(bop); | |
} | |
ops.push(instbops.join(" + ")); | |
} | |
} | |
var model = { | |
classname1: classname(tyname1), | |
tyname2: tyname2, | |
opname: opname, | |
tynameRes: tynameRes, | |
ops: ops.join(",\n") | |
}; | |
return [ | |
model.classname1+".prototype._"+model.opname+"."+model.tyname2+" = function(b) {", | |
"\treturn "+model.tynameRes+"("+model.ops+");", | |
"};" | |
].join("\n"); | |
} | |
Space.prototype.createBinop = function(opname, tyname1, tyname2) { | |
var resultType = this.binopResultType(opname, tyname1, tyname2); | |
var code = this.generateBinop(opname, tyname1, tyname2); | |
var f = new Function(classname(tyname1), resultType, code); | |
f(this.api.classes[tyname1], this.api.constructors[resultType]); | |
} | |
Space.prototype.createAffineOp = function(opname, tyname1, tyname2) { | |
var opsym = opname == "add" ? "+" : "-"; | |
var ty1 = this.types[tyname1]; | |
var ty2 = this.types[tyname2]; | |
var bases1Map = {}; | |
var bases2Map = {}; | |
var basesMap = {}; | |
for(var i=0; i < ty1.bases.length; ++i) { | |
bases1Map[ ty1.bases[i] ] = i; | |
basesMap[ ty1.bases[i] ] = ty1.bases[i]; | |
} | |
for(var i=0; i < ty2.bases.length; ++i) { | |
bases2Map[ ty2.bases[i] ] = i; | |
basesMap[ ty2.bases[i] ] = ty2.bases[i]; | |
} | |
var bases = []; | |
for(var name in basesMap) { | |
bases.push(basesMap[name]); | |
} | |
bases.sort(function(a, b) { | |
return (a<b) ? -1 : 1; | |
}); | |
var ops = []; | |
for(var i=0; i < bases.length; ++i) { | |
var operands = []; | |
var second = false; | |
if(bases1Map[ bases[i] ] != undefined) { | |
operands.push("this["+bases1Map[ bases[i] ]+"]"); | |
} | |
if(bases2Map[ bases[i] ] != undefined) { | |
second = true; | |
operands.push("b["+bases2Map[ bases[i] ]+"]"); | |
} | |
var op; | |
if(operands.length == 2) { | |
op = operands.join(opsym); | |
} | |
else { | |
op = operands[0]; | |
if(second && opname == "sub") { | |
op = opsym+op; | |
} | |
} | |
ops[i] = op; | |
} | |
var tynameRes = this.createType(bases, tyname1+tyname2+"_"+opname, false); | |
var tyRes = this.types[tynameRes]; | |
if(this.initialized && !tyRes.generated) { | |
// TODO: consolidate this with the generate() function | |
var code = this.generateType(tynameRes); | |
var functionBody = ["var api = { classes:{}, constructors:{} };"]; | |
functionBody.push([ | |
code, | |
"api.constructors."+tynameRes+" = "+tynameRes+";", | |
"api.classes."+tynameRes+" = "+classname(tynameRes)+";" | |
].join("\n") | |
); | |
functionBody.push("return api;"); | |
var f = new Function("space", functionBody.join("\n\n")); | |
var api = f(this); | |
for(var name in api.classes) { | |
this.api.classes[name] = api.classes[name]; | |
} | |
for(var name in api.constructors) { | |
this.api.constructors[name] = api.constructors[name]; | |
} | |
} | |
var model = { | |
classname1: classname(tyname1), | |
tyname2: tyname2, | |
opname: opname, | |
tynameRes: tynameRes, | |
ops: ops.join(", ") | |
}; | |
var code = [ | |
model.classname1+".prototype._"+model.opname+"."+model.tyname2+" = function(b) {", | |
"\treturn "+model.tynameRes+"("+model.ops+");", | |
"};" | |
].join("\n"); | |
var f = new Function(classname(tyname1), tynameRes, code); | |
f(this.api.classes[tyname1], this.api.constructors[tynameRes]); | |
} | |
Space.prototype.generateRegisteredTypes = function() { | |
var code = {}; | |
for(var name in this.types) { | |
var ty = this.types[name]; | |
if(!ty.generated) { | |
code[name] = this.generateType(name); | |
} | |
else { | |
code[name] = [ | |
"var "+classname(name)+" = "+classname(ty.name)+";", | |
"var "+name+" = "+ty.name+";" | |
].join("\n"); | |
} | |
} | |
return code; | |
} | |
Space.prototype.generateBinops = function(binops) { | |
var _this = this; | |
var code = []; | |
foreach(binops, function(v, i) { | |
code[i] = _this.generateBinop(v.op, v.types[0], v.types[1]); | |
}); | |
return code; | |
} | |
Space.prototype.createTypes = function(types) { | |
for(var i=0; i < types.length; ++i) { | |
var ty = types[i]; | |
var name = this.createType(basisBits(ty.bases), ty.name, true); | |
if(ty.dual != undefined) { | |
this.types[name].dual = ty.dual; | |
} | |
} | |
} | |
Space.prototype.ip = function(a, b) { | |
return a.ip(b); | |
} | |
Space.prototype.op = function(a, b) { | |
return a.op(b); | |
} | |
Space.prototype.gp = function(a, b) { | |
if(typeof a == "number") { | |
a = this.s(a); | |
} | |
return a.gp(b); | |
} | |
Space.prototype.sp = function(a, b) { | |
return a.sp(b); | |
} | |
/* | |
var C3 = new Space({ | |
metric:[1, 1, 1, 1, -1], | |
types: [ | |
{ name:"Vec3", bases:["e1", "e2", "e3"] }, | |
{ name:"Biv3", bases:["e12", "e13", "e23"] }, | |
{ name:"Rot", bases:["s", "e12", "e13", "e23"] }, | |
{ name:"Pnt", bases:["e1", "e2", "e3", "e4", "e5"] }, | |
{ name:"Dlp", bases:["e1", "e2", "e3", "e5"] }, | |
{ name:"Pln", bases:["e1235", "e1245", "e1345", "e2345"] }, | |
{ name:"Sph", bases:["e1235", "e1234", "e1245", "e1345", "e2345"] }, | |
{ name:"Dln", bases:["e12", "e13", "e23", "e15", "e25", "e35"] }, | |
{ name:"Lin", bases:["e145", "e245", "e345", "e125", "e135", "e235"] }, | |
{ name:"Flp", bases:["e15", "e25", "e35", "e45"] }, | |
{ name:"Par", bases:["e12", "e13", "e23", "e14", "e24", "e34", "e15", "e25", "e35", "e45"] }, | |
{ name:"Cir", bases:["e123", "e145", "e245", "e345", "e124", "e134", "e234", "e125", "e135", "e235"] }, | |
{ name:"Bst", bases:["s", "e12", "e13", "e23", "e14", "e24", "e34", "e15", "e25", "e35", "e45"] }, | |
{ name:"Dil", bases:["s", "e45"] }, | |
{ name:"Mot", bases:["s", "e12", "e13", "e23", "e15", "e25", "e35", "e1235"] }, | |
{ name:"Trs", bases:["s", "e14", "e24", "e34"] }, | |
{ name:"Drv", bases:["e15", "e25", "e35"] }, | |
{ name:"Drb", bases:["e125", "e135", "e235"] }, | |
{ name:"Tri3", bases:["e123"] }, | |
], | |
conformal:true | |
}); | |
function nullPnt(a, b, c) { | |
return C3.Pnt(a, b, c, 1, (a*a+b*b+c*c)*0.5); | |
} | |
var p1 = nullPnt(1, 0, 0); | |
console.log(p1.toString()); | |
console.log(C3.Vec3(p1).toString()); | |
*/ | |
return { | |
create: function(props) { | |
return new Space(props); | |
} | |
}; | |
}(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment