/******************************************************************************* * 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(); } }