// // $Id: ThreeD.java,v 1.3 1998/06/26 13:45:27 min Exp min $ // // In the method "add_scene_choices" the scenefiles that // can be loaded are added to a Choice option menu. // package threed; import java.lang.*; import java.net.*; import java.io.*; import java.awt.*; import java.applet.*; import myutil.*; import graphutil.*; import vectors.*; public class ThreeD extends Applet { private static URL BASE_URL; public static final boolean DEBUG = false; private static final int VRP_X = 3; private static final int VRP_Y = 6; private static final int VRP_Z = 1; private static final int LOOKAT_X = 0; private static final int LOOKAT_Y = 0; private static final int LOOKAT_Z = 0; private static final int VIEWPLANE_DISTANCE = 4; private static final int UPDATE_FREQUENCY = 3; // canvases private ThreeDCanvas overview_canvas = new ThreeDCanvas(); private ThreeDCanvas camera_canvas = new ThreeDCanvas(); private ThreeDCanvas ortho_canvas = new ThreeDCanvas(); // textfields and panels private Font my_font = new Font("Helvetica", Font.BOLD, 12); private TextField overview_text = new TextField("3D overview (modify Z of camera & lookat)"); private TextField topview_text = new TextField("Top view (modify X and Y of camera & lookat)"); private TextField cameraview_text = new TextField("Camera view"); private Panel top_panel = new Panel(); private Panel bottom_panel = new Panel(); // scenes and renderers private Scene overview_scene; private Scene camera_scene; private BasicRenderer overview_renderer = new BasicRenderer(); private BasicRenderer camera_renderer = new BasicRenderer(); private BasicRenderer ortho_renderer = new BasicRenderer(); // layout and constraints private Constrainer c = new Constrainer(); private GridBagLayout my_layout = new GridBagLayout(); // buttons, checkboxes, sliders private Panel left_button_panel = new Panel(); private Panel right_button_panel = new Panel(); private Checkbox display_axes = new Checkbox("Display axes"); private GraphicsObject axes = new GraphicsObject(); private Panel reset_button_panel = new Panel(); private Button reset_button = new Button("Reset camera"); private CheckboxGroup my_group = new CheckboxGroup(); private Checkbox perspective_checkbox = new Checkbox("Perspective"); private Checkbox parallel_checkbox = new Checkbox("Orthographic"); private Panel viewplane_panel = new Panel(); private Checkbox display_viewplane = new Checkbox("Display viewplane"); private TextField scrollbar_text = new TextField(); private Scrollbar viewplane_dist = new Scrollbar(Scrollbar.HORIZONTAL, VIEWPLANE_DISTANCE, 1, 1, 10); private Scenefile my_scenefile; private Choice scene_choice = new Choice(); private int last_selected; private Panel update_panel = new Panel(); private TextField update_text = new TextField(); private Scrollbar update_scrollbar = new Scrollbar(Scrollbar.HORIZONTAL, UPDATE_FREQUENCY, 1, 1, 10); // update variables private int update_frequency; private int current_frame = 0; // camera variables private GraphicsObject camera = new GraphicsObject(); private GraphicsObject lookat_point = new GraphicsObject(); private GraphicsObject viewplane = new GraphicsObject(); private double viewplane_distance; private Vector vrp = new Vector(); private Vector lookat = new Vector(); private Vector old_device_lookat; private Vector old_lookat = new Vector(); private Vector up = new Vector(); private Vector old_device_vrp; private Vector old_vrp = new Vector(); private double overview_device_y = 0; // nr of pixels corr. to 1 in WC private double ortho_device_x, ortho_device_y; private boolean dragging_vrp = false; // true if dragging the vrp private boolean dragging_lookat = false; public void start() { create_layout(); add_scene_choices(); initialize(); create_camera(); create_axes(); create_scenes(); // set scene and renderer for overview canvas overview_canvas.set_renderer(overview_renderer); // set scene and renderer for camera canvas camera_canvas.set_renderer(camera_renderer); camera_renderer.set_viewplane_distance(viewplane_distance); // set scene and renderer for ortho_canvas (top view) ortho_canvas.set_renderer(ortho_renderer); ortho_renderer.set_projection_type(BasicRenderer.PARALLEL); // compute the nr of pixels that correspond to 1 in world coordinates compute_device_y(); choose_scene(); } // constructor public String getAppletInfo() { return("3D Viewing v1.01 by Patrick Min, min@cs.princeton.edu\n$Id: ThreeD.java,v 1.3 1998/06/26 13:45:27 min Exp min $"); } // getAppletInfo private void create_scenes() { overview_scene = new Scene(); camera_scene = new Scene(); // create overview scene overview_scene.add_object(axes); overview_scene.add_object(camera); overview_scene.add_object(lookat_point); overview_scene.add_object(viewplane); // create camera scene camera_scene.add_object(axes); // let canvases know we changed the scene overview_canvas.set_scene(overview_scene); camera_canvas.set_scene(camera_scene); ortho_canvas.set_scene(overview_scene); } // create_scenes private void initialize() { vrp.set(VRP_X, VRP_Y, VRP_Z); // set the values of the user camera lookat.set(LOOKAT_X, LOOKAT_Y, LOOKAT_Z); up.set(0, 0, 1); viewplane_distance = VIEWPLANE_DISTANCE; scrollbar_text.setText("Viewplane dist: " + VIEWPLANE_DISTANCE); update_frequency = UPDATE_FREQUENCY; update_text.setText("Update every " + UPDATE_FREQUENCY + " frames"); compute_camera(); overview_renderer.set_view_pars(new Vector(3, 15, 3), new Vector(1, 1, 1), new Vector(0, 0, 1)); camera_renderer.set_view_pars(vrp, lookat, up); ortho_renderer.set_view_pars(new Vector(1, 1, 10), new Vector(1, 1, 0), new Vector(0, 1, 0)); ortho_renderer.set_viewplane_dimensions(-6.0, 6.0, -6.0, 6.0); } // initialize private void compute_device_y() // bad hack to get an estimate of how many pixels we should move // given a change in world coordinates { Vector one = new Vector(VRP_X, VRP_Y, VRP_Z + 1); Vector origin = new Vector(VRP_X, VRP_Y, VRP_Z); one = overview_renderer.transform(one); origin = overview_renderer.transform(origin); overview_device_y = Math.abs(one.get_y() - origin.get_y()); one.set(VRP_X + 1, VRP_Y, VRP_Z); origin.set(VRP_X, VRP_Y, VRP_Z); one = ortho_renderer.transform(one); origin = ortho_renderer.transform(origin); ortho_device_x = Math.abs(one.get_x() - origin.get_x()); one.set(VRP_X, VRP_Y + 1, VRP_Z); origin.set(VRP_X, VRP_Y, VRP_Z); one = ortho_renderer.transform(one); origin = ortho_renderer.transform(origin); ortho_device_y = Math.abs(one.get_y() - origin.get_y()); } // compute_device_y private void create_layout() { this.setLayout(my_layout); left_button_panel.setLayout(my_layout); reset_button_panel.setLayout(my_layout); right_button_panel.setLayout(my_layout); top_panel.setLayout(my_layout); bottom_panel.setLayout(my_layout); // set layout constraints for top panel overview_text.setEditable(false); topview_text.setEditable(false); cameraview_text.setEditable(false); overview_text.setFont(my_font); topview_text.setFont(my_font); cameraview_text.setFont(my_font); c.constrain(top_panel, overview_text, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, 1.0, 0.0, 1, 1, 1, 1); c.constrain(top_panel, topview_text, 1, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, 1.0, 0.0, 1, 1, 1, 1); c.constrain(top_panel, overview_canvas, 0, 1, 1, 1, GridBagConstraints.BOTH, GridBagConstraints.CENTER, 1.0, 1.0, 2, 2, 2, 2); c.constrain(top_panel, ortho_canvas, 1, 1, 1, 1, GridBagConstraints.BOTH, GridBagConstraints.CENTER, 1.0, 1.0, 2, 2, 2, 2); // set layout constraints for left button panel c.constrain_button(left_button_panel, display_axes, 0, 0); display_axes.setState(true); display_axes.setFont(my_font); c.constrain_button(left_button_panel, display_viewplane, 0, 1); display_viewplane.setState(true); display_viewplane.setFont(my_font); c.constrain_button(reset_button_panel, reset_button, 0, 0); c.constrain(left_button_panel, reset_button_panel, 0, 2, 1, 1, GridBagConstraints.BOTH, GridBagConstraints.CENTER, 1.0, 1.0, 1, 1, 1, 1); reset_button.setFont(my_font); // viewplane panel viewplane_panel.setLayout(my_layout); scrollbar_text.setEditable(false); scrollbar_text.setFont(my_font); c.constrain(viewplane_panel, scrollbar_text, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, 1.0, 0.1, 2, 2, 2, 2); viewplane_dist.setLineIncrement(1); viewplane_dist.setPageIncrement(1); c.constrain(viewplane_panel, viewplane_dist, 0, 1, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, 1.0, 0.1, 2, 2, 2, 2); // more left_button_panel c.constrain(left_button_panel, viewplane_panel, 0, 5, 1, 2, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, 1.0, 0.1, 2, 2, 2, 2); // update frequency panel update_panel.setLayout(my_layout); update_text.setEditable(false); update_text.setFont(my_font); c.constrain(update_panel, update_text, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, 1.0, 0.1, 2, 2, 2, 2); update_scrollbar.setLineIncrement(1); update_scrollbar.setPageIncrement(1); c.constrain(update_panel, update_scrollbar, 0, 1, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, 1.0, 0.1, 2, 2, 2, 2); // set layout constraints for right button panel perspective_checkbox.setFont(my_font); parallel_checkbox.setFont(my_font); scene_choice.setFont(my_font); c.constrain_button(right_button_panel, perspective_checkbox, 0, 0); c.constrain_button(right_button_panel, parallel_checkbox, 0, 1); c.constrain(right_button_panel, update_panel, 0, 2, 1, 2, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, 1.0, 0.1, 2, 2, 2, 2); c.constrain_button(right_button_panel, scene_choice, 0, 4); perspective_checkbox.setCheckboxGroup(my_group); parallel_checkbox.setCheckboxGroup(my_group); my_group.setCurrent(perspective_checkbox); // set layout constraints for bottom panel c.constrain(bottom_panel, left_button_panel, 0, 1, 1, 1, GridBagConstraints.VERTICAL, GridBagConstraints.WEST, 0.1, 1.0, 2, 2, 2, 2); c.constrain(bottom_panel, right_button_panel, 2, 1, 1, 1, GridBagConstraints.VERTICAL, GridBagConstraints.EAST, 0.1, 1.0, 2, 2, 2, 2); c.constrain(bottom_panel, camera_canvas, 1, 1, 1, 1, GridBagConstraints.BOTH, GridBagConstraints.CENTER, 1.0, 1.0, 2, 2, 2, 2); c.constrain(bottom_panel, cameraview_text, 1, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, 1.0, 0.1, 1, 1, 1, 1); // finally put top and bottom panel in our applet c.constrain(this, top_panel, 0, 0, 2, 1, GridBagConstraints.BOTH, GridBagConstraints.NORTHWEST, 1.0, 1.0, 0, 0, 0, 0); c.constrain(this, bottom_panel, 0, 1, 2, 1, GridBagConstraints.BOTH, GridBagConstraints.NORTHWEST, 1.0, 1.0, 0, 0, 0, 0); } // create_layout private void paint_all() { overview_canvas.paint(); camera_canvas.paint(); ortho_canvas.paint(); } // paint_all private void paint_sometimes() { current_frame++; if (current_frame == update_frequency) { current_frame = 0; paint_all(); } } // paint_sometimes public boolean action(Event e, Object arg) { if (e.target == reset_button) { initialize(); paint_all(); } else if (e.target == display_axes) { if (display_axes.getState()) axes.set_state(GraphicsObject.DISPLAY_COLOUR); else axes.set_state(GraphicsObject.DONT_DISPLAY); paint_all(); return true; } else if (e.target == display_viewplane) { if (display_viewplane.getState()) viewplane.set_state(GraphicsObject.DISPLAY_COLOUR); else viewplane.set_state(GraphicsObject.DONT_DISPLAY); overview_canvas.paint(); ortho_canvas.paint(); } else if (e.target == perspective_checkbox) { camera_renderer.set_projection_type(BasicRenderer.PERSPECTIVE); camera_canvas.paint(this.getGraphics()); } else if (e.target == parallel_checkbox) { camera_renderer.set_projection_type(BasicRenderer.PARALLEL); camera_canvas.paint(this.getGraphics()); } else if (e.target == scene_choice) { if (choose_scene()) { paint_all(); } return true; } // new scene choice return false; } // action public boolean handleEvent(Event e) { if (e.target == viewplane_dist) { int new_value = viewplane_dist.getValue(); if (new_value != viewplane_distance) { viewplane_distance = new_value; scrollbar_text.setText("Viewplane dist: " + (new Double(viewplane_distance)).toString()); camera_renderer.set_viewplane_distance(viewplane_distance); compute_camera(); paint_all(); } return true; } else if (e.target == update_scrollbar) { int new_value = update_scrollbar.getValue(); if (new_value != update_frequency) { update_frequency = new_value; current_frame = 0; String frame_string; if (update_frequency > 1) frame_string = new String(update_frequency + " frames"); else frame_string = new String("frame"); update_text.setText("Update every " + frame_string); } } return super.handleEvent(e); } // handleEvent public boolean mouseDown(Event e, int x, int y) { // check if we're in one of the top two canvases Component c = top_panel.locate(x, y); if ((c != (Component) overview_canvas) && (c != (Component) ortho_canvas)) return true; Point p = top_panel.location(); x -= p.x; y -= p.y; // first account for top panel p = c.location(); x -= p.x; y -= p.y; // then for canvas itself if (c == (Component) overview_canvas) { // transform vrp to get the device coordinates old_device_vrp = overview_renderer.transform(vrp); old_device_lookat = overview_renderer.transform(lookat); int dist_vrp = Math.abs((int) old_device_vrp.get_x() - x) + Math.abs((int) old_device_vrp.get_y() - y); if (dist_vrp < 10) { camera.set_state(GraphicsObject.DISPLAY_GREY); dragging_vrp = true; old_vrp.set(vrp); paint_all(); } else { int dist_lookat = Math.abs((int) old_device_lookat.get_x() - x) + Math.abs((int) old_device_lookat.get_y() - y); if (dist_lookat < 10) { lookat_point.set_state(GraphicsObject.DISPLAY_GREY); dragging_lookat = true; old_lookat.set(lookat); paint_all(); } } } else if (c == (Component) ortho_canvas) { // should be true old_device_vrp = ortho_renderer.transform(vrp); old_device_lookat = ortho_renderer.transform(lookat); int dist_vrp = Math.abs((int) old_device_vrp.get_x() - x) + Math.abs((int) old_device_vrp.get_y() - y); if (dist_vrp < 10) { camera.set_state(GraphicsObject.DISPLAY_GREY); dragging_vrp = true; old_vrp.set(vrp); paint_all(); } else { int dist_lookat = Math.abs((int) old_device_lookat.get_x() - x) + Math.abs((int) old_device_lookat.get_y() - y); if (dist_lookat < 10) { lookat_point.set_state(GraphicsObject.DISPLAY_GREY); dragging_lookat = true; old_lookat.set(lookat); paint_all(); } } } return true; } // mouseDown public boolean mouseUp(Event e, int x, int y) { if (camera.get_state() == GraphicsObject.DISPLAY_GREY) { camera.set_state(GraphicsObject.DISPLAY_COLOUR); dragging_vrp = false; paint_all(); } else if (lookat_point.get_state() == GraphicsObject.DISPLAY_GREY) { lookat_point.set_state(GraphicsObject.DISPLAY_COLOUR); dragging_lookat = false; paint_all(); } return true; } // mouseUp public boolean mouseDrag(Event e, int x, int y) { if (!dragging_vrp && !dragging_lookat) return true; Point p = top_panel.location(); x -= p.x; y -= p.y; // first account for top panel Component c = top_panel.locate(x, y); p = c.location(); x -= p.x; y -= p.y; // then for canvas itself double dx, dy; if (dragging_vrp) { dx = x - old_device_vrp.get_x(); dy = old_device_vrp.get_y() - y; } else { dx = x - old_device_lookat.get_x(); dy = old_device_lookat.get_y() - y; } if (c == (Component) overview_canvas) { if (dragging_vrp) vrp.set(vrp.get_x(), vrp.get_y(), old_vrp.get_z() + dy / overview_device_y); else if (dragging_lookat) // should be true lookat.set(lookat.get_x(), lookat.get_y(), old_lookat.get_z() + dy / overview_device_y); } // if, update overview_canvas else if (c == (Component) ortho_canvas) { if (dragging_vrp) vrp.set(old_vrp.get_x() + dx / ortho_device_x, old_vrp.get_y() + dy / ortho_device_y, vrp.get_z()); else if (dragging_lookat) lookat.set(old_lookat.get_x() + dx / ortho_device_x, old_lookat.get_y() + dy / ortho_device_y, lookat.get_z()); } // else if, update ortho_canvas else return true; compute_camera(); camera_renderer.set_view_pars(vrp, lookat, up); paint_sometimes(); return true; } // mouseDrag private boolean choose_scene() { // test if we've got something new if (scene_choice.getSelectedIndex() == last_selected) return false; else last_selected = scene_choice.getSelectedIndex(); try { BASE_URL = getCodeBase(); String url_string = new String(BASE_URL.toString() + "scenes/" + scene_choice.getSelectedItem()); URL my_url = new URL(url_string); create_scenes(); my_scenefile = new Scenefile(my_url.openStream()); my_scenefile.parse(overview_scene); my_scenefile = new Scenefile(my_url.openStream()); my_scenefile.parse(camera_scene); // has to be a better way to do this } catch (MalformedURLException e) { System.out.println(e.getMessage()); } catch (IOException e) { System.out.println(e.getMessage()); } return true; } // choose_scene private void add_scene_choices() { scene_choice.addItem("3d_scene.dat"); scene_choice.addItem("little_spheres.dat"); last_selected = -1; scene_choice.select(0); } // add_scene_choices private void create_axes() { axes.set_Color(Color.white); MyPolygon p = new MyPolygon(); p.addPoint(0, 0, 0); p.addPoint(3, 0, 0); axes.add_polygon(p); p = new MyPolygon(); p.addPoint(0, 0, 0); p.addPoint(0, 3, 0); axes.add_polygon(p); p = new MyPolygon(); p.addPoint(0, 0, 0); p.addPoint(0, 0, 3); axes.add_polygon(p); // the letter Z axes.set_Color(Color.orange); p = new MyPolygon(); p.set_closed(false); p.addPoint(-0.15, 0.15, 3.25); p.addPoint(0.15, -0.15, 3.25); p.addPoint(-0.15, 0.15, 3.5); p.addPoint(0.15, -0.15, 3.5); axes.add_polygon(p); // the letter X p = new MyPolygon(); p.set_closed(false); p.addPoint(3.35, -0.15, 0); p.addPoint(3.65, 0.15, 0); p.addPoint(3.5, 0, 0); p.addPoint(3.35, 0.15, 0); p.addPoint(3.65, -0.15, 0); axes.add_polygon(p); // the letter Y p = new MyPolygon(); p.set_closed(false); p.addPoint(-0.15, 3.35, 0); p.addPoint(0.15, 3.65, 0); p.addPoint(0, 3.5, 0); p.addPoint(-0.15, 3.65, 0); axes.add_polygon(p); } // create_axes private void create_camera() { // blue up vector camera.set_Color(Color.blue); MyPolygon p = new MyPolygon(); p.addPoint(0, 0, 0); p.addPoint(0, 0, 1); camera.add_polygon(p); // the x-axis becomes the lookat vector, and is made red p = new MyPolygon(); p.addPoint(0, 0, 0); p.addPoint(1, 0, 0); camera.set_Color(Color.red); camera.add_polygon(p); p = new MyPolygon(); p.addPoint(0, 0, 0); p.addPoint(0, 1, 0); camera.set_Color(Color.green); camera.add_polygon(p); // create the "point of interest" object p = new MyPolygon(); p.addPoint(0, 0, -0.5); p.addPoint(0, 0, 0.5); lookat_point.set_Color(Color.yellow); lookat_point.add_polygon(p); p = new MyPolygon(); p.addPoint(0, -0.5, 0); p.addPoint(0, 0.5, 0); lookat_point.add_polygon(p); p = new MyPolygon(); p.addPoint(-0.5, 0, 0); p.addPoint(0.5, 0, 0); lookat_point.add_polygon(p); // create the viewplane object p = new MyPolygon(); p.addPoint(0, -1, -1); p.addPoint(0, 1, -1); p.addPoint(0, 1, 1); p.addPoint(0, -1, 1); viewplane.set_Color(Color.lightGray); viewplane.add_polygon(p); } // create_camera private void compute_camera() { Matrix trans_matrix = new Matrix(); trans_matrix.translate(vrp); Matrix rot_matrix = new Matrix(); Vector n = lookat.subtract(vrp); n.normalize(); Vector u = up.cross_product(n); // keep it right handed u.normalize(); rot_matrix.set(n, u, n.cross_product(u)); rot_matrix = rot_matrix.transpose(); Matrix obj_matrix = trans_matrix.matrix_multiply(rot_matrix); camera.set_matrix(obj_matrix); trans_matrix.translate(lookat); lookat_point.set_matrix(trans_matrix); Matrix viewplane_matrix = new Matrix(); viewplane_matrix.translate(viewplane_distance, 0, 0); viewplane_matrix = obj_matrix.matrix_multiply(viewplane_matrix); viewplane.set_matrix(viewplane_matrix); } // compute_camera } // ThreeD class