I am using javascript with p5.js framework. I created an offscreen graphics buffer. Now I want to clear part of that buffer (so it becomes invisible again). What is the best way to do it?
Right now I can only achieve that by direct changing of the alpha value for every pixel I need.
Here is a MCVE:
// sketch.js, requires p5.js
function setup() {
createCanvas(100,100);
background(0);
let mymap = createGraphics(100,100);
mymap.fill(255);
mymap.rect(20,20,40,40);
mymap.loadPixels();
for (let i = 23; i < 37; i++) {
for (let j = 23; j < 37; j++) {
mymap.pixels[400*i+4*j+3] = 0;
}
}
mymap.updatePixels();
image(mymap,0,0);
}
Some side-notes:
1) there is a tiledmap.js library to p5.js, but I am still reading into it's source code and right now it doesn't look like they got 1 buffer for the entire tilemap.
2) there is a clear function to clear the canvas graphics, it does clear things, but it clears everything on the given buffer.
You should be able to use the set() function. More info can be found in the reference.
I would expect something like this to work:
const transparency = createGraphics(20, 20);
mymap.set(30, 30, transparency);
mymap.updatePixels();
Unfortunately, this seems to only set a single pixel. This smells like a bug to me, so I've filed this bug on GitHub.
Like you've discovered, you can also set the pixel values directly. This might be simpler than what you currently have:
let mymap;
function setup() {
createCanvas(100,100);
mymap = createGraphics(100,100);
mymap.fill(255, 0, 0);
mymap.rect(20,20,40,40);
for(let y = 30; y < 50; y++){
for(let x = 30; x < 50; x++){
mymap.set(x, y, color(0, 0, 0, 0));
}
}
mymap.updatePixels();
}
function draw() {
background(0, 255, 0);
image(mymap,0,0);
}
Note that there's a tradeoff: this code is simpler, but it's also slower.
Related
I made a project called "pixel paint" by javascript with p5js library, but when I run it, that project ran too slow. I don't know why and how to make it run faster. And here is my code:
let h = 40, w = 64;
let checkbox;
let scl = 10;
let painting = new Array(h);
let brush = [0, 0, 0];
for(let i = 0; i < h; i++) {
painting[i] = new Array(w);
for(let j = 0; j < w; j++) {
painting[i][j] = [255, 255, 255];
}
}
function setup() {
createCanvas(w * scl, h * scl);
checkbox = createCheckbox('Show gird line', true);
checkbox.changed(onChange);
}
function draw() {
background(220);
for(let y = 0; y < h; y++) {
for(let x = 0; x < w; x++) {
fill(painting[y][x]);
rect(x * scl, y * scl, scl, scl);
}
}
if(mouseIsPressed) {
paint();
}
}
function onChange() {
if (checkbox.checked()) {
stroke(0);
} else {
noStroke();
}
}
function paint() {
if(mouseX < w * scl && mouseY < h * scl) {
let x = floor(mouseX / scl);
let y = floor(mouseY / scl);
painting[y][x] = brush;
}
}
<!--Include-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Is there a solution to make my project run faster?
The code you have is easy to read.
It might not be worth optimising at this stage as it would make the code potentially needlessly more complex/harder to read and change in the future.
If you want to learn about different ways you could achieve the same thing I can provide a few ideas, though, for your particular use case int terms of performance might won't make a huge difference:
Instead of using the painting as a nested [w][h] array you could use a flat [w * h] array and use a single for loop instead of a nested for loop. This would be somewhat similar to using pixels[]. (You can convert x,y to an index (index = x + (y * width)) and the other way around(x = index % width, y = floor(index / width))
You could in theory use a p5.Image, access pixels[] to draw into it and render using image() (ideally you'd get lower level access to the WebGL renderer to enable antialiasing if it's supported by the browser). The grid itself could be a texture() for a quad where you'd use vertex() to specify not only x,y geometry positions, but also u, v texture coordinates in tandem with textureWrap(REPEAT). (I posted an older repeat Processing example: the logic is the same and syntax is almost identical)
Similar to the p5.Image idea, you can cache the drawing using createGraphics(): e.g. only update the p5.Graphics instance when the mouse is dragged, otherwise render the cached drawing. Additionally you can make use of noLoop()/loop() to control when p5's canvas gets updated (e.g. loop() on mousePressed(), updated graphics on mouseMoved(), noLoop() on mouseReleased())
There are probably other methods too.
While it's good to be aware of techniques to optimise your code,
I strongly recommend not optimising until you need to; and when you do
use a profiler (DevTools has that) to focus only the bits are the slowest
and not waste time and code readability on part of code where optimisation
wouldn't really make an impact.
I'm trying to generate a grid of points on the canvas described by vectors in order to make a flow field. I generate the vectors in a nested loop, then push them to a list, and finally attempt to draw them. However, when I attempt to draw them the .x and .y attributes aren't recognised. I think this is because the list of vectors is empty/only has one entry in it and I can't work out why. Apologies if this is a simple problem - this is my first time using javascript and p5.js. My code is shown below, it should generate a uniform grid of points.
let width = 600;
let height = 600;
let points = [];
function setup() {
createCanvas(width, height);
background(30);
let density = 50;
let spacing = width / density;
for (var x = 0; x < width; x += spacing); {
for (var y = 0; y < height; y += spacing); {
var p = createVector(x, y)
points.push(p)
}
}
}
function draw() {
noStroke();
fill(255);
for (var i = 0; i < points.length; i++); {
circle(points[i].x, points[i].y, 1);
}
}
EDIT: My code is definitely generating one vector, but only one for some reason. So I believe the issue is the for loops not executing correctly.
Your for loop syntax is incorrect. There should not be a semicolon after the closing parenthesis and the opening curly brace:
// !
for (var i = 0; i < points.length; i++); {
circle(points[i].x, points[i].y, 1);
}
You will need to fix each of your for loops.
I am working on a procedural terrain generator, but the 3d Map is constantly morphing and changing, calling for at least 4d noise (5d if I need to make it loop). I haven't found a good perlin/simplex noise library that will work in this many dimensions, so I thought this would be a good time to learn how those algorithms work. After starting to make my own "perlin" noise, I found a large problem. I need to get a psudo random value based on the nD coordinates of that point. So far I have found solutions online that use the dot product of a single point and a vector generated by the inputs, but those became very predictable very fast (I'm not sure why). I then tried a recursive approach (below), and this worked ok, but I got some weird behavior towards the edges.
Recursive 3d randomness attempt:
function Rand(seed = 123456, deg = 1){
let s = seed % 2147483647;
s = s < 1 ? s + 2147483647 : s;
while(deg > 0){
s = s * 16807 % 2147483647;
deg--;
}
return (s - 1) / 2147483646;
}
function DimRand(seed, args){
if(args.length < 2){
return Rand(seed, args[0]);
}else{
let zero = args[0];
args.shift();
return DimRand(Rand(seed, zero), args);
}
}
var T = 1;
var c = document.getElementById('canvas').getContext('2d');
document.getElementById('canvas').height = innerHeight;
document.getElementById('canvas').width = innerWidth;
c.width = innerWidth;
c.height = innerHeight;
var size = 50;
function display(){
for(let i = 0; i < 20; i ++){
for(let j = 0; j < 20; j ++){
var bright = DimRand(89,[i,j])*255
c.fillStyle = `rgb(${bright},${bright},${bright})`
c.fillRect(i*size, j*size, size, size);
}
}
T++;
}
window.onmousedown=()=>{display();}
And here is the result:
The top row was always 1 (White), the 2d row and first column were all 0 (Black), and the 3d row was always very dark (less than ≈ 0.3)
This might just be a bug, or I might have to just deal with it, but I was wondering if there was a better approach.
So I’m making a project in p5.js which aim to reproduce the Matrix rain code effect.
It ran pretty smooth until I decided to load a custom font.
When I did, my framerate dropped considerably. And it’s the case for every custom font that I try to load (only when I use the loadFont function; if I use textFont and some default font it doesn’t seem to slow down)
Of course, I use the loadFont function in the preload function, and textFont in setup, so I don’t know if this is a known thing ? How could I avoid this ?
Here is a Minimum Viable Example of the problem :
var characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$+-*/÷=%\"'#&_(),.;:?!\\|{}<>[]^~ "
var font;
var spacing_width = 10;
var spacing_height = 20;
function preload() {
font = loadFont('matrix_font.otf');
}
function setup() {
createCanvas(window.innerWidth, window.innerHeight);
frameRate(60);
background(0);
textFont(font);
fill(255)
}
var lastUpdate;
function draw()
{
background(0);
var timeSinceLastUpdate = new Date().getTime() - lastUpdate;
print(timeSinceLastUpdate + "ms") // prints time elapsed between each frame
lastUpdate = new Date().getTime();
for(var i = 0; i < 40; i++)
for(var j = 0; j < 40; j++)
text(characters[int(random(0, characters.length))], j * spacing_width, i * spacing_height)
}
This code prints in the console values around 40-50ms, but when I comment out the line textFont(font); the values are around 10-20ms
I'm using this font but I have the problem with every font that I try to load
See https://github.com/processing/p5.js/issues/3435 for a discussion of this issue
I drawed a grid based system on canvas using PIXI.js.
I'm trying to animate the thing, first each particle position.y is -200, then using Tween.js I'm trying to make them fall.
I change the position to the correct position, which is particle._y.
As you notice you will see after falling there are some empty spaces and CPU is over heating.
http://jsbin.com/wojosopibe/1/edit?html,js,output
function animateParticles() {
for (var k = 0; k < STAGE.children.length; k++) {
var square = STAGE.children[k];
new Tween(square, 'position.y', square._y, Math.floor(Math.random() * 80), true);
}
}
I think I'm doing something wrong.
Can someone please explain me what I'm doing wrong and why there are some empty spaces after falling?
The reason for the empty spaces is that some of your animations are not starting. The cause is in this line:
new Tween(square, 'position.y', square._y, Math.floor(Math.random() * 80), true);
Looking at your function definition for Tween.js, I see this:
function Tween(object, property, value, frames, autostart)
The fourth parameter is frames. I'm assuming this is the number of frames required to complete the animation.
Well your Math.floor function willl sometimes return zero, meaning the animation will have no frames and won't start!!
You can fix this by using math.ceil() instead. This way there will always be at least 1 frame for the animation:
new Tween(square, 'position.y', square._y, Math.ceil(Math.random() * 80), true);
Now, as for performance, I would suggest setting this up differently...
Animating all those graphics objects is very intensive. My suggestion would be to draw a single red square, and then use a RenderTexture to generate a bitmap from the square. Then you can add Sprites to the stage, which perform WAY better when animating.
//Cretae a single graphics object
var g = new PIXI.Graphics();
g.beginFill(0xFF0000).drawRect(0, 0, 2, 2).endFill();
//Render the graphics into a Texture
var renderTexture = new PIXI.RenderTexture(RENDERER, RENDERER.width, RENDERER.height);
renderTexture.render(g);
for (var i = 0; i < CONFIG.rows; i++) {
for (var j = 0; j < CONFIG.cols; j++) {
var x = j * 4;
var y = i * 4;
//Add Sprites to the stage instead of Graphics
var PARTICLE = new PIXI.Sprite(renderTexture);
PARTICLE.x = x;
PARTICLE.y = -200;
PARTICLE._y = H - y;
STAGE.addChild(PARTICLE);
}
}
This link will have some more examples of a RenderTexture:
http://pixijs.github.io/examples/index.html?s=demos&f=render-texture-demo.js&title=RenderTexture