// Copyright Sky Coyote 1996 // Use and distribute as you like // Import packages import java.awt.Graphics; import java.awt.Color; import java.awt.Event; import java.awt.Image; import java.awt.Font; import java.awt.FontMetrics; // applet class public class surf2 extends java.applet.Applet { int dh, dv; Image buf; Graphics gbuf; // vars for 3d -> 2d transform double xform[] = new double[11]; int hv[] = new int[2]; double alt = 30, az = 20, mag = 50; double mag0 = 50; int mx0 = 0, my0 = 0; Font f = new Font("TimesRoman", Font.PLAIN, 10); // arrays for surface coords // 3d double surfx[] = new double[10 * 10]; double surfy[] = new double[10 * 10]; double surfz[] = new double[10 * 10]; // 2d int surfh[] = new int[10 * 10]; int surfv[] = new int[10 * 10]; // slider params double fx = 1, fy = 1, amp = 1; // slider data type /* slider[0] = min slider[1] = max slider[2] = val slider[3] = h slider[4] = v slider[5] = hit slider[6] = dv0 slider[7] = name index */ // slider vars String snames[] = new String[3]; double slider1[] = new double[8]; double slider2[] = new double[8]; double slider3[] = new double[8]; public void init() { // create offscreen drawing dh = this.size().width; dv = this.size().height; buf = createImage(dh, dv); gbuf = buf.getGraphics(); // set up initial 3d coordinates, surface, and 2d representation reset_coords(0.0, 0.0, 0.0, alt, az, mag, xform); calc_surf(); project_surf(); // initialize sliders (not currently a separate class) snames[0] = "Amp"; snames[1] = "Fx"; snames[2] = "Fy"; slider1[0] = -2; slider1[1] = 2; slider1[2] = amp; slider1[3] = dh - 15; slider1[4] = 19; slider1[5] = 0; slider1[6] = 0; slider1[7] = 0; slider2[0] = -2; slider2[1] = 2; slider2[2] = fx; slider2[3] = dh - 15; slider2[4] = 129; slider2[5] = 0; slider2[6] = 0; slider2[7] = 1; slider3[0] = -2; slider3[1] = 2; slider3[2] = fy; slider3[3] = dh - 15; slider3[4] = 239; slider3[5] = 0; slider3[6] = 0; slider3[7] = 2; } // calc 3d surface values from slider params public void calc_surf() { for (int i = 0; i < 10; i ++) for (int j = 0; j < 10; j ++) { int k = i + j * 10; surfx[k] = (i - 4.5) / 2.0; surfy[k] = (j - 4.5) / 2.0; surfz[k] = amp * Math.sin(fx * surfx[k]) * Math.cos(fy * surfy[k]); } } // handle mouse click public boolean mouseDown(Event evt, int x, int y) { // save current mouse pos and magnification mx0 = x; my0 = y; mag0 = mag; // check for click in sliders slider_hit(slider1, x, y); slider_hit(slider2, x, y); slider_hit(slider3, x, y); return true; } // handle dragging (rotate or zoom, or sliders) // note that surface computation is synchronized with drawing public synchronized boolean mouseDrag(Event evt, int x, int y) { // track mouse in slider and update surface if (track_slider(slider1, x, y)) { amp = slider1[2]; calc_surf(); project_surf(); } else if (track_slider(slider2, x, y)) { fx = slider2[2]; calc_surf(); project_surf(); } else if (track_slider(slider3, x, y)) { fy = slider3[2]; calc_surf(); project_surf(); } else { // zooming if (evt.shiftDown()) { // get ratio of current pos wrt clicked pos double dx = mx0 - dh / 2; double dy = my0 - dv / 2; double r1 = Math.sqrt(dx * dx + dy * dy); dx = x - dh / 2; dy = y - dv / 2; double r2 = Math.sqrt(dx * dx + dy * dy); // reset magnification mag = r2 / r1 * mag0; } // rotation else { // change altitude & azimuth alt += y - my0; az += x - mx0; my0 = y; mx0 = x; } // update coords, surface, and redraw reset_coords(0.0, 0.0, 0.0, alt, az, mag, xform); project_surf(); } repaint(); return true; } // transform 3d surface to 2d public void project_surf() { for (int i = 0; i < 10; i ++) for (int j = 0; j < 10; j ++) { int k = i + j * 10; xform_3to2d(surfx[k], surfy[k], surfz[k], xform, hv); surfh[k] = hv[0]; surfv[k] = hv[1]; } } // draw axes and strings public void draw_frame(Graphics g) { g.setColor(Color.black); g.fillRect(0, 0, dh, dv); // transform 3d axis positions to 2d xform_3to2d(0.0, 0.0, 0.0, xform, hv); int h0 = hv[0]; int v0 = hv[1]; xform_3to2d(1.0, 0.0, 0.0, xform, hv); int h1 = hv[0]; int v1 = hv[1]; xform_3to2d(0.0, 1.0, 0.0, xform, hv); int h2 = hv[0]; int v2 = hv[1]; xform_3to2d(0.0, 0.0, 1.0, xform, hv); int h3 = hv[0]; int v3 = hv[1]; // draw axes g.setColor(Color.yellow); g.drawLine(h0, v0, h1, v1); g.drawLine(h0, v0, h2, v2); g.drawLine(h0, v0, h3, v3); // draw alt, az, mag strings g.setFont(f); g.drawString("Alt = " + String.valueOf(alt), 2, 12); g.drawString("Az = " + String.valueOf(az), 2, 24); g.drawString("Mag = " + String.valueOf(mag), 2, 36); // draw axis labels xform_3to2d(1.1, 0.0, 0.0, xform, hv); g.drawString("X", hv[0], hv[1]); xform_3to2d(0.0, 1.1, 0.0, xform, hv); g.drawString("Y", hv[0], hv[1]); xform_3to2d(0.0, 0.0, 1.1, xform, hv); g.drawString("Z", hv[0], hv[1]); g.drawString("Drag to rotate / Shift-drag to zoom", 2, dv - 5); FontMetrics fm = getFontMetrics(f); String s = "Z = Amp * sin(Fx * X) * cos(Fy * Y)"; int w = fm.stringWidth(s); g.drawString(s, dh - w - 2, dv - 5); } // draw surface public void draw_surf(Graphics g) { g.setColor(Color.red); // draw one set of lines for (int i = 0; i < 10; i ++) for (int j = 1; j < 10; j ++) { int k1 = i + (j - 1) * 10; int k2 = i + j * 10; g.drawLine(surfh[k1], surfv[k1], surfh[k2], surfv[k2]); } // draw other set of lines for (int j = 0; j < 10; j ++) for (int i = 1; i < 10; i ++) { int k1 = i - 1 + j * 10; int k2 = i + j * 10; g.drawLine(surfh[k1], surfv[k1], surfh[k2], surfv[k2]); } } // do drawing // note that drawing is synchronized with surface computation public synchronized void paint(Graphics g) { draw_frame(gbuf); draw_surf(gbuf); draw_slider(slider1, gbuf); draw_slider(slider2, gbuf); draw_slider(slider3, gbuf); // copy to screen g.drawImage(buf, 0, 0, this); } // overload for no flicker public void update(Graphics g) { paint(g); } // coord transform data structure /* xform[0] = x0 xform[1] = y0 xform[2] = z0 xform[3] = mag xform[4] = h0 xform[5] = v0 xform[6] = cxh xform[7] = cyh xform[8] = cxv xform[9] = cyv xform[10] = czv */ // update transform based on current alt, az, mag public void reset_coords(double x0, double y0, double z0, double alt, double az, double mag, double xform[]) { xform[0] = x0; xform[1] = y0; xform[2] = z0; xform[3] = mag; xform[4] = dh / 2.0; xform[5] = dv / 2.0; double cos_alt = Math.cos(alt * Math.PI / 180.0); double sin_alt = Math.sin(alt * Math.PI / 180.0); double cos_az = Math.cos(az * Math.PI / 180.0); double sin_az = Math.sin(az * Math.PI / 180.0); xform[6] = sin_az; xform[7] = cos_az; xform[8] = -sin_alt * cos_az; xform[9] = sin_alt * sin_az; xform[10] = cos_alt; } // transform 3d to 2d by matrix multiplication public void xform_3to2d(double x, double y, double z, double xform[], int hv[]) { x = (x - xform[0]) * xform[3]; y = (y - xform[1]) * xform[3]; z = (z - xform[2]) * xform[3]; hv[0] = (int) (xform[4] + xform[6] * x + xform[7] * y); hv[1] = (int) (xform[5] - xform[8] * x - xform[9] * y - xform[10] * z); } // draw a slider public void draw_slider(double slider[], Graphics g) { g.setColor(Color.yellow); int h1 = (int) slider[3]; int v1 = (int) slider[4]; int v2 = v1 + 60; g.drawLine(h1, v1, h1, v2); // get current thumb pos and draw int v3; if (slider[0] == slider[1]) v3 = (v1 + v2) / 2; else v3 = (int) (v2 - (v2 - v1) * (slider[2] - slider[0]) / (slider[1] - slider[0])); g.fillRect(h1 - 4, v3 - 4, 10, 10); g.setFont(f); FontMetrics fm = getFontMetrics(f); String s = String.valueOf(((int) (slider[2] * 100)) / 100.0); int w = fm.stringWidth(s); g.drawString(s, h1 - w / 2, v2 + 15); String s2 = snames[(int) slider[7]]; int w2 = fm.stringWidth(s2); g.drawString(s2, h1 - w2 / 2, v1 - 7); } // check to see if mouse click is in slider public void slider_hit(double slider[], int x, int y) { slider[5] = 0; slider[6] = 0; int h1 = (int) slider[3]; int v1 = (int) slider[4]; int v2 = v1 + 60; int v3; if (slider[0] == slider[1]) v3 = (v1 + v2) / 2; else v3 = (int) (v2 - (v2 - v1) * (slider[2] - slider[0]) / (slider[1] - slider[0])); if (x < h1 - 4) return; if (x > h1 + 4) return; if (y < v3 - 4) return; if (y > v3 + 4) return; slider[5] = 1; slider[6] = y - v3; } // track mouse dragging in slider public boolean track_slider(double slider[], int x, int y) { // see if slider is "hit" if (slider[5] != 1) return false; int h1 = (int) slider[3]; int v1 = (int) slider[4]; int v2 = v1 + 60; y -= slider[6]; if (y < v1) y = v1; if (y > v2) y = v2; // update slider value double val; if (slider[0] == slider[1]) val = slider[0]; else val = slider[0] + ((v2 - y) * (slider[1] - slider[0])) / (v2 - v1); if (val < slider[0]) val = slider[0]; if (val > slider[1]) val = slider[1]; slider[2] = val; return true; } }