package org.geogebra.common.kernel.discrete; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.apache.commons.collections15.Transformer; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.GraphAlgo; import org.geogebra.common.kernel.MyPoint; import org.geogebra.common.kernel.SegmentType; import org.geogebra.common.kernel.algos.AlgoElement; import org.geogebra.common.kernel.commands.Commands; import org.geogebra.common.kernel.geos.GeoBoolean; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoList; import org.geogebra.common.kernel.geos.GeoLocus; import org.geogebra.common.kernel.geos.GeoSegment; import org.geogebra.common.kernel.kernelND.GeoPointND; import edu.uci.ics.jung.algorithms.shortestpath.DijkstraShortestPath; import edu.uci.ics.jung.graph.SparseMultigraph; import edu.uci.ics.jung.graph.util.EdgeType; public class AlgoShortestDistance extends AlgoElement implements GraphAlgo { GeoPointND start, end; GeoList inputList; GeoLocus locus; GeoBoolean weighted; protected ArrayList<MyPoint> al; public AlgoShortestDistance(Construction cons, String label, GeoList inputList, GeoPointND start, GeoPointND end, GeoBoolean weighted) { super(cons); this.inputList = inputList; this.start = start; this.end = end; this.weighted = weighted; locus = new GeoLocus(cons); setInputOutput(); compute(); locus.setLabel(label); } @Override protected void setInputOutput() { input = new GeoElement[4]; input[0] = inputList; input[1] = start.toGeoElement(); input[2] = end.toGeoElement(); input[3] = weighted; setOnlyOutput(locus); setDependencies(); // done by AlgoElement } public GeoLocus getResult() { return locus; } @Override public Commands getClassName() { return Commands.ShortestDistance; } // weighted Shortest Path // use length of segments to weight private Transformer<MyLink, Double> wtTransformer = new Transformer<MyLink, Double>() { @Override public Double transform(MyLink link) { return link.weight; } }; @Override public final void compute() { int size = inputList.size(); if (!inputList.isDefined() || !weighted.isDefined() || size == 0) { locus.setUndefined(); return; } edgeCount = 0; HashMap<GeoPointND, MyNode> nodes = new HashMap<GeoPointND, MyNode>(); SparseMultigraph<MyNode, MyLink> g = new SparseMultigraph<MyNode, MyLink>(); MyNode node1, node2, startNode = null, endNode = null; for (int i = 0; i < size; i++) { GeoElement geo = inputList.get(i); if (geo.isDefined() && geo.isGeoSegment()) { GeoSegment seg = (GeoSegment) geo; GeoPointND p1 = seg.getStartPoint(); GeoPointND p2 = seg.getEndPoint(); node1 = nodes.get(p1); node2 = nodes.get(p2); if (node1 == null) { node1 = new MyNode(p1); nodes.put(p1, node1); } if (node2 == null) { node2 = new MyNode(p2); nodes.put(p2, node2); } // take note of start and end points if (p1 == start) { startNode = node1; } else if (p1 == end) { endNode = node1; } if (p2 == start) { startNode = node2; } else if (p2 == end) { endNode = node2; } // add edge to graph g.addEdge(new MyLink(seg.getLength(), node1, node2), node1, node2, EdgeType.UNDIRECTED); } } if (al == null) { al = new ArrayList<MyPoint>(); } else { al.clear(); } if (startNode == null || endNode == null) { locus.setPoints(al); locus.setDefined(false); return; } DijkstraShortestPath<MyNode, MyLink> alg; if (weighted.getBoolean()) { alg = new DijkstraShortestPath<MyNode, MyLink>(g, wtTransformer); } else { // Unweighted Shortest Path alg = new DijkstraShortestPath<MyNode, MyLink>(g); } List<MyLink> list = alg.getPath(startNode, endNode); double inhom1[] = new double[2]; double inhom2[] = new double[2]; double inhomLast[] = new double[2]; MyNode n1, n2; MyLink link = list.get(0); n1 = link.n1; n2 = link.n2; // nodes may not be in the right order, might need n1 or n2 if (n1 == startNode) { n1.id.getInhomCoords(inhomLast); } else if (n2 == startNode) { n2.id.getInhomCoords(inhomLast); } else if (n1 == endNode) { n1.id.getInhomCoords(inhomLast); } else if (n2 == endNode) { n2.id.getInhomCoords(inhomLast); } MyPoint pt = new MyPoint(inhomLast[0], inhomLast[1], SegmentType.MOVE_TO); al.add(pt); for (int i = 0; i < list.size(); i++) { link = list.get(i); link.n1.id.getInhomCoords(inhom1); link.n2.id.getInhomCoords(inhom2); // nodes may not be in the right order, might need n1 or n2 if (inhom1[1] == inhomLast[1] && inhom1[0] == inhomLast[0]) { pt = new MyPoint(inhom2[0], inhom2[1], SegmentType.LINE_TO); inhomLast[0] = inhom2[0]; inhomLast[1] = inhom2[1]; } else { pt = new MyPoint(inhom1[0], inhom1[1], SegmentType.LINE_TO); inhomLast[0] = inhom1[0]; inhomLast[1] = inhom1[1]; } al.add(pt); } locus.setPoints(al); locus.setDefined(true); } protected int edgeCount = 0; class MyLink { protected MyNode n1, n2; double weight; int id; public MyLink(double weight, MyNode n1, MyNode n2) { this.id = edgeCount++; // This is defined in the outer class. this.weight = weight; this.n1 = n1; this.n2 = n2; } @Override public String toString() { // Always good for debugging return "Edge" + id; } } }