creating squares through points stored in an array, p5.js - javascript

So, after the user has created a square, i want to create small squares within that big square,
for every line, i am running a loop where loop starts at point 0 and goes till point 1 , The problem i am facing right now is that, when the loop has created square from x of point 0 to x of point 1, i want to +1 in y of point 0 and run the same loop, i am confused on how to do that. maybe nested for loop.
Thank you.
function setup() {
createCanvas(400, 400);
}
var pts = [];
var bts = [];
function mousePressed()
{
if (pts.length == 4) {
pts = [];
}
pts.push([mouseX, mouseY])
if (bts.length == 4) {
bts = [];
}
bts.push([mouseX, mouseY])
}
function draw() {
background(220);
// draw the lines between the points
for (var i=0; i < pts.length-1; ++i) {
line(pts[i][0], pts[i][1], pts[i+1][0], pts[i+1][1]);
}
var close = pts.length == 4;
if (pts.length == 4) {
// draw line from 1st point to at point
line(pts[pts.length-1][0], pts[pts.length-1][1], pts[0][0], pts[0][1]);
}
else if (pts.length > 0) {
// draw a rubber line from last point to the mouse
line(pts[pts.length-1][0], pts[pts.length-1][1], mouseX,mouseY);
}
if (pts.length==4)
{ text("value of point 0 : "+pts[0][0],255,200);
text("value of point 1 : "+pts[1][0],255,255);
for (var j =1 ; j<=(pts[1][0]-pts[0][0]);j++)
{
square((pts[0][0]+j), pts[0][1],1);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.js"></script>

To define a rectangle, it is sufficient to draw a diagonal line. The 4 points of the rectangle, can be calculated by the 2 points of the diagonale:
// rectangle points
let rpts = [pts[0], [pts[1][0], pts[0][1]], pts[1], [pts[0][0], pts[1][1]]]
// draw rectangle
for (var i=0; i < rpts.length; ++i) {
line(rpts[i][0], rpts[i][1], rpts[(i+1) % rpts.length][0], rpts[(i+1) % rpts.length][1]);
}
The inner rectangles have to be draw in 2 nested loops. But you've to calculate the minimum and maximum coordinates. Note possibly the first point is somewhere at the bottom right and the 2nd point is at the top left:
let x0 = min(pts[0][0], pts[1][0]);
let x1 = max(pts[0][0], pts[1][0]);
let y0 = min(pts[0][1], pts[1][1]);
let y1 = max(pts[0][1], pts[1][1])
for (var x = x0; x < x1; x += 5) {
for (var y = y0; y < y1; y +=5) {
square(x, y, 4);
}
}
noLoop stops Processing from continuously executing the code within draw() and loop() restart the continuously executing.
Call noLoop when the inner triangles are draw and call loop() when a mouse button is pressed.
See the example:
function setup() {
createCanvas(400, 400);
}
var pts = [];
var bts = [];
function mousePressed()
{
if (pts.length == 2) {
pts = [];
}
pts.push([mouseX, mouseY])
loop()
}
function draw() {
background(220);
if (pts.length == 2) {
// rectangle points
let rpts = [pts[0], [pts[1][0], pts[0][1]], pts[1], [pts[0][0], pts[1][1]]]
// draw rectangle
for (var i=0; i < rpts.length; ++i) {
line(rpts[i][0], rpts[i][1], rpts[(i+1) % rpts.length][0], rpts[(i+1) % rpts.length][1]);
}
}
else if (pts.length > 0) {
// draw a rubber line from last point to the mouse
line(pts[pts.length-1][0], pts[pts.length-1][1], mouseX,mouseY);
}
let c = color(255, 204, 0);
fill(c);
if (pts.length==2) {
let x0 = min(pts[0][0], pts[1][0]);
let x1 = max(pts[0][0], pts[1][0]);
let y0 = min(pts[0][1], pts[1][1]);
let y1 = max(pts[0][1], pts[1][1])
for (var x = x0; x < x1; x += 5) {
for (var y = y0; y < y1; y +=5) {
square(x, y, 4);
}
}
noLoop()
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.js"></script>

i used a nested loop, and created another variable (y) which had value of y of point 0 and in parent loop i was increment-ing inside the variable (y) and in child loop i was running the loop for square, Thank you.
function setup() {
createCanvas(400, 400);
}
var pts = [];
var bts = [];
function mousePressed()
{
if (pts.length == 4) {
pts = [];
}
pts.push([mouseX, mouseY])
}
function draw() {
background(220);
// draw the lines between the points
for (var i=0; i < pts.length-1; ++i) {
line(pts[i][0], pts[i][1], pts[i+1][0], pts[i+1][1]);
}
var close = pts.length == 4;
if (pts.length == 4) {
// draw line from 1st point to at point
line(pts[pts.length-1][0], pts[pts.length-1][1], pts[0][0], pts[0][1]);
}
else if (pts.length > 0) {
// draw a rubber line from last point to the mouse
line(pts[pts.length-1][0], pts[pts.length-1][1], mouseX,mouseY);
}
let c = color(255, 204, 0);
fill(c);
if (pts.length==4)
{
for (var k = 0; k<=pts[3][1]-pts[0][1];k+=5)
{
if (k==pts[3][1]-pts[0][1])
{
noLoop()
}
var y = pts[0][1]+k;
for (var j =1 ; j<=(pts[1][0]-pts[0][0]);j+=5)
{
square((pts[0][0]+j), y,4);
}
}
}
}
I think there are more efficient methods, please answer, if you know.

Related

How to create a smooth transition p5.js with key function?

I'm trying to achieve that, everytime you type a different letter key, the lines of the letters 'merge' into eachother instead of just 'jumping' to the next letter like it's doing now. I'm looking into the lerp() function but i'm not sure how to apply this to my code. Can someone help me into the right direction? This is what i have untill now:
var redtown;
var fontSize = 500;
var myArray;
var r = 3;
function preload(){
redtown = loadFont('redtown.otf');
}
function setup(){
createCanvas(windowWidth,windowHeight);
textFont(redtown);
textSize(fontSize);
}
function draw(){
background(0);
myArray = redtown.textToPoints(key, width/2, 500, fontSize, {
sampleFactor:0.5
});
// text(key, width/2, height/2 );
for (var i = 0; i < myArray.length; i++) {
// ellipse(myArray[i].x, myArray[i].y, 10, 10)
push();
translate(myArray[i].x, myArray[i].y);
rotate(r);
r++;
stroke(255);
strokeWeight(1);
line(-10,-10,10,10,10);
frameRate(17);
pop();
}
}
Here is a snippet that transitions from one character to another by using textToPoints to get the points from the last two keys that have been pressed and then slides each point in the old character to its position in the new character.
It uses this formula to get the x and y positions of points along a line from the point in the old character to the point in the new character.
x = (1-t)*x+t*nextX;
y = (1-t)*y+t*nextY;
It also uses the spinning lines idea from the question to give the points some motion although it pins the line size to a constant.
rotate(r+=0.1);
line(-1,-1,1,1);
You can see it in action here Fonts Transition
var myFont;
var fontSize = 160;
var fontPoints =[];
var previousFontPoints = [];
var r = 0;
var oldKey = ' ';
function preload(){
myFont = loadFont('inconsolata.otf');
}
function setup(){
createCanvas(500, 500);
textFont(myFont);
textSize(fontSize);
frameRate(30);
stroke(255);
strokeWeight(1);
background(0);
}
function draw(){
if (oldKey != key){
previousFontPoints =
myFont.textToPoints(oldKey, width/10, height/2, fontSize, {
sampleFactor:1
});
oldKey = key;
fontPoints = myFont.textToPoints(key, width/10, height/2, fontSize, {
sampleFactor:1
});
t = 0.025;
}
t += .01;
if (fontPoints.length > 0 && t< 1.0){
background(0);
for (i = 0; i < fontPoints.length; i++){
let x = 0;
let y = 0;
// if we don't have enought points we will just float in from 0,0
let nextX = 0;
let nextY = 0;
push();
if (previousFontPoints.length > i){
x = previousFontPoints[i].x;
y = previousFontPoints[i].y;
// in case the new array does not have enough points
nextX = x;
nextY = y;
}
if (fontPoints.length > i){
nextX = fontPoints[i].x;
nextY = fontPoints[i].y;
}
x = (1-t)*x+t*nextX;
y = (1-t)*y+t*nextY;
translate(x, y);
rotate(r+=0.1);
line(-1,-1,1,1);
pop();
}
}
}

Changing color of intersecting area of squares

I am working on a project these days. My goal is to change the color of the intersecting areas of the two squares. I have written the code which detects whenever two squares intersect but I cant figure out how to change the color of the intersecting area. Kindly help me with this.
var sketch = function (p) {
with(p) {
let squares = [];
let dragObject = null; // variable to hold the object being dragged
p.setup = function() {
createCanvas(600, 520);
button1 = createButton("Alpha");
button2 = createButton("Bravo");
button3 = createButton("Charlie");
button4 = createButton("Delta");
button5 = createButton("Echo");
button6 = createButton("Foxtrot");
button7 = createButton("Golf");
button8 = createButton("Hotel");
button9 = createButton("India");
button10 = createButton("Juliet");
button1.mousePressed(doSomething);
};
p.draw = function() {
background(25, 240, 255);
// if a square is being dragged, update its position
if (this.dragObject != null) {
this.dragObject.position.x = mouseX;
this.dragObject.position.y = mouseY;
}
//draw all squares
for (let i = 0; i < squares.length; i++) {
let s = squares[i];
s.show();
}
for (let i = 0; i < squares.length; i++) {
for (let j = i + 1; j < squares.length; j++) {
if (i != j && squares[i].collides(squares[j])) {
squares[i].changecolor();
}
}
}
};
p.mousePressed = function () {
if (this.dragObject == null) {
//ask every square if they are being "hit"
for (let i = 0; i < squares.length; i++) {
let s = squares[i];
if (s.hitTest()) {
//if so, set the drag object as this square and return
this.dragObject = s;
return;
}
}
//no squares are hit, create a new square.
let square = new Square(mouseX, mouseY);
squares.push(square);
}
};
//mouse is released, release the current dragged object if there is one
p.mouseReleased = function () {
this.dragObject = null;
};
class Square {
constructor(InitialX, InitialY) {
this.w = 60;
this.h = 60;
this.position = {
x: InitialX,
y: InitialY
};
}
//basic test of mouse position against square position and if its inside the rectangle
hitTest() {
let x = mouseX - this.position.x;
let y = mouseY - this.position.y;
return (x > 0 && x < this.w) && (y > 0 && y < this.h);
}
show() {
fill(50);
rect(this.position.x, this.position.y, this.w, this.h);
}
collides(sqr) {
if (this.position.x < sqr.position.x + sqr.w &&
this.position.x + this.w > sqr.position.x &&
this.position.y < sqr.position.y + sqr.h &&
this.position.y + this.h > sqr.position.y) {
return true;
}
return false;
}
changecolor() {
fill(random(255), random(255), random(255));
background(200, 255, 100);
for (let i = 0; i < squares.length; i++) {
let s = squares[i];
s.show();
}
}
}
function doSomething() {
// fill(230, 170, 90);
// ellipse(random(600), random(410), 30, 30);
squares.pop();
}
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>
Lets think a bit how we could represent the intersecting area between two squares. Surely, one of the ways to do is simply to represent it as another rectangle, whose color we simply change based on the intercepting area. To draw a rectangle, we need to know the coordinates of the upper left corner, the width and the height. Therefore the challenge is to calculate those as we drag our squares around. This should be done in the draw() function. You already have the intersection check implemented, whats left is to calculate the new rectangle upper left point (newX, newY), width (newW) and height (newH).
In order to calculate the upper left corner, the width and height, we can add this to the block where we check for collision:
...
//block checking collision
if (i != j && squares[i].collides(squares[j])) {
squares[i].changecolor();
//set intersection color
fill(50);
//calculate parameters
newX = Math.max(squares[i].position.x, squares[j].position.x);
newY = Math.max(squares[i].position.y, squares[j].position.y);
newW = Math.min(squares[i].position.x + squares[i].w, squares[j].position.x + squares[j].w) - newX;
newH = Math.min(squares[i].position.y + squares[i].h, squares[j].position.y + squares[j].h) - newY;
//draw rectangle
rect(newX, newY, newW, newH);
}
Result:

How do you make a circle shaped button in p5.js?

I am making a p5.js project. In it I am generating a list (with 8 elements) and setting them to 1/0. Each one represents a bit (1,2,4,8,16,32,64,128) and if the element is 1, I add the index of the bit array.
For example i = 3, states[i] = 1, bit[i] = 8 so I add 8 to a number because the current state of that bit is 1.
Another thing is that it draws a circle that is red/green based on bit state.
Now that you know the basic idea, I want to add the ability for the user to press a circle to change its state (from 1 to 0 and from 0 to 1). I know how to change the state, but how do i test if the user has actually pressed the button (notice that the button is a circle)?
Here is my code so far:
var array = [0,1,0,1,1,1,1,1];
var values = [128,64,32,16,8,4,2,1];
function setup(){
//console.log(array);
createCanvas(600,600);
textStyle(BOLDITALIC);
textSize(50);
}
function draw(){
clear();
var a = calculate(array);
background(51);
fill(255);
text(a,250,500);
let crcl = 50;
let d = 20;
let r = d/2;
for (let i = 0; i < 8; i++){
}
for (let i = 0; i < 8; i++){
if (array[i] === 1){
fill(0,255,0);
circle(crcl, 50, d);
} else {
fill(255,0,0);
circle(crcl, 50, d);
}
crcl += 50;
}
}
function calculate(array){
let a = 0;
for (let i = 0; i < 8; i++){
if (array[i] === 1){
a += values[i];
}
}
return a;
}
My finished code for everyone who just wants to see the code!:
var array = [0,1,0,1,1,1,1,1];
var values = [128,64,32,16,8,4,2,1];
var positonsX = [50,100,150,200,250,300,350,400];
var crcl = 50;
var d = 20;
var r = d/2;
function setup(){
//console.log(array);
createCanvas(600,600);
textStyle(BOLDITALIC);
textSize(50);
}
function draw(){
clear();
let crcl = 50;
d = 20;
r = d/2;
a = calculate(array);
background(51);
fill(255);
text(a,250,500);
for (let i = 0; i < 8; i++){
if (array[i] === 1){
fill(0,255,0);
circle(crcl, 50, d);
} else {
fill(255,0,0);
circle(crcl, 50, d);
}
crcl += 50;
}
}
function calculate(array){
let a = 0;
for (let i = 0; i < 8; i++){
if (array[i] === 1){
a += values[i];
}
}
return a;
}
function mouseClicked(){
for (let i = 0; i < 8; i++){
if (dist(mouseX,mouseY,positonsX[i],50) <= d){
array[i] = 1 - array[i];
}
}
}
You can detect whether a point (in your case, mouseX, mouseY) is in a circle by comparing the distance between the point and the center of the circle, and comparing it to the radius of the circle. If the distance is less than the radius, then the point is inside the circle.
You can google "detect if point is inside circle" for a ton of results. Shameless self-promotion: this tutorials explains collision detection, including point-circle collision detection.
I'd check for mouseClicked (https://p5js.org/reference/#/p5/mouseClicked), and then mouseX, mouseY to see if a circle got clicked.
I had created this class to add circular buttons for one of my projects. Sharing a link to the sketch, might save others some time: p5.js web editor sketch
To check if the mouse pointer is within a circle:
Get the distance between the center of the circle and the mouse pointer
const distance = dist(circleX, circleY, mouseX, mouseY)
Mouse is within the circle if that distance is less that the circle's radius
const isInside = (distance < circleRadius)

How to avoid repeated?

Good day,
I am generating some circles with colors, sizes and positions. All of this things randomly.
But, my problem is that I do not want them to collide, so that no circle is inside another, not even a little bit.
The logic explained in detail within the code, I would like to know why the failure and why the infinite loop.
The important functions are:
checkSeparation and setPositions
window.addEventListener("load", draw);
function draw() {
var canvas = document.getElementById("balls"), // Get canvas
ctx = canvas.getContext("2d"); // Context
canvas.width = document.body.clientWidth; // Set canvas width
canvas.height = document.documentElement.scrollHeight; // Height
var cW = canvas.width, cH = canvas.height; // Save in vars
ctx.fillStyle = "#fff022"; // Paint background
ctx.fillRect(0, 0, cW, cH); // Coordinates to paint
var arrayOfBalls = createBalls(); // create all balls
setPositions(arrayOfBalls, cW, cH);
arrayOfBalls.forEach(ball => { // iterate balls to draw
ctx.beginPath(); // start the paint
ctx.fillStyle = ball.color;
ctx.arc(ball.x, ball.y, ball.radius, 0, (Math.PI/180) * 360, false); // draw the circle
ctx.fill(); // fill
ctx.closePath(); // end the paint
});
}
function Ball() {
this.x = 0; // x position of Ball
this.y = 0; // y position of Ball
this.radius = Math.floor(Math.random() * ( 30 - 10 + 1) + 10);
this.color = "";
}
Ball.prototype.setColor = function(){
for(var j = 0, hex = "0123456789ABCDEF", max = hex.length,
random, str = ""; j <= 6; j++, random = Math.floor(Math.random() * max), str += hex[random])
this.color = "#" + str;
};
function random(val, min) {
return Math.floor(Math.random() * val + min); // Random number
}
function checkSeparation(value, radius, toCompare) {
var min = value - radius, // Min border of circle
max = value + radius; // Max border of circle
// Why ? e.g => x position of circle + this radius it will be its right edge
for(; min <= max; min++) {
if(toCompare.includes(min)) return false;
/*
Since all the positions previously obtained, I add them to the array, in order to have a reference when verifying the other positions and that they do NOT collide.
Here I check if they collide.
In the range of:
[pos x - its radius, pos x + its radius]
*/
}
return true; // If they never collided, it returns true
}
function createBalls() {
var maxBalls = 50, // number of balls
balls = []; // array of balls
for(var j = 0; j < maxBalls; j++) { // create 50 balls
var newBall = new Ball(); // create ball
newBall.setColor(); // set the ball color
balls.push(newBall); //push the ball to the array of balls
}
return balls; // return all balls to draw later
}
function setPositions(balls, canvasW, canvasH) {
var savedPosX = [], // to save x pos of balls
savedPosY = []; // to save y pos of balls
for(var start = 0, max = balls.length; start < max; start++) {
var current = balls[start], // current ball
randomX = random(canvasW, current.radius), // get random value for x pos
randomY = random(canvasH, current.radius); // get random value for y pos
if(checkSeparation(randomX, current.radius, savedPosX)) {
current.x = randomX; // If it position, along with your radio does not touch another circle, I add the position
} else {
// start--; continue;
console.log("X: The above code causes an infinite loop");
}
if(checkSeparation(randomY, current.radius, savedPosY)) {
current.y = randomY;
} else {
// start--; continue;
console.log("Y: The above code causes an infinite loop");
}
}
}
body,html {
margin: 0; border: 0; padding: 0; overflow: hidden;
}
<canvas id="balls"></canvas>
In your code, you test possible collisions by means of arrays of already used x and y positions, but you never add new positions to these arrays. You also check the x and y coordinates separately, which means you are really testing a collision of a bounding box.
Two circles collide when the distance between their centres is smaller than the sum of their radii, so you could use:
function collides(balls, n, x, y, r) {
for (let i = 0; i < n; i++) {
let ball = balls[i];
let dx = ball.x - x;
let dy = ball.y - y;
let dd = dx*dx + dy*dy;
let rr = r + ball.radius;
if (dd < rr * rr) return true;
}
return false;
}
function setPositions(balls, canvasW, canvasH) {
for (let i = 0, max = balls.length; i < max; i++) {
let ball = balls[i],
r = ball.radius,
maxTries = 20;
ball.x = -canvasW;
ball.y = -canvasH;
for (let tries = 0; tries = maxTries; tries++) {
let x = random(canvasW - 2*r, r),
y = random(canvasH - 2*r, r);
if (!collides(balls, i, x, y, r)) {
ball.x = x;
ball.y = y;
break;
}
}
}
}
This is reasonably fast for 50 balls, but will be slow if you have more balls. In that case, some spatial data structures can speed up the collision search.
You must also guard against the case that no good place can be found. The code above gives up after 20 tries and moves the ball outside the visible canvas. You can improve the chances of placing balls by sorting the balls by radius and plaing the large balls first.
Finally, you add one hex digit too many to your random colour. (That for loop, where everything happens in the loop control is horrible, by the way.)

Can't make balls bounce back

I can't make the balls move in the opposite direction. Here is my code:
var xPositions = [random(0,400),random(0,400),random(0,400),random(0,400),random(0,400),random(0,400),random(0,400),random(0,400),random(0,400),random(0,400)];
var yPositions = [random(0,400),random(0,400),random(0,400),random(0,400),random(0,400),random(0,400),random(0,400),random(0,400),random(0,400),random(0,400)];
draw = function() {
background(250,250, 250);
var velocidad=5;
for (var i = 0; i < 10; i++) {
fill(230, 156, 156);
ellipse(xPositions[i], yPositions[i], 10, 10);
var d=1;
if(yPositions[i]>=400){
d= -1;
} else if(yPositions[i]<=0){
d = 1;
}
yPositions[i] = yPositions[i] + velocidad*d ;
}
};
You are setting d=1 in every iteration before you check the bounds.
Move
var d=1
out of the function or out of the loop.
Your problem is that you're not saving the direction d each ball is traveling. In your code, once a ball reaches the edge, it will go in the opposite direction. Until it's inside the bounds again, in that loop it'll change direction again.
Which leads to the main problem with your code, having two separate arrays of x and y coordinates instead of one array of ball objects. Something like
function Ball() {
this.x = random(0,400);
this.y = random(0,400);
this.direction = 1; // Or if you want to randomize: random(0,99)<50 ? -1 : 1;
this.velocity = 5; // Or if you want to randomize: random(1, 5)
}
Ball.prototype = {
draw:function() {
fill(230, 156, 156);
ellipse(this.x, this.y, 10, 10);
},
update:function() {
var newposition = this.y+this.direction*this.velocity;
if (newposition < 0 || newposition > 400) {
this.direction*=-1; // If outside of bounds, reverse position
}
this.y = this.y+this.direction*this.velocity;
}
};
Then you initiate your balls as such
var balls = [];
for (var i = 0; i < 10 ; i++){
balls.push(new Ball());
}
And in your main loop you just need to call balls[i].update() and balls[i].draw().
draw = function() {
for (var i = 0; i < balls.length; i++) {
balls[i].update();
balls[i].draw();
}
};
There are still lots of things to be improved, but here's the gist of OOP to get you started.

Categories

Resources