View Javadoc

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