So I implemented my own; here's the python prototype:
class Pt:
def __init__(self,x,y):
self.x = x
self.y = y
def __repr__(self):
return "(%s,%s)"%(self.x,self.y)
class TrapeziumIterator:
def __init__(self,map,width,height,a,b,c,d):
# create an iterator over the cells in the map that are in the trapezium described by a,b,c,d corners
# a,b,c,d describes the CLOCKWISE perimeter of the trapezium, e.g. tl,tr,br,bl from screenspace
self.map = map
self.width = width
self.height = height
# we re-orientate the trapezium so the lowest y is topmost
t = [a,b,c,d]
start_y = min(pt.y for pt in t)
while t[0].y != start_y:
t.append(t[0])
del t[0]
self.t = t
# work out starting position
self.y = int(max(0,start_y)) # init y before x!
self.x = self._start_x(self.y)-1 # x is iterated in the first call to next()
# work out stopping positions
self.stop_x = self._stop_x(self.y)
self.stop_y = int(min(height,max(pt.y for pt in t)+1))
def next(self):
"return True if there is a square to be visited; then the x and y variables member are set; else return False"
self.x += 1
while self.x >= self.stop_x:
self.y += 1
if self.y >= self.stop_y:
return False # reached the end
self.x = self._start_x(self.y)
self.stop_x = self._stop_x(self.y)
assert self.x >= 0 and self.x < self.width, self.x
assert self.y >= 0 and self.y < self.height, self.y
return True # you can read the x and y member variables
def _start_x(self,y):
# go down the left side
return max(0,self._side(y,0,3))
def _stop_x(self,y):
# go down the right side
return min(self.width,self._side(y,3,0)+1)
def _side(self,y,start,stop):
inc = -1 if start < stop else 1
while self.t[stop].y < y:
start = stop
stop += inc
f = (y - self.t[start].y)
f /= (self.t[stop].y - self.t[start].y)
return int(self.t[start].x + (self.t[stop].x - self.t[start].x) * f)
def test(image,col,a,b,c,d):
w,h = image.size
it = TrapeziumIterator(image,w,h,a,b,c,d)
while it.next():
image.putpixel((it.x,it.y),col)
# test it
import Image
w,h = 100,100
image = Image.new("RGB",(w,h),(255,255,255))
test(image,(0,255,0),Pt(-60.3,-10.4),Pt(20.2,25.3),Pt(70,90.2),Pt(60,110))
test(image,(255,0,0),Pt(30.3,40.4),Pt(60.2,25.3),Pt(70,50.2),Pt(33,47.7))
# debug show it a bit larger
image = image.resize((w*6,h*6))
image.show()
The quad is described by its four corners in clockwise order. The algorithm finds the top-most corner, and then walks down the left and right sides.