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 });
}
}
Related
I have a 2D board made with KonvaJS and tokens that can move on a square grid. I can already add fog of war and remove it manually. However, I would like to make it so, when each token moves, it reveals a certain around it, taking into account walls. Most of the work is done, however it's not entirely accurate.
Basically for each wall, I'm checking if the token is on the top/right/bottom/left of it. And then depending on which one it is, I reduce the width/height of the revealing area so it doesn't go beyond the wall. Here is an image explaining what I have and what I need
Legend:
Gray is fog of war
Red area is the wall/obstacle
Token is the movable token
Blue area is the revealed area
Blue lines inside red area is where it intersects
Purple lines are squares that should be revealed (aka, it should be blue)
Basically, in this case, an intersection was detected and the token is on the right side of the obstacle. So I got the right side of the wall (the x coordinate), and made the blue area starting point be that x coordinate and removed from the total width of the blue area the intersection width(the blue lines, so 1 square of width was removed).
However, because of that, the purple lines don't get filled in. Unfortunately, I can't just check the intersection points between blue and red and only remove those, because if the blue area is bigger than the red area, it would reveal the other side of the obstacle(which I don't want).
Here is the code I'm using to iterate the walls, checking if there is an intersection, checking where the token is, and then removing the width or height according to the intersection.
const tokenPosition = { x: 10, y: 10 };
const haveIntersection = (r1, r2) => !(
r2.x > r1.x + r1.width || // Compares top left with top right
r2.x + r2.width < r1.x || // Compares top right with top left
r2.y > r1.y + r1.height || // Compare bottom left with bottom right
r2.y + r2.height < r1.y // Compare bottom right with bottom left
);
walls.forEach(wall => {
const redArea = { x: wall.x, y: wall.y, width: wall.width, height: wall.height };
// blueArea has the same properties as redArea
if (haveIntersection(blueArea, redArea)) {
const tokenToTheRight = tokenPosition.x > wall.x + wall.width;
const tokenToTheLeft = tokenPosition.x < wall.x;
const tokenToTheTop = tokenPosition.y < wall.y;
const tokenToTheBottom = tokenPosition.y > wall.y + wall.height;
if (tokenToTheRight) {
let diff = wall.x + wall.width - blueArea.x;
blueArea.x = wall.x + wall.width;
blueArea.width = blueArea.width - diff;
}
if (tokenToTheLeft) {
let diff = blueArea.x + blueArea.width - wall.x;
blueArea.width = blueArea.width - diff;
}
if (tokenToTheTop) {
let diff = blueArea.y + blueArea.height - wall.y;
blueArea.height = blueArea.height - diff;
}
if (tokenToTheBottom) {
let diff = wall.y + wall.height - blueArea.y;
blueArea.y = wall.y + wall.height;
blueArea.height = blueArea.height - diff;
}
}
});
Any idea on how to fix this or if I should be taking a different approach?
You'll have to do something ray-tracing like to get this to work.
In the snippet below, I:
Loop over each cell in your token's field-of-view
Check for that cell center whether
it is in a box, or
a line between the token and the cell center intersects with a wall of a box
Color the cell based on whether it intersects
Note: the occlusion from the boxes is quite aggressive because we only check the center for quite a large grid cell. You can play around with some of the settings to see if it matches your requirements. Let me know if it doesn't.
Legend:
Red: box
Light blue: in field of view
Orange: blocked field of view because box-overlap
Yellow: blocked field of view because behind box
// Setup
const cvs = document.createElement("canvas");
cvs.width = 480;
cvs.height = 360;
const ctx = cvs.getContext("2d");
document.body.appendChild(cvs);
// Game state
const GRID = 40;
const H_GRID = GRID / 2;
const token = { x: 7.5, y: 3.5, fow: 2 };
const boxes = [
{ x: 2, y: 3, w: 4, h: 4 },
{ x: 8, y: 4, w: 1, h: 1 },
];
const getBoxSides = ({ x, y, w, h }) => [
[ [x + 0, y + 0], [x + w, y + 0]],
[ [x + w, y + 0], [x + w, y + h]],
[ [x + w, y + h], [x + 0, y + h]],
[ [x + 0, y + h], [x + 0, y + 0]],
];
const renderToken = ({ x, y, fow }) => {
const cx = x * GRID;
const cy = y * GRID;
// Render FOV
for (let ix = x - fow; ix <= x + fow; ix += 1) {
for (let iy = y - fow; iy <= y + fow; iy += 1) {
let intersectionFound = false;
for (const box of boxes) {
if (
// Check within boxes
pointInBox(ix, iy, box) ||
// Check walls
// Warning: SLOW
getBoxSides(box).some(
([[ x1, y1], [x2, y2]]) => intersects(x, y, ix, iy, x1, y1, x2, y2)
)
) {
intersectionFound = true;
break;
}
}
if (!intersectionFound) {
renderBox({ x: ix - .5, y: iy - .5, w: 1, h: 1 }, "rgba(0, 255, 255, 0.5)", 0);
ctx.fillStyle = "lime";
ctx.fillRect(ix * GRID - 2, iy * GRID - 2, 4, 4);
} else {
renderBox({ x: ix - .5, y: iy - .5, w: 1, h: 1 }, "rgba(255, 255, 0, 0.5)", 0);
ctx.fillStyle = "red";
ctx.fillRect(ix * GRID - 2, iy * GRID - 2, 4, 4);
}
}
}
ctx.lineWidth = 5;
ctx.fillStyle = "#efefef";
ctx.beginPath();
ctx.arc(cx, cy, GRID / 2, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
}
const renderBox = ({ x, y, w, h }, color = "red", strokeWidth = 5) => {
ctx.fillStyle = color;
ctx.strokeWidth = strokeWidth;
ctx.beginPath();
ctx.rect(x * GRID, y * GRID, w * GRID, h * GRID);
ctx.closePath();
ctx.fill();
if (strokeWidth) ctx.stroke();
}
const renderGrid = () => {
ctx.lineWidth = 1;
ctx.beginPath();
let x = 0;
while(x < cvs.width) {
ctx.moveTo(x, 0);
ctx.lineTo(x, cvs.height);
x += GRID;
}
let y = 0;
while(y < cvs.height) {
ctx.moveTo(0, y);
ctx.lineTo(cvs.width, y);
y += GRID;
}
ctx.stroke();
}
boxes.forEach(box => renderBox(box));
renderToken(token);
renderGrid();
// Utils
// https://errorsandanswers.com/test-if-two-lines-intersect-javascript-function/
function intersects(a,b,c,d,p,q,r,s) {
var det, gamma, lambda;
det = (c - a) * (s - q) - (r - p) * (d - b);
if (det === 0) {
return false;
} else {
lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / det;
gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / det;
return (0 <= lambda && lambda <= 1) && (0 <= gamma && gamma <= 1);
}
}
function pointInBox(x, y, box) {
return (
x > box.x &&
x < box.x + box.w &&
y > box.y &&
y < box.bottom
);
}
canvas { border: 1px solid black; }
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));
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 making a Game Of Life just for fun and practice but I am getting a TypeError: cell.computeNeighborsCoords is not a function and i just cant figure what is the cause. I've been at it for a while so maybe i am overlooking something simple.
I am looping through a grid which is an array of objects and calling a method of each object that is supposed to calculate all possible neighbor coords of a given cell. However, when i call the method, i seem to be getting the aforementioned error.
Help is much appreciated!
Cell.js
export class Cell {
constructor(x, y, resolution, state) {
this.x = x;
this.y = y;
this.sx = x * resolution;
this.sy = y * resolution;
this.w = resolution;
this.h = resolution;
this.state = state;
}
findCellByCoords(grid, x, y) {
// LeftTop ox - 1, oy - 1
// Left ox - 1, oy
// BLeft ox - 1, oy + 1
// Bottom ox oy + 1
// BRight ox + 1, oy + 1
// Right ox + 1, oy
// RightTo ox + 1, oy - 1
for(var i = 0; i < grid.length; i++) {
for(var j = 0; j < grid[i].length; j++) {
let cell = grid[i][j];
if(cell.x == x && cell.y == y) return cell;
}
}
return false;
}
getTotalAliveNeighbors(cellCollection) {
return cellCollection.reduce((acc, next) => {
return acc += next.state;
}, 0)
}
computeNeighborsCoords() {
let ox = this.x;
let oy = this.y;
// LeftTop ox - 1, oy - 1
// Left ox - 1, oy
// BLeft ox - 1, oy + 1
// Bottom ox oy + 1
// BRight ox + 1, oy + 1
// Right ox + 1, oy
// RightTo ox + 1, oy - 1
this.neighborCoords = [
{
x: ox - 1,
y: oy - 1
},
{
x: ox - 1,
y: oy
},
{
x: ox - 1,
y: oy + 1
},
{
x: ox,
y: oy + 1
},
{
x: ox + 1,
y: oy + 1
},
{
x: ox + 1,
y: oy
},
{
x: ox + 1,
y: oy - 1
},
{
x: ox,
y: oy - 1
}
];
// neighborCoordsArr.forEach((neighborCoords) => {
// let x = (neighborCoords.x + maxCellsPerRowCol) % maxCellsPerRowCol;
// let y = (neighborCoords.y + maxCellsPerRowCol) % maxCellsPerRowCol;
// let foundNeighbor = this.findCellByCoords(grid, x, y);
// neighbors.push(foundNeighbor);
// })
// return neighbors;
}
}
App.js
import { Cell } from "/Cell.js"
let canvas = document.getElementById("canvas");
var cachedCanvas = document.createElement('canvas');
cachedCanvas.width = canvas.width;
cachedCanvas.height = canvas.height;
var cachedCtx = cachedCanvas.getContext("2d");
let ctx = canvas.getContext("2d");
let grid,
rows,
cols,
// Square canvas size
canvasSize = 600,
resolution = 10;
let maxCellsPerRowCol = canvasSize / resolution;
let make2DArray = (cols, rows) => {
let arr = new Array()
for(let y = 0; y < cols; y++) {
arr[y] = new Array();
for(let x = 0; x < rows; x++) {
let alive = Math.round(Math.random());
arr[y][x] = new Cell(x, y, resolution, alive);
}
}
return arr;
}
function start() {
canvas.width = canvasSize;
canvas.height = canvasSize;
cachedCanvas.width = canvasSize;
cachedCanvas.height = canvasSize;
rows = canvas.width / resolution;
cols = canvas.height / resolution;
grid = make2DArray(cols, rows);
// console.table(grid)
}
function update() {
let newGrid = JSON.parse(JSON.stringify(grid))
// Update the new grid based on the rules
// for(var i = 0; i < grid.length; i++) {
// for(var j = 0; j < grid[i].length; j++) {
// let cell = grid[i][j];
// let newCell = newGrid[i][j];
// let neighbors = getNeighborsOfCell(grid, cell);
// let totalAliveNeighbors = getTotalAliveNeighbors(neighbors);
// if(cell.state == 1 && totalAliveNeighbors < 2) newCell.state = 0;
// if(cell.state == 1 && totalAliveNeighbors == 2 || totalAliveNeighbors == 3) newCell.state = 1;
// if(cell.state == 1 && totalAliveNeighbors > 3) newCell.state = 0;
// if(cell.state == 0 && totalAliveNeighbors == 3) newCell.state = 1;
// }
// }
prerender();
draw();
// Update the grid with the new grid
grid = JSON.parse(JSON.stringify(newGrid))
}
function prerender() {
// Pre-render the grid
for(var i = 0; i < grid.length; i++) {
for(var j = 0; j < grid[i].length; j++) {
let cell = grid[i][j];
cell.computeNeighborsCoords();
if(cell.state == 1) {
cell.fillColor = "#000"
} else {
cell.fillColor = "#FFF"
}
cachedCtx.beginPath();
cachedCtx.lineWidth = "1";
cachedCtx.fillStyle = cell.fillColor;
cachedCtx.strokeStyle = "black";
cachedCtx.rect(cell.sx, cell.sy, cell.w, cell.h);
cachedCtx.fill()
cachedCtx.stroke();
}
}
}
function draw() {
ctx.clearRect(0,0,canvas.width, canvas.height);
ctx.drawImage(cachedCanvas, 0, 0)
window.requestAnimationFrame(update);
}
start();
window.requestAnimationFrame(update);
First off, sorry about the bad title, I couldn't think of a better way to describe what I was trying to do. I have an HTML canvas, which, for argument's sake, is x pixels wide and y pixels tall. I have been trying to write a code that takes the location in the array of the canvas' image data of two pixels that are on lines z and z + 1 and fill in all the pixels in the higher row between the two pixels a certain color. I'm afraid I may not have made much sense, so here's a diagram:
Sorry about the poor graphics, but assume each rectangle is a pixel. The program should take in the first value for each of the black pixels (each is stored as r,g,b,a, the program gets the location of the r in the array representing the canvas' image data), and stores the r value for the lower pixel as bottomPixel and the higher one as topPixel. In this case, bottomPixel = 124 and topPixel = 112 It should use this to fill all pixels between the two base pixels a certain color. For example, using the previous pixel locations, the red pixels in the following picture should be colored in, but the blue one should not.
Here is the code I have: (Assume that the canvas has an Id "Canvas" and is 6px wide by 10px tall)
var cnvs = document.getElementById("Canvas");
cnvs.height = 10; //This height and width is here as an example.
cnvs.width = 6;
var cont = cnvs.getContext("2d");
var environment = cont.getImageData(0,0,6,10);
var bottomPixel = 124;//Not neccesarily 124 or 112, just example values
var topPixel = 112;
if ( bottomPixel - topPixel > 6*4 ) //If bottomPixel is to the right of topPixel
{
for ( var i = 0 ; i < ((bottomPixel-6*4)-topPixel)/4 ; i++ )
{
var index = topPixel + i * 4;
environment.data[index] = 0;
environment.data[index + 1 ] = 255;
environment.data[index + 2 ] = 0;
environment.data[index + 3 ] = 255;
}
}
if ( bottomPixel - topPixel > 6*4 ) //If bottomPixel is to the left of topPixel
{
for ( var i = 0 ; i < (topPixel-(bottomPixel-6*4))/4; i++ )
{
var index = topPixel - i * 4;
environment.data[index] = 0;
environment.data[index + 1 ] = 255;
environment.data[index + 2 ] = 0;
environment.data[index + 3 ] = 255;
}
}
I'd like to know why my code isn't doing what I previously described. If anything here needs clarification, please leave a comment. Thanks!
This is a method that works on the point coordinates and uses a the setPixel function to modify imageData. I'm using blue for start and black for end. You'll need to adjust for your exact condition but you can use setPixel to allow for direct x and y edits on the imageData.
update
I've included an alternate line method and your line method. There is also an animation that will help you find errors.
function ptIndex(p, w) {
return ((p.x|0) + ((p.y|0) * w)) * 4;
}
function setPixel(p, w, d, rgba) {
var i = ptIndex(p, w);
d[i] = rgba.r;
d[i + 1] = rgba.g;
d[i + 2] = rgba.b;
d[i + 3] = rgba.a;
}
function yourLine(p1, p2, w, d, rgba) {
var cnvs = document.getElementById("Canvas");
var bottomPixel = ptIndex(p1, w);
var topPixel = ptIndex(p2, w)
if (bottomPixel - topPixel > w * 4) //If bottomPixel is to the right of topPixel
{
for (var i = 0; i < ((bottomPixel - w * 4) - topPixel) / 4; i++) {
var index = topPixel + i * 4;
d[index] = rgba.r;
d[index + 1] = rgba.g;
d[index + 2] = rgba.b;
d[index + 3] = rgba.a
}
}
if (bottomPixel - topPixel > w * 4) //If bottomPixel is to the left of topPixel
{
for (var i = 0; i < (topPixel - (bottomPixel - w * 4)) / 4; i++) {
var index = topPixel - i * 4;
d[index] = rgba.r;
d[index + 1] = rgba.g;
d[index + 2] = rgba.b;
d[index + 3] = rgba.a
}
}
}
function drawRandPoints() {
var cnvs = document.getElementById("Canvas");
var cont = cnvs.getContext("2d");
// ghost last draw
cont.fillStyle = "white";
cont.fillRect(0, 0, cnvs.width, cnvs.height);
// get image data
var environment = cont.getImageData(0, 0, cnvs.width, cnvs.height);
var d = environment.data, w = cnvs.width;
// create colors
var black = {
r: 0,
g: 0,
b: 0,
a: 255
};
var red = {
r: 255,
g: 0,
b: 0,
a: 255
};
var blue = {
r: 0,
g: 0,
b: 255,
a: 255
};
var frames = 0;
var p1 = {x: ((cnvs.width / 2|0)), y: 0, sx: 1, sy:0};
var p2 = {x: cnvs.width, y: ((cnvs.height / 2)|0), sx: -1, sy: 0};
function step(p) {
if (p.x > cnvs.width) {
p.x = cnvs.width;
p.sx = 0;
p.sy = 1;
}
if (p.y > cnvs.height) {
p.y = cnvs.height;
p.sy = 0;
p.sx = -1;
}
if (p.x < 0) {
p.x = 0;
p.sx = 0;
p.sy = -1;
}
if (p.y < 0) {
p.y = 0;
p.sy = 0;
p.sx = 1;
}
}
function ani() {
cont.fillStyle = "white";
cont.fillRect(0, 0, cnvs.width, cnvs.height);
environment = cont.getImageData(0, 0, cnvs.width, cnvs.height);
d = environment.data;
step(p1);
step(p2);
var p3 = {
x: cnvs.width - p1.x,
y: cnvs.height - p2.y
};
var p4 = {
x: cnvs.width - p2.x,
y: cnvs.height - p1.y
};
yourLine(p1, p2, w, d, {r:0,g:255,b:0,a:255});
myDrawLine(p1, p2, w, d, red);
drawLineNoAliasing(p3, p4, w, d, blue);
setPixel(p1, w, d, black);
setPixel(p2, w, d, black);
frames %= 12;
p1.x += p1.sx;
p1.y += p1.sy;
p2.x += p2.sx;
p2.y += p2.sy;
// Put the pixel data on the canvas.
cont.putImageData(environment, 0, 0);
requestAnimationFrame(ani);
}
ani();
}
function myDrawLine(p1, p2, w, d, rgba) {
// Get the max length between x or y
var lenX = Math.abs(p1.x - p2.x);
var lenY = Math.abs(p1.y - p2.y);
var len = Math.sqrt(Math.pow(lenX,2) + Math.pow(lenY,2));
// Calculate the step increment between points
var stepX = lenX / len;
var stepY = lenY / len;
// If the first x or y is greater then make step negetive.
if (p2.x < p1.x) stepX *= -1;
if (p2.y < p1.y) stepY *= -1;
// Start at the first point
var x = p1.x;
var y = p1.y;
for (var i = 0; i < len; i++) {
x += stepX;
y += stepY;
// Make a point from new x and y
var p = {
x: x,
y: y
};
// Draw pixel on data
setPixel(p, w, d, rgba);
// reached goal (removes extra pixel)
if (Math.abs(p.x - p2.x) <= 1 && Math.abs(p.y - p2.y) <= 1) {
break;
}
}
// Draw start and end pixels. (might draw over line start and end)
setPixel(p1, w, d, rgba);
setPixel(p2, w, d, rgba);
}
// alternate from http://stackoverflow.com/questions/4261090/html5-canvas-and-anti-aliasing answer
// some helper functions
// finds the distance between points
function DBP(x1, y1, x2, y2) {
return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}
// finds the angle of (x,y) on a plane from the origin
function getAngle(x, y) {
return Math.atan(y / (x == 0 ? 0.01 : x)) + (x < 0 ? Math.PI : 0);
}
// the function
function drawLineNoAliasing(p1, p2, w, d, rgba) {
var dist = DBP(p1.x, p1.y, p2.x, p2.y); // length of line
var ang = getAngle(p2.x - p1.x, p2.y - p1.y); // angle of line
var cos = Math.cos(ang);
var sin = Math.sin(ang);
for (var i = 0; i < dist; i++) {
// for each point along the line
var pt = {
x: p1.x + cos * i,
y: p1.y + sin * i
};
setPixel(pt, w, d, rgba);
}
}
// end alt
drawRandPoints();
#Canvas {
border: 1px solid red image-rendering: optimizeSpeed;
/* Older versions of FF */
image-rendering: -moz-crisp-edges;
/* FF 6.0+ */
image-rendering: -webkit-optimize-contrast;
/* Safari */
image-rendering: -o-crisp-edges;
/* OS X & Windows Opera (12.02+) */
image-rendering: pixelated;
/* Awesome future-browsers */
image-rendering: optimize-contrast;
/* CSS3 Proposed */
-ms-interpolation-mode: nearest-neighbor;
/* IE */
}
<canvas id="Canvas" width="128" height="64" style="width:320px"></canvas>