package oripa.paint.creasepattern; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.Map; import javax.vecmath.Vector2d; import oripa.value.OriLine; /** * For a fast access to vertex * @author koji * */ public class VerticesManager { /* * divides paper equally in order to localize access to vertices */ static public final int divNum = 32; public double interval; public double paperCenter; /** * the index of divided paper area. * A given point is converted to the index it should belongs to. * * @author Koji * */ public class AreaPosition{ public int x, y; /** * doubles point to index */ public AreaPosition(Vector2d v) { x = toDiv(v.x); y = toDiv(v.y); } public AreaPosition(double x, double y){ this.x = toDiv(x); this.y = toDiv(y); } } /** * Computes a index on one axis. * @param p * @return */ private int toDiv(double p){ int div = (int) ((p + paperCenter) / interval); if(div < 0){ return 0; } if(div >= divNum){ return divNum-1; } return div; } /** * [div_x][div_y] is a vertices in the divided area. */ private HashSet<Vector2d>[][] vertices = new HashSet[divNum][divNum]; /** * count existence of same values. */ private Map<Vector2d, Integer> counts = new HashMap<>(); /** * Constructor to initialize fields. * @param paperSize paper size in double. */ public VerticesManager(double paperSize) { changePaperSize(paperSize); // allocate memory for each area for(int x = 0; x < divNum; x++){ for(int y = 0; y < divNum; y++){ vertices[x][y] = new HashSet<Vector2d>(); } } } public void changePaperSize(double paperSize) { interval = paperSize / divNum; paperCenter = paperSize/2; } /** * remove all vertices. */ public void clear(){ for(int x = 0; x < divNum; x++){ for(int y = 0; y < divNum; y++){ vertices[x][y].clear(); } } counts.clear(); } /** * return vertices in the specified area. * @param ap index of area. * @return vertices in the specified area. */ private HashSet<Vector2d> getVertices(AreaPosition ap){ return vertices[ap.x][ap.y]; } /** * add given vertex to appropriate area. * @param v vertex to be managed by this class. */ public void add(Vector2d v){ HashSet<Vector2d> vertices = getVertices(new AreaPosition(v)); // v is a new value if (vertices.add(v)) { counts.put(v, 1); return; } // count duplication. Integer count = counts.get(v); counts.put(v, count+1); } /** * returns vertices in the area which the given vertex belongs to. * @param v vertex * @return */ public Collection<Vector2d> getAround(Vector2d v){ AreaPosition ap = new AreaPosition(v); return getVertices(ap); } /** * remove the given vertex from this class. * @param v */ public void remove(Vector2d v){ AreaPosition ap = new AreaPosition(v); Integer count = counts.get(v); // should never happen. if (count <= 0) { throw new IllegalStateException("Nothing to remove"); } // No longer same vertices exist.s if (count == 1) { getVertices(ap).remove(v); counts.remove(v); return; } // decrement existence. counts.put(v, count-1); } /** * similar to #getAround(). this method returns some areas in a large rectangle * (x-distanse, y-distance, x+distance, y+distance). * @param x * @param y * @param distance * @return */ public Collection<Collection<Vector2d>> getArea( double x, double y, double distance){ Collection<Collection<Vector2d>> result = new LinkedList<>(); int leftDiv = toDiv(x - distance); int rightDiv = toDiv(x + distance); int topDiv = toDiv(y - distance); int bottomDiv = toDiv(y + distance); for(int xDiv = leftDiv; xDiv <= rightDiv; xDiv++){ for(int yDiv = topDiv; yDiv <= bottomDiv; yDiv++){ result.add(vertices[xDiv][yDiv]); } } return result; } /** * set all vertices of given lines * @param lines */ public void load(Collection<OriLine> lines){ this.clear(); for(OriLine line : lines){ add(line.p0); add(line.p1); } } public boolean isEmpty() { for (HashSet<Vector2d>[] vertexSets : vertices) { for (HashSet<Vector2d> vertexSet : vertexSets) { if (! vertexSet.isEmpty()) { return false; } } } return true; } }