3

Problem:

I'm trying to draw a rotating image in Pygame but I only want the top half of the image to be displayed. I have managed to rotate the image so that it is centered correctly but I am unable to crop it to only display the top half.

I think that this is due to the rect being rotated and when I use surface.subsurface() to crop it to a certain rect, the cropped part has a "bouncing" effect as the rect is getting larger and smaller depending on the angle of rotation

Code:

def updateTime(screen, hudtimecircle, time):
    center = (200, 200)                                        # The center of where the rotated image will be drawn
    amount = time                                              # just a number 1-360

    newcircle = pygame.transform.rotate(hudtimecircle, amount) # The rotated version of the un-rotated "hudtimecircle" image
    newrect = newcircle.get_rect()                             # The rect of the rotated image
    newrect.center = center                                    # Setting the middle of the rotated rect to the point I want to be the center

    crop = (0, 0, 120, 60)                                     # An area that I want to be left when I "Crop", This is the part that needs fixing
    cropped = newcircle.subsurface(crop)                       # The cropped part of the rotated image, I want/need this to only give the top half of the image.
    screen.blit(cropped, newrect.topleft)                      # Drawing the rotated image with it's middle point where the variable "center" says.

I have tried cropping it relative to the rotated rect like so:

crop = (newrect.topleft[0], newrect.topleft[1], newrect.midright[0], newrect.midright[1])

However this doesn't work and returns ValueError: subsurface rectangle outside surface area as each or some of the points are outside the area of the image.

Other Info:

The image hudtimecircle is 120px*120px and is an image of a circle, with a transparent background, I only want to draw the top 60px of the circle, after it has been rotated.

amount and time are just numbers from 1 to 360, I have this line as I plan to do stuff with amount later.

I can't just blit another image over the top of the bottom half of the image to "crop" it as there are many different things underneath where this image needs to be placed.

I have looked at:

https://www.reddit.com/r/learnpython/comments/3xeu4c/image_cropping_with_pygame/ http://blog.tankorsmash.com/?p=128
pygame rotation around center point
And a few other questions that weren't too relevant.

Just to clarify, my issue isn't to rotate the image, or keep the rotated image centered. The issue is that I need to only display the top half of the rotated, centered image.

If there is anymore information that is needed, I will happily provide it.

Community
  • 1
  • 1
Charlton Lane
  • 438
  • 9
  • 13

2 Answers2

2

Is this the effect that you want to achieve? You have to calculate the new crop position after each rotation, because the size of the rotated image changes all the time. In the upper left corner you see how the parent image/surface changes.

import sys
import pygame as pg


def main():
    pg.init()
    screen = pg.display.set_mode((320, 240))
    clock = pg.time.Clock()
    done = False
    bg_color = pg.Color(50, 80, 120)
    # Create the original image that we use for the rotation
    # to avoid visual degeneration.
    orig_img = pg.Surface((100, 100))
    orig_img.fill((20, 50, 130))
    pg.draw.circle(orig_img, pg.Color('springgreen2'), (30, 30), 30)
    pg.draw.circle(orig_img, pg.Color('lightgoldenrod3'), (60, 60), 30)
    pg.draw.rect(orig_img, pg.Color('black'), (48, 0, 4, 100))
    pg.draw.rect(orig_img, pg.Color('black'), (0, 48, 100, 4))
    rect = orig_img.get_rect(topleft=(30, 30))

    angle = 0

    while not done:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True

        angle += 1
        # Rotate the image.
        img = pg.transform.rotate(orig_img, angle)
        rect = img.get_rect(center=rect.center)
        # Calculate new crop position. Half of the new
        # width & height minus half of the original dimensions.
        x, y = rect.width/2 - 50, rect.height/2 - 50
        sub = img.subsurface((x, y, 100, 50))

        screen.fill(bg_color)

        screen.blit(img, rect)
        screen.blit(sub, (150, 150))

        pg.display.flip()
        clock.tick(30)


if __name__ == "__main__":
    main()
    pg.quit()
    sys.exit()
skrx
  • 19,980
  • 5
  • 34
  • 48
1

You have to put crop variable in brackets, so:

Your code:

crop = (0, 0, 120, 60)
cropped = newcircle.subsurface(crop)

Right code:

crop = (0, 0, 120, 60)
cropped = newcircle.subsurface((crop))

Notice me if that works.

Giorgio B
  • 193
  • 6
  • Sadly no, it doesn't fix the problem. Your code and my code both crop the image but due to the rotation the area of the crop changes depending on the angle rotated. So if there is no rotation on the image, the crop works and gets exactly the top half of the image, but any rotation causes the cropped section to get larger or smaller. – Charlton Lane May 28 '16 at 23:26
  • @Charlton Lane did you try to set the rect of the cropped surface? – Giorgio B May 28 '16 at 23:30
  • This change doesn't have any effect. You just wrap crop in extra, redundant parentheses. – skrx May 28 '16 at 23:36