Python/Pygame: Drawing graph origin mathematically? - python

I am working on a graphing program that I am calling PyGraph.
It allows you to create a graph of any size and draw on it, and later in development I will provide coordinates and things, but for now I have one question: How can I draw a intersecting lines through the center to represent the origin?
Here is what I have so far:
#pygraph
import pygame
from pygame.locals import *
pygame.init()
screen=pygame.display.set_mode((640,480))
x=0
y=0
size=16
screen.fill((255,255,255))
pygame.draw.line(screen, (0,0,0), (screen.get_width()/2,0),(screen.get_width()/2,screen.get_height()),5)
pygame.draw.line(screen, (0,0,0), (0,screen.get_height()/2),(screen.get_width(),screen.get_height()/2),5)
while True:
while y<480:
pygame.draw.rect(screen,(0,0,0),(x,y,size,size),1)
if x>640:
x=0
y+=size
pygame.draw.rect(screen,(0,0,0),(x,y,size,size),1)
x+=size
for e in pygame.event.get():
if e.type==QUIT:
exit()
if e.type==KEYUP:
if e.key==K_SPACE:
x=0
y=0
screen.fill((255,255,255))
pygame.draw.line(screen, (0,0,0), (screen.get_width()/2,0),(screen.get_width()/2,screen.get_height()),5)
pygame.draw.line(screen, (0,0,0), (0,screen.get_height()/2),(screen.get_width(),screen.get_height()/2),5)
size=input('Enter size: ')
pygame.display.flip()
The lines go though the center, but it doesn't work for every size graph. I'm not the best at math, but I hope this isn't obvious.. any advice?

The problem is that you draw the grid using the top left corner as your anchor. That is, all your grid rectangles have one corner in the top left. This becomes a problem when the distance between the center line and the screen edge is not divisible by the size - you can't divide a line of 640 units into even divisions of 15, for example.
A far better solution would be to use the center as the anchor. So basically, all the grid rectangles have one corner in the center of the graph, which means you will never get any "remainder" on the center line, and the "remainder" will instead be on the border of the graph, which looks much nicer.
Here is code for anchoring your rectangles at the center (should replace your original while y<480 loop):
while y<=480/2+size:
pygame.draw.rect(screen,(0,0,0),(640/2+x, 480/2+y,size,size),1)
pygame.draw.rect(screen,(0,0,0),(640/2-x, 480/2+y,size,size),1)
pygame.draw.rect(screen,(0,0,0),(640/2+x, 480/2-y,size,size),1)
pygame.draw.rect(screen,(0,0,0),(640/2-x, 480/2-y,size,size),1)
x+=size
if x>=640/2+size:
x=0
y+=size
Brief explanation:
I change the anchor of the rectangle (the point you pass into pygame.draw.rect) to the center of the graph, and instead of drawing one rectangle, I draw four - one in each quadrant of the graph.
I also fixed the code a bit to not need to call pygame.draw.rect() in the if statement.
A minor style tip:
Replace 480 and 640 with "screen.width" and "screen.height", so you can adjust the width and height later without problems.

Related

Pygame update display

I am writing a program where sensors(the white circles) are moving on the map to cover things. I counted the number of covered things(the small white dots), it's the right number. But the old covered things and sensor(white circles) are still on the screen. How can I update the display so that only newest objects are on the screen(no red dots should be in the circle path, and the circle path history should not be displayed).
Here is my display code. It gets called every frame.
def _drawMap(self,ticks):
# draw the map
# draw the boundary
boundary = self._polygonCoordinationTransformation(self.data.boundary_polygon_list)
pygame.draw.polygon(self.screen, BLUE, boundary, LINEWIDTH)
# draw obstacles
for polygon in self.data.obstacles_list:
polygon = self._polygonCoordinationTransformation(polygon)
pygame.draw.polygon(self.screen, BLUE, polygon, LINEWIDTH)
# draw agents
self._executeSegment(self.agentsGroup.sprites()[0],(5,10),ticks)
for agent in self.agentsGroup:
pygame.draw.circle(self.screen, WHITE, agent.rect.center, self.data.sensor_range * self.scaleForMap,LINEWIDTH)
# draw items
self.updateUnseenItems()
for itemObj in self.unseenItemGroup:
pygame.draw.rect(self.screen, RED, itemObj, LINEWIDTH)
In pygame you can't clear the screen - instead you re-blit the background back over the top of what is currently there.
There's an excellent answer to the same question here.
Edit: In your case you probably want to fill the screen in with black:
screen.fill( (0,0,0) )

