Why is this grid not even? - python

It's probably something extremely obvious, but I can't seem to find why the first to columns in the grid are the same.
grid = [[1]*8 for n in range(8)]
cellWidth = 70
def is_odd(x):
return bool(x - ((x>>1)<<1))
def setup():
size(561, 561)
def draw():
x,y = 0,0
for xrow, row in enumerate(grid):
for xcol, col in enumerate(row):
rect(x, y, cellWidth, cellWidth)
if is_odd(xrow+xcol):
fill(0,0,0)
else:
fill(255)
x = x + cellWidth
y = y + cellWidth
x = 0
def mousePressed():
print mouseY/cellWidth, mouseX/cellWidth
print is_odd(mouseY/cellWidth + mouseX/cellWidth)
The result I get from the code above is:
Any ideas?

Looks like the fill command doesn't change the color of the last rectangle you drew; instead it changes the color of all the draw calls subsequent to it. According to the docs:
Sets the color used to fill shapes. For example, if you run fill(204, 102, 0), all subsequent shapes will be filled with orange.
So all of your colors are lagging one square behind. It's as if all of the tiles were shifted one to the right, except for the leftmost row which is shifted one down and eight to the left. This makes that row mismatch with all the others.
Try putting your fill calls before the rect call:
for xcol, col in enumerate(row):
if is_odd(xrow+xcol):
fill(0,0,0)
else:
fill(255)
rect(x, y, cellWidth, cellWidth)
x = x + cellWidth

Related

Is there a way to make a matplotlib grid interactable using motion_notify_event or the mouse package?

I'm making an implementation of Conway's Game of Life using matplotlib and numpy. However, I'm having lots of trouble trying to figure out how to make the grid interactable. Basically, whenever I click, the code checks whether the mouse is not on the grid (which would return a tuple None, None), and then using the mouse's position if it is on the grid the code sets the pixel of the grid closest to the mouse to ON (255). I tried to implement this using this function:
def mouse_move(event):
xy = event.x, event.y
return xy
and then in the function doing the updating of the grid:
def update(frameNum, img, grid, N, msx = 0, msy = 0):
# copy grid because we need 8 neighbors
# and we go line by line
newGrid = grid.copy()
# this is the part where I use the mouse pos
if ms.is_pressed('left') == True:
msx, msy = plt.connect('motion_notify_event', mouse_move)
print(msx, msy)
newGrid[msx, msy] = ON
# this is the end of the part where I use the mouse pos
for i in range(N):
for j in range(N):
# compute 8-neighbor sum using toroidal boundary conditions
# x and y wrap around so that sim is toroidal
total = int((grid[i, (j-1)%N] + grid[i, (j+1)%N] +
grid[(i-1)%N, j] + grid[(i+1)%N, j] +
grid[(i-1)%N, (j-1)%N] + grid[(i-1)%N, (j+1)%N] +
grid[(i+1)%N, (j-1)%N] + grid[(i+1)%N, (j+1)%N])/255)
# apply rules
if grid[i, j] == ON:
if (total < 2) or (total > 3):
newGrid[i, j] = OFF
else:
if (total == 3):
newGrid[i, j] = ON
# update data
img.set_data(newGrid)
grid[:] = newGrid[:]
return img
The issue here is that plt.connect() was only returning 1, 0 for some reason and not the mouse's position.
I then tried to ditch using motion_notify_event altogether and opted to use the mouse package:
def get_ms():
# the pixel dimensions of the grid are (200, 140) and (575, 510) on my screen
x, y = ms.get_position()
if (x > 575 or x < 200) and (y > 510 or y < 140):
return None, None
else:
return x, y
But this is where I'm stumped. Even though I have my mouse's pixel position, it isn't the grid position that I want. I know there has to be a way to do this with plt.connect('motion_notify_event', mouse_move) but I don't know what it is. I've already used basic debugging techniques. Am I doing something wrong?
Also, I've included the entire update() function in case I messed up there.

Draw a circle in a numpy array given index and radius without external libraries

