Affichage des articles dont le libellé est Rubik's Cube. Afficher tous les articles
Affichage des articles dont le libellé est Rubik's Cube. Afficher tous les articles

3D Rubik's Cube in Java Swing

How to Create a 3D Rubik's Cube in Java Netbeans

How to Create a 3D Rubik's Cube in Java Netbeans



In this Java Tutorial we will see How To Create a 3D Rubik's Cube with realistic lighting effects, smooth rotations, floating animations, and mouse controls. 
The cube features authentic Rubik's cube colors, gradient shading, and perspective projection to create a 3D experience In Java Using Netbeans.

This Rubik's Cube in Java Swing is an excellent project that combines computer graphics, 3D mathematics, and user interface design - all without using external 3D libraries..

What We Are Gonna Use In This Project:

- Java Programming Language.
- NetBeans Editor.





Project Source Code:



/**
 *
 * @author 1BestCsharp
 */
public class RubiksCube extends JFrame {
    
    // Target and current rotation angles
    private double currentRotationX = -30;  // Current X rotation in degrees
    private double currentRotationY = 45;   // Current Y rotation in degrees 
    private double currentRotationZ = 0;    // Current Z rotation in degrees
    
    private double targetRotationX = -30;   // Target X rotation for smooth animation
    private double targetRotationY = 45;    // Target Y rotation for smooth animation
    private double targetRotationZ = 0;     // Target Z rotation for smooth animation
    
    // Animation properties
    private final double ROTATION_SPEED = 0.15;  // Speed of rotation interpolation (0-1)
    private boolean isAnimating = false;         // Tracks if rotation animation is in progress
    
    // For mouse dragging interaction
    private boolean isDragging = false;          // Tracks if user is dragging the cube
    private Point previousPoint;                 // Stores previous mouse position for drag calculations
    
    // Faces colors - standard Rubik's cube colors
    private static final Color[] FACE_COLORS = {
        new Color(255, 0, 0),    // Front - Red
        new Color(255, 165, 0),  // Back - Orange
        new Color(0, 255, 0),    // Right - Green
        new Color(0, 0, 255),    // Left - Blue
        new Color(255, 255, 255),// Top - White
        new Color(255, 255, 0)   // Bottom - Yellow
    };
    
    // Darker versions of face colors for gradients and shading effects
    private static final Color[] FACE_COLORS_DARK = {
        new Color(180, 0, 0),    // Front - Dark Red
        new Color(180, 117, 0),  // Back - Dark Orange
        new Color(0, 180, 0),    // Right - Dark Green
        new Color(0, 0, 180),    // Left - Dark Blue
        new Color(200, 200, 200),// Top - Light Gray
        new Color(180, 180, 0)   // Bottom - Dark Yellow
    };
    
    // Face indices for improved code readability
    private static final int FRONT = 0;
    private static final int BACK = 1;
    private static final int RIGHT = 2;
    private static final int LEFT = 3;
    private static final int TOP = 4;
    private static final int BOTTOM = 5;
    
    // Stores cube state (colors of all cells)
    private CubeModel cubeModel;
    
    // Main drawing panel
    private CubePanel cubePanel;
    
    // Animation timers
    private Timer floatAnimationTimer;       // Timer for floating effect animation
    private Timer rotationAnimationTimer;    // Timer for smooth rotation animation
    private double floatOffset = 0;          // Current offset for floating animation
    private boolean floatingUp = true;       // Direction of floating animation
    
    
    public RubiksCube(){
        
        setTitle("Rubik's Cube");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());
        
         // Initialize cube model (stores the state of all cube cells)
        cubeModel = new CubeModel();
        
        // Create and configure the cube rendering panel
        cubePanel = new CubePanel();
        cubePanel.setPreferredSize(new Dimension(800, 600));
        cubePanel.setBackground(new Color(15, 15, 15));  // Very dark background
        add(cubePanel, BorderLayout.CENTER);
        
         // Create control panel with buttons
        JPanel controlPanel = createControlPanel();
        add(controlPanel, BorderLayout.SOUTH);
        
        // Set up mouse listeners for interactive rotation
        setupMouseListeners();
        
