/*
* This file is part of the Trickl Open Source Libraries.
*
* Trickl Open Source Libraries - http://open.trickl.com/
*
* Copyright (C) 2011 Tim Gee.
*
* Trickl Open Source Libraries are free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Trickl Open Source Libraries are distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this project. If not, see <http://www.gnu.org/licenses/>.
*/
package com.trickl.graph.planar;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlIDREF;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
@XmlType(name = "dcel-vertex")
public class DcelVertex<V, E, F> implements Serializable {
private final static int ITERATION_LIMIT = 10000;
private DcelHalfEdge<V, E, F> leaving;
private V vertex;
public DcelVertex() {
this(null);
}
public DcelVertex(V vertex) {
this.vertex = vertex;
}
@XmlID
@XmlAttribute(name="id")
public String getIdString() {
return "dcel-" + (vertex == null ? "null" : vertex.toString());
}
protected void setId(String id) {
}
@XmlIDREF
@XmlAttribute(name = "leaving-half-edge-id")
public DcelHalfEdge<V, E, F> getLeaving() {
return leaving;
}
@XmlIDREF
@XmlAttribute(name = "data-id")
public V getVertex() {
return vertex;
}
public void setVertex(V vertex) {
this.vertex = vertex;
}
public void setLeaving(DcelHalfEdge<V, E, F> leaving) {
this.leaving = leaving;
if (leaving != null && leaving.getOrigin() == null) {
leaving.setOrigin(this);
}
}
public void invalidate() {
setLeaving(null);
}
@XmlTransient
public DcelHalfEdge<V, E, F> getNextBoundaryEdge() {
for (DcelHalfEdge<V, E, F> halfEdge : outHalfEdges()) {
if (halfEdge.isEdgeBoundary()) {
return halfEdge;
}
}
return null;
}
@XmlTransient
public DcelHalfEdge<V, E, F> getPrevBoundaryEdge() {
for (DcelHalfEdge<V, E, F> halfEdge : outHalfEdges()) {
if (halfEdge.isEdgeBoundary()) {
return halfEdge.getPrev();
}
}
return null;
}
@XmlTransient
public boolean isBoundary() {
for (DcelHalfEdge<V, E, F> halfEdge : outHalfEdges()) {
if (halfEdge.isBoundary()) {
return true;
}
}
return false;
}
@XmlTransient
public int getEdgeCount() {
int count = 0;
for (DcelHalfEdge<V, E, F> halfEdge : outHalfEdges()) {
++count;
}
return count;
}
public DcelHalfEdge<V, E, F> getHalfEdge(final V target) {
for (DcelHalfEdge<V, E, F> halfEdge : outHalfEdges()) {
if (halfEdge.getNext().getOrigin().getVertex().equals(target)) {
return halfEdge;
}
}
return null;
}
@XmlTransient
public Set<E> getEdges() {
final Set<E> edges = new LinkedHashSet<E>(getEdgeCount());
for (DcelHalfEdge<V, E, F> halfEdge : outHalfEdges()) {
E edge = halfEdge.getEdge();
if (edge != null) {
edges.add(edge);
}
}
return edges;
}
@XmlTransient
public List<V> getConnectedVertices() {
return getConnectedVertices(getLeaving());
}
public List<V> getConnectedVertices(DcelHalfEdge<V, E, F> startEdge) {
return getConnectedVertices(startEdge, startEdge);
}
public List<V> getConnectedVertices(DcelHalfEdge<V, E, F> startEdge, DcelHalfEdge<V, E, F> endEdge) {
final List<V> vertices = new ArrayList<V>(getEdgeCount());
for (DcelHalfEdge<V, E, F> halfEdge : outHalfEdges(startEdge, endEdge)) {
V edgeVertex = halfEdge.getTwin().getOrigin().getVertex();
if (edgeVertex != null) {
vertices.add(edgeVertex);
}
}
return vertices;
}
Iterable<DcelHalfEdge<V, E, F>> outHalfEdges() {
return outHalfEdges(getLeaving());
}
Iterable<DcelHalfEdge<V, E, F>> outHalfEdges(final DcelHalfEdge<V, E, F> startEdge) {
return outHalfEdges(startEdge, startEdge);
}
Iterable<DcelHalfEdge<V, E, F>> outHalfEdges(final DcelHalfEdge<V, E, F> startEdge, final DcelHalfEdge<V, E, F> endEdge) {
return new Iterable<DcelHalfEdge<V, E, F>>() {
@Override
public Iterator<DcelHalfEdge<V, E, F>> iterator() {
return new Iterator<DcelHalfEdge<V, E, F>>() {
DcelHalfEdge<V, E, F> next = startEdge;
int iteration = 0;
@Override
public boolean hasNext() {
return (next != null);
}
@Override
public DcelHalfEdge<V, E, F> next() {
DcelHalfEdge<V, E, F> current = next;
next = current.getTwin().getNext();
if (next == endEdge) {
next = null;
}
if (++iteration > ITERATION_LIMIT) {
throw new IndexOutOfBoundsException(
"Iteration beyond limit, suggests corrupt structure.");
}
return current;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported yet.");
}
};
}
};
}
// beforeEdge -> newEdge -> afterEdge
public DcelHalfEdge<V, E, F> addEdge(DcelVertex<V, E, F> target,
DcelHalfEdge<V, E, F> beforeEdge,
DcelHalfEdge<V, E, F> afterEdge,
DcelFace<V, E, F> boundaryFace,
FaceFactory<V, F> faceFactory,
E e) {
if (leaving != null
&& beforeEdge == null) {
// Source has edges, set the before edge
beforeEdge = leaving.getTwin();
}
if (target.leaving != null
&& afterEdge == null) {
// Target has edges, set the after edge
if (beforeEdge == null) {
afterEdge = target.leaving;
}
else {
for (DcelHalfEdge<V, E, F> halfEdge : target.outHalfEdges()) {
if (halfEdge.getFace().equals(beforeEdge.getFace())) {
afterEdge = halfEdge;
break;
}
}
if (afterEdge == null) {
// Must maintain planarity
throw new NoSuchElementException("Target, source and before must share a face.");
}
}
}
// Check if a closed face is formed by the addition of this edge
// i.e. is after edge already connected to before edge?
F createdFace = null;
if (afterEdge != null) {
for (DcelHalfEdge<V, E, F> halfEdge : afterEdge.edges())
{
if (halfEdge.equals(beforeEdge)) {
createdFace = faceFactory.createFace(this.getVertex(),
target.getVertex(),
false);
}
}
}
/* Everything seems okay, we should be able to create this edge */
DcelHalfEdge<V, E, F> createdEdge = new DcelHalfEdge<V, E, F>(e);
DcelHalfEdge<V, E, F> createdTwin = new DcelHalfEdge<V, E, F>(e);
createdEdge.setTwin(createdTwin);
// Set the edge next pointers
if (beforeEdge == null) {
createdTwin.setNext(createdEdge);
} else {
createdTwin.setNext(beforeEdge.getNext());
beforeEdge.setNext(createdEdge);
}
if (afterEdge == null) {
createdEdge.setNext(createdTwin);
} else {
createdEdge.setNext(afterEdge);
afterEdge.getPrev().setNext(createdTwin);
}
// Set the edge faces
if (beforeEdge == null && afterEdge == null) {
createdEdge.setFace(boundaryFace);
createdTwin.setFace(boundaryFace);
boundaryFace.setAdjacent(createdTwin);
} else if (beforeEdge == null) {
createdEdge.setFace(afterEdge.getFace());
createdTwin.setFace(afterEdge.getFace());
afterEdge.getFace().setAdjacent(createdTwin);
} else if (afterEdge == null) {
createdEdge.setFace(beforeEdge.getFace());
createdTwin.setFace(beforeEdge.getFace());
beforeEdge.getFace().setAdjacent(createdTwin);
} else {
createdTwin.setFace(createdEdge.getNext().getFace());
createdEdge.getNext().getFace().setAdjacent(createdTwin);
if (createdFace != null) {
// By convention, the before -> edge -> after all belong to the new face
// if one is formed
final DcelFace<V, E, F> dcelFace = new DcelFace<V, E, F>(createdFace);
for (DcelHalfEdge<V, E, F> halfEdge : createdEdge.edges()) {
halfEdge.setFace(dcelFace);
dcelFace.setAdjacent(halfEdge);
}
}
else {
createdEdge.setFace(createdTwin.getNext().getFace());
createdTwin.getNext().getFace().setAdjacent(createdEdge);
}
}
// Note that setting the origin will set the vertex
// to have it's leaving edge point to this edge, the most recently created edge.
// This is the behaviour we want when for when we don't specify the
// before edge.
createdEdge.setOrigin(this);
setLeaving(createdEdge);
createdTwin.setOrigin(target);
target.setLeaving(createdTwin);
return createdEdge;
}
public void remove() {
Set<DcelHalfEdge<V, E, F> > halfEdges = new LinkedHashSet<DcelHalfEdge<V, E, F> >();
// Get a copy of the edges as the iterator will be invalidated
for (DcelHalfEdge<V, E, F> halfEdge : outHalfEdges()) {
halfEdges.add(halfEdge);
}
for (DcelHalfEdge<V, E, F> halfEdge : halfEdges) {
halfEdge.remove();
}
invalidate();
}
@Override
public String toString() {
return "V(" + ((vertex == null) ? "NULL" : vertex.toString()) + ") E("
+ ((leaving == null) ? "NULL" : leaving.getId()) + ")";
}
@Override
public boolean equals(Object rhs) {
if (!(rhs instanceof DcelVertex)) {
return false;
}
{
return ((DcelVertex) rhs).getVertex().equals(vertex);
}
}
@Override
public int hashCode() {
int hash = 7;
hash = 97 * hash + (this.vertex != null ? this.vertex.hashCode() : 0);
return hash;
}
}