I have an object on my canvas and I want to make an arrow pointing on it. I already have an image of arrow, that is pointing up, and I need to know the angle I must rotate it by to make it point on that image. The arrow is always at position (0, 0), but the position of the second picture can change.
I know I can count sine and cosine of that angle, but it's not the thing I'm looking for, It must be exactly that angle.
The angle is 90 - arctan(Y/X). You probably want to use math.atan2(y,x), and remember that returns radians, not degrees. If you want degrees, it's
angle = 90 - math.atan2(y,x) * 180 / math.pi
Related
Using PyCairo, I want to be able to have a method that can put, resize & rotate a given ImageSurface on a context, but rotating by the center of the image (not the top-left)
Okay I've tried the examples I've found here but without any success.
Let's introduce the "context" in details.
I've got a "finale" ImageSurface (say A) on which some other images & texts are written.
I want to put on it another ImageSurface (say B), at a specified position where this position is the top-left where to put B on A. Then I need to resize B (reduce its size) and to rotate it by its center instead of by its top-left corner.
Here is an illustration of the wanted result :
I've tried the following but without success:
def draw_rotated_image(ctx, image_surface, left, top, width, height, angle):
ctx.save()
w = image_surface.get_width()
h = image_surface.get_height()
cl = left / (width/w)
ct = top / (height/h)
ctx.rotate(angle*3.1415927/180)
ctx.scale(width/w, height/h)
ctx.translate(cl + (-0.5*w),ct + (-0.5*h) )
ctx.set_source_surface(image_surface, 0, 0)
ctx.paint()
ctx.restore()
return
Thanks a lot for your help :)
Well, I finally made it ! (thanks to my 14 year old son who made me revise my trigonometry)
I'm trying to explain here my solution.
First, I AM NOT A MATHEMATICIAN. So there is probably a best way, and surely my explanation have errors, but I'm just explaining the logical way I have used to get the good result.
The best idea for that is to first draw a circle around the rectangle, because we need to move the top-left corner of this rectangle, around its own circle, according to the desired angle.
So to get the radius of the rectangle circle, we need to compute its hypothenuse, then to divide by 2 :
hypothenuse = math.hypot(layerWidth,layerHeight)
radius = hypothenuse / 2
Then we will be able to draw a circle around the rectangle.
Second, we need to know at which angle, on this circle, is the actual top-left corner of the rectangle.
So for that, we need compute the invert tangent of the rectangle, which is arc-tan(height/width).
But because we want to know how many degrees are we far from 0°, we need to compute the opposite so arc-tan(width/height).
Finally, another singularity is that Cairo 0° is in fact at 90°, so we will have to rotate again.
This can be shown by this simple graphic :
So finally, what is necessary to understand ?
If you want to draw a layer, with an angle, rotated by its center, the top-left point will move around the circle according to the desired angle.
The top-left position with a given angle of 0 needs to be "the reference".
So we need to get the new X-Y position where to start putting the layer to be able to rotate it :
Now, we can write a function that will return the X-Y pos of the top left rectangle where to draw it with a given angle :
def getTopLeftForRectangleAtAngle(layerLeft,layerTop,layerWidth,layerHeight,angleInDegrees):
# now we need to know the angle of the top-left corner
# for that, we need to compute the arc tangent of the triangle-rectangle:
layerAngleRad = math.atan((layerWidth / layerHeight))
layerAngle = math.degrees(layerAngleRad)
# 0° is 3 o'clock. So we need to rotate left to 90° first
# Then we want that 0° will be the top left corner which is "layerAngle" far from 0
if (angleInDegrees >= (90 + layerAngle)):
angleInDegrees -= (90 + layerAngle)
else:
angleInDegrees = 360 - ((90 + layerAngle) - angleInDegrees)
angle = (angleInDegrees * math.pi / 180.0)
centerLeft = layerLeft + (layerWidth / 2)
centerTop = layerTop + (layerHeight / 2)
# hypothenuse will help us knowing the circle radius
hypothenuse = math.hypot(layerWidth,layerHeight)
radius = hypothenuse / 2
pointX = centerLeft + radius * math.cos(angle)
pointY = centerTop + radius * math.sin(angle)
return (pointX,pointY)
And finally, here is how to use it with an image we want to resize, rotate and write on a context:
def draw_rotated_image(ctx, image_surface, left, top, width, height, angle=0.0, alpha=1.0):
ctx.save()
w = image_surface.get_width()
h = image_surface.get_height()
# get the new top-left position according to the given angle
newTopLeft = getTopLeftForRectangleAtAngle(left, top, width, height, angle)
# translate
ctx.translate(newTopLeft[0], newTopLeft[1])
# rotate
ctx.rotate(angle * math.pi / 180)
# scale & write
ctx.scale(width/w, height/h)
ctx.set_source_surface(image_surface, 0, 0)
ctx.paint_with_alpha(alpha)
ctx.restore()
return
I have two points like this
I want to rotate the image, so it looks like this:
I have the code to rotate the image. I just want to find the rotation angle like 5 degrees, 2 degrees.
I have the coordinates of both the points like 50,100 and 150, 94 (X,Y). I want to rotate the image such that, both Y-axis points become 100.
You can find rotation angle using math.atan2 function with point coordinates.
angle = math.atan2(p2.y-p1.y, p2.x-p1,x)
Note that rotation about coordinate origin would shift the first point also, so you perhaps need to make rotation about the first point.
I need to draw an arc in pygame, through three points, however pygame's arc function doesn't seem to support this. I have researched three point arcs, both in and out of pygame, however cannot find anything that I understand and is useful.
I would ideally like a full code however any hints will be useful
Here are some hints, which will require a little mathematics to finish. If you need more help, show more of your work and ask.
Let's say you want to draw a circular arc from point a=(ax, ay) through point b=(bx, by) to point c=(cx, cy). You want to use the function
pygame.draw.arc(Surface, color, Rect, start_angle, stop_angle, width=1)
So you need to find Rect, the bounding rectangle (which will be a square) of the circle, and the start and stop angles. Here is an outline.
Use your point coordinates to calculate the center of the relevant circle, giving you point u=(ux, uy). If that calculation raises a divide-by-zero or overflow error, the points are (nearly) in a straight line, so just draw a line segment from point a to point c and you are done.
Calculate the distance from point u to point a (or b or c, it doesn't matter). That will be the radius of the desired circle.
Use point u and the radius to calculate the parameters of the bounding rectangle Rect. The rectangle will actually be a square, and point u will be its center.
Use a little trigonometry to calculate the direction angle from point u to point a: this (almost) will be start_angle. Be careful, since Cartesian coordinates have increasing y go up, while in pygame increasing y goes down. Also calculate the direction angle from point u to point c: this (almost) will be stop_angle. Then calculate the direction angle from point u to point b.
Examine those three direction angles. If necessary, swap the start and stop angles and/or add 2*pi to the stop angle to ensure that the start angle is less than the stop angle and that the arc goes through point b.
You now have Rect, start_angle, and stop_angle. This outline ignores some subtle issues such as rounding to integers, but I'll leave those to you.
I'm trying to rotate a rectangle based on the position of the mouse inside or outside of the circle.
The way I see it, if I can determine the point on the circle that is closest to the position of the mouse, I can then transform the rectangle along the circle using that point as the target.
I cannot however, figure out how to find that position. I thought that perhaps by using y=mx+b to follow the line from the mouse pos until it hits the point on the circle.
The problem with this however is that I do not have all of the points on the circle and there are hundreds if not thousands of points on the circle.
If the mouse position is outside of a circle, how do I find the point on the circle closest to the mouse-position?
Use math.atan2() to get the angle of the cursor from the center. The circle will be a fixed distance from the center, so you can just convert the angle and distance to a point on the circle with more trig.
angle = math.atan2(ymouse - ycenter, xmouse - xcenter)
I'm trying to do draw widgets on the circle, for this I need to paint the widgets as a arc. I know the number of widgets (let's say), then each widget is at 36 degrees from the origin to the circumference. The information I have is the center of circle, radius and I know the starting and end point on the circumference for each such widget.
This is computed by doing
dx = int(round(400 + 300 * np.cos(angle)))
dy = int(round(400 + 300 * np.sin(angle)))
where angle = 2 * np.pi / 15
I go over a for loop computing the new value for angle which is basically angle * i where i = (1, 10)
I don't understand the start angle and span angle for the arcs function in QPainter.QPainter Arc. I googled and not many terms came up. Maybe there is a different term for them.
So the problem is I have a starting point and ending point on the circumference and center and radius, how do I use them to draw Arcs such that I get something that looks like :
circos
What I have tried is, I can compute the center point (cx) of the two end points, if I draw a line from the center of the circle to this point cx, then I can compute how far this point circumference which essentially is my width, but how to get the orientation correct to represent them as circles.
Instead of circular I do have a layout with just lines like this, but would like to be like the circos one.
My image
I don't understand the start angle and span angle for the arcs function in QPainter.QPainter Arc.
Why? The documentation is IMHO very clear:
The startAngle and spanAngle must be specified in 1/16th of a degree, i.e. a full circle equals 5760 (16 * 360).
This means that your units are 1/16º. E.g. 45º is 45*16 units.
Positive values for the angles mean counter-clockwise while negative values mean the clockwise direction. Zero degrees is at the 3 o’clock position."
This means that 90*16 points at 12 o'clock (goes 90º counter-clockwise from 3 o'clock), and -90*16 points at 6 o'clock.
Of course the "zero" degrees only has sense for the start angle. The span angle states how much further does the arc go, and in which direction.
For example, to draw an arc from 3 o'clock to 12 o'clock, you'd do
painter.drawArc(rect, 0, 90*16)
*or*
painter.drawArc(rect, 90*16, -90*16)
But to draw an arc from 3 o'clock to 6 o'clock, you'd do
painter.drawArc(rect, 0, -90*16)
*or*
painter.drawArc(rect, -90*16, 90*16)
The arcs are not specified using center and radius, but rather using a bounding rectangle. If the arc was a full ellipse, it would be inscribed in the rectangle - the arcs are implicitly elliptical arcs.
So, given x and y centerpoint, and r for circular radius, the bounding rectangle is
rect = QRect(x-r, y-r, 2*r, 2*r)