/* * Copyright © 2009-2011 Rebecca G. Bettencourt / Kreative Software * <p> * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * <a href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a> * <p> * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * <p> * Alternatively, the contents of this file may be used under the terms * of the GNU Lesser General Public License (the "LGPL License"), in which * case the provisions of LGPL License are applicable instead of those * above. If you wish to allow use of your version of this file only * under the terms of the LGPL License and not to allow others to use * your version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the LGPL License. If you do not delete * the provisions above, a recipient may use your version of this file * under either the MPL or the LGPL License. * @since PowerPaint 1.0 * @author Rebecca G. Bettencourt, Kreative Software */ package com.kreative.paint.geom; import java.awt.geom.GeneralPath; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Vector; public class PathGraph { private static final int size = 10; private HashMap<ImmutablePoint, HashSet<ImmutablePoint>> graph; public PathGraph() { graph = new HashMap<ImmutablePoint, HashSet<ImmutablePoint>>(); } public PathGraph(PathGraph pg) { graph = new HashMap<ImmutablePoint, HashSet<ImmutablePoint>>(); for (Map.Entry<ImmutablePoint, HashSet<ImmutablePoint>> e : pg.graph.entrySet()) { HashSet<ImmutablePoint> s = new HashSet<ImmutablePoint>(); s.addAll(e.getValue()); graph.put(e.getKey(), s); } } public void plot(int x, int y) { ImmutablePoint bl = new ImmutablePoint(x*size, y*size); ImmutablePoint tl = new ImmutablePoint(x*size, y*size+size); ImmutablePoint tr = new ImmutablePoint(x*size+size, y*size+size); ImmutablePoint br = new ImmutablePoint(x*size+size, y*size); add(bl, tl); add(tl, tr); add(tr, br); add(br, bl); } public GeneralPath makePath() { removeOverlap(); simplifyPaths(); GeneralPath p = new GeneralPath(); ImmutablePoint[][] contours = getContours(); for (ImmutablePoint[] contour : contours) { p.moveTo((float)contour[0].x/(float)size, (float)contour[0].y/(float)size); for (int i = 1; i < contour.length; i++) { p.lineTo((float)contour[i].x/(float)size, (float)contour[i].y/(float)size); } p.closePath(); } return p; } private void add(ImmutablePoint src, ImmutablePoint dst) { if (graph.containsKey(src)) { graph.get(src).add(dst); } else { HashSet<ImmutablePoint> s = new HashSet<ImmutablePoint>(); s.add(dst); graph.put(src, s); } } private boolean contains(ImmutablePoint src, ImmutablePoint dst) { return (graph.containsKey(src) && graph.get(src).contains(dst)); } private boolean isEmpty() { if (!graph.isEmpty()) { for (HashSet<ImmutablePoint> s : graph.values()) { if (!s.isEmpty()) return false; } } return true; } private void remove(ImmutablePoint src, ImmutablePoint dst) { if (graph.containsKey(src)) { graph.get(src).remove(dst); if (graph.get(src).size() < 1) { graph.remove(src); } } } private ImmutablePoint[] getAllPoints() { HashSet<ImmutablePoint> s = new HashSet<ImmutablePoint>(); s.addAll(graph.keySet()); for (HashSet<ImmutablePoint> t : graph.values()) { s.addAll(t); } return s.toArray(new ImmutablePoint[0]); } private ImmutablePoint[] getAdjPoints(ImmutablePoint src) { if (graph.containsKey(src)) { return graph.get(src).toArray(new ImmutablePoint[0]); } else { return new ImmutablePoint[0]; } } private ImmutablePoint[][] getContours() { Vector<Vector<ImmutablePoint>> v = new Vector<Vector<ImmutablePoint>>(); PathGraph tmp = new PathGraph(this); while (!tmp.isEmpty()) { ImmutablePoint[] firsts = tmp.getAllPoints(); for (ImmutablePoint first : firsts) { if (tmp.graph.containsKey(first) && !tmp.graph.get(first).isEmpty()) { Vector<ImmutablePoint> w = new Vector<ImmutablePoint>(); int lastdx = 0, lastdy = 0; ImmutablePoint p = first; w.add(p); while (true) { ImmutablePoint[] candidates = tmp.getAdjPoints(p); if (candidates == null || candidates.length < 1) break; ImmutablePoint q = null; if (lastdx == 0 && lastdy == 0) { q = candidates[0]; } else { // looking for point going counterclockwise for (ImmutablePoint candidate : candidates) { int dy = signum(candidate.y - p.y); int dx = signum(candidate.x - p.x); if (dy == -lastdx && dx == lastdy && !candidate.equals(p)) { q = candidate; break; } } // looking for point going straight if (q == null) { for (ImmutablePoint candidate : candidates) { int dy = signum(candidate.y - p.y); int dx = signum(candidate.x - p.x); if (dy == lastdy && dx == lastdx && !candidate.equals(p)) { q = candidate; break; } } } // looking for point going clockwise if (q == null) { for (ImmutablePoint candidate : candidates) { int dy = signum(candidate.y - p.y); int dx = signum(candidate.x - p.x); if (dy == lastdx && dx == -lastdy && !candidate.equals(p)) { q = candidate; break; } } } if (q == null) { for (ImmutablePoint candidate : candidates) { if (!candidate.equals(p)) { q = candidate; break; } } } if (q == null) break; } w.add(q); tmp.remove(p, q); if (first.equals(q)) break; else { lastdy = signum(q.y - p.y); lastdx = signum(q.x - p.x); p = q; } } if (!w.isEmpty()) v.add(w); } } } ImmutablePoint[][] a = new ImmutablePoint[v.size()][]; for (int i = 0; i < v.size(); i++) { a[i] = v.get(i).toArray(new ImmutablePoint[0]); } return a; } private void removeOverlap() { Iterator<ImmutablePoint> i = graph.keySet().iterator(); while (i.hasNext()) { ImmutablePoint src = i.next(); Iterator<ImmutablePoint> j = graph.get(src).iterator(); while (j.hasNext()) { ImmutablePoint dst = j.next(); if (contains(dst, src)) { remove(src, dst); remove(dst, src); i = graph.keySet().iterator(); break; } } } } private void simplifyPaths() { Iterator<ImmutablePoint> i = graph.keySet().iterator(); while (i.hasNext()) { ImmutablePoint src = i.next(); Iterator<ImmutablePoint> j = graph.get(src).iterator(); while (j.hasNext()) { ImmutablePoint dst = j.next(); int yd = signum(dst.y - src.y); int xd = signum(dst.x - src.x); ImmutablePoint newdst = dst; boolean foundEnd = false; while (!foundEnd) { foundEnd = true; ImmutablePoint[] candidates = getAdjPoints(newdst); for (ImmutablePoint candidate : candidates) { int cyd = signum(candidate.y - newdst.y); int cxd = signum(candidate.x - newdst.x); if (cyd == yd && cxd == xd) { remove(newdst, candidate); newdst = candidate; foundEnd = false; } } } if (newdst != dst) { remove(src, dst); add(src, newdst); i = graph.keySet().iterator(); break; } } } } private static int signum(int v) { return (v < 0) ? -1 : (v > 0) ? 1 : 0; } private static class ImmutablePoint { private int x; private int y; public ImmutablePoint(int x, int y) { this.x = x; this.y = y; } } }