arcade
arcade copied to clipboard
Grids of SpriteSolidColor sometimes produce missing pixels at specific zoom levels
Bug Report
Originally found by @alejcas.
A grid of SpriteSolidColors can produce missing pixels on some machines on some irrational zoom levels. These missing pixels appear as very small squares, but also as full lines depending on the zoom level. See the video below:
In this video, there are 25x25 squares (50 squares total) which all have a width and height of 32.
https://github.com/pythonarcade/arcade/assets/2590700/3ea0e1f0-21c5-4c3a-86db-583767d88c88
These missing pixels apparently only appear between the squares. In this example, the grid lines are set to same spacing as the squares.
https://github.com/pythonarcade/arcade/assets/2590700/2c23fe47-9af6-45a3-8e32-fed2390842c9
System Info
This apparently is machine dependent.
Happens on this Intel Mac:
Arcade 3.0.0.dev29
------------------
vendor: Intel Inc.
renderer: Intel Iris Pro OpenGL Engine
version: (4, 1)
python: 3.11.5 (main, Aug 24 2023, 15:23:14) [Clang 13.0.0 (clang-1300.0.29.30)]
platform: darwin
pyglet version: 2.1.dev2
PIL version: 10.2.0
Does not happen on this Windows machine:
Arcade 3.0.0.dev29
------------------
vendor: NVIDIA Corporation
renderer: NVIDIA GeForce GTX 1080/PCIe/SSE2
version: (3, 3)
python: 3.11.8 (tags/v3.11.8:db85d51, Feb 6 2024, 22:03:32) [MSC v.1937 64 bit (AMD64)]
platform: win32
pyglet version: 2.1.dev2
PIL version: 10.2.0
Steps to reproduce/example code:
The below program produces the missing pixels, though, it appears to machine dependent. You can use the minus and equal key to subtract and add 0.01 zoom. You can use comma and period to increase or decrease the width and height of the squares and grid. Use 'g' to turn on or off the grid lines.
from typing import Optional
import arcade
from arcade.shape_list import ShapeElementList, create_line
from pyglet.math import Vec2
from map_data import MAP
GRID_COLOR = arcade.color.Color(255, 255, 255)
class MyWindow(arcade.Window):
def __init__(self):
super().__init__(width=1280, height=768)
# sprite list to hold the map sprites
self.map: arcade.SpriteList = arcade.SpriteList(use_spatial_hash=True)
self.zoom = 0.96
self.draw_grids = False
self.nx = 25
self.ny = 25
self.tile_size = 50
self.create_squares(self.nx, self.ny, self.tile_size)
self.create_grid(self.nx, self.ny, self.tile_size)
camera_viewport = (0, 0, self.width, self.height)
self.camera = arcade.camera.Camera2D(viewport=camera_viewport, zoom=self.zoom)
def create_squares(self, nx, ny, tile_size):
print("Recreating squares:", nx, ny, tile_size)
red = arcade.color.Color(255, 0, 0)
self.map = arcade.SpriteList(use_spatial_hash=True)
for x in range(0, nx):
for y in range(0, ny):
tile = arcade.sprite.SpriteSolidColor(width=tile_size, height=tile_size,
color=red)
tile.left = x * tile_size
tile.bottom = y * tile_size
self.map.append(tile)
def create_grid(self, nx, ny, tile_size):
print("Creating new grid:", nx, ny, tile_size)
self.grid_lines = ShapeElementList()
for x_pixels in range(0, (nx + 1) * tile_size, tile_size):
shape = create_line(x_pixels, -10000, x_pixels, 10000, GRID_COLOR)
self.grid_lines.append(shape)
for y_pixels in range(0, (ny + 1) * tile_size, tile_size):
shape = create_line(-10000, y_pixels, 10000, y_pixels, GRID_COLOR)
self.grid_lines.append(shape)
def on_draw(self):
self.camera.use()
self.clear(arcade.color.BLUE)
self.map.draw(pixelated=True)
if self.draw_grids:
self.grid_lines.draw()
def on_key_press(self, key, modifier):
if key == arcade.key.MINUS:
self.camera.zoom -= 0.01
print("Zoom:", self.camera.zoom)
elif key == arcade.key.EQUAL:
self.camera.zoom += 0.01
print("Zoom:", self.camera.zoom)
elif key == arcade.key.KEY_1:
self.camera.zoom = 1.0
print("Zoom:", self.camera.zoom)
elif key == arcade.key.G:
self.draw_grids = not self.draw_grids
elif key == arcade.key.COMMA:
self.tile_size -= 1
self.create_squares(self.nx, self.ny, self.tile_size)
self.create_grid(self.nx, self.ny, self.tile_size)
elif key == arcade.key.PERIOD:
self.tile_size += 1
self.create_squares(self.nx, self.ny, self.tile_size)
self.create_grid(self.nx, self.ny, self.tile_size)
def on_mouse_press(self, x: int, y: int, button: int, modifiers: int):
print(x, y)
if __name__ == '__main__':
window = MyWindow()
window.run()