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;
}
}