Skip to content

Instantly share code, notes, and snippets.

@ia7ck
Last active September 6, 2019 11:04
Show Gist options
  • Save ia7ck/4b39ce1d14bd0e60fecc9c8fe8e04934 to your computer and use it in GitHub Desktop.
Save ia7ck/4b39ce1d14bd0e60fecc9c8fe8e04934 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>ABC108/ARC102 D</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.5/svg.js"></script>
<script src="./svg.path.js"></script>
<link href="https://fonts.googleapis.com/css?family=Inconsolata|Lato:400,400i" rel="stylesheet">
</head>
<body>
<h3>ABC108/ARC102 D</h3>
<form id="input-form">
<input name="l" type="number" min="2" max="256" />
<button name="button" type="submit">set</button>
</form>
<form id="go-form">
<button name="button" type="submit">go</button>
</form>
<div id="drawing0">
</div>
<div id="drawing">
</div>
<script defer src="./index.js"></script>
</body>
<style>
body {
font-family: "Lato", sans-serif;
}
button,
input {
font-family: inherit;
font-size: 100%;
}
form {
padding: .3rem 0 .3rem 0;
}
input[type="number"] {
min-width: 5rem;
}
</style>
</html>
const W = 800, H = 400;
const draw0 = SVG("drawing0").size(W, H / 4).style({ border: "solid 1px" });
const draw = SVG("drawing").size(W, H).style({ border: "solid 1px" });
const radius = 12;
const h = 50, span = 100;
const TEXT_SIZE = 14;
const BASE_TIMEOUT = 1000;
let timeout = BASE_TIMEOUT;
let L = randInt(2, 256);
let digit = encode(L);
let idx = 1;
let firstNode = {
circle: draw.circle(radius * 2).center(radius * 4, h).fill("none").stroke("black"),
text: mytext("1").move(radius * 4, h).dy(-TEXT_SIZE / 1.9),
}
let nodes = [firstNode];
let edges = [];
let l = 1, ad = 1;
init(L);
function init(_L) {
L = _L;
digit = encode(L);
draw0.clear();
draw0.text("L").font({ family: "Lato", size: TEXT_SIZE * 1.8, style: "oblique" }).move(radius * 4, H / 15);
draw0.text("L'").font({ family: "Lato", size: TEXT_SIZE * 1.8, style: "oblique" }).move(radius * 4, H / 15 + TEXT_SIZE * 1.8);
for (let i = 0; i < digit.length; i++) {
digit[i] = {
val: digit[i],
textGoal: draw0.text(String(digit[i])).font({ family: "Inconsolata", size: TEXT_SIZE * 1.8 }).move(radius * 4 + (i + 3) * 15, H / 15),
textCurrent: draw0.text(" ").font({ family: "Inconsolata", size: TEXT_SIZE * 1.8 }).move(radius * 4 + (i + 3) * 15, H / 15 + TEXT_SIZE),
}
}
draw.clear();
idx = 1;
firstNode = {
circle: draw.circle(radius * 2).center(radius * 4, h).fill("none").stroke("black"),
text: mytext("1").move(radius * 4, h).dy(-TEXT_SIZE / 1.9),
}
nodes = [firstNode];
edges = [];
l = 1;
ad = 1;
}
const sleep = (ms) => (new Promise(resolve => setTimeout(resolve, ms)));
async function suc(i) {
for (let edge of edges) {
const val = parseInt(edge.text.text(), 10);
edge.text.text(String(val * 2))
}
await sleep(timeout);
const previousNode = nodes[nodes.length - 1];
const currentNode = {
circle: draw.circle(radius * 2).center(previousNode.circle.cx() + span, h).fill("none").stroke("black").hide(),
text: mytext(String(nodes.length + 1)).attr({ visibility: "hidden" }).move(previousNode.circle.cx() + span, h).dy(-TEXT_SIZE / 1.9),
};
nodes.push(currentNode);
const edge0 = {
path: draw.path().fill("none").stroke("black").M(previousNode.circle.cx() + radius, h).H(currentNode.circle.cx() - radius).drawAnimated(timeout).attr({ "stroke-width": 1 }),
text: mytext("0").move(Math.floor((previousNode.circle.cx() + currentNode.circle.cx()) / 2), h).dy(-TEXT_SIZE).attr({ visibility: "hidden" }),
};
const first = i === 1, last = i + 1 === digit.length;
const edge1 = {
path: draw.path().fill("none").stroke("black").M(previousNode.circle.cx() + (first ? 0 : 4), h + radius).v(radius * 2).H(currentNode.circle.cx() - (last ? 0 : 4)).v(-radius * 2).drawAnimated(timeout).attr({ "stroke-width": 1 }),
text: mytext("1").move(Math.floor((previousNode.circle.cx() + currentNode.circle.cx()) / 2), h + radius * 2).dy(-3).attr({ visibility: "hidden" })
};
edges.push(edge0, edge1);
await sleep(timeout);
edge0.path.marker("end", 10, 10, (add) => { add.path().fill("none").stroke("black").M(0, 0).l(5, 5).l(-5, 5) })
edge1.path.marker("end", 10, 10, (add) => { add.path().fill("none").stroke("black").M(0, 0).l(5, 5).l(-5, 5) }) // ???
currentNode.circle.show();
currentNode.text.attr({ visibility: "visible" });
edge0.text.attr({ visibility: "visible" })
edge1.text.attr({ visibility: "visible" })
l *= 2;
update(l);
if (digit[i].val) {
await sleep(timeout);
const extraEdge = {
path: draw.path().fill("none").stroke("black").M(firstNode.circle.cx(), firstNode.circle.cy() + radius).v(radius * 3 * (ad + 1)).H(currentNode.circle.cx() - (last ? 0 : 4)).v(-radius * 3 * (ad + 1)).drawAnimated(timeout).attr({ "stroke-width": 1 }),
text: mytext(String(l)).move(Math.floor((firstNode.circle.cx() + currentNode.circle.cx()) / 2), h + radius * 3 * (ad + 1)).dy(-3).attr({ visibility: "hidden" })
};
edges.push(extraEdge)
await sleep(timeout);
extraEdge.text.attr({ visibility: "visible" })
l += 1;
ad += 1;
update(l);
}
}
function mytext(str) {
return draw.text(str).fill("black").attr({ "text-anchor": "middle" }).font({ family: "Inconsolata", size: TEXT_SIZE });
}
function encode(x) {
let ret = []
while (x > 0) {
ret.push(x & 1);
x = x >> 1;
}
return ret.reverse();
}
function update(l) {
const _digit = encode(l);
while (_digit.length < digit.length) _digit.unshift(0);
let found = false;
for (let i = 0; i < _digit.length; i++) {
if (_digit[i]) {
found = true;
}
digit[i].textCurrent.text(found ? String(_digit[i]) : " ");
}
}
function randInt(lb, ub) {
return Math.floor(Math.random() * (ub - lb)) + lb;
}
const inputForm = document.getElementById("input-form");
const goForm = document.getElementById("go-form");
inputForm.l.value = L;
inputForm.addEventListener("submit", (ev) => {
ev.preventDefault();
const val = parseInt(ev.target.l.value, 10);
init(val);
goForm.button.disabled = false;
});
goForm.addEventListener("submit", async (ev) => {
ev.preventDefault();
ev.target.button.disabled = true;
inputForm.button.disabled = true;
await suc(idx++);
if (idx < digit.length) {
ev.target.button.disabled = false;
}
inputForm.button.disabled = false;
})
/** svg.path.js - v0.6.0 - 2014-08-15
* http://otm.github.io/svg.path.js/
* Copyright (c) 2014 Nils Lagerkvist; Licensed under the MIT license /
*/
(function () {
var slice = Function.prototype.call.bind(Array.prototype.slice);
SVG.extend(SVG.Path, {
M: function (p) {
p = (arguments.length === 1) ? [p.x, p.y] : slice(arguments);
this.addSegment('M', p, this._redrawEnabled);
if (this._segments.length === 1) {
return this.plot('M' + p[0] + ' ' + p[1]);
}
return this;
},
m: function (p) {
p = (arguments.length === 1) ? [p.x, p.y] : slice(arguments);
this.addSegment('m', p, this._redrawEnabled);
if (this._segments.length === 1) {
return this.plot('m' + p[0] + ' ' + p[1]);
}
return this;
},
// TODO: Solve
L: function (p) {
p = (arguments.length === 1) ? [p.x, p.y] : slice(arguments);
return this.addSegment('L', p, this._redrawEnabled);
},
l: function (p) {
p = (arguments.length === 1) ? [p.x, p.y] : slice(arguments);
return this.addSegment('l', p, this._redrawEnabled);
},
H: function (x) {
return this.addSegment('H', [x], this._redrawEnabled);
},
h: function (x) {
return this.addSegment('h', [x], this._redrawEnabled);
},
V: function (y) {
return this.addSegment('V', [y], this._redrawEnabled);
},
v: function (y) {
return this.addSegment('v', [y], this._redrawEnabled);
},
C: function (p1, p2, p) {
p = (arguments.length === 3) ? [p1.x, p1.y, p2.x, p2.y, p.x, p.y] : slice(arguments);
return this.addSegment('C', p, this._redrawEnabled);
},
c: function (p1, p2, p) {
p = (arguments.length === 3) ? [p1.x, p1.y, p2.x, p2.y, p.x, p.y] : slice(arguments);
return this.addSegment('c', p, this._redrawEnabled);
},
S: function (p2, p) {
p = (arguments.length === 2) ? [p2.x, p2.y, p.x, p.y] : slice(arguments);
return this.addSegment('S', p, this._redrawEnabled);
},
s: function (p2, p) {
p = (arguments.length === 2) ? [p2.x, p2.y, p.x, p.y] : slice(arguments);
return this.addSegment('s', p, this._redrawEnabled);
},
// Q x1 y1, x y
Q: function (p1, p) {
p = (arguments.length === 2) ? [p1.x, p1.y, p.x, p.y] : slice(arguments);
return this.addSegment('Q', p, this._redrawEnabled);
},
q: function (p1, p) {
p = (arguments.length === 2) ? [p1.x, p1.y, p.x, p.y] : slice(arguments);
return this.addSegment('q', p, this._redrawEnabled);
},
T: function (p) {
p = (arguments.length === 1) ? [p.x, p.y] : slice(arguments);
return this.addSegment('T', p, this._redrawEnabled);
},
t: function (p) {
p = (arguments.length === 1) ? [p.x, p.y] : slice(arguments);
return this.addSegment('t', p, this._redrawEnabled);
},
A: function (rx, ry, xAxisRotation, largeArcFlag, sweepFlag, p) {
p = (arguments.length === 6) ? [rx, ry, xAxisRotation, largeArcFlag, sweepFlag, p.x, p.y] : slice(arguments);
return this.addSegment('A', p, this._redrawEnabled);
},
a: function (rx, ry, xAxisRotation, largeArcFlag, sweepFlag, p) {
p = (arguments.length === 6) ? [rx, ry, xAxisRotation, largeArcFlag, sweepFlag, p.x, p.y] : slice(arguments);
return this.addSegment('a', p, this._redrawEnabled);
},
Z: function () {
return this.addSegment('Z', [], this._redrawEnabled);
},
// TODO: Add check that first element is moveto
addSegment: function (movement, coordinates, redraw) {
var segment = {
type: movement,
coords: coordinates
};
if (!this._segments) {
this._segments = [];
}
this._segments.push(segment);
if (redraw !== false) {
this._drawSegment(segment);
}
return this;
},
clearPath: function () {
if (this._segments) {
this._segments.length = 0;
}
this._lastSegment = null;
return this.plot();
},
getSegmentCount: function () {
return this._segments.length;
},
getSegment: function (index) {
return this._segments[index];
},
removeSegment: function (index) {
this._segments.splice(index, 1);
return this.redraw();
},
replaceSegment: function (index, segment) {
this._segments.splice(index, 1, segment);
return this.redraw();
},
/**
* Easing:
* <>: ease in and out
* >: ease out
* <: ease in
* -: linear
* =: external control
* a function
*/
drawAnimated: function (options) {
options = options || {};
options.duration = options.duration || '1000';
options.easing = options.easing || '<>';
options.delay = options.delay || 0;
var length = this.length();
this.stroke({
width: 2,
dasharray: length + ' ' + length,
dashoffset: length
});
var fx = this.animate(options.duration, options.easing, options.delay);
fx.stroke({
dashoffset: 0
});
return this;
},
update: function (redraw) {
if (redraw === true)
this._redrawEnabled = false;
if (redraw === false)
this._redrawEnabled = true;
return !!this._redrawEnabled;
},
redraw: function () {
// reset
this._lastSegment = null;
this.attr('d', '');
return this._drawSegment(this._segments);
},
_drawSegment: function (segment) {
var str = '', lastSegment = this._lastSegment;
if (!Array.isArray(segment)) {
segment = [segment];
}
for (var i = 0; i < segment.length; i += 1) {
if (lastSegment === segment[i].type) {
str += ' ' + segment[i].coords.join(' ');
}
else {
str += ' ' + segment[i].type + segment[i].coords.join(' ');
}
lastSegment = segment[i].type;
}
this._lastSegment = lastSegment;
return this.attr('d', (this.attr('d') || '') + str);
}
});
}).call(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment