1 /*
  2  * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 package experiments;
 26 
 27 
 28 import javax.swing.JFrame;
 29 import javax.swing.JPanel;
 30 import javax.swing.SwingUtilities;
 31 import java.awt.Color;
 32 import java.awt.Dimension;
 33 import java.awt.Graphics;
 34 import java.awt.Graphics2D;
 35 import java.awt.RenderingHints;
 36 
 37 public class InterpolatedFrame extends JPanel implements Runnable {
 38     // Window Constants
 39     private final int WIDTH = 800, HEIGHT = 600;
 40 
 41     // Logic Variables
 42     private boolean running = false;
 43     private Thread gameThread;
 44 
 45     // Physics Variables (Use doubles for precision)
 46     private double x = 100, lastX = 100;
 47     private double y = 100, lastY = 100;
 48     private double xSpeed = 200; // Pixels per second
 49     private float renderInterpolation = 0;
 50 
 51     public InterpolatedFrame() {
 52         this.setPreferredSize(new Dimension(WIDTH, HEIGHT));
 53         this.setBackground(Color.BLACK);
 54     }
 55 
 56     public void start() {
 57         running = true;
 58         gameThread = new Thread(this);
 59         gameThread.start();
 60     }
 61 
 62     @Override
 63     public void run() {
 64         double nsPerTick = 1000000000.0 / 60.0; // 60 Fixed Updates per second
 65         double delta = 0;
 66         long lastTime = System.nanoTime();
 67 
 68         while (running) {
 69             long now = System.nanoTime();
 70             delta += (now - lastTime) / nsPerTick;
 71             lastTime = now;
 72 
 73             // Fixed Update Loop
 74             while (delta >= 1) {
 75                 updatePhysics();
 76                 delta--;
 77             }
 78 
 79             // Calculate Interpolation for rendering
 80             renderInterpolation = (float) delta;
 81 
 82             // Schedule Render on EDT
 83             SwingUtilities.invokeLater(this::repaint);
 84 
 85             // Cap the loop to save CPU
 86             try { Thread.sleep(2); } catch (InterruptedException e) {}
 87         }
 88     }
 89 
 90     private void updatePhysics() {
 91         // Store previous state for interpolation
 92         lastX = x;
 93         lastY = y;
 94 
 95         // Move (at 60fps, 1 tick = 1/60th of a second)
 96         x += xSpeed / 60.0;
 97 
 98         // Wall Bounce
 99         if (x > WIDTH - 30 || x < 0) xSpeed *= -1;
100     }
101 
102   //  @Override
103     protected void paintComponent(Graphics g) {
104         super.paintComponent(g);
105         Graphics2D g2d = (Graphics2D) g;
106         g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
107 
108         // Interpolated Position: (Current - Last) * Alpha + Last
109         int drawX = (int) ((x - lastX) * renderInterpolation + lastX);
110         int drawY = (int) ((y - lastY) * renderInterpolation + lastY);
111 
112         g2d.setColor(Color.CYAN);
113         g2d.fillOval(drawX, drawY, 30, 30);
114     }
115 
116     public static void main(String[] args) {
117         JFrame frame = new JFrame("InterpolatedFrame");
118         InterpolatedFrame panel = new InterpolatedFrame();
119         frame.add(panel);
120         frame.pack();
121         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
122         frame.setVisible(true);
123         panel.start();
124     }
125 }