/* * Licensed Materials - Property of Perforce Software, Inc. * © Copyright Perforce Software, Inc. 2014, 2021 * © Copyright IBM Corp. 2009, 2014 * © Copyright ILOG 1996, 2009 * All Rights Reserved. * * Note to U.S. Government Users Restricted Rights: * The Software and Documentation were developed at private expense and * are "Commercial Items" as that term is defined at 48 CFR 2.101, * consisting of "Commercial Computer Software" and * "Commercial Computer Software Documentation", as such terms are * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4, * as applicable. */ package simulation; import java.awt.AWTEvent; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.io.IOException; import java.util.Enumeration; import java.util.Vector; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JToggleButton; import javax.swing.JToolBar; import ilog.views.IlvManagerView; import ilog.views.IlvManagerViewInteractor; import ilog.views.IlvPoint; import ilog.views.IlvTransformer; import ilog.views.event.InteractorChangedEvent; import ilog.views.event.InteractorListener; import ilog.views.interactor.IlvPermanentInteractorInterface; import ilog.views.maps.IlvCoordinate; import ilog.views.maps.IlvCoordinateSystemProperty; import ilog.views.maps.srs.coordsys.IlvCoordinateSystem; import ilog.views.maps.srs.coordsys.IlvGeographicCoordinateSystem; import ilog.views.maps.srs.coordtrans.IlvCoordinateTransformation; import ilog.views.maps.srs.coordtrans.IlvCoordinateTransformationException; import ilog.views.sdm.IlvSDMEngine; import ilog.views.sdm.event.SDMModelAdapter; import ilog.views.sdm.event.SDMModelEvent; import ilog.views.sdm.event.SDMModelListener; import ilog.views.sdm.model.IlvDefaultSDMNode; import ilog.views.sdm.model.IlvSDMNode; import ilog.views.sdm.renderer.IlvStyleSheetRenderer; import ilog.views.util.IlvImageUtil; /** * Simulation engine class providing basic controls to choose simulation * parameters as wall as play, pause, stop the simulation. */ public class SimulationController extends JToolBar { final static String FOLLOW = "Follow"; //$NON-NLS-1$ private final IlvSDMEngine symbology; final JButton playPauseButton = new JButton(); final JButton stopButton = new JButton(); final JToggleButton followSymbolButton = new JToggleButton(); private boolean isRunning = false; private boolean needInit = true; final JComboBox<Integer> mobileCountCombo = new JComboBox<Integer>(new Integer[] { Integer.valueOf(10), Integer.valueOf(50), Integer.valueOf(100), Integer.valueOf(300), Integer.valueOf(1000), Integer.valueOf(1500) }); final JComboBox<String> speedCombo = new JComboBox<String>( new String[] { "x1", "x10", "x25", "x50", "x75", "x100", "x250" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ final JComboBox<Integer> fpsCombo = new JComboBox<Integer>( new Integer[] { Integer.valueOf(5), Integer.valueOf(10), Integer.valueOf(25), Integer.valueOf(50), Integer.valueOf(100) }); SimulationContext simulationContext; Icon playIcon; Icon pauseIcon; Icon stopIcon; Icon followSymbolIcon; final static int VEHICLE = 0; final static String VEHICLE_TAG = "Vehicle"; //$NON-NLS-1$ final static int BOAT = 1; final static String BOAT_TAG = "Boat"; //$NON-NLS-1$ final static int PLANE = 2; final static String PLANE_TAG = "Plane"; //$NON-NLS-1$ final static String TYPE_PROPERTY = "type"; //$NON-NLS-1$ FollowSymbolInteractor followInteractor = new FollowSymbolInteractor(); /** * Create the replay interface. * * @param symbology */ public SimulationController(IlvSDMEngine symbology) { createUserInterface(); this.symbology = symbology; SimulationBoxListener listener = new SimulationBoxListener(); // add the main action listener to various toolbar buttons playPauseButton.addActionListener(listener); stopButton.addActionListener(listener); followSymbolButton.addActionListener(listener); // JV-4254 InteractorListener interactorListener = new InteractorListener() { Override public void interactorChanged(InteractorChangedEvent event) { boolean isMyInteractor = (event.getNewValue() == followInteractor); if (followSymbolButton.isSelected() != isMyInteractor) { followSymbolButton.setSelected(isMyInteractor); } } }; symbology.getReferenceView().addInteractorListener(interactorListener); // add listener to model to keep the total number of mobiles symbology.getModel().addSDMModelListener(new SDMModelAdapter() { Override public void objectRemoved(SDMModelEvent event) { if (isRunning) { super.objectRemoved(event); // find type of object and spawn a mobile of the same category String tag = ((IlvSDMNode) event.getObject()).getTag(); if (VEHICLE_TAG.equals(tag)) { spawnMobile(VEHICLE, false); } else if (BOAT_TAG.equals(tag)) { spawnMobile(BOAT, false); } else if (PLANE_TAG.equals(tag)) { spawnMobile(PLANE, false); } } } }); } private void createUserInterface() { speedCombo.setSelectedIndex(0); speedCombo.setToolTipText("Choose simulation speed factor"); //$NON-NLS-1$ (should // be // internationalized) SimulationContext.setTimeAcceleration(Integer.parseInt(((String) speedCombo.getSelectedItem()).substring(1))); speedCombo.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent e) { int accel = Integer.parseInt(((String) speedCombo.getSelectedItem()).substring(1)); SimulationContext.setTimeAcceleration(accel); } }); fpsCombo.setSelectedIndex(0); fpsCombo.setToolTipText("Choose target refresh rate for the display"); //$NON-NLS-1$ (should // be // internationalized) SimulationContext.setTargetFPS(((Integer) fpsCombo.getSelectedItem()).intValue()); fpsCombo.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent e) { SimulationContext.setTargetFPS(((Integer) fpsCombo.getSelectedItem()).intValue()); } }); Dimension buttonDim = new Dimension(26, 26); Dimension comboDim = new Dimension(55, 22); configureComponentSize(playPauseButton, buttonDim); playPauseButton.setSelected(false); add(playPauseButton); configureComponentSize(stopButton, buttonDim); add(stopButton); configureComponentSize(followSymbolButton, buttonDim); add(followSymbolButton); addSeparator(); add(new JLabel("Mobiles ")); //$NON-NLS-1$ (should be internationalized) mobileCountCombo.setToolTipText("Choose number of mobiles"); //$NON-NLS-1$ (should // be // internationalized) mobileCountCombo.setSelectedIndex(1); configureComponentSize(mobileCountCombo, comboDim); add(mobileCountCombo); addSeparator(); add(new JLabel("Speed ")); //$NON-NLS-1$ (should be internationalized) configureComponentSize(speedCombo, comboDim); add(speedCombo); addSeparator(); add(new JLabel("Fps ")); //$NON-NLS-1$ (should be internationalized) configureComponentSize(fpsCombo, comboDim); add(fpsCombo); mobileCountCombo.setEnabled(true); // install icons playIcon = createIcon("right.gif"); //$NON-NLS-1$ pauseIcon = createIcon("pause.gif"); //$NON-NLS-1$ stopIcon = createIcon("stop.gif"); //$NON-NLS-1$ followSymbolIcon = createIcon("target.gif"); //$NON-NLS-1$ playPauseButton.setIcon(playIcon); playPauseButton.setToolTipText("Run simulation"); //$NON-NLS-1$ (should be // internationalized) stopButton.setIcon(stopIcon); stopButton.setToolTipText("Stop simulation"); //$NON-NLS-1$ (should be // internationalized) followSymbolButton.setIcon(followSymbolIcon); followSymbolButton.setToolTipText("Choose a symbol to follow"); //$NON-NLS-1$ (should // be // internationalized) } private void configureComponentSize(JComponent comp, Dimension buttonDim) { comp.setPreferredSize(buttonDim); comp.setMinimumSize(buttonDim); comp.setMaximumSize(buttonDim); } private Icon createIcon(String iconURL) { Image image = null; try { image = IlvImageUtil.getImageFromFile(SimulationController.class, iconURL); } catch (IOException e1) { e1.printStackTrace(); } if (image != null) { return new ImageIcon(image); } return null; } /** * * */ private void spawnMobile(int mobileType, boolean randomLocation) { Mobile mobile = null; switch (mobileType) { case VEHICLE: mobile = new PathConstrainedMobile(VEHICLE_TAG, symbology, SimulationContext.getRandomGroundRoad(), randomLocation); mobile.setHorizontalSpeed((SimulationContext.random() * 50 + 30) * 1000 / 3600.0); mobile.setVerticalSpeed(0); mobile.setSymbolProperty(TYPE_PROPERTY, "2", true); //$NON-NLS-1$ break; case BOAT: mobile = new PathConstrainedMobile(BOAT_TAG, symbology, SimulationContext.getRandomWaterRoad(), randomLocation); mobile.setHorizontalSpeed((SimulationContext.random() * 30 + 15) * 1000 / 3600.0); mobile.setVerticalSpeed(0); mobile.setSymbolProperty(TYPE_PROPERTY, "4", true); //$NON-NLS-1$ break; case PLANE: mobile = new FlyingMobile(PLANE_TAG, symbology, randomLocation); mobile.setHorizontalSpeed((SimulationContext.random() * 300 + 300) * 1000 / 3600.0); mobile.setVerticalSpeed(0); mobile.setSymbolProperty(TYPE_PROPERTY, "1", true); //$NON-NLS-1$ break; default: // unknown break; } } private final class SimulationBoxListener implements ActionListener { /** * Called when the button "Run simulation" is pressed. * * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ Override public void actionPerformed(ActionEvent e) { if (e.getSource() == playPauseButton) { isRunning = !isRunning; if (isRunning) { if (needInit) { Container c = playPauseButton.getTopLevelAncestor(); Cursor oc = null; if (c.isCursorSet()) { oc = c.getCursor(); } c.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); buildSimulation(); // set triple buffering IlvStyleSheetRenderer renderer = IlvStyleSheetRenderer.getInstance(symbology); symbology.getReferenceView().setTripleBufferedLayerCount(renderer.getNodesLayer()); // mobileProvider.start(); mobileCountCombo.setEnabled(false); needInit = false; c.setCursor(oc); } else { // resume simulation // if (mobileProvider != null) // // targetProvider.start(); Mobile.resume(); } playPauseButton.setToolTipText("Pause simulation"); //$NON-NLS-1$ (should // be // internationalized) playPauseButton.setIcon(pauseIcon); } else { playPauseButton.setToolTipText("Run simulation"); //$NON-NLS-1$ (should // be // internationalized) playPauseButton.setIcon(playIcon); // pause simulation isRunning = false; Mobile.pause(); } } else if (e.getSource() == stopButton) { Mobile.resume(); isRunning = false; stopSimu(); // stop triple buffering symbology.getReferenceView().setTripleBufferedLayerCount(0); mobileCountCombo.setEnabled(true); playPauseButton.setToolTipText("Run simulation"); //$NON-NLS-1$ (should // be // internationalized) playPauseButton.setIcon(playIcon); playPauseButton.setEnabled(true); Enumeration<?> roots = symbology.getModel().getObjects(); Vector<Object> v = new Vector<Object>(); while (roots.hasMoreElements()) { v.add(roots.nextElement()); } for (int i = 0; i < v.size(); i++) { symbology.getModel().removeObject(v.get(i)); } Mobile.allMobiles.clear(); Mobile.currentMobileTask = null; needInit = true; } else if (e.getSource() == followSymbolButton) { IlvManagerView view = symbology.getReferenceView(); if (view != null) { view.requestFocus(); if (view.getInteractor() == followInteractor) { view.popInteractor(); } else { view.pushInteractor(followInteractor); } } } } void stopSimu() { Mobile.stop(); } /** * * */ void buildSimulation() { SimulationContext.setSeed(124); int mobileCount = ((Integer) (mobileCountCombo.getSelectedItem())).intValue(); // boats. int boatCount = (int) (mobileCount * 0.20); int vehicleCount = (int) (mobileCount * 0.65); int planeCount = mobileCount - boatCount - vehicleCount; symbology.setAdjusting(true); for (int i = 0; i < boatCount; i++) { spawnMobile(BOAT, true); } for (int i = 0; i < vehicleCount; i++) { spawnMobile(VEHICLE, true); } for (int i = 0; i < planeCount; i++) { spawnMobile(PLANE, true); } symbology.setAdjusting(false); Mobile.start(); } } final static java.text.DecimalFormat NS = new java.text.DecimalFormat("0 mph"); //$NON-NLS-1$ final static java.text.DecimalFormat FS = new java.text.DecimalFormat("0 ft"); //$NON-NLS-1$ /** * @author eaullas */ private class FollowSymbolInteractor extends IlvManagerViewInteractor implements IlvPermanentInteractorInterface { // boolean permanent = false; /** To save cursor. */ Cursor oldCursor = null; /** The cursor for this interactor. */ Cursor myCursor = null; /** * Creates a new <code>FollowSymbolInteractor</code>. */ public FollowSymbolInteractor() { super(); enableEvents(AWTEvent.MOUSE_EVENT_MASK); myCursor = new Cursor(Cursor.CROSSHAIR_CURSOR); } Override public boolean isPermanent() { return false; } Override public void setPermanent(boolean permanent) { // this.permanent = permanent; } /** * This method is called when the interactor is attached to the specified * manager view. * * @param v * view to attach to. */ Override protected void attach(IlvManagerView v) { super.attach(v); if (!getCursor().equals(v.getCursor())) { oldCursor = v.getCursor(); v.setCursor(getCursor()); } } /** * This method is called when the interactor is detached from the specified * manager view. */ Override protected void detach() { if (oldCursor != null) { getManagerView().setCursor(oldCursor); oldCursor = null; } if (modelListener != null) { symbology.getModel().removeSDMModelListener(modelListener); modelListener = null; } modelListener = null; follow(null); super.detach(); } /** * @return Returns the cursor used for edition. */ public Cursor getCursor() { if (myCursor == null) { if (getManagerView() != null) return getManagerView().getCursor(); return Cursor.getDefaultCursor(); } return myCursor; } Override protected void processMouseEvent(MouseEvent e) { switch (e.getID()) { case MouseEvent.MOUSE_RELEASED: Object dataObject = symbology.getObject(new IlvPoint(e.getX(), e.getY()), getManagerView()); // if(e.getClickCount()>1){ // if(dataObject != null){ // symbPanel.editSelectedSymbol(); // } // } if (dataObject instanceof IlvDefaultSDMNode) { follow((IlvDefaultSDMNode) dataObject); } else { follow(null); } break; } super.processMouseEvent(e); } IlvDefaultSDMNode currentSymbol; SDMModelListener modelListener; void follow(IlvDefaultSDMNode symbol) { if (currentSymbol != null) { symbology.getModel().setObjectProperty(currentSymbol, FOLLOW, "0"); //$NON-NLS-1$ } currentSymbol = symbol; if (currentSymbol != null) { symbology.getModel().setObjectProperty(currentSymbol, FOLLOW, "1");//$NON-NLS-1$ } updateForCurrentSymbol(); if (modelListener == null) { modelListener = new SDMModelListener() { Override public void adjustmentFinished(SDMModelEvent event) { if (currentSymbol != null) { updateForCurrentSymbol(); } } Override public void dataChanged(SDMModelEvent event) { if (event.getObject() == currentSymbol) { updateForCurrentSymbol(); } } Override public void linkDestinationChanged(SDMModelEvent event) {/* ignore */ } Override public void linkSourceChanged(SDMModelEvent event) {/* ignore */ } Override public void objectAdded(SDMModelEvent event) {/* ignore */ } Override public void objectRemoved(SDMModelEvent event) { if (event.getObject() == currentSymbol) { currentSymbol = null; follow(null); } } }; symbology.getModel().addSDMModelListener(modelListener); } } void updateForCurrentSymbol() { Object dir = null; if (currentSymbol != null) { dir = currentSymbol.getProperty("DIRECTION_OF_MOVEMENT_INDICATOR"); //$NON-NLS-1$ } double direction = -90; if (dir != null) { direction = Double.parseDouble(dir.toString()); } IlvManagerView view = getManagerView(); IlvCoordinateSystem system = IlvCoordinateSystemProperty.GetCoordinateSystem(view.getManager()); IlvCoordinateTransformation tr = IlvCoordinateTransformation .CreateTransformation(IlvGeographicCoordinateSystem.KERNEL, system); IlvTransformer t = view.getTransformer(); if (currentSymbol != null) { IlvCoordinate c1 = new IlvCoordinate(Mobile.getLongitude(currentSymbol), -Mobile.getLatitude(currentSymbol)); if (tr != null) { try { tr.transform(c1, c1); } catch (IlvCoordinateTransformationException e) { e.printStackTrace(); } } IlvPoint c = new IlvPoint(c1.x, c1.y); t.apply(c); t.translate(view.getWidth() / 2 - c.x, view.getHeight() / 2 - c.y); } double angle = Math.toDegrees(Math.atan2(t.getx21(), t.getx11())); double dangle = -90 - direction - angle; t.rotate(view.getWidth() / 2, view.getHeight() / 2, dangle); // make sure that an almost 0 rotation is exactly 0 (performance is // better) if (Math.abs(t.getx12()) < 1E-4 && Math.abs(t.getx21()) < 1E-4) {// RAZ // Rotation // when // it is // very // small. t.setValues(t.getx11(), 0, 0, t.getx22()); } view.setTransformer(t); view.invalidateView(); view.repaint(); } } }