在PieChart切片中间用三角形绘制PieChart

时间:2016-01-11 07:59:36

标签: java swing draw pie-chart paintcomponent

我想在饼图切片的中间绘制一个带有三角形的饼图。 目前,我在切片中间绘制了切片和三角形的piechat,但三角形不是直角。我需要知道如何以正确的方式定位三角形。我的代码和结果:

import java.awt.*;
import java.awt.geom.Ellipse2D;
import javax.swing.*;

class Slice {

   double value;
   Color color;
   public Slice(double value, Color color) {  
      this.value = value;
      this.color = color;
   }
}

class PieChart extends JPanel {

    private Color a = Color.RED;
    private Color b = Color.BLUE;
    private Color c = Color.YELLOW;
    Slice[] slices = { 
               new Slice(60, a),
               new Slice(100, b),
               new Slice(200, c)
    };

    public PieChart(){

    }

    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D)g;
        super.paintComponent(g2d);
        this.setBackground(new Color(255,255,255));

        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        double total = 0.0D;
        for (int i = 0; i < slices.length; i++) {
            total += slices[i].value;
        }

        double curValue = 90.0D;
        int startAngle = 0;

        for (int i = 0; i < slices.length; i++) {
            startAngle = (int) (curValue * 360 / total);
            int arcAngle = (int) (slices[i].value * 360 / total);
            g2d.setColor(slices[i].color);
            g2d.fillArc(20, 20, 200, 200, startAngle, arcAngle);

            g2d.setPaint(Color.BLACK);
            int x = (int)(110+100*Math.cos(((-(startAngle+(arcAngle/2)))*Math.PI)/180));
            int y = (int)(110+100*Math.sin(((-(startAngle+(arcAngle/2)))*Math.PI)/180));

            Polygon p = new Polygon(new int[] {x, x+14, x+7}, new int[] {y, y, y-14}, 3); // this values seems to be important
            g2d.draw(p);
            g2d.fill(p);

            curValue += slices[i].value;
        }
    }
}

enter image description here

编辑:应该如下所示:

enter image description here

2 个答案:

答案 0 :(得分:2)

我从0点开始做第一个弧(我想你的意思是这样做)。

由于您使用的fillArc需要int秒,因此向下舍入的double可能不会达到全部数量,并且切片之间会有间隙:

enter image description here

相反,使用Arc2D.Double来获得更好的精确度:

enter image description here

class Slice {

    double value;
    Color color;

    public Slice(double value, Color color) {

        this.value = value;
        this.color = color;
    }

    public Color getColor() {

        return color;
    }

    public double getValue() {

        return value;
    }
}

class PieChart extends JPanel {

    private final int SIZE = 500, START = 40, START_DEG = 90;
    private final int TRIG_HBASE = 66, TRIG_HEIGHT = 36;
    private final int x0 =(START + SIZE / 2), y0 = START;
    private final Polygon poly;

    private Color a = Color.RED;
    private Color b = Color.BLUE;
    private Color c = Color.YELLOW;
    Slice[] slices = {new Slice(65, a), new Slice(123, b), new Slice(212, c)};

    PieChart() {

        setBackground(Color.WHITE);

        int x1 = x0 + TRIG_HBASE,  y1 = y0;
        int x2 = x0 - TRIG_HBASE,  y2 = y0;
        int x3 = x0,               y3 = y0 - TRIG_HEIGHT;
        poly = new Polygon(new int[] {x1, x2, x3}, new int[] {y1, y2, y3}, 3);
    }

    @Override
    protected void paintComponent(Graphics g) {

        Graphics2D g2d = (Graphics2D) g;
        super.paintComponent(g2d);

        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        g2d.setColor(Color.LIGHT_GRAY);
        g2d.fillRect(START, START, SIZE, SIZE);

        double total = 0d;
        for (Slice slice : slices) {
            total += slice.getValue();
        }

        double startAngle = START_DEG;
        double arcAngle, centerAngle;
        double x, y;

        for (Slice slice : slices) {
            arcAngle = (slice.getValue() * 360 / total);
            g2d.setColor(slice.getColor());
            g2d.fill(new Arc2D.Double(START, START, SIZE, SIZE, startAngle, arcAngle, Arc2D.PIE));

            centerAngle = Math.toRadians(((startAngle - START_DEG) + arcAngle / 2));
            x = (START + SIZE / 2 * (1 - Math.sin(centerAngle)));
            y = (START + SIZE / 2 * (1 - Math.cos(centerAngle)));

            AffineTransform trans = AffineTransform.getTranslateInstance(x - x0, y - y0);
            AffineTransform rot = AffineTransform.getRotateInstance(-centerAngle, x, y);
            Shape s = trans.createTransformedShape(poly);
            s = rot.createTransformedShape(s);

            g2d.setColor(slice.getColor().darker());
            g2d.fill(s);

            startAngle += arcAngle;
        }
    }

