What is the best way to add and remove text and shapes? - p5.js

I am drawing a circle and want to show some text when the mouse is inside the circle and have the text not shown when the mouse is outside.
The code below works to show the text when the mouse goes in but it seems getting rid of the text is much more difficult! I have looked at different p5 functions such as remove() but they do not do what I expect/want.
What is the right way to hide the text in this situation?
I imagine the same method would be used to also hide a shape too? For example if I were to have drawn a rectangle instead of showing some text, or is that handled differently?
var centerX = 400;
var centerY = 400;
var radius = 40;
function setup() {
var canvas = createCanvas(800, 800);
canvas.parent("Example");
background(200);
fill(204, 101, 192, 127);
ellipse(centerX, centerY, radius, radius);
}
function draw() {
mouseOver(radius, centerX, centerY);
}
function mouseOver(radius, centerX, centerY) {
var d = Math.sqrt(((mouseX - centerX) * (mouseX - centerX)) + ((mouseY - centerY) * (mouseY - centerY)));
if (d < radius) {
textSize(12);
text("Hello", 400, 400);
} else {
//remove the text
}
}

To remove part of a drawing it is very common to simply call background in draw and then redraw the entire canvas
Your code could be written like this:
var centerX;
var centerY;
var radius = 40;
function setup() {
var canvas = createCanvas(200, 200);
centerX = width/2;
centerY = height/2
}
function draw() {
background(200);
fill(204, 101, 192, 127);
ellipse(centerX, centerY, radius*2, radius*2);
mouseOver(radius, centerX, centerY);
}
function mouseOver(radius, centerX, centerY) {
var d = Math.sqrt(((mouseX - centerX) * (mouseX - centerX)) + ((mouseY - centerY) * (mouseY - centerY)));
if (d < radius) {
fill(0);
textSize(12);
text("Hello", centerX, centerY);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>

Related

Trying to use spirals to show an image in p5.js

I have an image which I am trying to show using a spiral with varying thickness depending upon the image pixel. But the thickness of my line/ellipses in spiral is not coming out correctly.
The image I am using:
The image I am getting
As you can see for whatever reason the bottom right quadrant is getting thick and nothing else, this is happening even if I use other images.
My code:
FG = '#222323';
BG = '#f0f6f0';
function preload() {
Img = loadImage('black.png');
}
function setup() {
createCanvas(500, 500);
Img.resize(500, 500);
background(BG);
fill(FG);
colorMode(HSB, 255);
noLoop();
noStroke();
}
function draw() {
var r = width;
var a = 0;
while (r > 1) {
strokeWeight(1);
var x1 = r * cos(a);
var y1 = r * sin(a);
a += 0.01;
r -= 0.03;
var x2 = r * cos(a);
var y2 = r * sin(a);
let c = Img.get(x1, y1);
let b = brightness(c);
const val = map(b, 0, 255, 1, 10);
push();
translate(width/2, height/2);
ellipse(x1, y1, val, val);
pop();
}
}
The coordinates where you're sampling from aren't the same where you're rendering.
Because you use translate(), the ellipses themselves are offset based on the centre (but there's nothing changing where you're sampling from in the image (x1,y1).
You can offset x1,y1 to take the centre of the stage into account and then you sample from the right location (and draw offset to the centre):
FG = '#222323';
BG = '#f0f6f0';
function preload() {
// Img = loadImage('black.png');
Img = loadImage('data:image/png;base64,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');
}
function setup() {
createCanvas(500, 500);
Img.resize(500, 500);
background(BG);
fill(FG);
colorMode(HSB, 255);
noLoop();
noStroke();
}
function draw() {
var r = width;
var a = 0;
const centerX = width * 0.5;
const centerY = height * 0.5;
while (r > 1) {
var x1 = centerX + (r * cos(a));
var y1 = centerX + (r * sin(a));
a += 0.01;
r -= 0.03;
let c = Img.get(x1, y1);
let b = brightness(c);
const val = map(b, 0, 255, 1, 10);
ellipse(x1, y1, val, val);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.min.js"></script>
(Notice the central area where the source image has a black circle is what based on your mapping. You can reverse that as well if that's more interesting (const val = map(b, 0, 255, 10, 1);))

How to rotate an arc using trigonomentry p5js?

I want to create a spinning arc using trig, nothing fancy, just for it to rotate around the center point as if it was a full rotating circle cut in half. What I'm doing now results in a funky pacman, whereas I want it to remain a semicircle.
let angle = 0;
function setup() {
createCanvas(500, 500);
angleMode(DEGREES);
}
function draw() {
background(200);
let posX = 250;
let posY = 250;
let sizeW = 250;
let sizeH = 250;
let radius = 125 //I don't know what to put as my radius
let x = radius * cos(angle);
let y = radius * sin(angle);
fill(0);
arc(posX, posY , sizeW, sizeH, 270 + x, 90 + y);
angle++;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
You don't need any trigonometry because the arc() function takes care of that for you. You just need to make the start and end angles for the arc change based on the current value of angle. To get a semi-circle shape you want to start the arc ~90 degrees clockwise from angle and end your arc ~90 degrees counter clockwise from angle.
const posX = 250;
const posY = 250;
const sizeW = 250;
const sizeH = 250;
const radius = 125;
let angle = 0;
function setup() {
createCanvas(500, 500);
angleMode(DEGREES);
}
function draw() {
background(200);
fill(0);
arc(posX, posY , sizeW, sizeH, angle + 90, angle - 90);
angle++;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

p5.js Add a dissapearing ellipse trail to Lissajous curve line

I have a simple code that traces the Liss cruve with a small ellipse. I was wondering how to add a fading trail to this shape so it represents the cruve more clearly. I only know a bit about adding trails that follows the mouse but I'm not sure how to do this one.
Any help is appreciated, here is the code:
var t = 0;
function setup() {
createCanvas(500, 500);
fill(255);
}
function draw() {
background(0);
for (i = 0; i < 1; i++) {
y = 160*sin(3*t+PI/2);
x = 160*sin(1*t);
fill(255);
ellipse(width/2+x, height/2+y, 5, 5);
t += .01;
}
}
Try changing background(0) to background(0, 0, 0, 4) :)
Here is a working example:
https://editor.p5js.org/chen-ni/sketches/I-FbLFDXi
Edit:
Here is another solution that doesn't use the background trick:
https://editor.p5js.org/chen-ni/sketches/HiT4Ycd5U
Basically, it keeps track of each point's position and redraws them in every frame with updated alpha to create the "fading out" effect.
var t = 0;
var particleArray = [];
function setup() {
createCanvas(500, 500);
}
function draw() {
background(0);
y = width / 2 + 160 * sin(3 * t + PI / 2);
x = height / 2 + 160 * sin(1 * t);
particleArray.push(new Particle(x, y, t));
for (i=0; i<particleArray.length; i++) {
particleArray[i].show(t);
}
//keep the array short, otherwise it runs very slow
if (particleArray.length > 800) {
particleArray.shift();
}
t += .01;
}
function Particle(x, y, t) {
this.x = x;
this.y = y;
this.t = t;
this.show = function(currentT) {
var _ratio = t / currentT;
_alpha = map(_ratio, 0, 1, 0, 255); //points will fade out as time elaps
fill(255, 255, 255, _alpha);
ellipse(x, y, 5, 5);
}
}

HTML5 Canvas rotate trouble

I have a canvas element on which I am drawing a number of images and overlaying them with text. Unfortunately the problem requires that some of these images and corresponding text be rotated. Added to this the problem that there must be a corresponding background color on some of the images (images are simple outlines of desks for a floorplan)
Here is the function I have built to handle adding a single desk to the plan. The problem I am having is that when I use the rotate neither the text nor the background colors show up, while the appear correctly if I do not rotate the image, except that they are not rotated and the background fillRect() is oriented 90 degrees off.
function redrawDesk(desk, ctx, color) {
var rotate = desk.rotation == 90 || desk.rotation == 270;
if (rotate) {
ctx.save();
ctx.rotate(Math.PI / 2);
ctx.clearRect(desk.left, desk.top, desk.width, desk.height);
ctx.restore()
}
var img = $("#desk_" + desk.rowID)[0];
ctx.drawImage(img, desk.left, desk.top, desk.height, desk.width);
var x = desk.left;
var y = desk.top;
var h = desk.height;
var w = desk.width;
if (rotate) {
//ctx.save()
ctx.rotate(Math.PI / 2);
var tmp=x;
x=y;
y=tmp;
tmp=h;
h=w;
w=tmp;
}
ctx.textAlign = "center";
ctx.fillText(desk.deskID, x + w / 2,y + h/ 2);
if (color) {
ctx.fillStyle = color;
ctx.fillRect(x, y, w, h);
}
//ctx.restore();
if (rotate) {
ctx.rotate(Math.PI / -2);
}
}
Thank you
The main problem is that you are defining the desk and text as absolute coordinates.
Define objects in there local coordinate system. Eg the desk has a height and width but not a position. Its draw relative to its self (around 0,0)
const desk = {
w : 10, h : 10,
color : "blue",
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(-this.w / 2, -this.h / 2, this.w, this.h);
}
};
You can then position the desk into the world coordinate system (the canvas) by defining where its center will be.
function drawObj(obj, x, y) { // what to draw and where
ctx.setTransform(1,0,0,1,x,y); // Same as ctx.translate if 2D API is in default context
// The means you do not have to use ctx.save and
// ctx.restore in this function
obj.draw(); // draw desk
}
For a full transform its much the same
function drawObj(obj, x, y, scale, rotate) { // rotate is in radians
ctx.setTransform(scale, 0, 0, scale, x, y);
ctx.rotate(rotate);
obj.draw();
}
To add text you can add it as an object to the desk and draw it to its own local coordinate system
desk.name = {
text : "Desk",
color : "black",
font : "bold " + 20 + "px Calibri",
draw() {
ctx.font = this.font;
ctx.textAlign = "center";
ctx.fillStyle = this.color;
ctx.fillText(this.text, 0,0);
}
};
You can now draw the desk and name using the draw object function
drawObj(desk,200, 200, 1, Math.PI / 2); // Draw at 200,200 rotated 90deg CW
drawObj(desk.name, 200, 200, 1, Math.PI / 2); // draw the text rotated same and centered over desk
// Or if the text should be above and not rotated
drawObj(desk.name, 200, 200 - 30, 1, 0);
As the above functions use setTransform you may need to restore the transform. There are two ways to do this.
ctx.resetTransform(); // Check browser support for this call
ctx.setTransform(1,0,0,1,0,0); // same as above just does it manaly
In my code I tested to see if there is a rotation needed. If so I set a translate on the canvas to give me a new start point: ctx.translate(x, y); This allowed me to simplify my location settings for placing text and the background colors, which means they are showing up correctly. Here is the changed code to compare with the original:
if (rotate) {
ctx.save();
tmp = h;
h = w;
w = tmp;
ctx.translate(x, y);
}
if (color) {
ctx.fillStyle = color;
ctx.fillRect(0, 0, w, h);
}
ctx.font = "bold " + w / 2 + "px Calibri";
ctx.textAlign = "center";
ctx.fillStyle = "#000";
var c=ctx.canvas;
ctx.rotate(Math.PI / -2);
ctx.fillText(desk.deskID, 0-h/2, w/2); //x + w / 2, y + h / 2);
ctx.restore();

How can i register when text and shapes has been clicked on?

rect(x, y, 100, 100)
text("click here", 50, 50)
Is there a way to use mousePressed() so it registers when these two items have been clicked on?
It sounds like you're looking for collision detection. Specifically, you're probably looking for point-rectangle collision detection, to determine whether the mouse is inside a rectangle.
Google is your friend, but here's an example:
float rectX;
float rectY;
float rectWidth;
float rectHeight;
void setup() {
size(300, 300);
rectX = 50;
rectY = 100;
rectWidth = 200;
rectHeight = 100;
}
void draw() {
background(64);
if (mouseX > rectX && mouseX < rectX + rectWidth && mouseY > rectY && mouseY < rectY + rectHeight) {
fill(255, 0, 0);
}
else {
fill(0, 255, 0);
}
rect(rectX, rectY, rectWidth, rectHeight);
}
Shameless self-promotion: here is a tutorial on collision detection in Processing.

Resources