1 ! Copyright (C) 2018 John Benediktsson
2 ! See http://factorcode.org/license.txt for BSD license
4 USING: accessors arrays assocs bit-arrays byte-arrays calendar
5 colors combinators kernel kernel.private math
6 math.order ranges namespaces opengl random sequences
7 sequences.private timers ui ui.commands ui.gadgets
8 ui.gadgets.toolbar ui.gadgets.tracks ui.gestures ui.render words ;
12 : make-grid ( rows cols -- grid )
13 '[ _ <bit-array> ] replicate ;
15 : grid-dim ( grid -- rows cols )
16 [ length ] [ first length ] bi ;
18 : random-grid! ( grid -- )
20 [ length>> ] [ underlying>> length random-bytes ] bi
24 :: count-neighbors ( grid -- counts )
25 grid grid-dim { fixnum fixnum } declare :> ( rows cols )
26 rows 1 - { fixnum } declare :> max-rows
27 cols 1 - { fixnum } declare :> max-cols
28 rows [ cols <byte-array> ] replicate :> neighbors
29 grid { array } declare [| row j |
30 j 0 eq? [ max-rows ] [ j 1 - ] if
32 j max-rows eq? [ 0 ] [ j 1 + ] if
33 [ neighbors nth-unsafe { byte-array } declare ] tri@ :>
36 row { bit-array } declare [| cell i |
38 i 0 eq? [ max-cols ] [ i 1 - ] if
40 i max-cols eq? [ 0 ] [ i 1 + ] if
42 [ [ above [ 1 + ] change-nth-unsafe ] tri@ ]
43 [ nip [ same [ 1 + ] change-nth-unsafe ] bi@ ]
44 [ [ below [ 1 + ] change-nth-unsafe ] tri@ ]
48 ] each-index neighbors ;
50 :: next-step ( grid -- )
51 grid count-neighbors { array } declare :> neighbors
52 grid { array } declare [| row j |
53 j neighbors nth-unsafe { byte-array } declare :> neighbor-row
54 row { bit-array } declare [| cell i |
55 i neighbor-row nth-unsafe
57 2 3 between? i row set-nth-unsafe
59 3 = [ t i row set-nth-unsafe ] when
64 TUPLE: grid-gadget < gadget grid size timer ;
66 : <grid-gadget> ( grid -- gadget )
70 dup '[ _ [ grid>> next-step ] [ relayout-1 ] bi ]
71 f 1/5 seconds <timer> >>timer ;
73 M: grid-gadget ungraft*
74 [ timer>> stop-timer ] [ call-next-method ] bi ;
76 M: grid-gadget pref-dim*
77 [ grid>> grid-dim swap ] [ size>> '[ _ * ] bi@ 1 + 2array ] bi ;
79 :: update-grid ( gadget -- )
80 gadget dim>> first2 :> ( w h )
82 h w [ size /i ] bi@ :> ( new-rows new-cols )
84 grid grid-dim :> ( rows cols )
86 cols new-cols = not or [
87 new-rows new-cols make-grid :> new-grid
88 rows new-rows min <iota> [| j |
89 cols new-cols min <iota> [| i |
91 i j new-grid nth set-nth
94 new-grid gadget grid<<
97 :: draw-cells ( gadget -- )
100 gadget grid>> { array } declare [| row j |
101 row { bit-array } declare [| cell i |
103 i j [ size * ] bi@ 2array
104 { size size } gl-fill-rect
109 :: draw-lines ( gadget -- )
110 gadget size>> :> size
111 gadget grid>> grid-dim :> ( rows cols )
113 cols rows [ size * ] bi@ :> ( w h )
116 { 0 y } { w y } gl-line
119 { x 0 } { x h } gl-line
123 M: grid-gadget draw-gadget*
124 [ update-grid ] [ draw-cells ] [ draw-lines ] tri ;
128 :: on-click ( gadget -- )
129 gadget size>> :> size
130 gadget grid>> grid-dim :> ( rows cols )
131 gadget hand-rel first2 [ size /i ] bi@ :> ( i j )
132 i 0 cols 1 - between?
133 j 0 rows 1 - between? and [
134 i j gadget grid>> nth
135 [ not dup last-click set ] change-nth
136 ] when gadget relayout-1 ;
138 :: on-drag ( gadget -- )
139 gadget size>> :> size
140 gadget grid>> grid-dim :> ( rows cols )
141 gadget hand-rel first2 [ size /i ] bi@ :> ( i j )
142 i 0 cols 1 - between?
143 j 0 rows 1 - between? and [
144 last-click get i j gadget grid>> nth set-nth
145 ] when gadget relayout-1 ;
147 : on-scroll ( gadget -- )
149 scroll-direction get second {
150 { [ dup 0 > ] [ -2 ] }
151 { [ dup 0 < ] [ 2 ] }
153 } cond nip + 4 30 clamp
154 ] change-size relayout-1 ;
156 :: com-play ( gadget -- )
157 gadget timer>> restart-timer ;
159 :: com-step ( gadget -- )
160 gadget grid>> next-step
163 :: com-stop ( gadget -- )
164 gadget timer>> stop-timer ;
166 :: com-clear ( gadget -- )
167 gadget grid>> [ clear-bits ] each
170 :: com-random ( gadget -- )
171 gadget grid>> random-grid! gadget relayout-1 ;
173 :: com-glider ( gadget -- )
174 gadget grid>> :> grid
175 { { 2 1 } { 3 2 } { 1 3 } { 2 3 } { 3 3 } }
176 [ first2 grid nth t -rot set-nth ] each
179 grid-gadget "toolbar" f {
180 { T{ key-down { sym "1" } } com-play }
181 { T{ key-down { sym "2" } } com-stop }
182 { T{ key-down { sym "3" } } com-clear }
183 { T{ key-down { sym "4" } } com-random }
184 { T{ key-down { sym "5" } } com-glider }
185 { T{ key-down { sym "6" } } com-step }
188 grid-gadget "gestures" [
190 { T{ key-down f { A+ } "F" } [ toggle-fullscreen ] }
191 { T{ button-down { # 1 } } [ on-click ] }
192 { T{ drag { # 1 } } [ on-drag ] }
193 { mouse-scroll [ on-scroll ] }
197 TUPLE: life-gadget < track ;
199 : <life-gadget> ( -- gadget )
200 vertical life-gadget new-track
201 20 20 make-grid <grid-gadget>
202 [ <toolbar> format-toolbar f track-add ]
205 M: life-gadget focusable-child* children>> second ;
207 MAIN-WINDOW: life-window {
208 { title "Game of Life" }
209 } <life-gadget> >>gadgets ;