draw a hexagon of hexagons in p5.js - javascript

I am trying to create a hexagon made out of hexagon cells in p5.js.
I would like the algorithm to receive the following as input:
number of hexagon cells in the first row
total hexagon cells to draw
This is the code I have so far but it is not drawing the shape the way I want to.
// declare fixed variables
let diam = 30;
let total_hex = 30;
let diagonal = 7;
let first_row = 4;
let side = first_row;
// declare dependent variables
let radius = diam / 2;
let upper_half = side - 1;
let rows = upper_half + side;
function setup() {
createCanvas(700, 700);
}
// draw
function draw() {
background("#ffae5");
noFill();
strokeWeight(2);
stroke("#ffb703");
// start counters
// row_counter = rows;
counter = 0;
row_cells = first_row;
push();
translate(200, 200);
for (let j = 0; j < rows; j++) {
for (let i = 0; i < row_cells; i++) {
push();
translate(-j * (radius + radius / 2 + radius / 4), 0);
if (diagonal > i) {
console.log(diagonal, i);
push();
translate(i * 2 * diam - (i * radius) / 2, 3 * j * (diam / 2));
polygon(0, 0, diam, 6);
pop();
}
pop();
}
if (rows < j) {
row_cells -= 1;
}
if (rows > j) {
row_cells += 1;
}
}
pop();
}
function polygon(x, y, radius, npoints) {
let angle = TWO_PI / npoints;
beginShape();
for (let a = 0; a < TWO_PI; a += angle) {
let sx = x + sin(a) * radius;
let sy = y + cos(a) * radius;
vertex(sx, sy);
}
endShape(CLOSE);
}
Specifically, the hexagons from the bottom half of the shape appear to be shifted by some parameter I still can't figure out. Here's the link to the p5.js online editor.
What am I missing here? Thanks!

I logged i, j at each cell to determine the position.
Now if you look at the cells you want to remove, you will see that these cells are: i < 3 && j > 3 && j - i > 3.
So if you simply want to remove these cells, you could change your if (diagonal > i) to if (diagonal > i && !(i < 3 && j > 3 && j - i > 3))) or parametrize that 3 to be a dependent variable to Math.floor(diagonal/2) to make something like:
//declare fixed variables
let diagonal = 7;
...
//declare dependent variables
const bottomLeftThreshold = Math.floor(diagonal/2);
...
const bottomLeft = i < bottomLeftThreshold && j > bottomLeftThreshold && j - i > bottomLeftThreshold
if (diagonal > i && !(bottomLeft))
...
Now the code is much more readable and modularized.

I did some tweaks to #cSharp's code snippet (see first answer) here and there, and ended up making the code flexible enough so it can adapt to any number of hexagons and sizes.
Below are the "fixed" and "dependent" variables I declared to draw the hexagon as well as the draw() function. I had to do some auxiliary calculations to get the diagonal and side sizes here.
// declare data variables
let year_1920 = 21;
let year_2019 = 73;
// declare fixed variables
let diam = 30;
let total_hex = year_2019;
let diagonal = 11;
let first_row = 6;
let side = first_row;
// declare dependent variables
let radius = diam / 2;
let upper_half = side - 1;
let rows = upper_half + side;
function draw() {
background("#ffae5");
noFill();
strokeWeight(1.2);
stroke("#ffb703");
// start counters
yellow = 0;
counter = 0;
row_cells = first_row;
push();
translate(200, 100);
for (let j = 0; j < rows; j++) {
for (let i = 0; i < row_cells; i++) {
if (yellow >= year_1920) {
stroke(255);
fill("#ffb703");
}
yellow++;
if (counter >= total_hex) {
noStroke();
noFill();
}
push();
translate(-j * (radius + radius / 2 + radius / 4), 0);
if (
diagonal > i &&
!(i < upper_half && j > upper_half && j - i > upper_half)
) {
push();
translate(i * 2 * diam - (i * radius) / 2, 3 * j * (diam / 2));
polygon(0, 0, diam, 6);
pop();
counter++;
}
pop();
}
if (rows > j) {
row_cells++;
}
}
pop();
}

Related

Weird pattern of Heat Equation Simulation on 2D Canvas

