// ********************************************************************** // // <copyright> // // BBN Technologies // 10 Moulton Street // Cambridge, MA 02138 // (617) 873-8000 // // Copyright (C) BBNT Solutions LLC. All rights reserved. // // </copyright> // ********************************************************************** // // $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/util/FanCompress.java,v $ // $RCSfile: FanCompress.java,v $ // $Revision: 1.4 $ // $Date: 2009/01/21 01:24:42 $ // $Author: dietrick $ // // ********************************************************************** package com.bbn.openmap.util; import com.bbn.openmap.MoreMath; /** * Class to perform fan compression on points. * <h3>FAN COMPRESSION</h3> * <ul> * <li>R.A. Fowell, and D.D. McNeil, Faster Plots by Fan * Data-Compression, IEEE Computer Graphics & Applications, 1989, * 58-66. * <li>See also, J.G. Dunham, Optimum uniformr piecewise linear * approximation of planar curves, IEEE Trans. Pat. Anal and Mach. * Intel, Vol, PAMI-8, No 1, 1986, p. 67-75. * </ul> */ public abstract class FanCompress { public static class FanPoint { public double x; public double y; } /** * FanCompress class for float values. */ public static class FloatCompress extends FanCompress { private int read = 0; private int write = 0; protected double[] array; protected double zero_eps = 0.0001; /** * Construct a FanCompress object which deals in floats. * * @param array float[] array of coordinate pairs. */ public FloatCompress(double[] array) { this.array = array; } /** * Get the next point. * * @param p FanPoint * @return boolean false if no more points. */ public boolean next_point(FanPoint p) { if (read + 1 < array.length) { p.x = (double) array[read++]; p.y = (double) array[read++]; return true; } else { return false; } } /** * Save coordinates. This routine coalesces adjacent points * which have the same coordinates. * * @param x coordinate * @param y coordinate */ public void save_point(double x, double y) { if (write + 1 < read) { // coalesce points if ((write > 1) && MoreMath.approximately_equal((float) x, array[write - 2], zero_eps) && MoreMath.approximately_equal((float) y, array[write - 1], zero_eps)) { return; } array[write++] = (float) x; array[write++] = (float) y; } else { System.err.println("FanCompress.FloatCompress.save_point(): " + "ignoring extra..."); } } /** * Save only unique coordinates. If several points are the * same, then coalesce them into one point. * * @param epsilon threshold used to determine uniqueness of * coordinates. */ public void set_coalesce_points(double epsilon) { zero_eps = (float) epsilon; } /** * Return a copy of the internal array. Invoke this method * after running <code>fan_compress()</code> on this object. * * @return float[] */ public double[] getArray() { double[] ret_val = new double[write]; System.arraycopy(array, 0, ret_val, 0, write); return ret_val; } } /** * Get the next point. * * @param p FanPoint * @return boolean false if no more points. */ public abstract boolean next_point(FanPoint p); /** * Save coordinates. * * @param x coordinate * @param y coordinate */ public abstract void save_point(double x, double y); /** * Save only unique coordinates. If several points are the same, * then coalesce them into one point. * * @param epsilon threshold used to determine uniqueness of * coordinates. */ public abstract void set_coalesce_points(double epsilon); /** * Perform fan compression. IF there is only 1 point, save-point * will be run on it twice, sorry. * * @param fan FanCompress object * @param epsilon double */ public static final void fan_compress(FanCompress fan, double epsilon) { double epsilon_squared; /* epsilon * epsilon */ double p0x, p0y; /* Start of segment. */ FanPoint p = new FanPoint(); // double px, py; /* Current point x,y */ epsilon_squared = epsilon * epsilon; if (!fan.next_point(p)) /* Get first point */ return; fan.save_point(p.x, p.y); /* Save it. */ p0x = p.x; /* p0 <- p. */ p0y = p.y; while (true) { double distance; double pu; double ux, uy; double vx, vy; double f1, f2, f3; double xok, yok; /* Find first point > epsilon from p0. */ while (true) { double dx; double dy; dx = p.x - p0x; dy = p.y - p0y; distance = dx * dx + dy * dy; if (distance > epsilon_squared) break; if (!fan.next_point(p)) { // goto finish; fan.save_point(p.x, p.y); /* Save last point. */ return; } } distance = Math.sqrt(distance); pu = distance; /* U component of p. */ ux = (p.x - p0x) / pu; /* U unit vector. */ uy = (p.y - p0y) / pu; vx = -uy; /* V unit vector. */ vy = ux; f1 = pu; /* Region. */ f2 = epsilon / pu; f3 = -f2; xok = p.x; /* Last point we know is OK. */ yok = p.y; while (true) { double dx, dy; double /* pu, */pv; boolean keep; if (!fan.next_point(p)) { // goto finish; fan.save_point(p.x, p.y); /* Save last point. */ return; } keep = true; dx = p.x - p0x; dy = p.y - p0y; pu = ux * dx + uy * dy; pv = vx * dx + vy * dy; if (pu >= f1) { /* Still moving away from p0? */ double slope; slope = pv / pu; if (slope <= f2 && slope >= f3) { double temp; /* Still in region? */ double dslope; dslope = epsilon / pu; keep = false; f1 = pu; /* Adjust region. */ temp = slope + dslope; if (temp < f2) f2 = temp; /* Min. */ temp = slope - dslope; if (temp > f3) f3 = temp; /* Max. */ } } if (keep) { fan.save_point(xok, yok); p0x = xok; /* p0 <- pOK. */ p0y = yok; break; } else { xok = p.x; yok = p.y; } } } // System.out.println("should not reach here"); // finish: // // save_point(p.x, p.y); /* Save last point. */ // return; } }