/* * 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 com.trickl.graph.BreadthFirstSearch; import com.trickl.graph.HopCountVisitor; import com.trickl.graph.SpanningSearchVisitor; import com.vividsolutions.jts.geom.Coordinate; import java.util.HashMap; import java.util.Map; /* * This algorithm is described in the following paper: * Anchor-Free Distributed Localization in Sensor Networks * Nissanka B. Priyantha, Hari Balakrishnan, Erik Demaine, and Seth Teller * Tech Report #892, April 15, 2003 * MIT Laboratory for Computer Science * http://nms.lcs.mit.edu/cricket/ * */ public class FoldFreeLayout<V, E> implements PlanarLayout<V> { public static class LastVertexVisitor<V, E> implements SpanningSearchVisitor<V, E> { V lastVertex; public LastVertexVisitor() { } @Override public void initializeVertex(V u) { } @Override public void startVertex(V u) { } @Override public void discoverVertex(V u) { } @Override public void examineEdge(V source, V target) { } @Override public void treeEdge(V source, V target) { } @Override public void backEdge(V source, V target) { } @Override public void forwardOrCrossEdge(V source, V target) { } @Override public void finishVertex(V u) { this.lastVertex = u; } public V getLastVertex() { return lastVertex; } } private PlanarGraph<V, E> graph; private BreadthFirstSearch<V, E> breadthFirstSearch; private double scale = 100; private Coordinate centre; private V initialVertex; public FoldFreeLayout(PlanarGraph<V, E> graph) { this(graph, null); } public FoldFreeLayout(PlanarGraph<V, E> graph, V initialVertex) { this.graph = graph; this.breadthFirstSearch = new BreadthFirstSearch<V, E>(graph); this.centre = new Coordinate(0, 0); if (initialVertex == null && !graph.vertexSet().isEmpty()) { this.initialVertex = graph.vertexSet().iterator().next(); } else { this.initialVertex = initialVertex; } } Map<V, Coordinate> vertexLocations; public double getScale() { return scale; } public void setScale(double scale) { this.scale = scale; } public Coordinate getCentre() { return centre; } public void setCentre(Coordinate centre) { this.centre = centre; } @Override public Coordinate getCoordinate(V vertex) { if (vertexLocations == null) layout(); return vertexLocations.get(vertex); } private void layout() { if (initialVertex == null) return; // Select [n1] to maximize h[0, 1] LastVertexVisitor<V, E> lastVertexVisitor = new LastVertexVisitor<V, E>(); breadthFirstSearch.traverse(initialVertex, lastVertexVisitor); V n1 = lastVertexVisitor.getLastVertex(); // Select [n2] to maximize h[1, 2] Map<V, Integer> h1 = new HashMap<V, Integer>(); breadthFirstSearch.traverse(n1, lastVertexVisitor); breadthFirstSearch.traverse(n1, new HopCountVisitor<V, E>(h1)); V n2 = lastVertexVisitor.getLastVertex(); // Select [n3] to minimize h[1,3] - h[2,3], while maximizing h[1,3] + h[2, 3] in a tie Map<V, Integer> h2 = new HashMap<V, Integer>(); breadthFirstSearch.traverse(n2, new HopCountVisitor<V, E>(h2)); V n3 = n1; int h1323minDiff = Integer.MAX_VALUE; int h1323maxSum = 0; for (V v : graph.vertexSet()) { Integer h13 = h1.get(v); Integer h23 = h2.get(v); if (h13 != null || h23 != null) { int h1323diff = Math.abs(h13 - h23); int h1323sum = h13 + h23; if (h1323diff < h1323minDiff || (h1323diff == h1323minDiff && h1323sum > h1323maxSum)) { h1323minDiff = h1323diff; h1323maxSum = h1323sum; n3 = v; } } } // Select [n4] to minimize h[1,4] - h[2,4], while maximizing h[3,4] in a tie Map<V, Integer> h3 = new HashMap<V, Integer>(); breadthFirstSearch.traverse(n3, new HopCountVisitor<V, E>(h3)); V n4 = n1; int h1424minDiff = Integer.MAX_VALUE; int h34max = 0; for (V v : graph.vertexSet()) { Integer h14 = h1.get(v); Integer h24 = h2.get(v); if (h14 != null && h24 != null) { int h1424_diff = Math.abs(h14 - h24); int h34 = h3.get(v); if (h1424_diff < h1424minDiff || (h1424_diff == h1424minDiff && h34 > h34max)) { h1424minDiff = h1424_diff; h34max = h34; n4 = v; } } } // Select [n5] to minimize h[1,5] - h[2,5], while minimizing h[3,5] - h[4,5] in a tie Map<V, Integer> h4 = new HashMap<V, Integer>(); breadthFirstSearch.traverse(n4, new HopCountVisitor<V, E>(h4)); V n5 = n1; int h1525minDiff = Integer.MAX_VALUE; int h3545minDiff = Integer.MAX_VALUE; for (V v : graph.vertexSet()) { Integer h15 = h1.get(v); Integer h25 = h2.get(v); Integer h35 = h3.get(v); Integer h45 = h4.get(v); if (h15 != null & h25 != null && h35 != null && h45 != null) { int h1525diff = Math.abs(h15 - h25); int h3545diff = Math.abs(h35 - h45); if (h1525diff < h1525minDiff || (h1525diff == h1525minDiff && h3545diff < h3545minDiff)) { h1525minDiff = h1525diff; h3545minDiff = h3545diff; n5 = v; } } } // Get the hop_counts from [n5] Map<V, Integer> h5 = new HashMap<V, Integer>(); breadthFirstSearch.traverse(n5, new HopCountVisitor<V, E>(h5)); vertexLocations = new HashMap<V, Coordinate>(); for (V v : graph.vertexSet()) { double radius = h5.get(v) * scale; double theta = Math.atan2((double) h2.get(v) - (double) h1.get(v), (double) h4.get(v) - (double) h3.get(v)); vertexLocations.put(v, new Coordinate(centre.x + (radius * Math.cos(theta)), centre.y + (radius * Math.sin(theta)))); } } }