I'm animating a marker along a route with waypoints and I am trying to detect when the marker reaches/ crosses a waypoint.
I've managed to get the animation part but seems to fail on the detections
here is my code
I'm creating the route
p = data.map(function(o){ return o.steps.map(function(g) { return g.geometry.coordinates }) });
const locations = p.reduce(function(arr, val){
arr = arr.concat(val); return arr ;
}, [] ),
route = new MultiLineString(locations);
I get the waypoints
intersections = data.reduce(function(s,c){ s=(!s?[]:s); return s = s.concat(c.steps.reduce(
function(e,a){
e = (!e? [] : e);
e.push(a.maneuver.location);
return e; },[])); return s;
},[]),
I create an extent from the waypoint on "right arrow click"
waypoint = fromLonLat(intersections[0])
.concat(fromLonLat(intersections[1]));
Then trying to detect when the marker is animated pass the waypoint
function moveFeature(event) {
const speed = 60;
const time = event.frameState.time;
const elapsedTime = time - lastTime;
distance = (distance + (speed * elapsedTime) / 1e6) % 2;
lastTime = time;
const currentCoordinate = route.getLineString(distance).getCoordinateAt(distance);
if(isNaN(currentCoordinate[0]) || (distance > 1) || Extent.containsCoordinate(waypoint, currentCoordinate) ) return stopAnimation();
position.setCoordinates(currentCoordinate);
const vectorContext = getVectorContext(event);
vectorContext.setStyle(styles.geoMarker);
vectorContext.drawGeometry(position);
map.getView().setCenter(currentCoordinate);
map.render();
}
But it doesn't seem to be true. Is there another way I can detect when the marker is past a certain point/ radius ?
Unless movefeature stops exactly on the extent Extent.containsCoordinate will not return true and the animation will continue past the point. It would be better to calculate the distance (or fraction of total distance) to the waypoint and stop when that is reached
const nearest = route.getClosestPoint(waypoint);
const partCoordinates = [];
route.forEachSegment(function(start, end){
partCoordinates.push(start.slice());
if (new LineString([start, end]).intersectsCoordinate(nearest)) {
partCoordinates.push(nearest);
return true;
}
});
const fraction = new LineString(partCoordinates).getLength() / route.getLength();
Related
I'm making a car simulation in Javascript and I am using this website to help me with the physics: http://www.asawicki.info/Mirror/Car%20Physics%20for%20Games/Car%20Physics%20for%20Games.html
The current progress of the simulation can be seen on my website:
https://cloudynet.tk/projects/car-sim/code.html
The problem I have is with the car steering physics. I have managed to get the low-speed steering to work correctly but the high-speed steering (where lateral forces are introduced) is very hard to get right. I understand how slip angles influence the lateral force but in my simulation, it is not working very well. I'm wondering if the implementation of the lateral force is correct or is there something wrong with the code? Also, I don't quite understand if the longitudinal force and the lateral force affects a single velocity vector or is separated into two "directional" vectors.
Here's my current physics function (whole code can be seen on the website):
applyPhysics() {
// Get car direction vector
let direction = new Vector(1, 0);
direction = Vector.rotate(direction, this.carAngle);
// LONGITUDINAL FORCES
// Traction forces
let tractionForce = direction.copy(); // Traction force (engine power)
if (this.engineForce) {
tractionForce.mult(this.engineForce);
}
else if (this.brakingForce) {
tractionForce.mult(-this.brakingForce);
}
// Frictional forces
let dragForce = this.velocity.copy(); // Air resistance force
dragForce.mult(this.velocity.getMag())
dragForce.mult(-this.drag);
let rollingResistanceForce = this.velocity.copy(); // Rolling resistance force (friction with ground)
rollingResistanceForce.mult(-this.rrDrag);
let netLongitudinalForce = tractionForce.copy(); // Total longitudinal force
netLongitudinalForce.add(dragForce)
netLongitudinalForce.add(rollingResistanceForce);
// Calculate acceleration
this.acceleration = netLongitudinalForce.copy();
this.acceleration.div(this.mass);
if (this.acceleration.getMag() < 0.001)
this.acceleration = new Vector();
// Calculate velocity
let accelerationDelta = this.acceleration.copy();
accelerationDelta.mult(dt);
this.velocity.add(accelerationDelta);
this.velDir = this.velocity.getDir();
this.sideslipAngle = this.carAngle - this.velDir; // Calculate side slip angle
if (this.speed > 20) { // High speed-turning
// LATERAL FORCES
let peakSlipAngle = 5;
// Calculate slip angle for back wheel
var c = this.wheels.baseline/2;
var omegaC = this.angularVelocity*c;
var longV = Math.cos(this.carAngle) * this.velocity.getMag();
var latV = Math.sin(this.carAngle) * this.velocity.getMag();
this.wheels.back.slipAngle = Math.atan(((latV - omegaC)/Math.abs(longV)) || 0);
var backSlipDeg = deg(this.wheels.back.slipAngle)
this.wheels.back.lateralForce = 5000*Math.sign(this.wheels.back.slipAngle);
if (backSlipDeg < peakSlipAngle && backSlipDeg > -peakSlipAngle) {
this.wheels.back.lateralForce = 5000*backSlipDeg/peakSlipAngle;
} else {
this.wheels.back.lateralForce = 5000*(1-((Math.abs(backSlipDeg)-peakSlipAngle)/500))*Math.sign(this.wheels.back.slipAngle);
}
// Calculate slip angle for front wheel
var b = this.wheels.baseline/2;
var omegaB = this.angularVelocity*b;
var longV = Math.cos(this.wheels.front.slipAngle) * this.velocity.getMag();
var latV = Math.sin(this.wheels.front.slipAngle) * this.velocity.getMag();
this.wheels.front.slipAngle = Math.atan((((latV - omegaB)/Math.abs(longV)) || 0)-this.steeringAngle*Math.sign(longV));
var frontSlipDeg = deg(this.wheels.front.slipAngle);
this.wheels.front.lateralForce = 5000*Math.sign(this.wheels.front.slipAngle);
if (frontSlipDeg < peakSlipAngle && frontSlipDeg > -peakSlipAngle) {
this.wheels.front.lateralForce = 5000*frontSlipDeg/peakSlipAngle;
} else {
this.wheels.front.lateralForce = 5000*(1-((Math.abs(frontSlipDeg)-peakSlipAngle)/500))*Math.sign(this.wheels.front.slipAngle);
}
// Calculate cornering force
this.corneringForce = this.wheels.back.lateralForce + Math.cos(this.steeringAngle) * this.wheels.front.lateralForce;
// Calculate centripetal force
this.centripetalForce = this.mass * (this.velocity.getMag() ** 2) / this.wheels.baseline/Math.sin(this.steeringAngle);
var lateralDirection = new Vector(0, -1);
lateralDirection = Vector.rotate(lateralDirection, this.carAngle);
let lateralForce = lateralDirection.copy();
lateralForce.mult(this.corneringForce);
this.latAcceleration = lateralForce.copy();
this.latAcceleration.div(this.mass);
if (this.latAcceleration.getMag() < 0.001)
this.latAcceleration = new Vector();
let latAccelerationDelta = this.latAcceleration.copy();
latAccelerationDelta.mult(dt);
this.latVelocity.add(latAccelerationDelta);
// Calculate position
let latVelocityDelta = this.latVelocity.copy();
latVelocityDelta.mult(dt);
this.pos.add(latVelocityDelta);
} else {
this.velocity = Vector.rotate(this.velocity, this.carAngle - this.velDir); // Correct velocity based on car orientation
}
// Calculate position
let velocityDelta = this.velocity.copy();
velocityDelta.mult(dt);
this.pos.add(velocityDelta);
// Calculate speed
this.speed = this.velocity.getMag();
}
I believe the problem is with the lines regarding the slip angles. I would look over the script carefully or ctrl f to find where you typed Math.sign rather than Math.sin(). I'm not sure if this is the problem but it's something I noticed looking over your code. I'm working on a basic drifting game with javascript and it requires a lot of the same physics as your project.
Hopefully, I was able to help
So right now, I'm trying to implement a search bar function into my d3.js plot. Right now it doesn't do anything, but that's not the issue at the moment. The problem is that when I type/delete something from the bar, there's visible lag/choppiness in the characters appearing/disappearing. I believe the issue is stemming from my plot. I have 140+ dots moving around the screen, and their position is being interpolated. So from the beginning to the end of the transition, my code has to compute 140 positions thousands of times over.
I've looked into trying to reduce the cardinality of the d3.interpolateNumber function, but it appears that there isn't a third argument to change the number of terms like in a linspace command. Right now I have an array of 1000 numbers for my function to run through, but I don't know how to pass the array to my other functions.
Below are the pertinent functions for this issue. The commented line in tweenPatch is the original code I had that made my code run, but gave my plot computational issues. Variables arr, curr, and step were my attempt to fix the situation, but I haven't been able to figure out how to pass the array into displayPatch().
function tweenPatch() {
var patch = d3.interpolateNumber(1, 26);
var arr = [];
var curr = 1;
var step = (26 - 1) / (1000 - 1);
for (var i = 0; i < 1000; i++) {
arr.push(curr + (step * i));
}
return arr.forEach(function(d) {
console.log(arr[d]);
displayPatch(arr[d]);
});
//return function(t) { displayPatch(t); };
}
function displayPatch(patch) {
dots.data(interpolateData(patch), function(d) { return d.name; }).call(position).sort(order);
var inter = Math.floor(patch);
var seas = 8;
var patc = 1;
if (inter > 24) {
seas = 9;
patc = inter - 24;
} else {
patc = inter;
}
label.text("Patch " + seas + "." + patc);
}
function interpolateValues(values, number) {
old = Math.floor(number);
upd = Math.ceil(number);
var old_data = values.filter(function(d) {return d.internal == old;});
var new_data = values.filter(function(d) {return d.internal == upd;});
var oobj = old_data[0];
var nobj = new_data[0];
var onum = oobj[Object.keys(oobj)[4]];
var nnum = nobj[Object.keys(nobj)[4]];
var difint = number - old;
var difdis = 0;
var newnum = nnum;
if (nnum > onum) {
difdis = nnum - onum;
newnum = ((difint) * difdis) + onum;
} else if (onum > nnum) {
difdis = onum - nnum;
newnum = onum - ((difint) * difdis);
}
return newnum;
}
I believe switching my SVG to a canvas may help things, but since I have no knowledge of canvas I'd rather leave that as a last resort.
I'm writing an A* pathing script for a game set on a 7x7 grid of tiles with the player always in the middle (tile 24). Zeros are added as a visual and it's actually one array, not a 7x7 2D array.
[00,01,02,03,04,05,06]
[07,08,09,10,11,12,13]
[14,15,16,17,18,19,20]
[21,22,23,24,25,26,27]
[28,29,30,31,32,33,34]
[35,36,37,38,39,40,41]
[42,43,44,45,46,47,48]
The game is server-driven so the player uses relative coordinates. What that means is, if the player moves, tile[0] changes. The short version of that is the player will always move from tile 24, which is the center tile. The grid is hard coded in, but if I post it publicly I'll change the code a little; no problem.
The function should take a destination and find a good path from tile 24 to that square but what it actually does it return "undefined".
If I input 24 I want the game to output an array like this
[18,12,6]
Here's the code:
z = 0;
function pathTo(goal){
var createPath = function (goal){
var createNode = function(i){
this.id = i;
this.g = Infinity;
this.f = Infinity;
this.parent = null;
this.open = null;
};
this.nodes = Array(49);
for(i=0;i<this.nodes.length;i++){
this.nodes[i] = new createNode(i);
}
this.start = this.nodes[24];
this.start.g = 0;
this.currentNodeId = 24;
this.goal = this.nodes[goal];
this.bestPath = null;
};//end createPath
var getBestNeighbor = function(nodeId){
z++
if(z>50){throw z}debugger;
console.log(nodeId);
var getG = function(parentG){
//here you can check the map for water, sand, and ruins penalties
/*
default = 1
path = .9
water = 3
*/
return (parentG + 1);
};
var closeNode = function (node){
node.open = false;
};//end closeNode
var getF = function(startId,endId,g){
if(g>9){
throw g;
}
var startX = startId % 7;
var startY = (startId - startX) / 7;
var endX = endId % 7;
var endY = (endId - endX) / 7;
var h = Math.sqrt( Math.pow((startX - endX) , 2 ) + Math.pow(( startY - endY ), 2 ) );
console.log("Start.id:"+startId+"H:"+h+" Start.id.g:"+g);
return (h + g);
};//end getF
var tracePath = function(tracedNode){
path.bestPath = [];
while(tracedNode != path.start){
path.bestPath.unshift(tracedNode.id);
tracedNode = tracedNode.parent;
}
return path.bestPath;
};//end tracePath
var getNeighborNodeId = function(x,y,currentId){return currentId + (y*7) + x;};//end getNeighborNodeId
if(path.bestPath === null){
var neighborNode = {};
var bestNode = {f: Infinity};
if(nodeId == path.goal.id){//may need to pass path
return tracePath(path.nodes[nodeId]);
}else{
for(x=-1;x<=1;x++){
for(y=-1;y<=1;y++){
var nnId = getNeighborNodeId(x,y,nodeId);
if(nnId==24){debugger}
if( ( (x!=0) && (y!=0) ) ||( (nnId>=0) && (nnId<=48))){
var neighborNode = path.nodes[nnId];
if(neighborNode.open === null){ neighborNode.open = true; }
if(neighborNode.open === true ){//don't check closed neighbors
if(typeof neighborNode === "object"){
neighborNode.parent = path.nodes[nodeId]
debugger;
neighborNode.g = getG(neighborNode.parent.g);
neighborNode.f = getF(neighborNode.id , path.goal.id , neighborNode.g);
if( neighborNode.f < bestNode.f){
bestNode = neighborNode;
}//endif
}//endif
}//endif Note: if the node isn't null or true, it's false.
}
}//endfor
}//endfor - Note: Here we should have the best neighbor
if(bestNode.f == Infinity){
closeNode(path.nodes[nodeId]);//need escape for no possible path
return;
}else{
//bestNode.parent = path.nodes[nodeId];
path.currentNodeId = bestNode.id;
getBestNeighbor(bestNode.id);
}//endelse
}//endelse
}//endif
};//end getBestNeighbor
var path = new createPath(goal);
while(path.bestPath === null){
getBestNeighbor(path.currentNodeId);
}//end while
return path.bestPath;
}//end pathTo
console.log(pathTo(41)); //testing with 6
and a JSFiddle link: https://jsfiddle.net/jb4xtf3h/
It's my first time not just slapping globals everywhere, so it may have a scope issue I'm not familiar with.
Most likely my issue is in the getNeighborId function; I don't think I have anything declaring a good node's parent.
The problem is that it goes NW three times instead of NE three times. That probably means I have a mistake in the getBestNeighbor function where I'm reading a -1 as a 1.
Also I don't think I'm escaping the recursive function correctly.
For some reason, when I put in 41 it gets really confused. This either has to do with how I set G and H which are classically used in A* to record distance traveled on this path and the estimated distance remaining. Specifically the G number is wrong because it's taking bad steps for some reason.
Here is the working code. I didn't implement walls or anything but I do show where you would do that. All you need to do is close all the nodes that are walls before you begin pathing and you can assign movement penalties if you want the AI to "know" to avoid water or sand.
I actually can't pin down a single problem but a major one was the way the statement:
if( ( (x!=0) && (y!=0) ) ||( (nnId>=0) && (nnId<=48))){
was changed to:
if( ( !(x==0 && y==0) && ( nnId>=0 && nnId<=48))){
The intent of this line was to prevent searching the tile you are standing on x,y = (0,0) and also to make sure that the neighbor you want to look at is on the grid (7x7 grid has 49 squares numbered 0-48)
What I was trying to say is "IF X & Y ARE BOTH NOT ZERO" apparently that actually makes it the same as an or statement so if either square was 0 it skipped it and tiles that needed that space were having problems since there were several directions that weren't working.
I hope that helps somebody if they need a nice simple pathing script I tried really hard to make the code readable and I'm not the strongest coder in the world but a working A* script in 100 lines that I think is fairly easy to follow. If you are reading this and you're not familiar with A* pathing what you might need to know is
H is your heuristic value it's an estimation of the remaining distance form a tile. In this code it's under the path object path.nodes[array#].h
G is the distance you've moved so far to get to that square path.nodes[array#].g.
F just adds h+g for the total value. This pseudocode on Wikipedia helped me write it.
var z = 0;
function pathTo(goal){
var createPath = function (goal){
var createNode = function(i){
this.id = i;
this.g = Infinity;
this.f = Infinity;
this.parent = null;
this.open = null;
};
this.nodes = Array(49);
for(i=0;i<this.nodes.length;i++){
this.nodes[i] = new createNode(i);
}
this.start = this.nodes[24];
this.start.g = 0;
this.currentNodeId = 24;
this.goal = this.nodes[goal];
this.bestPath = null;
};//end createPath
var path = new createPath(goal);
var getBestNeighbor = function(nodeId){
var getG = function(parentG){
//here you can check the map for water, sand, and ruins penalties
/*
default = 1
path = .9
water = 3
*/
return (parentG + 1);
};
var closeNode = function (node){
node.open = false;
};//end closeNode
var getF = function(startId,endId,g){
var startX = startId % 7;
var startY = (startId - startX) / 7;
var endX = endId % 7;
var endY = (endId - endX) / 7;
var h = Math.sqrt( Math.pow((startX - endX) , 2 ) + Math.pow(( startY - endY ), 2 ) );
return (h + g);
};//end getF
var tracePath = function(tracedNode){
path.bestPath = [];
while(tracedNode != path.start){
path.bestPath.unshift(tracedNode.id);
tracedNode = tracedNode.parent;
}
return path.bestPath;
};//end tracePath
var getNeighborNodeId = function(x,y,currentId){return currentId + (y*7) + x;};//end getNeighborNodeId
debugger;
z++
if(z>50){throw z}
if(path.bestPath === null){
var neighborNode = {};
var bestNode = {f: Infinity};
if(nodeId == path.goal.id){//may need to pass path
return tracePath(path.nodes[nodeId]);
}else{
for(y=-1;y<=1;y++){
for(x=-1;x<=1;x++){
var nnId = getNeighborNodeId(x,y,nodeId);
if( ( !(x==0 && y==0) && ( nnId>=0 && nnId<=48))){
var neighborNode = path.nodes[nnId];
if(path.nodes[nodeId].parent!=neighborNode){
if(neighborNode.open === null){ neighborNode.open = true; }
if(neighborNode.open === true ){//don't check closed neighbors
if(typeof neighborNode === "object"){
neighborNode.parent = path.nodes[nodeId]
neighborNode.g = getG(neighborNode.parent.g);
neighborNode.f = getF(neighborNode.id , path.goal.id , neighborNode.g);
if( neighborNode.f < bestNode.f){
bestNode = neighborNode;
}//endif
}//endif
}
}//endif Note: if the node isn't null or true, it's false.
}
}//endfor
}//endfor - Note: Here we should have the best neighbor
if(bestNode.f >= 50){
closeNode(path.nodes[nodeId]);//need escape for no possible path
return;
}else{
path.currentNodeId = bestNode.id;
getBestNeighbor(bestNode.id);
}//endelse
}//endelse
}//endif
};//end getBestNeighbor
while(path.bestPath === null){
getBestNeighbor(path.currentNodeId);
}//end while
return path.bestPath;
}//end pathTo
myPath = pathTo(41); //testing with 6
console.log("path2:"+myPath);
I'm building an application in three.js, however I'm having real problems with performance. This part of the application is based upon the Voxel Painter example. In my version, the user clicks on a cell to begin placement, drags the cursor to where they wish to end placement, and clicks to end.
function onDocumentMouseMove(event) {
//set up mouse and raycaster
event.preventDefault();
mouse.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1);
raycaster.setFromCamera(mouse, camera);
switch (buildMode) {
case buildModes.CORRIDOR:
scene.add(rollOverFloor);
var intersects = raycaster.intersectObjects(gridObject);
if (intersects.length > 0) {
var intersect = intersects[0];
if (beginPlace == true) {
//store the intersection position
var endPlace = new THREE.Vector3(0, 0, 0);
endPlace.copy(intersect.point).add(intersect.face.normal);
endPlace.divideScalar(step).floor().multiplyScalar(step).addScalar(step / step);
endPlace.set(endPlace.x, 0, endPlace.z);
corridorDrag(endPlace);
}
//if user hasn't begun to place the wall
else {
//show temporary wall on grid
rollOverFloor.position.copy(intersect.point).add(intersect.face.normal);
rollOverFloor.position.divideScalar(step).floor().multiplyScalar(step).addScalar(step / step);
rollOverFloor.position.set(rollOverFloor.position.x, 0, rollOverFloor.position.z);
}
}
break;
}
render();
}
The code above is called when the user moves the mouse (there are many buildmodes in the main application, but I have not included them here). This function simply gets a start and end point, the corridorDrag() function fills in the cells between the start and end points:
function corridorDrag(endPlace) {
deleteFromScene(stateType.CORRIDOR_DRAG);
var startPoint = startPlace;
var endPoint = endPlace;
var zIntersect = new THREE.Vector3(startPoint.x, 0, endPoint.z);
var xIntersect = new THREE.Vector3(endPoint.x, 0, startPoint.z);
var differenceZ = Math.abs(startPlace.z - zIntersect.z);
var differenceX = Math.abs(startPlace.x - xIntersect.x);
var mergedGeometry = new THREE.Geometry();
for (var i = 0; i <= (differenceZ / step); i++) {
for (var j = 0; j <= (differenceX / step); j++) {
var x = startPlace.x;
var y = startPlace.y;
var z = startPlace.z;
if (endPoint.x <= (startPlace.x )) {
if (endPoint.z <= (startPlace.z)) {
x = x - (step * j);
z = z - (step * i);
}
else if (endPoint.z >= (startPlace.z)) {
x = x - (step * j);
z = z + (step * i);
}
} else if (endPoint.x >= (startPlace.x)) {
if (endPoint.z <= (startPlace.z)) {
x = x + (step * j);
z = z - (step * i);
}
else if (endPoint.z >= (startPlace.z)) {
x = x + (step * j);
z = z + (step * i);
}
}
floorGeometry.translate(x, y, z);
mergedGeometry.merge(floorGeometry);
floorGeometry.translate(-x, -y, -z);
}
}
var voxel = new THREE.Mesh(mergedGeometry, tempMaterial);
voxel.state = stateType.CORRIDOR_DRAG;
scene.add(voxel);
tempObjects.push(voxel);
}
Firstly, the deleteFromScene() function removes all current highlighted cells from the scene (see below). The code then (I believe), should create a number of meshes, depending on the start and end points, and add them to the scene.
function deleteFromScene(state) {
tempObjects = [];
var i = scene.children.length;
while (i--) {
if (scene.children[i].state != undefined)
if (scene.children[i].state == state)
scene.children.splice(i, 1);
}
}
As I said, it is very, very slow. It also appears to be adding an obscene amount of vertices to the renderer, as seen in the WebGLRenderer stats window. I have no idea why it's adding so many vertices, but I'm assuming that's why it's rendering so slowly.
The application can be viewed here - the problem can be seen by clicking on one cell, dragging the cursor to the other end of the grid, and observing the time taken to fill in the cells.
Thank you in advance, this really is a last resort.
A few years ago Twitter put out an update. In this update they had just introduced infinite scrolling and on the day of its release the update was crashing users browsers. Twitter engineers did some investigating and found that the crashes were the result of the scroll event firing hundreds of times a second.
Mouse events can fire many MANY times a second and can cause your code to execute too often, which slows down the browser and (in many cases) crashes it. The solution for Twitter (and hopefully you) was simple: Poll your event.
Inside your mousemove event handler check that it has been some number of milliseconds since the last move event.
var lastMove = Date.now();
function onDocumentMouseMove(event) {
if (Date.now() - lastMove < 31) { // 32 frames a second
return;
} else {
lastMove = Date.now();
}
// your code here
}
I hope that helps!
I am trying to create an analytical program which keeps track of user mouse movement on a website and stores the data in a DB. Here is where I am stuck:
Assuming the mouse is always starting at the middle of the screen, and the user is instructed to move it to a particular element, how do I determine the efficiency and accuracy of that movement. I need to keep in mind the duration from start of hovering till the click, but I want to also include the hovering path of the mouse.
A perfect score would be a perfect line from Point A to Point B in x seconds, how do I determine the score of a curved path in 2x seconds, or an instance where the path goes in the wrong direction before proceeding to Point B? Are there any algorithms in existence?
Thanks for your help!
Here is a JSFiddle that I created. Click on the START box and then click on the FINISH box. Hopefully this will help you get started.
var start = false;
var start_time,end_time;
var points = [];
$("#start").click(function() {
start = true;
points = [];
start_time = Date.now();
});
$("#finish").click(function() {
start = false;
distance = travelledDistance();
time = (Date.now() - start_time)/1000;
var center_x_start = $("#start").offset().left + $("#start").width() / 2;
var center_y_start = $("#start").offset().top + $("#start").height() / 2;
var center_x_finish = $("#finish").offset().left + $("#finish").width() / 2;
var center_y_finish = $("#finish").offset().top + $("#finish").height() / 2;
var straight_distance = Math.round(Math.sqrt(Math.pow(center_x_finish - center_x_start, 2) + Math.pow(center_y_finish - center_y_start, 2)));
$("#time").text(+time+"s");
$("#distance").text(distance+"px");
$("#straight_distance").text(straight_distance+"px");
});
$(document).mousemove(function( event ) {
if(!start)
return;
points.push(event.pageX + "," + event.pageY);
});
function travelledDistance(){
var distance = 0;
for (i = 0; i < points.length - 1; i++) {
start_point = points[i].split(",");
end_point = points[i+1].split(",");
distance += Math.round(Math.sqrt(Math.pow(end_point[0] - start_point[0], 2) + Math.pow(end_point[1] - start_point[1], 2)));
}
return distance;
}
UPDATE
I made a new version here. Now you can drag the targets to check the different results.