====================================GeometryUtil.java====================================
importjava.awt.geom.Point2D;
publicclassGeometryUtil
{
//
两点之间的距离
publicstaticdoubledistanceOfPoints(Point2D
p1, Point2D p2) {
doubledisX
= p2.getX() - p1.getX();
doubledisY
= p2.getY() - p1.getY();
doubledis
= Math.sqrt(disX * disX + disY * disY);
returndis;
}
//
两点的中点
publicstaticPoint2D
middlePoint(Point2D p1, Point2D p2) {
doublex
= (p1.getX() + p2.getX()) / 2;
doubley
= (p1.getY() + p2.getY()) / 2;
returnnewPoint2D.Double(x,
y);
}
//
在两点所在直线上,以从startPoint到endPoint为方向,离startPoint的距离disToStartPoint的点
publicstaticPoint2D
extentPoint(Point2D startPoint, Point2D endPoint,doubledisToStartPoint)
{
doubledisX
= endPoint.getX() - startPoint.getX();
doubledisY
= endPoint.getY() - startPoint.getY();
doubledis
= Math.sqrt(disX * disX + disY * disY);
doublesin
= (endPoint.getY() - startPoint.getY()) / dis;
doublecos
= (endPoint.getX() - startPoint.getX()) / dis;
doubledeltaX
= disToStartPoint * cos;
doubledeltaY
= disToStartPoint * sin;
returnnewPoint2D.Double(startPoint.getX()
+ deltaX, startPoint.getY() + deltaY);
}
}
====================================Pie.java====================================
importjava.awt.Color;
importjava.awt.geom.Arc2D;
importjava.awt.geom.Area;
importjava.awt.geom.GeneralPath;
importjava.awt.geom.Point2D;
classPie
{
privateArc2Darc;//
这里的弧并不是圆上的一弧,而是椭圆的一部分.
privateAreafrontSite;
privateArealeftSite;
privateArearightSite;
privateColorcolor;
privatePieselectedPie;
privatePoint2DarcMiddle;
privatePoint2DlabelPosition;
privatedoublevalue;
privateintshadowDepth;
privatedoubleselectedShiftDis;//
被选中的饼图在他的中线上移动的距离
publicPie(Arc2D
arc, Color color,doublevalue)
{
this(arc,
color, value, 10, 30);
}
publicPie(Arc2D
arc, Color color,doublevalue,intshadowDepth,doubleselectedShiftDis)
{
this.arc=
arc;
this.color=
color;
this.value=
value;
this.selectedShiftDis=
selectedShiftDis;
this.shadowDepth=
shadowDepth;
Arc2D arcBottom =newArc2D.Double(arc.getX(),
arc.getY() + shadowDepth, arc.getWidth(),
arc.getHeight() + 0, arc.getAngleStart(), arc.getAngleExtent(),
Arc2D.CHORD);
Point2D[] topPs = getPointsOfArc(arc);
Point2D[] bottomPs =
getPointsOfArc(arcBottom);
//
Front site
GeneralPath font =newGeneralPath();
font.moveTo(topPs[1].getX(),
topPs[1].getY());
font.lineTo(topPs[2].getX(),
topPs[2].getY());
font.lineTo(bottomPs[2].getX(),
bottomPs[2].getY());
font.lineTo(bottomPs[1].getX(),
bottomPs[1].getY());
font.closePath();
this.frontSite=newArea(arcBottom);
this.frontSite.add(newArea(font));
//
Left site
GeneralPath left =newGeneralPath();
left.moveTo(topPs[0].getX(),
topPs[0].getY());
left.lineTo(topPs[1].getX(),
topPs[1].getY());
left.lineTo(bottomPs[1].getX(),
bottomPs[1].getY());
left.lineTo(topPs[0].getX(), topPs[0].getY() +
3);
left.closePath();
this.leftSite=newArea(left);
//
Right site
GeneralPath right =newGeneralPath();
right.moveTo(topPs[0].getX(),
topPs[0].getY());
right.lineTo(topPs[2].getX(),
topPs[2].getY());
right.lineTo(bottomPs[2].getX(),
bottomPs[2].getY());
right.lineTo(topPs[0].getX(), topPs[0].getY() +
3);
right.closePath();
this.rightSite=newArea(right);
arcMiddle=
calculateArcMiddle();
//
Label position: 五分之四处
Point2D c = getPieCenter();
//
Point2D m = getChordMiddle();
Point2D m =arcMiddle;
doubledis
= GeometryUtil.distanceOfPoints(c, m) * 0.8;
labelPosition=
GeometryUtil.extentPoint(c, m, dis);
}
//
取得Arc上的三个点,在对Arc: center, left, right.
publicstaticPoint2D[]
getPointsOfArc(Arc2D arc) {
Point2D center =newPoint2D.Double(arc.getCenterX(),
arc.getCenterY());
Point2D left = arc.getStartPoint();
Point2D right = arc.getEndPoint();
Point2D[] points =newPoint2D[]
{ center, left, right };
returnpoints;
}
publicPie
getSelectedPie() {
if(selectedPie==null)
{
selectedPie=
createSeletecdPie();
}
returnselectedPie;
}
privatePie
createSeletecdPie() {
//
沿中线方向移动selectedShiftDis个单位
Point2D c = getPieCenter();
Point2D m = getChordMiddle();
Point2D p = GeometryUtil.extentPoint(c,
m,selectedShiftDis);
doubledeltaX
= p.getX() - c.getX();
doubledeltaY
= p.getY() - c.getY();
doublex
=arc.getX()
+ deltaX;
doubley
=arc.getY()
+ deltaY;
Arc2D shiftArc = (Arc2D)arc.clone();
shiftArc.setFrame(x, y,arc.getWidth(),arc.getHeight());
returnnewPie(shiftArc,color,value,shadowDepth,selectedShiftDis);
}
publicArc2D
getArc() {
returnarc;
}
publicArea
getFrontSite() {
returnfrontSite;
}
publicArea
getLeftSite() {
returnleftSite;
}
publicArea
getRightSite() {
returnrightSite;
}
publicColor
getColor() {
returncolor;
}
publicvoidsetColor(Color
color) {
this.color=
color;
}
publicPoint2D
getLabelPosition() {
returnlabelPosition;
}
publicvoidsetLabelPosition(Point2D
labelPosition) {
this.labelPosition=
labelPosition;
}
publicdoublegetValue()
{
returnvalue;
}
publicString
getLabel() {
returnvalue+"%";
}
//
弦的中心点
publicPoint2D
getChordMiddle() {
returnGeometryUtil.middlePoint(arc.getStartPoint(),arc.getEndPoint());
}
//
饼图的圆心
publicPoint2D
getPieCenter() {
returnnewPoint2D.Double(arc.getCenterX(),arc.getCenterY());
}
//
弧上的中心点
publicPoint2D
getArcMiddle() {
returnarcMiddle;
}
privatePoint2D
calculateArcMiddle() {
//
创建一个新的弧,其扩展角度为当前弧的一半
returnnewArc2D.Double(arc.getX(),arc.getY(),arc.getWidth(),arc.getHeight(),
arc.getAngleStart(),arc.getAngleExtent()
/ 2, Arc2D.PIE).getEndPoint();
}
}
====================================Pie3DPainter.java====================================
importjava.awt.Color;
importjava.awt.FontMetrics;
importjava.awt.Graphics;
importjava.awt.Graphics2D;
importjava.awt.RenderingHints;
importjava.awt.Toolkit;
importjava.awt.event.MouseAdapter;
importjava.awt.event.MouseEvent;
importjava.awt.geom.Arc2D;
importjava.util.ArrayList;
importjava.util.List;
importjavax.swing.JFrame;
importjavax.swing.JPanel;
@SuppressWarnings("serial")
publicclassPie3DPainterextendsJPanel
{
privatedouble[]data;//
在饼图中显示的数据
privateColor[]defaultColors;//
预定义饼图的颜色
privatePie[]pies;
privateintshadowDepth=
8;
privateintshiftAngle=
-30;
privateintselectedPieIndex=
-1;//
鼠标点击是选中的Arc, -1为没有选中
publicPie3DPainter()
{
data=newdouble[]
{ 20.72, 6.56, 3.74, 10.26, 15.38, 5.69, 10.72, 15.38, 6.15, 18.0
};
defaultColors=
createColors();
intx
= 50;
inty
= 50;
intw
= 380;
inth
= (int)
(w * 0.618);//
黄金分割
pies=
createPies(x, y, w, h,shadowDepth,shiftAngle,data,defaultColors);
//
取得鼠标选中的饼图的下标
addMouseListener(newMouseAdapter()
{
@Override
publicvoidmouseClicked(MouseEvent
e) {
selectedPieIndex=
-1;
for(inti
= 0; i <
class=Apple-converted-space> pies.length;
++i) {
if(pies[i].getArc().contains(e.getX(),
e.getY())) {
selectedPieIndex=
i;
break;
}
}
repaint();
}
});
}
privateColor[]
createColors() {
//
返回16进制的值颜色
List colors =newArrayList();
colors.add(Color.decode("#FF7321"));
colors.add(Color.decode("#169800"));
colors.add(Color.decode("#00E500"));
colors.add(Color.decode("#D0F15A"));
colors.add(Color.decode("#AA6A2D"));
colors.add(Color.decode("#BFDD89"));
colors.add(Color.decode("#E2FF55"));
colors.add(Color.decode("#D718A5"));
colors.add(Color.decode("#00DBFF"));
colors.add(Color.decode("#00FF00"));
returncolors.toArray(newColor[0]);
}
publicstaticPie[]
createPies(intx,
inty,
intw,
inth,
intshadowDepth,
doubleshiftAngle,
double[]
data,
Color[]
colors) {
doublesum
= 0;
for(doubled
: data) {
sum +=
d;
}
//
初始化Pies
doublearcAngle
= 0;
doublestartAngle
= shiftAngle;
Pie[] pies =newPie[data.length];
for(inti
= 0; i < data.length;
++i) {
arcAngle =
data[i] * 360 / sum;//
使用百分比计算角度
if(i
+ 1 == data.length)
{
arcAngle = 360 + shiftAngle -
startAngle;//
保证闭合
arcAngle = arcAngle > 0 ?
arcAngle : 0;
}
Arc2D.Double arc =newArc2D.Double(x,
y, w, h, startAngle, arcAngle, Arc2D.PIE);
pies[i]
=newPie(arc,
colors[i % colors.length],
data[i], shadowDepth, 30);
startAngle
+= arcAngle;
}
returnpies;
}
@Override
protectedvoidpaintComponent(Graphics
g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if(pies!=null)
{
drawPies(g2d,pies,selectedPieIndex);
}
}
privatevoiddrawPies(Graphics2D
g2d, Pie[] pies,intselectedIndex)
{
intstartIndex
= 0;//
从第几个饼图开始绘制
intendIndex
= pies.length;//
要画的饼图的数量.
booleanclosed
= (endIndex - startIndex == pies.length)
?true:false;
booleanselected
= (selectedIndex >= startIndex && selectedIndex <
endIndex) ?true:false;
FontMetrics metrics = g2d.getFontMetrics();
//
一次性绘制完3D效果,然后再绘制饼图的效果比绘制饼图的同时绘制好
for(inti
= startIndex; i < endIndex; ++i) {
if(i
!= selectedIndex) {
Pie p = pies[i];
g2d.setColor(p.getColor().darker());
g2d.fill(p.getFrontSite());
}
}
//
如果没有闭合时,且选中的不是第一块,则第一块画左面
if(!closed
&& selectedIndex != startIndex) {
g2d.setColor(pies[startIndex].getColor().darker());
g2d.fill(pies[startIndex].getLeftSite());
}
//
如果没有闭合时,且选中的不是最后一块,则最后一块画右面
if(!closed
&& selectedIndex + 1 != endIndex) {
g2d.setColor(pies[endIndex - 1].getColor().darker());
g2d.fill(pies[endIndex - 1].getRightSite());
}
//
有饼图被选中
if(selected)
{
intprevIndex
= selectedIndex > startIndex ? (selectedIndex - 1) : endIndex -
1;
intnextIndex
= (selectedIndex + 1) >= endIndex ? startIndex : (selectedIndex
+ 1);
//
前一个画右墙
g2d.setColor(pies[prevIndex].getColor().darker());
g2d.fill(pies[prevIndex].getRightSite());
//
后一个画左墙
g2d.setColor(pies[nextIndex].getColor().darker());
g2d.fill(pies[nextIndex].getLeftSite());
}
//
最后再绘制饼图的上面部分,把不需要的部分隐藏掉
for(inti
= startIndex; i < endIndex; ++i) {
if(i
!= selectedIndex) {
Pie p = pies[i];
g2d.setColor(p.getColor());
g2d.fill(p.getArc());
intsw
= metrics.stringWidth(p.getLabel()) / 2;
intsh
= (metrics.getAscent()) / 2;
intx
= (int)
(p.getLabelPosition().getX() - sw);
inty
= (int)
(p.getLabelPosition().getY() + sh);
g2d.setColor(Color.BLACK);
g2d.drawString(p.getLabel(),
x, y);
}
}
//
绘制被选中的饼图
if(selected)
{
Pie p =
pies[selectedIndex].getSelectedPie();
g2d.setColor(p.getColor().darker());
g2d.fill(p.getFrontSite());
g2d.fill(p.getLeftSite());
g2d.fill(p.getRightSite());
g2d.setColor(p.getColor());
g2d.fill(p.getArc());
intsw
= metrics.stringWidth(p.getLabel()) / 2;
intsh
= (metrics.getAscent()) / 2;
intx
= (int)
(p.getLabelPosition().getX() - sw);
inty
= (int)
(p.getLabelPosition().getY() + sh);
g2d.setColor(Color.BLACK);
g2d.drawString(p.getLabel(), x, y);
}
}
privatestaticvoidcreateGuiAndShow()
{
JFrame frame =newJFrame("Pie
with 3D Effect");
frame.getContentPane().add(newPie3DPainter());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
intsw
= Toolkit.getDefaultToolkit().getScreenSize().width;
intsh
= Toolkit.getDefaultToolkit().getScreenSize().height;
intw
= 500;
inth
= 400;
intx
= (sw - w) / 2;
inty
= (sh - h) / 2 - 40;
x = x > 0 ? x : 0;
y = y > 0 ? y : 0;
frame.setBounds(x, y, w, h);
frame.setVisible(true);
}
publicstaticvoidmain(String[]
args) {
createGuiAndShow();
}
}