/* Roots.java * * Dibuja la rep. gráfica de una función y demuestra el cálculo de raíces. * * Está basado en un código tomado de * * http://www.mcluhan.toronto.edu/~zoli/java/Graph.html * * En realidad, debería tener una clase de representación que tome * una función para dibujar, con métodos add_point, add_line, * del_point, del_line y de restablecimiento y con una * función a la que se pueda llamar con un solo clic. */ import java.awt.*; import java.lang.Math; import java.awt.event.*; import javax.swing.*; class Coordinate { double x, y; } class Segment { double x0, y0, x1, y1; Color colour; } class ColouredPoint { double x, y; Color colour; } public class Roots extends JFrame implements ActionListener, MouseMotionListener, MouseListener, AdjustmentListener { private void exitForm(java.awt.event.WindowEvent evt) { System.exit(0); } public static void main(String args[]) { Roots roots = new Roots(); roots.setTitle("Raíces"); roots.show(); roots.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } int MAX_NUM_SEGS = 100; int MAX_NUM_POINTS = 100; int POINT_RADIUS = 3; double SCALE_FACTOR = 0.4; double INITIAL_SCALE = 50; /* Algunos colores: * * 34, 139, 34 verde bosque * 205, 92, 92 rojo indio * 210, 105, 30 chocolate * 143, 188, 143 verde botella * 192, 96, 119 "ventana no seleccionada" en X */ Color not_found_colour = Color.red; Color found_colour = Color.green; Color prev_colour = Color.red; Color curr_colour = Color.orange; Color next_colour = Color.yellow; double mid_x, mid_y, pix_per_unit; JScrollBar zoom_bar; Coordinate dragFrom = new Coordinate(); Coordinate dragTo = new Coordinate(); Coordinate origin = new Coordinate(); Segment segs[]; ColouredPoint pts[]; int num_segs = 0; int num_pts = 0; DrawingArea drawingArea; double Scale = INITIAL_SCALE * SCALE_FACTOR; int MouseUp = 1; // ---------------------------------------------------------------- Raíces // ---------------------------------------------------------------- Raíces // ---------------------------------------------------------------- Raíces JComboBox method_choice; JComboBox function_choice; JButton reset_button; JPanel graph; String functions[] = { "3 sin(x)", "x^2/10", "1/x", "5 sin(x) / x", "sin(1/x)", "" }; String methods[] = { "Método de bisección", "Método de Newton", "Método secante", "" }; int num_functions, num_methods; int ROOT_DIST = 4; // dist (en píxels) para ampliar la línea vertical en la raíz int TANGENT_DIST = 20; // dist para ampliar la línea tangente más allá de los extremos int mode = 0; // 0 = parada, 1 = ejecución int alg = 1; // 0 = bisección, 1 = newton, 2 = secante int step = 0; // paso actual dentro del algoritmo double prev_x, prev_y; // posiciones actual y anterior double curr_x, curr_y; double next_x, next_y; /* La propia función */ public double f( double x ) { switch (function_choice.getSelectedIndex()) { case 0: return 3 * Math.sin(x); case 1: return x * x / 10.0; case 2: if (x != 0) return 1.0 / x; else return 0; case 3: if (x != 0) return 5 * Math.sin(x) / x; else return 5; case 4: if (x == 0) return 0; else return Math.sin(1/x); default: System.out.println( "Tipo de función desconocido" ); return 0; } } public double f_deriv( double x ) { switch (function_choice.getSelectedIndex()) { case 0: return 3 * Math.cos(x); case 1: if (x<= 0.001 || x>=-0.001) return 2 * x / 10.0; else return 1; case 2: if (x != 0) return -1.0 / (x * x); else return 0.0; case 3: if (x != 0) return 5 * (Math.cos(x) / x - Math.sin(x) / (x * x)); else return 99999; case 4: if (x != 0) return -1/(x*x)*Math.cos(1/x); else return 99999; default: System.out.println( "Tipo de función desconocido" ); return 0; } } /* Qué hacer en cada clic del ratón */ public void mouse_click( int x, int y ) { switch (alg) { case 0: bisect_click( x, y ); break; case 1: newton_click( x, y ); break; case 2: secant_click( x, y ); break; } } public void bisect_click( int scr_x, int scr_y ) { double x, y, signum; switch (step) { case 0: // obtiene prev_x y prev_y x = world_x( scr_x ); y = f(x); if (y < 0) signum = -1; else signum = +1; reset_graph(); add_pt( x, y, prev_colour ); add_extended_seg( ROOT_DIST, x, 0, x, y, prev_colour ); prev_x = x; prev_y = y; step++; break; case 1: // obtiene curr_x y curr_y x = world_x( scr_x ); y = f(x); if (y < 0) signum = -1; else signum = +1; add_pt( x, y, prev_colour ); add_extended_seg( ROOT_DIST, x, 0, x, y, prev_colour ); curr_x = x; curr_y = y; step++; break; case 2: // Obtuvo anterior (prev) y actual (curr) (ya representadas); ahora dibuja el punto medio x = (prev_x + curr_x) / 2.0; y = f(x); add_extended_seg( ROOT_DIST, x, 0, x, y, next_colour ); next_x = x; next_y = y; step++; break; case 3: // selecciona qué extremo se sustituye reset_graph(); if (Math.abs(next_x - curr_x) < .04) { add_pt( next_x, next_y, found_colour ); add_extended_seg( ROOT_DIST, next_x, 0, next_x, next_y, found_colour ); step++; } else { if (prev_y * next_y > 0) { prev_x = next_x; prev_y = next_y; } else if (curr_y * next_y > 0) { curr_x = next_x; curr_y = next_y; } else { prev_x = next_x; curr_x = next_x; prev_y = next_y; curr_y = next_y; } add_pt( prev_x, prev_y, prev_colour ); add_extended_seg( ROOT_DIST, prev_x, 0, prev_x, prev_y, prev_colour ); add_pt( curr_x, curr_y, prev_colour ); add_extended_seg( ROOT_DIST, curr_x, 0, curr_x, curr_y, prev_colour ); step = 2; } break; case 4: step = 0; reset_graph(); break; } } public void newton_click( int scr_x, int scr_y ) { double x, y, signum; switch (step) { case 0: // parado // Define la aproximación inicial en esta posición x = world_x( scr_x ); y = f(x); if (y < 0) signum = -1; else signum = +1; reset_graph(); add_pt( x, y, not_found_colour ); add_extended_seg( ROOT_DIST, x, 0, x, y, not_found_colour ); curr_x = x; curr_y = y; step++; break; case 1: // En un valor de la función (es decir, una raíz aproximada), busca la tangente x = curr_x - f(curr_x) / f_deriv(curr_x); y = 0; reset_graph(); add_extended_seg( ROOT_DIST, curr_x, 0, curr_x, curr_y, not_found_colour ); add_pt( curr_x, curr_y, not_found_colour ); add_extended_seg( TANGENT_DIST, x, y, curr_x, curr_y, next_colour ); prev_x = curr_x; prev_y = curr_y; curr_x = x; curr_y = y; step++; break; case 2: // En una tangente, busca el valor de la función y = f(curr_x); reset_graph(); if (Math.abs(y) < .01) { step++; add_pt( curr_x, y, found_colour ); add_extended_seg( ROOT_DIST, curr_x, 0, curr_x, y, found_colour ); } else { add_extended_seg( TANGENT_DIST, curr_x, curr_y, prev_x, prev_y, next_colour ); add_pt( curr_x, y, not_found_colour ); add_extended_seg( ROOT_DIST, curr_x, 0, curr_x, y, not_found_colour ); step = 1; } curr_y = y; break; case 3: step = 0; reset_graph(); break; } } public void secant_click( int scr_x, int scr_y ) { double x, y, signum; switch (step) { case 0: // obtiene prev_x & prev_y x = world_x( scr_x ); y = f(x); if (y < 0) signum = -1; else signum = +1; reset_graph(); add_pt( x, y, prev_colour ); add_extended_seg( ROOT_DIST, x, 0, x, y, prev_colour ); prev_x = x; prev_y = y; step++; break; case 1: // obtiene curr_x y curr_y x = world_x( scr_x ); y = f(x); if (y < 0) signum = -1; else signum = +1; add_pt( x, y, curr_colour ); add_extended_seg( ROOT_DIST, x, 0, x, y, curr_colour ); curr_x = x; curr_y = y; step++; break; case 2: // Obtuvo anterior (prev) y actual (curr) (ya representados); ahora dibuja la secante double delta = f(curr_x) - f(prev_x); if(delta <= 0.001 && delta >= -0.001) delta = 0.1; x = (prev_x * f(curr_x) - curr_x * f(prev_x)) / delta; if(x>200 && (function_choice.getSelectedIndex()==1)) x=200; if(x<-200 && (function_choice.getSelectedIndex()==1)) x=-200; y = f(x); add_extended_seg( TANGENT_DIST, x, 0, curr_x, curr_y, next_colour ); add_extended_seg( TANGENT_DIST, x, 0, prev_x, prev_y, next_colour ); next_x = x; next_y = y; step++; break; case 3: // Ya están dibujados el anterior (prev), el actual (curr) y la secante; ahora se dibuja la línea hasta f() if (Math.abs(next_y) < .01) { reset_graph(); add_pt( next_x, next_y, found_colour ); add_extended_seg( ROOT_DIST, next_x, 0, next_x, next_y, found_colour ); step++; } else { //add_pt( prev_x, prev_y, prev_colour ); //add_extended_seg( ROOT_DIST, prev_x, 0, prev_x, prev_y, prev_colour ); //add_pt( curr_x, curr_y, curr_colour ); //add_extended_seg( ROOT_DIST, curr_x, 0, curr_x, curr_y, curr_colour ); add_pt( next_x, next_y, next_colour ); add_extended_seg( ROOT_DIST, next_x, next_y, next_x, 0, next_colour ); } step++; break; case 4: prev_x = curr_x; prev_y = curr_y; curr_x = next_x; curr_y = next_y; reset_graph(); add_pt( prev_x, prev_y, prev_colour ); add_extended_seg( ROOT_DIST, prev_x, 0, prev_x, prev_y, prev_colour ); add_pt( curr_x, curr_y, curr_colour ); add_extended_seg( ROOT_DIST, curr_x, 0, curr_x, curr_y, curr_colour ); step = 2; break; case 5: step = 0; reset_graph(); break; } } // ---------------------------------------------------------------- fin de Raíces // ---------------------------------------------------------------- fin de Raíces // ---------------------------------------------------------------- fin de Raíces public Roots() { int i; setForeground(Color.black); origin.x = 0; origin.y = 0; drawingArea = new DrawingArea(this); //HiddenIm = createImage( getSize().width, getSize().height ); // slate = HiddenIm.getGraphics(); setup_panels(); pts = new ColouredPoint[MAX_NUM_POINTS]; for (i=0; i= MAX_NUM_POINTS) System.out.println( "Demasiados puntos agregados a la representación" ); else { pts[num_pts].x = x; pts[num_pts].y = y; pts[num_pts].colour = colour; num_pts++; } } public void add_seg( double x0, double y0, double x1, double y1, Color colour ) { if (num_segs >= MAX_NUM_SEGS) System.out.println( "Demasiados segmentos agregados a la representación" ); else { segs[num_segs].x0 = x0; segs[num_segs].y0 = y0; segs[num_segs].x1 = x1; segs[num_segs].y1 = y1; segs[num_segs].colour = colour; num_segs++; } } public void add_extended_seg( int dist, double x0, double y0, double x1, double y1, Color colour ) { double incx, incy, len; len = Math.sqrt( (y1-y0)*(y1-y0) + (x1-x0)*(x1-x0) ); incx = (x1-x0) / len * POINT_RADIUS * dist / pix_per_unit; incy = (y1-y0) / len * POINT_RADIUS * dist / pix_per_unit; add_seg( x0-incx, y0-incy, x1+incx, y1+incy, colour ); } private void setup_panels() { setSize(500,500); JPanel panel = new JPanel(); // elección del método method_choice = new JComboBox(); for (num_methods = 0; methods[ num_methods ].length() != 0; num_methods++) method_choice.addItem( methods[ num_methods ] ); method_choice.setSelectedItem( "Método de Newton" ); // elección de la función function_choice = new JComboBox(); for (num_functions = 0; functions[ num_functions ].length() != 0; num_functions++) function_choice.addItem( functions[ num_functions ] ); function_choice.setSelectedItem( "x^2/10" ); // barra de desplazamiento zoom_bar = new JScrollBar( JScrollBar.VERTICAL ); zoom_bar.setValues( (int)INITIAL_SCALE, 5, 1, 2 * (int) INITIAL_SCALE ); // restablecimiento reset_button = new JButton( "Restablecer" ); // Definición del subpanel Sur panel.setLayout( new FlowLayout( FlowLayout.LEFT, 25, 0 ) ); panel.add( method_choice ); panel.add( function_choice ); panel.add( reset_button ); // Se agrega al panel principal getContentPane().setLayout( new BorderLayout() ); getContentPane().add( "North", panel ); getContentPane().add( "East", zoom_bar ); getContentPane().add( "Center", drawingArea ); } ///////////////////////////////////////////////////////// // Gestión de sucesos ///////////////////////////////////////////////////////// public void actionPerformed(ActionEvent e) { if (e.getSource() == function_choice) { drawingArea.repaint(); } else if (e.getSource() == method_choice) { reset_graph(); step = 0; alg = method_choice.getSelectedIndex(); drawingArea.repaint(); } else if (e.getSource() == reset_button) { origin.x = 0; origin.y = 0; Scale = INITIAL_SCALE * SCALE_FACTOR; zoom_bar.setValue( (int) INITIAL_SCALE ); step = 0; drawingArea.repaint(); } else super.processEvent(e); } // Permite que el usuario vea la representación al presionar Intro protected void processKeyEvent(KeyEvent e) { } // Permite que el usuario arrastre el sistema de coordenadas con el ratón public void mouseExited(MouseEvent e) { } public void mousePressed(MouseEvent e) { dragFrom.x = e.getX(); dragFrom.y = e.getY(); MouseUp = 0; } public void mouseReleased(MouseEvent e) { int x = e.getX(); int y = e.getY(); if (x == dragFrom.x && y == dragFrom.y) { // sin movimiento en la dirección del ratón mouse_click( x, y ); } else { // el ratón se ha arrastrado origin.x += dragTo.x - dragFrom.x; origin.y += dragTo.y - dragFrom.y; } dragFrom.x = dragTo.x; dragFrom.y = dragTo.y; MouseUp = 1; drawingArea.repaint(); } public void mouseClicked(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseDragged(MouseEvent e) { dragTo.x = e.getX(); dragTo.y = e.getY(); drawingArea.repaint(); } public void mouseMoved(java.awt.event.MouseEvent mouseEvent) { } public double world_x( int scr_x ) { return (double) (scr_x - mid_x) / pix_per_unit; } public double world_y( int scr_y ) { return (double) (mid_y - scr_y) / pix_per_unit; } public int screen_x( double world_x ) { return (int) mid_x + (int) (world_x * pix_per_unit); } public int screen_y( double world_y ) { return (int) mid_y - (int) (world_y * pix_per_unit); } public void adjustmentValueChanged(java.awt.event.AdjustmentEvent adjustmentEvent) { double newScale; newScale = zoom_bar.getValue() * SCALE_FACTOR; origin.x = (double) origin.x * Scale / newScale; // mantiene la misma posición centrada origin.y = (double) origin.y * Scale / newScale; // en la ventana Scale = newScale; drawingArea.repaint(); } } class DrawingArea extends JPanel { Roots roots; public DrawingArea(Roots r){ roots = r; } public void paintComponent(Graphics g){ DrawCoordSystem(g); Draw(g); } private void DrawCoordSystem(Graphics g) { int i, x, y; roots.mid_x = getSize().width * 0.5 + roots.origin.x + roots.dragTo.x - roots.dragFrom.x; roots.mid_y = getSize().height * 0.5 + roots.origin.y + roots.dragTo.y - roots.dragFrom.y; roots.pix_per_unit = getSize().width / roots.Scale; // Primero, borra la g g.setColor( getBackground() ); g.fillRect( 0, 0, getSize().width, getSize().height ); // Dibuja los ejes x e y g.setColor( Color.black ); g.drawLine( 0, (int)(roots.mid_y), getSize().width, (int)(roots.mid_y) ); // eje x g.drawLine( (int)(roots.mid_x), 0, (int)(roots.mid_x), getSize().height ); // eje y // Dibuja las marcas for (i=1; i<100; i++) { x = screen_x(i); if (x >= 0 && x < getSize().width) g.drawLine( x, (int)(roots.mid_y) + 2, x, (int)(roots.mid_y) - 2); x = screen_x(-i); if (x >= 0 && x < getSize().width) g.drawLine( x, (int)(roots.mid_y) + 2, x, (int)(roots.mid_y) - 2); y = screen_y(i); if (y >= 0 && x < getSize().height) g.drawLine( (int)(roots.mid_x) - 2, y, (int)(roots.mid_x) + 2, y ); y = screen_y(-i); if (y >= 0 && x < getSize().height) g.drawLine( (int)(roots.mid_x) - 2, y, (int)(roots.mid_x) + 2, y ); } } /* Lo dibuja todo: * * - la función f() * - cualquier punto guardado * - cualquier línea guardada */ private void Draw(Graphics g) { double y, x; int Y, OLD_Y; int i; roots.mid_x = getSize().width * 0.5 + roots.origin.x + roots.dragTo.x - roots.dragFrom.x; roots.mid_y = getSize().height * 0.5 + roots.origin.y + roots.dragTo.y - roots.dragFrom.y; roots.pix_per_unit = getSize().width / roots.Scale; // Dibuja la función x = world_x(0); y = roots.f(x); Y = roots.screen_y(y); g.setColor(Color.black); for (i = 1; i <= getSize().width; i++) { x = world_x(i); y = roots.f(x); OLD_Y = Y; Y = screen_y(y); g.drawLine(i - 1, OLD_Y, i, Y); } // Dibuja los segmentos for (i=0; i