Unverified Commit 342fea6f authored by Juon Kawakami's avatar Juon Kawakami 🥗
Browse files

init

parent 54f6cedf
package maps.gml.editor;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.Color;
import java.awt.Point;
import java.awt.Insets;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import maps.gml.view.NodeDecorator;
import maps.gml.view.SquareNodeDecorator;
import maps.gml.view.EdgeDecorator;
import maps.gml.view.LineEdgeDecorator;
import maps.gml.view.RectangleOverlay;
import maps.gml.GMLNode;
import maps.gml.GMLEdge;
import maps.gml.GMLCoordinates;
import maps.gml.GMLObject;
import javax.swing.undo.AbstractUndoableEdit;
/**
A tool for deleting nodes.
*/
public class DeleteNodeTool extends AbstractTool {
private static final Color HIGHLIGHT_COLOUR = Color.BLUE;
private static final int HIGHLIGHT_SIZE = 6;
private static final Color OVERLAY_COLOUR = new Color(0, 0, 128, 128);
private Listener listener;
private NodeDecorator nodeHighlight;
private EdgeDecorator edgeHighlight;
private GMLNode selected;
private Collection<GMLEdge> attachedEdges;
private GMLCoordinates pressPoint;
private GMLCoordinates dragPoint;
private RectangleOverlay overlay;
/**
Construct a DeleteNodeTool.
@param editor The editor instance.
*/
public DeleteNodeTool(GMLEditor editor) {
super(editor);
listener = new Listener();
nodeHighlight = new SquareNodeDecorator(HIGHLIGHT_COLOUR, HIGHLIGHT_SIZE);
edgeHighlight = new LineEdgeDecorator(HIGHLIGHT_COLOUR);
selected = null;
attachedEdges = new HashSet<GMLEdge>();
overlay = new RectangleOverlay(OVERLAY_COLOUR, true);
}
@Override
public String getName() {
return "Delete node";
}
@Override
public void activate() {
editor.getViewer().addMouseListener(listener);
editor.getViewer().addMouseMotionListener(listener);
selected = null;
attachedEdges.clear();
}
@Override
public void deactivate() {
editor.getViewer().removeMouseListener(listener);
editor.getViewer().removeMouseMotionListener(listener);
editor.getViewer().clearAllNodeDecorators();
editor.getViewer().clearAllEdgeDecorators();
editor.getViewer().removeOverlay(overlay);
editor.getViewer().repaint();
}
private void highlightNode(GMLNode node) {
if (selected == node) {
return;
}
if (selected != null) {
editor.getViewer().clearNodeDecorator(selected);
editor.getViewer().clearEdgeDecorator(attachedEdges);
}
selected = node;
attachedEdges.clear();
if (selected != null) {
attachedEdges.addAll(editor.getMap().getAttachedEdges(selected));
editor.getViewer().setNodeDecorator(nodeHighlight, selected);
editor.getViewer().setEdgeDecorator(edgeHighlight, attachedEdges);
}
editor.getViewer().repaint();
}
private void removeNodes() {
double xMin = Math.min(pressPoint.getX(), dragPoint.getX());
double xMax = Math.max(pressPoint.getX(), dragPoint.getX());
double yMin = Math.min(pressPoint.getY(), dragPoint.getY());
double yMax = Math.max(pressPoint.getY(), dragPoint.getY());
Collection<GMLNode> nodes = editor.getMap().getNodesInRegion(xMin, yMin, xMax, yMax);
Map<GMLNode, Collection<GMLObject>> deleted = new HashMap<GMLNode, Collection<GMLObject>>();
for (GMLNode next : nodes) {
deleted.put(next, editor.getMap().removeNode(next));
}
editor.getViewer().repaint();
editor.setChanged();
editor.addEdit(new DeleteNodesEdit(nodes, deleted));
}
private class Listener implements MouseListener, MouseMotionListener {
@Override
public void mouseMoved(MouseEvent e) {
Point p = fixEventPoint(e.getPoint());
GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y);
GMLNode node = editor.getMap().findNearestNode(c.getX(), c.getY());
highlightNode(node);
}
@Override
public void mouseClicked(MouseEvent e) {
if (selected == null) {
return;
}
if (e.getButton() == MouseEvent.BUTTON1) {
Collection<GMLObject> deleted = editor.getMap().removeNode(selected);
editor.getViewer().repaint();
editor.setChanged();
editor.addEdit(new DeleteNodeEdit(selected, deleted));
}
}
@Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
Point p = fixEventPoint(e.getPoint());
pressPoint = editor.getViewer().getCoordinatesAtPoint(p.x, p.y);
overlay.setLeft(pressPoint.getX());
overlay.setBottom(pressPoint.getY());
editor.getViewer().addOverlay(overlay);
editor.getViewer().repaint();
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
Point p = fixEventPoint(e.getPoint());
dragPoint = editor.getViewer().getCoordinatesAtPoint(p.x, p.y);
overlay.setLeft(Double.NaN);
overlay.setRight(Double.NaN);
overlay.setBottom(Double.NaN);
overlay.setTop(Double.NaN);
editor.getViewer().removeOverlay(overlay);
editor.getViewer().repaint();
removeNodes();
pressPoint = null;
dragPoint = null;
}
}
@Override
public void mouseDragged(MouseEvent e) {
if (pressPoint != null) {
Point p = fixEventPoint(e.getPoint());
dragPoint = editor.getViewer().getCoordinatesAtPoint(p.x, p.y);
overlay.setRight(dragPoint.getX());
overlay.setTop(dragPoint.getY());
editor.getViewer().repaint();
highlightNode(null);
}
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
private Point fixEventPoint(Point p) {
Insets insets = editor.getViewer().getInsets();
return new Point(p.x - insets.left, p.y - insets.top);
}
}
private class DeleteNodeEdit extends AbstractUndoableEdit {
private GMLNode node;
private Collection<GMLObject> deletedObjects;
public DeleteNodeEdit(GMLNode node, Collection<GMLObject> deletedObjects) {
this.node = node;
this.deletedObjects = deletedObjects;
}
@Override
public void undo() {
super.undo();
editor.getMap().addNode(node);
editor.getMap().add(deletedObjects);
editor.getViewer().repaint();
}
@Override
public void redo() {
super.redo();
editor.getMap().removeNode(node);
editor.getMap().remove(deletedObjects);
editor.getViewer().repaint();
}
}
private class DeleteNodesEdit extends AbstractUndoableEdit {
private Collection<GMLNode> nodes;
private Map<GMLNode, Collection<GMLObject>> deletedObjects;
public DeleteNodesEdit(Collection<GMLNode> nodes, Map<GMLNode, Collection<GMLObject>> deletedObjects) {
this.nodes = nodes;
this.deletedObjects = deletedObjects;
}
@Override
public void undo() {
super.undo();
for (GMLNode next : nodes) {
Collection<GMLObject> deleted = deletedObjects.get(next);
editor.getMap().addNode(next);
editor.getMap().add(deleted);
}
editor.getViewer().repaint();
}
@Override
public void redo() {
super.redo();
for (GMLNode next : nodes) {
Collection<GMLObject> deleted = deletedObjects.get(next);
editor.getMap().removeNode(next);
editor.getMap().remove(deleted);
}
editor.getViewer().repaint();
}
}
}
\ No newline at end of file
package maps.gml.editor;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.Color;
import java.awt.Point;
import java.awt.Insets;
import javax.swing.undo.AbstractUndoableEdit;
import maps.gml.view.FilledShapeDecorator;
import maps.gml.GMLRoad;
import maps.gml.GMLBuilding;
import maps.gml.GMLSpace;
import maps.gml.GMLShape;
import maps.gml.GMLCoordinates;
/**
A tool for deleting shapes.
*/
public class DeleteShapeTool extends AbstractTool {
private static final Color HIGHLIGHT_COLOUR = Color.BLUE;
private Listener listener;
private FilledShapeDecorator highlight;
private GMLShape shape;
/**
Construct a DeleteShapeTool.
@param editor The editor instance.
*/
public DeleteShapeTool(GMLEditor editor) {
super(editor);
listener = new Listener();
highlight = new FilledShapeDecorator(HIGHLIGHT_COLOUR, HIGHLIGHT_COLOUR, HIGHLIGHT_COLOUR);
}
@Override
public String getName() {
return "Delete shape";
}
@Override
public void activate() {
editor.getViewer().addMouseListener(listener);
editor.getViewer().addMouseMotionListener(listener);
shape = null;
}
@Override
public void deactivate() {
editor.getViewer().removeMouseListener(listener);
editor.getViewer().removeMouseMotionListener(listener);
editor.getViewer().clearAllBuildingDecorators();
editor.getViewer().clearAllRoadDecorators();
editor.getViewer().clearAllSpaceDecorators();
editor.getViewer().repaint();
}
private void highlightShape(GMLShape newShape) {
if (shape == newShape) {
return;
}
if (shape != null) {
if (shape instanceof GMLBuilding) {
editor.getViewer().clearBuildingDecorator((GMLBuilding)shape);
}
if (shape instanceof GMLRoad) {
editor.getViewer().clearRoadDecorator((GMLRoad)shape);
}
if (shape instanceof GMLSpace) {
editor.getViewer().clearSpaceDecorator((GMLSpace)shape);
}
}
shape = newShape;
if (shape != null) {
if (shape instanceof GMLBuilding) {
editor.getViewer().setBuildingDecorator(highlight, (GMLBuilding)shape);
}
if (shape instanceof GMLRoad) {
editor.getViewer().setRoadDecorator(highlight, (GMLRoad)shape);
}
if (shape instanceof GMLSpace) {
editor.getViewer().setSpaceDecorator(highlight, (GMLSpace)shape);
}
}
editor.getViewer().repaint();
}
private class Listener implements MouseListener, MouseMotionListener {
@Override
public void mouseClicked(MouseEvent e) {
if (shape != null && e.getButton() == MouseEvent.BUTTON1) {
editor.getMap().remove(shape);
editor.getViewer().repaint();
editor.setChanged();
editor.addEdit(new DeleteShapeEdit(shape));
}
}
@Override
public void mouseMoved(MouseEvent e) {
Point p = fixEventPoint(e.getPoint());
GMLCoordinates c = editor.snap(editor.getViewer().getCoordinatesAtPoint(p.x, p.y));
highlightShape(editor.getMap().findShapeUnder(c.getX(), c.getY()));
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseDragged(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
private Point fixEventPoint(Point p) {
Insets insets = editor.getViewer().getInsets();
return new Point(p.x - insets.left, p.y - insets.top);
}
}
private class DeleteShapeEdit extends AbstractUndoableEdit {
private GMLShape shape;
public DeleteShapeEdit(GMLShape shape) {
this.shape = shape;
}
@Override
public void undo() {
super.undo();
editor.getMap().add(shape);
editor.getViewer().repaint();
}
@Override
public void redo() {
super.redo();
editor.getMap().remove(shape);
editor.getViewer().repaint();
}
}
}
\ No newline at end of file
package maps.gml.editor;
import java.util.Set;
import java.util.HashSet;
import maps.gml.GMLShape;
import maps.gml.GMLEdge;
/**
A function for fixing the lists of attached shapes.
*/
public class FixAttachedObjectsFunction extends ProgressFunction {
/**
Construct a FixAttachedObjectsFunction.
@param editor The editor instance.
*/
public FixAttachedObjectsFunction(GMLEditor editor) {
super(editor);
}
@Override
public String getName() {
return "Fix attached objects";
}
@Override
protected String getTitle() {
return "Fixing attached objects";
}
@Override
protected void executeImpl() {
// Remove and re-add all edges and shapes.
final Set<GMLShape> shapes = new HashSet<GMLShape>();
final Set<GMLEdge> edges = new HashSet<GMLEdge>();
synchronized (editor.getMap()) {
shapes.addAll(editor.getMap().getAllShapes());
edges.addAll(editor.getMap().getEdges());
}
setProgressLimit(shapes.size() + edges.size());
synchronized (editor.getMap()) {
editor.getMap().removeAllEdges();
}
for (GMLEdge next : edges) {
synchronized (editor.getMap()) {
editor.getMap().add(next);
}
bumpProgress();
}
for (GMLShape next : shapes) {
synchronized (editor.getMap()) {
editor.getMap().add(next);
}
bumpProgress();
}
editor.setChanged();
editor.getViewer().repaint();
}
}
\ No newline at end of file
package maps.gml.editor;
import java.util.Set;
import java.util.HashSet;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import maps.gml.GMLShape;
import maps.gml.GMLNode;
import maps.gml.GMLEdge;
import maps.gml.GMLDirectedEdge;
import rescuecore2.log.Logger;
/**
A function for fixing degenerate shapes.
*/
public class FixDegenerateShapesFunction extends ProgressFunction {
/**
Construct a FixDegenerateShapesFunction.
@param editor The editor instance.
*/
public FixDegenerateShapesFunction(GMLEditor editor) {
super(editor);
}
@Override
public String getName() {
return "Fix degenerate shapes";
}
@Override
protected String getTitle() {
return "Fixing degenerate shapes";
}
@Override
protected void executeImpl() {
// Go through all shapes and remove any that have two or fewer edges.
Set<GMLShape> shapes = new HashSet<GMLShape>();
Set<GMLEdge> edges = new HashSet<GMLEdge>();
synchronized (editor.getMap()) {
shapes.addAll(editor.getMap().getAllShapes());
edges.addAll(editor.getMap().getEdges());
}
setProgressLimit(shapes.size() + edges.size());
int shapeCount = 0;
int spurCount = 0;
int edgeCount = 0;
int outlineCount = 0;
for (GMLShape next : shapes) {
synchronized (editor.getMap()) {
removeDuplicateEdges(next);
if (checkForDegenerateShape(next)) {
++shapeCount;
}
else {
if (checkForSpurs(next)) {
++spurCount;
}
if (!checkOutline(next)) {
++outlineCount;
editor.getMap().remove(next);
}
}
}
bumpProgress();
}
for (GMLEdge next : edges) {
synchronized (editor.getMap()) {
if (checkForDegenerateEdge(next)) {
++edgeCount;
}
}
bumpProgress();
}
Logger.debug("Removed " + shapeCount + " degenerate shapes and " + edgeCount + " edges");
Logger.debug("Removed " + outlineCount + " shapes with broken outlines");
Logger.debug("Fixed " + spurCount + " spurs");
editor.setChanged();
editor.getViewer().repaint();
}
private void removeDuplicateEdges(GMLShape shape) {
List<GMLDirectedEdge> result = new ArrayList<GMLDirectedEdge>(shape.getEdges());
Set<GMLEdge> seen = new HashSet<GMLEdge>();
/*
Logger.debug("Checking for duplicate edges in " + shape);
Logger.debug("Edges:");
for (GMLDirectedEdge next : result) {
Logger.debug(" " + next);
}
*/
for (Iterator<GMLDirectedEdge> it = result.iterator(); it.hasNext();) {
GMLDirectedEdge dEdge = it.next();
GMLEdge edge = dEdge.getEdge();
if (seen.contains(edge)) {
// Logger.debug("Duplicate found: " + dEdge);
it.remove();
}
seen.add(edge);
}
/*
Logger.debug("Resulting edges:");
for (GMLDirectedEdge next : result) {
Logger.debug(" " + next);
}
*/
editor.getMap().remove(shape);
shape.reorderEdges(result);
// Update attached edges by removing and re-adding
editor.getMap().add(shape);
}
private boolean checkForSpurs(GMLShape shape) {
boolean spur = false;
List<GMLDirectedEdge> good = new ArrayList<GMLDirectedEdge>(shape.getEdges().size());
/*
Logger.debug("Checking for spurs in " + shape);
Logger.debug("Edges:");
for (GMLDirectedEdge next : shape.getEdges()) {
Logger.debug(" " + next);
}
*/
for (GMLDirectedEdge dEdge : shape.getEdges()) {
// This edge is good if both its nodes are part of other edges.
GMLNode start = dEdge.getStartNode();
GMLNode end = dEdge.getEndNode();
if (isFound(start, shape, dEdge) && isFound(end, shape, dEdge)) {
good.add(dEdge);
}
else {
// Logger.debug("Found spur edge: " + dEdge);
spur = true;
}
}
if (spur) {
editor.getMap().remove(shape);
shape.reorderEdges(good);
// Update attached edges by removing and re-adding
editor.getMap().add(shape);
}
return spur;
}
private boolean checkOutline(GMLShape shape) {
List<GMLDirectedEdge> edges = shape.getEdges();
List<GMLDirectedEdge> result = new ArrayList<GMLDirectedEdge>(edges.size());
Set<GMLEdge> seen = new HashSet<GMLEdge>();
GMLDirectedEdge dEdge = edges.get(0);
GMLNode start = dEdge.getStartNode();
GMLNode current = dEdge.getEndNode();
result.add(dEdge);
seen.add(dEdge.getEdge());
/*
Logger.debug("Checking outline of " + shape);
Logger.debug("Edges:");
for (GMLDirectedEdge next : edges) {
Logger.debug(" " + next);
}
Logger.debug("First edge: " + dEdge);
Logger.debug("Start node: " + start);
*/
while (current != start) {
// Logger.debug("Current node: " + current);
GMLDirectedEdge next = findNextEdge(current, edges, seen);
// Logger.debug("Next edge: " + next);
if (next == null) {
// Logger.debug("No next edge found!");
return false;
}
current = next.getEndNode();
seen.add(next.getEdge());
result.add(next);
}
/*
Logger.debug("Finished checking outline");
Logger.debug("New edges:");
for (GMLDirectedEdge next : result) {
Logger.debug(" " + next);
}
*/
editor.getMap().remove(shape);
shape.reorderEdges(result);
// Update attached edges by removing and re-adding
editor.getMap().add(shape);
return true;
}
private boolean checkForDegenerateShape(GMLShape shape) {
// CHECKSTYLE:OFF:MagicNumber
if (shape.getEdges().size() < 3) {
// CHECKSTYLE:ON:MagicNumber
editor.getMap().remove(shape);
return true;
}
return false;
}
private boolean checkForDegenerateEdge(GMLEdge edge) {
if (edge.getStart().equals(edge.getEnd())) {
// Remove this edge from all attached shapes
Collection<GMLShape> attached = new HashSet<GMLShape>(editor.getMap().getAttachedShapes(edge));
for (GMLShape shape : attached) {
editor.getMap().remove(shape);
shape.removeEdge(edge);
editor.getMap().add(shape);
}
editor.getMap().remove(edge);
return true;
}
return false;
}
private boolean isFound(GMLNode node, GMLShape shape, GMLDirectedEdge ignore) {
for (GMLDirectedEdge edge : shape.getEdges()) {
if (edge == ignore) {
continue;
}
if (node.equals(edge.getStartNode()) || node.equals(edge.getEndNode())) {
return true;
}
}
return false;
}
private GMLDirectedEdge findNextEdge(GMLNode start, Collection<GMLDirectedEdge> possible, Set<GMLEdge> seen) {
for (GMLDirectedEdge next : possible) {
if (next.getStartNode() == start && !seen.contains(next.getEdge())) {
return next;
}
}
// No edges found. Try reversing them.
for (GMLDirectedEdge next : possible) {
if (next.getEndNode() == start && !seen.contains(next.getEdge())) {
// Logger.debug("Reversed edge " + next);
next.reverse();
return next;
}
}
// Nothing found.
return null;
}
}
\ No newline at end of file
package maps.gml.editor;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import maps.gml.GMLEdge;
import rescuecore2.log.Logger;
/**
A function for fixing duplicate edges.
*/
public class FixDuplicateEdgesFunction extends ProgressFunction {
/**
Construct a FixDuplicateEdgesFunction.
@param editor The editor instance.
*/
public FixDuplicateEdgesFunction(GMLEditor editor) {
super(editor);
}
@Override
public String getName() {
return "Fix duplicate edges";
}
@Override
protected String getTitle() {
return "Fixing duplicate edges";
}
@Override
protected void executeImpl() {
// Go through all edges and replace any duplicates
final Set<GMLEdge> remaining = new HashSet<GMLEdge>(editor.getMap().getEdges());
setProgressLimit(remaining.size());
int count = 0;
while (!remaining.isEmpty()) {
GMLEdge next = remaining.iterator().next();
remaining.remove(next);
// Look at other edges for a duplicate
Iterator<GMLEdge> it = remaining.iterator();
while (it.hasNext()) {
GMLEdge test = it.next();
if ((test.getStart() == next.getStart() || test.getStart() == next.getEnd())
&& (test.getEnd() == next.getStart() || test.getEnd() == next.getEnd())) {
// Duplicate found
editor.getMap().replaceEdge(test, next);
editor.getMap().removeEdge(test);
it.remove();
++count;
bumpProgress();
}
}
bumpProgress();
}
if (count != 0) {
editor.setChanged();
editor.getViewer().repaint();
}
Logger.debug("Removed " + count + " duplicate edges");
}
}
\ No newline at end of file
package maps.gml.editor;
import maps.ScaleConversion;
import maps.MapTools;
/**
A tool for fixing latitude/longitude coordinates.
*/
public class FixLatLongTool extends AbstractTool {
/**
Construct a FixLatLongTool.
@param editor The editor instance.
*/
public FixLatLongTool(GMLEditor editor) {
super(editor);
}
@Override
public String getName() {
return "Fix lat/long";
}
@Override
public void activate() {
double minX = editor.getMap().getMinX();
double minY = editor.getMap().getMinY();
double factor = 1.0 / MapTools.sizeOf1Metre(minX, minY);
ScaleConversion c = new ScaleConversion(minX, minY, factor, factor);
editor.getMap().convertCoordinates(c);
editor.setChanged();
}
@Override
public void deactivate() {
}
}
\ No newline at end of file
package maps.gml.editor;
import javax.swing.JOptionPane;
import java.util.Set;
import java.util.HashSet;
import maps.gml.GMLNode;
import rescuecore2.log.Logger;
/**
A function for fixing nearby nodes.
*/
public class FixNearbyNodesFunction extends ProgressFunction {
private static final double DEFAULT_TOLERANCE = 0.001;
private double tolerance;
/**
Construct a FixNearbyNodesFunction.
@param editor The editor instance.
*/
public FixNearbyNodesFunction(GMLEditor editor) {
super(editor);
}
@Override
public String getName() {
return "Fix nearby nodes";
}
@Override
protected String getTitle() {
return "Fixing nearby nodes";
}
@Override
public void execute() {
String s = JOptionPane.showInputDialog(editor.getViewer(), "Enter the desired tolerance (in m)", DEFAULT_TOLERANCE);
if (s == null) {
return;
}
tolerance = Double.parseDouble(s);
super.execute();
}
@Override
protected void executeImpl() {
// Go through all nodes and replace any nearby ones.
final Set<GMLNode> remaining = new HashSet<GMLNode>(editor.getMap().getNodes());
setProgressLimit(remaining.size());
int count = 0;
while (!remaining.isEmpty()) {
GMLNode next = remaining.iterator().next();
remaining.remove(next);
double x = next.getX();
double y = next.getY();
// Logger.debug("Next node: " + next);
// Logger.debug("Finding nodes near " + x + ", " + y);
bumpProgress();
for (GMLNode replaced : editor.getMap().getNodesInRegion(x - tolerance, y - tolerance, x + tolerance, y + tolerance)) {
if (replaced == next) {
continue;
}
// Logger.debug("Found " + replaced);
editor.getMap().replaceNode(replaced, next);
remaining.remove(replaced);
editor.getMap().removeNode(replaced);
++count;
bumpProgress();
}
}
if (count != 0) {
editor.setChanged();
editor.getViewer().repaint();
}
Logger.debug("Removed " + count + " nodes");
}
}
\ No newline at end of file
package maps.gml.editor;
/**
Interface for an editing function.
*/
public interface Function {
/**
Get the name of this function.
@return The name of the function.
*/
String getName();
/**
Execute this function.
*/
void execute();
}
\ No newline at end of file
package maps.gml.editor;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEdit;
import maps.MapException;
import maps.MapReader;
import maps.MapWriter;
import maps.gml.GMLCoordinates;
import maps.gml.GMLMap;
import maps.gml.GMLObject;
import maps.gml.formats.RobocupFormat;
import maps.gml.view.GMLMapViewer;
import maps.gml.view.GMLObjectInspector;
import rescuecore2.log.Logger;
/**
* A component for editing GML maps.
*/
public class GMLEditor extends JPanel {
private static final int VIEWER_PREFERRED_SIZE = 500;
private static final int INSPECTOR_PREFERRED_WIDTH = 300;
private static final int INSPECTOR_PREFERRED_HEIGHT = 500;
private static final double SNAP_MIN_RESOLUTION = 0.001;
private static final double SNAP_MAX_RESOLUTION = 1000;
private static final NumberFormat FORMAT = new DecimalFormat("#0.000");
private GMLMap map;
private GMLMapViewer viewer;
private GMLObjectInspector inspector;
private JLabel x;
private JLabel y;
private boolean changed;
private ViewerMouseListener viewerMouseListener;
private Tool currentTool;
private UndoManager undoManager;
private Action undoAction;
private Action redoAction;
private File saveFile;
private File baseDir;
private Snap snap;
/**
* Construct a new GMLEditor.
*
* @param menuBar The menu bar to add menus to.
*/
public GMLEditor(JMenuBar menuBar) {
super(new BorderLayout());
map = new GMLMap();
viewer = new GMLMapViewer(map);
inspector = new GMLObjectInspector(map);
undoManager = new UndoManager();
viewer.setPreferredSize(new Dimension(VIEWER_PREFERRED_SIZE, VIEWER_PREFERRED_SIZE));
inspector.setPreferredSize(new Dimension(INSPECTOR_PREFERRED_WIDTH, INSPECTOR_PREFERRED_HEIGHT));
viewer.setBackground(Color.GRAY);
viewer.getPanZoomListener().setPanOnRightMouse();
snap = new Snap();
changed = false;
x = new JLabel("X: ");
y = new JLabel("Y: ");
JToolBar fileToolbar = new JToolBar("File");
JToolBar viewToolbar = new JToolBar("View");
JToolBar editToolbar = new JToolBar("Edit");
JToolBar toolsToolbar = new JToolBar("Tools");
JToolBar functionsToolbar = new JToolBar("Functions");
JMenu fileMenu = new JMenu("File", false);
JMenu viewMenu = new JMenu("View", false);
JMenu editMenu = new JMenu("Edit", false);
JMenu toolsMenu = new JMenu("Tools", false);
JMenu functionsMenu = new JMenu("Functions", false);
createFileActions(fileMenu, fileToolbar);
createViewActions(viewMenu, viewToolbar);
createEditActions(editMenu, editToolbar);
createToolActions(toolsMenu, toolsToolbar);
createFunctionActions(functionsMenu, functionsToolbar);
JPanel main = new JPanel(new BorderLayout());
JPanel labels = new JPanel(new GridLayout(1, 2));
labels.add(x);
labels.add(y);
JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, viewer, inspector);
main.add(split, BorderLayout.CENTER);
main.add(labels, BorderLayout.SOUTH);
add(main, BorderLayout.CENTER);
JPanel toolbars = new JPanel(new GridLayout(0, 1));
toolbars.add(fileToolbar);
toolbars.add(viewToolbar);
toolbars.add(editToolbar);
toolbars.add(toolsToolbar);
toolbars.add(functionsToolbar);
add(toolbars, BorderLayout.NORTH);
menuBar.add(fileMenu);
menuBar.add(viewMenu);
menuBar.add(editMenu);
menuBar.add(toolsMenu);
menuBar.add(functionsMenu);
viewerMouseListener = new ViewerMouseListener();
viewer.addMouseListener(viewerMouseListener);
viewer.addMouseMotionListener(viewerMouseListener);
baseDir = new File(System.getProperty("user.dir"));
}
/**
* Entry point.
*
* @param args Command line arguments.
*/
public static void main(String[] args) {
final JFrame frame = new JFrame("GMLEditor");
JMenuBar menuBar = new JMenuBar();
final GMLEditor editor = new GMLEditor(menuBar);
if (args.length > 0 && args[0].length() > 0) {
try {
editor.load(args[0]);
} catch (CancelledByUserException e) {
return;
} catch (MapException e) {
e.printStackTrace();
}
}
frame.setJMenuBar(menuBar);
frame.setContentPane(editor);
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
frame.pack();
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
try {
editor.close();
frame.setVisible(false);
frame.dispose();
System.exit(0);
} catch (CancelledByUserException ex) {
frame.setVisible(true);
}
}
});
frame.setVisible(true);
}
/**
* Load a map by showing a file chooser dialog.
*
* @throws CancelledByUserException If the user cancels the change due to
* unsaved changes.
* @throws MapException If there is a problem reading the map.
*/
public void load() throws CancelledByUserException, MapException {
JFileChooser chooser = new JFileChooser(baseDir);
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
load(chooser.getSelectedFile());
}
}
/**
* Load a map from a file.
*
* @param filename The name of the file to read.
* @throws CancelledByUserException If the user cancels the change due to
* unsaved changes.
* @throws MapException If there is a problem reading the map.
*/
public void load(String filename) throws CancelledByUserException, MapException {
load(new File(filename));
}
/**
* Load a map from a file.
*
* @param file The file to read.
* @throws CancelledByUserException If the user cancels the change due to
* unsaved changes.
* @throws MapException If there is a problem reading the map.
*/
public void load(File file) throws CancelledByUserException, MapException {
setMap((GMLMap) MapReader.readMap(file));
saveFile = file;
baseDir = saveFile.getParentFile();
}
/**
* Set the map.
*
* @param newMap The new map.
* @throws CancelledByUserException If the user cancels the change due to
* unsaved changes.
*/
public void setMap(GMLMap newMap) throws CancelledByUserException {
checkForChanges();
map = newMap;
changed = false;
viewer.setMap(map);
inspector.setMap(map);
viewer.repaint();
}
/**
* Get the map.
*
* @return The map.
*/
public GMLMap getMap() {
return map;
}
/**
* Save the map.
*
* @throws MapException If there is a problem saving the map.
*/
public void save() throws MapException {
if (saveFile == null) {
JFileChooser chooser = new JFileChooser(baseDir);
if (chooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
saveFile = chooser.getSelectedFile();
}
}
if (saveFile != null) {
Logger.debug("Saving to " + saveFile.getAbsolutePath());
MapWriter.writeMap(map, saveFile, RobocupFormat.INSTANCE);
baseDir = saveFile.getParentFile();
changed = false;
}
}
/**
* Close the editor.
*
* @throws CancelledByUserException If the user cancels the close due to unsaved
* changes."
*/
public void close() throws CancelledByUserException {
checkForChanges();
}
/**
* Get the map viewer.
*
* @return The map viewer.
*/
public GMLMapViewer getViewer() {
return viewer;
}
/**
* Get the object inspector.
*
* @return The object inspector.
*/
public GMLObjectInspector getInspector() {
return inspector;
}
/**
* Register a change to the map.
*/
public void setChanged() {
changed = true;
}
/**
* Register an undoable edit.
*
* @param edit The edit to add.
*/
public void addEdit(UndoableEdit edit) {
undoManager.addEdit(edit);
undoAction.setEnabled(undoManager.canUndo());
redoAction.setEnabled(undoManager.canRedo());
}
/**
* Snap coordinates to the grid.
*
* @param c The coordinates to snap.
* @return The passed-in coordinates object.
*/
public GMLCoordinates snap(GMLCoordinates c) {
snap.snap(c);
return c;
}
private void updatePositionLabels(final Point p) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
GMLCoordinates c = viewer.getCoordinatesAtPoint(p.x, p.y);
x.setText("X: " + FORMAT.format(c.getX()));
y.setText("Y: " + FORMAT.format(c.getY()));
}
});
}
private void checkForChanges() throws CancelledByUserException {
if (changed) {
switch (JOptionPane.showConfirmDialog(null, "The current map has changes. Do you want to save them?")) {
case JOptionPane.YES_OPTION:
try {
save();
} catch (MapException e) {
JOptionPane.showMessageDialog(null, e);
throw new CancelledByUserException();
}
break;
case JOptionPane.NO_OPTION:
changed = false;
return;
case JOptionPane.CANCEL_OPTION:
throw new CancelledByUserException();
default:
throw new RuntimeException("JOptionPane.showConfirmDialog returned something weird");
}
}
}
private void createFileActions(JMenu menu, JToolBar toolbar) {
Action newAction = new AbstractAction("New") {
@Override
public void actionPerformed(ActionEvent e) {
try {
checkForChanges();
setMap(new GMLMap());
} catch (CancelledByUserException ex) {
return;
}
}
};
Action loadAction = new AbstractAction("Load") {
@Override
public void actionPerformed(ActionEvent e) {
try {
checkForChanges();
load();
} catch (CancelledByUserException ex) {
return;
} catch (MapException ex) {
JOptionPane.showMessageDialog(null, ex);
}
}
};
Action saveAction = new AbstractAction("Save") {
@Override
public void actionPerformed(ActionEvent e) {
try {
save();
} catch (MapException ex) {
JOptionPane.showMessageDialog(null, ex);
}
}
};
Action saveAsAction = new AbstractAction("Save as") {
@Override
public void actionPerformed(ActionEvent e) {
try {
saveFile = null;
save();
} catch (MapException ex) {
JOptionPane.showMessageDialog(null, ex);
}
}
};
toolbar.add(newAction);
toolbar.add(loadAction);
toolbar.add(saveAction);
toolbar.add(saveAsAction);
menu.add(newAction);
menu.add(loadAction);
menu.add(saveAction);
menu.add(saveAsAction);
}
private void createViewActions(JMenu menu, JToolBar toolbar) {
final JCheckBox snapBox = new JCheckBox("Snap to grid", snap.isEnabled());
final JSpinner snapSpinner = new JSpinner(
new SpinnerNumberModel(snap.getResolution(), SNAP_MIN_RESOLUTION, SNAP_MAX_RESOLUTION, SNAP_MIN_RESOLUTION));
snapSpinner.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
snap.setResolution((Double) snapSpinner.getValue());
}
});
snapBox.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
snap.setEnabled(snapBox.isSelected());
}
});
Action gridAction = new AbstractAction("Show grid") {
@Override
public void actionPerformed(ActionEvent e) {
viewer.setGridEnabled((Boolean) getValue(Action.SELECTED_KEY));
viewer.repaint();
}
};
gridAction.putValue(Action.SELECTED_KEY, false);
toolbar.add(snapSpinner);
toolbar.add(snapBox);
toolbar.add(new JToggleButton(gridAction));
menu.add(new JCheckBoxMenuItem(gridAction));
// Create the "show objects" button and textfield
final JTextField showField = new JTextField();
JButton showButton = new JButton("Show");
showButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String s = showField.getText();
List<GMLObject> objects = new ArrayList<GMLObject>();
for (String next : s.split(",")) {
int id = Integer.parseInt(next.trim());
GMLObject o = map.getObject(id);
if (o != null) {
objects.add(o);
}
}
viewer.view(objects);
viewer.repaint();
}
});
toolbar.addSeparator();
toolbar.add(showField);
toolbar.add(showButton);
// Add the reset zoom action
Action resetZoom = new AbstractAction("Reset zoom") {
@Override
public void actionPerformed(ActionEvent e) {
viewer.viewAll();
viewer.repaint();
}
};
toolbar.addSeparator();
menu.addSeparator();
toolbar.add(resetZoom);
menu.add(resetZoom);
}
private void createEditActions(JMenu menu, JToolBar toolbar) {
undoAction = new AbstractAction("Undo") {
@Override
public void actionPerformed(ActionEvent e) {
try {
undoManager.undo();
} catch (CannotUndoException ex) {
JOptionPane.showMessageDialog(null, ex);
}
setEnabled(undoManager.canUndo());
redoAction.setEnabled(undoManager.canRedo());
}
};
redoAction = new AbstractAction("Redo") {
@Override
public void actionPerformed(ActionEvent e) {
try {
undoManager.redo();
} catch (CannotUndoException ex) {
JOptionPane.showMessageDialog(null, ex);
}
setEnabled(undoManager.canRedo());
undoAction.setEnabled(undoManager.canUndo());
}
};
undoAction.setEnabled(false);
redoAction.setEnabled(false);
toolbar.add(undoAction);
toolbar.add(redoAction);
menu.add(undoAction);
menu.add(redoAction);
}
private void createToolActions(JMenu menu, JToolBar toolbar) {
ButtonGroup toolbarGroup = new ButtonGroup();
ButtonGroup menuGroup = new ButtonGroup();
addTool(new InspectTool(this), menu, toolbar, menuGroup, toolbarGroup);
menu.addSeparator();
toolbar.addSeparator();
addTool(new CreateNodeTool(this), menu, toolbar, menuGroup, toolbarGroup);
addTool(new CreateEdgeTool(this), menu, toolbar, menuGroup, toolbarGroup);
addTool(new CreateRoadTool(this), menu, toolbar, menuGroup, toolbarGroup);
addTool(new CreateBuildingTool(this), menu, toolbar, menuGroup, toolbarGroup);
addTool(new CreateSpaceTool(this), menu, null, menuGroup, null);
menu.addSeparator();
toolbar.addSeparator();
addTool(new DeleteNodeTool(this), menu, toolbar, menuGroup, toolbarGroup);
addTool(new DeleteEdgeTool(this), menu, toolbar, menuGroup, toolbarGroup);
addTool(new DeleteShapeTool(this), menu, toolbar, menuGroup, toolbarGroup);
menu.addSeparator();
toolbar.addSeparator();
addTool(new MoveNodeTool(this), menu, toolbar, menuGroup, toolbarGroup);
addTool(new MergeNodesTool(this), menu, toolbar, menuGroup, toolbarGroup);
addTool(new MergeLinesTool(this), menu, toolbar, menuGroup, toolbarGroup);
addTool(new SplitEdgeTool(this), menu, toolbar, menuGroup, toolbarGroup);
addTool(new SplitShapeTool(this), menu, toolbar, menuGroup, toolbarGroup);
addTool(new TogglePassableTool(this), menu, toolbar, menuGroup, toolbarGroup);
}
private void createFunctionActions(JMenu menu, JToolBar toolbar) {
addFunction(new ScaleFunction(this), menu, toolbar);
addFunction(new FixNearbyNodesFunction(this), menu, toolbar);
addFunction(new FixDuplicateEdgesFunction(this), menu, toolbar);
addFunction(new SplitEdgesFunction(this), menu, toolbar);
addFunction(new ComputePassableEdgesFunction(this), menu, toolbar);
addFunction(new PruneOrphanNodesFunction(this), menu, toolbar);
addFunction(new PruneOrphanEdgesFunction(this), menu, toolbar);
addFunction(new FixDegenerateShapesFunction(this), menu, toolbar);
addFunction(new FixAttachedObjectsFunction(this), menu, toolbar);
addFunction(new ValidateFunction(this), menu, toolbar);
addFunction(new AddNoiseFunction(this), menu, toolbar);
}
private void addTool(final Tool t, JMenu menu, JToolBar toolbar, ButtonGroup menuGroup, ButtonGroup toolbarGroup) {
final JToggleButton toggle = new JToggleButton();
final JCheckBoxMenuItem check = new JCheckBoxMenuItem();
Action action = new AbstractAction(t.getName()) {
@Override
public void actionPerformed(ActionEvent e) {
if (currentTool != null) {
currentTool.deactivate();
}
currentTool = t;
toggle.setSelected(true);
check.setSelected(true);
currentTool.activate();
}
};
toggle.setAction(action);
check.setAction(action);
menu.add(check);
if (toolbar != null) {
toolbar.add(toggle);
toolbarGroup.add(toggle);
}
menuGroup.add(check);
}
private void addFunction(final Function f, JMenu menu, JToolBar toolbar) {
Action action = new AbstractAction(f.getName()) {
@Override
public void actionPerformed(ActionEvent e) {
f.execute();
}
};
toolbar.add(action);
menu.add(action);
}
private class ViewerMouseListener implements MouseListener, MouseMotionListener {
@Override
public void mouseMoved(MouseEvent e) {
updatePositionLabels(fixEventPoint(e.getPoint()));
}
@Override
public void mouseDragged(MouseEvent e) {
updatePositionLabels(fixEventPoint(e.getPoint()));
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
updatePositionLabels(fixEventPoint(e.getPoint()));
}
private Point fixEventPoint(Point p) {
Insets insets = viewer.getInsets();
return new Point(p.x - insets.left, p.y - insets.top);
}
}
}
\ No newline at end of file
package maps.gml.editor;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.Point;
import java.awt.Insets;
import java.awt.Color;
import maps.gml.GMLNode;
import maps.gml.GMLEdge;
import maps.gml.GMLRoad;
import maps.gml.GMLBuilding;
import maps.gml.GMLSpace;
import maps.gml.GMLShape;
import maps.gml.GMLObject;
import maps.gml.GMLCoordinates;
import maps.gml.view.FilledShapeDecorator;
import maps.gml.view.NodeDecorator;
import maps.gml.view.EdgeDecorator;
import maps.gml.view.SquareNodeDecorator;
import maps.gml.view.LineEdgeDecorator;
import rescuecore2.misc.geometry.Point2D;
import rescuecore2.misc.geometry.Line2D;
import rescuecore2.misc.geometry.GeometryTools2D;
/**
A tool for inspecting objects.
*/
public class InspectTool extends AbstractTool {
/** Distance in pixels to consider an object "nearby". */
private static final int NEARBY = 5;
private static final Color HIGHLIGHT_COLOUR = Color.BLUE;
private static final int NODE_SIZE = 5;
private Listener listener;
private NodeDecorator nodeHighlight;
private EdgeDecorator edgeHighlight;
private FilledShapeDecorator shapeHighlight;
/**
Construct an InspectTool.
@param editor The editor instance.
*/
public InspectTool(GMLEditor editor) {
super(editor);
listener = new Listener();
nodeHighlight = new SquareNodeDecorator(HIGHLIGHT_COLOUR, NODE_SIZE);
edgeHighlight = new LineEdgeDecorator(HIGHLIGHT_COLOUR);
shapeHighlight = new FilledShapeDecorator(HIGHLIGHT_COLOUR, HIGHLIGHT_COLOUR, HIGHLIGHT_COLOUR);
}
@Override
public String getName() {
return "Inspect object";
}
@Override
public void activate() {
editor.getViewer().addMouseListener(listener);
editor.getViewer().addMouseMotionListener(listener);
}
@Override
public void deactivate() {
editor.getViewer().removeMouseListener(listener);
editor.getViewer().removeMouseMotionListener(listener);
editor.getViewer().clearAllNodeDecorators();
editor.getViewer().clearAllEdgeDecorators();
editor.getViewer().clearAllBuildingDecorators();
editor.getViewer().clearAllRoadDecorators();
editor.getViewer().clearAllSpaceDecorators();
editor.getViewer().repaint();
}
private void highlight(GMLObject object) {
editor.getViewer().clearAllNodeDecorators();
editor.getViewer().clearAllEdgeDecorators();
editor.getViewer().clearAllBuildingDecorators();
editor.getViewer().clearAllRoadDecorators();
editor.getViewer().clearAllSpaceDecorators();
if (object instanceof GMLNode) {
editor.getViewer().setNodeDecorator(nodeHighlight, (GMLNode)object);
}
if (object instanceof GMLEdge) {
editor.getViewer().setEdgeDecorator(edgeHighlight, (GMLEdge)object);
}
if (object instanceof GMLBuilding) {
editor.getViewer().setBuildingDecorator(shapeHighlight, (GMLBuilding)object);
}
if (object instanceof GMLRoad) {
editor.getViewer().setRoadDecorator(shapeHighlight, (GMLRoad)object);
}
if (object instanceof GMLSpace) {
editor.getViewer().setSpaceDecorator(shapeHighlight, (GMLSpace)object);
}
editor.getViewer().repaint();
}
private boolean closeEnough(GMLNode node, Point p) {
GMLCoordinates lowerLeft = editor.getViewer().getCoordinatesAtPoint(p.x - NEARBY, p.y + NEARBY);
GMLCoordinates topRight = editor.getViewer().getCoordinatesAtPoint(p.x + NEARBY, p.y - NEARBY);
return (node.getX() > lowerLeft.getX() && node.getX() < topRight.getX() && node.getY() > lowerLeft.getY() && node.getY() < topRight.getY());
}
private boolean closeEnough(GMLEdge edge, Point p) {
Point start = editor.getViewer().getScreenCoordinates(edge.getStart().getCoordinates());
Point end = editor.getViewer().getScreenCoordinates(edge.getEnd().getCoordinates());
Point2D startPoint = new Point2D(start.x, start.y);
Point2D endPoint = new Point2D(end.x, end.y);
Line2D line = new Line2D(startPoint, endPoint);
Point2D testPoint = new Point2D(p.x, p.y);
Point2D closest = GeometryTools2D.getClosestPointOnSegment(line, testPoint);
return GeometryTools2D.getDistance(testPoint, closest) < NEARBY;
}
private class Listener implements MouseListener, MouseMotionListener {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
Point p = fixEventPoint(e.getPoint());
editor.getInspector().inspect(findNearbyObject(p));
}
}
@Override
public void mouseMoved(MouseEvent e) {
Point p = fixEventPoint(e.getPoint());
highlight(findNearbyObject(p));
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseDragged(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
private GMLObject findNearbyObject(Point p) {
GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y);
GMLNode node = editor.getMap().findNearestNode(c.getX(), c.getY());
GMLEdge edge = editor.getMap().findNearestEdge(c.getX(), c.getY());
GMLShape shape = editor.getMap().findShapeUnder(c.getX(), c.getY());
// If the node is close enough inspect that
// Otherwise, if the edge is close enough
// Otherwise the shape
if (node != null && closeEnough(node, p)) {
return node;
}
else if (edge != null && closeEnough(edge, p)) {
return edge;
}
else {
return shape;
}
}
private Point fixEventPoint(Point p) {
Insets insets = editor.getViewer().getInsets();
return new Point(p.x - insets.left, p.y - insets.top);
}
}
}
\ No newline at end of file
package maps.gml.editor;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.Color;
import java.awt.Point;
import java.awt.Insets;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import javax.swing.undo.AbstractUndoableEdit;
import maps.gml.view.NodeDecorator;
import maps.gml.view.SquareNodeDecorator;
import maps.gml.view.EdgeDecorator;
import maps.gml.view.LineEdgeDecorator;
import maps.gml.GMLNode;
import maps.gml.GMLEdge;
import maps.gml.GMLCoordinates;
import maps.gml.GMLObject;
/**
A tool for merging lines by deleting a common node.
*/
public class MergeLinesTool extends AbstractTool {
private static final Color HIGHLIGHT_COLOUR = Color.BLUE;
private static final int HIGHLIGHT_SIZE = 6;
private Listener listener;
private NodeDecorator nodeHighlight;
private EdgeDecorator edgeHighlight;
private GMLNode selected;
private Collection<GMLEdge> attachedEdges;
/**
Construct a MergeLinesTool.
@param editor The editor instance.
*/
public MergeLinesTool(GMLEditor editor) {
super(editor);
listener = new Listener();
nodeHighlight = new SquareNodeDecorator(HIGHLIGHT_COLOUR, HIGHLIGHT_SIZE);
edgeHighlight = new LineEdgeDecorator(HIGHLIGHT_COLOUR);
selected = null;
attachedEdges = new HashSet<GMLEdge>();
}
@Override
public String getName() {
return "Merge lines";
}
@Override
public void activate() {
editor.getViewer().addMouseListener(listener);
editor.getViewer().addMouseMotionListener(listener);
selected = null;
attachedEdges.clear();
}
@Override
public void deactivate() {
editor.getViewer().removeMouseListener(listener);
editor.getViewer().removeMouseMotionListener(listener);
editor.getViewer().clearAllNodeDecorators();
editor.getViewer().clearAllEdgeDecorators();
editor.getViewer().repaint();
}
private void highlightNode(GMLNode node, Collection<GMLEdge> attached) {
if (selected == node) {
return;
}
if (selected != null) {
editor.getViewer().clearNodeDecorator(selected);
editor.getViewer().clearEdgeDecorator(attachedEdges);
}
selected = node;
attachedEdges.clear();
if (selected != null) {
attachedEdges.addAll(attached);
editor.getViewer().setNodeDecorator(nodeHighlight, selected);
editor.getViewer().setEdgeDecorator(edgeHighlight, attachedEdges);
}
editor.getViewer().repaint();
}
private class Listener implements MouseListener, MouseMotionListener {
@Override
public void mouseMoved(MouseEvent e) {
Point p = fixEventPoint(e.getPoint());
GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y);
GMLNode node = editor.getMap().findNearestNode(c.getX(), c.getY());
Collection<GMLEdge> attached = editor.getMap().getAttachedEdges(node);
if (attached.size() == 2) {
highlightNode(node, attached);
}
}
@Override
public void mouseClicked(MouseEvent e) {
if (selected == null) {
return;
}
if (e.getButton() == MouseEvent.BUTTON1) {
Iterator<GMLEdge> it = attachedEdges.iterator();
GMLEdge newEdge = editor.getMap().mergeEdges(it.next(), it.next());
Collection<GMLObject> deleted = editor.getMap().removeNode(selected);
editor.setChanged();
editor.addEdit(new MergeEdit(selected, deleted, newEdge));
highlightNode(null, null);
}
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseDragged(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
private Point fixEventPoint(Point p) {
Insets insets = editor.getViewer().getInsets();
return new Point(p.x - insets.left, p.y - insets.top);
}
}
private class MergeEdit extends AbstractUndoableEdit {
private GMLNode deletedNode;
private Collection<GMLObject> deletedObjects;
private GMLEdge newEdge;
public MergeEdit(GMLNode node, Collection<GMLObject> deletedObjects, GMLEdge newEdge) {
this.deletedNode = node;
this.deletedObjects = deletedObjects;
this.newEdge = newEdge;
}
@Override
public void undo() {
super.undo();
editor.getMap().addNode(deletedNode);
editor.getMap().add(deletedObjects);
editor.getMap().removeEdge(newEdge);
editor.getViewer().repaint();
}
@Override
public void redo() {
super.redo();
editor.getMap().removeNode(deletedNode);
editor.getMap().remove(deletedObjects);
editor.getMap().addEdge(newEdge);
editor.getViewer().repaint();
}
}
}
\ No newline at end of file
package maps.gml.editor;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.Color;
import java.awt.Point;
import java.awt.Insets;
import maps.gml.view.NodeDecorator;
import maps.gml.view.SquareNodeDecorator;
import maps.gml.GMLNode;
import maps.gml.GMLCoordinates;
/**
A tool for merging nodes.
*/
public class MergeNodesTool extends AbstractTool {
private static final Color HOVER_COLOUR = Color.BLUE;
private static final Color MERGE_COLOUR = Color.RED;
private static final int HIGHLIGHT_SIZE = 6;
private Listener listener;
private NodeDecorator hoverHighlight;
private NodeDecorator mergeHighlight;
private GMLNode hover;
private GMLNode merge;
private boolean merging;
/**
Construct a MergeNodesTool.
@param editor The editor instance.
*/
public MergeNodesTool(GMLEditor editor) {
super(editor);
listener = new Listener();
hoverHighlight = new SquareNodeDecorator(HOVER_COLOUR, HIGHLIGHT_SIZE);
mergeHighlight = new SquareNodeDecorator(MERGE_COLOUR, HIGHLIGHT_SIZE);
}
@Override
public String getName() {
return "Merge nodes";
}
@Override
public void activate() {
editor.getViewer().addMouseListener(listener);
editor.getViewer().addMouseMotionListener(listener);
hover = null;
merge = null;
merging = false;
}
@Override
public void deactivate() {
editor.getViewer().removeMouseListener(listener);
editor.getViewer().removeMouseMotionListener(listener);
editor.getViewer().clearAllNodeDecorators();
editor.getViewer().repaint();
}
private void hover(GMLNode node) {
if (hover == node) {
return;
}
if (hover != null) {
editor.getViewer().clearNodeDecorator(hover);
}
hover = node;
if (hover != null) {
editor.getViewer().setNodeDecorator(hoverHighlight, hover);
}
editor.getViewer().repaint();
}
private void setMerge(GMLNode node) {
if (merge == node) {
return;
}
if (node == hover) {
return;
}
if (merge != null) {
editor.getViewer().clearNodeDecorator(merge);
}
merge = node;
if (merge != null) {
editor.getViewer().setNodeDecorator(mergeHighlight, merge);
}
editor.getViewer().repaint();
}
private class Listener implements MouseListener, MouseMotionListener {
@Override
public void mouseMoved(MouseEvent e) {
Point p = fixEventPoint(e.getPoint());
GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y);
GMLNode node = editor.getMap().findNearestNode(c.getX(), c.getY());
hover(node);
}
@Override
public void mouseDragged(MouseEvent e) {
if (hover == null) {
return;
}
if (!merging) {
return;
}
Point p = fixEventPoint(e.getPoint());
GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y);
GMLNode node = editor.getMap().findNearestNode(c.getX(), c.getY());
setMerge(node);
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
if (merging) {
if (hover != null && merge != null) {
editor.getMap().replaceNode(hover, merge);
editor.getMap().removeNode(hover);
editor.setChanged();
}
if (hover != null) {
editor.getViewer().clearNodeDecorator(hover);
}
if (merge != null) {
editor.getViewer().clearNodeDecorator(merge);
}
editor.getViewer().repaint();
hover = null;
merge = null;
merging = false;
}
}
}
@Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
merging = true;
}
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
private Point fixEventPoint(Point p) {
Insets insets = editor.getViewer().getInsets();
return new Point(p.x - insets.left, p.y - insets.top);
}
}
}
\ No newline at end of file
package maps.gml.editor;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.Color;
import java.awt.Point;
import java.awt.Insets;
import maps.gml.view.NodeDecorator;
import maps.gml.view.SquareNodeDecorator;
import maps.gml.GMLNode;
import maps.gml.GMLCoordinates;
import javax.swing.undo.AbstractUndoableEdit;
/**
A tool for moving nodes.
*/
public class MoveNodeTool extends AbstractTool {
private static final Color HIGHLIGHT_COLOUR = Color.BLACK;
private static final int HIGHLIGHT_SIZE = 6;
private Listener listener;
private NodeDecorator highlight;
private GMLNode selected;
private GMLCoordinates pressCoords;
private GMLCoordinates originalCoords;
/**
Construct a MoveNodeTool.
@param editor The editor instance.
*/
public MoveNodeTool(GMLEditor editor) {
super(editor);
listener = new Listener();
highlight = new SquareNodeDecorator(HIGHLIGHT_COLOUR, HIGHLIGHT_SIZE);
selected = null;
}
@Override
public String getName() {
return "Move node";
}
@Override
public void activate() {
editor.getViewer().addMouseListener(listener);
editor.getViewer().addMouseMotionListener(listener);
selected = null;
}
@Override
public void deactivate() {
editor.getViewer().removeMouseListener(listener);
editor.getViewer().removeMouseMotionListener(listener);
editor.getViewer().clearAllNodeDecorators();
editor.getViewer().repaint();
}
private void highlightNode(GMLNode node) {
if (selected == node) {
return;
}
if (selected != null) {
editor.getViewer().clearNodeDecorator(selected);
}
selected = node;
if (selected != null) {
editor.getViewer().setNodeDecorator(highlight, selected);
}
editor.getViewer().repaint();
}
private class Listener implements MouseListener, MouseMotionListener {
@Override
public void mouseMoved(MouseEvent e) {
Point p = fixEventPoint(e.getPoint());
GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y);
GMLNode node = editor.getMap().findNearestNode(c.getX(), c.getY());
highlightNode(node);
}
@Override
public void mousePressed(MouseEvent e) {
if (selected == null) {
return;
}
if (e.getButton() == MouseEvent.BUTTON1) {
Point p = fixEventPoint(e.getPoint());
pressCoords = editor.getViewer().getCoordinatesAtPoint(p.x, p.y);
originalCoords = new GMLCoordinates(selected.getCoordinates());
}
}
@Override
public void mouseReleased(MouseEvent e) {
pressCoords = null;
editor.addEdit(new MoveNodeEdit(selected, originalCoords, new GMLCoordinates(selected.getCoordinates())));
}
@Override
public void mouseDragged(MouseEvent e) {
if (selected == null) {
return;
}
if (pressCoords == null) {
return;
}
Point p = fixEventPoint(e.getPoint());
GMLCoordinates dragCoords = editor.getViewer().getCoordinatesAtPoint(p.x, p.y);
double dx = dragCoords.getX() - pressCoords.getX();
double dy = dragCoords.getY() - pressCoords.getY();
GMLCoordinates result = new GMLCoordinates(originalCoords.getX() + dx, originalCoords.getY() + dy);
editor.snap(result);
selected.setCoordinates(result);
editor.setChanged();
editor.getViewer().repaint();
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
private Point fixEventPoint(Point p) {
Insets insets = editor.getViewer().getInsets();
return new Point(p.x - insets.left, p.y - insets.top);
}
}
private class MoveNodeEdit extends AbstractUndoableEdit {
private GMLNode node;
private GMLCoordinates oldPosition;
private GMLCoordinates newPosition;
public MoveNodeEdit(GMLNode node, GMLCoordinates oldPosition, GMLCoordinates newPosition) {
this.node = node;
this.oldPosition = oldPosition;
this.newPosition = newPosition;
}
@Override
public void undo() {
super.undo();
node.setCoordinates(oldPosition);
editor.getViewer().repaint();
}
@Override
public void redo() {
super.redo();
node.setCoordinates(newPosition);
editor.getViewer().repaint();
}
}
}
\ No newline at end of file
package maps.gml.editor;
/**
A tool for panning and zooming the view.
*/
public class PanZoomTool extends AbstractTool {
/**
Construct a PanZoomTool.
@param editor The editor instance.
*/
public PanZoomTool(GMLEditor editor) {
super(editor);
}
@Override
public String getName() {
return "Pan/Zoom";
}
@Override
public void activate() {
}
@Override
public void deactivate() {
}
}
\ No newline at end of file
package maps.gml.editor;
import java.awt.Window;
import java.awt.Dialog;
import java.awt.BorderLayout;
import javax.swing.JDialog;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import rescuecore2.log.Logger;
/**
Abstract base class for Function implementations that require a progress dialog.
*/
public abstract class ProgressFunction extends AbstractFunction {
private JProgressBar progress;
/**
Construct a ProgressFunction.
@param editor The editor instance.
*/
protected ProgressFunction(GMLEditor editor) {
super(editor);
progress = new JProgressBar();
progress.setStringPainted(true);
}
@Override
public void execute() {
final JDialog dialog = new JDialog((Window)editor.getViewer().getTopLevelAncestor(), getTitle(), Dialog.ModalityType.APPLICATION_MODAL);
dialog.getContentPane().add(progress, BorderLayout.CENTER);
dialog.pack();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progress.setValue(0);
progress.setIndeterminate(true);
}
});
Thread t = new Thread() {
@Override
public void run() {
try {
executeImpl();
}
// CHECKSTYLE:OFF:IllegalCatch
catch (RuntimeException e) {
// CHECKSTYLE:ON:IllegalCatch
Logger.error("Error running " + this, e);
}
dialog.setVisible(false);
dialog.dispose();
}
};
t.start();
dialog.setVisible(true);
}
/**
Execute the function.
*/
protected abstract void executeImpl();
/**
Get the title for the progress dialog.
@return The dialog title.
*/
protected abstract String getTitle();
/**
Set the progress level.
@param amount The new progress.
*/
protected void setProgress(final int amount) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progress.setValue(amount);
progress.setString(progress.getValue() + " / " + progress.getMaximum());
}
});
}
/**
Increase the progress level by one.
*/
protected void bumpProgress() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progress.setValue(progress.getValue() + 1);
progress.setString(progress.getValue() + " / " + progress.getMaximum());
}
});
}
/**
Increase the maximum progress level by one.
*/
protected void bumpMaxProgress() {
bumpMaxProgress(1);
}
/**
Increase the maximum progress level by some amount.
@param amount The amount to increase the maximum progress level.
*/
protected void bumpMaxProgress(final int amount) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progress.setMaximum(progress.getMaximum() + amount);
progress.setString(progress.getValue() + " / " + progress.getMaximum());
}
});
}
/**
Set the progress maximum.
@param max The new progress maximum.
*/
protected void setProgressLimit(final int max) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progress.setIndeterminate(false);
progress.setMaximum(max);
progress.setString(progress.getValue() + " / " + progress.getMaximum());
}
});
}
/**
Set the progress string.
@param s The new progress string.
*/
protected void setProgressString(final String s) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progress.setString(s);
}
});
}
}
\ No newline at end of file
package maps.gml.editor;
import javax.swing.undo.AbstractUndoableEdit;
import java.util.HashSet;
import java.util.Collection;
import maps.gml.GMLEdge;
import rescuecore2.log.Logger;
/**
A function for pruning edges that are not attached to any shapes.
*/
public class PruneOrphanEdgesFunction extends AbstractFunction {
/**
Construct a PruneOrphanEdgesFunction.
@param editor The editor instance.
*/
public PruneOrphanEdgesFunction(GMLEditor editor) {
super(editor);
}
@Override
public String getName() {
return "Prune orphaned edges";
}
@Override
public void execute() {
// Go through all edges and remove any that are not attached to shapes.
final Collection<GMLEdge> remaining = new HashSet<GMLEdge>(editor.getMap().getEdges());
final Collection<GMLEdge> deleted = new HashSet<GMLEdge>();
for (GMLEdge next : remaining) {
if (editor.getMap().getAttachedShapes(next).isEmpty()) {
editor.getMap().removeEdge(next);
deleted.add(next);
}
}
if (!deleted.isEmpty()) {
editor.setChanged();
editor.getViewer().repaint();
}
Logger.debug("Removed " + deleted.size() + " edges");
editor.addEdit(new DeleteEdgesEdit(deleted));
}
private class DeleteEdgesEdit extends AbstractUndoableEdit {
private Collection<GMLEdge> edges;
public DeleteEdgesEdit(Collection<GMLEdge> edges) {
this.edges = edges;
}
@Override
public void undo() {
super.undo();
editor.getMap().add(edges);
editor.getViewer().repaint();
}
@Override
public void redo() {
super.redo();
editor.getMap().remove(edges);
editor.getViewer().repaint();
}
}
}
\ No newline at end of file
package maps.gml.editor;
import javax.swing.undo.AbstractUndoableEdit;
import java.util.HashSet;
import java.util.Collection;
import maps.gml.GMLNode;
import rescuecore2.log.Logger;
/**
A function for pruning nodes that are not attached to any edges.
*/
public class PruneOrphanNodesFunction extends AbstractFunction {
/**
Construct a PruneOrphanNodesFunction.
@param editor The editor instance.
*/
public PruneOrphanNodesFunction(GMLEditor editor) {
super(editor);
}
@Override
public String getName() {
return "Prune orphaned nodes";
}
@Override
public void execute() {
// Go through all nodes and remove any that are not attached to edges.
final Collection<GMLNode> remaining = new HashSet<GMLNode>(editor.getMap().getNodes());
final Collection<GMLNode> deleted = new HashSet<GMLNode>();
for (GMLNode next : remaining) {
if (editor.getMap().getAttachedEdges(next).isEmpty()) {
editor.getMap().removeNode(next);
deleted.add(next);
}
}
if (!deleted.isEmpty()) {
editor.setChanged();
editor.getViewer().repaint();
}
Logger.debug("Removed " + deleted.size() + " nodes");
editor.addEdit(new DeleteNodesEdit(deleted));
}
private class DeleteNodesEdit extends AbstractUndoableEdit {
private Collection<GMLNode> nodes;
public DeleteNodesEdit(Collection<GMLNode> nodes) {
this.nodes = nodes;
}
@Override
public void undo() {
super.undo();
editor.getMap().add(nodes);
editor.getViewer().repaint();
}
@Override
public void redo() {
super.redo();
editor.getMap().remove(nodes);
editor.getViewer().repaint();
}
}
}
\ No newline at end of file
package maps.gml.editor;
import javax.swing.JOptionPane;
import maps.ScaleConversion;
/**
A function for scaling the map.
*/
public class ScaleFunction extends AbstractFunction {
/**
Construct a ScaleFunction.
@param editor The editor instance.
*/
public ScaleFunction(GMLEditor editor) {
super(editor);
}
@Override
public String getName() {
return "Scale map";
}
@Override
public void execute() {
String s = JOptionPane.showInputDialog("Enter scale factor");
if (s != null) {
try {
double factor = Double.parseDouble(s);
editor.getMap().convertCoordinates(new ScaleConversion(editor.getMap().getMinX(), editor.getMap().getMinY(), factor, factor));
editor.setChanged();
}
catch (NumberFormatException e) {
e.printStackTrace();
}
}
}
}
\ No newline at end of file
package maps.gml.editor;
import maps.gml.GMLCoordinates;
/**
Class for snapping coordinates to a grid.
*/
public class Snap {
private static final double DEFAULT_RESOLUTION = 0.001;
private double resolution;
private boolean enabled;
/**
Create a disabled Snap with default resolution.
*/
public Snap() {
resolution = DEFAULT_RESOLUTION;
enabled = false;
}
/**
Set the resolution.
@param d The new resolution.
*/
public void setResolution(double d) {
resolution = d;
}
/**
Get the resolution.
@return The resolution.
*/
public double getResolution() {
return resolution;
}
/**
Set whether to enable snapping.
@param b Whether to enable snapping.
*/
public void setEnabled(boolean b) {
enabled = b;
}
/**
Find out if this Snap is enabled.
@return True if enabled.
*/
public boolean isEnabled() {
return enabled;
}
/**
Snap a set of coordinates to the grid.
@param c The coordinates to snap.
*/
public void snap(GMLCoordinates c) {
if (!enabled) {
return;
}
double x = round(c.getX());
double y = round(c.getY());
c.setX(x);
c.setY(y);
}
private double round(double d) {
// CHECKSTYLE:OFF:MagicNumber
return Math.floor((d / resolution) + 0.5) * resolution;
// CHECKSTYLE:ON:MagicNumber
}
}
\ No newline at end of file
package maps.gml.editor;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.Color;
import java.awt.Point;
import java.awt.Insets;
import maps.gml.view.NodeDecorator;
import maps.gml.view.SquareNodeDecorator;
import maps.gml.view.EdgeDecorator;
import maps.gml.view.LineEdgeDecorator;
import maps.gml.GMLNode;
import maps.gml.GMLEdge;
import maps.gml.GMLCoordinates;
import maps.gml.GMLTools;
import rescuecore2.misc.geometry.Point2D;
import rescuecore2.misc.geometry.Line2D;
import rescuecore2.misc.geometry.GeometryTools2D;
/**
A tool for splitting edges.
*/
public class SplitEdgeTool extends AbstractTool {
private static final Color EDGE_COLOUR = Color.BLUE;
private static final Color NODE_COLOUR = Color.BLACK;
private static final int NODE_SIZE = 6;
private Listener listener;
private NodeDecorator nodeHighlight;
private EdgeDecorator edgeHighlight;
private GMLNode node;
private GMLEdge edge;
/**
Construct a SplitEdgeTool.
@param editor The editor instance.
*/
public SplitEdgeTool(GMLEditor editor) {
super(editor);
listener = new Listener();
nodeHighlight = new SquareNodeDecorator(NODE_COLOUR, NODE_SIZE);
edgeHighlight = new LineEdgeDecorator(EDGE_COLOUR);
edge = null;
node = null;
}
@Override
public String getName() {
return "Split edge";
}
@Override
public void activate() {
editor.getViewer().addMouseListener(listener);
editor.getViewer().addMouseMotionListener(listener);
node = null;
edge = null;
}
@Override
public void deactivate() {
editor.getViewer().removeMouseListener(listener);
editor.getViewer().removeMouseMotionListener(listener);
editor.getViewer().clearAllNodeDecorators();
editor.getViewer().clearAllEdgeDecorators();
editor.getViewer().repaint();
if (node != null) {
editor.getMap().removeNode(node);
}
}
private void update(GMLCoordinates c) {
if (node == null) {
node = editor.getMap().createNode(c);
editor.getViewer().setNodeDecorator(nodeHighlight, node);
}
GMLEdge newEdge = editor.getMap().findNearestEdge(c.getX(), c.getY());
if (newEdge != edge) {
if (edge != null) {
editor.getViewer().clearEdgeDecorator(edge);
}
edge = newEdge;
editor.getViewer().setEdgeDecorator(edgeHighlight, edge);
}
// Snap the node coordinates to the edge
Line2D line = GMLTools.toLine(edge);
Point2D point = new Point2D(c.getX(), c.getY());
Point2D closest = GeometryTools2D.getClosestPointOnSegment(line, point);
c.setX(closest.getX());
c.setY(closest.getY());
node.setCoordinates(c);
editor.getViewer().repaint();
}
private void split() {
if (node == null || edge == null) {
return;
}
editor.getMap().splitEdge(edge, node);
editor.getMap().removeEdge(edge);
editor.setChanged();
editor.getViewer().clearAllNodeDecorators();
editor.getViewer().clearAllEdgeDecorators();
editor.getViewer().repaint();
node = null;
edge = null;
}
private class Listener implements MouseListener, MouseMotionListener {
@Override
public void mouseMoved(MouseEvent e) {
Point p = fixEventPoint(e.getPoint());
GMLCoordinates c = editor.snap(editor.getViewer().getCoordinatesAtPoint(p.x, p.y));
update(c);
}
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
split();
}
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseDragged(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
private Point fixEventPoint(Point p) {
Insets insets = editor.getViewer().getInsets();
return new Point(p.x - insets.left, p.y - insets.top);
}
}
}
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment