/* class ComplexViewer (Applet) Authors: Keith Orpen, Math department, University of British Columbia. Porting, interface, and extensions by Djun Kim, Mathematics UBC. Revisions: November, 1995 - First version - Keith Orpen December, 1995 - Ported to Beta API, cosine and Exp functions added, user interface improved. - Djun Kim March, 1996 - Added Double buffering, cleaned up code. - Djun Kim Copyright (c)1995 Keith Orpen. Extensions copyright (c)1995 Djun Kim. The authors reserve all rights. December 1999 - Corrected sin function. Added polar coordinates. Added rect/polar option. Added adjustable grid sizing. Added adjustable scaling. - Kent Pearce (Texas Tech University) May 2000 - Changed function selection controls to a Choice box allow expanded function selections - Kent Pearce */ import java.awt.*; import java.awt.event.*; import java.applet.*; public class ComplexViewer extends Applet { public Image hiddenimagebuffer; public Graphics hiddengraphics; public void init() { setLayout(new BorderLayout()); ViewPanel vp = new ViewPanel(this); add("Center", vp); add("North", new ControlsTop(vp)); add("South", new ControlsBase(vp)); hiddenimagebuffer = createImage(this.size().width, this.size().height); hiddengraphics = hiddenimagebuffer.getGraphics(); repaint(); } public boolean handleEvent(Event e) { switch (e.id) { case Event.WINDOW_DESTROY: System.exit(0); return true; default: return false; } } public static void main(String args[]) { Frame f = new Frame("ComplexViewer"); ComplexViewer view = new ComplexViewer(); view.init(); view.start(); f.add("Center", view); f.show(); } } // // ***************************************************************************** // class ViewPanel extends Panel { ComplexViewer parent; public static final int Identity = 0; public static final int Square = 1; public static final int Cube = 2; public static final int Cardiod = 3; public static final int Sin = 4; public static final int Cos = 5; public static final int Exp = 6; public static final int Invert = 7; public static final int Poisson = 8; public static final int Convex = 9; public static final int Koebe = 10; public static final int Airfoil = 11; private int functionG = Sin; private int functionF = Identity; public static final int Rect = 0; public static final int Polar = 1; private int coordtype = Rect; public static final int Small = 0; public static final int Large = 2; public int scalevalue; public static final int Smaller = 0; public static final int Larger = 2; public int grid; public static final int Medium = 1; private float scale = 0.015f; private int gridspace = 4; private int gridlines = 3; /* lines on either side of the center */ private int gridsize = 2 * gridlines + 1; private int gridcircles = 3; /* circles around the center */ private int gridrays = (int) 2 * (Math.round((gridcircles + 1)/2 + 1)); private float x1 = 1.0f + gridlines*gridspace; /* coords of domain grid */ private float y1 = 1.0f + gridlines*gridspace; /* coords of domain grid */ ViewPanel(ComplexViewer target) { this.parent = target; } public void init() { resize(500, 450); } public void start() { } public void stop() { } public void destroy() { } public void setfunctionG(int functionG) { switch (functionG) { case Identity: case Square: case Cube: case Cardiod: case Sin: case Cos: case Exp: case Invert: case Poisson: case Convex: case Koebe: case Airfoil: this.functionG = functionG; repaint(); break; default: throw new IllegalArgumentException(); } } public void setfunctionF(int functionF) { switch (functionF) { case Identity: case Square: case Cube: case Exp: case Invert: this.functionF = functionF; repaint(); break; default: throw new IllegalArgumentException(); } } public void setCoordType(int coordtype) { switch (coordtype) { case Rect: case Polar: this.coordtype = coordtype; repaint(); break; default: throw new IllegalArgumentException(); } } public void setScaleValue(int scalevalue) { float gtemp; float sf = 0.75f; int gtemp2; switch (scalevalue) { case Small: scale = sf*scale; x1 = (x1/sf + (1.0f-1.0f/sf)*size().width/2); y1 = (y1/sf + (1.0f-1.0f/sf)*size().height/2); gridspace = (int) Math.round(gridspace/sf); this.scalevalue = Medium; repaint(); break; case Large: scale = scale/sf; x1 = (x1*sf + (1.0f-1.0f*sf)*size().width/2); y1 = (y1*sf + (1.0f-1.0f*sf)*size().height/2); gridspace = (int) Math.round(gridspace*sf); this.scalevalue = Medium; repaint(); break; default: throw new IllegalArgumentException(); } } public void setGridSize(int grid) { switch (grid) { case Smaller: gridlines = gridlines - 1; if (gridlines==0) { gridlines = 1; } gridcircles = gridcircles - 1; if (gridcircles==0) { gridcircles = 1; } gridsize = 2 * gridlines + 1; gridrays = (int) 2 * (Math.round((gridcircles + 1)/2 + 1)); this.grid = Medium; repaint(); break; case Larger: gridlines = gridlines + 1; gridcircles = gridcircles + 1; gridsize = 2 * gridlines + 1; gridrays = (int) 2 * (Math.round((gridcircles + 1)/2 + 1)); this.grid = Medium; repaint(); break; default: throw new IllegalArgumentException(); } } /* REAL part of the complex function G */ private double g_re( double re, double im ) { double gw, denom; double huge = 200.0d; switch (functionG) { case Identity: return re; case Square: gw = re*re - im*im; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Cube: gw = re*re*re - 3.0d*re*im*im; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Cardiod: gw = re + (re*re - im*im)/2.0d; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Sin: gw = Math.sin( re ) * (Math.exp( im ) + Math.exp( -im ) )/2.0d; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Cos: gw = Math.cos( re ) * (Math.exp( im ) + Math.exp( -im ) )/2.0d; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Exp: gw = Math.exp( re ) * Math.cos( im ); if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Invert: denom = re*re + im*im; gw = re/denom; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Poisson: denom = 1.0d - 2.0d*re + re*re + im*im; gw = (1 - (re*re + im*im))/denom; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Convex: denom = (1.0d - re)*(1.0d - re) + im*im; gw = (re - re*re - im*im)/denom; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Koebe: denom = (1.0d - re)*(1.0d - re) + im*im; gw = (re + (-2.0d +re)*(re*re + im*im))/(denom*denom); if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Airfoil: denom = re*re + im*im; gw = re + re/denom; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; default: throw new IllegalArgumentException(); } } /* IMAGINARY part of the complex function G */ private double g_im( double re, double im ) { double gw, denom; double huge = 200.0d; switch (functionG) { case Identity: return im; case Square: gw = 2.0d*re*im; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Cube: gw = 3.0d*re*re*im - im*im*im; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Cardiod: gw = im + re*im; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Sin: gw = Math.cos( re ) * (Math.exp( im ) - Math.exp( -im ) )/2.0d; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Cos: gw = Math.sin( re ) * -(Math.exp( im ) - Math.exp( -im ) )/2.0d; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Exp: gw = (Math.exp( re ) * (Math.sin( im ))); if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Invert: denom = re*re + im*im; gw = -im/denom; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Poisson: denom = 1.0d - 2.0d*re + re*re + im*im; gw = (2.0d*im)/denom; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Convex: denom = (1.0d - re)*(1.0d- re) + im*im; gw = im/denom; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Koebe: denom = (1.0d - re)*(1.0d - re) + im*im; gw = (im - (im)*(re*re + im*im))/(denom*denom); if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; case Airfoil: denom = re*re + im*im; gw = im - im/denom; if (Math.abs(gw) > huge) { gw = gw*huge/Math.abs(gw); } return gw; default: throw new IllegalArgumentException(); } } /* REAL part of the complex function F */ private double f_re( double re, double im ) { double fz, denom; double huge = 200.0d; switch (functionF) { case Identity: return re; case Square: fz = re*re - im*im; return fz; case Cube: fz = re*re*re - 3.0d*re*im*im; return fz; case Cardiod: fz = re + (re*re - im*im)/2.0d; return fz; case Sin: fz = Math.sin( re ) * (Math.exp( im ) + Math.exp( -im ) )/2.0d; return fz; case Cos: fz = Math.cos( re ) * (Math.exp( im ) + Math.exp( -im ) )/2.0d; return fz; case Exp: fz = Math.exp( re ) * Math.cos( im ); if (Math.abs(fz) > huge) { fz = fz*huge/Math.abs(fz); } return fz; case Invert: denom = re*re + im*im; fz = re/denom; return fz; case Poisson: denom = 1.0d - 2.0d*re + re*re + im*im; fz = (1 - (re*re + im*im))/denom; case Convex: denom = (1.0d - re)*(1.0d - re) + im*im; fz = (re - re*re - im*im)/denom; return fz; case Koebe: denom = (1.0d - re)*(1.0d - re) + im*im; fz = (re + (-2.0d +re)*(re*re + im*im))/(denom*denom); if (Math.abs(fz) > huge) { fz = fz*huge/Math.abs(fz); } return fz; case Airfoil: denom = re*re + im*im; fz = re + re/denom; return fz; default: throw new IllegalArgumentException(); } } /* IMAGINARY part of the complex function F */ private double f_im( double re, double im ) { double fz, denom; double huge = 200.0d; switch (functionF) { case Identity: return im; case Square: fz = 2.0d*re*im; return fz; case Cube: fz = 3.0d*re*re*im - im*im*im; return fz; case Cardiod: fz = im + re*im; return fz; case Sin: fz = Math.cos( re ) * (Math.exp( im ) - Math.exp( -im ) )/2.0d; return fz; case Cos: fz = Math.sin( re ) * -(Math.exp( im ) - Math.exp( -im ) )/2.0d; return fz; case Exp: fz = (Math.exp( re ) * (Math.sin( im ))); if (Math.abs(fz) > huge) { fz = fz*huge/Math.abs(fz); } return fz; case Invert: denom = re*re + im*im; fz = -im/denom; return fz; case Poisson: denom = 1.0d - 2.0d*re + re*re + im*im; fz = (2.0d*im)/denom; return fz; case Convex: denom = (1.0d - re)*(1.0d- re) + im*im; fz = im/denom; return fz; case Koebe: denom = (1.0d - re)*(1.0d - re) + im*im; fz = (im - (im)*(re*re + im*im))/(denom*denom); if (Math.abs(fz) > huge) { fz = fz*huge/Math.abs(fz); } return fz; case Airfoil: denom = re*re + im*im; fz = im - im/denom; return fz; default: throw new IllegalArgumentException(); } } private long pointValue_x( float x, float y ) { return Math.round( g_re ( f_re( (x-size().width/2)*scale, (y-size().height/2)*scale ), f_im( (x-size().width/2)*scale, (y-size().height/2)*scale ) ) /scale+size().width/2); } private long pointValue_y( float x, float y ) { return Math.round( g_im ( f_re( (x-size().width/2)*scale, (y-size().height/2)*scale ), f_im( (x-size().width/2)*scale, (y-size().height/2)*scale ) ) /scale+size().height/2); } public void paint(Graphics g) { int x, y, r; int min_x, max_x, min_y, max_y; double pi = 3.141592653589793; int x2[][] = new int[gridsize][gridsize]; int y2[][] = new int[gridsize][gridsize]; int x3[][] = new int[gridcircles+1][gridcircles*gridrays]; int y3[][] = new int[gridcircles+1][gridcircles*gridrays]; int unitlen = Math.round(1/scale); int max_r = gridspace * gridcircles; int x1i, y1i; x1i = (int) Math.round(x1); y1i = (int) Math.round(y1); min_x = (x1i - gridspace * gridlines); max_x = (x1i + gridspace * gridlines); min_y = (y1i - gridspace * gridlines); max_y = (y1i + gridspace * gridlines); parent.hiddengraphics.setColor(Color.white); parent.hiddengraphics.fillRect(0,0,size().width,size().height); parent.hiddengraphics.setColor(Color.black); parent.hiddengraphics.drawRect(0,0,size().width-1,size().height-1); parent.hiddengraphics.drawArc(size().width/2-unitlen, size().height/2-unitlen, unitlen*2, unitlen*2, 0, 360); parent.hiddengraphics.drawLine(size().width/2, 0, size().width/2, size().height-1); parent.hiddengraphics.drawLine(0, size().height/2, size().width-1, size().height/2); if (coordtype==Rect) { // Draw the source grid parent.hiddengraphics.setColor(Color.red); for (int i = -gridlines; i <= gridlines; i++) { x = (x1i + i * gridspace); parent.hiddengraphics.drawLine( x, min_y, x, max_y ); } for (int i = -gridlines; i <= gridlines; i++) { y = (y1i + i * gridspace); parent.hiddengraphics.drawLine( min_x, y, max_x, y ); } // Draw the target (mapped) grid for (int i = 0; i < gridsize; i++) { x = (x1i + (i - gridlines) * gridspace); for (int j = 0; j < gridsize; j++) { y = (y1i + (j - gridlines) * gridspace); x2[i][j] = (int) pointValue_x( x, y ); y2[i][j] = (int) pointValue_y( x, y ); } } parent.hiddengraphics.setColor(Color.blue); for (int i = 0; i < gridsize; i++) { for (int j = 1; j < gridsize; j++) { parent.hiddengraphics.drawLine( x2[i][j-1], y2[i][j-1], x2[i][j], y2[i][j] ); parent.hiddengraphics.drawLine( x2[j-1][i], y2[j-1][i], x2[j][i], y2[j][i] ); } } } else { // Draw the source grid parent.hiddengraphics.setColor(Color.red); for (int i = 0; i <= gridrays; i++) { x = (int) Math.round(x1 + max_r*Math.cos(2*pi*i/gridrays)); y = (int) Math.round(y1 + max_r*Math.sin(2*pi*i/gridrays)); parent.hiddengraphics.drawLine( x1i, y1i, x, y ); } for (int i = 1; i <= gridcircles; i++) { r = i * gridspace; parent.hiddengraphics.drawArc(x1i - r, y1i - r, 2*r, 2*r, 0, 360); } // Calculate the target (mapped) grid x3[0][0] = (int) pointValue_x( x1, y1 ); y3[0][0] = (int) pointValue_y( x1, y1 ); for (int j = 1; j < gridrays; j++) { x3[0][j] = x3[0][0]; y3[0][j] = y3[0][0]; } for (int i = 1; i <= gridcircles; i++) { r = i * gridspace; for (int j = 0; j < i*gridrays; j++) { x = (int) Math.round(x1 + r*Math.cos(2*pi*j/(i*gridrays))); y = (int) Math.round(y1 + r*Math.sin(2*pi*j/(i*gridrays))); x3[i][j] = (int) pointValue_x( x, y ); y3[i][j] = (int) pointValue_y( x, y ); } } // Draw the target (mapped) grid parent.hiddengraphics.setColor(Color.blue); for (int j = 0; j < gridrays; j++) { for (int i = 0; i < gridcircles; i++) { parent.hiddengraphics.drawLine( x3[i][i*j], y3[i][i*j], x3[i+1][(i+1)*j], y3[i+1][(i+1)*j] ); } } for (int i = 1; i <= gridcircles; i++) { for (int j = 0; j < i*gridrays-1; j++) { parent.hiddengraphics.drawLine( x3[i][j], y3[i][j], x3[i][j+1], y3[i][j+1] ); } parent.hiddengraphics.drawLine( x3[i][i*gridrays-1], y3[i][i*gridrays-1], x3[i][0], y3[i][0] ); } } g.drawImage(parent.hiddenimagebuffer, 0,0, this); } public void update(Graphics g) { paint(g); } public boolean mouseDown(java.awt.Event evt, int x, int y) { if (inside(x,y)) { x1 = (float) x; y1 = (float) y; repaint(); } return true; } public boolean mouseDrag(java.awt.Event evt, int x, int y) { if (inside(x,y)) { x1 = (float) x; y1 = (float) y; repaint(); } return true; } } // // ***************************************************************************** // class ControlsTop extends Panel { ViewPanel target; String st_Identity_label = "Identity"; String st_Square_label = "Square"; String st_Cube_label = "Cube"; String st_Cardiod_label = "Cardiod"; String st_Sin_label = "Sin"; String st_Cos_label = "Cos"; String st_Exp_label = "Exp"; String st_Invert_label = "Invert"; String st_Poisson_label = "Poisson"; String st_Convex_label = "Convex"; String st_Koebe_label = "Koebe"; String st_Airfoil_label = "Airfoil"; Checkbox cb_rect; Checkbox cb_polar; String st_rect_label = "Rect"; String st_polar_label = "Polar"; public ControlsTop (ViewPanel target) { this.target = target; setLayout(new FlowLayout(FlowLayout.CENTER)); setBackground(Color.lightGray); add(new Label("g(w) =", Label.RIGHT)); Choice gw = new Choice(); gw.addItem("Identity"); gw.addItem("Square"); gw.addItem("Cube"); gw.addItem("Cardiod"); gw.addItem("Sin"); gw.addItem("Cos"); gw.addItem("Exp"); gw.addItem("Invert"); gw.addItem("Poisson"); gw.addItem("Convex"); gw.addItem("Koebe"); gw.addItem("Airfoil"); gw.addItemListener(itemgw); gw.select(4); add(gw); add(new Label("f(z) =", Label.RIGHT)); Choice fz = new Choice(); fz.addItem("Identity"); fz.addItem("Square"); fz.addItem("Cube"); fz.addItem("Exp"); fz.addItem("Invert"); fz.addItemListener(itemfz); fz.select(0); add(fz); add(new Label("Grid Type:", Label.RIGHT)); CheckboxGroup coordtype_group = new CheckboxGroup(); add(cb_rect = new Checkbox(st_rect_label, coordtype_group, true)); add(cb_polar = new Checkbox(st_polar_label, coordtype_group, false)); } ItemListener itemgw = new ItemListener () { public void itemStateChanged(ItemEvent itemgw) { if (itemgw.getItem().equals(st_Identity_label)) { target.setfunctionG(ViewPanel.Identity); } else if (itemgw.getItem().equals(st_Square_label)) { target.setfunctionG(ViewPanel.Square); } else if (itemgw.getItem().equals(st_Cube_label)) { target.setfunctionG(ViewPanel.Cube); } else if (itemgw.getItem().equals(st_Cardiod_label)) { target.setfunctionG(ViewPanel.Cardiod); } else if (itemgw.getItem().equals(st_Sin_label)) { target.setfunctionG(ViewPanel.Sin); } else if (itemgw.getItem().equals(st_Cos_label)) { target.setfunctionG(ViewPanel.Cos); } else if (itemgw.getItem().equals(st_Exp_label)) { target.setfunctionG(ViewPanel.Exp); } else if (itemgw.getItem().equals(st_Invert_label)) { target.setfunctionG(ViewPanel.Invert); } else if (itemgw.getItem().equals(st_Poisson_label)) { target.setfunctionG(ViewPanel.Poisson); } else if (itemgw.getItem().equals(st_Convex_label)) { target.setfunctionG(ViewPanel.Convex); } else if (itemgw.getItem().equals(st_Koebe_label)) { target.setfunctionG(ViewPanel.Koebe); } else if (itemgw.getItem().equals(st_Airfoil_label)) { target.setfunctionG(ViewPanel.Airfoil); } } }; ItemListener itemfz = new ItemListener () { public void itemStateChanged(ItemEvent itemfz) { if (itemfz.getItem().equals(st_Identity_label)) { target.setfunctionF(ViewPanel.Identity); } else if (itemfz.getItem().equals(st_Square_label)) { target.setfunctionF(ViewPanel.Square); } else if (itemfz.getItem().equals(st_Cube_label)) { target.setfunctionF(ViewPanel.Cube); } else if (itemfz.getItem().equals(st_Exp_label)) { target.setfunctionF(ViewPanel.Exp); } else if (itemfz.getItem().equals(st_Invert_label)) { target.setfunctionF(ViewPanel.Invert); } } }; public boolean action(Event e, Object arg) { if (e.target instanceof Checkbox) { String cbox = ((Checkbox)(e.target)).getLabel(); if (cbox.equals(st_rect_label)) { target.setCoordType(ViewPanel.Rect); } else if (cbox.equals(st_polar_label)) { target.setCoordType(ViewPanel.Polar); } } return true; } } // // ***************************************************************************** // class ControlsBase extends Panel { ViewPanel target; public ControlsBase (ViewPanel target) { this.target = target; setLayout(new FlowLayout(FlowLayout.CENTER)); setBackground(Color.lightGray); add(new Label("Grid Size:", Label.RIGHT)); Button gs1 = new Button("-"); add(gs1); gs1.addActionListener(gsless); Button gs2 = new Button("+"); add(gs2); gs2.addActionListener(gsmore); add(new Label(" Zoom:", Label.RIGHT)); Button sg1 = new Button("+"); add(sg1); sg1.addActionListener(sgin); Button sg2 = new Button("-"); add(sg2); sg2.addActionListener(sgout); add(new Label(" ", Label.RIGHT)); } ActionListener gsless = new ActionListener () { public void actionPerformed(ActionEvent gsless) { target.setGridSize(ViewPanel.Smaller); } }; ActionListener gsmore = new ActionListener () { public void actionPerformed(ActionEvent gsmore) { target.setGridSize(ViewPanel.Larger); } }; ActionListener sgin = new ActionListener () { public void actionPerformed(ActionEvent sgin) { target.setScaleValue(ViewPanel.Small); } }; ActionListener sgout = new ActionListener () { public void actionPerformed(ActionEvent sgout) { target.setScaleValue(ViewPanel.Large); } }; }