/**************************************************************************/ /* Graph Reader V2.0 (C) Ju Li (liju99@mit.edu) - Wed Sep 8 1999 */ /* http://mmm.mit.edu/~liju99/Archive/NetApp/JavaScript/Reader/reader.htm */ /* Includes: reader.htm reader.java -> reader.class example.gif test.gif */ /* Usage: % javac -O -ver 1.1.3 reader.java; netscape reader.html & */ /**************************************************************************/ import java.applet.*; import java.awt.*; import java.awt.event.*; public class reader extends Applet implements ActionListener, KeyListener, MouseListener, ItemListener { // toy list String graph_file; Image graph; MediaTracker tracker; TextField T_Anchor; // TextField T_Command; Button B_Anchor,B_Print_Matlab,B_Print_Latex,B_Print_HTML; Checkbox C_Show_Saved; Graphics applet_graphics; boolean show_saved = true; // (basex,basey) is the upper-left corner of the graph int basex=10,basey=10; // default graph properties int graph_width=510,graph_height=440; // cover the floor with this color in case graph is transparent and thin float graph_background_R=(float)1,graph_background_G=(float)1, graph_background_B=(float)1; Color graph_background_color; // default graph font for messaging and labelling String graph_fontname = "TimesRoman"; final static int GRAPH_FONTSTYLE = Font.ITALIC; int graph_fontsize = 12; Font graph_font; FontMetrics graph_fontmetrics; // default graph pointer properties int pointer_size=2,hollow_pointer_size_added=1,hollow_pointer_size; float pointer_R=(float)0,pointer_G=(float)0,pointer_B=(float)0; Color pointer_color; final static int POINTER_STEPSIZE_CONTORL=10, POINTER_STEPSIZE_SHIFT=10, POINTER_STEPSIZE_ALT=5, POINTER_STEPSIZE_META=5; // (curx,cury) is the screen position of the pointer int curx=-10000,cury=-10000; // anchor screen coordinates and corresponding values int nanchor=0,anchorx[]=new int[3],anchory[]=new int[3]; double fanchorx[]=new double[3],fanchory[]=new double[3]; String anchorname[] = {"Set 1st anchor (x1 y1):", "Set 2nd anchor (x2 y2):", "Set 3rd anchor (x3 y3):", "Press again to reset"}; // floating-point tolerable tiny and relative error final static double tolerable_tiny = 1E-14; final static double tolerable_relative_error = 1E-4; // (ax,ay) is how much a unit A vector corresponds on screen // (bx,by) is how much a unit B vector corresponds on screen double ax,ay,bx,by,det; // saved data stack int nsaved = 0; final static int MAXSAVE = 512; int savex[]=new int[MAXSAVE], savey[]=new int[MAXSAVE]; double fcurx,fcury,fsavex[]=new double[MAXSAVE], fsavey[]=new double[MAXSAVE]; // global status bar messaging String status_mesg; static boolean already_been = false; public void init() { int i; String arg; tracker = new MediaTracker(this); graph_file = getParameter("graph_file"); if ( graph_file.startsWith("http:") || graph_file.startsWith("HTTP:") ) // remote graph = getImage(getDocumentBase(), graph_file); else // local graph = getToolkit().getImage(graph_file); status_mesg = "Loading " + graph_file + "..."; showStatus(status_mesg); System.out.println(); System.out.println(); System.out.println(status_mesg); tracker.addImage(graph,0); // graph ID = 0 setLayout(new BorderLayout()); Panel south = new Panel(); add ("South", south); south.setLayout(new FlowLayout()); south.add(B_Anchor = new Button(anchorname[nanchor])); // Buttons feel Action, inform applet before & after themselves B_Anchor.addActionListener(this); south.add(T_Anchor = new TextField("0. 0. ")); // TextFields feel Key, inform applet before & after themselves T_Anchor.addKeyListener(this); // T_Command = new TextField(0); // south.add(T_Command); // T_Command.addKeyListener(this); south.add(B_Print_Matlab = new Button("MLB")); B_Print_Matlab.addActionListener(this); south.add(B_Print_Latex = new Button("LTX")); B_Print_Latex.addActionListener(this); south.add(B_Print_HTML = new Button("HTML")); B_Print_HTML.addActionListener(this); south.add(C_Show_Saved = new Checkbox("Show saved",show_saved)); // Checkbox feels Item, inform applet before & after themselves C_Show_Saved.addItemListener(this); // the applet itself feels Mouse addMouseListener(this); // if we comment out the next line and uncomment all // lines involving T_Command, we get a vi prototype addKeyListener(this); // disable all the gadgets - very narrow event path B_Anchor.setEnabled(false); T_Anchor.setEnabled(false); // T_Command.setEnabled(false); B_Print_Matlab.setEnabled(false); B_Print_Latex.setEnabled(false); B_Print_HTML.setEnabled(false); C_Show_Saved.setEnabled(false); arg = getParameter("basex"); if (arg != null) basex = Integer.parseInt(arg); arg = getParameter("basey"); if (arg != null) basey = Integer.parseInt(arg); basex += getBounds().x; basey += getBounds().y; arg = getParameter("graph_width"); if (arg != null) graph_width = Integer.parseInt(arg); arg = getParameter("graph_height"); if (arg != null) graph_height = Integer.parseInt(arg); arg = getParameter("pointer_size"); if (arg != null) pointer_size = Integer.parseInt(arg); hollow_pointer_size = pointer_size + hollow_pointer_size_added; arg = getParameter("pointer_R"); if (arg != null) pointer_R = (new Float(arg)).floatValue(); arg = getParameter("pointer_G"); if (arg != null) pointer_G = (new Float(arg)).floatValue(); arg = getParameter("pointer_B"); if (arg != null) pointer_B = (new Float(arg)).floatValue(); pointer_color = new Color(pointer_R,pointer_G,pointer_B); arg = getParameter("graph_fontsize"); if (arg != null) graph_fontsize = Integer.parseInt(arg); arg = getParameter("graph_fontname"); if (arg != null) graph_fontname = arg; graph_font = new Font(graph_fontname, GRAPH_FONTSTYLE, graph_fontsize); arg = getParameter("graph_background_R"); if (arg != null) graph_background_R = (new Float(arg)).floatValue(); arg = getParameter("graph_background_G"); if (arg != null) graph_background_G = (new Float(arg)).floatValue(); arg = getParameter("graph_background_B"); if (arg != null) graph_background_B = (new Float(arg)).floatValue(); graph_background_color = new Color(graph_background_R, graph_background_G, graph_background_B); applet_graphics = getGraphics(); graph_fontmetrics = applet_graphics.getFontMetrics(graph_font); try { tracker.waitForID(0); status_mesg = "Click in the graph. You will see your pointer."; } catch ( Exception e ) { status_mesg = "Exception: " + e.getMessage(); System.out.println(status_mesg); } showStatus(status_mesg); return; } // end init() public void draw_pointer (Graphics g, int x, int y, int size) { // draw pointer centered at x,y int px[] = {x+size, x, x-size, x }; int py[] = {y, y+size, y, y-size}; g.fillPolygon (px, py, 4); return; } // end draw_pointer() public void draw_hollow_pointer (Graphics g, int x, int y, int size) { int px[] = {x+size, x, x-size, x }; int py[] = {y, y+size, y, y-size}; g.drawPolygon (px, py, 4); return; } // end draw_hollow_pointer() public boolean in_graph (int x, int y) { return ( (x >= basex) && (y >= basey) && (x <= basex+graph_width-1) && (y <= basey+graph_height-1) ); } // end in_graph() public void xorpaint_cur_pointer (Graphics g) { // XOR overlap paint mode: g.setColor (graph_background_color); g.setXORMode (pointer_color); // c -> graph_background_color (+) pointer_color (+) c if (in_graph(curx,cury)) draw_pointer (g, curx, cury, pointer_size); return; } // end xorpaint_cur_pointer() public void xorpaint_saved (Graphics g, int start, int finish) { int i; String data_index; g.setColor (graph_background_color); g.setXORMode (pointer_color); // c -> graph_background_color (+) pointer_color (+) c for (i=start; i<=finish; i++) if (in_graph(savex[i],savey[i])) { draw_hollow_pointer (g, savex[i], savey[i], hollow_pointer_size); g.setFont(graph_font); data_index = Integer.toString(i+1); // drawString origin is lower-left corner: g.drawString (data_index, savex[i] - graph_fontmetrics.stringWidth(data_index)/2, savey[i] + graph_fontmetrics.getHeight() + hollow_pointer_size); } return; } // end xorpaint_saved() public void update (Graphics g) { // update(), if not overridden, will // 1) clear g 2) set colors 3) call paint(g). // The "clear g" part is the reason screen flashes. paint (g); showStatus(status_mesg); } // end update() public void paint (Graphics g) { // pretend to always start all over g.setColor(graph_background_color); g.setPaintMode(); // clean whatever marks one has left g.fillRect(basex, basey, graph_width, graph_height); if (tracker.isErrorID(0)) { // tracks progress of loading the graph status_mesg = "Bad image. Check by clicking the top-most link."; showStatus(status_mesg); g.setFont(graph_font); g.setXORMode(pointer_color); g.drawString(status_mesg, basex + (graph_width - graph_fontmetrics.stringWidth(status_mesg))/2, basey + (graph_height+graph_fontmetrics.getHeight())/2); } else g.drawImage (graph, basex, basey, graph_width, graph_height, this); xorpaint_cur_pointer(g); if (show_saved) xorpaint_saved(g,0,nsaved-1); } // end paint() public void move_pointer_to (int x,int y) { int i, dx, dy; // clear current pointer first xorpaint_cur_pointer (applet_graphics); curx = x; cury = y; if (nanchor==3) { // at the mature stage dx = curx - anchorx[0]; dy = cury - anchory[0]; fcurx = (by*dx-bx*dy)/det + fanchorx[0]; fcury = (-ay*dx+ax*dy)/det + fanchory[0]; status_mesg = "s to save: " + fcurx + " " + fcury; if (!in_graph(curx,cury)) status_mesg += " (warning: point out of graph)"; } else status_mesg = "Move the pointer around... press " + "return to submit your anchor " + (nanchor+1); xorpaint_cur_pointer (applet_graphics); // mouse click always highlights command TextField goto_command_mode(); return; } // end move_pointer_to() public boolean calculate_frame() { int dx2, dy2, dx3, dy3; double la2, lb2, la3, lb3, denom, modulus; dx2 = anchorx[1] - anchorx[0]; dy2 = anchory[1] - anchory[0]; dx3 = anchorx[2] - anchorx[0]; dy3 = anchory[2] - anchory[0]; modulus = ( Math.abs(dx2) + Math.abs(dy2) + Math.abs(dx3) + Math.abs(dy3) ) / 4; // anchors too close in terms of screen resolution if (modulus < pointer_size + 1) return false; // anchors form straight line on screen if (Math.abs((dx2/modulus*dy3/modulus- dy2/modulus*dx3/modulus)) < tolerable_relative_error) return false; la2 = fanchorx[1] - fanchorx[0]; lb2 = fanchory[1] - fanchory[0]; la3 = fanchorx[2] - fanchorx[0]; lb3 = fanchory[2] - fanchory[0]; modulus = ( Math.abs(la2) + Math.abs(lb2) + Math.abs(la3) + Math.abs(lb3) ) / 4; // anchors too close in terms of value if (modulus < tolerable_tiny) return false; // anchors form straight line in value space if (Math.abs((la2/modulus*lb3/modulus- lb2/modulus*la3/modulus)) < tolerable_relative_error) return false; // matrix inversion and multiplication denom = la2*lb3 - lb2*la3; ax = (dx2*lb3-dx3*lb2) / denom; ay = (dy2*lb3-dy3*lb2) / denom; bx = (-dx2*la3+dx3*la2) / denom; by = (-dy2*la3+dy3*la2) / denom; det = ax*by - ay*bx; return true; } // end calculate_frame() public void mouseClicked(MouseEvent e) {return;} public void mouseExited(MouseEvent e) {return;} public void mouseReleased(MouseEvent e) {return;} public void mouseMoved(MouseEvent e) {return;} public void mouseEntered(MouseEvent e) { // global status messaging showStatus(status_mesg); return; } // end mouseEntered() public void mousePressed(MouseEvent e) { // different with usual UNIX/C treatment // because stupid BUTTON1_MASK could be 0 // System.out.println(e.getModifiers()); // System.out.println(e.CTRL_MASK + " " + e.SHIFT_MASK); // System.out.println(e.BUTTON1_MASK + " " + // e.BUTTON2_MASK + " " + // e.BUTTON3_MASK + "\n"); switch (e.getModifiers()) { case e.CTRL_MASK | e.BUTTON1_MASK: // left case e.CTRL_MASK | e.BUTTON1_MASK & 15: // Netscape bug move_pointer_to (curx-1, cury); return; case e.CTRL_MASK | e.BUTTON3_MASK: // right move_pointer_to (curx+1, cury); return; case e.SHIFT_MASK | e.BUTTON1_MASK: // down case e.SHIFT_MASK | e.BUTTON1_MASK & 15: // Netscape bug move_pointer_to (curx, cury+1); return; case e.SHIFT_MASK | e.BUTTON3_MASK: // up move_pointer_to (curx, cury-1); return; case e.CTRL_MASK | e.BUTTON2_MASK: // down move_pointer_to (curx, cury+1); return; case e.SHIFT_MASK | e.BUTTON2_MASK: // up move_pointer_to (curx, cury-1); return; default: move_pointer_to(e.getX(),e.getY()); } return; } // end mousePressed() public void goto_command_mode() { showStatus (status_mesg); // freeze text input T_Anchor.setEnabled(false); B_Anchor.setEnabled(nanchor==3); // activate command window // T_Command.setEnabled(true); // T_Command.requestFocus(); this.requestFocus(); return; } // end goto_command_mode() public void goto_input_mode() { showStatus (status_mesg); // T_Command.setEnabled(false); B_Anchor.setEnabled(true); T_Anchor.setEnabled(true); T_Anchor.requestFocus(); return; } // end goto_input_mode() public boolean isFocusTraversable() {return true;} public void anchor_button_pressed() { String Anchor_Text; Double G; int i; switch (nanchor) { case 0: case 1: case 2: anchorx[nanchor] = curx; anchory[nanchor] = cury; try { Anchor_Text = T_Anchor.getText().trim(); i = Anchor_Text.indexOf(" "); G = Double.valueOf(Anchor_Text.substring(0,i)); fanchorx[nanchor] = G.doubleValue(); G = Double.valueOf(Anchor_Text.substring(i+1)); fanchory[nanchor] = G.doubleValue(); status_mesg = "Anchor " + (nanchor+1) + " set at " + fanchorx[nanchor] + " " + fanchory[nanchor] + (nanchor==2?". Done! Click on": ". More clicks..."); } catch (Exception e) { status_mesg = "Wrong input: re-input two numbers separated by space"; goto_input_mode(); return; } // fool-proof help: if (nanchor==1) { // second anchor just comes in if ( (anchorx[1]==anchorx[0]) && (anchory[1]==anchory[0]) ) { status_mesg = "Same as anchor 1 on screen: reset anchor 2"; goto_command_mode(); return; } if ( (fanchorx[1]==fanchorx[0]) && (fanchory[1]==fanchory[0]) ) { status_mesg = "Same as anchor 1 in values: re-input anchor 2"; goto_input_mode(); return; } } if (nanchor==2) { // third anchor just comes in if ( (anchorx[2]==anchorx[0]) && (anchory[2]==anchory[0]) ) { status_mesg = "Same as anchor 1 on screen: reset anchor 3"; goto_command_mode(); return; } if ( (fanchorx[2]==fanchorx[0]) && (fanchory[2]==fanchory[0]) ) { status_mesg = "Same as anchor 1 in values: re-input anchor 3"; goto_input_mode(); return; } if ( (anchorx[2]==anchorx[1]) && (anchory[2]==anchory[1]) ) { status_mesg = "Same as anchor 2 on screen: reset anchor 3"; goto_command_mode(); return; } if ( (fanchorx[2]==fanchorx[1]) && (fanchory[2]==fanchory[1]) ) { status_mesg = "Same as anchor 2 in values: re-input anchor 3"; goto_input_mode(); return; } // check if the Jacobians are all right... if ( !calculate_frame() ) { nanchor = 0; status_mesg = "Bad triple anchors: reset them all"; B_Anchor.setLabel(anchorname[nanchor]); goto_command_mode(); return; } // pass all tests -> mature } nanchor++; B_Anchor.setLabel(anchorname[nanchor]); goto_command_mode(); return; case 3: // now we are going back to where we started // clear marked if painted if (show_saved) xorpaint_saved(applet_graphics, 0, nsaved-1); // abolish all previously saved nsaved = 0; nanchor = 0; B_Anchor.setLabel(anchorname[nanchor]); B_Print_Matlab.setEnabled(false); B_Print_Latex.setEnabled(false); B_Print_HTML.setEnabled(false); C_Show_Saved.setEnabled(false); status_mesg = "Move the pointer around... press " + "return to submit your anchor " + (nanchor+1); goto_command_mode(); return; } return; } // end anchor_button_pressed() public int pointer_stepsize (KeyEvent e) { if (e.isControlDown()) return POINTER_STEPSIZE_CONTORL; else if (e.isShiftDown()) return POINTER_STEPSIZE_SHIFT; else if (e.isAltDown()) return POINTER_STEPSIZE_ALT; else if (e.isMetaDown()) return POINTER_STEPSIZE_META; return 1; } // end pointer_stepsize() public void keyTyped (KeyEvent e){} public void keyReleased (KeyEvent e) { // after TextField has done its own stuff if (e.getSource() == T_Anchor) switch (e.getKeyCode()) { case e.VK_UNDEFINED: case e.VK_ENTER: return; default: status_mesg = "[" + T_Anchor.getText() + "]"; showStatus (status_mesg); return; } return; } // end keyReleased() public void keyPressed (KeyEvent e) { // before TextField has done its own stuff int i,j; double d2,d2min; if (e.getSource() == T_Anchor) { // input window events switch (e.getKeyCode()) { case e.VK_ENTER: // equivalent to press the button B_Anchor anchor_button_pressed(); e.consume(); return; case e.VK_X: // query status showStatus ("x spy: " + status_mesg); e.consume(); return; } return; } // input window events else { // command window events e.consume(); // no need to show anything in the command TextField switch (e.getKeyCode()) { case e.VK_ENTER: if (nanchor!=3) { // goto input window for data entry status_mesg = "Now you can key in the values. " + "Press return to submit"; if (already_been) T_Anchor.setText(""); else already_been = true; goto_input_mode(); } else // at mature stage { // prevent accidents status_mesg = "Return is invalid now. " + "To reset anchors, you must click the button"; showStatus (status_mesg); } return; case e.VK_S: // save a new record if (nanchor==3) { // at mature stage if (nsaved >= MAXSAVE) { status_mesg = "Too much saved (" + MAXSAVE + " rec.), reduce inventory please"; showStatus (status_mesg); return; } // fcurx, fcury already evaluated by move_pointer_to() savex[nsaved] = curx; savey[nsaved] = cury; fsavex[nsaved] = fcurx; fsavey[nsaved] = fcury; if (show_saved) xorpaint_saved (applet_graphics, nsaved, nsaved); nsaved++; status_mesg = fcurx + " " + fcury + " saved; total " + nsaved + " rec."; if (nsaved==1) { // enable output channels B_Print_Matlab.setEnabled(true); B_Print_Latex.setEnabled(true); B_Print_HTML.setEnabled(true); C_Show_Saved.setEnabled(true); } } else status_mesg = "Cannot because the anchors are not yet set!"; showStatus (status_mesg); return; case e.VK_D: // delete nearest data to pointer if (nsaved == 0) status_mesg = "no records to be deleted"; else { for (j=-1,d2min=1E20,i=0; i"); System.out.println ("
"); System.out.println (""); for (i=0; i"); System.out.println ("
#XY
" + (i+1) + "" + fsavex[i] + "" + fsavey[i] + "
"); } // end print_in_HTML() public String getAppletInfo() { return "Graph Reader V2.0 (C) Ju Li (liju99@mit.edu)"; } // end getAppletInfo() } // end class reader