I'm trying to learn canvas by implementing a pie chart. I've managed to parse my data, draw the slices, and calculate the center of each arc, as noted by the black circles. But now I'm trying to draw one of the slices as though it had been "slid out". Not animate it (yet), just simply draw the slice as though it had been slid out.
I thought the easiest way would be to first calculate the point at which the new corner of the slice should be (free-hand drawn with the red X), translate there, draw my slice, then translate the origin back. I thought I could calculate this easily, since I know the center of the pie chart, and the point of the center of the arc (connected with a free-hand black line on the beige slice). But after asking this question, it seems this will involve solving a system of equations, one of which is second order. That's easy with a pen and paper, dauntingly hard in JavaScript.
Is there a simpler approach? Should I take a step back and realize that doing this is really the same as doing XYZ?
I know I haven't provided any code, but I'm just looking for ideas / pseudocode. (jQuery is tagged in the off chance there's a plugin will somehow help in this endeavor)
Getting the x and y of the translation is easy enough.
// cx and cy are the coordinates of the centre of your pie
// px and py are the coordinates of the black circle on your diagram
// off is the amount (range 0-1) by which to offset the arc
// adjust off as needed.
// rx and ry will be the amount to translate by
var dx = px-cx, dy = py-cy,
angle = Math.atan2(dy,dx),
dist = Math.sqrt(dx*dx+dy*dy);
rx = Math.cos(angle)*off*dist;
ry = Math.sin(angle)*off*dist;
Plug that into the code Simon Sarris gave you and you're done. I'd suggest an off value of 0.25.
Merely translating an element on a canvas is very easy and there shouldn't be any tricky equations here. In the most basic sense it is:
ctx.save();
ctx.translate(x, y);
// Draw the things you want offset by x, y
ctx.restore();
Here's a rudimentary example of a square pie and the same pie with one of the four "slices" translated:
http://jsfiddle.net/XqwY2/
To make the pie piece "slide out" the only thing you need to calculate is how far you want it to be. In my simple example the blue block is slid out 10, -10.
If you are wondering merely how to get the X and Y you want in the first place, well, that's not quite a javascript/canvas question. For points on a line given a distance this question: Finding points on a line with a given distance seems the most clear
Edit, here you are (from comments):
// Center point of pie
var x1 = 100;
var y1 = 100;
// End of pie slice (your black dot)
var x2 = 200;
var y2 = 0;
// The distance you want
var distance = 3;
var vx = x2 - x1; // x vector
var vy = y2 - y1; // y vector
var mag = Math.sqrt(vx*vx + vy*vy); // length
vx = mag/vx;
vy = mag/vy;
// The red X location that you want:
var px = x1 + vx * ( distance);
var py = y1 + vy * ( distance);
This would give you a px,py of (104.24, 95.76) for my made-up inputs.
Related
Could you help me please find coordinates of point on a plane?
I try to find coordinates of the point.
If working with JavaScript, you would employ Math.sin() and Math.cos()
If you imagine a unit circle (a circle with radius 1),
and a straight line A starting from the circle's center going towards the
edge,
and you know the line A's angle (in radians). Or the angle in degrees to a reference line which points straight right (on your drawing, the reference line would be +90 degrees to the one which shows radius), in which case if line A is in the top half of the circle the angle would be positive, while if in the bottom half of the circle (like your drawing) the angle would be negative.
Of course, if you only have the angle in degrees, you would have to convert it to radians before passing it to sine and cosine functions. It is a simple operation, you can find many examples online:
function degrees_to_radians(degrees)
{
var pi = Math.PI;
return degrees * (pi/180);
}
Then Math.sin(angleInRadians) would tell you the Y location of the spot where line intersects the circle, while Math.cos(angleInRadians) would tell you the X location. Both X and Y would be relative to the circle center.
And, since the result is for the unit circle, you would also have to multiply both X and Y with actual radius (250). And then add circle's center location (543,250) to get the actual world coordinates of the point.
X = (X * 250) + 543 and Y = (Y * 250) + 250
Hope that helped, you can use Google image search to get some sine and cosine drawings if you're not getting clear picture.
Formula:
x = R * cos(a1) = R * sqrt(1 - ((y + ∆y) / R)^2)
Solution:
let delta = 466.5 - 440.5;
let x = 250 * Math.sqrt(1 - Math.pow(466.5 + delta) / 250, 2));
Scenario
In this interactive, let's say the user clicks in four places on a canvas. I want my fill to follow the exact order of clicks. Please consider my "demonstration" below
The GREEN line shows the fill area I'm going for (the red is min bounding box and can be safely ignored).
I need to clip/mask the area otherwise I would use a stroke (easy). I have yet to find a processor friendly way to convert a stroke to a fill or I would use that (this has to be usable on mobile devices).
Question
How can I programmatically generate the green bounding path (fill) for these click points? I have all the coordinate (mouseevents) in an array to loop through.
Code
Here's the simplified stroke equivalent of what I'm trying to accomplish (jsfiddle)
var c = document.getElementById("c");
var ctx = c.getContext("2d");
ctx.moveTo(40, 40);
ctx.lineTo(180, 40);
ctx.lineTo(40, 180);
ctx.lineTo(180, 180);
ctx.lineWidth = 40;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.stroke();
Been working on this all day. Here's what I came up with:
Most important piece:
You HAVE to know the current orientation / direction of the cursor before you can save a point to the array. This is crucial and if you mess this up you will get a path that collapses on itself.
Here's code for that:
var radians = null;
var x2 = lastMouseEvent.clientX,
x1 = currentMouseEvent.clientX,
y2 = lastMouseEvent.clientY,
y1 = currentMouseEvent.clientY
radians = Math.atan2(y1 - y2, x1 - x2) * -1 // you can remove the -1 if everything is backwards for your app
collect all mouse coords in an array, store the current radians (inside/outside) too
loop through array drawing a line for the INSIDE of the path, factor in orientation of the pointer
reverse array to start drawing outside of path
connect last point from INSIDE track to first point of OUTSIDE track
loop through the array again, drawing a line for the OUTSIDE of the path, factor in orientation of the pointer
connect last point from OUTSIDE track to first point of INSIDE track
If you don't make sure all your points connect, you won't get a complete path and this won't work
Here's how you can factor in orientation, depending on track:
var radians = [orientation radians stored in your array]
var diameter = [diameter you want your path to be]
var inside = {
x: Math.sin(radians) * (diameter / 2),
y: Math.cos(radians) * (diameter / 2)
}
var outside = {
x: Math.sin(radians) * (diameter / -2),
y: Math.cos(radians) * (diameter / -2)
}
And all you do is add x/y values from your respect track to the coordinates you're currently looping through.
Yes, this is a pain in the ass, but the performance is great on all devices. GL HF
I created an array of small circles (dots) laid on a circumference of a larger circle in a loop, with each iteration I rotate the same dot with the same cx and cy by a different angle using transform=rotate(i*angle,0,0).
Now I want to connect a line between two of the dots, but since each dot has the same cx and cy, my naive way of passing in the cx and cy of the two dots as coordinates doesn't seem to work.
Strangely on JSFiddle, half of the line is shown, even though the line's x0 == x1 and y0 == y1: https://jsfiddle.net/8wn30vqn/1/
What is a good way of obtaining the coordinates of a transformed svg element so I can pass them into the line? Or is there some other way I can connect two elements with a line?
The good way would be to take transformation matrix from each of your small circles and apply it to the same point to get new point (and then connect these new points).
var svgNode = d3circle.node();
var matrix = svgNode.transform.baseVal.consolidate().matrix;
var pt = svgNode.ownerSVGElement.createSVGPoint();
pt.x = 0;
pt.y = 0;
var transformedPoint = pt.matrixTransform(matrix);
... use transformedPoint to create line
I have a small little game I'm making in javascript and Raphael.js(which i'm fairly new to) and I'm making a turret essentially, just a circle that has a rectangle swivel around it. And that works fine and dandy!
Code for transform is :
this.self = this.self.animate({ transform : this.transform }, 250);
However, I need to find the coords of the rectangle after I animate it, but getBBox() keeps getting the same coords. Does anyone have any suggestions? A visual picture of the transform would be:
So I need the turret coords after the transformation. I need to find the front of the turret so I know where the bullet needs to come out of! Any advice will be appreciated!
By using the rotation number, will help you to find the coordinates. Lets say the rotation angel is q = 45 degrees.
This means that y changes by asin(q) and x changes by a - acos(q).
EDIT
Pay attention to all cases. In this particular case, both coordinates got decreased, but if you turn to southeast, then y increases and x decreases. Or if northwest: y and x decrease.
Transform is just a visual effect, it's not affects on coordinates.
You know width of turret and you know rotation angle.
Use sin & cos to calculate new coords.
X = Math.cos((i * Math.PI) / 180) * R + x;
Y = Math.sin((i * Math.PI) / 180) * R + y;
i - angle
R - width of turret
x and y - turret offset
I am currently working on a game using javascript and processing.js and I am having trouble trying to figure out how to move stuff diagonally. In this game, there is an object in the center that shoots other objects around it. Now I have no problem moving the bullet only vertically or only horizontally, however I am having difficulty implementing a diagonal motion for the bullet algorithm.
In terms of attempts, I tried putting on my math thinking cap and used the y=mx+b formula for motion along a straight line, but this is what my code ends up looking like:
ellipse(shuriken.xPos, shuriken.yPos, shuriken.width, shuriken.height); //this is what I want to move diagonally
if(abs(shuriken.slope) > 0.65) {
if(shuriken.targetY < shuriken.OrigYPos) {
shuriken.yPos -= 4;
} else {
shuriken.yPos += 4;
}
shuriken.xPos = (shuriken.yPos - shuriken.intercept)/shuriken.slope;
} else {
if(shuriken.targetX < shuriken.OrigXPos) {
shuriken.xPos -= 4;
} else {
shuriken.xPos += 4;
}
shuriken.yPos = shuriken.slope * shuriken.xPos + shuriken.intercept;
}
The above code is very bad and hacky as the speed varies with the slope of the line.
I tried implementing a trigonometry relationship but still in vain.
Any help/advice will be greatly appreciated!
Think of it this way: you want the shuriken to move s pixels. If the motion is horizontal, it should move s pixels horizontally; if vertical, s pixels vertically. However, if it's anything else, it will be a combination of pixels horizontally/vertically. What's the correct combination? Well, what shape do you get if you project s distance in any direction from a given point? That's right, a circle with radius s. Let's represent the direction in terms of an angle, a. So we have this picture:
How do we get the x and the y? If you notice, we have a triangle. If you recall your trigonometry, this is precisely what the sine, cosine, and tangent functions are for. I learned their definitions via the mnemonic SOHCAHTOA. That is: Sin (a) = Opposite/Hypotenuse, Cos(a) = Adjacent/Hypotenuse, Tan(a) = Opposite/Adjacent. In this case, opposite of angle a is y, and adjacent of angle a is x. Thus we have:
cos(a) = x / s
sin(a) = y / s
Solving for x and y:
x = s * cos(a)
y = s * sin(a)
So, given the angle a, and that you want to move your shuriken s pixels, you want to move it s * cos(a) horizontally and s * sin(a) vertically.
Just be sure you pass a in radians, not degrees, to javascript's Math.sin and Math.cos functions:
radians = degrees * pi / 180.0
This may be why your trigonometric solution didn't work as this has bitten me a bunch in the past.
If you know the angle and speed you are trying to move at, you can treat it as a polar coordinate, then convert to cartesian coordinates to get an x,y vector you would need to move the object by to go in that direction and speed.
If you don't know the angle, you could also come up with the vector by taking the difference in X and difference in Y (this I know you can do as you are able to calculate the slope between the 2 points). Then take the resulting vector and divide by the length of the vector to get a unit vector, which you can then scale to your speed to get a final vector in which you can move your object by.
(This is what probably what kennypu means by sticking with vectors?)