I've been trying to follow this tutorial: https://www.youtube.com/watch?v=aKYlikFAV4k&t=1848s&ab_channel=TheCodingTrain
However, I'm using vanilla Javascript. I'm struggling to get the neighboring cells for each cell in my grid. I'm pretty new to coding so help would be very much appreciated!
Here is my code so far:
//GLOBAL VARIABLES
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const wh = 600;
const cellSize = 30;
const rows = 20;
const cols = 20;
const grid = new Array(rows);
const open = [];
const closed = [];
let start;
let end;
//FUNCTIONS
//Immediately-invoked function expression
//Runs code immediately when the page loads and keeps it out of the global scope (avoids naming conflicts)
(function() {
setup();
})();
function Cell(x, y) { //Constructor function for each cell in the array
this.x = 0;
this.y = 0;
this.f = 0;
this.g = 0;
this.h = 0;
this.show = function(color) { //Function to show cell on grid
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, cellSize, cellSize);
ctx.strokeStyle = 'white';
ctx.strokeRect(this.x, this.y, cellSize, cellSize);
}
}
//Function to setup the canvas
function setup() {
let interval = setInterval(update, 120);
canvas.setAttribute('width', wh);
canvas.setAttribute('height', wh);
document.body.insertBefore(canvas, document.body.childNodes[0]); //Inserts canvas before the first element in body
createGrid();
setStartEnd();
}
//Function to create grid
function createGrid() {
for (let i = 0; i < rows; i++) { //Creating 2D array
grid[i] = new Array(cols);
}
let x = 0;
let y = 0;
for (let i = 0; i < rows; i++) { //Creating a new cell for each spot in the array
for (let j = 0; j < cols; j++) {
grid[i][j] = new Cell();
grid[i][j].x = x;
grid[i][j].y = y;
grid[i][j].show();
x = x + 1 * 30;
}
x = 0;
y = y + 1 * 30;
}
}
//Function that defines the start and end points
function setStartEnd() {
start = grid[0][0];
end = grid[cols - 1][rows - 1];
open.push(start);
}
//Function to remove a node from an array
function removeArray(arr, e) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === e) {
arr.splice(i, 1);
}
}
}
//Main function
function update() {
//nodes part of "open" array are green
for (let i = 0; i < open.length; i++) {
open[i].show('green');
}
//nodes part of "closed" array are red
for (let i = 0; i < closed.length; i++) {
closed[i].show('red');
}
}
You've made it a bit hard for yourself by having Cell not store its own x,y position in the grid.
If you move some logic from your nested i,j for loop to your Cell class, it gets easier. I modified Cell to store its x and y grid coordinate rather than pixel coordinate. You can then, in update, do something like this:
const nextOpenSet = new Set();
open.forEach(cell => {
const above = grid[cell.y - 1]?.[cell.x];
if (above) nextOpenSet.add(above);
const below = grid[cell.y + 1]?.[cell.x];
if (below) nextOpenSet.add(below);
const left = grid[cell.y][cell.x - 1];
if (left) nextOpenSet.add(left);
const right = grid[cell.y][cell.x + 1];
if (right) nextOpenSet.add(right);
});
open = Array.from(nextOpenSet);
Here's a runnable example:
//GLOBAL VARIABLES
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const wh = 600;
const cellSize = 30;
const rows = 20;
const cols = 20;
const grid = new Array(rows);
let open = [];
const closed = [];
let start;
let end;
//FUNCTIONS
//Immediately-invoked function expression
//Runs code immediately when the page loads and keeps it out of the global scope (avoids naming conflicts)
(function() {
setup();
})();
function Cell(x, y) { //Constructor function for each cell in the array
this.x = x;
this.y = y;
this.show = function(color) { //Function to show cell on grid
ctx.fillStyle = color;
ctx.fillRect(this.x * cellSize, this.y * cellSize, cellSize, cellSize);
ctx.strokeStyle = 'white';
ctx.strokeRect(this.x * cellSize, this.y * cellSize, cellSize, cellSize);
}
}
//Function to setup the canvas
function setup() {
let interval = setInterval(update, 120);
canvas.setAttribute('width', wh);
canvas.setAttribute('height', wh);
document.body.insertBefore(canvas, document.body.childNodes[0]); //Inserts canvas before the first element in body
createGrid();
setStartEnd();
}
//Function to create grid
function createGrid() {
for (let i = 0; i < rows; i++) { //Creating 2D array
grid[i] = new Array(cols);
}
for (let i = 0; i < rows; i++) { //Creating a new cell for each spot in the array
for (let j = 0; j < cols; j++) {
grid[i][j] = new Cell(i, j);
grid[i][j].show();
}
}
}
//Function that defines the start and end points
function setStartEnd() {
start = grid[0][0];
end = grid[cols - 1][rows - 1];
open.push(start);
}
//Function to remove a node from an array
function removeArray(arr, e) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === e) {
arr.splice(i, 1);
}
}
}
//Main function
function update() {
//nodes part of "open" array are green
for (let i = 0; i < open.length; i++) {
open[i].show('green');
}
//nodes part of "closed" array are red
for (let i = 0; i < closed.length; i++) {
closed[i].show('red');
}
const nextOpenSet = new Set();
open.forEach(cell => {
const above = grid[cell.y - 1]?.[cell.x];
if (above) nextOpenSet.add(above);
const below = grid[cell.y + 1]?.[cell.x];
if (below) nextOpenSet.add(below);
const left = grid[cell.y][cell.x - 1];
if (left) nextOpenSet.add(left);
const right = grid[cell.y][cell.x + 1];
if (right) nextOpenSet.add(right);
});
open = Array.from(nextOpenSet);
}
Related
//Editable Vars
let cols = 35;
let rows = 35;
let fps = 5;
//Declarations
let canvas;
let ctx;
let background;
let grid = new Array(cols);
let w;
let h;
let pathfinder;
let target;
let timer;
let renderQueue = [];
let canPathfind = true;
//Space Class
class Space{
constructor(x,y,c='lightgrey'){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.c = c;
}
draw(){
ctx.fillStyle = this.c;
ctx.strokeStyle = 'black';
ctx.fillRect(this.x * w, this.y * h, this.w, this.h);
ctx.rect(this.x * w, this.y * h, this.w, this.h);
ctx.stroke();
}
move(x,y){
if(x < 0 || x > cols-1 || y < 0|| y > rows-1){
return;
}
grid[this.x][this.y] = new Space(this.x,this.y);
renderQueue.push(grid[this.x][this.y]);
this.x = x;
this.y = y;
grid[this.x][this.y] = this;
renderQueue.push(grid[this.x][this.y]);
}
}
//Game-Run code
function gameStart(){
canvas = document.getElementById('gameCanvas');
ctx = canvas.getContext('2d');
w = canvas.width / cols;
h = canvas.height / rows;
createGrid();
pathfinder = new Space(randomInt(cols-1),randomInt(rows-1),'green');
grid[pathfinder.x][pathfinder.y] = pathfinder;
target = new Space(randomInt(cols-1),randomInt(rows-1),'red');
grid[target.x][target.y] = target;
drawGrid();
timer = setInterval(updateScreen, 1000/fps);
}
function restartGame(){
clearInterval(timer);
gameStart();
}
//Starts loading process on windows load
window.onload = gameStart();
//Checks all 8 possible move directions and calls pathfinder.move for best move
function pathfind(pathfinder, target){
if(!canPathfind) return;
let p = {x: pathfinder.x, y: pathfinder.y};
let t = {x: target.x, y: target.y};
let move = {x : 0, y : 0};
// 0,0 9,9
//if(p.x == t.x && p.y == t.y){
//restartGame();
//}
if(t.x - p.x >= 1){
move.x = 1;
}else if(t.x - p.x <= -1){
move.x = -1;
}else{
move.x = 0;
}
if(t.y - p.y >= 1){
move.y = 1;
}else if(t.y - p.y <= -1){
move.y = -1;
}else{
move.y = 0;
}
pathfinder.move(pathfinder.x + move.x, pathfinder.y + move.y);
}
function updateScreen(){
pathfind(pathfinder,target);
drawUpdatedSpaces();
}
function drawUpdatedSpaces(){
for(let i = 0; i < renderQueue.length; i++){
renderQueue[i].draw();
}
renderQueue = [];
}
function drawGrid(){
for(let i = 0; i < grid.length; i++){
for(let j = 0; j < grid[i].length; j++){
grid[i][j].draw();
}
}
}
//Creates grid and instantiates Space in every cell
function createGrid(){
for(let i = 0; i < grid.length; i++){
grid[i] = new Array(rows);
}
for(let i = 0; i < grid.length; i++){
for(let j = 0; j < grid[i].length; j++){
grid[i][j] = new Space(i,j);
}
}
}
// Returns distance to target from specified coords
function distanceFromTarget(x, y) {
return (Math.sqrt(Math.pow(Math.abs(x - target.x), 2) + (Math.pow(Math.abs(y - target.y), 2))));
}
// Returns random Integer between 0 and Max
function randomInt(max) {
return Math.floor(Math.random() * max);
}
It runs as expected which is great, but performance is super slow. That may be because I'm using jsfiddle to work on this while away from my personal PC setup, but is there a way to make this perform better? As of right now I can't move the grid size to >50 without it running extremely slow. I would love to eventually move to a 4k x 4k grid and create an auto-generating maze for the 'pathfinder' to pathfind through.
Thoughts/things I'm considering for performance:
Using HTML grid and updating via CSS instead of HTML5 Canvas
Only re-drawing cells that have changed (Implemented in the Space.move() function with array renderQueue)
literally re-doing everything in python :D
I have made a grid and need to check whether a cell has a or several bombs around it, but I'm a bit confused on how to do it now. I have tried this code;
function placeNumbers() {
for (let x = -ColumnRow; x < ColumnRow * 3; x += ColumnRow) {
for (let y = -ColumnRow; y < ColumnRow * 3; y += ColumnRow) {
if (cells[x].bomb == false) {
//do something
}
}
}
}
but then it says that I can't use .bomb. What can I do? I have made a class Cell which has a bomb feature and if a bomb is on the cell then the cell should have bomb = true. So then i have to check wether the neighbour of a cell does has the bomb true or false?
Does anyone have any tips or know what to do here?
Here's a sample of the code:
const canvas = document.getElementById("myCanvas")
const ctx = canvas.getContext("2d")
class Cell {
constructor(x, y, w) {
this.x = x
this.y = y
this.w = w
this.bomb = false
this.revealed = false
}
show() {
const cell = new Path2D();
cell.rect(this.x, this.y, this.w, this.w);
ctx.stroke(cell);
this.cell = cell;
}
}
const w = canvas.width
const h = canvas.height
const ColumnRow = w / 15
const cells = []
const bombs = 10
let checked = true
let bombPosition = []
function setup() {
for (let x = 0; x < w - 1; x += ColumnRow) {
for (let y = 0; y < h - 1; y += ColumnRow) {
cells.push(new Cell(x, y, ColumnRow))
}
}
}
function drawCells() {
for (let c of cells) {
c.show()
}
}
function numOfBombs() {
for (let i = 0; i < bombs; i++) {
randomX = Math.floor(Math.random() * w / ColumnRow) * ColumnRow
randomY = Math.floor(Math.random() * h / ColumnRow) * ColumnRow
bombPosition.push({ x: randomX, y: randomY });
}
}
function drawBomb() {
let img = new Image();
img.onload = function () {
for (let i = 0; i < bombPosition.length; i++) {
ctx.drawImage(img, bombPosition[i].x, bombPosition[i].y, ColumnRow, ColumnRow)
}
};
img.src = "https://raw.githubusercontent.com/americosp/Minesweeper/master/Minesweeper/images/mine.png";
}
function bombCollision(cell) {
for (let i = 0; i < bombPosition.length; i++) {
if (cell.x == bombPosition[i].x && cell.y == bombPosition[i].y) {
console.log("same position");
}
}
}
canvas.addEventListener('click', function (e) {
for (const cell of cells) {
if (ctx.isPointInPath(cell.cell, e.offsetX, e.offsetY)) {
ctx.clearRect(cell.x, cell.y, cell.w, cell.w);
checked = true
cell.revealed = true
bombCollision(cell)
} else {
/* ctx.clearRect(cell.x, cell.y, cell.w, cell.w); */
}
}
});
function update() {
drawCells()
if (checked) {
drawBomb()
// coverCell()
checked = false
}
requestAnimationFrame(update)
}
function update2() {
numOfBombs()
setup()
}
update2()
update()
<canvas id="myCanvas" width="600" height="600"></canvas>
I am trying to build a maze generator for a personal project. I have a recursive depth-first search function that recursively goes through each cell in the grid, checks if it has unvisited neighbors, then calls the recursive function again with the next neighbor. It is able to generate the maze just fine but I want to add a delay between each call to the recursive function so I can animate the creation of the maze as it visits each cell. Using the chrome debugger, it seems to do the 1s delay for the first iteration and then it stops waiting and jumps from the await delay back to the beginning of the function over and over without ever moving on. What am I doing wrong?
Here is the recursive function and delay function:
async function recursiveDFS(currentCell) {
await delay(1000);
highlightCell(currentCell);
currentCell.visited = true;
var [next, direction] = getNextNeighbor(currentCell);
while(typeof(next) != 'undefined') {
removeWall(currentCell, next, direction);
highlightCell(next);
recursiveDFS(next);
[next, direction] = getNextNeighbor(currentCell);
}
}
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms)
});
}
and here is the full javascript code:
"use strict"
// declare globals
const numCols = 10;
const numRows = 10;
const cellSize = 50;
var grid = [];
// create canvas
var canvas = document.createElement('canvas');
canvas.id = 'canvas';
canvas.width = numCols * cellSize;
canvas.height = numRows * cellSize;
var body = document.getElementsByTagName("body")[0];
body.appendChild(canvas);
var context = canvas.getContext('2d');
function setup() {
createGrid();
const start = grid[0][0]; // start at top left cell
const end = grid[1][1];
recursiveDFS(start);
}
class Cell {
constructor(col, row) {
this.col = col;
this.row = row;
this.neighbors = {};
this.walls = {
top: true,
right: true,
bottom: true,
left: true
};
this.visited = false;
}
setNeighbors() {
//top
if(this.row - 1 >= 0) {
this.neighbors.top = grid[this.col][this.row - 1];
}
//right
if (this.col + 1 < numCols) {
this.neighbors.right = grid[this.col + 1][this.row];
}
//bottom
if (this.row + 1 < numRows) {
this.neighbors.bottom = grid[this.col][this.row + 1];
}
//left
if (this.col - 1 >= 0) {
this.neighbors.left = grid[this.col - 1][this.row];
}
}
}
// create 2d array of Cell objects
// indexing as grid[col][row]
// grid = [[(0,0), (1,0)],
// [(0,1), (1,1)]]
function createGrid() {
for (var col = 0; col < numCols; col++) {
var colArr = []
for (var row = 0; row < numRows; row++) {
var cell = new Cell(col, row);
colArr.push(cell);
drawGridLines(cell);
}
grid.push(colArr);
}
for (var row = 0; row < numRows; row++) {
for (var col = 0; col < numCols; col++) {
grid[col][row].setNeighbors();
}
}
}
// return single neighbor randomized from all possible neighbors
function getNextNeighbor(cell) {
if (cell.neighbors) {
var neighbors = [];
for (var neighbor in cell.neighbors) {
if (cell.neighbors[neighbor].visited === false){
neighbors.push([cell.neighbors[neighbor], neighbor]);
}
}
}
if(neighbors.length > 0) {
return neighbors[Math.floor(Math.random() * neighbors.length)];
} else {
return [undefined, undefined];
}
}
function delay(ms) {
return new Promise(resolve => {
console.log("waiting...");
setTimeout(resolve, ms)
});
}
async function recursiveDFS(currentCell) {
await delay(1000);
highlightCell(currentCell);
currentCell.visited = true;
var [next, direction] = getNextNeighbor(currentCell);
while(typeof(next) != 'undefined') {
removeWall(currentCell, next, direction);
highlightCell(next);
recursiveDFS(next);
[next, direction] = getNextNeighbor(currentCell);
}
}
function highlightCell(cell) {
context.globalCompositeOperation='destination-over'; // fill rect under existing grid
const topLeft = [(cell.col) * cellSize, (cell.row) * cellSize];
context.fillStyle = '#FF0000';
context.fillRect(topLeft[0], topLeft[1], cellSize, cellSize);
}
function removeWall(cell1, cell2, direction) {
switch (direction) {
case 'top':
cell1.walls.top = false;
cell2.walls.bottom = false;
break;
case 'right':
cell1.walls.right = false;
cell2.walls.left = false;
break;
case 'bottom':
cell1.walls.bottom = false;
cell2.walls.top = false;
break;
case 'left':
cell1.walls.left = false;
cell2.walls.right = false;
break;
}
redrawGrid();
}
function redrawGrid() {
context.clearRect(0, 0, numCols * cellSize, numRows * cellSize); // clear canvas
for (var col = 0; col < numCols; col++) {
for (var row = 0; row < numRows; row++) {
drawGridLines(grid[col][row]);
}
}
}
function drawGridLines(cell) {
const topLeft = [ cell.col * cellSize, cell.row * cellSize];
const topRight = [(cell.col + 1) * cellSize, cell.row * cellSize];
const bottomLeft = [ cell.col * cellSize, (cell.row + 1) * cellSize];
const bottomRight = [(cell.col + 1) * cellSize, (cell.row + 1) * cellSize];
context.lineWidth = 2;
//draw top line
if(cell.walls.top){
context.beginPath();
context.moveTo(topLeft[0], topLeft[1]);
context.lineTo(topRight[0], topRight[1]);
context.stroke();
}
//draw right line
if(cell.walls.right) {
context.beginPath();
context.moveTo(topRight[0], topRight[1]);
context.lineTo(bottomRight[0], bottomRight[1]);
context.stroke();
}
//draw bottom line
if(cell.walls.bottom) {
context.beginPath();
context.moveTo(bottomRight[0], bottomRight[1]);
context.lineTo(bottomLeft[0], bottomLeft[1]);
context.stroke();
}
//draw left line
if(cell.walls.left) {
context.beginPath();
context.moveTo(bottomLeft[0], bottomLeft[1]);
context.lineTo(topLeft[0], topLeft[1]);
context.stroke();
}
}
setup();
async function recursiveDFS(currentCell) {
await delay(1000);
highlightCell(currentCell);
currentCell.visited = true;
var [next, direction] = getNextNeighbor(currentCell);
while(typeof(next) != 'undefined') {
removeWall(currentCell, next, direction);
highlightCell(next);
await recursiveDFS(next);
[next, direction] = getNextNeighbor(currentCell);
}
}
Add await when u call recursiveDFS(next); so that it will wait for the function to be done before going to the next step as you have set the function as async.
I'm trying to make trails of moving objects by using vector history array in p5js.
but after push updated vector, all elements in this.history replaced as last one.
I've searched some question here but still can't understand.
let ppp = [];
function setup() {
createCanvas(400, 400);
for (let i = 0; i < 3; i++) {
let p = new Particle();
ppp.push(p);
}
}
function draw() {
background(220);
for (let i = 0; i < ppp.length; i++) {
ppp[i].display();
ppp[i].update();
}
}
function Particle() {
this.pv = createVector(random(width), random(height));
this.history = [];
let rndV = p5.Vector.random2D();
this.spdV = rndV.mult(random(1, 3));
this.update = function() {
this.pv.add(this.spdV);
this.history.push(this.pv); // replace all vector element
console.log(this.history);
}
this.display = function() {
fill(30);
ellipse(this.pv.x, this.pv.y, 30);
for (let i = 0; i < this.history.length; i++) {
let trail = this.history[i];
ellipse(trail.x, trail.y, 10);
}
}
}
or if you think my approach isn't the best, I'll be happy to hear any suggestion^^
Thanks,
This can be a bit misleading in javascript:
this.history.push(this.pv);
You're pushing a reference to the same this.pv pre-allocated vector
What you are trying to do is something like:
this.history.push(this.pv.copy());
Where you are allocating memory for a completely new p5.Vector object with the x,y coordinates copied from this.pv (using the copy() method)
Demo:
let ppp = [];
function setup() {
createCanvas(400, 400);
for (let i = 0; i < 3; i++) {
let p = new Particle();
ppp.push(p);
}
}
function draw() {
background(220);
for (let i = 0; i < ppp.length; i++) {
ppp[i].display();
ppp[i].update();
}
}
function Particle() {
this.pv = createVector(random(width), random(height));
this.history = [];
let rndV = p5.Vector.random2D();
this.spdV = rndV.mult(random(1, 3));
this.update = function() {
this.pv.add(this.spdV);
this.history.push(this.pv.copy()); // replace all vector element
//console.log(this.history);
}
this.display = function() {
fill(30);
ellipse(this.pv.x, this.pv.y, 30);
for (let i = 0; i < this.history.length; i++) {
let trail = this.history[i];
ellipse(trail.x, trail.y, 10);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
Bare in mind as the sketch runs this will use more and more memory.
If simply need to render the trails and don't need the vector data for anything else you can simply render into a separate graphics layer (using createGraphics()) immediately which will save memory on the long run:
let ppp = [];
let trailsLayer;
function setup() {
createCanvas(400, 400);
// make a new graphics layer for trails
trailsLayer = createGraphics(400, 400);
trailsLayer.noStroke();
trailsLayer.fill(0);
for (let i = 0; i < 3; i++) {
let p = new Particle();
ppp.push(p);
}
}
function draw() {
background(220);
// render the trails layer
image(trailsLayer, 0, 0);
for (let i = 0; i < ppp.length; i++) {
ppp[i].display();
ppp[i].update();
}
}
function Particle() {
this.pv = createVector(random(width), random(height));
let rndV = p5.Vector.random2D();
this.spdV = rndV.mult(random(1, 3));
this.update = function() {
this.pv.add(this.spdV);
// render trails
trailsLayer.ellipse(this.pv.x, this.pv.y, 10);
}
this.display = function() {
fill(30);
ellipse(this.pv.x, this.pv.y, 30);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
Update to fade trails you could try something like Moving On Curves example. Notice noStroke(); is called in setup() and
fill(0, 2);
rect(0, 0, width, height);
render a faded out (alpha=2) rectangle ?
You could do something similar:
let ppp = [];
let trailsLayer;
function setup() {
createCanvas(400, 400);
background(255);
// make a new graphics layer for trails
trailsLayer = createGraphics(400, 400);
trailsLayer.noStroke();
// set translucent fill for fade effect
trailsLayer.fill(255, 25);
for (let i = 0; i < 3; i++) {
let p = new Particle();
ppp.push(p);
}
}
function draw() {
background(220);
// fade out trail layer by rendering a faded rectangle each frame
trailsLayer.rect(0, 0, width, height);
// render the trails layer
image(trailsLayer, 0, 0);
for (let i = 0; i < ppp.length; i++) {
ppp[i].display();
ppp[i].update();
}
}
function Particle() {
this.pv = createVector(random(width), random(height));
let rndV = p5.Vector.random2D();
this.spdV = rndV.mult(random(1, 3));
this.update = function() {
this.pv.add(this.spdV);
// reset at bounds
if(this.pv.x > width){
this.pv.x = 0;
}
if(this.pv.y > height){
this.pv.y = 0;
}
if(this.pv.x < 0){
this.pv.x = width;
}
if(this.pv.y < 0){
this.pv.y = height;
}
// render trails
trailsLayer.push();
trailsLayer.fill(0);
trailsLayer.noStroke();
trailsLayer.ellipse(this.pv.x, this.pv.y, 10);
trailsLayer.pop();
}
this.display = function() {
fill(30);
ellipse(this.pv.x, this.pv.y, 30);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
For the sake of completeness here's a version using the history vector array, but limiting that to a set size and reusing vectors allocated once (instead making new ones continuously):
let ppp = [];
function setup() {
createCanvas(400, 400);
noStroke();
for (let i = 0; i < 3; i++) {
let p = new Particle();
ppp.push(p);
}
}
function draw() {
background(220);
for (let i = 0; i < ppp.length; i++) {
ppp[i].display();
ppp[i].update();
}
}
function Particle() {
this.pv = createVector(random(width), random(height));
// limit number of history vectors
this.historySize = 24;
this.history = new Array(this.historySize);
// pre-allocate all vectors
for(let i = 0 ; i < this.historySize; i++){
this.history[i] = this.pv.copy();
}
let rndV = p5.Vector.random2D();
this.spdV = rndV.mult(random(1, 6));
this.update = function() {
this.pv.add(this.spdV);
this.resetBounds();
this.updateHistory();
};
this.updateHistory = function(){
// shift values back to front by 1 (loop from last to 2nd index)
for(let i = this.historySize -1; i > 0; i--){
// copy previous to current values (re-using existing vectors)
this.history[i].set(this.history[i-1].x, this.history[i-1].y);
}
// finally, update the first element
this.history[0].set(this.pv.x, this.pv.y);
};
this.resetBounds = function(){
// reset at bounds
if(this.pv.x > width){
this.pv.x = 0;
}
if(this.pv.y > height){
this.pv.y = 0;
}
if(this.pv.x < 0){
this.pv.x = width;
}
if(this.pv.y < 0){
this.pv.y = height;
}
};
this.display = function() {
fill(30);
ellipse(this.pv.x, this.pv.y, 30);
for (let i = 0; i < this.historySize; i++) {
let trail = this.history[i];
// fade trails
let alpha = map(i, 0, this.historySize -1, 192, 0);
fill(30, alpha);
ellipse(trail.x, trail.y, 10);
}
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
I'm trying to set up a feather-looking particle system in processing. I am trying to base it off of some code I found on OpenProcessing. When I copy and paste the code into processing (using Java) I get an error saying "expecting SEMI, found 'points'.
I figured this was maybe because the code uses var and not int indicating that this would be Javascript code instead. So I switched the processing mode to p5.js, and it runs but the browser that opens is just a blank white screen.
Any help on getting this to run would be appreciated! Thanks!
The code is below:
var points = [];
var painting = false;
var strokeNumber = 0;
var scl = 6;
var cols, rows;
var inc = 0.1;
var zOff = 0;
var particles = [];
var flowField = [];
var saturation = [];
function setup() {
createCanvas(windowWidth, windowHeight);
// createCanvas(400, 400);
background(0);
pixelDensity(5);
cols = floor(width / scl);
rows = floor(height / scl);
flowField = Array(cols * rows);
saturation = Array(width * height).fill(0);
greateForceField();
}
function mousePressed() {
painting = true;
strokeNumber++;
}
function mouseReleased() {
painting = false;
}
function updateForceField(){
var v = createVector(mouseX, mouseY);
var vPrev = createVector(pmouseX, pmouseY);
v.sub(vPrev);
v.setMag(1);
var i = floor(mouseX / scl);
var j = floor(mouseY / scl);
var index = i * rows + j;
flowField[index] = v;
}
function showForceField(){
for(var i = 0; i < cols; i++){
for(var j = 0; j < rows; j++){
var index = i * rows + j;
var v = flowField[index];
stroke(0,50);
strokeWeight(1);
push();
translate(i * scl, j * scl);
rotate(v.heading());
line(0,0,scl,0);
pop();
}
}
}
function greateForceField(){
var xOff = 0;
for(var i = 0; i < cols; i++){
var yOff = 0;
for(var j = 0; j < rows; j++){
yOff += inc;
var angle = noise(xOff, yOff, zOff) * TWO_PI;
var v = p5.Vector.fromAngle(angle);
v.setMag(.1);
var index = i * rows + j;
flowField[index] = v;
}
xOff += inc;
}
// zOff += inc * 0.1;
}
function draw() {
// background(255);
// showForceField();
if(painting){
updateForceField();
var idx = mouseY * width + mouseX;
if(saturation[idx] < 10){
var r = 1+sqrt(sq(mouseX-pmouseX)+sq(mouseY-pmouseY));
for(var a = 0; a < 100; a++){
var particle = new Particle(mouseX+random()*r*cos(random(TWO_PI)), mouseY+random()*r*sin(random(TWO_PI)));
particles.push(particle);
}
saturation[idx] ++;
}
}
particles.filter(particle => particle.spread > 0).map(particle => {
particle.update();
particle.show();
// particle.edges();
particle.follow();
})
particles.map((particle, idx) => {
if(particle.spread <= 0){
particles.splice(idx,1);
}
});
}
function Particle(x,y){
this.pos = createVector(x,y);
// this.color = color(245, 225, 50);
// this.color = color(145, 225, 192);
this.color = color(255);
this.spread = 127;
this.spreadInc = this.spread/100;
this.prevPos = this.pos.copy();
this.vel = p5.Vector.random2D();
this.acc = createVector(0,0);
this.maxSpeed = 2;
this.update = function(){
this.spread -= this.spreadInc;
this.vel.add(this.acc);
this.vel.limit(this.maxSpeed);
this.pos.add(this.vel);
this.acc.mult(0);
}
this.applyForce = function(force){
this.acc.add(force);
}
this.follow = function(){
var i = floor(this.pos.x / scl);
var j = floor(this.pos.y / scl);
var index = i * rows + j;
var force = flowField[index];
this.applyForce(force);
}
this.show = function(){
stroke(red(this.color),green(this.color),blue(this.color),this.spread);
strokeWeight(.3*this.spread/127);
// point(this.pos.x, this.pos.y);
line(this.pos.x, this.pos.y, this.prevPos.x, this.prevPos.y);
this.updatePrev();
}
this.updatePrev = function(){
this.prevPos = this.pos.copy();
}
this.edges = function(){
if(this.pos.x > width) {
this.pos.x = 0;
this.updatePrev();
}
if(this.pos.x < 0){
this.pos.x = width;
this.updatePrev();
}
if(this.pos.y > height){
this.pos.y = 0;
this.updatePrev();
}
if(this.pos.y < 0) {
this.pos.y = height;
this.updatePrev();
}
}
}
This looks like javascript waaay more than java. I'm not exactly a buff in these matters, but... are you trying to run javascript as java?
If you are using the Processing IDE, look in the upper right corner. Do you see the word "Java" ?
Like this:
If this is the case, you might want to consider installing p5.js :
Click here and choose "Add mode":
Now search for p5.js and install it:
Now your code will compile. I'm not saying it'll work, though, but your current problem will be behind you. Have fun!