[cooja] plugins/Visualizer: Multi-mote selection functionality
This adds multi-mote selection capabilities to the visualizer plugin: - [Ctrl + Mouse Drag]: Rectangular selection of multiple motes - [Ctrl + Mouse Click]: Add/Remove motes from current selection - Mouse Drag on any selected Mote: Move all currently selected motes Note: This changes previous behaviour of using Ctrl key.
This commit is contained in:
parent
a7dbf46b8e
commit
22ac769781
|
@ -33,6 +33,7 @@ import java.awt.Color;
|
||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
@ -72,20 +73,23 @@ public class MRMVisualizerSkin implements VisualizerSkin {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color[] getColorOf(Mote mote) {
|
public Color[] getColorOf(Mote mote) {
|
||||||
Mote selectedMote = visualizer.getSelectedMote();
|
if (visualizer.getSelectedMotes().contains(mote)) {
|
||||||
if (mote == selectedMote) {
|
|
||||||
return new Color[] { Color.CYAN };
|
return new Color[] { Color.CYAN };
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void paintBeforeMotes(Graphics g) {
|
public void paintBeforeMotes(Graphics g) {
|
||||||
final Mote selectedMote = visualizer.getSelectedMote();
|
Set<Mote> selectedMotes = visualizer.getSelectedMotes();
|
||||||
if (simulation == null
|
if (simulation == null || selectedMotes == null || selectedMotes.isEmpty()) {
|
||||||
|| selectedMote == null
|
|
||||||
|| selectedMote.getInterfaces().getRadio() == null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Mote selectedMote = visualizer.getSelectedMotes().iterator().next();
|
||||||
|
if (selectedMote.getInterfaces().getRadio() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final Position sPos = selectedMote.getInterfaces().getPosition();
|
final Position sPos = selectedMote.getInterfaces().getPosition();
|
||||||
|
|
||||||
/* Paint transmission and interference range for selected mote */
|
/* Paint transmission and interference range for selected mote */
|
||||||
|
|
|
@ -30,10 +30,12 @@
|
||||||
|
|
||||||
package org.contikios.cooja.plugins;
|
package org.contikios.cooja.plugins;
|
||||||
|
|
||||||
|
import java.awt.BasicStroke;
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Cursor;
|
import java.awt.Cursor;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Event;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
|
@ -55,7 +57,7 @@ import java.awt.event.ItemListener;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.event.MouseMotionListener;
|
import java.awt.event.MouseMotionAdapter;
|
||||||
import java.awt.event.MouseWheelEvent;
|
import java.awt.event.MouseWheelEvent;
|
||||||
import java.awt.event.MouseWheelListener;
|
import java.awt.event.MouseWheelListener;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
|
@ -64,9 +66,14 @@ import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Observable;
|
import java.util.Observable;
|
||||||
import java.util.Observer;
|
import java.util.Observer;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
|
@ -74,7 +81,6 @@ import javax.swing.JCheckBoxMenuItem;
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JPopupMenu;
|
import javax.swing.JPopupMenu;
|
||||||
import javax.swing.JSeparator;
|
import javax.swing.JSeparator;
|
||||||
|
@ -128,6 +134,7 @@ import org.contikios.cooja.plugins.skins.UDGMVisualizerSkin;
|
||||||
* @see #registerVisualizerSkin(Class)
|
* @see #registerVisualizerSkin(Class)
|
||||||
* @see UDGMVisualizerSkin
|
* @see UDGMVisualizerSkin
|
||||||
* @author Fredrik Osterlind
|
* @author Fredrik Osterlind
|
||||||
|
* @author Enrico Jorns
|
||||||
*/
|
*/
|
||||||
@ClassDescription("Network")
|
@ClassDescription("Network")
|
||||||
@PluginType(PluginType.SIM_STANDARD_PLUGIN)
|
@PluginType(PluginType.SIM_STANDARD_PLUGIN)
|
||||||
|
@ -149,20 +156,34 @@ public class Visualizer extends VisPlugin implements HasQuickHelp {
|
||||||
private AffineTransform viewportTransform;
|
private AffineTransform viewportTransform;
|
||||||
public int resetViewport = 0;
|
public int resetViewport = 0;
|
||||||
|
|
||||||
/* Actions: move motes, pan view, and zoom view */
|
private static final int SELECT_MASK = Event.CTRL_MASK;
|
||||||
private boolean panning = false;
|
private static final int MOVE_MASK = Event.SHIFT_MASK;
|
||||||
private Position panningPosition = null; /* Panning start position */
|
|
||||||
private boolean zooming = false;
|
enum MotesActionState {
|
||||||
private double zoomStart = 0;
|
|
||||||
private Position zoomingPosition = null; /* Zooming center position */
|
UNKNWON,
|
||||||
private Point zoomingPixel = null; /* Zooming center pixel */
|
SELECT_PRESS,
|
||||||
private boolean moving = false;
|
DEFAULT_PRESS,
|
||||||
private Point mouseDownPixel = null; /* Records position of mouse down to differentiate a click from a move */
|
PAN_PRESS,
|
||||||
private Mote movedMote = null;
|
PANNING,
|
||||||
public Mote clickedMote = null;
|
MOVING,
|
||||||
|
// rectangular select
|
||||||
|
SELECTING
|
||||||
|
}
|
||||||
|
|
||||||
|
/* All selected motes */
|
||||||
|
public Set<Mote> selectedMotes = new HashSet<>();
|
||||||
|
/* Mote that was under curser while mouse press */
|
||||||
|
Mote cursorMote;
|
||||||
|
|
||||||
|
MotesActionState mouseActionState = MotesActionState.UNKNWON;
|
||||||
|
/* Position where mouse button was pressed */
|
||||||
|
Position pressedPos;
|
||||||
|
|
||||||
|
private Set<Mote> movedMotes = null;
|
||||||
private long moveStartTime = -1;
|
private long moveStartTime = -1;
|
||||||
private boolean moveConfirm;
|
private static final Cursor MOVE_CURSOR = new Cursor(Cursor.MOVE_CURSOR);
|
||||||
private Cursor moveCursor = new Cursor(Cursor.MOVE_CURSOR);
|
private Selection selection;
|
||||||
|
|
||||||
/* Visualizers */
|
/* Visualizers */
|
||||||
private static ArrayList<Class<? extends VisualizerSkin>> visualizerSkins =
|
private static ArrayList<Class<? extends VisualizerSkin>> visualizerSkins =
|
||||||
|
@ -277,6 +298,7 @@ public class Visualizer extends VisPlugin implements HasQuickHelp {
|
||||||
});
|
});
|
||||||
zoomMenu.add(resetViewportItem);
|
zoomMenu.add(resetViewportItem);
|
||||||
|
|
||||||
|
selection = new Selection();
|
||||||
/* Main canvas */
|
/* Main canvas */
|
||||||
canvas = new JPanel() {
|
canvas = new JPanel() {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
@ -296,6 +318,7 @@ public class Visualizer extends VisPlugin implements HasQuickHelp {
|
||||||
for (VisualizerSkin skin: currentSkins) {
|
for (VisualizerSkin skin: currentSkins) {
|
||||||
skin.paintAfterMotes(g);
|
skin.paintAfterMotes(g);
|
||||||
}
|
}
|
||||||
|
selection.drawSelection(g);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
canvas.setBackground(Color.WHITE);
|
canvas.setBackground(Color.WHITE);
|
||||||
|
@ -378,18 +401,16 @@ public class Visualizer extends VisPlugin implements HasQuickHelp {
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Popup menu */
|
/* Popup menu */
|
||||||
canvas.addMouseMotionListener(new MouseMotionListener() {
|
canvas.addMouseMotionListener(new MouseMotionAdapter() {
|
||||||
public void mouseMoved(MouseEvent e) {
|
@Override
|
||||||
handleMouseMove(e, false);
|
|
||||||
}
|
|
||||||
public void mouseDragged(MouseEvent e) {
|
public void mouseDragged(MouseEvent e) {
|
||||||
handleMouseMove(e, false);
|
handleMouseDrag(e, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
canvas.addMouseListener(new MouseAdapter() {
|
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());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,19 +420,13 @@ public class Visualizer extends VisPlugin implements HasQuickHelp {
|
||||||
}
|
}
|
||||||
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());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMouseMove(e, true);
|
if (SwingUtilities.isLeftMouseButton(e)) {
|
||||||
}
|
handleMouseRelease(e);
|
||||||
public void mouseClicked(MouseEvent e) {
|
|
||||||
if (e.isPopupTrigger()) {
|
|
||||||
handlePopupRequest(e.getPoint().x, e.getPoint().y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMouseMove(e, true);
|
|
||||||
repaint();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
canvas.addMouseWheelListener(new MouseWheelListener() {
|
canvas.addMouseWheelListener(new MouseWheelListener() {
|
||||||
|
@ -645,11 +660,11 @@ public class Visualizer extends VisPlugin implements HasQuickHelp {
|
||||||
visualizerSkins.remove(skin);
|
visualizerSkins.remove(skin);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePopupRequest(final int x, final int y) {
|
private void handlePopupRequest(Point point) {
|
||||||
JPopupMenu menu = new JPopupMenu();
|
JPopupMenu menu = new JPopupMenu();
|
||||||
|
|
||||||
/* Mote specific actions */
|
/* Mote specific actions */
|
||||||
final Mote[] motes = findMotesAtPosition(x, y);
|
final Mote[] motes = findMotesAtPosition(point.x, point.y);
|
||||||
if (motes != null && motes.length > 0) {
|
if (motes != null && motes.length > 0) {
|
||||||
menu.add(new JSeparator());
|
menu.add(new JSeparator());
|
||||||
|
|
||||||
|
@ -679,7 +694,7 @@ public class Visualizer extends VisPlugin implements HasQuickHelp {
|
||||||
|
|
||||||
/* Simulation specific actions */
|
/* Simulation specific actions */
|
||||||
menu.add(new JSeparator());
|
menu.add(new JSeparator());
|
||||||
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(this, simulation)) {
|
if (menuAction.isEnabled(this, simulation)) {
|
||||||
|
@ -709,8 +724,8 @@ public class Visualizer extends VisPlugin implements HasQuickHelp {
|
||||||
|
|
||||||
/* Show menu */
|
/* Show menu */
|
||||||
menu.setLocation(new Point(
|
menu.setLocation(new Point(
|
||||||
canvas.getLocationOnScreen().x + x,
|
canvas.getLocationOnScreen().x + point.x,
|
||||||
canvas.getLocationOnScreen().y + y));
|
canvas.getLocationOnScreen().y + point.y));
|
||||||
menu.setInvoker(canvas);
|
menu.setInvoker(canvas);
|
||||||
menu.setVisible(true);
|
menu.setVisible(true);
|
||||||
}
|
}
|
||||||
|
@ -826,43 +841,169 @@ public class Visualizer extends VisPlugin implements HasQuickHelp {
|
||||||
private void handleMousePress(MouseEvent mouseEvent) {
|
private void handleMousePress(MouseEvent mouseEvent) {
|
||||||
int x = mouseEvent.getX();
|
int x = mouseEvent.getX();
|
||||||
int y = mouseEvent.getY();
|
int y = mouseEvent.getY();
|
||||||
clickedMote = null;
|
|
||||||
|
|
||||||
if (mouseEvent.isControlDown()) {
|
pressedPos = transformPixelToPosition(mouseEvent.getPoint());
|
||||||
/* Zoom */
|
|
||||||
zooming = true;
|
// this is the state we have from pressing button
|
||||||
zoomingPixel = new Point(x, y);
|
final Mote[] foundMotes = findMotesAtPosition(x, y);
|
||||||
zoomingPosition = transformPixelToPosition(zoomingPixel);
|
if (foundMotes == null) {
|
||||||
zoomStart = viewportTransform.getScaleX();
|
cursorMote = null;
|
||||||
return;
|
}
|
||||||
|
else {
|
||||||
|
// select top mote
|
||||||
|
cursorMote = foundMotes[foundMotes.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
final Mote[] motes = findMotesAtPosition(x, y);
|
int modifiers = mouseEvent.getModifiers();
|
||||||
if (mouseEvent.isShiftDown() ||
|
|
||||||
(!mouseEvent.isAltDown() && (motes == null || motes.length == 0))) {
|
|
||||||
/* No motes clicked or shift pressed: We should pan */
|
|
||||||
panning = true;
|
|
||||||
panningPosition = transformPixelToPosition(x, y);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (motes != null && motes.length > 0) {
|
/* translate input */
|
||||||
/* One of the clicked motes should be moved */
|
if ((modifiers & SELECT_MASK) != 0) {
|
||||||
mouseDownPixel = new Point(x, y);
|
mouseActionState = MotesActionState.SELECT_PRESS;
|
||||||
clickedMote = motes[0];
|
}
|
||||||
beginMoveRequest(motes[0], false, false);
|
else if ((modifiers & MOVE_MASK) != 0) {
|
||||||
|
// only move viewport
|
||||||
|
mouseActionState = MotesActionState.PAN_PRESS;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (foundMotes == null) {
|
||||||
|
// move viewport
|
||||||
|
selectedMotes.clear();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// if this mote was not selected before, assume a new selection
|
||||||
|
if (!selectedMotes.contains(cursorMote)) {
|
||||||
|
selectedMotes.clear();
|
||||||
|
selectedMotes.add(cursorMote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mouseActionState = MotesActionState.DEFAULT_PRESS;
|
||||||
|
}
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Mote, double[]> moveStartPositions = new HashMap<>();
|
||||||
|
|
||||||
|
private void handleMouseDrag(MouseEvent e, boolean stop) {
|
||||||
|
Position currPos = transformPixelToPosition(e.getPoint());
|
||||||
|
|
||||||
|
switch (mouseActionState) {
|
||||||
|
case DEFAULT_PRESS:
|
||||||
|
if (cursorMote == null) {
|
||||||
|
mouseActionState = MotesActionState.PANNING;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mouseActionState = MotesActionState.MOVING;
|
||||||
|
// save start position
|
||||||
|
for (Mote m : selectedMotes) {
|
||||||
|
Position pos = m.getInterfaces().getPosition();
|
||||||
|
moveStartPositions.put(m, new double[]{
|
||||||
|
pos.getXCoordinate(),
|
||||||
|
pos.getYCoordinate(),
|
||||||
|
pos.getZCoordinate()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MOVING:
|
||||||
|
canvas.setCursor(MOVE_CURSOR);
|
||||||
|
for (Mote moveMote : selectedMotes) {
|
||||||
|
moveMote.getInterfaces().getPosition().setCoordinates(
|
||||||
|
moveStartPositions.get(moveMote)[0]
|
||||||
|
+ (currPos.getXCoordinate() - pressedPos.getXCoordinate()),
|
||||||
|
moveStartPositions.get(moveMote)[1]
|
||||||
|
+ (currPos.getYCoordinate() - pressedPos.getYCoordinate()),
|
||||||
|
moveStartPositions.get(moveMote)[2]
|
||||||
|
);
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PAN_PRESS:
|
||||||
|
mouseActionState = MotesActionState.PANNING;
|
||||||
|
break;
|
||||||
|
case PANNING:
|
||||||
|
/* The current mouse position should correspond to where panning started */
|
||||||
|
viewportTransform.translate(
|
||||||
|
currPos.getXCoordinate() - pressedPos.getXCoordinate(),
|
||||||
|
currPos.getYCoordinate() - pressedPos.getYCoordinate()
|
||||||
|
);
|
||||||
|
repaint();
|
||||||
|
break;
|
||||||
|
case SELECT_PRESS:
|
||||||
|
mouseActionState = MotesActionState.SELECTING;
|
||||||
|
selection.setEnabled(true);
|
||||||
|
break;
|
||||||
|
case SELECTING:
|
||||||
|
int pressedX = transformToPixelX(pressedPos.getXCoordinate());
|
||||||
|
int pressedY = transformToPixelY(pressedPos.getYCoordinate());
|
||||||
|
int currX = transformToPixelX(currPos.getXCoordinate());
|
||||||
|
int currY = transformToPixelY(currPos.getYCoordinate());
|
||||||
|
int startX = pressedX < currX ? pressedX : currX;
|
||||||
|
int startY = pressedY < currY ? pressedY : currY;
|
||||||
|
int width = Math.abs(pressedX - currX);
|
||||||
|
int height = Math.abs(pressedY - currY);
|
||||||
|
|
||||||
|
selection.setSelection(startX, startY, width, height);
|
||||||
|
selectedMotes.clear();
|
||||||
|
selectedMotes.addAll(Arrays.asList(findMotesInRange(startX, startY, width, height)));
|
||||||
|
|
||||||
|
repaint();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void beginMoveRequest(Mote moteToMove, boolean withTiming, boolean confirm) {
|
private void handleMouseRelease(MouseEvent mouseEvent) {
|
||||||
|
|
||||||
|
switch (mouseActionState) {
|
||||||
|
case PAN_PRESS:
|
||||||
|
// ignore
|
||||||
|
break;
|
||||||
|
case SELECT_PRESS:
|
||||||
|
if (cursorMote == null) {
|
||||||
|
/* Click on free canvas deselects all mote */
|
||||||
|
selectedMotes.clear();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* toggle selection for mote */
|
||||||
|
if (selectedMotes.contains(cursorMote)) {
|
||||||
|
selectedMotes.remove(cursorMote);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
selectedMotes.add(cursorMote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DEFAULT_PRESS:
|
||||||
|
if (cursorMote == null) {
|
||||||
|
/* Click on free canvas deselects all mote */
|
||||||
|
selectedMotes.clear();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Click on mote selects single mote */
|
||||||
|
selectedMotes.clear();
|
||||||
|
selectedMotes.add(cursorMote);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MOVING:
|
||||||
|
/* Release stops moving */
|
||||||
|
canvas.setCursor(Cursor.getDefaultCursor());
|
||||||
|
break;
|
||||||
|
case SELECTING:
|
||||||
|
/* Release stops moving */
|
||||||
|
selection.setEnabled(false);
|
||||||
|
repaint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void beginMoveRequest(Mote motesToMove, boolean withTiming, boolean confirm) {
|
||||||
if (withTiming) {
|
if (withTiming) {
|
||||||
moveStartTime = System.currentTimeMillis();
|
moveStartTime = System.currentTimeMillis();
|
||||||
} else {
|
} else {
|
||||||
moveStartTime = -1;
|
moveStartTime = -1;
|
||||||
}
|
}
|
||||||
moving = true;
|
mouseActionState = MotesActionState.DEFAULT_PRESS;
|
||||||
moveConfirm = confirm;
|
selectedMotes.clear();
|
||||||
movedMote = moteToMove;
|
selectedMotes.add(motesToMove);
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -888,107 +1029,37 @@ public class Visualizer extends VisPlugin implements HasQuickHelp {
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleMouseMove(MouseEvent e, boolean stop) {
|
/**
|
||||||
int x = e.getX();
|
* Returns all motes in rectangular range
|
||||||
int y = e.getY();
|
*
|
||||||
|
* @param startX
|
||||||
/* Panning */
|
* @param startY
|
||||||
if (panning) {
|
* @param width
|
||||||
if (panningPosition == null || stop) {
|
* @param height
|
||||||
panning = false;
|
* @return All motes in range
|
||||||
return;
|
*/
|
||||||
|
public Mote[] findMotesInRange(int startX, int startY, int width, int height) {
|
||||||
|
List<Mote> motes = new LinkedList<>();
|
||||||
|
for (Mote m : simulation.getMotes()) {
|
||||||
|
Position pos = m.getInterfaces().getPosition();
|
||||||
|
int moteX = transformToPixelX(pos.getXCoordinate());
|
||||||
|
int moteY = transformToPixelY(pos.getYCoordinate());
|
||||||
|
if (moteX > startX && moteX < startX + width
|
||||||
|
&& moteY > startY && moteY < startY + height) {
|
||||||
|
motes.add(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The current mouse position should correspond to where panning started */
|
|
||||||
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(x != mouseDownPixel.x || y != mouseDownPixel.y) {
|
|
||||||
Position newPos = transformPixelToPosition(x, y);
|
|
||||||
|
|
||||||
if (!stop) {
|
|
||||||
canvas.setCursor(moveCursor);
|
|
||||||
movedMote.getInterfaces().getPosition().setCoordinates(
|
|
||||||
newPos.getXCoordinate(),
|
|
||||||
newPos.getYCoordinate(),
|
|
||||||
movedMote.getInterfaces().getPosition().getZCoordinate()
|
|
||||||
);
|
|
||||||
repaint();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* Restore cursor */
|
|
||||||
canvas.setCursor(Cursor.getDefaultCursor());
|
|
||||||
|
|
||||||
|
|
||||||
/* Move mote */
|
|
||||||
if (moveStartTime < 0 || System.currentTimeMillis() - moveStartTime > 300) {
|
|
||||||
if (moveConfirm) {
|
|
||||||
String options[] = {"Yes", "Cancel"};
|
|
||||||
int returnValue = JOptionPane.showOptionDialog(Visualizer.this,
|
|
||||||
"Move mote to" +
|
|
||||||
"\nX=" + newPos.getXCoordinate() +
|
|
||||||
"\nY=" + newPos.getYCoordinate() +
|
|
||||||
"\nZ=" + movedMote.getInterfaces().getPosition().getZCoordinate(),
|
|
||||||
"Move mote?",
|
|
||||||
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,
|
|
||||||
null, options, options[0]);
|
|
||||||
moving = returnValue == JOptionPane.YES_OPTION;
|
|
||||||
}
|
|
||||||
if (moving) {
|
|
||||||
movedMote.getInterfaces().getPosition().setCoordinates(
|
|
||||||
newPos.getXCoordinate(),
|
|
||||||
newPos.getYCoordinate(),
|
|
||||||
movedMote.getInterfaces().getPosition().getZCoordinate()
|
|
||||||
);
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
moving = false;
|
|
||||||
movedMote = null;
|
|
||||||
repaint();
|
|
||||||
}
|
}
|
||||||
|
Mote[] motesArr = new Mote[motes.size()];
|
||||||
|
return motes.toArray(motesArr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all motes at given position.
|
* Returns all motes at given position.
|
||||||
*
|
*
|
||||||
|
* If multiple motes were found on at a position the motes are returned
|
||||||
|
* in the order they are painted on screen.
|
||||||
|
* First mote in array is the bottom mote, last mote is the top mote.
|
||||||
|
*
|
||||||
* @param clickedX
|
* @param clickedX
|
||||||
* X coordinate
|
* X coordinate
|
||||||
* @param clickedY
|
* @param clickedY
|
||||||
|
@ -1066,7 +1137,7 @@ public class Visualizer extends VisPlugin implements HasQuickHelp {
|
||||||
int x = pixelCoord.x;
|
int x = pixelCoord.x;
|
||||||
int y = pixelCoord.y;
|
int y = pixelCoord.y;
|
||||||
|
|
||||||
if (mote == movedMote) {
|
if (mote == movedMotes) {
|
||||||
g.setColor(MOVE_COLOR);
|
g.setColor(MOVE_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);
|
||||||
|
@ -1292,8 +1363,8 @@ public class Visualizer extends VisPlugin implements HasQuickHelp {
|
||||||
/**
|
/**
|
||||||
* @return Selected mote
|
* @return Selected mote
|
||||||
*/
|
*/
|
||||||
public Mote getSelectedMote() {
|
public Set<Mote> getSelectedMotes() {
|
||||||
return clickedMote;
|
return selectedMotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<Element> getConfigXML() {
|
public Collection<Element> getConfigXML() {
|
||||||
|
@ -1585,4 +1656,44 @@ public class Visualizer extends VisPlugin implements HasQuickHelp {
|
||||||
"Multiple views can be active at the same time. " +
|
"Multiple views can be active at the same time. " +
|
||||||
"Use the View menu to select views. ";
|
"Use the View menu to select views. ";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private class Selection {
|
||||||
|
|
||||||
|
private int x;
|
||||||
|
private int y;
|
||||||
|
private int width;
|
||||||
|
private int height;
|
||||||
|
private boolean enable;
|
||||||
|
|
||||||
|
public void setSelection(int x, int y, int width, int height) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enable) {
|
||||||
|
this.enable = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawSelection(Graphics g) {
|
||||||
|
/* only draw if enabled */
|
||||||
|
if (!enable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Graphics2D g2d = (Graphics2D) g;
|
||||||
|
g2d.setColor(new Color(64, 64, 64, 10));
|
||||||
|
g2d.fillRect(x, y, width, height);
|
||||||
|
|
||||||
|
BasicStroke dashed
|
||||||
|
= new BasicStroke(1.0f,
|
||||||
|
BasicStroke.CAP_BUTT,
|
||||||
|
BasicStroke.JOIN_MITER,
|
||||||
|
10.0f, new float[]{5.0f}, 0.0f);
|
||||||
|
g2d.setColor(Color.BLACK);
|
||||||
|
g2d.setStroke(dashed);
|
||||||
|
g2d.drawRect(x, y, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import java.awt.Color;
|
||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
@ -74,21 +75,25 @@ public class DGRMVisualizerSkin implements VisualizerSkin {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color[] getColorOf(Mote mote) {
|
public Color[] getColorOf(Mote mote) {
|
||||||
Mote selectedMote = visualizer.getSelectedMote();
|
if (visualizer.getSelectedMotes().contains(mote)) {
|
||||||
if (mote == selectedMote) {
|
|
||||||
return new Color[] { Color.CYAN };
|
return new Color[] { Color.CYAN };
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void paintBeforeMotes(Graphics g) {
|
public void paintBeforeMotes(Graphics g) {
|
||||||
Mote selectedMote = visualizer.getSelectedMote();
|
Set<Mote> selectedMotes = visualizer.getSelectedMotes();
|
||||||
if (simulation == null
|
if (simulation == null
|
||||||
|| selectedMote == null
|
|| selectedMotes == null
|
||||||
|| selectedMote.getInterfaces().getRadio() == null) {
|
|| selectedMotes.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Mote selectedMote = visualizer.getSelectedMotes().iterator().next();
|
||||||
|
if (selectedMote.getInterfaces().getRadio() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Paint transmission and interference range for selected mote */
|
/* Paint transmission and interference range for selected mote */
|
||||||
Position motePos = selectedMote.getInterfaces().getPosition();
|
Position motePos = selectedMote.getInterfaces().getPosition();
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
import java.beans.PropertyVetoException;
|
import java.beans.PropertyVetoException;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
|
@ -259,8 +260,7 @@ public class UDGMVisualizerSkin implements VisualizerSkin {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Color[] getColorOf(Mote mote) {
|
public Color[] getColorOf(Mote mote) {
|
||||||
Mote selectedMote = visualizer.getSelectedMote();
|
if (visualizer.getSelectedMotes().contains(mote)) {
|
||||||
if (mote == selectedMote) {
|
|
||||||
return new Color[] { Color.CYAN };
|
return new Color[] { Color.CYAN };
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -268,10 +268,15 @@ public class UDGMVisualizerSkin implements VisualizerSkin {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintBeforeMotes(Graphics g) {
|
public void paintBeforeMotes(Graphics g) {
|
||||||
Mote selectedMote = visualizer.getSelectedMote();
|
Set<Mote> selectedMotes = visualizer.getSelectedMotes();
|
||||||
if (simulation == null
|
if (simulation == null
|
||||||
|| selectedMote == null
|
|| selectedMotes == null
|
||||||
|| selectedMote.getInterfaces().getRadio() == null) {
|
|| selectedMotes.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Mote selectedMote = visualizer.getSelectedMotes().iterator().next();
|
||||||
|
if (selectedMote.getInterfaces().getRadio() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue