How to plot a imperfect circle in JS with p5 - javascript

I've created a script to calculate coordinates of a circle in JS. I'm using p5.js to draw the circle but when I run the script nothing happens. I assume it has to do with the way I'm plotting the vertices?
var xValues = [];
var yValues = [];
function setup() {
createCanvas(400, 400);
background(220);
crookedCircle(10, 10, 10, 10);
}
function draw() {}
function crookedCircle(radius, steps, centerX, centerY) {
for (var i = 0; i < steps; i++) {
xValues[i] = (centerX + radius * Math.cos(2 * Math.PI * i / steps));
yValues[i] = (centerY + radius * Math.sin(2 * Math.PI * i / steps));
for (let x = 0; x < xValues.length; x++) {
for (let y = 0; y < yValues.length; y++) {
//console.log("x: "+xValues[x] + " y: "+yValues[y])
beginShape();
vertex(xValues[x] + random(-10, 10), yValues[y]) + random(-10, 10);
endShape(CLOSE);
}
}
}
}

Here I cleaned what was written, I can annotate it to explain if you wish so. Also instead of random, I recommend you explore the noise() function here, which would make the circle look smoother.
function setup() {
createCanvas(400, 400);
background(220);
crookedCircle(10, 10, width / 2, height / 2);
}
function draw() {}
function crookedCircle(radius, steps, centerX, centerY) {
var xValues = [];
var yValues = [];
for (var i = 0; i < steps; i++) {
let rad = radius + random(-radius / 10,radius / 10) // you can change the 10 here to how intense you want the change to be;
xValues[i] = (centerX + rad * cos(2 * PI * i / steps));
yValues[i] = (centerY + rad * sin(2 * PI * i / steps));
}
beginShape();
for(let i = 0; i < xValues.length; i ++){
curveVertex(xValues[i], yValues[i]);
}
endShape(CLOSE);
}

You draw many many shapes with just 1 point. beginShape and endShape encloses the vertices of a shape. Therefore you have to call beginShape before the loop and endShape after the loop:
function crookedCircle(radius, steps, centerX, centerY) {
beginShape();
for (var i = 0; i < steps; i++) {
// [...]
}
endShape(CLOSE);
}
One loop is enough if you want to draw 1 circle:
var xValues = [];
var yValues = [];
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
fill(255)
crookedCircle(100, 90, 120, 120);
}
function crookedCircle(radius, steps, centerX, centerY) {
for (var i = 0; i < steps; i++) {
xValues[i] = centerX + radius * Math.cos(2 * Math.PI * i / steps);
yValues[i] = centerY + radius * Math.sin(2 * Math.PI * i / steps);
}
beginShape();
for(let i = 0; i < steps; i ++) {
vertex(xValues[i] + random(-2, 2), yValues[i] + random(-2, 2));
}
endShape(CLOSE);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>

Related

Canvas - Chrome and Firefox display small ctx.lineWidth differently

I am using a small value (0.06) for ctx.lineWidth in my canvas design, and I noticed that Chrome (and also Edge) displays the result differently than Firefox (System: Windows 10). My desired output is Firefox version, which looks grainy. You can see the difference below (left: Firefox, right: Chrome):
Is there any remedy to this issue?
Here's my code:
const canvas = document.querySelector('canvas'), ctx = canvas.getContext('2d');
var cw = canvas.width = canvas.height = Math.min(window.innerWidth, window.innerHeight);
ctx.fillStyle = '#000000'; ctx.fillRect(0, 0, cw, cw);
var u = cw / 1000;
var cx = 500 * u, cy = 500 * u;
var colorsP = [['304B5D','B48B35'],['3B999F','E5C91C'],['854B68','B9602B'],['AF1D16','779C3C'],['9E4EB3','F1D628'],['6B2142','DF748A'],['663388','AA6600'],['BBDFD5','E9D038'],['304350','D9B15C'],['99BC44','274140'],['4A122F','90181A'],['184299','24B885'],['B84224','D28928'],['CD4316','E5D03C'],['826071','D77D48'],['DF7D36','5065A8'],['B0244B','D39639'],['CE2C14','91A86D'],['A16DAF','D1C578'],['42408C','D4455E'],['481F31','F28FA3'],['334E94','7C85B0'],['1C3982','4175AC']];
var colors = randomItem(colorsP);
colors.push('FFFFFF','000000','000000');
// Origins
for (let i = 1; i <= 100; i++) {
let color = colors[randomNumber(0, colors.length - 1)];
// Points
let points = [];
for (let j = 1; j <= 60; j++) {
let x = j * j * randomNumber(240, 860) * u;
let y = j * j * randomNumber(240, 860) * u;
points.push([x,y]);
}
// Rotations
for (let j = 1; j <= 200; j++) {
ctx.beginPath(); ctx.moveTo(points[0][0], points[0][1]); for (let k = 1; k < points.length; k++) ctx.lineTo(points[k][0], points[k][1]); ctx.lineWidth = 0.06; ctx.strokeStyle = '#'+color; ctx.stroke();
ctx.translate(cx, cy); ctx.rotate(0.5 * Math.PI / 180); ctx.translate(-cx, -cy);
}
}
function randomItem(arr) { return arr[Math.floor(Math.random() * arr.length)]; }
function randomNumber(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); }
Thanks!

