I have an assignment i'm working on and i a little stuck.. I think I'm on the right path, but this keeps returning undefined.
EDIT: the goal is to pass in an array of two points and find witch in the source array is closer.
Any guidance with some explanation would be greatly appreciated.
function calcDistance(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x1 - y1, 2) + Math.pow(x2 - y2, 2));
}
function minimumDistance(inputArray, destArray) {
let inputX = inputArray.x;
let inputY = inputArray.y;
let minDistance = calcDistance(inputX, inputY, destArray[0].x, destArray[0].y)
let minPoint;
for (let i = 0; i < destArray.lenght; i++) {
let distance = calcDistance(inputX, inputY, destArray[i].x, destArray[i].y);
if (minDistance > distance) {
minDistance = distance;
minPoint = i;
}
return destArray[minPoint];
}
}
testData = { x: 0, y: 0 }
sourceArr = [{ x: 100, y: 0 }, { x: 200, y: 10 }]
console.log(minimumDistance(testDatac, sourceArr));
I'm not exactly sure what you're trying to achieve, but here are some changes I made:
start minDistance at +Infinity and then loop over all the points (alternatively, initialize minPoint on 0 does the same)
fix typo lenght => length
move return statement to after the loop (so the program can check all points before returning a value)
Fix the calcDistance function to use the right formula (sqrt((x2-x1)**2 + (y2-y1)**2))
function calcDistance(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}
function minimumDistance(inputArray, destArray) {
let inputX = inputArray.x;
let inputY = inputArray.y;
let minDistance = +Infinity;
let minPoint;
for (let i = 0; i < destArray.length; i++) {
let distance = calcDistance(inputX, inputY, destArray[i].x, destArray[i].y);
if (minDistance > distance) {
minDistance = distance;
minPoint = i;
}
}
return destArray[minPoint];
}
testSrc = { x: 0, y: 0 }
testArr = [{ x: 100, y: 0 }, { x: 200, y: 10 }]
console.log(minimumDistance(testSrc, testArr));
testSrc = { x: 200, y: 80 }
console.log(minimumDistance(testSrc, testArr));
I can only assume you want something like this.
// Calculate euclidean distance between a and b (which should be objects with x/y properties)
function euclideanDistance(a, b) {
return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
}
// Find an object `x` in `otherPoints` that minimizes `metric(point, x)`
function minimumDistance(point, otherPoints, metric) {
let minDistance, minPoint;
otherPoints.forEach((otherPoint) => {
const distance = metric(point, otherPoint);
if (minDistance === undefined || distance < minDistance) {
minDistance = distance;
minPoint = otherPoint;
}
});
return [minPoint, minDistance];
}
testSrc = { x: 0, y: 0 };
testArr = [
{ x: 100, y: 0 },
{ x: 200, y: 10 },
];
console.log(minimumDistance(testSrc, testArr, euclideanDistance));
You initialize minDistance with the distance the first point has to the inputArray and then compare all other distances to this. This works fine if you also initialize the minPoint to 0.
In the current program you would only enter the ifstatement if your first distance is not the smallest in total.
To fix this: initialize minPoint to 0.
Almost there yes! I fixed some typos and you omitted to default minPoint to 0
function calcDistance(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}
function minimumDistance(inputObject, destArray) {
let inputX = inputObject.x;
let inputY = inputObject.y;
let minDistance = calcDistance(inputX, inputY, destArray[0].x, destArray[0].y);
let minPoint = 0;
for (let i = 1; i < destArray.length; i++) {
let distance = calcDistance(inputX, inputY, destArray[i].x, destArray[i].y);
if (minDistance > distance) {
minDistance = distance;
minPoint = i;
}
}
return destArray[minPoint];
}
testSrc = { x: 0, y: 0 };
testArr = [{ x: 100, y: 0 }, { x: 200, y: 10 }]
console.log(minimumDistance(testSrc, testArr));
Related
I am making a game where the player can move around a grid, but they can only move to areas around them in a certain range. I need to check if where the player is trying to move to is actually somewhere they are allowed to go.
My code so far is this:
const generateMoveableAreas = (playerX, playerY, range) => {
const moveableAreas = [];
for (let i = range; i > 0; i--) {
moveableAreas.push(
{ x: playerX + i, y: playerY },
{ x: playerX + i, y: playerY + i },
{ x: playerX, y: playerY + i },
{ x: playerX - i, y: playerY + i },
{ x: playerX - i, y: playerY },
{ x: playerX - i, y: playerY - i },
{ x: playerX, y: playerY - i },
{ x: playerX + i, y: playerY - i },
);
}
return moveableAreas;
};
This works correctly if the range is 1, but if the range is 2 or more, a problem occurs.
Here is an example of what the grid looks like - 0 is not reachable, R is reachable, U is should be reachable, but isn't, and P is player. The player has a range of 2.
0 0 0 0 0 0 0
0 R U R U R 0
0 U R R R U 0
0 R R P R R 0
0 U R R R U 0
0 R U R U R 0
0 0 0 0 0 0 0
What I need is for all those U spots to be R.
I think you can massively simplify your code.
Seeing as all spots within <range> are reachable, you can just use the range as offsets for some loops, looping over all reachable x/y coordinates:
const generateMoveableAreas = (playerX, playerY, range) => {
const moveableAreas = [];
for (let x = -range; x <= range; x++) {
for (let y = -range; y <= range; y++) {
moveableAreas.push({ x: playerX + x, y: playerY + y })
}
}
return moveableAreas;
};
const movable = generateMoveableAreas(0, 0, 2);
console.log(movable);
Loop through all the spots in range:
const generateMoveableAreas = (playerX, playerY, range) => {
const moveableAreas = [];
for (let i = Math.max(0, playerX - range); i <= playerX + range; i++) {
for (let j = Math.max(0, playerY - range); j <= playerY + range; j++) {
moveableAreas.push(
{ x: i, y: j },
);
}
}
return moveableAreas;
};
I added a check to make sure it doesn't try to add moves that are out of range (< 0), but you'll have to add additional checks to make sure you don't try to go out of range to the right or down, since you haven't shown how large the grid is.
The available squares are basically a square of range x 2 + 1
This means that you could do something like :
for (let i = playerX - range; i <= playerX + range; i++) {
for (let j = playerY - range; j <= playerY + range; j++) {
moveableAreas.push({ x: i, y: j });
}
}
I have implemented a recursive maze generation algorithm in Javascript, and am rendering using HTML canvas.
The problem I have, is that I am unable to render to the canvas while the recursive function is in progress. The render only appears after recursive calls have finished.
Here is my (rough) functioning recursive function :
function start(canvas_context)
{
//do some stuff here...
render(canvas_context);
if()...{
start(canvas_context);
}
else if(...)
{
if(...)
{
start(canvas_context);
}
}
}
Here is my rendering function:
function render(canvas_context)
{
canvas_context.fillStyle = 'black';
canvas_context.fillRect(0,0,canvas.width, canvas.height);
for(..)
{
for(...)
{
canvas_context.strokeStyle = 'red';
canvas_context.beginPath();
canvas_context.moveTo(start.x, start.y);
canvas_context.lineTo(end.x,end.y);
canvas_context.stroke();
}
}
}
Here are the full functions if they are needed:
function start(canvas_context)
{
//Vector2.print(game_config["current_grid_pos"]);
var current_block = game_config["grid_space"][game_config["current_grid_pos"].y][game_config["current_grid_pos"].x];
var random_neighbor_grid_pos = random_neighbor(current_block);
//render(canvas_context);
if(random_neighbor_grid_pos.x != -1 && random_neighbor_grid_pos.y != -1)
{
var next_block = game_config["grid_space"][random_neighbor_grid_pos.y][random_neighbor_grid_pos.x];
next_block.visited = true;
game_config["visited_stack"].push(random_neighbor_grid_pos);
remove_walls(current_block, next_block);
game_config["current_grid_pos"] = random_neighbor_grid_pos;
start(canvas_context);
}
else if (game_config["visited_stack"].length > 0)
{
current_block.visited = true;
game_config["visited_stack"].pop();
if(game_config["visited_stack"].length != 0)
{
game_config["current_grid_pos"] = game_config["visited_stack"][game_config["visited_stack"].length - 1];
start(canvas_context);
}
}
}
function render(canvas_context)
{
//Initialise the canvas background.
canvas_context.fillStyle = 'black';
canvas_context.fillRect(0,0,canvas.width, canvas.height);
for(var y = 0; y < game_config["grid_space"].length; y++)
{
for(var x = 0; x < game_config["grid_space"][0].length; x++)
{
var block = game_config["grid_space"][y][x];
var walls = block.get_walls();
for(var key in walls)
{
var wall = walls[key];
render_line(canvas_context, wall.start, wall.end);
}
}
}
}
function render_line(canvas_context, start, end)
{
canvas_context.strokeStyle = 'red';
canvas_context.beginPath();
canvas_context.moveTo(start.x, start.y);
canvas_context.lineTo(end.x,end.y);
canvas_context.stroke();
}
Is it even possible to render during recursive calls with HTML canvas?
I think you can do it without a recursion but I show both ways:
const cellsise = 10;
class block {
constructor (x, y){
this.x = x;
this. y = y;
this.delta = {x: x * cellsise , y: y * cellsise};
this.walls = {
left: {
start: {
x: 0 + this.delta.x,
y: 0 + this.delta.y
},
end: {
x: 0 + this.delta.x,
y: cellsise + this.delta.y
}
},
top: {
start: {
x: 0 + this.delta.x,
y: 0 + this.delta.y
},
end: {
x: cellsise + this.delta.x,
y: 0 + this.delta.y
}
},
right: {
start: {
x: cellsise + this.delta.x,
y: 0 + this.delta.y
},
end: {
x: cellsise + this.delta.x,
y: cellsise + this.delta.y
}
},
bottom: {
start: {
x: 0 + this.delta.x,
y: cellsise + this.delta.y
},
end: {
x: cellsise + this.delta.x,
y: cellsise + this.delta.y
}
}
};
}
get_walls(){
return this.walls;
}
}
const game_config = {
grid_space:[],
height: 10,
width: 10,
}
function generateGridSpace() {
game_config.grid_space=[];
for(y = 0 ; y < game_config.height; ++y){
game_config.grid_space.push([]);
for(x = 0 ; x < game_config.width; ++x){
game_config.grid_space[y].push(new block(x, y))
}
}
}
function remove_walls(current_block){
let left = Math.random() > 0.5;
let top = !left //Math.random() > 0.5;
let right = Math.random() > 0.5;
let bottom = !right //false// Math.random() > 0.5;
if (left && current_block.x - 1 > 0) {
delete current_block.walls.left;
delete game_config.grid_space[current_block.x-1][current_block.y].walls.right
}
if (top && current_block.y - 1 > 0) {
delete current_block.walls.top;
delete game_config.grid_space[current_block.x][current_block.y-1].walls.bottom;
}
if (right && current_block.x+1 < game_config.width -1) {
delete current_block.walls.right;
delete game_config.grid_space[current_block.x + 1][current_block.y].walls.left;
}
if (bottom && current_block.y+1 < game_config.height -1) {
delete current_block.walls.bottom;
delete game_config.grid_space[current_block.x][current_block.y + 1].walls.top;
}
}
function start_with_recursion(canvas_context, mix=null) {
if (!mix) {
mix = game_config.grid_space.reduce((acc, e) => [...acc,...e], [])
.sort(() => Math.random() - 0.5)
} else if (mix.length === 0) {
return;
}
const block = mix.pop();
const i = game_config.width * game_config.height - mix.length
setTimeout(() => {
remove_walls(block);
render_block(canvas_context, block);
}, i * 50)
start_with_recursion(canvas_context, mix)
}
function start_without_recursion(canvas_context) {
const mix = game_config.grid_space.reduce((acc, e) => [...acc,...e], [])
.sort(() => Math.random() - 0.5)
for(let i = 0; i < mix.length; ++i){
setTimeout(() => {
remove_walls(mix[i]);
render_block(canvas_context, mix[i]);
}, i * 50)
}
}
function render_block(canvas_context, block) {
var walls = block.get_walls();
for(var key in walls)
{
var wall = walls[key];
render_line(canvas_context, wall.start, wall.end);
}
}
function render_line(canvas_context, start, end) {
canvas_context.strokeStyle = 'red';
canvas_context.beginPath();
canvas_context.moveTo(
5 + start.x + .5,
5 + start.y + .5
);
canvas_context.lineTo(
5 + end.x + .5,
5 + end.y + .5
);
canvas_context.stroke();
}
canvas = document.querySelector('canvas');
canvas.height = game_config.height * cellsise + 5 * 2;
canvas.width = game_config.width * cellsise + 5 * 2;
ctx = canvas.getContext('2d');
ctx.fillStyle = 'black';
ctx.fillRect(0,0,canvas.width, canvas.height);
// ctx.lineWidth = 2;
generateGridSpace();
// start_without_recursion(ctx);
start_with_recursion(ctx);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<canvas></canvas>
</body>
</html>
also for get a random neighbor:
function random_neighbor(current_block) {
const {height, width} = game_config;
const neighbors = [
{x: current_block.x, y: current_block.y - 1}, // from top
{x: current_block.x - 1, y: current_block.y}, // from left
{x: current_block.x + 1, y: current_block.y}, // from right
{x: current_block.x, y: current_block.y + 1}, // from bottom
].filter(e => e.x >= 0 && e.x < width && e.y >= 0 && e.y < height)
const rnd = neighbors[Math.floor(Math.random() * neighbors.length)]
return rnd; // {x, y}
}
You also need to take into account that the drawn wall can be removed later, which will not correspond to reality. Therefore, it is better to render (even with a delay) after the maze is fully generated.
Suggest converting your recursive function into an iterator function, and then introducing a second function that makes use of setInterval to iteratively draw your maze, and once complete, then performs a clearInterval.
Here's a quick sample that I whipped up exemplifying the concept, which simply draws increasingly larger rectangles on the canvas every 50ms, with the iterator function returning the size of the next rectangle to be drawn. In your case, you'll likely want to yield an object defining the portion of the maze to be drawn...
var ctx = document.getElementById('canvas').getContext('2d');
function* recurse( n, max ) {
yield n;
if ( n < max ) {
yield* recurse( n + 1, max );
}
}
function drawRectangles() {
function drawIteratively() {
let nd = iterator.next();
if ( nd.done ) {
clearInterval( interval );
} else {
ctx.lineWidth = 1;
ctx.strokeStyle = '#FF0000';
ctx.beginPath();
ctx.rect( nd.value, nd.value, nd.value * 2, nd.value * 2 );
ctx.stroke();
}
}
let iterator = recurse( 25, 100 );
let interval = setInterval( drawIteratively, 50 );
}
drawRectangles();
<canvas id='canvas' width="300" height="300"></canvas>
Note that the iterator function is a very basic recursive function, but recursive nonetheless, showing the critical syntax involved in a recursive iterator function...
The following code generates random points(x,y) and then for each point it splits the canvas (one square) into four. With the next point in the iteration it searches for the square where the point is located and splits it into four smaller squares - up to a certain square size.
The problem is it is very fast to run in Chrome and extremely slow in Ps (for 11k points it takes 2 seconds in Chrome and 30 minutes in Ps! For 1k points it takes around 10 secs in Ps.
Is there any better rewriting to this? btw, Ps doesn't support ES5
var squares = [];
var canvaswidth = app.activeDocument.width.as("px");
var canvasheight = app.activeDocument.height.as("px");
squares.push([{
x: 0,
y: 0
}, {
x: canvaswidth,
y: 0
}, {
x: canvaswidth,
y: canvasheight
}, {
x: 0,
y: canvasheight
}])
vertices = [];
for (i = 0; i < 8000; i++) {
vertices.push({
x: Math.floor(Math.random() * canvaswidth),
y: Math.floor(Math.random() * canvasheight)
})
}
var t0 = new Date().getTime();
var minsquaresize = 24;
for (v = 0; v < vertices.length; v++) {
if (v > 0 && Math.abs(vertices[v].x - vertices[v - 1].x) > minsquaresize && Math.abs(vertices[v].y - vertices[v - 1].y) > minsquaresize) {
r = 2;
for (s = 0; s < squares.length; s++) {
var squares_s = squares[s];
if (squares_s != undefined && vertices[v].x >= squares_s[0].x && vertices[v].x <= squares_s[2].x && vertices[v].y >= squares_s[0].y && vertices[v].y <= squares_s[2].y && squares_s[1].x - squares_s[0].x > minsquaresize && squares_s[3].y - squares_s[0].y > minsquaresize) {
var s1p1 = {
x: Math.round(squares_s[0].x),
y: Math.round(squares_s[0].y)
};
var s1p2 = {
x: Math.round((squares_s[0].x + squares_s[1].x) / 2),
y: Math.round((squares_s[0].y + squares_s[1].y) / 2)
};
var s1p3 = {
x: Math.round(((squares_s[1].x - squares_s[0].x) / r) + squares_s[0].x),
y: Math.round(((squares_s[3].y - squares_s[0].y) / r) + squares_s[0].y)
}
var s1p4 = {
x: (squares_s[0].x + squares_s[3].x) / 2,
y: Math.round((squares_s[0].y + squares_s[3].y) / 2)
}
var s2p2 = {
x: squares_s[1].x,
y: squares_s[1].y
}
var s2p3 = {
x: Math.round((squares_s[1].x + squares_s[2].x) / 2),
y: Math.round((squares_s[1].y + squares_s[2].y) / 2)
}
var s3p3 = {
x: squares_s[2].x,
y: squares_s[2].y
}
var s3p4 = {
x: Math.round((squares_s[2].x + squares_s[3].x) / 2),
y: Math.round(Math.round((squares_s[2].y + squares_s[3].y) / 2))
}
var s4p4 = {
x: squares_s[3].x,
y: squares_s[3].y
}
//alert(s4p4.y)
delete squares[s];
squares.push([s1p1, s1p2, s1p3, s1p4])
squares.push([s1p2, s2p2, s2p3, s1p3])
squares.push([s1p3, s2p3, s3p3, s3p4])
squares.push([s1p4, s1p3, s3p4, s4p4])
break;
}
}
}
}
var t1 = new Date().getTime() - t0;
alert("time: "+t1)
Managed a significant performance increase by looping the squares in reverse.
So normally it was:
for(vertices length, v++){
for(squares length, s++){
if vertex is within square then delete square from square array, split square into 4 equal squares and add them to array
}
}
Vertices are collected from a path, so vertex 4 will probably be close to vertex 3 so probably in the area of the last squares created from vertex 3 - in the end of the squares array. So:
for(var s = squares.length; s--;){...}
This works much faster (maybe 10 times). Strange that it is also faster with randomly placed vertices.
So I am currently working on a perlin noise generator, for some reason I'm getting incorrect output:
The perlin noise is in a "grid" and does not resemble traditional perlin noise.
Here is my code:
var perlinNoise = {
vectors: [{x:1,y:0},{x:1,y:-1},{x:0,y:-1},{x:-1,y:-1},{x:-1,y:0},{x:-1,y:1},{x:0,y:1},{x:1,y:1}],
fade: function(t){
return t * t * t * (t * (t * 6 - 15) + 10);
},
pointToCell: function(x, y){
cellX = Math.floor(x);
cellY = Math.floor(y);
return {x:cellX, y:cellY};
},
cellToVectors: function(cellX, cellY){
halfCell = .5;
//I use the four intercardinal directions to label the vectors.
//The random values are multiplied by 8 to map them to the 8 entries of the vectors array.
NEvector = this.vectors[Math.floor(randomFromCoords(cellX + halfCell, cellY + halfCell)*8)];
SEvector = this.vectors[Math.floor(randomFromCoords(cellX + halfCell, cellY - halfCell)*8)];
SWvector = this.vectors[Math.floor(randomFromCoords(cellX - halfCell, cellY - halfCell)*8)];
NWvector = this.vectors[Math.floor(randomFromCoords(cellX - halfCell, cellY + halfCell)*8)];
return {NE: NEvector, SE: SEvector, SW: SWvector, NW: NWvector};
},
dotProduct: function(vector1, vector2){
//Another way to calculate the dot product. This is more performance friendly than cosine calculations.
return vector1.x * vector2.x + vector1.y * vector2.y;
},
lerp: function(value1, value2, t){
return (1 - t) * value1 + t * value2;
},
perlinNoise: function(x, y){
var cellCoord = this.pointToCell(x, y);
//Get the positions of the x and y coordinants relative to the cell
var Xoffset = this.fade(x - cellCoord.x);
var Yoffset = this.fade(y - cellCoord.y);
var vectors = this.cellToVectors(cellCoord.x, cellCoord.y);
//The offset from each corner is calculated.
//Then the dotproduct between the offset vector and the random vector is calculated.
var NEoffset = {x: 1 - Xoffset, y: 1 - Yoffset};
var NEdotProduct = this.dotProduct(NEoffset, vectors.NE);
var SEoffset = {x: 1 - Xoffset, y: Yoffset};
var SEdotProduct = this.dotProduct(SEoffset, vectors.SE);
var SWoffset = {x: Xoffset, y: Yoffset};
var SWdotProduct = this.dotProduct(SWoffset, vectors.SW);
var NWoffset = {x: Xoffset, y: 1 - Yoffset};
var NWdotProduct = this.dotProduct(NWoffset, vectors.NW);
var Nlerp = this.lerp(NWdotProduct, NEdotProduct, Xoffset);
var Slerp = this.lerp(SWdotProduct, SEdotProduct, Xoffset);
var finalValue = this.lerp(Slerp, Nlerp, Yoffset);
if(finalValue < -.5 ){
console.log("less than -.5");
}
return finalValue;
},
noise: function(width, height){
var values = [];
for (var y = 0; y < height; ++y) {
for (var x = 0; x < width; ++x) {
values[x + y * width] = (this.perlinNoise(x/30,y/30)+1)/2;
}
}
return values;
},
element: document.getElementById("PerlinDisplay1"),
loop: function (){},
initialize: function(){
initCanvas("PerlinDisplay1Canvas");
var values = this.noise(canvas.width, canvas.height);
//maps values from 0 to 1 to grey scale pixels, already tested.
var valuesAsPixels = values.map(x => ValueToPixel(x));
UpdateCanvas(valuesAsPixels);
},
deinitialize: function(){}
}
The functions initCanvas, ValueToPixel, and UpdateCanvas work and have been tested with other use cases. I would really appreciate some insight as to what I am doing wrong.
Edit: Upon request I am adding the functions ValueToPixel and UpdateCanvas.
function UpdateCanvas(pixels){
var n = 0;
for (var y = 0; y < canvas.height; ++y) {
for (var x = 0; x < canvas.width; ++x) {
var index = (y * canvas.width + x) * 4;
for(var channel = 0; channel < 4; ++channel){
data[index + channel] = pixels[index/4][channel];
n++;
}
}
}
context.putImageData(imageData, 0, 0);
}
function ValueToPixel(value){
//red, green, blue, alpha
return [value*255, value*255, value*255, 255];
}
I figured it out! I was calculating the offsets from the corners incorrectly. I was wrongly calculating the absolute values of the distance from the corners rather than the offset from the corner. Here is the corrected code snipped below:
//The offset from each corner is calculated.
//Then the dotproduct between the offset vector and the random vector is calculated.
var NEoffset = {x: Xoffset - 1, y: Yoffset - 1};
var NEdotProduct = this.dotProduct(NEoffset, vectors.NE);
var SEoffset = {x: Xoffset - 1, y: Yoffset};
var SEdotProduct = this.dotProduct(SEoffset, vectors.SE);
var SWoffset = {x: Xoffset, y: Yoffset};
var SWdotProduct = this.dotProduct(SWoffset, vectors.SW);
var NWoffset = {x: Xoffset, y: Yoffset - 1};
var NWdotProduct = this.dotProduct(NWoffset, vectors.NW);
Note that instead of subtracting the offset from 1, 1 is being subtracted from the offset.
How change the speed of each shape?
I tried to play with pct but I guess this is wrong way:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
window.requestAnimFrame = (function (callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
// shape stuff
var shapes = [];
var points;
// shape#1
points = pointsToSingleArray([{
x: 20,
y: 20
}, {
x: 50,
y: 100
}, {
x: 75,
y: 20
}, {
x: 100,
y: 100
}]);
shapes.push({
width: 20,
height: 10,
waypoints: points,
color: "red"
});
// shape#2
points = pointsToSingleArray([{
x: 0,
y: 0
}, {
x: 180,
y: 0
}, {
x: 180,
y: 180
}, {
x: 0,
y: 180
}, {
x: 0,
y: 0
}, {
x: 100,
y: 80
}]);
shapes.push({
width: 20,
height: 20,
waypoints: points,
color: "blue"
});
// animation stuff
var index = 0;
var fps = 60;
// start animating
animate();
function pointsToSingleArray(points) {
// array to hold all points on this polyline
var allPoints = [];
// analyze all lines formed by this points array
for (var a = 1; a < points.length; a++) { // loop through each array in points[]
// vars for interpolating steps along a line
var dx = points[a].x - points[a - 1].x;
var dy = points[a].y - points[a - 1].y;
var startX = points[a - 1].x;
var startY = points[a - 1].y;
// calc 100 steps along this particular line segment
for (var i = 1; i <= 100; i++) {
var pct = Math.min(1, i * .01);
var nextX = startX + dx * pct;
var nextY = startY + dy * pct;
allPoints.push({
x: nextX,
y: nextY
});
}
}
return (allPoints);
}
function animate() {
setTimeout(function () {
// this flag becomes true if we made any moves
// If true, we later request another animation frame
var weMoved = false;
// clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw all shapes
for (var i = 0; i < shapes.length; i++) {
// get reference to next shape
var shape = shapes[i];
// check if we still have waypoint steps for this shape
if (index < shape.waypoints.length) {
// we're not done, so set the weMoved flag
weMoved = true;
// draw this shape at its next XY
drawShape(shape, index);
} else {
// we're done animating this shape
// draw it in its final position
drawShape(shape, shape.waypoints.length - 1);
}
}
// goto next index XY in the waypoints array
// Note: incrementing by 2 doubles animation speed
index += 2;
// if weMoved this frame, request another animation loop
if (weMoved) {
requestAnimFrame(animate)
};
}, 1000 / fps);
}
function drawShape(shape, waypointIndex) {
var x = shape.waypoints[waypointIndex].x;
var y = shape.waypoints[waypointIndex].y;
ctx.fillStyle = shape.color;
ctx.fillRect(x, y, shape.width, shape.height);
}
Maybe somebody know examples with changing speed, or how to make the code better.
http://jsfiddle.net/4DxLL/ - changing speed
var index = [0, 0];
shapes.push({
width: 20,
height: 10,
waypoints: points,
color: "red",
speed: 10,
});
index[i] += shape.speed;