/* * Copyright (c) 1996 CONNIE K. PENG All Rights Reserved. * * Permission to use, copy, modify, and distribute this software * and its documentation for NON-COMMERCIAL purposes. * */ import java.awt.*; import java.io.InputStream; import java.net.URL; public class WireFrame extends java.applet.Applet implements Runnable { VwCanvas ViewWindow; VwControl ControlPanel; ControlOption options; ViewFrame Camera; Object3D Object; Object3D originObject; Object3D newObject; Object3D Axis; Object3D newAxis; Thread runner = null; public void init() { setLayout(new BorderLayout(5, 5)); // initialize camera Camera = new ViewFrame(); ViewWindow = new VwCanvas(this); ControlPanel = new VwControl(this); add ("West", ControlPanel); add ("East", ViewWindow); // set initial options options = new ControlOption(true, 0, 1, 0, 0); // get the world axis object InputStream is = null; try { is = new URL(getDocumentBase(), "model/axis").openStream(); Axis = new Object3D(is); } catch (Exception e) {} // projection transform the axis Matrix m = new Matrix(); newAxis = Axis.Move_Transform(m, Camera); // initialize the object model init_Model(0); } public void init_Model(int selection) { String mdName = null; InputStream is = null; // get different model for object according to selection switch (selection) { case 0: mdName = new String("model/cube"); break; case 1: mdName = new String("model/pyramid"); break; case 2: mdName = new String("model/cylinder"); break; } try { is = new URL(getDocumentBase(), mdName).openStream(); Object = new Object3D(is); } catch (Exception e) {} // perspective transform the object originObject = new Object3D(Object); Matrix m = new Matrix(); m.translate(0, 0, 150); newObject = Object.Move_Transform(m, Camera); } public Insets insets() { return new Insets(10, 10, 10, 10); } public void start() { options.animation = true; if (runner == null) { runner = new Thread(this); runner.start(); } } public void stop() { options.animation = false; if (runner != null) { runner.stop(); runner = null; } } // function for object animation public void run() { Thread.currentThread().setPriority(Thread.MIN_PRIORITY); while (runner != null) { Matrix m = new Matrix(); // rotate the object along rotating axis given in the control options switch (options.rotate_index) { case 0: m.rotatex(1); break; case 1: m.rotatey(1); break; case 2: m.rotatez(1); break; case 3: m.rotatex(-1); break; case 4: m.rotatey(-1); break; case 5: m.rotatez(-1); break; } // spin the object according to the spinning axis given in control options Vector axis = new Vector(); switch (options.spin_index) { case 0: axis.set_element(1, 0, 0); axis.make_vector(); break; case 1: axis.set_element(0, 1, 0); axis.make_vector(); break; case 2: axis.set_element(0, 0, 1); axis.make_vector(); break; case 3: axis.set_element(-1, 0, 0); axis.make_vector(); break; case 4: axis.set_element(0, -1, 0); axis.make_vector(); break; case 5: axis.set_element(0, 0, -1); axis.make_vector(); break; } m.rotate(Object.center, axis, 3); // projecting transform object and repaint the view window newObject = Object.Move_Transform(m, Camera); ViewWindow.repaint(); try {Thread.sleep(50);} catch (InterruptedException e) { } } } // function for manully moving(rotation, spin and translate) public void move(int x, int y, int z, boolean flag) { Matrix m = new Matrix(); // move object, object can be manully moved only when animation is not // running if (options.object_index == 0 && options.animation == false) { switch (options.method_index) { // translating case 0: if (flag == true) // Action from mouse drag { Vector direction = new Vector(x, y, z); direction.make_vector(); // transform mouse direction from screen coordinator to world // coordinator direction.multiply(Camera.S2F_Matrix); direction.multiply(Camera.F2W_Matrix); m.translate(direction); } else // Action from button m.translate((float)5*x, (float)5*y, (float)5*z); break; // rotation case 1: if (x == 1) m.rotatex(10); if (y == 1) m.rotatey(10); if (z == 1) m.rotatez(10); if (x == -1) m.rotatex(-10); if (y == -1) m.rotatey(-10); if (z == -1) m.rotatez(-10); break; // spinning case 2: Vector axis = new Vector(x, y, z); axis.make_vector(); if (flag == true) // Action from mouse drag { axis.orthogonal(); // convert the screen vector to viewframe vector; axis.multiply(Camera.S2F_Matrix); axis.multiply(Camera.F2W_Matrix); } m.rotate(Object.center, axis, 10); break; } // transform the object and repaint the view window newObject = Object.Move_Transform(m, Camera); ViewWindow.repaint(); } // Move camera else { switch (options.method_index) { // translate case 0: Vector distance = new Vector(5*x, 5*y, 5*z); distance.make_vector(); Camera.Translate(distance); break; // rotate case 1: if (x == 1) Camera.RotateX(10); if (y == 1) Camera.RotateY(10); if (z == 1) Camera.RotateZ(10); if (x == -1) Camera.RotateX(-10); if (y == -1) Camera.RotateY(-10); if (z == -1) Camera.RotateZ(-10); break; } // transform the axis newAxis = Axis.Move_Transform(m, Camera); // transform the object and repaint the view window // if the animation is running, this step is not necessary since // animation will do object transformation and repaint automatically if (options.animation == false) { newObject = Object.Move_Transform(m, Camera); ViewWindow.repaint(); } } } public void resetCamera() { Matrix m = new Matrix(); // initialize camera and transform axis Camera = new ViewFrame(); newAxis = Axis.Move_Transform(m, Camera); // when animation is not running, transform object and repaint window if (options.animation == false) { newObject = Object.Move_Transform(m, Camera); ViewWindow.repaint(); } } public void resetObject() { Matrix m = new Matrix(); // reset the object to be original object, and do transformation Object = new Object3D(originObject); newObject = Object.Move_Transform(m, Camera); ViewWindow.repaint(); } } // class for the all the control options class ControlOption { boolean animation; //flag indicate if animation is running int rotate_index; //which way to rotate object during animation int spin_index; //which way to spin object during animation int object_index; //move object or camera int method_index; //translate or spin or rotate ControlOption(boolean flag, int ri, int si, int oi, int mi) { set_animation(flag); set_rindex(ri); set_sindex(si); set_oindex(oi); set_mindex(mi); } public void set_animation(boolean flag) { animation = flag; } public void set_rindex(int ri) { rotate_index = ri; } public void set_mindex(int mi) { method_index = mi; } public void set_sindex(int si) { spin_index = si; } public void set_oindex(int oi) { object_index = oi; } } // class for control panel class VwControl extends Panel { Button start, stop; Choice rotate, spin; Choice object_list, method_list; Choice model_list; Button up, down, left, right, in, out; Button resetCamera, resetObject; WireFrame caller; // layout of all the buttons, labels and choices. VwControl(WireFrame parent) { caller = parent; GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); setLayout(gridbag); c.fill = GridBagConstraints.BOTH; c.weightx = 1.0; c.gridwidth = GridBagConstraints.RELATIVE; Label model = new Label("Select Model:", Label.LEFT); gridbag.setConstraints(model, c); add (model); c.gridwidth = GridBagConstraints.REMAINDER; model_list = new Choice(); model_list.addItem("Cube"); model_list.addItem("Pyramid"); model_list.addItem("Cylinder"); gridbag.setConstraints(model_list, c); add(model_list); model_list.select("Cube"); c.gridwidth = GridBagConstraints.RELATIVE; resetCamera = new Button("Reset Camera"); gridbag.setConstraints(resetCamera, c); add (resetCamera); c.gridwidth = GridBagConstraints.REMAINDER; resetObject = new Button("Reset Object"); gridbag.setConstraints(resetObject, c); add (resetObject); c.gridwidth = GridBagConstraints.REMAINDER; c.weightx = 0.0; Label l1 = new Label("Animation:", Label.CENTER); gridbag.setConstraints(l1, c); add (l1); c.weightx = 1.0; c.gridwidth = GridBagConstraints.RELATIVE; Label l2 = new Label("Rotate Around: ", Label.LEFT); gridbag.setConstraints(l2, c); add (l2); c.gridwidth = GridBagConstraints.REMAINDER; rotate = new Choice(); rotate.addItem("X-Axis(CCW)"); rotate.addItem("Y-Axis(CCW)"); rotate.addItem("Z-Axis(CCW)"); rotate.addItem("X-Axis(CW)"); rotate.addItem("Y-Axis(CW)"); rotate.addItem("Z-Axis(CW)"); gridbag.setConstraints(rotate, c); add(rotate); rotate.select("X-Axis(CCW)"); c.gridwidth = GridBagConstraints.RELATIVE; Label l3 = new Label("Spin Around: ", Label.LEFT); gridbag.setConstraints(l3, c); add (l3); c.gridwidth = GridBagConstraints.REMAINDER; spin = new Choice(); spin.addItem("X-Axis(CCW)"); spin.addItem("Y-Axis(CCW)"); spin.addItem("Z-Axis(CCW)"); spin.addItem("X-Axis(CW)"); spin.addItem("Y-Axis(CW)"); spin.addItem("Z-Axis(CW)"); gridbag.setConstraints(spin, c); add(spin); spin.select("Y-Axis(CCW)"); c.gridwidth = GridBagConstraints.RELATIVE; start = new Button("Start"); gridbag.setConstraints(start, c); add (start); c.gridwidth = GridBagConstraints.REMAINDER; stop = new Button("Stop"); gridbag.setConstraints(stop, c); add(stop); c.weightx = 0.0; c.gridwidth = GridBagConstraints.REMAINDER; Label l4 = new Label("Manual:", Label.CENTER); gridbag.setConstraints(l4, c); add(l4); c.weightx = 1.0; c.gridwidth = GridBagConstraints.RELATIVE; Label l5 = new Label("Move What:", Label.LEFT); gridbag.setConstraints(l5, c); add (l5); c.gridwidth = GridBagConstraints.REMAINDER; object_list = new Choice(); object_list.addItem("Object"); object_list.addItem("Camera"); gridbag.setConstraints(object_list, c); add(object_list); object_list.select("Object"); c.gridwidth = GridBagConstraints.RELATIVE; Label l6 = new Label("Moving Method: ", Label.LEFT); gridbag.setConstraints(l6, c); add (l6); c.gridwidth = GridBagConstraints.REMAINDER; method_list = new Choice(); method_list.addItem("Translate"); method_list.addItem("Rotate"); method_list.addItem("Spin"); gridbag.setConstraints(method_list, c); add(method_list); method_list.select("Translate"); c.gridwidth = GridBagConstraints.RELATIVE; up = new Button("Up"); gridbag.setConstraints(up, c); add(up); c.gridwidth = GridBagConstraints.REMAINDER; down = new Button("Down"); gridbag.setConstraints(down, c); add(down); c.gridwidth = GridBagConstraints.RELATIVE; left = new Button("Left"); gridbag.setConstraints(left, c); add(left); c.gridwidth = GridBagConstraints.REMAINDER; right = new Button("Right"); gridbag.setConstraints(right, c); add (right); c.gridwidth = GridBagConstraints.RELATIVE; in = new Button("Inward"); gridbag.setConstraints(in, c); add(in); c.gridwidth = GridBagConstraints.REMAINDER; out = new Button("Outward"); gridbag.setConstraints(out, c); add(out); } public Insets insets() { return new Insets(10, 10, 10, 10); } public boolean action(Event evt, Object arg) { // button action if (evt.target instanceof Button) handleButton(evt); // choice action if (evt.target instanceof Choice) handleChoice(evt, arg); return true; } // function to handle choice selection public void handleChoice(Event evt, Object arg) { int index = ((Choice)evt.target).getSelectedIndex(); // chose a different model for object and restart the animation if (evt.target == model_list) { if (caller.options.animation == true) caller.stop(); caller.init_Model(index); caller.start(); } if (evt.target == rotate) caller.options.set_rindex(index); if (evt.target == spin) caller.options.set_sindex(index); if (evt.target == object_list) { caller.options.set_oindex(index); //if choose to move camera, and current moving method is spin, change // the method to be rotate. if (index == 1 && method_list.getSelectedIndex() == 2) { method_list.select(1); caller.options.set_mindex(1); } } if (evt.target == method_list) { if (caller.options.object_index == 1) // camera is moving { if (index == 2) // change spin to rotate { index = 1; method_list.select(index); } } if (index != 0) // traslating { up.setLabel("Y-Axis(CCW)"); down.setLabel("Y-Axis(CW)"); left.setLabel("X-Axis(CW)"); right.setLabel("X-Axis(CCW)"); in.setLabel("Z-Axis(CCW)"); out.setLabel("Z-Axis(CW)"); } else // rotating or spinning { up.setLabel("UP"); down.setLabel("Down"); left.setLabel("Left"); right.setLabel("Right"); in.setLabel("Inward"); out.setLabel("Outward"); } caller.options.set_mindex(index); } } // function for button action public void handleButton(Event evt) { if (evt.target == start) caller.start(); if (evt.target == stop) caller.stop(); if (evt.target == up) caller.move(0, 1, 0, false); if (evt.target == down) caller.move(0, -1, 0, false); if (evt.target == left) caller.move(-1, 0, 0, false); if (evt.target == right) caller.move(1, 0, 0, false); if (evt.target == in) caller.move(0, 0, 1, false); if (evt.target == out) caller.move(0, 0, -1, false); if (evt.target == resetCamera) caller.resetCamera(); // do not allow to reset object when animation is running if (evt.target == resetObject && caller.options.animation == false) caller.resetObject(); } } // class for viewing window class VwCanvas extends Canvas { Object3D Object; Object3D Axis; WireFrame caller; // Double Buffering offscreen Image and Graphics Image offImage; Graphics offGraphics; int prevx, prevy; VwCanvas(WireFrame parent) { caller = parent; setBackground(Color.white); resize ((int)caller.Camera.VW, (int)caller.Camera.VH); } // modify the update function, do not clear view window at this time public void update(Graphics g) { paint(g); } // paint the object and world axis on view window public void paint(Graphics g) { Face f; Line l; int lindex; int x1, y1, x2, y2; Object = caller.newObject; Axis = caller.newAxis; // boolean flat indicates if the line is already painted or not boolean linePainted[] = new boolean[Object.numLines + 1]; for (int i = 0; i < Object.numLines; i++) linePainted[i] = false; //initialize offscreen buffer for paint if (offGraphics == null) { offImage = createImage(size().width, size().height); offGraphics = offImage.getGraphics(); } // clear the offscreen Graphics offGraphics.setColor(getBackground()); offGraphics.fillRect(0, 0, size().width, size().height); // paint the visible lines in black color offGraphics.setColor(Color.black); // draw lines on each face that is not behind the view for (int i = 0; i < Object.numFaces; i++) { f = Object.faces[i]; if (f.behind == false) { for (int j = 0; j < f.numLines; j++) { lindex = Math.abs(f.line[j]) - 1; l = Object.lines[lindex]; if (l.OutOfRange == false) { if (linePainted[lindex] == false) { x1 = (int)Object.points[l.start].element[0]; y1 = (int)Object.points[l.start].element[1]; x2 = (int)Object.points[l.end].element[0]; y2 = (int)Object.points[l.end].element[1]; offGraphics.drawLine(x1, y1, x2, y2); linePainted[lindex] = true; } } } } } // paint the invisible line in light gray color offGraphics.setColor(Color.lightGray); for (int i = 0; i < Object.numLines; i++) { l = Object.lines[i]; if (linePainted[i] == false && l.OutOfRange == false) { x1 = (int)Object.points[l.start].element[0]; y1 = (int)Object.points[l.start].element[1]; x2 = (int)Object.points[l.end].element[0]; y2 = (int)Object.points[l.end].element[1]; offGraphics.drawLine(x1, y1, x2, y2); linePainted[i] = true; } } // paint the world axis in blue color offGraphics.setColor(Color.blue); // draw world axis for (int i = 0; i < Axis.numLines; i++) { // paint the line that are not out of range if (Axis.lines[i].OutOfRange != true) { int start = Axis.lines[i].start; float startx = Axis.points[start].element[0]; float starty = Axis.points[start].element[1]; int end = Axis.lines[i].end; float endx = Axis.points[end].element[0]; float endy = Axis.points[end].element[1]; offGraphics.drawLine((int)startx, (int)starty, (int)endx, (int)endy); if (i == 0) offGraphics.drawString("x", (int)endx, (int)endy); if (i == 1) offGraphics.drawString("y", (int)endx, (int)endy); if (i == 2) offGraphics.drawString("z", (int)endx, (int)endy); } } // paint the offscreen image to the view window g.drawImage(offImage, 0, 0, this); } public boolean mouseDown(Event evt, int x, int y) { // set the staring position prevx = x; prevy = y; return true; } public boolean mouseDrag(Event evt, int x, int y) { // get the moving vector int vectorx = x - prevx; int vectory = y - prevy; // mouse drag action is only effective for object translation and spinning if (caller.options.method_index != 1 && caller.options.object_index == 0) caller.move(vectorx, vectory, 0, true); //reset staring position prevx = x; prevy = y; return true; } }