import java.awt.*; import java.awt.image.*; import utilities.*; import utilities.gfx.*; public class Dial extends Canvas { protected Circle circle; // we use Circles to render part of a Dial protected int radius, diameter; protected int dialWidth, dialHeight; protected int divisions; protected double heading; protected boolean headingVisible; protected boolean fixedFace; // true= heading moves in relation to face protected Image dialImage; // off-screen image protected Graphics gg; // handle to off-screen Graphics context //------------------------------------------------------------------- // Dial Constructor //------------------------------------------------------------------- // default dial, diameter = 50 pixels public Dial() { this(50); } // default dial, 8 divisions, heading 0 deg, heading not visible public Dial(int diameter) { this(diameter, 8, 0.0, false); } // heading specified -> heading visible public Dial(int diameter, int divisions, double heading) { this(diameter, divisions, heading, true); } public Dial(int diameter, int divisions, double heading, boolean visHeading) { this.diameter = diameter; this.divisions = divisions; this.heading = heading; this.headingVisible = visHeading; this.fixedFace = true; radius = diameter/2; dialWidth = diameter; dialHeight= diameter + extraHeight(); circle = new Circle(radius, radius, radius); circle.useDegrees(true); } //------------------------------------------------------------------- // set()/get() for 'Heading' property //------------------------------------------------------------------- public void setHeading(double value) { this.heading = value; reflectChanges(); } public double getHeading() { return heading; } //------------------------------------------------------------------- // set()/get() for 'Divisions' property //------------------------------------------------------------------- public void setDivisions(int value) { this.divisions = value; reflectChanges(); } public int getDivisions() { return divisions; } //------------------------------------------------------------------- // set()/get() for 'HeadingVisible' property //------------------------------------------------------------------- public void setHeadingVisible(boolean value) { this.headingVisible = value; reflectChanges(); } public boolean getHeadingVisible() { return headingVisible; } public void showHeading() { setHeadingVisible(true); } public void hideHeading() { setHeadingVisible(false); } //------------------------------------------------------------------- // set()/get() for 'FixedFace' property //------------------------------------------------------------------- public void setFixedFace(boolean value) { this.fixedFace = value; } public boolean getFixedFace() { return fixedFace; } //------------------------------------------------------------------- // Some visible dial property changed. Reflect the change graphically //------------------------------------------------------------------- protected void reflectChanges() { renderDial(); repaint(); } //------------------------------------------------------------------- // extraHeight lets subclasses reserve some extra height to render // a dial label below the dial face. //------------------------------------------------------------------- protected int extraHeight() { return 0; // default extra space = none. } //------------------------------------------------------------------- // Render dial. //------------------------------------------------------------------- public void renderDial() { if ( dialImage == null ) { return; } GfxKit.wipeImage(dialImage, getBackground(), this); circle.draw(gg); circle.setRadialSize(0.90, 0.97); // percentage of radius if ( fixedFace ) { circle.drawRadials(gg, divisions); } else { circle.drawRadials(gg, divisions, heading, heading + 360.0); } if ( headingVisible ) { renderHeading(); } renderCustomFeatures(gg); } //------------------------------------------------------------------- // Render any extra gadgets or embellishments to the dial. // Another method for subclasses to override. //------------------------------------------------------------------- protected void renderCustomFeatures(Graphics g) { // empty, subclasses need to override } //------------------------------------------------------------------- // Render heading as a small triangle pointing outwards //------------------------------------------------------------------- protected void renderHeading() { circle.setRadialSize(0.60, 0.85); circle.drawArrow(gg, heading, 16.0); // n degree "wide" arrow base } //------------------------------------------------------------------- // Initialize the off-screen dial image. //------------------------------------------------------------------- protected void allocImage() { dialImage = createImage(dialWidth, dialHeight); gg = dialImage.getGraphics(); } //------------------------------------------------------------------- // Overridden update()/paint() //------------------------------------------------------------------- public void update(Graphics g) { paint(g); } public void paint(Graphics g) { if ( dialImage == null ) { allocImage(); renderDial(); } g.drawImage(dialImage, 0,0, this); } //------------------------------------------------------------------- // Overridden setSize() //------------------------------------------------------------------- public Dimension preferredSize() { return new Dimension(dialWidth, dialHeight); } public Dimension getPreferredSize() { return preferredSize(); } //------------------------------------------------------------------- // Dial self-test //------------------------------------------------------------------- public static void main (String[] args) { Frame window = new Frame("Dial Test"); Dial aDial = new Dial(); Dial bDial = new Dial(80); Dial cDial = new Dial(150); window.setLayout(new FlowLayout()); window.add(aDial); window.add(bDial); window.add(cDial); window.pack(); window.setVisible(true); MiscKit.delay(2000); aDial.showHeading(); bDial.showHeading(); cDial.showHeading(); MiscKit.delay(2000); for (int i=0; i < 10; i++) { cDial.hideHeading(); MiscKit.delay(200); cDial.showHeading(); MiscKit.delay(200); } while ( true ) { for (int i=0; i < 90; i++) { aDial.setHeading((double) i); bDial.setHeading((double) i*2); cDial.setHeading((double) i*4); MiscKit.delay(100); cDial.setFixedFace( i > 50 ); } } } } // End of Class Dial