View Javadoc
1   /*
2    * Copyright (c) 2009, The JUNG Authors 
3    *
4    * All rights reserved.
5    *
6    * This software is open-source under the BSD license; see either
7    * "license.txt" or
8    * https://github.com/jrtom/jung/blob/master/LICENSE for a description.
9    */
10  
11  package edu.uci.ics.jung.algorithms.generators;
12  
13  import java.util.ArrayList;
14  import java.util.List;
15  
16  import com.google.common.base.Supplier;
17  
18  import edu.uci.ics.jung.graph.Graph;
19  import edu.uci.ics.jung.graph.util.EdgeType;
20  
21  /**
22   * Simple generator of an m x n lattice where each vertex
23   * is incident with each of its neighbors (to the left, right, up, and down).
24   * May be toroidal, in which case the vertices on the edges are connected to
25   * their counterparts on the opposite edges as well.
26   * 
27   * <p>If the graph Supplier supplied has a default edge type of {@code EdgeType.DIRECTED},
28   * then edges will be created in both directions between adjacent vertices.
29   * 
30   * @author Joshua O'Madadhain
31   */
32  public class Lattice2DGenerator<V,E> implements GraphGenerator<V,E>
33  {
34      protected int row_count;
35      protected int col_count;
36      protected boolean is_toroidal;
37      protected boolean is_directed;
38      protected Supplier<? extends Graph<V, E>> graph_factory;
39      protected Supplier<V> vertex_factory;
40      protected Supplier<E> edge_factory;
41      private List<V> v_array;
42  
43      /**
44       * Constructs a generator of square lattices of size {@code latticeSize} 
45       * with the specified parameters.
46       * 
47       * @param graph_factory used to create the {@code Graph} for the lattice
48       * @param vertex_factory used to create the lattice vertices
49       * @param edge_factory used to create the lattice edges
50       * @param latticeSize the number of rows and columns of the lattice
51       * @param isToroidal if true, the created lattice wraps from top to bottom and left to right
52       */
53      public Lattice2DGenerator(Supplier<? extends Graph<V,E>> graph_factory, Supplier<V> vertex_factory, 
54              Supplier<E> edge_factory, int latticeSize, boolean isToroidal)
55      {
56          this(graph_factory, vertex_factory, edge_factory, latticeSize, latticeSize, isToroidal);
57      }
58  
59      /**
60       * Creates a generator of {@code row_count} x {@code col_count} lattices 
61       * with the specified parameters.
62       * 
63       * @param graph_factory used to create the {@code Graph} for the lattice
64       * @param vertex_factory used to create the lattice vertices
65       * @param edge_factory used to create the lattice edges
66       * @param row_count the number of rows in the lattice
67       * @param col_count the number of columns in the lattice
68       * @param isToroidal if true, the created lattice wraps from top to bottom and left to right
69       */
70      public Lattice2DGenerator(Supplier<? extends Graph<V,E>> graph_factory, Supplier<V> vertex_factory, 
71              Supplier<E> edge_factory, int row_count, int col_count, boolean isToroidal)
72      {
73          if (row_count < 2 || col_count < 2)
74          {
75              throw new IllegalArgumentException("Row and column counts must each be at least 2.");
76          }
77  
78          this.row_count = row_count;
79          this.col_count = col_count;
80          this.is_toroidal = isToroidal;
81          this.graph_factory = graph_factory;
82          this.vertex_factory = vertex_factory;
83          this.edge_factory = edge_factory;
84          this.is_directed = (graph_factory.get().getDefaultEdgeType() == EdgeType.DIRECTED);
85      }
86      
87      /**
88       * Generates a graph based on the constructor-specified settings.
89       * 
90       * @return the generated graph
91       */
92      public Graph<V,E> get()
93      {
94          int vertex_count = row_count * col_count;
95          Graph<V,E> graph = graph_factory.get();
96          v_array = new ArrayList<V>(vertex_count);
97          for (int i = 0; i < vertex_count; i++)
98          {
99              V v = vertex_factory.get();
100             graph.addVertex(v);
101             v_array.add(i, v);
102         }
103 
104         int start = is_toroidal ? 0 : 1;
105         int end_row = is_toroidal ? row_count : row_count - 1;
106         int end_col = is_toroidal ? col_count : col_count - 1;
107         
108         // fill in edges
109         // down
110         for (int i = 0; i < end_row; i++)
111             for (int j = 0; j < col_count; j++)
112                 graph.addEdge(edge_factory.get(), getVertex(i,j), getVertex(i+1, j));
113         // right
114         for (int i = 0; i < row_count; i++)
115             for (int j = 0; j < end_col; j++)
116                 graph.addEdge(edge_factory.get(), getVertex(i,j), getVertex(i, j+1));
117 
118         // if the graph is directed, fill in the edges going the other direction...
119         if (graph.getDefaultEdgeType() == EdgeType.DIRECTED)
120         {
121             // up
122             for (int i = start; i < row_count; i++)
123                 for (int j = 0; j < col_count; j++)
124                     graph.addEdge(edge_factory.get(), getVertex(i,j), getVertex(i-1, j));
125             // left
126             for (int i = 0; i < row_count; i++)
127                 for (int j = start; j < col_count; j++)
128                     graph.addEdge(edge_factory.get(), getVertex(i,j), getVertex(i, j-1));
129         }
130         
131         return graph;
132     }
133 
134     /**
135      * Returns the number of edges found in a lattice of this generator's specifications.
136      * (This is useful for subclasses that may modify the generated graphs to add more edges.)
137      * 
138      * @return the number of edges that this generator will generate
139      */
140     public int getGridEdgeCount()
141     {
142         int boundary_adjustment = (is_toroidal ? 0 : 1);
143         int vertical_edge_count = col_count * (row_count - boundary_adjustment);
144         int horizontal_edge_count = row_count * (col_count - boundary_adjustment);
145         
146         return (vertical_edge_count + horizontal_edge_count) * (is_directed ? 2 : 1);
147     }
148     
149     protected int getIndex(int i, int j)
150     {
151         return ((mod(i, row_count)) * col_count) + (mod(j, col_count));
152     }
153 
154     protected int mod(int i, int modulus) 
155     {
156         int i_mod = i % modulus;
157         return i_mod >= 0 ? i_mod : i_mod + modulus;
158     }
159     
160     /**
161      * @param i row index into the lattice
162      * @param j column index into the lattice
163      * @return the vertex at position ({@code i mod row_count, j mod col_count})
164      */
165     protected V getVertex(int i, int j)
166     {
167         return v_array.get(getIndex(i, j));
168     }
169     
170     /**
171      * @param i row index into the lattice
172      * @return the {@code i}th vertex (counting row-wise)
173      */
174     protected V getVertex(int i)
175     {
176         return v_array.get(i);
177     }
178     
179     /**
180      * @param i index of the vertex whose row we want
181      * @return the row in which the vertex with index {@code i} is found
182      */
183     protected int getRow(int i)
184     {
185         return i / col_count;
186     }
187     
188     /**
189      * @param i index of the vertex whose column we want
190      * @return the column in which the vertex with index {@code i} is found
191      */
192     protected int getCol(int i)
193     {
194         return i % col_count;
195     }
196 }