/*
* This code is part of Project Waterloo from King's College London
*
*
* Copyright King's College London 2011-
*
* @author Malcolm Lidierth, King's College London [Contact]
*
* Project Waterloo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Project Waterloo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
*/
package kcl.waterloo.widget;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.Polygon;
import java.awt.RadialGradientPaint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import org.jdesktop.swingx.painter.effects.ShadowPathEffect;
/**
* A dial with a moving needle. For convenience this extends GJDial, which
* itself extends JSlider, as much of the code is used in common.
*
* The default setting provide a "vintage" look.
*
* @author ML
*/
public class GJGauge extends GJDial implements ComponentListener {
/**
* Set true to use internal buffering. True by default.
*/
protected boolean useBuffer = true;
/**
* Set true to show the glass cover effect. True by default.
*/
protected boolean glassShown = true;
/**
* Set true to draw shadow. True by default. Set false for speed.
*/
protected boolean shadowShown = true;
/**
* Sets the primary color used to generate the gradients
*/
protected Color primaryColor = new Color(47, 79, 79);
/**
* Color of the gauge needle
*/
protected Color needleColor = new Color(255, 140, 0);
protected double outerRimWidth = 10d;
protected double dialInset = 20d;
protected double dialWidth = 50d;
protected GradientPaint outerGradientPaint;
protected GradientPaint innerGradientPaint;
protected GradientPaint dialGradientPaint;
protected ShadowPathEffect shadow = new ShadowPathEffect();
protected Arc2D arc2, arc4;
protected BufferedImage buffer = null;
public GJGauge() {
setMinimum(0);
setMaximum(100);
setMajorTickSpacing(10);
setMinorTickSpacing(1);
removeMouseListener(this);
removeMouseMotionListener(this);
addComponentListener(this);
}
@Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
if (buffer == null) {
// If the buffer is null, fill it
if (useBuffer) {
buffer = g2.getDeviceConfiguration().createCompatibleImage(getWidth(), getHeight());
g2 = buffer.createGraphics();
g2.setClip(g.getClip());
}
if (isOpaque()) {
g2.setPaint(getBackground());
g2.fill(getBounds());
}
dialInset = getWidth() / 50d;
dialWidth = getWidth() / 10d;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Rectangle r = getBounds();
// Outer Rim
outerGradientPaint = new GradientPaint(getWidth() / 2,
0,
primaryColor.darker(),
getWidth() / 2, getHeight(),
Color.WHITE,
true);
Arc2D arc0 = new Arc2D.Double(r.getX() + 1, r.getY() + 1, r.getWidth() - 2, r.getHeight() - 2, 0d, 360d, Arc2D.CHORD);
Arc2D arc1 = new Arc2D.Double(r.getX() + outerRimWidth / 2d,
r.getY() + outerRimWidth / 2d,
r.getWidth() - outerRimWidth,
r.getHeight() - outerRimWidth,
0d, 360d, Arc2D.CHORD);
Area area = new Area(arc0);
area.subtract(new Area(arc1));
g2.setPaint(outerGradientPaint);
g2.fill(area);
innerGradientPaint = new GradientPaint((int) arc1.getCenterX(), (int) arc1.getY(),
primaryColor, (int) arc1.getCenterX(), (int) arc1.getHeight(), Color.WHITE, true);
g2.setPaint(innerGradientPaint);
g2.fill(arc1);
// Dial
arc2 = new Arc2D.Double(arc1.getX() + dialInset,
arc1.getY() + dialInset,
arc1.getWidth() - 2 * dialInset,
arc1.getHeight() - 2 * dialInset,
0d, 360d, Arc2D.CHORD);
Arc2D arc3 = new Arc2D.Double(arc2.getX() + dialWidth + dialInset,
arc2.getY() + dialWidth + dialInset,
arc2.getWidth() - 2 * dialWidth - 2 * dialInset,
arc2.getHeight() - 2 * dialWidth - 2 * dialInset,
0d, 360d, Arc2D.CHORD);
dialGradientPaint = new GradientPaint((int) arc2.getX(), (int) arc2.getY(),
primaryColor.brighter(), (int) arc2.getWidth(), (int) arc2.getHeight(), Color.WHITE, true);
Area area1 = new Area(arc2);
area1.subtract(new Area(arc3));
g2.setPaint(dialGradientPaint);
g2.fill(area1);
g2.setPaint(Color.BLACK);
g2.draw(arc0);
g2.draw(arc1);
g2.draw(arc2);
// Paint ticks
g2.setPaint(getForeground());
double maxR = Math.max(arc0.getWidth(), arc0.getHeight()) / 2d;
arc4 = new Arc2D.Double(arc2.getX() + ((dialWidth + dialInset) / 2d),
arc2.getY() + ((dialWidth + dialInset) / 2d),
arc2.getWidth() - dialWidth - dialInset,
arc2.getHeight() - dialWidth - dialInset,
0d, 360d, Arc2D.CHORD);
Area area2 = new Area(arc2);
area2.subtract(new Area(arc4));
g2.setStroke(new BasicStroke(1.5f));
Area clip = new Area(g.getClip());
area2.intersect(clip);
g2.setClip(area2);
for (double theta = -Math.PI / 2d; theta < 1.5 * Math.PI; theta += 2d * Math.PI / ((getMaximum() - getMinimum()) / getMajorTickSpacing())) {
double x0, y0, x1, y1;
x0 = arc2.getCenterX();
y0 = arc2.getCenterY();
x1 = arc2.getCenterX() + Math.cos(theta) * maxR;
y1 = arc2.getCenterY() + Math.sin(theta) * maxR;
g2.drawLine((int) x0, (int) y0, (int) x1, (int) y1);
}
Arc2D arc5 = new Arc2D.Double(arc2.getX() + ((dialWidth + dialInset) / 4d),
arc2.getY() + ((dialWidth + dialInset) / 4d),
arc2.getWidth() - ((dialWidth + dialInset) / 2d),
arc2.getHeight() - ((dialWidth + dialInset) / 2d),
0d, 360d, Arc2D.CHORD);
Area area3 = new Area(arc2);
area3.subtract(new Area(arc5));
g2.setStroke(new BasicStroke(1.2f));
g2.draw(arc5);
area3.intersect(clip);
g2.setClip(area3);
for (double theta = -Math.PI / 2d; theta < 1.5 * Math.PI; theta += 2d * Math.PI / ((getMaximum() - getMinimum()) / getMinorTickSpacing())) {
double x0, y0, x1, y1;
x0 = arc2.getCenterX();
y0 = arc2.getCenterY();
x1 = arc2.getCenterX() + (Math.cos(theta) * maxR);
y1 = arc2.getCenterY() + (Math.sin(theta) * maxR);
g2.drawLine((int) x0, (int) y0, (int) x1, (int) y1);
}
g2.setClip(clip);
double label = 0;
FontMetrics metrics = g2.getFontMetrics();
// Code is based on the JavaGeom library available at {@link http://geom-java.sourceforge.net/index.html}
for (double theta = -Math.PI / 2d; theta < 1.5 * Math.PI; theta += 2d * Math.PI / ((getMaximum() - getMinimum()) / getMajorTickSpacing())) {
double x1 = arc2.getCenterX();
double y1 = arc2.getCenterY();
double x2 = arc2.getCenterX() + (Math.cos(theta) * arc4.getHeight());
double y2 = arc2.getCenterY() + (Math.sin(theta) * arc4.getWidth());
double horzAngle = (Math.atan2(y2 - y1, x2 - x1) + Math.PI * 2) % (Math.PI * 2);
double xc = arc4.getCenterX();
double yc = arc4.getCenterY();
double r1 = arc4.getWidth() / 2d - arc4.getWidth() / 30d;
double r2 = arc4.getHeight() / 2d - arc4.getHeight() / 30d;
double cot = Math.cos(0);
double sit = Math.sin(0);
double x = xc + r1 * Math.cos(horzAngle) * cot - r2 * Math.sin(horzAngle) * sit;
double y = yc + r1 * Math.cos(horzAngle) * sit + r2 * Math.sin(horzAngle) * cot;
String s = String.format("%2.0f", label);
g2.drawString(s, (float) (x - metrics.stringWidth(s) / 2f), (float) y);
label += getMajorTickSpacing();
}
g2.dispose();
g2 = (Graphics2D) g.create();
}
// Paint the buffer if required then superimpose needle and glass cover
if (useBuffer && buffer != null) {
g2.drawImage(buffer, null, 0, 0);
}
//Needle
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Arc2D arc6 = new Arc2D.Double(arc2.getCenterX() - arc2.getWidth() / 40d,
arc2.getCenterY() - arc2.getWidth() / 40d,
arc2.getWidth() / 20d,
arc2.getWidth() / 20d,
0d, 360d, Arc2D.CHORD);
Area area4 = new Area(arc6);
Rectangle2D r1 = new Rectangle2D.Double(arc6.getCenterX() - arc6.getWidth() / 4d,
arc6.getCenterY() - arc4.getHeight() / 2d + getHeight() / 20d,
arc6.getWidth() / 2d,
arc4.getHeight() / 2d - getHeight() / 20d);
area4.add(new Area(r1));
int[] tx = new int[]{(int) r1.getX(),
(int) r1.getCenterX(),
(int) (r1.getX() + r1.getWidth())};
int[] ty = new int[]{(int) r1.getY() + 1,
(int) r1.getY() - 10,
(int) r1.getY() + 1};
area4.add(new Area(new Polygon(tx, ty, 3)));
AffineTransform af = AffineTransform.getRotateInstance(-getTheta(), arc2.getCenterX(), arc2.getCenterY());
area4 = area4.createTransformedArea(af);
GradientPaint needlePaint = new GradientPaint((int) area4.getBounds().getCenterX(),
area4.getBounds().y, needleColor,
(int) area4.getBounds().getCenterX() + area4.getBounds().width / 2,
area4.getBounds().y + area4.getBounds().height, needleColor.brighter(), true);
g2.setPaint(needlePaint);
g2.fill(area4);
if (shadowShown) {
shadow.apply(g2, area4, 20, 20);
}
if (glassShown) {
Arc2D arc7 = new Arc2D.Double(getWidth() / 2d - getWidth() / 4d,
getHeight() / 4d,
getWidth() / 2d,
getHeight() / 2d,
5d, 170d, Arc2D.CHORD);
float radius = (float) getWidth() / 4f;
Point2D center = new Point2D.Float((float) arc7.getCenterX(), (float) getHeight() / 2f);
//Point2D focus = center;
float[] dist = {0.0f, 1f};
Color[] colors = {new Color(1f, 1f, 1f, 0f), new Color(1f, 1f, 1f, 0.4f)};
RadialGradientPaint glass
= new RadialGradientPaint(center, radius, center,
dist, colors,
CycleMethod.NO_CYCLE);
// GradientPaint glass = new GradientPaint((float) arc7.getCenterX(),
// (float) arc7.getY(), new Color(1f, 1f, 1f, 0.8f),
// (float) (arc7.getCenterX()),
// (float) arc7.getCenterY(),
// new Color(1f, 1f, 1f, 0.0f));
//g2.setClip(new Rectangle.Double(getX(), getY(), getX() + getWidth(), getY() + getHeight() / 2));
g2.setPaint(glass);
g2.setClip(new Rectangle2D.Double(0, 0, getWidth(), getHeight() / 2d));
g2.fill(arc7);
}
g2.dispose();
}
/**
* @return the outerRimWidth
*/
public double getOuterRimWidth() {
return outerRimWidth;
}
/**
* @param outerRimWidth the outerRimWidth to set
*/
public void setOuterRimWidth(double outerRimWidth) {
this.outerRimWidth = outerRimWidth;
}
/**
* @return the dialInset
*/
public double getDialInset() {
return dialInset;
}
/**
* @param dialInset the dialInset to set
*/
public void setDialInset(double dialInset) {
this.dialInset = dialInset;
}
/**
* @return the primaryColor
*/
public Color getPrimaryColor() {
return primaryColor;
}
/**
* @param primaryColor the primaryColor to set
*/
public void setPrimaryColor(Color primaryColor) {
this.primaryColor = primaryColor;
}
/**
* @return the needleColor
*/
public Color getNeedleColor() {
return needleColor;
}
/**
* @param needleColor the needleColor to set
*/
public void setNeedleColor(Color needleColor) {
this.needleColor = needleColor;
}
/**
* @return the glassShown
*/
public boolean isGlassShown() {
return glassShown;
}
/**
* @return the shadowShown
*/
public boolean isShadowShown() {
return shadowShown;
}
/**
* @param shadowShown the shadowShown to set
*/
public void setShadowShown(boolean shadowShown) {
this.shadowShown = shadowShown;
}
/**
* @param glassShown the glassShown to set
*/
public void setGlassShown(boolean glassShown) {
this.glassShown = glassShown;
}
@Override
public void componentResized(ComponentEvent e) {
buffer = null;
}
@Override
public void componentMoved(ComponentEvent e) {
}
@Override
public void componentShown(ComponentEvent e) {
}
@Override
public void componentHidden(ComponentEvent e) {
}
/**
* @return the useBuffer
*/
public boolean isUseBuffer() {
return useBuffer;
}
/**
* @param useBuffer the useBuffer to set
*/
public void setUseBuffer(boolean useBuffer) {
this.useBuffer = useBuffer;
}
}
Ads help maintain this website.