how can we draw a polygon. when only the sides and radius is given.
I have to make a pop up box which will take as input the radius and number of sides and will draw a ploygon. just need the formula.
Imagine a circle of radius r. It is like a regular polygon with an infinite number of sides.
Trigonometry tells us:
x = r * cos(a);
y = r * sin(a);
We know there are 360 degrees or 2pi radians in a circle. So to draw it we would start with angle = 0, calculate that co-ord, step to the next angle and calculate that point, then draw a line between the two.
There are only so many points we can calculate around the edge of the circle, eventually it won't make any difference. If the circle is small enough, even 8 sides will look round.
To draw an 8 sided circle we want 8 points evenly spaced around the circle. Divide the circle into 8 angles, each one is 2 * pi / 8 radians.
So:
angle = 0.0;
step = 2 * pi / 8;
for ( n = 0; n < 8; n++ ) {
x = radius * cos(angle);
y = radius * sin(angle);
angle += step;
}
Now you can draw an octagon, change it to draw the general case.
Related
I have a custom Dougnut chart drawer, which can draw doughnuts like this:
I'm pretty satisfied with this, however I'd like to finetune it and I just have no idea of the right solution.
I'd like to have parallel gaps (paddings) between slices.
I raised those paddings to have better view of my goal.
This is how I'm drawing this currently:
double cx, cy; //center points of circle
double r1, r2; //radius of outer and inner circle
double pad = M_PI / 360 * 12; //12 degree pad
double alpha = -1 * M_PI / 2; //starting from up (noon)
double da = 0.0; // delta of current slice
for (ALL_SLICES) {
calculate_da(&da); //calculate slice's delta
// drawing
move_pen(cx + r2 * cos(alpha + pad/2), cy + r2 * sin(alpha + pad/2)); //STEP1
draw_arc(cx, cy, r1, alpha + pad/2, alpha + da - pad/2); //STEP2
draw_negative_arc(cx, cy, r2, alpha + da - pad/2, alpha + pad/2); //STEP3
fill();
//update next alpha
alpha += da;
}
Drawing steps:
So this is fairly simple, however to have parallel gaps between slices, I'd have to calculate another angle for the outer end and start points:
I've drawn quite a lot of triangles already but I was not able to solve this easily.
Again: the goal is to have parallel paddings between the slices, i.e. bringing these 2 red dots closer together on the circumference, to get exact parallel lines between two slices.
Update:
This is how it looks with scaled padding from #chux answer:
Unfortunately it's not perfect, these lines are not parallel:
"Pad should be 12degree" --> If you want parallel lines, the inner padding angle and outer padding angles are different - not both 12°.
Use a scaled padding.
Given the outer padding angle is double pad = ...,
Draw first arc as before.
draw_arc(cx, cy, r1, alpha + pad/2, alpha + da - pad/2); //STEP2
The inner padding angle is proportionally larger.
double inner_pad = pad*r1/r2;
draw_negative_arc(cx, cy, r2, alpha + da - inner_pad/2, alpha + inner_pad/2); //STEP3
Tip: Less confusing to use r_inner, r_outer, than r2, r1.
Minor: Padding calculation looks off by 2x.
// double pad = M_PI / 360 * 12; //12 degree pad.
double pad = 2 * M_PI / 360 * 12; //12 degree pad.
We'll solve this for the case of one arbitrary gap. Then, you can apply our solution to any number of gaps.
First, we can calculate the average of the angles at which the two boundaries radiate from the center. This will be the angle at which our two new segments will radiate from the inner circle to the outer circle. If the original segments radiate from the center of the inner circle at angles a and b, let c be the angle going through the middle of the smaller segment of the circle between them. If a = 60 deg and b = 90 deg, choose c = 75 deg as an example.
Now, for the points of intersection with the edge of the inner circle you've already found, place lines whose slopes have angle c (w.r.t. the positive x-axis, as normal). Then, find the correspondibg points of intersection with the outer circle. These new outer points and your old inner points define the parallel segments that you are looking for.
Example: inner circle radius r = 10; outer circle radius 20; angles a = 30 deg and b = 60 deg. Your current inner and outer points are p1 = (5sqrt(3), 5), p2 = (5, 5sqrt(3)), q1 = 2p1, q2 = 2p2 (assume the center of the inner circle is at the origin here). Calculate c = 45 deg. The slope of lines with this angle can be found using tangent; m = 1. Define two lines going through p1 and p2 with slope 1; we get y = x + 5(1 - sqrt(3)) and y = x + 5(sqrt(3) - 1). Now we find the points of intersection of these lines with the outer circle; I will leave this as an exercise but basically just take the equation of the outer circle, x^2 + y^2 = 400, replace y in this with the right-hand side of each equation, and solve for x. You will get two solutions (a line passing through a point inside the outer circle must intersect the circle in two places)... pick the one the is in the smaller segment defined by your original outer points.
This seems pretty tedious to do by hand, and it is, but in reality once you write the code once the computer will have no issue doing this for you all day long.
(Please comment if you need me to try to work out the points of intersection in the example, on my phone now).
For #chux - Reinstate Monica's request, I'm posting here my final solution which relies heavily on his link in comment: Radius and central angle on a circular segment.
1.) Set the initial constants:
double pad = PADDING_VALUE;
double r_inner = INNER_RADIUS;
double r_outer = OUTER_RADIUS;
2.) Calculate the slice's starting point's coordinates (on the inner circle):
double alpha = CURRENT_SLICE_ANGLE;
double inner_angle = alpha + pad/2; // slice will begin with pad/2 offset
double x1 = r_inner * cos(inner_angle);
double y1 = r_inner * sin(inner_angle);
3.) Now calculate the previous slice's ending point's coordinates (also on the inner circle):
double x2 = r_inner * cos(inner_angle - pad); //easy, exactly angle-pad
double y2 = r_inner * sin(inner_angle - pad); //easy, exactly angle-pad
4.) Now I have the coords for the current slice's start and the previous slice's end. I want to keep a constant length between the 2 slices, and this length should be exactly the segment's length between (x1,y1) and (x2,y2). This is a right-angled triangle, and the segment's length I'm looking for is easily expressable with Pitagoras formula:
double hyp = sqrt(pow(x1-x2,2) + pow(y1-y2,2));
5.) This hyp is exactly the chord length (c) here:
6.) From the same Wikipedia page the Theta is expressible with chord length and radius:
double theta = 2 * asin(hyp / 2 / r_outer);
7.) I have to draw the outer arc with the help of theta and pad:
double outer_angle_correction = (pad - theta) / 2;
Applying this calculation results this:
This is a bit odd, as the gaps are a bit too large. But anyway these huge gaps were only used for demonstration, and after I changed them back to my initial intended values this is the result:
Perfectly parallel gaps between all slices without using any approximation - just pure math. Sweet.
So I want to make a script that can draw various microscope darkfield filters.
in those you have 3 support bars with a 120° angle holding a circle inside a circle.
I came up with something but wrongfully calculated the blue triangle value while I should have computed the purple triangle value.
I had to wake up my 9 years old trigonometry knowledge and it worked however I can't figure a way to compute the purple triangle values.
Here is an image :
for now since I'm computing the blue triangle I get this result :
Would anyone know what I need to compute the purple triangle?
Thanks.
Three intersect points will have coordinates (if circle center is 0,0), where i = 0,1,2:
for i = 0..2 do
Fi = Pi/2 + i * 2 * Pi / 3 //probably -Pi/2 depending on your graphics coordinate system
X = R * Cos(Fi)
Y = R * Sin(Fi)
Trivially: ac = r;
For de we look at half the side of an equal sided triangle: de = sin(a) = sin(30 degrees) = r/2
When it comes to ad we can use Pythagoras to compute; ad = cos(a)= cos(30 degrees) = r*sqrt(3)/2:
I have a bounding Box that is represented as a Cartesian starting point(0,0) with a width and height.
I have a circle with centre point that can be anywhere within the bounding box.
the circumference of the circle is fixed.
When the circle intersects the edge of the bounding box an arc is formed.
This new arc has to have a length = to the circumference of the original circle.
The location of the centre of the circle is is known there for the distance from the centre to the edge of the bounding box is know.
as you move closer to the edge of the bound box the radius of the circle must increase to keep the arc length the same
the start and stop points of the arc are unknown as the radius is unknown.
This is where I'm stuck. knowing only the distance from the bounding box and the fixed length of the arc how can I find the radius of the circle ?
I have drawn an image to represent the question but I'm unable to post due to lack of reputation.
Any help on this will be greatly appreciated as I have spent many days trying to figure this out.
What I am trying to achieve is a radial menu with fixed number of items (of a fixed size) can be displayed around a centre point. the fixed length is a calculated length that all menu items can fit around.
I am implementing this in .net but for the sake of this query its purely a Math question.
Edit: here is image of the issue:
Here is a possible line of attack. Let's put some names:
alpha = angle at which the circle intercepts the horizontal line on the right side
r = radius
arc = length of the "visible" circle (known)
L = length to edge (known) (Let's assume L > 0)
Pi the number pi.
Using that arc = radius * angle (radians), we have:
arc = Pi * r + 2 * alpha * r
sin(alpha) = L / r
Solving for alpha in the first equation
alpha = arc / (2 * r) - Pi / 2
Using that sin(a - b) = sin(a)cos(b) - cos(a)sin(b)
L / r = sin(alpha) = -cos(arc / (2 * r))
Now put u = L/r. Since L is known, u becomes the unknown. Replacing:
u = -cos(arc / (2 * L) * u)
Finally put F = arc / (2 * L). Then F is known and
u = - cos(F * u)
So, the problem reduces to solve this equation, which will require some numerical algorithm.
What I have done is created a multiplier and maped the distance from the edge so Y = 0 to 150 and mapped that down to 2 to 1 so if Y = 150 the map = 1 and if Y = 0 the map is 2 so if y = 75 then the map = 1.5 ect
This mapping is then used as a multiplier
radius = radius * map
This gets me close enough...
Then in the corner i do the same thing for X and add the 2 multiplier together so if you're in the far corner both maps = 2 and
so radius = radius * (mapX + mapY)
Doubles it on edges and quadruples it in the corners. which is ~ close enough
Let's say we have a 100x100 coordinate system, like the one below. 0,0 is its left-top corner, 50,50 is its center point, 100,100 is its bottom right corner, etc.
Now we need to draw a line from the center outwards. We know the angle of the line, but need to calculate the coordinates of its end point. What do you think would be the best way to do it?
For example, if the angle of the line is 45 degrees, its end point coordinates would be roughly 75,15.
You need to use the trigonometric functions sin and cos.
Something like this:
theta = 45
// theta = pi * theta / 180 // convert to radians.
radius = 50
centerX = 50
centerY = 50
p.x = centerX + radius * cos(theta)
p.y = centerY - radius * sin(theta)
Keep in mind that most implementations assume that you're working with radians and have positive y pointing upwards.
Use the unit circle to calculate X and Y, but because your radius is 50, multiply by 50
http://en.wikipedia.org/wiki/Unit_circle
Add the offset (50,50) and bob's your uncle
X = 50 + (cos(45) * 50) ~ 85,36
Y = 50 - (sin(45) * 50) ~ 14,65
The above happens to be 45 degrees.
EDIT: just saw the Y axis is inverted
First you would want to calculate the X and Y coordinates as if the circle were the unit circle (radius 1). The X coordinate of a given angle is given by cos(angle), and the Y coordinate is given by sin(angle). Most implementations of sin and cos take their inputs in radians, so a conversion is necessary (1 degree = 0.0174532925 radians). Now, since your coordinate system is not in fact the unit circle, you need to multiply the resultant values by the radius of your circle. In this given instance, you would multiply by 50, since your circle extends 50 units in each direction. Finally, using a unit circle coorindate system assumes your circle is centered at the origin (0,0). To account for this, add (or subtract) the offset of your center from your calculated X and Y coordinates. In your scenario, the offset from (0,0) is 50 in the positive X direction, and 50 in the negative Y direction.
For example:
cos(45) = x ~= .707
sin(45) = y ~= .707
.707*50 = 35.35
35.35+50 = 85.35
abs(35.35-50) = 14.65
Thus the coordinates of the ending segment would be (85.35, 14.65).
Note, there is probably a built-in degrees-to-radians function in your language of choice, I provided the unit conversion for reference.
edit: oops, used degrees at first
I'm writing a script where icons rotate around a given pivot (or origin). I've been able to make this work for rotating the icons around an ellipse but I also want to have them move around the perimeter of a rectangle of a certain width, height and origin.
I'm doing it this way because my current code stores all the coords in an array with each angle integer as the key, and reusing this code would be much easier to work with.
If someone could give me an example of a 100x150 rectangle, that would be great.
EDIT: to clarify, by rotating around I mean moving around the perimeter (or orbiting) of a shape.
You know the size of the rectangle and you need to split up the whole angle interval into four different, so you know if a ray from the center of the rectangle intersects right, top, left or bottom of the rectangle.
If the angle is: -atan(d/w) < alfa < atan(d/w) the ray intersects the right side of the rectangle. Then since you know that the x-displacement from the center of the rectangle to the right side is d/2, the displacement dy divided by d/2 is tan(alfa), so
dy = d/2 * tan(alfa)
You would handle this similarily with the other three angle intervals.
Ok, here goes. You have a rect with width w and depth d. In the middle you have the center point, cp. I assume you want to calculate P, for different values of the angle alfa.
I divided the rectangle in four different areas, or angle intervals (1 to 4). The interval I mentioned above is the first one to the right. I hope this makes sense to you.
First you need to calculate the angle intervals, these are determined completely by w and d. Depending on what value alfa has, calculate P accordingly, i.e. if the "ray" from CP to P intersects the upper, lower, right or left sides of the rectangle.
Cheers
This was made for and verified to work on the Pebble smartwatch, but modified to be pseudocode:
struct GPoint {
int x;
int y;
}
// Return point on rectangle edge. Rectangle is centered on (0,0) and has a width of w and height of h
GPoint getPointOnRect(int angle, int w, int h) {
var sine = sin(angle), cosine = cos(angle); // Calculate once and store, to make quicker and cleaner
var dy = sin>0 ? h/2 : h/-2; // Distance to top or bottom edge (from center)
var dx = cos>0 ? w/2 : w/-2; // Distance to left or right edge (from center)
if(abs(dx*sine) < abs(dy*cosine)) { // if (distance to vertical line) < (distance to horizontal line)
dy = (dx * sine) / cosine; // calculate distance to vertical line
} else { // else: (distance to top or bottom edge) < (distance to left or right edge)
dx = (dy * cosine) / sine; // move to top or bottom line
}
return GPoint(dx, dy); // Return point on rectangle edge
}
Use:
rectangle_width = 100;
rectangle_height = 150;
rectangle_center_x = 300;
rectangle_center_y = 300;
draw_rect(rectangle_center_x - (rectangle_width/2), rectangle_center_y - (rectangle_center_h/2), rectangle_width, rectangle_height);
GPoint point = getPointOnRect(angle, rectangle_width, rectangle_height);
point.x += rectangle_center_x;
point.y += rectangle_center_y;
draw_line(rectangle_center_x, rectangle_center_y, point.x, point.y);
One simple way to do this using an angle as a parameter is to simply clip the X and Y values using the bounds of the rectangle. In other words, calculate position as though the icon will rotate around a circular or elliptical path, then apply this:
(Assuming axis-aligned rectangle centered at (0,0), with X-axis length of XAxis and Y-axis length of YAxis):
if (X > XAxis/2)
X = XAxis/2;
if (X < 0 - XAxis/2)
X = 0 - XAxis/2;
if (Y > YAxis/2)
Y = YAxis/2;
if (Y < 0 - YAxis/2)
Y = 0 - YAxis/2;
The problem with this approach is that the angle will not be entirely accurate and the speed along the perimeter of the rectangle will not be constant. Modelling an ellipse that osculates the rectangle at its corners can minimize the effect, but if you are looking for a smooth, constant-speed "orbit," this method will not be adequate.
If think you mean rotate like the earth rotates around the sun (not the self-rotation... so your question is about how to slide along the edges of a rectangle?)
If so, you can give this a try:
# pseudo coode
for i = 0 to 499
if i < 100: x++
else if i < 250: y--
else if i < 350: x--
else y++
drawTheIcon(x, y)
Update: (please see comment below)
to use an angle, one line will be
y / x = tan(th) # th is the angle
the other lines are simple since they are just horizontal or vertical. so for example, it is x = 50 and you can put that into the line above to get the y. do that for the intersection of the horizontal line and vertical line (for example, angle is 60 degree and it shoot "NorthEast"... now you have two points. Then the point that is closest to the origin is the one that hits the rectangle first).
Use a 2D transformation matrix. Many languages (e.g. Java) support this natively (look up AffineTransformation); otherwise, write out a routine to do rotation yourself, once, debug it well, and use it forever. I must have five of them written in different languages.
Once you can do the rotation simply, find the location on the rectangle by doing line-line intersection. Find the center of the orbited icon by intersecting two lines:
A ray from your center of rotation at the angle you desire
One of the four sides, bounded by what angle you want (the four quadrants).
Draw yourself a sketch on a piece of paper with a rectangle and a centre of rotation. First translate the rectangle to centre at the origin of your coordinate system (remember the translation parameters, you'll need to reverse the translation later). Rotate the rectangle so that its sides are parallel to the coordinate axes (same reason).
Now you have a triangle with known angle at the origin, the opposite side is of known length (half of the length of one side of the rectangle), and you can now:
-- solve the triangle
-- undo the rotation
-- undo the translation