import java.applet.*; import java.awt.*; import Polytope; public class PolytopeDisplay extends Applet implements LayoutManager { private static final Color backgroundColor = Color . black; private int cogStringYPos; private Polytope cube; private boolean dragFlag; private boolean facesFlag; private static final Color foregroundColor = Color . white; private Image image; private Graphics imageGraphics; private int imageHeight; private int imageWidth; private static final double lightAmbience = 0.5; private static final double lightCoordX = 1.0; private static final double lightCoordY = 0.0; private static final double lightCoordZ = 1.0; private double[] lightCoords; private static final int preferredHeight = 360; private static final int preferredWidth = 480; private int prevX; private int prevY; private Checkbox radioEdges; private Checkbox radioFaces; private CheckboxGroup radioFacesOrEdges; private Checkbox radioMono; private CheckboxGroup radioMotions; private Checkbox radioNoShading; private Checkbox radioRotate; private Checkbox radioShading; private CheckboxGroup radioShadingFlag; private Checkbox radioStereo; private CheckboxGroup radioStereoFlag; private Checkbox radioTranslate; private boolean rotationFlag; private boolean shadingFlag; private boolean stereoFlag; private static final double trueViewerEyeSeparation = 220.0; private double[] viewerCoords; private static double viewerEyeSeparation; private static final double viewerZCoord = 1500.0; // unit = pixel public String getAppletInfo() { return "Name: PolytopeDisplay\r\n" + "Author: Mike McKenna\r\n" + "Created with Microsoft Visual J++ Version 1.1"; } public Color getBackgroundColor () { return backgroundColor; } public Color getForegroundColor () { return foregroundColor; } public int getNumRows () { return imageHeight; } public int getNumColumns () { return imageWidth; } public double getHeight () { return (double) imageHeight; } public double getWidth () { return (double) imageWidth; } public void init() { setBackground (backgroundColor); setForeground (foregroundColor); resize (preferredWidth, preferredHeight); setLayout (this); cogStringYPos = 0; dragFlag = false; image = null; imageGraphics = null; imageHeight = -1; imageWidth = -1; prevX = 0; prevY = 0; viewerEyeSeparation = trueViewerEyeSeparation * 1.5; double [] temp = {0.0, 0.0, viewerZCoord}; viewerCoords = temp; double[] m_center = {0.0, 0.0, -(viewerZCoord * viewerZCoord + 0.25 * viewerEyeSeparation * viewerEyeSeparation) / viewerZCoord}; cube = Polytope . newCube (3, 0.5 * viewerEyeSeparation, m_center); double[] m_fromV1 = {0.5, 0.0, 0.5}; double[] m_toV1 = {0.0, 0.0, Math.sqrt(0.5)}; cube . rotate (m_fromV1, m_toV1); double[] m_fromV2 = {0.0, 0.5, Math.sqrt(0.5)}; double[] m_toV2 = {0.0, 0.0, Math.sqrt(0.75)}; cube . rotate (m_fromV2, m_toV2); double lightCoordsMultiplier = (1.0 - lightAmbience) / Math . sqrt (lightCoordX * lightCoordX + lightCoordY * lightCoordY + lightCoordZ * lightCoordZ ); lightCoords = new double [3]; lightCoords [0] = lightCoordX * lightCoordsMultiplier; lightCoords [1] = lightCoordY * lightCoordsMultiplier; lightCoords [2] = lightCoordZ * lightCoordsMultiplier; rotationFlag = true; radioMotions = new CheckboxGroup (); add (radioRotate = new Checkbox(null,radioMotions, rotationFlag)); add (radioTranslate = new Checkbox(null,radioMotions,!rotationFlag)); stereoFlag = true; radioStereoFlag = new CheckboxGroup (); add (radioStereo = new Checkbox (null, radioStereoFlag, stereoFlag)); add (radioMono = new Checkbox (null, radioStereoFlag,!stereoFlag)); facesFlag = true; radioFacesOrEdges = new CheckboxGroup (); add (radioFaces = new Checkbox (null, radioFacesOrEdges, facesFlag)); add (radioEdges = new Checkbox (null, radioFacesOrEdges,!facesFlag)); shadingFlag = false; radioShadingFlag = new CheckboxGroup (); add(radioNoShading=new Checkbox(null,radioShadingFlag,!shadingFlag)); add(radioShading =new Checkbox(null,radioShadingFlag, shadingFlag)); repaint (); } // // Drawing methods. We use double buffering to avoid flicker. // public void update (Graphics g) { Dimension m_currentSize = size (); if (imageWidth != m_currentSize . width || imageHeight != m_currentSize . height ) { // Resize: imageWidth = m_currentSize . width; imageHeight = m_currentSize . height; layoutContainer (this); image = createImage (imageWidth, imageHeight); imageGraphics = image . getGraphics (); } imageGraphics . setColor (backgroundColor); imageGraphics . fillRect (0, 0, imageWidth, imageHeight); if (stereoFlag) { double[] stereoViewerCoords = new double [viewerCoords . length]; for (int i = viewerCoords . length; --i >= 1 ;) stereoViewerCoords [i] = viewerCoords [i]; stereoViewerCoords [0] = -0.5 * viewerEyeSeparation; cube . draw (this, stereoViewerCoords, facesFlag, shadingFlag); stereoViewerCoords [0] = 0.5 * viewerEyeSeparation; cube . draw (this, stereoViewerCoords, facesFlag, shadingFlag); } else { cube . draw (this, viewerCoords, facesFlag, shadingFlag); } imageGraphics . setColor (foregroundColor); imageGraphics . drawString ( "center of gravity = (" + cube . getCenter () [0] + ", " + cube . getCenter () [1] + ", " + cube . getCenter () [2] + ")" , 6, cogStringYPos); // I see no other way to set radio button labels with a custom color: int fontAscent = getFontMetrics (getFont ()) . getAscent (); drawComponentLabel (imageGraphics, radioRotate, "Rotate",fontAscent); drawComponentLabel (imageGraphics, radioTranslate, "Translate",fontAscent); drawComponentLabel (imageGraphics, radioStereo, "Stereo",fontAscent); drawComponentLabel (imageGraphics, radioMono , "Mono" ,fontAscent); drawComponentLabel (imageGraphics, radioFaces , "Faces" ,fontAscent); drawComponentLabel (imageGraphics, radioEdges , "Edges" ,fontAscent); drawComponentLabel (imageGraphics, radioNoShading, "Don't Shade Faces",fontAscent); drawComponentLabel (imageGraphics, radioShading, "Shade Faces (best with 24-bit color)",fontAscent); paint (g); } void drawComponentLabel (Graphics g, Component comp, String string, int fontAscent) { Point m_loc = comp . location (); Dimension m_size = comp . size (); g . drawString (string, m_loc.x + m_size.width, m_loc.y + (m_size.height + fontAscent) / 2); } public void paint (Graphics g) { if (image != null) g .drawImage (image, 0, 0, null); } public void fillPolygon (Color color, double[] xCoords,double[] yCoords,int numPoints) { int[] intXCoords = new int [numPoints]; int[] intYCoords = new int [numPoints]; for (int p = numPoints; --p >= 0 ;) { intXCoords [p] = (imageWidth >> 1) + (int) xCoords [p]; intYCoords [p] = (imageHeight >> 1) + (int) yCoords [p]; } Color savedColor = imageGraphics . getColor (); imageGraphics . setColor (color); imageGraphics . fillPolygon (intXCoords, intYCoords, numPoints); imageGraphics . setColor (savedColor); } public void drawLine (Color color, double fromX, double fromY, double toX, double toY) { Color savedColor = imageGraphics . getColor (); imageGraphics . setColor (color); imageGraphics . drawLine ( (imageWidth >> 1) + (int) fromX, (imageHeight >> 1) + (int)fromY, (imageWidth >> 1) + (int) toX , (imageHeight >> 1) + (int)toY ); imageGraphics . setColor (savedColor); } public void drawPoint (Color color, double x, double y) { drawLine (color, x, y, x, y); } public double shading (double[] outwardNormal) { if (outwardNormal == null) return 1.0; int m_length = lightCoords . length; if (m_length > outwardNormal.length) m_length = outwardNormal.length; double m_outwardNormalLength = 0.0; for (int i = m_length; --i >= 0 ;) m_outwardNormalLength += outwardNormal [i] * outwardNormal [i]; if (m_outwardNormalLength <= 0.0) return 1.0; double m_shading = 0.0; for (int i = m_length; --i >= 0 ;) m_shading += lightCoords [i] * outwardNormal [i]; if (m_shading < 0.0) m_shading = 0.0; return lightAmbience + m_shading / Math.sqrt (m_outwardNormalLength); } // // Event Handlers // public boolean action (Event e, Object o) { if (e.target == radioStereo || e.target == radioMono) { stereoFlag = radioStereo . getState (); repaint (); } else if (e.target == radioRotate || e.target == radioTranslate) { rotationFlag = radioRotate . getState (); } else if (e.target == radioFaces || e.target == radioEdges) { facesFlag = radioFaces . getState (); repaint (); } else if (e.target == radioNoShading || e.target == radioShading) { shadingFlag = radioShading . getState (); repaint (); } return true; } public boolean mouseDown(Event evt, int x, int y) { return handleMouse (x, y, true); } public boolean mouseUp(Event evt, int x, int y) { return handleMouse (x, y, false); } public boolean mouseDrag(Event evt, int x, int y) { return handleMouse (x, y, true); } public boolean mouseMove(Event evt, int x, int y) { return handleMouse (x, y, false); } public boolean mouseEnter(Event evt, int x, int y) { return handleMouse (x, y, false); } public boolean mouseExit(Event evt, int x, int y) { return handleMouse (x, y, false); } private boolean handleMouse (int x, int y, boolean newDragFlag) { if (dragFlag || newDragFlag) { if (x != prevX || y != prevY) { if (rotationFlag) { double[] fromV = {0.5 * (double) (prevX - x), 0.5 * (double) (prevY - y), 0.5 * viewerEyeSeparation}; double[] toV = {-fromV [0], -fromV [1], fromV [2]}; cube . rotate (fromV, toV); } else { int m_dimension = cube . getDimension (); double[] m_translation = new double [m_dimension]; if (m_dimension >= 1) m_translation [0] = (double) (x - prevX); if (m_dimension >= 2) m_translation [1] = (double) (y - prevY); for (int i = m_dimension; --i >=2; m_translation[i]=0.0); cube . translate (m_translation); } } dragFlag = newDragFlag; repaint (); } prevX = x; prevY = y; return true; } // // This applet implements its own layout manager. // public void addLayoutComponent (String name, Component comp) {} public void removeLayoutComponent (Component comp) {} public Dimension preferredLayoutSize (Container target) { return new Dimension (preferredWidth, preferredHeight); } public Dimension minimumLayoutSize (Container target) { return preferredLayoutSize (target); } public void layoutContainer (Container target) { cogStringYPos = 6 + getFontMetrics (getFont ()) . getHeight (); moveComponent (radioRotate , 6, imageHeight - 6, -11); moveComponent (radioTranslate, 6, imageHeight - 6, -10); moveComponent (radioStereo , 6, imageHeight - 6, -8); moveComponent (radioMono , 6, imageHeight - 6, -7); moveComponent (radioFaces , 6, imageHeight - 6, -5); moveComponent (radioEdges , 6, imageHeight - 6, -4); moveComponent (radioNoShading, 6, imageHeight - 6, -2); moveComponent (radioShading , 6, imageHeight - 6, -1); } private void moveComponent (Component comp, int x, int y, int multiplier) { if (comp != null) { Dimension m_preferredSize = comp . preferredSize (); comp.reshape(x, y + multiplier * m_preferredSize . height, m_preferredSize . width, m_preferredSize . height); } } public void reshape (int x, int y, int w, int h) { super . reshape (x, y, w, h); repaint (); } }