Mercurial > traipse_dev
comparison orpg/mapper/region.py @ 0:4385a7d0efd1 grumpy-goblin
Deleted and repushed it with the 'grumpy-goblin' branch. I forgot a y
author | sirebral |
---|---|
date | Tue, 14 Jul 2009 16:41:58 -0500 |
parents | |
children | 072ffc1d466f |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4385a7d0efd1 |
---|---|
1 # Copyright (C) 2000-2001 The OpenRPG Project | |
2 # | |
3 # openrpg-dev@lists.sourceforge.net | |
4 # | |
5 # This program is free software; you can redistribute it and/or modify | |
6 # it under the terms of the GNU General Public License as published by | |
7 # the Free Software Foundation; either version 2 of the License, or | |
8 # (at your option) any later version. | |
9 # | |
10 # This program is distributed in the hope that it will be useful, | |
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 # GNU General Public License for more details. | |
14 # | |
15 # You should have received a copy of the GNU General Public License | |
16 # along with this program; if not, write to the Free Software | |
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
18 # -- | |
19 # | |
20 # File: mapper/region.py | |
21 # Author: Mark Tarrabain | |
22 # Maintainer: | |
23 # Version: | |
24 # $Id: region.py,v 1.10 2006/11/04 21:24:21 digitalxero Exp $ | |
25 # | |
26 __version__ = "$Id: region.py,v 1.10 2006/11/04 21:24:21 digitalxero Exp $" | |
27 | |
28 import sys | |
29 | |
30 # This class manages the edge of a polygon | |
31 # it is employed by scan_convert to trace each edge of the polygon as the | |
32 # scan converter advances past each Y coordinate | |
33 # A bresenham-type of algorithm is used to avoid expensive computation during | |
34 # use - no floating point arithmetic is needed. | |
35 class Edge: | |
36 def __init__(self,start,end): | |
37 self.startx=start.X | |
38 self.starty=start.Y | |
39 self.endx=end.X | |
40 self.endy=end.Y | |
41 self.dy=self.endy-self.starty | |
42 self.cx=self.startx | |
43 self.bottom=0 | |
44 self.dir = 0 | |
45 if self.endx>self.startx: | |
46 self.dx = int((self.endx-self.startx)/self.dy) | |
47 else: | |
48 self.dx = -int((self.startx-self.endx)/self.dy) | |
49 self.error = 0 | |
50 if self.endx >= self.startx: | |
51 self.inc = (self.endx-self.startx)%self.dy | |
52 else: | |
53 self.inc = (self.startx-self.endx)%self.dy | |
54 | |
55 def advance(self): | |
56 self.cx += self.dx | |
57 self.error += self.inc | |
58 if (self.error>=self.dy): | |
59 if (self.endx<self.startx): | |
60 self.cx -= 1 | |
61 else: | |
62 self.cx += 1 | |
63 self.error -= self.dy | |
64 | |
65 # Utilitarian class for describing a coordinate in 2D space | |
66 class IPoint: | |
67 def __init__(self): | |
68 X=0 | |
69 Y=0 | |
70 | |
71 def __str__(self): | |
72 return "(X:" + str(self.X) + ", Y:" + str(self.Y) + ")" | |
73 | |
74 def pluseq(self,b): | |
75 self.X += b.X | |
76 self.Y += b.Y | |
77 | |
78 def minuseq(self,b): | |
79 self.X -= b.X | |
80 self.Y -= b.Y | |
81 | |
82 def make(self,x,y): | |
83 self.X=x | |
84 self.Y=y | |
85 return self | |
86 | |
87 def equals(self,b): | |
88 if self.X==b.X and self.Y==b.Y: | |
89 return 1 | |
90 return 0 | |
91 | |
92 def less(self,b): | |
93 if self.Y<b.Y or (self.Y==b.Y and self.X<b.X): | |
94 return 1 | |
95 return 0 | |
96 | |
97 def greater(self,b): | |
98 if self.Y>b.Y or (self.Y==b.Y and self.X>b.X): | |
99 return 1 | |
100 return 0 | |
101 | |
102 # generic rectangle class | |
103 class IRect: | |
104 def __init__(self): #initializes to an invalid rectangle | |
105 self.left=999999 | |
106 self.top=999999 | |
107 self.right=-999999 | |
108 self.bottom=-999999 | |
109 | |
110 def __str__(self): | |
111 return "[ left:"+str(self.left)+", top:"+str(self.top)+", right:"+str(self.right)+", bottom:"+str(self.bottom)+" ]" | |
112 | |
113 def ToPoly(self): | |
114 thelist = [] | |
115 thelist.append(IPoint().make(self.left,self.top)) | |
116 thelist.append(IPoint().make(self.right,self.top)) | |
117 thelist.append(IPoint().make(self.right,self.bottom)) | |
118 thelist.append(IPoint().make(self.left,self.bottom)) | |
119 return thelist | |
120 | |
121 def Width(self): | |
122 return self.right-self.left+1 | |
123 | |
124 def Height(self): | |
125 return self.bottom-self.top+1 | |
126 | |
127 def GetX(self): | |
128 return self.left | |
129 | |
130 def GetY(self): | |
131 return self.top | |
132 | |
133 def GetW(self): | |
134 return self.right-self.left+1 | |
135 | |
136 def GetH(self): | |
137 return self.bottom-self.top+1 | |
138 | |
139 def Size(self): | |
140 return IPoint().make(self.right-self.left+1,self.bottom-self.top+1) | |
141 | |
142 def TopLeft(self): | |
143 return IPoint().make(self.left,self.top) | |
144 | |
145 def BottomRight(self): | |
146 return IPoint().make(self.right,self.bottom) | |
147 | |
148 def Bounds(self): | |
149 return IRect().make(0,0,self.right-self.left,self.bottom-self.top) | |
150 | |
151 def Resize(self,nL,nT,nR,nB): | |
152 self.left+=nL | |
153 self.top+=nT | |
154 self.right+=nR | |
155 sel.bottom+=nB | |
156 | |
157 def IsValid(self): | |
158 if (self.left<=self.right and self.top<=self.bottom): | |
159 return 1 | |
160 return 0 | |
161 | |
162 def add(self,pt): | |
163 return IRect().make(self.left+pt.X,self.top+pt.Y,self.right+pt.X,self.bottom+pt.Y) | |
164 | |
165 def subtract(self,pt): | |
166 return IRect().make(self.left+pt.X,self.top+pt.Y,self.right+pt.X,self.bottom+pt.Y) | |
167 | |
168 def intersect(self,rect): | |
169 return IRect().make(max(self.left,rect.left),max(self.top,rect.top),min(self.right,rect.right),min(self.bottom,rect.bottom)) | |
170 | |
171 def union(self,rect): | |
172 return IRect().make(min(self.left,rect.left),min(self.top,rect.top),max(self.right,rect.right),max(self.bottom,rect.bottom)) | |
173 | |
174 def equals(self,rect): | |
175 if (self.top==rect.top and self.bottom==rect.bottom and self.left==rect.left and self.right==rect.right): | |
176 return 1 | |
177 return 0 | |
178 | |
179 def make(self,l,t,r,b): | |
180 self.left=l | |
181 self.right=r | |
182 self.top=t | |
183 self.bottom=b | |
184 return self | |
185 | |
186 # A span is a single, contiguous horizontal line. The routine scan_convert | |
187 # returns a list of these that describe the polygon | |
188 class ISpan: | |
189 def __init__(self,x1,x2,ypos): | |
190 self.left=x1 | |
191 self.right=x2 | |
192 self.y=ypos | |
193 | |
194 def __str__(self): | |
195 return "(" + str(self.left) + " to " + str(self.right) + " at " + str(self.y) + ")" | |
196 | |
197 # clipping rectangle class -- this class is a single node in a linked list | |
198 # of rectangles that describe a region | |
199 | |
200 class ClipRect: | |
201 def __init__(self): | |
202 self.next=None | |
203 self.prev=None | |
204 self.bounds=IRect().make(0,0,0,0) | |
205 | |
206 | |
207 # cliprectlist -- the container class that manages a list of cliprects | |
208 class ClipRectList: | |
209 def __init__(self): | |
210 self.first=None | |
211 self.last=None | |
212 self.count=0 | |
213 | |
214 def __str__(self): | |
215 x="[" | |
216 for y in self.GetList(): | |
217 x+=" "+str(y.bounds)+" " | |
218 x+="]" | |
219 return x | |
220 | |
221 #remove all rectangles from list | |
222 def Clear(self): | |
223 while(self.first): | |
224 rect=self.first | |
225 self.first=self.first.next | |
226 del rect | |
227 self.last=None | |
228 self.count=0 | |
229 | |
230 #add a new clipping rectangle to list | |
231 def AddRect(self,rect): | |
232 rect.prev=None | |
233 rect.next=self.first | |
234 if not (self.first is None): | |
235 self.first.prev=rect | |
236 self.first=rect | |
237 if self.last is None: | |
238 self.last=rect | |
239 self.count += 1 | |
240 | |
241 #removes the passed clipping rectangle from the list | |
242 def RemoveRect(self,rect): | |
243 if not (rect.prev is None): | |
244 rect.prev.next=rect.next | |
245 else: | |
246 self.first=rect.next | |
247 if not (rect.next is None): | |
248 rect.next.prev=rect.prev | |
249 else: | |
250 self.last=rect.prev | |
251 self.count -= 1 | |
252 | |
253 # find the clipping rectangle at the the beginning of the list, remove it, | |
254 # and return it | |
255 def RemoveHead(self): | |
256 if self.count==0: | |
257 return None | |
258 rect=self.first | |
259 self.first=rect.next | |
260 if (self.first is None): | |
261 self.last=None | |
262 self.count -= 1 | |
263 return rect | |
264 | |
265 # stealrects -- appends the list of clipping rectangles in pclist to the current | |
266 # list. removes the entries from pclist | |
267 def StealRects(self,pclist): | |
268 if pclist.first is None: | |
269 return | |
270 if self.first is None: | |
271 self.first=pclist.first | |
272 self.last=pclist.last | |
273 else: | |
274 self.last.next=pclist.first | |
275 pclist.first.prev=self.last | |
276 self.last=pclist.last | |
277 self.count += pclist.count | |
278 pclist.first = None | |
279 pclist.last = None | |
280 pclist.count = 0 | |
281 | |
282 # utilitarian procedure to return all clipping rectangles as a Python list | |
283 def GetList(self): | |
284 result=[] | |
285 f = self.first | |
286 while f: | |
287 result.append(f) | |
288 f = f.next | |
289 return result | |
290 | |
291 # some utility procedures, defined outside the scope of any class to ensure | |
292 # efficiency | |
293 | |
294 def _hSortCmp(rect1,rect2): | |
295 if (rect1.bounds.left<rect2.bounds.left): | |
296 return -1 | |
297 if (rect1.bounds.left>rect2.bounds.left): | |
298 return 1 | |
299 if (rect1.bounds.top<rect2.bounds.top): | |
300 return -1 | |
301 if (rect1.bounds.top>rect2.bounds.top): | |
302 return 1 | |
303 return 0 | |
304 | |
305 def _vSortCmp(rect1,rect2): | |
306 if (rect1.bounds.top<rect2.bounds.top): | |
307 return -1 | |
308 if (rect1.bounds.top>rect2.bounds.top): | |
309 return 1 | |
310 if (rect1.bounds.left<rect2.bounds.left): | |
311 return -1 | |
312 if (rect1.bounds.left>rect2.bounds.left): | |
313 return 1 | |
314 return 0 | |
315 | |
316 # this is the class for which this whole source file is designed! | |
317 # a Region is maintained as an optimal set of non-intersecting rectangles | |
318 # whose union is the 2D area of interest. At the end of each of the public | |
319 # procedures that may alter the region's structure, the set of rectangles is | |
320 # passed through an optimization phase, that joins any rectangles that are found | |
321 # to be adjacent and can be combined into a single, larger rectangle | |
322 | |
323 class IRegion: | |
324 def __init__(self): | |
325 self.crects=ClipRectList() | |
326 self.firstclip=None | |
327 | |
328 def __AllocClipRect(self): | |
329 return ClipRect() | |
330 | |
331 def __FreeClipRect(self,p): | |
332 del p | |
333 | |
334 def Clear(self): | |
335 self.crects.Clear() | |
336 | |
337 def isEmpty(self): | |
338 if self.crects.first: | |
339 return 0 | |
340 return 1 | |
341 | |
342 def Copy(self,dest): | |
343 dest.Clear() | |
344 p=self.crects.first | |
345 while p: | |
346 dest.__AddRect(p.bounds) | |
347 p=p.next | |
348 | |
349 def __AddRect(self,rect): | |
350 cr=self.__AllocClipRect() | |
351 cr.bounds=IRect().make(rect.left,rect.top,rect.right,rect.bottom) | |
352 self.crects.AddRect(cr) | |
353 return cr | |
354 | |
355 # This is the magic procedure that always makes it possible to merge adjacent | |
356 # rectangles later. It is called once for each rectangle to be combined | |
357 # with the current region. Basically, it dices the current region into | |
358 # horizontal bands where any rectangle is found to be beside the one being | |
359 # examined. This tends to leave more rectangles than necessary in the region, | |
360 # but they can be culled during the optimization phase when they are combined | |
361 # with adjacent rectangles. | |
362 | |
363 def __Examine(self,rect): | |
364 newlist = ClipRectList() | |
365 while not (self.crects.first is None): | |
366 p = self.crects.RemoveHead() | |
367 if (p.bounds.right+1==rect.left or p.bounds.left==rect.right+1): | |
368 if (p.bounds.top<rect.top and p.bounds.bottom>rect.bottom): | |
369 cnew=[IRect().make(p.bounds.left,p.bounds.top,p.bounds.right,rect.top-1), | |
370 IRect().make(p.bounds.left,rect.top,p.bounds.right,rect.bottom), | |
371 IRect().make(p.bounds.left,rect.bottom+1,p.bounds.right,p.bounds.bottom)] | |
372 elif (p.bounds.top<rect.top and p.bounds.bottom>rect.top): | |
373 cnew=[IRect().make(p.bounds.left,p.bounds.top,p.bounds.right,rect.top-1), | |
374 IRect().make(p.bounds.left,rect.top,p.bounds.right,p.bounds.bottom)] | |
375 elif (p.bounds.top<rect.bottom and p.bounds.bottom>rect.bottom): | |
376 cnew=[IRect().make(p.bounds.left,p.bounds.top,p.bounds.right,rect.bottom), | |
377 IRect().make(p.bounds.left,rect.bottom+1,p.bounds.right,p.bounds.bottom)] | |
378 else: | |
379 cnew=[IRect().make(p.bounds.left,p.bounds.top,p.bounds.right,p.bounds.bottom)] | |
380 self.__FreeClipRect(p) | |
381 for i in cnew: | |
382 if (i.IsValid()): | |
383 newclip=self.__AllocClipRect() | |
384 newclip.bounds=i | |
385 newlist.AddRect(newclip) | |
386 else: | |
387 newlist.AddRect(p) | |
388 self.crects.StealRects(newlist) | |
389 | |
390 def __IncludeRect(self,rect): | |
391 tmprg=IRegion() | |
392 tmprg.__AddRect(rect) | |
393 p = self.crects.first | |
394 while p: | |
395 tmprg.__ExcludeRect(p.bounds) | |
396 p = p.next | |
397 self.crects.StealRects(tmprg.crects) | |
398 | |
399 def IncludeRect(self,rect): | |
400 tmprg = IRegion() | |
401 tmprg.Clear() | |
402 tmprg.__AddRect(rect) | |
403 self.__Examine(rect) | |
404 p= self.crects.first | |
405 while p: | |
406 tmprg.__Examine(p.bounds) | |
407 p=p.next | |
408 p=tmprg.crects.first | |
409 while p: | |
410 self.__IncludeRect(p.bounds) | |
411 p=p.next | |
412 self.Optimize() | |
413 | |
414 def IncludeRegion(self,regn): | |
415 tmprg = IRegion() | |
416 regn.Copy(tmprg) | |
417 p = self.crects.first | |
418 while p: | |
419 tmprg.__Examine(p.bounds) | |
420 p=p.next | |
421 p = tmprg.crects.first | |
422 while p: | |
423 self.__Examine(p.bounds) | |
424 self.__IncludeRect(p.bounds) | |
425 p = p.next | |
426 self.Optimize() | |
427 | |
428 def __ExcludeRect(self,rect): | |
429 newlist=ClipRectList() | |
430 while not (self.crects.first is None): | |
431 pcclip=self.crects.RemoveHead() | |
432 hide=rect.intersect(pcclip.bounds) | |
433 if not hide.IsValid(): | |
434 newlist.AddRect(pcclip) | |
435 else: | |
436 #make 4 new rectangles to replace the one taken out | |
437 # | |
438 # AAAAAAAAAAAA | |
439 # AAAAAAAAAAAA | |
440 # BBBB CCCC | |
441 # BBBB CCCC | |
442 # DDDDDDDDDDDD | |
443 # DDDDDDDDDDDD | |
444 # the center rectangle is the one to remove, rectangles A,B,C and D | |
445 # get created. If the rectangle is not in the middle, then the | |
446 # corresponding rectangle that should have been on "that side" of | |
447 # the current rectangle will have IsValid==False, so it will be | |
448 # rejected. | |
449 cnew=[IRect().make(pcclip.bounds.left,hide.top,hide.left-1,hide.bottom), | |
450 IRect().make(hide.right+1,hide.top,pcclip.bounds.right,hide.bottom), | |
451 IRect().make(pcclip.bounds.left,hide.bottom+1,pcclip.bounds.right,pcclip.bounds.bottom), | |
452 IRect().make(pcclip.bounds.left,pcclip.bounds.top,pcclip.bounds.right,hide.top-1) ] | |
453 self.__FreeClipRect(pcclip) | |
454 for i in cnew: | |
455 if (i.IsValid()): | |
456 newclip=self.__AllocClipRect() | |
457 newclip.bounds=i | |
458 newlist.AddRect(newclip) | |
459 self.crects.last=None | |
460 self.crects.StealRects(newlist) | |
461 self.Optimize() | |
462 | |
463 def ExcludeRect(self,rect): | |
464 self.__ExcludeRect(rect) | |
465 self.Optimize() | |
466 | |
467 def ExcludeRegion(self,reg): | |
468 pclist = reg.crects.first | |
469 while pclist: | |
470 self.__ExcludeRect(pclist.bounds) | |
471 pclist = pclist.next | |
472 self.Optimize() | |
473 | |
474 def __IntersectRect(self,rect): | |
475 newlist=ClipRectList() | |
476 while not self.crects.first is None: | |
477 pcclip=self.crects.RemoveHead() | |
478 hide=rect.intersect(pcclip.bounds) | |
479 if not hide.IsValid(): | |
480 newlist.AddRect(pcclip) | |
481 else: | |
482 cnew=[IRect().make(pcclip.bounds.left,hide.top,hide.left-1,hide.bottom), | |
483 IRect().make(hide.right+1,hide.top,pcclip.bounds.right,hide.bottom), | |
484 IRect().make(pcclip.bounds.left,hide.bottom+1,pcclip.bounds.right,pcclip.bounds.bottom), | |
485 IRect().make(pcclip.bounds.left,pcclip.bounds.top,pcclip.bounds.right,hide.top-1) ] | |
486 self.__FreeClipRect(pcclip) | |
487 for i in cnew: | |
488 if (i.IsValid()): | |
489 newclip=self.__AllocClipRect() | |
490 newclip.bounds=i | |
491 newlist.AddRect(newclip) | |
492 self.crects.last = None | |
493 self.crects.StealRects(newlist) | |
494 | |
495 def IntersectRect(self,rect): | |
496 self.__IntersectRect(rect) | |
497 self.Optimize() | |
498 | |
499 def IntersectRegion(self,reg): | |
500 clist=ClipRectList() | |
501 pcvclip = reg.crects.first | |
502 while pcvclip: | |
503 pchclip = self.crects.first | |
504 while pchclip: | |
505 crect=pcvclip.bounds.intersect(pchclip.bounds) | |
506 if crect.IsValid(): | |
507 newclip=self.__AllocClipRect() | |
508 newclip.bounds=crect | |
509 clist.AddRect(newclip) | |
510 pchclip = pchclip.next | |
511 pcvclip = pcvclip.next | |
512 self.Clear() | |
513 self.crects.StealRects(clist) | |
514 self.Optimize() | |
515 | |
516 def GetBounds(self): | |
517 bounds=IRect() | |
518 pcclip = self.crects.first | |
519 while pcclip: | |
520 bounds=bounds.union(pcclip.bounds) | |
521 pcclip = pcclip.next | |
522 return bounds | |
523 | |
524 def Optimize(self): | |
525 clist=self.crects.GetList() | |
526 removed=[] | |
527 keepgoing = 1 | |
528 while len(clist)>1 and keepgoing: | |
529 keepgoing=0 | |
530 clist.sort(lambda a,b:_vSortCmp(a,b)) | |
531 keys=range(len(clist)-1) | |
532 keys.reverse() | |
533 for i in keys: | |
534 if (clist[i].bounds.right==clist[i+1].bounds.left-1 and | |
535 clist[i].bounds.top==clist[i+1].bounds.top and | |
536 clist[i].bounds.bottom==clist[i+1].bounds.bottom): | |
537 clist[i].bounds.right=clist[i+1].bounds.right | |
538 x=clist[i+1] | |
539 del clist[i+1] | |
540 self.crects.RemoveRect(x) | |
541 self.__FreeClipRect(x) | |
542 keepgoing=1 | |
543 if len(clist)<=1: | |
544 break | |
545 clist.sort(lambda a,b:_hSortCmp(a,b)) | |
546 keys=range(len(clist)-1) | |
547 keys.reverse() | |
548 for i in keys: | |
549 if (clist[i].bounds.bottom==clist[i+1].bounds.top-1 and | |
550 clist[i].bounds.left==clist[i+1].bounds.left and | |
551 clist[i].bounds.right==clist[i+1].bounds.right): | |
552 clist[i].bounds.bottom=clist[i+1].bounds.bottom | |
553 x=clist[i+1] | |
554 del clist[i+1] | |
555 self.crects.RemoveRect(x) | |
556 self.__FreeClipRect(x) | |
557 keepgoing=1 | |
558 | |
559 def FromPolygon(self,polypt,mode): | |
560 thelist=self.scan_Convert(polypt) | |
561 for i in thelist: | |
562 r=IRect().make(i.left,i.y,i.right,i.y) | |
563 if mode: | |
564 self.__Examine(r) | |
565 self.__IncludeRect(r) | |
566 else: | |
567 self.__ExcludeRect(r) | |
568 self.Optimize() | |
569 | |
570 def GetRectList(self): | |
571 result=[] | |
572 i = self.crects.first | |
573 while i: | |
574 result.append(i.bounds) | |
575 i = i.next | |
576 return result | |
577 | |
578 #Portable implementation to scan-convert a polygon | |
579 #from algoritm described in Section 3.6.3 of Foley, et al | |
580 #returns a (possibly redundant) list of spans that comprise the polygon | |
581 #this routine does not manipulate the current region in any way, but | |
582 #is enclosed in this class to isolate its name from the global namespace | |
583 #invoke via IRegion().scan_Convert(polypt) | |
584 | |
585 def scan_Convert(self,polypt): | |
586 result=[] | |
587 ET = {} # edge table | |
588 i = 0 | |
589 size = len(polypt) | |
590 polylines=[] # list of lines in polygon | |
591 y = -1 | |
592 for i in xrange(size): | |
593 n = (i+1) % size | |
594 if polypt[i].Y < polypt[n].Y: | |
595 e = Edge(polypt[i],polypt[n]) | |
596 e.dir = 1 #moving top to bottom | |
597 polylines.append(e) | |
598 elif polypt[i].Y > polypt[n].Y: | |
599 e = Edge(polypt[n],polypt[i]) | |
600 e.dir = -1 #moving bottom to top | |
601 polylines.append(e) | |
602 elif polypt[i].X != polypt[n].X: # any horizontal lines just get added directly | |
603 sx = min(polypt[i].X,polypt[n].X) | |
604 ex = max(polypt[i].X,polypt[n].X) | |
605 result.append(ISpan(sx,ex,polypt[i].Y)) | |
606 size = len(polylines) | |
607 for i in xrange(size): | |
608 if i == 0 and polylines[i].dir == -1: | |
609 n = size-1 | |
610 elif (polylines[i].dir == -1): | |
611 n = i-1 | |
612 else: | |
613 n = (i+1) % size | |
614 if (polylines[i].dir != polylines[n].dir): #find out if at bottom end | |
615 polylines[i].bottom = 1 | |
616 if i == 0 or polylines[i].starty < y: | |
617 y = polylines[i].starty | |
618 if not ET.has_key(polylines[i].starty): | |
619 ET[polylines[i].starty] = [] | |
620 ET[polylines[i].starty].append(polylines[i]) #add to edge table, indexed by smaller y coordinate | |
621 AET = [] #active edge table | |
622 while len(AET) > 0 or len(ET) > 0: | |
623 if ET.has_key(y): #if current y value has entries in edge tabe | |
624 AET.extend(ET[y]) # add them to active edge table | |
625 del ET[y] #and delete the entries in the edge table | |
626 i = len(AET)-1 | |
627 vals = [] | |
628 while i >= 0: | |
629 if (AET[i].endy == y): #if at the end of this edge's run | |
630 if (AET[i].bottom): #check and see if we need one more point | |
631 vals.append(AET[i].cx) #we do, add it | |
632 del AET[i] # y=end of edge, so remove it | |
633 i -= 1 | |
634 i = 0 | |
635 while i < len(AET): # for every edge in AET | |
636 vals.append(AET[i].cx) # add x intercept | |
637 AET[i].advance() #and advance the edge one down | |
638 i += 1 | |
639 vals.sort() | |
640 i = 0 | |
641 while i < len(vals)-1: #split vals[] array into pairs and output them | |
642 result.append(ISpan(vals[i],vals[i+1],y)) | |
643 i += 2 | |
644 y += 1 | |
645 return result |