Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/eireneh/bible/control/map/Map.java


1   
2   package com.eireneh.bible.control.map;
3   
4   import java.io.*;
5   
6   import com.eireneh.util.*;
7   import com.eireneh.bible.passage.*;
8   
9   /**
10  * A map is an array of Nodes (verses with position).
11  * 
12  * <table border='1' cellPadding='3' cellSpacing='0' width="100%">
13  * <tr><td bgColor='white'class='TableRowColor'><font size='-7'>
14  * Distribution Licence:<br />
15  * Project B is free software; you can redistribute it
16  * and/or modify it under the terms of the GNU General Public License,
17  * version 2 as published by the Free Software Foundation.<br />
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21  * General Public License for more details.<br />
22  * The License is available on the internet
23  * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, by writing to
24  * <i>Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25  * MA 02111-1307, USA</i>, Or locally at the Licence link below.<br />
26  * The copyright to this program is held by it's authors.
27  * </font></td></tr></table>
28  * @see <a href='http://www.eireneh.com/servlets/Web'>Project B Home</a>
29  * @see docs.Licence
30  * @author Joe Walker
31  * @version D0.I0.T0
32  */
33  public class Map implements Serializable
34  {
35      /**
36      * Basic constructor
37      */
38      public Map(int dimensions)
39      {
40          this.dimensions = dimensions;
41          this.nodes = new Position[Books.versesInBible()];
42  
43          try
44          {
45              // Create the array of Nodes
46              for (int i=1; i<=Books.versesInBible(); i++)
47              {
48                  Verse verse = new Verse(i);
49                  nodes[i-1] = new Position(new float[dimensions]);
50              }
51          }
52          catch (NoSuchVerseException ex)
53          {
54              throw new LogicError(ex);
55          }
56      }
57  
58      /**
59      * Get the number of dimensions in the nodes in this map
60      * @return The number of dimensions
61      */
62      public int getDimensions()
63      {
64          return dimensions;
65      }
66  
67      /**
68      * Get the position (as a float array) of a node by the ordinal number
69      * of the verse that it contains
70      * @param ord The verse ordinal number
71      * @return The requested node position
72      */
73      public float[] getPosition(int ord)
74      {
75          return (float[]) nodes[ord-1].pos.clone();
76      }
77  
78      /**
79      * Get the position (as a float array) of a node by the ordinal number
80      * of the verse that it contains
81      * @param ord The verse ordinal number
82      * @param idx The index into the position array for the given verse
83      * @return The requested node position
84      */
85      public float getPositionDimension(int ord, int idx)
86      {
87          return nodes[ord-1].pos[idx];
88      }
89  
90      /**
91      * Get the position of a node by the ordinal number of the verse that
92      * it contains
93      * @param ord The verse ordinal number
94      * @return The requested node position
95      */
96      public void setPosition(int ord, float[] pos)
97      {
98          nodes[ord-1].pos = pos;
99  
100         fireMapChanged(ord);
101     }
102 
103     /**
104     * Get the position of a node by the ordinal number of the verse that
105     * it contains
106     * @param ord The verse ordinal number
107     * @param idx The index into the position array for the given verse
108     * @param f The new position
109     * @return The requested node position
110     */
111     public void setPositionDimension(int ord, int idx, float f)
112     {
113         nodes[ord-1].pos[idx] = f;
114 
115         fireMapChanged(ord);
116     }
117 
118     /**
119     * Fix the layout to a fairly random one
120     */
121     public void setLayoutRandom()
122     {
123         int vie = Books.versesInBible();
124         for (int i=1; i<=vie; i++)
125         {
126             nodes[i-1] = new Position(new float[] { (float) Math.random(), (float) Math.random() });
127         }
128     }
129 
130     /**
131     * Fix the layout to a simple book/chapter line default
132     */
133     public void setLayoutSimple()
134     {
135         if (dimensions != 2 && dimensions != 3)
136             throw new IllegalArgumentException("Can't set simple layout for maps with "+dimensions+" dimensions.");
137 
138         try
139         {
140             int bie = Books.booksInBible();
141             int ord = 1;
142 
143             for (int b=1; b<=bie; b++)
144             {
145                 int vib = Books.versesInBook(b);
146                 int cib = Books.chaptersInBook(b);
147                 int vord = 1;
148                 for (int c=1; c<=cib; c++)
149                 {
150                     int vic = Books.versesInChapter(b, c);
151                     for (int v=1; v<=vic; v++)
152                     {
153                         float[] fla;
154                         if (dimensions == 2)
155                         {
156                             float x = ((float) (vord - 1)) / (vib - 1);
157                             float y = ((float) (b - 1)) / (bie - 1);
158                             fla = new float[] { x, y };
159                         }
160                         else
161                         {
162                             float x = 1 - ((float) (vic - v)) / vic;
163                             float y = 1 - ((float) (cib - c)) / cib;
164                             float z = 1 - ((float) (bie - b)) / bie;
165                             fla = new float[] { x, y, z };
166                         }
167 
168                         nodes[ord-1] = new Position(fla);
169                         ord++;
170                         vord++;
171                     }
172                 }
173             }
174 
175             fireMapRewritten();
176         }
177         catch (NoSuchVerseException ex)
178         {
179             throw new LogicError(ex);
180         }
181     }
182 
183     /**
184     * Apply the rules to the map.
185     * @param map The set of nodes to move around
186     * @param rules The rules to apply
187     */
188     public void applyRules(Rule[] rules)
189     {
190         // For each verse
191         for (int i=1; i<=Books.versesInBible(); i++)
192         {
193             Position[][] dar = new Position[rules.length][];
194             for (int j=0; j<rules.length; j++)
195             {
196                 dar[j] = rules[j].getDesiredPosition(this, i);
197             }
198 
199             Position[] total = cat(dar);
200             Position ave = average(total);
201 
202             nodes[i-1] = ave;
203         }
204 
205         fireMapRewritten();
206     }
207 
208     /**
209     * Add a map listener to the list of things wanting
210     * to know whenever we make some changes to the map
211     */
212     public void addMapListener(MapListener li)
213     {
214         listeners.add(MapListener.class, li);
215     }
216 
217     /**
218     * Remove a progress listener from the list of things wanting
219     * to know whenever we make some progress
220     */
221     public void removeMapListener(MapListener li)
222     {
223         listeners.remove(MapListener.class, li);
224     }
225 
226     /**
227     * Before we save/load something to/from disk we want to ensure that
228     * we don't loose the list of things that have registered to recieve
229     * map change events.
230     */
231     public EventListenerList getEventListenerList()
232     {
233         return listeners;
234     }
235 
236     /**
237     * Before we save/load something to/from disk we want to ensure that
238     * we don't loose the list of things that have registered to recieve
239     * map change events.
240     */
241     public void setEventListenerList(EventListenerList listeners)
242     {
243         this.listeners = listeners;
244     }
245 
246     /**
247     * What is the average position of all the nodes in this map
248     * @return The center of gravity
249     */
250     public Position getCenterOfGravity()
251     {
252         // to cheat ...
253         // return new Position(new float[] { 0.5F, 0.5F });
254 
255         if (cog == null || replies > MAX_REPLIES)
256         {
257             cog = average(nodes);
258             replies = 0;
259         }
260 
261         replies++;
262         return cog;
263     }
264 
265     /**
266     * Called to fire a MapEvent to all the Listeners, when a single node
267     * has changed position.
268     * @param percent The percentage of the way through that we are now
269     */
270     protected void fireMapChanged(int ord)
271     {
272         // Guaranteed to return a non-null array
273         Object[] contents = listeners.getListenerList();
274 
275         // Process the listeners last to first, notifying
276         // those that are interested in this event
277         MapEvent ev = null;
278         for (int i=contents.length-2; i>=0; i-=2)
279         {
280             if (contents[i] == MapListener.class)
281             {
282                 if (ev == null)
283                     ev = new MapEvent(this, ord);
284 
285                 ((MapListener) contents[i+1]).mapChanged(ev);
286             }
287         }
288     }
289 
290     /**
291     * Called to fire a MapEvent to all the Listeners, when a single node
292     * has changed position.
293     * @param percent The percentage of the way through that we are now
294     */
295     protected void fireMapRewritten()
296     {
297         // Guaranteed to return a non-null array
298         Object[] contents = listeners.getListenerList();
299 
300         // Process the listeners last to first, notifying
301         // those that are interested in this event
302         MapEvent ev = null;
303         for (int i=contents.length-2; i>=0; i-=2)
304         {
305             if (contents[i] == MapListener.class)
306             {
307                 if (ev == null)
308                     ev = new MapEvent(this);
309 
310                 ((MapListener) contents[i+1]).mapRewritten(ev);
311             }
312         }
313     }
314 
315     /**
316     * Take an array of Position arrays can cat them all together to make
317     * a single array containing all of them.
318     * @param The array of Position arrays
319     * @return The single big array
320     */
321     public static Position[] cat(Position[][] dar)
322     {
323         int size = 0;
324         for (int i=0; i<dar.length; i++)
325         {
326             size += dar[i].length;
327         }
328 
329         Position[] total = new Position[size];
330 
331         int offset = 0;
332         for (int i=0; i<dar.length; i++)
333         {
334             System.arraycopy(dar[i], 0, total, offset, dar[i].length);
335             offset += dar[i].length;
336         }
337 
338         return total;
339     }
340 
341     /**
342     * Find the avaerage position of an array of Positions
343     */
344     public static Position average(Position[] array)
345     {
346         int dimensions = array[0].pos.length;
347         double[] tot = new double[dimensions];
348 
349         for (int i=0; i<array.length; i++)
350         {
351             for (int j=0; j<dimensions; j++)
352             {
353                 tot[j] += array[i].pos[j];
354             }
355         }
356 
357         float[] retcode = new float[dimensions];
358 
359         for (int j=0; j<dimensions; j++)
360         {
361             retcode[j] = (float) (tot[j] / array.length);
362         }
363 
364         return new Position(retcode);
365     }
366 
367     /** 
368     * Initialize the transient fields
369     * @param in The stream to read our state from
370     */
371     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
372     {
373         in.defaultReadObject();
374         listeners = new EventListenerList();
375     }
376 
377     /** What is the maximum calculations between re-calcing the CoG */
378     private static final int MAX_REPLIES = 1000;
379 
380     /** The current center of gravity */
381     private Position cog = null;
382 
383     /** How long until we next calculate the center of gravity */
384     private int replies = 0;
385 
386     /** The array of verse nodes */
387     private Position[] nodes;
388 
389     /** The number of dimensions in the display */
390     private int dimensions;
391 
392     /** The number of links that we track for a node */
393     public static final int LINKS_PER_NODE = 20;
394 
395     /** The list of listeners */
396     protected transient EventListenerList listeners = new EventListenerList();
397 
398     /** Serialization ID - a serialization of nodes and dimensions */
399     static final long serialVersionUID = -193572391252539071L;
400 }