简体   繁体   中英

drawing a diagonal ellipse with pygame

Does anyone know of an easy way to draw ellipses that are not aligned to the x & y axis. I am very new to pygame so please forgive my ignorance, but I cannot find anything related to it.

If no easy method exists, can someone help me in how I might draw this besides generating many many points on the ellipse and plotting all of them?

You can do that. Create a surface, draw the ellipse onto that surface and then rotate the entire surface (with the ellipse on it). Here's my test code:

import pygame, sys

screen = pygame.display.set_mode((1024, 640))

running = True

#let's create a surface to hold our ellipse:
surface = pygame.Surface((320, 240))

red = (180, 50, 50)
size = (0, 0, 300, 200)

#drawing an ellipse onto the 
ellipse = pygame.draw.ellipse(surface, red, size)

#new surface variable for clarity (could use our existing though)
#we use the pygame.transform module to rotate the original surface by 45°
surface2 = pygame.transform.rotate(surface, 45)

while running:
    screen.fill((255, 250, 200))
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    screen.blit(surface2, (100, 100))
    pygame.display.update()

The result will be a rotated ellipse. It would be possible to make the "holder" of the ellipse transparent. Check the documentations on the modules:

http://www.pygame.org/docs/ref/transform.html#pygame.transform.rotate

http://www.pygame.org/docs/ref/surface.html

Hope that helps!

If anyone is curious how to draw a "flower" using rotated ellipses, the following might help with that.

