SoFunction
Updated on 2025-05-12

Detailed explanation of the code to implement pie chart rotation angle in Java

1. Project introduction

1.1 Background

In the field of modern data visualization, Pie Chart is widely used for its intuitive display of the overall proportion of each part. In order to enhance interactivity and appeal, pie charts are often givenRotateAnimation: Rotate automatically and smoothly, allowing users to focus on sectors from different angles. The rotation angle can highlight data changes, guide viewing order, and enhance interface dynamics. However, to achieve a smooth and interactive pie chart rotation in the Java Swing/Java2D environment, you need to understand the following difficulties:

  • Angle Mapping: Map time or frames to rotation angle and align correctly with the pie sector.

  • Drawing order: During the rotation process, correctly handle the drawing order of sectors to avoid confusion in front and rear sector occlusion.

  • Animation driver:useOr high-precision timer to control rotational fluency.

  • Interactive response: Supports pause/continue, direction switching, rate adjustment and drag control.

1.2 Goal

This article will start from scratch and implement one step by stepJava2D Swing EditionofRotable pie chart component, the focus is:

  1. Automatic rotation: Rotate smoothly and continuously at the set rate.

  2. Angle control: You can obtain and set the current rotation angle at any time to achieve "instantaneous jump" or animation transition.

  3. Direction switching: rotate clockwise or counterclockwise to switch dynamically.

  4. Drag and drop control: Mouse drag and drop to control the pie chart angle in real time, interrupt/recover automatic rotation.

  5. Complete package: Provides an easy-to-use API that supports embedding in any Swing interface.

2. Related technologies and knowledge

To achieve the above functions, you need to master and understand the following technical points.

2.1 Java2D drawing basics

  • Graphics2D: The core rendering context of Java2D, supporting anti-aliasing, transformation, compounding, etc.

  • Shape structureArc2DDraw the fan shape,Path2DConstruct the side shape.

  • Anti-aliasing:passRenderingHint.KEY_ANTIALIASINGImprove drawing quality.

  • transparency:useAlphaCompositeControl translucent effects.

2.2 Animation Driver

  • Swing TimerPeriodic events are triggered in the event distribution thread (EDT), and the graph is safely refreshed.

  • Frame rate and rate: Calculate the increment angle per frame based on delay and rotation degree per minute (RPM)delta = rpm * 360° / (60_000ms / delay)

  • Smoothness: Choose the right onedelay(For example, 16ms≈60FPS or 40ms≈25FPS) Balance smoothness and performance.

2.3 Depth sorting

Although we are demonstrating a 2D pie chart, if you add3D sideorshadow, thenDepth sorting: In each frame, judge the "front and back" relationship based on the current central angle of the sector, first draw the distant sector and then draw the near sector to ensure the natural occlusion effect.

2.4 Interactive processing

  • Drag and drag the mouseMouseListener + MouseMotionListenerCapture press, drag, and release events, and map drag distance to angle offset in real time.

  • Pause/resume: The automatic rotation stops when dragging starts, and can continue when released.

  • Direction switching and rate adjustment: Allow callers to change dynamically by exposing the APIrpmandclockwiseLottery.

Ideas for realization

Combining the above-mentioned technology stack, we will implement it according to the following ideas:

  1. Data Model

    • Define the internalPieSliceClass: Save sectorvaluecolorlabelstartAnglearcAngle

    • totalValueAccumulate all sector values.

    • computeAngles()Methods assign angles proportionally.

  2. Component packaging

    • InheritanceJPanel, namedRotatingPieChartPanel, expose API:

      • addSlice(value, color, label)

      • setRotateSpeed(rpm)

      • setClockwise(boolean)

      • start() / stop()

      • setAngle(double) / getAngle()Achieve "instantaneous jump".

  3. Animation and drawing

    • Create in the constructorTimer(animationDelay, e->{ advanceOffset(); repaint(); })

    • advanceOffset()according torpmandclockwisecalculateangleOffset

    • paintComponent()Called indrawPie(), divided into three steps: Shadow → Side (deep sorting required) → Top.

  4. Interaction

    • Add toMouseAdapter

      • mousePressedStart dragging and record the initialangleOffsetwith mouse pointer;

      • mouseDraggedMap to incremental angle according to horizontal displacement, updateangleOffsetandrepaint()

      • mouseReleasedEnd drag and restart the painting.

  5. Depth sorting

    • When drawing the side, first copy the sector list and follow each sector.Center anglesort of sine values ​​(or cosine values);

    • depthKey = ((startAngle + arcAngle/2 + angleOffset)), draw after the value is greater.

4. Complete implementation of the code

import .*;
import .*;
import .*;
import .*;
import .*;
import ;
 
/**
  * RotatingPieChartPanel: Pie Chart component that can be rotated automatically/manually and correctly sorted
  */
public class RotatingPieChartPanel extends JPanel {
 
