function PoincareCircle(svg, x, y, scale, k=20) {
var circle = ProjectionInput(svg, x, y, scale, 1.0);
var lines = [];
var segments = [];
function CirclePoint(c,u,v,theta) {
return geom.Translate(c,
geom.Translate(
geom.Scale(u, Math.cos(theta)),
geom.Scale(v, Math.sin(theta))
));
}
function LinePoint(u,v,t) {
return geom.Translate(
geom.Scale(u, t),
geom.Scale(v, 1-t)
);
}
function PoincareLine(l,i,j) {
return function() {
var c = l.data.c;
if (c != Infinity) {
var u = geom.Scale(l.data.pn, l.data.r);
var v = geom.Scale(l.data.qn, l.data.r);
return [ CirclePoint(c,u,v,l.data.theta * i),
CirclePoint(c,u,v,l.data.theta * j) ];
} else {
var p = l.data.cp;
var q = l.data.cq;
return [ LinePoint(p,q,i), LinePoint(p,q,j) ];
}
}
}
function PoincareSegment(l,i,j) {
return function() {
var c = l.data.c;
if (c != Infinity) {
var u = geom.Scale(l.data.pn, l.data.r);
var v = geom.Scale(l.data.qn, l.data.r);
return [ CirclePoint(c,u,v,
l.data.thetap * i + l.data.thetaq * (1-i)),
CirclePoint(c,u,v,
l.data.thetap * j + l.data.thetaq * (1-j)) ];
} else {
var p = l.data.p;
var q = l.data.q;
return [ LinePoint(p,q,i), LinePoint(p,q,j) ];
}
}
}
function AddLine(p, q) {
var l = {p:p, q:q, data:geom.PoincareLine(p.Coordinates(), q.Coordinates())};
lines.push(l);
for (var i = 0; i < k; ++i) {
circle.AddSegment(PoincareLine(l, i/k, (i+1)/k), "blue", 2);
}
}
function AddLineDir(p, d) {
var l = {p:p, d:d, data:geom.PoincareLineDir(p.Coordinates(), d)};
lines.push(l);
for (var i = 0; i < k; ++i) {
circle.AddSegment(PoincareLine(l, i/k, (i+1)/k), "blue", 2);
}
}
function AddSegment(p, q, color = "blue") {
var l = {p:p, q:q, data:geom.PoincareLine(p.Coordinates(), q.Coordinates())};
segments.push(l);
for (var i = 0; i < k; ++i) {
circle.AddSegment(PoincareSegment(l, i/k, (i+1)/k), color, 2);
}
}
function AddSegmentDir(p, d, dist, color="blue") {
var l = {p:p, d:d, dist:dist, data:geom.PoincareSegmentDir(p.Coordinates(), d, dist)};
segments.push(l);
for (var i = 0; i < k; ++i) {
circle.AddSegment(PoincareSegment(l, i/k, (i+1)/k), color, 2);
}
}
function WrapVector(v) {
return {
Coordinates: function() { return v; },
};
}
function AddFixedSegment(p,q, color="blue") {
AddSegment(WrapVector(p), WrapVector(q), color);
}
function AddEuclidianSegment(p,q) {
circle.AddSegment(function() { return [p,q]; }, "red", 1);
}
function Update() {
lines.forEach(function(l) {
if (l.d != undefined) {
l.data = geom.PoincareLineDir(l.p.Coordinates(), l.d);
} else {
l.data = geom.PoincareLine(l.p.Coordinates(), l.q.Coordinates());
}
});
segments.forEach(function(l) {
if (l.d != undefined) {
l.data = geom.PoincareSegmentDir(l.p.Coordinates(), l.d, l.dist)
} else {
l.data = geom.PoincareLine(l.p.Coordinates(), l.q.Coordinates());
}
});
circle.Update();
}
return {
AddPoint: circle.AddPoint,
AddLine: AddLine,
AddLineDir: AddLineDir,
AddSegment: AddSegment,
AddFixedSegment: AddFixedSegment,
AddSegmentDir: AddSegmentDir,
AddEuclidianSegment: AddEuclidianSegment,
Update: Update,
SetLocked : function(locked_) { circle.SetLocked(locked_); },
};
}