{
var width = 600;
var height = 600;
var svg = d3.select(DOM.svg(width, height))
.style("overflow", "visible");
var margin = ({top: 10, right: 10, bottom: 10, left: 10})
var x = d3.scaleLinear()
.domain([-1, 1])
.range([margin.left, width - margin.right])
var y = d3.scaleLinear()
.domain([-1, 1])
.range([height - margin.bottom, margin.top])
var xAxis = g => g
.attr("transform", `translate(0,${(height + margin.top - margin.bottom)/2})`)
.call(d3.axisBottom(x))
var yAxis = g => g
.attr("transform", `translate(${(width + margin.left - margin.right)/2},0)`)
.call(d3.axisLeft(y))
var grid = g => g
.attr("stroke", "currentColor")
.attr("stroke-opacity", 0.1)
.call(g => g.append("g")
.selectAll("line")
.data(x.ticks())
.join("line")
.attr("x1", d => 0.5 + x(d))
.attr("x2", d => 0.5 + x(d))
.attr("y1", margin.top)
.attr("y2", height - margin.bottom))
.call(g => g.append("g")
.selectAll("line")
.data(y.ticks())
.join("line")
.attr("y1", d => 0.5 + y(d))
.attr("y2", d => 0.5 + y(d))
.attr("x1", margin.left)
.attr("x2", width - margin.right));
svg.append("g")
.call(xAxis);
svg.append("g")
.call(yAxis);
svg.append("g")
.call(grid);
var drawer = arc_drawer(svg, x, y);
drawer.AddArc([1,0], [-1,0], y(1)-y(0), "red");
drawer.AddArc([-1,0], [1,0], y(1)-y(0), "red");
// Apeirogon tiling initial edge
function ApeirogonSide(k) {
var x = Math.cos(Math.PI / k);
return Math.log((1+x)/(1-x));
}
var k = 3;
var side = ApeirogonSide(k);
var ap = [.1,.1];
var aq = hyperbolic.SegmentDir(ap, [0,1], side).q;
var drawing_threshold = 0.1;
var max_level = 20;
function DrawTiling(ap, aq) {
drawer.ClearHArcs();
drawer.AddHArc(ap, aq);
var Queue = [];
var aseg = hyperbolic.Line(ap, aq);
Queue.push({p:ap, d:euclid.Scale(aseg.tp, -1), l:1});
Queue.push({p:aq, d:euclid.Scale(aseg.tq, -1), l:1});
while (Queue.length > 0) {
var e = Queue.pop();
for (var i = 1; i < k; ++i) {
var d1 = euclid.Rotate(e.d, 2 * Math.PI * i/ k);
var seg = hyperbolic.SegmentDir(e.p, d1, side);
drawer.AddHArc(e.p, seg.q);
var dist = euclid.Distance(e.p, seg.q);
if (e.l <= max_level && dist > drawing_threshold) {
Queue.push({p:seg.q, d:euclid.Scale(seg.tq, -1), l:e.l+1});
}
}
}
}
DrawTiling(ap, aq);
//////////////////////
var ladybug = [await ladybug1.url(), await ladybug2.url()];
function Bug(svg) {
var bug = svg.append("image").attr("xlink:href", ladybug[0])
.attr("height", "80");
var bug_state = 0;
function MoveBug(p,d) {
var theta = Math.atan2(d[0], d[1]) * 180 / Math.PI;
bug.attr("transform", `translate(${x(p[0])},${y(p[1])}) rotate(${theta}) translate(-25,-42)`);
}
function MoveLegs() {
bug.attr("xlink:href", ladybug[bug_state]);
bug_state = (bug_state+1)%2;
}
function SetOnClick(f) {
bug.on("click", f);
}
return {
MoveBug: MoveBug,
MoveLegs: MoveLegs,
SetOnClick: SetOnClick,
};
}
function BugControl(bug, linear_speed, angular_speed) {
var p = [0,0];
var d = [0,1];
bug.MoveBug(p,d);
function Rotate(sign=1) {
var dtheta = sign * angular_speed * Math.PI / 180.0;
d = matrix.Prod(matrix.Rotation(dtheta), d);
bug.MoveBug(p,d);
}
function GoForward(draw=false) {
var seg = hyperbolic.SegmentDir(p, d, linear_speed);
if (draw) {
drawer.AddHArc(p, seg.q);
}
p = seg.q;
d = seg.tq;
bug.MoveBug(p,d);
}
return {
Rotate: Rotate,
GoForward: GoForward,
};
}
var bug = Bug(svg);
var bug_control = BugControl(bug, /*linear_speed*/ .02, /*angular_speed*/ 2);
var keys_pressed = {'d':false, 'f':false, 'j':false};
function UpdateKeys() {
keys_pressed[d3.event.key] = d3.event.type == 'keydown';
}
function RegisterKeyboardControls() {
d3.select("body")
.on("keydown", UpdateKeys)
.on("keyup", UpdateKeys);
}
RegisterKeyboardControls();
bug.SetOnClick(RegisterKeyboardControls);
function Move() {
if (keys_pressed['f']) {
bug_control.Rotate();
}
if (keys_pressed['d']) {
bug_control.Rotate(-1);
}
if (keys_pressed['j'] && keys_pressed['k']) {
bug_control.GoForward(true);
} else if (keys_pressed['j']) {
bug_control.GoForward(false);
}
if (keys_pressed['f'] || keys_pressed['d'] || keys_pressed['j']) {
bug.MoveLegs();
}
setTimeout(Move, 50);
}
Move();
return svg.node();
}