Is there a way to rotate the ellipse in PlotlyJS? To draw a circle/ellipse in Plotly, you define the center and the radii in the positive and negative x and y directions. Is there a way to define an angle at which the ellipse is rotated around its center or perhaps defining the 4 outermost points of the ellipse instead perhaps rather than having plotly deducing them through the radii values?
You can "manually" construct the points of the ellipse:
var center_x = 0;
var center_y = 0;
var a = 3; // major radius
var b = 1; // minor radius
var alpha = Math.PI / 4; // angle of rotation;
var X = [];
var Y = [];
var npoints = 100;
for(var i = 0; i <= npoints; i++) {
var t = 2 * Math.PI * i / npoints;
var x = a * Math.cos(t);
var y = b * Math.sin(t);
X.push(center_x + Math.cos(alpha)*x - Math.sin(alpha)*y);
Y.push(center_y + Math.sin(alpha)*x + Math.cos(alpha)*y);
}
Then plot it with a line type.
my script isn't working. It was supposed to create random circles all touching the bottom of the canvas, with their radius shrinking each circle by the number of circles.
Here is the code:
for (var i = 0; i < NUM_CIRCLES; i++){
var circle = new Circle(radius);
circle.setPosition(CENTER_X,CENTER_Y);
circle.setColor(color);
add(circle);
radius /= i;
CENTER_Y = getHeight() - radius;
color = Randomizer.nextColor();
}
Ok so here is the full code:
var NUM_CIRCLES = 30;
var BIG_RADIUS = 180;
var color = Randomizer.nextColor();
var radius = BIG_RADIUS;
var CENTER_X = getWidth() / 2;
var CENTER_Y = getHeight() - radius;
function start(){
for (var i = 0; i < NUM_CIRCLES; i++){
var circle = new Circle(radius);
circle.setPosition(CENTER_X,CENTER_Y);
circle.setColor(color);
add(circle);
radius /= i;
CENTER_Y = getHeight() - radius;
color = Randomizer.nextColor();
}
}
as far as i understand you are trying to produce circles from bigger to smaller reducing radius of them by number of circles
to achive that do eachReduce = radius / numberofcircles and use it as eachReduce constant to reduce each circle by that number to achieve bigger to smaller size till zero
take a look at below example
let numofcir = 10,
radius = 80,
reduceEachBy = radius / numofcir,
offset = radius / 2
function setup() {
createCanvas(640, 360);
noLoop()
}
function draw() {
background(0)
stroke(255)
noFill()
for(let i=0;i < numofcir;i++){
// in p5 ellipse creates circle by x and y radius
ellipse(offset + (i * offset),150,radius,radius)
radius -= reduceEachBy
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js" integrity="sha512-N4kV7GkNv7QR7RX9YF/olywyIgIwNvfEe2nZtfyj73HdjCUkAfOBDbcuJ/cTaN04JKRnw1YG1wnUyNKMsNgg3g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
I'd like to evenly devide a n number of chairs around a round table.
Tried several solutions that involve animation an object around a circle, but I'm not able to convert any of them to a solution for my problem.
(http://jsfiddle.net/m1erickson/dFctW/ and http://jsfiddle.net/Cu6Zv/1/)
The project I'm working on involves a chosen amount of chairs that need to be devided among a chosen amount of tables. I managed to sort of build a prototype, but the chairs are not evenly devided and not rotated toward the center of the table.
var step = 360 / chairs;
for(var count = 0; count < chairs; count++){
angle += Math.acos(1-Math.pow(step/radius,2)/2);
var x = cx + radius * Math.cos(angle);
var y = cy + radius * Math.sin(angle);
ctx.rect(x-5,y-5,10,10);
ctx.stroke();
}
I created a jsfiddle of what I've got so far.
Hopefully someone can explain me how to:
Translate the chairs evenly around the circle
Rotate each chair to line up with the table (pointed towards the center of the table)
Perhaps explain the math behind it, so I can understand what it's doing and how it could be adapted if needed.
Thanks in advance.
You're almost at right track with the code. Simply use radians instead and drop the acos line:
var ctx = c.getContext("2d");
var angle = 0;
var chairs = 6;
var cx = c.width>>1, cy = c.height>>1, radius = (c.height>>1)-10;
var step = Math.PI * 2 / chairs;
for(var count = 0; count < chairs; count++){
var x = cx + radius * Math.cos(angle);
var y = cy + radius * Math.sin(angle);
ctx.rect(x-5,y-5,10,10);
angle += step;
}
ctx.stroke();
<canvas id=c></canvas>
Now, all the chairs will face the same direction. If you want to rotate them so they face center of tables it's perhaps easier to use transforms instead of manually calculating the position:
var ctx = c.getContext("2d");
var angle = 0;
var chairs = 6;
var cx = c.width>>1, cy = c.height>>1, radius = (c.height>>1)-10;
var step = Math.PI * 2 / chairs;
// translate to center
ctx.translate(cx, cy);
for(var count = 0; count < chairs; count++){
// rotate around center (0,0)
ctx.rotate(step);
// draw using radius as offser on x-axis only
ctx.rect(radius -5,-5,10,10);
ctx.rect(radius -5, -1, 4,2);
}
ctx.stroke();
<canvas id=c></canvas>
For your first problem, try changing:
var step = 360 / chairs;
to
var step = 360 / (chairs + 1);
I'm trying to display an image with particles. It works, but the amount of particles is dependant on a variable (numberOfParticles) that can range between 0 and 3000. On any value, the image should be rendered in the best way possible with the given amount of particles. There is a nested for loop that goes through the image data (height and width) and creates particles like this.
for (var y = 0; y < data.height; y+=averageDistance) {
for (var x = 0; x < data.width; x+=averageDistance) {
if (particles.length < numberOfParticles){
var particle = {
x0: x,
y0: y,
color: "rgb("+data.data[(y * 4 * data.width)+ (x * 4)]+","+data.data[(y * 4 * data.width)+ (x * 4) +1]+","+data.data[(y * 4 * data.width)+ (x * 4) +2]+")"
};
particles.push(particle);
}
}
}
Later in the code, the particles get rendered with a given size.
My question is, how do I calculate the size the particles should have and the distance that should be between them?
I've tried calculating the 'average distance', counting the amount of pixels that are not covered by particles and dividing that through the amount of particles, but I can't get it to work correctly. There's always leftover space (so the bottom part doesn't get filled) or leftover particles (so there are only 40 particles shown, instead of 50) on some value of the variable numberOfParticles.
A solution to the mathematical part can be found in this answer.
To find number of points for x (nx) we can use that formula:
Then number of points for y (ny):
ny = n / nx
In JavaScript code:
nx = Math.sqrt((w / h) * n + Math.pow(w - h, 2) / (4 * Math.pow(h, 2))) - (w - h) / (2 * h);
ny = n / nx;
Using the numbers nx and ny we can then calculate the deltas for x and y:
dx = w / nx;
dy = h / ny;
Example
var ctx = c.getContext("2d"), n, w, h, nx, ny, dx, dy, x, y;
// define values
n = 1600;
w = c.width - 1; // make inclusive
h = c.height - 1;
// plug values into formula
nx = Math.sqrt((w / h) * n + Math.pow(w - h, 2) / (4 * Math.pow(h, 2))) - (w - h) / (2 * h);
ny = n / nx;
// calculate deltas
dx = w / nx;
dy = h / ny;
// render proof-of-concept
for(y = 0; y < h; y += dy) {
for(x = 0; x < w; x += dx) {
ctx.fillStyle = "hsl(" + (360*Math.random()) + ",50%,50%";
ctx.fillRect(x, y, dx-1, dy-1);
}
}
o.innerHTML = "Points to place: " + n + "<br>" +
"<strong>n<sub>x</sub></strong>: " + nx.toFixed(2) + "<br>" +
"<strong>n<sub>y</sub></strong>: " + ny.toFixed(2) + "<br>" +
"ΔX: " + dy.toFixed(2) + "<br>" +
"ΔY: " + dy.toFixed(2) + "<br>" +
"Total (nx × ny): " + (nx * ny).toFixed(0);
<canvas id=c width=600 height=400></canvas>
<br><output id=o></output>
Best fit maintaining aspect.
These types of packing problems come up a lot in CG.
To fill a square box with smaller square boxes you just need to find the square root of the count you want.
var c = 100; // number of boxes
var w = 10; // box width
var h = 10; // box height
var sW = 1000; // screen width
var sH = 1000; // screen height
The fit is sqrt(c) = sqrt(100) = 10. That's 10 across, divide the screen width by the count sW/10 = 100 to get the width of the box so that 100 will fit the screen.
This works for counts that are squares of integers, if no square root we can factorise to find a better solution. But even then there is not always a solution that fits.
Prime numbers will never fit
Any count that is a prime number will not have a solution, it is impossible to fit a prime number into x rows of y columns. This is because the resulting count is x * y and that means its not a prime.
Compromise
In the end you will need to compromise. Either you control the count to only allow counts with a solution (not a prime) or you accept that there will be some error and allow the solution to go outside the bounds.
This solution will fit but will allow the count to change and the area outside the screen to be at a minimum while still maintaining the box aspect ratio.
The width count is the sqrt((c / sA) * bA) where sA is the screen aspect and bA is the box aspect.
var c = 100; // number of boxes
var w = 10; // box width
var h = 10; // box height
var sW = 1000; // screen width
var sH = 1000; // screen height
var sA = sH / sW; // screen aspect
var bA = h / w; // box aspect
var wCount = Math.sqrt((c / sA) * bA); // get nearest fit for width
wCount = Math.round(wCount); // round to an integers. This forces the width to fit
Now you have the wCount it is just a matter of dividing the screen width by that value to get the box render width and multiply the box render width by the box aspect to get the box render height.
var rW = w / wCount; // the size of the box to render
var rH = rW * bA; // the height is the width time aspect
Sometimes you will get a perfect solution but most times you will not. The actual count will be above of below the requested count due to the rounding. But the fit will be the best for both the required box and screen aspects.
Demo
Updates every 3 seconds with a random box size, random screen size (overlaid with green box to show top and bottom excess) Text shows the column and row counts, the requested count and the actual count.
var canvas,ctx;
function createCanvas(){
canvas = document.createElement("canvas");
canvas.style.position = "absolute";
canvas.style.left = "0px";
canvas.style.top = "0px";
canvas.style.zIndex = 1000;
document.body.appendChild(canvas);
}
function resize(){
if(canvas === undefined){
createCanvas();
}
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx = canvas.getContext("2d");
}
window.addEventListener("resize",resize);
resize();
var w = 10;
var h = 20;
var c = 100;
var sW = canvas.width*0.7;
var sH = canvas.height*0.5;
var sA = sH /sW;
var bA = h / w;
function getParticleWidth(particleImageX,particleImageY,w,h,particleCount){
var a = particleImageY / particleImageX; // get particle aspect
var c = particleCount; // same with this
var b = h/w
return Math.sqrt((c/b)*a)
}
function drawTheParticles(){
var x,y;
pCount = Math.round(Math.sqrt((c/sA)*bA));
var pWidth = sW / pCount;
var pHeight = pWidth * bA;
ctx.lineWidth = 1;
ctx.strokeStyle = "black";
ctx.fillStyle = "white";
ctx.clearRect(0,0,canvas.width,canvas.height); // clear last result
var cc = 0;
var sx = (canvas.width-sW)/2;
var sy = (canvas.height-sH)/2;
var hc = ((Math.ceil(sH / pHeight)*pHeight)-sH)/2;
var wc =0;
for(y = 0; y < sH; y += pHeight){
for(x = 0; x < sW-(pWidth/2); x += pWidth){
ctx.fillRect(x + 1+sx-wc, y + 1+sy-hc, pWidth - 2, pHeight - 2);
ctx.strokeRect(x + 1+sx-wc, y + 1+sy-hc, pWidth - 2, pHeight - 2);
cc ++;
}
}
ctx.strokeStyle = "black";
ctx.fillStyle = "rgba(50,200,70,0.25)";
ctx.fillRect(sx, sy, sW, sH);
ctx.strokeRect(sx, sy, sW, sH);
// show the details
ctx.font = "20px arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
var str = ""+pCount+" by "+Math.ceil(sH / pHeight)+" need " + c + " got "+cc;
var width = ctx.measureText(str).width;
ctx.lineWidth = 2;
// clear an area for text
// with a shadow and not the stupid built in shadow
ctx.fillStyle = "rgba(0,0,0,0.4)";
ctx.fillRect((canvas.width / 2) - (width + 8) / 2+6, (canvas.height / 2) - 14+6, width + 8, 28 );
ctx.fillStyle = "#CCC";
ctx.fillRect((canvas.width / 2) - (width + 8) / 2, (canvas.height / 2) - 14, width + 8, 28 );
ctx.fillRect((canvas.width / 2) - (width + 8) / 2, (canvas.height / 2) - 14, width + 8, 28 );
// now draw the text with a bit of an outline
ctx.fillStyle = "blue"
ctx.strokeStyle = "white";
ctx.lineJoin = "round";
ctx.strokeText(str, canvas.width/2, canvas.height / 2);
ctx.fillText(str, canvas.width/2, canvas.height / 2);
// And set up to do it all again in 3 seconds
// get random particle image size
w = Math.floor(Math.random() * 100 + 10);
h = Math.floor(Math.random() * 100 + 10);
// get random particle count
c = Math.floor(Math.random() * 500 + 10);
// get random screen width height
sW = canvas.height*(Math.random()*0.4 + 0.6);
sH = canvas.height*(Math.random()*0.6 + 0.4);
// recaculate aspects
sA = sH /sW;
bA = h / w;
// redo it in 3 seconds
setTimeout(drawTheParticles,3000)
}
drawTheParticles()
So I have this code:
var points = 6;
var width = w;
var height = h;
var angle = ((2 * Math.PI) / points);
var hexagon = [];
for (i = 0; i < points; i++) {
hexagon.push({
x: width * Math.cos(angle * i),
y: height * Math.sin(angle * i)
})
}
Which will produce 6 points/locations and if I draw a line between them I get a hexagon, but I want to rotate the points or generate the points differently so that the first point is always center-top, like so:
How would I go about doing that?
You could just swap sin and cos.