I'm playing with a Heat Equation on 2D Canvas with additional heat source in a form of a donut. The result I got some "acidic" pattern around this donut.
const width = 200; // width of the grid
const height = 200; // height of the grid
const dt = 0.25; // time step
const dx = 1; // space step in the x-direction
const dy = 1; // space step in the y-direction
const alpha = 0.25; // thermal diffusivity
const Q = [];
// Heat of the heat source
const Q0 = 80;
const r1 = 8;
const r2 = 12;
for (let i = 0; i < width - 1; i++) {
Q[i] = [];
for (let j = 0; j < height - 1; j++) {
// Calculate the distance from the center of the region
const r = Math.sqrt((i - width / 2) ** 2 + (j - height / 2) ** 2);
Q[i][j] = (r1 < r && r < r2) ? Q0 : 0;
}
}
let grid = []; // array to store the grid
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Initialize the grid with random temperatures
for (let i = 0; i < width; i++) {
grid[i] = [];
for (let j = 0; j < height; j++) {
grid[i][j] = 50
}
}
function updateGrid() {
// Update the temperature of each cell based on the heat equation
for (let i = 1; i < width - 1; i++) {
for (let j = 1; j < height - 1; j++) {
const d2Tdx2 = (grid[i + 1][j] - 2 * grid[i][j] + grid[i - 1][j]) / (dx ** 2);
const d2Tdy2 = (grid[i][j + 1] - 2 * grid[i][j] + grid[i][j - 1]) / (dy ** 2);
grid[i][j] = grid[i][j] + alpha * dt * (d2Tdx2 + d2Tdy2) + (Q[i][j] * dt);
}
}
}
// This function is called repeatedly to update the grid and render it
function main() {
updateGrid();
renderGrid();
requestAnimationFrame(main);
}
// This function render the grid
function renderGrid() {
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Iterate over the grid and draw each cell
for (let i = 0; i < width; i++) {
for (let j = 0; j < height; j++) {
let hue = ((100 - grid[i][j]) / 100) * 240;
//ctx.fillStyle = `rgb(${temp}, ${temp}, ${temp})`;
ctx.fillStyle = `hsl(${hue}, 100%, 50%)`;
ctx.fillRect(i * dx, j * dy, dx, dy);
}
}
}
// Start the simulation
main();
Tried different approaches, like playing with hsl colors, initial parameters but something definitely missing there.
What I aslo noticed is that during debuging, some values seems to be blowing up and I think that's the root of the problem but can't find the source of it, I have tried to found more information about this behaviour of differential equiations and why this can happen but couldn't apply it to the source code.
during calculation values in grid can go over 100, thus making formula ((100 - grid[i][j]) / 100) * 240; produce negative values
the simples way to fix is to limit values:
grid[i][j] = Math.min(100, grid[i][j] + alpha * dt * (d2Tdx2 + d2Tdy2) + (Q[i][j] * dt));
const width = 200; // width of the grid
const height = 200; // height of the grid
const dt = 0.25; // time step
const dx = 1; // space step in the x-direction
const dy = 1; // space step in the y-direction
const alpha = 0.25; // thermal diffusivity
const Q = [];
// Heat of the heat source
const Q0 = 80;
const r1 = 8;
const r2 = 12;
for (let i = 0; i < width - 1; i++) {
Q[i] = [];
for (let j = 0; j < height - 1; j++) {
// Calculate the distance from the center of the region
const r = Math.sqrt((i - width / 2) ** 2 + (j - height / 2) ** 2);
Q[i][j] = (r1 < r && r < r2) ? Q0 : 0;
}
}
let grid = []; // array to store the grid
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Initialize the grid with random temperatures
for (let i = 0; i < width; i++) {
grid[i] = [];
for (let j = 0; j < height; j++) {
grid[i][j] = 50
}
}
function updateGrid() {
// Update the temperature of each cell based on the heat equation
for (let i = 1; i < width - 1; i++) {
for (let j = 1; j < height - 1; j++) {
const d2Tdx2 = (grid[i + 1][j] - 2 * grid[i][j] + grid[i - 1][j]) / (dx ** 2);
const d2Tdy2 = (grid[i][j + 1] - 2 * grid[i][j] + grid[i][j - 1]) / (dy ** 2);
grid[i][j] = Math.min(100, grid[i][j] + alpha * dt * (d2Tdx2 + d2Tdy2) + (Q[i][j] * dt));
}
}
}
// This function is called repeatedly to update the grid and render it
function main() {
updateGrid();
renderGrid();
requestAnimationFrame(main);
}
// This function render the grid
function renderGrid() {
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Iterate over the grid and draw each cell
for (let i = 0; i < width; i++) {
for (let j = 0; j < height; j++) {
let hue = (100 - grid[i][j]) / 100 * 240;
//ctx.fillStyle = `rgb(${temp}, ${temp}, ${temp})`;
ctx.fillStyle = `hsl(${hue}, 100%, 50%)`;
ctx.fillRect(i * dx, j * dy, dx, dy);
}
}
}
// Start the simulation
main();
<canvas width="500" height="500" id="canvas"></canvas>

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>

Different results with the same code on a computer and a smartphone