    @Override
    public Dimension getPreferredSize() {

        return new Dimension(START * 2 + SIZE, START * 2 + SIZE);
    }
}

poly用作基本三角形,朝上,其基点以0点钟为中心。每个弧都会对此多边形进行平移和变换(副本),使其基点位于弧长的中心,并使其向外指向。

备注:

  • 请勿在{{1​​}}内拨打setBackground,在外面拨打电话。它会使绘制机制自动在每次重绘上绘制背景。如果你把它放在里面,你只需要重写每次重绘的指令。或者,您可以使用paintComponent将背景设置为白色(或g.clearRect设置不同的颜色。)
  • 覆盖面板的fillRect方法以与其内容兼容。
  • 使用常量(getPreferredSize)而不是内联数字。这样,您只需要在一个地方更改它们,并考​​虑所有依赖项。
  • Slice可以使用getter方法(通常比直接字段访问更受欢迎),并且它还允许final循环。
  • 使用for each s并且仅在最新点转换为double,否则您将失去精确度(将角度转换为int,然后将其用作int } argument。。
  • doubleMath.toRadians值得熟悉。
  • 我将三角形变宽以显示它们如何与弧相交,更改Math.toDegrees常数以使用它们的大小。我也给它们着色以了解哪个三角形属于哪个弧。
  • 我在弧线上加了一个背景,只是为了更好地看周边。

以下是您的参数的结果(并没有特殊颜色):

TRIG

enter image description here

答案 1 :(得分:1)

这里我提出了一个解决方案(稍微改变算法):

public class Chart {
public static void main(String[] args) {
    JFrame frame= new JFrame();
    frame.add(new PieChart());
    frame.setSize(new Dimension(400, 400));
    frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    EventQueue.invokeLater( () ->{
        frame.setVisible(true);
    });
}

public static class Slice {
    double value;
    Color color;

    public Slice(double value, Color color) {
        this.value = value;
        this.color = color;
    }
}

public static class  PieChart extends JPanel {

    private static final long serialVersionUID = 1L;
    private Color a = Color.RED;
    private Color b = Color.BLUE;
    private Color c = Color.YELLOW;
    Slice[] slices = { new Slice(60, a), new Slice(100, b),
            new Slice(200, c) };

    public PieChart() {

    }

    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        super.paintComponent(g2d);

        this.setBackground(Color.WHITE);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);

        int upperLeftX = 20;
        int upperLeftY = 20;
        int r = 100;
        int startAngle = 0;


        double curValue = 0.0;

        for (int i = 0; i < slices.length; i++) {
            startAngle = (int) curValue ;
            int arcAngle = (int) slices[i].value ;
            g2d.setColor(slices[i].color);
            g2d.fillArc(upperLeftX, upperLeftY, r*2, r*2, startAngle, arcAngle);

            g2d.setPaint(Color.BLACK);

            double qi = curValue+slices[i].value/2;

            int x = upperLeftX + r + (int)(Math.cos(qi*Math.PI/180)*r);
            int y = upperLeftY + r - (int)(Math.sin(qi*Math.PI/180)*r);
            //point touching the circle (x,y)->half point of the base               
            int x1 = x - (int)(7*Math.sin(qi*Math.PI/180));
            int y1 = y - (int)(7*Math.cos(qi*Math.PI/180));

            int x2 = x + (int)(7*Math.sin(qi*Math.PI/180));
            int y2 = y + (int)(7*Math.cos(qi*Math.PI/180));

            int x3 = upperLeftX + r + (int)(Math.cos(qi*Math.PI/180)*(r+12));
            int y3 = upperLeftY + r - (int)(Math.sin(qi*Math.PI/180)*(r+12));


            Polygon p = new Polygon(new int[] { x1, x2, x3  },
                    new int[] { y1, y2, y3 }, 3); // this values seems to
                                                    // be important

            g2d.draw(p);
            g2d.fill(p);

            curValue += slices[i].value;
        }
    }
}
}