I noticed recently that I never made the rotation mechanism used in the SGE rotate images about what is called the origin: the point where an image is positioned from, so I decided to implement this recently. Unfortunately, the way I have done it seems to be completely wrong. Without the new code I added (albeit with some fixes to the rotation, which was slightly off previously), images rotate perfectly about the center. With the new code which is intended to make the image rotate around the origin, however, the rotation is completely different, and what exactly happens is hard to describe.
To see the problem, checkout or download the Git repository from one of the Git repos (they are currently the same; the GitHub repo will be dropped at some point in the future) and run examples/rotation.py. You will need Python 2 and Pygame 1.9 or later. You will see a few needles rotating; the point they should be rotating around is the head (the fatter parts of the needles), but instead they rotate in a completely different way which I don't really understand.
I have found that the problem is somewhere between lines 1651 and 1697 of sge-pygame/StellarClass.py. However, I ran through a test case using the method on paper with origin_x=18, origin_y=4, rotation=112, image_width=32, image_height=32 and got a result that seems right: an x offset of about -6.5 and a y offset of about 51.6, resulting in the origin being at about (11.5, 55.6). (In fact, I did find some problems with some calculations, but I fixed those, and the rotation is still wrong.)
Here is the code where the problem lies (a little bit more than the problem lines; the problem lines start at "if rotation % 360"):
- Code: Select all
def _get_rotation_offset(origin_x, origin_y, rotation, image_width,
# Return what to offset an origin when the object is rotated as a
# two-part tuple: (x_offset, y_offset)
x_offset = 0
y_offset = 0
if rotation % 180:
# Adjust offset for the borders getting bigger.
x_offset += (image_width - image_width_normal) / 2
y_offset += (image_height - image_height_normal) / 2
if rotation % 360:
# Rotate about the origin
center_x = image_width / 2
center_y = image_height / 2
x = origin_x - image_width
# We have to make y negative to work with the unit circle.
y = -(origin_y - image_height)
if x or y:
if not x:
rot = math.radians(90 if y > 0 else 270)
h = abs(y)
elif not y:
rot = math.radians(0 if x > 0 else 180)
h = abs(x)
rot = abs(math.atan(y / x))
h = abs(y / math.sin(rot))
# Find quadrant
if y > 0:
if x > 0:
# Quadrant I; nothing to do
# Quadrant II
rot = math.pi - rot
if x < 0:
# Quadrant III
rot += math.pi
# Quadrant IV
rot = (2 * math.pi) - rot
rot += math.radians(rotation)
rot %= 2 * math.pi
new_x = h * math.cos(rot)
new_y = h * math.sin(rot)
new_origin_x = new_x + image_width
# Now that we're done with the unit circle,
# we need to change back to the SGE's
# version of y.
new_origin_y = -new_y + image_height
x_offset += new_origin_x - origin_x
y_offset += new_origin_y - origin_y
return (x_offset, y_offset)
I use an "offset" method: x_offset and y_offset indicate how to offset the origin to get it in the right place.
Can anyone see a problem with the method I'm using that I've missed?
EDIT: I added some green circles to the test program; they show where the origin of each needle is (where the heads should reside).