Avoid crossing of geometries - javascript

I would like to work with this code:
let vertices = [];
function setup() {
createCanvas(windowWidth, windowHeight);
let numberOfVertices = random(3, 11); //pick the number of points
for (let i = 0; i < numberOfVertices; i++) {
vertices.push(createVector(random(width), random(height))); //add a point to the list of points
}
}
function draw() {
background(220);
fill(255, 0, 0);
noStroke();
beginShape();
for (let i = 0; i < vertices.length; i++) {
vertex(vertices[i].x, vertices[i].y);//place each vertex
}
endShape();
}
* {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.3.1/lib/p5.js"></script>
Unfortunately, the geometries sometimes cross. How is it possible to avoid that?
Would be very thankful for help!

Calculate the center of the bounding box of the points and sort the points by the angle of the vector from the center to the point:
let ax = vertices.map(v => v.x);
let ay = vertices.map(v => v.y);
let cx = (Math.min(...ax) + Math.max(...ax)) / 2;
let cy = (Math.min(...ay) + Math.max(...ay)) / 2;
vertices.sort((a, b) => {
let v1 = p5.Vector.sub(a, createVector(cx, cy));
let v2 = p5.Vector.sub(b, createVector(cx, cy));
return Math.atan2(v1.y, v1.x) - Math.atan2(v2.y, v2.x);
});
In the following example, the lines that form the center point to the vertices are drawn to visualize the algorithm:
let vertices = [];
let cx, cy
function setup() {
createCanvas(windowWidth, windowHeight);
let numberOfVertices = random(3, 11); //pick the number of points
for (let i = 0; i < numberOfVertices; i++) {
vertices.push(createVector(random(width), random(height))); //add a point to the list of points
}
let ax = vertices.map(v => v.x);
let ay = vertices.map(v => v.y);
cx = (Math.min(...ax) + Math.max(...ax)) / 2;
cy = (Math.min(...ay) + Math.max(...ay)) / 2;
vertices.sort((a, b) => {
let v1 = p5.Vector.sub(a, createVector(cx, cy));
let v2 = p5.Vector.sub(b, createVector(cx, cy));
return Math.atan2(v1.y, v1.x) - Math.atan2(v2.y, v2.x);
});
}
function draw() {
background(220);
fill(255, 0, 0);
noStroke();
beginShape();
for (let i = 0; i < vertices.length; i++) {
vertex(vertices[i].x, vertices[i].y);//place each vertex
}
endShape();
stroke(255, 255, 255);
strokeWeight(3)
for (let i = 0; i < vertices.length; i++) {
line(cx, cy, vertices[i].x, vertices[i].y);
}
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.3.1/lib/p5.js"></script>
Demo

Related

How to make a grid of ellipses with subdivisions

I have created a circle with subdivisions (function CircleSubDivs) in p5.js and now want to generate a grid filled with those. Ideally the ellipse would also be disproportionate if the width and height of a tile is not the same, or if the amount of tiles were to be controlled by the mouse position the ellipse would move flexibly.
This was my inspiration
This is my code so far:
// let colors = [
// "#F48668 ",
// "#5D2E8C",
// "#F7F7F7"
// ];
function CircleSubDivs(x, y, size) {
let amount = 13;
let r = 360 / amount;
for (let j = 0; j < 10; j++) {
for (let i = 0; i < amount; i++) {
fill(random(255));
let s = map(j, 0, 8, width, 100);
arc(width / 2, height / 2, s, s, radians(r * i), radians(r * (i + 1)));
}
}
}
function setup() {
createCanvas(500, 500);
frameRate(1);
}
function draw() {
background("#0F0F0F");
noStroke();
let tilesX = 3;
let tilesY = 2;
let tileW = width / tilesX;
let tileH = height / tilesY;
for (let x = 0; x < tilesX; x++) {
for (let y = 0; y < tilesY; y++) {
CircleSubDivs(x * tileW, y * tileH, tileW, tileH);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
As you can see I have created a grid and tried to store my function for the ellipse with divisions in there, but it just shows me one single ellipse when I run it. I would really appreciate some help :)
Your parameters to your function are unused, so the function does the same thing every time.
You might try something like:
function drawSubdividedCircle(x, y, size, segments=13, layers=10) {
const r = 360 / segments;
for (let i = 0; i < segments; i++) {
for (let j = 0; j < layers; j++) {
fill(random(255));
const s = map(j, 0, layers, size, 0);
arc(
x + size / 2,
y + size / 2,
s,
s,
radians(r * i),
radians(r * (i + 1))
);
}
}
}
function setup() {
createCanvas(500, 500);
frameRate(1);
}
function draw() {
background("#0F0F0F");
noStroke();
const tilesX = 3;
const tilesY = 2;
const tileSize = width / tilesX;
for (let x = 0; x < tilesX; x++) {
for (let y = 0; y < tilesY; y++) {
drawSubdividedCircle(x * tileSize, y * tileSize, tileSize);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>

How to make a grid of moiré circles in p5.js?

I'm attempting to make a grid where every tile is filled with a single "moire-style" ellipse (a circle with circles inside).
function setup() {
createCanvas(500, 500);
ellipseMode(CENTER);
}
function draw() {
background(255);
noFill();
strokeWeight(3);
stroke(0);
let tilesX = 3;
let tilesY = tilesX;
let tileW = width / tilesX;
let tileH = height / tilesY;
for (let x = 0; x < tilesX; x++) {
for (let y = 0; y < tilesY; y++) {
moire(tilesX, tilesY, tileW);
}
}
function moire(x, y, size) {
for (var i = 1; i <= tileW; i = i + 8) {
ellipse(width / 2, height / 2, 2 * i, 2 * i);
translate(tilesX, tilesY);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
This is what I have in my p5.js sketch but the moire elements don't align to the tiles.
The following demo will create a grid of moire circles:
let _numRows = 3;
let _numCols = 3;
let _colGap = 30;
let _rowGap = 30;
function moire(x, y, size) {
for (var i = 1; i <= size; i = i + 8) {
ellipse(x, y, 2 * i, 2 * i);
translate(3, 3);
}
}
function setup() {
createCanvas(500, 500);
ellipseMode(CENTER);
}
function draw() {
background(255);
noFill();
strokeWeight(3);
stroke(0);
let _xOffset = width/5;
let _yOffset = height/5;
for (let j = 0; j < _numCols; j++) {
for (let k = 0; k < _numRows; k++) {
push();
moire(_xOffset + j * (100 + _colGap), _yOffset + k * (100 + _rowGap), 50);
pop();
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>

Small issues with an interactive project in p5.js / javascript

I need help with an interactive project in p5.js.
Here are the things that are still missing:
the clock hands should rotate
when the puzzle is solved "won" should be displayed and the timer should stop (Here I have a code part in my program but it doesn´t work)
the "moves" don´t count up when you click somewhere - only at the specific field
at "Game Over" the picture and the clock should stop
Here is a picture of the puzzle: Slide Puzzle
I hope someone can help me. Thank you very much.
Here is the code:
let w = window.innerWidth/2;
let h = window.innerHeight/1.5;
let ground;
let plate = [];
let tiles = [];
let col = 4;
let row = 4;
let bubbles = [];
let timerValue = 160;
let moves = 0;
function setup() {
canvas = createCanvas(w, h);
ground = createGraphics(w, h);
w = width / col;
h = height / row;
textAlign(CENTER, CENTER);
setInterval(timeIt, 1000);
imag = loadImage('Mauszeiger.png');
for (let i = 0; i < col; i++) {
for (let j = 0; j < row; j++) {
let img = createImage(w, h);
let index = i + j * col;
plate.push(index);
let teil = new Tile(index, img);
tiles[index] = teil;
}
}
tiles.pop();
plate.pop();
plate.push(-1);
startViz();
simpleShuffle(plate);
}
function draw() {
background(100);
push();
scale(1, 0.9);
drawViz();
updateTiles();
for (let i = 0; i < col; i++) {
for (let j = 0; j < row; j++) {
let index = i + j * col;
let x = i * w;
let y = j * h;
let teileIndex = plate[index];
if (teileIndex > -1) {
let img = tiles[teileIndex].img;
image(img, x, y, w, h);
}
strokeWeight(5);
noFill();
rect(x, y, w, h);
}
}
pop();
if (isSolved()) {
console.log('SOLVED');
timerValue = 0;
text('Won', width/2, height/2);
}
if (timerValue < 10 || timerValue >= 10) {
textSize(15);
fill(255);
text(timerValue, width-30, height*0.95);
}
textSize(45);
if (timerValue == 0) {
text('Game Over', width/2, height/2);
draw = function(){}
}
textSize(15);
text('moves: ' + moves, width/2, height*0.95);
push();
noFill();
stroke(255);
ellipse(27, height*0.95, 40, 40);
imageMode(CENTER);
image(imag, 30, height*0.95, imag.width / 100, imag.height / 100);
pop();
}
function mousePressed() {
let i = floor(mouseX / w);
let j = floor(mouseY / h);
move(i, j, plate);
if (mousePressed) {
moves++;
}
}
function windowResized() {
w = window.innerWidth/2;
h = window.innerHeight/1.5;
resizeCanvas(w, h);
}
function timeIt() {
if (timerValue > 0) {
timerValue--;
}
}
function isSolved() {
for (let i = 0; i < plate.length - 1; i++) {
if (plate[i] !== tiles[i].index) {
return false;
}
}
return true;
}
function updateTiles() {
for (let i = 0; i < col; i++) {
for (let j = 0; j < row; j++) {
let x = j * w;
let y = i * h;
let index = i + j * col;
if (tiles[index]) tiles[index].img.copy(ground, x, y, w, h, 0, 0, w, h);
}
}
}
function swap(i, j, arr) {
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
function randomMove(arr) {
let r1 = floor(random(col));
let r2 = floor(random(row));
move(r1, r2, arr);
}
function simpleShuffle(arr) {
for (let i = 0; i < 1000; i++) {
randomMove(arr);
}
}
function move(i, j, arr) {
let blank = findBlank();
let blankCol = blank % col;
let blankRow = floor(blank / row);
if (isNeighbor(i, j, blankCol, blankRow)) {
swap(blank, i + j * col, arr);
}
}
function isNeighbor(i, j, x, y) {
if (i !== x && j !== y) {
return false;
}
if (abs(i - x) == 1 || abs(j - y) == 1) {
return true;
}
return false;
}
function findBlank() {
for (let i = 0; i < plate.length; i++) {
if (plate[i] == -1) return i;
}
}
function startViz() {
for (let i = 0; i < 5; i++) {
bubbles.push(new Bubble());
}
}
function drawViz() {
ground.background(30);
for (let b of bubbles) {
b.update();
b.show();
b.show1();
}
}
class Bubble {
constructor() {
this.r = random(50, 80);
this.x = random(this.r, width - this.r);
this.y = random(this.r, height - this.r);
this.vx = random(-1 * (w*0.005), 3 * (w*0.005));
this.vy = random(-1 * (w*0.005), 3 * (w*0.005));
this.color = color(random(255), random(255), random(255));
}
show() {
ground.noFill();
ground.stroke(255);
ground.fill(this.color);
ground.strokeWeight(3);
ground.circle(this.x, this.y, this.r * 2 * (w*0.005));
}
show1() {
ground.line(this.x, this.y, this.x + this.r / 2 * (w*0.005), this.y + this.r / 2 * (w*0.005));
ground.line(this.x, this.y, this.x - this.r / 2 * (w*0.005), this.y + this.r / 2 * (w*0.005));
}
update() {
this.x += this.vx;
this.y += this.vy;
if (this.x > width - this.r || this.x < this.r) {
this.vx *= -1;
}
if (this.y > height - this.r || this.y < this.r) {
this.vy *= -1;
}
}
}
class Tile {
constructor(i, img) {
this.index = i;
this.img = img;
}
}

P5.js how to do 8 directional lerp color/color change?

A last issue i have in this code is that i am not sure how to create color change effect as 8 directional.
I can only map a mouseX to make it lerpColor horizontally or vertically .
But how do i make it work through moving both mouseX and mouseY?
I had it in shift_Color method within the class. I tried to state that within a certain dist(), lerpColor. But now it only showing black color rather than the changing color effect.
let cubes = [];
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
backCol = color(243, 243, 243);
//background(backCol);
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
let xPos = map(i, 0, 9, 50, width - 50);
let yPos = map(j, 0, 9, 50, height - 50);
cubes.push(new Cubes(xPos, yPos));
}
}
}
function draw() {
background(backCol);
noFill();
for (let a = 0; a < cubes.length; a++) {
cubes[a].update();
}
}
class Cubes {
constructor(x, y) {
this.x = x;
this.y = y;
this.size = 30;
this.stroke = 70;
this.gap = 110
this.shift1 = color(96);
this.shift2 = color(244);
}
update() {
this.shape();
this.shift_Color();
}
shape() {
push();
stroke(this.stroke);
//fill(this.shift1);
this.shift_Color();
translate(this.x - width / 2, this.y - height / 2, 0);
this.magnetic()
box(this.size);
pop();
}
shift_Color() {
let distance = dist(mouseX, mouseY, this.x, this.y);
if (distance < this.gap) {
this.shift1 = color(96);
this.shift2 = color(244);
this.shiftX = map(mouseX - width/2,this.gap,distance,0,1.0);
this.change = lerpColor(this.shift1, this.shift2, this.shiftX);
fill(this.change);
} else {
fill(this.shift1);
}
}
magnetic() {
let distance = dist(mouseX, mouseY, this.x, this.y);
if (distance < this.gap) {
this.attract = atan2(mouseY - this.y, mouseX - this.x);
rotateX(this.attract);
rotateY(this.attract);
} else {
rotateX(millis() / 1000);
rotateY(millis() / 1000);
}
}
}
If I understood your question properly, there are two problems in your code.
The first is the fact that because you are trying to map the distance between mouse and the cube to number between 0 and 1, you should write lerpColor(this.shift2, this.shift1, this.shiftX) instead of lerpColor(this.shift1, this.shift2, this.shiftX), since this.shift2 is the lighter color and will be the inner color.
Another problem is when mapping the change variable, change should be calculated based on the distance between mouse and the cube (which is the distance variable), not mouseX or mouseY. Solution is to simply do map(distance, 0, this.gap, 0, 1);.
let cubes = [];
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
backCol = color(243, 243, 243);
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
let xPos = map(i, 0, 9, 50, width - 50);
let yPos = map(j, 0, 9, 50, height - 50);
cubes.push(new Cubes(xPos, yPos));
}
}
}
function draw() {
background(backCol);
noFill();
for (let cube of cubes) {
cube.update();
}
}
class Cubes {
constructor(x, y) {
this.x = x;
this.y = y;
this.size = 30;
this.stroke = 70;
this.gap = 110
this.shift1 = color(96);
this.shift2 = color(244);
}
update() {
this.shape();
this.shift_Color();
}
shape() {
push();
stroke(this.stroke);
//fill(this.shift1);
this.shift_Color();
translate(this.x - width / 2, this.y - height / 2, 0);
this.magnetic()
box(this.size);
pop();
}
shift_Color() {
let distance = dist(mouseX, mouseY, this.x, this.y);
if (distance < this.gap) {
this.shiftX = map(distance, 0, this.gap, 0, 1);
// this.shiftX = map(mouseX - width/2,this.gap,distance,0,1.0);
this.change = lerpColor(this.shift2, this.shift1, this.shiftX);
// this.change = lerpColor(this.shift1, this.shift2, this.shiftX);
fill(this.change);
} else {
fill(this.shift1);
}
}
magnetic() {
let distance = dist(mouseX, mouseY, this.x, this.y);
if (distance < this.gap) {
this.attract = atan2(mouseY - this.y, mouseX - this.x);
rotateX(this.attract);
rotateY(this.attract);
} else {
rotateX(millis() / 1000);
rotateY(millis() / 1000);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
I also modified some of your code. Like the line
this.shift1 = color(96);
this.shift2 = color(244);
which is not needed currently since neither of these variable changes during execution.

How to constrain randomness

I'm drawing a random shape with vertices onto a canvas.
Sometimes the shape is very narrow and small.
Is there a way to constrain the randomness so this wont happen.
As I am very new to p5 any tips would be appreciated.
Thanks for your time and help
let noOfShapes = 3;
function setup(){
createCanvas(1240, 1754);
noLoop();
background(0,230)
colorMode(RGB)
rectMode(CENTER);
strokeWeight(3);
}
function draw(){
//fill(56, 37, 34);
for(let x = 0; x < noOfShapes; x++) {
beginShape();
stroke(255);
fill(25, 255, 255,100);
//noFill();
for(let y = 2; y < 6; y++) {
vertex(random(10,width), random(10,width))
// vertex(random(10,width), random(10,width))
// vertex(random(10,width), random(10,width))
// vertex(random(10,width), random(10,width))
}
endShape(CLOSE);
}
stroke(255);
for(let x=20; x<= width; x = x+20){
blendMode(DODGE);
//fill(200,200,200)
stroke(255);
beginShape();
vertex(x, 0)
vertex(x, height+20)
endShape();
}
}
Create a list of random numbers for the x and y coordinates:
rx = []; ry = [];
for (let y = 0; y < 4; y++) {
rx.push(random(10, width-20));
ry.push(random(10, height-20));
}
Compute the difference between the minimum (min) and maximum (max) for each list:
drx = max(rx) - min(rx);
dry = max(ry) - min(ry);
Define a threshold an repeat the process as long any of the differences is below the threshold:
let threshold = 200;
do {
// [...]
}
while (drx < threshold || dry < threshold)
The algorithm can be further improved, by taking into account the distance between the points along 45° diagonals. Use the Dot product to calculate the distances:
rd1.push(rx[y]*0.707 + ry[y]*0.707);
rd2.push(rx[y]*0.707 - ry[y]*0.707);
Use the random coordinates to generate the shape:
let threshold = 400;
let rx, ry, drx, dry, rd1, rd2, drd1, drd2;
do {
rx = []; ry = []; rd1 = []; rd2 = [];
for (let y = 0; y < 4; y++) {
rx.push(random(10, width-20));
ry.push(random(10, height-20));
rd1.push(rx[y]*0.707 + ry[y]*0.707);
rd2.push(rx[y]*0.707 - ry[y]*0.707);
}
drx = max(rx) - min(rx);
dry = max(ry) - min(ry);
drd1 = max(rd1) - min(rd1);
drd2 = max(rd2) - min(rd2);
}
while (drx < threshold || dry < threshold || drd1 < threshold || drd2 < threshold)
beginShape();
stroke(255);
fill(25, 255, 255,100);
//noFill();
for (let y = 0; y < rx.length; y++) {
vertex(rx[y], ry[y]);
}
endShape(CLOSE);
let noOfShapes = 1;
function setup(){
createCanvas(1240, 1754);
noLoop();
background(0,230)
colorMode(RGB)
rectMode(CENTER);
strokeWeight(3);
}
function draw(){
for(let x = 0; x < noOfShapes; x++) {
let threshold = 400;
let rx, ry, drx, dry, rd1, rd2, drd1, drd2;
do {
rx = []; ry = []; rd1 = []; rd2 = [];
for (let y = 0; y < 4; y++) {
rx.push(random(10, width-20));
ry.push(random(10, height-20));
rd1.push(rx[y]*0.707 + ry[y]*0.707);
rd2.push(rx[y]*0.707 - ry[y]*0.707);
}
drx = max(rx) - min(rx);
dry = max(ry) - min(ry);
drd1 = max(rd1) - min(rd1);
drd2 = max(rd2) - min(rd2);
}
while (drx < threshold || dry < threshold || drd1 < threshold || drd2 < threshold)
beginShape();
stroke(255);
fill(25, 255, 255,100);
//noFill();
for (let y = 0; y < rx.length; y++) {
vertex(rx[y], ry[y]);
}
endShape(CLOSE);
}
stroke(255);
for(let x=20; x<= width; x = x+20){
blendMode(DODGE);
//fill(200,200,200)
stroke(255);
beginShape();
vertex(x, 0)
vertex(x, height+20)
endShape();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.js"></script>

Categories

Resources