/** * @file DrawCanvas class * @author R Daniel Bergeron * @see BSPTree.java * @version 2.0 *
* description DrawCanvas provides a canvas into which we will do all our * drawing. * * modified: Robert S Laramee * modification date: Tuesday 10 November 1998 * description: Shapes are now kept in a new data structure -the BSP * Tree. Also, a new inner class was added for listening * AutoRotate menu options. This inner action listener * class starts running a separate AutoRotate thread so * that the DrawCanvas object itself can still listen * for mouse events from the user. * * modification date: Tuesday 17 November 1998 * description: Changes were made to the EDIT mode. Now new solid * objects may be created. */ import java.awt.*; import java.awt.event.*; import java.util.*; // for vector class import javax.swing.*; // for swing components class DrawCanvas extends Canvas implements MouseListener, MouseMotionListener { /* File mode constants */ public static final int NULL = 0; public static final int OPEN = 1; public static final int SAVE = 2; public static final int QUIT = 3; private int curFileMode = NULL; // default edit mode // Shape constants public static final int CUBE = 1; public static final int TETRA = 2; public static final int SOLID = 3; private int curShapeType = CUBE; // default shape private String curShapeName = null; // default solid shape // Fill mode constants public static final int FILLED = 1; public static final int HOLLOW = 2; private static int curFillMode = FILLED; // default fill mode /* Edit mode constants */ public static final int DRAWNORMAL = 0; public static final int DRAWSOLID = 1; public static final int DELETE_ONE = 2; public static final int ERASE = 4; public static final int RESETVIEW = 5; private int curEditMode = DRAWNORMAL; // default edit mode // Auto Rotate constants public static final int ROTATEX = 0; public static final int ROTATEY = 1; public static final int ROTATEZ = 2; public static final int STOP = 3; private static int curRotateMode = STOP; // default rotate mode // Auto rotate degrees private int degreesX = 0; private int degreesY = 0; private int degreesZ = 0; private static final double MINZSIZE = 0.01; private static double zFront = 0.0; // front z of position box private static double zBack = -1; // back z of position box private static double ndcScale; // scale for ndc conversion private Point mouseStart = new Point( 100, 100 ); private Point mouseEnd = new Point( 200, 200 ); private Point4 startVC = new Point4(); private Point4 endVC = new Point4(); /** * startSet: set if a start position has been set, but not used yet. * This allows us to ignore extraneous mouse events with popup menu * enabled. */ private boolean startSet = false; private static Color curColor = Color.white; // default white private JPopupMenu savePopup; // set to active Jpopup, if one private static Dimension curSize = new Dimension(100, 100); // small default private BSPTree tree; // stores tree of shapes private Vector CTinstances; // a list of cube and // tetradrahedron instances private SolidObjectList SOlist; // a list of solid objects private SolidObject solid; // a solid object pointer private Face[] faces; private View curView = null; // the view handler private Point4 eyePoint; // the view point private Point4 mappedPoint; // the vp w/ inverse applied private OutSerialize output; // an output object private InSerialize input; // an input object private static int sleepTime = 60; // to set sleeptime // these objects are for double buffering //private Dimension offDimension; //private Image offImage; // buffer in which to draw //private Graphics2D offGraphics; // off screen graphics context /** * DrawCanvas constructor * The BSP Tree is really just another list. So, in theory, we should * be able to handle it just like the shape list. */ public DrawCanvas () { curView = new View(); tree = new BSPTree("bobs BSP Tree"); // added CTinstances = new Vector(); // added SOlist = new SolidObjectList(); // added eyePoint = new Point4(0.5, 0.5, 1.0); // added output = new OutSerialize(); // added input = new InSerialize(tree, SOlist); // added addMouseListener ( this ); addMouseMotionListener ( this ); addComponentListener(new ListenComponent()); } /** * getFillMode, getColor * setFillMode, setColor */ public static int getFillMode () { return curFillMode; } public static Color getColor () { return curColor; } public void setFillMode (int newFillMode) { curFillMode = newFillMode; } public void setColor (Color newColor) { curColor = newColor; } public static int getSleepTime() { return sleepTime; } public static void setSleepTime(int st) { sleepTime = st; } /** * setzFront * getzFront * setzBack * getzBack */ public static float getzFront ( ) { return (float) zFront; } public static float getzBack ( ) { return (float) zBack; } public static void setzFront ( float newval ) { zFront = newval; if ( zBack > zFront - MINZSIZE ) zBack = zFront - MINZSIZE; } public static void setzBack ( float newval ) { zBack = newval; if ( zBack > zFront - MINZSIZE ) zBack = zFront - MINZSIZE; } /** * overridden methods * Component methods */ public void paint(Graphics g) { /* map the viewpoint to the current world coordinates */ mappedPoint = tree.mapViewPoint(eyePoint); if (curEditMode == DRAWNORMAL) { /** tell the tree do draw itself here */ tree.displayTree(g, tree.getRoot(), mappedPoint); } else if (curEditMode == DRAWSOLID) { /** tell the solid do draw itself here */ solid.drawDefinition(g, this); } } /** * getPreferredSize * getMinimumSize */ public Dimension getPreferredSize() { return new Dimension (curSize); } public Dimension getMinimumSize() { return getPreferredSize(); } /** mouseListener methods * mousePressed */ public void mousePressed ( MouseEvent event ) { mouseStart.x = event.getX(); mouseStart.y = event.getY(); if (curEditMode == DRAWNORMAL) { startSet = true; } } /** mouseReleased * This method was modified to remove the old shapelist method calls */ public void mouseReleased ( MouseEvent event ) { mouseEnd.x = event.getX(); mouseEnd.y = event.getY(); if (curEditMode == DELETE_ONE ) { } else if ( startSet ) { // only update if last mouseEvent was a press addShape(); } startSet = false; // and reset i`n any case repaint(); } /** * mouseDragged */ public void mouseDragged ( MouseEvent event ) { if ( startSet ) { mouseEnd.x = event.getX(); mouseEnd.y = event.getY(); } } /** * mouseClicked */ public void mouseClicked ( MouseEvent event ) { if (curEditMode == DRAWSOLID) { solid.collectDefinition(SOlist, mouseStart.x, mouseStart.y, zFront-zBack); } } /** * empty Mouse Listener methods */ public void mouseEntered ( MouseEvent event ) {} public void mouseExited ( MouseEvent event ) {} public void mouseMoved ( MouseEvent event ) {} /** * processMouseEvent */ public void processMouseEvent ( MouseEvent event ) { // isPopupTrigger is normally set on right button DOWN, apparently // Obviously, we could check this in the mousePressed method, // but by handling it this way, we don't care what the trigger is. if ( ( event.isPopupTrigger() ) && ( savePopup != null ) ) { savePopup.show( this, event.getX(), event.getY() ); startSet = false; } else super.processMouseEvent( event ); } /** * additional methods of DrawCanvas: * reSize */ public void reSize(int w, int h) { reSize ( new Dimension( w, h )); } public void reSize(Dimension newSize) { curSize = newSize; curView.reSize( newSize ); } /** * setShape * This sets the current shape. IF the current shape is a solid, it * also sets the current solid's name */ public void setShape (int newShape, String newName) { curShapeType = newShape; curShapeName = newName; } /** * setAutoRotateMode * @param newRotateMode -the axis about which to rotate */ public void setAutoRotateMode (int newRotateMode) { curRotateMode = newRotateMode; } /** * getAutoRotateMode *
x * These methods are all called from the AutoRotate object/thread. * @see AutoRotate.java */ public int getAutoRotateMode () { return curRotateMode; } public int getDegreesX() { return degreesX; } public int getDegreesY() { return degreesY; } public int getDegreesZ() { return degreesZ; } public void setDegreesX() { degreesX++; } public void setDegreesY() { degreesY++; } public void setDegreesZ() { degreesZ++; } /** * setFileMode * since these are all short term events, the file mode is just set back * to null each time. * @param newFileMode -either null, open, save, or quit */ public void setFileMode ( int newFileMode ) { switch ( newFileMode ) { case NULL: break; case OPEN: View.reset(); input.run(); repaint(); break; case SAVE: output.save(SOlist, CTinstances); break; case QUIT: System.exit( 0 ); break; default: curFileMode = NULL; } curFileMode = NULL; } /** * setEditMode */ public void setEditMode ( int newEditMode ) { switch ( newEditMode ) { case DRAWNORMAL: curEditMode = newEditMode; View.reset(); repaint(); break; case DRAWSOLID: curEditMode = newEditMode; View.reset(); solid = new SolidObject(); // repaint(); break; case ERASE: tree = new BSPTree("new BSP Tree"); //SOlist = new SolidObjectList(); // added input = new InSerialize(tree, SOlist); // which tree will you repaint(); // add input to ?! break; case RESETVIEW: View.reset(); repaint(); break; default: curEditMode = newEditMode; } } /** * addPopup * PopupMenu --> JPopupMenu */ public void addPopup ( JPopupMenu popup ) { //add( popup ); enableEvents( AWTEvent.MOUSE_EVENT_MASK ); savePopup = popup; } /** * The addShape method adds a new shape to the BSP Tree. addShape() is * called from the mouseReleased() event only when the mousePressed() * event is called in DRAWNORMAL mode. * The HOUSE option was removed * The SOLID option was added */ private void addShape ( ) { Cube cube = null; Tetrahedron tetrahedron = null; SolidObject so = null; startVC = View.toVC ( mouseStart, zFront ); endVC = View.toVC ( mouseEnd, zFront ); Matrix4 trans = Matrix4.translation ( Math.min(startVC.x, endVC.x), Math.min(startVC.y, endVC.y), zFront ); Matrix4 scale = Matrix4.scale ( Math.abs(startVC.x - endVC.x), Math.abs(startVC.y - endVC.y), zFront - zBack ); Matrix4 xform = View.inverse().times(trans).times(scale); switch ( curShapeType ) { case CUBE: cube = new Cube (xform, curColor, tree ); // added 'tree' CTinstances.addElement(new Instance("cube", xform, curColor)); break; case TETRA: tetrahedron = new Tetrahedron (xform, curColor, tree ); CTinstances.addElement(new Instance("tetrahedron", xform, curColor)); break; case SOLID: so = SOlist.getSolidObject(curShapeName); so.addToTree(xform, curColor, tree ); // added break; } } /** named inner classes * ComponentListener inner class */ class ListenComponent extends ComponentAdapter { public ListenComponent() {} /** * componentResized */ public void componentResized (ComponentEvent event) { Dimension wSize; // width and height of window wSize = getSize (); reSize(wSize); getParent().validate(); } } // end ListenComponent class /** * SetFileModeAL inner class of DrawCanvas * SetFileModeAL is an ActionListener that sets the file mode */ public static class SetFileModeAL implements ActionListener { private int fileModePicked; private DrawCanvas dC; public SetFileModeAL (DrawCanvas canvas, int newFileMode) { dC = canvas; fileModePicked = newFileMode; } public void actionPerformed ( ActionEvent event ) { dC.setFileMode ( fileModePicked ); } } /** * SetEditModeAL inner class of DrawCanvas * SetEditModeAL is an ActionListener that sets the edit mode */ public static class SetEditModeAL implements ActionListener { private int editModePicked; private DrawCanvas dC; public SetEditModeAL (DrawCanvas canvas, int newEditMode) { dC = canvas; editModePicked = newEditMode; } public void actionPerformed ( ActionEvent event ) { dC.setEditMode ( editModePicked ); } } /** * SetColorAL inner class of DrawCanvas * SetColorAL is an ActionListener that sets the line color */ public static class SetColorAL implements ActionListener { private Color colorPicked; private DrawCanvas dC; public SetColorAL (DrawCanvas canvas, Color newColor) { dC = canvas; colorPicked = newColor; } public void actionPerformed ( ActionEvent event ) { dC.setColor ( colorPicked ); dC.repaint(); } } /** * SetShapeAL inner class of DrawCanvas * SetModeAL is an ActionListener that sets the Fill mode */ public static class SetShapeAL implements ActionListener { private int thisShape; private String thisName; private DrawCanvas dC; public SetShapeAL (DrawCanvas canvas, int shapeType, String name ) { dC = canvas; thisShape = shapeType; thisName = name; } public void actionPerformed ( ActionEvent event ) { dC.setShape ( thisShape, thisName ); dC.repaint(); } } /** * SetFillModeAL inner class of DrawCanvas * SetModeAL is an ActionListener that sets the Fill mode */ public static class SetFillModeAL implements ActionListener { private int pickedFillMode; private DrawCanvas dC; public SetFillModeAL (DrawCanvas canvas, int newFillMode) { dC = canvas; pickedFillMode = newFillMode; } public void actionPerformed ( ActionEvent event ) { dC.setFillMode ( pickedFillMode ); dC.repaint(); } } /** * SetAutoRotateAL inner class of DrawCanvas * SetAutoRotateAL is an ActionListener that sets the Auto Rotate mode */ public static class SetAutoRotateAL implements ActionListener { private int pickedRotateMode; private DrawCanvas dC; private JRotateDialog rd; public SetAutoRotateAL (DrawCanvas canvas, int newRotateMode) { dC = canvas; pickedRotateMode = newRotateMode; } /** * Here we do two things: * 1 We start a new thread running which runs the autorotation * 2 We put up a new dialog box that adjusts the speed of the rotation * IF there's already a dialog box present, dispose of it */ public void actionPerformed ( ActionEvent event ) { dC.setAutoRotateMode(pickedRotateMode); AutoRotate a = new AutoRotate((DrawCanvas)dC); a.start(); // start the AutoRotate thread if (rd != null) // if there is already a dialog rd.dispose(); // dispose of it if (pickedRotateMode != STOP) { rd = new JRotateDialog( new JFrame("AutoRotate"), (DrawCanvas)dC); rd.show(); } else { if (rd != null) // if there is already a dialog rd.dispose(); // dispose of it } } } }