Different results are obtained with the same code on a computer(windows 10) and a smartphone(android).I'm working in P5.JS with used loadPixels(). Below is an example code and screenshots. I will also leave a link to OpenProcessing so that you can test the program:
https://openprocessing.org/sketch/1703228
function setup() {
createCanvas(300, 300);
randomSeed(1);
for (let x2=0; x2<width; x2 +=100) {
for (let y2=0; y2<height; y2 += 100) {
fill(random(200),random(55),random(155));
rect(x2,y2,100,100);
}
}
///////
loadPixels();
background(255);
for (let y1=0; y1<height; y1+=100) {
for (let x1=0; x1<width; x1+=100) {
let poz=(x1+y1*width)*4;
let r=pixels[poz];
let g=pixels[poz+1];
let b=pixels[poz+2];
fill(r,g,b);
rect(x1,y1,100,100);
}
}
//////////
}
Computer picture
Smartphone picture
p5js pixels array is different from the original Java's processing pixels array. Very different.
It stores all canvas pixels in 1d array, 4 slots for each pixel:
[pix1R, pix1G, pix1B, pix1A, pix2R, pix2G, pix2B, pix2A...] And also the pixel density mathers.
So your issue is with pixel density that are different from one device to another.
Try differents values to pixelDensity() in the code below. With 1 you get the result that you are getting in PC, with 3 you get the result you get with mobile.
function setup() {
createCanvas(300, 300);
//change here!!
pixelDensity(3);
randomSeed(1);
for (let x2 = 0; x2 < width; x2 += 100) {
for (let y2 = 0; y2 < height; y2 += 100) {
fill(random(200), random(55), random(155));
rect(x2, y2, 100, 100);
}
}
///////
loadPixels();
background(255);
for (let y1 = 0; y1 < height; y1 += 100) {
for (let x1 = 0; x1 < width; x1 += 100) {
let poz = (x1 + y1 * width) * 4;
let r = pixels[poz];
let g = pixels[poz + 1];
let b = pixels[poz + 2];
fill(r, g, b);
rect(x1, y1, 100, 100);
}
}
//////////
}
To make them consistent you need to account for different pixelsDensity in your code.
the following code shows how to account for density using pixels in a determined area, in you case that would be the entire canvas.
To work any given area (a loaded image for instance) you can adapt this snippet:
(here i'm setting the color of the area, but you can get the idea;)
//the area data
const area_x = 35;
const area_y = 48;
const width_of_area = 180;
const height_of_area = 200;
//the pixel density
const d = pixelDensity();
loadPixels();
// those 2 first loops goes trough every pixel in the area
for (let x = area_x; x < width_of_area; x++) {
for (let y = area_y; y < height_of_area; y++) {
//here we go trough the pixels array to get each value of a pixel minding the density.
for (let i = 0; i < d; i++) {
for (let j = 0; j < d; j++) {
// calculate the index of the 1d array for every pixel
// 4 values in the array for each pixel
// y times density times #of pixels
// x idem
index = 4 * ((y * d + j) * width * d + (x * d + i));
// numbers for rgb color
pixels[index] = 255;
pixels[index + 1] = 30;
pixels[index + 2] = 200;
pixels[index + 3] = 255;
}
}
}
}
updatePixels();

P5.js curveVertex function is closing at a point

I've created a noise function that pairs with a circle function to create a random noise circle thing that looks pretty cool. My problem is the curveVertex function in P5.js works correctly except for the connection of the first and last vertex. My code is:
let start = Array(50).fill(0); // dont change
let amount = 1; // amount of shapes
let gap = 30; // between shapes
let amplify = 50; // 0 -->
let colorSpeed = 1; // 1 - 9
let colorSeparation = 3; // 0 - 80 recomended 0 - 10
function setup() {
createCanvas(windowWidth, windowHeight);
for(let i = 0 ; i < start.length; i++){
start[i] = random(i);
}
}
function draw() {
background(0);
for(let dnc = (amount + 1) * gap; dnc > gap; dnc -= gap){
drawNoiseCircle(dnc, getNoise(start.length));
}
start = start.map( c => c + 0.01 );
}
function getNoise(amount){
let lengths = [];
for(let i = 1; i < amount + 1; i++){
let n1 = noise(start[i - 1]);
let noise1 = map(n1, 0, 1, -amplify, amplify);
lengths.push(abs(-noise1));
}
return lengths;
}
function drawNoiseCircle(radius, lengths){
colorMode(HSB);
fill(((frameCount + radius) * colorSeparation)/-map(colorSpeed, 1, 10, -10, -1) % 360, 100, 50);
noStroke()
let x;
let y;
beginShape();
for(let l = 0; l < lengths.length; l++){
x = Math.cos(radians(l * 360 / lengths.length)) * (radius + lengths[l]) + width/2;
y = Math.sin(radians(l * 360 / lengths.length)) * (radius + lengths[l]) + height/2;
curveVertex(x, y);
}
endShape(CLOSE);
stroke("black");
line(width/2, height/2, width, height/2);
line(width/2, height/2 + 9, width, height/2 + 9);
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.js"></script>
I understand the endShape(CLOSED) closes the shape with a straight line, but I'm not sure any other way to close the shape.
You can see the pointed edge on the right side, directly in the middle of the shape.
!EDIT!
I've added lines to the shape to show the line segment that isn't affected by the curve vertex. Also, I understand it may not be a very significant problem, but if the amount of vertexes shrink, it becomes a much bigger problem (eg. a square or a triangle).
Unfortunately I won't have time to dive deep and debug the actual issue with curveVertex (or it's math) at the time, but it seems there's something interesting with curveVertex() in particular.
#Ouoborus point makes sense and the function "should" behave that way (and it was with vertex(), but not curveVertex()). For some reason curveVertex() requires looping over the not just the first point again, but the second and third.
Here's basic example:
function setup() {
createCanvas(300, 300);
background(220);
let numPoints = 6;
let angleIncrement = TWO_PI / numPoints;
let radius = 120;
beginShape();
for(let i = 0 ; i < numPoints + 3; i++){
let angle = angleIncrement * i;
let x = 150 + cos(angle) * radius;
let y = 150 + sin(angle) * radius;
curveVertex(x, y);
}
endShape();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.min.js"></script>
Try decreasing numPoints + 3 to numPoints + 2 or notice the behaviour you're describing.
(I could speculate it might have something to do with how curveVertex() (Catmull-Rom splines) are implemented in p5 and how many coordinates/points it requires, but this isn't accurate without reading the source code and debugging a bit)
Here's a version of your code using the above notes:
let start = Array(30).fill(0);
let colorSpeed = 1; // 1 - 9
let colorSeparation = 3; // 0 - 80 recomended 0 - 10
function setup() {
createCanvas(600, 600);
colorMode(HSB);
noStroke();
// init noise seeds
for(let i = 0 ; i < start.length; i++){
start[i] = random(i);
}
}
function getNoise(seeds, amplify = 50){
let amount = seeds.length;
let lengths = [];
for(let i = 1; i < amount + 1; i++){
let n1 = noise(seeds[i - 1]);
let noise1 = map(n1, 0, 1, -amplify, amplify);
lengths.push(abs(-noise1));
}
return lengths;
}
function drawNoiseCircle(radius, lengths){
let sides = lengths.length;
let ai = TWO_PI / sides;
let cx = width * 0.5;
let cy = height * 0.5;
fill(((frameCount + radius) * colorSeparation)/-map(colorSpeed, 1, 10, -10, -1) % 360, 100, 50);
beginShape();
for(let i = 0 ; i < sides + 3; i++){
let noiseRadius = radius + lengths[i % sides];
let a = ai * i;
let x = cx + cos(a) * noiseRadius;
let y = cy + sin(a) * noiseRadius;
curveVertex(x, y);
}
endShape();
}
function draw() {
background(0);
// draw with updated perlin noise values
drawNoiseCircle(120, getNoise(start));
// increment noise seed
start = start.map( c => c + 0.01 );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.min.js"></script>

draggable backgroun

I want to achieve something like an infinite drag like the one in Konva js Can anyone help me with this. I try varius things but non of them were ok. Im new in p5js and javascript. Please for any hints. Only this element prevents me from completing the entire project.
var grid;
var current_img;
var BgCat1 = [];
var layerOne;
let show_grid = false;
There may be a more elegant solution, but here I draw an extra cell on each side of the grid to handle the wraparound, so a 12x12 grid with 10x10 visible. See it run here: https://editor.p5js.org/rednoyz/full/uJCADfZXv
let dim = 10, sz;
let xoff = 0, yoff = 0;
function setup() {
createCanvas(400, 400);
sz = width/ dim;
}
function mouseDragged() {
xoff += mouseX - pmouseX;
yoff += mouseY - pmouseY;
}
function draw() {
background(255);
for (let i = 0; i < dim+2; i++) {
for (let j = 0; j < dim+2; j++) {
let x = ((xoff + j * sz) % (width+sz)) - sz;
if (x < -sz) x += width+sz;
let y = ((yoff + i * sz) % (height+sz)) - sz;
if (y < -sz) y += height+sz;
rect(x, y, sz, sz);
text(i * 10 + j, x + sz/2, y + sz/2);
}
}
}

Categories

Resources