Creating multiple colored turtles in one window in editor on Python 3.4

So far I have this and it makes two circles but one is off-screen. I want to center it and have them separate from each other. Right now it does two loops but I want it to do one small circle then go on to make a larger one around the first in the middle of the screen. Both need to be diff. colors.
def sun_and_earth():
import turtle #allows me to use the turtles library
turtle.Turtle()
turtle.Screen() #creates turtle screen
turtle.window_height()
turtle.window_width()
turtle.bgcolor('grey') #makes background color
turtle.color("red", "green")
turtle.circle(2, 360) #draws a (size, radius) circle
turtle.circle(218, 360)
turtle.exitonclick() #exits out of turtle window on click of window
I think you may have some misunderstanding with regard to some of the functions in the turtle library. Firstly, turtle.window_height() and turtle.window_width() return the height and width of the window, so (as these values are not being assigned) those two lines do nothing. Similarly, turtle.Screen() returns an object, so again that line does nothing.
In order to centre your circles, you need to change where the turtle starts by using the turtle.setpos() function. This will change the x and y coordinates of where your turtle is. If you start the turtle one radius down, this will effectively centre the circle at (0, 0), because the center of the circle is (from the documentation) one radius to the left.
Remember to take your pen off the page when you are moving so that you don't draw lines between the two points by accident, and to put the pen back down again when you want to draw again.
Try this code:
import turtle
turtle.Turtle()
turtle.bgcolor('grey')
# decide what your small circle will look like
smallColour = "red"
smallRadius = 5
# draw the small circle
turtle.color(smallColour)
turtle.penup()
turtle.setpos(0, -smallRadius)
turtle.pendown()
turtle.circle(smallRadius)
# decide what your large circle will look like
largeColour = "white"
largeRadius = 100
# draw the large circle
turtle.color(largeColour)
turtle.penup()
print(turtle.pos())
turtle.setpos(0, -largeRadius)
print(turtle.pos())
turtle.pendown()
turtle.circle(largeRadius)
turtle.penup()
turtle.setpos(0, 0)
I hope this helps, but I think that you have a few misunderstandings about the use of the turtle, it might be a good idea to look at a tutorial or maybe take a look at the documentation
Best of luck

Pygame: Problems with pointing image to mouse

