blob: 3476c5984017b4991cc149da58400297b8a704f3 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.chartlib;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.Timer;
public class AnimatedComponent extends JComponent implements ActionListener, HierarchyListener {
protected static final Font DEFAULT_FONT = new Font("Sans", Font.PLAIN, 10);
protected final Timer mTimer;
/**
* The length of the last frame in seconds.
*/
protected float mFrameLength;
protected long mLastRenderTime;
protected boolean mDrawDebugInfo;
protected boolean mUpdateData;
protected boolean mStep;
private List<String> mDebugInfo;
public AnimatedComponent(int fps) {
mUpdateData = true;
mTimer = new Timer(1000 / fps, this);
mDebugInfo = new LinkedList<String>();
addHierarchyListener(this);
}
/**
* A linear interpolation that accumulates over time. This gives an exponential effect where the
* value {@code from} moves towards the value {@code to} at a rate of {@code fraction} per
* second. The actual interpolated amount depends on the current frame length.
*
* @param from the value to interpolate from.
* @param to the target value.
* @param fraction the interpolation fraction.
* @return the interpolated value.
*/
protected float lerp(float from, float to, float fraction) {
float q = (float) Math.pow(1.0f - fraction, mFrameLength);
return from * q + to * (1.0f - q);
}
public boolean isDrawDebugInfo() {
return mDrawDebugInfo;
}
public void setDrawDebugInfo(boolean drawDebugInfo) {
mDrawDebugInfo = drawDebugInfo;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
// Update frame length.
long now = System.nanoTime();
mFrameLength = (now - mLastRenderTime) / 1000000000.0f;
mLastRenderTime = now;
if (mUpdateData || mStep) {
mStep = false;
updateData();
}
draw(g2d);
if (mDrawDebugInfo) {
doDebugDraw(g2d);
}
g2d.dispose();
}
protected void addDebugInfo(String format, Object... values) {
if (mDrawDebugInfo) {
mDebugInfo.add(String.format(format, values));
}
}
private void doDebugDraw(Graphics2D g) {
debugDraw(g);
addDebugInfo("Render time: %.2fms", (System.nanoTime() - mLastRenderTime) / 1000000.f);
addDebugInfo("FPS: %.2f", (1.0f / mFrameLength));
g.setFont(DEFAULT_FONT);
g.setColor(Color.BLACK);
int i = 0;
for (String s : mDebugInfo) {
g.drawString(s, getSize().width - 150, getSize().height - 10 * i++ - 5);
}
mDebugInfo.clear();
}
/**
* First step of the animation, this is where the data is read and the current animation
* values are fixed.
*/
protected void updateData() {
}
/**
* Renders the data constructed in the update phase to the given graphics context.
*/
protected void draw(Graphics2D g) {
}
/**
* Draws visual debug information.
*/
protected void debugDraw(Graphics2D g) {
}
@Override
public void actionPerformed(ActionEvent actionEvent) {
repaint();
}
@Override
public void hierarchyChanged(HierarchyEvent hierarchyEvent) {
if (mTimer.isRunning() && !isShowing()) {
mTimer.stop();
} else if (!mTimer.isRunning() && isShowing()) {
mTimer.start();
}
}
public void setUpdateData(boolean updateData) {
mUpdateData = updateData;
}
public void step() {
mStep = true;
}
}