How to translate and rotate chairs around a table - javascript

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);

Related

PlotlyJS rotate ellipse

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.

Fill content of a roulette wheel

I have roulette wheel (as an image) without numbers. I want to dynamically fill the numbers of a roulette wheel at the right positions and with the right angle. I want to absolute position the numbers on that image.
My wheel image is 1000 x 1000 pixel.
I try to set the positions and angles with a loop, but the positions are not linear and (in my non-mathematical eyes) to random.
const roulette_arr = []; //contains als numbers in right order. 0..32..15..19...
for (let i = 0; i < roulette_arr.length; i++) {
let degree = 10 + (i * 10); //360 degree / 36 Numbers
let position_x=...
let position_y=...
//function do all the stuff later
setNumber(roulette_arr[i], degree, position_x, position_y);
}
I think angle works, but position is still a problem. What can I do?
It's trigonometry. Is there a bigger problem?
var can = document.getElementById("can");
var ctx = can.getContext("2d");
var numbers = "0-32-15-19-4-21-2-25-17-34-6-27-13-36-11-30-8-23-10-5-24-16-33-1-20-14-31-9-22-18-29-7-28-12-35-3-26".split("-");
var cx = can.width / 2;
var cy = can.height / 2;
var cr = Math.min(cx, cy) * 0.9;
//
for (var i = 0; i < numbers.length; i++) {
var angle = i / numbers.length * 360;
var rad = i / numbers.length * Math.PI * 2 - Math.PI / 2;
var x = cx + Math.cos(rad) * cr;
var y = cx + Math.sin(rad) * cr;
ctx.fillText(numbers[i], x, y);
}
<canvas width="400" height="400" id="can">oh no</canvas>

Apply noise on circle according to mouseX and mouseY

