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 }