001    // GraphLab Project: http://graphlab.sharif.edu
002    // Copyright (C) 2008 Mathematical Science Department of Sharif University of Technology
003    // Distributed under the terms of the GNU General Public License (GPL): http://www.gnu.org/licenses/
004    package graphlab.graph.graph;
005    
006    import graphlab.graph.event.GraphModelListener;
007    import graphlab.library.ListGraph;
008    import graphlab.library.BaseVertex;
009    import graphlab.library.BaseGraph;
010    import graphlab.library.exceptions.InvalidEdgeException;
011    import graphlab.library.exceptions.InvalidVertexException;
012    import graphlab.platform.lang.ArrayX;
013    import graphlab.platform.preferences.lastsettings.StorableOnExit;
014    import graphlab.platform.preferences.lastsettings.UserModifiableProperty;
015    
016    import java.awt.*;
017    import java.awt.geom.Rectangle2D;
018    import java.util.*;
019    
020    
021    /**
022     * @author Azin Azadi,roozbeh ebrahimi
023     */
024    
025    public class GraphModel extends ListGraph<VertexModel, EdgeModel> implements StorableOnExit {
026        {
027            SETTINGS.registerSetting(this, "Graph Drawings");
028        }
029    
030        private Font font = Font.decode("Arial");
031        @UserModifiableProperty(displayName = "Show Edge Labels")
032        public static boolean showEdgeWeights = true;
033        @UserModifiableProperty(displayName = "Show Vertex Labels")
034        public static boolean vertexLabelsEnabled = true;
035        private boolean drawVertexLabels = vertexLabelsEnabled;
036        private boolean drawEdgeLabels = showEdgeWeights;
037        private ArrayX<String> zoom = new ArrayX<String>("100%", "10%", "25%", "50%", "75%", "100%", "150%", "200%", "400%");
038    
039        /**
040         * a number which is constructed from zoom, (150% -> 1.5)
041         */
042        private double zoomFactor = 1;
043    
044        public GraphModel createEmptyGraph() {
045            return new GraphModel(isDirected());
046        }
047    
048        static final int VERTEX_ADDED_GRAPH_CHANGE = 0;
049        static final int VERTEX_REMOVED_GRAPH_CHANGE = 1;
050        static final int EDGE_ADDED_GRAPH_CHANGE = 2;
051        static final int EDGE_REMOVED_GRAPH_CHANGE = 3;
052        static final int GRAPH_CLEARED_GRAPH_CHANGE = 4;
053        static final int REPAINT_GRAPH_GRAPH_CHANGE = 5;
054    
055        boolean showChangesOnView;
056        private String label;
057        public static final Color[] color = {Color.magenta, Color.blue, Color.yellow, Color.green, Color.orange, Color.red, Color.cyan, Color.pink, Color.lightGray, Color.darkGray};
058    
059        /**
060         * It is true if the edges of this graph are curves (not line),
061         * This will be applied to all edges of graph
062         */
063        public boolean isEdgesCurved;
064    
065        /**
066         * generates an undirected graph
067         */
068        public GraphModel() {
069            super(true, 20);
070            showChangesOnView = false;
071            drawVertexLabels = vertexLabelsEnabled;
072        }
073    
074        public GraphModel(boolean isdirected) {
075            super(isdirected, 20);
076            showChangesOnView = false;
077            drawVertexLabels = vertexLabelsEnabled;
078        }
079    
080    
081        //________________________   + Userdefined Attributes    _________________________________
082        /**
083         * This is a place to put custom attributes in the graph, It will be shown in property editor and editable
084         */
085        private HashMap<String, Object> userDefinedAttributes = null;
086    
087        /**
088         * these attributed will be added to each graph's userDefinedAttributes on constructing time.
089         */
090        private static HashMap<String, Object> globalUserDefinedAttributes = null;
091    
092    
093        /**
094         * sets and stores a user defined attribute for the graph. here you can put any attribute you like that are not available
095         * in the standard attributes. your attributes will be editable in property editor part of GUI.
096         *
097         * @param name
098         * @param value
099         */
100        public void setUserDefinedAttribute(String name, Object value) {
101            if (userDefinedAttributes == null) {
102                userDefinedAttributes = new HashMap<String, Object>();
103            }
104            userDefinedAttributes.put(name, value);
105        }
106    
107        /**
108         * returns the specified user defined attribute, or null if it does not exists.
109         *
110         * @param name
111         * @return
112         */
113        public <t> t getUserDefinedAttribute(String name) {
114            if (userDefinedAttributes == null)
115                return null;
116            return (t) userDefinedAttributes.get(name);
117        }
118    
119        /**
120         * removes the given attribute from the list of user defined attributes
121         *
122         * @param name
123         */
124        public void removeUserDefinedAttribute(String name) {
125            userDefinedAttributes.remove(name);
126            if (userDefinedAttributes.size() == 0)
127                userDefinedAttributes = null;
128        }
129    
130        /**
131         * @return a HashMap containing all user defined attributes.
132         */
133        public HashMap<String, Object> getUserDefinedAttributes() {
134            return userDefinedAttributes;
135        }
136    
137    
138        /**
139         * sets and stores a global user defined attribute for the graph. this attributes will be added to each graph on
140         * constructing time using setUserDefinedAttribute method.
141         * <p/>
142         * note that this method only affects the afterward created graphs, and current graph will not affected by this method.
143         */
144        public static void addGlobalUserDefinedAttribute(String name, Object defaultvalue) {
145            if (globalUserDefinedAttributes == null) {
146                globalUserDefinedAttributes = new HashMap<String, Object>();
147            }
148            globalUserDefinedAttributes.put(name, defaultvalue);
149        }
150    
151        /**
152         * @see GraphModel#addGlobalUserDefinedAttribute
153         */
154        public static void removeGlobalUserDefinedAttribute(String name) {
155            globalUserDefinedAttributes.remove(name);
156            if (globalUserDefinedAttributes.size() == 0)
157                globalUserDefinedAttributes = null;
158        }
159    
160        {
161            //default constructor
162            if (globalUserDefinedAttributes != null) {
163                userDefinedAttributes = new HashMap<String, Object>();
164                userDefinedAttributes.putAll(globalUserDefinedAttributes);
165            }
166        }
167        //________________________   - Userdefined Attributes    _________________________________
168    
169        /**
170         * determines whether show changes in model to view, for example when an algorithm changes the color of a vertex
171         * in VertexModel(BaseVertex) should a color be assigned in GUI to it or not?
172         *
173         * @param showChangesOnView
174         */
175        public void setShowChangesOnView(boolean showChangesOnView) {
176            this.showChangesOnView = showChangesOnView;
177            fireGraphChange(REPAINT_GRAPH_GRAPH_CHANGE, null, null);
178        }
179    
180        public boolean isShowChangesOnView() {
181            return showChangesOnView;
182        }
183    
184        /**
185         * same to insertVertex
186         */
187        public void insertVertex(VertexModel newVertex) {
188            super.insertVertex(newVertex);
189            fireGraphChange(VERTEX_ADDED_GRAPH_CHANGE, newVertex, null);
190        }
191    
192        public void insertVertices(Collection<VertexModel> vertices) {
193            for (VertexModel v : vertices) {
194                insertVertex(v);
195            }
196        }
197    
198        public void removeAllEdges(VertexModel source, VertexModel target) throws InvalidVertexException {
199            removeEdge(getEdge(source, target));
200        }
201    
202        public void removeEdge(EdgeModel edge) throws InvalidEdgeException {
203            super.removeEdge(edge);
204            fireGraphChange(EDGE_REMOVED_GRAPH_CHANGE, null, edge);
205        }
206    
207        public void removeVertex(VertexModel v) throws InvalidVertexException {
208            Iterator<EdgeModel> it = edgeIterator(v);
209            while (it.hasNext()) {
210                removeEdge(it.next());
211    
212            }
213            super.removeVertex(v);
214            fireGraphChange(VERTEX_REMOVED_GRAPH_CHANGE, v, null);
215        }
216    
217        public void clear() {
218            super.clear();
219            fireGraphChange(GRAPH_CLEARED_GRAPH_CHANGE, null, null);
220        }
221    
222        public EdgeModel getEdge(VertexModel v1, VertexModel v2) {
223            Object t[] = null;
224            try {
225                t = super.getEdges(v2, v1).toArray();
226            }
227            catch (Exception e) {
228                e.printStackTrace();
229            }
230            if (t.length == 0)
231                return null;
232            else
233                return (EdgeModel) t[0];
234        }
235    
236    /**return true if the new edge didn't exist in the graph and the operation was succesfully*/
237        /**
238         * adds new edge only if it doesn't exist in graph
239         *
240         * @param newedge
241         */
242        public void insertEdge(EdgeModel newedge) {
243            try {
244                if (!isEdge(newedge.source, newedge.target) && !(newedge.source.getId() == newedge.target.getId())) {
245                    super.insertEdge(newedge);
246    //                return true;
247                }
248                fireGraphChange(EDGE_ADDED_GRAPH_CHANGE, null, newedge);
249            }
250            catch (Exception e) {
251                e.printStackTrace();
252            }
253        }
254    
255        public int getEdgesCount() {
256            return super.getEdgesCount();
257    //        //graph fact: num of edges = 1/2 * sigma(degrees)
258    //        int ret = 0;
259    //        for (VertexModel v : this) {
260    //            ret += getInDegree(v);
261    //        }
262    //        return (int) (ret / (isDirected() ? 1 : 2));
263        }
264    
265        //                         Listener
266    
267        HashSet<GraphModelListener> glisteners = new HashSet<GraphModelListener>();
268    
269        public void addGraphListener(GraphModelListener listener) {
270            glisteners.add(listener);
271        }
272    
273        public void removeGraphListener(GraphModelListener listener) {
274            glisteners.remove(listener);
275        }
276    
277        void fireGraphChange(int change, VertexModel v, EdgeModel e) {
278            for (GraphModelListener l : glisteners) {
279                switch (change) {
280                    case VERTEX_ADDED_GRAPH_CHANGE:
281                        l.vertexAdded(v);
282                        break;
283                    case VERTEX_REMOVED_GRAPH_CHANGE:
284                        l.vertexRemoved(v);
285                        break;
286                    case EDGE_ADDED_GRAPH_CHANGE:
287                        l.edgeAdded(e);
288                        break;
289                    case EDGE_REMOVED_GRAPH_CHANGE:
290                        l.edgeRemoved(e);
291                        break;
292                    case GRAPH_CLEARED_GRAPH_CHANGE:
293                        l.graphCleared();
294                        break;
295                    case REPAINT_GRAPH_GRAPH_CHANGE:
296                        l.repaintGraph();
297                        break;
298                }
299            }
300        }
301        // -            GraphModelListener
302    
303        /**
304         * @return the smallest rectangle that fits arround graph without considering the ZOOM
305         */
306        public Rectangle2D.Double getAbsBounds() {
307            Rectangle2D.Double ret = new Rectangle2D.Double();
308            boolean first = true;
309            for (VertexModel v : this) {
310                GraphPoint location = v.getLocation();
311                Point center = v.getCenter();
312                GraphPoint shapeSize = v.getSize();
313                Rectangle2D.Double p = new Rectangle2D.Double(location.x * zoomFactor - center.x, location.y * zoomFactor - center.y, shapeSize.x, shapeSize.y);
314                if (first) {
315                    ret = new Rectangle2D.Double(p.x, p.y, 0, 0);
316                    first = false;
317                }
318                ret.add(p);
319            }
320            return ret;
321        }
322    
323        /**
324         * @return the smallest rectangle that fits arround graph with considering zoom
325         */
326        public Rectangle2D.Double getZoomedBounds() {
327            Rectangle2D.Double ret = new Rectangle2D.Double();
328            boolean first = true;
329            for (VertexModel v : this) {
330                GraphPoint location = v.getLocation();
331                Point center = v.getCenter();
332                GraphPoint shapeSize = v.getSize();
333                Rectangle2D.Double p = new Rectangle2D.Double(location.x * zoomFactor - center.x, location.y * zoomFactor - center.y, shapeSize.x, shapeSize.y);
334    
335                if (first) {
336                    ret = p;
337                    first = false;
338                }
339    //            p.x = p.x;
340    //            p.y = p.y;
341                ret.add(p);
342    //            System.out.println(v.getId() + "" +ret);
343            }
344            return ret;
345        }
346    
347    
348        public double getZoomFactor() {
349            return zoomFactor;
350        }
351    
352    
353        public ArrayX<String> getZoom() {
354            return zoom;
355        }
356    
357        public void setZoom(ArrayX<String> zoom) {
358            this.zoom = zoom;
359            String vl = zoom.getValue();
360            zoomFactor = Integer.parseInt(vl.substring(0, vl.length() - 1)) / 100.0;
361            fireGraphChange(REPAINT_GRAPH_GRAPH_CHANGE, null, null);
362        }
363    
364        public void zoomIn(){
365            double zF = zoomFactor * 1.5;
366            setZoom(zF);
367        }
368    
369        public void zoomOut(){
370            double zF = zoomFactor / 1.5;
371            setZoom(zF);
372        }
373    
374        public void setZoom(double zoomFactor) {
375            if (zoomFactor<0.01 || zoomFactor>20)
376                return;
377            String nz = ((int)(zoomFactor * 100))+ "%";
378            this.zoom.addValidValue(nz);
379            zoom.setValue(nz);
380            setZoom(zoom);
381        }
382    
383        public void setLabel(String s) {
384            this.label = s;
385        }
386    
387        public String getLabel() {
388            return label;
389        }
390    
391        /**
392         * the standard way to convert simple integers (1,2,3...) to colors
393         */
394        private static Color color(int m) {
395            return color[m % color.length];
396        }
397    
398        public void insertEdges(Iterable<EdgeModel> edgeModels) {
399            for (EdgeModel _ : edgeModels)
400                insertEdge(_);
401        }
402    
403        /**
404         * adds graph to this graph and place it in the given rectangle
405         *
406         * @param graph
407         * @param _rect
408         */
409        public void addSubGraph(GraphModel graph, Rectangle _rect) {
410            Rectangle2D.Double bounds1 = graph.getZoomedBounds();
411            Rectangle2D.Double rect = new Rectangle2D.Double(_rect.getX(), _rect.getY(), _rect.getWidth(), _rect.getHeight());
412            double kx = rect.width / bounds1.getWidth();
413            double ky = rect.height / bounds1.getHeight();
414            for (VertexModel vm : graph) {
415                GraphPoint p = vm.getLocation();
416                insertVertex(vm);
417                vm.setLocation(new GraphPoint(((p.x - bounds1.x) * kx + rect.x), (int) ((p.y - bounds1.y) * ky + rect.y)));
418            }
419            Iterator<EdgeModel> eiter = graph.lightEdgeIterator();
420            for (; eiter.hasNext();) {
421                EdgeModel edge = eiter.next();
422                insertEdge(edge);
423            }
424        }
425    
426        /**
427         * in GraphLab all Colors that assign to Vertices/Edges are in values, so they
428         * can not directly shown with distinct colors, this method gived the standard GraphLab
429         * solution to this which assigns unique colors to 1..20 and if i>20, It will regards the i
430         * itself as the color (new Color(i)) regarding the fact that normally in GraphTheory Colors have
431         * small values.
432         *
433         * @return an RGB color which is representing the given integer Color in GraphLab
434         */
435        public static Color getColor(Integer i) {
436            Color c = null;
437            if (i == null)
438                i = 0;
439            if (i < 20 && i >= 0) {
440                int ii = i % 20;
441                if (ii < 10)
442                    c = color(i);
443                else
444                    c = color(i).darker();
445            } else {
446                c = new Color(i);
447            }
448            return c;
449        }
450    
451        public Font getFont() {
452            return this.font;
453        }
454    
455        public void setFont(Font font) {
456            this.font = font;
457            fireGraphChange(REPAINT_GRAPH_GRAPH_CHANGE, null, null);
458        }
459    
460        public boolean isDrawEdgeLabels() {
461            return drawEdgeLabels;
462        }
463    
464        public void setDrawEdgeLabels(boolean drawEdgeLabels) {
465            this.drawEdgeLabels = drawEdgeLabels;
466            fireGraphChange(REPAINT_GRAPH_GRAPH_CHANGE, null, null);
467        }
468    
469        public boolean isDrawVertexLabels() {
470            return drawVertexLabels;
471        }
472    
473        public void setDrawVertexLabels(boolean drawVertexLabels) {
474            this.drawVertexLabels = drawVertexLabels;
475            fireGraphChange(REPAINT_GRAPH_GRAPH_CHANGE, null, null);
476        }
477    
478        /**
479         * @return true if the edges of this graph are curves (not lines),
480         *         This is about all edges of graph
481         */
482        public boolean isEdgesCurved() {
483            return isEdgesCurved;
484        }
485    
486        /**
487         * set the edges of this graph to be curves or lines
488         *
489         * @param isCurve
490         */
491        public void setIsEdgesCurved(boolean isCurve) {
492            this.isEdgesCurved = isCurve;
493            fireGraphChange(REPAINT_GRAPH_GRAPH_CHANGE, null, null);
494        }
495    
496        public void insertVertices(VertexModel[] vertices) {
497            for (VertexModel v : vertices) {
498                insertVertex(v);
499            }
500        }
501    
502        public void insertEdges(EdgeModel[] edges) {
503            for (EdgeModel e:edges)
504                insertEdge(e);
505        }
506    
507    
508        @Override
509        public VertexModel[] getVertexArray() {
510            VertexModel[] arr = new VertexModel[getVerticesCount()];
511    
512            for (VertexModel v : this)
513                arr[getId(v)] = v;
514    
515            return arr;
516        }
517    }