/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* 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.badlogic.gdx.tests;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.List;
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.tests.utils.GdxTest;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.reflect.ClassReflection;
import com.badlogic.gdx.utils.reflect.Field;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
public class InterpolationTest extends GdxTest {
Stage stage;
private Skin skin;
private Table table;
List<String> list;
String interpolationNames[], selectedInterpolation;
private ShapeRenderer renderer;
float graphSize, steps, time = 0, duration = 2.5f;
Vector2 startPosition = new Vector2(), targetPosition = new Vector2(), position = new Vector2();
/** resets {@link #startPosition} and {@link #targetPosition} */
void resetPositions () {
startPosition.set(stage.getWidth() - stage.getWidth() / 5f, stage.getHeight() - stage.getHeight() / 5f);
targetPosition.set(startPosition.x, stage.getHeight() / 5f);
}
/** @return the {@link #position} with the {@link #selectedInterpolation interpolation} applied */
Vector2 getPosition (float time) {
position.set(targetPosition);
position.sub(startPosition);
position.scl(getInterpolation(selectedInterpolation).apply(time / duration));
position.add(startPosition);
return position;
}
/** @return the {@link #selectedInterpolation selected} interpolation */
private Interpolation getInterpolation (String name) {
try {
return (Interpolation)ClassReflection.getField(Interpolation.class, name).get(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void create () {
Gdx.gl.glClearColor(.3f, .3f, .3f, 1);
renderer = new ShapeRenderer();
skin = new Skin(Gdx.files.internal("data/uiskin.json"));
stage = new Stage(new ScreenViewport());
resetPositions();
Field[] interpolationFields = ClassReflection.getFields(Interpolation.class);
// see how many fields are actually interpolations (for safety; other fields may be added with future)
int interpolationMembers = 0;
for (int i = 0; i < interpolationFields.length; i++)
if (ClassReflection.isAssignableFrom(Interpolation.class, interpolationFields[i].getDeclaringClass()))
interpolationMembers++;
// get interpolation names
interpolationNames = new String[interpolationMembers];
for (int i = 0; i < interpolationFields.length; i++)
if (ClassReflection.isAssignableFrom(Interpolation.class, interpolationFields[i].getDeclaringClass()))
interpolationNames[i] = interpolationFields[i].getName();
selectedInterpolation = interpolationNames[0];
list = new List(skin);
list.setItems(interpolationNames);
list.addListener(new ChangeListener() {
public void changed (ChangeEvent event, Actor actor) {
selectedInterpolation = list.getSelected();
time = 0;
resetPositions();
}
});
ScrollPane scroll = new ScrollPane(list, skin);
scroll.setFadeScrollBars(false);
scroll.setScrollingDisabled(true, false);
table = new Table();
table.setFillParent(true);
table.add(scroll).expandX().left().width(100);
stage.addActor(table);
Gdx.input.setInputProcessor(new InputMultiplexer(new InputAdapter() {
public boolean scrolled (int amount) {
if (!Gdx.input.isKeyPressed(Keys.CONTROL_LEFT)) return false;
duration -= amount / 15f;
duration = MathUtils.clamp(duration, 0, Float.POSITIVE_INFINITY);
return true;
}
}, stage, new InputAdapter() {
public boolean touchDown (int screenX, int screenY, int pointer, int button) {
if (!Float.isNaN(time)) // if "walking" was interrupted by this touch down event
startPosition.set(getPosition(time)); // set startPosition to the current position
targetPosition.set(stage.screenToStageCoordinates(targetPosition.set(screenX, screenY)));
time = 0;
return true;
}
}));
}
public void render () {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
float bottomLeftX = Gdx.graphics.getWidth() / 2 - graphSize / 2, bottomLeftY = Gdx.graphics.getHeight() / 2 - graphSize / 2;
// only show up to two decimals
String text = String.valueOf(duration);
if (text.length() > 4) text = text.substring(0, text.lastIndexOf('.') + 3);
text = "duration: " + text + " s (ctrl + scroll to change)";
stage.getBatch().begin();
list.getStyle().font.draw(stage.getBatch(), text, bottomLeftX + graphSize / 2, bottomLeftY + graphSize
+ list.getStyle().font.getLineHeight(), 0, Align.center, false);
stage.getBatch().end();
renderer.begin(ShapeType.Line);
renderer.rect(bottomLeftX, bottomLeftY, graphSize, graphSize); // graph bounds
float lastX = bottomLeftX, lastY = bottomLeftY;
for (float step = 0; step <= steps; step++) {
Interpolation interpolation = getInterpolation(selectedInterpolation);
float percent = step / steps;
float x = bottomLeftX + graphSize * percent, y = bottomLeftY + graphSize * interpolation.apply(percent);
renderer.line(lastX, lastY, x, y);
lastX = x;
lastY = y;
}
time += Gdx.graphics.getDeltaTime();
if (time > duration) {
time = Float.NaN; // stop "walking"
startPosition.set(targetPosition); // set startPosition to targetPosition for next click
}
// draw time marker
renderer.line(bottomLeftX + graphSize * time / duration, bottomLeftY, bottomLeftX + graphSize * time / duration,
bottomLeftY + graphSize);
// draw path
renderer.setColor(Color.GRAY);
renderer.line(startPosition, targetPosition);
renderer.setColor(Color.WHITE);
renderer.end();
// draw the position
renderer.begin(ShapeType.Filled);
if (!Float.isNaN(time)) // don't mess up position if time is NaN
getPosition(time);
renderer.circle(position.x, position.y, 7);
renderer.end();
stage.act();
stage.draw();
}
public void resize (int width, int height) {
stage.getViewport().update(width, height, true);
table.invalidateHierarchy();
renderer.setProjectionMatrix(stage.getViewport().getCamera().combined);
graphSize = 0.75f * Math.min(stage.getViewport().getWorldWidth(), stage.getViewport().getWorldHeight());
steps = graphSize * 0.5f;
}
public void dispose () {
stage.dispose();
skin.dispose();
}
}