001/*
002 * Copyright 2006 - 2013
003 *     Stefan Balev     <stefan.balev@graphstream-project.org>
004 *     Julien Baudry    <julien.baudry@graphstream-project.org>
005 *     Antoine Dutot    <antoine.dutot@graphstream-project.org>
006 *     Yoann Pigné      <yoann.pigne@graphstream-project.org>
007 *     Guilhelm Savin   <guilhelm.savin@graphstream-project.org>
008 * 
009 * This file is part of GraphStream <http://graphstream-project.org>.
010 * 
011 * GraphStream is a library whose purpose is to handle static or dynamic
012 * graph, create them from scratch, file or any source and display them.
013 * 
014 * This program is free software distributed under the terms of two licenses, the
015 * CeCILL-C license that fits European law, and the GNU Lesser General Public
016 * License. You can  use, modify and/ or redistribute the software under the terms
017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by
019 * the Free Software Foundation, either version 3 of the License, or (at your
020 * option) any later version.
021 * 
022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
024 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
025 * 
026 * You should have received a copy of the GNU Lesser General Public License
027 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
028 * 
029 * The fact that you are presently reading this means that you have had
030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms.
031 */
032package org.graphstream.algorithm.generator;
033
034import java.awt.image.BufferedImage;
035import java.io.File;
036import java.io.IOException;
037import java.io.InputStream;
038import java.util.Arrays;
039
040import javax.imageio.ImageIO;
041
042public class LifeGenerator extends BaseGenerator {
043
044        private static final int[] NEIGHT = { -1, -1, 0, -1, 1, -1, -1, 0, 1, 0,
045                        -1, 1, 0, 1, 1, 1 };
046        private static final int[] LINK_WITH = { -1, -1, 0, -1, 1, -1, -1, 0 };
047
048        int width, height;
049        boolean[] cells;
050        boolean[] swap;
051        boolean tore;
052        boolean pushCoords;
053        double step;
054
055        public LifeGenerator(int width, int height, boolean[] data) {
056                this.width = width;
057                this.height = height;
058                this.cells = Arrays.copyOf(data, data.length);
059                this.swap = new boolean[width * height];
060        }
061
062        public LifeGenerator(String path) throws IOException {
063                File in = new File(path);
064                BufferedImage data = ImageIO.read(in);
065
066                loadData(data);
067
068                pushCoords = true;
069                tore = true;
070        }
071        
072        public LifeGenerator(InputStream in) throws IOException {
073                BufferedImage data = ImageIO.read(in);
074
075                loadData(data);
076
077                pushCoords = true;
078                tore = true;
079        }
080
081        public LifeGenerator(BufferedImage cellsData) {
082                loadData(cellsData);
083
084                pushCoords = true;
085                tore = true;
086        }
087
088        protected void loadData(BufferedImage data) {
089                int rgb;
090
091                width = data.getWidth();
092                height = data.getHeight();
093                cells = new boolean[width * height];
094                swap = new boolean[width * height];
095
096                for (int x = 0; x < width; x++) {
097                        for (int y = 0; y < height; y++) {
098                                rgb = data.getRGB(x, y);
099                                cells[x * height + y] = ((rgb & (0xFF << 16)) != 0x00000000);
100                        }
101                }
102        }
103
104        protected void computeNextState() {
105                int nx, ny, c, idx;
106                boolean alive;
107
108                for (int x = 0; x < width; x++)
109                        for (int y = 0; y < height; y++) {
110                                c = 0;
111                                idx = x * height + y;
112                                alive = cells[idx];
113
114                                for (int i = 0; i < NEIGHT.length; i += 2) {
115                                        if (!tore
116                                                        && (x + NEIGHT[i] < 0 || x + NEIGHT[i] >= width
117                                                                        || y + NEIGHT[i + 1] < 0 || y
118                                                                        + NEIGHT[i + 1] >= height))
119                                                continue;
120
121                                        nx = (x + NEIGHT[i] + width) % width;
122                                        ny = (y + NEIGHT[i + 1] + height) % height;
123
124                                        if (cells[nx * height + ny])
125                                                c++;
126                                }
127
128                                swap[idx] = false;
129
130                                if (!alive && c == 3)
131                                        swap[idx] = true;
132                                else if (alive && c < 2)
133                                        swap[idx] = false;
134                                else if (alive && c < 4)
135                                        swap[idx] = true;
136                                else if (alive && c > 3)
137                                        swap[idx] = false;
138                        }
139        }
140
141        protected void addNode(int x, int y) {
142                String id = nodeId(x, y);
143                addNode(id);
144
145                if (pushCoords)
146                        sendNodeAttributeAdded(sourceId, id, "xyz", new float[] { x, y, 0 });
147        }
148
149        protected void delNode(int x, int y) {
150                delNode(nodeId(x, y));
151        }
152
153        protected void addEdge(int x1, int y1, int x2, int y2) {
154                addEdge(edgeId(x1, y1, x2, y2), nodeId(x1, y1), nodeId(x2, y2));
155        }
156
157        protected void delEdge(int x1, int y1, int x2, int y2) {
158                delEdge(edgeId(x1, y1, x2, y2));
159        }
160
161        protected String nodeId(int x, int y) {
162                return String.format("%d_%d", x, y);
163        }
164
165        protected String edgeId(int x1, int y1, int x2, int y2) {
166                return String.format("%d_%d__%d_%d", x1, y1, x2, y2);
167        }
168
169        public void begin() {
170                step = 0;
171
172                for (int x = 0; x < width; x++)
173                        for (int y = 0; y < height; y++) {
174                                if (cells[x * height + y])
175                                        addNode(x, y);
176                        }
177
178                boolean alive, nalive;
179                int nx, ny;
180
181                for (int x = 0; x < width; x++)
182                        for (int y = 0; y < height; y++) {
183                                alive = cells[x * height + y];
184
185                                for (int i = 0; i < LINK_WITH.length; i += 2) {
186
187                                        if (!tore
188                                                        && (x + LINK_WITH[i] < 0
189                                                                        || x + LINK_WITH[i] >= width
190                                                                        || y + LINK_WITH[i + 1] < 0 || y
191                                                                        + LINK_WITH[i + 1] >= height))
192                                                continue;
193
194                                        nx = (x + LINK_WITH[i] + width) % width;
195                                        ny = (y + LINK_WITH[i + 1] + height) % height;
196
197                                        nalive = cells[nx * height + ny];
198
199                                        if (alive && nalive)
200                                                addEdge(x, y, nx, ny);
201                                }
202                        }
203        }
204
205        public boolean nextEvents() {
206                int idx;
207
208                computeNextState();
209                sendStepBegins(sourceId, step++);
210
211                boolean alive, alived, nalive, nalived;
212                int nx, ny;
213
214                for (int x = 0; x < width; x++)
215                        for (int y = 0; y < height; y++) {
216                                idx = x * height + y;
217
218                                //
219                                // Node added
220                                //
221                                if (!cells[idx] && swap[idx])
222                                        addNode(x, y);
223                        }
224
225                for (int x = 0; x < width; x++)
226                        for (int y = 0; y < height; y++) {
227                                alive = swap[x * height + y];
228                                alived = cells[x * height + y];
229
230                                for (int i = 0; i < LINK_WITH.length; i += 2) {
231
232                                        if (!tore
233                                                        && (x + LINK_WITH[i] < 0
234                                                                        || x + LINK_WITH[i] >= width
235                                                                        || y + LINK_WITH[i + 1] < 0 || y
236                                                                        + LINK_WITH[i + 1] >= height))
237                                                continue;
238
239                                        nx = (x + LINK_WITH[i] + width) % width;
240                                        ny = (y + LINK_WITH[i + 1] + height) % height;
241
242                                        nalive = swap[nx * height + ny];
243                                        nalived = cells[nx * height + ny];
244
245                                        //
246                                        // Edge removed
247                                        //
248                                        if (alived && nalived
249                                                        && ((alive && !nalive) || (!alive && nalive)))
250                                                delEdge(x, y, nx, ny);
251                                        //
252                                        // Edge added
253                                        //
254                                        else if (((!alived && nalived) || (alived && !nalived) || (!alived && !nalived))
255                                                        && alive && nalive)
256                                                addEdge(x, y, nx, ny);
257                                }
258                        }
259
260                for (int x = 0; x < width; x++)
261                        for (int y = 0; y < height; y++) {
262                                idx = x * height + y;
263
264                                //
265                                // Node removed
266                                //
267                                if (cells[idx] && !swap[idx])
268                                        delNode(x, y);
269                        }
270
271                //
272                // Swap
273                //
274                boolean[] tmp = cells;
275                cells = swap;
276                swap = tmp;
277
278                return true;
279        }
280
281        public void setTore(boolean on) {
282                tore = on;
283        }
284
285        public boolean isTore() {
286                return tore;
287        }
288
289        public void setPushCoords(boolean on) {
290                pushCoords = on;
291        }
292
293        public boolean isCoordsPushed() {
294                return pushCoords;
295        }
296        
297        public int getWidth() {
298                return width;
299        }
300        
301        public int getHeight() {
302                return height;
303        }
304}