/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.bearsoft.routing; import com.bearsoft.routing.graph.Vertex; import java.awt.Rectangle; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; /** * * @author mg */ public class Sweeper { public static List<Vertex<PathFragment>> build(int width, int height, Set<Rectangle> obstacles, QuadTree aVertexIndex) { Sweeper sweeper = new Sweeper(); for (Rectangle o : obstacles) { sweeper.add(o); } sweeper.build(width, height, aVertexIndex); return sweeper.getGraph(); } private void removeSweeped(int aSweepStop, SortedMap<Integer, List<Segment>> aSweepLine) { List<Integer> toRemove = new ArrayList<>(); for (Entry<Integer, List<Segment>> el : aSweepLine.entrySet()) { for (int i = el.getValue().size() - 1; i >= 0; i--) { Segment s = el.getValue().get(i); if (aSweepStop > s.last) { el.getValue().remove(i); } } if (el.getValue().isEmpty()) { toRemove.add(el.getKey()); } } for (Integer key : toRemove) { aSweepLine.remove(key); } } private void addSweepers(SortedMap<Integer, List<Segment>> aDestination, SortedMap<Integer, List<Segment>> aSource) { for (Entry<Integer, List<Segment>> sourceEl : aSource.entrySet()) { assert sourceEl.getValue() != null; assert !sourceEl.getValue().isEmpty(); List<Segment> destSegs = aDestination.get(sourceEl.getKey()); if (destSegs == null) { destSegs = new ArrayList<>(); aDestination.put(sourceEl.getKey(), destSegs); } destSegs.addAll(sourceEl.getValue()); } } protected class Segment { // first and last y-coordinate, inclusive. public int start; public int end; // last x-coordinate of the segment, inclusive. public int last; public Segment(boolean aFirst) { } public Segment(int aStart, int aEnd, int aLast) { start = aStart; end = aEnd; last = aLast; } } public static final int TURN_COST_BIAS = 100; protected SortedMap<Integer, SortedMap<Integer, List<Segment>>> sweepedlayers = new TreeMap<>(); protected List<Vertex<PathFragment>> graph; protected Sweeper() { super(); } List<Vertex<PathFragment>> getGraph() { return graph; } void add(Rectangle o) { // add real segment to sweep stops add(o.x, new Segment(o.y >= 0 ? o.y : 0, (o.y >= 0 ? o.y : 0) + o.height, o.x + o.width - 1)); // give a segment a chance to be closed, while sweeping, by // adding another sweep stop, with may stay as a fake or // it may be also filled by real segments in future. add(o.x + o.width, null); } void add(int aLayer, Segment aValue) { SortedMap<Integer, List<Segment>> sweepedLayer = sweepedlayers.get(aLayer); if (sweepedLayer == null) { sweepedLayer = new TreeMap<>(); sweepedlayers.put(aLayer, sweepedLayer); } if (aValue != null) { List<Segment> sameStartedSegments = sweepedLayer.get(aValue.start); if (sameStartedSegments == null) { sameStartedSegments = new ArrayList<>(); sweepedLayer.put(aValue.start, sameStartedSegments); } sameStartedSegments.add(aValue); } } void build(int aRight, int aBottom, QuadTree aVertexIndex) { graph = new ArrayList<>(); add(0, null); //add(aRight, null); SortedMap<Integer, List<Segment>> sweepLine = new TreeMap<>(); List<Segment> bottomSegment = new ArrayList<>(); bottomSegment.add(new Segment(aBottom, aBottom, aRight)); sweepLine.put(aBottom, bottomSegment); SortedMap<Integer, Vertex<PathFragment>> prevFreeTiles = null; int prevSweepStop = 0; for (Entry<Integer, SortedMap<Integer, List<Segment>>> layer : sweepedlayers.entrySet()) { if (aRight < layer.getKey()) { aRight = layer.getKey(); } // Let's remove completly sweeped segments removeSweeped(layer.getKey(), sweepLine); // Let's add segments from new layer to sweepline addSweepers(sweepLine, layer.getValue()); // Let's find holes between generic filled segments int top = -1;// top of generic filled segment int bottom = -1;// bottom of generic filled segment // Holes between them will be free segments SortedMap<Integer, Vertex<PathFragment>> freeTiles = new TreeMap<>(); for (List<Segment> sameTopSegments : sweepLine.values()) { for (Segment s : sameTopSegments) { if (s.start - bottom > 1) { int freeTop = bottom + 1; int freeBottom = s.start - 1; assert freeTop <= freeBottom; Vertex<PathFragment> freeVertex = new Vertex<>(new PathFragment(new Rectangle(layer.getKey(), freeTop, 0, freeBottom - freeTop + 1))); freeTiles.put(freeTop, freeVertex); graph.add(freeVertex); top = s.start; bottom = s.end; } else { // max finding - bottom tracking if (s.end > bottom) { bottom = s.end; } assert s.start >= top; } } } if (prevFreeTiles != null) { for (Vertex<PathFragment> v : prevFreeTiles.values()) { v.attribute.rect.width = layer.getKey() - prevSweepStop; aVertexIndex.insert(v.attribute.rect, v); } } interconnect(prevFreeTiles, freeTiles); prevFreeTiles = freeTiles; prevSweepStop = layer.getKey(); } for (Vertex<PathFragment> v : prevFreeTiles.values()) { if (v.attribute.rect.width < aRight - prevSweepStop) { v.attribute.rect.width = aRight - prevSweepStop; aVertexIndex.insert(v.attribute.rect, v); } } } protected void interconnect(SortedMap<Integer, Vertex<PathFragment>> aLeftTiles, SortedMap<Integer, Vertex<PathFragment>> aRightTiles) { if (aLeftTiles != null && aRightTiles != null) { if (!aLeftTiles.isEmpty() && !aRightTiles.isEmpty()) { Vertex<PathFragment>[] left = aLeftTiles.values().toArray(new Vertex[]{}); Vertex<PathFragment>[] right = aRightTiles.values().toArray(new Vertex[]{}); int leftIdx = 0; int rightIdx = 0; while (leftIdx < left.length && rightIdx < right.length) { Vertex<PathFragment> leftVertex = left[leftIdx]; Vertex<PathFragment> rightVertex = right[rightIdx]; if (touches(leftVertex.attribute.rect, rightVertex.attribute.rect)) { leftVertex.getAjacent().add(rightVertex); rightVertex.getAjacent().add(leftVertex); } if (leftVertex.attribute.rect.y + leftVertex.attribute.rect.height < rightVertex.attribute.rect.y + rightVertex.attribute.rect.height) { leftIdx++; } else { rightIdx++; } } } } } private boolean touches(Rectangle aValue, Rectangle aValue1) { return aValue.y >= aValue1.y && aValue.y < aValue1.y + aValue1.height || aValue1.y >= aValue.y && aValue1.y < aValue.y + aValue.height; } /** * Computes planar metric of * * @param aValue * @param aValue1 * @return * private int edgeCost(Rectangle aValue, Rectangle aValue1) { int xLen = Math.abs(aValue.x + aValue.width / 2 - aValue1.x + aValue1.width / 2); int yLen = Math.abs(aValue.y + aValue.height / 2 - aValue1.y + aValue1.height / 2); if (xLen > 0 && yLen > 0) { return xLen + yLen + TURN_COST_BIAS; } else { return xLen + yLen; } } */ }