Pygame touble when drawing a rectangle - python

So, I'm randomly generating a "world" and drawing it with pygame. That part worked perfectly fine until I decided to add something above what I already drew.
The code is as follows. What each thing is is of no consequence, but DISPLAY is the surface I'm working on, y.colour is a size 3 Tuple, y.coord is a (x,y) Tuple
for x in W_Map:
for y in x:
DISPLAY.fill(y.colour, pygame.Rect(y.coord[0]-tile_size,
y.coord[1]-tile_size,
y.coord[0]+tile_size,
y.coord[1]+tile_size))
DISPLAY.fill(lime, pygame.Rect(300,300,310,310))
According to the game above, this should create a lime coloured 10x10 square centered on 305x305. The result, however, is the following picture:
As you can see, the first part of the code draws the terrain perfectly, but when creating the lime square on top of what's already drawn, it goes completely crazy. The whole function is:
pygame.init()
DISPLAY = pygame.display.set_mode(
(shape[0]*2*tile_size, shape[1]*2*tile_size))
DISPLAY.fill((0,0,0))
#Make and draw the Rects
for x in W_Map:
for y in x:
DISPLAY.fill(y.colour, pygame.Rect(y.coord[0]-tile_size,
y.coord[1]-tile_size,
y.coord[0]+tile_size,
y.coord[1]+tile_size))
DISPLAY.fill(lime, pygame.Rect(300,300,310,310))

Pygame's Rect takes four arguments: x, y, width, and height, where x and y are relative to the top left of the viewport. Your lime rectangle is created with pygame.Rect(300,300,310,310), meaning a width and height of 310 pixels and a location of (300, 300).
To create a 10x10 rectangle centered at (305, 305) you'll need to use pygame.Rect(300, 300, 10, 10). You can also create a helper function to translate size and center point to the necessary rectangle parameters:
def center_rect(x, y, width, height):
return pygame.Rect(x - width / 2, y - height / 2, width, height)
Then you could use this helper function like so:
DISPLAY.fill(lime, center_rect(305, 305, 10, 10))

Related

using the pygame: [spritesheet].subsurface function, is there a way to draw the sprite from the bottom rather than the top left corner

I have a set of sprites, also on a spritesheet, which bounces slightly up and down, so the sprite height changes slightly.
By coding: spritesheet.subsurface(x, y, width, height), it will draw the sprite starting from (x, y) and then the width and height to the right and down respectively, however, when drawing the frames from the same screen-relative position, the sprite looks like the feet are moving down and up.
So that, if I had positioned frame 1 of the sprite to have the feet touching the floor, frame 2 would have the feet through the floor.
Is there a way to draw the sprite, say, from the bottom left, or bottom centre?
Many thanks
pygame.Surface.subsurface uses a rectangular area to create a new surface that references its parent. The origin (0, 0) of the pygame coordinate system is the top left. This cannot be changed.
However, if you know the bottom left coordinate (left_x, bottom_y) of a sprit on a spritsheet the rectangle of the sprite can be computed (spritesheet is a pygame.Surface object):
sheet_height = spritsheet.get_height()
sprite = spritsheet.subsurface((left_x, sheet_height - bottom_y - height, width, height))
The code can be made more comprehensible with a pygame.Rect object:
sheet_height = spritsheet.get_height()
sprite_rect = pygame.Rect(left_x, 0, height, height)
sprite_rect.bottom = sheet_height - bottom_y
sprite = spritsheet.subsurface(sprite_rect )

Fill PIL ImageDraw Line partially

I'm trying to fill a line by a different color increasingly like a progress bar. This is the image:
It was created with this code
from PIL import Image, ImageDraw
image = Image.new("RGBA", (300, 300), color="black")
draw = ImageDraw.Draw(image)
width = 7
image_w, image_h = image.size
coord_a = image_w / 2, width
coord_b = width, image_h / 2
coord_c = image_w / 2, image_h - width
coord_d = image_w - width, image_h / 2
draw.line([coord_a, coord_b, coord_c, coord_d, coord_a], fill="red", width=width, joint="curve")
image.show()
image.save("test.png")
I'm trying to fill it with different color like this:
Should I just fill each line separately and combine them all?
Interesting question! You could have lots of fun thinking up ways to do this.
As you suggest, you could draw the rhombus as four separate lines. You would have to calculate the point where the red and blue portion met using sin/cos but that's not too hard.
You could draw it much more simply as the four sides of a square with its sides initially horizontal and vertical, then rotate it 45 degrees into place when you are finished drawing. I think I would go for this option.
You could draw a single long horizontal red line, and then overdraw the correct percentage in blue. Then cut it into four pieces, rotate and paste onto the black square background.
You could get the coordinates of all the points on the rhombus using scikit-image draw.polygon_perimeter() as documented here. Then colour the first however many percent blue and the remainder in red. You could make the lines thicker using morphological dilation.

Python Coordinate system question for centering drawn objects

