/* * $Id: RotDiag.java,v 1.8 1996/04/29 14:17:29 schar Exp $ * * Java Implementation of Rotation Diagram Demo * * Daniel Scharstein */ import java.awt.*; import java.applet.*; import java.lang.Math; public class RotDiag extends Applet { PolyPanel polypanel; // final static Color myBG = new Color(25, 100, 150); final static Color myBG = new Color(10, 40, 60); void makebutton(Panel p, String name, GridBagLayout g, GridBagConstraints c) { Button button = new Button(name); g.setConstraints(button, c); button.setBackground(Color.white); button.setForeground(myBG); p.add(button); } public void init() { setLayout(new BorderLayout()); setBackground(Color.white); setForeground(myBG); setFont(new Font("Dialog", Font.BOLD, 14)); polypanel = new PolyPanel(); add("Center", polypanel); Panel buttonpanel = new Panel(); buttonpanel.setLayout(new BorderLayout()); add("West", buttonpanel); Panel buttons = new Panel(); GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints constraints = new GridBagConstraints(); buttons.setLayout(gridbag); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridwidth = GridBagConstraints.REMAINDER; //end row constraints.ipady = 8; constraints.weighty = 1.0; constraints.insets = new Insets(3, 3, 3, 3); makebutton(buttons, "Add Point", gridbag, constraints); makebutton(buttons, "Remove Point", gridbag, constraints); Checkbox x = new Checkbox("Circles"); gridbag.setConstraints(x, constraints); buttons.add(x); x = new Checkbox("Polygons"); gridbag.setConstraints(x, constraints); buttons.add(x); makebutton(buttons, "Add Vertex", gridbag, constraints); makebutton(buttons, "Remove Vertex", gridbag, constraints); makebutton(buttons, "Randomize", gridbag, constraints); // buttonpanel.add("North", buttons); // keep at the top buttonpanel.add("Center", buttons); // fill whole column } public boolean action(Event evt, Object arg) { if (arg instanceof Boolean) { if (((Checkbox)evt.target).getLabel().equals("Circles")) { polypanel.switch_circles(((Boolean)arg).booleanValue()); return true; } else if (((Checkbox)evt.target).getLabel().equals("Polygons")) { polypanel.switch_polys(((Boolean)arg).booleanValue()); return true; } return false; } if ("Add Point".equals(arg)) { polypanel.addPoint(); return true; } if ("Remove Point".equals(arg)) { polypanel.removePoint(); return true; } if ("Randomize".equals(arg)) { polypanel.newPoly(); return true; } if ("Add Vertex".equals(arg)) { polypanel.addVertex(); return true; } if ("Remove Vertex".equals(arg)) { polypanel.removeVertex(); return true; } return false; } public static void main(String args[]) { Frame f = new Frame("Rotation Diagram Application"); RotDiag RotDiag0 = new RotDiag(); RotDiag0.init(); f.add("Center", RotDiag0); f.pack(); f.resize(630, 470); f.show(); } } class PolyPanel extends Panel { ConvPoly p; final int MAXPOINTS = 6; final int MAXVERTICES = 6; final int MINVERTICES = 3; final int STEPDENS[] = {1, 3, 4, 3, 4, 5, 6, 7, 8}; final int RD_BORDER = 20; int mx[] = new int[MAXPOINTS+1]; int my[] = new int[MAXPOINTS+1]; // final static Color myBG = new Color(25, 100, 150); final static Color myBG = new Color(10, 40, 60); // final static Color myBG = Color.blue.darker(); final static Color myRed = new Color(225, 50, 50); // final static Color myBlue = new Color(0, 120, 215); final static Color myBlue = new Color(0, 80, 170); final static Color myGreen = new Color(30, 190, 30); final static Color myPurple = new Color(210, 50, 210); final static Color myYellow = new Color(220, 190, 0); final static Color myCyan = new Color(0, 175, 175); final static Color myGray = new Color(150, 150, 150); Color col[] = {Color.black, myRed, myBlue, myGreen, myPurple, myYellow, myCyan}; // Color col[] = {Color.black, Color.blue, Color.red, // Color.green.darker(), Color.magenta.darker(), // Color.cyan.darker(), Color.gray}; final Font textfont = new Font("Dialog", Font.BOLD, 14); final Font symbolfont = new Font("Symbol", Font.BOLD, 15); final char PiChar = (char)28; // "pi" in font Symbol int numpoints, current_point; int steps; boolean show_circles = false; boolean show_polys = false; double rhoP = 0.0, tP = 1.0; int RDx0, RDy0, RDw, RDh; int Ox, Oy; double radius = 0.0; boolean fixed_radius = false; boolean redraw_rotDiag = true; int dotX, dotY, dotXold, dotYold; Image PolyIm, RdIm; Dimension PolyImsize = new Dimension(); Dimension RdImsize = new Dimension(); Graphics PolyG, RdG; public PolyPanel() { setBackground(Color.white); p = new ConvPoly(); p.addPoint(100, 100); p.addPoint(100, 200); p.addPoint(200, 200); p.self_intersect(1,1); numpoints=2; mx[1] = 220; my[1] = 130; mx[2] = 190; my[2] = 170; rhoP = 4.5; tP = 0.92; } public void addPoint() { if (numpoints < MAXPOINTS) { numpoints++; mx[numpoints] = p.xpoints[0]+(int)((Math.random()-0.5)*60); my[numpoints] = p.ypoints[0]+(int)((Math.random()-0.5)*60); redraw_rotDiag = true; repaint(); } } public void removePoint() { if (numpoints > 0) { numpoints--; redraw_rotDiag = true; repaint(); } } public void addVertex() { int nv = p.npoints+1; if (nv <= MAXVERTICES) { p = new ConvPoly(nv); newPoly(); } } public void removeVertex() { int nv = p.npoints-1; if (nv >= MINVERTICES) { p = new ConvPoly(nv); newPoly(); } } public void newPoly() { int w = size().width/10, h = size().height/10; p.randomize(4*w, 2*h, 7*w, 4*h); p.self_intersect(1,1); rhoP = 0.0; tP = 1.0; redraw_rotDiag = true; repaint(); } public void switch_circles(boolean b) { show_circles = b; repaint(); } public void switch_polys(boolean b) { show_polys = b; repaint(); } public void drawRotDiag(Graphics g, ConvPoly p, int qx, int qy, int steps, double stepoffs) { double r = Math.sqrt(qx*qx+qy*qy); double phi0 = Math.atan2(qy, qx); for(int s=0; s h2) { current_point = 0; } else { current_point = 1; int bestdist = Integer.MAX_VALUE; for (int i = 1 ; i <= numpoints ; i++) { int dist = (mx[i]-cx)*(mx[i]-cx)+(my[i]-cy)*(my[i]-cy); if (dist < bestdist) { current_point = i; bestdist = dist; } } if (show_circles) { fixed_radius = true; int fx = mx[current_point] - Ox; int fy = my[current_point] - Oy; radius = Math.sqrt((double)(fx*fx+fy*fy)); } } } if (current_point == 0) { // bottom half redraw_rotDiag = false; cy = cy - h2; cx = Math.min(RDx0+RDw, Math.max(RDx0, cx)); cy = Math.min(RDy0+RDh, Math.max(RDy0, cy)); rhoP = 2.0*Math.PI*(double)(cx-RD_BORDER) / (double)(w-2*RD_BORDER); tP = (double)(cy-RD_BORDER) / (double)(h2-2*RD_BORDER); } else { // top_half cx = Math.min(w-4, Math.max(3, cx)); cy = Math.min(h2-4, Math.max(3, cy)); if (fixed_radius) { int dx = cx-Ox, dy = cy-Oy; double a = Math.atan2(dy, dx); cx = Ox+(int)Math.round(radius*Math.cos(a)); cy = Oy+(int)Math.round(radius*Math.sin(a)); } mx[current_point] = cx; my[current_point] = cy; redraw_rotDiag = true; } repaint(); return true; case Event.WINDOW_DESTROY: System.exit(0); return true; default: return false; } } } // A Convex Polygon class ConvPoly { public int npoints = 0; public int xpoints[] = new int[4]; public int ypoints[] = new int[4]; public double cumlength[] = new double[4]; public double circumference = 0; public int numInters = 0; public int xInters[] = new int[4]; public int yInters[] = new int[4]; public double posInters[] = new double[4]; // Creates an empty polygon public ConvPoly() { } public ConvPoly(int xpoints[], int ypoints[], int npoints) { this.npoints = npoints; this.xpoints = new int[npoints]; this.ypoints = new int[npoints]; cumlength = new double[npoints]; System.arraycopy(xpoints, 0, this.xpoints, 0, npoints); System.arraycopy(ypoints, 0, this.ypoints, 0, npoints); } public ConvPoly(ConvPoly p) { this(p.xpoints, p.ypoints, p.npoints); } public ConvPoly(int npoints) { this.npoints = npoints; this.xpoints = new int[npoints]; this.ypoints = new int[npoints]; cumlength = new double[npoints]; randomize(10, 10, 100, 100); } public void addPoint(int x, int y) { if (npoints == xpoints.length) { int tmp[]; tmp = new int[npoints * 2]; System.arraycopy(xpoints, 0, tmp, 0, npoints); xpoints = tmp; tmp = new int[npoints * 2]; System.arraycopy(ypoints, 0, tmp, 0, npoints); ypoints = tmp; cumlength = new double[npoints * 2]; } xpoints[npoints] = x; ypoints[npoints] = y; npoints++; } public void move(int mx, int my) { int dx = mx - xpoints[0]; int dy = my - ypoints[0]; for (int i = 0; i < npoints; i++) { xpoints[i] += dx; ypoints[i] += dy; } } // randomize position // not really random, but all points lie on ellipse bounded // by xmin..max, ymin..ymax public void randomize(int xmin, int ymin, int xmax, int ymax) { int angle = 0; for (int i = 0; i < npoints; i++) { angle += (int)(Math.random()*100); xpoints[i] = angle; } for (int i = 0; i < npoints; i++) { double phi = (double)xpoints[i]*2.0*Math.PI/(double)angle; xpoints[i] = xmin+(int)((Math.cos(phi)+1.0)*(double)(xmax-xmin)/2.0); ypoints[i] = ymin+(int)((Math.sin(phi)+1.0)*(double)(ymax-ymin)/2.0); } } void draw(Graphics g, int tx, int ty, double rho, boolean show_poly, boolean transl_first) { int markersize = 3; int x, y, xold=0, yold=0; int m2 = markersize/2; int px0 = xpoints[0], py0 = ypoints[0]; for (int i = 0; i <= npoints; i++) { double ss = Math.sin(rho), cc = Math.cos(rho); if (transl_first) { int x0 = tx-px0+xpoints[i % npoints]; int y0 = ty-py0+ypoints[i % npoints]; x = px0+(int)Math.round( cc*x0 + ss*y0); y = py0+(int)Math.round(-ss*x0 + cc*y0); } else { int x0 = -px0+xpoints[i % npoints]; int y0 = -py0+ypoints[i % npoints]; x = tx+px0+(int)Math.round( cc*x0 + ss*y0); y = ty+py0+(int)Math.round(-ss*x0 + cc*y0); } if (i>0) { if (! show_poly) return; g.fillRect(x-m2, y-m2, markersize, markersize); g.drawLine(x, y, xold, yold); } else g.fillOval(x-m2-2, y-m2-2, markersize+4, markersize+4); xold = x; yold = y; } } void draw(Graphics g) { draw(g, 0, 0, 0.0, true, false); } /** * Is point (x, y) inside the polygon? Uses even-odd winding. * @param x the X coordinate of the point to be tested * @param y the Y coordinate of the point to be tested * * Based on code by Hanpeter van Vliet . */ public boolean inside(int x, int y) { int hits = 0; // Walk the edges of the polygon for (int i = 0; i < npoints; i++) { int j = (i + 1) % npoints; int dx = xpoints[j] - xpoints[i]; int dy = ypoints[j] - ypoints[i]; // ignore horizontal edges completely if (dy != 0) { // Check to see if the edge intersects // the horizontal halfline through (x, y) int rx = x - xpoints[i]; int ry = y - ypoints[i]; // Quick and dirty way to deal with vertices // that fall exactly on the halfline if (ypoints[i] == y) ry--; if (ypoints[j] == y) dy++; double s = (double)ry / (double)dy; if (s >= 0.0 && s <= 1.0) { if ((int)Math.round(s * dx) > rx) hits++; } } } return (hits % 2) != 0; } public boolean inside(double x, double y) { int hits = 0; // Walk the edges of the polygon for (int i = 0; i < npoints; i++) { int j = (i + 1) % npoints; double dx = (double)(xpoints[j] - xpoints[i]); double dy = (double)(ypoints[j] - ypoints[i]); // ignore horizontal edges completely if (dy != 0.0) { // Check to see if the edge intersects // the horizontal halfline through (x, y) double rx = x - xpoints[i]; double ry = y - ypoints[i]; // Quick and dirty way to deal with vertices // that fall exactly on the halfline if (ypoints[i] == y) ry -= 1e-10; if (ypoints[j] == y) dy += 1e-10; double s = ry / dy; if (s >= 0.0 && s <= 1.0) { if ((s * dx) > rx) hits++; } } } return (hits % 2) != 0; } // computes intersections of boundaries of p and p+(tx, ty) // initializes cicumference, cumlength[], // numInters, xInters[], yIntersy[], posInters[] void self_intersect(int tx, int ty) { circumference = 0; numInters = 0; for (int i = 0; i < npoints; i++) { cumlength[i] = circumference; int i1 = (i+1) % npoints; int px1 = xpoints[i], py1 = ypoints[i]; int px2 = xpoints[i1], py2 = ypoints[i1]; double pl = Math.sqrt((px1-px2)*(px1-px2)+(py1-py2)*(py1-py2)); forj: for (int j = 0; j < npoints; j++) { if (j != i) { double f1, f2; int j1 = (j+1) % npoints; int qx1 = tx+xpoints[j], qy1 = ty+ypoints[j]; int qx2 = tx+xpoints[j1], qy2 = ty+ypoints[j1]; // test for intersection int a11 = px2-px1, a12 = qx1-qx2, b1 = qx1-px1; int a21 = py2-py1, a22 = qy1-qy2, b2 = qy1-py1; if (a22 == 0) { if (a21 == 0 || a12 == 0) continue forj; f1 = ((double)b2) / ((double)a21); f2 = ((double)(b1 - f1*a11))/((double)a12); } else { double denom = (a11 - ((double)a21*a12)/(double)a22); if (denom == 0.0) continue forj; f1 = ((double)(b1 - ((double)b2*a12)/(double)a22)) / denom; f2 = ((double)(b2 - f1*a21))/((double)a22); } if (0<=f1 && f1<1 && 0<=f2 && f2<1) { if (numInters>=2) System.out.println("numInters = " + (numInters+1)); // else { xInters[numInters] = (int)Math.round((1-f1)*px1 + f1*px2); yInters[numInters] = (int)Math.round((1-f1)*py1 + f1*py2); posInters[numInters] = circumference + f1*pl; // } numInters++; } } } circumference += pl; } } void self_intersect(double tx, double ty) { circumference = 0; numInters = 0; for (int i = 0; i < npoints; i++) { cumlength[i] = circumference; int i1 = (i+1) % npoints; int px1 = xpoints[i], py1 = ypoints[i]; int px2 = xpoints[i1], py2 = ypoints[i1]; double pl = Math.sqrt((px1-px2)*(px1-px2)+(py1-py2)*(py1-py2)); forj: for (int j = 0; j < npoints; j++) { if (j != i) { double f1, f2; int j1 = (j+1) % npoints; double qx1 = tx+xpoints[j], qy1 = ty+ypoints[j]; double qx2 = tx+xpoints[j1], qy2 = ty+ypoints[j1]; // test for intersection double a11 = px2-px1, a12 = qx1-qx2, b1 = qx1-px1; double a21 = py2-py1, a22 = qy1-qy2, b2 = qy1-py1; if (a22 == 0.0) { if (a21 == 0.0 || a12 == 0.0) continue forj; f1 = b2 / a21; f2 = (b1 - f1*a11)/a12; } else { double denom = a11 - a21*a12/a22; if (denom == 0.0) continue forj; f1 = (b1 - b2*a12/a22) / denom; f2 = (b2 - f1*a21)/a22; } if (0<=f1 && f1<1 && 0<=f2 && f2<1) { if (numInters>=2) System.out.println("numInters = " + (numInters+1)); // else { xInters[numInters] = (int)Math.round((1-f1)*px1 + f1*px2); yInters[numInters] = (int)Math.round((1-f1)*py1 + f1*py2); posInters[numInters] = circumference + f1*pl; // } numInters++; } } } circumference += pl; } } }