         // Start animation timers
         startRotationAnimation();
         startFloatingAnimation();

        pack();
        setLocationRelativeTo(null);  // Center on screen
        
    }
    
    
     /**
     * Creates the control panel with rotation buttons
     * @return JPanel containing control buttons
     */
      private JPanel createControlPanel() {
          
            JPanel panel = new JPanel();
            panel.setBackground(new Color(30, 30, 30));
            panel.setBorder(new EmptyBorder(15, 15, 15, 15));
            panel.setLayout(new FlowLayout(FlowLayout.CENTER, 15, 0));
            
            // Create control buttons
            JButton rotateXButton = createControlButton("Rotate X");
            JButton rotateYButton = createControlButton("Rotate Y");
            JButton rotateZButton = createControlButton("Rotate Z");
            JButton resetButton = createControlButton("Reset View");
            
            // Add action listeners to buttons
            rotateXButton.addActionListener(e -> {
                targetRotationX += 90;  // Rotate 90 degrees around X axis
                isAnimating = true;
            });
            
            rotateYButton.addActionListener(e -> {
                targetRotationY += 90;  // Rotate 90 degrees around Y axis
                isAnimating = true;
            });
            
            rotateZButton.addActionListener(e -> {
                targetRotationZ += 90;  // Rotate 90 degrees around Z axis
                isAnimating = true;
            });
            
            resetButton.addActionListener(e -> {
                // Reset to default view angles
                targetRotationX = -30;
                targetRotationY = 45;
                targetRotationZ = 0;
                isAnimating = true;
            });
            
            // Add buttons to panel
            panel.add(rotateXButton);
            panel.add(rotateYButton);
            panel.add(rotateZButton);
            panel.add(resetButton);

            return panel;
          
        }
      
     /**
     * Creates a styled control button with hover effects
     * @param text Button text
     * @return Styled JButton
     */
      private JButton createControlButton(String text) {
          
            JButton button = new JButton(text);
            button.setForeground(Color.WHITE);
            button.setBackground(new Color(60, 60, 60));
            button.setFocusPainted(false);
            button.setBorderPainted(false);
            button.setFont(new Font("SansSerif", Font.BOLD, 14));
            button.setPreferredSize(new Dimension(120, 40));
            
            // Add custom hover and press effects
            button.addMouseListener(new MouseAdapter() {
                
                @Override
                public void mouseEntered(MouseEvent e) {
                    button.setBackground(new Color(80, 80, 80));  // Lighter on hover
                    button.setCursor(new Cursor(Cursor.HAND_CURSOR));
                }
                
                 @Override
                public void mouseExited(MouseEvent e) {
                    button.setBackground(new Color(60, 60, 60));  // Back to normal when not hovering
                    button.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                }
                
                @Override
                public void mousePressed(MouseEvent e) {
                    button.setBackground(new Color(100, 100, 100));  // Even lighter when pressed
                }
                
                @Override
                public void mouseReleased(MouseEvent e) {
                    button.setBackground(new Color(80, 80, 80));  // Back to hover state when released
                }
                
            });

            return button;
          
      }
      
      
     /**
     * Sets up mouse listeners for interactive cube rotation
     */
      private void setupMouseListeners() {
          
           cubePanel.addMouseListener(new MouseAdapter() {
               
                @Override
                public void mousePressed(MouseEvent e) {
                    isDragging = true;
                    previousPoint = e.getPoint();
                    cubePanel.setCursor(new Cursor(Cursor.MOVE_CURSOR));
                }
                
                @Override
                public void mouseReleased(MouseEvent e) {
                    isDragging = false;
                    cubePanel.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                }
               
           });
           
           cubePanel.addMouseMotionListener(new MouseMotionAdapter() {
               
                 @Override
                 public void mouseDragged(MouseEvent e) {
                     
                        if (isDragging) {
                            
                              Point currentPoint = e.getPoint();
                              
                              // Calculate movement deltas
                              double deltaX = currentPoint.x - previousPoint.x;
                              double deltaY = currentPoint.y - previousPoint.y;
                              
                               // Update target rotations - mouse X movement rotates Y axis and vice versa
                               targetRotationY += deltaX * 0.5;  // Horizontal movement -> Y rotation
                               targetRotationX += deltaY * 0.5;  // Vertical movement -> X rotation
                               
                                // Update current rotations partially for immediate visual feedback
                                currentRotationY += deltaX * 0.25;
                                currentRotationX += deltaY * 0.25;
                                
                                 previousPoint = currentPoint;
                                 cubePanel.repaint();
                            
                        }
                     
                 }
               
           });
          
      }
      
      
      
     /**
     * Starts the floating animation that makes the cube hover up and down
     */
      private void startFloatingAnimation() {
          
            floatAnimationTimer = new Timer(50, e -> {
                
                  // Update floating offset based on direction
                  if (floatingUp) {
                      
                      floatOffset += 0.15;
                      
                     if (floatOffset >= 8) {  // Max height reached
                            floatingUp = false;  // Change direction
                     }
          
                  }
                  
                   else {
                      
                           floatOffset -= 0.15;
                            if (floatOffset <= -8) {  // Min height reached
                                floatingUp = true;    // Change direction
                            }
                  }
                  
                 cubePanel.repaint();
                
            });
            
          floatAnimationTimer.start();
          
      }
      
      
      
      
      
     /**
     * Starts the rotation animation for smooth transitions
     */
      private void startRotationAnimation() {
          
           rotationAnimationTimer = new Timer(16, e -> { 
               
                 boolean needsUpdate = false;
                 
                 // Smoothly interpolate X rotation using easing
                 if (Math.abs(targetRotationX - currentRotationX) > 0.1) {
                          currentRotationX += (targetRotationX - currentRotationX) * ROTATION_SPEED;
                          needsUpdate = true;
                 }
                  else {
                    currentRotationX = targetRotationX;  // Snap to exact value when close enough
                }
                 
                 // Smoothly interpolate Y rotation using easing
                 if (Math.abs(targetRotationY - currentRotationY) > 0.1) {
                          currentRotationY += (targetRotationY - currentRotationY) * ROTATION_SPEED;
                          needsUpdate = true;        
                  }
                 else {
                        currentRotationY = targetRotationY;  // Snap to exact value when close enough
                 }
                 
                  // Smoothly interpolate Z rotation using easing
                 if (Math.abs(targetRotationZ - currentRotationZ) > 0.1) {
                           currentRotationZ += (targetRotationZ - currentRotationZ) * ROTATION_SPEED;
                           needsUpdate = true;
                 }
                  else {
                        currentRotationZ = targetRotationZ;  // Snap to exact value when close enough
                  }
                 
                  // Only repaint if rotations changed
                  if (needsUpdate) {
                        cubePanel.repaint();             
                  }
                  else if (isAnimating) {
                        isAnimating = false;  // Stop animation flag when target reached
                  }

           });
           
         rotationAnimationTimer.start();
          
      }
    
    
    
     /**
     * Inner class to store the state of the Rubik's Cube
     * Currently just stores colors, but could be extended for moves/rotations
     */
     class CubeModel {
         
         private final int SIZE = 3;       // 3x3x3 standard cube
         private Color[][][] cells;        // [face][row][col] array of cell colors
         
         public CubeModel() {
             
             cells = new Color[6][SIZE][SIZE];
             
             for (int face = 0; face < 6; face++) {
                 
                 for (int row = 0; row < SIZE; row++) {
                     
                    for (int col = 0; col < SIZE; col++) {
                        cells[face][row][col] = FACE_COLORS[face];
                    }
                     
                 }
                 
             }
             
         }
         
         /**
         * Gets the color of a specific cell
         * @param face Face index (0-5)
         * @param row Row index (0-2)
         * @param col Column index (0-2)
         * @return Color of the specified cell
         */
        public Color getCellColor(int face, int row, int col) {
            return cells[face][row][col];
        }
        
         /**
         * Gets the darker color variant for a specific cell (for gradients)
         * @param face Face index (0-5)
         * @param row Row index (0-2)
         * @param col Column index (0-2)
         * @return Darker version of the cell color
         */
        public Color getCellDarkColor(int face, int row, int col) {
            return FACE_COLORS_DARK[face];
        }
         
     }
     
     /**
     * Inner class for rendering the 3D cube
     */
      class CubePanel extends JPanel {
          
            private static final int CUBE_SIZE = 300;  // Size of the cube in pixels
            private static final int CELL_GAP = 6;     // Gap between cells for visual separation
            
            // Array to store 3D coordinates of cube corners for each face
            private double[][][] faceVertices;
            
            public CubePanel() {
                // Initialize 3D vertices for all cube faces
                initFaceVertices();

                // Set cursor to show interactivity
                setCursor(new Cursor(Cursor.HAND_CURSOR));
        }
            
         /**
         * Initializes the 3D vertices for all cube faces
         * Each face has 4 corners defined in 3D space
         */
         private void initFaceVertices() {
             
            // Each face has 4 vertices in 3D space
            faceVertices = new double[16][4][3];
            
            double s = CUBE_SIZE / 2.0;  // Half size of the cube
            
            // FRONT face (z = s)
            faceVertices[FRONT][0] = new double[]{-s, -s, s};  // Top-left
            faceVertices[FRONT][1] = new double[]{s, -s, s};   // Top-right
            faceVertices[FRONT][2] = new double[]{s, s, s};    // Bottom-right
            faceVertices[FRONT][3] = new double[]{-s, s, s};   // Bottom-left
            
            // BACK face (z = -s)
            faceVertices[BACK][0] = new double[]{s, -s, -s};   // Top-left (mirrored)
            faceVertices[BACK][1] = new double[]{-s, -s, -s};  // Top-right (mirrored)
            faceVertices[BACK][2] = new double[]{-s, s, -s};   // Bottom-right (mirrored)
            faceVertices[BACK][3] = new double[]{s, s, -s};    // Bottom-left (mirrored)
            
            // RIGHT face (x = s)
            faceVertices[RIGHT][0] = new double[]{s, -s, s};   // Top-left
            faceVertices[RIGHT][1] = new double[]{s, -s, -s};  // Top-right
            faceVertices[RIGHT][2] = new double[]{s, s, -s};   // Bottom-right
            faceVertices[RIGHT][3] = new double[]{s, s, s};    // Bottom-left
            
            // LEFT face (x = -s)
            faceVertices[LEFT][0] = new double[]{-s, -s, -s};  // Top-left
            faceVertices[LEFT][1] = new double[]{-s, -s, s};   // Top-right
            faceVertices[LEFT][2] = new double[]{-s, s, s};    // Bottom-right
            faceVertices[LEFT][3] = new double[]{-s, s, -s};   // Bottom-left
            
            // TOP face (y = -s)
            faceVertices[TOP][0] = new double[]{-s, -s, -s};   // Top-left
            faceVertices[TOP][1] = new double[]{s, -s, -s};    // Top-right
            faceVertices[TOP][2] = new double[]{s, -s, s};     // Bottom-right
            faceVertices[TOP][3] = new double[]{-s, -s, s};    // Bottom-left
            
            // BOTTOM face (y = s)
            faceVertices[BOTTOM][0] = new double[]{-s, s, s};  // Top-left
            faceVertices[BOTTOM][1] = new double[]{s, s, s};   // Top-right
            faceVertices[BOTTOM][2] = new double[]{s, s, -s};  // Bottom-right
            faceVertices[BOTTOM][3] = new double[]{-s, s, -s}; // Bottom-left

         }
         
        
        @Override
        protected void paintComponent(Graphics g) {
            
             super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            
            // Enable antialiasing for smoother rendering
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                    RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_RENDERING, 
                    RenderingHints.VALUE_RENDER_QUALITY);
            
            // Create background gradient
            GradientPaint bgGradient = new GradientPaint(
                0, 0, new Color(25, 25, 30),
                getWidth(), getHeight(), new Color(10, 10, 15)
            );
            g2d.setPaint(bgGradient);
            g2d.fillRect(0, 0, getWidth(), getHeight());
            
            // Get center of panel for translation
            int centerX = getWidth() / 2;
            int centerY = getHeight() / 2;
            
            // Apply floating animation effect
            double scaleFloat = 1.0 + Math.abs(floatOffset) / 100.0;  // Subtle scale change
            double yFloat = floatOffset;  // Y position offset for floating
            
            // Move to center of panel with floating offset
            g2d.translate(centerX, centerY + yFloat);
            g2d.scale(scaleFloat, scaleFloat);  // Apply subtle scaling
            
            // Calculate visibility of faces based on current rotation
            int[] faceVisibility = calculateFaceVisibility();
            
            // Draw faces in order of visibility (back to front)
            for (int i = 0; i < 6; i++) {
                int face = faceVisibility[i];
                drawFace(g2d, face);
            }
            
            
        }
        
        
        /**
         * Calculates which faces are visible and their drawing order
         * Uses z-ordering (painter's algorithm) to determine the order
         * @return Array of face indices in back-to-front order
         */
        private int[] calculateFaceVisibility() {
            
            // Calculate center point of each face after rotation
            double[][] faceCenters = new double[6][3];
            
            // Find center of each face by averaging its vertices
            for (int face = 0; face < 6; face++) {
                
                  double[] center = {0, 0, 0};
                  
                 // Average all 4 corners to get center
                for (int i = 0; i < 4; i++) {
                    
                    center[0] += faceVertices[face][i][0];  // Sum x coordinates
                    center[1] += faceVertices[face][i][1];  // Sum y coordinates
                    center[2] += faceVertices[face][i][2];  // Sum z coordinates
                    
                }
                center[0] /= 4;  // Average x
                center[1] /= 4;  // Average y
                center[2] /= 4;  // Average z
                
                // Apply current rotation to center point
                double[] rotatedCenter = rotatePoint(center);
                faceCenters[face] = rotatedCenter;
                
            }
            
             // Sort faces by z-coordinate
            // Faces with smaller z (further back) are drawn first
            int[] faceOrder = {0, 1, 2, 3, 4, 5};
            
            // Simple sort by z-coordinate
            for (int i = 0; i < 5; i++) {
                for (int j = i + 1; j < 6; j++) {
                    
                    if (faceCenters[faceOrder[i]][2] > faceCenters[faceOrder[j]][2]) {
                        
                        // Swap indices if z-order is incorrect
                        int temp = faceOrder[i];
                        faceOrder[i] = faceOrder[j];
                        faceOrder[j] = temp;
                        
                    }
                    
                }
            }
            
           return faceOrder;
            
        }
        
        
         /**
         * Draws a single face of the cube
         * @param g2d Graphics context
         * @param face Face index to draw
         */
         private void drawFace(Graphics2D g2d, int face) {
             
            // Get the projected 2D points for the face after rotation
            Point2D.Double[] projectedPoints = new Point2D.Double[4];
            
            // Apply rotation and projection to each vertex
            for (int i = 0; i < 4; i++) {
                
                // Rotate the 3D point according to current rotation angles
                double[] rotated = rotatePoint(faceVertices[face][i]);
                
                // Apply perspective projection effect
                double scale = 1.2 + rotated[2] / 1000.0;  // Scale based on z-depth
                
                // Project 3D point to 2D screen space
                projectedPoints[i] = new Point2D.Double(
                    rotated[0] * scale,  // Scale X by perspective factor
                    rotated[1] * scale   // Scale Y by perspective factor
                );
                
            }
            
            // Create face outline shape
            Path2D.Double facePath = new Path2D.Double();
            facePath.moveTo(projectedPoints[0].x, projectedPoints[0].y);  // Start at first point
            
            // Connect all points to form the face
            for (int i = 1; i < 4; i++) {
                facePath.lineTo(projectedPoints[i].x, projectedPoints[i].y);
            }
            facePath.closePath();  // Close the path
            
            // Draw face background
            g2d.setColor(new Color(15, 15, 15));
            g2d.fill(facePath);
            
            // Draw face border with gradient effect
            GradientPaint borderGradient = new GradientPaint(
                (float)projectedPoints[0].x, (float)projectedPoints[0].y, new Color(60, 60, 60),
                (float)projectedPoints[2].x, (float)projectedPoints[2].y, new Color(30, 30, 30)
            );
            g2d.setPaint(borderGradient);
            g2d.setStroke(new BasicStroke(2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
            g2d.draw(facePath);
            
            // Calculate cell size for 3x3 grid
            double cellSize = CUBE_SIZE / 3.0;
            
            // Draw each cell in the 3x3 grid
            for (int row = 0; row < 3; row++) {
                
                for (int col = 0; col < 3; col++) {
                    
                      drawCell(g2d, projectedPoints, row, col, cellSize, face);
                    
                }
                
            }
             
         }
         
         
         /**
         * Draws a single cell within a face
         * @param g2d Graphics context
         * @param facePoints The projected 2D points of the face corners
         * @param row Cell row (0-2)
         * @param col Cell column (0-2)
         * @param cellSize Size of each cell
         * @param face Face index
         */
         private void drawCell(Graphics2D g2d, Point2D.Double[] facePoints, int row, int col, double cellSize, int face) {
             
            // Calculate the normalized position of this cell (0-1 range within the face)
            double u1 = (col) / 3.0;     // Left edge position (0, 1/3, or 2/3)
            double v1 = (row) / 3.0;     // Top edge position (0, 1/3, or 2/3)
            double u2 = (col + 1) / 3.0; // Right edge position (1/3, 2/3, or 1)
            double v2 = (row + 1) / 3.0; // Bottom edge position (1/3, 2/3, or 1)
            
            // Adjust for gap between cells
            double gapRatio = CELL_GAP / cellSize;
            u1 += gapRatio / 3;  // Move right edge inward
            v1 += gapRatio / 3;  // Move top edge inward
            u2 -= gapRatio / 3;  // Move left edge inward
            v2 -= gapRatio / 3;  // Move bottom edge inward
            
            // Array to hold the cell's corner points
            Point2D.Double[] cellPoints = new Point2D.Double[4];
            for (int i = 0; i < 4; i++) {
                cellPoints[i] = new Point2D.Double(0, 0);
            }
            
            
            // Calculate the cell's corners using bilinear interpolation within the face
            // Top-left
            interpolate(facePoints, u1, v1, cellPoints[0]);
            // Top-right
            interpolate(facePoints, u2, v1, cellPoints[1]);
            // Bottom-right
            interpolate(facePoints, u2, v2, cellPoints[2]);
            // Bottom-left
            interpolate(facePoints, u1, v2, cellPoints[3]);
            
            
            // Create path for the cell
            Path2D.Double cellPath = new Path2D.Double();
            cellPath.moveTo(cellPoints[0].x, cellPoints[0].y);
            for (int i = 1; i < 4; i++) {
                cellPath.lineTo(cellPoints[i].x, cellPoints[i].y);
            }
            cellPath.closePath();
            
            // Get base colors from cube model
            Color baseColor = cubeModel.getCellColor(face, row, col);
            Color darkColor = cubeModel.getCellDarkColor(face, row, col);
            
            // Calculate center point for radial gradient
            Point2D.Double center = new Point2D.Double();
            center.x = (cellPoints[0].x + cellPoints[1].x + cellPoints[2].x + cellPoints[3].x) / 4;
            center.y = (cellPoints[0].y + cellPoints[1].y + cellPoints[2].y + cellPoints[3].y) / 4;
            
            // Calculate maximum distance from center to any corner (for gradient radius)
            double maxDist = 0;
            for (Point2D.Double point : cellPoints) {
                double dist = point.distance(center);
                if (dist > maxDist) {
                    maxDist = dist;
                }
            }
            
            // Create three-color radial gradient
            RadialGradientPaint gradient = new RadialGradientPaint(
                center,
                (float)maxDist * 1.2f,
                new float[]{0.0f, 0.7f, 1.0f},
                new Color[]{
                    brightenColor(baseColor),  // Lighter in center
                    baseColor,                 // Normal color in middle
                    darkColor                  // Darker at edges
                }
            );
            
            // Fill cell with gradient
            g2d.setPaint(gradient);
            g2d.fill(cellPath);
            
            // Add subtle inner shadow effect
            g2d.setColor(new Color(0, 0, 0, 40)); // 40% opacity black
            g2d.setStroke(new BasicStroke(5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
            g2d.draw(cellPath);
            
            // Add shine effect (highlight from top-left to center)
            Path2D.Double shinePath = new Path2D.Double();
            shinePath.moveTo(cellPoints[0].x, cellPoints[0].y);
            shinePath.lineTo(cellPoints[1].x, cellPoints[1].y);
            
            // Add midpoints to make highlight area look better
            double midX = (cellPoints[1].x + cellPoints[2].x) / 2;
            double midY = (cellPoints[1].y + cellPoints[2].y) / 2;
            shinePath.lineTo(midX, midY);
            
            midX = (cellPoints[0].x + cellPoints[3].x) / 2;
            midY = (cellPoints[0].y + cellPoints[3].y) / 2;
            shinePath.lineTo(midX, midY);
            shinePath.closePath();
            
            // Create highlight gradient from top-left to center
            GradientPaint shineGradient = new GradientPaint(
                (float)cellPoints[0].x, (float)cellPoints[0].y, new Color(255, 255, 255, 80),
                (float)center.x, (float)center.y, new Color(255, 255, 255, 0)
            );
            
            // Apply highlight gradient
            g2d.setPaint(shineGradient);
            g2d.fill(shinePath);
             
         }
         
         
         private void interpolate(Point2D.Double[] quad, double u, double v, Point2D.Double result) {
             
            // Bilinear interpolation on a quadrilateral
            double nu = 1.0 - u;
            double nv = 1.0 - v;
            
            // Interpolate along top and bottom edges
            double topX = nu * quad[0].x + u * quad[1].x;
            double topY = nu * quad[0].y + u * quad[1].y;
            double bottomX = nu * quad[3].x + u * quad[2].x;
            double bottomY = nu * quad[3].y + u * quad[2].y;
            
            // Interpolate between top and bottom
            result.x = nv * topX + v * bottomX;
            result.y = nv * topY + v * bottomY;
             
         }
        
        
         private Color brightenColor(Color color) {
            // Create a brighter version of the color for highlights
            float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
            float brightness = Math.min(1.0f, hsb[2] + 0.2f);
            float saturation = Math.max(0.0f, hsb[1] - 0.1f); // Slightly less saturated
            return Color.getHSBColor(hsb[0], saturation, brightness);
        }
         
        
        
        
        private double[] rotatePoint(double[] point) {
            
            double[] result = new double[3];
            double[] temp = new double[3];
            
             // Apply rotations (in radians)
            double radX = Math.toRadians(currentRotationX);
            double radY = Math.toRadians(currentRotationY);
            double radZ = Math.toRadians(currentRotationZ);
            
            // Copy original point
            System.arraycopy(point, 0, temp, 0, 3);
            
            // Rotate around X-axis
            result[0] = temp[0];
            result[1] = temp[1] * Math.cos(radX) - temp[2] * Math.sin(radX);
            result[2] = temp[1] * Math.sin(radX) + temp[2] * Math.cos(radX);
            
            // Copy result to temp
            System.arraycopy(result, 0, temp, 0, 3);
            
            // Rotate around Y-axis
            result[0] = temp[0] * Math.cos(radY) + temp[2] * Math.sin(radY);
            result[1] = temp[1];
            result[2] = -temp[0] * Math.sin(radY) + temp[2] * Math.cos(radY);
            
            // Copy result to temp
            System.arraycopy(result, 0, temp, 0, 3);
            
            // Rotate around Z-axis
            result[0] = temp[0] * Math.cos(radZ) - temp[1] * Math.sin(radZ);
            result[1] = temp[0] * Math.sin(radZ) + temp[1] * Math.cos(radZ);
            result[2] = temp[2];
            
            return result;
            
        }
        

      }
    
    

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        
         // Run the application on the Event Dispatch Thread
        SwingUtilities.invokeLater(() -> {
            try {
                // Apply a modern look and feel
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (Exception e) {
                e.printStackTrace();
            }
            
            new RubiksCube().setVisible(true);
        });
        
    }

}


  


The Final Result:

3D Rubik's Cube in Java Swing - 1

3D Rubik's Cube in Java Swing - 2

3D Rubik's Cube in Java Swing - 3



if you want the source code click on the download button below