// ********************************************************************** // // <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/proj/DrawUtil.java,v $ // $RCSfile: DrawUtil.java,v $ // $Revision: 1.6 $ // $Date: 2009/01/21 01:24:41 $ // $Author: dietrick $ // // ********************************************************************** package com.bbn.openmap.proj; import java.awt.Point; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.List; import com.bbn.openmap.MoreMath; /** * Drawing utility functions. */ public class DrawUtil { // cannot construct private DrawUtil() {} /** * Generate additional vertices between two points. * <p> * * @param x1 x coord * @param y1 y coord * @param x2 x coord * @param y2 y coord * @param n num segments * @param include_last include the last one? * @param ret_val the array to put them in * @return int[] ret_val */ public final static int[] lineSegments(int x1, int y1, int x2, int y2, int n, boolean include_last, int[] ret_val) { if (n <= 0) { ret_val = new int[2]; ret_val[0] = x1; ret_val[1] = y1; return ret_val; } float dx = x2 - x1; float dy = y2 - y1; int end = include_last ? n + 1 : n; end <<= 1; float inc = 1f / (float) n; float t = inc; // add all the vertices in x,y order ret_val[0] = x1; ret_val[1] = y1; for (int i = 2; i < end; i += 2, t += inc) { ret_val[i] = x1 + (int) (dx * t); ret_val[i + 1] = y1 + (int) (dy * t); } return ret_val; } /** * Returns n or n+1 points along a line. * <p> * * @param pt1 point * @param pt2 point * @param n count * @param include_last boolean * @return Point[] */ public final static Point[] lineSegments(Point pt1, Point pt2, int n, boolean include_last) { Point v = new Point(pt2.x - pt1.x, pt2.y - pt1.y); int end = include_last ? n + 1 : n; Point[] ret_val = new Point[end]; float inc = 1f / (float) n; float t = inc; ret_val[0] = pt1; for (int i = 1; i < end; i++, t += inc) { ret_val[i] = new Point(pt1.x + (int) ((float) v.x * t), pt1.y + (int) ((float) v.y * t)); } return ret_val; } /** * Bresenham's line algorithm. * <p> * Returns an array of points to draw. * <p> * * @param pt1 point * @param pt2 point * @return Point[] * */ public final static Point[] bresenham_line(Point pt1, Point pt2) { return bresenham_line(pt1.x, pt1.y, pt2.x, pt2.y); } /** * Bresenham's line algorithm. * <p> * * @param x1 horizontal pixel window location of first point. * @param y1 vertical pixel window location of first point. * @param x2 horizontal pixel window location of second point. * @param y2 vertical pixel window location of second point. * @return Point[] * */ public final static Point[] bresenham_line(int x1, int y1, int x2, int y2) { // This is actually NOT bresenhams algorithm. It is faster! // -rmf // Debug.output("DrawUtil.bresenham_line(" + // x1 + "," + y1 + ")->(" + x2 + "," + y2 + ")"); int i; int d, x, y, ax, ay, sx, sy, dx, dy, t; dx = x2 - x1; ax = Math.abs(dx) << 1; sx = MoreMath.sign(dx); dy = y2 - y1; ay = Math.abs(dy) << 1; sy = MoreMath.sign(dy); t = Math.max(Math.abs(dx), Math.abs(dy)) + 1; Point[] ret_val = new Point[t]; x = x1; y = y1; if (ax > ay) { /* x dominant */ d = ay - (ax >> 1); for (i = 0;;) { ret_val[i++] = new Point(x, y); // ret_val[i].x = x; ret_val[i++].y = y; if (x == x2) return ret_val; if (d >= 0) { y += sy; d -= ax; } x += sx; d += ay; } } else { /* y dominant */ d = ax - (ay >> 1); for (i = 0;;) { ret_val[i++] = new Point(x, y); // ret_val[i].x = x; ret_val[i++].y = y; if (y == y2) return ret_val; if (d >= 0) { x += sx; d -= ay; } y += sy; d += ax; } } } /** * Tests if a point is inside a polygon. * <p> * * @param xpts horizontal pixel window points of polygon. * @param ypts vertical pixel window points of polygon. * @param ptx horizontal pixel window points of location * @param pty vertical pixel window points of location. * @return boolean */ public final static boolean inside_polygon(float[] xpts, float[] ypts, double ptx, double pty) { int j, inside_flag = 0; int numverts = xpts.length; if (numverts <= 2) return false; Point2D vtx0 = new Point2D.Float(), vtx1 = new Point2D.Float(); double dv0; // prevents OVERFLOW!! int crossings = 0; boolean xflag0 = false, yflag0 = false, yflag1 = false; vtx0.setLocation(xpts[numverts - 1], ypts[numverts - 1]); // get test bit for above/below Y axis yflag0 = ((dv0 = vtx0.getY() - pty) >= 0); for (j = 0; j < numverts; j++) { if ((j & 0x1) != 0) { // HACK - slightly changed vtx0.setLocation(xpts[j], ypts[j]); yflag0 = ((dv0 = vtx0.getY() - pty) >= 0); } else { vtx1.setLocation(xpts[j], ypts[j]); yflag1 = (vtx1.getY() >= pty); } /* * check if points not both above/below X axis - can't hit ray */ if (yflag0 != yflag1) { /* check if points on same side of Y axis */ if ((xflag0 = (vtx0.getX() >= ptx)) == (vtx1.getX() >= ptx)) { if (xflag0) crossings++; } else { crossings += ((vtx0.getX() - dv0 * (vtx1.getX() - vtx0.getX()) / (vtx1.getY() - vtx0.getY())) >= ptx) ? 1 : 0; } } inside_flag = crossings & 0x01; } return (inside_flag != 0); } /** * Returns the distance from Point (x,y) to the closest line segment in the * Poly (int[] xpts, int[] ypts). * <p> * This procedure assumes that xpts.length == ypts.length. * <p> * * @param xpts X points of the polygon * @param ypts Y points of the polygon * @param ptx x location of the point * @param pty y location of the point * @param connected polyline or polygon */ public final static float closestPolyDistance(float[] xpts, float[] ypts, double ptx, double pty, boolean connected) { if (xpts.length == 0) return Float.POSITIVE_INFINITY; if (xpts.length == 1) return (float) distance(xpts[0], ypts[0], ptx, pty); float temp, distance = Float.POSITIVE_INFINITY; int i, j; for (i = 0, j = 1; j < xpts.length; i++, j++) { temp = (float) distance_to_line(xpts[i], ypts[i], xpts[j], ypts[j], ptx, pty); // Debug.output( // "\tdistance from line (" + from.x + "," + from.y + // "<->" + // to.x + "," + to.y + ") to point (" + ptx + "," + pty + // ")=" + // temp); if (temp < distance) distance = temp; } // connect if (connected) { temp = (float) distance_to_line(xpts[i], ypts[i], xpts[0], ypts[0], ptx, pty); if (temp < distance) distance = temp; } return distance; } /** * 2D distance formula. * <p> * * @param x1 x coord * @param y1 y coord * @param x2 x coord * @param y2 y coord * @return float distance */ public final static float distance(float x1, float y1, float x2, float y2) { double xdiff = x2 - x1; double ydiff = y2 - y1; return (float) Math.sqrt((xdiff * xdiff + ydiff * ydiff)); } /** * 2D distance formula. * <p> * * @param x1 x coord * @param y1 y coord * @param x2 x coord * @param y2 y coord * @return float distance */ public final static float distance(int x1, int y1, int x2, int y2) { double xdiff = x2 - x1; double ydiff = y2 - y1; return (float) Math.sqrt((xdiff * xdiff + ydiff * ydiff)); } /** * 2D distance formula. * <p> * * @param x1 x coord * @param y1 y coord * @param x2 x coord * @param y2 y coord * @return double distance */ public final static double distance(double x1, double y1, double x2, double y2) { double xdiff = x2 - x1; double ydiff = y2 - y1; return Math.sqrt((xdiff * xdiff + ydiff * ydiff)); } /** * Calculate the "pixel distance" between two points (squaring not * involved). * <p> * * @param x1 x coord * @param y1 y coord * @param x2 x coord * @param y2 y coord * @return int pixel distance */ public final static int pixel_distance(int x1, int y1, int x2, int y2) { int dx = Math.abs(x1 - x2); int dy = Math.abs(y1 - y2); return (dx > dy) ? dx : dy; } /** * Distance to closest endpoint. * <p> * * @param x1 x coord * @param y1 y coord * @param x2 x coord * @param y2 y coord * @param x x coord of point * @param y y coord of point * @return float distance to endpoint */ public final static float distance_to_endpoint(int x1, int y1, int x2, int y2, int x, int y) { return (float) Math.min(distance(x1, y1, x, y), distance(x2, y2, x, y)); } /** * Compute distance from point to line segment. * <p> * Compute the distance from point (x,y) to a line by computing the * perpendicular line from (x,y) to the line and finding the intersection of * this perpendicular and the line. If the intersection is on the line * segment, then the distance is the distance from the mouse to the * intersection, otherwise it is the distance from (x,y) to the nearest * endpoint. * <p> * Equations used to compute distance: <br> * <ul> * <li>m = (y2-y1)/(x2-x1) slope of the line * <li>y = mx + b equation of the line * <li>c = -1/m slope of line perpendicular to it * <li>y = cx + d equation of perpendicular line * <li>xi = (d-b)/(m-c) x-intersection, from equating the 2 line equations * <li>y1 = c* xi + d y-intersection * <li>distance = sqrt(sqr(x-xi) + sqr(y-yi)) distance between two points * </ul> * * @param x1 line x coord1 * @param y1 line y coord1 * @param x2 line x coord2 * @param y2 line y coord2 * @param x point x coord * @param y point y coord * @return float distance to line segment * @deprecated USE THE NEW FUNCTION * */ public final static float OLD_distance_to_line(int x1, int y1, int x2, int y2, int x, int y) { float m; /* slope of the line */ float c; /* slope of a line perpendicular to the line */ float b; /* y intercept of line */ float d; /* y intercept of a line perpendicular to the line */ int xi, yi; /* intersection of line and perpendicular */ /* vertical line */ if (x2 == x1) { if (y1 <= y && y <= y2 || y2 <= y && y <= y1) return (float) Math.abs(x - x1); // mouse is alongside // line return distance_to_endpoint(x1, y1, x2, y2, x, y); } /* horizontal line */ if (y2 == y1) { if (x1 <= x && x <= x2 || x2 <= x && x <= x1) return (float) Math.abs(y - y1); // mouse is alongside // line return distance_to_endpoint(x1, y1, x2, y2, x, y); } m = ((float) (y2 - y1)) / ((float) (x2 - x1)); /* * slope of the line */ c = -1.0f / m; /* slope of perpendicular line */ d = (float) y - c * (float) x;/* * perpendicular line through mouse */ b = (float) y1 - m * (float) x1; /* the line in the drawing */ // NOTE: round error xi = (int) ProjMath.qint((d - b) / (m - c));// x intersection yi = (int) ProjMath.qint(c * (float) xi + d);// y intersection /* * If intersection is on the line segment distance is distance from * mouse to it. */ if ((x1 <= xi && xi <= x2 || x2 <= xi && xi <= x1) && (y1 <= yi && yi <= y2 || y2 <= yi && yi <= y1)) return distance(xi, yi, x, y); /* distance is distance from mouse to nearest endpt */ return distance_to_endpoint(x1, y1, x2, y2, x, y); } /** * Compute perpendicular distance from point to line. * <p> * * @param x1 line x coord1 * @param y1 line y coord1 * @param x2 line x coord2 * @param y2 line y coord2 * @param x3 point x coord * @param y3 point y coord * @return float distance to line * */ public final static float perpendicular_distance_to_line(int x1, int y1, int x2, int y2, int x3, int y3) { int x12 = x2 - x1; int y12 = y2 - y1; int x13 = x3 - x1; int y13 = y3 - y1; float D12 = distance(x1, y1, x2, y2); return Math.abs((/* Math.abs */(x12 * y13) - /* Math.abs */(x13 * y12)) / D12); } /** * Computes the distance from a point to a line segment. * <p> * Variable usage as follows: * <p> * <ul> * <li>x12 x distance from the first endpoint to the second. * <li>y12 y distance from the first endpoint to the second. * <li>x13 x distance from the first endpoint to point being tested. * <li>y13 y distance from the first endpoint to point being tested. * <li>x23 x distance from the second endpoint to point being tested. * <li>y23 y distance from the second endpoint to point being tested. * <li>D12 Length of the line segment. * <li>pp distance along the line segment to the intersection of the * perpendicular from the point to line extended. * </ul> * * Procedure: * <p> * * Compute D12, the length of the line segment. Compute pp, the distance to * the perpendicular. If pp is negative, the intersection is before the * start of the line segment, so return the distance from the start point. * If pp exceeds the length of the line segment, then the intersection is * beyond the end point so return the distance of the point from the end * point. Otherwise, return the absolute value of the length of the * perpendicular line. The sign of the length of the perpendicular line * indicates whether the point lies to the right or left of the line as one * travels from the start point to the end point. * <p> * * @param x1 line x coord1 * @param y1 line y coord1 * @param x2 line x coord2 * @param y2 line y coord2 * @param x3 point x coord * @param y3 point y coord * @return float distance to line segment * */ public final static double distance_to_line(double x1, double y1, double x2, double y2, double x3, double y3) { // algorithm courtesy of Ray 1/16/98 double x12 = x2 - x1; double y12 = y2 - y1; double x13 = x3 - x1; double y13 = y3 - y1; double D12 = Math.sqrt(x12 * x12 + y12 * y12); double pp = (x12 * x13 + y12 * y13) / D12; if (pp < 0.0) { return (float) Math.sqrt(x13 * x13 + y13 * y13); } if (pp > D12) { double x23 = x3 - x2; double y23 = y3 - y2; return Math.sqrt(x23 * x23 + y23 * y23); } return Math.abs(((x12 * y13 - y12 * x13) / D12)); } /** * Generates a line with width lw, returns an ArrayList of 4 x-y coords. * <p> * * @param lw line width * @param x1 line x coord1 * @param y1 line y coord1 * @param x2 line x coord2 * @param y2 line y coord2 * @return List int[] of x[], y[] */ public static List<int[]> generateWideLine(int lw, int x1, int y1, int x2, int y2) { ArrayList<int[]> ret_val = new ArrayList<int[]>(2); int[] x = new int[4]; int[] y = new int[4]; // calculate the offsets // lw = lw -1; int off1 = (int) lw / 2; int off2 = (lw % 2 != 0) ? (int) lw / 2 + 1 : (int) lw / 2; // slope <= 1 if (Math.abs((float) (y2 - y1) / (float) (x2 - x1)) <= 1f) { x[0] = x[3] = x1; x[1] = x[2] = x2; y[0] = y1 + off1; y[1] = y2 + off1; y[2] = y2 - off2; y[3] = y1 - off2; ret_val.add(x); ret_val.add(y); } // slope > 1 else { x[0] = x1 + off1; x[1] = x2 + off1; x[2] = x2 - off2; x[3] = x1 - off2; y[0] = y[3] = y1; y[1] = y[2] = y2; ret_val.add(x); ret_val.add(y); } return ret_val; } /** * Generates a polygon or polyline with positive width lw. * <p> * Returns ArrayList of x-y array pairs of coordinates of polygon segments. * the parameter altx must either be null, or an alternate array of points * to draw. * <p> * * @param lw line width * @param xpts int[] x coords * @param ypts int[] y coords * @param altx int[] altx coords * @param connect polygon or polyline? * @return List int[] of x[], y[] * */ final public static List<int[]> generateWidePoly(int lw, int[] xpts, int[] ypts, int[] altx, boolean connect) { return generateWidePoly(lw, xpts.length, xpts, ypts, altx, connect); } /** * Generates a polygon or polyline with positive width lw. * <p> * Returns ArrayList of x-y array pairs of coordinates of polygon segments. * the parameter altx must either be null, or an alternate array of points * to draw. * <p> * * @param lw line width * @param len numcoords * @param xpts int[] x coords * @param ypts int[] y coords * @param altx int[] altx coords * @param connect polygon or polyline? * @return List int[] of x[], y[] * */ final public static List<int[]> generateWidePoly(int lw, int len, int[] xpts, int[] ypts, int[] altx, boolean connect) { // HACK - altx deprecated? ArrayList<int[]> ret_val = new ArrayList<int[]>(len * 4); int off1 = 0, off2 = 0; int[] x = null, y = null, a_x = null; float slope; int end = (connect) ? len : len - 1; if (len <= 1) return new ArrayList<int[]>(); // lw = lw -1; // calculate the offsets - HACK: +1 side not consistent... off1 = (int) lw / 2; off2 = (int) Math.ceil((float) lw / 2f); // System.out.print("DrawUtil.generateWidePoly Points for // lw="+lw); // for (int i=0;i<len;i++) { // System.out.print("(" + xpts[i] + "," + ypts[i] + ")"); // } // Debug.output(""); for (int i = 0, j = (i + 1) % len; i < end; i++, j = (i + 1) % len) { x = new int[4]; y = new int[4]; // handle division by zero if (xpts[i] == xpts[j]) slope = Float.POSITIVE_INFINITY; else slope = Math.abs((float) (ypts[j] - ypts[i]) / (float) (xpts[j] - xpts[i])); // slope <= 1 if (slope <= 1f) { x[0] = x[3] = xpts[i]; x[1] = x[2] = xpts[j]; y[0] = ypts[i] + off1; y[1] = ypts[j] + off1; y[2] = ypts[j] - off2; y[3] = ypts[i] - off2; ret_val.add(x); ret_val.add(y); if (altx != null) { a_x = new int[4]; a_x[0] = a_x[3] = altx[i]; a_x[1] = a_x[2] = altx[j]; ret_val.add(a_x); ret_val.add(y); } } // slope > 1 else { x[0] = xpts[i] + off1; x[1] = xpts[j] + off1; x[2] = xpts[j] - off2; x[3] = xpts[i] - off2; y[0] = y[3] = ypts[i]; y[1] = y[2] = ypts[j]; ret_val.add(x); ret_val.add(y); if (altx != null) { a_x = new int[4]; a_x[0] = altx[i] + off1; a_x[1] = altx[j] + off1; a_x[2] = altx[j] - off2; a_x[3] = altx[i] - off2; ret_val.add(a_x); ret_val.add(y); } } } return ret_val; } /* * public static void main(String[] args) { * * * Debug.output("distance_to_line(0,0,4,4, 2,0): " + * distance_to_line(0,0,4,4, 2,0)); * Debug.output("OLD_distance_to_line(0,0,4,4, 2,0): " + * OLD_distance_to_line(0,0,4,4, 2,0)); * Debug.output("distance_to_line(0,0,4,4, 50,50): " + * distance_to_line(0,0,4,4, 50,50)); * Debug.output("OLD_distance_to_line(0,0,4,4, 50,50): " + * OLD_distance_to_line(0,0,4,4, 50,50)); * Debug.output("distance_to_line(-34,12,44,104, -44,-50): " + * distance_to_line(-34,12,44,104, -44,-50)); * Debug.output("OLD_distance_to_line(-34,12,44,104, -44,-50): " + * OLD_distance_to_line(-34,12,44,104, -44,-50)); System.exit(0); // 3-4-5 * triangle Debug.output(distance(0,0,3,4)); * Debug.output(distance(0,0,-3,4)); Debug.output(distance(0,0,-3,-4)); * Debug.output(distance(0,0,3,-4)); Debug.output(); * * Debug.output(distance_to_line(0,0,2,2, 0,2)); // root 2 * Debug.output(distance_to_line(0,0,2,0, 0,2)); // 2 * Debug.output(distance_to_line(0,0,2,0, -1,-1)); // root 2 * Debug.output(distance_to_line(0,0,2,0, 1,0)); // 0 * Debug.output(distance_to_line(0,0,2,2, 1,0)); // rounded! Debug.output(); * * int[] xpts = new int[3]; int[] ypts = new int[3]; xpts[0] = 0; ypts[0] = * 0; xpts[1] = 3; ypts[1] = 0; xpts[2] = 3; ypts[2] = 4; * * Debug.output(closestPolyDistance(xpts, ypts, 0,4, true)); * Debug.output(closestPolyDistance(xpts, ypts, 0,4, false));//3 * * xpts[0] = 0; ypts[0] = 0; xpts[1] = 2; ypts[1] = 0; xpts[2] = 2; ypts[2] * = 2; Debug.output(closestPolyDistance(xpts, ypts, 0,1, true));//round * Debug.output(closestPolyDistance(xpts, ypts, 0,1, false));//1 // * linewidth testing * * Debug.output(""); ArrayList vec = generateWideLine(3, 0, 0, 5, 5); * * int[] x = (int[])vec.elementAt(0); int[] y = (int[])vec.elementAt(1); * System.out.print("wide line: "); for (int i = 0; i <x.length; i++) { * System.out.print(x[i] + "," + y[i] + " "); } Debug.output(""); * * Debug.output(""); vec = generateWideLine(4, 0, 0, -5, -3); * * x = (int[])vec.elementAt(0); y = (int[])vec.elementAt(1); * System.out.print("wide line: "); for (int i = 0; i <x.length; i++) { * System.out.print(x[i] + "," + y[i] + " "); } Debug.output(""); * Debug.output(""); * * xpts = new int[4]; ypts = new int[4]; xpts[0] = 0; ypts[0] = 0; xpts[1] = * 5; ypts[1] = 2; xpts[2] = 4; ypts[2] = 8; xpts[3] = -2; ypts[3] = 6; vec * = generateWidePoly(3, xpts, ypts, null, false); int size = vec.size(); * for (int j = 0; j < size; j+=2) { x = (int[])vec.elementAt(j); y = * (int[])vec.elementAt(j+1); System.out.print("wide poly: "); for (int i = * 0; i <x.length; i++) { System.out.print(x[i] + "," + y[i] + " "); } * Debug.output(""); } } */ }