visualizer plugin major update: support for zooming and panning, hiding window decorations, and a more powerful visualization skin api

This commit is contained in:
fros4943 2009-08-27 13:57:42 +00:00
parent b8e268e598
commit 23f13a2b6f
2 changed files with 440 additions and 198 deletions

View file

@ -26,12 +26,18 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. * SUCH DAMAGE.
* *
* $Id: Visualizer.java,v 1.9 2009/07/03 14:06:20 fros4943 Exp $ * $Id: Visualizer.java,v 1.10 2009/08/27 13:57:43 fros4943 Exp $
*/ */
package se.sics.cooja.plugins; package se.sics.cooja.plugins;
import java.awt.*; import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable; import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.datatransfer.UnsupportedFlavorException;
@ -41,26 +47,59 @@ import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener; import java.awt.dnd.DropTargetListener;
import java.awt.event.*; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Vector;
import javax.swing.*; import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.SwingUtilities;
import javax.swing.Timer; import javax.swing.Timer;
import javax.swing.plaf.basic.BasicInternalFrameUI;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.jdom.Element; import org.jdom.Element;
import se.sics.cooja.*; import se.sics.cooja.ClassDescription;
import se.sics.cooja.GUI;
import se.sics.cooja.Mote;
import se.sics.cooja.MoteInterface;
import se.sics.cooja.PluginType;
import se.sics.cooja.Simulation;
import se.sics.cooja.VisPlugin;
import se.sics.cooja.GUI.MoteRelation; import se.sics.cooja.GUI.MoteRelation;
import se.sics.cooja.SimEventCentral.MoteCountListener; import se.sics.cooja.SimEventCentral.MoteCountListener;
import se.sics.cooja.interfaces.*; import se.sics.cooja.interfaces.LED;
import se.sics.cooja.interfaces.Position;
import se.sics.cooja.interfaces.SerialPort;
import se.sics.cooja.plugins.skins.AddressVisualizerSkin; import se.sics.cooja.plugins.skins.AddressVisualizerSkin;
import se.sics.cooja.plugins.skins.GridVisualizerSkin;
import se.sics.cooja.plugins.skins.IDVisualizerSkin; import se.sics.cooja.plugins.skins.IDVisualizerSkin;
import se.sics.cooja.plugins.skins.LEDVisualizerSkin; import se.sics.cooja.plugins.skins.LEDVisualizerSkin;
import se.sics.cooja.plugins.skins.LogVisualizerSkin; import se.sics.cooja.plugins.skins.LogVisualizerSkin;
import se.sics.cooja.plugins.skins.PositionVisualizerSkin;
import se.sics.cooja.plugins.skins.TrafficVisualizerSkin;
import se.sics.cooja.plugins.skins.UDGMVisualizerSkin;
/** /**
* Simulation visualizer supporting visualization skins. * Simulation visualizer supporting visualization skins.
@ -79,29 +118,30 @@ import se.sics.cooja.plugins.skins.LogVisualizerSkin;
@ClassDescription("Simulation visualizer") @ClassDescription("Simulation visualizer")
@PluginType(PluginType.SIM_STANDARD_PLUGIN) @PluginType(PluginType.SIM_STANDARD_PLUGIN)
public class Visualizer extends VisPlugin { public class Visualizer extends VisPlugin {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(Visualizer.class); private static Logger logger = Logger.getLogger(Visualizer.class);
private static final int CANVAS_BORDER_WIDTH = 25;
public static final int MOTE_RADIUS = 8; public static final int MOTE_RADIUS = 8;
private static final Color[] DEFAULT_MOTE_COLORS = { Color.WHITE }; private static final Color[] DEFAULT_MOTE_COLORS = { Color.WHITE };
private GUI gui = null; private GUI gui = null;
private Simulation simulation = null; private Simulation simulation = null;
private final JPanel canvas; private final JPanel canvas;
/* Transformation */ /* Viewport */
private double factorXCoordToPixel; private AffineTransform viewportTransform;
private double factorYCoordToPixel; public int resetViewport = 0;
private double smallestXCoord;
private double smallestYCoord;
/* Moving motes */ /* Actions: move motes, pan view, and zoom view */
private boolean moteIsBeingMoved = false; private boolean panning = false;
private long moteMoveBeginTime = -1; private Position panningPosition = null; /* Panning start position */
private Mote moteToMove = null; private boolean zooming = false;
private double zoomStart = 0;
private Position zoomingPosition = null; /* Zooming center position */
private Point zoomingPixel = null; /* Zooming center pixel */
private boolean moving = false;
private Mote movedMote = null;
private long moveStartTime = -1;
private Cursor moveCursor = new Cursor(Cursor.MOVE_CURSOR); private Cursor moveCursor = new Cursor(Cursor.MOVE_CURSOR);
/* Visualizer skins */ /* Visualizer skins */
@ -114,6 +154,9 @@ public class Visualizer extends VisPlugin {
registerVisualizerSkin(AddressVisualizerSkin.class); registerVisualizerSkin(AddressVisualizerSkin.class);
registerVisualizerSkin(LogVisualizerSkin.class); registerVisualizerSkin(LogVisualizerSkin.class);
registerVisualizerSkin(LEDVisualizerSkin.class); registerVisualizerSkin(LEDVisualizerSkin.class);
registerVisualizerSkin(TrafficVisualizerSkin.class);
registerVisualizerSkin(PositionVisualizerSkin.class);
registerVisualizerSkin(GridVisualizerSkin.class);
} }
private ArrayList<VisualizerSkin> currentSkins = new ArrayList<VisualizerSkin>(); private ArrayList<VisualizerSkin> currentSkins = new ArrayList<VisualizerSkin>();
@ -127,14 +170,13 @@ public class Visualizer extends VisPlugin {
/* Popup menu */ /* Popup menu */
public static interface SimulationMenuAction { public static interface SimulationMenuAction {
public boolean isEnabled(Simulation simulation); public boolean isEnabled(Visualizer visualizer, Simulation simulation);
public String getDescription(Simulation simulation); public String getDescription(Visualizer visualizer, Simulation simulation);
public void doAction(Visualizer visualizer, Simulation simulation); public void doAction(Visualizer visualizer, Simulation simulation);
} }
public static interface MoteMenuAction { public static interface MoteMenuAction {
public boolean isEnabled(Mote mote); public boolean isEnabled(Visualizer visualizer, Mote mote);
public String getDescription(Mote mote); public String getDescription(Visualizer visualizer, Mote mote);
public void doAction(Visualizer visualizer, Mote mote); public void doAction(Visualizer visualizer, Mote mote);
} }
@ -153,14 +195,23 @@ public class Visualizer extends VisPlugin {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public void paintComponent(Graphics g) { public void paintComponent(Graphics g) {
super.paintComponent(g); super.paintComponent(g);
paintSkinGeneric(g);
if (resetViewport > 0) {
resetViewport();
resetViewport--;
}
for (VisualizerSkin skin: currentSkins) { for (VisualizerSkin skin: currentSkins) {
skin.paintSkin(g); skin.paintBeforeMotes(g);
}
paintMotes(g);
for (VisualizerSkin skin: currentSkins) {
skin.paintAfterMotes(g);
} }
} }
}; };
canvas.setBackground(Color.WHITE); canvas.setBackground(Color.WHITE);
calculateTransformations(); viewportTransform = new AffineTransform();
/* Skin selector */ /* Skin selector */
skinButton.addActionListener(new ActionListener() { skinButton.addActionListener(new ActionListener() {
@ -236,7 +287,6 @@ public class Visualizer extends VisPlugin {
/* Observe simulation and mote positions */ /* Observe simulation and mote positions */
posObserver = new Observer() { posObserver = new Observer() {
public void update(Observable obs, Object obj) { public void update(Observable obs, Object obj) {
calculateTransformations();
repaint(); repaint();
} }
}; };
@ -245,18 +295,21 @@ public class Visualizer extends VisPlugin {
Position pos = mote.getInterfaces().getPosition(); Position pos = mote.getInterfaces().getPosition();
if (pos != null) { if (pos != null) {
pos.addObserver(posObserver); pos.addObserver(posObserver);
} SwingUtilities.invokeLater(new Runnable() {
calculateTransformations(); public void run() {
resetViewport = 1;
repaint(); repaint();
} }
});
}
}
public void moteWasRemoved(Mote mote) { public void moteWasRemoved(Mote mote) {
Position pos = mote.getInterfaces().getPosition(); Position pos = mote.getInterfaces().getPosition();
if (pos != null) { if (pos != null) {
pos.deleteObserver(posObserver); pos.deleteObserver(posObserver);
}
calculateTransformations();
repaint(); repaint();
} }
}
}); });
for (Mote mote: simulation.getMotes()) { for (Mote mote: simulation.getMotes()) {
Position pos = mote.getInterfaces().getPosition(); Position pos = mote.getInterfaces().getPosition();
@ -264,8 +317,6 @@ public class Visualizer extends VisPlugin {
pos.addObserver(posObserver); pos.addObserver(posObserver);
} }
} }
calculateTransformations();
repaint();
/* Observe mote highlights */ /* Observe mote highlights */
gui.addMoteHighlightObserver(moteHighligtObserver = new Observer() { gui.addMoteHighlightObserver(moteHighligtObserver = new Observer() {
@ -310,49 +361,37 @@ public class Visualizer extends VisPlugin {
/* Popup menu */ /* Popup menu */
canvas.addMouseMotionListener(new MouseMotionListener() { canvas.addMouseMotionListener(new MouseMotionListener() {
public void mouseMoved(MouseEvent e) { public void mouseMoved(MouseEvent e) {
handleMoveRequest(e.getPoint().x, e.getPoint().y, false); handleMouseMove(e, false);
} }
public void mouseDragged(MouseEvent e) { public void mouseDragged(MouseEvent e) {
handleMoveRequest(e.getPoint().x, e.getPoint().y, false); handleMouseMove(e, false);
} }
}); });
canvas.addMouseListener(new MouseListener() { canvas.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) { public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) { if (e.isPopupTrigger()) {
handlePopupRequest(e.getPoint().x, e.getPoint().y); handlePopupRequest(e.getPoint().x, e.getPoint().y);
} else if (SwingUtilities.isLeftMouseButton(e)){ return;
//handleMoveRequest(e.getPoint().x, e.getPoint().y, false); }
beginMoveRequest(e.getPoint().x, e.getPoint().y);
if (SwingUtilities.isLeftMouseButton(e)){
handleMousePress(e);
} }
} }
public void mouseReleased(MouseEvent e) { public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) { if (e.isPopupTrigger()) {
handlePopupRequest(e.getPoint().x, e.getPoint().y); handlePopupRequest(e.getPoint().x, e.getPoint().y);
} else { return;
handleMoveRequest(e.getPoint().x, e.getPoint().y, true);
}
}
public void mouseEntered(MouseEvent e) {
if (e.isPopupTrigger()) {
handlePopupRequest(e.getPoint().x, e.getPoint().y);
}
}
public void mouseExited(MouseEvent e) {
if (e.isPopupTrigger()) {
handlePopupRequest(e.getPoint().x, e.getPoint().y);
} }
handleMouseMove(e, true);
} }
public void mouseClicked(MouseEvent e) { public void mouseClicked(MouseEvent e) {
if (e.isPopupTrigger()) { if (e.isPopupTrigger()) {
handlePopupRequest(e.getPoint().x, e.getPoint().y); handlePopupRequest(e.getPoint().x, e.getPoint().y);
} }
}
});
addComponentListener(new ComponentAdapter() { handleMouseMove(e, true);
public void componentResized(ComponentEvent ce) {
calculateTransformations();
repaint();
} }
}); });
@ -364,7 +403,8 @@ public class Visualizer extends VisPlugin {
registerMoteMenuAction(DeleteMoteMenuAction.class); registerMoteMenuAction(DeleteMoteMenuAction.class);
/* Register simulation menu actions */ /* Register simulation menu actions */
/* ... */ registerSimulationMenuAction(ResetViewportAction.class);
registerSimulationMenuAction(ToggleDecorationsMenuAction.class);
/* Drag and drop files to motes */ /* Drag and drop files to motes */
DropTargetListener dTargetListener = new DropTargetListener() { DropTargetListener dTargetListener = new DropTargetListener() {
@ -469,7 +509,7 @@ public class Visualizer extends VisPlugin {
this.setSize(300, 300); this.setSize(300, 300);
setLocation(gui.getDesktopPane().getWidth() - getWidth(), 0); setLocation(gui.getDesktopPane().getWidth() - getWidth(), 0);
setVisible(true); resetViewport = 3; /* XXX Quick-fix */
} }
private void generateAndActivateSkin(Class<? extends VisualizerSkin> skinClass) { private void generateAndActivateSkin(Class<? extends VisualizerSkin> skinClass) {
@ -548,9 +588,7 @@ public class Visualizer extends VisPlugin {
visualizerSkins.remove(skin); visualizerSkins.remove(skin);
} }
private void handlePopupRequest(final int x, final int y) { private void handlePopupRequest(final int x, final int y) {
JPopupMenu menu = new JPopupMenu(); JPopupMenu menu = new JPopupMenu();
menu.add(new JLabel("Select action:")); menu.add(new JLabel("Select action:"));
@ -565,8 +603,8 @@ public class Visualizer extends VisPlugin {
for (Class<? extends MoteMenuAction> menuActionClass: moteMenuActions) { for (Class<? extends MoteMenuAction> menuActionClass: moteMenuActions) {
try { try {
final MoteMenuAction menuAction = menuActionClass.newInstance(); final MoteMenuAction menuAction = menuActionClass.newInstance();
if (menuAction.isEnabled(mote)) { if (menuAction.isEnabled(this, mote)) {
JMenuItem menuItem = new JMenuItem(menuAction.getDescription(mote)); JMenuItem menuItem = new JMenuItem(menuAction.getDescription(this, mote));
menuItem.addActionListener(new ActionListener() { menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
menuAction.doAction(Visualizer.this, mote); menuAction.doAction(Visualizer.this, mote);
@ -581,7 +619,6 @@ public class Visualizer extends VisPlugin {
} }
} }
} }
} }
/* Simulation specific actions */ /* Simulation specific actions */
@ -589,8 +626,8 @@ public class Visualizer extends VisPlugin {
for (Class<? extends SimulationMenuAction> menuActionClass: simulationMenuActions) { for (Class<? extends SimulationMenuAction> menuActionClass: simulationMenuActions) {
try { try {
final SimulationMenuAction menuAction = menuActionClass.newInstance(); final SimulationMenuAction menuAction = menuActionClass.newInstance();
if (menuAction.isEnabled(simulation)) { if (menuAction.isEnabled(this, simulation)) {
JMenuItem menuItem = new JMenuItem(menuAction.getDescription(simulation)); JMenuItem menuItem = new JMenuItem(menuAction.getDescription(this, simulation));
menuItem.addActionListener(new ActionListener() { menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
menuAction.doAction(Visualizer.this, simulation); menuAction.doAction(Visualizer.this, simulation);
@ -613,57 +650,127 @@ public class Visualizer extends VisPlugin {
menu.setVisible(true); menu.setVisible(true);
} }
private void beginMoveRequest(final int x, final int y) { private void handleMousePress(MouseEvent mouseEvent) {
int x = mouseEvent.getPoint().x;
int y = mouseEvent.getPoint().y;
final Mote[] motes = findMotesAtPosition(x, y); final Mote[] motes = findMotesAtPosition(x, y);
if (motes == null || motes.length == 0) {
/* No motes clicked: We should either pan or zoom.
* Control or Shift pressed: We should either pan or zoom */
if (mouseEvent.isControlDown() || mouseEvent.isShiftDown()
|| motes == null || motes.length == 0) {
if (mouseEvent.isControlDown()) {
/* Zoom */
zooming = true;
zoomingPixel = new Point(x, y);
zoomingPosition = transformPixelToPosition(zoomingPixel);
zoomStart = viewportTransform.getScaleX();
} else {
/* Pan */
panning = true;
panningPosition = transformPixelToPosition(x, y);
}
return; return;
} }
moteMoveBeginTime = System.currentTimeMillis(); /* One of the clicked motes should be moved */
beginMoveRequest(motes[0]); beginMoveRequest(motes[0], true);
} }
private void beginMoveRequest(Mote moteToMove) { private void beginMoveRequest(Mote moteToMove, boolean withTiming) {
moteIsBeingMoved = true; if (withTiming) {
this.moteToMove = moteToMove; moveStartTime = System.currentTimeMillis();
} else {
moveStartTime = -1;
}
moving = true;
movedMote = moteToMove;
repaint(); repaint();
} }
private void handleMoveRequest(final int x, final int y, private void handleMouseMove(MouseEvent e, boolean stop) {
boolean wasJustReleased) { int x = e.getX();
int y = e.getY();
if (!moteIsBeingMoved) { /* Panning */
if (panning) {
if (panningPosition == null || stop) {
panning = false;
return; return;
} }
if (!wasJustReleased) { /* The current mouse position should correspond to where panning started */
// Still moving mote Position moved = transformPixelToPosition(x,y);
viewportTransform.translate(
moved.getXCoordinate() - panningPosition.getXCoordinate(),
moved.getYCoordinate() - panningPosition.getYCoordinate()
);
repaint();
return;
}
/* Zooming */
if (zooming) {
if (zoomingPosition == null || zoomingPixel == null || stop) {
zooming = false;
return;
}
/* The zooming start pixel should correspond to the zooming center position */
/* The current mouse position should correspond to where panning started */
double zoomFactor = 1.0 + Math.abs((double) zoomingPixel.y - y)/100.0;
double newZoom = (zoomingPixel.y - y)>0?zoomStart*zoomFactor: zoomStart/zoomFactor;
if (newZoom < 0.00001) {
newZoom = 0.00001;
}
viewportTransform.setToScale(
newZoom,
newZoom
);
Position moved = transformPixelToPosition(zoomingPixel);
viewportTransform.translate(
moved.getXCoordinate() - zoomingPosition.getXCoordinate(),
moved.getYCoordinate() - zoomingPosition.getYCoordinate()
);
repaint();
return;
}
/* Moving */
if (moving) {
if (!stop) {
canvas.setCursor(moveCursor); canvas.setCursor(moveCursor);
return; return;
} }
// Stopped moving mote /* Restore cursor */
canvas.setCursor(Cursor.getDefaultCursor()); canvas.setCursor(Cursor.getDefaultCursor());
moteIsBeingMoved = false;
Position newXYValues = transformPixelToPositon(new Point(x, y)); /* Move mote */
Position newPos = transformPixelToPosition(x, y);
if (moteMoveBeginTime <= 0 || System.currentTimeMillis() - moteMoveBeginTime > 300) { if (moveStartTime < 0 || System.currentTimeMillis() - moveStartTime > 300) {
int returnValue = JOptionPane.showConfirmDialog(Visualizer.this, "Move mote to" String options[] = {"Yes", "Cancel"};
+ "\nX=" + newXYValues.getXCoordinate() + "\nY=" int returnValue = JOptionPane.showOptionDialog(Visualizer.this,
+ newXYValues.getYCoordinate() + "\nZ=" "Move mote to" +
+ moteToMove.getInterfaces().getPosition().getZCoordinate()); "\nX=" + newPos.getXCoordinate() +
"\nY=" + newPos.getYCoordinate() +
if (returnValue == JOptionPane.OK_OPTION) { "\nZ=" + movedMote.getInterfaces().getPosition().getZCoordinate(),
moteToMove.getInterfaces().getPosition().setCoordinates( "Move mote?",
newXYValues.getXCoordinate(), newXYValues.getYCoordinate(), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,
moteToMove.getInterfaces().getPosition().getZCoordinate()); null, options, options[0]);
if (returnValue == JOptionPane.YES_OPTION) {
movedMote.getInterfaces().getPosition().setCoordinates(
newPos.getXCoordinate(),
newPos.getYCoordinate(),
movedMote.getInterfaces().getPosition().getZCoordinate()
);
} }
} }
moteMoveBeginTime = -1;
moteToMove = null; moving = false;
repaint(); repaint();
} }
}
/** /**
* Returns all motes at given position. * Returns all motes at given position.
@ -675,16 +782,16 @@ public class Visualizer extends VisPlugin {
* @return All motes at given position * @return All motes at given position
*/ */
public Mote[] findMotesAtPosition(int clickedX, int clickedY) { public Mote[] findMotesAtPosition(int clickedX, int clickedY) {
double xCoord = factorXPixelToCoord(clickedX); double xCoord = transformToPositionX(clickedX);
double yCoord = factorYPixelToCoord(clickedY); double yCoord = transformToPositionY(clickedY);
ArrayList<Mote> motes = new ArrayList<Mote>(); ArrayList<Mote> motes = new ArrayList<Mote>();
// Calculate painted mote radius in coordinates // Calculate painted mote radius in coordinates
double paintedMoteWidth = factorXPixelToCoord(MOTE_RADIUS) double paintedMoteWidth = transformToPositionX(MOTE_RADIUS)
- factorXPixelToCoord(0); - transformToPositionX(0);
double paintedMoteHeight = factorYPixelToCoord(MOTE_RADIUS) double paintedMoteHeight = transformToPositionY(MOTE_RADIUS)
- factorYPixelToCoord(0); - transformToPositionY(0);
for (int i = 0; i < simulation.getMotesCount(); i++) { for (int i = 0; i < simulation.getMotesCount(); i++) {
Position pos = simulation.getMote(i).getInterfaces().getPosition(); Position pos = simulation.getMote(i).getInterfaces().getPosition();
@ -707,8 +814,9 @@ public class Visualizer extends VisPlugin {
return motes.toArray(motesArr); return motes.toArray(motesArr);
} }
public void paintSkinGeneric(Graphics g) { public void paintMotes(Graphics g) {
Mote[] allMotes = simulation.getMotes(); Mote[] allMotes = simulation.getMotes();
for (Mote mote: allMotes) { for (Mote mote: allMotes) {
/* Use the first skin's non-null mote colors */ /* Use the first skin's non-null mote colors */
@ -733,8 +841,8 @@ public class Visualizer extends VisPlugin {
g.setColor(HIGHLIGHT_COLOR); g.setColor(HIGHLIGHT_COLOR);
g.fillOval(x - MOTE_RADIUS, y - MOTE_RADIUS, 2 * MOTE_RADIUS, g.fillOval(x - MOTE_RADIUS, y - MOTE_RADIUS, 2 * MOTE_RADIUS,
2 * MOTE_RADIUS); 2 * MOTE_RADIUS);
} else if (mote == moteToMove) { } else if (mote == movedMote) {
// Don't fill mote /* No fill */
} else if (moteColors.length >= 2) { } else if (moteColors.length >= 2) {
g.setColor(moteColors[0]); g.setColor(moteColors[0]);
g.fillOval(x - MOTE_RADIUS, y - MOTE_RADIUS, 2 * MOTE_RADIUS, g.fillOval(x - MOTE_RADIUS, y - MOTE_RADIUS, 2 * MOTE_RADIUS,
@ -779,55 +887,70 @@ public class Visualizer extends VisPlugin {
} }
} }
/** /**
* Recalculate size of canvas and factors for transforming between real and * Reset transform to show all motes.
* pixel coordinates. This method is called every time this frame is resized
* or created.
*/ */
protected void calculateTransformations() { protected void resetViewport() {
if (simulation.getMotesCount() == 0) { Mote[] motes = simulation.getMotes();
smallestXCoord = 0; if (motes.length == 0) {
smallestYCoord = 0; /* No motes */
factorXCoordToPixel = 1; viewportTransform.setToIdentity();
factorYCoordToPixel = 1;
return; return;
} }
double biggestXCoord, biggestYCoord; final double BORDER_SCALE_FACTOR = 1.1;
double smallX, bigX, smallY, bigY, scaleX, scaleY;
Position pos = simulation.getMote(0).getInterfaces().getPosition(); /* Init values */
smallestXCoord = biggestXCoord = pos.getXCoordinate(); {
smallestYCoord = biggestYCoord = pos.getYCoordinate(); Position pos = motes[0].getInterfaces().getPosition();
smallX = bigX = pos.getXCoordinate();
smallY = bigY = pos.getYCoordinate();
}
// Get extreme coordinates /* Extremes */
Mote[] motes = simulation.getMotes();
for (Mote mote: motes) { for (Mote mote: motes) {
pos = mote.getInterfaces().getPosition(); Position pos = mote.getInterfaces().getPosition();
smallX = Math.min(smallX, pos.getXCoordinate());
smallestXCoord = Math.min(smallestXCoord, pos.getXCoordinate()); bigX = Math.max(bigX, pos.getXCoordinate());
smallestYCoord = Math.min(smallestYCoord, pos.getYCoordinate()); smallY = Math.min(smallY, pos.getYCoordinate());
biggestXCoord = Math.max(biggestXCoord, pos.getXCoordinate()); bigY = Math.max(bigY, pos.getYCoordinate());
biggestYCoord = Math.max(biggestYCoord, pos.getYCoordinate());
} }
if ((biggestXCoord - smallestXCoord) == 0) { /* Scale viewport */
factorXCoordToPixel = 1; if (smallX == bigX) {
scaleX = 1;
} else { } else {
factorXCoordToPixel = ((double) scaleX = (bigX - smallX) / (double) (canvas.getWidth());
canvas.getSize().width - 2 }
* CANVAS_BORDER_WIDTH) if (smallY == bigY) {
/ (biggestXCoord - smallestXCoord); scaleY = 1;
} else {
scaleY = (bigY - smallY) / (double) (canvas.getHeight());
} }
if ((biggestYCoord - smallestYCoord) == 0) { viewportTransform.setToIdentity();
factorYCoordToPixel = 1; double newZoom = (1.0/(BORDER_SCALE_FACTOR*Math.max(scaleX, scaleY)));
} else { viewportTransform.setToScale(
factorYCoordToPixel = ((double) newZoom,
canvas.getSize().height - 2 newZoom
* CANVAS_BORDER_WIDTH) );
/ (biggestYCoord - smallestYCoord);
/* Center visible motes */
final double smallXfinal = smallX, bigXfinal = bigX, smallYfinal = smallY, bigYfinal = bigY;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Position viewMid =
transformPixelToPosition(canvas.getWidth()/2, canvas.getHeight()/2);
double motesMidX = (smallXfinal + bigXfinal) / 2.0;
double motesMidY = (smallYfinal + bigYfinal) / 2.0;
viewportTransform.translate(
viewMid.getXCoordinate() - motesMidX,
viewMid.getYCoordinate() - motesMidY);
canvas.repaint();
} }
});
} }
/** /**
@ -839,8 +962,11 @@ public class Visualizer extends VisPlugin {
* @return Pixel coordinates * @return Pixel coordinates
*/ */
public Point transformPositionToPixel(Position pos) { public Point transformPositionToPixel(Position pos) {
return new Point(factorXCoordToPixel(pos.getXCoordinate()), return transformPositionToPixel(
factorYCoordToPixel(pos.getYCoordinate())); pos.getXCoordinate(),
pos.getYCoordinate(),
pos.getZCoordinate()
);
} }
/** /**
@ -853,7 +979,14 @@ public class Visualizer extends VisPlugin {
* @return Pixel coordinates * @return Pixel coordinates
*/ */
public Point transformPositionToPixel(double x, double y, double z) { public Point transformPositionToPixel(double x, double y, double z) {
return new Point(factorXCoordToPixel(x), factorYCoordToPixel(y)); return new Point(transformToPixelX(x), transformToPixelY(y));
}
/**
* @return Canvas
*/
public JPanel getCurrentCanvas() {
return canvas;
} }
/** /**
@ -863,31 +996,30 @@ public class Visualizer extends VisPlugin {
* On-screen pixel coordinate * On-screen pixel coordinate
* @return Real world coordinate (z=0). * @return Real world coordinate (z=0).
*/ */
public Position transformPixelToPositon(Point pixelPos) { public Position transformPixelToPosition(Point pixelPos) {
Position dummyPosition = new Position(null); return transformPixelToPosition(pixelPos.x, pixelPos.y);
dummyPosition.setCoordinates(factorXPixelToCoord(pixelPos.x), }
factorYPixelToCoord(pixelPos.y), 0.0); public Position transformPixelToPosition(int x, int y) {
return dummyPosition; Position position = new Position(null);
position.setCoordinates(
transformToPositionX(x),
transformToPositionY(y),
0.0
);
return position;
} }
/** private int transformToPixelX(double x) {
* @return The current canvas to paint on return (int) (viewportTransform.getScaleX()*x + viewportTransform.getTranslateX());
*/
public JPanel getCurrentCanvas() {
return canvas;
} }
private int transformToPixelY(double y) {
private int factorXCoordToPixel(double xCoordinate) { return (int) (viewportTransform.getScaleY()*y + viewportTransform.getTranslateY());
return (int) ((xCoordinate - smallestXCoord) * factorXCoordToPixel + CANVAS_BORDER_WIDTH);
} }
private int factorYCoordToPixel(double yCoordinate) { private double transformToPositionX(int x) {
return (int) ((yCoordinate - smallestYCoord) * factorYCoordToPixel) + CANVAS_BORDER_WIDTH; return (x - viewportTransform.getTranslateX())/viewportTransform.getScaleX() ;
} }
private double factorXPixelToCoord(int xPixel) { private double transformToPositionY(int y) {
return ((xPixel - CANVAS_BORDER_WIDTH) / factorXCoordToPixel) + smallestXCoord; return (y - viewportTransform.getTranslateY())/viewportTransform.getScaleY() ;
}
private double factorYPixelToCoord(int yPixel) {
return ((yPixel - CANVAS_BORDER_WIDTH) / factorYCoordToPixel) + smallestYCoord;
} }
public void closePlugin() { public void closePlugin() {
@ -923,9 +1055,32 @@ public class Visualizer extends VisPlugin {
Vector<Element> config = new Vector<Element>(); Vector<Element> config = new Vector<Element>();
Element element; Element element;
/* Skins */
for (VisualizerSkin skin: currentSkins) { for (VisualizerSkin skin: currentSkins) {
element = new Element("skin"); element = new Element("skin");
element.setText(GUI.getDescriptionOf(skin.getClass())); element.setText(skin.getClass().getName());
config.add(element);
}
/* Viewport */
element = new Element("viewport");
double[] matrix = new double[6];
viewportTransform.getMatrix(matrix);
element.setText(
matrix[0] + " " +
matrix[1] + " " +
matrix[2] + " " +
matrix[3] + " " +
matrix[4] + " " +
matrix[5]
);
config.add(element);
/* Hide decorations */
BasicInternalFrameUI ui = (BasicInternalFrameUI) getUI();
if (ui.getNorthPane().getPreferredSize() == null ||
ui.getNorthPane().getPreferredSize().height == 0) {
element = new Element("hidden");
config.add(element); config.add(element);
} }
@ -937,7 +1092,9 @@ public class Visualizer extends VisPlugin {
if (element.getName().equals("skin")) { if (element.getName().equals("skin")) {
String wanted = element.getText(); String wanted = element.getText();
for (Class<? extends VisualizerSkin> skinClass: visualizerSkins) { for (Class<? extends VisualizerSkin> skinClass: visualizerSkins) {
if (wanted.equals(GUI.getDescriptionOf(skinClass))) { if (wanted.equals(skinClass.getName())
/* Backwards compatibility */
|| wanted.equals(GUI.getDescriptionOf(skinClass))) {
final Class<? extends VisualizerSkin> skin = skinClass; final Class<? extends VisualizerSkin> skin = skinClass;
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
public void run() { public void run() {
@ -951,17 +1108,37 @@ public class Visualizer extends VisPlugin {
if (wanted != null) { if (wanted != null) {
logger.warn("Could not load skin: " + element.getText()); logger.warn("Could not load skin: " + element.getText());
} }
} else if (element.getName().equals("viewport")) {
try {
String[] matrix = element.getText().split(" ");
viewportTransform.setTransform(
Double.parseDouble(matrix[0]),
Double.parseDouble(matrix[1]),
Double.parseDouble(matrix[2]),
Double.parseDouble(matrix[3]),
Double.parseDouble(matrix[4]),
Double.parseDouble(matrix[5])
);
resetViewport = 0;
} catch (Exception e) {
logger.warn("Bad viewport: " + e.getMessage());
resetViewport();
}
} else if (element.getName().equals("hidden")) {
BasicInternalFrameUI ui = (BasicInternalFrameUI) getUI();
ui.getNorthPane().setPreferredSize(new Dimension(0,0));
skinButton.setVisible(false);
} }
} }
return true; return true;
} }
protected static class ButtonClickMoteMenuAction implements MoteMenuAction { protected static class ButtonClickMoteMenuAction implements MoteMenuAction {
public boolean isEnabled(Mote mote) { public boolean isEnabled(Visualizer visualizer, Mote mote) {
return mote.getInterfaces().getButton() != null return mote.getInterfaces().getButton() != null
&& !mote.getInterfaces().getButton().isPressed(); && !mote.getInterfaces().getButton().isPressed();
} }
public String getDescription(Mote mote) { public String getDescription(Visualizer visualizer, Mote mote) {
return "Click button on " + mote; return "Click button on " + mote;
} }
public void doAction(Visualizer visualizer, Mote mote) { public void doAction(Visualizer visualizer, Mote mote) {
@ -970,10 +1147,10 @@ public class Visualizer extends VisPlugin {
}; };
protected static class DeleteMoteMenuAction implements MoteMenuAction { protected static class DeleteMoteMenuAction implements MoteMenuAction {
public boolean isEnabled(Mote mote) { public boolean isEnabled(Visualizer visualizer, Mote mote) {
return true; return true;
} }
public String getDescription(Mote mote) { public String getDescription(Visualizer visualizer, Mote mote) {
return "Delete " + mote; return "Delete " + mote;
} }
public void doAction(Visualizer visualizer, Mote mote) { public void doAction(Visualizer visualizer, Mote mote) {
@ -982,10 +1159,10 @@ public class Visualizer extends VisPlugin {
}; };
protected static class ShowLEDMoteMenuAction implements MoteMenuAction { protected static class ShowLEDMoteMenuAction implements MoteMenuAction {
public boolean isEnabled(Mote mote) { public boolean isEnabled(Visualizer visualizer, Mote mote) {
return mote.getInterfaces().getLED() != null; return mote.getInterfaces().getLED() != null;
} }
public String getDescription(Mote mote) { public String getDescription(Visualizer visualizer, Mote mote) {
return "Show LEDs on " + mote; return "Show LEDs on " + mote;
} }
public void doAction(Visualizer visualizer, Mote mote) { public void doAction(Visualizer visualizer, Mote mote) {
@ -1013,7 +1190,7 @@ public class Visualizer extends VisPlugin {
}; };
protected static class ShowSerialMoteMenuAction implements MoteMenuAction { protected static class ShowSerialMoteMenuAction implements MoteMenuAction {
public boolean isEnabled(Mote mote) { public boolean isEnabled(Visualizer visualizer, Mote mote) {
for (MoteInterface intf: mote.getInterfaces().getInterfaces()) { for (MoteInterface intf: mote.getInterfaces().getInterfaces()) {
if (intf instanceof SerialPort) { if (intf instanceof SerialPort) {
return true; return true;
@ -1021,7 +1198,7 @@ public class Visualizer extends VisPlugin {
} }
return false; return false;
} }
public String getDescription(Mote mote) { public String getDescription(Visualizer visualizer, Mote mote) {
return "Show serial port on " + mote; return "Show serial port on " + mote;
} }
public void doAction(Visualizer visualizer, Mote mote) { public void doAction(Visualizer visualizer, Mote mote) {
@ -1056,15 +1233,71 @@ public class Visualizer extends VisPlugin {
}; };
protected static class MoveMoteMenuAction implements MoteMenuAction { protected static class MoveMoteMenuAction implements MoteMenuAction {
public boolean isEnabled(Mote mote) { public boolean isEnabled(Visualizer visualizer, Mote mote) {
return true; return true;
} }
public String getDescription(Mote mote) { public String getDescription(Visualizer visualizer, Mote mote) {
return "Move " + mote; return "Move " + mote;
} }
public void doAction(Visualizer visualizer, Mote mote) { public void doAction(Visualizer visualizer, Mote mote) {
visualizer.moteMoveBeginTime = -1; visualizer.beginMoveRequest(mote, false);
visualizer.beginMoveRequest(mote); }
};
protected static class ResetViewportAction implements SimulationMenuAction {
public void doAction(Visualizer visualizer, Simulation simulation) {
visualizer.resetViewport = 1;
visualizer.repaint();
}
public String getDescription(Visualizer visualizer, Simulation simulation) {
return "Reset viewport";
}
public boolean isEnabled(Visualizer visualizer, Simulation simulation) {
return true;
}
};
protected static class ToggleDecorationsMenuAction implements SimulationMenuAction {
public void doAction(final Visualizer visualizer, Simulation simulation) {
if (!(visualizer.getUI() instanceof BasicInternalFrameUI)) {
return;
}
BasicInternalFrameUI ui = (BasicInternalFrameUI) visualizer.getUI();
if (ui.getNorthPane().getPreferredSize() == null ||
ui.getNorthPane().getPreferredSize().height == 0) {
/* Restore window decorations */
ui.getNorthPane().setPreferredSize(null);
visualizer.skinButton.setVisible(true);
} else {
/* Hide window decorations */
ui.getNorthPane().setPreferredSize(new Dimension(0,0));
visualizer.skinButton.setVisible(false);
}
visualizer.revalidate();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
visualizer.repaint();
}
});
}
public String getDescription(Visualizer visualizer, Simulation simulation) {
if (!(visualizer.getUI() instanceof BasicInternalFrameUI)) {
return "Hide window decorations";
}
BasicInternalFrameUI ui = (BasicInternalFrameUI) visualizer.getUI();
if (ui.getNorthPane().getPreferredSize() == null ||
ui.getNorthPane().getPreferredSize().height == 0) {
return "Restore window decorations";
}
return "Hide window decorations";
}
public boolean isEnabled(Visualizer visualizer, Simulation simulation) {
if (!(visualizer.getUI() instanceof BasicInternalFrameUI)) {
return false;
}
return true;
} }
}; };
} }

View file

@ -26,7 +26,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. * SUCH DAMAGE.
* *
* $Id: VisualizerSkin.java,v 1.2 2009/04/14 15:40:26 fros4943 Exp $ * $Id: VisualizerSkin.java,v 1.3 2009/08/27 13:57:42 fros4943 Exp $
*/ */
package se.sics.cooja.plugins; package se.sics.cooja.plugins;
@ -70,7 +70,7 @@ public interface VisualizerSkin {
* Used by skin generic visualization. * Used by skin generic visualization.
* May return a single color, or two colors. * May return a single color, or two colors.
* *
* @see Visualizer#paintSkinGeneric(Graphics) * @see Visualizer#paintMotes(Graphics)
* *
* @param mote Mote * @param mote Mote
* @return Color[] { Inner color, Outer color } * @return Color[] { Inner color, Outer color }
@ -78,11 +78,20 @@ public interface VisualizerSkin {
public Color[] getColorOf(Mote mote); public Color[] getColorOf(Mote mote);
/** /**
* Called when plugin is repainted on the currently active skin. * Called every time the visualizer plugin is repainted.
* Paints graphics that should be underneath the motes.
* *
* @param g Graphics * @param g Graphics
*/ */
public void paintSkin(Graphics g); public void paintBeforeMotes(Graphics g);
/**
* Called every time the visualizer plugin is repainted.
* Paints graphics that should be on top of the motes.
*
* @param g Graphics
*/
public void paintAfterMotes(Graphics g);
/** /**
* @return Visualizer plugin where this skin is showing * @return Visualizer plugin where this skin is showing