package edu.jsu.leathrum.mathlets.shared; import javax.swing.*; import java.awt.geom.*; import java.awt.event.*; import java.awt.Graphics2D; import java.awt.Graphics; import java.awt.Font; import java.awt.Dimension; import java.awt.Image; import java.awt.Color; public abstract class AGraphPanel3d extends JPanel implements MouseListener, MouseMotionListener { protected final int HEIGHT = 300; protected final int WIDTH = 300; AGraphCanvas3d canvas; protected JButton windowButton = new JButton("bounds"); protected JButton viewButton = new JButton("view/grid"); public CoordsConverter3d c = new CoordsConverter3d(HEIGHT, WIDTH, -10, 10, -10, 10, -10, 10); Box b1, b2; protected ButtonList blist = new ButtonList(); private boolean showGridFields = true; public static final boolean NO_GRID = false; public AGraphPanel3d () { this.setOpaque(false); } public AGraphPanel3d (boolean b) { this.setOpaque(false); showGridFields = b; viewDisplay = new ViewPanel3d(); } public void initPanel(AGraphCanvas3d canv) { canvas = canv; canvas.addMouseListener(this); canvas.addMouseMotionListener(this); b1 = new Box(BoxLayout.Y_AXIS); b1.add(canvas); b2 = new Box(BoxLayout.X_AXIS); b2.add(windowButton); windowButton.setOpaque(false); b2.add(viewButton); viewButton.setOpaque(false); b2.add(Box.createHorizontalGlue()); b1.add(b2); add(b1); blist.registerButtonAction(windowButton, new WindowAction()); blist.registerButtonAction(viewButton, new ViewAction()); } class WindowAction implements ActionWrapper { public void doThis() { windowDisplay.pack(); windowDisplay.show(); } } class ViewAction implements ActionWrapper { public void doThis() { viewDisplay.pack(); viewDisplay.show(); } } protected WindowPanel3d windowDisplay = new WindowPanel3d(); protected ViewPanel3d viewDisplay = new ViewPanel3d(); public void localinit() { windowButton.setText(AMathlet.getProperty("window")); windowDisplay.initwindow(); if (showGridFields) viewButton.setText(AMathlet.getProperty("viewgrid")); else viewButton.setText(AMathlet.getProperty("view")); viewDisplay.initwindow(); canvas.setAxisLabels(AMathlet.getProperty("xaxis"), AMathlet.getProperty("yaxis"), AMathlet.getProperty("zaxis")); } public void reapply() { // this is where values in windows are transferred to CoordsConverter3D windowDisplay.applyValues(); viewDisplay.applyValues(); // *both* include call to redraw() -- redundant, but easier this way } public void redraw() { // this method does *not* change values in CoordsConverter3D canvas.drawNew(); // difference between redraw() and repaint() is this call canvas.repaint(); } public void setGridLabels(String xgrid, String ygrid) { // note here: xgrid and ygrid strings are *not* the actual labels -- // instead, they are stubs which give: // 1) with "eq" suffix added, property names for AMathlet.getProperty() // 2) field names for handlers, for AMathlet.setHandler() // note that will wind up with *both* sets of field names registered viewDisplay.setGridLabels(xgrid,ygrid); } public void setGridDefaults(double newxgrid, double newygrid) { // default values are stored in the ViewPanel3d object viewDisplay.setGridDefaults(newxgrid, newygrid); } int prevx=0, prevy=0; public void mouseClicked(MouseEvent e) { } public void mousePressed(MouseEvent e) { prevx = e.getX(); prevy = e.getY(); e.consume(); } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mouseDragged(MouseEvent e) { int x = e.getX(); int y = e.getY(); Point2D.Double p=viewDisplay.getViewAngles(); viewDisplay.setViewAngles(p.x-(x-prevx),p.y-(y-prevy)); canvas.repaint(); prevx = x; prevy = y; e.consume(); } public void mouseMoved(MouseEvent e) { } public class CoordsConverter3d { public double xmin, xmax, delx, ymin, ymax, dely, zmin, zmax, delz; int ht, wd; public AffineTransform tr = new AffineTransform(); public GeneralPath axpath = new GeneralPath(); public CoordsConverter3d(int ht0, int wd0, double x0, double x1, double y0, double y1, double z0, double z1) { ht = ht0; wd = wd0; xmin = x0; xmax = x1; ymin = y0; ymax = y1; zmin = z0; zmax = z1; delx = dely = delz = 1.0; setScale(); } private void setScale() { double cwd = Math.max(Math.max(xmax,-xmin),Math.max(ymax,-ymin)); double hsc = ((double) wd)/(2*cwd); double vsc = ((double) ht)/(zmax-zmin); tr.setToIdentity(); tr.scale(hsc,-vsc); tr.translate(cwd,-zmax); buildAxesPath(); } // next two methods are utility methods for buildAxesPath() private void moveTo(Point2D p) { axpath.moveTo((float) p.getX(), (float) p.getY()); } private void lineTo(Point2D p) { axpath.lineTo((float) p.getX(), (float) p.getY()); } private void buildAxesPath() { double cwd = Math.max(Math.max(xmax,-xmin),Math.max(ymax,-ymin)); double hrez=2*cwd/((double) WIDTH); double vrez=(zmax-zmin)/((double) HEIGHT); double x, y, z, q; axpath = new GeneralPath(); moveTo(project(xmin,0.0,0.0)); lineTo(project(xmax,0.0,0.0)); q=Math.sqrt(cb*cb+sa*sa*sb*sb); for (x=delx; xxmin; x-=delx) { moveTo(project(x,-2*cb*hrez/q,2*sa*sb*vrez/q)); lineTo(project(x,2*cb*hrez/q,-2*sa*sb*vrez/q)); } moveTo(project(0.0,ymin,0.0)); lineTo(project(0.0,ymax,0.0)); q=Math.sqrt(cb*cb+ca*ca*sb*sb); for (y=dely; yymin; y-=dely) { moveTo(project(2*cb*hrez/q,y,-2*ca*sb*vrez/q)); lineTo(project(-2*cb*hrez/q,y,2*ca*sb*vrez/q)); } moveTo(project(0.0,0.0,zmin)); lineTo(project(0.0,0.0,zmax)); for (z=delz; zzmin; z-=delz) { moveTo(project(2*hrez*sa,-2*hrez*ca,z)); lineTo(project(-2*hrez*sa,2*hrez*ca,z)); } } public void setCoords(double x0, double x1, double y0, double y1, double z0, double z1) { xmin = x0; xmax = x1; ymin = y0; ymax = y1; zmin = z0; zmax = z1; setScale(); } public void setDeltas(double delxnew, double delynew, double delznew) { delx = delxnew; dely = delynew; delz = delznew; buildAxesPath(); } private final double DEG2RAD = Math.PI/180; private double va=45.0; private double vb=45.0; public double sa=0.0; public double ca=0.0; public double sb=0.0; public double cb=0.0; private DoubleTriad viewvec = new DoubleTriad(0,0,0); public void setViewAngles(double newa, double newb) { va=newa; vb=newb; sa=Math.sin(DEG2RAD*va); ca=Math.cos(DEG2RAD*va); sb=Math.sin(DEG2RAD*vb); cb=Math.cos(DEG2RAD*vb); viewvec = new DoubleTriad(ca*sb, sa*sb, cb); buildAxesPath(); } public double dotview(DoubleTriad t) { return t.x*viewvec.x+t.y*viewvec.y+t.z*viewvec.z; } public Point2D.Double getViewAngles() { return new Point2D.Double(va, vb); } public Point2D.Double project(DoubleTriad pt) { return new Point2D.Double(-pt.x*sa+pt.y*ca, -pt.x*ca*cb-pt.y*sa*cb+pt.z*sb); } public Point2D.Double project(double x, double y, double z) { return project(new DoubleTriad(x,y,z)); } } // end CoordsConverter3d, inner class of AGraphPanel3d public abstract class AGraphCanvas3d extends JComponent { Dimension size; protected double xgrid=1.0, ygrid=1.0; private String xaxis = "x", yaxis = "y", zaxis = "z"; public AGraphCanvas3d(int initialWidth, int initialHeight) { size = new Dimension(initialWidth, initialHeight); this.setBackground(Color.white); this.setOpaque(false); } public Dimension getPreferredSize() { return getMinimumSize(); } public Dimension getMinimumSize() { return size; } // instantiating subclass of this abstract class typically includes // variable of type GraphModel or GraphModel[] // -- following method doesn't actually paint anything, but generates the // wireframe by adding points (as DoubleTriads) to the GraphModel(s) protected abstract void drawNew(); public void setAxisLabels(String newx, String newy, String newz) { xaxis = newx; yaxis = newy; zaxis = newz; } protected void drawAxes(Graphics2D g) { g.setPaint(Color.black); g.draw(c.tr.createTransformedShape(c.axpath)); Point2D p1=c.tr.transform(c.project(c.xmax,0.0,0.0),null); FormattedLabel flabel = new FormattedLabel(xaxis); Dimension lsize = flabel.getPreferredSize(); flabel.setSize(lsize); g.setPaint(this.getBackground()); g.fill(new Rectangle2D.Double(p1.getX(),p1.getY(),lsize.width,lsize.height)); g.setPaint(Color.black); flabel.paintComponent(g.create((int) p1.getX(), (int) p1.getY(), lsize.width, lsize.height)); p1=c.tr.transform(c.project(0.0,c.ymax,0.0),null); g.setPaint(this.getBackground()); flabel.setText(yaxis); lsize = flabel.getPreferredSize(); flabel.setSize(lsize); g.fill(new Rectangle2D.Double(p1.getX(),p1.getY(),lsize.width,lsize.height)); g.setPaint(Color.black); flabel.paintComponent(g.create((int) p1.getX(), (int) p1.getY(), lsize.width, lsize.height)); p1=c.tr.transform(c.project(0.0,0.0,c.zmax),null); g.setPaint(this.getBackground()); flabel.setText(zaxis); lsize = flabel.getPreferredSize(); flabel.setSize(lsize); g.fill(new Rectangle2D.Double(p1.getX()+1,p1.getY(),lsize.width,lsize.height)); g.setPaint(Color.black); flabel.paintComponent(g.create((int) p1.getX()+1, (int) p1.getY(), lsize.width, lsize.height)); } // instantiating subclass of this abstract class typically includes // variable of type GraphModel or GraphModel[] // -- following method sets color and // calls paint() method, if necessary for each index of the array protected abstract void paintModel(Graphics2D g); public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; Dimension size = getPreferredSize(); Image buffer=createImage(size.width, size.height); Graphics2D bg = (Graphics2D) buffer.getGraphics(); bg.setPaint(this.getBackground()); bg.fill(new Rectangle2D.Double(0, 0, size.width, size.height)); paintModel(bg); drawAxes(bg); g2.setPaint(this.getBackground()); g2.fill(new Rectangle2D.Double(0, 0, size.width, size.height)); g2.drawImage(buffer, 0, 0, this); } public void setGridValues(double a, double b) { xgrid=a; ygrid=b; } public Point2D.Double getGridValues() { return new Point2D.Double(xgrid,ygrid); } } // end AGraphCanvas3d, inner class of AGraphPanel3d private class WindowPanel3d extends JFrame { DoubleInputSet xminField = new DoubleInputSet("xmin=",12); DoubleInputSet xmaxField = new DoubleInputSet("xmax=",12); DoubleInputSet delxField = new DoubleInputSet("delx=",12); DoubleInputSet yminField = new DoubleInputSet("ymin=",12); DoubleInputSet ymaxField = new DoubleInputSet("ymax=",12); DoubleInputSet delyField = new DoubleInputSet("dely=",12); DoubleInputSet zminField = new DoubleInputSet("zmin=",12); DoubleInputSet zmaxField = new DoubleInputSet("zmax=",12); DoubleInputSet delzField = new DoubleInputSet("delz=",12); JButton stdButton = new JButton("std"); JButton applyButton = new JButton("apply"); JButton okButton = new JButton("ok"); JButton cancelButton = new JButton("cancel"); Box b1, b2; ButtonList bb = new ButtonList(); KeyHandlerSequence kseq = new KeyHandlerSequence(); public WindowPanel3d () { this.getContentPane().setBackground(Color.white); b1 = new Box(BoxLayout.Y_AXIS); b1.add(xminField); b1.add(xmaxField); b1.add(delxField); b1.add(yminField); b1.add(ymaxField); b1.add(delyField); b1.add(zminField); b1.add(zmaxField); b1.add(delzField); b2 = new Box(BoxLayout.X_AXIS); b2.add(stdButton); stdButton.setOpaque(false); b2.add(applyButton); applyButton.setOpaque(false); b2.add(okButton); okButton.setOpaque(false); b2.add(cancelButton); cancelButton.setOpaque(false); b1.add(b2); this.getContentPane().add(b1); setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); } class StandardAction implements ActionWrapper { public void doThis() { setFieldsToDefaults(); applyValues(); } } class ApplyAction implements ActionWrapper { public void doThis() { applyValues(); } } class OkAction implements ActionWrapper { public void doThis() { applyValues(); dispose(); } } class CancelAction implements ActionWrapper { public void doThis() { dispose(); } } public void initwindow() { xminField.setName(AMathlet.getProperty("xmineq")); xmaxField.setName(AMathlet.getProperty("xmaxeq")); delxField.setName(AMathlet.getProperty("delxeq")); yminField.setName(AMathlet.getProperty("ymineq")); ymaxField.setName(AMathlet.getProperty("ymaxeq")); delyField.setName(AMathlet.getProperty("delyeq")); zminField.setName(AMathlet.getProperty("zmineq")); zmaxField.setName(AMathlet.getProperty("zmaxeq")); delzField.setName(AMathlet.getProperty("delzeq")); bb.registerButtonAction(stdButton, new StandardAction()); ApplyAction ap = new ApplyAction(); bb.registerButtonAction(applyButton, ap); bb.registerButtonAction(okButton, new OkAction()); bb.registerButtonAction(cancelButton, new CancelAction()); stdButton.setText(AMathlet.getProperty("std")); applyButton.setText(AMathlet.getProperty("apply")); okButton.setText(AMathlet.getProperty("ok")); cancelButton.setText(AMathlet.getProperty("cancel")); kseq.registerKeyHandler(xminField, ap); kseq.registerKeyHandler(xmaxField, ap); kseq.registerKeyHandler(delxField, ap); kseq.registerKeyHandler(yminField, ap); kseq.registerKeyHandler(ymaxField, ap); kseq.registerKeyHandler(delyField, ap); kseq.registerKeyHandler(zminField, ap); kseq.registerKeyHandler(zmaxField, ap); kseq.registerKeyHandler(delzField, ap); AMathlet.setHandler("xmin",xminField); AMathlet.setHandler("xmax",xmaxField); AMathlet.setHandler("delx",delxField); AMathlet.setHandler("ymin",yminField); AMathlet.setHandler("ymax",ymaxField); AMathlet.setHandler("dely",delyField); AMathlet.setHandler("zmin",yminField); AMathlet.setHandler("zmax",ymaxField); AMathlet.setHandler("delz",delyField); xminField.setDefault(c.xmin); xmaxField.setDefault(c.xmax); delxField.setDefault(c.delx); yminField.setDefault(c.ymin); ymaxField.setDefault(c.ymax); delyField.setDefault(c.dely); zminField.setDefault(c.zmin); zmaxField.setDefault(c.zmax); delzField.setDefault(c.delz); setFieldsToDefaults(); } public void setFieldsToDefaults() { xminField.setToDefault(); xmaxField.setToDefault(); delxField.setToDefault(); yminField.setToDefault(); ymaxField.setToDefault(); delyField.setToDefault(); zminField.setToDefault(); zmaxField.setToDefault(); delzField.setToDefault(); } public void applyValues() { // includes call to redraw(); c.setCoords(xminField.getValue(), xmaxField.getValue(), yminField.getValue(), ymaxField.getValue(), zminField.getValue(), zmaxField.getValue()); c.setDeltas(delxField.getValue(), delyField.getValue(), delzField.getValue()); redraw(); } } // end WindowPanel3d, inner class of AGraphPanel3d protected class ViewPanel3d extends JFrame { MLabel viewLabel = new MLabel("view angles:"); DoubleInputSet aView = new DoubleInputSet("a=",12); DoubleInputSet bView = new DoubleInputSet("b=",12); JButton vptButton = new JButton("enter view point"); MLabel gridLabel = new MLabel("grid spacing:"); DoubleInputSet xGrid = new DoubleInputSet("xgrid=",12); DoubleInputSet yGrid = new DoubleInputSet("ygrid=",12); JButton stdButton = new JButton("std"); JButton applyButton = new JButton("apply"); JButton okButton = new JButton("ok"); JButton cancelButton = new JButton("cancel"); private String[] gridnames = {"xgrid", "ygrid"}; Box b1, b2; ButtonList bb = new ButtonList(); KeyHandlerSequence kseq = new KeyHandlerSequence(); public ViewPanel3d () { this.getContentPane().setBackground(Color.white); b1 = new Box(BoxLayout.Y_AXIS); b2 = new Box(BoxLayout.X_AXIS); b2.add(viewLabel); b1.add(b2); b1.add(aView); b1.add(bView); b2 = new Box(BoxLayout.X_AXIS); b2.add(Box.createHorizontalGlue()); b2.add(vptButton); b2.add(Box.createHorizontalGlue()); b1.add(b2); vptButton.setOpaque(false); // a few applets don't need the grid fields, so the private // field showGridFields provides a way to skip them here // to use, call AGraphPanel constructor as AGraphPanel(NO_GRID); // see constructor at beginning of this file, and also the localinit() method // note only needed in wires version though if (showGridFields) { b2 = new Box(BoxLayout.X_AXIS); b2.add(gridLabel); b1.add(b2); b1.add(xGrid); b1.add(yGrid); } b2 = new Box(BoxLayout.X_AXIS); b2.add(stdButton); stdButton.setOpaque(false); b2.add(applyButton); applyButton.setOpaque(false); b2.add(okButton); okButton.setOpaque(false); b2.add(cancelButton); cancelButton.setOpaque(false); b1.add(b2); this.getContentPane().add(b1); setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); } class ViewPtAction implements ActionWrapper { public void doThis() { vptDisplay.pack(); vptDisplay.show(); } } class StandardAction implements ActionWrapper { public void doThis() { setFieldsToDefaults(); applyValues(); } } class ApplyAction implements ActionWrapper { public void doThis() { applyValues(); } } class OkAction implements ActionWrapper { public void doThis() { applyValues(); dispose(); } } class CancelAction implements ActionWrapper { public void doThis() { dispose(); } } public void initwindow() { bb.registerButtonAction(vptButton, new ViewPtAction()); bb.registerButtonAction(stdButton, new StandardAction()); ApplyAction ap = new ApplyAction(); bb.registerButtonAction(applyButton, ap); bb.registerButtonAction(okButton, new OkAction()); bb.registerButtonAction(cancelButton, new CancelAction()); kseq.registerKeyHandler(aView, ap); kseq.registerKeyHandler(bView, ap); kseq.registerKeyHandler(xGrid, ap); kseq.registerKeyHandler(yGrid, ap); aView.setName(AMathlet.getProperty("aeq")); bView.setName(AMathlet.getProperty("beq")); xGrid.setName(AMathlet.getProperty(gridnames[0]+"eq")); yGrid.setName(AMathlet.getProperty(gridnames[1]+"eq")); vptButton.setText(AMathlet.getProperty("viewpt")); stdButton.setText(AMathlet.getProperty("std")); applyButton.setText(AMathlet.getProperty("apply")); okButton.setText(AMathlet.getProperty("ok")); cancelButton.setText(AMathlet.getProperty("cancel")); viewLabel.setText(AMathlet.getProperty("angles")); gridLabel.setText(AMathlet.getProperty("spacing")); AMathlet.setHandler("viewa",aView); AMathlet.setHandler("viewb",bView); AMathlet.setHandler(gridnames[0],xGrid); AMathlet.setHandler(gridnames[1],yGrid); Point2D.Double p = c.getViewAngles(); aView.setDefault(p.x); bView.setDefault(p.y); p = canvas.getGridValues(); xGrid.setDefault(p.x); yGrid.setDefault(p.y); setFieldsToDefaults(); vptDisplay.initwindow(); } public void setGridLabels(String xstr, String ystr) { // note in initwindow how property name is constructed, and how handler name is used // to get labels right, this must be called *before* initwindow, pref. at time of constructor gridnames[0] = xstr; gridnames[1] = ystr; } public void setGridDefaults(double newxgrid, double newygrid) { xGrid.setDefault(newxgrid); yGrid.setDefault(newygrid); xGrid.setToDefault(); yGrid.setToDefault(); } protected ViewPtPanel3d vptDisplay = new ViewPtPanel3d(); private void setFieldsToDefaults() { aView.setToDefault(); bView.setToDefault(); xGrid.setToDefault(); yGrid.setToDefault(); } public void setViewAngles(double anew, double bnew) { aView.setValue(anew); bView.setValue(bnew); // these values sent *immediately* to CoordsConverter3D c.setViewAngles(aView.getValue(),bView.getValue()); } public Point2D.Double getViewAngles() { return new Point2D.Double(aView.getValue(), bView.getValue()); } public void applyValues() { // includes call to redraw() c.setViewAngles(aView.getValue(),bView.getValue()); canvas.setGridValues(xGrid.getValue(),yGrid.getValue()); redraw(); } protected class ViewPtPanel3d extends JFrame { DoubleInputSet xViewPt = new DoubleInputSet("x=",12); DoubleInputSet yViewPt = new DoubleInputSet("y=",12); DoubleInputSet zViewPt = new DoubleInputSet("z=",12); JButton okButton = new JButton("ok"); JButton cancelButton = new JButton("cancel"); Box b1, b2; ButtonList bb = new ButtonList(); KeyHandlerSequence kseq = new KeyHandlerSequence(); public ViewPtPanel3d () { this.getContentPane().setBackground(Color.white); b1 = new Box(BoxLayout.Y_AXIS); b1.add(xViewPt); b1.add(yViewPt); b1.add(zViewPt); b2 = new Box(BoxLayout.X_AXIS); b2.add(okButton); okButton.setOpaque(false); b2.add(cancelButton); cancelButton.setOpaque(false); b1.add(b2); this.getContentPane().add(b1); setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); } private void setAngles() { double x = xViewPt.getValue(); double y = yViewPt.getValue(); double z = zViewPt.getValue(); double d = Math.sqrt(x*x+y*y+z*z); double a = Math.atan(y/x); if (x<0) a += Math.PI/2; double b = Math.acos(z/d); aView.setValue(180*a/Math.PI); bView.setValue(180*b/Math.PI); } class OkAction implements ActionWrapper { public void doThis() { setAngles(); applyValues(); dispose(); } } class CancelAction implements ActionWrapper { public void doThis() { dispose(); } } class KeyAction implements ActionWrapper { public void doThis() { setAngles(); applyValues(); } } public void initwindow() { bb.registerButtonAction(okButton, new OkAction()); bb.registerButtonAction(cancelButton, new CancelAction()); KeyAction kp = new KeyAction(); kseq.registerKeyHandler(xViewPt, kp); kseq.registerKeyHandler(yViewPt, kp); kseq.registerKeyHandler(zViewPt, kp); xViewPt.setName(AMathlet.getProperty("xeq")); yViewPt.setName(AMathlet.getProperty("yeq")); zViewPt.setName(AMathlet.getProperty("zeq")); okButton.setText(AMathlet.getProperty("ok")); cancelButton.setText(AMathlet.getProperty("cancel")); } private void initValues() { xViewPt.setInputText(Double.toString(c.ca*c.sb)); yViewPt.setInputText(Double.toString(c.sa*c.sb)); zViewPt.setInputText(Double.toString(c.cb)); } public void show() { initValues(); super.show(); } // overrides } // end ViewPtPanel3d, inner class of ViewPanel3d } // end ViewPanel3d, inner class of AGraphPanel3d }