Test of C2 in Versor.js by Wesley Smith and Jason Merrill
<!DOCTYPE html>
<html>
<title>Circle through three points</title>
<script src="versor.js"></script>
<script src="//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>