def flower(width: int, color: tuple, edges: bool=False):
    W, H = width, width
    # create a flower surface with an alpha channel on which to draw 
    # the petals
    flower = pygame.Surface((W, H), pygame.SRCALPHA, 32).convert_alpha()
    R = flower.get_rect()
    cx, cy = R.center
    # assuming petal height should be half their width
    petal_size = (width//2, width//4)
    pw, ph = petal_size
    radius = pw/2
    center_radius = width//10
    center_color = (255-color[0], 255-color[1], 255-color[2])

    def draw_petal(S, x, y, w, h, angle):
        # Create surface for drawing an individual petal
        surface = pygame.Surface((w, h), pygame.SRCALPHA, 32).convert_alpha()
        # Draw the un-rotated petal
        pygame.draw.ellipse(surface, color, (0, 0, w, h), 0)
        if edges:
            pygame.draw.ellipse(surface, BLACK, (0, 0, w, h), 1)

        # Create a new surface with the petal rotated by angle
        rot_surface = pygame.transform.rotate(surface, angle)
        # Need center of rotated surface to blit (draw) the rotated
        # petal at the given (x, y) coordinate
        rcx, rcy = rot_surface.get_rect().center
        # Draw the center of the rotated petal at (x, y)
        S.blit(rot_surface, (x - rcx, y - rcy))

    # Petals are drawn at diagonals first, then the horizontal petals,
    # then the vertical petals
    angles = [
        45, 135, 225, 315,      # diagonals
        0, 180,                 # horizontal
        90, 270                 # vertical
    ]
    for a in angles:
        # placing petal centers onto circle of radius (petal_width/2)
        x, y = map(int, (
            radius*math.cos(math.radians(a)), radius*math.sin(math.radians(a))
        ))
        draw_petal(flower, cx+x, cy+y, pw, ph, -a)
    # draw flower center (don't remember what it's called)
    pygame.draw.circle(flower, center_color, (cx, cx), center_radius)
    if edges:
        pygame.draw.circle(flower, BLACK, (cx, cx), center_radius, 1)

    def draw_flower(S, x, y, flower=flower):
        S.blit(flower, (x - cx, y - cy))
    return draw_flower

To use this code:

import math
import pygame

BLACK    = (   0,   0,   0)
GREEN    = (   0, 255,   0)
RED      = ( 255,   0,   0)

pygame.init()

size = (800, 800)
SW, SH = size
screen = pygame.display.set_mode(size)

pygame.display.set_caption("Flower Demo")

done = False
clock = pygame.time.Clock()

# insert above flower code

draw_green_flower = flower(100, GREEN)
draw_red_flower = flower(100, RED)
try:
    while not done:

        for event in pygame.event.get(): 
            if event.type == pygame.QUIT:
                done = True 
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    done = True 

        screen.fill(WHITE)

        draw_green_flower(screen, SW/2,SH/2)
        draw_red_flower(screen, SW/2-100,SH/2-100)

        pygame.display.flip()

        clock.tick(60)

finally:
    pygame.quit()

In case this helps someone, I wrote a function that lets you draw an ellipse between any two points A and B.

Not the most elegant math but it works! See example below:

import pygame
import math


def draw_ellipse(A, B, width, color, line):
    """
    draws ellipse between two points
    A = start point (x,y)
    B = end point (x,y)
    width in pixel
    color (r,g,b)
    line thickness int, if line=0 fill ellipse
    """
    # point coordinates
    xA, yA = A[0], A[1]
    xB, yB = B[0], B[1]
    # calculate ellipse height, distance between A and B
    AB = math.sqrt((xB - xA)**2 + (yB - yA)**2)

    # difference between corner point coord and ellipse endpoint
    def sp(theta):
        return abs((width / 2 * math.sin(math.radians(theta))))

    def cp(theta):
        return abs((width / 2 * math.cos(math.radians(theta))))

    if xB >= xA and yB < yA:
        # NE quadrant
        theta = math.degrees(math.asin((yA - yB) / AB))
        xP = int(xA - sp(theta))
        yP = int(yB - cp(theta))
    elif xB < xA and yB <= yA:
        # NW
        theta = math.degrees(math.asin((yB - yA) / AB))
        xP = int(xB - sp(theta))
        yP = int(yB - cp(theta))
    elif xB <= xA and yB > yA:
        # SW
        theta = math.degrees(math.asin((yB - yA) / AB))
        xP = int(xB - sp(theta))
        yP = int(yA - cp(theta))
    else:
        # SE
        theta = math.degrees(math.asin((yA - yB) / AB))
        xP = int(xA - sp(theta))
        yP = int(yA - cp(theta))

    # create surface for ellipse
    ellipse_surface = pygame.Surface((AB, width), pygame.SRCALPHA)
    # draw surface onto ellipse
    pygame.draw.ellipse(ellipse_surface, color, (0, 0, AB, width), line)
    # rotate ellipse
    ellipse = pygame.transform.rotate(ellipse_surface, theta)
    # blit ellipse onto screen
    screen.blit(ellipse, (xP, yP))


screen = pygame.display.set_mode((1000, 1000))

running = True
while running:
    screen.fill((255, 250, 200))

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()

    draw_ellipse((500, 500), (420, 350), 100, (0, 255, 0), 5)
    draw_ellipse((400, 600), (700, 280), 80, (255, 0, 0), 5)
    draw_ellipse((260, 190), (670, 440), 50, (0, 0, 255), 5)

    pygame.display.update()

Unfortunately, there is no direct way to draw a rotated shape. pygame.transform.rotate() can rotate a pygame.Surface object but you cannot rotate a shape directly. You need to draw the shape on a Surface and rotate that Surface :

  1. Create a pygame.Surface object with a per-pixel alpha format and with the size of the shape.
  2. Draw the shapeon the _Surface.
  3. Rotate the Surface with the shape around its center. See How do I rotate an image around its center using PyGame?
  4. blit the Surface with the shapeonto the target Surface .

Write a functions that draws the rotated shapes:

def draw_ellipse_angle(surface, color, rect, angle, width=0):
    target_rect = pygame.Rect(rect)
    shape_surf = pygame.Surface(target_rect.size, pygame.SRCALPHA)
    pygame.draw.ellipse(shape_surf, color, (0, 0, *target_rect.size), width)
    rotated_surf = pygame.transform.rotate(shape_surf, angle)
    surface.blit(rotated_surf, rotated_surf.get_rect(center = target_rect.center))

Minimal example:

import pygame

def draw_ellipse_angle(surface, color, rect, angle, width=0):
    target_rect = pygame.Rect(rect)
    shape_surf = pygame.Surface(target_rect.size, pygame.SRCALPHA)
    pygame.draw.ellipse(shape_surf, color, (0, 0, *target_rect.size), width)
    rotated_surf = pygame.transform.rotate(shape_surf, angle)
    surface.blit(rotated_surf, rotated_surf.get_rect(center = target_rect.center))

pygame.init()
window = pygame.display.set_mode((250, 250))
clock = pygame.time.Clock()

background = pygame.Surface(window.get_size())
ts, w, h, c1, c2 = 50, *window.get_size(), (160, 160, 160), (192, 192, 192)
tiles = [((x*ts, y*ts, ts, ts), c1 if (x+y) % 2 == 0 else c2) for x in range((w+ts-1)//ts) for y in range((h+ts-1)//ts)]
for rect, color in tiles:
    pygame.draw.rect(background, color, rect)

angle = 0
run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    window.blit(background, (0, 0))
    draw_ellipse_angle(window, (0, 0, 255), (25, 75, 200, 100), angle, 5)
    angle += 1
    pygame.display.flip()

pygame.quit()
exit()

I do not know python or pygame, but depending on what you are building, it may be easier to just make an image using a program like inkscape for pc and mac, or inkpad for iPad. Both of these let you make a diagonal ellipse, and then export it as a .png and use it in your code. Again, if this is possible really depends on what you are doing with the ellipse.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM