Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Finally fix all interpolation jumps, add startTime to cvc-sim and box…

…xy-interface, first version of real minimap
  • Loading branch information...
commit af38ad712c27907fca7718ed9dbab50202f3ac1e 1 parent ec65091
Michiel Van den Berghe mivdnber authored
3  boxxy/public/interpolationdemo/demo.html
View
@@ -54,11 +54,14 @@
error = predictedPosition - message.station.position;
error = Math.min(error, 400 - error);
interpolation.update(message);
+ var newPredictedPosition = interpolation.getPosition(message.team.id),
+ calcError = newPredictedPosition - predictedPosition;
$('#' + message.team.id).children().eq(1).html(message.station.position);
$('#' + message.team.id).children().eq(3).html(message.speed);
$('#' + message.team.id).children().eq(4).html(interpolation.getSpeed(message.team.id));
$('#' + message.team.id).children().eq(5).html(error);
log.innerHTML = "[POSITION] " + JSON.stringify(message) + "\n" + log.innerHTML;
+ log.innerHTML = "[ERROR] " + calcError + "\n" + log.innerHTML;
}
client.connect();
34 boxxy/public/interpolationdemo/drawing.js
View
@@ -32,6 +32,34 @@ function drawCircuit() {
context.stroke();
}
+function drawRealCircuit() {
+ var canvas = document.getElementById("boxxy-interpolation"),
+ context = canvas.getContext("2d"),
+ scale = 300,
+ offsetX = 50,
+ offsetY = 50,
+ shape = [
+ {x: 0, y: 0.07},
+ {x: 0.1, y: 0},
+ {x: 0.9, y: 0},
+ {x: 1, y: 0.07},
+ {x: 1, y: 0.28},
+ {x: 0.9, y: 0.35},
+ {x: 0.1, y: 0.35},
+ {x: 0, y: 0.28}
+ ];
+ context.beginPath();
+ context.moveTo(shape[0].x * scale + offsetX, shape[0].y * scale + offsetY)
+ for(var idx = 1; idx <= shape.length; idx++) {
+ var pos = shape[idx % shape.length];
+ context.lineTo(pos.x * scale + offsetX, pos.y * scale + offsetY);
+ }
+ context.lineWidth = 10;
+ context.strokeStyle = "#8ED6FF";
+ context.lineCap = 'round';
+ context.stroke();
+}
+
function initCanvas(boxxy, interpolation) {
var fps = 30;
@@ -50,17 +78,17 @@ function initCanvas(boxxy, interpolation) {
clearCanvas()
// draw everything!
- drawCircuit();
+ drawRealCircuit();
var stations = boxxy.getStations();
for(var name in stations) {
var station = stations[name];
- var pos = shapes.rect(station.position / boxxy.circuitLength);
+ var pos = shapes.real(station.position / boxxy.circuitLength);
drawStation(station.name[station.name.length - 1], pos);
}
var teams = boxxy.getTeams();
for(var idx in teams) {
- var coords = interpolation.getCoords(teams[idx].id, shapes.rect);
+ var coords = interpolation.getCoords(teams[idx].id, shapes.real);
drawTeam(teams[idx].name, coords);
}
1  boxxy/public/lib/boxxy-interface.js
View
@@ -43,6 +43,7 @@ function Boxxy() {
}
self.circuitLength = config.circuitLength;
+ self.startTime = config.startTime;
updateRanking();
self.receivedConfig(config);
99 boxxy/public/lib/interpolation.js
View
@@ -17,7 +17,8 @@ function Interpolation() {
speed: team.speed,
station: team.station,
time: new Date(team.time),
- lastPosition: team.station.position
+ lastPosition: team.station.position,
+ correction: 0
}
}
@@ -26,6 +27,13 @@ function Interpolation() {
}
this.update = function(message) {
+ // Only correct time once. Can cause jumps otherwise.
+ // TODO: use timestamp from config message
+ if(self.timeOffset == 0) {
+ console.log(message.time);
+ self.timeOffset = new Date() - new Date(message.time);
+ console.log(self.timeOffset);
+ }
var team = self.teams[message.team.id],
predictedPosition = self.getPosition(team.id, new Date(message.time)),
actualPosition = message.station.position,
@@ -36,17 +44,13 @@ function Interpolation() {
team.time = new Date(message.time);
team.speed = targetSpeed;
team.station = message.station;
- // Only correct time once. Can cause jumps otherwise.
- // TODO: use timestamp from config message
- if(self.timeOffset == 0) {
- self.timeOffset = (new Date()) - team.time;
- }
}
this.getPosition = function(teamid, time) {
if(time == undefined) {
- time = (new Date() - self.timeOffset);
+ time = new Date();
}
+ time -= self.timeOffset;
var team = self.teams[teamid],
distanceTravelled = (time - team.time) / 1000 * team.speed;
return self.mod(team.lastPosition + distanceTravelled);
@@ -85,33 +89,56 @@ var shapes = {
rect: function(pos) {
var ratio = 1.61803, // It's art!
width = 1.0,
- height = 1.0 / ratio,
- total = 2 * (width + height),
- progress = pos * total;
- // Bottom
- if(progress < width) {
- return {
- x: progress,
- y: 0
- };
- // Right
- } else if(progress < width + height) {
- return {
- x: width,
- y: progress - width
- };
- // Top
- } else if(progress < 2 * width + height) {
- return {
- x: width - (progress - width - height),
- y: height
- }
- // Left
- } else {
- return {
- x: 0,
- y: total - progress
- }
+ height = 1.0 / ratio;
+
+ return shapes.segmented(pos, [
+ {x: 0, y: 0},
+ {x: width, y: 0},
+ {x: width, y: height},
+ {x: 0, y: height}
+ ]);
+ },
+
+ // Approximation of the real octagonal circuit
+ real: function(pos) {
+ return shapes.segmented(pos, [
+ {x: 0, y: 0.07},
+ {x: 0.1, y: 0},
+ {x: 0.9, y: 0},
+ {x: 1, y: 0.07},
+ {x: 1, y: 0.28},
+ {x: 0.9, y: 0.35},
+ {x: 0.1, y: 0.35},
+ {x: 0, y: 0.28}
+ ]);
+ },
+
+ segmented: function(pos, segments) {
+ var totalLength = 0;
+ // make it a linked list, store length of each segment and
+ // calculate total circuit length.
+ for(var idx = 0; idx < segments.length; idx++) {
+ var last = segments[idx],
+ next = segments[(idx + 1) % segments.length];
+ last.length = Math.sqrt((next.x - last.x) * (next.x - last.x) + (next.y - last.y) * (next.y - last.y));
+ last.next = next;
+ totalLength += last.length;
+ }
+
+ var progressLeft = totalLength * pos,
+ segment = segments[0];
+
+ while(progressLeft > segment.length) {
+ progressLeft -= segment.length;
+ segment = segment.next;
+ }
+
+ var diffY = (segment.next.y - segment.y),
+ diffX = (segment.next.x - segment.x),
+ segmentPercentage = progressLeft / segment.length;
+ return {
+ x: segment.x + segmentPercentage * diffX,
+ y: segment.y + segmentPercentage * diffY
}
- }
-}
+ }
+}
159 boxxy/public/minimap/drawing.js
View
@@ -0,0 +1,159 @@
+var draw = {
+ ctx: undefined,
+ logo: undefined,
+ shape: [
+ {x: 0, y: 0.07},
+ {x: 0.1, y: 0},
+ {x: 0.9, y: 0},
+ {x: 1, y: 0.07},
+ {x: 1, y: 0.28},
+ {x: 0.9, y: 0.35},
+ {x: 0.1, y: 0.35},
+ {x: 0, y: 0.28}
+ ],
+ scale: 250,
+ width: 288,
+ height: 160,
+ padding: 17,
+ offsetY: 55,
+ circuitHeight: 88,
+ fps: 30,
+ boxxy: undefined,
+ interpolation: undefined,
+ highlight: 0,
+
+ init: function(context, boxxy, interpolation) {
+ draw.context = context;
+ draw.logo = new Image();
+ draw.logo.src = '/assets/logo-200.png';
+ draw.boxxy = boxxy;
+ draw.interpolation = interpolation;
+
+ window.requestAnimFrame = (function(callback){
+ return window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ function(callback){
+ window.setTimeout(callback, 1000 / fps);
+ };
+ })();
+
+ draw.highlight = boxxy.getTeams()[0];
+ window.setInterval(function() {
+ var teams = boxxy.getTeams(),
+ idx = Math.round(Math.random() * (teams.length - 1));
+ draw.highlight = teams[idx];
+ }, 3000);
+ },
+
+ clear: function() {
+ draw.context.clearRect(0, 0, draw.width, draw.height);
+ },
+
+ circuit: function() {
+ var scale = 0.4,
+ logoWidth = 328 * scale,
+ logoHeight = 200 * scale,
+ logoX = (draw.width - logoWidth) / 2,
+ logoY = draw.offsetY + (draw.circuitHeight - logoHeight) / 2;
+ draw.context.globalAlpha = 0.2;
+ draw.context.drawImage(draw.logo, logoX, logoY, logoWidth, logoHeight);
+ draw.context.globalAlpha = 1.0;
+ draw.context.beginPath();
+ draw.context.moveTo(draw.shape[0].x * draw.scale + draw.padding, draw.shape[0].y * draw.scale + draw.offsetY)
+ for(var idx = 1; idx <= draw.shape.length; idx++) {
+ var pos = draw.shape[idx % draw.shape.length];
+ draw.context.lineTo(pos.x * draw.scale + draw.padding, pos.y * draw.scale + draw.offsetY);
+ }
+ draw.context.lineWidth = 10;
+ draw.context.strokeStyle = "#8ED6FF";
+ draw.context.lineCap = 'round';
+ draw.context.stroke();
+ },
+
+ team: function(team) {
+ var teamX = draw.padding + team.coords.x * draw.scale,
+ teamY = draw.offsetY + team.coords.y * draw.scale,
+ teamFont = "14pt Verdana";
+
+ draw.context.beginPath();
+ draw.context.fillStyle = "#FF6103"
+ draw.context.arc(teamX, teamY, 10, 0, Math.PI*2, true);
+ draw.context.closePath();
+ draw.context.fill();
+ draw.context.font = teamFont;
+ draw.context.textAlign = "center";
+ draw.context.textBaseline = "middle";
+ draw.context.fillStyle = "black";
+ draw.context.fillText(team.id, teamX, teamY);
+
+ if(team.highlight) {
+ var teamNameX = 0.1 * draw.scale + draw.padding,
+ teamNameY = 0.07 * draw.scale + draw.offsetY,
+ teamNameFont = "14pt Verdana",
+ teamStatsFont = "12pt Verdana";
+
+ draw.context.textBaseline = "top";
+ draw.context.textAlign = "left";
+ draw.context.font = teamNameFont;
+ draw.context.fillText(team.name, teamNameX, teamNameY);
+ draw.context.font = teamStatsFont;
+ draw.context.fillText('rondjes: ' + team.info.laps, teamNameX, teamNameY + 17);
+
+ draw.context.beginPath();
+ draw.context.fillStyle = "#FF6103"
+ draw.context.arc(teamX, teamY, 15, 0, Math.PI*2, true);
+ draw.context.closePath();
+ draw.context.fill();
+
+ draw.context.font = "20pt Verdana";
+ draw.context.textAlign = "center";
+ draw.context.textBaseline = "middle";
+ draw.context.fillStyle = "black";
+
+ draw.context.fillText(team.id, teamX, teamY);
+ }
+ },
+
+ frame: function() {
+ draw.clear()
+
+ // draw everything!
+ draw.circuit();
+
+ var teams = draw.boxxy.getTeams();
+
+ for(var idx in teams) {
+ var coords = draw.interpolation.getCoords(teams[idx].id, shapes.real);
+ draw.team({
+ name: teams[idx].name,
+ id: teams[idx].id.split('-')[1],
+ coords: coords,
+ info: teams[idx],
+ highlight: false
+ });
+ }
+
+ var coords = draw.interpolation.getCoords(draw.highlight.id, shapes.real);
+ draw.team({
+ name: draw.highlight.name,
+ id: draw.highlight.id.split('-')[1],
+ coords: coords,
+ info: draw.highlight,
+ highlight: true
+ })
+
+ requestAnimFrame(function(){
+ draw.frame();
+ });
+ },
+
+ start: function() {
+ requestAnimFrame(function(){
+ draw.frame();
+ });
+ }
+}
+
43 boxxy/public/minimap/index.html
View
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>12 Urenloop :: Minimap</title>
+ <script type="text/javascript">
+ var g = document.createElement('script'),
+ s = document.getElementsByTagName('script')[0];
+ g.src = 'http://' + window.location.hostname + ':8080/boxxy.js';
+ s.parentNode.insertBefore(g, s);
+ </script>
+ <script type="text/javascript" src="/lib/jquery-1.7.1.min.js"></script>
+ <script type="text/javascript" src="/lib/boxxy-interface.js"></script>
+ <script type="text/javascript" src="/lib/interpolation.js"></script>
+
+ <script type="text/javascript" src="drawing.js"></script>
+
+ <script type="text/javascript">
+ window.onload = function() {
+ var client = new Boxxy(),
+ interpolation = new Interpolation(),
+ context = document.getElementById('minimap').getContext('2d');
+
+ client.receivedConfig = function(config) {
+ interpolation.init(config);
+ draw.init(context, client, interpolation);
+ draw.start();
+
+ }
+ client.updatedPosition = function(message) {
+ interpolation.update(message);
+
+ }
+ client.connect();
+
+
+ }
+ </script>
+ <script type="text/javascript" src="drawing.js"></script>
+</head>
+<body>
+ <canvas id="minimap" width="288" height="160"></canvas>
+</body>
+</html>
2  boxxy/server/state.js
View
@@ -33,7 +33,7 @@ exports.updateLaps = function(message) {
var team = state.teams[message.team.id]
var previousLap = team.lastLapCompleted
team.lastLapCompleted = new Date(message.time)
- team.laps = message.team.laps
+ team.laps = message.team.laps + parseInt(message.laps)
message.lapTime = Math.round((team.lastLapCompleted - previousLap) / 1000)
}
1  boxxy/util/cvc-simulator.js
View
@@ -4,6 +4,7 @@ var team = require('./team')
var names = ["Michiel", "Sander", "Pieter"]
var runners = [];
var config = {
+ startTime: team.ISODateString(new Date()),
stations: [
{
position: 0.0,
5 boxxy/util/team.js
View
@@ -1,18 +1,19 @@
exports.Team = Team
-
+exports.ISODateString = ISODateString
var events = require('events'),
util = require('util')
// From Mozilla Developer Network
function ISODateString(d){
function pad(n){return n<10 ? '0'+n : n}
+ function padm(n){return (n < 100 ? '0' : '') + pad(n)}
return d.getUTCFullYear()+'-'
+ pad(d.getUTCMonth()+1)+'-'
+ pad(d.getUTCDate())+'T'
+ pad(d.getUTCHours())+':'
+ pad(d.getUTCMinutes())+':'
+ pad(d.getUTCSeconds())+'.'
- + pad(d.getUTCMilliseconds()) + 'Z'
+ + padm(d.getUTCMilliseconds()) + 'Z'
}
function Team(id, name, config) {
129 drawing.js
View
@@ -0,0 +1,129 @@
+var draw = {
+ ctx: undefined,
+ logo: undefined,
+ shape: [
+ {x: 0, y: 0.07},
+ {x: 0.1, y: 0},
+ {x: 0.9, y: 0},
+ {x: 1, y: 0.07},
+ {x: 1, y: 0.28},
+ {x: 0.9, y: 0.35},
+ {x: 0.1, y: 0.35},
+ {x: 0, y: 0.28}
+ ],
+ scale: 250,
+ padding: 17,
+ offsetY: 55,
+ fps: 30,
+ boxxy: undefined,
+ interpolation: undefined,
+
+ init: function(context, boxxy, interpolation) {
+ draw.context = context;
+ draw.logo = new Image();
+ draw.logo.src = '/assets/logo-200.png';
+ draw.boxxy = boxxy;
+ draw.interpolation = interpolation;
+
+ window.requestAnimFrame = (function(callback){
+ return window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ function(callback){
+ window.setTimeout(callback, 1000 / fps);
+ };
+ })();
+ },
+
+ clear: function() {
+ draw.context.clearRect(0, 0, canvas.width, canvas.height);
+ },
+
+ circuit: function() {
+ draw.context.drawImage(draw.logo, draw.padding, draw.offsetY, 250, 88);
+ draw.context.beginPath();
+ draw.context.moveTo(draw.shape[0].x * draw.scale + draw.padding, draw.shape[0].y * draw.scale + draw.offsetY)
+ for(var idx = 1; idx <= draw.shape.length; idx++) {
+ var pos = draw.shape[idx % draw.shape.length];
+ draw.context.lineTo(pos.x * draw.scale + draw.padding, pos.y * draw.scale + draw.offsetY);
+ }
+ draw.context.lineWidth = 10;
+ draw.context.strokeStyle = "#8ED6FF";
+ draw.context.lineCap = 'round';
+ draw.context.stroke();
+ },
+
+ frame: function() {
+ draw.clear()
+
+ // draw everything!
+ draw.circuit();
+
+ var teams = boxxy.getTeams();
+ for(var idx in teams) {
+ var coords = draw.interpolation.getCoords(teams[idx].id, shapes.real);
+ draw.team(teams[idx].name, coords);
+ }
+
+ requestAnimFrame(function(){
+ draw.frame();
+ });
+ },
+
+ start: draw.frame
+}
+
+function drawTeam(context, name, position) {
+ context.font = "18pt Verdana";
+ context.textAlign = "center";
+ context.fillStyle = "black";
+ context.fillText(name[0], 50 + position.x * 300, 50 + position.y * 300);
+}
+
+function drawStation(context, id, position) {
+ context.font = "14pt Verdana";
+ context.textAlign = "center";
+ context.fillStyle = "red";
+ context.fillText("" + id, 50 + position.x * 300, 50 + position.y * 300);
+}
+
+function initCanvas(boxxy, interpolation) {
+ var fps = 30;
+
+ window.requestAnimFrame = (function(callback){
+ return window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ function(callback){
+ window.setTimeout(callback, 1000 / fps);
+ };
+ })();
+
+ function frame() {
+ clearCanvas()
+
+ // draw everything!
+ drawRealCircuit();
+
+ var stations = boxxy.getStations();
+ for(var name in stations) {
+ var station = stations[name];
+ var pos = shapes.real(station.position / boxxy.circuitLength);
+ drawStation(station.name[station.name.length - 1], pos);
+ }
+ var teams = boxxy.getTeams();
+ for(var idx in teams) {
+ var coords = interpolation.getCoords(teams[idx].id, shapes.real);
+ drawTeam(teams[idx].name, coords);
+ }
+
+ requestAnimFrame(function(){
+ frame();
+ });
+ }
+ frame();
+}
Please sign in to comment.
Something went wrong with that request. Please try again.