/*
* Copyright (C) 2016 Google Inc. All Rights Reserved.
*
* 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.google.android.apps.santatracker.doodles.shared;
import static com.google.android.apps.santatracker.doodles.shared.Interpolator.EASE_IN_AND_OUT;
/**
* A camera class to contain the scale, translation, and rotation of the world. Note that the camera
* is defined to be positioned at the top-left corner of the screen.
*/
public class Camera extends Actor {
public int screenWidth;
public int screenHeight;
public Camera(int screenWidth, int screenHeight) {
this.position = Vector2D.get();
scale = 1.0f;
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
}
/**
* Get the world coordinates for the screen coordinates specified.
*
* @param x The x value in screen space.
* @param y The y value in screen space.
*/
public Vector2D getWorldCoords(float x, float y) {
return Vector2D.get(xToWorld(x), yToWorld(y));
}
public float xToWorld(float x) {
return position.x + x / scale;
}
public float yToWorld(float y) {
return position.y + y / scale;
}
/**
* Converts a length from screen scale to world space.
*
* @param dimension: the length in screen space.
* @return the length in world space.
*/
public float toWorldScale(float dimension) {
return dimension / scale;
}
/**
* Move the center of the camera's viewport to the specified position.
*
* @param position The position to center the camera on.
*/
public void focusOn(Vector2D position) {
this.position.set(getPositionToFocusOn(position, scale));
}
/**
* Get the camera position needed to focus on the specified position at the specified scale.
*
* @param position The position to center on.
* @param scale The scale at which to focus.
*/
private Vector2D getPositionToFocusOn(Vector2D position, float scale) {
return Vector2D.get(position).subtract((screenWidth / 2) / scale, (screenHeight / 2) / scale);
}
/**
* Move the camer immediately so that it can see the bounding box specified by the min and max
* position vectors.
*
* @param levelMinPosition The desired minimum visible portion of the level.
* @param levelMaxPosition The desired maximum visible portion of the level.
*/
public void moveImmediatelyTo(Vector2D levelMinPosition, Vector2D levelMaxPosition) {
Vector2D levelDimens = Vector2D.get(levelMaxPosition).subtract(levelMinPosition);
float pannedScale = Math.min(screenWidth / levelDimens.x, screenHeight / levelDimens.y);
Vector2D screenDimensInWorldCoords = Vector2D.get(screenWidth, screenHeight)
.scale(1 / pannedScale);
// pannedPosition = levelMinPosition - (screenDimensInWorldCoords - levelDimens) / 2
Vector2D pannedPosition = Vector2D.get(levelMinPosition).subtract(
(screenDimensInWorldCoords.x - levelDimens.x) * 0.5f,
(screenDimensInWorldCoords.y - levelDimens.y) * 0.5f);
position.set(pannedPosition);
scale = pannedScale;
screenDimensInWorldCoords.release();
levelDimens.release();
pannedPosition.release();
}
/**
* Pan to the specified position over the specified duration.
*
* @param levelMinPosition The desired minimum visible portion of the level.
* @param levelMaxPosition The desired maximum visible portion of the level.
* @param duration How many seconds the pan should take.
* @return The tween to pan the camera.
*/
public Tween panTo(
final Vector2D levelMinPosition, final Vector2D levelMaxPosition, float duration) {
final Vector2D startMin = Vector2D.get(position);
final Vector2D startMax = getMaxVisiblePosition();
Tween panTween = new Tween(duration) {
@Override
protected void updateValues(float percentDone) {
float xMin = EASE_IN_AND_OUT.getValue(percentDone, startMin.x, levelMinPosition.x);
float xMax = EASE_IN_AND_OUT.getValue(percentDone, startMax.x, levelMaxPosition.x);
float yMin = EASE_IN_AND_OUT.getValue(percentDone, startMin.y, levelMinPosition.y);
float yMax = EASE_IN_AND_OUT.getValue(percentDone, startMax.y, levelMaxPosition.y);
Vector2D min = Vector2D.get(xMin, yMin);
Vector2D max = Vector2D.get(xMax, yMax);
moveImmediatelyTo(min, max);
min.release();
max.release();
}
@Override
protected void onFinish() {
startMin.release();
startMax.release();
}
};
return panTween;
}
private Vector2D getMaxVisiblePosition() {
return Vector2D.get(position.x + screenWidth / scale, position.y + screenHeight / scale);
}
}