// Created by plusminus on 13:24:05 - 21.09.2008 package org.androad.osm.util; import java.util.Comparator; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; import org.androad.osm.util.constants.OSMConstants; public class Util implements OSMConstants { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== // =========================================================== // Constructors // =========================================================== // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods from SuperClass/Interfaces // =========================================================== // =========================================================== // Methods // =========================================================== /** * May crash when External-Media is not mounted. * @return path, like <code>"/sdcard/androad/"</code> always ending with a <code>"/"</code> */ public static final String getAndRoadExternalStoragePath(){ final String absoluteExternalPath = android.os.Environment.getExternalStorageDirectory().getAbsolutePath(); if(absoluteExternalPath.endsWith("/")) { return absoluteExternalPath + BASEPATH_ON_EXTERNAL_MEDIA; } else { return absoluteExternalPath + "/" + BASEPATH_ON_EXTERNAL_MEDIA; } } /** * Tests if the line segment from (X1, Y1) to (X2, Y2) intersects * the line segment from (X3, Y3) to (X4, Y4). * * @param X1 * , Y1 the coordinates of the beginning of the first * specified line segment * @param X2 * , Y2 the coordinates of the end of the first specified * line segment * @param X3 * , Y3 the coordinates of the beginning of the second * specified line segment * @param X4 * , Y4 the coordinates of the end of the second specified * line segment * @return <code>true</code> if the first specified line segment and the * second specified line segment intersect each other; * <code>false</code> otherwise. */ public static boolean linesIntersect(final int X1, final int Y1, final int X2, final int Y2, final int X3, final int Y3, final int X4, final int Y4) { return ((relativeCCW(X1, Y1, X2, Y2, X3, Y3) * relativeCCW(X1, Y1, X2, Y2, X4, Y4) <= 0) && (relativeCCW(X3, Y3, X4, Y4, X1, Y1) * relativeCCW(X3, Y3, X4, Y4, X2, Y2) <= 0)); } /** * Returns an indicator of where the specified point (PX, PY) lies with * respect to the line segment from (X1, Y1) to (X2, Y2). The * return value can be either 1, -1, or 0 and indicates in which direction * the specified line must pivot around its first endpoint, (X1, Y1), * in order to point at the specified point (PX, PY). * <p> * A return value of 1 indicates that the line segment must turn in the * direction that takes the positive X axis towards the negative Y axis. In * the default coordinate system used by Java 2D, this direction is * counterclockwise. * <p> * A return value of -1 indicates that the line segment must turn in the * direction that takes the positive X axis towards the positive Y axis. In * the default coordinate system, this direction is clockwise. * <p> * A return value of 0 indicates that the point lies exactly on the line * segment. Note that an indicator value of 0 is rare and not useful for * determining colinearity because of floating point rounding issues. * <p> * If the point is colinear with the line segment, but not between the * endpoints, then the value will be -1 if the point lies * "beyond (X1, Y1)" or 1 if the point lies "beyond (X2, Y2)". * * @param X1 * , Y1 the coordinates of the beginning of the specified * line segment * @param X2 * , Y2 the coordinates of the end of the specified line * segment * @param PX * , PY the coordinates of the specified point to be * compared with the specified line segment * @return an integer that indicates the position of the third specified * coordinates with respect to the line segment formed by the first * two specified coordinates. */ private static int relativeCCW(final int X1, final int Y1, int X2, int Y2, int PX, int PY) { X2 -= X1; Y2 -= Y1; PX -= X1; PY -= Y1; int ccw = PX * Y2 - PY * X2; if (ccw == 0) { // The point is colinear, classify based on which side of // the segment the point falls on. We can calculate a // relative value using the projection of PX,PY onto the // segment - a negative value indicates the point projects // outside of the segment in the direction of the particular // endpoint used as the origin for the projection. ccw = PX * X2 + PY * Y2; if (ccw > 0) { // Reverse the projection to be relative to the original X2,Y2 // X2 and Y2 are simply negated. // PX and PY need to have (X2 - X1) or (Y2 - Y1) subtracted // from them (based on the original values) // Since we really want to get a positive answer when the // point is "beyond (X2,Y2)", then we want to calculate // the inverse anyway - thus we leave X2 & Y2 negated. PX -= X2; PY -= Y2; ccw = PX * X2 + PY * Y2; if (ccw < 0) { ccw = 0; } } } return (ccw < 0) ? -1 : ((ccw > 0) ? 1 : 0); } /** * * @param pOrderedPoints Ordered means that the points are placed as if they are a line. */ public static void smoothLine(final Set<ValuePair> pPointsInLineorder){ ValuePair prev = null; ValuePair cur; /* We want only uniqe new values. */ final Set<ValuePair> newPoints = new TreeSet<ValuePair>(new Comparator<ValuePair>(){ @Override public int compare(final ValuePair a, final ValuePair b) { return a.compareTo(b); } }); for(final Iterator<ValuePair> lineIterator = pPointsInLineorder.iterator(); lineIterator.hasNext(); ) { cur = lineIterator.next(); if(prev != null){ final int difA = cur.a - prev.a; final int difB = cur.b - prev.b; /* Check if we have a diagonal jump, like: (See X's) * * X---- * ----X * * */ if(Math.abs(difA) == 1 && Math.abs(difB) == 1){ /* Add two new points (See O's) * * 0X---- * ----X0 * */ newPoints.add(new ValuePair(cur.a, prev.b)); newPoints.add(new ValuePair(prev.a, cur.b)); } } prev = cur; } pPointsInLineorder.addAll(newPoints); } /** * @see http://www.cs.unc.edu/~mcmillan/comp136/Lecture6/Lines.html */ public static void rasterLine(int x0, int y0, int x1, int y1, final PixelSetter raster) { int dy = y1 - y0; int dx = x1 - x0; int stepx, stepy; if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; } if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; } raster.setPixel(x0, y0); raster.setPixel(x1, y1); if (dx > dy) { final int length = (dx - 1) >> 2; final int extras = (dx - 1) & 3; final int incr2 = (dy << 2) - (dx << 1); if (incr2 < 0) { final int c = dy << 1; final int incr1 = c << 1; int d = incr1 - dx; for (int i = 0; i < length; i++) { x0 += stepx; x1 -= stepx; if (d < 0) { // Pattern: raster.setPixel(x0, y0); // raster.setPixel(x0 += stepx, y0); // x o o raster.setPixel(x1, y1); // raster.setPixel(x1 -= stepx, y1); d += incr1; } else { if (d < c) { // Pattern: raster.setPixel(x0, y0); // o raster.setPixel(x0 += stepx, y0 += stepy); // x o raster.setPixel(x1, y1); // raster.setPixel(x1 -= stepx, y1 -= stepy); } else { raster.setPixel(x0, y0 += stepy); // Pattern: raster.setPixel(x0 += stepx, y0); // o o raster.setPixel(x1, y1 -= stepy); // x raster.setPixel(x1 -= stepx, y1); // } d += incr2; } } if (extras > 0) { if (d < 0) { raster.setPixel(x0 += stepx, y0); if (extras > 1) { raster.setPixel(x0 += stepx, y0); } if (extras > 2) { raster.setPixel(x1 -= stepx, y1); } } else if (d < c) { raster.setPixel(x0 += stepx, y0); if (extras > 1) { raster.setPixel(x0 += stepx, y0 += stepy); } if (extras > 2) { raster.setPixel(x1 -= stepx, y1); } } else { raster.setPixel(x0 += stepx, y0 += stepy); if (extras > 1) { raster.setPixel(x0 += stepx, y0); } if (extras > 2) { raster.setPixel(x1 -= stepx, y1 -= stepy); } } } } else { final int c = (dy - dx) << 1; final int incr1 = c << 1; int d = incr1 + dx; for (int i = 0; i < length; i++) { x0 += stepx; x1 -= stepx; if (d > 0) { raster.setPixel(x0, y0 += stepy); // Pattern: raster.setPixel(x0 += stepx, y0 += stepy); // o raster.setPixel(x1, y1 -= stepy); // o raster.setPixel(x1 -= stepx, y1 -= stepy); // x d += incr1; } else { if (d < c) { raster.setPixel(x0, y0); // Pattern: raster.setPixel(x0 += stepx, y0 += stepy); // o raster.setPixel(x1, y1); // x o raster.setPixel(x1 -= stepx, y1 -= stepy); // } else { raster.setPixel(x0, y0 += stepy); // Pattern: raster.setPixel(x0 += stepx, y0); // o o raster.setPixel(x1, y1 -= stepy); // x raster.setPixel(x1 -= stepx, y1); // } d += incr2; } } if (extras > 0) { if (d > 0) { raster.setPixel(x0 += stepx, y0 += stepy); if (extras > 1) { raster.setPixel(x0 += stepx, y0 += stepy); } if (extras > 2) { raster.setPixel(x1 -= stepx, y1 -= stepy); } } else if (d < c) { raster.setPixel(x0 += stepx, y0); if (extras > 1) { raster.setPixel(x0 += stepx, y0 += stepy); } if (extras > 2) { raster.setPixel(x1 -= stepx, y1); } } else { raster.setPixel(x0 += stepx, y0 += stepy); if (extras > 1) { raster.setPixel(x0 += stepx, y0); } if (extras > 2) { if (d > c) { raster.setPixel(x1 -= stepx, y1 -= stepy); } else { raster.setPixel(x1 -= stepx, y1); } } } } } } else { final int length = (dy - 1) >> 2; final int extras = (dy - 1) & 3; final int incr2 = (dx << 2) - (dy << 1); if (incr2 < 0) { final int c = dx << 1; final int incr1 = c << 1; int d = incr1 - dy; for (int i = 0; i < length; i++) { y0 += stepy; y1 -= stepy; if (d < 0) { raster.setPixel(x0, y0); raster.setPixel(x0, y0 += stepy); raster.setPixel(x1, y1); raster.setPixel(x1, y1 -= stepy); d += incr1; } else { if (d < c) { raster.setPixel(x0, y0); raster.setPixel(x0 += stepx, y0 += stepy); raster.setPixel(x1, y1); raster.setPixel(x1 -= stepx, y1 -= stepy); } else { raster.setPixel(x0 += stepx, y0); raster.setPixel(x0, y0 += stepy); raster.setPixel(x1 -= stepx, y1); raster.setPixel(x1, y1 -= stepy); } d += incr2; } } if (extras > 0) { if (d < 0) { raster.setPixel(x0, y0 += stepy); if (extras > 1) { raster.setPixel(x0, y0 += stepy); } if (extras > 2) { raster.setPixel(x1, y1 -= stepy); } } else if (d < c) { raster.setPixel(stepx, y0 += stepy); if (extras > 1) { raster.setPixel(x0 += stepx, y0 += stepy); } if (extras > 2) { raster.setPixel(x1, y1 -= stepy); } } else { raster.setPixel(x0 += stepx, y0 += stepy); if (extras > 1) { raster.setPixel(x0, y0 += stepy); } if (extras > 2) { raster.setPixel(x1 -= stepx, y1 -= stepy); } } } } else { final int c = (dx - dy) << 1; final int incr1 = c << 1; int d = incr1 + dy; for (int i = 0; i < length; i++) { y0 += stepy; y1 -= stepy; if (d > 0) { raster.setPixel(x0 += stepx, y0); raster.setPixel(x0 += stepx, y0 += stepy); raster.setPixel(x1 -= stepy, y1); raster.setPixel(x1 -= stepx, y1 -= stepy); d += incr1; } else { if (d < c) { raster.setPixel(x0, y0); raster.setPixel(x0 += stepx, y0 += stepy); raster.setPixel(x1, y1); raster.setPixel(x1 -= stepx, y1 -= stepy); } else { raster.setPixel(x0 += stepx, y0); raster.setPixel(x0, y0 += stepy); raster.setPixel(x1 -= stepx, y1); raster.setPixel(x1, y1 -= stepy); } d += incr2; } } if (extras > 0) { if (d > 0) { raster.setPixel(x0 += stepx, y0 += stepy); if (extras > 1) { raster.setPixel(x0 += stepx, y0 += stepy); } if (extras > 2) { raster.setPixel(x1 -= stepx, y1 -= stepy); } } else if (d < c) { raster.setPixel(x0, y0 += stepy); if (extras > 1) { raster.setPixel(x0 += stepx, y0 += stepy); } if (extras > 2) { raster.setPixel(x1, y1 -= stepy); } } else { raster.setPixel(x0 += stepx, y0 += stepy); if (extras > 1) { raster.setPixel(x0, y0 += stepy); } if (extras > 2) { if (d > c) { raster.setPixel(x1 -= stepx, y1 -= stepy); } else { raster.setPixel(x1, y1 -= stepy); } } } } } } } // =========================================================== // Inner and Anonymous Classes // =========================================================== public static interface PixelSetter { public void setPixel(final int x, final int y); } }