2012-03-15 17:44:48 -06:00
|
|
|
/*
|
|
|
|
* LADD Roller Derby Track
|
|
|
|
* Copyright © 2012 Neale Pickett <neale@woozle.org>
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or (at
|
|
|
|
* your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2012-03-15 16:20:29 -06:00
|
|
|
var canvas;
|
|
|
|
var ctx;
|
|
|
|
var scale;
|
|
|
|
|
|
|
|
var tau = Math.PI * 2;
|
|
|
|
|
|
|
|
// Measurements from Appendix B, 2010 rulebook
|
|
|
|
var ri = 12.5;
|
|
|
|
var ro = 26.5;
|
|
|
|
var halflen = 17.5;
|
|
|
|
var offset = 1.0; // How far off-center the outer circle is
|
|
|
|
var curve_d = 7 + (0.5/12); // Distance between lines in curves
|
|
|
|
var theta = Math.acos(1 - (curve_d * curve_d) / (2 * ri * ri));
|
|
|
|
|
|
|
|
var rp = 1; // Radius of a piece
|
|
|
|
var JAMMER = 0;
|
|
|
|
var PIVOT = 1;
|
2012-03-15 17:37:50 -06:00
|
|
|
var players = [];
|
2012-03-15 16:20:29 -06:00
|
|
|
|
2012-03-15 16:46:05 -06:00
|
|
|
function debug(msg) {
|
|
|
|
var e = document.getElementById("debug");
|
|
|
|
e.innerHTML = msg;
|
|
|
|
}
|
2012-03-15 16:20:29 -06:00
|
|
|
|
2012-03-15 22:32:06 -06:00
|
|
|
var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,';
|
|
|
|
|
|
|
|
function aenc(i) {
|
|
|
|
var neg = i < 0;
|
|
|
|
i = Math.max(Math.min(i, 127), -127);
|
|
|
|
i = Math.round(Math.abs(i) * 16);
|
|
|
|
|
|
|
|
var al = i & 0x3f;
|
|
|
|
var ah = (i >> 6) | (neg?0x20:0);
|
|
|
|
|
|
|
|
return b64.charAt(ah) + b64.charAt(al);
|
|
|
|
}
|
|
|
|
|
|
|
|
function adec(s) {
|
|
|
|
var ah = b64.indexOf(s[0]);
|
|
|
|
var al = b64.indexOf(s[1]);
|
|
|
|
var neg = (ah & 0x20);
|
|
|
|
var i = ((ah & 0x1f) << 6) | al;
|
|
|
|
|
|
|
|
return i * (neg?-1:1) / 16;
|
|
|
|
}
|
|
|
|
|
2012-03-15 17:37:50 -06:00
|
|
|
function player(color, pos) {
|
2012-03-15 16:20:29 -06:00
|
|
|
var e = document.createElement("canvas");
|
|
|
|
var ctx = e.getContext("2d");
|
|
|
|
var body = document.getElementsByTagName("body")[0];
|
|
|
|
var midpoint = rp + 0.5;
|
|
|
|
|
2012-03-15 22:32:06 -06:00
|
|
|
e.enc = function() {
|
|
|
|
return aenc(e.pos[0]) + aenc(e.pos[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
e.dec = function(s) {
|
|
|
|
var x = adec(s.substr(0, 2));
|
|
|
|
var y = adec(s.substr(2, 2));
|
|
|
|
|
|
|
|
e.moveTo(x, y);
|
|
|
|
}
|
|
|
|
|
2012-03-15 16:20:29 -06:00
|
|
|
e.moveTo = function(x, y) {
|
|
|
|
var wx = ((x - midpoint) * scale) + window.innerWidth/2;
|
2012-03-15 19:01:05 -06:00
|
|
|
var wy = (y - midpoint) * scale + canvas.height/2;
|
2012-03-15 16:20:29 -06:00
|
|
|
|
2012-03-15 17:37:50 -06:00
|
|
|
e.pos = [x, y];
|
2012-03-15 16:20:29 -06:00
|
|
|
e.style.left = wx + "px";
|
|
|
|
e.style.top = wy + "px";
|
|
|
|
}
|
|
|
|
|
2012-03-15 20:34:11 -06:00
|
|
|
var moved = false;
|
2012-03-15 16:20:29 -06:00
|
|
|
function mouseMove(evt) {
|
|
|
|
var x = (evt.pageX - window.innerWidth/2) / scale;
|
2012-03-15 19:01:05 -06:00
|
|
|
var y = (evt.pageY - canvas.height/2) / scale;
|
2012-03-15 16:20:29 -06:00
|
|
|
|
|
|
|
e.moveTo(x, y);
|
2012-03-15 20:34:11 -06:00
|
|
|
moved = true;
|
2012-03-15 16:20:29 -06:00
|
|
|
}
|
|
|
|
|
2012-03-15 20:34:11 -06:00
|
|
|
function drop() {
|
|
|
|
if (! moved) {
|
|
|
|
e.onmouseup = null;
|
|
|
|
e.onmousedown = null;
|
|
|
|
e.onclick = drop;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-15 17:37:50 -06:00
|
|
|
var l = document.getElementById("link");
|
2012-03-15 22:32:06 -06:00
|
|
|
var positions = '';
|
2012-03-15 17:37:50 -06:00
|
|
|
|
|
|
|
for (var i = 0; i < players.length; i += 1) {
|
2012-03-15 22:32:06 -06:00
|
|
|
positions += players[i].enc();
|
2012-03-15 17:37:50 -06:00
|
|
|
}
|
|
|
|
|
2012-03-15 22:32:06 -06:00
|
|
|
l.href = "#" + positions;
|
2012-03-15 17:37:50 -06:00
|
|
|
|
2012-03-15 20:34:11 -06:00
|
|
|
e.style.backgroundColor = "inherit";
|
2012-03-15 16:20:29 -06:00
|
|
|
window.onmousemove = null;
|
2012-03-15 20:34:11 -06:00
|
|
|
e.onmousedown = lift;
|
|
|
|
e.onclick = null;
|
2012-03-15 16:20:29 -06:00
|
|
|
}
|
|
|
|
|
2012-03-15 20:34:11 -06:00
|
|
|
function lift() {
|
|
|
|
moved = false;
|
2012-03-15 16:20:29 -06:00
|
|
|
window.onmousemove = mouseMove;
|
2012-03-15 20:34:11 -06:00
|
|
|
e.style.backgroundColor = "rgba(255, 255, 0, 0.5)";
|
|
|
|
e.onmouseup = drop;
|
2012-03-15 16:20:29 -06:00
|
|
|
}
|
2012-03-15 20:34:11 -06:00
|
|
|
e.onmousedown = lift;
|
2012-03-15 16:20:29 -06:00
|
|
|
|
2012-03-15 17:37:50 -06:00
|
|
|
players.push(e);
|
2012-03-15 16:20:29 -06:00
|
|
|
body.appendChild(e);
|
|
|
|
e.style.position = "absolute";
|
|
|
|
e.moveTo(0, 0);
|
|
|
|
|
|
|
|
// Draw it
|
|
|
|
e.width = scale * (midpoint*2);
|
|
|
|
e.height = scale * (midpoint*2);
|
|
|
|
ctx.scale(scale, scale);
|
|
|
|
ctx.translate(midpoint, midpoint);
|
|
|
|
|
|
|
|
ctx.lineWidth = 2/12;
|
|
|
|
ctx.strokeStyle = "#fff";
|
|
|
|
ctx.fillStyle = color;
|
|
|
|
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.arc(0, 0, rp, 0, tau);
|
|
|
|
ctx.closePath();
|
|
|
|
ctx.fill();
|
|
|
|
ctx.stroke();
|
|
|
|
|
2012-03-15 17:37:50 -06:00
|
|
|
if (pos == PIVOT) {
|
2012-03-15 16:20:29 -06:00
|
|
|
ctx.fillStyle = "#fff";
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.arc(0, 0, rp, tau*31/32, tau* 1/32);
|
|
|
|
ctx.arc(0, 0, rp, tau*15/32, tau*17/32);
|
|
|
|
ctx.closePath();
|
|
|
|
ctx.fill();
|
2012-03-15 17:37:50 -06:00
|
|
|
} else if (pos == JAMMER) {
|
2012-03-15 16:20:29 -06:00
|
|
|
ctx.save();
|
|
|
|
ctx.fillStyle = "#fff";
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.rotate(tau/2);
|
|
|
|
ctx.moveTo(0, rp);
|
|
|
|
for (var i = 0; i < 5; i += 1) {
|
|
|
|
ctx.rotate(tau/10);
|
|
|
|
ctx.lineTo(0, rp * 0.4);
|
|
|
|
ctx.rotate(tau/10);
|
|
|
|
ctx.lineTo(0, rp);
|
|
|
|
}
|
|
|
|
ctx.fill();
|
|
|
|
ctx.restore();
|
2012-03-15 17:37:50 -06:00
|
|
|
} else {
|
2012-03-15 20:34:11 -06:00
|
|
|
// android doesn't scale text :<
|
|
|
|
ctx.scale(1/scale, 1/scale);
|
2012-03-15 17:37:50 -06:00
|
|
|
ctx.fillStyle = "#fff";
|
2012-03-15 20:34:11 -06:00
|
|
|
ctx.font = (scale) + "px sans-serif";
|
|
|
|
ctx.fillText(pos, -0.25 * scale, 0.25 * scale);
|
2012-03-15 16:20:29 -06:00
|
|
|
}
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2012-03-15 18:41:46 -06:00
|
|
|
function drawTrack(ctx) {
|
2012-03-15 16:20:29 -06:00
|
|
|
ctx.beginPath();
|
|
|
|
ctx.arc( halflen, -offset, ro, tau*3/4, tau*1/4);
|
|
|
|
ctx.arc(-halflen, offset, ro, tau*1/4, tau*3/4);
|
2012-03-15 18:41:46 -06:00
|
|
|
ctx.lineTo( halflen, -offset - ro);
|
|
|
|
ctx.moveTo( halflen, -ri);
|
|
|
|
ctx.lineTo(-halflen, -ri);
|
|
|
|
ctx.arc(-halflen, 0, ri, tau*3/4, tau*1/4, true);
|
|
|
|
ctx.arc( halflen, 0, ri, tau*1/4, tau*3/4, true);
|
2012-03-15 16:20:29 -06:00
|
|
|
ctx.closePath();
|
|
|
|
}
|
|
|
|
|
|
|
|
function start() {
|
|
|
|
canvas = document.getElementById("canvas");
|
|
|
|
ctx = canvas.getContext("2d");
|
|
|
|
|
2012-03-15 19:01:05 -06:00
|
|
|
scale = Math.min(window.innerWidth / 100, (window.innerHeight - 20) / 60);
|
2012-03-15 16:20:29 -06:00
|
|
|
|
|
|
|
canvas.width = scale * 100;
|
|
|
|
canvas.height = scale * 60;
|
|
|
|
|
|
|
|
// Set things up so all measurements are in feet.
|
|
|
|
// This gives a 6-foot vertical border on each side,
|
|
|
|
// and a 2.5-foot horizontal border on each side.
|
|
|
|
ctx.scale(scale, scale);
|
|
|
|
ctx.translate(50, 30);
|
|
|
|
|
|
|
|
|
|
|
|
// Draw track according to WFTDA 2010 rulebook, Appendix B
|
|
|
|
|
|
|
|
|
|
|
|
// Fill in track area
|
|
|
|
ctx.fillStyle = "#888";
|
2012-03-15 18:41:46 -06:00
|
|
|
drawTrack(ctx);
|
2012-03-15 16:20:29 -06:00
|
|
|
ctx.fill();
|
|
|
|
|
|
|
|
// A bunch of lines
|
|
|
|
ctx.lineWidth = 1/12;
|
|
|
|
ctx.strokeStyle = "rgba(0, 0, 0, 0.4)";
|
|
|
|
ctx.beginPath();
|
|
|
|
for (var i = 0; i < 4; i += 1) {
|
2012-03-15 18:41:46 -06:00
|
|
|
ctx.moveTo(halflen - i*10, ri);
|
2012-03-15 16:20:29 -06:00
|
|
|
ctx.lineTo(halflen - i*10, ro + offset);
|
|
|
|
|
2012-03-15 18:41:46 -06:00
|
|
|
ctx.moveTo(i*10 - halflen, -ri);
|
2012-03-15 16:20:29 -06:00
|
|
|
ctx.lineTo(i*10 - halflen, -ro - offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (var j = 0; j < 2; j += 1) {
|
|
|
|
ctx.save();
|
|
|
|
ctx.translate(halflen * (j?-1:1), 0);
|
|
|
|
ctx.rotate(j*tau/2);
|
|
|
|
for (var i = 0; i < 5; i += 1) {
|
|
|
|
ctx.rotate(-theta);
|
2012-03-15 18:41:46 -06:00
|
|
|
ctx.moveTo(0, ri);
|
2012-03-15 16:20:29 -06:00
|
|
|
ctx.lineTo(0, 12.5 + 15.0);
|
|
|
|
}
|
|
|
|
ctx.restore();
|
|
|
|
}
|
|
|
|
ctx.stroke();
|
|
|
|
|
|
|
|
// Pivot and Jammer lines
|
|
|
|
ctx.lineWidth = 4/12;
|
|
|
|
ctx.strokeStyle = "#000";
|
|
|
|
ctx.beginPath();
|
2012-03-15 18:41:46 -06:00
|
|
|
ctx.moveTo(halflen , ri);
|
|
|
|
ctx.lineTo(halflen , ro - offset);
|
|
|
|
ctx.moveTo(halflen - 30, ri);
|
2012-03-15 16:20:29 -06:00
|
|
|
ctx.lineTo(halflen - 30, ro + offset);
|
|
|
|
ctx.stroke();
|
|
|
|
|
2012-03-15 18:41:46 -06:00
|
|
|
// Now draw track boundaries
|
2012-03-15 16:20:29 -06:00
|
|
|
ctx.lineWidth = 4/12;
|
|
|
|
ctx.strokeStyle = "#ff0";
|
|
|
|
ctx.fillStyle = "#000";
|
2012-03-15 18:41:46 -06:00
|
|
|
drawTrack(ctx);
|
2012-03-15 16:20:29 -06:00
|
|
|
ctx.stroke();
|
|
|
|
|
|
|
|
|
2012-03-15 22:32:06 -06:00
|
|
|
var positions = location.hash.substr(1);
|
2012-03-15 16:20:29 -06:00
|
|
|
|
|
|
|
for (var team = 0; team < 2; team += 1) {
|
|
|
|
for (var pos = 0; pos < 5; pos += 1) {
|
|
|
|
var p = player(team?"#080":"#f0f", pos);
|
|
|
|
|
2012-03-15 17:37:50 -06:00
|
|
|
if (positions) {
|
2012-03-15 22:32:06 -06:00
|
|
|
var s = positions.substr(4*(team*5 + pos), 4);
|
|
|
|
|
|
|
|
p.dec(s)
|
2012-03-15 17:37:50 -06:00
|
|
|
} else if (pos == JAMMER) {
|
2012-03-15 16:46:05 -06:00
|
|
|
p.moveTo(halflen - 30 - rp, ri + rp*(team*4 + 4));
|
2012-03-15 16:20:29 -06:00
|
|
|
} else {
|
|
|
|
p.moveTo(halflen-rp - team * (rp*3), ri + rp*(2.5*pos));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
window.onload = start;
|