I need to draw a circle in a 2D numpy array given [i,j] as indexes of the array, and r as the radius of the circle. Each time a condition is met at index [i,j], a circle should be drawn with that as the center point, increasing all values inside the circle by +1. I want to avoid the for-loops at the end where I draw the circle (where I use p,q to index) because I have to draw possibly millions of circles. Is there a way without for loops? I also don't want to import another library for just a single task.
Here is my current implementation:
for i in range(array_shape[0]):
for j in range(array_shape[1]):
if (condition): # Draw circle if condition is fulfilled
# Create a square of pixels with side lengths equal to radius of circle
x_square_min = i-r
x_square_max = i+r+1
y_square_min = j-r
y_square_max = j+r+1
# Clamp this square to the edges of the array so circles near edges don't wrap around
if x_square_min < 0:
x_square_min = 0
if y_square_min < 0:
y_square_min = 0
if x_square_max > array_shape[0]:
x_square_max = array_shape[0]
if y_square_max > array_shape[1]:
y_square_max = array_shape[1]
# Now loop over the box and draw circle inside of it
for p in range(x_square_min , x_square_max):
for q in range(y_square_min , y_square_max):
if (p - i) ** 2 + (q - j) ** 2 <= r ** 2:
new_array[p,q] += 1 # Incrementing because need to have possibility of
# overlapping circles
If you're using the same radius for every single circle, you can simplify things significantly by only calculating the circle coordinates once and then adding the center coordinates to the circle points when needed. Here's the code:
# The main array of values is called array.
shape = array.shape
row_indices = np.arange(0, shape[0], 1)
col_indices = np.arange(0, shape[1], 1)
# Returns xy coordinates for a circle with a given radius, centered at (0,0).
def points_in_circle(radius):
a = np.arange(radius + 1)
for x, y in zip(*np.where(a[:,np.newaxis]**2 + a**2 <= radius**2)):
yield from set(((x, y), (x, -y), (-x, y), (-x, -y),))
# Set the radius value before running code.
radius = RADIUS
circle_r = np.array(list(points_in_circle(radius)))
# Note that I'm using x as the row number and y as the column number.
# Center of circle is at (x_center, y_center). shape_0 and shape_1 refer to the main array
# so we can get rid of coordinates outside the bounds of array.
def add_center_to_circle(circle_points, x_center, y_center, shape_0, shape_1):
circle = np.copy(circle_points)
circle[:, 0] += x_center
circle[:, 1] += y_center
# Get rid of rows where coordinates are below 0 (can't be indexed)
bad_rows = np.array(np.where(circle < 0)).T[:, 0]
circle = np.delete(circle, bad_rows, axis=0)
# Get rid of rows that are outside the upper bounds of the array.
circle = circle[circle[:, 0] < shape_0, :]
circle = circle[circle[:, 1] < shape_1, :]
return circle
for x in row_indices:
for y in col_indices:
# You need to set CONDITION before running the code.
if CONDITION:
# Because circle_r is the same for all circles, it doesn't need to be recalculated all the time. All you need to do is add x and y to circle_r each time CONDITION is met.
circle_coords = add_center_to_circle(circle_r, x, y, shape[0], shape[1])
array[tuple(circle_coords.T)] += 1
When I set radius = 10, array = np.random.rand(1200).reshape(40, 30) and replaced if CONDITION with if (x == 20 and y == 20) or (x == 25 and y == 20), I got this, which seems to be what you want:
Let me know if you have any questions.
Adding each circle can be vectorized. This solution iterates over the coordinates where the condition is met. On a 2-core colab instance ~60k circles with radius 30 can be added per second.
import numpy as np
np.random.seed(42)
arr = np.random.rand(400,300)
r = 30
xx, yy = np.mgrid[-r:r+1, -r:r+1]
circle = xx**2 + yy**2 <= r**2
condition = np.where(arr > .999) # np.where(arr > .5) to benchmark 60k circles
for x,y in zip(*condition):
# valid indices of the array
i = slice(max(x-r,0), min(x+r+1, arr.shape[0]))
j = slice(max(y-r,0), min(y+r+1, arr.shape[1]))
# visible slice of the circle
ci = slice(abs(min(x-r, 0)), circle.shape[0] - abs(min(arr.shape[0]-(x+r+1), 0)))
cj = slice(abs(min(y-r, 0)), circle.shape[1] - abs(min(arr.shape[1]-(y+r+1), 0)))
arr[i, j] += circle[ci, cj]
Visualizing np.array arr
import matplotlib.pyplot as plt
plt.figure(figsize=(8,8))
plt.imshow(arr)
plt.show()

Locate color position on screen?

