Say I have a tile based system using 16x16 pixels. How would you find out what tiles are covered by a rectangle defined by floating point pixel units?
for eg,
rect(x=16.0,y=16.0, w=1.0, h=1.0) -> tile(x=1, y=1, w=1, h=1)
rect(x=16.0,y=16.0, w=16.0, h=16.0) -> tile(x=1, y=1, w=1, h=1) (still within same tile)
rect(x=24.0,y=24.0, w=8.0, y=8.0) -> (x=1,y=1,w=1,h=1) (still within same tile)
rect(x=24.0,y=24.0, w=8.1, y=8.1) -> (x=1,y=1,w=2,h=2)
The only way I can do this reliably is by using a loop. Is there a better way? Dividing by 16 gives me the wrong answer on edge cases. Here's some example code I use in python:
#!/usr/bin/env python
import math
TILE_W = 16
TILE_H = 16
def get_tile(x,y,w,h):
t_x = int(x / TILE_W)
t_x2 = t_x
while t_x2*TILE_W < (x+w):
t_x2 += 1
t_w = t_x2-t_x
t_y = int( y / TILE_H)
t_y2 = t_y
while t_y2*TILE_H < (y+h):
t_y2 += 1
t_h = t_y2-t_y
return t_x,t_y,t_w,t_h
(x,y) = 16.0,16.0
(w,h) = 1.0, 1.0
assert get_tile(x,y,w,h) == (1,1,1,1)
(x,y) = 16.0,16.0
(w,h) = 15.0, 15.0
assert get_tile(x,y,w,h) == (1,1,1,1)
(x,y) = 16.0,16.0
(w,h) = 16.0, 16.0
assert get_tile(x,y,w,h) == (1,1,1,1)
(x,y) = 16.0,16.0
(w,h) = 16.1, 16.1
assert get_tile(x,y,w,h) == (1,1,2,2)
(x,y) = 24.0, 24.0
(w,h) = 1.0, 1.0
assert get_tile(x,y,w,h) == (1,1,1,1)
(x,y) = 24.0, 24.0
(w,h) = 8.0, 8.0
assert get_tile(x,y,w,h) == (1,1,1,1)
(x,y) = 24.0, 24.0
(w,h) = 8.1, 8.1
assert get_tile(x,y,w,h) == (1,1,2,2)
(x,y) = 24.0, 24.0
(w,h) = 9.0, 9.0
assert get_tile(x,y,w,h) == (1,1,2,2)
Matt's solution with bug-fixes:
from __future__ import division
import math
TILE_W = TILE_H = 16
def get_tile(x,y,w,h):
x1 = int(math.floor(x/TILE_W))
x2 = int(math.ceil((x + w)/TILE_W))
y1 = int(math.floor(y/TILE_H))
y2 = int(math.ceil((y + h)/TILE_H))
return x1, y1, x2-x1, y2-y1
here is the one which passes your test cases, tell me if there is any edge case
TILE_W = TILE_H = 16
from math import floor, ceil
def get_tile2(x,y,w,h):
x1 = int(x/TILE_W)
y1 = int(y/TILE_H)
x2 = int((x+w)/TILE_W)
y2 = int((y+h)/TILE_H)
if (x+w)%16 == 0: #edge case
x2-=1
if (y+h)%16 == 0: #edge case
y2-=1
tw = x2-x1 + 1
th = y2-y1 + 1
return x1, y1, tw, th
(x,y) = 16.0, 16.0
(w,h) = 1.0, 1.0
assert get_tile2(x,y,w,h) == (1,1,1,1)
(x,y) = 16.0, 16.0
(w,h) = 15.0, 15.0
assert get_tile2(x,y,w,h) == (1,1,1,1)
(x,y) = 16.0, 16.0
(w,h) = 16.0, 16.0
assert get_tile2(x,y,w,h) == (1,1,1,1)
(x,y) = 16.0, 16.0
(w,h) = 16.1, 16.1
assert get_tile2(x,y,w,h) == (1,1,2,2)
I am now explicitly checking for the edge case, but be aware that floating point comparison sometime may not seem obvious and result may not be as expected.
You could try aligning you pixel coordinates ot integers before dividing by tile width:
xlower = int(floor(x))
xupper = int(ceil(x + w))
This could probably be condensed a bit more, but here you go.
def get_tile(x,y,w,h):
x1 = int(x / TILE_W)
x2 = (x + w) / TILE_W
y1 = int(y / TILE_H)
y2 = (x + w) / TILE_H
if int(x2) == x2:
x2 = int(x2 - 1)
else:
x2 = int(x2)
if int(y2) == y2:
y2 = int(y2 - 1)
else:
y2 = int(y2)
tw = x2 - x1 + 1
th = y2 - y1 + 1
return x1, y1, tw, th
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.