From my understanding, this:
angle_to_pointer = degrees(atan2((py+32)-mouse[0], px-mouse[1]))+90
is a good way to get the angle between points..
I have this image:
and I'm trying to make it point to the mouse with this script:
import pygame
from pygame.locals import *
from math import degrees,atan2
pygame.init()
screen=pygame.display.set_mode((640,480))
arrow=pygame.image.load('arrow.png')
px=30
py=30
while True:
screen.fill((0,0,255))
mouse=pygame.mouse.get_pos()
angle_to_pointer = degrees(atan2((py+32)-mouse[0], px-mouse[1]))+90
for e in pygame.event.get():
if e.type==QUIT:
exit()
spr=pygame.transform.rotate(arrow,angle_to_pointer)
screen.blit(spr,(px,py))
pygame.display.flip()
It appears to work at first, but upon closer inspection, it appears to be pointing a little bit away from the mouse.
I tried fiddling with the values, but the result never came out the way I wanted it to, the code I posted contains the best combination I could create.
Could someone tell me what I am doing incorrectly?
This is getting too much for a comment. In your angle_to_pointer calculation you are offsetting your mouse in the Y coordinate by 32, which puts you at the bottom left of your unrotated image. you probably ment to add the 32 to the X coordinate which would put you on the center for X but still off on the Y. Also I think your mouse coordinates are backwards.
Even if you added 16 to the Y and 32 to the X this is still all based on the unrotated image. Once you rotate the image your size will change. The easiest way I can think of to do what you are wanting is to not draw your image off of the top left, but use the center. Find the point you want to be the center and base your angle_to_pointer off that. Then when you blit use the new rotated image size to find the top left.
for example:
your image is 64x32 so for fun, lets use the point (37,37) as our center (to keep it from going over the edge of the screen)
px=37 # center of arrow
py=37 # center of arrow
while True:
screen.fill((0,0,255))
mouseX, mouseY=pygame.mouse.get_pos() # unpack to avoid confustion
angle_to_pointer = degrees(atan2(mouseY - py, mouseX - px)) # calculate off center of image
for e in pygame.event.get():
if e.type == QUIT:
exit()
spr=pygame.transform.rotate(arrow, -angle_to_pointer) # clockwise rotation
# adjust draw top left based on center and rotated image size
blit_pos = (px - spr.get_width()//2, py - spr.get_height//2)
screen.blit(spr, blit_pos)
pygame.display.flip()
**disclaimer, haven't tried this since my work computer doesn't have pygame,

how do i adjust this turtle graphics drawing to draw around a circle?

with the current code, this is the result.
but I'm trying to get it to look like this, but not sure what to tweak.
#circle circle
import turtle
turtle.colormode(255)
window=turtle.Screen()
draw=turtle.Turtle()
draw.pensize(2)
window.screensize(1200,1200)
draw.speed('fastest')
red, green, blue= 255, 255, 0
for shape in range(30):
for circle in range(1):
draw.circle(200)
draw.penup()
draw.forward(30)
draw.pendown()
draw.left(20)
green=green-5
blue=blue+6
draw.color(red, green, blue)
window.mainloop()
If you notice your two images, the one you want has the circles drawn outside the center, while the wrong one draws them around the center. Try changing draw.left(20) to draw.right(20) and adjust your values from there to get the sizes you want.
The change keeps the turtle outside of the circle just drawn, which is what you're looking for.

How can I make my circles fly off the screen in pygame?

I am a begginner at python and I'm trying to make a circle game. So far it draws a circle at your mouse with a random color and radius when you click.
Next, I would like the circle to fly off the screen in a random direction. How would I go about doing this? This is the main chunk of my code so far:
check1 = None
check2 = None
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit
if event.type == MOUSEBUTTONDOWN:
last_mouse_pos = pygame.mouse.get_pos()
if last_mouse_pos:
global check
color1 = random.randint(0,255)
color2 = random.randint(0,255)
color3 = random.randint(0,255)
color = (color1,color2,color3)
radius = random.randint (5,40)
posx,posy = last_mouse_pos
if posx != check1 and posy != check2:
global check1, check2
screen.lock()
pygame.draw.circle(screen, color, (posx,posy), radius)
screen.unlock()
check1,check2 = posx,posy
pygame.display.update()
Again, I want the circle to fly off the screen in a random direction.
I have made a few attempts but no successes yet.
Also, thanks to jdi who helped me s
When you create the circle (on click), generate 2 random numbers. These will be your (x,y) components for a two dimensional Euclidean velocity vector:
# interval -1.0 to 1.0, adjust as necessary
vx, vy = ( (random.random()*2) -1, (random.random()*2) - 1 )
Then after the ball has been created, on each iteration of the game loop, increment your ball's position by the velocity vector:
posx, posy = posx + vx, posy + vy
Note that the overall speed might be variable. If you want to always have a speed of 1.0 per seconds, normalize the vector:
To normalize the vector, you divide it by its magnitude:
So in your case:
And hence:
So in Python, after importing math with import math:
mag = math.sqrt(vx*vx + vy*vy)
v_norm = vx/mag, vy/mag
# use v_norm instead of your (vx, vy) tuple
Then you can multiply this with some speed variable, to get reliable velocity.
Once you progress to having multiple objects moving around and potentially off screen, it is useful to remove the offscreen objects which have no chance of coming back, and have nothing to do with your program anymore. Otherwise, if you keep tracking all those offscreen objects while creating more, you get essentially a memory leak, and will run out of memory given enough time/actions.
While what you are doing right now is quite simple, assuming you haven't already, learning some basic vector math will pay itself off very soon. Eventually you may need to foray into some matrix math to do certain transformations. If you are new to it, its not as hard as it first looks. You can probably get away with not studying it, but you will save yourself effort later if you start attempting to do more ambitious things.
Right now, you are doing the following (drastically simplifying your code)...
while True:
if the mouse was clicked:
draw a circle on the screen where the mouse was clicked
Let's make things a little easier, and build up gradually.
Start with the circle without the user clicking
To keep things simple, let's make the circle near the top left of the screen, that way we can always assume there will be a circle (making some of the logic easier)
circle_x, circle_y = 10,10
while True:
draw the circle at circle_x, circle_y
pygame.display.update()
Animate the circle
Before going into too much about "random directions", let's just make it easy and go in one direction (let's say, always down and to the right).
circle_x, circle_y = 0,0
while True:
# Update
circle_x += 0.1
circle_y += 0.1
# Draw
draw the circle at circle_x, circle_y
update the display
Now, every time through the loop, the center of the circle moves a bit, and then you draw it in its new position. Note that you might need to reduce the values that you add to circle_x and y (in my code, 0.1) in case the circle moves too fast.
However, you'll notice that your screen is now filling up with circles! Rather than one circle that is "moving", you're just drawing the circle many times! To fix this, we're going to "clear" the screen before each draw...
screen = ....
BLACK = (0,0,0) # Defines the "black" color
circle_x, circle_y = 0,0
while True:
# Update
circle_x += 0.1
circle_y += 0.1
# Draw
screen.fill(BLACK)
draw the circle at circle_x, circle_y
update the display
Notice that we are "clearing" the screen by painting the entire thing black right before we draw our circle.
Now, you can start work the rest of what you want back into your code.
Keep track of multiple circles
You can do this by using a list of circles, rather than two circle variables
circles = [...list of circle positions...]
while True:
# Update
for circle in circles:
... Update the circle position...
# Draw
screen.fill(BLACK)
for circle in circles:
draw the circle at circle position # This will occur once for each circle
update the display
One thing to note is that if you keep track of the circle positions in a tuple, you won't be able to change their values. If you're familiar with Object Oriented Programming, you could create a Circle class, and use that to keep track of the data relating to your circles. Otherwise, you can every loop create a list of updated coordinates for each circle.
Add circle when the user clicks
circles = []
while True:
# event handling
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
circles.append( pos ) # Add a new circle to the list
# Update all the circles
# ....
# Draw
clear the screen
for circle_position in circles:
draw the circle at circle_position # This will occur once for each circle
update the display
Have the circle move in a random direction
This is where a good helping of math comes into play. Basically, you'll need a way to determine what to update the x and y coordinate of the circle by each loop. Keep in mind it's completely possible to just say that you want it to move somewhere between -1 and 1 for each axis (X, y), but that isn't necessarily right. It's possible that you get both X and Y to be zero, in which case the circle won't move at all! The next Circle could be 1 and 1, which will go faster than the other circles.
I'm not sure what your math background is, so you might have a bit of learning to do in order to understand some math behind how to store a "direction" (sometimes referred to as a "vector") in a program. You can try Preet's answer to see if that helps. True understanding is easier with a background in geometry and trigonometry (although you might be able to get by without it if you find a good resource).
Some other thoughts
Some other things you'll want to keep in mind:
Right now, the code that we're playing with "frame rate dependent". That is, the speed in which the circles move across the screen is entirely dependent on how fast the computer can run; a slower computer will see the circles move like snails, while a faster computer will barely see the circles before they fly off the screen! There are ways of fixing this, which you can look up on your own (do a search for "frame rate dependence" or other terms in your favorite search engine).
Right now, you have screen.lock() and screen.unlock(). You don't need these. You only need to lock/unlock the screen's surface if the surface requires it (some surfaces do not) and if you are going to manually access the pixel data. Doing things like drawing circles to the screen, pygame in lock/unlock the surfaces for you automatically. In short, you don't need to deal with lock/unlock right now.

Categories

Resources