I want to click on a specific color on the screen with pyautogui, but for that I need its position, and I can't find any useful information about the topic. I'm trying to make a Piano Tiles autoclicker and for that I've thought about identifying the tiles' color and clicking it.
You can find color position with pyautogui:
import pyautogui
color = (255, 255, 255)
s = pyautogui.screenshot()
for x in range(s.width):
for y in range(s.height):
if s.getpixel((x, y)) == color:
pyautogui.click(x, y) # do something here
Consider making a screenshot of smaller area to identify pixels faster.
pyautogui.screenshot(region=(0,0, 300, 400))
The argument is a four-integer tuple of the left, top, width, and height of the region to capture. You can even grab only one pixel of each tile to make it work better. I don't think making a screenshot of the whole screen would be a great idea, especially when tiles goes fast.
How I would do it:
use pyautogui.position() to get coords of one pixel of each region where tiles appears (assuming color of tile is solid and is not changing during the game)
use getpixel() to obtain the RGB values of tile pixel
check in loop if pixels with coordinates from step 1 have the same RGB values you obtained in step 2.
Call pyautogui.click() if yes
Here is another version that counts how many pixels are in the region:
import pyautogui
def checkForRGBValues(start=(0,0), end=(50,50), R=255, G=255, B=255): #start/end/r/g/b value, I put some standard values for testing
x = int(end[0]-start[0] + 1) #calculates x value between start and end
y = int(end[1]-start[1] + 1) #calculates y value between start and end
how_many_pixels_found = 0 #nothing found yet
for x in range(start[0], end[0] + 1, 1): #loops through x value
for y in range(start[1], end[1] + 1, 1): #loops through y value
if pyautogui.pixel(x, y)[0] == R and pyautogui.pixel(x, y)[1] == G and pyautogui.pixel(x, y)[2] == B: #checks if the wanted RGB value is in the region
how_many_pixels_found = how_many_pixels_found + 1 #adds one to the result
y = y + 1
x = x + 1
return how_many_pixels_found #returns the total value of pixels with the wanted color in the region.
x = checkForRGBValues((150,200), (200,250), R=60, G=63, B=65)
print(x)
This is my first post! :) I had the same problem, but I found a solution. My code is probably not following any programming standard, but it is working hahaha! I started programming in Python 2 months ago (some experience 20 years ago (QBasic/C/Java), but never anything professional). Please tell me if is working for you and if there is anything that I can improve. I hope I can help somebody with this post, since this site has been helping me so much in the last 2 months!
def checkForRGBValues(start=(0,0), end=(50,50), R=255, G=255, B=255):
x = int(end[0]-start[0] + 1)
y = int(end[1]-start[1] + 1)
# print(x)
# print(y)
for x in range(start[0], end[0] + 1, 1):
for y in range(start[1], end[1] + 1, 1):
print(str(x) + "/" + str(y))
if pyautogui.pixel(x, y)[0] == R and pyautogui.pixel(x, y)[1] == G and pyautogui.pixel(x, y)[2] == B:
print("Color found")
with open("color_found.txt", "a") as file_found:
file_found.write(str(x) + "/" + str(y))
file_found.write("\n")
else:
with open("color_not_found.txt", "a") as file:
file.write(str(x) + "/" + str(y))
file.write("\n")
y = y + 1
x = x + 1
checkForRGBValues((150,200), (200,250), R=255, G=0, B=0) #if nothing (0,0), (50,50)

Increasing image width by one pixel

I am trying to implement a program, that will increase the width of an image by one pixel. I then want to take the new maximum x ordinate and put this with a random y ordinate (that is within the range of the image) to create a new pixel.
for x in range (0,getWidth(pic)):
for y in range (0,getHeight(pic)):
X=getWidth(pic)
newX = (X+1)
colr=(255,0,0)
newPixel = getPixel (pic, newX, y)//line 25
setColor(newPixel, colr)
Y=getHeight(pic)
newY= (Y+1)
newPixel = getPixel( pic,x, newY)
setColor(newPixel, colr)
I get this error:
getPixel(picture,x,y): x (= 226) is less than 0 or bigger than the width (= 224)
The error was:
Inappropriate argument value (of correct type).
An error occurred attempting to pass an argument to a function.
Please check line 25 of D:\bla bla
I understand it is out of the range. What am I doing wrong?
Here is generalized approach to increase the size of an image keeping its current content:
Feel free to adapt.
# Increase a picture given an offset, a color and the anciant
# content must be centered or not.
# Offsets must be positive.
def increaseAndCopy(pic, offsetX, offsetY, bg_color=black, center=True):
# Offsets must be positive
if (offsetX < 0.0) or (offsetY < 0.0):
printNow("Error: Offsets must be positive !")
return None
new_w = pic.getWidth() + int(2*offsetX)
new_h = pic.getHeight() + int(2*offsetY)
startX = 0
startY = 0
if (center) and (offsetX > 1.0):
startX = int(offsetX)
if (center) and (offsetY > 1.0):
startY = int(offsetY)
new_pic = makeEmptyPicture(new_w, new_h)
# Fill with background color
setAllPixelsToAColor(new_pic, bg_color)
# Process copy
for x in xrange(pic.getWidth()):
for y in xrange(pic.getHeight()):
px = getPixel(pic, x, y)
new_px = getPixel(new_pic, x + startX, y + startY)
setColor(new_px, getColor(px))
return new_pic
file = pickAFile()
picture = makePicture(file)
# Pass an offset of 0.5 to increase by 1 pixel
#new_picture = increaseAndCopy(picture, 0.5, 0, blue)
new_picture = increaseAndCopy(picture, 10, 20, gray, True)
if (new_picture):
writePictureTo(new_picture, "/home/biggerPic.png")
show(new_picture)
Output (Painting by Jean-Michel Basquiat):
...........................................................
How can you get something that an object does not have?
newPixel = getPixel (pic, newX, y)//line 25
The original image remains sized at getWidth(pic) but you are asking for a pixel at getWidth(pic) + 1 which does not exist.
You can enlarge the image by copying it to a new picture similar to this answer.
...
newPic=makeEmptyPicture(newX,newY)
xstart=0
ystart=0
for y in range(ystart,newY):
for x in range(xstart, newX):
if x == newX or y == newY:
colour=(255,0,0)
else:
oldPixel=getPixel(oldPic,x,y)
colour=getColor(oldPixel)
newPixel=getPixel(newPic,x,y)
setColor(newPixel,colour)
explore(newPic)

