File: flood-fill.py

package info (click to toggle)
golly 3.2-2
  • links: PTS
  • area: main
  • in suites: buster
  • size: 19,516 kB
  • sloc: cpp: 69,819; ansic: 25,894; python: 7,921; sh: 4,267; objc: 3,721; java: 2,781; xml: 1,362; makefile: 530; perl: 69
file content (129 lines) | stat: -rw-r--r-- 4,723 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# Fill clicked region with current drawing state.
# Author: Andrew Trevorrow (andrew@trevorrow.com), Jan 2011.

import golly as g
from time import time

# avoid an unbounded fill
if g.empty():
    if g.getwidth() == 0 or g.getheight() == 0:
        g.exit("You cannot fill an empty universe that is unbounded!")
else:
    # set fill limits to the pattern's bounding box
    # (these will be extended below if the grid is bounded)
    r = g.getrect()
    minx = r[0]
    miny = r[1]
    maxx = minx + r[2] - 1
    maxy = miny + r[3] - 1

# allow filling to extend to the edges of bounded grid
if g.getwidth() > 0:
    minx = -int(g.getwidth()/2)
    maxx = minx + g.getwidth() - 1
if g.getheight() > 0:
    miny = -int(g.getheight()/2)
    maxy = miny + g.getheight() - 1

# ------------------------------------------------------------------------------

def checkneighbor(x, y, oldstate):
    # first check if x,y is outside fill limits
    if x < minx or x > maxx: return False
    if y < miny or y > maxy: return False
    return g.getcell(x, y) == oldstate

# ------------------------------------------------------------------------------

def floodfill():
    newstate = g.getoption("drawingstate")
    oldstate = newstate

    # wait for user to click a cell
    g.show("Click the region you wish to fill... (hit escape to abort)")
    while oldstate == newstate:
        event = g.getevent()
        if event.startswith("click"):
            # event is a string like "click 10 20 left none"
            evt, xstr, ystr, butt, mods = event.split()
            x = int(xstr)
            y = int(ystr)
            if x < minx or x > maxx or y < miny or y > maxy:
                # click is outside pattern's bounding box in unbounded universe
                g.warn("Click within the pattern's bounding box\n"+
                       "otherwise the fill will be unbounded.")
            else:
                # note that user might have changed drawing state
                newstate = g.getoption("drawingstate")
                oldstate = g.getcell(x, y)
                if oldstate == newstate:
                    g.warn("The clicked cell must have a different state\n"+
                           "to the current drawing state.")
        else:
            g.doevent(event)

    # tell Golly to handle all further keyboard/mouse events
    g.getevent(False)

    # do flood fill starting with clicked cell
    g.show("Filling clicked region... (hit escape to stop)")
    clist = [ (x,y) ]
    g.setcell(x, y, newstate)
    oldsecs = time()
    while len(clist) > 0:
        # remove cell from start of clist
        x, y = clist.pop(0)
        newsecs = time()
        if newsecs - oldsecs >= 0.5:     # show changed pattern every half second
            oldsecs = newsecs
            g.update()

        # check if any orthogonal neighboring cells are in oldstate
        if checkneighbor(  x, y-1, oldstate):
            g.setcell(     x, y-1, newstate)
            clist.append( (x, y-1) )
        
        if checkneighbor(  x, y+1, oldstate):
            g.setcell(     x, y+1, newstate)
            clist.append( (x, y+1) )
        
        if checkneighbor(  x+1, y, oldstate):
            g.setcell(     x+1, y, newstate)
            clist.append( (x+1, y) )
        
        if checkneighbor(  x-1, y, oldstate):
            g.setcell(     x-1, y, newstate)
            clist.append( (x-1, y) )

        # diagonal neighbors are more complicated because we don't
        # want to cross a diagonal line of live cells
        if checkneighbor(  x+1, y+1, oldstate) and (g.getcell(x, y+1) == 0 or
                                                   g.getcell(x+1, y) == 0):
            g.setcell(     x+1, y+1, newstate)
            clist.append( (x+1, y+1) )
        
        if checkneighbor(  x+1, y-1, oldstate) and (g.getcell(x, y-1) == 0 or
                                                   g.getcell(x+1, y) == 0):
            g.setcell(     x+1, y-1, newstate)
            clist.append( (x+1, y-1) )
        
        if checkneighbor(  x-1, y+1, oldstate) and (g.getcell(x, y+1) == 0 or
                                                   g.getcell(x-1, y) == 0):
            g.setcell(     x-1, y+1, newstate)
            clist.append( (x-1, y+1) )
        
        if checkneighbor(  x-1, y-1, oldstate) and (g.getcell(x, y-1) == 0 or
                                                   g.getcell(x-1, y) == 0):
            g.setcell(     x-1, y-1, newstate)
            clist.append( (x-1, y-1) )

# ------------------------------------------------------------------------------

oldcursor = g.getcursor()
g.setcursor("Draw")

try:
    floodfill()
finally:
    g.setcursor(oldcursor)
    g.show(" ")