    /** Internal sector model */
    private static class PieSlice {
        double value;          // Sector value        Color color;           // Sector color        String label;          // Sector tags        double startAngle;     // Start angle (degree)        double arcAngle;       // Sector angle (degree)        boolean highlighted;   // Is it bright? 
        PieSlice(double value, Color color, String label) {
             = value;
             = color;
             = label;
             = false;
        }
    }
 
    private final List<PieSlice> slices = new ArrayList<>();
    private double totalValue = 0.0;
 
    // Rotary control    private double angleOffset = 0.0;  // Current offset angle    private double rpm = 1.0;          // Degrees per minute    private boolean clockwise = true;  // Rotation direction    private Timer animationTimer;      // for automatic rotation 
    // 3D effect depth (pixel)    private double depth = 50.0;
 
    // Drag and drop interaction state    private boolean dragging = false;
    private double dragStartOffset;
    private Point dragStartPoint;
 
    public RotatingPieChartPanel() {
        setBackground();
        setPreferredSize(new Dimension(600, 400));
        initInteraction();
    }
 
    /** Initialize mouse interaction: drag and drop control */
    private void initInteraction() {
        MouseAdapter ma = new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                // Stop automatic rotation and enter drag state                stop();
                dragging = true;
                dragStartOffset = angleOffset;
                dragStartPoint = ();
            }
 
            @Override
            public void mouseDragged(MouseEvent e) {
                if (!dragging) return;
                Point pt = ();
                double dx =  - ;
                // 0.5 degrees per pixel                angleOffset = dragStartOffset + dx * 0.5;
                repaint();
            }
 
            @Override
            public void mouseReleased(MouseEvent e) {
                dragging = false;
                start(); // Restore automatic rotation            }
        };
        addMouseListener(ma);
        addMouseMotionListener(ma);
    }
 
    /** Add sector */
    public void addSlice(double value, Color color, String label) {
        (new PieSlice(value, color, label));
        totalValue += value;
        computeAngles();
        repaint();
    }
 
    /** Recalculate sector angle */
    private void computeAngles() {
        double angle = 0.0;
        for (PieSlice s : slices) {
             = angle;
             =  / totalValue * 360.0;
            angle += ;
        }
    }
 
    /** Set the rotation rate (RPM) */
    public void setRotateSpeed(double rpm) {
         = rpm;
        if (animationTimer != null && ()) {
            stop();
            start();
        }
    }
 
    /** Set rotation direction */
    public void setClockwise(boolean cw) {
         = cw;
    }
 
    /** Set 3D depth */
    public void setDepth(double depth) {
         = depth;
        repaint();
    }
 
    /** Start automatic rotation */
    public void start() {
        if (animationTimer != null && ()) return;
        int delay = 40; // 25 FPS
        double deltaDeg = rpm * 360.0 / (60_000.0 / delay);
        animationTimer = new Timer(delay, e -> {
            angleOffset += (clockwise ? -deltaDeg : deltaDeg);
            repaint();
        });
        ();
    }
 
    /** Stop automatic rotation */
    public void stop() {
        if (animationTimer != null) {
            ();
            animationTimer = null;
        }
    }
 
    /** Get the current angle */
    public double getAngle() {
        return angleOffset;
    }
 
    /** Directly set the angle (instantaneous jump) */
    public void setAngle(double angle) {
         = angle % 360.0;
        repaint();
    }
 
    @Override
    protected void paintComponent(Graphics g) {
        (g);
        renderPie((Graphics2D) g);
    }
 
    /** Draw pie chart: Shadow → Side (Deep Sort) → Top */
    private void renderPie(Graphics2D g2) {
        // Anti-aliasing        (RenderingHints.KEY_ANTIALIASING,
                            RenderingHints.VALUE_ANTIALIAS_ON);
 
        int w = getWidth(), h = getHeight();
        double cx = w / 2.0, cy = h / 2.0 - depth / 2.0;
        double r = (w, h - depth) / 2.0 - 20.0;
 
        // 1. Draw shadows        drawShadow(g2, cx, cy, r);
 
        // 2. Depth sort and draw sides        List<PieSlice> sorted = new ArrayList<>(slices);
        ((this::depthKey));
        for (PieSlice s : sorted) {
            drawSide(g2, cx, cy, r, s);
        }
 
        // 3. Draw the top surface        for (PieSlice s : sorted) {
            drawTop(g2, cx, cy, r, s);
        }
    }
 
    /** Calculate depth sort key: the sin value of the sector center angle */
    private double depthKey(PieSlice s) {
        double mid =  +  / 2.0 + angleOffset;
        return ((mid));
    }
 
    /** Draw the bottom shadow */
    private void drawShadow(Graphics2D g2,
                            double cx, double cy, double r) {
        Ellipse2D shadow = new (
            cx - r, cy + depth - r / 3.0 * 2, 2 * r, r / 2.0
        );
        Composite old = ();
        ((
            AlphaComposite.SRC_OVER, 0.3f
        ));
        ();
        (shadow);
        (old);
    }
 
    /** Draw sector side */
    private void drawSide(Graphics2D g2,
                          double cx, double cy, double r, PieSlice s) {
        double sa = ( + angleOffset);
        double ea = ( +  + angleOffset);
 
        Point2D p1 = new (
            cx + r * (sa), cy + r * (sa)
        );
        Point2D p2 = new (
            cx + r * (ea), cy + r * (ea)
        );
        Point2D p3 = new ((), () + depth);
        Point2D p4 = new ((), () + depth);
 
        Path2D side = new ();
        ((), ());
        ((), ());
        ((), ());
        ((), ());
        ();
 
        (());
        (side);
        if () {
            ();
            (new BasicStroke(2));
            (side);
        }
    }
 
    /** Draw the top surface of the sector */
    private void drawTop(Graphics2D g2,
                         double cx, double cy, double r, PieSlice s) {
        Arc2D top = new (
            cx - r, cy - r, 2 * r, 2 * r,
             + angleOffset,
            , 
        );
        ();
        (top);
        if () {
            ();
            (new BasicStroke(2));
            (top);
        }
    }
 
    // Extensible: Add highlighting and prompting functions}
 
