// Useful graphic functions // author: Jan Lemeire import java.awt.*; import java.io.BufferedReader; import java.io.IOException; import java.text.NumberFormat; import java.text.DecimalFormat; import javax.swing.JFrame; import javax.swing.JPanel; public class StandardGraphics{ /** * DEMO PROGRAM WITH EXAMPLES FOR ALL METHODS */ public static void main(String s[]) { System.out.println("Number formatting :"); System.out.println(formatNumberDynamic(34546f, 3)); System.out.println(formatNumberDynamic(3.4546f, 3)); System.out.println(formatNumberDynamic(1f, 3)); System.out.println(formatNumberDynamic(0.9134546f, 3)); System.out.println(formatNumberDynamic(0.034546f, 3)); System.out.println(formatNumberDynamic(0.01f, 3)); System.out.println(formatNumberDynamic(0.0034546f, 3)); final int SCREEN_WIDTH = 800, SCREEN_HEIGHT = 800; JPanel panel = new JPanel(){ public void paintComponent(Graphics g) { // ********************* // ***** EXAMPLES ****** // ********************* Graphics2D g2D = (Graphics2D)g; // draw a grid on the panel where gap between the lines is 50 drawGrid(g2D, 0, 0, getWidth(), getHeight(), 50); g.setFont(new Font("Times", Font.BOLD, 14)); // change font drawStringCentered(g, "String centered at (250, 50)", 250, 50); drawStringRightAligned(g, "String right aligned at (700, 50)", 700, 50); drawStringInRectangle(g, "String in rectangle at (250, 100)", 250, 100, Color.RED); DrawArrow(g2D, 200, 150, 400, 200, 20); g.drawString("DrawArrow between two given points and arrow size 20", 400, 175); DrawArrow(g2D, 100, 300, Math.PI/4, 120, 20); g.drawString("DrawArrow from given point with angle 45 and size 120", 190, 260); DrawRoundArrow(g2D, 200, 400, 100, 100, 0, Math.PI*3/4, 20, false); g.drawString("DrawRoundArrow from angle 0 to angle 135 degrees", 310, 350); connectWithRoundLine(g2D, 150, 450, 275, 450, "connectWithRoundLine"); DrawWheel(g, 200, 600, 50, 5, Math.PI/4); g.drawString("Wheel with 5 'spaken' and start angle at 45 degrees (change angle dynamically to spin the wheel)", 20, 670); drawSpeedometer(g2D, 550, 500, 60, "Speedometer", "km/h", 70, 150); g.drawString("Speedometer at (550, 500) with radius 60", 450, 580); } }; panel.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT)); JFrame frame = new JFrame("Demo of StandardGraphics Functions"); frame.add(panel); frame.pack(); // calculate frame size frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // show centered Dimension frameSize = frame.getSize(); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); frame.setLocation(screenSize.width / 2 - frameSize.width / 2, screenSize.height / 2 - frameSize.height / 2); frame.setVisible(true); } // angles are in radians & counterclockwise (0 is X-axis) public static void drawStringCentered(Graphics g, String str, int x, int y){ int titleX=x - g.getFontMetrics(g.getFont()).stringWidth(str) / 2; g.drawString(str, titleX, y); } public static void drawStringRightAligned(Graphics g, String str, int rightX, int y){ int titleX=rightX - g.getFontMetrics(g.getFont()).stringWidth(str); if (titleX < 0) titleX = 2; g.drawString(str, titleX, y); } public static void drawStringInRectangle(Graphics g, String str, int x, int y, Color c){ int width = g.getFontMetrics(g.getFont()).stringWidth(str) + 4; int height = g.getFontMetrics(g.getFont()).getHeight() + 2; Color oldColor = g.getColor(); g.setColor(c); g.fillRect(x, y, width, height); g.setColor(oldColor); g.drawString(str, x+2, y+height-4); } static NumberFormat sFormat = NumberFormat.getNumberInstance(); static DecimalFormat sDecFormat = new DecimalFormat("0.##E0"); public static String formatNumberDynamic(float number, int minNbrDigits){ return formatNumberDynamic((double)number, minNbrDigits); } public static String formatNumberDynamic(double number, int minNbrDigits){ // if (number > Math.pow((double)10, (double)(minNbrDigits-1))){ // sFormat.setMaximumFractionDigits(0); // } else { if (number < 0.001){ sDecFormat.setMaximumFractionDigits(minNbrDigits-1); return sDecFormat.format(number); } else { int nbr_betekenisvolle_digits = minNbrDigits - (int)Math.round(Math.log(number)/Math.log(10)); if (nbr_betekenisvolle_digits < 0) nbr_betekenisvolle_digits=0; sFormat.setMaximumFractionDigits(Math.abs(nbr_betekenisvolle_digits)); } // } //System.out.println("nbr="+number+" log10="+(int)Math.round(Math.log(number)/Math.log(10))+" #digits="+nbr_betekenisvolle_digits+":"+sFormat.format(number)); return sFormat.format(number); } // IN & OUT /////////// public static String readStringAndEnter(BufferedReader br, boolean waitForEnter){ try { String s=""; if (waitForEnter || br.ready()){ int i=br.read(); while(i!=13){ // System.out.print("'"+(char)i+"'"); s+=(char)i; i=br.read(); } // System.out.println("."); if (br.ready()){ i=br.read(); // lees de laatste 10 if (i!= 10) throw new RuntimeException("character '10' expected after the 13 for the enter, and not: "+i); } } return s; } catch (IOException e) { return "";} } // ARROWS ///////// public static void DrawArrow(Graphics2D g, int x0, int y0, int x1, int y1, int arrowSize){ float angle = AngleWithXAx(x0, y0, x1, y1); DrawArrow(g, x0, y0, x1, y1, angle, arrowSize); } /** * * @param x0,y0 beginpoint */ public static void DrawArrow(Graphics2D g, int x0, int y0, double angle, int length, int arrowSize){ int x1 = x0 + (int)((float) length * Math.cos(angle)); int y1 = y0 - (int)((float) length * Math.sin(angle)); DrawArrow(g, x0, y0, x1, y1, angle, arrowSize); } private static void DrawArrow(Graphics2D g, int x0, int y0, int x1, int y1, double angle, int arrowSize){ final float ARROW_ANGLE = 0.3f; int xa[] = new int[4]; int ya[] = new int[4]; g.drawLine(x0, y0, x1, y1); xa[0] = x1; ya[0] = y1; xa[1] = x1 - (int)((float)arrowSize * Math.cos(angle + ARROW_ANGLE)); ya[1] = y1 + (int)((float)arrowSize * Math.sin(angle + ARROW_ANGLE)); xa[2] = x1 - (int)((float)arrowSize * Math.cos(angle - ARROW_ANGLE)); ya[2] = y1 + (int)((float)arrowSize * Math.sin(angle - ARROW_ANGLE)); g.fillPolygon(xa, ya, 3); } /** * * @param g * @param centerX * @param centerY * @param radiusX * @param radiusY * @param beginAngle from (east = 0) in radians * @param endAngle to * @param arrowSize */ public static void DrawRoundArrow(Graphics2D g, int centerX, int centerY, int radiusX, int radiusY, double beginAngle, double endAngle, int arrowSize, boolean biDirected) { int begin_deg =(int)(beginAngle*180f/Math.PI)%360, end_deg = (int)((endAngle-beginAngle)*180f/Math.PI)%360; g.drawArc(centerX - radiusX, centerY-radiusY, radiusX*2, radiusY*2, begin_deg, end_deg); //0 is east, counterclockwise if (arrowSize>0){ int x1 = centerX + (int)(radiusX*Math.cos(endAngle)); int y1 = centerY - (int)(radiusY*Math.sin(endAngle) ); if (endAngle > 0) DrawArrow(g, x1, y1, endAngle + Math.PI/2, arrowSize, arrowSize); else DrawArrow(g, x1, y1, endAngle - Math.PI/2, arrowSize, arrowSize); if (biDirected){ int x0 = centerX + (int)(radiusX*Math.cos(beginAngle)); int y0 = centerY - (int)(radiusY*Math.sin(beginAngle) ); if (beginAngle > 0) DrawArrow(g, x0, y0, beginAngle - Math.PI/2, arrowSize, arrowSize); else DrawArrow(g, x0, y0, beginAngle - Math.PI/2, arrowSize, arrowSize); } } } /** * * Connects (x1, y1) with (x2, y2) */ public static void connectWithRoundLine(Graphics2D g, int x1, int y1, int x2, int y2, String text) { if (x2 Math.abs(y2-y1)) drawStringCentered(g, text, x0-r/5, y0-r/7); // g.drawString(text, x0-r/5, y0-r/7); else drawStringCentered(g, text, x0+r/4, y0-r/8+12); // g.drawString(text, x0+r/4, y0-r/8+12); } // g.rotate(a1, x0, y0); } final static Font GRID_FONT = new Font("Dialog", Font.PLAIN, 12); public static void drawGrid(Graphics2D g, int x, int y, int width, int height, int delta){ drawGrid(g, x, y, width, height, delta, 0, 0); } public static void drawGrid(Graphics2D g, int x, int y, int width, int height, int delta, int centerX, int centerY){ g.setColor(Color.gray); g.drawRect(x, y, width, height); g.setFont(GRID_FONT); Stroke old_stroke = g.getStroke(); g.setStroke(new BasicStroke(1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 3f, new float[]{3}, 0f)); for(int _x=x;_x < (x+width); _x += delta){ g.drawLine(_x, y, _x, y+height); g.drawString(""+(_x-centerX), _x+2, 11); } for(int _y=y;_y < (y+height); _y += delta){ g.drawLine(x, _y, x+width, _y); g.drawString(""+(_y-centerY), 0, _y-1); } g.setStroke(old_stroke); g.setColor(Color.black); } // ANGLES ///////// /** * @return Angle with x-ax in radians of line connecting (x0, y0) and (x1, y1) */ public static float AngleWithXAx(int x0, int y0, int x1, int y1){ float alfa; if (x0 != x1){ alfa = (float)Math.atan((float)(y0-y1)/(float)(x1-x0)); if (x0 > x1) alfa += (float)Math.PI; } else { if (y1 > y0) alfa = -(float)Math.PI/2.0f; else alfa = (float)Math.PI/2.0f; } return alfa; } /** * @return Slope of line connecting (x0, y0) and (x1, y1) */ public static float slope(int x0, int y0, int x1, int y1){ if (x0 != x1) return (float)(y1-y0)/(float)(x1-x0); else return Float.POSITIVE_INFINITY; } /** * @return Intercept of line connecting (x0, y0) and (x1, y1) */ public static float intercept(int x0, int y0, int x1, int y1){ if (x0 != x1) return y1 - (float)(y1-y0) * x1/(float)(x1-x0); else return Float.POSITIVE_INFINITY; } /** * @return CrossPoint of line connecting (x1, y1) and (x2, y2) and line connecting (x3, y3) and (x4, y4)? */ public static Point2f crossPoint(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { if (x1 == x3 && y1 == y3) return new Point2f(x1, y1); if (x1 == x4 && y1 == y4) return new Point2f(x1, y1); if (x2 == x3 && y2 == y3) return new Point2f(x2, y2); if (x2 == x4 && y2 == y4) return new Point2f(x2, y2); // convert to y=mx + q formula float m1 = slope(x1, y1, x2, y2), m2 = slope(x3, y3, x4, y4); if (m1 == m2) // lines parallelize return new Point2f(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY); else { // find cross point float q1 = intercept(x1, y1, x2, y2), q2 = intercept(x3, y3, x4, y4); float xc, yc; if (Float.isInfinite(q1)){ // vertical line xc = x1; yc = m2 * xc + q2; } else if (Float.isInfinite(q2)){ xc = x3; yc = m1 * xc + q1; } else { xc = (q1 - q2)/(m2 - m1); yc = m1 * xc + q1; } return new Point2f(xc, yc); } } /** * @return Does the line connecting (x1, y1) and (x2, y2) crosses with (x3, y3) and (x4, y4)? */ public static boolean doLinesCross(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { // find cross point Point2f pCross = crossPoint(x1, y1, x2, y2, x3, y3, x4, y4); if (Float.isInfinite(pCross.x)){ // lines are parallel // true if a point is on the other line return isPointOnLine(x1, y1, x3, y3, x4, y4, 0) || isPointOnLine(x2, y2, x3, y3, x4, y4, 0) || isPointOnLine(x3, y3, x1, y1, x2, y2, 0) || isPointOnLine(x4, y4, x1, y1, x2, y2, 0); } else { float yc = pCross.y; // does cross point lie on line? return (y1 - yc)*(y2-yc) <= 0 && (y3 - yc)*(y4 - yc) <= 0; } } /** * @return Does the line connecting (x1, y1) and (x2, y2) crosses with (x3, y3) and (x4, y4)? * But not on the endpoints themselves */ public static boolean doLinesCrossWithinLine(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { // find cross point Point2f pCross = crossPoint(x1, y1, x2, y2, x3, y3, x4, y4); if (Float.isInfinite(pCross.x)){ // lines are parallel // true if a point is on the other line return isPointWithinLine(x1, y1, x3, y3, x4, y4) || isPointWithinLine(x2, y2, x3, y3, x4, y4) || isPointWithinLine(x3, y3, x1, y1, x2, y2) || isPointWithinLine(x4, y4, x1, y1, x2, y2); } else { float yc = pCross.y; float a = (y1 - yc)*(y2-yc), b = (y3 - yc)*(y4 - yc); // does cross point lie within line? if (y1 == y2) return yc == y1 && b < -0.01f; else if (y3 == y4) return yc == y3 && a < -0.01f; else return a < -0.01f && b < -0.01f; } } // VARIOUS ////////// public static void drawSpeedometer(Graphics2D g, String name, String unit, float value, float maxValue) { drawSpeedometer(g, 0, 0, 60, name, unit, value, maxValue); } /** * draws a speedometer with center (x0, y0) and given radius */ public static void drawSpeedometer(Graphics2D g, int x0, int y0, int radius, String name, String unit, float value, float maxValue) { final int TITLE_HEIGHT = 20; // better not change g.translate(x0-radius, y0-radius-TITLE_HEIGHT); // set new origin // (0,0) is top left, width = radius*2, height = width +20 final int R1 = radius; // straal!!! final int centerx2 = R1; final int centery2 = R1+TITLE_HEIGHT; g.setStroke(new BasicStroke(2f)); // lijndikte // plaat g.setColor(Color.red); drawStringCentered(g,name, R1, 15); drawStringCentered(g, ""+(int)maxValue/2, R1, 40); g.drawString("0", 20, 100); drawStringCentered(g, ""+(int)maxValue, 96, 100); drawStringCentered(g, unit, R1, 105); g.setColor(Color.black); g.fillRect(40, 110, 40, TITLE_HEIGHT); g.setColor(Color.red); drawStringCentered(g, ""+(int)value, R1 , 125); g.setColor(Color.black); g.drawOval( centerx2-R1, centery2-R1, 2*R1, 2*R1); // snelheidsmeter g.setColor(Color.lightGray); g.fillOval(centerx2-5, centery2-5, 10, 10); g.setColor(Color.black); g.drawOval(centerx2-5, centery2-5, 10, 10); //streepjes double line_angle = 60.0; while (line_angle < 301){ double r1; if ((line_angle == 60.0)||(line_angle == 300.0)){ r1 = 45;} else {r1 = 50;} // punt streepje uiterste cirkel double x1 = centerx2-R1*Math.sin(line_angle/360*Math.PI*2); int intx1 = (int)Math.round(x1); double y1 = centery2+(R1*Math.cos(line_angle/360*Math.PI*2)); int inty1 = (int)Math.round(y1); // punt streepje binnenste cirkel double x2 = centerx2-(r1*Math.sin(line_angle/360*Math.PI*2)); int intx2 = (int)Math.round(x2); double y2 = centery2+(r1*Math.cos(line_angle/360*Math.PI*2)); int inty2 = (int)Math.round(y2); // tekenen lijntje g.drawLine(intx1, inty1, intx2, inty2); line_angle = line_angle + 30.0; } // wijzer double x = centerx2-Math.sin(Math.PI/3+value/(maxValue*3/2)*Math.PI*2)*R1; double y = centery2+Math.cos(Math.PI/3+value/(maxValue*3/2)*Math.PI*2)*R1; int intx = (int)Math.round(x); int inty = (int)Math.round(y); g.setColor(Color.red); g.drawLine(centerx2, centery2 ,intx, inty); g.setColor(Color.black); g.translate(-x0+radius, -y0+radius+TITLE_HEIGHT); // reset origin } public static void DrawWheel(Graphics g, int centerX, int centerY, int radius, int nbrSpaken, double startAngle){ g.drawOval(centerX-radius, centerY-radius, radius*2, radius*2); final float SPAAK_ANGLE = 2*(float)Math.PI/nbrSpaken; for(int i=0; i< nbrSpaken;i++){ g.drawLine(centerX, centerY, centerX + (int)(Math.cos(startAngle)*radius), centerY - (int)(Math.sin(startAngle)*radius)); startAngle += SPAAK_ANGLE; } } // Geometry /////////// public static int distanceToLine(int x, int y, int m, int q){ return (y-m*x-q); } public static boolean isPointOnLine(int x, int y, int x1, int y1, int x2, int y2, int lineWidth){ // point in rectangle (x1, y1) - (x2, y2) if ((x-x1)*(x-x2) <= lineWidth*lineWidth && (y-y1)*(y-y2) <= lineWidth*lineWidth ){ if (x1 == x2){ return true; } else { // distance to line float m = ((float)(y1-y2))/((float)(x1-x2)); float q = y1 - m*x1; //System.out.println("Line between ("+x1+", "+y1+") and ("+x2+", "+y2+") gives m="+m+", q="+q); float distance = y - m*x - q; //System.out.println("distance of ("+x+", "+y+") to line is "+distance); if (Math.abs(distance) <= lineWidth) return true; } } return false; } public static boolean isPointWithinLine(int x, int y, int x1, int y1, int x2, int y2){ return isPointOnLine(x, y, x1, y1, x2, y2, 0) && !(x==x1 && y==y1) && !(x==x2 && y==y2); } public static float getEuclidianDistance(int x1, int y1, int x2, int y2) { return (float)Math.pow( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2),0.5); } public static float getEuclidianDistance(float[] p1, float[] p2) { float f=0; for (int i=0;i