Set circumference as boundary for loop-ed line in P5.js

I am trying to limit the height of the looped lines to the boundary/circumference of a circle()
I couldn't find any solution to solve the issue...
let numCols = 10
let margin = 100
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
test = circle(100,100,100);
for (let i = 0; i < numCols; i++) {
let iMapped = map(i, 0, numCols - 1, 50, 150);
line(iMapped, 100, iMapped, 200);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
Thanks!
Define the center and the radius of the circle:
let centerx = 100;
let centery = 100;
let radius = 50;
Compute the angle from the center to circle dependent on the x coordinate by acos and the y distance by sin:
let x = iMapped - centerx;
let angle = acos(x/50);
line(iMapped, centery, iMapped, centery + 50 * sin(angle));
See the example:
let numCols = 10
let margin = 100
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
let centerx = 100;
let centery = 100;
let radius = 50;
test = circle(centerx, centery, radius*2);
for (let i = 0; i < numCols; i++) {
let iMapped = map(i, 0, numCols - 1, 50, 150);
let x = iMapped - centerx;
let angle = acos(x/50);
line(iMapped, centery, iMapped, centery + 50 * sin(angle));
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>

rotating a custom shape moves it in the corner

When rotating a custom shape using translate(width/2,height/2); rotate(angle)),
it moves the shape in the bottom left corner. I tried making translate values negative, it fixed it but then the origin was at 0,0. I have used pop(); push() and beginShape(); endShape with no success.
var points = [];
var r;
var lines = 30;
function setup() {
createCanvas(window.innerWidth, window.windowHeight);
angleMode(DEGREES);
// get the points of the corners of the hexagon
r = Math.min(width, height) * 0.4;
var angle = 60;
for (var i = 1; i < 7; i++) {
var tempX = r * sin((angle * i + 30) % 360) + width / 2;
var tempY = r * cos((angle * i + 30) % 360) + height / 2;
points.push([tempX, tempY]);
}
background(0);
stroke(0, 0, 255);
rectMode(CENTER);
}
function draw() {
background(0);
// draw the lines of ...
push();
translate(width/2, height/2);
rotate(frameCount * 0.75);
beginShape();
for (var i = 0; i < points.length; i++) {
// ... the hexagon perimeter
line(points[i][0], points[i][1], points[(i + 1) % 6][0], points[(i + 1) % 6][1]);
var tempAngle = 240 + i * 60;
var tempX = r * 1.1545 * sin(tempAngle) + points[i][0];
var tempY = r * 1.1545 * cos(tempAngle) + points[i][1];
for (var j = 0; j < lines + 1; j++) {
// ... the lines inside the hexagon
var tempAngle2 = tempAngle = (30 / lines * j) + 210 + i * 60;
var distance = r / cos(30 / lines * j);
var tempX2 = distance * sin(tempAngle2) + points[i][0];
var tempY2 = distance * cos(tempAngle2) + points[i][1];;
line(points[i][0], points[i][1], tempX2, tempY2);
}
endShape();
}
pop();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.js"></script>
I think the problem is that you are defining your points for your shape with an x/y offset. By removing the width / 2 & height / 2 from your point definitions it centers your shape.
var points = [];
var r;
var lines = 30;
function setup() {
createCanvas(window.innerWidth, window.windowHeight);
angleMode(DEGREES);
// get the points of the corners of the hexagon
r = Math.min(width, height) * 0.4;
var angle = 60;
for (var i = 1; i < 7; i++) {
var tempX = r * sin((angle * i + 30) % 360)
var tempY = r * cos((angle * i + 30) % 360)
points.push([tempX, tempY]);
}
background(0);
stroke(0, 0, 255);
//rectMode(CENTER);
}
function draw() {
background(0);
// draw the lines of ...
push();
translate(width/2, height/2);
rotate(frameCount * 0.75);
beginShape();
for (var i = 0; i < points.length; i++) {
// ... the hexagon perimeter
line(points[i][0], points[i][1], points[(i + 1) % 6][0], points[(i + 1) % 6][1]);
var tempAngle = 240 + i * 60;
var tempX = r * 1.1545 * sin(tempAngle) + points[i][0];
var tempY = r * 1.1545 * cos(tempAngle) + points[i][1];
for (var j = 0; j < lines + 1; j++) {
// ... the lines inside the hexagon
var tempAngle2 = tempAngle = (30 / lines * j) + 210 + i * 60;
var distance = r / cos(30 / lines * j);
var tempX2 = distance * sin(tempAngle2) + points[i][0];
var tempY2 = distance * cos(tempAngle2) + points[i][1];;
line(points[i][0], points[i][1], tempX2, tempY2);
}
endShape();
}
pop();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.js"></script>

Ring of tangent circles inscribed in another circle

My question is based on this question.
The code of the post is fantastic.
It creates a ring of satellite circles tangent to a base circle (Out of the base circle).
This part calculates the radius external to the circle:
var angle = Math.PI / n;
var s = Math.sin(angle);
var r = baseRadius * s / (1-s);
How to calculate the radius of the circles if I want the circle ring to be tangent inside the base circle?
I would greatly appreciate your help.
Modify your sub circles by Sine and Cosine and PI.
You can use this as a baseline:
//Classes
var Coord = (function() {
function Coord(x, y) {
this.x = x;
this.y = y;
}
return Coord;
}());
var Circle = (function() {
function Circle(origo, radius) {
this.origo = origo;
this.radius = radius;
}
Circle.prototype.circumference = function() {
return this.radius * 2 * Math.PI;
};
return Circle;
}());
//functions
function innerTangentCircles(master, count) {
if (count === void 0) {
count = 4;
}
var result = [];
for (var index = 0; index < count; index++) {
var cos = Math.cos(((Math.PI * 2) / count) * index);
var sin = Math.sin(((Math.PI * 2) / count) * index);
var circle = new Circle(new Coord(master.origo.x + (master.radius * cos) - (master.radius / (count / 2)) * cos, master.origo.y + (master.radius * sin) - (master.radius / (count / 2)) * sin), master.radius / (count / 2));
result.push(circle);
}
return result;
}
//>>Test<<
//Create circles
var bigC = new Circle(new Coord(200, 200), 200);
//Create DOM
var c = document.createElement("canvas").getContext("2d");
c.canvas.width = 500;
c.canvas.height = 500;
document.body.appendChild(c.canvas);
//Run loop
function draw(i) {
var circles = innerTangentCircles(bigC, i);
c.fillStyle = "red";
c.beginPath();
c.arc(bigC.origo.x, bigC.origo.y, bigC.radius, 0, Math.PI * 2);
c.fill();
c.closePath();
c.fillStyle = "yellow";
for (var index = 0; index < circles.length; index++) {
var circle = circles[index];
c.beginPath();
c.arc(circle.origo.x, circle.origo.y, circle.radius, 0, Math.PI * 2);
c.fill();
c.closePath();
}
if (i > 500) {
i = 4;
}
i = i * 2;
setTimeout(function() {
draw(i);
}, 1000);
}
draw(8);
/**
* Coord
*/
var Coord = (function() {
function Coord(x, y) {
this.x = x;
this.y = y;
}
return Coord;
}());
/**
* Circle
*/
var Circle = (function() {
function Circle(origo, radius) {
this.origo = origo;
this.radius = radius;
}
Circle.radiusFromCircumference = function(circumference) {
return circumference / Math.PI / 2;
};
Circle.circumference = function(radius) {
return 2 * Math.PI * radius;
};
Circle.prototype.circumference = function() {
return Circle.circumference(this.radius);
};
return Circle;
}());
//Functions
function distance(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
}
function getRadian(count, index) {
return ((Math.PI * 2) / count) * index;
}
function circlesFromCircumference(master, count) {
var circles = [];
var masterCircumference = master.circumference();
var innerRadius = Circle.radiusFromCircumference(masterCircumference);
//var innerCircumference = Circle.circumference(innerRadius);
var circleRadius = 0;
var radian = getRadian(count, 1);
var maxCalc = 1000;
while (maxCalc--) {
var dist = distance(Math.cos(0) * innerRadius + master.origo.x, Math.sin(0) * innerRadius + master.origo.y, Math.cos(radian) * innerRadius + master.origo.x, Math.sin(radian) * innerRadius + master.origo.y);
if (Math.abs(dist / 2 - circleRadius) < 0.5) {
break;
}
circleRadius = dist / 2;
innerRadius = master.radius - circleRadius;
}
for (var index = 0; index < count; index++) {
var radian = getRadian(count, index);
var cos = Math.cos(radian);
var sin = Math.sin(radian);
var c = new Circle(new Coord(cos * innerRadius + master.origo.x, sin * innerRadius + master.origo.y), circleRadius);
circles.push(c);
}
return circles;
}
//>>TEST<<
var c = document.createElement("canvas").getContext("2d");
c.canvas.height = 200;
c.canvas.width = c.canvas.height;
document.body.appendChild(c.canvas);
var bigC = new Circle(new Coord(c.canvas.height / 2, c.canvas.height / 2), c.canvas.height / 2.1);
function draw(size) {
size++;
c.fillStyle = "red";
c.beginPath();
c.arc(bigC.origo.x, bigC.origo.y, bigC.radius, 0, Math.PI * 2);
c.fill();
c.closePath();
var circles = circlesFromCircumference(bigC, size);
c.fillStyle = "yellow";
for (var index = 0; index < circles.length; index++) {
var circle = circles[index];
c.beginPath();
c.arc(circle.origo.x, circle.origo.y, circle.radius, 0, Math.PI * 2);
c.fill();
c.closePath();
}
setTimeout(function() {
if (size > 32) {
size = 2;
}
draw(size);
}, 1000 / 4);
}
draw(1);

Outputting an array of text values in the shape of a circle in p5.js

Ahoy! I am new to programming and working in p5.js. My conundrum is this: I'd like to create a digital clock, and output the numbers on the clock by using some for loops and an array for the clock values (text 1-12). I've figured out how to make a ticking image of a clock...but can't figure the rest out. When I run the below code, it doesn't throw any errors but the text/numbers on the clock aren't executing. I've tried putting the first for loop below within the setup function, and nothing changes. What am I doing wrong? I feel like I'm confused around the second for loop and how to actually print the numbers to the screen, like regarding: (text([i])). Please let me know if I need to clarify more - any help is appreciated! Trying to learn as much as I can.
//Simple second clock.
// An exercise in translating from polar to cartesian coordinates
var radius = 120.0;
var angle = 0.0;
var x=0, y=0;
var digits = [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
function setup() {
createCanvas(windowWidth,windowHeight);
}
function draw() {
for (var i = 0; i < 12; i++) { //loop for digits. Populate array.
digits[i] = text("[i]", 10, -radius+10);
for (var i = 0; i < digits.length; i++) {
fill(255,0,255)
text([i]);
}
}
background(255);
translate(width/2, height/2);
stroke(205,205,55);
fill(255,0,255);
ellipse(0,0,10,10);
noFill();
ellipse(0,0,250,250);
stroke(25);
fill(205,205,55);
//text("12", 0, -radius+PI+10); //if I were to manually do each number
// text("1", 30, -radius+PI+20);
// text("2", 60, -radius+PI+30);
// text("3", 90, -radius+PI+40);
angle = (second() / 59.0) * TWO_PI;
// memorize this formula, it's helpful
x = cos(angle)* radius;
y = sin(angle)* radius;
stroke(255,0,255);
//draw a line from the center of our screen and as long as our radius
line(0,0,x,y);
ellipse(x,y,10,10);
}
This is really a question about polar coordinates. Your x and y coordinates in the commented section are off. This is the idea:
var angleOffset = -1*PI/2;
for (var i=1; i<=12; i++) {
angle = 2*PI*i/12 + angleOffset;
text(i, radius*cos(angle), radius*sin(angle));
}
Edit: Full working code below
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.23/p5.min.js"></script>
<script>
var radius = 120.0;
var angle = 0.0;
var x=0, y=0;
var digits = [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
function setup() {
createCanvas(windowWidth,windowHeight);
}
function draw() {
for (var i = 0; i < 12; i++) { //loop for digits. Populate array.
digits[i] = text("[i]", 10, -radius+10);
for (var i = 0; i < digits.length; i++) {
fill(255,0,255)
text([i]);
}
}
background(255);
translate(width/2, height/2);
stroke(205,205,55);
fill(255,0,255);
ellipse(0,0,10,10);
noFill();
ellipse(0,0,250,250);
stroke(25);
fill(205,205,55);
var angleOffset = -1*PI/2;
for (var i=1; i<=12; i++) {
angle = 2*PI*i/12 + angleOffset;
text(i, radius*cos(angle), radius*sin(angle));
}
angle = (second() / 59.0) * TWO_PI;
// memorize this formula, it's helpful
x = cos(angle)* radius;
y = sin(angle)* radius;
stroke(255,0,255);
//draw a line from the center of our screen and as long as our radius
line(0,0,x,y);
ellipse(x,y,10,10);
}
setup();
draw();
</script>
// ABC CLOCK
var radius = 120.0;
var angle = 0.0;
var x = 0,
y = 0;
var digits = [];
var se = 0;
var nloops=0
var sentence="12 1 2 3 4 5 6 7 8 9 10 11"
//var sentence="A B C D E F G"
var nl = 0;
var letter1=''
var letter2=''
function setup() {
createCanvas(windowWidth, windowHeight);
digits = sentence.split(" ");
letter1=digits[0]
letter2=digits[0]
nl = digits.length; //numberOfLetters
textAlign(CENTER, CENTER);
frameRate(1);
}
function draw() {
background(9);
textAlign(LEFT, CENTER);
fill(255)
text(letter1+' '+letter2, 40,40)
//text(nloops+' '+se,40,60)
translate(width / 2, height / 2);
textAlign(CENTER, CENTER);
cont = 0;
mod = (2 * PI) / nl;
ia = -HALF_PI; //init angle
pr1= 0.9; //proportion radius letters
pr2= 0.5; //proportion radius letters
rl = radius * pr1; //radius letter
for (var i = 0; i < nl; i++) {
noFill();
stroke(120);
text(digits[i], rl * cos(cont + ia), rl * sin(cont + ia));
cont += mod;
}
angle = (TWO_PI / nl) * se;
angle2 = (TWO_PI / nl) * nloops;
x = rl * cos(angle + ia);
y = rl * sin(angle + ia);
x1 = rl * cos(angle2 + ia);
y1 = rl * sin(angle2 + ia);
stroke(55);
line(0, 0, x * pr1, y * pr1);
stroke(255);
line(0, 0, x1 * pr2, y1 * pr2);
stroke(55);
fill(0);
ellipse(0, 0, 10, 10);
noFill();
ellipse(0, 0, 250, 250);
se += 1;
if(se%nl==0){
nloops+=1
if (nloops%nl==0){
nloops=0
}
se=0
}
letter1=digits[nloops]
letter2=digits[se]
}
A little more developed

Categories