I have created a blob with small points. I want my blob to show noise on its surface according to mouseX and mouseY. I want it to show high noise in the quadrant in which the mouse lies. I want it to be wavy. Below is my code.
var ctx = document.querySelector("canvas").getContext("2d");
var cx = 200;
var cy = 200;
var radius = 50;
var amp = 2;
var mouseX = 0;
var mouseY = 0;
document.querySelector("canvas").addEventListener("mousemove", function (e) {
mouseX = e.clientX;
mouseY = e.clientY;
});
function drawTheBlob() {
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, 400, 400);
ctx.beginPath();
ctx.strokeStyle = "#fff";
for (var a = 0; a < 360; a ++) {
var angle = a * Math.PI/180;
var x = cx + radius * Math.cos(angle) + Math.random() * amp;
var y = cy + radius * Math.sin(angle) + Math.random() * amp;
ctx.lineTo(x, y);
}
ctx.stroke();
ctx.closePath();
requestAnimationFrame(drawTheBlob);
}
drawTheBlob();
<canvas width="400" height="400"></canvas>
Adds a sin wave on the circle. Use ctx.arc to draw the flat part of the circle for speed as drawing many circles with lines will be slow. See code for comments on what is done.
var ctx = document.querySelector("canvas").getContext("2d");
ctx.lineWidth = 3;
ctx.lineJoin = "round";
var cx = 100;
var cy = 100;
var radius = 50;
var mouseX = 0;
var mouseY = 0;
const quadWidth = Math.PI / 2; // area of effect PI/2 is 90 degree
const steps = radius / quadWidth; // number steps around the circle matches 1 pixel per step,
const noiseAmpMax = 5; // in pixels
const noiseWaveMoveSpeed = 2; // speed of waves on circle in radians per second
const noiseWaveFreq = 16; // how many waves per 360 deg
document.querySelector("canvas").addEventListener("mousemove", function(e) {
mouseX = e.clientX;
mouseY = e.clientY;
});
function drawTheBlob(time) { // time is passed from the requestAnimationFrame call
var amp = 0; // amplitude of noise
var wavePos = ((time / 1000) * Math.PI) * noiseWaveMoveSpeed;
var mouseDir = Math.atan2(mouseY - cy, mouseX - cx);
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, 400, 400);
ctx.beginPath();
ctx.strokeStyle = "#fff";
ctx.fillStyle = "red";
// draw arc for parts that have no noise as it is a log quicker
ctx.arc(cx, cy, radius, mouseDir + quadWidth / 2, mouseDir + Math.PI * 2 - quadWidth / 2);
for (var a = 0; a < 1; a += 1 / steps) {
var angle = (mouseDir - quadWidth / 2) + a * quadWidth;
var angDist = Math.abs(angle - mouseDir); // find angular distance from mouse
// as a positive value, it does not mater
// what the sign is
if (angDist < quadWidth / 2) { // is angle distance within the range of effect
// normalise the distance (make it 0 to 1)
amp = 1 - angDist / (quadWidth / 2);
} else {
amp = 0; // no noise
}
// amp will be zero if away from mouse direction and 0 to 1 the closer to
// mouse angle it gets.
// add a sin wave to the radius and scale it by amp
var dist = radius + Math.sin(wavePos + noiseWaveFreq * angle) * noiseAmpMax * amp;
var x = cx + dist * Math.cos(angle);
var y = cy + dist * Math.sin(angle);
ctx.lineTo(x, y);
}
ctx.closePath(); // use close path to close the gap (only needed if you need to draw a line from the end to the start. It is not needed to match beginPath
ctx.fill();
ctx.stroke();
requestAnimationFrame(drawTheBlob);
}
requestAnimationFrame(drawTheBlob); // start this way so that you get the time argument
<canvas width="200" height="200"></canvas>
How it works.
Mouse direction
First we need the direction from the circle to the mouse. To do that we use the function Math.atan2 It takes the vector from the circle to the mouse and returns the direction in radians. The function is a little weird as it takes y first, then x.
var mouseDir = Math.atan2(mouseY - cy, mouseX - cx);
Draw arc to save CPU time
Now that we have the direction to the mouse we can draw the parts of the circle that has no noise using arc .
ctx.arc(cx, cy, radius, mouseDir + quadWidth / 2, mouseDir + Math.PI * 2 - quadWidth / 2);
The variable quadWidth is angular size of the noise bit so from the mouseDir we add half that angular width and draw the arc around to mouseDir plus 360deg take half the quadWidth.
Quick word on Radians
Almost all programming languages use radians to define angles, 360deg is equal to 2 * PI or 2 * 3.1415, which can be hard to get your head around, but there is good reason to use radians. For now just remember that a full circle in radians is 2 * Math.PI = 360deg, Math.PI = 180deg, Math.PI / 2 = 90deg, Math.PI / 4 = 45Deg and Math.PI / 180 = 1deg. You dont have to remember the digits just Math.PI is half a circle.
quadWidth from above is a constant defined as const quadWidth = Math.PI / 2; which is 90deg.
The for loop
The for loop only draws the (Math.PI / 2) 90deg section around the mouseDir, from 45 deg left to 45 right. or whatever you set quadWidth to.
for (var a = 0; a < 1; a += 1 / steps) {
I loop from 0 to 1 the number of steps that give a reasonably smooth curve. We can find what part of the noisy arc we are drawing by multiplying the value a *
quadWidth and adding that to the mouseDir - quadWidth / 2. This means that we start at mouseDir - 45deg and move clock wise to mouseDir + 45deg
var angle = (mouseDir - quadWidth / 2) + a * quadWidth;
Next i find how far that angle is from the mouseDir (could optimize it here a bit here but this way is a little more flexible, if you want to draw more noise on the other part of the arc)
var angDist = Math.abs(angle - mouseDir);
If that number is less than quadWidth / 2 convert the value to the range 0 to 1 where 0 is at the angle furthest from the mouse direction and 1 closest.
if (angDist < quadWidth / 2) {
amp = 1 - angDist / (quadWidth / 2);
} else {
amp = 0;
}
The sin wave
Now we calculate the radius of the circle at the current angle and add a sin wave to it. First the radius then the sin wave multiplied by the amp calculated in the last step. Where amp is zero none of the sin wave is added, where amp is 1 (in the direction of the mouse) the full sin wave is added.
var dist = radius + Math.sin(wavePos + noiseWaveFreq * angle) * noiseAmpMax * amp
The values wavePos, noiseWaveFreq, and noiseAmpMax control the animation of the sin wave. Play around with these values to get a feel of what they do, wavePos is calculated based on the time at the start of the function.
With dist we can calculate the x,y position for the next line of the circle
var x = cx + dist * Math.cos(angle);
var y = cy + dist * Math.sin(angle);
ctx.lineTo(x, y);
Experiment
I added some constants
const quadWidth = Math.PI / 2; // area of effect PI/2 is 90 degree
const steps = radius / quadWidth; // number steps around the circle matches 1 pixel per step,
const noiseAmpMax = 5; // in pixels
const noiseWaveMoveSpeed = 2; // speed of waves on circle in radians per second
const noiseWaveFreq = 16; // how many waves per 360 deg
To get a understanding what they do experiment and change the numbers to see what happens.

aligning n-circels on a circle > no overlapping

for a data visualization im aligning n-circels on a circle.
That just works fine - but i dont't know how to stop the circles
overlapping each other. Anybody here knows howto?
The result should work like this sketch:
Link: http://www.xup.to/dl,79345003/sketch.jpg
So i dont know how to calculate the angle for the second node
- based on the radius an position of the first node - and the
radius of the second ...
JSFIDDLE to show what i mean: http://jsfiddle.net/0z9hyvxk/
var canvas = document.getElementById("canvas");
var stage = new createjs.Stage(canvas);
canvas.width = 500;
canvas.height = 500;
var midx = 250;
var midy = 250;
var radius = 200;
var angle = 0;
var count = 30;
var step = 2 * Math.PI / count;
var xpos;
var ypos;
var nodeSize;
var node = function(size){
var dot = new createjs.Shape();
dot.graphics.beginFill("#000").drawCircle(0, 0, size);
dot.x = dot.y = -5;
dot.alpha = .25;
return dot
};
for(var i = 0; i<count; i++)
{
xpos = radius * Math.cos(angle) + midx;
ypos = radius * Math.sin(angle) + midx;
nodeSize = i;
var n = new node(nodeSize);
n.x = xpos;
n.y = ypos;
stage.addChild(n)
angle += step;
}
stage.update();
thanks in advance
simon
Your program does not make corrections based on circle sizes and angle. Smaller circles are too far from each other, bigger ones are too close.
r1 = radius of the n-th small circle
r2 = radius of the (n+1)-th small circle.
r3 = radius of the (n+2)-th small circle
r1<r2<3, so angle between 1 and 2 is smaller than between 2 and 3.
Try to tangentially increase angle correction. I can't test code at work :(

Circle coordinates to array in Javascript

What's the best way to add the coordinates of a circle to an array in JavaScript? So far I've only been able to do a half circle, but I need a formula that returns the whole circle to two different arrays: xValues and yValues. (I'm trying to get the coordinates so I can animate an object along a path.)
Here's what I have so far:
circle: function(radius, steps, centerX, centerY){
var xValues = [centerX];
var yValues = [centerY];
for (var i = 1; i < steps; i++) {
xValues[i] = (centerX + radius * Math.cos(Math.PI * i / steps-Math.PI/2));
yValues[i] = (centerY + radius * Math.sin(Math.PI * i / steps-Math.PI/2));
}
}
Your loop should be set up like this instead:
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));
}
Start your loop at 0
Step through the entire 2 * PI range, not just PI.
You shouldn't have the var xValues = [centerX]; var yValues = [centerY]; -- the center of the circle is not a part of it.
Bresenham's algorithm is way faster. You hear of it in relation to drawing straight lines, but there's a form of the algorithm for circles.
Whether you use that or continue with the trig calculations (which are blazingly fast these days) - you only need to draw 1/8th of the circle. By swapping x,y you can get another 1/8th, and then the negative of x, of y, and of both - swapped and unswapped - gives you points for all the rest of the circle. A speedup of 8x!
Change:
Math.PI * i / steps
to:
2*Math.PI * i / steps
A full circle is 2pi radians, and you are only going to pi radians.
You need to use a partial function to input the radians into cos and sin; therefore take the values you're getting for a quarter or half of the circle, and reflect them over the center points' axis to get your full circle.
That said JavaScript's sin and cos aren't quite as picky, so you must have halved your radian or something; I'd write it as:
function circle(radius, steps, centerX, centerY){
var xValues = [centerX];
var yValues = [centerY];
var table="<tr><th>Step</th><th>X</th><th>Y</th></tr>";
var ctx = document.getElementById("canvas").getContext("2d");
ctx.fillStyle = "red"
ctx.beginPath();
for (var i = 0; i <= steps; i++) {
var radian = (2*Math.PI) * (i/steps);
xValues[i+1] = centerX + radius * Math.cos(radian);
yValues[i+1] = centerY + radius * Math.sin(radian);
if(0==i){ctx.moveTo(xValues[i+1],yValues[i+1]);}else{ctx.lineTo(xValues[i+1],yValues[i+1]);}
table += "<tr><td>" + i + "</td><td>" + xValues[i+1] + "</td><td>" + yValues[i+1] + "</td></tr>";
}
ctx.fill();
return table;
}
document.body.innerHTML="<canvas id=\"canvas\" width=\"300\" height=\"300\"></canvas><table id=\"table\"/>";
document.getElementById("table").innerHTML+=circle(150,15,150,150);
I assumed that for whatever reason you wanted xValues[0] and yValues[0] to be centerX and centerY. I can't figure out why you'd want that, as they're values passed into the function already.
If you already have half a circle, just mirror the points to get the other half
make sure you do this in the right order.
more speficically, for the other half you simply replace the "+ sin(...)" with a "- sin(...)"
I was able to solve it on my own by multiplying the number of steps by 2:
circle: function(radius, steps, centerX, centerY){
var xValues = [centerX];
var yValues = [centerY];
for (var i = 1; i < steps; i++) {
xValues[i] = (centerX + radius * Math.cos(Math.PI * i / steps*2-Math.PI/2));
yValues[i] = (centerY + radius * Math.sin(Math.PI * i / steps*2-Math.PI/2));
}
}

Categories

Resources