How to draw a fading object trail? Without background manipulation - p5.js

I have a simple ellipse, moving across the screen, is there any simple code I could implement to have this ellipse draw a trail behind it that has its alpha fade over time to a certain extent? I still want the trail visible in the end but less bright than the casting ellipse.

You could also do something like this:
let positions = [];
function draw(){
positions.push(mouseX);
positions.push(mouseY);
for(let i in positions){
let x = positions[i];
let y = positions[i + 1];
fill(255, 255 - i * 10); noStroke();
ellipse(mouseX, mouseY, x, y)
}
if(positions.length > 20){
positions.shift();
positions.shift();
}
}

Assuming the ellipse is the only thing being drawn then there is a simple solution which is to, instead of drawing a fully opaque background on each frame, draw a semi-transparent background:
function setup() {
createCanvas(windowWidth, windowHeight);
}
function draw() {
background(0, 35);
ellipse(mouseX, mouseY, 20, 20);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
However, there are some caveats, namely that for certain colors/transparency levels you will be left with permanent "ghost" of the trails due to some weird alpha blending math anomalies.

Related

Inverting colors within shape in Processing

I want to draw shapes which invert the color of the pixels underneath them.
I know similar effects can be achieved in black and white using by setting the blendMode() to DIFFERENCE. However this does not work for colours.
I have also tried to use filter(INVERT) but this affects the whole canvas rather than specific areas.
Is there any way to efficiently achieve these aims in Processing, hopefully with built-in functionality?
I'm not seeing why blendMode does not work for this:
function setup() {
createCanvas(windowWidth, windowHeight);
rectMode(CENTER);
noStroke();
noLoop();
}
function draw() {
background(0);
translate(width / 2, height / 2);
push();
fill(0, 255, 0);
rect(0, 0, width * 0.6, height * 0.6);
pop();
push();
blendMode(DIFFERENCE);
fill(255);
ellipse(width * 0.3, height * 0.3, 100, 100);
pop();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
Well you could probably do:
color col = get(x, y);
color colInv = color(255 - col.r, 255 - col.g, 255 - col.b, 255 - col.a);
Not sure if you can do 255 - col all at once. Also I'm not sure if this would actually work, but it should...
Also get(x, y) takes a while, so you can also do it with the pixel array The coding train has a nice video on the pixel array.

How do I use the X and Y values from an object from touches in P5js?

This is my first week playing with P5js and Processing so be gentle.
I've got the following P5js code where I'm looking at touch values. By pressing multi-points on a touch screen brings up more circles anchored to a central position.
What I'd like to know is how do I call the x value for object 1 that's printed in the console? or y value for object 0? how would i state them in the code?
What I'd like to do is use these values to change the dimensions of a shape in the middle of the screen, like something in object 0 driving the height of the shape or object 1 on the the x or y values driving strokeWeight or colour . However I'm in my first week and completely lost to how to use these values in the code.
function setup() {
createCanvas(windowWidth, windowHeight);
background(200);
}
function draw() {
background(255);
fill(255, 0, 0);
strokeWeight(0)
for (var i = 0; i < touches.length; i++) {
fill(255);
strokeWeight(3)
ellipse(touches[i].x, touches[i].y, 50, 50);
line(touches[i].x, touches[i].y, windowWidth / 2, windowHeight / 2);
fill(255);
ellipse(windowWidth / 2, windowHeight / 2, 10, 10);
print(touches);
}
}
// do this prevent default touch interaction
function mousePressed() {
return false;
}
document.addEventListener('gesturestart', function(e) {
e.preventDefault();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.2/p5.min.js"></script>
or Sketch file is here
any help would be muchly muchly appreciated, i'm a newb trying to find my way
There is only one mistake in it which I found till now, the color of the background and the fill colors are the same! you may change them to actually see the shapes, lines etc.

PGraphics + noSmooth() + alpha = drawing artifacts

Note: I also asked this question on the Processing forum here.
I have this sample code:
PGraphics pg;
void setup() {
size(400, 500);
pg = createGraphics(width, height);
pg.noSmooth();
pg.beginDraw();
pg.background(0, 0, 255);
pg.endDraw();
}
void draw() {
if (mousePressed) {
pg.beginDraw();
pg.stroke(255, 254);
pg.point(mouseX, mouseY);
pg.endDraw();
}
image(pg, 0, 0, width, height);
}
I would expect this code to show a point wherever the user presses the mouse. Instead, I am only able to see points in a couple rectangular areas:
If I remove the call to pg.noSmooth() or if I remove the alpha value in the pg.stroke() call, then it works fine:
If I replace the pg.point() call with pg.ellipse() or pg.rect() then it also works fine.
It seems like the combination of using a PGraphics, the noSmooth() function, the point() function, and an alpha value results in this buggy behavior. I’ve tried in Processing 3.3 and Processing 3.5.2 and I see the same behavior in both.
Am I missing something obvious?
After a wee bit of digging up turns out the JAVA2D renderer draws a point as a diagonal line(line(x, y, x + EPSILON, y + EPSILON);) with a very very very small spacing (static final float EPSILON = 0.0001f;). My guess is this particular configuration the lack aliasing might mean both points of this diagonal line land on the same pixel and end up not being rendered on the top right area which. Why that area and how come this small distance I don't know, but it sounds a bit like the headaches Jakub Valtar and Andres Colubri had to deal with.
FWIW here's a hacky workaround: using a larger distance that does get rendered with transparency and no aliasing:
PGraphics pg;
void setup() {
size(400, 500);
noSmooth();
pg = createGraphics(width/20, height/20);
pg.beginDraw();
// just for debug purposes: rectangle with edge
pg.fill(0, 0, 255);
pg.rect(0,0,pg.width-1,pg.height-1);
pg.stroke(255,255,255, 128);
pg.endDraw();
}
void pointNoSmooth(PGraphics pg, float x,float y){
pg.beginShape();
pg.vertex(x,y);
pg.vertex(x + 0.75,y);//any less than 0.75 distance between vertices and there's nothing to render with aliasing
pg.endShape();
}
void draw() {
background(255);
if (mousePressed) {
pg.beginDraw();
pointNoSmooth(pg,mouseX,mouseY);
pg.endDraw();
}
// render upscaled
image(pg, 0, 0, width, height);
// render small preview in TL corner
image(pg,0,0);
}
Notice that I've set the PGraphics resolution 20 times smaller, then drawn it upscaled so it's easier to see where the pixels land on the PGraphics. I'm not scaling the mouseX,mouseY coordinates though, so you'll need to draw in the small top left preview when testing. That 0.75 distance does the trick: from what I've tested, anything smaller than 0.7499995 starts acting buggy again.

Natural Color Mixing in Processing

I am trying to naturally mix green tint on one photo (elephant) and red tint on another photo (ship) by placing the two opaque and tinted photos on top of one another. While both photos appear with equal prominence, when the program is run and both colors can be observed, the red tint (because it is the last tint applied) is more dominant and there is no discernable yellow color in the image no matter which tint is applied first. If anyone knows how to naturally mix colors in processing I would love to hear your advice. I also do not want to simply apply a yellow tint as the last tint. Thanks!
PImage elephant;
PImage ship;
void setup(){
size(695,473);
elephant = loadImage("elephantRider.png");
ship = loadImage("ship.jpg");
ship.filter(OPAQUE);
elephant.filter(OPAQUE);
}
void draw(){
background(255);
tint(0,255,0, 127);
image(elephant, 0,0);
tint(255,0,0,127);
image(ship, 0,0);
}
Think of Processing as painting. What you're doing now is a little bit like drawing one color to the screen, and then drawing another color on top of the first color.
Instead, what you want to do is mix the two colors together before you draw the result to the screen. Think of this as mixing two paints together to make a new color.
You can do this by taking the average of the two colors, something like this:
PImage img;
void setup() {
size(695, 473);
img = loadImage("image.jpg");
}
void draw() {
background(255);
//green
tint(0, 255, 0, 127);
image(img, width/2, height/2, 50, 50);
float blendedRed = (0 + 255) / 2;
float blendedGreen = (255 + 0) / 2;
float blendedBlue = (0 + 0) / 2;
// blended
tint(blendedRed, blendedGreen, blendedBlue, 127);
image(img, mouseX, mouseY, 50, 50);
}
This example is a little contrived because the averages are pretty trivial, but this approach should work for any color.
If you want something more advanced, note that color averaging is a pretty advanced topic. Googling "color averaging" is probably a good start.

Processing.js : how to draw() only parts of the canvas to reduce cpu usage

Right now I have an html page that renders 20 canvases each with its own instance of a processing.js script. To decrease CPU usage, I have mouseover/out events to loop()/noLoop() only the graph the mouse is hovering over.
But I need to take this even further. The canvas right now is 300x400 where there is a 300x300 space that needs to dynamically draw all the time but there's a rectangle with a legend that is the rest of the space which really doesn't need to be redrawn using the framerate() of the rest.
Does any know of a way to essentially specify to the draw() function which pixels to redraw?
I'm pretty sure a 100x300 blank area is not going to add significantly to the computations. But you can of course pass to draw something like:
if(frameCount==1) [draw legend code];
or using if(frameCount % 30 == 0)... for a continuous but less frequent rendering.
Edit:
void setup(){
size(400,400);
noStroke();
background(255);
}
void draw(){
fill(255,255,255,50);
rect(0, 150, width, height-150);
if(frameCount%50 == 1) {
fill(255);
rect(0, 0, width, 150);
fill(0);
text("frame " + frameCount, width/2, 75);
}
fill(255,0,0);
ellipse(random(0, width), random(150, height), 10, 10);
}

Resources