osd-contiki/tools/cooja/apps/mrm/java/se/sics/mrm/AreaViewer.java

2386 lines
89 KiB
Java

/*
* Copyright (c) 2008, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: AreaViewer.java,v 1.5 2009/02/18 12:08:10 fros4943 Exp $
*/
package se.sics.mrm;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import org.apache.log4j.Logger;
import org.jdom.Element;
import se.sics.cooja.*;
import se.sics.cooja.interfaces.*;
/**
* The class AreaViewer belongs to the MRM package.
*
* It is used to visualize available radios, traffic between them as well
* as the current radio propagation area of single radios.
* Users may also add background images (such as maps) and color-analyze them
* in order to add simulated obstacles in the radio medium.
*
* For more information about MRM see MRM.java
*
* @see MRM
* @author Fredrik Osterlind
*/
@ClassDescription("MRM - Area Viewer")
@PluginType(PluginType.SIM_PLUGIN)
public class AreaViewer extends VisPlugin {
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(AreaViewer.class);
private final JPanel canvas;
private final VisPlugin thisPlugin;
ChannelModel.TransmissionData dataTypeToVisualize = ChannelModel.TransmissionData.SIGNAL_STRENGTH;
ButtonGroup visTypeSelectionGroup;
// General drawing parameters
private Point lastHandledPosition = new Point(0,0);
private double zoomCenterX = 0.0;
private double zoomCenterY = 0.0;
private Point zoomCenterPoint = new Point();
private double currentZoomX = 1.0f;
private double currentZoomY = 1.0f;
private double currentPanX = 0.0f;
private double currentPanY = 0.0f;
private boolean drawBackgroundImage = true;
private boolean drawCalculatedObstacles = true;
private boolean drawChannelProbabilities = true;
private boolean drawRadios = true;
private boolean drawRadioActivity = true;
private boolean drawScaleArrow = true;
// Background drawing parameters (meters)
private double backgroundStartX = 0.0;
private double backgroundStartY = 0.0;
private double backgroundWidth = 0.0;
private double backgroundHeight = 0.0;
private Image backgroundImage = null;
private File backgroundImageFile = null;
// Obstacle drawing parameters (same scale as background)
private boolean needToRepaintObstacleImage = false;
private double obstacleStartX = 0.0;
private double obstacleStartY = 0.0;
private double obstacleWidth = 0.0;
private double obstacleHeight = 0.0;
private Image obstacleImage = null;
// Channel probabilities drawing parameters (meters)
private double channelStartX = 0.0;
private double channelStartY = 0.0;
private double channelWidth = 0.0;
private double channelHeight = 0.0;
private Image channelImage = null;
private JSlider resolutionSlider;
private JPanel controlPanel;
private JScrollPane scrollControlPanel;
private Simulation currentSimulation;
private MRM currentRadioMedium;
private ChannelModel currentChannelModel;
private final String antennaImageFilename = "antenna.png";
private final Image antennaImage;
private Radio selectedRadio = null;
private boolean inSelectMode = true;
private boolean inTrackMode = false;
private Vector<Line2D> trackedComponents = null;
// Coloring variables
private JPanel coloringIntervalPanel = null;
private double coloringHighest = 0;
private double coloringLowest = 0;
private boolean coloringIsFixed = true;
private Thread attenuatorThread = null;
private JCheckBox showSettingsBox;
private JCheckBox backgroundCheckBox;
private JCheckBox obstaclesCheckBox;
private JCheckBox channelCheckBox;
private JCheckBox radiosCheckBox;
private JCheckBox radioActivityCheckBox;
private JCheckBox arrowCheckBox;
private JRadioButton noneButton = null;
/**
* Initializes an AreaViewer.
*
* @param simulationToVisualize Simulation using MRM
*/
public AreaViewer(Simulation simulationToVisualize, GUI gui) {
super("MRM - Area Viewer", gui);
currentSimulation = simulationToVisualize;
currentRadioMedium = (MRM) currentSimulation.getRadioMedium();
currentChannelModel = currentRadioMedium.getChannelModel();
// We want to listen to changes both in the channel model as well as in the radio medium
currentChannelModel.addSettingsObserver(channelModelSettingsObserver);
currentRadioMedium.addSettingsObserver(radioMediumSettingsObserver);
currentRadioMedium.addRadioMediumObserver(radioMediumActivityObserver);
// Set initial size etc.
setSize(500, 500);
setVisible(true);
thisPlugin = this;
// Canvas mode radio buttons + show settings checkbox
showSettingsBox = new JCheckBox ("settings", true);
showSettingsBox.setAlignmentY(Component.TOP_ALIGNMENT);
showSettingsBox.setContentAreaFilled(false);
showSettingsBox.setActionCommand("toggle show settings");
showSettingsBox.addActionListener(canvasModeHandler);
JRadioButton selectModeButton = new JRadioButton ("select");
selectModeButton.setAlignmentY(Component.BOTTOM_ALIGNMENT);
selectModeButton.setContentAreaFilled(false);
selectModeButton.setActionCommand("set select mode");
selectModeButton.addActionListener(canvasModeHandler);
selectModeButton.setSelected(true);
JRadioButton panModeButton = new JRadioButton ("pan");
panModeButton.setAlignmentY(Component.BOTTOM_ALIGNMENT);
panModeButton.setContentAreaFilled(false);
panModeButton.setActionCommand("set pan mode");
panModeButton.addActionListener(canvasModeHandler);
JRadioButton zoomModeButton = new JRadioButton ("zoom");
zoomModeButton.setAlignmentY(Component.BOTTOM_ALIGNMENT);
zoomModeButton.setContentAreaFilled(false);
zoomModeButton.setActionCommand("set zoom mode");
zoomModeButton.addActionListener(canvasModeHandler);
JRadioButton trackModeButton = new JRadioButton ("track rays");
trackModeButton.setAlignmentY(Component.BOTTOM_ALIGNMENT);
trackModeButton.setContentAreaFilled(false);
trackModeButton.setActionCommand("set track rays mode");
trackModeButton.addActionListener(canvasModeHandler);
ButtonGroup group = new ButtonGroup();
group.add(selectModeButton);
group.add(panModeButton);
group.add(zoomModeButton);
group.add(trackModeButton);
// Create canvas
canvas = new JPanel() {
private static final long serialVersionUID = 1L;
public void paintComponent(Graphics g) {
super.paintComponent(g);
repaintCanvas((Graphics2D) g);
}
};
canvas.setBorder(BorderFactory.createLineBorder(Color.BLACK));
canvas.setBackground(Color.WHITE);
canvas.setLayout(new BorderLayout());
canvas.addMouseListener(canvasMouseHandler);
// Create canvas mode panel
JPanel canvasModePanel = new JPanel();
canvasModePanel.setOpaque(false);
canvasModePanel.setLayout(new BoxLayout(canvasModePanel, BoxLayout.Y_AXIS));
canvasModePanel.setAlignmentY(Component.BOTTOM_ALIGNMENT);
canvasModePanel.add(showSettingsBox);
canvasModePanel.add(Box.createVerticalGlue());
canvasModePanel.add(selectModeButton);
canvasModePanel.add(panModeButton);
canvasModePanel.add(zoomModeButton);
canvasModePanel.add(trackModeButton);
canvas.add(BorderLayout.EAST, canvasModePanel);
// Create control graphics panel
JPanel graphicsComponentsPanel = new JPanel();
graphicsComponentsPanel.setLayout(new BoxLayout(graphicsComponentsPanel, BoxLayout.Y_AXIS));
graphicsComponentsPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
graphicsComponentsPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
graphicsComponentsPanel.add(new JLabel("Show/Hide:"));
backgroundCheckBox = new JCheckBox("Background image", true);
backgroundCheckBox.setActionCommand("toggle background");
backgroundCheckBox.addActionListener(selectGraphicsHandler);
graphicsComponentsPanel.add(backgroundCheckBox);
obstaclesCheckBox = new JCheckBox("Obstacles", true);
obstaclesCheckBox.setActionCommand("toggle obstacles");
obstaclesCheckBox.addActionListener(selectGraphicsHandler);
graphicsComponentsPanel.add(obstaclesCheckBox);
channelCheckBox = new JCheckBox("Channel", true);
channelCheckBox.setActionCommand("toggle channel");
channelCheckBox.addActionListener(selectGraphicsHandler);
graphicsComponentsPanel.add(channelCheckBox);
radiosCheckBox = new JCheckBox("Radios", true);
radiosCheckBox.setActionCommand("toggle radios");
radiosCheckBox.addActionListener(selectGraphicsHandler);
graphicsComponentsPanel.add(radiosCheckBox);
radioActivityCheckBox = new JCheckBox("Radio Activity", true);
radioActivityCheckBox.setActionCommand("toggle radio activity");
radioActivityCheckBox.addActionListener(selectGraphicsHandler);
graphicsComponentsPanel.add(radioActivityCheckBox);
arrowCheckBox = new JCheckBox("Scale arrow", true);
arrowCheckBox.setActionCommand("toggle arrow");
arrowCheckBox.addActionListener(selectGraphicsHandler);
graphicsComponentsPanel.add(arrowCheckBox);
graphicsComponentsPanel.add(Box.createRigidArea(new Dimension(0,20)));
graphicsComponentsPanel.add(new JLabel("Obstacle configuration:"));
noneButton = new JRadioButton("No obstacles");
noneButton.setActionCommand("set no obstacles");
noneButton.addActionListener(obstacleHandler);
noneButton.setSelected(true);
JRadioButton pre1Button = new JRadioButton("Predefined 1");
pre1Button.setActionCommand("set predefined 1");
pre1Button.addActionListener(obstacleHandler);
JRadioButton pre2Button = new JRadioButton("Predefined 2");
pre2Button.setActionCommand("set predefined 2");
pre2Button.addActionListener(obstacleHandler);
JRadioButton customButton = new JRadioButton("From bitmap");
customButton.setActionCommand("set custom bitmap");
customButton.addActionListener(obstacleHandler);
if (GUI.isVisualizedInApplet()) {
customButton.setEnabled(false);
}
group = new ButtonGroup();
group.add(noneButton);
group.add(pre1Button);
group.add(pre2Button);
group.add(customButton);
graphicsComponentsPanel.add(noneButton);
graphicsComponentsPanel.add(pre1Button);
graphicsComponentsPanel.add(pre2Button);
graphicsComponentsPanel.add(customButton);
// Create visualize channel output panel
JPanel visualizeChannelPanel = new JPanel();
visualizeChannelPanel.setLayout(new BoxLayout(visualizeChannelPanel, BoxLayout.Y_AXIS));
visualizeChannelPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
visualizeChannelPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
// Channel coloring intervals
visualizeChannelPanel.add(new JLabel("Channel coloring:"));
JPanel fixedVsRelative = new JPanel(new GridLayout(1, 2));
JRadioButton fixedColoringButton = new JRadioButton("Fixed");
fixedColoringButton.setSelected(true);
fixedColoringButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
coloringIsFixed = true;
}
});
fixedVsRelative.add(fixedColoringButton);
JRadioButton relativeColoringButton = new JRadioButton("Relative");
relativeColoringButton.setSelected(true);
relativeColoringButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
coloringIsFixed = false;
}
});
fixedVsRelative.add(relativeColoringButton);
ButtonGroup coloringGroup = new ButtonGroup();
coloringGroup.add(fixedColoringButton);
coloringGroup.add(relativeColoringButton);
fixedVsRelative.setAlignmentX(Component.LEFT_ALIGNMENT);
visualizeChannelPanel.add(fixedVsRelative);
coloringIntervalPanel = new JPanel() {
public void paintComponent(Graphics g) {
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
double diff = coloringHighest - coloringLowest;
int textHeight = g.getFontMetrics().getHeight();
// If computing
if (attenuatorThread != null && attenuatorThread.isAlive()) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
g.setColor(Color.BLACK);
String stringToDraw = "[calculating]";
g.drawString(stringToDraw, width/2 - g.getFontMetrics().stringWidth(stringToDraw)/2, height/2 + textHeight/2);
return;
}
// Check for infinite values
if (Double.isInfinite(coloringHighest) || Double.isInfinite(coloringLowest)) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
g.setColor(Color.BLACK);
String stringToDraw = "INFINITE VALUES EXIST";
g.drawString(stringToDraw, width/2 - g.getFontMetrics().stringWidth(stringToDraw)/2, height/2 + textHeight/2);
return;
}
// Check if values are constant
if (diff == 0) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
g.setColor(Color.BLACK);
NumberFormat formatter = DecimalFormat.getNumberInstance();
String stringToDraw = "CONSTANT VALUES (" + formatter.format(coloringHighest) + ")";
g.drawString(stringToDraw, width/2 - g.getFontMetrics().stringWidth(stringToDraw)/2, height/2 + textHeight/2);
return;
}
for (int i=0; i < width; i++) {
double paintValue = coloringLowest + (double) i / (double) width * diff;
g.setColor(
new Color(
getColorOfSignalStrength(paintValue, coloringLowest, coloringHighest)));
g.drawLine(i, 0, i, height);
}
if (dataTypeToVisualize == ChannelModel.TransmissionData.PROB_OF_RECEPTION) {
NumberFormat formatter = DecimalFormat.getPercentInstance();
g.setColor(Color.BLACK);
g.drawString(formatter.format(coloringLowest), 3, textHeight);
String stringToDraw = formatter.format(coloringHighest);
g.drawString(stringToDraw, width - g.getFontMetrics().stringWidth(stringToDraw) - 3, textHeight);
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.SIGNAL_STRENGTH ||
dataTypeToVisualize == ChannelModel.TransmissionData.SIGNAL_STRENGTH_VAR ) {
NumberFormat formatter = DecimalFormat.getNumberInstance();
g.setColor(Color.BLACK);
g.drawString(formatter.format(coloringLowest) + "dBm", 3, textHeight);
String stringToDraw = formatter.format(coloringHighest) + "dBm";
g.drawString(stringToDraw, width - g.getFontMetrics().stringWidth(stringToDraw) - 3, textHeight);
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.SNR ||
dataTypeToVisualize == ChannelModel.TransmissionData.SNR_VAR) {
NumberFormat formatter = DecimalFormat.getNumberInstance();
g.setColor(Color.BLACK);
g.drawString(formatter.format(coloringLowest) + "dB", 3, textHeight);
String stringToDraw = formatter.format(coloringHighest) + "dB";
g.drawString(stringToDraw, width - g.getFontMetrics().stringWidth(stringToDraw) - 3, textHeight);
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.DELAY_SPREAD_RMS) {
NumberFormat formatter = DecimalFormat.getNumberInstance();
g.setColor(Color.BLACK);
g.drawString(formatter.format(coloringLowest) + "us", 3, textHeight);
String stringToDraw = formatter.format(coloringHighest) + "us";
g.drawString(stringToDraw, width - g.getFontMetrics().stringWidth(stringToDraw) - 3, textHeight);
}
}
};
coloringIntervalPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
Dimension colorPanelSize = new Dimension(200, 20);
coloringIntervalPanel.setPreferredSize(colorPanelSize);
coloringIntervalPanel.setMinimumSize(colorPanelSize);
coloringIntervalPanel.setMaximumSize(colorPanelSize);
coloringIntervalPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
visualizeChannelPanel.add(coloringIntervalPanel);
// Choose channel output to visualize
visualizeChannelPanel.add(Box.createRigidArea(new Dimension(0,20)));
visualizeChannelPanel.add(new JLabel("Visualize:"));
JRadioButton signalStrengthButton = new JRadioButton("Signal strength");
signalStrengthButton.setActionCommand("signalStrengthButton");
signalStrengthButton.setSelected(true);
signalStrengthButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dataTypeToVisualize = ChannelModel.TransmissionData.SIGNAL_STRENGTH;
}
});
visualizeChannelPanel.add(signalStrengthButton);
JRadioButton signalStrengthVarButton = new JRadioButton("Signal strength variance");
signalStrengthVarButton.setActionCommand("signalStrengthVarButton");
signalStrengthVarButton.setSelected(false);
signalStrengthVarButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dataTypeToVisualize = ChannelModel.TransmissionData.SIGNAL_STRENGTH_VAR;
}
});
visualizeChannelPanel.add(signalStrengthVarButton);
JRadioButton SNRButton = new JRadioButton("Signal to Noise ratio");
SNRButton.setActionCommand("SNRButton");
SNRButton.setSelected(false);
SNRButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dataTypeToVisualize = ChannelModel.TransmissionData.SNR;
}
});
visualizeChannelPanel.add(SNRButton);
JRadioButton SNRVarButton = new JRadioButton("Signal to Noise variance");
SNRVarButton.setActionCommand("SNRVarButton");
SNRVarButton.setSelected(false);
SNRVarButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dataTypeToVisualize = ChannelModel.TransmissionData.SNR_VAR;
}
});
visualizeChannelPanel.add(SNRVarButton);
JRadioButton probabilityButton = new JRadioButton("Probability of reception");
probabilityButton.setActionCommand("probabilityButton");
probabilityButton.setSelected(false);
probabilityButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dataTypeToVisualize = ChannelModel.TransmissionData.PROB_OF_RECEPTION;
}
});
visualizeChannelPanel.add(probabilityButton);
JRadioButton rmsDelaySpreadButton = new JRadioButton("RMS delay spread");
rmsDelaySpreadButton.setActionCommand("rmsDelaySpreadButton");
rmsDelaySpreadButton.setSelected(false);
rmsDelaySpreadButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dataTypeToVisualize = ChannelModel.TransmissionData.DELAY_SPREAD_RMS;
}
});
visualizeChannelPanel.add(rmsDelaySpreadButton);
visTypeSelectionGroup = new ButtonGroup();
visTypeSelectionGroup.add(signalStrengthButton);
visTypeSelectionGroup.add(signalStrengthVarButton);
visTypeSelectionGroup.add(SNRButton);
visTypeSelectionGroup.add(SNRVarButton);
visTypeSelectionGroup.add(probabilityButton);
visTypeSelectionGroup.add(rmsDelaySpreadButton);
visualizeChannelPanel.add(Box.createRigidArea(new Dimension(0,20)));
visualizeChannelPanel.add(new JLabel("Resolution:"));
resolutionSlider = new JSlider(JSlider.HORIZONTAL, 30, 600, 100);
resolutionSlider.setMajorTickSpacing(100);
resolutionSlider.setPaintTicks(true);
resolutionSlider.setPaintLabels(true);
resolutionSlider.setAlignmentX(Component.LEFT_ALIGNMENT);
visualizeChannelPanel.add(resolutionSlider);
visualizeChannelPanel.add(Box.createRigidArea(new Dimension(0,20)));
JButton recalculateVisibleButton = new JButton("Paint radio channel");
recalculateVisibleButton.setActionCommand("recalculate visible area");
recalculateVisibleButton.addActionListener(formulaHandler);
visualizeChannelPanel.add(recalculateVisibleButton);
// Create control panel
controlPanel = new JPanel();
controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS));
graphicsComponentsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
controlPanel.add(graphicsComponentsPanel);
controlPanel.add(new JSeparator());
controlPanel.add(Box.createRigidArea(new Dimension(0, 5)));
visualizeChannelPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
controlPanel.add(visualizeChannelPanel);
controlPanel.setPreferredSize(new Dimension(250,700));
controlPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
scrollControlPanel = new JScrollPane(
controlPanel,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollControlPanel.setPreferredSize(new Dimension(250,0));
// Add everything
this.setLayout(new BorderLayout());
this.add(BorderLayout.CENTER, canvas); // Add canvas
this.add(BorderLayout.EAST, scrollControlPanel);
// Load external images (antenna)
Toolkit toolkit = Toolkit.getDefaultToolkit();
URL imageURL = this.getClass().getClassLoader().getResource(antennaImageFilename);
antennaImage = toolkit.getImage(imageURL);
MediaTracker tracker = new MediaTracker(canvas);
tracker.addImage(antennaImage, 1);
try {
tracker.waitForAll();
} catch (InterruptedException ex) {
logger.fatal("Interrupted during image loading, aborting");
return;
}
// Try to select current plugin
try {
setSelected(true);
} catch (java.beans.PropertyVetoException e) {
// Could not select
}
}
/**
* Listens to mouse event on canvas
*/
private MouseListener canvasMouseHandler = new MouseListener() {
public void mouseReleased(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
if (inSelectMode) {
Vector<Radio> hitRadios = trackClickedRadio(e.getPoint());
if (hitRadios == null || hitRadios.size() == 0) {
if (e.getButton() != MouseEvent.BUTTON1) {
selectedRadio = null;
channelImage = null;
canvas.repaint();
}
return;
}
if (hitRadios.size() == 1 && hitRadios.firstElement() == selectedRadio) {
return;
}
if (selectedRadio == null || !hitRadios.contains(selectedRadio)) {
selectedRadio = hitRadios.firstElement();
} else {
// Select next in list
selectedRadio = hitRadios.get(
(hitRadios.indexOf(selectedRadio)+1) % hitRadios.size()
);
}
channelImage = null;
canvas.repaint();
} else if (inTrackMode && selectedRadio != null) {
// Calculate real clicked position
double realClickedX = e.getX() / currentZoomX - currentPanX;
double realClickedY = e.getY() / currentZoomY - currentPanY;
Position radioPosition = currentRadioMedium.getRadioPosition(selectedRadio);
final double radioX = radioPosition.getXCoordinate();
final double radioY = radioPosition.getYCoordinate();
trackedComponents = currentChannelModel.getRaysOfTransmission(radioX, radioY, realClickedX, realClickedY);
canvas.repaint();
}
}
public void mouseEntered(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
lastHandledPosition = new Point(e.getX(), e.getY());
// Set zoom center (real world)
zoomCenterX = e.getX() / currentZoomX - currentPanX;
zoomCenterY = e.getY() / currentZoomY - currentPanY;
zoomCenterPoint = e.getPoint();
}
};
/**
* Listens to mouse movements when in pan mode
*/
private MouseMotionListener canvasPanModeHandler = new MouseMotionListener() {
public void mouseMoved(MouseEvent e) {
}
public void mouseDragged(MouseEvent e) {
if (lastHandledPosition == null) {
lastHandledPosition = e.getPoint();
return;
}
// Pan relative to mouse movement and current zoom
// This way the mouse "lock" to the canvas
currentPanX += ((e.getX() - lastHandledPosition.x)) / currentZoomX;
currentPanY += ((e.getY() - lastHandledPosition.y)) / currentZoomY;
lastHandledPosition = e.getPoint();
canvas.repaint();
}
};
/**
* Listens to mouse movements when in zoom mode
*/
private MouseMotionListener canvasZoomModeHandler = new MouseMotionListener() {
public void mouseMoved(MouseEvent e) {
}
public void mouseDragged(MouseEvent e) {
if (lastHandledPosition == null) {
lastHandledPosition = e.getPoint();
return;
}
// Zoom relative to mouse movement (keep XY-proportions)
currentZoomY += 0.005 * currentZoomY * ((lastHandledPosition.y - e.getY()));
currentZoomY = Math.max(0.05, currentZoomY);
currentZoomY = Math.min(1500, currentZoomY);
currentZoomX = currentZoomY;
// We also need to update the current pan in order to zoom towards the mouse
currentPanX = zoomCenterPoint.x/currentZoomX - zoomCenterX;
currentPanY = zoomCenterPoint.y/currentZoomY - zoomCenterY;
lastHandledPosition = e.getPoint();
canvas.repaint();
}
};
/**
* Selects which mouse mode the canvas should be in (select/pan/zoom)
*/
private ActionListener canvasModeHandler = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("set select mode")) {
// Select mode, no mouse motion listener needed
for (MouseMotionListener reggedListener: canvas.getMouseMotionListeners()) {
canvas.removeMouseMotionListener(reggedListener);
}
inTrackMode = false;
inSelectMode = true;
} else if (e.getActionCommand().equals("set pan mode")) {
// Remove all other mouse motion listeners
for (MouseMotionListener reggedListener: canvas.getMouseMotionListeners()) {
canvas.removeMouseMotionListener(reggedListener);
}
inSelectMode = false;
inTrackMode = false;
// Add the special pan mouse motion listener
canvas.addMouseMotionListener(canvasPanModeHandler);
} else if (e.getActionCommand().equals("set zoom mode")) {
// Remove all other mouse motion listeners
for (MouseMotionListener reggedListener: canvas.getMouseMotionListeners()) {
canvas.removeMouseMotionListener(reggedListener);
}
inSelectMode = false;
inTrackMode = false;
// Add the special zoom mouse motion listener
canvas.addMouseMotionListener(canvasZoomModeHandler);
} else if (e.getActionCommand().equals("set track rays mode")) {
// Remove all other mouse motion listeners
for (MouseMotionListener reggedListener: canvas.getMouseMotionListeners()) {
canvas.removeMouseMotionListener(reggedListener);
}
inSelectMode = false;
inTrackMode = true;
} else if (e.getActionCommand().equals("toggle show settings")) {
if (((JCheckBox) e.getSource()).isSelected()) {
scrollControlPanel.setVisible(true);
} else {
scrollControlPanel.setVisible(false);
}
thisPlugin.invalidate();
thisPlugin.revalidate();
}
}
};
/**
* Selects which graphical parts should be painted
*/
private ActionListener selectGraphicsHandler = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("toggle background")) {
drawBackgroundImage = ((JCheckBox) e.getSource()).isSelected();
} else if (e.getActionCommand().equals("toggle obstacles")) {
drawCalculatedObstacles = ((JCheckBox) e.getSource()).isSelected();
} else if (e.getActionCommand().equals("toggle channel")) {
drawChannelProbabilities = ((JCheckBox) e.getSource()).isSelected();
} else if (e.getActionCommand().equals("toggle radios")) {
drawRadios = ((JCheckBox) e.getSource()).isSelected();
} else if (e.getActionCommand().equals("toggle radio activity")) {
drawRadioActivity = ((JCheckBox) e.getSource()).isSelected();
} else if (e.getActionCommand().equals("toggle arrow")) {
drawScaleArrow = ((JCheckBox) e.getSource()).isSelected();
}
canvas.repaint();
}
};
/**
* Helps user set a background image which can be analysed for obstacles/freespace.
*/
private ActionListener obstacleHandler = new ActionListener() {
/**
* Choosable file filter that supports tif, gif, jpg, png, bmp.
*/
class ImageFilter extends FileFilter {
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
String filename = f.getName();
if (filename == null) {
return false;
}
if (filename.endsWith(".gif") || filename.endsWith(".GIF") ||
filename.endsWith(".jpg") || filename.endsWith(".JPG") ||
filename.endsWith(".jpeg") || filename.endsWith(".JPEG") ||
filename.endsWith(".png") || filename.endsWith(".PNG")){
return true;
}
return false;
}
public String getDescription() {
return "All supported images";
}
}
class ImageSettingsDialog extends JDialog {
private double
virtualStartX = 0.0,
virtualStartY = 0.0,
virtualWidth = 0.0,
virtualHeight = 0.0;
private JFormattedTextField
virtualStartXField,
virtualStartYField,
virtualWidthField,
virtualHeightField;
private boolean terminatedOK = false;
private NumberFormat doubleFormat = NumberFormat.getNumberInstance();
/**
* Creates a new dialog for settings background parameters
*/
protected ImageSettingsDialog(File imageFile, Image image, Frame frame) {
super(frame, "Image settings");
setupDialog();
}
protected ImageSettingsDialog(File imageFile, Image image, Dialog dialog) {
super(dialog, "Image settings");
setupDialog();
}
protected ImageSettingsDialog(File imageFile, Image image, Window window) {
super(window, "Image settings");
setupDialog();
}
private void setupDialog() {
JPanel mainPanel, tempPanel;
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
doubleFormat.setMinimumIntegerDigits(1);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// Set layout and add components
mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
tempPanel = new JPanel(new GridLayout(1, 2));
tempPanel.add(new JLabel("Start X (m) "));
virtualStartXField = new JFormattedTextField(doubleFormat);
virtualStartXField.setValue(new Double(0.0));
tempPanel.add(virtualStartXField);
mainPanel.add(tempPanel);
tempPanel = new JPanel(new GridLayout(1, 2));
tempPanel.add(new JLabel("Start Y (m)"));
virtualStartYField = new JFormattedTextField(doubleFormat);
virtualStartYField.setValue(new Double(0.0));
tempPanel.add(virtualStartYField);
mainPanel.add(tempPanel);
tempPanel = new JPanel(new GridLayout(1, 2));
tempPanel.add(new JLabel("Width (m)"));
virtualWidthField = new JFormattedTextField(doubleFormat);
virtualWidthField.setValue(new Double(100.0));
tempPanel.add(virtualWidthField);
mainPanel.add(tempPanel);
tempPanel = new JPanel(new GridLayout(1, 2));
tempPanel.add(new JLabel("Height (m)"));
virtualHeightField = new JFormattedTextField(doubleFormat);
virtualHeightField.setValue(new Double(100.0));
tempPanel.add(virtualHeightField);
mainPanel.add(tempPanel);
mainPanel.add(Box.createVerticalGlue());
mainPanel.add(Box.createVerticalStrut(10));
tempPanel = new JPanel();
tempPanel.setLayout(new BoxLayout(tempPanel, BoxLayout.X_AXIS));
tempPanel.add(Box.createHorizontalGlue());
final JButton okButton = new JButton("OK");
this.getRootPane().setDefaultButton(okButton);
final JButton cancelButton = new JButton("Cancel");
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
virtualStartX = ((Number) virtualStartXField.getValue()).doubleValue();
virtualStartY = ((Number) virtualStartYField.getValue()).doubleValue();
virtualWidth = ((Number) virtualWidthField.getValue()).doubleValue();
virtualHeight = ((Number) virtualHeightField.getValue()).doubleValue();
terminatedOK = true;
dispose();
}
});
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
terminatedOK = false;
dispose();
}
});
tempPanel.add(okButton);
tempPanel.add(cancelButton);
mainPanel.add(tempPanel);
mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
add(mainPanel);
// Show dialog
setModal(true);
pack();
setLocationRelativeTo(this.getParent());
setVisible(true);
}
public boolean terminatedOK() {
return terminatedOK;
}
public double getVirtualStartX() {
return virtualStartX;
}
public double getVirtualStartY() {
return virtualStartY;
}
public double getVirtualWidth() {
return virtualWidth;
}
public double getVirtualHeight() {
return virtualHeight;
}
}
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("set custom bitmap")) {
if (!setCustomBitmap() || !analyzeBitmapForObstacles()) {
backgroundImage = null;
currentChannelModel.removeAllObstacles();
noneButton.setSelected(true);
repaint();
}
} else if (e.getActionCommand().equals("set predefined 1")) {
currentChannelModel.removeAllObstacles();
currentChannelModel.addRectObstacle(0, 0, 50, 5, false);
currentChannelModel.addRectObstacle(0, 5, 5, 50, false);
currentChannelModel.addRectObstacle(70, 0, 20, 5, false);
currentChannelModel.addRectObstacle(0, 70, 5, 20, false);
currentChannelModel.notifySettingsChanged();
repaint();
} else if (e.getActionCommand().equals("set predefined 2")) {
currentChannelModel.removeAllObstacles();
currentChannelModel.addRectObstacle(0, 0, 10, 10, false);
currentChannelModel.addRectObstacle(30, 0, 10, 10, false);
currentChannelModel.addRectObstacle(60, 0, 10, 10, false);
currentChannelModel.addRectObstacle(90, 0, 10, 10, false);
currentChannelModel.addRectObstacle(5, 90, 10, 10, false);
currentChannelModel.addRectObstacle(25, 90, 10, 10, false);
currentChannelModel.addRectObstacle(45, 90, 10, 10, false);
currentChannelModel.addRectObstacle(65, 90, 10, 10, false);
currentChannelModel.addRectObstacle(85, 90, 10, 10, false);
currentChannelModel.notifySettingsChanged();
repaint();
} else if (e.getActionCommand().equals("set no obstacles")) {
backgroundImage = null;
currentChannelModel.removeAllObstacles();
repaint();
} else {
logger.fatal("Unhandled action command: " + e.getActionCommand());
}
}
private boolean setCustomBitmap() {
/* Select image file */
JFileChooser fileChooser = new JFileChooser();
ImageFilter filter = new ImageFilter();
fileChooser.addChoosableFileFilter(filter);
int returnVal = fileChooser.showOpenDialog(canvas);
if (returnVal != JFileChooser.APPROVE_OPTION) {
return false;
}
File file = fileChooser.getSelectedFile();
if (!filter.accept(file)) {
logger.fatal("Non-supported file type, aborting");
return false;
}
logger.info("Opening '" + file.getAbsolutePath() + "'");
/* Load image data */
Toolkit toolkit = Toolkit.getDefaultToolkit();
Image image = toolkit.getImage(file.getAbsolutePath());
MediaTracker tracker = new MediaTracker(canvas);
tracker.addImage(image, 1);
try {
tracker.waitForAll();
if (tracker.isErrorAny() || image == null) {
logger.info("Error when loading '" + file.getAbsolutePath() + "'");
image = null;
return false;
}
} catch (InterruptedException ex) {
logger.fatal("Interrupted during image loading, aborting");
return false;
}
/* Set virtual size of image */
Container topParent = GUI.getTopParentContainer();
ImageSettingsDialog dialog;
if (topParent instanceof Frame) {
dialog = new ImageSettingsDialog(file, image, (Frame) topParent);
} else if (topParent instanceof Dialog) {
dialog = new ImageSettingsDialog(file, image, (Dialog) topParent);
} else if (topParent instanceof Window) {
dialog = new ImageSettingsDialog(file, image, (Window) topParent);
} else {
logger.fatal("Unknown parent container, aborting");
return false;
}
if (!dialog.terminatedOK()) {
logger.fatal("User canceled, aborting");
image = null;
return false;
}
/* Show loaded image */
backgroundStartX = dialog.getVirtualStartX();
backgroundStartY = dialog.getVirtualStartY();
backgroundWidth = dialog.getVirtualWidth();
backgroundHeight = dialog.getVirtualHeight();
backgroundImage = image;
backgroundImageFile = file;
return true;
}
private boolean analyzeBitmapForObstacles() {
if (backgroundImage == null) {
return false;
}
/* Show obstacle finder dialog */
ObstacleFinderDialog obstacleFinderDialog;
Container parentContainer = GUI.getTopParentContainer();
if (parentContainer instanceof Window) {
obstacleFinderDialog = new ObstacleFinderDialog(
backgroundImage, currentChannelModel, (Window) GUI.getTopParentContainer()
);
} else if (parentContainer instanceof Frame) {
obstacleFinderDialog = new ObstacleFinderDialog(
backgroundImage, currentChannelModel, (Frame) GUI.getTopParentContainer()
);
} else if (parentContainer instanceof Dialog) {
obstacleFinderDialog = new ObstacleFinderDialog(
backgroundImage, currentChannelModel, (Dialog) GUI.getTopParentContainer()
);
} else {
logger.fatal("Unknown parent container");
return false;
}
if (!obstacleFinderDialog.exitedOK) {
return false;
}
/* Register obstacles */
final boolean[][] obstacleArray = obstacleFinderDialog.obstacleArray;
final int boxSize = obstacleFinderDialog.sizeSlider.getValue();
// Create progress monitor
final ProgressMonitor pm = new ProgressMonitor(
GUI.getTopParentContainer(),
"Registering obstacles",
null,
0,
obstacleArray.length - 1
);
// Thread that will perform the work
final Runnable runnable = new Runnable() {
public void run() {
try {
/* Remove already existing obstacles */
currentChannelModel.removeAllObstacles();
int foundObstacles = 0;
for (int x=0; x < obstacleArray.length; x++) {
for (int y=0; y < (obstacleArray[0]).length; y++) {
if (obstacleArray[x][y]) {
/* Register obstacle */
double realWidth = (boxSize * backgroundWidth) / backgroundImage.getWidth(null);
double realHeight = (boxSize * backgroundHeight) / backgroundImage.getHeight(null);
double realStartX = backgroundStartX + x * realWidth;
double realStartY = backgroundStartY + y * realHeight;
foundObstacles++;
if (realStartX + realWidth > backgroundStartX + backgroundWidth) {
realWidth = backgroundStartX + backgroundWidth - realStartX;
}
if (realStartY + realHeight > backgroundStartY + backgroundHeight) {
realHeight = backgroundStartY + backgroundHeight - realStartY;
}
currentChannelModel.addRectObstacle(
realStartX, realStartY,
realWidth, realHeight,
false
);
}
}
/* Check if user has aborted */
if (pm.isCanceled()) {
return;
}
/* Update progress monitor */
pm.setProgress(x);
pm.setNote("After/Before merging: " +
currentChannelModel.getNumberOfObstacles() + "/" + foundObstacles);
}
currentChannelModel.notifySettingsChanged();
thisPlugin.repaint();
} catch (Exception ex) {
if (pm.isCanceled()) {
return;
}
logger.fatal("Obstacle adding exception: " + ex.getMessage());
ex.printStackTrace();
pm.close();
return;
}
pm.close();
}
};
/* Start thread */
Thread thread = new Thread(runnable);
thread.start();
return true;
}
};
class ObstacleFinderDialog extends JDialog {
private NumberFormat intFormat = NumberFormat.getIntegerInstance();
private BufferedImage imageToAnalyze = null;
private BufferedImage obstacleImage = null;
private JPanel canvasPanel = null;
private boolean[][] obstacleArray = null;
private boolean exitedOK = false;
private JSlider redSlider, greenSlider, blueSlider, toleranceSlider, sizeSlider;
/**
* Listens to preview mouse motion event (when picking color)
*/
private MouseMotionListener myMouseMotionListener = new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
}
public void mouseMoved(MouseEvent e) {
// Convert from mouse to image pixel position
Point pixelPoint = new Point(
(int) (e.getX() * (((double) imageToAnalyze.getWidth()) / ((double) canvasPanel.getWidth()))),
(int) (e.getY() * (((double) imageToAnalyze.getHeight()) / ((double) canvasPanel.getHeight())))
);
// Fetch color
int color = imageToAnalyze.getRGB(pixelPoint.x, pixelPoint.y);
int red = (color & 0x00ff0000) >> 16;
int green = (color & 0x0000ff00) >> 8;
int blue = color & 0x000000ff;
// Update sliders
redSlider.setValue(red);
redSlider.repaint();
greenSlider.setValue(green);
greenSlider.repaint();
blueSlider.setValue(blue);
blueSlider.repaint();
}
};
/**
* Listens to preview mouse event (when picking color)
*/
private MouseListener myMouseListener = new MouseListener() {
public void mouseClicked(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
// Stop picking color again; remove mouse listeners and reset mouse cursor
MouseListener[] allMouseListeners = canvasPanel.getMouseListeners();
for (MouseListener mouseListener: allMouseListeners) {
canvasPanel.removeMouseListener(mouseListener);
}
MouseMotionListener[] allMouseMotionListeners = canvasPanel.getMouseMotionListeners();
for (MouseMotionListener mouseMotionListener: allMouseMotionListeners) {
canvasPanel.removeMouseMotionListener(mouseMotionListener);
}
canvasPanel.setCursor(Cursor.getDefaultCursor());
}
};
/**
* Creates a new dialog for settings background parameters
*/
protected ObstacleFinderDialog(Image currentImage, ChannelModel currentChannelModel, Frame frame) {
super(frame, "Analyze for obstacles");
setupDialog(currentImage, currentChannelModel);
}
protected ObstacleFinderDialog(Image currentImage, ChannelModel currentChannelModel, Window window) {
super(window, "Analyze for obstacles");
setupDialog(currentImage, currentChannelModel);
}
protected ObstacleFinderDialog(Image currentImage, ChannelModel currentChannelModel, Dialog dialog) {
super(dialog, "Analyze for obstacles");
setupDialog(currentImage, currentChannelModel);
}
private void setupDialog(Image currentImage, ChannelModel currentChannelModel) {
JPanel mainPanel = new JPanel();
mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
JPanel tempPanel;
JLabel tempLabel;
JSlider tempSlider;
JButton tempButton;
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
Dimension labelDimension = new Dimension(100, 20);
// Convert Image to BufferedImage
imageToAnalyze = new BufferedImage(
currentImage.getWidth(this),
currentImage.getHeight(this),
BufferedImage.TYPE_INT_ARGB
);
Graphics2D g = imageToAnalyze.createGraphics();
g.drawImage(currentImage, 0, 0, null);
// Prepare initial obstacle image
obstacleImage = new BufferedImage(
currentImage.getWidth(this),
currentImage.getHeight(this),
BufferedImage.TYPE_INT_ARGB
);
// Set layout and add components
intFormat.setMinimumIntegerDigits(1);
intFormat.setMaximumIntegerDigits(3);
intFormat.setParseIntegerOnly(true);
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
// Obstacle color
tempPanel = new JPanel();
tempPanel.setLayout(new BoxLayout(tempPanel, BoxLayout.X_AXIS));
tempPanel.add(tempLabel = new JLabel("Obstacle"));
tempLabel.setPreferredSize(labelDimension);
mainPanel.add(tempPanel);
tempPanel = new JPanel();
tempPanel.setLayout(new BoxLayout(tempPanel, BoxLayout.X_AXIS));
tempPanel.add(tempLabel = new JLabel("Red"));
tempLabel.setPreferredSize(labelDimension);
tempLabel.setAlignmentY(Component.BOTTOM_ALIGNMENT);
tempPanel.add(tempSlider = new JSlider(JSlider.HORIZONTAL, 0, 255, 0));
tempSlider.setMajorTickSpacing(50);
tempSlider.setPaintTicks(true);
tempSlider.setPaintLabels(true);
mainPanel.add(tempPanel);
redSlider = tempSlider;
tempPanel = new JPanel();
tempPanel.setLayout(new BoxLayout(tempPanel, BoxLayout.X_AXIS));
tempPanel.add(tempLabel = new JLabel("Green"));
tempLabel.setPreferredSize(labelDimension);
tempLabel.setAlignmentY(Component.BOTTOM_ALIGNMENT);
tempPanel.add(tempSlider = new JSlider(JSlider.HORIZONTAL, 0, 255, 0));
tempSlider.setMajorTickSpacing(50);
tempSlider.setPaintTicks(true);
tempSlider.setPaintLabels(true);
mainPanel.add(tempPanel);
greenSlider = tempSlider;
tempPanel = new JPanel();
tempPanel.setLayout(new BoxLayout(tempPanel, BoxLayout.X_AXIS));
tempPanel.add(tempLabel = new JLabel("Blue"));
tempLabel.setPreferredSize(labelDimension);
tempLabel.setAlignmentY(Component.BOTTOM_ALIGNMENT);
tempPanel.add(tempSlider = new JSlider(JSlider.HORIZONTAL, 0, 255, 0));
tempSlider.setMajorTickSpacing(50);
tempSlider.setPaintTicks(true);
tempSlider.setPaintLabels(true);
mainPanel.add(tempPanel);
blueSlider = tempSlider;
// Tolerance
tempPanel = new JPanel();
tempPanel.setLayout(new BoxLayout(tempPanel, BoxLayout.X_AXIS));
tempPanel.add(tempLabel = new JLabel("Tolerance"));
tempLabel.setPreferredSize(labelDimension);
tempLabel.setAlignmentY(Component.BOTTOM_ALIGNMENT);
tempPanel.add(tempSlider = new JSlider(JSlider.HORIZONTAL, 0, 128, 0));
tempSlider.setMajorTickSpacing(25);
tempSlider.setPaintTicks(true);
tempSlider.setPaintLabels(true);
mainPanel.add(tempPanel);
toleranceSlider = tempSlider;
// Obstacle size
tempPanel = new JPanel();
tempPanel.setLayout(new BoxLayout(tempPanel, BoxLayout.X_AXIS));
tempPanel.add(tempLabel = new JLabel("Obstacle size"));
tempLabel.setPreferredSize(labelDimension);
tempLabel.setAlignmentY(Component.BOTTOM_ALIGNMENT);
tempPanel.add(tempSlider = new JSlider(JSlider.HORIZONTAL, 1, 40, 40));
tempSlider.setInverted(true);
tempSlider.setMajorTickSpacing(5);
tempSlider.setPaintTicks(true);
tempSlider.setPaintLabels(true);
mainPanel.add(tempPanel);
sizeSlider = tempSlider;
// Buttons: Pick color, Preview obstacles etc.
tempPanel = new JPanel();
tempPanel.setLayout(new BoxLayout(tempPanel, BoxLayout.X_AXIS));
tempPanel.add(Box.createHorizontalGlue());
tempPanel.add(tempButton = new JButton("Pick color"));
tempButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Set to color picker mode (if not already there)
if (canvasPanel.getMouseMotionListeners().length == 0) {
canvasPanel.addMouseListener(myMouseListener);
canvasPanel.addMouseMotionListener(myMouseMotionListener);
canvasPanel.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
}
}
});
tempPanel.add(Box.createHorizontalStrut(5));
tempPanel.add(tempButton = new JButton("Preview obstacles"));
tempButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
obstacleImage = createObstacleImage();
canvasPanel.repaint();
}
});
mainPanel.add(tempPanel);
mainPanel.add(Box.createVerticalStrut(10));
// Preview image
tempPanel = new JPanel() {
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(imageToAnalyze, 0, 0, getWidth(), getHeight(), this);
g.drawImage(obstacleImage, 0, 0, getWidth(), getHeight(), this);
}
};
tempPanel.setBorder(
BorderFactory.createTitledBorder(
BorderFactory.createLineBorder(Color.BLACK), "Preview"));
tempPanel.setPreferredSize(new Dimension(400, 400));
tempPanel.setBackground(Color.CYAN);
mainPanel.add(tempPanel);
canvasPanel = tempPanel; // Saved in canvasPanel
// Buttons: Cancel, OK
tempPanel = new JPanel();
tempPanel.setLayout(new BoxLayout(tempPanel, BoxLayout.X_AXIS));
tempPanel.add(Box.createHorizontalGlue());
tempPanel.add(tempButton = new JButton("Cancel"));
tempButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dispose();
}
});
tempPanel.add(Box.createHorizontalStrut(5));
tempPanel.add(tempButton = new JButton("OK"));
tempButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
obstacleImage = createObstacleImage();
exitedOK = true;
dispose();
}
});
mainPanel.add(tempPanel);
mainPanel.add(Box.createVerticalGlue());
mainPanel.add(Box.createVerticalStrut(10));
add(mainPanel);
// Show dialog
setModal(true);
pack();
setLocationRelativeTo(this.getParent());
/* Make sure dialog is not too big */
Rectangle maxSize = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
if (maxSize != null &&
(getSize().getWidth() > maxSize.getWidth() ||
getSize().getHeight() > maxSize.getHeight())) {
Dimension newSize = new Dimension();
newSize.height = Math.min((int) maxSize.getHeight(),
(int) getSize().getHeight());
newSize.width = Math.min((int) maxSize.getWidth(),
(int) getSize().getWidth());
setSize(newSize);
}
setVisible(true);
}
/**
* Create obstacle image by analyzing current background image
* and using the current obstacle color, size and tolerance.
* This method also creates the boolean array obstacleArray.
*
* @return New obstacle image
*/
private BufferedImage createObstacleImage() {
int nrObstacles = 0;
// Create new obstacle image all transparent (no obstacles)
BufferedImage newObstacleImage = new BufferedImage(
imageToAnalyze.getWidth(),
imageToAnalyze.getHeight(),
BufferedImage.TYPE_INT_ARGB
);
for (int x=0; x < imageToAnalyze.getWidth(); x++) {
for (int y=0; y < imageToAnalyze.getHeight(); y++) {
newObstacleImage.setRGB(x, y, 0x00000000);
}
}
// Get target color to match against
int targetRed = redSlider.getValue();
int targetGreen = greenSlider.getValue();
int targetBlue = blueSlider.getValue();
// Get obstacle resolution and size
int boxSize = sizeSlider.getValue();
int tolerance = toleranceSlider.getValue();
// Divide image into boxes and check each box for obstacles
int arrayWidth = (int) Math.ceil((double) imageToAnalyze.getWidth() / (double) boxSize);
int arrayHeight = (int) Math.ceil((double) imageToAnalyze.getHeight() / (double) boxSize);
obstacleArray = new boolean[arrayWidth][arrayHeight];
for (int x=0; x < imageToAnalyze.getWidth(); x+=boxSize) {
for (int y=0; y < imageToAnalyze.getHeight(); y+=boxSize) {
boolean boxIsObstacle = false;
// Check all pixels in box for obstacles
for (int xx=x; xx < x + boxSize && xx < imageToAnalyze.getWidth(); xx++) {
for (int yy=y; yy < y + boxSize && yy < imageToAnalyze.getHeight(); yy++) {
// Get current pixel color
int color = imageToAnalyze.getRGB(xx, yy);
int red = (color & 0x00ff0000) >> 16;
int green = (color & 0x0000ff00) >> 8;
int blue = color & 0x000000ff;
// Calculate difference from target color
int difference =
Math.abs(red - targetRed) +
Math.abs(green - targetGreen) +
Math.abs(blue - targetBlue);
// If difference is small enough make this box an obstacle
if (difference <= tolerance) {
boxIsObstacle = true;
break;
}
}
if (boxIsObstacle) {
break;
}
}
// If box is obstacle, colorize it
if (boxIsObstacle) {
obstacleArray[x/boxSize][y/boxSize] = true;
nrObstacles++;
// Colorize all pixels in the box
for (int xx=x; xx < x + boxSize && xx < imageToAnalyze.getWidth(); xx++) {
for (int yy=y; yy < y + boxSize && yy < imageToAnalyze.getHeight(); yy++) {
newObstacleImage.setRGB(xx, yy, 0x9922ff22);
}
}
} else {
obstacleArray[x/boxSize][y/boxSize] = false;
}
}
} // End of "divide into boxes" for-loop
return newObstacleImage;
}
}
/**
* Listens to settings changes in the radio medium.
*/
private Observer radioMediumSettingsObserver = new Observer() {
public void update(Observable obs, Object obj) {
// Clear selected radio (if any selected) and radio medium coverage
selectedRadio = null;
channelImage = null;
canvas.repaint();
}
};
/**
* Listens to settings changes in the radio medium.
*/
private Observer radioMediumActivityObserver = new Observer() {
public void update(Observable obs, Object obj) {
// Just remove any selected radio (it may have been removed)
canvas.repaint();
}
};
/**
* Listens to settings changes in the channel model.
*/
private Observer channelModelSettingsObserver = new Observer() {
public void update(Observable obs, Object obj) {
needToRepaintObstacleImage = true;
canvas.repaint();
}
};
/**
* Returns a color corresponding to given value where higher values are more green, and lower values are more red.
*
* @param value Signal strength of received signal (dB)
* @param lowBorder
* @param highBorder
* @return Integer containing standard ARGB color.
*/
private int getColorOfSignalStrength(double value, double lowBorder, double highBorder) {
double upperLimit = highBorder; // Best signal adds green
double lowerLimit = lowBorder; // Bad signal adds red
double intervalSize = (upperLimit - lowerLimit) / 2;
double middleValue = lowerLimit + (upperLimit - lowerLimit) / 2;
if (value > highBorder) {
return 0xCC00FF00;
}
if (value < lowerLimit) {
return 0xCCFF0000;
}
int red = 0, green = 0, blue = 0, alpha = 0xCC;
// Upper limit (green)
if (value > upperLimit - intervalSize) {
green = (int) (255 - 255*(upperLimit - value)/intervalSize);
}
// Medium signal adds blue
if (value > middleValue - intervalSize && value < middleValue + intervalSize) {
blue = (int) (255 - 255*Math.abs(middleValue - value)/intervalSize);
}
// Bad signal adds red
if (value < lowerLimit + intervalSize) {
red = (int) (255 - 255*(value - lowerLimit)/intervalSize);
}
return (alpha << 24) | (red << 16) | (green << 8) | blue;
}
/**
* Helps user adjust and calculate the channel propagation formula
*/
private ActionListener formulaHandler = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("recalculate visible area")) {
// Get resolution of new image
final Dimension resolution = new Dimension(
resolutionSlider.getValue(),
resolutionSlider.getValue()
);
// Abort if no radio selected
if (selectedRadio == null) {
channelImage = null;
canvas.repaint();
return;
}
// Get new location/size of area to attenuate
final double startX = -currentPanX;
final double startY = -currentPanY;
final double width = canvas.getWidth() / currentZoomX;
final double height = canvas.getHeight() / currentZoomY;
// Get sending radio position
Position radioPosition = currentRadioMedium.getRadioPosition(selectedRadio);
final double radioX = radioPosition.getXCoordinate();
final double radioY = radioPosition.getYCoordinate();
// Create temporary image
final BufferedImage tempChannelImage = new BufferedImage(resolution.width, resolution.height, BufferedImage.TYPE_INT_ARGB);
// Save time for later analysis
final long timeBeforeCalculating = System.currentTimeMillis();
// Create progress monitor
final ProgressMonitor pm = new ProgressMonitor(
GUI.getTopParentContainer(),
"Calculating channel attenuation",
null,
0,
resolution.width - 1
);
// Thread that will perform the work
final Runnable runnable = new Runnable() {
public void run() {
try {
// Available signal strength intervals
double lowestImageValue = Double.MAX_VALUE;
double highestImageValue = -Double.MAX_VALUE;
// Create image values (calculate each pixel)
double[][] imageValues = new double[resolution.width][resolution.height];
for (int x=0; x < resolution.width; x++) {
for (int y=0; y < resolution.height; y++) {
if (dataTypeToVisualize == ChannelModel.TransmissionData.SIGNAL_STRENGTH) {
// Attenuate
double[] signalStrength = currentChannelModel.getReceivedSignalStrength(
radioX,
radioY,
startX + width * x/resolution.width,
startY + height * y/resolution.height
);
// Collecting signal strengths
if (signalStrength[0] < lowestImageValue) {
lowestImageValue = signalStrength[0];
}
if (signalStrength[0] > highestImageValue) {
highestImageValue = signalStrength[0];
}
imageValues[x][y] = signalStrength[0];
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.SIGNAL_STRENGTH_VAR) {
// Attenuate
double[] signalStrength = currentChannelModel.getReceivedSignalStrength(
radioX,
radioY,
startX + width * x/resolution.width,
startY + height * y/resolution.height
);
// Collecting variances
if (signalStrength[1] < lowestImageValue) {
lowestImageValue = signalStrength[1];
}
if (signalStrength[1] > highestImageValue) {
highestImageValue = signalStrength[1];
}
imageValues[x][y] = signalStrength[1];
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.SNR) {
// Get signal to noise ratio
double[] snr = currentChannelModel.getSINR(
radioX,
radioY,
startX + width * x/resolution.width,
startY + height * y/resolution.height,
-Double.MAX_VALUE
);
// Collecting signal to noise ratio
if (snr[0] < lowestImageValue) {
lowestImageValue = snr[0];
}
if (snr[0] > highestImageValue) {
highestImageValue = snr[0];
}
imageValues[x][y] = snr[0];
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.SNR_VAR) {
// Get signal to noise ratio
double[] snr = currentChannelModel.getSINR(
radioX,
radioY,
startX + width * x/resolution.width,
startY + height * y/resolution.height,
-Double.MAX_VALUE
);
// Collecting variances
if (snr[1] < lowestImageValue) {
lowestImageValue = snr[1];
}
if (snr[1] > highestImageValue) {
highestImageValue = snr[1];
}
imageValues[x][y] = snr[1];
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.PROB_OF_RECEPTION) {
// Get probability of receiving a packet TODO What size? Does it matter?
double probability = currentChannelModel.getProbability(
radioX,
radioY,
startX + width * x/resolution.width,
startY + height * y/resolution.height,
-Double.MAX_VALUE
)[0];
// Collecting variances
if (probability < lowestImageValue) {
lowestImageValue = probability;
}
if (probability > highestImageValue) {
highestImageValue = probability;
}
imageValues[x][y] = probability;
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.DELAY_SPREAD_RMS) {
// Get RMS delay spread of receiving a packet
double rmsDelaySpread = currentChannelModel.getRMSDelaySpread(
radioX,
radioY,
startX + width * x/resolution.width,
startY + height * y/resolution.height
);
// Collecting variances
if (rmsDelaySpread < lowestImageValue) {
lowestImageValue = rmsDelaySpread;
}
if (rmsDelaySpread > highestImageValue) {
highestImageValue = rmsDelaySpread;
}
imageValues[x][y] = rmsDelaySpread;
}
// Check if the dialog has been canceled
if (pm.isCanceled()) {
return;
}
// Update progress
pm.setProgress(x);
}
}
// Adjust coloring signal strength limit
if (coloringIsFixed) {
if (dataTypeToVisualize == ChannelModel.TransmissionData.SIGNAL_STRENGTH) {
lowestImageValue = -100;
highestImageValue = 0;
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.SIGNAL_STRENGTH_VAR) {
lowestImageValue = 0;
highestImageValue = 20;
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.SNR) {
lowestImageValue = -10;
highestImageValue = 30;
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.SNR_VAR) {
lowestImageValue = 0;
highestImageValue = 20;
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.PROB_OF_RECEPTION) {
lowestImageValue = 0;
highestImageValue = 1;
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.DELAY_SPREAD_RMS) {
lowestImageValue = 0;
highestImageValue = 5;
}
}
// Save coloring high-low interval
coloringHighest = highestImageValue;
coloringLowest = lowestImageValue;
// Create image
for (int x=0; x < resolution.width; x++) {
for (int y=0; y < resolution.height; y++) {
tempChannelImage.setRGB(
x,
y,
getColorOfSignalStrength(imageValues[x][y], lowestImageValue, highestImageValue)
);
}
}
logger.info("Attenuating area done, time=" + (System.currentTimeMillis() - timeBeforeCalculating));
// Repaint to show the new channel propagation
channelStartX = startX;
channelStartY = startY;
channelWidth = width;
channelHeight = height;
channelImage = tempChannelImage;
thisPlugin.repaint();
coloringIntervalPanel.repaint();
} catch (Exception ex) {
if (pm.isCanceled()) {
return;
}
logger.fatal("Attenuation aborted: " + ex);
ex.printStackTrace();
pm.close();
}
pm.close();
}
};
// Start thread
attenuatorThread = new Thread(runnable);
attenuatorThread.start();
}
}
};
/**
* Repaint the canvas
* @param g2d Current graphics to paint on
*/
protected void repaintCanvas(Graphics2D g2d) {
AffineTransform originalTransform = g2d.getTransform();
// Create "real-world" transformation (scaled 100 times to reduce double->int rounding errors)
g2d.scale(currentZoomX, currentZoomY);
g2d.translate(currentPanX, currentPanY);
AffineTransform realWorldTransform = g2d.getTransform();
g2d.scale(0.01, 0.01);
AffineTransform realWorldTransformScaled = g2d.getTransform();
// -- Draw background image if any --
if (drawBackgroundImage && backgroundImage != null) {
g2d.setTransform(realWorldTransformScaled);
g2d.drawImage(backgroundImage,
(int) (backgroundStartX * 100.0),
(int) (backgroundStartY * 100.0),
(int) (backgroundWidth * 100.0),
(int) (backgroundHeight * 100.0),
this);
}
// -- Draw calculated obstacles --
if (drawCalculatedObstacles) {
// (Re)create obstacle image if needed
if (obstacleImage == null || needToRepaintObstacleImage) {
// Abort if no obstacles exist
if (currentChannelModel.getNumberOfObstacles() > 0) {
// Get bounds of obstacles
obstacleStartX = currentChannelModel.getObstacle(0).getMinX();
obstacleStartY = currentChannelModel.getObstacle(0).getMinY();
obstacleWidth = currentChannelModel.getObstacle(0).getMaxX();
obstacleHeight = currentChannelModel.getObstacle(0).getMaxY();
double tempVal = 0;
for (int i=0; i < currentChannelModel.getNumberOfObstacles(); i++) {
if ((tempVal = currentChannelModel.getObstacle(i).getMinX()) < obstacleStartX) {
obstacleStartX = tempVal;
}
if ((tempVal = currentChannelModel.getObstacle(i).getMinY()) < obstacleStartY) {
obstacleStartY = tempVal;
}
if ((tempVal = currentChannelModel.getObstacle(i).getMaxX()) > obstacleWidth) {
obstacleWidth = tempVal;
}
if ((tempVal = currentChannelModel.getObstacle(i).getMaxY()) > obstacleHeight) {
obstacleHeight = tempVal;
}
}
obstacleWidth -= obstacleStartX;
obstacleHeight -= obstacleStartY;
// Create new obstacle image
BufferedImage tempObstacleImage;
if (backgroundImage != null) {
tempObstacleImage = new BufferedImage(
Math.max(600, backgroundImage.getWidth(null)),
Math.max(600, backgroundImage.getHeight(null)),
BufferedImage.TYPE_INT_ARGB
);
} else {
tempObstacleImage = new BufferedImage(
600,
600,
BufferedImage.TYPE_INT_ARGB
);
}
Graphics2D obstacleGraphics = (Graphics2D) tempObstacleImage.getGraphics();
// Set real world transform
obstacleGraphics.scale(
tempObstacleImage.getWidth()/obstacleWidth,
tempObstacleImage.getHeight()/obstacleHeight
);
obstacleGraphics.translate(-obstacleStartX, -obstacleStartY);
// Paint all obstacles
obstacleGraphics.setColor(new Color(0, 0, 0, 128));
// DEBUG: Use random obstacle color to distinguish different obstacles
//Random random = new Random();
for (int i=0; i < currentChannelModel.getNumberOfObstacles(); i++) {
//obstacleGraphics.setColor((new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255), 128)));
obstacleGraphics.fill(currentChannelModel.getObstacle(i));
}
obstacleImage = tempObstacleImage;
} else {
// No obstacles exist - create dummy obstacle image
obstacleStartX = 0;
obstacleStartY = 0;
obstacleWidth = 1;
obstacleHeight = 1;
obstacleImage = new BufferedImage(
1,
1,
BufferedImage.TYPE_INT_ARGB
);
}
needToRepaintObstacleImage = false;
}
// Painting in real world coordinates
g2d.setTransform(realWorldTransformScaled);
g2d.drawImage(obstacleImage,
(int) (obstacleStartX * 100.0),
(int) (obstacleStartY * 100.0),
(int) (obstacleWidth * 100.0),
(int) (obstacleHeight * 100.0),
this);
}
// -- Draw channel probabilities if calculated --
if (drawChannelProbabilities && channelImage != null) {
g2d.setTransform(realWorldTransformScaled);
g2d.drawImage(channelImage,
(int) (channelStartX * 100.0),
(int) (channelStartY * 100.0),
(int) (channelWidth * 100.0),
(int) (channelHeight * 100.0),
this);
}
// -- Draw radios --
if (drawRadios) {
for (int i=0; i < currentRadioMedium.getRegisteredRadioCount(); i++) {
g2d.setStroke(new BasicStroke((float) 0.0));
g2d.setTransform(realWorldTransform);
// Translate to real world radio position
Radio radio = currentRadioMedium.getRegisteredRadio(i);
Position radioPosition = currentRadioMedium.getRadioPosition(radio);
g2d.translate(
radioPosition.getXCoordinate(),
radioPosition.getYCoordinate()
);
// Fetch current translation
double xPos = g2d.getTransform().getTranslateX();
double yPos = g2d.getTransform().getTranslateY();
// Jump to identity transform and paint without scaling
g2d.setTransform(new AffineTransform());
if (selectedRadio == radio) {
g2d.setColor(new Color(255, 0, 0, 100));
g2d.fillRect(
(int) xPos - antennaImage.getWidth(this)/2,
(int) yPos - antennaImage.getHeight(this)/2,
antennaImage.getWidth(this),
antennaImage.getHeight(this)
);
g2d.setColor(Color.BLUE);
g2d.drawRect(
(int) xPos - antennaImage.getWidth(this)/2,
(int) yPos - antennaImage.getHeight(this)/2,
antennaImage.getWidth(this),
antennaImage.getHeight(this)
);
}
g2d.drawImage(antennaImage, (int) xPos - antennaImage.getWidth(this)/2, (int) yPos - antennaImage.getHeight(this)/2, this);
}
}
// -- Draw radio activity --
if (drawRadioActivity) {
for (RadioConnection connection: currentRadioMedium.getActiveConnections()) {
Position sourcePosition = connection.getSource().getPosition();
// Paint scaled (otherwise bad rounding to integers may occur)
g2d.setTransform(realWorldTransformScaled);
g2d.setStroke(new BasicStroke((float) 0.0));
for (Radio receivingRadio: connection.getDestinations()) {
g2d.setColor(Color.GREEN);
// Get source and destination coordinates
Position destinationPosition = receivingRadio.getPosition();
g2d.draw(new Line2D.Double(
sourcePosition.getXCoordinate()*100.0,
sourcePosition.getYCoordinate()*100.0,
destinationPosition.getXCoordinate()*100.0,
destinationPosition.getYCoordinate()*100.0
));
}
for (Radio interferedRadio: connection.getInterfered()) {
g2d.setColor(Color.RED);
// Get source and destination coordinates
Position destinationPosition = interferedRadio.getPosition();
g2d.draw(new Line2D.Double(
sourcePosition.getXCoordinate()*100.0,
sourcePosition.getYCoordinate()*100.0,
destinationPosition.getXCoordinate()*100.0,
destinationPosition.getYCoordinate()*100.0
));
}
g2d.setColor(Color.BLUE);
g2d.setTransform(realWorldTransform);
g2d.translate(
sourcePosition.getXCoordinate(),
sourcePosition.getYCoordinate()
);
// Fetch current translation
double xPos = g2d.getTransform().getTranslateX();
double yPos = g2d.getTransform().getTranslateY();
// Jump to identity transform and paint without scaling
g2d.setTransform(new AffineTransform());
g2d.fillOval(
(int) xPos,
(int) yPos,
5,
5
);
}
}
// -- Draw scale arrow --
if (drawScaleArrow) {
g2d.setStroke(new BasicStroke((float) .0));
g2d.setColor(Color.BLACK);
// Decide on scale comparator
double currentArrowDistance = 0.1; // in meters
if (currentZoomX < canvas.getWidth() / 2) {
currentArrowDistance = 0.1; // 0.1m
}
if (currentZoomX < canvas.getWidth() / 2) {
currentArrowDistance = 1; // 1m
}
if (10 * currentZoomX < canvas.getWidth() / 2) {
currentArrowDistance = 10; // 10m
}
if (100 * currentZoomX < canvas.getWidth() / 2) {
currentArrowDistance = 100; // 100m
}
if (1000 * currentZoomX < canvas.getWidth() / 2) {
currentArrowDistance = 1000; // 100m
}
// "Arrow" points
int pixelArrowLength = (int) (currentArrowDistance * currentZoomX);
int xPoints[] = new int[] { -pixelArrowLength, -pixelArrowLength, -pixelArrowLength, 0, 0, 0 };
int yPoints[] = new int[] { -5, 5, 0, 0, -5, 5 };
// Paint arrow and text
g2d.setTransform(originalTransform);
g2d.translate(canvas.getWidth() - 120, canvas.getHeight() - 20);
g2d.drawString(currentArrowDistance + "m", -30, -10);
g2d.drawPolyline(xPoints, yPoints, xPoints.length);
}
// -- Draw tracked components (if any) --
if (inTrackMode && trackedComponents != null) {
g2d.setTransform(realWorldTransformScaled);
g2d.setStroke(new BasicStroke((float) 0.0));
Random random = new Random(); /* Do not use main random generator */
for (int i=0; i < trackedComponents.size(); i++) {
g2d.setColor(new Color(255, random.nextInt(255), random.nextInt(255), 255));
Line2D originalLine = trackedComponents.get(i);
Line2D newLine = new Line2D.Double(
originalLine.getX1()*100.0,
originalLine.getY1()*100.0,
originalLine.getX2()*100.0,
originalLine.getY2()*100.0
);
g2d.draw(newLine);
}
}
g2d.setTransform(originalTransform);
}
/**
* Tracks an on-screen position and returns all hit radios.
* May for example be used by a mouse listener to determine
* if user clicked on a radio.
*
* @param clickedPoint On-screen position
* @return All hit radios
*/
protected Vector<Radio> trackClickedRadio(Point clickedPoint) {
Vector<Radio> hitRadios = new Vector<Radio>();
if (currentRadioMedium.getRegisteredRadioCount() == 0) {
return null;
}
double realIconHalfWidth = antennaImage.getWidth(this) / (currentZoomX*2.0);
double realIconHalfHeight = antennaImage.getHeight(this) / (currentZoomY*2.0);
double realClickedX = clickedPoint.x / currentZoomX - currentPanX;
double realClickedY = clickedPoint.y / currentZoomY - currentPanY;
for (int i=0; i < currentRadioMedium.getRegisteredRadioCount(); i++) {
Radio testRadio = currentRadioMedium.getRegisteredRadio(i);
Position testPosition = currentRadioMedium.getRadioPosition(testRadio);
if (realClickedX > testPosition.getXCoordinate() - realIconHalfWidth &&
realClickedX < testPosition.getXCoordinate() + realIconHalfWidth &&
realClickedY > testPosition.getYCoordinate() - realIconHalfHeight &&
realClickedY < testPosition.getYCoordinate() + realIconHalfHeight) {
hitRadios.add(testRadio);
}
}
if (hitRadios.size() == 0) {
return null;
}
return hitRadios;
}
public void closePlugin() {
// Remove all our observers
if (currentChannelModel != null && channelModelSettingsObserver != null) {
currentChannelModel.deleteSettingsObserver(channelModelSettingsObserver);
} else {
logger.fatal("Could not remove observer: " + channelModelSettingsObserver);
}
if (currentRadioMedium != null && radioMediumSettingsObserver != null) {
currentRadioMedium.deleteSettingsObserver(radioMediumSettingsObserver);
} else {
logger.fatal("Could not remove observer: " + radioMediumSettingsObserver);
}
if (currentRadioMedium != null && radioMediumActivityObserver != null) {
currentRadioMedium.deleteRadioMediumObserver(radioMediumActivityObserver);
} else {
logger.fatal("Could not remove observer: " + radioMediumActivityObserver);
}
}
/**
* Returns XML elements representing the current configuration.
*
* @see #setConfigXML(Collection)
* @return XML element collection
*/
public Collection<Element> getConfigXML() {
Vector<Element> config = new Vector<Element>();
Element element;
// Controls visible
element = new Element("controls_visible");
element.setText(Boolean.toString(showSettingsBox.isSelected()));
config.add(element);
// Viewport
element = new Element("zoom_x");
element.setText(Double.toString(currentZoomX));
config.add(element);
element = new Element("zoom_y");
element.setText(Double.toString(currentZoomY));
config.add(element);
element = new Element("pan_x");
element.setText(Double.toString(currentPanX));
config.add(element);
element = new Element("pan_y");
element.setText(Double.toString(currentPanY));
config.add(element);
// Components shown
element = new Element("show_background");
element.setText(Boolean.toString(drawBackgroundImage));
config.add(element);
element = new Element("show_obstacles");
element.setText(Boolean.toString(drawCalculatedObstacles));
config.add(element);
element = new Element("show_channel");
element.setText(Boolean.toString(drawChannelProbabilities));
config.add(element);
element = new Element("show_radios");
element.setText(Boolean.toString(drawRadios));
config.add(element);
element = new Element("show_activity");
element.setText(Boolean.toString(drawRadioActivity));
config.add(element);
element = new Element("show_arrow");
element.setText(Boolean.toString(drawScaleArrow));
config.add(element);
// Visualization type
element = new Element("vis_type");
element.setText(visTypeSelectionGroup.getSelection().getActionCommand());
config.add(element);
// Background image
if (backgroundImageFile != null) {
element = new Element("background_image");
element.setText(backgroundImageFile.getPath());
config.add(element);
element = new Element("back_start_x");
element.setText(Double.toString(backgroundStartX));
config.add(element);
element = new Element("back_start_y");
element.setText(Double.toString(backgroundStartY));
config.add(element);
element = new Element("back_width");
element.setText(Double.toString(backgroundWidth));
config.add(element);
element = new Element("back_height");
element.setText(Double.toString(backgroundHeight));
config.add(element);
}
// Resolution
element = new Element("resolution");
element.setText(Integer.toString(resolutionSlider.getValue()));
config.add(element);
return config;
}
/**
* Sets the configuration depending on the given XML elements.
*
* @see #getConfigXML()
* @param configXML
* Config XML elements
* @return True if config was set successfully, false otherwise
*/
public boolean setConfigXML(Collection<Element> configXML, boolean visAvailable) {
for (Element element : configXML) {
if (element.getName().equals("controls_visible")) {
showSettingsBox.setSelected(Boolean.parseBoolean(element.getText()));
canvasModeHandler.actionPerformed(new ActionEvent(showSettingsBox,
ActionEvent.ACTION_PERFORMED, showSettingsBox.getActionCommand()));
} else if (element.getName().equals("zoom_x")) {
currentZoomX = Double.parseDouble(element.getText());
} else if (element.getName().equals("zoom_y")) {
currentZoomY = Double.parseDouble(element.getText());
} else if (element.getName().equals("pan_x")) {
currentPanX = Double.parseDouble(element.getText());
} else if (element.getName().equals("pan_y")) {
currentPanY = Double.parseDouble(element.getText());
} else if (element.getName().equals("show_background")) {
backgroundCheckBox.setSelected(Boolean.parseBoolean(element.getText()));
selectGraphicsHandler.actionPerformed(new ActionEvent(backgroundCheckBox,
ActionEvent.ACTION_PERFORMED, backgroundCheckBox.getActionCommand()));
} else if (element.getName().equals("show_obstacles")) {
obstaclesCheckBox.setSelected(Boolean.parseBoolean(element.getText()));
selectGraphicsHandler.actionPerformed(new ActionEvent(obstaclesCheckBox,
ActionEvent.ACTION_PERFORMED, obstaclesCheckBox.getActionCommand()));
} else if (element.getName().equals("show_channel")) {
channelCheckBox.setSelected(Boolean.parseBoolean(element.getText()));
selectGraphicsHandler.actionPerformed(new ActionEvent(channelCheckBox,
ActionEvent.ACTION_PERFORMED, channelCheckBox.getActionCommand()));
} else if (element.getName().equals("show_radios")) {
radiosCheckBox.setSelected(Boolean.parseBoolean(element.getText()));
selectGraphicsHandler.actionPerformed(new ActionEvent(radiosCheckBox,
ActionEvent.ACTION_PERFORMED, radiosCheckBox.getActionCommand()));
} else if (element.getName().equals("show_activity")) {
radioActivityCheckBox.setSelected(Boolean.parseBoolean(element.getText()));
selectGraphicsHandler.actionPerformed(new ActionEvent(radioActivityCheckBox,
ActionEvent.ACTION_PERFORMED, radioActivityCheckBox.getActionCommand()));
} else if (element.getName().equals("show_arrow")) {
arrowCheckBox.setSelected(Boolean.parseBoolean(element.getText()));
selectGraphicsHandler.actionPerformed(new ActionEvent(arrowCheckBox,
ActionEvent.ACTION_PERFORMED, arrowCheckBox.getActionCommand()));
} else if (element.getName().equals("vis_type")) {
String visTypeIdentifier = element.getText();
Enumeration<AbstractButton> buttonEnum = visTypeSelectionGroup.getElements();
while (buttonEnum.hasMoreElements()) {
AbstractButton button = buttonEnum.nextElement();
if (button.getActionCommand().equals(visTypeIdentifier)) {
visTypeSelectionGroup.setSelected(button.getModel(), true);
button.getActionListeners()[0]
.actionPerformed(new ActionEvent(button,
ActionEvent.ACTION_PERFORMED, button.getActionCommand()));
}
}
} else if (element.getName().equals("background_image")) {
backgroundImageFile = new File(element.getText());
if (backgroundImageFile.exists()) {
Toolkit toolkit = Toolkit.getDefaultToolkit();
backgroundImage = toolkit.getImage(backgroundImageFile.getAbsolutePath());
MediaTracker tracker = new MediaTracker(canvas);
tracker.addImage(backgroundImage, 1);
try {
tracker.waitForAll();
} catch (InterruptedException ex) {
logger.fatal("Interrupted during image loading, aborting");
backgroundImage = null;
}
}
} else if (element.getName().equals("back_start_x")) {
backgroundStartX = Double.parseDouble(element.getText());
} else if (element.getName().equals("back_start_y")) {
backgroundStartY = Double.parseDouble(element.getText());
} else if (element.getName().equals("back_width")) {
backgroundWidth = Double.parseDouble(element.getText());
} else if (element.getName().equals("back_height")) {
backgroundHeight = Double.parseDouble(element.getText());
} else if (element.getName().equals("resolution")) {
resolutionSlider.setValue(Integer.parseInt(element.getText()));
} else {
logger.fatal("Unknown configuration value: " + element.getName());
}
}
canvas.repaint();
return true;
}
}