Loop patchwork in python3

I need to create a patchwork in Python 3. All I have left to do is create a loop which makes the design border the graphics window. I know I need a for loop however I am not sure how to do this.
This is what I have so far:
from graphics import *
def main():
height = eval(input("What is the height of the window"))
width = eval(input("What is the width of the window"))
colour = input("enter the colour of the patch")
win = GraphWin("Patch", 100*width, 100*height)
boat_x = 0
boat_y = 0
for x in range (4):
boat(win, boat_x, boat_y, colour)
boat_x = boat_x + 23
for i in range(height * 5):
boat(win, boat_x, boat_y, colour)
boat_x = boat_x + 24
for j in range(height * 5):
boat(win, boat_x, boat_y, colour)
boat_y = boat_y + 100
win.getMouse()
win.close()
def boat(win, x, y, colour):
body1 = Polygon(Point(1+x,95+y), Point(5+x,100+y),
Point(20+x,100+y), Point(24+x,95+y))
body1.draw(win)
line1 = Line(Point(13+x,95+y), Point(13+x,90+y))
line1.draw(win)
sail1 = Polygon(Point(1+x,90+y), Point(24+x,90+y), Point(13+x, 73+y))
sail1.setFill(colour)
sail1.draw(win)
body2 = Polygon(Point(1+x, 63), Point(5+x, 68),
Point(20+x,68), Point(24+x,63))
body2.draw(win)
line2 = Line(Point(13+x,63), Point(13+x,58))
line2.draw(win)
sail2 = Polygon(Point(1+x,58), Point(24+x, 58), Point(13+x,40))
sail2.setFill(colour)
sail2.draw(win)
body3 = Polygon(Point(1+x,28), Point(5+x,33),
Point(20+x,33), Point(24+x, 28))
body3.draw(win)
line3 = Polygon(Point(13+x,28), Point(13+x,23))
line3.draw(win)
sail3 = Polygon(Point(1+x,23), Point(24+x, 23), Point(13+x, 5))
sail3.setFill(colour)
sail3.draw(win)
main()
So far this creates the top border but nothing else.
I am also aware that the boat function isn't the most efficient way of drawing
When you say that you need to "make the design border the graphics window" I assume you want your boat design to be repeated several times along each edge of the window (that is, the top, bottom, left and right).
This should be doable in two loops. One will draw the top and bottom edges, the other two will draw the left and right edges. I'm not too sure how your drawing code works, so I'm guessing at some offsets here:
top = 0
bottom = (height-1) * 100
for x in range(0, width*100, 25):
boat(x, top, colour)
boat(x, bottom, colour)
left = 0
right = width * 100 - 25
for y in range(100, (height-1)*100, 100):
boat(left, y, colour)
boat(right, y, colour)
This should call your boat subroutine every 25 pixels across the top and bottom, and every 100 pixels along the left and right edges. Adjust the top, bottom, left and right values and the parameters in the range calls in the loops to make the spacing suit your needs (I just made it up). This code avoids drawing the corner items twice, though depending on how the drawing routine works that might not be necessary.

Categories