I'm new one in coding, so I expect that this is easy one, but I still can't figure this out.
So I'm trying to make a program that animates a diagram, but the problem is when am making animation with equation (0,0) obviously in upper left corner.
I need to make a grid that will represent this, so I cant input my equation and get right animation depends on this grid's numbers Grid here
UPD:
Sorry for being less specific than I should.
The goal is to make a program that animates HR diagram, outputs luminosity and temperature after you inputs solar mass.
I didn't input equation so far because I'm trying to figure out how pygame animation works.
What I got so far trying to animate diagonal line:
import pygame
from pygame.locals import *
pygame.init()
width = 600
height = 600
screen = pygame.display.set_mode((width, height))
background = pygame.image.load("background.png")
point = pygame.image.load("point.png")
clock = pygame.time.Clock()
speed = 100
x = 0
y = 0
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
quit()
screen.blit(background, (0,0))
screen.blit(point, (x,y),)
milli = clock.tick()
second = milli/1000.
dm=second * speed
x += dm
y = x
print (x, y)
if x > 600 or y > 600:
x = 0
y = 0
pygame.display.update()
So I need to make axis like in diagram, so I can just type down the equation and make correct animation and correct outputs
Use a 2 dimensional array to represent a grid structure in python.
w, h = 10000, 50000;
grid = [[0 for x in range(w)] for y in range(h)]
Related
This question already has answers here:
Pygame: Draw single pixel
(6 answers)
Closed 4 months ago.
I'm trying to iterate through the numpy array and assigning a 0 - 255 value based on the distance to the mouse.
WIDTH and HEIGHT are in this case set to 400 and GRID[] is a numpy matrix with WIDTH and HEIGHT dimensions.
I'm using the window.set_at() function to draw each pixel on the screen with the color stored in the numpy matrix, I'm getting about 5 FPS.
Is there a more efficient way to handle this type of pixel processing, or should I switch to something like c++ & SFML
#update pixels
for y in range(HEIGHT):
for x in range(WIDTH):
#get color based on distance to mouse; 0 -> 255
mousePosition = pg.mouse.get_pos()
dx = mousePosition[0] - x
dy = mousePosition[1] - y
d = math.sqrt(abs(dx ** 2 + dy ** 2))
#constraining the distance value between 0 - 255
c = min(max(d, 0), 255)
GRID[x,y] = c
#draw pixels
for y in range(HEIGHT):
for x in range(WIDTH):
c = GRID[x,y]
window.set_at((x, y), (c, c, c))
What constitutes fast enough?
Tidying up your code to create a minimal example with your 400x400 resolution:
import math
import time
import pygame
width, height = 400, 400
pygame.init()
screen = pygame.display.set_mode((width, height))
screen.fill(pygame.Color("black"))
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Graphics
# update pixels
start = time.time()
mx, my = pygame.mouse.get_pos()
for y in range(height):
for x in range(width):
distance = math.sqrt( (mx - x)**2 + (my - y)**2 ) # 2.2 FPS 0.455s
#constrain the distance value between 0 - 255
c = min(max(distance, 0), 255)
screen.set_at((x, y), (c, c, c))
# Update Screen
pygame.display.set_caption(f"FPS: {clock.get_fps():.1f} Pixel Processing {time.time() - start:.3f} s")
pygame.display.update()
pygame.quit()
This results in 2.2 FPS on my PC.
The Python math module has a function that calculates the hypotenuse, so it's probably a little more optimised. Change the calculation to:
distance = math.hypot(mx - x, my - y)
This increases my frame rate by 50% to 3, probably still too slow.
We can be smarter about the pixels we modify, instead of clamping the distance to 255, fill the screen with white and then if the distance is greater than 255, don't change the pixel.
screen.fill(pygame.Color("white"))
for y in range(height):
for x in range(width):
distance = math.hypot(mx - x, my - y)
if distance <= 255: # don't set far pixels
c = round(distance)
screen.set_at((x, y), (c, c, c))
This increases my frame rate to 12 when the mouse is in the corner, 8 in the middle. Perhaps this is approaching usable.
If you look at the documentation for surface.set_at(), it says that using get_at() and set_at is too slow and recommends using PixelArray or SurfArray. So we can create a SurfArray:
surfarray = pygame.surfarray.pixels3d(screen)
Then to set the pixel values, we replace screen.set_at():
surfarray[x, y] = (c,c,c)
Surprisingly and unfortunately this doesn't change the frame rate significantly. Maybe this requires hardware acceleration.
I also tried manually locking the surface before iterating through the pixels as suggested in the docs, but this made no significant improvement.
So lets consider what we're doing, drawing the same circle wherever the mouse is every frame. It will be faster if we draw the circle once, and then blit it every frame centered on the mouse position. To create the circle, it's similar to what's already been done:
size = 255 * 2
dist_image = pygame.Surface((size, size), pygame.SRCALPHA)
for y in range(size):
for x in range(size):
distance = math.hypot(255 - x, 255 - y)
if distance <= 255:
c = round(distance)
dist_image.set_at((x, y), (c, c, c))
Then our graphics update logic becomes:
screen.fill(pygame.Color("white"))
dist_rect = dist_image.get_rect(center=pygame.mouse.get_pos())
screen.blit(dist_image, dist_rect)
This runs at 60 FPS (max) and takes almost no processing time:
pygame pixel processing is slow
Yes it is. So don't process pixels.
Given your colour resolution of 256, this means we're really dealing with a bunch of coloured circles around the mouse cursor. By considering only these circles, you're specifically not processing all those other pixels that can never be anything other than colour-zero.
The code below implements the program by drawing circles about the mouse cursor where the circle radius is the known distance. So we're drawing a circle in the same "distance-colour". Assuming the circle is drawn using the midpoint circle algorithm, this means that it only needs to calculate 1/8 of the pixels, and the rest are just quadrant (octant?) reflected about an axis of circle-symmetry.
import pygame
import random
# Window size
WINDOW_WIDTH = 600
WINDOW_HEIGHT = 600
###
### MAIN
###
pygame.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ) )
pygame.display.set_caption("Mouse Distance")
# Main loop
clock = pygame.time.Clock()
running = True
while running:
time_now = pygame.time.get_ticks()
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
running = False
# Paint the screen in a gradient centred about the mouse
window.fill( ( 0, 0, 0 ) ) # max distance
mouse_pos = pygame.mouse.get_pos()
for distance in range( 255 ):
colour = ( 255-distance, 255-distance, 255-distance )
pygame.draw.circle( window, colour, mouse_pos, distance, 2 ) # use a width of 2 so there's no "holes"
pygame.display.flip()
# Clamp FPS
clock.tick(60)
pygame.quit()
I don't have time to make the change right now, but this example should draw to a surface, and then blit() that surface to the window for painting. That way we only need to re-compute the surface when the mouse moves.
NOTE: This is a school-related assignment and I am in no capacity looking for a direct answer. Looking for support on coming up with algorithms as this is my first time with such a question
Programs Intended Purpose: Take a command line provided image and scale it up by a unit of 5. Use the RGB values from the original image to re-create it in a randomized fashion.
Algorithm Attempts:
Test image is 250x250 scaling up to 1250x1250. I've tried to break this into sections of 50(original image side divided by 5) and then use (r + g + b)/5 number of circles to generate the color needed. Ex: Color: (100,50,5) would use 20 red circles, 10 green circles, 1 blue circle in the 50x50 space, (100+50+5)/5 = 31, 20 + 10 + 1 = 31. The x and y coordinates of these circles inside the 50x50 space should be random.
My main issue here has been putting this into code.
Code Attempt 1: Is not related to algorithm, is simply attempt to print image using pygame.draw.circle(this is what I am required to use to make the circle)
import pygame
import sys
import random
image_name = sys.argv[1]
#Import Image
src_image = pygame.image.load(image_name)
(w,h) = src_image.get_size()
window = pygame.display.set_mode((w*5,h*5))
#Nested Loop To Iterate Through Rows And Columns Of Pixels
for y in range(h):
for x in range(w):
(r,g,b,_) = src_image.get_at((x,y))
print(f"{r},{g},{b},x:{x},y:{y}")
pygame.draw.circle(window,(r,g,b),(x*5,y*5),2)
pygame.display.update()
pygame.time.delay(5000)
Possible Solution, works as intended but is still a bit messy due to the randomization:
By dividing the r, g and b values by the scaling value, in this case 5, we get the number of circles needed to be drawn of each color in each block which is broken down to increments of 15. (reduced from 50 to allow more curves and edges of images to be shown as they were hidden with a randomization area of 50). Further luminance calculation is used to find any dark spots, specifically black, and prevent color from being printed in that section. 3 while loops are used to draw each colors separately as they are all drawn a different amount of times.
import pygame
import sys
import random
pygame.init()
image_name = sys.argv[1]
#Import Image
src_image = pygame.image.load(image_name)
#Get image size
(w,h) = src_image.get_size()
#Scale up and display window
window = pygame.display.set_mode((w*5,h*5))
#Nested Loop To Iterate Through Rows And Columns Of Pixels
for y in range(h):
for x in range(w):
#Get rgb values at x and y of image
(r,g,b,_) = src_image.get_at((x,y))
#chck if area is black
lum = (0.2126 * r + 0.7152 * g + 0.0788 * b)*255
#Calculate required number of circles
a = int(r/5)
k = int(g/5)
d = int(b/5)
#draw required number of circles
while(a > 0):
if(lum > 0.625):
pygame.draw.circle(window,(255,0,0),(random.randint((x*5)-15,(x*5)),random.randint((y*5)-15,(y*5))),1)
a-=1
while(k > 0):
if(lum > 0.625):
pygame.draw.circle(window,(0,255,0),(random.randint((x*5)-15,(x*5)),random.randint((y*5)-15,(y*5))),1)
k-=1
while(d > 0):
if(lum > 0.625):
pygame.draw.circle(window,(0,0,255),(random.randint((x*5)-15,(x*5)),random.randint((y*5)-15,(y*5))),1)
d-=1
#update screen
pygame.display.update()
#Keep window open until closed by user
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
You have to initialize pygame, before you can use any pygame feature:
pygame.init()
src_image = pygame.image.load(image_name)
You have to handle the events in the application loop. See pygame.event.get() respectively pygame.event.pump():
For each frame of your game, you will need to make some sort of call to the event queue. This ensures your program can internally interact with the rest of the operating system.
It is recommended to use an application loop.
Create a list of colors and shuffle it with random.shuffle:
import pygame
import sys
import random
pygame.init()
#Import Image
image_name = sys.argv[1]
src_image = pygame.image.load(image_name)
(w,h) = src_image.get_size()
window = pygame.display.set_mode((w*5,h*5))
for y in range(h):
for x in range(w):
(r, g, b, _) = src_image.get_at((x,y))
lum = (0.2126 * r + 0.7152 * g + 0.0788 * b)*255
if lum > 0.625:
a, k, d = r // 5, g // 5, b // 5
colors = [(255,0,0) for _ in range(a)] + [(0,255,0) for _ in range(k)] + [(0,0,255) for _ in range(d)]
random.shuffle(colors)
for c in colors:
pygame.draw.circle(window,c,(random.randint((x*5)-15,(x*5)),random.randint((y*5)-15,(y*5))),1)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.update()
pygame.time.delay(100)
pygame.quit()
I am just getting started with Pygame, and I did a little test of just printing pixels in random spots. However, I noticed that the pixels don't seem to be single pixels at all, but 'fuzzy' blobs of a few pixels, as shown in the image. Here's the code I used to draw the pixels:
Is there any way to just display single pixels?
Edit: Here's the whole code I used:
import pygame.gfxdraw
import pygame
import random
width = 1000
height = 1000
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()
running = True
while running:
x = random.randint(0,1000)
y = random.randint(0,1000)
pygame.gfxdraw.pixel(screen, x, y, (225,225,225))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.flip()
clock.tick(240)
more fuzzy pixels
pygame.gfxdraw.pixel(surface, x, y, color) will draw a single pixel on the given surface.
Also you will need to add import pygame.gfxdraw.
EDIT: Full code:
import pygame.gfxdraw
import pygame
import random
width = 1680
height = 1050
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()
running = True
x = [i for i in range(width - 10)]
x_full = [i for i in range(width)]
y = 100
y_full = [i for i in range(height // 2)]
while running:
for i in x:
for j in y_full:
pygame.gfxdraw.pixel(screen, i, j, pygame.Color("red"))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.flip()
clock.tick(1)
Try to test it this way, I set the window size to fit my monitor resolution so it goes fullscreen. x_full and y should give you horizontal line. And if you subtract, for example, 10 you will get slightly shorter line, and vice-versa with y_full and some random x. Also using (width//2, height//2) will cover exactly quarter of the screen. I think it is accurate and that pygame.gfxdraw.pixel(screen, i, j, pygame.Color("red")) displays only single pixel as it should.
In your code you are using random to display pixels and it adds them 240 per second so you are very fast ending up with bunch of pixels at random positions resulting to have pixels close to each other looking as a "bigger one". I think this is what was happening here. Please someone correct me if I am wrong.
Also make small window e.g. (100, 100) and draw one pixel at (50, 50) this way it can be more easily seen. If you are on windows use magnifier to test it.
IMPORTANT:
While testing this with huge number of pixels do it OUTSIDE of the loop because it will consume much processor power to display them.
Hope this answers your question
I've been going through some Python tutorials using Python 2.7 and Pygame and I decided to challenge myself. The tutorial showed how to make a ball move (right) across the screen, then pop back to the other (left) side of the screen at a specific speed. I wanted to make the ball bounce back and forth from left to right, so I wrote this:
bif = "bg.jpg"
mif = "ball1.png"
import pygame, sys
from pygame import *
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((816,460),0,32)
background = pygame.image.load(bif).convert()
ball = pygame.image.load(mif).convert_alpha()
x = 0
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
screen.blit(background, (0,0))
screen.blit(ball, (x, 160))
speed = 500
milli = clock.tick() #A tick is 1 millisecond
seconds = milli/1000.000000
dm = seconds * speed
if x == 0:
a = dm
elif x == 770:
a = -dm
x += a
pygame.display.update()
"bg.jpg" is a jpeg image that is 816 x 460 pixels and "bif.png" is a png image of a ball with a 50 pixel radius. Instead of moving back and forth at 500 pixels per second, the ball moves at a random speed to the right, then bounces off of the right side of the screen at a random speed to the left, and repeats this a random number of times. Then the ball keeps going in one direction and doesn't come back. I can't figure out why it's doing this. It behaves differently every time I run it. If anybody can figure out why, I'd be really thankful.
tick(), without arguments returns time which passed since last call. In your use it depends on rendering speed which will always be different that is why you get different speed each time.
Replace from speed = 500 to the end with:
speed = 1
if x == 0 or x == 770:
speed = -speed
x += speed
pygame.display.update()
clock.tick(60)
How would you make a ball move in a repetitive wavey pattern just like a sin() graph does?
You can use a counter, pygame's Clock, or just pygame.time.get_ticks to figure out the time. Here's some sample code to get you started.
import math
import pygame
pygame.init()
screen = pygame.display.set_mode((400,400))
while True:
t = pygame.time.get_ticks() / 2 % 400 # scale and loop time
x = t
y = math.sin(t/50.0) * 100 + 200 # scale sine wave
y = int(y) # needs to be int
screen.fill((0,0,0))
pygame.draw.circle(screen, (255,255,255), (x, y), 40)
pygame.display.flip()