View Javadoc

1   /*
2    * @(#) EditorUtilities.java 28-jul-2004 Copyright (c) 2003 Delft University of
3    * Technology Jaffalaan 5, 2628 BX Delft, the Netherlands All rights reserved.
4    * This software is proprietary information of Delft University of Technology
5    * The code is published under the General Public License
6    */
7   package nl.tudelft.simulation.dsol.gui.editor2D.actions;
8   
9   import java.awt.geom.Point2D;
10  import java.lang.reflect.Constructor;
11  import java.rmi.RemoteException;
12  import java.util.ArrayList;
13  import java.util.Iterator;
14  import java.util.List;
15  
16  import nl.tudelft.simulation.dsol.animation.Editable;
17  import nl.tudelft.simulation.dsol.animation.D2.EditableRenderable2DInterface;
18  import nl.tudelft.simulation.dsol.animation.D2.Renderable2DInterface;
19  import nl.tudelft.simulation.dsol.gui.DSOLApplicationInterface;
20  import nl.tudelft.simulation.dsol.gui.editor2D.Editor2DPanel;
21  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
22  import nl.tudelft.simulation.language.d3.CartesianPoint;
23  import nl.tudelft.simulation.language.d3.DirectedPoint;
24  import nl.tudelft.simulation.logger.Logger;
25  
26  /***
27   * Utility methods to select, create, edit and delete editables
28   * <p>
29   * (c) copyright 2003 <a href="http://www.simulation.tudelft.nl">Delft
30   * University of Technology </a>, the Netherlands. <br>
31   * See for project information <a
32   * href="http://www.simulation.tudelft.nl">www.simulation.tudelft.nl </a> <br>
33   * License of use: <a href="http://www.gnu.org/copyleft/gpl.html">General Public
34   * License (GPL) </a>, no warranty <br>
35   * 
36   * @version 1.0 <br>
37   * @author <a href="http://www.tbm.tudelft.nl/webstaf/royc/index.htm">Roy Chin
38   *         </a>
39   */
40  public final class EditorUtilities
41  {
42  
43      /*** the default offset value in x and y dirextion for new points */
44      public static final int DEFAULT_OFFSET = 8;
45  
46      /*** a hidden constructor */
47      private EditorUtilities()
48      {
49          // Do nothing
50      }
51  
52      /***
53       * determine the selected object
54       * 
55       * @param worldCoordinate
56       *            point in world coordinates
57       * @param panel
58       *            the editable animation panel
59       * @return the selected renderable
60       */
61      public static Renderable2DInterface selectEditable(
62              final Point2D worldCoordinate, final Editor2DPanel panel)
63      {
64          // Determine which object was selected.
65          Renderable2DInterface selected = EditorUtilities.determineSelected(
66                  worldCoordinate, panel);
67          if (selected == null)
68          {
69              panel.setSelectedEditableRenderable(null);
70          } else
71          {
72              try
73              {
74                  if (selected instanceof EditableRenderable2DInterface)
75                  {
76                      panel
77                              .setSelectedEditableRenderable((EditableRenderable2DInterface) selected);
78                  }
79              } catch (ClassCastException exception)
80              {
81                  Logger.warning(EditorUtilities.class, "selectEditable",
82                          exception);
83              }
84          }
85          return selected;
86      }
87  
88      /***
89       * instantiate a new editable
90       * 
91       * @param worldCoordinate
92       *            point in world coordinates
93       * @param application
94       *            the application
95       * @param panel
96       *            the editable animation panel
97       */
98      public static void instantiateNewEditable(final Point2D worldCoordinate,
99              final DSOLApplicationInterface application,
100             final Editor2DPanel panel)
101     {
102         panel.setSelectedEditableRenderable(null);
103         Class editableClass = panel.getSelectedRenderableClass();
104 
105         // Now try to instantiate
106         try
107         {
108             // Create a new editable
109             Class[] argsClass = new Class[]{SimulatorInterface.class,
110                     DirectedPoint.class};
111             Object[] args = new Object[]{
112                     application.getExperiment().getSimulator(),
113                     new DirectedPoint(worldCoordinate.getX(), worldCoordinate
114                             .getY(), 0)};
115             Constructor constructor = editableClass.getConstructor(argsClass);
116             constructor.newInstance(args);
117         } catch (Exception exception)
118         {
119             Logger.warning(EditorUtilities.class, "instantiateNewEditable",
120                     exception);
121         }
122     }
123 
124     /***
125      * move the selected control point
126      * 
127      * @param target
128      *            the editableRenderable which is moved.
129      * @param worldCoordinate
130      *            point in world coordinates where to move to
131      * @param panel
132      *            the Editor2D panel
133      */
134     public static void moveSelectedPoint(
135             final EditableRenderable2DInterface target,
136             final Point2D worldCoordinate, final Editor2DPanel panel)
137     {
138         try
139         {
140             Editable editable = (Editable) target.getSource();
141             CartesianPoint[] vertices = editable.getVertices();
142 
143             if (target.allowEditPoints())
144             {
145                 // Determine the new CartesianPoints
146                 CartesianPoint[] newPos = new CartesianPoint[vertices.length];
147                 for (int i = 0; i < vertices.length; i++)
148                 {
149                     newPos[i] = new CartesianPoint();
150                     if (vertices[i] == panel.getSelectedPoint())
151                     {
152                         CartesianPoint coord = EditorUtilities
153                                 .convertToLocalCoordinates(new CartesianPoint(
154                                         worldCoordinate.getX(), worldCoordinate
155                                                 .getY(), 0), editable
156                                         .getLocation());
157 
158                         newPos[i].x = coord.x;
159                         newPos[i].y = coord.y;
160                         newPos[i].z = vertices[i].z;
161                     } else
162                     {
163                         newPos[i].x = vertices[i].x;
164                         newPos[i].y = vertices[i].y;
165                         newPos[i].z = vertices[i].z;
166                     }
167                 }
168                 // Now let the selected editable decide for itself
169                 // how it handles this modification.
170                 editable.setVertices(newPos);
171             }
172         } catch (RemoteException exception)
173         {
174             Logger.warning(EditorUtilities.class, "moveSelectedPoint",
175                     exception);
176         }
177     }
178 
179     /***
180      * move the object
181      * 
182      * @param target
183      *            the target editable renderable
184      * @param newCoordinate
185      *            Point in world coordinates where to move to
186      * @param oldCoordinate
187      *            Point in world coordinates where the mouse was the last
188      *            iteration, because we move relative to this coordinate
189      */
190     public static void moveEditable(final EditableRenderable2DInterface target,
191             final Point2D newCoordinate, final Point2D oldCoordinate)
192     {
193         try
194         {
195             Editable editable = (Editable) target.getSource();
196             if (editable != null)
197             {
198                 if (target.allowMove())
199                 {
200                     double dx = newCoordinate.getX() - oldCoordinate.getX();
201                     double dy = newCoordinate.getY() - oldCoordinate.getY();
202                     DirectedPoint location = editable.getLocation();
203                     location.x += dx;
204                     location.y += dy;
205                 }
206             }
207         } catch (RemoteException exception)
208         {
209             Logger.warning(EditorUtilities.class, "moveEditable", exception);
210         }
211     }
212 
213     /***
214      * rotate the editable
215      * 
216      * @param target
217      *            the target editable renderable
218      * @param newCoordinate
219      *            point in world coordinates where to rotate to
220      * @param oldCoordinate
221      *            Point in world coordinates where the mouse was the last
222      *            iteration, because we move relative to this coordinate
223      * @param centerCoordinate
224      *            the center of rotation in world coordinates
225      */
226     public static void rotateEditable(
227             final EditableRenderable2DInterface target,
228             final Point2D newCoordinate, final Point2D centerCoordinate,
229             final Point2D oldCoordinate)
230     {
231         try
232         {
233             Editable editable = (Editable) target.getSource();
234             if (editable != null)
235             {
236                 if (target.allowRotate())
237                 {
238                     double dx = newCoordinate.getX() - oldCoordinate.getX();
239                     double dy = newCoordinate.getY() - oldCoordinate.getY();
240                     double dxR = oldCoordinate.getX() - centerCoordinate.getX();
241                     double dyR = oldCoordinate.getY() - centerCoordinate.getY();
242                     double dxR2 = newCoordinate.getX()
243                             - centerCoordinate.getX();
244                     double dyR2 = newCoordinate.getY()
245                             - centerCoordinate.getY();
246                     double distY = Math.sqrt(dx * dx + dy * dy);
247                     double distX = Math.sqrt(dxR * dxR + dyR * dyR);
248                     int factor = 1;
249                     if (Math.atan2(dyR, dxR) > Math.atan2(dyR2, dxR2))
250                     {
251                         factor = -1;
252                     }
253 
254                     double angle = factor * Math.atan2(distY, distX);
255 
256                     // Change the location too
257                     DirectedPoint location = editable.getLocation();
258                     double dxP = location.x - centerCoordinate.getX();
259                     double dyP = location.y - centerCoordinate.getY();
260                     double distanceP = Math.sqrt(dxP * dxP + dyP * dyP);
261                     double angleP = Math.atan2(dyP, dxP);
262                     location.x = centerCoordinate.getX() + distanceP
263                             * Math.cos(angle + angleP);
264                     location.y = centerCoordinate.getY() + distanceP
265                             * Math.sin(angle + angleP);
266                     location.setRotZ(angle + location.getRotZ());
267                 }
268             }
269         } catch (RemoteException exception)
270         {
271             Logger.warning(EditorUtilities.class, "rotateEditable", exception);
272         }
273     }
274 
275     /***
276      * delete the selected editable
277      * 
278      * @param target
279      *            the target editable renderable
280      * @param panel
281      *            the editable animation panel
282      */
283     public static void deleteEditable(
284             final EditableRenderable2DInterface target,
285             final Editor2DPanel panel)
286     {
287         Editable editable = (Editable) target.getSource();
288         if (editable != null)
289         {
290             try
291             {
292                 if (target.allowDelete())
293                 {
294                     target.destroy();
295                     panel.setSelectedEditableRenderable(null);
296                     panel.setSelectedPoint(null);
297                     panel.repaint();
298                 }
299             } catch (RemoteException exception)
300             {
301                 Logger.warning(EditorUtilities.class, "deleteEditable",
302                         exception);
303             }
304         }
305     }
306 
307     /***
308      * add a point behind the last point of the editable
309      * 
310      * @param target
311      *            the target editable renderable
312      * @param panel
313      *            the animation panel
314      */
315     public static void addPointToEditable(
316             final EditableRenderable2DInterface target,
317             final Editor2DPanel panel)
318     {
319         try
320         {
321             // The distance in screen coordinates that the new point
322             // will be offset in x and y direction from the last point.
323             double delta = EditorUtilities.DEFAULT_OFFSET
324                     * Renderable2DInterface.Util.getScale(panel.getExtent(),
325                             panel.getSize());
326 
327             Editable editable = (Editable) target.getSource();
328             CartesianPoint[] vertices = editable.getVertices();
329 
330             // Add the point
331             if ((target.allowAddOrDeletePoints())
332                     && (vertices.length < target.getMaxNumberOfPoints()))
333             {
334                 CartesianPoint[] newPos = new CartesianPoint[vertices.length + 1];
335                 for (int i = 0; i < newPos.length; i++)
336                 {
337                     newPos[i] = new CartesianPoint();
338                     if (i == (newPos.length - 1))
339                     {
340                         newPos[i].x = vertices[i - 1].x + delta;
341                         newPos[i].y = vertices[i - 1].y + delta;
342                         newPos[i].z = vertices[i - 1].z;
343                     } else
344                     {
345                         newPos[i].x = vertices[i].x;
346                         newPos[i].y = vertices[i].y;
347                         newPos[i].z = vertices[i].z;
348                     }
349                 }
350                 editable.setVertices(newPos);
351                 panel.repaint();
352             }
353         } catch (RemoteException exception)
354         {
355             Logger.warning(EditorUtilities.class, "addPointToEditable",
356                     exception);
357         }
358     }
359 
360     /***
361      * determine selected object as the mouse CartesianPoint. This is the
362      * topmost target when objects overlap.
363      * 
364      * @param worldCoordinate
365      *            the selected point in world coordinates
366      * @param panel
367      *            the editable animation panel
368      * @return a renderable if there is one at the selected point
369      */
370     public static Renderable2DInterface determineSelected(
371             final Point2D worldCoordinate, final Editor2DPanel panel)
372     {
373         Renderable2DInterface selected = null;
374         Renderable2DInterface previous = panel.getSelectedEditableRenderable();
375         List targets = EditorUtilities.determineTargets(worldCoordinate, panel);
376 
377         if (previous != null)
378         {
379             // Check if 'previous' is in 'targets'
380             int index = -1;
381             for (int i = 0; i < targets.size(); i++)
382             {
383                 if (previous == (Renderable2DInterface) targets.get(i))
384                 {
385                     index = i;
386                 }
387             }
388             if (index >= 0)
389             {
390                 if (index + 1 < targets.size())
391                 {
392                     selected = (Renderable2DInterface) targets.get(index + 1);
393                 } else
394                 {
395                     selected = (Renderable2DInterface) targets.get(0);
396                 }
397             } else if (targets.size() > 0)
398             {
399                 selected = (Renderable2DInterface) targets.get(0);
400             }
401         } else
402         {
403             try
404             {
405                 double zValue = -Double.MAX_VALUE;
406                 for (Iterator i = targets.iterator(); i.hasNext();)
407                 {
408                     Renderable2DInterface next = (Renderable2DInterface) i
409                             .next();
410 
411                     double z = next.getSource().getLocation().z;
412                     if (z > zValue)
413                     {
414                         zValue = z;
415                         selected = next;
416                     }
417 
418                 }
419             } catch (RemoteException exception)
420             {
421                 Logger.warning(EditorUtilities.class, "determineSelected",
422                         exception);
423             }
424 
425         }
426 
427         return selected;
428     }
429 
430     /***
431      * determine the targeted objects at the mouse CartesianPoint
432      * 
433      * @param worldCoordinate
434      *            point in world coordinates
435      * @param panel
436      *            the editable animation panel
437      * @return targeted objects
438      */
439     public static List determineTargets(final Point2D worldCoordinate,
440             final Editor2DPanel panel)
441     {
442         List targets = new ArrayList();
443         try
444         {
445             for (Iterator i = panel.getElements().iterator(); i.hasNext();)
446             {
447                 Renderable2DInterface renderable = (Renderable2DInterface) i
448                         .next();
449                 if (renderable.contains(worldCoordinate, panel.getExtent(),
450                         panel.getSize()))
451                 {
452                     targets.add(renderable);
453                 }
454             }
455         } catch (Exception exception)
456         {
457             Logger
458                     .warning(EditorUtilities.class, "determineTargets",
459                             exception);
460         }
461         return targets;
462     }
463 
464     /***
465      * converts a coordinate in local coordinates to global coordinates note:
466      * this works only for 2D
467      * 
468      * @param point
469      *            point in local coordinates
470      * @param location
471      *            location vector
472      * @return point in global coordinates
473      */
474     public static CartesianPoint convertToGlobalCoordinates(
475             final CartesianPoint point, final DirectedPoint location)
476     {
477         CartesianPoint global = new CartesianPoint();
478         // Rotate
479         double angle = Math.atan2(point.y, point.x);
480         double length = Math.sqrt(point.x * point.x + point.y * point.y);
481         double rotation = location.getRotZ() + angle;
482         global.x = length * Math.cos(rotation);
483         global.y = length * Math.sin(rotation);
484 
485         // Translate
486         global.x += location.x;
487         global.y += location.y;
488         return global;
489     }
490 
491     /***
492      * converts a coordinate in global coordinates to local coordinates note:
493      * this works only for 2D
494      * 
495      * @param point
496      *            point in local coordinates
497      * @param location
498      *            location vector
499      * @return point in global coordinates
500      */
501     public static CartesianPoint convertToLocalCoordinates(
502             final CartesianPoint point, final DirectedPoint location)
503     {
504         CartesianPoint local = new CartesianPoint();
505 
506         // Translate
507         local.x = point.x - location.x;
508         local.y = point.y - location.y;
509 
510         // Rotate
511         double angle = Math.atan2(local.y, local.x);
512         double length = Math.sqrt(local.x * local.x + local.y * local.y);
513         double rotation = angle - location.getRotZ();
514         local.x = length * Math.cos(rotation);
515         local.y = length * Math.sin(rotation);
516         return local;
517     }
518 }