We would like to create a rectangle with width w and height h, on a surface whose width is a and whose height is b.
What would be the values of left, top, width, height that we have to pass to the pygame.Rect function, if the rectangle needs to be at a distance x_offset from the left edge of the surface and centered vertically on the surface?
I got this question and I know that left would be equal to x_offset but I have no idea how to figure out any of the other ones I've tried drawing it out.
Just create your Rect object first with the values you know:
rect = pygame.Rect(x_offset, 0, w, h)
and center the rect by using its centery attribute:
rect.centery = the_other_surface.get_rect().centery
It can help if you rename the variables into something readable.
For example:
rect_width = w
rect_height = h
surface_width = a
surface_height = b
rect_width and rect_height will be the Rect's width and height.
The x_offset will be the left.
rect_left = x_offset
To center it vertically, find the vertical center:
surface_center_v = surface_height / 2
Then place the rectangle there.
rect_top = surface_center_v
However this only places the rectangle's top edge, and we want the vertical center.
So adjust the rectangle's position upwards by half of the rectangle's height, to make the rectangle's vertical center align with the surface's vertical center.
rect_top -= rect_height / 2
Now you have all of rect_left, rect_top, rect_width and rect_height.
To use all of the specified variables (rather than hard-coding absolute sizes into the program), I would make use of pygame's Rectangle object for both the surface and the rectangle we want to draw.
import pygame
from time import sleep
pygame.init()
# Declare our variables and initialize
a,b = 0,0 # Declare screen dimensions.
w,h = 0,0 # Declare rectangle dimensions.
x,y = 0,0 # Declare placement locations on screen surface.
# Now set specific values to our variables
a = 500 # Screen Width
b = 500 # Screen Height
# We could use any surface here (such as for a sprite), but let's use the screen.
screen = pygame.display.set_mode((a,b),32) # Create a screen surface of size a X b
screen.fill(pygame.Color("DARKBLUE")) # Fill it with Dark Blue.
screen_rect = screen.get_rect() # Create a rectangle object for the screen.
w = 200 # Make the rectangle 200 wide.
h = 100 # Make the rectangle 100 high.
rec = pygame.Rect((x,y,w,h)) # Create a rectangle object for our drawn rectangle.
# Rec holds all of the placement and size values for the rectangle we want to draw.
rec_color = (255,0,0) # Let's draw a red rectangle.
distance_x = 50 # Place the rectangle 50 pixels over from the left side of the screen.
rec.x = distance_x # Set rec's x value equal to distance_x.
rec.centery = screen_rect.centery # Set rec's y value to center of the screen y-axis
# Pygame's Rectangle Object handles the calculations for us. NICE!
pygame.draw.rect(screen,rec_color,rec,2) # Draw a rectangle on the screen using a line
# that is 2-pixels wide. (Use 0 to fill.)
pygame.display.flip() # Show the screen.
print("\nrec =",rec,end="\n\n") # Let's see rec's final values.
sleep(3) # Sleep for 3 seconds.
exit() # Quit

Is it possible to change turtle's pen stroke?

I need to draw a bar graph using Python's turtle graphics and I figured it would be easier to simply make the pen a thick square so I could draw the bars like that and not have to worry about making dozens of rectangles and filling them in.
When I set the turtle shape using turtle.shape('square') though, it only changes the appearance of the pen but has no effect on the actual drawing:
Is there a way to make turtle actually draw a rectangular stroke, whether that be through built-in methods or through modifying the turtle file?
I DON'T want rounded edges, like this:
To answer the question asked in the title: No, it is not possible to change the pen stroke directly (see cdlane's answer for a possible way to do it by modifying the hardcoded values from tkinter).
I did find a workaround for the use case presented in the question body, however.
A custom pen shape (in this case, representing the exact shape and size of the bar) can be registered like this:
screen.register_shape("bar", ((width / 2, 0), (-width / 2, 0), (-width / 2, height), (width / 2, height)))`
We can then simply loop through each bar, update the pen shape with the new values, and use turtle.stamp to stamp the completed bars onto the graph, no drawing required.
It looks like changing the shape of the pen stroke itself isn't possible. turtle.shape('square') only changes the shape of the turtle, not the pen stroke. I suggest lowering the pen size, and creating a function to draw a rectangle. You could use this do draw the bars.
I've two solutions to this problem that I've used in various programs.
The first is a variation on your stamp solution. Rather than use screen.register_shape() to register a custom polygon for each line, use a square turtle and for each line turtle.turtlesize() it into the rectangle you want to stamp:
from turtle import Turtle, Screen
STAMP_SIZE = 20 # size of the square turtle shape
WIDTH, LENGTH = 25, 125
yertle = Turtle(shape="square")
yertle.penup()
yertle.turtlesize(WIDTH / STAMP_SIZE, LENGTH / STAMP_SIZE)
yertle.goto(100 + LENGTH//2, 100) # stamps are centered, so adjust X
yertle.stamp()
screen = Screen()
screen.exitonclick()
My other solution, when I need to draw instead of stamp, is to reach into turtle's tkinter underpinning and modify turtle's hardcoded line end shape itself:
from turtle import Turtle, Screen
import tkinter as _
_.ROUND = _.BUTT
WIDTH, LENGTH = 25, 125
yertle = Turtle()
yertle.width(WIDTH)
yertle.penup()
yertle.goto(100, 100)
yertle.pendown()
yertle.forward(LENGTH)
screen = Screen()
screen.exitonclick()
Use multiple stamps like so:
import turtle
turtle.shape("square")
for count in range(x):
turtle.stamp()
turtle.forward(1)

axhspan set limits using coordinates

Is it possible to set the limits of the how far axhspan spand the x axis using coordinates rather than a value of 0-1? Usually the command takes:
axhspan(ymin, ymax, xmin=0, xmax=1, **kwargs)
I know that you can calculate the the 0-1 value for xmin and xmaxfrom the coordinates, but it just seem a long winded way to o it?
Example:
I would like the blue shading to go from 0-100, white 100-200, blue 200-400.
Is the only way to do this either by converting to value of 0-1 or just adding the rectangle shape s as opposed to using axhspan()?
Use matplotlib.patches.Rectangle with matplotlib.axes.Axes.add_patch.
For example:
from pylab import *
from matplotlib.patches import Rectangle
plot(arange(0, 500), arange(0, 500))
gca().add_patch(Rectangle((100, 100), 200, 200)) # (x, y), width, height
show()
NOTE the 2nd, 3rd parameters of Rectangle constructor are width, height (not x, y position).

Categories