/**
  * DemoMain: Demo RotatingPieChartPanel usage
  */
class DemoMain {
    public static void main(String[] args) {
        (() -> {
            RotatingPieChartPanel pie = new RotatingPieChartPanel();
            (30, ,   "red");
            (20, ,  "blue");
            (40, , "green");
            (10, ,"orange");
            (60);
            (2.5);
            (false);
            ();
 
            JFrame f = new JFrame("Sortable Pie Chart Example");
            (JFrame.EXIT_ON_CLOSE);
            (pie);
            ();
            (null);
            (true);
        });
    }
}

5. Interpretation of method-level functions

  1. addSlice(value, color, label)

    • CreatePieSliceObject, accumulatedtotalValue, callcomputeAngles()Recalculate the angular distribution of all sectors.

  2. computeAngles()

    • TraversalslicesList, proportional(value / totalValue) * 360°Assign each sectorarcAngle, and accumulate in turnstartAngle

  3. start() / stop()

    • usedelay = 40ms,every timeactionPerformedCalculate increments

double deltaDeg = rpm * 360.0 / (60_000.0 / delay);
angleOffset += clockwise ? -deltaDeg : deltaDeg;
  • Callrepaint()Refresh the component.
  1. paintComponent(...)

    • First(g)Clear the background and callrenderPie(g2)

      • Enable anti-aliasing

      • Computing Center(cx,cy)and radiusr

      • CalldrawShadowdrawSide(Deep sort) anddrawTop

  2. depthKey(PieSlice s)

    • Calculate the sector center angle: + /2 + angleOffset

    • Take the sine value as the basis for depth sorting (the larger the "front"), and sort the list, ensuring that the side of "back" is drawn first, then the side and top of "front".

  3. drawShadow

    • Draw a translucent black ellipse at the bottom, useAlphaCompositeSet to 0.3f.

  4. drawSide

    • Calculate two points at the edge of the sector(p1, p2), and extend downwarddepthGet two points at the bottom(p4, p3)

    • StructurePath2DQuadrilateral fills darker colors;

  5. drawTop

    • useDraw the fan-shaped top surface;

  6. Drag and drop interaction

    • mousePressedStop automatic rotation and record the initial state;

    • mouseDraggedUpdated based on horizontal displacement map to incremental angleangleOffset

    • mouseReleasedRestore automatic rotation.

6. Project summary and extended thinking

6.1 Core gains

  • In-depth understandingJava2DApplication skills in complex dynamic graphics;

  • masterRotate animationandFrame rate controlImplementation;

  • Learn to useDepth sortingSolve the problem of rotation occlusion;

  • familiarDrag and drop interactionIntegration in graphics components.

6.2 Performance optimization suggestions

  1. Shape Cache: Pre-generated for each sector at a fixed angle step sizePath2DandArc2D, avoiding the creation of large numbers of objects per frame.

  2. Off-screen buffering:useBufferedImageorVolatileImageRender the static part off-screen (shadow, side basic shape), and only dynamically draw the rotating part.

  3. OpenGL Acceleration: Set system properties-Dsun.=trueEnable hardware acceleration.

6.3 Extended Functions

  • Gradients and textures: Add gradient fill or map to the fan.

  • Multi-layer pie chart/ring chart: Supports rings (Donut) or nested pie charts.

  • Tags and guide lines: Dynamically display the label during rotation, and the guide wire can be displayed optionally.

  • JavaFX version: Higher performance and lighting effects based on JavaFX Canvas or 3D API.

The above is the detailed explanation of the code for Java to implement pie chart rotation angle. For more information about Java pie chart rotation angle, please pay attention to my other related articles!