/* * Copyright 2014 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.fpl.gamecontroller; import android.graphics.PointF; import android.graphics.RectF; import android.util.Log; /** * A few utility functions and classes used by this sample. */ public class Utils { /** * String used to identify log messages from this program. */ private static final String LOG_TAG = "GameControllerSample"; /** * The 2D vertex positions for a square centered around the origin. * * The vertices are ordered so that one triangle strip can be used to render * the square. * * Using scaling and rotation, these vertices can be used to render any rectangular shape. */ public static final float[] SQUARE_SHAPE = { 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f }; /** * Prints debugging messages to the console. * * Disabled for non-debug builds. * * @param message The message to print to the console. */ public static void logDebug(String message) { if (BuildConfig.DEBUG) { Log.d(LOG_TAG, message); } } /** * Prints an error message to the console. * * Disabled for non-debug builds. * * @param message The message to print to the console. */ public static void logError(String message) { if (BuildConfig.DEBUG) { Log.e(LOG_TAG, message); } } /** * Returns a random floating point value >= lowerBound and < upperBound. */ public static float randFloatInRange(float lowerBound, float upperBound) { return (float) (Math.random() * (upperBound - lowerBound) + lowerBound); } /** * Returns a random integer value >= lowerBound and < upperBound. */ public static int randIntInRange(int lowerBound, int upperBound) { return (int) randFloatInRange(lowerBound, upperBound); } /** * Returns a randomly chosen point inside the given rectangle. * * @param rect a rectangle bounding the location for the point. * @return a new PointF object. */ public static PointF randPointInRect(RectF rect) { float x = randFloatInRange(rect.left, rect.right); float y = randFloatInRange(rect.bottom, rect.top); return new PointF(x, y); } /** * Computes a randomly chosen direction vector. * * @return a new PointF object that is a normalized direction vector. */ public static PointF randDirectionVector() { // Pick a random point in a square centered about the origin. PointF direction = randPointInRect(new RectF(-1.0f, 1.0f, 1.0f, -1.0f)); // Turn the chosen point into a direction vector by normalizing it. normalizeDirectionVector(direction); return direction; } /** * Normalizes the given direction vector. * * Changes the length of the given vector so that it is "1". If the given direction * already has a length of "0", the direction will be set to "(1, 0)". * * @param direction the direction to normalize. */ public static void normalizeDirectionVector(PointF direction) { float length = direction.length(); if (length == 0.0f) { direction.set(1.0f, 0.0f); } else { direction.x /= length; direction.y /= length; } } /** * Determines the squared length of the given 2-component vector. * * @param x the x component of the vector. * @param y the y component of the vector. * @return the squared length of the vector. */ public static float vector2DLengthSquared(float x, float y) { return x * x + y * y; } /** * Determines the length of the given 2-component vector. * * @param x the x component of the vector. * @param y the y component of the vector. * @return the length of the vector. */ public static float vector2DLength(float x, float y) { return (float) Math.sqrt(vector2DLengthSquared(x, y)); } /** * Computes the distance between two 2-d points. * * @param x1 x position of the first point. * @param y1 y position of the first point. * @param x2 x position of the second point. * @param y2 y position of the second point. * @return the distance between the two points. */ public static float distanceBetweenPoints(float x1, float y1, float x2, float y2) { float xSquared = (x2 - x1); xSquared *= xSquared; float ySquared = (y2 - y1); ySquared *= ySquared; return (float) Math.sqrt(xSquared + ySquared); } /** * Ensures that the given value falls within the given range. * * @param value The value to clamp. * @param min The lower bound of the range. * @param max The upper bound of the range. * @return The clamped value. */ public static float clamp(float value, float min, float max) { return Math.max(min, Math.min(max, value)); } /** * Ensures that the given value falls within the given range. * * @param value The value to clamp. * @param min The lower bound of the range. * @param max The upper bound of the range. * @return The clamped value. */ public static float clamp(int value, int min, int max) { return Math.max(min, Math.min(max, value)); } /** * Class for storing and manipulating RGBA colors. * * The color components are stored internally in a 32-bit int, with 8 bits for each * component. * * This class is similar to android.graphics.Color, but differs in 2 main ways: * 1) This class is a container for color data, so it can be instantiated and used as * data type. android.graphics.Color is just a set of static functions for packing * color components into integers. * 2) android.graphics.Color only provides one component packing order, and that order * does not match what OpenGl/OpenGl ES expects. This class provides accessors * to get the colors packed in the order expected by OpenGl/OpenGl ES. */ public static class Color { // Some common colors for easy reference and readability. Add more as needed. public static final Color RED = new Color(1.0f, 0.0f, 0.0f); public static final Color GREEN = new Color(0.0f, 1.0f, 0.0f); public static final Color BLUE = new Color(0.0f, 0.0f, 1.0f); public static final Color YELLOW = new Color(1.0f, 1.0f, 0.0f); public static final Color WHITE = new Color(1.0f, 1.0f, 1.0f); // Masks and bit locations for each color component. private static final int RED_MASK = 0xffffff00; private static final int RED_SHIFT = 0; private static final int GREEN_MASK = 0xffff00ff; private static final int GREEN_SHIFT = 8; private static final int BLUE_MASK = 0xff00ffff; private static final int BLUE_SHIFT = 16; private static final int ALPHA_MASK = 0x00ffffff; private static final int ALPHA_SHIFT = 24; /** * The packed color data. * * Alpha is packed into the high-order byte and red is the low-order byte. This * matches the component packing order used by OpenGl/OpenGl ES. */ private int mABGR; /** * Converts a floating point color value in the range [0..1] to an integer in the * range [0..255]. * * @param normalizedColor A number in the range 0 to 1, inclusive. No range-checking * is performed. * @return An int in the range 0 to 255, inclusive. */ private static int normalizedColorToInt(float normalizedColor) { return (int) (255.0f * normalizedColor); } /** * Converts an integer color value in the range [0..255] to floating point number in the * range [0..1]. * * @param intColor A number in the range 0 to 255, inclusive. No range-checking * is performed. * @return A float in the range 0 to 1, inclusive. */ private static float intColorToNormalized(int intColor) { return (float) intColor / 255.0f; } /** * Packs 4 floating point color components into a single 32-bit integer. * * The color components must be in the range 0 to 1, inclusive. */ private static int packNormalizedRGBAToABGR(float red, float green, float blue, float alpha) { return packABGR( normalizedColorToInt(red), normalizedColorToInt(green), normalizedColorToInt(blue), normalizedColorToInt(alpha)); } /** * Packs 4 integer color components into a single 32-bit integer. * * The color components must be in the range 0 to 255, inclusive. */ private static int packABGR(int red, int green, int blue, int alpha) { return (red << RED_SHIFT) | (green << GREEN_SHIFT) | (blue << BLUE_SHIFT) | (alpha << ALPHA_SHIFT); } /** * Creates an uninitialized color object. */ public Color() {} /** * Creates a Color object with the given color components. * * Each component must be in the range 0 to 1, inclusive. */ public Color(float red, float green, float blue, float alpha) { set(red, green, blue, alpha); } /** * Creates a Color object with the given color components and an alpha of 1.0. * * Each component must be in the range 0 to 1, inclusive. */ public Color(float red, float green, float blue) { mABGR = packNormalizedRGBAToABGR(red, green, blue, 1.0f); } /** * Creates a copy of the given color. */ public Color(Utils.Color other) { set(other); } /** * Sets all of the color components. * * Each component must be in the range 0 to 1, inclusive. */ public void set(float red, float green, float blue, float alpha) { mABGR = packNormalizedRGBAToABGR(red, green, blue, alpha); } /** * Changes our color to match the given color. */ public void set(Color other) { this.mABGR = other.mABGR; } /** * Returns a packed integer representation of this color. * * @return The 32-bit packed color, with 8 bits per components. Alpha is in the * high-order bits, then blue, then green, and then red in the low-order bits. */ public int getPackedABGR() { return mABGR; } /** * Returns the red component of the color as a floating point number in the * range 0 to 1, inclusive. */ public float red() { return intColorToNormalized((mABGR >> RED_SHIFT) & 0xff); } /** * Sets the red component of the color. The given color component must be a * a floating point number in the range 0 to 1, inclusive. */ public void setRed(float red) { mABGR = (mABGR & RED_MASK) | (normalizedColorToInt(red) << RED_SHIFT); } /** * Returns the green component of the color as a floating point number in the * range 0 to 1, inclusive. */ public float green() { return intColorToNormalized((mABGR >> GREEN_SHIFT) & 0xff); } /** * Sets the green component of the color. The given color component must be a * a floating point number in the range 0 to 1, inclusive. */ public void setGreen(float green) { mABGR = (mABGR & GREEN_MASK) | (normalizedColorToInt(green) << GREEN_SHIFT); } /** * Returns the blue component of the color as a floating point number in the * range 0 to 1, inclusive. */ public float blue() { return intColorToNormalized((mABGR >> BLUE_SHIFT) & 0xff); } /** * Sets the blue component of the color. The given color component must be a * a floating point number in the range 0 to 1, inclusive. */ public void setBlue(float blue) { mABGR = (mABGR & BLUE_MASK) | (normalizedColorToInt(blue) << BLUE_SHIFT); } /** * Returns the alpha component of the color as a floating point number in the * range 0 to 1, inclusive. */ public float alpha() { return intColorToNormalized((mABGR >> ALPHA_SHIFT) & 0xff); } /** * Sets the alpha component of the color. The given color component must be a * a floating point number in the range 0 to 1, inclusive. */ public void setAlpha(float alpha) { mABGR = (mABGR & ALPHA_MASK) | (normalizedColorToInt(alpha) << ALPHA_SHIFT); } /** * Changes this color by multiplying each color component by the given factor. * * The alpha component of the color remains unchanged. * * @param factor A value in the range 0..1, inclusive, to indicate how much darkening * to apply. 0 sets the color to black, and 1 leaves the color unchanged. * Values outside of this range have undefined results. */ public void darken(float factor) { set(red() * factor, green() * factor, blue() * factor, alpha()); } /** * Sets this color to a color computed by interpolating between two other colors. * * The interpolated color is created by linearly interpolating each component * of the two given colors. * * @param colorA The first color to mix. * @param colorB The second color to mix. * @param factor A value between 0 and 1. A value of "0" will set this color to * colorA, and a value of "1" will set this color to colorB. */ public void setToLerp(Color colorA, Color colorB, float factor) { set(colorA.red() * factor + colorB.red() * (1.0f - factor), colorA.green() * factor + colorB.green() * (1.0f - factor), colorA.blue() * factor + colorB.blue() * (1.0f - factor), colorA.alpha() * factor + colorB.alpha() * (1.0f - factor)); } } }