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    
005    package graphlab.plugins.main.core.actions;
006    
007    import graphlab.graph.event.GraphEvent;
008    import graphlab.graph.graph.*;
009    import graphlab.graph.ui.GraphRectRegionSelect;
010    import graphlab.platform.core.AbstractAction;
011    import graphlab.platform.core.BlackBoard;
012    import graphlab.plugins.commonplugin.undo.Undoable;
013    import graphlab.plugins.commonplugin.undo.UndoableActionOccuredData;
014    import graphlab.plugins.main.GraphData;
015    import graphlab.plugins.main.core.AlgorithmUtils;
016    
017    import java.awt.*;
018    import java.awt.geom.Rectangle2D;
019    import java.util.HashSet;
020    
021    /**
022     * A class to transform the seleected vertices using mouse
023     * This class is not completely working yet!
024     * <p/>
025     * Now has conflicts with GraphRectRegionSelect
026     *
027     * @author Azin Azadi
028     */
029    public class VertexTransformer extends AbstractAction implements PaintHandler<AbstractGraphRenderer>, Undoable {
030        GraphData gd;
031    
032        //the moving recangles
033        Rectangle2D.Double up;
034        Rectangle2D.Double down;
035        Rectangle2D.Double left;
036        Rectangle2D.Double right;
037    
038        //Center Points of boxes
039        GraphPoint upp, downp, leftp, rightp;
040        public static String IS_TRANSFORMING = "VertexTransformer.isTransforming";
041        private SubGraph sd;
042        private GraphPoint[] verticesPositionsBackUp;
043    
044        double drgStartMouseX, drgStartMouseY;
045        double drgStartWidth, drgStartHeight;
046        double drgFixX, drgFixY;
047    
048        /**
049         * the active transforming box , null if it isn't any
050         */
051        String activeBox;
052    
053        public VertexTransformer(BlackBoard blackboard) {
054            super(blackboard);
055            gd = new GraphData(blackboard);
056            listen4Event(GraphEvent.EVENT_KEY);
057        }
058    
059        public void performAction(String eventName, Object value) {
060            if (GraphRectRegionSelect.isSelecting)
061                return;
062    
063            HashSet<VertexModel> selectedVertices = gd.select.getSelectedVertices();
064            if (selectedVertices.size() <= 1)
065                return;
066    
067            GraphModel g = gd.getGraph();
068            GraphEvent ge = (GraphEvent) value;
069    
070            if (ge.eventType == GraphEvent.DRAGGING_STARTED) {
071    //            System.out.println("start drag ***********");
072                gd.getGraphRenderer().addPostPaintHandler(this);
073                //Gather initial and undo information
074                GraphPoint pos = ge.mousePos;
075                verticesPositionsBackUp = new GraphPoint[g.getVerticesCount()];
076                for (VertexModel _ : g) {
077                    verticesPositionsBackUp[_.getId()] = _.getLocation();
078                }
079                drgStartMouseX = ge.mousePos.x;
080                drgStartMouseY = ge.mousePos.y;
081                if (up == null)//not initialized yet
082                    return;
083                if (up.contains(pos)) {
084    //                blackboard.setData(IS_TRANSFORMING, true);
085                    activeBox = "up";
086                    drgFixX = leftp.x;
087                    drgFixY = downp.y;
088                    drgStartWidth = rightp.x - leftp.x;
089                    drgStartHeight = -downp.y + upp.y;
090                }
091                if (down.contains(pos)) {
092    //                blackboard.setData(IS_TRANSFORMING, true);
093                    activeBox = "down";
094                    drgFixX = leftp.x;
095                    drgFixY = upp.y;
096                    drgStartWidth = rightp.x - leftp.x;
097                    drgStartHeight = downp.y - upp.y;
098                }
099                if (left.contains(pos)) {
100    //                blackboard.setData(IS_TRANSFORMING, true);
101                    activeBox = "left";
102                    drgFixX = rightp.x;
103                    drgFixY = upp.y;
104                    drgStartWidth = -rightp.x + leftp.x;
105                    drgStartHeight = downp.y - upp.y;
106                }
107                if (right.contains(pos)) {
108                    activeBox = "right";
109                    drgFixX = leftp.x;
110                    drgFixY = upp.y;
111                    drgStartWidth = rightp.x - leftp.x;
112                    drgStartHeight = downp.y - upp.y;
113                }
114    
115            }
116            if (ge.eventType == GraphEvent.DRAGGING) {
117    //            System.out.println("drag");
118                if (activeBox == null) {    //if not transforming then exit
119                    return;
120                }
121                blackboard.setData(IS_TRANSFORMING, true);
122    
123    //            System.out.println("active drag");
124                //process event
125                double dx = 0, dy = 0;
126                if (activeBox.equals("up")) {
127                    dy = ge.mousePos.y - drgStartMouseY;
128                }
129                if (activeBox.equals("down")) {
130                    dy = ge.mousePos.y - drgStartMouseY;
131                }
132                if (activeBox.equals("left")) {
133                    dx = ge.mousePos.x - drgStartMouseX;
134                }
135                if (activeBox.equals("right")) {
136                    dx = ge.mousePos.x - drgStartMouseX;
137                }
138    
139                //snap
140                //transform vertices
141    //            System.out.println(Math.abs((drgStartWidth + dx) - (drgStartHeight + dy)));
142    //            System.out.println(Math.abs(drgStartWidth) + dx - Math.abs(drgStartHeight) + dy);
143                int snapAmount = 15;
144                if (abs(abs(drgStartWidth + dx) - abs(drgStartHeight + dy)) < snapAmount) {
145                    if (dx == 0) {
146                        double d1 = Math.signum(dy) * abs(abs(drgStartWidth) + abs(drgStartHeight));
147                        double d2 = Math.signum(dy) * abs(abs(drgStartHeight) - abs(drgStartWidth));
148                        if (abs(dy - d1) > abs(dy - d2)) {
149                            dy = d2;
150                        } else {
151                            dy = d1;
152                        }
153                    } else {//dy==0
154                        double d1 = Math.signum(dx) * abs(abs(drgStartWidth) + abs(drgStartHeight));
155                        double d2 = Math.signum(dx) * abs(abs(drgStartHeight) - abs(drgStartWidth));
156                        if (abs(dx - d1) > abs(dx - d2)) {
157                            dx = d2;
158                        } else {
159                            dx = d1;
160                        }
161                    }
162                }
163    
164                //transform
165                for (VertexModel v : selectedVertices) {
166                    GraphPoint loc = verticesPositionsBackUp[v.getId()];
167                    double vx = loc.x;
168                    double vy = loc.y;
169                    v.setLocation(new GraphPoint(loc.x + dx * (vx - drgFixX) / drgStartWidth, loc.y + dy * (vy - drgFixY) / drgStartHeight));
170                }
171            }
172    
173            if (ge.eventType == GraphEvent.DROPPED) {
174    //            System.out.println("dropped");
175                //add undo data
176                GraphPoint[] newPos = new GraphPoint[gd.getGraph().getVerticesCount()];
177                for (VertexModel _ : gd.getGraph()) {
178                    newPos[_.getId()] = _.getLocation();
179                }
180    
181                UndoableActionOccuredData uaod = new UndoableActionOccuredData(this);
182                uaod.properties.put("oldPositions", verticesPositionsBackUp);
183                uaod.properties.put("newPositions", newPos);
184                blackboard.setData(UndoableActionOccuredData.EVENT_KEY, uaod);
185    
186                blackboard.setData(IS_TRANSFORMING, false);
187            }
188    
189    //        gd.select.setSelected(sd);
190        }
191    
192        private double abs(double dy) {
193            return Math.abs(dy);
194        }
195    
196        public void paint(Graphics g, Object destinationComponent, Boolean drawExtras) {
197            HashSet<VertexModel> selectedVertices = gd.select.getSelectedVertices();
198            if (selectedVertices.size() <= 1)
199                return;
200    
201            //just get a backup
202            sd = gd.select.getSelected();
203            g.setColor(Color.LIGHT_GRAY);
204            Rectangle2D.Double boundingRegion = AlgorithmUtils.getBoundingRegion(selectedVertices);
205            int x = (int) boundingRegion.x;
206            int y = (int) boundingRegion.y;
207            int width = (int) boundingRegion.width;
208            int height = (int) boundingRegion.height;
209            g.drawRoundRect(x, y, width, height, 15, 15);
210    
211            int trboxsz = 15;
212            //draw transform boxes
213            upp = new GraphPoint(x + width / 2, y);
214            downp = new GraphPoint(x + width / 2, y + height);
215            leftp = new GraphPoint(x, y + height / 2);
216            rightp = new GraphPoint(x + width, y + height / 2);
217            up = createTransformControlRect(upp, trboxsz);
218            down = createTransformControlRect(downp, trboxsz);
219            left = createTransformControlRect(leftp, trboxsz);
220            right = createTransformControlRect(rightp, trboxsz);
221    
222            drawRoundRect(g, up, trboxsz / 3);
223            drawRoundRect(g, down, trboxsz / 3);
224            drawRoundRect(g, left, trboxsz / 3);
225            drawRoundRect(g, right, trboxsz / 3);
226        }
227    
228        private Rectangle2D.Double createTransformControlRect(GraphPoint rr, int trboxsz) {
229            return new Rectangle2D.Double(rr.x - trboxsz / 2, rr.y - trboxsz / 2, trboxsz, trboxsz);
230        }
231    
232        public static void drawRoundRect(Graphics g, Rectangle2D.Double rect, int arcsize) {
233            g.drawRoundRect((int) rect.x, (int) rect.y, (int) rect.height, (int) rect.width, arcsize, arcsize);
234    
235        }
236    
237        /**
238         * @return true if the given position is on some of resize boxes according to the graph assigned
239         *         to the blackboard, if any of boxes are visible
240         */
241        public static boolean isPositionOnResizeBoxes(GraphPoint mousPos, BlackBoard b) {
242            Boolean aBoolean = b.getData(IS_TRANSFORMING);
243            if (aBoolean != null && aBoolean) {
244                return true;
245            }
246    
247            GraphData gd = new GraphData(b);
248            HashSet<VertexModel> selectedVertices = gd.select.getSelectedVertices();
249            if (selectedVertices.size() <= 1)
250                return false;
251    
252            Rectangle2D.Double boundingRegion = AlgorithmUtils.getBoundingRegion(selectedVertices);
253            int x = (int) boundingRegion.x;
254            int y = (int) boundingRegion.y;
255            int width = (int) boundingRegion.width;
256            int height = (int) boundingRegion.height;
257    
258            //the moving recangles
259            Rectangle2D.Double up;
260            Rectangle2D.Double down;
261            Rectangle2D.Double left;
262            Rectangle2D.Double right;
263    
264            int trboxsz = 15;
265            //draw transform boxes
266            up = new Rectangle2D.Double(x + width / 2 - trboxsz / 2, y - trboxsz / 2, trboxsz, trboxsz);
267            down = new Rectangle2D.Double(x + width / 2 - trboxsz / 2, y + height - trboxsz / 2, trboxsz, trboxsz);
268            left = new Rectangle2D.Double(x - trboxsz / 2, y + height / 2 - trboxsz / 2, trboxsz, trboxsz);
269            right = new Rectangle2D.Double(x + width - trboxsz / 2, y + height / 2 - trboxsz / 2, trboxsz, trboxsz);
270    
271            return up.contains(mousPos) | down.contains(mousPos) | left.contains(mousPos) | right.contains(mousPos);
272    
273        }
274    
275        public void undo(UndoableActionOccuredData uaod) {
276            GraphPoint verticesPositionsBackUp[] = (GraphPoint[]) uaod.properties.get("oldPositions");
277            GraphModel g = gd.getGraph();
278            if (g.getVerticesCount() != verticesPositionsBackUp.length) {
279                System.err.println("Graph has changed, Undo can not be done");
280                return;
281            }
282    
283            int i = 0;
284            for (VertexModel v : g) {
285                v.setLocation(verticesPositionsBackUp[i++]);
286            }
287    
288        }
289    
290        public void redo(UndoableActionOccuredData uaod) {
291            GraphPoint newPos[] = (GraphPoint[]) uaod.properties.get("newPositions");
292            GraphModel g = gd.getGraph();
293            if (g.getVerticesCount() != this.verticesPositionsBackUp.length) {
294                System.err.println("Graph has changed, Undo can not be done");
295                return;
296            }
297    
298            int i = 0;
299            for (VertexModel v : g) {
300                v.setLocation(newPos[i++]);
301            }
302    
303        }
304    }