mirror of
https://github.com/SlimeVR/SlimeVR-Server.git
synced 2026-04-05 18:01:56 +02:00
Remove Slime Java Commons submodule, (#236)
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,6 +1,3 @@
|
||||
[submodule "slime-java-commons"]
|
||||
path = slime-java-commons
|
||||
url = https://github.com/Eirenliel/slime-java-commons.git
|
||||
[submodule "solarxr-protocol"]
|
||||
path = solarxr-protocol
|
||||
url = https://github.com/SlimeVR/SolarXR-Protocol.git
|
||||
|
||||
@@ -42,7 +42,6 @@ allprojects {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':slime-java-commons')
|
||||
implementation project(":solarxr-protocol")
|
||||
|
||||
|
||||
|
||||
665
java/com/jme3/math/ColorRGBA.java
Normal file
665
java/com/jme3/math/ColorRGBA.java
Normal file
@@ -0,0 +1,665 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 jMonkeyEngine All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* *
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.math;
|
||||
|
||||
import io.eiren.math.FloatMath;
|
||||
|
||||
|
||||
/**
|
||||
* <code>ColorRGBA</code> defines a color made from a collection of red, green
|
||||
* and blue values. An alpha value determines is transparency. All values must
|
||||
* be between 0 and 1. If any value is set higher or lower than these
|
||||
* constraints they are clamped to the min or max. That is, if a value smaller
|
||||
* than zero is set the value clamps to zero. If a value higher than 1 is
|
||||
* passed, that value is clamped to 1. However, because the attributes r, g, b,
|
||||
* a are public for efficiency reasons, they can be directly modified with
|
||||
* invalid values. The client should take care when directly addressing the
|
||||
* values. A call to clamp will assure that the values are within the
|
||||
* constraints.
|
||||
*
|
||||
* @author Mark Powell
|
||||
* @version $Id: ColorRGBA.java,v 1.29 2007/09/09 18:25:14 irrisor Exp $
|
||||
*/
|
||||
public final class ColorRGBA implements Cloneable, java.io.Serializable {
|
||||
|
||||
static final long serialVersionUID = 1;
|
||||
/**
|
||||
* The color black (0,0,0).
|
||||
*/
|
||||
public static final ColorRGBA Black = new ColorRGBA(0f, 0f, 0f, 1f);
|
||||
/**
|
||||
* The color white (1,1,1).
|
||||
*/
|
||||
public static final ColorRGBA White = new ColorRGBA(1f, 1f, 1f, 1f);
|
||||
/**
|
||||
* The color gray (.2,.2,.2).
|
||||
*/
|
||||
public static final ColorRGBA DarkGray = new ColorRGBA(0.2f, 0.2f, 0.2f, 1.0f);
|
||||
/**
|
||||
* The color gray (.5,.5,.5).
|
||||
*/
|
||||
public static final ColorRGBA Gray = new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
/**
|
||||
* The color gray (.8,.8,.8).
|
||||
*/
|
||||
public static final ColorRGBA LightGray = new ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f);
|
||||
/**
|
||||
* The color red (1,0,0).
|
||||
*/
|
||||
public static final ColorRGBA Red = new ColorRGBA(1f, 0f, 0f, 1f);
|
||||
/**
|
||||
* The color green (0,1,0).
|
||||
*/
|
||||
public static final ColorRGBA Green = new ColorRGBA(0f, 1f, 0f, 1f);
|
||||
/**
|
||||
* The color blue (0,0,1).
|
||||
*/
|
||||
public static final ColorRGBA Blue = new ColorRGBA(0f, 0f, 1f, 1f);
|
||||
/**
|
||||
* The color yellow (1,1,0).
|
||||
*/
|
||||
public static final ColorRGBA Yellow = new ColorRGBA(1f, 1f, 0f, 1f);
|
||||
/**
|
||||
* The color magenta (1,0,1).
|
||||
*/
|
||||
public static final ColorRGBA Magenta = new ColorRGBA(1f, 0f, 1f, 1f);
|
||||
/**
|
||||
* The color cyan (0,1,1).
|
||||
*/
|
||||
public static final ColorRGBA Cyan = new ColorRGBA(0f, 1f, 1f, 1f);
|
||||
/**
|
||||
* The color orange (251/255, 130/255,0).
|
||||
*/
|
||||
public static final ColorRGBA Orange = new ColorRGBA(251f / 255f, 130f / 255f, 0f, 1f);
|
||||
/**
|
||||
* The color brown (65/255, 40/255, 25/255).
|
||||
*/
|
||||
public static final ColorRGBA Brown = new ColorRGBA(65f / 255f, 40f / 255f, 25f / 255f, 1f);
|
||||
/**
|
||||
* The color pink (1, 0.68, 0.68).
|
||||
*/
|
||||
public static final ColorRGBA Pink = new ColorRGBA(1f, 0.68f, 0.68f, 1f);
|
||||
/**
|
||||
* The black color with no alpha (0, 0, 0, 0).
|
||||
*/
|
||||
public static final ColorRGBA BlackNoAlpha = new ColorRGBA(0f, 0f, 0f, 0f);
|
||||
/**
|
||||
* The red component of the color. 0 is none and 1 is maximum red.
|
||||
*/
|
||||
public float r;
|
||||
/**
|
||||
* The green component of the color. 0 is none and 1 is maximum green.
|
||||
*/
|
||||
public float g;
|
||||
/**
|
||||
* The blue component of the color. 0 is none and 1 is maximum blue.
|
||||
*/
|
||||
public float b;
|
||||
/**
|
||||
* The alpha component of the color. 0 is transparent and 1 is opaque.
|
||||
*/
|
||||
public float a;
|
||||
|
||||
/**
|
||||
* Constructor instantiates a new <code>ColorRGBA</code> object. This color
|
||||
* is the default "white" with all values 1.
|
||||
*/
|
||||
public ColorRGBA() {
|
||||
r = g = b = a = 1.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor instantiates a new <code>ColorRGBA</code> object. The values
|
||||
* are defined as passed parameters. These values are then clamped to insure
|
||||
* that they are between 0 and 1.
|
||||
*
|
||||
* @param r The red component of this color.
|
||||
* @param g The green component of this <code>ColorRGBA</code>.
|
||||
* @param b The blue component of this <code>ColorRGBA</code>.
|
||||
* @param a The alpha component of this <code>ColorRGBA</code>.
|
||||
*/
|
||||
public ColorRGBA(float r, float g, float b, float a) {
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor creates a new <code>ColorRGBA</code> object, based on a
|
||||
* provided color.
|
||||
*
|
||||
* @param rgba The <code>ColorRGBA</code> object to copy.
|
||||
*/
|
||||
public ColorRGBA(ColorRGBA rgba) {
|
||||
this.a = rgba.a;
|
||||
this.r = rgba.r;
|
||||
this.g = rgba.g;
|
||||
this.b = rgba.b;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>set</code> sets the RGBA values of this <code>ColorRGBA</code>. The
|
||||
* values are then clamped to insure that they are between 0 and 1.
|
||||
*
|
||||
* @param r The red component of this color.
|
||||
* @param g The green component of this color.
|
||||
* @param b The blue component of this color.
|
||||
* @param a The alpha component of this color.
|
||||
* @return this
|
||||
*/
|
||||
public ColorRGBA set(float r, float g, float b, float a) {
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>set</code> sets the values of this <code>ColorRGBA</code> to those
|
||||
* set by a parameter color.
|
||||
*
|
||||
* @param rgba The color to set this <code>ColorRGBA</code> to.
|
||||
* @return this
|
||||
*/
|
||||
public ColorRGBA set(ColorRGBA rgba) {
|
||||
if (rgba == null) {
|
||||
r = 0;
|
||||
g = 0;
|
||||
b = 0;
|
||||
a = 0;
|
||||
} else {
|
||||
r = rgba.r;
|
||||
g = rgba.g;
|
||||
b = rgba.b;
|
||||
a = rgba.a;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorRGBA setR(float r) {
|
||||
this.r = r;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorRGBA setG(float g) {
|
||||
this.g = g;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorRGBA setB(float b) {
|
||||
this.b = b;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorRGBA setA(float a) {
|
||||
this.a = a;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>clamp</code> insures that all values are between 0 and 1. If any
|
||||
* are less than 0 they are set to zero. If any are more than 1 they are set
|
||||
* to one.
|
||||
*/
|
||||
public void clamp() {
|
||||
if (r < 0) {
|
||||
r = 0;
|
||||
} else if (r > 1) {
|
||||
r = 1;
|
||||
}
|
||||
|
||||
if (g < 0) {
|
||||
g = 0;
|
||||
} else if (g > 1) {
|
||||
g = 1;
|
||||
}
|
||||
|
||||
if (b < 0) {
|
||||
b = 0;
|
||||
} else if (b > 1) {
|
||||
b = 1;
|
||||
}
|
||||
|
||||
if (a < 0) {
|
||||
a = 0;
|
||||
} else if (a > 1) {
|
||||
a = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>getColorArray</code> retrieves the color values of this
|
||||
* <code>ColorRGBA</code> as a four element <code>float</code> array in the
|
||||
* order: r,g,b,a.
|
||||
*
|
||||
* @return The <code>float</code> array that contains the color components.
|
||||
*/
|
||||
public float[] getColorArray() {
|
||||
return new float[] { r, g, b, a };
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the current r,g,b,a values into the given array. The given array
|
||||
* must have a length of 4 or greater, or an array index out of bounds
|
||||
* exception will be thrown.
|
||||
*
|
||||
* @param store The <code>float</code> array to store the values into.
|
||||
* @return The <code>float</code> array after storage.
|
||||
*/
|
||||
public float[] getColorArray(float[] store) {
|
||||
store[0] = r;
|
||||
store[1] = g;
|
||||
store[2] = b;
|
||||
store[3] = a;
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the alpha component value of this <code>ColorRGBA</code>.
|
||||
*
|
||||
* @return The alpha component value.
|
||||
*/
|
||||
public float getAlpha() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the red component value of this <code>ColorRGBA</code>.
|
||||
*
|
||||
* @return The red component value.
|
||||
*/
|
||||
public float getRed() {
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the blue component value of this <code>ColorRGBA</code>.
|
||||
*
|
||||
* @return The blue component value.
|
||||
*/
|
||||
public float getBlue() {
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the green component value of this <code>ColorRGBA</code>.
|
||||
*
|
||||
* @return The green component value.
|
||||
*/
|
||||
public float getGreen() {
|
||||
return g;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this <code>ColorRGBA</code> to the interpolation by changeAmnt from
|
||||
* this to the finalColor: this=(1-changeAmnt)*this + changeAmnt *
|
||||
* finalColor
|
||||
*
|
||||
* @param finalColor The final color to interpolate towards.
|
||||
* @param changeAmnt An amount between 0.0 - 1.0 representing a percentage
|
||||
* change from this towards finalColor.
|
||||
*/
|
||||
public void interpolate(ColorRGBA finalColor, float changeAmnt) {
|
||||
this.r = (1 - changeAmnt) * this.r + changeAmnt * finalColor.r;
|
||||
this.g = (1 - changeAmnt) * this.g + changeAmnt * finalColor.g;
|
||||
this.b = (1 - changeAmnt) * this.b + changeAmnt * finalColor.b;
|
||||
this.a = (1 - changeAmnt) * this.a + changeAmnt * finalColor.a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this <code>ColorRGBA</code> to the interpolation by changeAmnt from
|
||||
* beginColor to finalColor: this=(1-changeAmnt)*beginColor + changeAmnt *
|
||||
* finalColor
|
||||
*
|
||||
* @param beginColor The begining color (changeAmnt=0).
|
||||
* @param finalColor The final color to interpolate towards (changeAmnt=1).
|
||||
* @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
|
||||
* change from beginColor towards finalColor.
|
||||
*/
|
||||
public void interpolate(ColorRGBA beginColor, ColorRGBA finalColor, float changeAmnt) {
|
||||
this.r = (1 - changeAmnt) * beginColor.r + changeAmnt * finalColor.r;
|
||||
this.g = (1 - changeAmnt) * beginColor.g + changeAmnt * finalColor.g;
|
||||
this.b = (1 - changeAmnt) * beginColor.b + changeAmnt * finalColor.b;
|
||||
this.a = (1 - changeAmnt) * beginColor.a + changeAmnt * finalColor.a;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>randomColor</code> is a utility method that generates a random
|
||||
* opaque color.
|
||||
*
|
||||
* @return a random <code>ColorRGBA</code> with an alpha set to 1.
|
||||
*/
|
||||
public static ColorRGBA randomColor() {
|
||||
ColorRGBA rVal = new ColorRGBA(0, 0, 0, 1);
|
||||
rVal.r = FastMath.nextRandomFloat();
|
||||
rVal.g = FastMath.nextRandomFloat();
|
||||
rVal.b = FastMath.nextRandomFloat();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies each r,g,b,a of this <code>ColorRGBA</code> by the
|
||||
* corresponding r,g,b,a of the given color and returns the result as a new
|
||||
* <code>ColorRGBA</code>. Used as a way of combining colors and lights.
|
||||
*
|
||||
* @param c The color to multiply by.
|
||||
* @return The new <code>ColorRGBA</code>. this*c
|
||||
*/
|
||||
public ColorRGBA mult(ColorRGBA c) {
|
||||
return new ColorRGBA(c.r * r, c.g * g, c.b * b, c.a * a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies each r,g,b,a of this <code>ColorRGBA</code> by the given
|
||||
* scalar and returns the result as a new <code>ColorRGBA</code>. Used as a
|
||||
* way of making colors dimmer or brighter.
|
||||
*
|
||||
* @param scalar The scalar to multiply by.
|
||||
* @return The new <code>ColorRGBA</code>. this*scalar
|
||||
*/
|
||||
public ColorRGBA mult(float scalar) {
|
||||
return new ColorRGBA(scalar * r, scalar * g, scalar * b, scalar * a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies each r,g,b,a of this <code>ColorRGBA</code> by the given
|
||||
* scalar and returns the result (this). Used as a way of making colors
|
||||
* dimmer or brighter.
|
||||
*
|
||||
* @param scalar The scalar to multiply by.
|
||||
* @return this*c
|
||||
*/
|
||||
public ColorRGBA multLocal(float scalar) {
|
||||
this.r *= scalar;
|
||||
this.g *= scalar;
|
||||
this.b *= scalar;
|
||||
this.a *= scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds each r,g,b,a of this <code>ColorRGBA</code> by the corresponding
|
||||
* r,g,b,a of the given color and returns the result as a new
|
||||
* <code>ColorRGBA</code>. Used as a way of combining colors and lights.
|
||||
*
|
||||
* @param c The color to add.
|
||||
* @return The new <code>ColorRGBA</code>. this+c
|
||||
*/
|
||||
public ColorRGBA add(ColorRGBA c) {
|
||||
return new ColorRGBA(c.r + r, c.g + g, c.b + b, c.a + a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds each r,g,b,a of this <code>ColorRGBA</code> by the r,g,b,a the given
|
||||
* color and returns the result (this). Used as a way of combining colors
|
||||
* and lights.
|
||||
*
|
||||
* @param c The color to add.
|
||||
* @return this+c
|
||||
*/
|
||||
public ColorRGBA addLocal(ColorRGBA c) {
|
||||
set(c.r + r, c.g + g, c.b + b, c.a + a);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies alpha component by the given scalar and returns the result as
|
||||
* a new color.
|
||||
*/
|
||||
public ColorRGBA dilute(float scalarA) {
|
||||
return new ColorRGBA(r, g, b, a * scalarA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies alpha component by the given scalar and returns the result
|
||||
* (this).
|
||||
*/
|
||||
public ColorRGBA diluteLocal(float scalarA) {
|
||||
this.a *= scalarA;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>toString</code> returns the string representation of this
|
||||
* <code>ColorRGBA</code>. The format of the string is:<br>
|
||||
* <Class Name>: [R=RR.RRRR, G=GG.GGGG, B=BB.BBBB, A=AA.AAAA]
|
||||
*
|
||||
* @return The string representation of this <code>ColorRGBA</code>.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Color[" + r + ", " + g + ", " + b + ", " + a + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorRGBA clone() {
|
||||
try {
|
||||
return (ColorRGBA) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError(); // can not happen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this <code>ColorRGBA</code> into the given <code>float</code>
|
||||
* array.
|
||||
*
|
||||
* @param floats The <code>float</code> array to take this
|
||||
* <code>ColorRGBA</code>. If null, a new <code>float[4]</code> is created.
|
||||
* @return The array, with r,g,b,a float values in that order.
|
||||
*/
|
||||
public float[] toArray(float[] floats) {
|
||||
if (floats == null) {
|
||||
floats = new float[4];
|
||||
}
|
||||
floats[0] = r;
|
||||
floats[1] = g;
|
||||
floats[2] = b;
|
||||
floats[3] = a;
|
||||
return floats;
|
||||
}
|
||||
|
||||
public ColorRGBA fromArray(float[] floats) {
|
||||
r = floats[0];
|
||||
g = floats[1];
|
||||
b = floats[2];
|
||||
a = floats[3];
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>equals</code> returns true if this <code>ColorRGBA</code> is
|
||||
* logically equivalent to a given color. That is, if all the components of
|
||||
* the two colors are the same. False is returned otherwise.
|
||||
*
|
||||
* @param o The object to compare against.
|
||||
* @return true if the colors are equal, false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof ColorRGBA)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ColorRGBA comp = (ColorRGBA) o;
|
||||
if (!FloatMath.equalsWithEpsilon(r, comp.r, FastMath.ZERO_TOLERANCE)) {
|
||||
return false;
|
||||
}
|
||||
if (!FloatMath.equalsWithEpsilon(g, comp.g, FastMath.ZERO_TOLERANCE)) {
|
||||
return false;
|
||||
}
|
||||
if (!FloatMath.equalsWithEpsilon(b, comp.b, FastMath.ZERO_TOLERANCE)) {
|
||||
return false;
|
||||
}
|
||||
if (!FloatMath.equalsWithEpsilon(a, comp.a, FastMath.ZERO_TOLERANCE)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>hashCode</code> returns a unique code for this
|
||||
* <code>ColorRGBA</code> based on its values. If two colors are logically
|
||||
* equivalent, they will return the same hash code value.
|
||||
*
|
||||
* @return The hash code value of this <code>ColorRGBA</code>.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 37;
|
||||
hash += 37 * hash + Float.floatToIntBits(r);
|
||||
hash += 37 * hash + Float.floatToIntBits(g);
|
||||
hash += 37 * hash + Float.floatToIntBits(b);
|
||||
hash += 37 * hash + Float.floatToIntBits(a);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the component values of this <code>ColorRGBA</code> as a four
|
||||
* element <code>byte</code> array in the order: r,g,b,a.
|
||||
*
|
||||
* @return the <code>byte</code> array that contains the color components.
|
||||
*/
|
||||
public byte[] asBytesRGBA() {
|
||||
byte[] store = new byte[4];
|
||||
store[0] = (byte) ((int) (r * 255) & 0xFF);
|
||||
store[1] = (byte) ((int) (g * 255) & 0xFF);
|
||||
store[2] = (byte) ((int) (b * 255) & 0xFF);
|
||||
store[3] = (byte) ((int) (a * 255) & 0xFF);
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the component values of this <code>ColorRGBA</code> as an
|
||||
* <code>int</code> in a,r,g,b order. Bits 24-31 are alpha, 16-23 are red,
|
||||
* 8-15 are green, 0-7 are blue.
|
||||
*
|
||||
* @return The integer representation of this <code>ColorRGBA</code> in
|
||||
* a,r,g,b order.
|
||||
*/
|
||||
public int asIntARGB() {
|
||||
int argb = (((int) (a * 255) & 0xFF) << 24)
|
||||
| (((int) (r * 255) & 0xFF) << 16)
|
||||
| (((int) (g * 255) & 0xFF) << 8)
|
||||
| (((int) (b * 255) & 0xFF));
|
||||
return argb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the component values of this <code>ColorRGBA</code> as an
|
||||
* <code>int</code> in r,g,b,a order. Bits 24-31 are red, 16-23 are green,
|
||||
* 8-15 are blue, 0-7 are alpha.
|
||||
*
|
||||
* @return The integer representation of this <code>ColorRGBA</code> in
|
||||
* r,g,b,a order.
|
||||
*/
|
||||
public int asIntRGBA() {
|
||||
int rgba = (((int) (r * 255) & 0xFF) << 24)
|
||||
| (((int) (g * 255) & 0xFF) << 16)
|
||||
| (((int) (b * 255) & 0xFF) << 8)
|
||||
| (((int) (a * 255) & 0xFF));
|
||||
return rgba;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the component values of this <code>ColorRGBA</code> as an
|
||||
* <code>int</code> in a,b,g,r order. Bits 24-31 are alpha, 16-23 are blue,
|
||||
* 8-15 are green, 0-7 are red.
|
||||
*
|
||||
* @return The integer representation of this <code>ColorRGBA</code> in
|
||||
* a,b,g,r order.
|
||||
*/
|
||||
public int asIntABGR() {
|
||||
int abgr = (((int) (a * 255) & 0xFF) << 24)
|
||||
| (((int) (b * 255) & 0xFF) << 16)
|
||||
| (((int) (g * 255) & 0xFF) << 8)
|
||||
| (((int) (r * 255) & 0xFF));
|
||||
return abgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the component values of this <code>ColorRGBA</code> with the given
|
||||
* combined ARGB <code>int</code>. Bits 24-31 are alpha, bits 16-23 are red,
|
||||
* bits 8-15 are green, bits 0-7 are blue.
|
||||
*
|
||||
* @param color The integer ARGB value used to set this
|
||||
* <code>ColorRGBA</code>.
|
||||
*/
|
||||
public void fromIntARGB(int color) {
|
||||
a = ((byte) (color >> 24) & 0xFF) / 255f;
|
||||
r = ((byte) (color >> 16) & 0xFF) / 255f;
|
||||
g = ((byte) (color >> 8) & 0xFF) / 255f;
|
||||
b = ((byte) (color) & 0xFF) / 255f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the RGBA values of this <code>ColorRGBA</code> with the given
|
||||
* combined RGBA value Bits 24-31 are red, bits 16-23 are green, bits 8-15
|
||||
* are blue, bits 0-7 are alpha.
|
||||
*
|
||||
* @param color The integer RGBA value used to set this object.
|
||||
*/
|
||||
public void fromIntRGBA(int color) {
|
||||
r = ((byte) (color >> 24) & 0xFF) / 255f;
|
||||
g = ((byte) (color >> 16) & 0xFF) / 255f;
|
||||
b = ((byte) (color >> 8) & 0xFF) / 255f;
|
||||
a = ((byte) (color) & 0xFF) / 255f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform this <code>ColorRGBA</code> to a <code>Vector3f</code> using x
|
||||
* = r, y = g, z = b. The Alpha value is not used. This method is useful to
|
||||
* use for shaders assignment.
|
||||
*
|
||||
* @return A <code>Vector3f</code> containing the RGB value of this
|
||||
* <code>ColorRGBA</code>.
|
||||
*/
|
||||
public Vector3f toVector3f() {
|
||||
return new Vector3f(r, g, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform this <code>ColorRGBA</code> to a <code>Vector4f</code> using x
|
||||
* = r, y = g, z = b, w = a. This method is useful to use for shaders
|
||||
* assignment.
|
||||
*
|
||||
* @return A <code>Vector4f</code> containing the RGBA value of this
|
||||
* <code>ColorRGBA</code>.
|
||||
*/
|
||||
public Vector4f toVector4f() {
|
||||
return new Vector4f(r, g, b, a);
|
||||
}
|
||||
}
|
||||
1072
java/com/jme3/math/FastMath.java
Normal file
1072
java/com/jme3/math/FastMath.java
Normal file
File diff suppressed because it is too large
Load Diff
1362
java/com/jme3/math/Matrix3f.java
Normal file
1362
java/com/jme3/math/Matrix3f.java
Normal file
File diff suppressed because it is too large
Load Diff
2277
java/com/jme3/math/Matrix4f.java
Normal file
2277
java/com/jme3/math/Matrix4f.java
Normal file
File diff suppressed because it is too large
Load Diff
1721
java/com/jme3/math/Quaternion.java
Normal file
1721
java/com/jme3/math/Quaternion.java
Normal file
File diff suppressed because it is too large
Load Diff
399
java/com/jme3/math/Transform.java
Normal file
399
java/com/jme3/math/Transform.java
Normal file
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.math;
|
||||
|
||||
/**
|
||||
* Started Date: Jul 16, 2004<br>
|
||||
* <br>
|
||||
* Represents a translation, rotation and scale in one object.
|
||||
*
|
||||
* @author Jack Lindamood
|
||||
* @author Joshua Slack
|
||||
*/
|
||||
public final class Transform implements Cloneable, java.io.Serializable {
|
||||
|
||||
static final long serialVersionUID = 1;
|
||||
|
||||
public static final Transform IDENTITY = new Transform();
|
||||
|
||||
private Quaternion rot = new Quaternion();
|
||||
private Vector3f translation = new Vector3f();
|
||||
private Vector3f scale = new Vector3f(1, 1, 1);
|
||||
|
||||
public Transform(Vector3f translation, Quaternion rot) {
|
||||
this.translation.set(translation);
|
||||
this.rot.set(rot);
|
||||
}
|
||||
|
||||
public Transform(Vector3f translation, Quaternion rot, Vector3f scale) {
|
||||
this(translation, rot);
|
||||
this.scale.set(scale);
|
||||
}
|
||||
|
||||
public Transform(Vector3f translation) {
|
||||
this(translation, Quaternion.IDENTITY);
|
||||
}
|
||||
|
||||
public Transform(Quaternion rot) {
|
||||
this(Vector3f.ZERO, rot);
|
||||
}
|
||||
|
||||
public Transform() {
|
||||
this(Vector3f.ZERO, Quaternion.IDENTITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this rotation to the given Quaternion value.
|
||||
*
|
||||
* @param rot The new rotation for this matrix.
|
||||
* @return this
|
||||
*/
|
||||
public Transform setRotation(Quaternion rot) {
|
||||
this.rot.set(rot);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this translation to the given value.
|
||||
*
|
||||
* @param trans The new translation for this matrix.
|
||||
* @return this
|
||||
*/
|
||||
public Transform setTranslation(Vector3f trans) {
|
||||
this.translation.set(trans);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the translation vector in this matrix.
|
||||
*
|
||||
* @return translation vector.
|
||||
*/
|
||||
public Vector3f getTranslation() {
|
||||
return translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this scale to the given value.
|
||||
*
|
||||
* @param scale The new scale for this matrix.
|
||||
* @return this
|
||||
*/
|
||||
public Transform setScale(Vector3f scale) {
|
||||
this.scale.set(scale);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this scale to the given value.
|
||||
*
|
||||
* @param scale The new scale for this matrix.
|
||||
* @return this
|
||||
*/
|
||||
public Transform setScale(float scale) {
|
||||
this.scale.set(scale, scale, scale);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the scale vector in this matrix.
|
||||
*
|
||||
* @return scale vector.
|
||||
*/
|
||||
public Vector3f getScale() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores this translation value into the given vector3f. If trans is null,
|
||||
* a new vector3f is created to hold the value. The value, once stored, is
|
||||
* returned.
|
||||
*
|
||||
* @param trans The store location for this matrix's translation.
|
||||
* @return The value of this matrix's translation.
|
||||
*/
|
||||
public Vector3f getTranslation(Vector3f trans) {
|
||||
if (trans == null)
|
||||
trans = new Vector3f();
|
||||
trans.set(this.translation);
|
||||
return trans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores this rotation value into the given Quaternion. If quat is null, a
|
||||
* new Quaternion is created to hold the value. The value, once stored, is
|
||||
* returned.
|
||||
*
|
||||
* @param quat The store location for this matrix's rotation.
|
||||
* @return The value of this matrix's rotation.
|
||||
*/
|
||||
public Quaternion getRotation(Quaternion quat) {
|
||||
if (quat == null)
|
||||
quat = new Quaternion();
|
||||
quat.set(rot);
|
||||
return quat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the rotation quaternion in this matrix.
|
||||
*
|
||||
* @return rotation quaternion.
|
||||
*/
|
||||
public Quaternion getRotation() {
|
||||
return rot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores this scale value into the given vector3f. If scale is null, a new
|
||||
* vector3f is created to hold the value. The value, once stored, is
|
||||
* returned.
|
||||
*
|
||||
* @param scale The store location for this matrix's scale.
|
||||
* @return The value of this matrix's scale.
|
||||
*/
|
||||
public Vector3f getScale(Vector3f scale) {
|
||||
if (scale == null)
|
||||
scale = new Vector3f();
|
||||
scale.set(this.scale);
|
||||
return scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this matrix to the interpolation between the first matrix and the
|
||||
* second by delta amount.
|
||||
*
|
||||
* @param t1 The begining transform.
|
||||
* @param t2 The ending transform.
|
||||
* @param delta An amount between 0 and 1 representing how far to
|
||||
* interpolate from t1 to t2.
|
||||
*/
|
||||
public void interpolateTransforms(Transform t1, Transform t2, float delta) {
|
||||
this.rot.slerp(t1.rot, t2.rot, delta);
|
||||
this.translation.interpolate(t1.translation, t2.translation, delta);
|
||||
this.scale.interpolate(t1.scale, t2.scale, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the values of this matrix acording to it's parent. Very similar
|
||||
* to the concept of Node/Spatial transforms.
|
||||
*
|
||||
* @param parent The parent matrix.
|
||||
* @return This matrix, after combining.
|
||||
*/
|
||||
public Transform combineWithParent(Transform parent) {
|
||||
scale.multLocal(parent.scale);
|
||||
// rot.multLocal(parent.rot);
|
||||
parent.rot.mult(rot, rot);
|
||||
|
||||
// This here, is evil code
|
||||
// parent
|
||||
// .rot
|
||||
// .multLocal(translation)
|
||||
// .multLocal(parent.scale)
|
||||
// .addLocal(parent.translation);
|
||||
|
||||
translation.multLocal(parent.scale);
|
||||
parent.rot
|
||||
.multLocal(translation)
|
||||
.addLocal(parent.translation);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #combineWithParent(Transform)}, but assumes that rotation
|
||||
* is global, so it's not modified.
|
||||
*
|
||||
* @param parent
|
||||
* @return
|
||||
*/
|
||||
public Transform combineWithParentGlobalRotation(Transform parent) {
|
||||
scale.multLocal(parent.scale);
|
||||
translation.multLocal(parent.scale);
|
||||
|
||||
parent.rot
|
||||
.multLocal(translation)
|
||||
.addLocal(parent.translation);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this matrix's translation to the given x,y,z values.
|
||||
*
|
||||
* @param x This matrix's new x translation.
|
||||
* @param y This matrix's new y translation.
|
||||
* @param z This matrix's new z translation.
|
||||
* @return this
|
||||
*/
|
||||
public Transform setTranslation(float x, float y, float z) {
|
||||
translation.set(x, y, z);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this matrix's scale to the given x,y,z values.
|
||||
*
|
||||
* @param x This matrix's new x scale.
|
||||
* @param y This matrix's new y scale.
|
||||
* @param z This matrix's new z scale.
|
||||
* @return this
|
||||
*/
|
||||
public Transform setScale(float x, float y, float z) {
|
||||
scale.set(x, y, z);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3f transformVector(final Vector3f in, Vector3f store) {
|
||||
if (store == null)
|
||||
store = new Vector3f();
|
||||
|
||||
// multiply with scale first, then rotate, finally translate (cf.
|
||||
// Eberly)
|
||||
return rot.mult(store.set(in).multLocal(scale), store).addLocal(translation);
|
||||
}
|
||||
|
||||
public Vector3f transformInverseVector(final Vector3f in, Vector3f store) {
|
||||
if (store == null)
|
||||
store = new Vector3f();
|
||||
|
||||
// The author of this code should look above and take the inverse of
|
||||
// that
|
||||
// But for some reason, they didnt ..
|
||||
// in.subtract(translation, store).divideLocal(scale);
|
||||
// rot.inverse().mult(store, store);
|
||||
|
||||
in.subtract(translation, store);
|
||||
rot.inverse().mult(store, store);
|
||||
store.divideLocal(scale);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the identity. Equal to translation=0,0,0 scale=1,1,1 rot=0,0,0,1.
|
||||
*/
|
||||
public void loadIdentity() {
|
||||
translation.set(0, 0, 0);
|
||||
scale.set(1, 1, 1);
|
||||
rot.set(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName()
|
||||
+ "[ "
|
||||
+ translation.x
|
||||
+ ", "
|
||||
+ translation.y
|
||||
+ ", "
|
||||
+ translation.z
|
||||
+ "]\n"
|
||||
+ "[ "
|
||||
+ rot.x
|
||||
+ ", "
|
||||
+ rot.y
|
||||
+ ", "
|
||||
+ rot.z
|
||||
+ ", "
|
||||
+ rot.w
|
||||
+ "]\n"
|
||||
+ "[ "
|
||||
+ scale.x
|
||||
+ " , "
|
||||
+ scale.y
|
||||
+ ", "
|
||||
+ scale.z
|
||||
+ "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this matrix to be equal to the given matrix.
|
||||
*
|
||||
* @param matrixQuat The matrix to be equal to.
|
||||
* @return this
|
||||
*/
|
||||
public Transform set(Transform matrixQuat) {
|
||||
this.translation.set(matrixQuat.translation);
|
||||
this.rot.set(matrixQuat.rot);
|
||||
this.scale.set(matrixQuat.scale);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((rot == null) ? 0 : rot.hashCode());
|
||||
result = prime * result + ((scale == null) ? 0 : scale.hashCode());
|
||||
result = prime * result + ((translation == null) ? 0 : translation.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Transform other = (Transform) obj;
|
||||
if (rot == null) {
|
||||
if (other.rot != null)
|
||||
return false;
|
||||
} else if (!rot.equals(other.rot))
|
||||
return false;
|
||||
if (scale == null) {
|
||||
if (other.scale != null)
|
||||
return false;
|
||||
} else if (!scale.equals(other.scale))
|
||||
return false;
|
||||
if (translation == null) {
|
||||
if (other.translation != null)
|
||||
return false;
|
||||
} else if (!translation.equals(other.translation))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transform clone() {
|
||||
try {
|
||||
Transform tq = (Transform) super.clone();
|
||||
tq.rot = rot.clone();
|
||||
tq.scale = scale.clone();
|
||||
tq.translation = translation.clone();
|
||||
return tq;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
718
java/com/jme3/math/Vector2f.java
Normal file
718
java/com/jme3/math/Vector2f.java
Normal file
@@ -0,0 +1,718 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.math;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* <code>Vector2f</code> defines a Vector for a two float value vector.
|
||||
*
|
||||
* @author Mark Powell
|
||||
* @author Joshua Slack
|
||||
*/
|
||||
public final class Vector2f implements Cloneable, java.io.Serializable {
|
||||
|
||||
static final long serialVersionUID = 1;
|
||||
private static final Logger logger = Logger.getLogger(Vector2f.class.getName());
|
||||
|
||||
public static final Vector2f ZERO = new Vector2f(0f, 0f);
|
||||
public static final Vector2f UNIT_XY = new Vector2f(1f, 1f);
|
||||
|
||||
/**
|
||||
* the x value of the vector.
|
||||
*/
|
||||
public float x;
|
||||
/**
|
||||
* the y value of the vector.
|
||||
*/
|
||||
public float y;
|
||||
|
||||
/**
|
||||
* Creates a Vector2f with the given initial x and y values.
|
||||
*
|
||||
* @param x The x value of this Vector2f.
|
||||
* @param y The y value of this Vector2f.
|
||||
*/
|
||||
public Vector2f(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Vector2f with x and y set to 0. Equivalent to Vector2f(0,0).
|
||||
*/
|
||||
public Vector2f() {
|
||||
x = y = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Vector2f that contains the passed vector's information
|
||||
*
|
||||
* @param vector2f The vector to copy
|
||||
*/
|
||||
public Vector2f(Vector2f vector2f) {
|
||||
this.x = vector2f.x;
|
||||
this.y = vector2f.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the x and y values of the vector
|
||||
*
|
||||
* @param x the x value of the vector.
|
||||
* @param y the y value of the vector.
|
||||
* @return this vector
|
||||
*/
|
||||
public Vector2f set(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the x and y values of the vector from another vector
|
||||
*
|
||||
* @param vec the vector to copy from
|
||||
* @return this vector
|
||||
*/
|
||||
public Vector2f set(Vector2f vec) {
|
||||
this.x = vec.x;
|
||||
this.y = vec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>add</code> adds a provided vector to this vector creating a
|
||||
* resultant vector which is returned. If the provided vector is null, null
|
||||
* is returned.
|
||||
*
|
||||
* @param vec the vector to add to this.
|
||||
* @return the resultant vector.
|
||||
*/
|
||||
public Vector2f add(Vector2f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
return new Vector2f(x + vec.x, y + vec.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>addLocal</code> adds a provided vector to this vector internally,
|
||||
* and returns a handle to this vector for easy chaining of calls. If the
|
||||
* provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec the vector to add to this vector.
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f addLocal(Vector2f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
x += vec.x;
|
||||
y += vec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>addLocal</code> adds the provided values to this vector internally,
|
||||
* and returns a handle to this vector for easy chaining of calls.
|
||||
*
|
||||
* @param addX value to add to x
|
||||
* @param addY value to add to y
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f addLocal(float addX, float addY) {
|
||||
x += addX;
|
||||
y += addY;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>add</code> adds this vector by <code>vec</code> and stores the
|
||||
* result in <code>result</code>.
|
||||
*
|
||||
* @param vec The vector to add.
|
||||
* @param result The vector to store the result in.
|
||||
* @return The result vector, after adding.
|
||||
*/
|
||||
public Vector2f add(Vector2f vec, Vector2f result) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
if (result == null)
|
||||
result = new Vector2f();
|
||||
result.x = x + vec.x;
|
||||
result.y = y + vec.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>dot</code> calculates the dot product of this vector with a
|
||||
* provided vector. If the provided vector is null, 0 is returned.
|
||||
*
|
||||
* @param vec the vector to dot with this vector.
|
||||
* @return the resultant dot product of this vector and a given vector.
|
||||
*/
|
||||
public float dot(Vector2f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, 0 returned.");
|
||||
return 0;
|
||||
}
|
||||
return x * vec.x + y * vec.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>cross</code> calculates the cross product of this vector with a
|
||||
* parameter vector v.
|
||||
*
|
||||
* @param v the vector to take the cross product of with this.
|
||||
* @return the cross product vector.
|
||||
*/
|
||||
public Vector3f cross(Vector2f v) {
|
||||
return new Vector3f(0, 0, determinant(v));
|
||||
}
|
||||
|
||||
public float determinant(Vector2f v) {
|
||||
return (x * v.y) - (y * v.x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this vector to the interpolation by changeAmnt from this to the
|
||||
* finalVec this=(1-changeAmnt)*this + changeAmnt * finalVec
|
||||
*
|
||||
* @param finalVec The final vector to interpolate towards
|
||||
* @param changeAmnt An amount between 0.0 - 1.0 representing a percentage
|
||||
* change from this towards finalVec
|
||||
*/
|
||||
public Vector2f interpolate(Vector2f finalVec, float changeAmnt) {
|
||||
this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
|
||||
this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this vector to the interpolation by changeAmnt from beginVec to
|
||||
* finalVec this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
|
||||
*
|
||||
* @param beginVec The begining vector (delta=0)
|
||||
* @param finalVec The final vector to interpolate towards (delta=1)
|
||||
* @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
|
||||
* change from beginVec towards finalVec
|
||||
*/
|
||||
public Vector2f interpolate(
|
||||
Vector2f beginVec,
|
||||
Vector2f finalVec,
|
||||
float changeAmnt
|
||||
) {
|
||||
this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
|
||||
this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a vector... if it is null or its floats are NaN or infinite, return
|
||||
* false. Else return true.
|
||||
*
|
||||
* @param vector the vector to check
|
||||
* @return true or false as stated above.
|
||||
*/
|
||||
public static boolean isValidVector(Vector2f vector) {
|
||||
if (vector == null)
|
||||
return false;
|
||||
if (
|
||||
Float.isNaN(vector.x)
|
||||
||
|
||||
Float.isNaN(vector.y)
|
||||
)
|
||||
return false;
|
||||
if (
|
||||
Float.isInfinite(vector.x)
|
||||
||
|
||||
Float.isInfinite(vector.y)
|
||||
)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>length</code> calculates the magnitude of this vector.
|
||||
*
|
||||
* @return the length or magnitude of the vector.
|
||||
*/
|
||||
public float length() {
|
||||
return FastMath.sqrt(lengthSquared());
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>lengthSquared</code> calculates the squared value of the magnitude
|
||||
* of the vector.
|
||||
*
|
||||
* @return the magnitude squared of the vector.
|
||||
*/
|
||||
public float lengthSquared() {
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>distanceSquared</code> calculates the distance squared between this
|
||||
* vector and vector v.
|
||||
*
|
||||
* @param v the second vector to determine the distance squared.
|
||||
* @return the distance squared between the two vectors.
|
||||
*/
|
||||
public float distanceSquared(Vector2f v) {
|
||||
double dx = x - v.x;
|
||||
double dy = y - v.y;
|
||||
return (float) (dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>distanceSquared</code> calculates the distance squared between this
|
||||
* vector and vector v.
|
||||
*
|
||||
* @param otherX The X coordinate of the v vector
|
||||
* @param otherY The Y coordinate of the v vector
|
||||
* @return the distance squared between the two vectors.
|
||||
*/
|
||||
public float distanceSquared(float otherX, float otherY) {
|
||||
double dx = x - otherX;
|
||||
double dy = y - otherY;
|
||||
return (float) (dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>distance</code> calculates the distance between this vector and
|
||||
* vector v.
|
||||
*
|
||||
* @param v the second vector to determine the distance.
|
||||
* @return the distance between the two vectors.
|
||||
*/
|
||||
public float distance(Vector2f v) {
|
||||
return FastMath.sqrt(distanceSquared(v));
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>mult</code> multiplies this vector by a scalar. The resultant
|
||||
* vector is returned.
|
||||
*
|
||||
* @param scalar the value to multiply this vector by.
|
||||
* @return the new vector.
|
||||
*/
|
||||
public Vector2f mult(float scalar) {
|
||||
return new Vector2f(x * scalar, y * scalar);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>multLocal</code> multiplies this vector by a scalar internally, and
|
||||
* returns a handle to this vector for easy chaining of calls.
|
||||
*
|
||||
* @param scalar the value to multiply this vector by.
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f multLocal(float scalar) {
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>multLocal</code> multiplies a provided vector to this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls. If the provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec the vector to mult to this vector.
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f multLocal(Vector2f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
x *= vec.x;
|
||||
y *= vec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies this Vector2f's x and y by the scalar and stores the result in
|
||||
* product. The result is returned for chaining. Similar to
|
||||
* product=this*scalar;
|
||||
*
|
||||
* @param scalar The scalar to multiply by.
|
||||
* @param product The vector2f to store the result in.
|
||||
* @return product, after multiplication.
|
||||
*/
|
||||
public Vector2f mult(float scalar, Vector2f product) {
|
||||
if (null == product) {
|
||||
product = new Vector2f();
|
||||
}
|
||||
|
||||
product.x = x * scalar;
|
||||
product.y = y * scalar;
|
||||
return product;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>divide</code> divides the values of this vector by a scalar and
|
||||
* returns the result. The values of this vector remain untouched.
|
||||
*
|
||||
* @param scalar the value to divide this vectors attributes by.
|
||||
* @return the result <code>Vector</code>.
|
||||
*/
|
||||
public Vector2f divide(float scalar) {
|
||||
return new Vector2f(x / scalar, y / scalar);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>divideLocal</code> divides this vector by a scalar internally, and
|
||||
* returns a handle to this vector for easy chaining of calls. Dividing by
|
||||
* zero will result in an exception.
|
||||
*
|
||||
* @param scalar the value to divides this vector by.
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f divideLocal(float scalar) {
|
||||
x /= scalar;
|
||||
y /= scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>negate</code> returns the negative of this vector. All values are
|
||||
* negated and set to a new vector.
|
||||
*
|
||||
* @return the negated vector.
|
||||
*/
|
||||
public Vector2f negate() {
|
||||
return new Vector2f(-x, -y);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>negateLocal</code> negates the internal values of this vector.
|
||||
*
|
||||
* @return this.
|
||||
*/
|
||||
public Vector2f negateLocal() {
|
||||
x = -x;
|
||||
y = -y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtract</code> subtracts the values of a given vector from those
|
||||
* of this vector creating a new vector object. If the provided vector is
|
||||
* null, an exception is thrown.
|
||||
*
|
||||
* @param vec the vector to subtract from this vector.
|
||||
* @return the result vector.
|
||||
*/
|
||||
public Vector2f subtract(Vector2f vec) {
|
||||
return subtract(vec, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtract</code> subtracts the values of a given vector from those
|
||||
* of this vector storing the result in the given vector object. If the
|
||||
* provided vector is null, an exception is thrown.
|
||||
*
|
||||
* @param vec the vector to subtract from this vector.
|
||||
* @param store the vector to store the result in. It is safe for this to be
|
||||
* the same as vec. If null, a new vector is created.
|
||||
* @return the result vector.
|
||||
*/
|
||||
public Vector2f subtract(Vector2f vec, Vector2f store) {
|
||||
if (store == null)
|
||||
store = new Vector2f();
|
||||
store.x = x - vec.x;
|
||||
store.y = y - vec.y;
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtract</code> subtracts the given x,y values from those of this
|
||||
* vector creating a new vector object.
|
||||
*
|
||||
* @param valX value to subtract from x
|
||||
* @param valY value to subtract from y
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f subtract(float valX, float valY) {
|
||||
return new Vector2f(x - valX, y - valY);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtractLocal</code> subtracts a provided vector to this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls. If the provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec the vector to subtract
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f subtractLocal(Vector2f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
x -= vec.x;
|
||||
y -= vec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtractLocal</code> subtracts the provided values from this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls.
|
||||
*
|
||||
* @param valX value to subtract from x
|
||||
* @param valY value to subtract from y
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f subtractLocal(float valX, float valY) {
|
||||
x -= valX;
|
||||
y -= valY;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>normalize</code> returns the unit vector of this vector.
|
||||
*
|
||||
* @return unit vector of this vector.
|
||||
*/
|
||||
public Vector2f normalize() {
|
||||
float length = length();
|
||||
if (length != 0) {
|
||||
return divide(length);
|
||||
}
|
||||
|
||||
return divide(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>normalizeLocal</code> makes this vector into a unit vector of
|
||||
* itself.
|
||||
*
|
||||
* @return this.
|
||||
*/
|
||||
public Vector2f normalizeLocal() {
|
||||
float length = length();
|
||||
if (length != 0) {
|
||||
return divideLocal(length);
|
||||
}
|
||||
|
||||
return divideLocal(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>smallestAngleBetween</code> returns (in radians) the minimum angle
|
||||
* between two vectors. It is assumed that both this vector and the given
|
||||
* vector are unit vectors (iow, normalized).
|
||||
*
|
||||
* @param otherVector a unit vector to find the angle against
|
||||
* @return the angle in radians.
|
||||
*/
|
||||
public float smallestAngleBetween(Vector2f otherVector) {
|
||||
float dotProduct = dot(otherVector);
|
||||
float angle = FastMath.acos(dotProduct);
|
||||
return angle;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>angleBetween</code> returns (in radians) the angle required to
|
||||
* rotate a ray represented by this vector to lie colinear to a ray
|
||||
* described by the given vector. It is assumed that both this vector and
|
||||
* the given vector are unit vectors (iow, normalized).
|
||||
*
|
||||
* @param otherVector the "destination" unit vector
|
||||
* @return the angle in radians.
|
||||
*/
|
||||
public float angleBetween(Vector2f otherVector) {
|
||||
float angle = FastMath.atan2(otherVector.y, otherVector.x)
|
||||
- FastMath.atan2(y, x);
|
||||
return angle;
|
||||
}
|
||||
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public Vector2f setX(float x) {
|
||||
this.x = x;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public Vector2f setY(float y) {
|
||||
this.y = y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>getAngle</code> returns (in radians) the angle represented by this
|
||||
* Vector2f as expressed by a conversion from rectangular coordinates
|
||||
* (<code>x</code>, <code>y</code>) to polar coordinates
|
||||
* (r, <i>theta</i>).
|
||||
*
|
||||
* @return the angle in radians. [-pi, pi)
|
||||
*/
|
||||
public float getAngle() {
|
||||
return FastMath.atan2(y, x);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>zero</code> resets this vector's data to zero internally.
|
||||
*/
|
||||
public Vector2f zero() {
|
||||
x = y = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>hashCode</code> returns a unique code for this vector object based
|
||||
* on it's values. If two vectors are logically equivalent, they will return
|
||||
* the same hash code value.
|
||||
*
|
||||
* @return the hash code value of this vector.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 37;
|
||||
hash += 37 * hash + Float.floatToIntBits(x);
|
||||
hash += 37 * hash + Float.floatToIntBits(y);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2f clone() {
|
||||
try {
|
||||
return (Vector2f) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError(); // can not happen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this Vector2f into the given float[] object.
|
||||
*
|
||||
* @param floats The float[] to take this Vector2f. If null, a new float[2]
|
||||
* is created.
|
||||
* @return The array, with X, Y float values in that order
|
||||
*/
|
||||
public float[] toArray(float[] floats) {
|
||||
if (floats == null) {
|
||||
floats = new float[2];
|
||||
}
|
||||
floats[0] = x;
|
||||
floats[1] = y;
|
||||
return floats;
|
||||
}
|
||||
|
||||
/**
|
||||
* are these two vectors the same? they are is they both have the same x and
|
||||
* y values.
|
||||
*
|
||||
* @param o the object to compare for equality
|
||||
* @return true if they are equal
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Vector2f)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector2f comp = (Vector2f) o;
|
||||
if (Float.compare(x, comp.x) != 0)
|
||||
return false;
|
||||
if (Float.compare(y, comp.y) != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>toString</code> returns the string representation of this vector
|
||||
* object. The format of the string is such: com.jme.math.Vector2f
|
||||
* [X=XX.XXXX, Y=YY.YYYY]
|
||||
*
|
||||
* @return the string representation of this vector.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + x + ", " + y + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Used with serialization. Not to be called manually.
|
||||
*
|
||||
* @param in ObjectInput
|
||||
* @throws IOException
|
||||
* @throws ClassNotFoundException
|
||||
* @see java.io.Externalizable
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException,
|
||||
ClassNotFoundException {
|
||||
x = in.readFloat();
|
||||
y = in.readFloat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used with serialization. Not to be called manually.
|
||||
*
|
||||
* @param out ObjectOutput
|
||||
* @throws IOException
|
||||
* @see java.io.Externalizable
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeFloat(x);
|
||||
out.writeFloat(y);
|
||||
}
|
||||
|
||||
public void rotateAroundOrigin(float angle, boolean cw) {
|
||||
if (cw)
|
||||
angle = -angle;
|
||||
float newX = FastMath.cos(angle) * x - FastMath.sin(angle) * y;
|
||||
float newY = FastMath.sin(angle) * x + FastMath.cos(angle) * y;
|
||||
x = newX;
|
||||
y = newY;
|
||||
}
|
||||
}
|
||||
1101
java/com/jme3/math/Vector3f.java
Normal file
1101
java/com/jme3/math/Vector3f.java
Normal file
File diff suppressed because it is too large
Load Diff
971
java/com/jme3/math/Vector4f.java
Normal file
971
java/com/jme3/math/Vector4f.java
Normal file
@@ -0,0 +1,971 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.math;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* <code>Vector4f</code> defines a Vector for a four float value tuple.
|
||||
* <code>Vector4f</code> can represent any four dimensional value, such as a
|
||||
* vertex, a normal, etc. Utility methods are also included to aid in
|
||||
* mathematical calculations.
|
||||
*
|
||||
* @author Maarten Steur
|
||||
*/
|
||||
public final class Vector4f implements Cloneable, java.io.Serializable {
|
||||
|
||||
static final long serialVersionUID = 1;
|
||||
|
||||
private static final Logger logger = Logger.getLogger(Vector4f.class.getName());
|
||||
|
||||
public final static Vector4f ZERO = new Vector4f(0, 0, 0, 0);
|
||||
public final static Vector4f NAN = new Vector4f(Float.NaN, Float.NaN, Float.NaN, Float.NaN);
|
||||
public final static Vector4f UNIT_X = new Vector4f(1, 0, 0, 0);
|
||||
public final static Vector4f UNIT_Y = new Vector4f(0, 1, 0, 0);
|
||||
public final static Vector4f UNIT_Z = new Vector4f(0, 0, 1, 0);
|
||||
public final static Vector4f UNIT_W = new Vector4f(0, 0, 0, 1);
|
||||
public final static Vector4f UNIT_XYZW = new Vector4f(1, 1, 1, 1);
|
||||
public final static Vector4f POSITIVE_INFINITY = new Vector4f(
|
||||
Float.POSITIVE_INFINITY,
|
||||
Float.POSITIVE_INFINITY,
|
||||
Float.POSITIVE_INFINITY,
|
||||
Float.POSITIVE_INFINITY
|
||||
);
|
||||
public final static Vector4f NEGATIVE_INFINITY = new Vector4f(
|
||||
Float.NEGATIVE_INFINITY,
|
||||
Float.NEGATIVE_INFINITY,
|
||||
Float.NEGATIVE_INFINITY,
|
||||
Float.NEGATIVE_INFINITY
|
||||
);
|
||||
|
||||
/**
|
||||
* the x value of the vector.
|
||||
*/
|
||||
public float x;
|
||||
|
||||
/**
|
||||
* the y value of the vector.
|
||||
*/
|
||||
public float y;
|
||||
|
||||
/**
|
||||
* the z value of the vector.
|
||||
*/
|
||||
public float z;
|
||||
|
||||
/**
|
||||
* the w value of the vector.
|
||||
*/
|
||||
public float w;
|
||||
|
||||
/**
|
||||
* Constructor instantiates a new <code>Vector3f</code> with default values
|
||||
* of (0,0,0).
|
||||
*
|
||||
*/
|
||||
public Vector4f() {
|
||||
x = y = z = w = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor instantiates a new <code>Vector4f</code> with provides
|
||||
* values.
|
||||
*
|
||||
* @param x the x value of the vector.
|
||||
* @param y the y value of the vector.
|
||||
* @param z the z value of the vector.
|
||||
* @param w the w value of the vector.
|
||||
*/
|
||||
public Vector4f(float x, float y, float z, float w) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor instantiates a new <code>Vector3f</code> that is a copy of
|
||||
* the provided vector
|
||||
*
|
||||
* @param copy The Vector3f to copy
|
||||
*/
|
||||
public Vector4f(Vector4f copy) {
|
||||
this.set(copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>set</code> sets the x,y,z,w values of the vector based on passed
|
||||
* parameters.
|
||||
*
|
||||
* @param x the x value of the vector.
|
||||
* @param y the y value of the vector.
|
||||
* @param z the z value of the vector.
|
||||
* @param w the w value of the vector.
|
||||
* @return this vector
|
||||
*/
|
||||
public Vector4f set(float x, float y, float z, float w) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>set</code> sets the x,y,z values of the vector by copying the
|
||||
* supplied vector.
|
||||
*
|
||||
* @param vect the vector to copy.
|
||||
* @return this vector
|
||||
*/
|
||||
public Vector4f set(Vector4f vect) {
|
||||
this.x = vect.x;
|
||||
this.y = vect.y;
|
||||
this.z = vect.z;
|
||||
this.w = vect.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>add</code> adds a provided vector to this vector creating a
|
||||
* resultant vector which is returned. If the provided vector is null, null
|
||||
* is returned.
|
||||
*
|
||||
* @param vec the vector to add to this.
|
||||
* @return the resultant vector.
|
||||
*/
|
||||
public Vector4f add(Vector4f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
return new Vector4f(x + vec.x, y + vec.y, z + vec.z, w + vec.w);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>add</code> adds the values of a provided vector storing the values
|
||||
* in the supplied vector.
|
||||
*
|
||||
* @param vec the vector to add to this
|
||||
* @param result the vector to store the result in
|
||||
* @return result returns the supplied result vector.
|
||||
*/
|
||||
public Vector4f add(Vector4f vec, Vector4f result) {
|
||||
result.x = x + vec.x;
|
||||
result.y = y + vec.y;
|
||||
result.z = z + vec.z;
|
||||
result.w = w + vec.w;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>addLocal</code> adds a provided vector to this vector internally,
|
||||
* and returns a handle to this vector for easy chaining of calls. If the
|
||||
* provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec the vector to add to this vector.
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f addLocal(Vector4f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
x += vec.x;
|
||||
y += vec.y;
|
||||
z += vec.z;
|
||||
w += vec.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>add</code> adds the provided values to this vector, creating a new
|
||||
* vector that is then returned.
|
||||
*
|
||||
* @param addX the x value to add.
|
||||
* @param addY the y value to add.
|
||||
* @param addZ the z value to add.
|
||||
* @return the result vector.
|
||||
*/
|
||||
public Vector4f add(float addX, float addY, float addZ, float addW) {
|
||||
return new Vector4f(x + addX, y + addY, z + addZ, w + addW);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>addLocal</code> adds the provided values to this vector internally,
|
||||
* and returns a handle to this vector for easy chaining of calls.
|
||||
*
|
||||
* @param addX value to add to x
|
||||
* @param addY value to add to y
|
||||
* @param addZ value to add to z
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f addLocal(float addX, float addY, float addZ, float addW) {
|
||||
x += addX;
|
||||
y += addY;
|
||||
z += addZ;
|
||||
w += addW;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>scaleAdd</code> multiplies this vector by a scalar then adds the
|
||||
* given Vector3f.
|
||||
*
|
||||
* @param scalar the value to multiply this vector by.
|
||||
* @param add the value to add
|
||||
*/
|
||||
public Vector4f scaleAdd(float scalar, Vector4f add) {
|
||||
x = x * scalar + add.x;
|
||||
y = y * scalar + add.y;
|
||||
z = z * scalar + add.z;
|
||||
w = w * scalar + add.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>scaleAdd</code> multiplies the given vector by a scalar then adds
|
||||
* the given vector.
|
||||
*
|
||||
* @param scalar the value to multiply this vector by.
|
||||
* @param mult the value to multiply the scalar by
|
||||
* @param add the value to add
|
||||
*/
|
||||
public Vector4f scaleAdd(float scalar, Vector4f mult, Vector4f add) {
|
||||
this.x = mult.x * scalar + add.x;
|
||||
this.y = mult.y * scalar + add.y;
|
||||
this.z = mult.z * scalar + add.z;
|
||||
this.w = mult.w * scalar + add.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>dot</code> calculates the dot product of this vector with a
|
||||
* provided vector. If the provided vector is null, 0 is returned.
|
||||
*
|
||||
* @param vec the vector to dot with this vector.
|
||||
* @return the resultant dot product of this vector and a given vector.
|
||||
*/
|
||||
public float dot(Vector4f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, 0 returned.");
|
||||
return 0;
|
||||
}
|
||||
return x * vec.x + y * vec.y + z * vec.z + w * vec.w;
|
||||
}
|
||||
|
||||
public Vector4f project(Vector4f other) {
|
||||
float n = this.dot(other); // A . B
|
||||
float d = other.lengthSquared(); // |B|^2
|
||||
return new Vector4f(other).normalizeLocal().multLocal(n / d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this vector is a unit vector (length() ~= 1), returns
|
||||
* false otherwise.
|
||||
*
|
||||
* @return true if this vector is a unit vector (length() ~= 1), or false
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean isUnitVector() {
|
||||
float len = length();
|
||||
return 0.99f < len && len < 1.01f;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>length</code> calculates the magnitude of this vector.
|
||||
*
|
||||
* @return the length or magnitude of the vector.
|
||||
*/
|
||||
public float length() {
|
||||
return FastMath.sqrt(lengthSquared());
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>lengthSquared</code> calculates the squared value of the magnitude
|
||||
* of the vector.
|
||||
*
|
||||
* @return the magnitude squared of the vector.
|
||||
*/
|
||||
public float lengthSquared() {
|
||||
return x * x + y * y + z * z + w * w;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>distanceSquared</code> calculates the distance squared between this
|
||||
* vector and vector v.
|
||||
*
|
||||
* @param v the second vector to determine the distance squared.
|
||||
* @return the distance squared between the two vectors.
|
||||
*/
|
||||
public float distanceSquared(Vector4f v) {
|
||||
double dx = x - v.x;
|
||||
double dy = y - v.y;
|
||||
double dz = z - v.z;
|
||||
double dw = w - v.w;
|
||||
return (float) (dx * dx + dy * dy + dz * dz + dw * dw);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>distance</code> calculates the distance between this vector and
|
||||
* vector v.
|
||||
*
|
||||
* @param v the second vector to determine the distance.
|
||||
* @return the distance between the two vectors.
|
||||
*/
|
||||
public float distance(Vector4f v) {
|
||||
return FastMath.sqrt(distanceSquared(v));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>mult</code> multiplies this vector by a scalar. The resultant
|
||||
* vector is returned.
|
||||
*
|
||||
* @param scalar the value to multiply this vector by.
|
||||
* @return the new vector.
|
||||
*/
|
||||
public Vector4f mult(float scalar) {
|
||||
return new Vector4f(x * scalar, y * scalar, z * scalar, w * scalar);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>mult</code> multiplies this vector by a scalar. The resultant
|
||||
* vector is supplied as the second parameter and returned.
|
||||
*
|
||||
* @param scalar the scalar to multiply this vector by.
|
||||
* @param product the product to store the result in.
|
||||
* @return product
|
||||
*/
|
||||
public Vector4f mult(float scalar, Vector4f product) {
|
||||
if (null == product) {
|
||||
product = new Vector4f();
|
||||
}
|
||||
|
||||
product.x = x * scalar;
|
||||
product.y = y * scalar;
|
||||
product.z = z * scalar;
|
||||
product.w = w * scalar;
|
||||
return product;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>multLocal</code> multiplies this vector by a scalar internally, and
|
||||
* returns a handle to this vector for easy chaining of calls.
|
||||
*
|
||||
* @param scalar the value to multiply this vector by.
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f multLocal(float scalar) {
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
z *= scalar;
|
||||
w *= scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>multLocal</code> multiplies a provided vector to this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls. If the provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec the vector to mult to this vector.
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f multLocal(Vector4f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
x *= vec.x;
|
||||
y *= vec.y;
|
||||
z *= vec.z;
|
||||
w *= vec.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>multLocal</code> multiplies this vector by 3 scalars internally,
|
||||
* and returns a handle to this vector for easy chaining of calls.
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @param z
|
||||
* @param w
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f multLocal(float x, float y, float z, float w) {
|
||||
this.x *= x;
|
||||
this.y *= y;
|
||||
this.z *= z;
|
||||
this.w *= w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>multLocal</code> multiplies a provided vector to this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls. If the provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec the vector to mult to this vector.
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f mult(Vector4f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
return mult(vec, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>multLocal</code> multiplies a provided vector to this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls. If the provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec the vector to mult to this vector.
|
||||
* @param store result vector (null to create a new vector)
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f mult(Vector4f vec, Vector4f store) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
if (store == null)
|
||||
store = new Vector4f();
|
||||
return store.set(x * vec.x, y * vec.y, z * vec.z, w * vec.w);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>divide</code> divides the values of this vector by a scalar and
|
||||
* returns the result. The values of this vector remain untouched.
|
||||
*
|
||||
* @param scalar the value to divide this vectors attributes by.
|
||||
* @return the result <code>Vector</code>.
|
||||
*/
|
||||
public Vector4f divide(float scalar) {
|
||||
scalar = 1f / scalar;
|
||||
return new Vector4f(x * scalar, y * scalar, z * scalar, w * scalar);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>divideLocal</code> divides this vector by a scalar internally, and
|
||||
* returns a handle to this vector for easy chaining of calls. Dividing by
|
||||
* zero will result in an exception.
|
||||
*
|
||||
* @param scalar the value to divides this vector by.
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f divideLocal(float scalar) {
|
||||
scalar = 1f / scalar;
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
z *= scalar;
|
||||
w *= scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>divide</code> divides the values of this vector by a scalar and
|
||||
* returns the result. The values of this vector remain untouched.
|
||||
*
|
||||
* @param scalar the value to divide this vectors attributes by.
|
||||
* @return the result <code>Vector</code>.
|
||||
*/
|
||||
public Vector4f divide(Vector4f scalar) {
|
||||
return new Vector4f(x / scalar.x, y / scalar.y, z / scalar.z, w / scalar.w);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>divideLocal</code> divides this vector by a scalar internally, and
|
||||
* returns a handle to this vector for easy chaining of calls. Dividing by
|
||||
* zero will result in an exception.
|
||||
*
|
||||
* @param scalar the value to divides this vector by.
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f divideLocal(Vector4f scalar) {
|
||||
x /= scalar.x;
|
||||
y /= scalar.y;
|
||||
z /= scalar.z;
|
||||
w /= scalar.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>negate</code> returns the negative of this vector. All values are
|
||||
* negated and set to a new vector.
|
||||
*
|
||||
* @return the negated vector.
|
||||
*/
|
||||
public Vector4f negate() {
|
||||
return new Vector4f(-x, -y, -z, -w);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>negateLocal</code> negates the internal values of this vector.
|
||||
*
|
||||
* @return this.
|
||||
*/
|
||||
public Vector4f negateLocal() {
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
w = -w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>subtract</code> subtracts the values of a given vector from those
|
||||
* of this vector creating a new vector object. If the provided vector is
|
||||
* null, null is returned.
|
||||
*
|
||||
* @param vec the vector to subtract from this vector.
|
||||
* @return the result vector.
|
||||
*/
|
||||
public Vector4f subtract(Vector4f vec) {
|
||||
return new Vector4f(x - vec.x, y - vec.y, z - vec.z, w - vec.w);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtractLocal</code> subtracts a provided vector to this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls. If the provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec the vector to subtract
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f subtractLocal(Vector4f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
x -= vec.x;
|
||||
y -= vec.y;
|
||||
z -= vec.z;
|
||||
w -= vec.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>subtract</code>
|
||||
*
|
||||
* @param vec the vector to subtract from this
|
||||
* @param result the vector to store the result in
|
||||
* @return result
|
||||
*/
|
||||
public Vector4f subtract(Vector4f vec, Vector4f result) {
|
||||
if (result == null) {
|
||||
result = new Vector4f();
|
||||
}
|
||||
result.x = x - vec.x;
|
||||
result.y = y - vec.y;
|
||||
result.z = z - vec.z;
|
||||
result.w = w - vec.w;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>subtract</code> subtracts the provided values from this vector,
|
||||
* creating a new vector that is then returned.
|
||||
*
|
||||
* @param subtractX the x value to subtract.
|
||||
* @param subtractY the y value to subtract.
|
||||
* @param subtractZ the z value to subtract.
|
||||
* @param subtractW the w value to subtract.
|
||||
* @return the result vector.
|
||||
*/
|
||||
public Vector4f subtract(float subtractX, float subtractY, float subtractZ, float subtractW) {
|
||||
return new Vector4f(x - subtractX, y - subtractY, z - subtractZ, w - subtractW);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtractLocal</code> subtracts the provided values from this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls.
|
||||
*
|
||||
* @param subtractX the x value to subtract.
|
||||
* @param subtractY the y value to subtract.
|
||||
* @param subtractZ the z value to subtract.
|
||||
* @param subtractW the w value to subtract.
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f subtractLocal(
|
||||
float subtractX,
|
||||
float subtractY,
|
||||
float subtractZ,
|
||||
float subtractW
|
||||
) {
|
||||
x -= subtractX;
|
||||
y -= subtractY;
|
||||
z -= subtractZ;
|
||||
w -= subtractW;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>normalize</code> returns the unit vector of this vector.
|
||||
*
|
||||
* @return unit vector of this vector.
|
||||
*/
|
||||
public Vector4f normalize() {
|
||||
// float length = length();
|
||||
// if (length != 0) {
|
||||
// return divide(length);
|
||||
// }
|
||||
//
|
||||
// return divide(1);
|
||||
float length = x * x + y * y + z * z + w * w;
|
||||
if (length != 1f && length != 0f) {
|
||||
length = 1.0f / FastMath.sqrt(length);
|
||||
return new Vector4f(x * length, y * length, z * length, w * length);
|
||||
}
|
||||
return clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>normalizeLocal</code> makes this vector into a unit vector of
|
||||
* itself.
|
||||
*
|
||||
* @return this.
|
||||
*/
|
||||
public Vector4f normalizeLocal() {
|
||||
// NOTE: this implementation is more optimized
|
||||
// than the old jme normalize as this method
|
||||
// is commonly used.
|
||||
float length = x * x + y * y + z * z + w * w;
|
||||
if (length != 1f && length != 0f) {
|
||||
length = 1.0f / FastMath.sqrt(length);
|
||||
x *= length;
|
||||
y *= length;
|
||||
z *= length;
|
||||
w *= length;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>maxLocal</code> computes the maximum value for each component in
|
||||
* this and <code>other</code> vector. The result is stored in this vector.
|
||||
*
|
||||
* @param other
|
||||
*/
|
||||
public Vector4f maxLocal(Vector4f other) {
|
||||
x = other.x > x ? other.x : x;
|
||||
y = other.y > y ? other.y : y;
|
||||
z = other.z > z ? other.z : z;
|
||||
w = other.w > w ? other.w : w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>minLocal</code> computes the minimum value for each component in
|
||||
* this and <code>other</code> vector. The result is stored in this vector.
|
||||
*
|
||||
* @param other
|
||||
*/
|
||||
public Vector4f minLocal(Vector4f other) {
|
||||
x = other.x < x ? other.x : x;
|
||||
y = other.y < y ? other.y : y;
|
||||
z = other.z < z ? other.z : z;
|
||||
w = other.w < w ? other.w : w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>zero</code> resets this vector's data to zero internally.
|
||||
*/
|
||||
public Vector4f zero() {
|
||||
x = y = z = w = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>angleBetween</code> returns (in radians) the angle between two
|
||||
* vectors. It is assumed that both this vector and the given vector are
|
||||
* unit vectors (iow, normalized).
|
||||
*
|
||||
* @param otherVector a unit vector to find the angle against
|
||||
* @return the angle in radians.
|
||||
*/
|
||||
public float angleBetween(Vector4f otherVector) {
|
||||
float dotProduct = dot(otherVector);
|
||||
float angle = FastMath.acos(dotProduct);
|
||||
return angle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this vector to the interpolation by changeAmnt from this to the
|
||||
* finalVec this=(1-changeAmnt)*this + changeAmnt * finalVec
|
||||
*
|
||||
* @param finalVec The final vector to interpolate towards
|
||||
* @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
|
||||
* change from this towards finalVec
|
||||
*/
|
||||
public Vector4f interpolate(Vector4f finalVec, float changeAmnt) {
|
||||
this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
|
||||
this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
|
||||
this.z = (1 - changeAmnt) * this.z + changeAmnt * finalVec.z;
|
||||
this.w = (1 - changeAmnt) * this.w + changeAmnt * finalVec.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this vector to the interpolation by changeAmnt from beginVec to
|
||||
* finalVec this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
|
||||
*
|
||||
* @param beginVec the beging vector (changeAmnt=0)
|
||||
* @param finalVec The final vector to interpolate towards
|
||||
* @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
|
||||
* change from beginVec towards finalVec
|
||||
*/
|
||||
public Vector4f interpolate(Vector4f beginVec, Vector4f finalVec, float changeAmnt) {
|
||||
this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
|
||||
this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
|
||||
this.z = (1 - changeAmnt) * beginVec.z + changeAmnt * finalVec.z;
|
||||
this.w = (1 - changeAmnt) * beginVec.w + changeAmnt * finalVec.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a vector... if it is null or its floats are NaN or infinite, return
|
||||
* false. Else return true.
|
||||
*
|
||||
* @param vector the vector to check
|
||||
* @return true or false as stated above.
|
||||
*/
|
||||
public static boolean isValidVector(Vector4f vector) {
|
||||
if (vector == null)
|
||||
return false;
|
||||
if (
|
||||
Float.isNaN(vector.x)
|
||||
||
|
||||
Float.isNaN(vector.y)
|
||||
||
|
||||
Float.isNaN(vector.z)
|
||||
||
|
||||
Float.isNaN(vector.w)
|
||||
)
|
||||
return false;
|
||||
if (
|
||||
Float.isInfinite(vector.x)
|
||||
||
|
||||
Float.isInfinite(vector.y)
|
||||
||
|
||||
Float.isInfinite(vector.z)
|
||||
||
|
||||
Float.isInfinite(vector.w)
|
||||
)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector4f clone() {
|
||||
try {
|
||||
return (Vector4f) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError(); // can not happen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this Vector3f into the given float[] object.
|
||||
*
|
||||
* @param floats The float[] to take this Vector3f. If null, a new float[3]
|
||||
* is created.
|
||||
* @return The array, with X, Y, Z float values in that order
|
||||
*/
|
||||
public float[] toArray(float[] floats) {
|
||||
if (floats == null) {
|
||||
floats = new float[4];
|
||||
}
|
||||
floats[0] = x;
|
||||
floats[1] = y;
|
||||
floats[2] = z;
|
||||
floats[3] = w;
|
||||
return floats;
|
||||
}
|
||||
|
||||
/**
|
||||
* are these two vectors the same? they are is they both have the same x,y,
|
||||
* and z values.
|
||||
*
|
||||
* @param o the object to compare for equality
|
||||
* @return true if they are equal
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Vector4f)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector4f comp = (Vector4f) o;
|
||||
if (Float.compare(x, comp.x) != 0)
|
||||
return false;
|
||||
if (Float.compare(y, comp.y) != 0)
|
||||
return false;
|
||||
if (Float.compare(z, comp.z) != 0)
|
||||
return false;
|
||||
if (Float.compare(w, comp.w) != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>hashCode</code> returns a unique code for this vector object based
|
||||
* on it's values. If two vectors are logically equivalent, they will return
|
||||
* the same hash code value.
|
||||
*
|
||||
* @return the hash code value of this vector.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 37;
|
||||
hash += 37 * hash + Float.floatToIntBits(x);
|
||||
hash += 37 * hash + Float.floatToIntBits(y);
|
||||
hash += 37 * hash + Float.floatToIntBits(z);
|
||||
hash += 37 * hash + Float.floatToIntBits(w);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>toString</code> returns the string representation of this vector.
|
||||
* The format is:
|
||||
*
|
||||
* org.jme.math.Vector3f [X=XX.XXXX, Y=YY.YYYY, Z=ZZ.ZZZZ, W=WW.WWWW]
|
||||
*
|
||||
* @return the string representation of this vector.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + x + ", " + y + ", " + z + ", " + w + ")";
|
||||
}
|
||||
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public Vector4f setX(float x) {
|
||||
this.x = x;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public Vector4f setY(float y) {
|
||||
this.y = y;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public Vector4f setZ(float z) {
|
||||
this.z = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getW() {
|
||||
return w;
|
||||
}
|
||||
|
||||
public Vector4f setW(float w) {
|
||||
this.w = w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index
|
||||
* @return x value if index == 0, y value if index == 1 or z value if index
|
||||
* == 2
|
||||
* @throws IllegalArgumentException if index is not one of 0, 1, 2.
|
||||
*/
|
||||
public float get(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return x;
|
||||
case 1:
|
||||
return y;
|
||||
case 2:
|
||||
return z;
|
||||
case 3:
|
||||
return w;
|
||||
}
|
||||
throw new IllegalArgumentException("index must be either 0, 1, 2 or 3");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index which field index in this vector to set.
|
||||
* @param value to set to one of x, y, z or w.
|
||||
* @throws IllegalArgumentException if index is not one of 0, 1, 2, 3.
|
||||
*/
|
||||
public void set(int index, float value) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
x = value;
|
||||
return;
|
||||
case 1:
|
||||
y = value;
|
||||
return;
|
||||
case 2:
|
||||
z = value;
|
||||
return;
|
||||
case 3:
|
||||
w = value;
|
||||
return;
|
||||
}
|
||||
throw new IllegalArgumentException("index must be either 0, 1, 2 or 3");
|
||||
}
|
||||
|
||||
}
|
||||
105
java/com/jme3/system/NanoTimer.java
Normal file
105
java/com/jme3/system/NanoTimer.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.system;
|
||||
|
||||
/**
|
||||
* <code>NanoTimer</code> is a System.nanoTime implementation of
|
||||
* <code>Timer</code>. This is primarily useful for headless applications
|
||||
* running on a server.
|
||||
*
|
||||
* @author Matthew D. Hicks
|
||||
*/
|
||||
public class NanoTimer extends Timer {
|
||||
|
||||
private static final long TIMER_RESOLUTION = 1000000000L;
|
||||
private static final float INVERSE_TIMER_RESOLUTION = 1f / TIMER_RESOLUTION;
|
||||
|
||||
private long startTime;
|
||||
private long previousTime;
|
||||
private float tpf;
|
||||
private float fps;
|
||||
private long currentTime;
|
||||
|
||||
public NanoTimer() {
|
||||
startTime = System.nanoTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time in seconds. The timer starts at 0.0 seconds.
|
||||
*
|
||||
* @return the current time in seconds
|
||||
*/
|
||||
|
||||
protected long getTimeInternal() {
|
||||
return System.nanoTime() - startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getTimeInSeconds() {
|
||||
return getTime() * INVERSE_TIMER_RESOLUTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTime() {
|
||||
return currentTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getResolution() {
|
||||
return TIMER_RESOLUTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFrameRate() {
|
||||
return fps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getTimePerFrame() {
|
||||
return tpf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
currentTime = getTimeInternal();
|
||||
tpf = (currentTime - previousTime) * (1.0f / TIMER_RESOLUTION);
|
||||
fps = 1.0f / tpf;
|
||||
previousTime = getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
startTime = System.nanoTime();
|
||||
currentTime = getTimeInternal();
|
||||
previousTime = getTime();
|
||||
}
|
||||
}
|
||||
93
java/com/jme3/system/Timer.java
Normal file
93
java/com/jme3/system/Timer.java
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.system;
|
||||
|
||||
/**
|
||||
* <code>Timer</code> is the base class for a high resolution timer. It is
|
||||
* created from getTimer("display system")
|
||||
*
|
||||
* @author Mark Powell
|
||||
* @version $Id: Timer.java,v 1.18 2007/03/09 10:19:34 rherlitz Exp $
|
||||
*/
|
||||
public abstract class Timer {
|
||||
|
||||
/**
|
||||
* Returns the current time in ticks. A tick is an arbitrary measure of time
|
||||
* defined by the timer implementation. The number of ticks per second is
|
||||
* given by <code>getResolution()</code>. The timer starts at 0 ticks.
|
||||
*
|
||||
* @return a long value representing the current time
|
||||
*/
|
||||
public abstract long getTime();
|
||||
|
||||
/**
|
||||
* Returns the time in seconds. The timer starts at 0.0 seconds.
|
||||
*
|
||||
* @return the current time in seconds
|
||||
*/
|
||||
public float getTimeInSeconds() {
|
||||
return getTime() / (float) getResolution();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resolution of the timer.
|
||||
*
|
||||
* @return the number of timer ticks per second
|
||||
*/
|
||||
public abstract long getResolution();
|
||||
|
||||
/**
|
||||
* Returns the "calls per second". If this is called every frame, then it
|
||||
* will return the "frames per second".
|
||||
*
|
||||
* @return The "calls per second".
|
||||
*/
|
||||
public abstract float getFrameRate();
|
||||
|
||||
/**
|
||||
* Returns the time, in seconds, between the last call and the current one.
|
||||
*
|
||||
* @return Time between this call and the last one.
|
||||
*/
|
||||
public abstract float getTimePerFrame();
|
||||
|
||||
/**
|
||||
* <code>update</code> recalculates the frame rate based on the previous
|
||||
* call to update. It is assumed that update is called each frame.
|
||||
*/
|
||||
public abstract void update();
|
||||
|
||||
/**
|
||||
* Reset the timer to 0. Clear any tpf history.
|
||||
*/
|
||||
public abstract void reset();
|
||||
}
|
||||
178
java/com/jme3/util/TempVars.java
Normal file
178
java/com/jme3/util/TempVars.java
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.util;
|
||||
|
||||
import com.jme3.math.*;
|
||||
|
||||
|
||||
/**
|
||||
* Temporary variables assigned to each thread. Engine classes may access these
|
||||
* temp variables with TempVars.get(), all retrieved TempVars instances must be
|
||||
* returned via TempVars.release(). This returns an available instance of the
|
||||
* TempVar class ensuring this particular instance is never used elsewhere in
|
||||
* the mean time.
|
||||
*/
|
||||
public class TempVars {
|
||||
|
||||
/**
|
||||
* Allow X instances of TempVars in a single thread.
|
||||
*/
|
||||
private static final int STACK_SIZE = 5;
|
||||
|
||||
/**
|
||||
* <code>TempVarsStack</code> contains a stack of TempVars. Every time
|
||||
* TempVars.get() is called, a new entry is added to the stack, and the
|
||||
* index incremented. When TempVars.release() is called, the entry is
|
||||
* checked against the current instance and then the index is decremented.
|
||||
*/
|
||||
private static class TempVarsStack {
|
||||
|
||||
int index = 0;
|
||||
TempVars[] tempVars = new TempVars[STACK_SIZE];
|
||||
}
|
||||
|
||||
/**
|
||||
* ThreadLocal to store a TempVarsStack for each thread. This ensures each
|
||||
* thread has a single TempVarsStack that is used only in method calls in
|
||||
* that thread.
|
||||
*/
|
||||
private static final ThreadLocal<TempVarsStack> varsLocal = new ThreadLocal<TempVarsStack>() {
|
||||
|
||||
@Override
|
||||
public TempVarsStack initialValue() {
|
||||
return new TempVarsStack();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* This instance of TempVars has been retrieved but not released yet.
|
||||
*/
|
||||
private boolean isUsed = false;
|
||||
|
||||
private TempVars() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire an instance of the TempVar class. You have to release the
|
||||
* instance after use by calling the release() method. If more than
|
||||
* STACK_SIZE (currently 5) instances are requested in a single thread then
|
||||
* an ArrayIndexOutOfBoundsException will be thrown.
|
||||
*
|
||||
* @return A TempVar instance
|
||||
*/
|
||||
public static TempVars get() {
|
||||
TempVarsStack stack = varsLocal.get();
|
||||
|
||||
TempVars instance = stack.tempVars[stack.index];
|
||||
|
||||
if (instance == null) {
|
||||
// Create new
|
||||
instance = new TempVars();
|
||||
|
||||
// Put it in there
|
||||
stack.tempVars[stack.index] = instance;
|
||||
}
|
||||
|
||||
stack.index++;
|
||||
|
||||
instance.isUsed = true;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases this instance of TempVars. Once released, the contents of the
|
||||
* TempVars are undefined. The TempVars must be released in the opposite
|
||||
* order that they are retrieved, e.g. Acquiring vars1, then acquiring
|
||||
* vars2, vars2 MUST be released first otherwise an exception will be
|
||||
* thrown.
|
||||
*/
|
||||
public void release() {
|
||||
if (!isUsed) {
|
||||
throw new IllegalStateException("This instance of TempVars was already released!");
|
||||
}
|
||||
|
||||
isUsed = false;
|
||||
|
||||
TempVarsStack stack = varsLocal.get();
|
||||
|
||||
// Return it to the stack
|
||||
stack.index--;
|
||||
|
||||
// Check if it is actually there
|
||||
if (stack.tempVars[stack.index] != this) {
|
||||
throw new IllegalStateException(
|
||||
"An instance of TempVars has not been released in a called method!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Color
|
||||
*/
|
||||
public final ColorRGBA color = new ColorRGBA();
|
||||
/**
|
||||
* General vectors.
|
||||
*/
|
||||
public final Vector3f vect1 = new Vector3f();
|
||||
public final Vector3f vect2 = new Vector3f();
|
||||
public final Vector3f vect3 = new Vector3f();
|
||||
public final Vector3f vect4 = new Vector3f();
|
||||
public final Vector3f vect5 = new Vector3f();
|
||||
public final Vector3f vect6 = new Vector3f();
|
||||
public final Vector3f vect7 = new Vector3f();
|
||||
// seems the maximum number of vector used is 7 in com.jme3.bounding.java
|
||||
public final Vector3f vect8 = new Vector3f();
|
||||
public final Vector3f vect9 = new Vector3f();
|
||||
public final Vector3f vect10 = new Vector3f();
|
||||
public final Vector4f vect4f = new Vector4f();
|
||||
public final Vector3f[] tri = { new Vector3f(),
|
||||
new Vector3f(),
|
||||
new Vector3f() };
|
||||
/**
|
||||
* 2D vector
|
||||
*/
|
||||
public final Vector2f vect2d = new Vector2f();
|
||||
public final Vector2f vect2d2 = new Vector2f();
|
||||
/**
|
||||
* General matrices.
|
||||
*/
|
||||
public final Matrix3f tempMat3 = new Matrix3f();
|
||||
public final Matrix4f tempMat4 = new Matrix4f();
|
||||
public final Matrix4f tempMat42 = new Matrix4f();
|
||||
/**
|
||||
* General quaternions.
|
||||
*/
|
||||
public final Quaternion quat1 = new Quaternion();
|
||||
public final Quaternion quat2 = new Quaternion();
|
||||
|
||||
public final float[] matrixWrite = new float[16];
|
||||
}
|
||||
672
java/io/eiren/math/FloatMath.java
Normal file
672
java/io/eiren/math/FloatMath.java
Normal file
@@ -0,0 +1,672 @@
|
||||
package io.eiren.math;
|
||||
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.math.Vector4f;
|
||||
|
||||
|
||||
public class FloatMath {
|
||||
|
||||
public static final float PI = (float) Math.PI;
|
||||
public static final float TWO_PI = (float) (Math.PI * 2);
|
||||
public static final float ANGLE_EPSILON = 0.028f; // in degrees (float
|
||||
// epsilon for sin/cos)
|
||||
public static final float ANGLE_EPSILON_RAD = toRad(ANGLE_EPSILON);
|
||||
|
||||
public static final float ZERO_TOLERANCE_F = FastMath.ZERO_TOLERANCE;
|
||||
public static final double ZERO_TOLERANCE_D = 0.0001d;
|
||||
|
||||
public static final float SQRT_TWO = (float) Math.sqrt(2f);
|
||||
public static final float INV_SQRT_TWO = 1f / SQRT_TWO;
|
||||
public static final float SQRT_THREE = (float) Math.sqrt(3f);
|
||||
public static final float INV_SQRT_THREE = 1f / SQRT_THREE;
|
||||
public static final float TWO_FPI = PI * 2;
|
||||
|
||||
public static final float SIN_75_DEG = 0.965926f;
|
||||
public static final float SIN_60_DEG = 0.866025f;
|
||||
public static final float SIN_45_DEG = 0.707107f;
|
||||
public static final float SIN_30_DEG = 0.5f;
|
||||
public static final float SIN_15_DEG = 0.258819f;
|
||||
|
||||
public static final float COS_75_DEG = 0.258819f;
|
||||
public static final float COS_60_DEG = 0.5f;
|
||||
public static final float COS_45_DEG = 0.707107f;
|
||||
public static final float COS_30_DEG = 0.866025f;
|
||||
public static final float COS_15_DEG = 0.965926f;
|
||||
|
||||
public static final int TEN_BITS = ~(~0 << 10);
|
||||
public static final int TENTH_BIT = 1 << 10;
|
||||
public static final int TEN_BITS_MAX = ~(~0 << 9);
|
||||
public static final int TEN_BITS_MAX_UNSIGNED = ~(~0 << 10);
|
||||
public static final int TWO_BITS = ~(~0 << 2);
|
||||
public static final int SECOND_BIT = 1 << 2;
|
||||
public static final int TWO_BITS_MAX = ~(~0 << 1);
|
||||
public static final int TWO_BITS_MAX_UNSIGNED = ~(~0 << 2);
|
||||
|
||||
public static float roundIfZero(float x) {
|
||||
return Math.abs(x) < ZERO_TOLERANCE_F ? 0.0f : x;
|
||||
}
|
||||
|
||||
public static boolean equalsToZero(float x) {
|
||||
return Math.abs(x) < ZERO_TOLERANCE_F;
|
||||
}
|
||||
|
||||
public static boolean lessThanZero(float x) {
|
||||
return (x < -ZERO_TOLERANCE_F);
|
||||
}
|
||||
|
||||
public static boolean lessOrEqualsToZero(float x) {
|
||||
return (x < ZERO_TOLERANCE_F);
|
||||
}
|
||||
|
||||
public static boolean greaterThanZero(float x) {
|
||||
return (x > ZERO_TOLERANCE_F);
|
||||
}
|
||||
|
||||
public static boolean greaterOrEqualsToZero(float x) {
|
||||
return (x > -ZERO_TOLERANCE_F);
|
||||
}
|
||||
|
||||
public static boolean equalsToZero(float x, float epsilon) {
|
||||
return Math.abs(x) < epsilon;
|
||||
}
|
||||
|
||||
public static boolean equalsWithEpsilon(float x, float y) {
|
||||
return Math.abs(x - y) < ZERO_TOLERANCE_F;
|
||||
}
|
||||
|
||||
public static boolean equalsWithEpsilon(float x, float y, float epsilon) {
|
||||
return Math.abs(x - y) < epsilon;
|
||||
}
|
||||
|
||||
public static boolean lessWithEpsilon(float x, float y) {
|
||||
return (x < y - ZERO_TOLERANCE_F);
|
||||
}
|
||||
|
||||
public static boolean lessOrEqualsWithEpsilon(float x, float y) {
|
||||
return (x < y + ZERO_TOLERANCE_F);
|
||||
}
|
||||
|
||||
public static boolean lessWithEpsilon(float x, float y, float epsilon) {
|
||||
return (x < y - epsilon);
|
||||
}
|
||||
|
||||
public static boolean lessOrEqualsWithEpsilon(float x, float y, float epsilon) {
|
||||
return (x < y + epsilon);
|
||||
}
|
||||
|
||||
public static boolean greaterWithEpsilon(float x, float y) {
|
||||
return (x > y + ZERO_TOLERANCE_F);
|
||||
}
|
||||
|
||||
public static boolean greaterOrEqualsWithEpsilon(float x, float y) {
|
||||
return (x > y - ZERO_TOLERANCE_F);
|
||||
}
|
||||
|
||||
public static boolean greaterWithEpsilon(float x, float y, float epsilon) {
|
||||
return (x > y + epsilon);
|
||||
}
|
||||
|
||||
public static boolean greaterOrEqualsWithEpsilon(float x, float y, float epsilon) {
|
||||
return (x > y - epsilon);
|
||||
}
|
||||
|
||||
public static double roundIfZero(double x) {
|
||||
return Math.abs(x) < ZERO_TOLERANCE_D ? 0.0d : x;
|
||||
}
|
||||
|
||||
public static boolean equalsToZero(double x) {
|
||||
return Math.abs(x) < ZERO_TOLERANCE_D;
|
||||
}
|
||||
|
||||
public static boolean equalsWithEpsilon(double x, double y) {
|
||||
return Math.abs(x - y) < ZERO_TOLERANCE_D;
|
||||
}
|
||||
|
||||
public static boolean lessWithEpsilon(double x, double y) {
|
||||
return (x < y - ZERO_TOLERANCE_D);
|
||||
}
|
||||
|
||||
public static boolean lessOrEqualsWithEpsilon(double x, double y) {
|
||||
return (x < y + ZERO_TOLERANCE_D);
|
||||
}
|
||||
|
||||
public static boolean greaterWithEpsilon(double x, double y) {
|
||||
return (x > y + ZERO_TOLERANCE_D);
|
||||
}
|
||||
|
||||
public static boolean greaterOrEqualsWithEpsilon(double x, double y) {
|
||||
return (x > y - ZERO_TOLERANCE_D);
|
||||
}
|
||||
|
||||
public static float toDegrees(float angrad) {
|
||||
return angrad * 180.0f / PI;
|
||||
}
|
||||
|
||||
public static float toRad(float deg) {
|
||||
return deg / 180.0f * PI;
|
||||
}
|
||||
|
||||
public static boolean radEqual(float angle1, float angle2) {
|
||||
float diff = clampRad(angle1 - angle2);
|
||||
return Math.abs(diff) < ANGLE_EPSILON_RAD;
|
||||
}
|
||||
|
||||
public static boolean degreesEqual(float angle1, float angle2) {
|
||||
float diff = clampDegrees(angle1 - angle2);
|
||||
return Math.abs(diff) < ANGLE_EPSILON;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #normalizeRad(float)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static float clampRad(float angle) {
|
||||
return normalizeRad(angle);
|
||||
}
|
||||
|
||||
public static float normalizeRad(float angle) {
|
||||
return FastMath.normalize(angle, -FastMath.PI, FastMath.PI);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #normalizeDegrees(float)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static float clampDegrees(float angle) {
|
||||
return normalizeDegrees(angle);
|
||||
}
|
||||
|
||||
public static float normalizeDegrees(float angle) {
|
||||
return FastMath.normalize(angle, -180f, 180f);
|
||||
}
|
||||
|
||||
public static float animateEase(float t) {
|
||||
// Special case of Bezier interpolation (p0 = p1 = 0, p2 = p3 = 1)
|
||||
return (3.0f - 2.0f * t) * t * t;
|
||||
}
|
||||
|
||||
public static float animateEaseIn(float t) {
|
||||
return t * t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lineary remaps value from the source interval to the target interval.
|
||||
* <a href="https://en.wikipedia.org/wiki/Linear_interpolation">details</a>
|
||||
*/
|
||||
public static float mapValue(
|
||||
float value,
|
||||
float sourceStart,
|
||||
float sourceEnd,
|
||||
float targetStart,
|
||||
float targetEnd
|
||||
) {
|
||||
return targetStart
|
||||
+ (value - sourceStart) * (targetEnd - targetStart) / (sourceEnd - sourceStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamps the given value and remaps to the target interval.
|
||||
* <p>
|
||||
* Note the source interval values should be sorted.
|
||||
*/
|
||||
public static float mapValueWithClampBefore(
|
||||
float value,
|
||||
float sourceBottom,
|
||||
float sourceTop,
|
||||
float targetBottom,
|
||||
float targetTop
|
||||
) {
|
||||
return mapValue(
|
||||
clamp(value, sourceBottom, sourceTop),
|
||||
sourceBottom,
|
||||
sourceTop,
|
||||
targetBottom,
|
||||
targetTop
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remaps the given value to the target interval and clamps.
|
||||
* <p>
|
||||
* Note the target interval values should be sorted.
|
||||
*/
|
||||
public static float mapValueWithClampAfter(
|
||||
float value,
|
||||
float sourceBottom,
|
||||
float sourceTop,
|
||||
float targetBottom,
|
||||
float targetTop
|
||||
) {
|
||||
return clamp(
|
||||
mapValue(value, sourceBottom, sourceTop, targetBottom, targetTop),
|
||||
targetBottom,
|
||||
targetTop
|
||||
);
|
||||
}
|
||||
|
||||
public static float smoothstep(float edge0, float edge1, float x) {
|
||||
// Scale, bias and saturate x to 0..1 range
|
||||
x = FastMath.clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
|
||||
// Evaluate polynomial
|
||||
return x * x * (3f - 2f * x);
|
||||
}
|
||||
|
||||
public static float smootherstep(float edge0, float edge1, float x) {
|
||||
// Scale, and clamp x to 0..1 range
|
||||
x = FastMath.clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
|
||||
// Evaluate polynomial
|
||||
return x * x * x * (x * (x * 6f - 15f) + 10f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies linear contrast (with clamping).
|
||||
*
|
||||
* @param t - input value in range (0..1)
|
||||
* @param k - contrast factor in range (-1..1):
|
||||
* <ul>
|
||||
* <li>1.0 - maximal contrast</li>
|
||||
* <li><b>0.0</b> - bypass (returns input value)</li>
|
||||
* <li>-1.0 - minimal contrast (returns 0.5f for any input)</li>
|
||||
* </ul>
|
||||
* @return contrasted value in range (0..1)
|
||||
*/
|
||||
public static float contrastLinear(float t, float k) {
|
||||
float x = 2f * t - 1f; // -1..1
|
||||
float gamma = (1f + k) / (1f - k);
|
||||
float f = FastMath.clamp(gamma * x, -1f, 1f); // -1..1
|
||||
return 0.5f * (f + 1f); // 0..1
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies non-linear contrast by power function.
|
||||
*
|
||||
* @param t - input value in range (0..1)
|
||||
* @param k - contrast factor in range (-1..1) exclusive:
|
||||
* <ul>
|
||||
* <li>0.999 - maximal contrast</li>
|
||||
* <li>0.0 - bypass (returns input value)</li>
|
||||
* <li>-0.999 - minimal contrast</li>
|
||||
* </ul>
|
||||
* @return contrasted value in range (0..1)
|
||||
*/
|
||||
public static float contrastPower(float t, float k) {
|
||||
float x = 2f * t - 1f; // -1..1
|
||||
float gamma = (1f - k) / (1f + k);
|
||||
float f = FastMath.sign(x) * FastMath.pow(FastMath.abs(x), gamma); // -1..1
|
||||
return 0.5f * (f + 1f); // 0..1
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies non-linear contrast by square splines.
|
||||
*
|
||||
* @param t - input value in range (0..1)
|
||||
* @param k - contrast factor in range (-1..1):
|
||||
* <ul>
|
||||
* <li>1.0 - maximal contrast</li>
|
||||
* <li>0.0 - bypass (returns input value)</li>
|
||||
* <li>-1.0 - minimal contrast</li>
|
||||
* </ul>
|
||||
* @return contrasted value in range (0..1)
|
||||
*/
|
||||
public static float contrastQuadricSpline(float t, float k) {
|
||||
float x = 2f * t - 1f; // -1..1
|
||||
float f = x * (1f + k * (1f - FastMath.abs(x))); // -1..1
|
||||
return 0.5f * (f + 1f); // 0..1
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies non-linear contrast by square splines inverted function.
|
||||
*
|
||||
* @param t - input value in range (0..1)
|
||||
* @param k - contrast factor in range (-2..2):
|
||||
* <ul>
|
||||
* <li>2.0 - maximal contrast</li>
|
||||
* <li>0.0 - bypass (returns input value)</li>
|
||||
* <li>-2.0 - minimal contrast</li>
|
||||
* </ul>
|
||||
* @return contrasted value in range (0..1)
|
||||
*/
|
||||
public static float contrastInvertQuadricSpline(float t, float k) {
|
||||
float x = 2f * t - 1f; // -1..1
|
||||
float g;
|
||||
if (k > 0) {
|
||||
g = FastMath.sign(x) * FastMath.sqrt(FastMath.abs(x)) - 2f * x;
|
||||
} else {
|
||||
g = FastMath.sign(x) * (FastMath.sqrt(1f - FastMath.abs(x)) - 1f);
|
||||
}
|
||||
float f = (1f + k) * x + k * g; // -1..1
|
||||
return 0.5f * (f + 1f); // 0..1
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies non-linear contrast by cubic splines.
|
||||
*
|
||||
* @param t - input value in range (0..1)
|
||||
* @param k - contrast factor in range (-1..1):
|
||||
* <ul>
|
||||
* <li>1.0 - maximal contrast</li>
|
||||
* <li>0.0 - bypass (returns input value)</li>
|
||||
* <li>-1.0 - minimal contrast</li>
|
||||
* </ul>
|
||||
* @return contrasted value in range (0..1)
|
||||
*/
|
||||
public static float contrastCubicSpline(float t, float k) {
|
||||
float x = 2f * t - 1f; // -1..1
|
||||
float f = x * (1f + FastMath.abs(k) * (x * x - 1f));
|
||||
if (k < 0)
|
||||
f -= x * 3f * k * (1f - FastMath.abs(x));
|
||||
return 0.5f * (f + 1f); // 0..1
|
||||
}
|
||||
|
||||
public static float fraction(float f) {
|
||||
return f - (int) f;
|
||||
}
|
||||
|
||||
public static double fraction(double d) {
|
||||
return d - (long) d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Do not copy {@link Math} methods.
|
||||
*/
|
||||
@Deprecated
|
||||
public static float min(float a, float b) {
|
||||
return a > b ? b : a;
|
||||
}
|
||||
|
||||
public static float min(float a, float b, float c) {
|
||||
return Math.min(Math.min(a, b), c);
|
||||
}
|
||||
|
||||
public static float min(float a, float b, float c, float d) {
|
||||
return Math.min(Math.min(a, b), Math.min(c, d));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Do not copy {@link Math} methods.
|
||||
*/
|
||||
@Deprecated
|
||||
public static float max(float a, float b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
public static float max(float a, float b, float c) {
|
||||
return Math.max(Math.max(a, b), c);
|
||||
}
|
||||
|
||||
public static float max(float a, float b, float c, float d) {
|
||||
return Math.max(Math.max(a, b), Math.max(c, d));
|
||||
}
|
||||
|
||||
public static float cos(float value) {
|
||||
return (float) Math.cos(value);
|
||||
}
|
||||
|
||||
public static float sin(float value) {
|
||||
return (float) Math.sin(value);
|
||||
}
|
||||
|
||||
public static float ceil(float value) {
|
||||
return (float) Math.ceil(value);
|
||||
}
|
||||
|
||||
public static float floor(float value) {
|
||||
return (float) Math.floor(value);
|
||||
}
|
||||
|
||||
public static float pow(float value, float power) {
|
||||
return (float) Math.pow(value, power);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Do not copy {@link Math} methods.
|
||||
*/
|
||||
@Deprecated
|
||||
public static float abs(float value) {
|
||||
return (float) Math.abs(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Do not copy {@link Math} methods.
|
||||
*/
|
||||
@Deprecated
|
||||
public static float round(float value) {
|
||||
return (float) Math.round(value);
|
||||
}
|
||||
|
||||
public static float sqrt(float value) {
|
||||
return (float) Math.sqrt(value);
|
||||
}
|
||||
|
||||
public static float distance(float x0, float y0, float z0, float x1, float y1, float z1) {
|
||||
return distance(x1 - x0, y1 - y0, z1 - z0);
|
||||
}
|
||||
|
||||
public static float distance(float x, float y, float z) {
|
||||
return sqrt(sqrDistance(x, y, z));
|
||||
}
|
||||
|
||||
public static float sqrDistance(float x, float y, float z) {
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
public static float distance(float x, float y) {
|
||||
return sqrt(sqrDistance(x, y));
|
||||
}
|
||||
|
||||
public static float sqrDistance(float x, float y) {
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
public static float sqrDistance(Vector3f v, float x1, float y1, float z1) {
|
||||
return sqrDistance(x1 - v.x, y1 - v.y, z1 - v.z);
|
||||
}
|
||||
|
||||
public static float sqrDistance(float x0, float y0, float z0, float x1, float y1, float z1) {
|
||||
return sqrDistance(x1 - x0, y1 - y0, z1 - z0);
|
||||
}
|
||||
|
||||
public static float hypot(float x, float y) {
|
||||
return FastMath.sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
public static float hypot(float x, float y, float z) {
|
||||
return FastMath.sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as FastMath.clamp
|
||||
*/
|
||||
public static float clamp(float value, float min, float max) {
|
||||
if (value <= min)
|
||||
return min;
|
||||
if (value >= max)
|
||||
return max;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Vector3f int2101010RevToFloats(int packedValue, Vector3f store) {
|
||||
if (store == null)
|
||||
store = new Vector3f();
|
||||
store.x = packedValue & TEN_BITS_MAX;
|
||||
if ((packedValue & TENTH_BIT) != 0)
|
||||
store.x *= -1;
|
||||
store.y = (packedValue >>> 10) & TEN_BITS_MAX;
|
||||
if ((packedValue & (TENTH_BIT << 10)) != 0)
|
||||
store.y *= -1;
|
||||
store.z = (packedValue >>> 20) & TEN_BITS_MAX;
|
||||
if ((packedValue & (TENTH_BIT << 20)) != 0)
|
||||
store.z *= -1;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static int floatToInt210101Rev(Vector3f values) {
|
||||
int store = 0;
|
||||
store |= ((int) values.x) & TEN_BITS_MAX;
|
||||
if (values.x < 0)
|
||||
store |= TENTH_BIT;
|
||||
store |= (((int) values.y) & TEN_BITS_MAX) << 10;
|
||||
if (values.y < 0)
|
||||
store |= TENTH_BIT << 10;
|
||||
store |= (((int) values.z) & TEN_BITS_MAX) << 20;
|
||||
if (values.z < 0)
|
||||
store |= TENTH_BIT << 20;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static int floatToInt210101RevNormalized(Vector3f values) {
|
||||
int store = 0;
|
||||
store |= ((int) (values.x * TEN_BITS)) & TEN_BITS_MAX;
|
||||
if (values.x < 0)
|
||||
store |= TENTH_BIT;
|
||||
store |= (((int) (values.y * TEN_BITS)) & TEN_BITS_MAX) << 10;
|
||||
if (values.y < 0)
|
||||
store |= TENTH_BIT << 10;
|
||||
store |= (((int) (values.z * TEN_BITS)) & TEN_BITS_MAX) << 20;
|
||||
if (values.z < 0)
|
||||
store |= TENTH_BIT << 20;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static int floatToUnsignedInt210101Rev(Vector3f values) {
|
||||
int store = 0;
|
||||
store |= ((int) values.x) & TEN_BITS;
|
||||
store |= (((int) values.y) & TEN_BITS) << 10;
|
||||
store |= (((int) values.z) & TEN_BITS) << 20;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static int floatToUnsignedInt210101RevNormalized(Vector3f values) {
|
||||
int store = 0;
|
||||
store |= ((int) (values.x * TEN_BITS)) & TEN_BITS;
|
||||
store |= (((int) (values.y * TEN_BITS)) & TEN_BITS) << 10;
|
||||
store |= (((int) (values.z * TEN_BITS)) & TEN_BITS) << 20;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static int floatToInt210101Rev(float x, float y, float z) {
|
||||
int store = 0;
|
||||
store |= ((int) x) & TEN_BITS_MAX;
|
||||
if (x < 0)
|
||||
store |= TENTH_BIT;
|
||||
store |= (((int) y) & TEN_BITS_MAX) << 10;
|
||||
if (y < 0)
|
||||
store |= TENTH_BIT << 10;
|
||||
store |= (((int) z) & TEN_BITS_MAX) << 20;
|
||||
if (z < 0)
|
||||
store |= TENTH_BIT << 20;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static int floatToUnsignedInt210101Rev(float x, float y, float z) {
|
||||
int store = 0;
|
||||
store |= ((int) x) & TEN_BITS;
|
||||
store |= (((int) y) & TEN_BITS) << 10;
|
||||
store |= (((int) z) & TEN_BITS) << 20;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static Vector4f int2101010RevToFloats(int packedValue, Vector4f store) {
|
||||
if (store == null)
|
||||
store = new Vector4f();
|
||||
store.x = packedValue & TEN_BITS_MAX;
|
||||
if ((packedValue & TENTH_BIT) != 0)
|
||||
store.x *= -1;
|
||||
store.y = (packedValue >>> 10) & TEN_BITS_MAX;
|
||||
if ((packedValue & (TENTH_BIT << 10)) != 0)
|
||||
store.y *= -1;
|
||||
store.z = (packedValue >>> 20) & TEN_BITS_MAX;
|
||||
if ((packedValue & (TENTH_BIT << 20)) != 0)
|
||||
store.z *= -1;
|
||||
store.w = (packedValue >>> 30) & TWO_BITS_MAX;
|
||||
if ((packedValue & (SECOND_BIT << 30)) != 0)
|
||||
store.w *= -1;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static int floatToInt210101Rev(Vector4f values) {
|
||||
int store = 0;
|
||||
store |= ((int) values.x) & TEN_BITS_MAX;
|
||||
if (values.x < 0)
|
||||
store |= TENTH_BIT;
|
||||
store |= (((int) values.y) & TEN_BITS_MAX) << 10;
|
||||
if (values.y < 0)
|
||||
store |= TENTH_BIT << 10;
|
||||
store |= (((int) values.z) & TEN_BITS_MAX) << 20;
|
||||
if (values.z < 0)
|
||||
store |= TENTH_BIT << 20;
|
||||
store |= (((int) values.z) & TWO_BITS_MAX) << 30;
|
||||
if (values.w < 0)
|
||||
store |= SECOND_BIT << 30;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static int floatToUnsignedInt210101Rev(Vector4f values) {
|
||||
int store = 0;
|
||||
store |= ((int) values.x) & TEN_BITS;
|
||||
store |= (((int) values.y) & TEN_BITS) << 10;
|
||||
store |= (((int) values.z) & TEN_BITS) << 20;
|
||||
store |= (((int) values.z) & TWO_BITS) << 30;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static Vector3f unsignedInt2101010RevToFloats(int packedValue, Vector3f store) {
|
||||
if (store == null)
|
||||
store = new Vector3f();
|
||||
store.x = packedValue & TEN_BITS;
|
||||
store.y = (packedValue >>> 10) & TEN_BITS;
|
||||
store.z = (packedValue >>> 20) & TEN_BITS;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static Vector4f unsignedInt2101010RevToFloats(int packedValue, Vector4f store) {
|
||||
if (store == null)
|
||||
store = new Vector4f();
|
||||
store.x = packedValue & TEN_BITS;
|
||||
store.y = (packedValue >>> 10) & TEN_BITS;
|
||||
store.z = (packedValue >>> 20) & TEN_BITS;
|
||||
store.w = (packedValue >>> 30) & TWO_BITS;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static Vector3f int2101010RevNormalizedToFloats(int packedValue, Vector3f store) {
|
||||
store = int2101010RevToFloats(packedValue, store);
|
||||
store.x /= TEN_BITS_MAX;
|
||||
store.y /= TEN_BITS_MAX;
|
||||
store.z /= TEN_BITS_MAX;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static Vector4f int2101010RevNormalizedToFloats(int packedValue, Vector4f store) {
|
||||
store = int2101010RevToFloats(packedValue, store);
|
||||
store.x /= TEN_BITS_MAX;
|
||||
store.y /= TEN_BITS_MAX;
|
||||
store.z /= TEN_BITS_MAX;
|
||||
store.w /= TWO_BITS_MAX;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static Vector3f unsignedInt2101010RevNormalizedToFloats(
|
||||
int packedValue,
|
||||
Vector3f store
|
||||
) {
|
||||
store = unsignedInt2101010RevToFloats(packedValue, store);
|
||||
store.x /= TEN_BITS;
|
||||
store.y /= TEN_BITS;
|
||||
store.z /= TEN_BITS;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static Vector4f unsignedInt2101010RevNormalizedToFloats(
|
||||
int packedValue,
|
||||
Vector4f store
|
||||
) {
|
||||
store = unsignedInt2101010RevToFloats(packedValue, store);
|
||||
store.x /= TEN_BITS;
|
||||
store.y /= TEN_BITS;
|
||||
store.z /= TEN_BITS;
|
||||
store.w /= TWO_BITS;
|
||||
return store;
|
||||
}
|
||||
}
|
||||
257
java/io/eiren/math/Vector3d.java
Normal file
257
java/io/eiren/math/Vector3d.java
Normal file
@@ -0,0 +1,257 @@
|
||||
package io.eiren.math;
|
||||
|
||||
import com.jme3.math.Vector3f;
|
||||
|
||||
|
||||
public class Vector3d implements Cloneable {
|
||||
|
||||
public double x;
|
||||
public double y;
|
||||
public double z;
|
||||
|
||||
public Vector3d() {
|
||||
}
|
||||
|
||||
public Vector3d(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public Vector3d(double x1, double y1, double z1, double x2, double y2, double z2) {
|
||||
this.x = x2 - x1;
|
||||
this.y = y2 - y1;
|
||||
this.z = z2 - z1;
|
||||
}
|
||||
|
||||
public Vector3d(Vector3f src) {
|
||||
this(src.x, src.y, src.z);
|
||||
}
|
||||
|
||||
public Vector3d set(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3d set(Vector3d v) {
|
||||
this.x = v.x;
|
||||
this.y = v.y;
|
||||
this.z = v.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3d add(double addX, double addY, double addZ) {
|
||||
return new Vector3d(this.x + addX, this.y + addY, this.z + addZ);
|
||||
}
|
||||
|
||||
public Vector3d addLocal(Vector3d vec) {
|
||||
return addLocal(vec.x, vec.y, vec.z);
|
||||
}
|
||||
|
||||
public Vector3d addLocal(double addX, double addY, double addZ) {
|
||||
x += addX;
|
||||
y += addY;
|
||||
z += addZ;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3d substract(double subX, double subY, double subZ) {
|
||||
return new Vector3d(this.x - subX, this.y - subY, this.z - subZ);
|
||||
}
|
||||
|
||||
public Vector3d substractLocal(Vector3d vec) {
|
||||
if (null == vec) {
|
||||
return null;
|
||||
}
|
||||
x -= vec.x;
|
||||
y -= vec.y;
|
||||
z -= vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3d substractLocal(double subX, double subY, double subZ) {
|
||||
x -= subX;
|
||||
y -= subY;
|
||||
z -= subZ;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3d negate() {
|
||||
return new Vector3d(-x, -y, -z);
|
||||
}
|
||||
|
||||
public Vector3d negateLocal() {
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3d mult(double scalar) {
|
||||
return new Vector3d(x * scalar, y * scalar, z * scalar);
|
||||
}
|
||||
|
||||
public Vector3d multLocal(double scalar) {
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
z *= scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3d divide(double scalar) {
|
||||
return new Vector3d(x / scalar, y / scalar, z / scalar);
|
||||
}
|
||||
|
||||
public Vector3d divideLocal(double scalar) {
|
||||
x /= scalar;
|
||||
y /= scalar;
|
||||
z /= scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double dot(Vector3d v) {
|
||||
return x * v.x + y * v.y + z * v.z;
|
||||
}
|
||||
|
||||
public double dot(double vx, double vy, double vz) {
|
||||
return x * vx + y * vy + z * vz;
|
||||
}
|
||||
|
||||
public Vector3d cross(Vector3d other, Vector3d result) {
|
||||
if (result == null)
|
||||
result = new Vector3d();
|
||||
double resX = ((y * other.z) - (z * other.y));
|
||||
double resY = ((z * other.x) - (x * other.z));
|
||||
double resZ = ((x * other.y) - (y * other.x));
|
||||
result.set(resX, resY, resZ);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3d clone() {
|
||||
return new Vector3d(this.x, this.y, this.z);
|
||||
}
|
||||
|
||||
public Vector3d normalize() {
|
||||
double length = x * x + y * y + z * z;
|
||||
if (length != 1.0 && length != 0.0) {
|
||||
double invLength = 1.0 / Math.sqrt(length);
|
||||
return mult(invLength);
|
||||
}
|
||||
return clone();
|
||||
}
|
||||
|
||||
public Vector3d normalizeLocal() {
|
||||
double length = x * x + y * y + z * z;
|
||||
if (length != 1.0 && length != 0.0) {
|
||||
length = Math.sqrt(length);
|
||||
double invLength = 1.0 / length;
|
||||
x *= invLength;
|
||||
z *= invLength;
|
||||
y *= invLength;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3f toVector3f() {
|
||||
return new Vector3f((float) x, (float) y, (float) z);
|
||||
}
|
||||
|
||||
public double length() {
|
||||
return Math.sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
public double lengthSquared() {
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder("Vector3D{")
|
||||
.append(x)
|
||||
.append(',')
|
||||
.append(y)
|
||||
.append(',')
|
||||
.append(z)
|
||||
.append('}')
|
||||
.toString();
|
||||
}
|
||||
|
||||
public void rotateAroundX(float f) {
|
||||
double f1 = Math.cos(f);
|
||||
double f2 = Math.sin(f);
|
||||
double d = x;
|
||||
double d1 = y * f1 + z * f2;
|
||||
double d2 = z * f1 - y * f2;
|
||||
x = (float) d;
|
||||
y = (float) d1;
|
||||
z = (float) d2;
|
||||
}
|
||||
|
||||
public void rotateAroundY(float f) {
|
||||
double f1 = Math.cos(f);
|
||||
double f2 = Math.sin(f);
|
||||
double d = x * f1 + z * f2;
|
||||
double d1 = y;
|
||||
double d2 = z * f1 - x * f2;
|
||||
x = (float) d;
|
||||
y = (float) d1;
|
||||
z = (float) d2;
|
||||
}
|
||||
|
||||
public double distanceTo(Vector3d vec3d) {
|
||||
return Math.sqrt(squaredDistance(vec3d));
|
||||
}
|
||||
|
||||
public double squaredDistance(Vector3d point) {
|
||||
return squaredDistance(point.x, point.y, point.z);
|
||||
}
|
||||
|
||||
public double squaredDistance(double toX, double toY, double toZ) {
|
||||
return (this.x - toX) * (this.x - toX)
|
||||
+ (this.y - toY) * (this.y - toY)
|
||||
+ (this.z - toZ) * (this.z - toZ);
|
||||
}
|
||||
|
||||
public Vector3d add(Vector3d dir) {
|
||||
return add(dir.x, dir.y, dir.z);
|
||||
}
|
||||
|
||||
public Vector3d substract(Vector3d dir) {
|
||||
return substract(dir.x, dir.y, dir.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(x);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(y);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(z);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Vector3d other = (Vector3d) obj;
|
||||
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
|
||||
return false;
|
||||
if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
|
||||
return false;
|
||||
if (Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
130
java/io/eiren/util/BufferedTimer.java
Normal file
130
java/io/eiren/util/BufferedTimer.java
Normal file
@@ -0,0 +1,130 @@
|
||||
package io.eiren.util;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
|
||||
import com.jme3.system.NanoTimer;
|
||||
|
||||
|
||||
/**
|
||||
* This timer accumulate measured TPF and returns average/min/max FPS value
|
||||
*/
|
||||
public class BufferedTimer extends NanoTimer {
|
||||
|
||||
private final float measureInterval;
|
||||
private float averageTpf;
|
||||
private float averageFps;
|
||||
private float averageFrameRenderTime;
|
||||
private float sumFrameRenderTime;
|
||||
private float sumTpf;
|
||||
private float minFpsCurrent;
|
||||
private float maxFpsCurrent;
|
||||
private float maxFps;
|
||||
private float minFps;
|
||||
private int count;
|
||||
private boolean measured = false;
|
||||
|
||||
/**
|
||||
* Measure average tpf over the provided inverval in seconds
|
||||
*
|
||||
* @param measureInterval interval to measure averages over
|
||||
*/
|
||||
public BufferedTimer(float measureInterval) {
|
||||
averageFps = 0;
|
||||
sumTpf = 0;
|
||||
count = 0;
|
||||
this.measureInterval = measureInterval;
|
||||
}
|
||||
|
||||
public float getAverageFPS() {
|
||||
return averageFps;
|
||||
}
|
||||
|
||||
public float getMinFPS() {
|
||||
return minFps;
|
||||
}
|
||||
|
||||
public float getMaxFPS() {
|
||||
return maxFps;
|
||||
}
|
||||
|
||||
public void addRenderTime(float renderTime) {
|
||||
sumFrameRenderTime += renderTime;
|
||||
}
|
||||
|
||||
public float getAverageFrameRenderTime() {
|
||||
return averageFrameRenderTime;
|
||||
}
|
||||
|
||||
public boolean isMeasured() {
|
||||
if (measured) {
|
||||
measured = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public TimerSample getCurrentData() {
|
||||
return new TimerSample(getFrameRate(), minFps, maxFps, averageFps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
super.update();
|
||||
// Accumulate instant rate
|
||||
sumTpf += getTimePerFrame();
|
||||
float fps = getFrameRate();
|
||||
if (fps < minFpsCurrent)
|
||||
minFpsCurrent = fps;
|
||||
if (fps > maxFpsCurrent)
|
||||
maxFpsCurrent = fps;
|
||||
++count;
|
||||
// Calculate results once per measure interval
|
||||
if (!measured || sumTpf > measureInterval) {
|
||||
// Average results
|
||||
averageTpf = sumTpf / count;
|
||||
averageFps = 1.0f / averageTpf;
|
||||
averageFrameRenderTime = sumFrameRenderTime / count;
|
||||
minFps = minFpsCurrent;
|
||||
maxFps = maxFpsCurrent;
|
||||
// Reset counter
|
||||
sumTpf = 0;
|
||||
sumFrameRenderTime = 0;
|
||||
minFpsCurrent = Float.MAX_VALUE;
|
||||
maxFpsCurrent = 0;
|
||||
count = 0;
|
||||
measured = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TimerSample {
|
||||
|
||||
public float fps;
|
||||
public float minFps;
|
||||
public float maxFps;
|
||||
public float averageFps;
|
||||
|
||||
@ConstructorProperties({ "fps", "minFps", "maxFps", "averageFps" })
|
||||
public TimerSample(float fps, float minFps, float maxFps, float averageFps) {
|
||||
this.fps = fps;
|
||||
this.minFps = minFps;
|
||||
this.maxFps = maxFps;
|
||||
this.averageFps = averageFps;
|
||||
}
|
||||
|
||||
public float getFps() {
|
||||
return fps;
|
||||
}
|
||||
|
||||
public float getMinFps() {
|
||||
return minFps;
|
||||
}
|
||||
|
||||
public float getMaxFps() {
|
||||
return maxFps;
|
||||
}
|
||||
|
||||
public float getAverageFps() {
|
||||
return averageFps;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
java/io/eiren/util/MacOSX.java
Normal file
41
java/io/eiren/util/MacOSX.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package io.eiren.util;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.Toolkit;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class MacOSX {
|
||||
|
||||
public static void setIcons(List<? extends Image> icons) {
|
||||
try {
|
||||
Class<?> applicationClass = Class.forName("com.apple.eawt.Application");
|
||||
Method m = applicationClass.getDeclaredMethod("getApplication");
|
||||
Object application = m.invoke(null);
|
||||
m = application.getClass().getDeclaredMethod("setDockIconImage", Image.class);
|
||||
m.invoke(application, icons.get(icons.size() - 1));
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
public static void setTitle(String title) {
|
||||
try {
|
||||
Class<?> applicationClass = Class.forName("com.apple.eawt.Application");
|
||||
Method m = applicationClass.getDeclaredMethod("getApplication");
|
||||
Object application = m.invoke(null);
|
||||
m = application.getClass().getDeclaredMethod("setDockIconImage", String.class);
|
||||
m.invoke(application, title);
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
public static boolean hasRetinaDisplay() {
|
||||
Object obj = Toolkit.getDefaultToolkit().getDesktopProperty("apple.awt.contentScaleFactor");
|
||||
if (obj instanceof Float) {
|
||||
Float f = (Float) obj;
|
||||
int scale = f.intValue();
|
||||
return (scale == 2); // 1 indicates a regular mac display.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
47
java/io/eiren/util/OperatingSystem.java
Normal file
47
java/io/eiren/util/OperatingSystem.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package io.eiren.util;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
public enum OperatingSystem {
|
||||
|
||||
//@formatter:off
|
||||
LINUX("linux", new String[]{"linux", "unix"}),
|
||||
WINDOWS("windows", new String[]{"win"}),
|
||||
OSX("osx", new String[]{"mac"}),
|
||||
UNKNOWN("unknown", new String[0]);
|
||||
//@fomatter: on
|
||||
|
||||
private final String[] aliases;
|
||||
public final String name;
|
||||
private static OperatingSystem currentPlatform;
|
||||
|
||||
private OperatingSystem(String name, String[] aliases) {
|
||||
this.aliases = aliases;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static String getJavaExecutable(boolean forceConsole) {
|
||||
String separator = System.getProperty("file.separator");
|
||||
String path = System.getProperty("java.home") + separator + "bin" + separator;
|
||||
if(getCurrentPlatform() == WINDOWS) {
|
||||
if(!forceConsole && new File(path + "javaw.exe").isFile())
|
||||
return path + "javaw.exe";
|
||||
return path + "java.exe";
|
||||
}
|
||||
return path + "java";
|
||||
}
|
||||
|
||||
public static OperatingSystem getCurrentPlatform() {
|
||||
if(currentPlatform != null)
|
||||
return currentPlatform;
|
||||
String osName = System.getProperty("os.name").toLowerCase();
|
||||
for(OperatingSystem os : values()) {
|
||||
for(String alias : os.aliases) {
|
||||
if(osName.contains(alias))
|
||||
return currentPlatform = os;
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
35
java/io/eiren/util/StringUtils.java
Normal file
35
java/io/eiren/util/StringUtils.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package io.eiren.util;
|
||||
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
public class StringUtils {
|
||||
|
||||
private static char DECIMAL_SEP;
|
||||
|
||||
public static char getDecimalSeparator() {
|
||||
if (DECIMAL_SEP == '\u0000') {
|
||||
final Locale l = Locale.getDefault(Locale.Category.FORMAT);
|
||||
// Formatter.java always use "." in the Locale.US
|
||||
DECIMAL_SEP = (l == null || l.equals(Locale.US)
|
||||
? '.'
|
||||
: DecimalFormatSymbols.getInstance(l).getDecimalSeparator());
|
||||
}
|
||||
return DECIMAL_SEP;
|
||||
}
|
||||
|
||||
public static String prettyNumber(float f) {
|
||||
return prettyNumber(f, 4);
|
||||
}
|
||||
|
||||
public static String prettyNumber(float f, int numDigits) {
|
||||
String str = String.format("%." + numDigits + "f", f);
|
||||
if (numDigits != 0)
|
||||
str = org.apache.commons.lang3.StringUtils.stripEnd(str, "0");
|
||||
char lastChar = str.charAt(str.length() - 1);
|
||||
if (lastChar == getDecimalSeparator())
|
||||
str = str.substring(0, str.length() - 1);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
176
java/io/eiren/util/Util.java
Normal file
176
java/io/eiren/util/Util.java
Normal file
@@ -0,0 +1,176 @@
|
||||
package io.eiren.util;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
||||
public class Util {
|
||||
|
||||
public static void close(Object r) {
|
||||
try {
|
||||
if (r != null) {
|
||||
if (r instanceof Closeable)
|
||||
((Closeable) r).close();
|
||||
else if (r instanceof AutoCloseable)
|
||||
((AutoCloseable) r).close();
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
public static void close(Object r1, Object r2) {
|
||||
close(r1);
|
||||
close(r2);
|
||||
}
|
||||
|
||||
public static void close(Object... r) {
|
||||
for (int i = 0; i < r.length; ++i)
|
||||
try {
|
||||
if (r[i] != null) {
|
||||
if (r[i] instanceof Closeable)
|
||||
((Closeable) r[i]).close();
|
||||
else if (r[i] instanceof AutoCloseable)
|
||||
((AutoCloseable) r[i]).close();
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
public static void close(AutoCloseable... r) {
|
||||
for (int i = 0; i < r.length; ++i)
|
||||
try {
|
||||
if (r[i] != null)
|
||||
r[i].close();
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
public static void close(Closeable... r) {
|
||||
for (int i = 0; i < r.length; ++i)
|
||||
try {
|
||||
if (r[i] != null)
|
||||
r[i].close();
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Performs a deep toString of provided object. It shows content of arrays,
|
||||
* collections and maps (trove not supported yet).
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Highly ineffective, use only for debug.</b>
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
public static String toString(Object object) {
|
||||
if (object == null)
|
||||
return "null";
|
||||
StringBuilder buf = new StringBuilder();
|
||||
elementToString(object, buf, new HashSet<Object>());
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static void deepToString(Map<Object, Object> m, StringBuilder buf, Set<Object> dejaVu) {
|
||||
if (m == null) {
|
||||
buf.append("null");
|
||||
return;
|
||||
}
|
||||
if (m.size() == 0) {
|
||||
buf.append("{}");
|
||||
return;
|
||||
}
|
||||
dejaVu.add(m);
|
||||
buf.append('{');
|
||||
Iterator<Entry<Object, Object>> iterator = m.entrySet().iterator();
|
||||
boolean has = false;
|
||||
while (iterator.hasNext()) {
|
||||
if (has)
|
||||
buf.append(',');
|
||||
Entry<Object, Object> e = iterator.next();
|
||||
elementToString(e.getKey(), buf, dejaVu);
|
||||
buf.append(':');
|
||||
elementToString(e.getValue(), buf, dejaVu);
|
||||
has = true;
|
||||
}
|
||||
buf.append('}');
|
||||
dejaVu.remove(m);
|
||||
}
|
||||
|
||||
private static void deepToString(
|
||||
Collection<Object> list,
|
||||
StringBuilder buf,
|
||||
Set<Object> dejaVu
|
||||
) {
|
||||
Object[] array = list.toArray();
|
||||
deepToString(array, buf, dejaVu);
|
||||
}
|
||||
|
||||
private static void deepToString(Object[] a, StringBuilder buf, Set<Object> dejaVu) {
|
||||
if (a == null) {
|
||||
buf.append("null");
|
||||
return;
|
||||
}
|
||||
if (a.length == 0) {
|
||||
buf.append("[]");
|
||||
return;
|
||||
}
|
||||
dejaVu.add(a);
|
||||
buf.append('[');
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
if (i != 0)
|
||||
buf.append(',');
|
||||
Object element = a[i];
|
||||
elementToString(element, buf, dejaVu);
|
||||
}
|
||||
buf.append(']');
|
||||
dejaVu.remove(a);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void elementToString(Object element, StringBuilder buf, Set<Object> dejaVu) {
|
||||
if (element == null) {
|
||||
buf.append("null");
|
||||
} else {
|
||||
Class<?> eClass = element.getClass();
|
||||
if (eClass.isArray()) {
|
||||
if (eClass == byte[].class)
|
||||
buf.append(Arrays.toString((byte[]) element));
|
||||
else if (eClass == short[].class)
|
||||
buf.append(Arrays.toString((short[]) element));
|
||||
else if (eClass == int[].class)
|
||||
buf.append(Arrays.toString((int[]) element));
|
||||
else if (eClass == long[].class)
|
||||
buf.append(Arrays.toString((long[]) element));
|
||||
else if (eClass == char[].class)
|
||||
buf.append(Arrays.toString((char[]) element));
|
||||
else if (eClass == float[].class)
|
||||
buf.append(Arrays.toString((float[]) element));
|
||||
else if (eClass == double[].class)
|
||||
buf.append(Arrays.toString((double[]) element));
|
||||
else if (eClass == boolean[].class)
|
||||
buf.append(Arrays.toString((boolean[]) element));
|
||||
else { // element is an array of object references
|
||||
if (dejaVu.contains(element))
|
||||
buf.append("[...]");
|
||||
else
|
||||
deepToString((Object[]) element, buf, dejaVu);
|
||||
}
|
||||
} else { // element is non-null and not an array
|
||||
if (element instanceof Collection)
|
||||
deepToString((Collection<Object>) element, buf, dejaVu);
|
||||
else if (element instanceof Map)
|
||||
deepToString((Map<Object, Object>) element, buf, dejaVu);
|
||||
else if (element instanceof CharSequence)
|
||||
buf.append('"').append(element.toString()).append('"');
|
||||
else
|
||||
buf.append(element.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10
java/io/eiren/util/ann/AWTThread.java
Normal file
10
java/io/eiren/util/ann/AWTThread.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package io.eiren.util.ann;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
|
||||
@Retention(value = RetentionPolicy.SOURCE)
|
||||
public @interface AWTThread {
|
||||
|
||||
}
|
||||
9
java/io/eiren/util/ann/DebugSwitch.java
Normal file
9
java/io/eiren/util/ann/DebugSwitch.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package io.eiren.util.ann;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD })
|
||||
public @interface DebugSwitch {
|
||||
}
|
||||
19
java/io/eiren/util/ann/NativeUnsafe.java
Normal file
19
java/io/eiren/util/ann/NativeUnsafe.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package io.eiren.util.ann;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/**
|
||||
* Marks methods and classes that use unsafe or direct access to memory. Proceed
|
||||
* with caution.
|
||||
*
|
||||
* @author Rena
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
public @interface NativeUnsafe {
|
||||
|
||||
}
|
||||
30
java/io/eiren/util/ann/Synchronize.java
Normal file
30
java/io/eiren/util/ann/Synchronize.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package io.eiren.util.ann;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Означает необходимость обязательной синхронизации этого меcта во внешних
|
||||
* методах. В аргументах передаётся название поля для синхронизации.
|
||||
* </p>
|
||||
* <p>
|
||||
* Методы, помеченные данной аннотацией могут вызывать только Thread-Safe
|
||||
* методы, либо методы, помеченные такой же аннотацией с тем же полем
|
||||
* синхронизации.
|
||||
* </p>
|
||||
* <p>
|
||||
* Поля, помеченные данной аннотацией должны быть синхронизированны на указанное
|
||||
* поле при чтении или записи.
|
||||
* </p>
|
||||
*
|
||||
* @see {@link ThreadSafe}, {@link ThreadSecure}, {@link ThreadSafeSingle}
|
||||
* @author Rena
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.SOURCE)
|
||||
public @interface Synchronize {
|
||||
|
||||
String[] value();
|
||||
|
||||
}
|
||||
27
java/io/eiren/util/ann/ThreadSafe.java
Normal file
27
java/io/eiren/util/ann/ThreadSafe.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package io.eiren.util.ann;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Методы, помеченные этой аннотацией должны быть Thread-Safe.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Важно:</b> данные методы гарантированно должны обеспечивать потоковую
|
||||
* безопасность, но не обязаны обеспечивать концессивность (полноту данных или
|
||||
* точность синхронизации).
|
||||
* </p>
|
||||
* <p>
|
||||
* Для полностью потоко-безопасных методов можно использовать аннотацию
|
||||
* {@link ThreadSecure}.
|
||||
* </p>
|
||||
*
|
||||
* @see {@link ThreadSecure}, {@link Synchronize}, {@link ThreadSafeSingle}
|
||||
* @author Rena
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.SOURCE)
|
||||
public @interface ThreadSafe {
|
||||
|
||||
}
|
||||
17
java/io/eiren/util/ann/ThreadSafeSingle.java
Normal file
17
java/io/eiren/util/ann/ThreadSafeSingle.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package io.eiren.util.ann;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
|
||||
/**
|
||||
* Соблюдает те же требования что и {@link ThreadSafe} но при условии, что сам
|
||||
* метод вызывается только из одного потока одновременно.
|
||||
*
|
||||
* @see {@link ThreadSafe}, {@link ThreadSecure}, {@link Synchronize}
|
||||
* @author Rena
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.SOURCE)
|
||||
public @interface ThreadSafeSingle {
|
||||
|
||||
}
|
||||
22
java/io/eiren/util/ann/ThreadSecure.java
Normal file
22
java/io/eiren/util/ann/ThreadSecure.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package io.eiren.util.ann;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Методы, помеченные этой аннотацией должны быть полностью Thread-Safe.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Важно:</b> данные методы гарантированно должны обеспечивать потоковую
|
||||
* безопасность и консистентность (полноту данных и точность синхронизации).
|
||||
* </p>
|
||||
*
|
||||
* @see {@link ThreadSafe}, {@link Synchronize}, {@link ThreadSafeSingle}
|
||||
* @author Rena
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.SOURCE)
|
||||
public @interface ThreadSecure {
|
||||
|
||||
}
|
||||
39
java/io/eiren/util/ann/Transient.java
Normal file
39
java/io/eiren/util/ann/Transient.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package io.eiren.util.ann;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Означает что поле используется для временных или быстро изменяющихся
|
||||
* переменных.
|
||||
* </p>
|
||||
* <p>
|
||||
* Поле помеченное этой аннотацией не влияет на долгосрочное состояние объекта,
|
||||
* не участвует в сериализации, вычислении equals и hashCode, не определяет
|
||||
* поведение объекта для внешнего кода. Поэтому такие поля не должны
|
||||
* использоваться внешним кодом, их состояние имеет смысл только для самого
|
||||
* объекта в котором они объявлены.
|
||||
* </p>
|
||||
* Примеры:
|
||||
* <ul>
|
||||
* <li>Временный объект, который используется в методах для внутренних
|
||||
* вычислений. Например векторные и матричные вычисления.</li>
|
||||
* <li>Внутренний флаг для мультитрединга. Например, флаг апдейта графического
|
||||
* состояния взводимый из игрового потока.</li>
|
||||
* <li>Выведенное значение или структура, которое инициализируется самим
|
||||
* объектом по фиксированному правилу. Например, производное значение от
|
||||
* переменной параметризующей объект. Инициализируемый в конструкторе lookup
|
||||
* table.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author tort32
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.SOURCE)
|
||||
@Target({ ElementType.FIELD })
|
||||
public @interface Transient {
|
||||
|
||||
}
|
||||
547
java/io/eiren/util/collections/FastList.java
Normal file
547
java/io/eiren/util/collections/FastList.java
Normal file
@@ -0,0 +1,547 @@
|
||||
package io.eiren.util.collections;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class FastList<E> extends AbstractList<E>
|
||||
implements RandomAccess, Cloneable, RemoveAtSwapList<E> {
|
||||
|
||||
private static final Object[] emptyArray = new Object[0];
|
||||
|
||||
public static final int MAX_ARRAY_SIZE = 2147483639;
|
||||
|
||||
protected int size = 0;
|
||||
protected Object[] array;
|
||||
|
||||
public FastList(int capacity) {
|
||||
array = capacity == 0 ? emptyArray : new Object[capacity];
|
||||
}
|
||||
|
||||
public FastList() {
|
||||
this(5);
|
||||
}
|
||||
|
||||
public FastList(Collection<E> source) {
|
||||
this(source.size());
|
||||
addAll(source);
|
||||
}
|
||||
|
||||
public FastList(FastList<E> source) {
|
||||
this(source.size);
|
||||
addAllInternal(0, source.array, source.size);
|
||||
}
|
||||
|
||||
public FastList(E[] source) {
|
||||
this(source.length);
|
||||
addAll(source);
|
||||
}
|
||||
|
||||
public FastList(E source) {
|
||||
this();
|
||||
add(source);
|
||||
}
|
||||
|
||||
private FastList(Object[] arr, int size) {
|
||||
this(size);
|
||||
System.arraycopy(arr, 0, array, 0, size);
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
private FastList(boolean f) {
|
||||
}
|
||||
|
||||
public static <E> FastList<E> reuseArray(E[] source) {
|
||||
FastList<E> list = new FastList<>(true);
|
||||
list.array = source;
|
||||
list.size = source.length;
|
||||
return list;
|
||||
}
|
||||
|
||||
private void checkBounds(int index) {
|
||||
if (index < 0 || index >= size)
|
||||
throw new ArrayIndexOutOfBoundsException(
|
||||
new StringBuilder("Index: ")
|
||||
.append(index)
|
||||
.append(", size: ")
|
||||
.append(size)
|
||||
.toString()
|
||||
);
|
||||
}
|
||||
|
||||
public void ensureCapacity(int numToFit) {
|
||||
if (array.length < size + numToFit)
|
||||
grow(numToFit + size);
|
||||
}
|
||||
|
||||
private void grow(int i) {
|
||||
int j = array.length;
|
||||
int k = j + (j >> 1);
|
||||
if (k - i < 0)
|
||||
k = i;
|
||||
if (k - 2147483639 > 0)
|
||||
k = hugeCapacity(i);
|
||||
array = Arrays.copyOf(array, k);
|
||||
}
|
||||
|
||||
private static int hugeCapacity(int i) {
|
||||
if (i < 0)
|
||||
throw new OutOfMemoryError("Huge capacity negative: " + i);
|
||||
else
|
||||
return i <= MAX_ARRAY_SIZE ? MAX_ARRAY_SIZE : 2147483647;
|
||||
}
|
||||
|
||||
public void copyInto(Object[] anArray) {
|
||||
System.arraycopy(array, 0, anArray, 0, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(int index) {
|
||||
checkBounds(index);
|
||||
return (E) array[index];
|
||||
}
|
||||
|
||||
public E unsafeGet(int index) {
|
||||
return (E) array[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object obj) {
|
||||
for (int j = 0; j < size; j++)
|
||||
if (obj == array[j])
|
||||
return j;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object obj) {
|
||||
for (int j = size - 1; j >= 0; j--)
|
||||
if (obj == array[j])
|
||||
return j;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object obj) {
|
||||
return indexOf(obj) >= 0;
|
||||
}
|
||||
|
||||
public void trimToSize() {
|
||||
int i = array.length;
|
||||
if (size < i)
|
||||
array = Arrays.copyOf(array, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return Arrays.copyOf(array, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T aobj[]) {
|
||||
if (aobj.length < size)
|
||||
return (T[]) Arrays.copyOf(array, size, aobj.getClass());
|
||||
System.arraycopy(array, 0, aobj, 0, size);
|
||||
if (aobj.length > size)
|
||||
aobj[size] = null;
|
||||
return aobj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E e) {
|
||||
ensureCapacity(1);
|
||||
array[size++] = e;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E remove(int i) {
|
||||
checkBounds(i);
|
||||
E obj = (E) array[i];
|
||||
removeInternal(i);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object obj) {
|
||||
for (int j = 0; j < size; j++)
|
||||
if (obj == array[j]) {
|
||||
removeInternal(j);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean removeAll(Object[] toRemove) {
|
||||
boolean removed = false;
|
||||
for (int i = toRemove.length - 1; i >= 0; --i) {
|
||||
int index = indexOf(toRemove[i]);
|
||||
if (index != -1) {
|
||||
removeInternal(index);
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
protected void removeInternal(int i) {
|
||||
int j = size - i - 1;
|
||||
if (j > 0)
|
||||
System.arraycopy(array, i + 1, array, i, j);
|
||||
array[--size] = null;
|
||||
}
|
||||
|
||||
public void unsafeRemove(int i) {
|
||||
removeInternal(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
Objects.requireNonNull(c);
|
||||
return batchRemove(c, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
Objects.requireNonNull(c);
|
||||
return batchRemove(c, true);
|
||||
}
|
||||
|
||||
private boolean batchRemove(Collection<?> c, boolean complement) {
|
||||
final Object[] elementData = this.array;
|
||||
int r = 0, w = 0;
|
||||
boolean modified = false;
|
||||
try {
|
||||
for (; r < size; r++)
|
||||
if (c.contains(elementData[r]) == complement)
|
||||
elementData[w++] = elementData[r];
|
||||
} finally {
|
||||
// Preserve behavioral compatibility with AbstractCollection,
|
||||
// even if c.contains() throws.
|
||||
if (r != size) {
|
||||
System.arraycopy(elementData, r, elementData, w, size - r);
|
||||
w += size - r;
|
||||
}
|
||||
if (w != size) {
|
||||
for (int i = w; i < size; i++)
|
||||
elementData[i] = null;
|
||||
size = w;
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
for (int i = 0; i < size; i++)
|
||||
array[i] = null;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
public void fakeClear() {
|
||||
size = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends E> collection) {
|
||||
return addAll(size, collection);
|
||||
}
|
||||
|
||||
public void addAll(E[] arr) {
|
||||
addAllInternal(size, arr, arr.length);
|
||||
}
|
||||
|
||||
public void addAll(E[] arr, int limit) {
|
||||
addAllInternal(size, arr, limit);
|
||||
}
|
||||
|
||||
public void addAll(int index, E[] arr) {
|
||||
addAllInternal(index, arr, arr.length);
|
||||
}
|
||||
|
||||
public void addAll(int index, E[] arr, int limit) {
|
||||
addAllInternal(index, arr, limit);
|
||||
}
|
||||
|
||||
private void addAllInternal(int index, Object[] arr, int limit) {
|
||||
if (limit > arr.length)
|
||||
limit = arr.length;
|
||||
if (limit == 1) {
|
||||
add(index, (E) arr[0]);
|
||||
} else if (limit > 0) {
|
||||
if (index >= size) {
|
||||
ensureCapacity(size - index + limit);
|
||||
System.arraycopy(arr, 0, array, index, limit);
|
||||
size = index + limit;
|
||||
} else {
|
||||
if (array.length < size + limit) {
|
||||
Object[] newArray = new Object[size + limit];
|
||||
System.arraycopy(array, 0, newArray, 0, index);
|
||||
System.arraycopy(arr, 0, newArray, index, limit);
|
||||
System.arraycopy(array, index, newArray, index + limit, size - index);
|
||||
array = newArray;
|
||||
} else {
|
||||
System.arraycopy(array, index, array, index + 1, size - index);
|
||||
System.arraycopy(arr, 0, array, index, limit);
|
||||
}
|
||||
size += limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends E> collection) {
|
||||
if (collection.size() > 0) {
|
||||
if (collection instanceof FastList) {
|
||||
addAllInternal(
|
||||
index,
|
||||
((FastList<? extends E>) collection).array,
|
||||
collection.size()
|
||||
);
|
||||
} else if (collection instanceof RandomAccess) {
|
||||
Object[] arr = collection.toArray(new Object[collection.size()]);
|
||||
addAllInternal(index, arr, arr.length);
|
||||
} else {
|
||||
if (index >= size) {
|
||||
ensureCapacity(size - index + collection.size());
|
||||
Iterator<? extends E> iterator = collection.iterator();
|
||||
int i = index;
|
||||
while (iterator.hasNext())
|
||||
array[i++] = iterator.next();
|
||||
size = index + collection.size();
|
||||
} else {
|
||||
if (array.length < size + collection.size()) {
|
||||
Object[] newArray = new Object[size + collection.size()];
|
||||
System.arraycopy(array, 0, newArray, 0, index);
|
||||
Iterator<? extends E> iterator = collection.iterator();
|
||||
int i = index;
|
||||
while (iterator.hasNext())
|
||||
newArray[i++] = iterator.next();
|
||||
System
|
||||
.arraycopy(
|
||||
array,
|
||||
index,
|
||||
newArray,
|
||||
index + collection.size(),
|
||||
size - index
|
||||
);
|
||||
array = newArray;
|
||||
} else {
|
||||
System.arraycopy(array, index, array, index + 1, size - index);
|
||||
Iterator<? extends E> iterator = collection.iterator();
|
||||
while (iterator.hasNext())
|
||||
array[index++] = iterator.next();
|
||||
}
|
||||
size += collection.size();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, E element) {
|
||||
if (index >= size) {
|
||||
ensureCapacity(size - index + 1);
|
||||
size = index + 1;
|
||||
array[index] = element;
|
||||
} else {
|
||||
if (array.length < size + 1) {
|
||||
Object[] newArray = new Object[size + 1];
|
||||
System.arraycopy(array, 0, newArray, 0, index);
|
||||
newArray[index] = element;
|
||||
System.arraycopy(array, index, newArray, index + 1, size - index);
|
||||
array = newArray;
|
||||
} else {
|
||||
System.arraycopy(array, index, array, index + 1, size - index);
|
||||
array[index] = element;
|
||||
}
|
||||
size++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public E set(int index, E element) {
|
||||
checkBounds(index);
|
||||
E oldValue = (E) array[index];
|
||||
array[index] = element;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FastList<E> clone() {
|
||||
return new FastList<E>(array, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<? super E> action) {
|
||||
Objects.requireNonNull(action);
|
||||
final int expectedModCount = modCount;
|
||||
final E[] elementData = (E[]) this.array;
|
||||
final int size = this.size;
|
||||
for (int i = 0; modCount == expectedModCount && i < size; i++) {
|
||||
action.accept(elementData[i]);
|
||||
}
|
||||
if (modCount != expectedModCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public E removeAtSwap(int i) {
|
||||
checkBounds(i);
|
||||
E obj = (E) array[i];
|
||||
removeAtSwapInternal(i);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAtSwap(Object obj) {
|
||||
for (int j = 0; j < size; j++)
|
||||
if (obj == array[j]) {
|
||||
removeAtSwapInternal(j);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void removeAtSwapInternal(int i) {
|
||||
int j = size - i - 1;
|
||||
if (j > 0)
|
||||
array[i] = array[size - 1];
|
||||
array[--size] = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRange(int i, int toIndex) {
|
||||
checkBounds(i);
|
||||
checkBounds(toIndex);
|
||||
int j = size - toIndex - 1;
|
||||
if (j > 0)
|
||||
System.arraycopy(array, toIndex + 1, array, i, j);
|
||||
size -= (toIndex - i + 1);
|
||||
Arrays.fill(array, i, toIndex, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceAll(UnaryOperator<E> operator) {
|
||||
Objects.requireNonNull(operator);
|
||||
for (int i = 0; i < size; ++i)
|
||||
set(i, operator.apply(get(i)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sort(Comparator<? super E> c) {
|
||||
Arrays.sort((E[]) array, 0, size, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = 1;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
Object o = array[i];
|
||||
hashCode = 31 * hashCode + (o == null ? 0 : o.hashCode());
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<E> spliterator() {
|
||||
return Spliterators.spliterator(array, 0, size, Spliterator.ORDERED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special comodification iterator. <b>Use with caution.</b>
|
||||
* <p>
|
||||
* <i>To get element type correctly assign result to reference type
|
||||
* {@code FastList<T>.SkipFastListIterator}</i>
|
||||
*
|
||||
* @return skip iterator to iterate this list in thread-safe manner
|
||||
*/
|
||||
public SkipFastListIterator skipIterator() {
|
||||
return new SkipFastListIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeIf(Predicate<? super E> filter) {
|
||||
Objects.requireNonNull(filter);
|
||||
// figure out which elements are to be removed
|
||||
// any exception thrown from the filter predicate at this stage
|
||||
// will leave the collection unmodified
|
||||
int removeCount = 0;
|
||||
final BitSet removeSet = new BitSet(size);
|
||||
final int expectedModCount = modCount;
|
||||
final int size = this.size;
|
||||
for (int i = 0; modCount == expectedModCount && i < size; i++) {
|
||||
final E element = (E) array[i];
|
||||
if (filter.test(element)) {
|
||||
removeSet.set(i);
|
||||
removeCount++;
|
||||
}
|
||||
}
|
||||
if (modCount != expectedModCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
|
||||
// shift surviving elements left over the spaces left by removed
|
||||
// elements
|
||||
final boolean anyToRemove = removeCount > 0;
|
||||
if (anyToRemove) {
|
||||
final int newSize = size - removeCount;
|
||||
for (int i = 0, j = 0; (i < size) && (j < newSize); i++, j++) {
|
||||
i = removeSet.nextClearBit(i);
|
||||
array[j] = array[i];
|
||||
}
|
||||
for (int k = newSize; k < size; k++) {
|
||||
array[k] = null; // Let gc do its work
|
||||
}
|
||||
this.size = newSize;
|
||||
if (modCount != expectedModCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
modCount++;
|
||||
}
|
||||
|
||||
return anyToRemove;
|
||||
}
|
||||
|
||||
public class SkipFastListIterator implements ResettableIterator<E>, SkipIterator<E> {
|
||||
|
||||
public int position;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return position < size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
Object[] arr = array;
|
||||
if (arr.length > position) {
|
||||
return (E) arr[position++];
|
||||
}
|
||||
position++; // Increase position so hasNext() never loops infinitely
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
position = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
java/io/eiren/util/collections/RemoveAtSwapFastList.java
Normal file
41
java/io/eiren/util/collections/RemoveAtSwapFastList.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package io.eiren.util.collections;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
/**
|
||||
* FastList that performs Remove-At-Swap on stanard remove() operations.
|
||||
*
|
||||
* <p>
|
||||
* Remove operations breaks ordering of this list
|
||||
*
|
||||
* @author Rena
|
||||
*
|
||||
* @param <E>
|
||||
*/
|
||||
public class RemoveAtSwapFastList<E> extends FastList<E> {
|
||||
|
||||
public RemoveAtSwapFastList(int capacity) {
|
||||
super(capacity);
|
||||
}
|
||||
|
||||
public RemoveAtSwapFastList() {
|
||||
}
|
||||
|
||||
public RemoveAtSwapFastList(Collection<E> source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
public RemoveAtSwapFastList(E[] source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
public RemoveAtSwapFastList(E source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeInternal(int i) {
|
||||
super.removeAtSwapInternal(i);
|
||||
}
|
||||
}
|
||||
11
java/io/eiren/util/collections/RemoveAtSwapList.java
Normal file
11
java/io/eiren/util/collections/RemoveAtSwapList.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package io.eiren.util.collections;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public interface RemoveAtSwapList<E> extends List<E> {
|
||||
|
||||
public E removeAtSwap(int i);
|
||||
|
||||
public boolean removeAtSwap(Object object);
|
||||
}
|
||||
17
java/io/eiren/util/collections/ResettableIterator.java
Normal file
17
java/io/eiren/util/collections/ResettableIterator.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package io.eiren.util.collections;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
|
||||
/**
|
||||
* {@link Iterator} that can be reset and iterated from the start by using
|
||||
* {@link #reset()}
|
||||
*
|
||||
* @author Rena
|
||||
*
|
||||
* @param <E>
|
||||
*/
|
||||
public interface ResettableIterator<E> extends Iterator<E> {
|
||||
|
||||
public void reset();
|
||||
}
|
||||
16
java/io/eiren/util/collections/SkipIterator.java
Normal file
16
java/io/eiren/util/collections/SkipIterator.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package io.eiren.util.collections;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
|
||||
/**
|
||||
* {@link Iterator} that can return null on {@link #next()} or can lie on
|
||||
* {@link #hasNext()}. It is <b>not thread-secure!</b>
|
||||
*
|
||||
* @param <E> the type of elements returned by this iterator
|
||||
*/
|
||||
public interface SkipIterator<E> extends Iterator<E> {
|
||||
|
||||
@Override
|
||||
E next();
|
||||
}
|
||||
137
java/io/eiren/util/logging/DefaultGLog.java
Normal file
137
java/io/eiren/util/logging/DefaultGLog.java
Normal file
@@ -0,0 +1,137 @@
|
||||
package io.eiren.util.logging;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
public class DefaultGLog extends Thread implements IGLog {
|
||||
|
||||
private final Logger logger;
|
||||
|
||||
public static class LogEntry {
|
||||
|
||||
private Level level;
|
||||
private String message;
|
||||
private Throwable t;
|
||||
|
||||
public LogEntry(Level level, String message, Throwable t) {
|
||||
this(level, message);
|
||||
this.t = t;
|
||||
}
|
||||
|
||||
public LogEntry(Level level, String message) {
|
||||
this.level = level;
|
||||
this.message = message;
|
||||
this.t = null;
|
||||
}
|
||||
|
||||
public Level getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public Throwable getException() {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
private final ArrayBlockingQueue<LogEntry> queue = new ArrayBlockingQueue<LogEntry>(50000);
|
||||
private volatile LoggerRecorder recorder;
|
||||
|
||||
@Override
|
||||
public void info(String message) {
|
||||
add(new LogEntry(Level.INFO, message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String message, Throwable t) {
|
||||
add(new LogEntry(Level.INFO, message, t));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void severe(String message) {
|
||||
add(new LogEntry(Level.SEVERE, message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void severe(String message, Throwable t) {
|
||||
add(new LogEntry(Level.SEVERE, message, t));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warning(String message) {
|
||||
add(new LogEntry(Level.WARNING, message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warning(String message, Throwable t) {
|
||||
add(new LogEntry(Level.WARNING, message, t));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message) {
|
||||
add(new LogEntry(Level.INFO, "[DBG] " + message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message, Throwable t) {
|
||||
add(new LogEntry(Level.INFO, "[DBG] " + message, t));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String message) {
|
||||
add(new LogEntry(level, message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String message, Throwable t) {
|
||||
add(new LogEntry(level, message, t));
|
||||
}
|
||||
|
||||
private void add(LogEntry entry) {
|
||||
try {
|
||||
queue.put(entry);
|
||||
} catch (InterruptedException e) {}
|
||||
try {
|
||||
if (recorder != null)
|
||||
recorder.addEntry(entry);
|
||||
} catch (NullPointerException e) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecorder(LoggerRecorder recorder) {
|
||||
this.recorder = recorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoggerRecorder removeRecorder() {
|
||||
LoggerRecorder lr = this.recorder;
|
||||
this.recorder = null;
|
||||
return lr;
|
||||
}
|
||||
|
||||
public DefaultGLog(Logger logger) {
|
||||
super("Logger");
|
||||
this.logger = logger;
|
||||
this.setDaemon(true);
|
||||
this.setPriority(7);
|
||||
this.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
LogEntry log = queue.take();
|
||||
if (log.t != null)
|
||||
logger.log(log.level, log.message, log.t);
|
||||
else
|
||||
logger.log(log.level, log.message);
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
58
java/io/eiren/util/logging/FileLogFormatter.java
Normal file
58
java/io/eiren/util/logging/FileLogFormatter.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package io.eiren.util.logging;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
|
||||
public class FileLogFormatter extends Formatter {
|
||||
|
||||
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public String format(LogRecord record) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(dateFormat.format(record.getMillis()));
|
||||
Level localLevel = record.getLevel();
|
||||
if (localLevel == Level.FINEST)
|
||||
sb.append(" [FINEST] ");
|
||||
else if (localLevel == Level.FINER)
|
||||
sb.append(" [FINER] ");
|
||||
else if (localLevel == Level.FINE)
|
||||
sb.append(" [FINE] ");
|
||||
else if (localLevel == Level.INFO)
|
||||
sb.append(" [INFO] ");
|
||||
else if (localLevel == Level.WARNING)
|
||||
sb.append(" [WARNING] ");
|
||||
else if (localLevel == Level.SEVERE)
|
||||
sb.append(" [SEVERE] ");
|
||||
else
|
||||
sb.append(" [" + localLevel.getLocalizedName() + "] ");
|
||||
|
||||
sb.append(record.getMessage());
|
||||
sb.append('\n');
|
||||
|
||||
Throwable localThrowable = record.getThrown();
|
||||
if (localThrowable != null) {
|
||||
StringWriter localStringWriter = new StringWriter();
|
||||
localThrowable.printStackTrace(new PrintWriter(localStringWriter));
|
||||
sb.append(localStringWriter.toString());
|
||||
}
|
||||
|
||||
String message = sb.toString();
|
||||
Object parameters[] = record.getParameters();
|
||||
if (parameters == null || parameters.length == 0)
|
||||
return message;
|
||||
if (
|
||||
message.indexOf("{0") >= 0
|
||||
|| message.indexOf("{1") >= 0
|
||||
|| message.indexOf("{2") >= 0
|
||||
|| message.indexOf("{3") >= 0
|
||||
)
|
||||
return java.text.MessageFormat.format(message, parameters);
|
||||
return message;
|
||||
}
|
||||
}
|
||||
48
java/io/eiren/util/logging/IGLog.java
Normal file
48
java/io/eiren/util/logging/IGLog.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package io.eiren.util.logging;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
|
||||
public interface IGLog {
|
||||
|
||||
public void info(String message);
|
||||
|
||||
public void severe(String message);
|
||||
|
||||
public void warning(String message);
|
||||
|
||||
public void debug(String message);
|
||||
|
||||
public default void info(String message, Throwable t) {
|
||||
log(Level.INFO, message, t);
|
||||
}
|
||||
|
||||
public default void severe(String message, Throwable t) {
|
||||
log(Level.SEVERE, message, t);
|
||||
}
|
||||
|
||||
public default void warning(String message, Throwable t) {
|
||||
log(Level.WARNING, message, t);
|
||||
}
|
||||
|
||||
public default void debug(String message, Throwable t) {
|
||||
log(Level.INFO, "[DBG] " + message, t);
|
||||
}
|
||||
|
||||
public void log(Level level, String message);
|
||||
|
||||
public void log(Level level, String message, Throwable t);
|
||||
|
||||
public void setRecorder(LoggerRecorder recorder);
|
||||
|
||||
public LoggerRecorder removeRecorder();
|
||||
|
||||
static class GLevel extends Level {
|
||||
|
||||
private static final long serialVersionUID = -539856764608026895L;
|
||||
|
||||
private GLevel(String s, int i) {
|
||||
super(s, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
136
java/io/eiren/util/logging/LogManager.java
Normal file
136
java/io/eiren/util/logging/LogManager.java
Normal file
@@ -0,0 +1,136 @@
|
||||
package io.eiren.util.logging;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.FileHandler;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
public class LogManager {
|
||||
|
||||
private static AtomicBoolean initialized = new AtomicBoolean(false);
|
||||
|
||||
public static Logger global = Logger.getLogger("");
|
||||
public static final IGLog log = new DefaultGLog(global);
|
||||
public static ConsoleHandler handler;
|
||||
|
||||
public static void initialize(File logsDir, File mainLogDir)
|
||||
throws SecurityException, IOException {
|
||||
if (initialized.getAndSet(true))
|
||||
return;
|
||||
FileLogFormatter loc = new FileLogFormatter();
|
||||
if (mainLogDir != null) {
|
||||
if (!mainLogDir.exists())
|
||||
mainLogDir.mkdirs();
|
||||
File lastLogFile = new File(mainLogDir, "log_last.log");
|
||||
if (lastLogFile.exists())
|
||||
lastLogFile.delete();
|
||||
File mainLog = new File(mainLogDir, "log_main.log");
|
||||
FileHandler mHandler = new FileHandler(mainLog.getPath(), true);
|
||||
FileHandler filehandler = new FileHandler(lastLogFile.getPath(), true);
|
||||
mHandler.setFormatter(loc);
|
||||
filehandler.setFormatter(loc);
|
||||
global.addHandler(mHandler);
|
||||
global.addHandler(filehandler);
|
||||
}
|
||||
if (logsDir != null) {
|
||||
if (!logsDir.exists())
|
||||
logsDir.mkdir();
|
||||
if (!logsDir.isDirectory())
|
||||
System.out.println("*** WARNING *** LOG FOLDER IS NOT A DIRECTORY!");
|
||||
File currentLog = new File(
|
||||
logsDir,
|
||||
"log_"
|
||||
+ new SimpleDateFormat("yyyy-MM-dd")
|
||||
.format(Long.valueOf(System.currentTimeMillis()))
|
||||
+ ".log"
|
||||
);
|
||||
FileHandler filehandler2 = new FileHandler(currentLog.getPath(), true);
|
||||
filehandler2.setFormatter(loc);
|
||||
global.addHandler(filehandler2);
|
||||
}
|
||||
}
|
||||
|
||||
public static void replaceMainHandler(ConsoleHandler newHandler) {
|
||||
handler.close();
|
||||
global.removeHandler(handler);
|
||||
handler = newHandler;
|
||||
global.addHandler(newHandler);
|
||||
}
|
||||
|
||||
public static void addHandler(Handler add) {
|
||||
global.addHandler(add);
|
||||
}
|
||||
|
||||
public static void removeHandler(Handler remove) {
|
||||
global.removeHandler(remove);
|
||||
}
|
||||
|
||||
public static void enablePreciseTimestamp() {
|
||||
handler.setFormatter(new PreciseConsoleLogFormatter());
|
||||
}
|
||||
|
||||
public static void info(String message) {
|
||||
log.info(message);
|
||||
}
|
||||
|
||||
public static void severe(String message) {
|
||||
log.severe(message);
|
||||
}
|
||||
|
||||
public static void warning(String message) {
|
||||
log.warning(message);
|
||||
}
|
||||
|
||||
public static void debug(String message) {
|
||||
log.debug(message);
|
||||
}
|
||||
|
||||
public static void info(String message, Throwable t) {
|
||||
log.info(message, t);
|
||||
}
|
||||
|
||||
public static void severe(String message, Throwable t) {
|
||||
log.severe(message, t);
|
||||
}
|
||||
|
||||
public static void warning(String message, Throwable t) {
|
||||
log.warning(message, t);
|
||||
}
|
||||
|
||||
public static void debug(String message, Throwable t) {
|
||||
log.debug(message, t);
|
||||
}
|
||||
|
||||
public static void log(Level level, String message) {
|
||||
log.log(level, message);
|
||||
}
|
||||
|
||||
public static void log(Level level, String message, Throwable t) {
|
||||
log.log(level, message, t);
|
||||
}
|
||||
|
||||
static {
|
||||
boolean hasConsoleHandler = false;
|
||||
for (Handler h : global.getHandlers()) {
|
||||
if (h instanceof ConsoleHandler) {
|
||||
handler = (ConsoleHandler) h;
|
||||
hasConsoleHandler = true;
|
||||
}
|
||||
}
|
||||
if (!hasConsoleHandler) {
|
||||
handler = new ConsoleHandler();
|
||||
global.addHandler(handler);
|
||||
}
|
||||
handler.setFormatter(new ShortConsoleLogFormatter());
|
||||
|
||||
System.setOut(new PrintStream(new LoggerOutputStream(log, Level.INFO), true));
|
||||
System.setErr(new PrintStream(new LoggerOutputStream(log, Level.SEVERE), true));
|
||||
}
|
||||
}
|
||||
52
java/io/eiren/util/logging/LoggerOutputStream.java
Normal file
52
java/io/eiren/util/logging/LoggerOutputStream.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package io.eiren.util.logging;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
|
||||
public class LoggerOutputStream extends ByteArrayOutputStream {
|
||||
|
||||
private static final String separator = System.getProperty("line.separator");
|
||||
|
||||
private final IGLog logger;
|
||||
private final Level level;
|
||||
private final String prefix;
|
||||
private final StringBuilder buffer = new StringBuilder();
|
||||
|
||||
public LoggerOutputStream(IGLog logger, Level level) {
|
||||
this(logger, level, "");
|
||||
}
|
||||
|
||||
public LoggerOutputStream(IGLog logger, Level level, String prefix) {
|
||||
super();
|
||||
this.logger = logger;
|
||||
this.level = level;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
synchronized (this) {
|
||||
super.flush();
|
||||
String record = this.toString();
|
||||
super.reset();
|
||||
if (record.length() > 0) {
|
||||
buffer.append(record);
|
||||
if (record.contains(separator)) {
|
||||
String s = buffer.toString();
|
||||
String[] split = s.split(separator);
|
||||
for (int i = 0; i < split.length; ++i)
|
||||
logger.log(level, prefix + split[i]);
|
||||
buffer.setLength(0);
|
||||
// buffer.append(split[split.length - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
flush();
|
||||
}
|
||||
}
|
||||
23
java/io/eiren/util/logging/LoggerRecorder.java
Normal file
23
java/io/eiren/util/logging/LoggerRecorder.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package io.eiren.util.logging;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.eiren.util.collections.FastList;
|
||||
import io.eiren.util.logging.DefaultGLog.LogEntry;
|
||||
|
||||
|
||||
public class LoggerRecorder {
|
||||
|
||||
private final List<LogEntry> recorded = new FastList<LogEntry>();
|
||||
|
||||
public LoggerRecorder() {
|
||||
}
|
||||
|
||||
public synchronized void addEntry(LogEntry e) {
|
||||
recorded.add(e);
|
||||
}
|
||||
|
||||
public List<LogEntry> getEntries() {
|
||||
return recorded;
|
||||
}
|
||||
}
|
||||
32
java/io/eiren/util/logging/PreciseConsoleLogFormatter.java
Normal file
32
java/io/eiren/util/logging/PreciseConsoleLogFormatter.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package io.eiren.util.logging;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
|
||||
/**
|
||||
* Format message timestamp as time passed from the start with milliseconds.
|
||||
*/
|
||||
public class PreciseConsoleLogFormatter extends ShortConsoleLogFormatter {
|
||||
|
||||
private final long startMills;
|
||||
|
||||
public PreciseConsoleLogFormatter() {
|
||||
startMills = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SimpleDateFormat createDateFormat() {
|
||||
return new SimpleDateFormat("mm:ss.SSS");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildMessage(StringBuilder builder, LogRecord record) {
|
||||
builder.append(date.format(record.getMillis() - startMills));
|
||||
builder.append(" [");
|
||||
builder.append(record.getLevel().getLocalizedName().toUpperCase());
|
||||
builder.append("] ");
|
||||
builder.append(record.getMessage());
|
||||
builder.append('\n');
|
||||
}
|
||||
}
|
||||
58
java/io/eiren/util/logging/ShortConsoleLogFormatter.java
Normal file
58
java/io/eiren/util/logging/ShortConsoleLogFormatter.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package io.eiren.util.logging;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
|
||||
public class ShortConsoleLogFormatter extends Formatter {
|
||||
|
||||
protected final SimpleDateFormat date;
|
||||
|
||||
public ShortConsoleLogFormatter() {
|
||||
this.date = createDateFormat();
|
||||
}
|
||||
|
||||
protected SimpleDateFormat createDateFormat() {
|
||||
return new SimpleDateFormat("HH:mm:ss");
|
||||
}
|
||||
|
||||
protected void buildMessage(StringBuilder builder, LogRecord record) {
|
||||
builder.append(date.format(record.getMillis()));
|
||||
builder.append(" [");
|
||||
builder.append(record.getLevel().getLocalizedName().toUpperCase());
|
||||
builder.append("] ");
|
||||
builder.append(record.getMessage());
|
||||
builder.append('\n');
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(LogRecord record) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
Throwable ex = record.getThrown();
|
||||
|
||||
buildMessage(builder, record);
|
||||
|
||||
if (ex != null) {
|
||||
StringWriter writer = new StringWriter();
|
||||
ex.printStackTrace(new PrintWriter(writer));
|
||||
builder.append(writer);
|
||||
}
|
||||
|
||||
String message = builder.toString();
|
||||
Object parameters[] = record.getParameters();
|
||||
if (parameters == null || parameters.length == 0)
|
||||
return message;
|
||||
if (
|
||||
message.indexOf("{0") >= 0
|
||||
|| message.indexOf("{1") >= 0
|
||||
|| message.indexOf("{2") >= 0
|
||||
|| message.indexOf("{3") >= 0
|
||||
)
|
||||
return java.text.MessageFormat.format(message, parameters);
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
23
java/io/eiren/yaml/YamlException.java
Normal file
23
java/io/eiren/yaml/YamlException.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package io.eiren.yaml;
|
||||
|
||||
/**
|
||||
* Configuration exception.
|
||||
*
|
||||
* @author sk89q
|
||||
*/
|
||||
public class YamlException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = -2442886939908724203L;
|
||||
|
||||
public YamlException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public YamlException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public YamlException(String msg, Throwable t) {
|
||||
super(msg, t);
|
||||
}
|
||||
}
|
||||
149
java/io/eiren/yaml/YamlFile.java
Normal file
149
java/io/eiren/yaml/YamlFile.java
Normal file
@@ -0,0 +1,149 @@
|
||||
package io.eiren.yaml;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.BaseConstructor;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
import org.yaml.snakeyaml.reader.UnicodeReader;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
|
||||
|
||||
/**
|
||||
* YAML configuration loader. To use this class, construct it with path to a
|
||||
* file and call its load() method. For specifying node paths in the various
|
||||
* get*() methods, they support SK's path notation, allowing you to select child
|
||||
* nodes by delimiting node names with periods.
|
||||
*
|
||||
* <p>
|
||||
* For example, given the following configuration file:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* members:
|
||||
* - Hollie
|
||||
* - Jason
|
||||
* - Bobo
|
||||
* - Aya
|
||||
* - Tetsu
|
||||
* worldguard:
|
||||
* fire:
|
||||
* spread: false
|
||||
* blocks: [cloth, rock, glass]
|
||||
* sturmeh:
|
||||
* cool: false
|
||||
* eats:
|
||||
* babies: true
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Calling code could access sturmeh's baby eating state by using
|
||||
* <code>getBoolean("sturmeh.eats.babies", false)</code>. For lists, there are
|
||||
* methods such as <code>getStringList</code> that will return a type safe list.
|
||||
*
|
||||
* <p>
|
||||
* This class is currently incomplete. It is not yet possible to get a node.
|
||||
* </p>
|
||||
*
|
||||
* @author sk89q
|
||||
*/
|
||||
public class YamlFile extends YamlNode {
|
||||
|
||||
private static Representer defaultRepresenter = new Representer();
|
||||
|
||||
private Representer representer = defaultRepresenter;
|
||||
private BaseConstructor constructor = new SafeConstructor();
|
||||
private DumperOptions options = new DumperOptions();
|
||||
|
||||
{
|
||||
options.setIndent(4);
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.AUTO);
|
||||
}
|
||||
|
||||
public YamlFile(Map<String, Object> rootMap) {
|
||||
super(rootMap);
|
||||
}
|
||||
|
||||
public YamlFile() {
|
||||
super(new HashMap<String, Object>());
|
||||
}
|
||||
|
||||
public DumperOptions getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public void setRepresenter(Representer representer) {
|
||||
this.representer = representer;
|
||||
}
|
||||
|
||||
public void setBaseConstructor(BaseConstructor constr) {
|
||||
this.constructor = constr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the configuration file. All errors are thrown away.
|
||||
*
|
||||
* @throws YamlException
|
||||
*/
|
||||
public void load(InputStream input) throws YamlException {
|
||||
try {
|
||||
Yaml yaml = new Yaml(constructor, representer, options);
|
||||
read(yaml.load(new UnicodeReader(input)));
|
||||
} catch (YamlException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new YamlException("Exception while parsing yaml", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the configuration to disk. All errors are clobbered.
|
||||
*
|
||||
* @return true if it was successful
|
||||
* @throws UnsupportedEncodingException
|
||||
*/
|
||||
public void save(OutputStream output) {
|
||||
Yaml yaml = new Yaml(constructor, representer, options);
|
||||
try {
|
||||
yaml.dump(root, new OutputStreamWriter(output, "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void read(Object input) throws YamlException {
|
||||
try {
|
||||
root = (Map<String, Object>) (input == null ? new HashMap<String, Object>() : input);
|
||||
} catch (ClassCastException e) {
|
||||
throw new YamlException(
|
||||
"Root document must be an key-value structure, recieved: " + input
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns an empty ConfigurationNode for using as a default in
|
||||
* methods that select a node from a node list.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static YamlNode getEmptyNode() {
|
||||
return new YamlNode(new HashMap<String, Object>());
|
||||
}
|
||||
|
||||
public static YamlNode getEmptySortedNode() {
|
||||
return new YamlNode(new TreeMap<String, Object>());
|
||||
}
|
||||
|
||||
public static void setDefaultRepresenter(Representer r) {
|
||||
defaultRepresenter = r;
|
||||
}
|
||||
}
|
||||
642
java/io/eiren/yaml/YamlNode.java
Normal file
642
java/io/eiren/yaml/YamlNode.java
Normal file
@@ -0,0 +1,642 @@
|
||||
package io.eiren.yaml;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import io.eiren.util.Util;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a configuration node.
|
||||
*
|
||||
* @author sk89q
|
||||
*/
|
||||
public class YamlNode {
|
||||
|
||||
public Map<String, Object> root;
|
||||
|
||||
public YamlNode(Map<String, Object> root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
public Object getProperty(String path) {
|
||||
return getProperty(path, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a property at a location. This will either return an Object or null,
|
||||
* with null meaning that no configuration value exists at that location.
|
||||
* This could potentially return a default value (not yet implemented) as
|
||||
* defined by a plugin, if this is a plugin-tied configuration.
|
||||
*
|
||||
* @param path path to node (dot notation)
|
||||
* @return object or null
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object getProperty(String path, boolean allowDot) {
|
||||
if (allowDot || !path.contains(".")) {
|
||||
Object val = root.get(path);
|
||||
if (val == null) {
|
||||
return null;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
String[] parts = path.split("\\.");
|
||||
Map<String, Object> node = root;
|
||||
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
Object o = node.get(parts[i]);
|
||||
if (o == null)
|
||||
return null;
|
||||
|
||||
if (i == parts.length - 1)
|
||||
return o;
|
||||
|
||||
try {
|
||||
node = (Map<String, Object>) o;
|
||||
} catch (ClassCastException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setProperty(String path, Object value) {
|
||||
setProperty(path, value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the property at a location. This will override existing configuration
|
||||
* data to have it conform to key/value mappings.
|
||||
*
|
||||
* @param path
|
||||
* @param value
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setProperty(String path, Object value, boolean allowDot) {
|
||||
if (value instanceof YamlNode)
|
||||
value = ((YamlNode) value).root;
|
||||
if (allowDot || !path.contains(".")) {
|
||||
root.put(path, value);
|
||||
return;
|
||||
}
|
||||
|
||||
String[] parts = path.split("\\.");
|
||||
Map<String, Object> node = root;
|
||||
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
Object o = node.get(parts[i]);
|
||||
|
||||
// Found our target!
|
||||
if (i == parts.length - 1) {
|
||||
node.put(parts[i], value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (o == null || !(o instanceof Map)) {
|
||||
// This will override existing configuration data!
|
||||
o = new HashMap<String, Object>();
|
||||
node.put(parts[i], o);
|
||||
}
|
||||
|
||||
node = (Map<String, Object>) o;
|
||||
}
|
||||
}
|
||||
|
||||
public void merge(YamlNode node) {
|
||||
merge(node.root);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void merge(Map<String, Object> map) {
|
||||
Iterator<Entry<String, Object>> iterator = map.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Entry<String, Object> e = iterator.next();
|
||||
if (e.getValue() == null)
|
||||
continue;
|
||||
Object old = root.get(e.getKey());
|
||||
if (old == null) {
|
||||
root.put(e.getKey(), e.getValue());
|
||||
} else if (old instanceof Map) {
|
||||
if (!(e.getValue() instanceof Map)) {
|
||||
root.put(e.getKey(), e.getValue());
|
||||
} else {
|
||||
new YamlNode((Map<String, Object>) old)
|
||||
.merge((Map<String, Object>) e.getValue());
|
||||
}
|
||||
} else if (old instanceof List) {
|
||||
if (!(e.getValue() instanceof List)) {
|
||||
root.put(e.getKey(), e.getValue());
|
||||
} else {
|
||||
((List) old).addAll((List) e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string at a location. This will either return an String or null,
|
||||
* with null meaning that no configuration value exists at that location. If
|
||||
* the object at the particular location is not actually a string, it will
|
||||
* be converted to its string representation.
|
||||
*
|
||||
* @param path path to node (dot notation)
|
||||
* @return string or null
|
||||
*/
|
||||
public String getString(String path) {
|
||||
Object o = getProperty(path);
|
||||
if (o == null)
|
||||
return null;
|
||||
return o.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string at a location. This will either return an String or the
|
||||
* default value. If the object at the particular location is not actually a
|
||||
* string, it will be converted to its string representation.
|
||||
*
|
||||
* @param path path to node (dot notation)
|
||||
* @param def default value
|
||||
* @return string or default
|
||||
*/
|
||||
public String getString(String path, String def) {
|
||||
String o = getString(path);
|
||||
if (o == null)
|
||||
return def;
|
||||
return o;
|
||||
}
|
||||
|
||||
public int getInt(String path, int def) {
|
||||
return getInt(path, def, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an integer at a location. This will either return an integer or the
|
||||
* default value. If the object at the particular location is not actually a
|
||||
* integer, the default value will be returned. However, other number types
|
||||
* will be casted to an integer.
|
||||
*
|
||||
* @param path path to node (dot notation)
|
||||
* @param def default value
|
||||
* @return int or default
|
||||
*/
|
||||
public int getInt(String path, int def, boolean allowDot) {
|
||||
Integer o = castInt(getProperty(path, allowDot));
|
||||
if (o == null)
|
||||
return def;
|
||||
else
|
||||
return o;
|
||||
}
|
||||
|
||||
public long getLong(String path, long def) {
|
||||
Long o = castLong(getProperty(path));
|
||||
if (o == null)
|
||||
return def;
|
||||
else
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a double at a location. This will either return an double or the
|
||||
* default value. If the object at the particular location is not actually a
|
||||
* double, the default value will be returned. However, other number types
|
||||
* will be casted to an double.
|
||||
*
|
||||
* @param path path to node (dot notation)
|
||||
* @param def default value
|
||||
* @return double or default
|
||||
*/
|
||||
public double getDouble(String path, double def) {
|
||||
Double o = castDouble(getProperty(path));
|
||||
if (o == null)
|
||||
return def;
|
||||
else
|
||||
return o;
|
||||
}
|
||||
|
||||
public float getFloat(String path, float def) {
|
||||
return getFloat(path, def, false);
|
||||
}
|
||||
|
||||
public float getFloat(String path, float def, boolean allowDot) {
|
||||
Float o = castFloat(getProperty(path, allowDot));
|
||||
if (o == null)
|
||||
return def;
|
||||
else
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a boolean at a location. This will either return an boolean or the
|
||||
* default value. If the object at the particular location is not actually a
|
||||
* boolean, the default value will be returned.
|
||||
*
|
||||
* @param path path to node (dot notation)
|
||||
* @param def default value
|
||||
* @return boolean or default
|
||||
*/
|
||||
public boolean getBoolean(String path, boolean def) {
|
||||
Boolean o = castBoolean(getProperty(path));
|
||||
if (o == null) {
|
||||
return def;
|
||||
} else {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of keys at a location. If the map at the particular location
|
||||
* does not exist or it is not a map, null will be returned.
|
||||
*
|
||||
* @param path path to node (dot notation)
|
||||
* @return list of keys
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<String> getKeys(String path) {
|
||||
if (path == null)
|
||||
return new ArrayList<String>(root.keySet());
|
||||
Object o = getProperty(path);
|
||||
if (o == null) {
|
||||
return null;
|
||||
} else if (o instanceof Map) {
|
||||
return new ArrayList<String>(((Map<String, Object>) o).keySet());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of objects at a location. If the list is not defined, null
|
||||
* will be returned. The node must be an actual list.
|
||||
*
|
||||
* @param path path to node (dot notation)
|
||||
* @return boolean or default
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Object> getList(String path) {
|
||||
Object o = getProperty(path);
|
||||
if (o == null) {
|
||||
return null;
|
||||
} else if (o instanceof List) {
|
||||
return (List<Object>) o;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of strings. Non-valid entries will not be in the list. There
|
||||
* will be no null slots. If the list is not defined, the default will be
|
||||
* returned. 'null' can be passed for the default and an empty list will be
|
||||
* returned instead. If an item in the list is not a string, it will be
|
||||
* converted to a string. The node must be an actual list and not just a
|
||||
* string.
|
||||
*
|
||||
* @param path path to node (dot notation)
|
||||
* @param def default value or null for an empty list as default
|
||||
* @return list of strings
|
||||
*/
|
||||
public List<String> getStringList(String path, List<String> def) {
|
||||
List<Object> raw = getList(path);
|
||||
if (raw == null) {
|
||||
return def != null ? def : new ArrayList<String>(0);
|
||||
}
|
||||
|
||||
List<String> list = new ArrayList<String>();
|
||||
for (Object o : raw) {
|
||||
if (o == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
list.add(o.toString());
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of integers. Non-valid entries will not be in the list. There
|
||||
* will be no null slots. If the list is not defined, the default will be
|
||||
* returned. 'null' can be passed for the default and an empty list will be
|
||||
* returned instead. The node must be an actual list and not just an
|
||||
* integer.
|
||||
*
|
||||
* @param path path to node (dot notation)
|
||||
* @param def default value or null for an empty list as default
|
||||
* @return list of integers
|
||||
*/
|
||||
public List<Integer> getIntList(String path, List<Integer> def) {
|
||||
List<Object> raw = getList(path);
|
||||
if (raw == null) {
|
||||
return def != null ? def : new ArrayList<Integer>(0);
|
||||
}
|
||||
|
||||
List<Integer> list = new ArrayList<Integer>();
|
||||
for (Object o : raw) {
|
||||
Integer i = castInt(o);
|
||||
if (i != null) {
|
||||
list.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of doubles. Non-valid entries will not be in the list. There
|
||||
* will be no null slots. If the list is not defined, the default will be
|
||||
* returned. 'null' can be passed for the default and an empty list will be
|
||||
* returned instead. The node must be an actual list and cannot be just a
|
||||
* double.
|
||||
*
|
||||
* @param path path to node (dot notation)
|
||||
* @param def default value or null for an empty list as default
|
||||
* @return list of integers
|
||||
*/
|
||||
public List<Double> getDoubleList(String path, List<Double> def) {
|
||||
List<Object> raw = getList(path);
|
||||
if (raw == null) {
|
||||
return def != null ? def : new ArrayList<Double>(0);
|
||||
}
|
||||
|
||||
List<Double> list = new ArrayList<Double>();
|
||||
for (Object o : raw) {
|
||||
Double i = castDouble(o);
|
||||
if (i != null) {
|
||||
list.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of booleans. Non-valid entries will not be in the list. There
|
||||
* will be no null slots. If the list is not defined, the default will be
|
||||
* returned. 'null' can be passed for the default and an empty list will be
|
||||
* returned instead. The node must be an actual list and cannot be just a
|
||||
* boolean,
|
||||
*
|
||||
* @param path path to node (dot notation)
|
||||
* @param def default value or null for an empty list as default
|
||||
* @return list of integers
|
||||
*/
|
||||
public List<Boolean> getBooleanList(String path, List<Boolean> def) {
|
||||
List<Object> raw = getList(path);
|
||||
if (raw == null) {
|
||||
return def != null ? def : new ArrayList<Boolean>(0);
|
||||
}
|
||||
|
||||
List<Boolean> list = new ArrayList<Boolean>();
|
||||
for (Object o : raw) {
|
||||
Boolean tetsu = castBoolean(o);
|
||||
if (tetsu != null) {
|
||||
list.add(tetsu);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of nodes. Non-valid entries will not be in the list. There
|
||||
* will be no null slots. If the list is not defined, the default will be
|
||||
* returned. 'null' can be passed for the default and an empty list will be
|
||||
* returned instead. The node must be an actual node and cannot be just a
|
||||
* boolean,
|
||||
*
|
||||
* @param path path to node (dot notation)
|
||||
* @param def default value or null for an empty list as default
|
||||
* @return list of integers
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<YamlNode> getNodeList(String path, List<YamlNode> def) {
|
||||
List<Object> raw = getList(path);
|
||||
if (raw == null) {
|
||||
return def != null ? def : new ArrayList<YamlNode>(0);
|
||||
}
|
||||
|
||||
List<YamlNode> list = new ArrayList<YamlNode>();
|
||||
for (Object o : raw) {
|
||||
if (o instanceof Map) {
|
||||
list.add(new YamlNode((Map<String, Object>) o));
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public YamlNode getNode(String path) {
|
||||
return getNode(path, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a configuration node at a path. If the node doesn't exist or the path
|
||||
* does not lead to a node, null will be returned. A node has key/value
|
||||
* mappings.
|
||||
*
|
||||
* @param path
|
||||
* @return node or null
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public YamlNode getNode(String path, boolean allowDot) {
|
||||
Object raw = getProperty(path, allowDot);
|
||||
if (raw instanceof Map) {
|
||||
return new YamlNode((Map<String, Object>) raw);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of nodes at a location. If the map at the particular location
|
||||
* does not exist or it is not a map, null will be returned.
|
||||
*
|
||||
* @param path path to node (dot notation)
|
||||
* @return map of nodes
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, YamlNode> getNodes(String path) {
|
||||
Object o = getProperty(path);
|
||||
if (o == null) {
|
||||
return null;
|
||||
} else if (o instanceof Map) {
|
||||
Map<String, YamlNode> nodes = new HashMap<String, YamlNode>();
|
||||
|
||||
for (Map.Entry<String, Object> entry : ((Map<String, Object>) o).entrySet()) {
|
||||
if (entry.getValue() instanceof Map) {
|
||||
nodes.put(entry.getKey(), new YamlNode((Map<String, Object>) entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts a value to an integer. May return null.
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
private static Integer castInt(Object o) {
|
||||
if (o == null) {
|
||||
return null;
|
||||
} else if (o instanceof Byte) {
|
||||
return (int) (Byte) o;
|
||||
} else if (o instanceof Integer) {
|
||||
return (Integer) o;
|
||||
} else if (o instanceof Double) {
|
||||
return (int) (double) (Double) o;
|
||||
} else if (o instanceof Float) {
|
||||
return (int) (float) (Float) o;
|
||||
} else if (o instanceof Long) {
|
||||
return (int) (long) (Long) o;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Long castLong(Object o) {
|
||||
if (o == null) {
|
||||
return null;
|
||||
} else if (o instanceof Byte) {
|
||||
return (long) (Byte) o;
|
||||
} else if (o instanceof Integer) {
|
||||
return (long) (int) (Integer) o;
|
||||
} else if (o instanceof Double) {
|
||||
return (long) (double) (Double) o;
|
||||
} else if (o instanceof Float) {
|
||||
return (long) (float) (Float) o;
|
||||
} else if (o instanceof Long) {
|
||||
return (Long) o;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts a value to a double. May return null.
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
private static Double castDouble(Object o) {
|
||||
if (o == null) {
|
||||
return null;
|
||||
} else if (o instanceof Float) {
|
||||
return (double) (Float) o;
|
||||
} else if (o instanceof Double) {
|
||||
return (Double) o;
|
||||
} else if (o instanceof Byte) {
|
||||
return (double) (Byte) o;
|
||||
} else if (o instanceof Integer) {
|
||||
return (double) (Integer) o;
|
||||
} else if (o instanceof Long) {
|
||||
return (double) (Long) o;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Float castFloat(Object o) {
|
||||
if (o == null) {
|
||||
return null;
|
||||
} else if (o instanceof Float) {
|
||||
return (Float) o;
|
||||
} else if (o instanceof Double) {
|
||||
return ((Double) o).floatValue();
|
||||
} else if (o instanceof Byte) {
|
||||
return (float) (Byte) o;
|
||||
} else if (o instanceof Integer) {
|
||||
return (float) (Integer) o;
|
||||
} else if (o instanceof Long) {
|
||||
return (float) (Long) o;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts a value to a boolean. May return null.
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
private static Boolean castBoolean(Object o) {
|
||||
if (o == null) {
|
||||
return null;
|
||||
} else if (o instanceof Boolean) {
|
||||
return (Boolean) o;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void removeProperty(String path) {
|
||||
removeProperty(path, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the property at a location. This will override existing
|
||||
* configuration data to have it conform to key/value mappings.
|
||||
*
|
||||
* @param path
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void removeProperty(String path, boolean allowDot) {
|
||||
if (allowDot || !path.contains(".")) {
|
||||
root.remove(path);
|
||||
return;
|
||||
}
|
||||
|
||||
String[] parts = path.split("\\.");
|
||||
Map<String, Object> node = root;
|
||||
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
Object o = node.get(parts[i]);
|
||||
|
||||
// Found our target!
|
||||
if (i == parts.length - 1) {
|
||||
node.remove(parts[i]);
|
||||
return;
|
||||
}
|
||||
|
||||
node = (Map<String, Object>) o;
|
||||
if (node == null)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void fullDump(Map<String, Object> target, String prefix) {
|
||||
Iterator<Entry<String, Object>> i = root.entrySet().iterator();
|
||||
while (i.hasNext()) {
|
||||
Entry<String, Object> e = i.next();
|
||||
if (e.getValue() instanceof Map)
|
||||
new YamlNode((Map<String, Object>) e.getValue())
|
||||
.fullDump(target, prefix + e.getKey() + ".");
|
||||
else
|
||||
target.put(prefix + e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> fullDump() {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
fullDump(map, "");
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Util.toString(root);
|
||||
}
|
||||
}
|
||||
880
java/org/json/JSONArray.java
Normal file
880
java/org/json/JSONArray.java
Normal file
@@ -0,0 +1,880 @@
|
||||
package org.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* A JSONArray is an ordered sequence of values. Its external text form is a
|
||||
* string wrapped in square brackets with commas separating the values. The
|
||||
* internal form is an object having <code>get</code> and <code>opt</code>
|
||||
* methods for accessing the values by index, and <code>put</code> methods for
|
||||
* adding or replacing values. The values can be any of these types:
|
||||
* <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
|
||||
* <code>Number</code>, <code>String</code>, or the
|
||||
* <code>JSONObject.NULL object</code>.
|
||||
* <p>
|
||||
* The constructor can convert a JSON text into a Java object. The
|
||||
* <code>toString</code> method converts to JSON text.
|
||||
* <p>
|
||||
* A <code>get</code> method returns a value if one can be found, and throws an
|
||||
* exception if one cannot be found. An <code>opt</code> method returns a
|
||||
* default value instead of throwing an exception, and so is useful for
|
||||
* obtaining optional values.
|
||||
* <p>
|
||||
* The generic <code>get()</code> and <code>opt()</code> methods return an
|
||||
* object which you can cast or query for type. There are also typed
|
||||
* <code>get</code> and <code>opt</code> methods that do type checking and type
|
||||
* coercion for you.
|
||||
* <p>
|
||||
* The texts produced by the <code>toString</code> methods strictly conform to
|
||||
* JSON syntax rules. The constructors are more forgiving in the texts they will
|
||||
* accept:
|
||||
* <ul>
|
||||
* <li>An extra <code>,</code> <small>(comma)</small> may appear just
|
||||
* before the closing bracket.</li>
|
||||
* <li>The <code>null</code> value will be inserted when there is <code>,</code>
|
||||
* <small>(comma)</small> elision.</li>
|
||||
* <li>Strings may be quoted with <code>'</code> <small>(single
|
||||
* quote)</small>.</li>
|
||||
* <li>Strings do not need to be quoted at all if they do not begin with a quote
|
||||
* or single quote, and if they do not contain leading or trailing spaces, and
|
||||
* if they do not contain any of these characters:
|
||||
* <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
|
||||
* if they are not the reserved words <code>true</code>, <code>false</code>, or
|
||||
* <code>null</code>.</li>
|
||||
* <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as
|
||||
* well as by <code>,</code> <small>(comma)</small>.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2012-04-20
|
||||
*/
|
||||
public class JSONArray {
|
||||
|
||||
/**
|
||||
* The arrayList where the JSONArray's properties are kept.
|
||||
*/
|
||||
private final ArrayList<Object> myArrayList;
|
||||
|
||||
/**
|
||||
* Construct an empty JSONArray.
|
||||
*/
|
||||
public JSONArray() {
|
||||
this.myArrayList = new ArrayList<Object>();
|
||||
}
|
||||
|
||||
public JSONArray(int initialLength) {
|
||||
this.myArrayList = new ArrayList<Object>(initialLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from a JSONTokener.
|
||||
*
|
||||
* @param x A JSONTokener
|
||||
* @throws JSONException If there is a syntax error.
|
||||
*/
|
||||
public JSONArray(JSONTokener x) throws JSONException {
|
||||
this();
|
||||
if (x.nextClean() != '[') {
|
||||
throw x.syntaxError("A JSONArray text must start with '['");
|
||||
}
|
||||
if (x.nextClean() != ']') {
|
||||
x.back();
|
||||
for (;;) {
|
||||
if (x.nextClean() == ',') {
|
||||
x.back();
|
||||
this.myArrayList.add(JSONObject.NULL);
|
||||
} else {
|
||||
x.back();
|
||||
this.myArrayList.add(x.nextValue());
|
||||
}
|
||||
switch (x.nextClean()) {
|
||||
case ';':
|
||||
case ',':
|
||||
if (x.nextClean() == ']') {
|
||||
return;
|
||||
}
|
||||
x.back();
|
||||
break;
|
||||
case ']':
|
||||
return;
|
||||
default:
|
||||
throw x.syntaxError("Expected a ',' or ']'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from a source JSON text.
|
||||
*
|
||||
* @param source A string that begins with <code>[</code> <small>(left
|
||||
* bracket)</small> and ends with <code>]</code> <small>(right
|
||||
* bracket)</small>.
|
||||
* @throws JSONException If there is a syntax error.
|
||||
*/
|
||||
public JSONArray(String source) throws JSONException {
|
||||
this(new JSONTokener(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from a Collection.
|
||||
*
|
||||
* @param collection A Collection.
|
||||
*/
|
||||
public JSONArray(Collection<Object> collection) {
|
||||
this.myArrayList = new ArrayList<Object>();
|
||||
if (collection != null) {
|
||||
Iterator<Object> iter = collection.iterator();
|
||||
while (iter.hasNext()) {
|
||||
this.myArrayList.add(JSONObject.wrap(iter.next()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from an array
|
||||
*
|
||||
* @throws JSONException If not an array.
|
||||
*/
|
||||
public JSONArray(Object array) {
|
||||
this();
|
||||
if (array.getClass().isArray()) {
|
||||
int length = Array.getLength(array);
|
||||
for (int i = 0; i < length; i += 1) {
|
||||
this.put(JSONObject.wrap(Array.get(array, i)));
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"JSONArray initial value should be a string or collection or array."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return An object value.
|
||||
* @throws JSONException If there is no value for the index.
|
||||
*/
|
||||
public Object get(int index) throws JSONException {
|
||||
Object object = this.opt(index);
|
||||
if (object == null) {
|
||||
throw new JSONException("JSONArray[" + index + "] not found.");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the boolean value associated with an index. The string values "true"
|
||||
* and "false" are converted to boolean.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The truth.
|
||||
* @throws JSONException If there is no value for the index or if the value
|
||||
* is not convertible to boolean.
|
||||
*/
|
||||
public boolean getBoolean(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if (
|
||||
object.equals(Boolean.FALSE)
|
||||
|| (object instanceof String && ((String) object).equalsIgnoreCase("false"))
|
||||
) {
|
||||
return false;
|
||||
} else if (
|
||||
object.equals(Boolean.TRUE)
|
||||
|| (object instanceof String && ((String) object).equalsIgnoreCase("true"))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] is not a boolean.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the double value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
* @throws JSONException If the key is not found or if the value cannot be
|
||||
* converted to a number.
|
||||
*/
|
||||
public double getDouble(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
try {
|
||||
return object instanceof Number
|
||||
? ((Number) object).doubleValue()
|
||||
: Double.parseDouble((String) object);
|
||||
} catch (Exception e) {
|
||||
throw new JSONException("JSONArray[" + index + "] is not a number.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the int value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
* @throws JSONException If the key is not found or if the value is not a
|
||||
* number.
|
||||
*/
|
||||
public int getInt(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
try {
|
||||
return object instanceof Number
|
||||
? ((Number) object).intValue()
|
||||
: Integer.parseInt((String) object);
|
||||
} catch (Exception e) {
|
||||
throw new JSONException("JSONArray[" + index + "] is not a number.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSONArray associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A JSONArray value.
|
||||
* @throws JSONException If there is no value for the index. or if the value
|
||||
* is not a JSONArray
|
||||
*/
|
||||
public JSONArray getJSONArray(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if (object instanceof JSONArray) {
|
||||
return (JSONArray) object;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] is not a JSONArray.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSONObject associated with an index.
|
||||
*
|
||||
* @param index subscript
|
||||
* @return A JSONObject value.
|
||||
* @throws JSONException If there is no value for the index or if the value
|
||||
* is not a JSONObject
|
||||
*/
|
||||
public JSONObject getJSONObject(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if (object instanceof JSONObject) {
|
||||
return (JSONObject) object;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] is not a JSONObject.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the long value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
* @throws JSONException If the key is not found or if the value cannot be
|
||||
* converted to a number.
|
||||
*/
|
||||
public long getLong(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
try {
|
||||
return object instanceof Number
|
||||
? ((Number) object).longValue()
|
||||
: Long.parseLong((String) object);
|
||||
} catch (Exception e) {
|
||||
throw new JSONException("JSONArray[" + index + "] is not a number.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A string value.
|
||||
* @throws JSONException If there is no string value for the index.
|
||||
*/
|
||||
public String getString(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if (object instanceof String) {
|
||||
return (String) object;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] not a string.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the value is null.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return true if the value at the index is null, or if there is no value.
|
||||
*/
|
||||
public boolean isNull(int index) {
|
||||
return JSONObject.NULL.equals(this.opt(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a string from the contents of this JSONArray. The
|
||||
* <code>separator</code> string is inserted between each element. Warning:
|
||||
* This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @param separator A string that will be inserted between the elements.
|
||||
* @return a string.
|
||||
* @throws JSONException If the array contains an invalid number.
|
||||
*/
|
||||
public String join(String separator) throws JSONException {
|
||||
int len = this.length();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
for (int i = 0; i < len; i += 1) {
|
||||
if (i > 0) {
|
||||
sb.append(separator);
|
||||
}
|
||||
sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of elements in the JSONArray, included nulls.
|
||||
*
|
||||
* @return The length (or size).
|
||||
*/
|
||||
public int length() {
|
||||
return this.myArrayList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional object value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return An object value, or null if there is no object at that index.
|
||||
*/
|
||||
public Object opt(int index) {
|
||||
return (index < 0 || index >= this.length()) ? null : this.myArrayList.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional boolean value associated with an index. It returns false
|
||||
* if there is no value at that index, or if the value is not Boolean.TRUE
|
||||
* or the String "true".
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The truth.
|
||||
*/
|
||||
public boolean optBoolean(int index) {
|
||||
return this.optBoolean(index, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional boolean value associated with an index. It returns the
|
||||
* defaultValue if there is no value at that index or if it is not a Boolean
|
||||
* or the String "true" or "false" (case insensitive).
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue A boolean default.
|
||||
* @return The truth.
|
||||
*/
|
||||
public boolean optBoolean(int index, boolean defaultValue) {
|
||||
try {
|
||||
return this.getBoolean(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional double value associated with an index. NaN is returned
|
||||
* if there is no value for the index, or if the value is not a number and
|
||||
* cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
*/
|
||||
public double optDouble(int index) {
|
||||
return this.optDouble(index, Double.NaN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional double value associated with an index. The defaultValue
|
||||
* is returned if there is no value for the index, or if the value is not a
|
||||
* number and cannot be converted to a number.
|
||||
*
|
||||
* @param index subscript
|
||||
* @param defaultValue The default value.
|
||||
* @return The value.
|
||||
*/
|
||||
public double optDouble(int index, double defaultValue) {
|
||||
try {
|
||||
return this.getDouble(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional int value associated with an index. Zero is returned if
|
||||
* there is no value for the index, or if the value is not a number and
|
||||
* cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
*/
|
||||
public int optInt(int index) {
|
||||
return this.optInt(index, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional int value associated with an index. The defaultValue is
|
||||
* returned if there is no value for the index, or if the value is not a
|
||||
* number and cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue The default value.
|
||||
* @return The value.
|
||||
*/
|
||||
public int optInt(int index, int defaultValue) {
|
||||
try {
|
||||
return this.getInt(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional JSONArray associated with an index.
|
||||
*
|
||||
* @param index subscript
|
||||
* @return A JSONArray value, or null if the index has no value, or if the
|
||||
* value is not a JSONArray.
|
||||
*/
|
||||
public JSONArray optJSONArray(int index) {
|
||||
Object o = this.opt(index);
|
||||
return o instanceof JSONArray ? (JSONArray) o : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional JSONObject associated with an index. Null is returned if
|
||||
* the key is not found, or null if the index has no value, or if the value
|
||||
* is not a JSONObject.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A JSONObject value.
|
||||
*/
|
||||
public JSONObject optJSONObject(int index) {
|
||||
Object o = this.opt(index);
|
||||
return o instanceof JSONObject ? (JSONObject) o : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional long value associated with an index. Zero is returned if
|
||||
* there is no value for the index, or if the value is not a number and
|
||||
* cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
*/
|
||||
public long optLong(int index) {
|
||||
return this.optLong(index, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional long value associated with an index. The defaultValue is
|
||||
* returned if there is no value for the index, or if the value is not a
|
||||
* number and cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue The default value.
|
||||
* @return The value.
|
||||
*/
|
||||
public long optLong(int index, long defaultValue) {
|
||||
try {
|
||||
return this.getLong(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional string value associated with an index. It returns an
|
||||
* empty string if there is no value at that index. If the value is not a
|
||||
* string and is not null, then it is coverted to a string.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A String value.
|
||||
*/
|
||||
public String optString(int index) {
|
||||
return this.optString(index, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional string associated with an index. The defaultValue is
|
||||
* returned if the key is not found.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue The default value.
|
||||
* @return A String value.
|
||||
*/
|
||||
public String optString(int index, String defaultValue) {
|
||||
Object object = this.opt(index);
|
||||
return JSONObject.NULL.equals(object) ? defaultValue : object.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a boolean value. This increases the array's length by one.
|
||||
*
|
||||
* @param value A boolean value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(boolean value) {
|
||||
this.put(value ? Boolean.TRUE : Boolean.FALSE);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a JSONArray which
|
||||
* is produced from a Collection.
|
||||
*
|
||||
* @param value A Collection value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(Collection<Object> value) {
|
||||
this.put(new JSONArray(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a double value. This increases the array's length by one.
|
||||
*
|
||||
* @param value A double value.
|
||||
* @throws JSONException if the value is not finite.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(double value) throws JSONException {
|
||||
Double d = new Double(value);
|
||||
JSONObject.testValidity(d);
|
||||
this.put(d);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an int value. This increases the array's length by one.
|
||||
*
|
||||
* @param value An int value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(int value) {
|
||||
this.put(new Integer(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an long value. This increases the array's length by one.
|
||||
*
|
||||
* @param value A long value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(long value) {
|
||||
this.put(new Long(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a JSONObject which
|
||||
* is produced from a Map.
|
||||
*
|
||||
* @param value A Map value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(Map<Object, Object> value) {
|
||||
this.put(new JSONObject(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an object value. This increases the array's length by one.
|
||||
*
|
||||
* @param value An object value. The value should be a Boolean, Double,
|
||||
* Integer, JSONArray, JSONObject, Long, or String, or the JSONObject.NULL
|
||||
* object.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(Object value) {
|
||||
this.myArrayList.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace a boolean value in the JSONArray. If the index is greater
|
||||
* than the length of the JSONArray, then null elements will be added as
|
||||
* necessary to pad it out.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value A boolean value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative.
|
||||
*/
|
||||
public JSONArray put(int index, boolean value) throws JSONException {
|
||||
this.put(index, value ? Boolean.TRUE : Boolean.FALSE);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a JSONArray which
|
||||
* is produced from a Collection.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value A Collection value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the value is not
|
||||
* finite.
|
||||
*/
|
||||
public JSONArray put(int index, Collection<Object> value) throws JSONException {
|
||||
this.put(index, new JSONArray(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace a double value. If the index is greater than the length of
|
||||
* the JSONArray, then null elements will be added as necessary to pad it
|
||||
* out.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value A double value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the value is not
|
||||
* finite.
|
||||
*/
|
||||
public JSONArray put(int index, double value) throws JSONException {
|
||||
this.put(index, new Double(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace an int value. If the index is greater than the length of
|
||||
* the JSONArray, then null elements will be added as necessary to pad it
|
||||
* out.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value An int value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative.
|
||||
*/
|
||||
public JSONArray put(int index, int value) throws JSONException {
|
||||
this.put(index, new Integer(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace a long value. If the index is greater than the length of
|
||||
* the JSONArray, then null elements will be added as necessary to pad it
|
||||
* out.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value A long value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative.
|
||||
*/
|
||||
public JSONArray put(int index, long value) throws JSONException {
|
||||
this.put(index, new Long(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a JSONObject that
|
||||
* is produced from a Map.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value The Map value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the the value is an
|
||||
* invalid number.
|
||||
*/
|
||||
public JSONArray put(int index, Map<Object, Object> value) throws JSONException {
|
||||
this.put(index, new JSONObject(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace an object value in the JSONArray. If the index is greater
|
||||
* than the length of the JSONArray, then null elements will be added as
|
||||
* necessary to pad it out.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value The value to put into the array. The value should be a
|
||||
* Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
|
||||
* JSONObject.NULL object.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the the value is an
|
||||
* invalid number.
|
||||
*/
|
||||
public JSONArray put(int index, Object value) throws JSONException {
|
||||
JSONObject.testValidity(value);
|
||||
if (index < 0) {
|
||||
throw new JSONException("JSONArray[" + index + "] not found.");
|
||||
}
|
||||
if (index < this.length()) {
|
||||
this.myArrayList.set(index, value);
|
||||
} else {
|
||||
while (index != this.length()) {
|
||||
this.put(JSONObject.NULL);
|
||||
}
|
||||
this.put(value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an index and close the hole.
|
||||
*
|
||||
* @param index The index of the element to be removed.
|
||||
* @return The value that was associated with the index, or null if there
|
||||
* was no value.
|
||||
*/
|
||||
public Object remove(int index) {
|
||||
Object o = this.opt(index);
|
||||
this.myArrayList.remove(index);
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONObject by combining a JSONArray of names with the values of
|
||||
* this JSONArray.
|
||||
*
|
||||
* @param names A JSONArray containing a list of key strings. These will be
|
||||
* paired with the values.
|
||||
* @return A JSONObject, or null if there are no names or if this JSONArray
|
||||
* has no values.
|
||||
* @throws JSONException If any of the names are null.
|
||||
*/
|
||||
public JSONObject toJSONObject(JSONArray names) throws JSONException {
|
||||
if (names == null || names.length() == 0 || this.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
JSONObject jo = new JSONObject();
|
||||
for (int i = 0; i < names.length(); i += 1) {
|
||||
jo.put(names.getString(i), this.opt(i));
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a JSON text of this JSONArray. For compactness, no unnecessary
|
||||
* whitespace is added. If it is not possible to produce a syntactically
|
||||
* correct JSON text then null will be returned instead. This could occur if
|
||||
* the array contains an invalid number.
|
||||
* <p>
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @return a printable, displayable, transmittable representation of the
|
||||
* array.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return '[' + this.join(",") + ']';
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a prettyprinted JSON text of this JSONArray. Warning: This method
|
||||
* assumes that the data structure is acyclical.
|
||||
*
|
||||
* @param indentFactor The number of spaces to add to each level of
|
||||
* indentation.
|
||||
* @return a printable, displayable, transmittable representation of the
|
||||
* object, beginning with <code>[</code> <small>(left bracket)</small>
|
||||
* and ending with <code>]</code> <small>(right bracket)</small>.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public String toString(int indentFactor) throws JSONException {
|
||||
StringWriter sw = new StringWriter();
|
||||
synchronized (sw.getBuffer()) {
|
||||
return this.write(sw, indentFactor, 0).toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the contents of the JSONArray as JSON text to a writer. For
|
||||
* compactness, no whitespace is added.
|
||||
* <p>
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @return The writer.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public Writer write(Writer writer) throws JSONException {
|
||||
return this.write(writer, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the contents of the JSONArray as JSON text to a writer. For
|
||||
* compactness, no whitespace is added.
|
||||
* <p>
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @param indentFactor The number of spaces to add to each level of
|
||||
* indentation.
|
||||
* @param indent The indention of the top level.
|
||||
* @return The writer.
|
||||
* @throws JSONException
|
||||
*/
|
||||
Writer write(Writer writer, int indentFactor, int indent) throws JSONException {
|
||||
try {
|
||||
boolean commanate = false;
|
||||
int length = this.length();
|
||||
writer.write('[');
|
||||
|
||||
if (length == 1) {
|
||||
JSONObject.writeValue(writer, this.myArrayList.get(0), indentFactor, indent);
|
||||
} else if (length != 0) {
|
||||
final int newindent = indent + indentFactor;
|
||||
|
||||
for (int i = 0; i < length; i += 1) {
|
||||
if (commanate) {
|
||||
writer.write(',');
|
||||
}
|
||||
if (indentFactor > 0) {
|
||||
writer.write('\n');
|
||||
}
|
||||
JSONObject.indent(writer, newindent);
|
||||
JSONObject.writeValue(writer, this.myArrayList.get(i), indentFactor, newindent);
|
||||
commanate = true;
|
||||
}
|
||||
if (indentFactor > 0) {
|
||||
writer.write('\n');
|
||||
}
|
||||
JSONObject.indent(writer, indent);
|
||||
}
|
||||
writer.write(']');
|
||||
return writer;
|
||||
} catch (IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
java/org/json/JSONException.java
Normal file
26
java/org/json/JSONException.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package org.json;
|
||||
|
||||
/**
|
||||
* The JSONException is thrown by the JSON.org classes when things are amiss.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2010-12-24
|
||||
*/
|
||||
public class JSONException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
|
||||
/**
|
||||
* Constructs a JSONException with an explanatory message.
|
||||
*
|
||||
* @param message Detail about the reason for the exception.
|
||||
*/
|
||||
public JSONException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JSONException(Throwable cause) {
|
||||
super(cause.getMessage());
|
||||
initCause(cause);
|
||||
}
|
||||
}
|
||||
1590
java/org/json/JSONObject.java
Normal file
1590
java/org/json/JSONObject.java
Normal file
File diff suppressed because it is too large
Load Diff
19
java/org/json/JSONString.java
Normal file
19
java/org/json/JSONString.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package org.json;
|
||||
|
||||
/**
|
||||
* The <code>JSONString</code> interface allows a <code>toJSONString()</code>
|
||||
* method so that a class can change the behavior of
|
||||
* <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>, and
|
||||
* <code>JSONWriter.value(</code>Object<code>)</code>. The
|
||||
* <code>toJSONString</code> method will be used instead of the default behavior
|
||||
* of using the Object's <code>toString()</code> method and quoting the result.
|
||||
*/
|
||||
public interface JSONString {
|
||||
/**
|
||||
* The <code>toJSONString</code> method allows a class to produce its own
|
||||
* JSON serialization.
|
||||
*
|
||||
* @return A strictly syntactically correct JSON text.
|
||||
*/
|
||||
public String toJSONString();
|
||||
}
|
||||
434
java/org/json/JSONTokener.java
Normal file
434
java/org/json/JSONTokener.java
Normal file
@@ -0,0 +1,434 @@
|
||||
package org.json;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* A JSONTokener takes a source string and extracts characters and tokens from
|
||||
* it. It is used by the JSONObject and JSONArray constructors to parse JSON
|
||||
* source strings.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2012-02-16
|
||||
*/
|
||||
public class JSONTokener {
|
||||
|
||||
private long character;
|
||||
private boolean eof;
|
||||
private long index;
|
||||
private long line;
|
||||
private char previous;
|
||||
private Reader reader;
|
||||
private boolean usePrevious;
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from a Reader.
|
||||
*
|
||||
* @param reader A reader.
|
||||
*/
|
||||
public JSONTokener(Reader reader) {
|
||||
this.reader = reader.markSupported() ? reader : new BufferedReader(reader);
|
||||
this.eof = false;
|
||||
this.usePrevious = false;
|
||||
this.previous = 0;
|
||||
this.index = 0;
|
||||
this.character = 1;
|
||||
this.line = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from an InputStream.
|
||||
*/
|
||||
public JSONTokener(InputStream inputStream) throws JSONException {
|
||||
this(new InputStreamReader(inputStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from a string.
|
||||
*
|
||||
* @param s A source string.
|
||||
*/
|
||||
public JSONTokener(String s) {
|
||||
this(new StringReader(s));
|
||||
}
|
||||
|
||||
/**
|
||||
* Back up one character. This provides a sort of lookahead capability, so
|
||||
* that you can test for a digit or letter before attempting to parse the
|
||||
* next number or identifier.
|
||||
*/
|
||||
public void back() throws JSONException {
|
||||
if (this.usePrevious || this.index <= 0) {
|
||||
throw new JSONException("Stepping back two steps is not supported");
|
||||
}
|
||||
this.index -= 1;
|
||||
this.character -= 1;
|
||||
this.usePrevious = true;
|
||||
this.eof = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hex value of a character (base16).
|
||||
*
|
||||
* @param c A character between '0' and '9' or between 'A' and 'F' or
|
||||
* between 'a' and 'f'.
|
||||
* @return An int between 0 and 15, or -1 if c was not a hex digit.
|
||||
*/
|
||||
public static int dehexchar(char c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
return c - ('A' - 10);
|
||||
}
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
return c - ('a' - 10);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean end() {
|
||||
return this.eof && !this.usePrevious;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the source string still contains characters that next() can
|
||||
* consume.
|
||||
*
|
||||
* @return true if not yet at the end of the source.
|
||||
*/
|
||||
public boolean more() throws JSONException {
|
||||
this.next();
|
||||
if (this.end()) {
|
||||
return false;
|
||||
}
|
||||
this.back();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next character in the source string.
|
||||
*
|
||||
* @return The next character, or 0 if past the end of the source string.
|
||||
*/
|
||||
public char next() throws JSONException {
|
||||
int c;
|
||||
if (this.usePrevious) {
|
||||
this.usePrevious = false;
|
||||
c = this.previous;
|
||||
} else {
|
||||
try {
|
||||
c = this.reader.read();
|
||||
} catch (IOException exception) {
|
||||
throw new JSONException(exception);
|
||||
}
|
||||
|
||||
if (c <= 0) { // End of stream
|
||||
this.eof = true;
|
||||
c = 0;
|
||||
}
|
||||
}
|
||||
this.index += 1;
|
||||
if (this.previous == '\r') {
|
||||
this.line += 1;
|
||||
this.character = c == '\n' ? 0 : 1;
|
||||
} else if (c == '\n') {
|
||||
this.line += 1;
|
||||
this.character = 0;
|
||||
} else {
|
||||
this.character += 1;
|
||||
}
|
||||
this.previous = (char) c;
|
||||
return this.previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume the next character, and check that it matches a specified
|
||||
* character.
|
||||
*
|
||||
* @param c The character to match.
|
||||
* @return The character.
|
||||
* @throws JSONException if the character does not match.
|
||||
*/
|
||||
public char next(char c) throws JSONException {
|
||||
char n = this.next();
|
||||
if (n != c) {
|
||||
throw this.syntaxError("Expected '" + c + "' and instead saw '" + n + "'");
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next n characters.
|
||||
*
|
||||
* @param n The number of characters to take.
|
||||
* @return A string of n characters.
|
||||
* @throws JSONException Substring bounds error if there are not n
|
||||
* characters remaining in the source string.
|
||||
*/
|
||||
public String next(int n) throws JSONException {
|
||||
if (n == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
char[] chars = new char[n];
|
||||
int pos = 0;
|
||||
|
||||
while (pos < n) {
|
||||
chars[pos] = this.next();
|
||||
if (this.end()) {
|
||||
throw this.syntaxError("Substring bounds error");
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next char in the string, skipping whitespace.
|
||||
*
|
||||
* @throws JSONException
|
||||
* @return A character, or 0 if there are no more characters.
|
||||
*/
|
||||
public char nextClean() throws JSONException {
|
||||
for (;;) {
|
||||
char c = this.next();
|
||||
if (c == 0 || c > ' ') {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the characters up to the next close quote character. Backslash
|
||||
* processing is done. The formal JSON format does not allow strings in
|
||||
* single quotes, but an implementation is allowed to accept them.
|
||||
*
|
||||
* @param quote The quoting character, either <code>"</code>
|
||||
* <small>(double quote)</small> or <code>'</code>
|
||||
* <small>(single quote)</small>.
|
||||
* @return A String.
|
||||
* @throws JSONException Unterminated string.
|
||||
*/
|
||||
public String nextString(char quote) throws JSONException {
|
||||
char c;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (;;) {
|
||||
c = this.next();
|
||||
switch (c) {
|
||||
case 0:
|
||||
case '\n':
|
||||
case '\r':
|
||||
throw this.syntaxError("Unterminated string");
|
||||
case '\\':
|
||||
c = this.next();
|
||||
switch (c) {
|
||||
case 'b':
|
||||
sb.append('\b');
|
||||
break;
|
||||
case 't':
|
||||
sb.append('\t');
|
||||
break;
|
||||
case 'n':
|
||||
sb.append('\n');
|
||||
break;
|
||||
case 'f':
|
||||
sb.append('\f');
|
||||
break;
|
||||
case 'r':
|
||||
sb.append('\r');
|
||||
break;
|
||||
case 'u':
|
||||
sb.append((char) Integer.parseInt(this.next(4), 16));
|
||||
break;
|
||||
case '"':
|
||||
case '\'':
|
||||
case '\\':
|
||||
case '/':
|
||||
sb.append(c);
|
||||
break;
|
||||
default:
|
||||
throw this.syntaxError("Illegal escape.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (c == quote) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text up but not including the specified character or the end of
|
||||
* line, whichever comes first.
|
||||
*
|
||||
* @param delimiter A delimiter character.
|
||||
* @return A string.
|
||||
*/
|
||||
public String nextTo(char delimiter) throws JSONException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (;;) {
|
||||
char c = this.next();
|
||||
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
this.back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text up but not including one of the specified delimiter
|
||||
* characters or the end of line, whichever comes first.
|
||||
*
|
||||
* @param delimiters A set of delimiter characters.
|
||||
* @return A string, trimmed.
|
||||
*/
|
||||
public String nextTo(String delimiters) throws JSONException {
|
||||
char c;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (;;) {
|
||||
c = this.next();
|
||||
if (delimiters.indexOf(c) >= 0 || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
this.back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next value. The value can be a Boolean, Double, Integer,
|
||||
* JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
|
||||
*
|
||||
* @throws JSONException If syntax error.
|
||||
*
|
||||
* @return An object.
|
||||
*/
|
||||
public Object nextValue() throws JSONException {
|
||||
char c = this.nextClean();
|
||||
String string;
|
||||
|
||||
switch (c) {
|
||||
case '"':
|
||||
case '\'':
|
||||
return this.nextString(c);
|
||||
case '{':
|
||||
this.back();
|
||||
return new JSONObject(this);
|
||||
case '[':
|
||||
this.back();
|
||||
return new JSONArray(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle unquoted text. This could be the values true, false, or null,
|
||||
* or it can be a number. An implementation (such as this one) is
|
||||
* allowed to also accept non-standard forms.
|
||||
*
|
||||
* Accumulate characters until we reach the end of the text or a
|
||||
* formatting character.
|
||||
*/
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
|
||||
sb.append(c);
|
||||
c = this.next();
|
||||
}
|
||||
this.back();
|
||||
|
||||
string = sb.toString().trim();
|
||||
if ("".equals(string)) {
|
||||
throw this.syntaxError("Missing value");
|
||||
}
|
||||
return JSONObject.stringToValue(string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip characters until the next character is the requested character. If
|
||||
* the requested character is not found, no characters are skipped.
|
||||
*
|
||||
* @param to A character to skip to.
|
||||
* @return The requested character, or zero if the requested character is
|
||||
* not found.
|
||||
*/
|
||||
public char skipTo(char to) throws JSONException {
|
||||
char c;
|
||||
try {
|
||||
long startIndex = this.index;
|
||||
long startCharacter = this.character;
|
||||
long startLine = this.line;
|
||||
this.reader.mark(1000000);
|
||||
do {
|
||||
c = this.next();
|
||||
if (c == 0) {
|
||||
this.reader.reset();
|
||||
this.index = startIndex;
|
||||
this.character = startCharacter;
|
||||
this.line = startLine;
|
||||
return c;
|
||||
}
|
||||
} while (c != to);
|
||||
} catch (IOException exc) {
|
||||
throw new JSONException(exc);
|
||||
}
|
||||
|
||||
this.back();
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a JSONException to signal a syntax error.
|
||||
*
|
||||
* @param message The error message.
|
||||
* @return A JSONException object, suitable for throwing
|
||||
*/
|
||||
public JSONException syntaxError(String message) {
|
||||
return new JSONException(message + this.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a printable string of this JSONTokener.
|
||||
*
|
||||
* @return " at {index} [character {character} line {line}]"
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return " at " + this.index + " [character " + this.character + " line " + this.line + "]";
|
||||
}
|
||||
}
|
||||
57
java/org/json/JSONUtil.java
Normal file
57
java/org/json/JSONUtil.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package org.json;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
||||
public class JSONUtil {
|
||||
|
||||
public static JSONObject toJSON(JSONEntry... entries) {
|
||||
return o(entries);
|
||||
}
|
||||
|
||||
public static JSONObject o(JSONEntry... entries) {
|
||||
JSONObject object = new JSONObject();
|
||||
for (int i = 0; i < entries.length; ++i) {
|
||||
JSONEntry e = entries[i];
|
||||
object.put(e.getKey(), e.getValue());
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
public static JSONArray a(Object... values) {
|
||||
return new JSONArray(values);
|
||||
}
|
||||
|
||||
public static JSONEntry e(String k, Object v) {
|
||||
return new JSONEntry(k, v);
|
||||
}
|
||||
|
||||
public static class JSONEntry implements Entry<String, Object> {
|
||||
|
||||
private String key;
|
||||
private Object value;
|
||||
|
||||
public JSONEntry(String key, Object value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(Object value) {
|
||||
Object oldValue = this.value;
|
||||
this.value = value;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Submodule slime-java-commons deleted from 23a977aefe
1072
src/main/java/com/jme3/math/FastMath.java
Normal file
1072
src/main/java/com/jme3/math/FastMath.java
Normal file
File diff suppressed because it is too large
Load Diff
1360
src/main/java/com/jme3/math/Matrix3f.java
Normal file
1360
src/main/java/com/jme3/math/Matrix3f.java
Normal file
File diff suppressed because it is too large
Load Diff
2275
src/main/java/com/jme3/math/Matrix4f.java
Normal file
2275
src/main/java/com/jme3/math/Matrix4f.java
Normal file
File diff suppressed because it is too large
Load Diff
1712
src/main/java/com/jme3/math/Quaternion.java
Normal file
1712
src/main/java/com/jme3/math/Quaternion.java
Normal file
File diff suppressed because it is too large
Load Diff
147
src/main/java/com/jme3/math/TempVars.java
Normal file
147
src/main/java/com/jme3/math/TempVars.java
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.math;
|
||||
|
||||
/**
|
||||
* Temporary variables assigned to each thread. Engine classes may access these
|
||||
* temp variables with TempVars.get(), all retrieved TempVars instances must be
|
||||
* returned via TempVars.release(). This returns an available instance of the
|
||||
* TempVar class ensuring this particular instance is never used elsewhere in
|
||||
* the mean time.
|
||||
*/
|
||||
public class TempVars {
|
||||
|
||||
/**
|
||||
* Allow X instances of TempVars in a single thread.
|
||||
*/
|
||||
private static final int STACK_SIZE = 5;
|
||||
|
||||
/**
|
||||
* <code>TempVarsStack</code> contains a stack of TempVars. Every time
|
||||
* TempVars.get() is called, a new entry is added to the stack, and the
|
||||
* index incremented. When TempVars.release() is called, the entry is
|
||||
* checked against the current instance and then the index is decremented.
|
||||
*/
|
||||
private static class TempVarsStack {
|
||||
|
||||
int index = 0;
|
||||
TempVars[] tempVars = new TempVars[STACK_SIZE];
|
||||
}
|
||||
|
||||
/**
|
||||
* ThreadLocal to store a TempVarsStack for each thread. This ensures each
|
||||
* thread has a single TempVarsStack that is used only in method calls in
|
||||
* that thread.
|
||||
*/
|
||||
private static final ThreadLocal<TempVarsStack> varsLocal = new ThreadLocal<TempVarsStack>() {
|
||||
|
||||
@Override
|
||||
public TempVarsStack initialValue() {
|
||||
return new TempVarsStack();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* This instance of TempVars has been retrieved but not released yet.
|
||||
*/
|
||||
private boolean isUsed = false;
|
||||
|
||||
private TempVars() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire an instance of the TempVar class. You have to release the
|
||||
* instance after use by calling the release() method. If more than
|
||||
* STACK_SIZE (currently 5) instances are requested in a single thread then
|
||||
* an ArrayIndexOutOfBoundsException will be thrown.
|
||||
*
|
||||
* @return A TempVar instance
|
||||
*/
|
||||
public static TempVars get() {
|
||||
TempVarsStack stack = varsLocal.get();
|
||||
|
||||
TempVars instance = stack.tempVars[stack.index];
|
||||
|
||||
if (instance == null) {
|
||||
// Create new
|
||||
instance = new TempVars();
|
||||
|
||||
// Put it in there
|
||||
stack.tempVars[stack.index] = instance;
|
||||
}
|
||||
|
||||
stack.index++;
|
||||
|
||||
instance.isUsed = true;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases this instance of TempVars. Once released, the contents of the
|
||||
* TempVars are undefined. The TempVars must be released in the opposite
|
||||
* order that they are retrieved, e.g. Acquiring vars1, then acquiring
|
||||
* vars2, vars2 MUST be released first otherwise an exception will be
|
||||
* thrown.
|
||||
*/
|
||||
public void release() {
|
||||
if (!isUsed) {
|
||||
throw new IllegalStateException("This instance of TempVars was already released!");
|
||||
}
|
||||
|
||||
isUsed = false;
|
||||
|
||||
TempVarsStack stack = varsLocal.get();
|
||||
|
||||
// Return it to the stack
|
||||
stack.index--;
|
||||
|
||||
// Check if it is actually there
|
||||
if (stack.tempVars[stack.index] != this) {
|
||||
throw new IllegalStateException(
|
||||
"An instance of TempVars has not been released in a called method!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* General vectors.
|
||||
*/
|
||||
public final Vector3f vect1 = new Vector3f();
|
||||
public final Vector3f vect2 = new Vector3f();
|
||||
public final Vector3f vect3 = new Vector3f();
|
||||
/**
|
||||
* General matrices.
|
||||
*/
|
||||
public final Matrix4f tempMat4 = new Matrix4f();
|
||||
|
||||
public final float[] matrixWrite = new float[16];
|
||||
}
|
||||
399
src/main/java/com/jme3/math/Transform.java
Normal file
399
src/main/java/com/jme3/math/Transform.java
Normal file
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.math;
|
||||
|
||||
/**
|
||||
* Started Date: Jul 16, 2004<br>
|
||||
* <br>
|
||||
* Represents a translation, rotation and scale in one object.
|
||||
*
|
||||
* @author Jack Lindamood
|
||||
* @author Joshua Slack
|
||||
*/
|
||||
public final class Transform implements Cloneable, java.io.Serializable {
|
||||
|
||||
static final long serialVersionUID = 1;
|
||||
|
||||
public static final Transform IDENTITY = new Transform();
|
||||
|
||||
private Quaternion rot = new Quaternion();
|
||||
private Vector3f translation = new Vector3f();
|
||||
private Vector3f scale = new Vector3f(1, 1, 1);
|
||||
|
||||
public Transform(Vector3f translation, Quaternion rot) {
|
||||
this.translation.set(translation);
|
||||
this.rot.set(rot);
|
||||
}
|
||||
|
||||
public Transform(Vector3f translation, Quaternion rot, Vector3f scale) {
|
||||
this(translation, rot);
|
||||
this.scale.set(scale);
|
||||
}
|
||||
|
||||
public Transform(Vector3f translation) {
|
||||
this(translation, Quaternion.IDENTITY);
|
||||
}
|
||||
|
||||
public Transform(Quaternion rot) {
|
||||
this(Vector3f.ZERO, rot);
|
||||
}
|
||||
|
||||
public Transform() {
|
||||
this(Vector3f.ZERO, Quaternion.IDENTITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this rotation to the given Quaternion value.
|
||||
*
|
||||
* @param rot The new rotation for this matrix.
|
||||
* @return this
|
||||
*/
|
||||
public Transform setRotation(Quaternion rot) {
|
||||
this.rot.set(rot);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this translation to the given value.
|
||||
*
|
||||
* @param trans The new translation for this matrix.
|
||||
* @return this
|
||||
*/
|
||||
public Transform setTranslation(Vector3f trans) {
|
||||
this.translation.set(trans);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the translation vector in this matrix.
|
||||
*
|
||||
* @return translation vector.
|
||||
*/
|
||||
public Vector3f getTranslation() {
|
||||
return translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this scale to the given value.
|
||||
*
|
||||
* @param scale The new scale for this matrix.
|
||||
* @return this
|
||||
*/
|
||||
public Transform setScale(Vector3f scale) {
|
||||
this.scale.set(scale);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this scale to the given value.
|
||||
*
|
||||
* @param scale The new scale for this matrix.
|
||||
* @return this
|
||||
*/
|
||||
public Transform setScale(float scale) {
|
||||
this.scale.set(scale, scale, scale);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the scale vector in this matrix.
|
||||
*
|
||||
* @return scale vector.
|
||||
*/
|
||||
public Vector3f getScale() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores this translation value into the given vector3f. If trans is null,
|
||||
* a new vector3f is created to hold the value. The value, once stored, is
|
||||
* returned.
|
||||
*
|
||||
* @param trans The store location for this matrix's translation.
|
||||
* @return The value of this matrix's translation.
|
||||
*/
|
||||
public Vector3f getTranslation(Vector3f trans) {
|
||||
if (trans == null)
|
||||
trans = new Vector3f();
|
||||
trans.set(this.translation);
|
||||
return trans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores this rotation value into the given Quaternion. If quat is null, a
|
||||
* new Quaternion is created to hold the value. The value, once stored, is
|
||||
* returned.
|
||||
*
|
||||
* @param quat The store location for this matrix's rotation.
|
||||
* @return The value of this matrix's rotation.
|
||||
*/
|
||||
public Quaternion getRotation(Quaternion quat) {
|
||||
if (quat == null)
|
||||
quat = new Quaternion();
|
||||
quat.set(rot);
|
||||
return quat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the rotation quaternion in this matrix.
|
||||
*
|
||||
* @return rotation quaternion.
|
||||
*/
|
||||
public Quaternion getRotation() {
|
||||
return rot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores this scale value into the given vector3f. If scale is null, a new
|
||||
* vector3f is created to hold the value. The value, once stored, is
|
||||
* returned.
|
||||
*
|
||||
* @param scale The store location for this matrix's scale.
|
||||
* @return The value of this matrix's scale.
|
||||
*/
|
||||
public Vector3f getScale(Vector3f scale) {
|
||||
if (scale == null)
|
||||
scale = new Vector3f();
|
||||
scale.set(this.scale);
|
||||
return scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this matrix to the interpolation between the first matrix and the
|
||||
* second by delta amount.
|
||||
*
|
||||
* @param t1 The begining transform.
|
||||
* @param t2 The ending transform.
|
||||
* @param delta An amount between 0 and 1 representing how far to
|
||||
* interpolate from t1 to t2.
|
||||
*/
|
||||
public void interpolateTransforms(Transform t1, Transform t2, float delta) {
|
||||
this.rot.slerp(t1.rot, t2.rot, delta);
|
||||
this.translation.interpolate(t1.translation, t2.translation, delta);
|
||||
this.scale.interpolate(t1.scale, t2.scale, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the values of this matrix acording to it's parent. Very similar
|
||||
* to the concept of Node/Spatial transforms.
|
||||
*
|
||||
* @param parent The parent matrix.
|
||||
* @return This matrix, after combining.
|
||||
*/
|
||||
public Transform combineWithParent(Transform parent) {
|
||||
scale.multLocal(parent.scale);
|
||||
// rot.multLocal(parent.rot);
|
||||
parent.rot.mult(rot, rot);
|
||||
|
||||
// This here, is evil code
|
||||
// parent
|
||||
// .rot
|
||||
// .multLocal(translation)
|
||||
// .multLocal(parent.scale)
|
||||
// .addLocal(parent.translation);
|
||||
|
||||
translation.multLocal(parent.scale);
|
||||
parent.rot
|
||||
.multLocal(translation)
|
||||
.addLocal(parent.translation);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #combineWithParent(Transform)}, but assumes that rotation
|
||||
* is global, so it's not modified.
|
||||
*
|
||||
* @param parent
|
||||
* @return
|
||||
*/
|
||||
public Transform combineWithParentGlobalRotation(Transform parent) {
|
||||
scale.multLocal(parent.scale);
|
||||
translation.multLocal(parent.scale);
|
||||
|
||||
parent.rot
|
||||
.multLocal(translation)
|
||||
.addLocal(parent.translation);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this matrix's translation to the given x,y,z values.
|
||||
*
|
||||
* @param x This matrix's new x translation.
|
||||
* @param y This matrix's new y translation.
|
||||
* @param z This matrix's new z translation.
|
||||
* @return this
|
||||
*/
|
||||
public Transform setTranslation(float x, float y, float z) {
|
||||
translation.set(x, y, z);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this matrix's scale to the given x,y,z values.
|
||||
*
|
||||
* @param x This matrix's new x scale.
|
||||
* @param y This matrix's new y scale.
|
||||
* @param z This matrix's new z scale.
|
||||
* @return this
|
||||
*/
|
||||
public Transform setScale(float x, float y, float z) {
|
||||
scale.set(x, y, z);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3f transformVector(final Vector3f in, Vector3f store) {
|
||||
if (store == null)
|
||||
store = new Vector3f();
|
||||
|
||||
// multiply with scale first, then rotate, finally translate (cf.
|
||||
// Eberly)
|
||||
return rot.mult(store.set(in).multLocal(scale), store).addLocal(translation);
|
||||
}
|
||||
|
||||
public Vector3f transformInverseVector(final Vector3f in, Vector3f store) {
|
||||
if (store == null)
|
||||
store = new Vector3f();
|
||||
|
||||
// The author of this code should look above and take the inverse of
|
||||
// that
|
||||
// But for some reason, they didnt ..
|
||||
// in.subtract(translation, store).divideLocal(scale);
|
||||
// rot.inverse().mult(store, store);
|
||||
|
||||
in.subtract(translation, store);
|
||||
rot.inverse().mult(store, store);
|
||||
store.divideLocal(scale);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the identity. Equal to translation=0,0,0 scale=1,1,1 rot=0,0,0,1.
|
||||
*/
|
||||
public void loadIdentity() {
|
||||
translation.set(0, 0, 0);
|
||||
scale.set(1, 1, 1);
|
||||
rot.set(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName()
|
||||
+ "[ "
|
||||
+ translation.x
|
||||
+ ", "
|
||||
+ translation.y
|
||||
+ ", "
|
||||
+ translation.z
|
||||
+ "]\n"
|
||||
+ "[ "
|
||||
+ rot.x
|
||||
+ ", "
|
||||
+ rot.y
|
||||
+ ", "
|
||||
+ rot.z
|
||||
+ ", "
|
||||
+ rot.w
|
||||
+ "]\n"
|
||||
+ "[ "
|
||||
+ scale.x
|
||||
+ " , "
|
||||
+ scale.y
|
||||
+ ", "
|
||||
+ scale.z
|
||||
+ "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this matrix to be equal to the given matrix.
|
||||
*
|
||||
* @param matrixQuat The matrix to be equal to.
|
||||
* @return this
|
||||
*/
|
||||
public Transform set(Transform matrixQuat) {
|
||||
this.translation.set(matrixQuat.translation);
|
||||
this.rot.set(matrixQuat.rot);
|
||||
this.scale.set(matrixQuat.scale);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((rot == null) ? 0 : rot.hashCode());
|
||||
result = prime * result + ((scale == null) ? 0 : scale.hashCode());
|
||||
result = prime * result + ((translation == null) ? 0 : translation.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Transform other = (Transform) obj;
|
||||
if (rot == null) {
|
||||
if (other.rot != null)
|
||||
return false;
|
||||
} else if (!rot.equals(other.rot))
|
||||
return false;
|
||||
if (scale == null) {
|
||||
if (other.scale != null)
|
||||
return false;
|
||||
} else if (!scale.equals(other.scale))
|
||||
return false;
|
||||
if (translation == null) {
|
||||
if (other.translation != null)
|
||||
return false;
|
||||
} else if (!translation.equals(other.translation))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transform clone() {
|
||||
try {
|
||||
Transform tq = (Transform) super.clone();
|
||||
tq.rot = rot.clone();
|
||||
tq.scale = scale.clone();
|
||||
tq.translation = translation.clone();
|
||||
return tq;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
718
src/main/java/com/jme3/math/Vector2f.java
Normal file
718
src/main/java/com/jme3/math/Vector2f.java
Normal file
@@ -0,0 +1,718 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.math;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* <code>Vector2f</code> defines a Vector for a two float value vector.
|
||||
*
|
||||
* @author Mark Powell
|
||||
* @author Joshua Slack
|
||||
*/
|
||||
public final class Vector2f implements Cloneable, java.io.Serializable {
|
||||
|
||||
static final long serialVersionUID = 1;
|
||||
private static final Logger logger = Logger.getLogger(Vector2f.class.getName());
|
||||
|
||||
public static final Vector2f ZERO = new Vector2f(0f, 0f);
|
||||
public static final Vector2f UNIT_XY = new Vector2f(1f, 1f);
|
||||
|
||||
/**
|
||||
* the x value of the vector.
|
||||
*/
|
||||
public float x;
|
||||
/**
|
||||
* the y value of the vector.
|
||||
*/
|
||||
public float y;
|
||||
|
||||
/**
|
||||
* Creates a Vector2f with the given initial x and y values.
|
||||
*
|
||||
* @param x The x value of this Vector2f.
|
||||
* @param y The y value of this Vector2f.
|
||||
*/
|
||||
public Vector2f(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Vector2f with x and y set to 0. Equivalent to Vector2f(0,0).
|
||||
*/
|
||||
public Vector2f() {
|
||||
x = y = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Vector2f that contains the passed vector's information
|
||||
*
|
||||
* @param vector2f The vector to copy
|
||||
*/
|
||||
public Vector2f(Vector2f vector2f) {
|
||||
this.x = vector2f.x;
|
||||
this.y = vector2f.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the x and y values of the vector
|
||||
*
|
||||
* @param x the x value of the vector.
|
||||
* @param y the y value of the vector.
|
||||
* @return this vector
|
||||
*/
|
||||
public Vector2f set(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the x and y values of the vector from another vector
|
||||
*
|
||||
* @param vec the vector to copy from
|
||||
* @return this vector
|
||||
*/
|
||||
public Vector2f set(Vector2f vec) {
|
||||
this.x = vec.x;
|
||||
this.y = vec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>add</code> adds a provided vector to this vector creating a
|
||||
* resultant vector which is returned. If the provided vector is null, null
|
||||
* is returned.
|
||||
*
|
||||
* @param vec the vector to add to this.
|
||||
* @return the resultant vector.
|
||||
*/
|
||||
public Vector2f add(Vector2f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
return new Vector2f(x + vec.x, y + vec.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>addLocal</code> adds a provided vector to this vector internally,
|
||||
* and returns a handle to this vector for easy chaining of calls. If the
|
||||
* provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec the vector to add to this vector.
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f addLocal(Vector2f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
x += vec.x;
|
||||
y += vec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>addLocal</code> adds the provided values to this vector internally,
|
||||
* and returns a handle to this vector for easy chaining of calls.
|
||||
*
|
||||
* @param addX value to add to x
|
||||
* @param addY value to add to y
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f addLocal(float addX, float addY) {
|
||||
x += addX;
|
||||
y += addY;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>add</code> adds this vector by <code>vec</code> and stores the
|
||||
* result in <code>result</code>.
|
||||
*
|
||||
* @param vec The vector to add.
|
||||
* @param result The vector to store the result in.
|
||||
* @return The result vector, after adding.
|
||||
*/
|
||||
public Vector2f add(Vector2f vec, Vector2f result) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
if (result == null)
|
||||
result = new Vector2f();
|
||||
result.x = x + vec.x;
|
||||
result.y = y + vec.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>dot</code> calculates the dot product of this vector with a
|
||||
* provided vector. If the provided vector is null, 0 is returned.
|
||||
*
|
||||
* @param vec the vector to dot with this vector.
|
||||
* @return the resultant dot product of this vector and a given vector.
|
||||
*/
|
||||
public float dot(Vector2f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, 0 returned.");
|
||||
return 0;
|
||||
}
|
||||
return x * vec.x + y * vec.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>cross</code> calculates the cross product of this vector with a
|
||||
* parameter vector v.
|
||||
*
|
||||
* @param v the vector to take the cross product of with this.
|
||||
* @return the cross product vector.
|
||||
*/
|
||||
public Vector3f cross(Vector2f v) {
|
||||
return new Vector3f(0, 0, determinant(v));
|
||||
}
|
||||
|
||||
public float determinant(Vector2f v) {
|
||||
return (x * v.y) - (y * v.x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this vector to the interpolation by changeAmnt from this to the
|
||||
* finalVec this=(1-changeAmnt)*this + changeAmnt * finalVec
|
||||
*
|
||||
* @param finalVec The final vector to interpolate towards
|
||||
* @param changeAmnt An amount between 0.0 - 1.0 representing a percentage
|
||||
* change from this towards finalVec
|
||||
*/
|
||||
public Vector2f interpolate(Vector2f finalVec, float changeAmnt) {
|
||||
this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
|
||||
this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this vector to the interpolation by changeAmnt from beginVec to
|
||||
* finalVec this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
|
||||
*
|
||||
* @param beginVec The begining vector (delta=0)
|
||||
* @param finalVec The final vector to interpolate towards (delta=1)
|
||||
* @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
|
||||
* change from beginVec towards finalVec
|
||||
*/
|
||||
public Vector2f interpolate(
|
||||
Vector2f beginVec,
|
||||
Vector2f finalVec,
|
||||
float changeAmnt
|
||||
) {
|
||||
this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
|
||||
this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a vector... if it is null or its floats are NaN or infinite, return
|
||||
* false. Else return true.
|
||||
*
|
||||
* @param vector the vector to check
|
||||
* @return true or false as stated above.
|
||||
*/
|
||||
public static boolean isValidVector(Vector2f vector) {
|
||||
if (vector == null)
|
||||
return false;
|
||||
if (
|
||||
Float.isNaN(vector.x)
|
||||
||
|
||||
Float.isNaN(vector.y)
|
||||
)
|
||||
return false;
|
||||
if (
|
||||
Float.isInfinite(vector.x)
|
||||
||
|
||||
Float.isInfinite(vector.y)
|
||||
)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>length</code> calculates the magnitude of this vector.
|
||||
*
|
||||
* @return the length or magnitude of the vector.
|
||||
*/
|
||||
public float length() {
|
||||
return FastMath.sqrt(lengthSquared());
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>lengthSquared</code> calculates the squared value of the magnitude
|
||||
* of the vector.
|
||||
*
|
||||
* @return the magnitude squared of the vector.
|
||||
*/
|
||||
public float lengthSquared() {
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>distanceSquared</code> calculates the distance squared between this
|
||||
* vector and vector v.
|
||||
*
|
||||
* @param v the second vector to determine the distance squared.
|
||||
* @return the distance squared between the two vectors.
|
||||
*/
|
||||
public float distanceSquared(Vector2f v) {
|
||||
double dx = x - v.x;
|
||||
double dy = y - v.y;
|
||||
return (float) (dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>distanceSquared</code> calculates the distance squared between this
|
||||
* vector and vector v.
|
||||
*
|
||||
* @param otherX The X coordinate of the v vector
|
||||
* @param otherY The Y coordinate of the v vector
|
||||
* @return the distance squared between the two vectors.
|
||||
*/
|
||||
public float distanceSquared(float otherX, float otherY) {
|
||||
double dx = x - otherX;
|
||||
double dy = y - otherY;
|
||||
return (float) (dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>distance</code> calculates the distance between this vector and
|
||||
* vector v.
|
||||
*
|
||||
* @param v the second vector to determine the distance.
|
||||
* @return the distance between the two vectors.
|
||||
*/
|
||||
public float distance(Vector2f v) {
|
||||
return FastMath.sqrt(distanceSquared(v));
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>mult</code> multiplies this vector by a scalar. The resultant
|
||||
* vector is returned.
|
||||
*
|
||||
* @param scalar the value to multiply this vector by.
|
||||
* @return the new vector.
|
||||
*/
|
||||
public Vector2f mult(float scalar) {
|
||||
return new Vector2f(x * scalar, y * scalar);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>multLocal</code> multiplies this vector by a scalar internally, and
|
||||
* returns a handle to this vector for easy chaining of calls.
|
||||
*
|
||||
* @param scalar the value to multiply this vector by.
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f multLocal(float scalar) {
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>multLocal</code> multiplies a provided vector to this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls. If the provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec the vector to mult to this vector.
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f multLocal(Vector2f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
x *= vec.x;
|
||||
y *= vec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies this Vector2f's x and y by the scalar and stores the result in
|
||||
* product. The result is returned for chaining. Similar to
|
||||
* product=this*scalar;
|
||||
*
|
||||
* @param scalar The scalar to multiply by.
|
||||
* @param product The vector2f to store the result in.
|
||||
* @return product, after multiplication.
|
||||
*/
|
||||
public Vector2f mult(float scalar, Vector2f product) {
|
||||
if (null == product) {
|
||||
product = new Vector2f();
|
||||
}
|
||||
|
||||
product.x = x * scalar;
|
||||
product.y = y * scalar;
|
||||
return product;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>divide</code> divides the values of this vector by a scalar and
|
||||
* returns the result. The values of this vector remain untouched.
|
||||
*
|
||||
* @param scalar the value to divide this vectors attributes by.
|
||||
* @return the result <code>Vector</code>.
|
||||
*/
|
||||
public Vector2f divide(float scalar) {
|
||||
return new Vector2f(x / scalar, y / scalar);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>divideLocal</code> divides this vector by a scalar internally, and
|
||||
* returns a handle to this vector for easy chaining of calls. Dividing by
|
||||
* zero will result in an exception.
|
||||
*
|
||||
* @param scalar the value to divides this vector by.
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f divideLocal(float scalar) {
|
||||
x /= scalar;
|
||||
y /= scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>negate</code> returns the negative of this vector. All values are
|
||||
* negated and set to a new vector.
|
||||
*
|
||||
* @return the negated vector.
|
||||
*/
|
||||
public Vector2f negate() {
|
||||
return new Vector2f(-x, -y);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>negateLocal</code> negates the internal values of this vector.
|
||||
*
|
||||
* @return this.
|
||||
*/
|
||||
public Vector2f negateLocal() {
|
||||
x = -x;
|
||||
y = -y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtract</code> subtracts the values of a given vector from those
|
||||
* of this vector creating a new vector object. If the provided vector is
|
||||
* null, an exception is thrown.
|
||||
*
|
||||
* @param vec the vector to subtract from this vector.
|
||||
* @return the result vector.
|
||||
*/
|
||||
public Vector2f subtract(Vector2f vec) {
|
||||
return subtract(vec, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtract</code> subtracts the values of a given vector from those
|
||||
* of this vector storing the result in the given vector object. If the
|
||||
* provided vector is null, an exception is thrown.
|
||||
*
|
||||
* @param vec the vector to subtract from this vector.
|
||||
* @param store the vector to store the result in. It is safe for this to be
|
||||
* the same as vec. If null, a new vector is created.
|
||||
* @return the result vector.
|
||||
*/
|
||||
public Vector2f subtract(Vector2f vec, Vector2f store) {
|
||||
if (store == null)
|
||||
store = new Vector2f();
|
||||
store.x = x - vec.x;
|
||||
store.y = y - vec.y;
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtract</code> subtracts the given x,y values from those of this
|
||||
* vector creating a new vector object.
|
||||
*
|
||||
* @param valX value to subtract from x
|
||||
* @param valY value to subtract from y
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f subtract(float valX, float valY) {
|
||||
return new Vector2f(x - valX, y - valY);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtractLocal</code> subtracts a provided vector to this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls. If the provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec the vector to subtract
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f subtractLocal(Vector2f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
x -= vec.x;
|
||||
y -= vec.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtractLocal</code> subtracts the provided values from this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls.
|
||||
*
|
||||
* @param valX value to subtract from x
|
||||
* @param valY value to subtract from y
|
||||
* @return this
|
||||
*/
|
||||
public Vector2f subtractLocal(float valX, float valY) {
|
||||
x -= valX;
|
||||
y -= valY;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>normalize</code> returns the unit vector of this vector.
|
||||
*
|
||||
* @return unit vector of this vector.
|
||||
*/
|
||||
public Vector2f normalize() {
|
||||
float length = length();
|
||||
if (length != 0) {
|
||||
return divide(length);
|
||||
}
|
||||
|
||||
return divide(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>normalizeLocal</code> makes this vector into a unit vector of
|
||||
* itself.
|
||||
*
|
||||
* @return this.
|
||||
*/
|
||||
public Vector2f normalizeLocal() {
|
||||
float length = length();
|
||||
if (length != 0) {
|
||||
return divideLocal(length);
|
||||
}
|
||||
|
||||
return divideLocal(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>smallestAngleBetween</code> returns (in radians) the minimum angle
|
||||
* between two vectors. It is assumed that both this vector and the given
|
||||
* vector are unit vectors (iow, normalized).
|
||||
*
|
||||
* @param otherVector a unit vector to find the angle against
|
||||
* @return the angle in radians.
|
||||
*/
|
||||
public float smallestAngleBetween(Vector2f otherVector) {
|
||||
float dotProduct = dot(otherVector);
|
||||
float angle = FastMath.acos(dotProduct);
|
||||
return angle;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>angleBetween</code> returns (in radians) the angle required to
|
||||
* rotate a ray represented by this vector to lie colinear to a ray
|
||||
* described by the given vector. It is assumed that both this vector and
|
||||
* the given vector are unit vectors (iow, normalized).
|
||||
*
|
||||
* @param otherVector the "destination" unit vector
|
||||
* @return the angle in radians.
|
||||
*/
|
||||
public float angleBetween(Vector2f otherVector) {
|
||||
float angle = FastMath.atan2(otherVector.y, otherVector.x)
|
||||
- FastMath.atan2(y, x);
|
||||
return angle;
|
||||
}
|
||||
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public Vector2f setX(float x) {
|
||||
this.x = x;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public Vector2f setY(float y) {
|
||||
this.y = y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>getAngle</code> returns (in radians) the angle represented by this
|
||||
* Vector2f as expressed by a conversion from rectangular coordinates
|
||||
* (<code>x</code>, <code>y</code>) to polar coordinates
|
||||
* (r, <i>theta</i>).
|
||||
*
|
||||
* @return the angle in radians. [-pi, pi)
|
||||
*/
|
||||
public float getAngle() {
|
||||
return FastMath.atan2(y, x);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>zero</code> resets this vector's data to zero internally.
|
||||
*/
|
||||
public Vector2f zero() {
|
||||
x = y = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>hashCode</code> returns a unique code for this vector object based
|
||||
* on it's values. If two vectors are logically equivalent, they will return
|
||||
* the same hash code value.
|
||||
*
|
||||
* @return the hash code value of this vector.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 37;
|
||||
hash += 37 * hash + Float.floatToIntBits(x);
|
||||
hash += 37 * hash + Float.floatToIntBits(y);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2f clone() {
|
||||
try {
|
||||
return (Vector2f) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError(); // can not happen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this Vector2f into the given float[] object.
|
||||
*
|
||||
* @param floats The float[] to take this Vector2f. If null, a new float[2]
|
||||
* is created.
|
||||
* @return The array, with X, Y float values in that order
|
||||
*/
|
||||
public float[] toArray(float[] floats) {
|
||||
if (floats == null) {
|
||||
floats = new float[2];
|
||||
}
|
||||
floats[0] = x;
|
||||
floats[1] = y;
|
||||
return floats;
|
||||
}
|
||||
|
||||
/**
|
||||
* are these two vectors the same? they are is they both have the same x and
|
||||
* y values.
|
||||
*
|
||||
* @param o the object to compare for equality
|
||||
* @return true if they are equal
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Vector2f)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector2f comp = (Vector2f) o;
|
||||
if (Float.compare(x, comp.x) != 0)
|
||||
return false;
|
||||
if (Float.compare(y, comp.y) != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>toString</code> returns the string representation of this vector
|
||||
* object. The format of the string is such: com.jme.math.Vector2f
|
||||
* [X=XX.XXXX, Y=YY.YYYY]
|
||||
*
|
||||
* @return the string representation of this vector.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + x + ", " + y + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Used with serialization. Not to be called manually.
|
||||
*
|
||||
* @param in ObjectInput
|
||||
* @throws IOException
|
||||
* @throws ClassNotFoundException
|
||||
* @see java.io.Externalizable
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException,
|
||||
ClassNotFoundException {
|
||||
x = in.readFloat();
|
||||
y = in.readFloat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used with serialization. Not to be called manually.
|
||||
*
|
||||
* @param out ObjectOutput
|
||||
* @throws IOException
|
||||
* @see java.io.Externalizable
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeFloat(x);
|
||||
out.writeFloat(y);
|
||||
}
|
||||
|
||||
public void rotateAroundOrigin(float angle, boolean cw) {
|
||||
if (cw)
|
||||
angle = -angle;
|
||||
float newX = FastMath.cos(angle) * x - FastMath.sin(angle) * y;
|
||||
float newY = FastMath.sin(angle) * x + FastMath.cos(angle) * y;
|
||||
x = newX;
|
||||
y = newY;
|
||||
}
|
||||
}
|
||||
1101
src/main/java/com/jme3/math/Vector3f.java
Normal file
1101
src/main/java/com/jme3/math/Vector3f.java
Normal file
File diff suppressed because it is too large
Load Diff
971
src/main/java/com/jme3/math/Vector4f.java
Normal file
971
src/main/java/com/jme3/math/Vector4f.java
Normal file
@@ -0,0 +1,971 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.math;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* <code>Vector4f</code> defines a Vector for a four float value tuple.
|
||||
* <code>Vector4f</code> can represent any four dimensional value, such as a
|
||||
* vertex, a normal, etc. Utility methods are also included to aid in
|
||||
* mathematical calculations.
|
||||
*
|
||||
* @author Maarten Steur
|
||||
*/
|
||||
public final class Vector4f implements Cloneable, java.io.Serializable {
|
||||
|
||||
static final long serialVersionUID = 1;
|
||||
|
||||
private static final Logger logger = Logger.getLogger(Vector4f.class.getName());
|
||||
|
||||
public final static Vector4f ZERO = new Vector4f(0, 0, 0, 0);
|
||||
public final static Vector4f NAN = new Vector4f(Float.NaN, Float.NaN, Float.NaN, Float.NaN);
|
||||
public final static Vector4f UNIT_X = new Vector4f(1, 0, 0, 0);
|
||||
public final static Vector4f UNIT_Y = new Vector4f(0, 1, 0, 0);
|
||||
public final static Vector4f UNIT_Z = new Vector4f(0, 0, 1, 0);
|
||||
public final static Vector4f UNIT_W = new Vector4f(0, 0, 0, 1);
|
||||
public final static Vector4f UNIT_XYZW = new Vector4f(1, 1, 1, 1);
|
||||
public final static Vector4f POSITIVE_INFINITY = new Vector4f(
|
||||
Float.POSITIVE_INFINITY,
|
||||
Float.POSITIVE_INFINITY,
|
||||
Float.POSITIVE_INFINITY,
|
||||
Float.POSITIVE_INFINITY
|
||||
);
|
||||
public final static Vector4f NEGATIVE_INFINITY = new Vector4f(
|
||||
Float.NEGATIVE_INFINITY,
|
||||
Float.NEGATIVE_INFINITY,
|
||||
Float.NEGATIVE_INFINITY,
|
||||
Float.NEGATIVE_INFINITY
|
||||
);
|
||||
|
||||
/**
|
||||
* the x value of the vector.
|
||||
*/
|
||||
public float x;
|
||||
|
||||
/**
|
||||
* the y value of the vector.
|
||||
*/
|
||||
public float y;
|
||||
|
||||
/**
|
||||
* the z value of the vector.
|
||||
*/
|
||||
public float z;
|
||||
|
||||
/**
|
||||
* the w value of the vector.
|
||||
*/
|
||||
public float w;
|
||||
|
||||
/**
|
||||
* Constructor instantiates a new <code>Vector3f</code> with default values
|
||||
* of (0,0,0).
|
||||
*
|
||||
*/
|
||||
public Vector4f() {
|
||||
x = y = z = w = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor instantiates a new <code>Vector4f</code> with provides
|
||||
* values.
|
||||
*
|
||||
* @param x the x value of the vector.
|
||||
* @param y the y value of the vector.
|
||||
* @param z the z value of the vector.
|
||||
* @param w the w value of the vector.
|
||||
*/
|
||||
public Vector4f(float x, float y, float z, float w) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor instantiates a new <code>Vector3f</code> that is a copy of
|
||||
* the provided vector
|
||||
*
|
||||
* @param copy The Vector3f to copy
|
||||
*/
|
||||
public Vector4f(Vector4f copy) {
|
||||
this.set(copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>set</code> sets the x,y,z,w values of the vector based on passed
|
||||
* parameters.
|
||||
*
|
||||
* @param x the x value of the vector.
|
||||
* @param y the y value of the vector.
|
||||
* @param z the z value of the vector.
|
||||
* @param w the w value of the vector.
|
||||
* @return this vector
|
||||
*/
|
||||
public Vector4f set(float x, float y, float z, float w) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>set</code> sets the x,y,z values of the vector by copying the
|
||||
* supplied vector.
|
||||
*
|
||||
* @param vect the vector to copy.
|
||||
* @return this vector
|
||||
*/
|
||||
public Vector4f set(Vector4f vect) {
|
||||
this.x = vect.x;
|
||||
this.y = vect.y;
|
||||
this.z = vect.z;
|
||||
this.w = vect.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>add</code> adds a provided vector to this vector creating a
|
||||
* resultant vector which is returned. If the provided vector is null, null
|
||||
* is returned.
|
||||
*
|
||||
* @param vec the vector to add to this.
|
||||
* @return the resultant vector.
|
||||
*/
|
||||
public Vector4f add(Vector4f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
return new Vector4f(x + vec.x, y + vec.y, z + vec.z, w + vec.w);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>add</code> adds the values of a provided vector storing the values
|
||||
* in the supplied vector.
|
||||
*
|
||||
* @param vec the vector to add to this
|
||||
* @param result the vector to store the result in
|
||||
* @return result returns the supplied result vector.
|
||||
*/
|
||||
public Vector4f add(Vector4f vec, Vector4f result) {
|
||||
result.x = x + vec.x;
|
||||
result.y = y + vec.y;
|
||||
result.z = z + vec.z;
|
||||
result.w = w + vec.w;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>addLocal</code> adds a provided vector to this vector internally,
|
||||
* and returns a handle to this vector for easy chaining of calls. If the
|
||||
* provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec the vector to add to this vector.
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f addLocal(Vector4f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
x += vec.x;
|
||||
y += vec.y;
|
||||
z += vec.z;
|
||||
w += vec.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>add</code> adds the provided values to this vector, creating a new
|
||||
* vector that is then returned.
|
||||
*
|
||||
* @param addX the x value to add.
|
||||
* @param addY the y value to add.
|
||||
* @param addZ the z value to add.
|
||||
* @return the result vector.
|
||||
*/
|
||||
public Vector4f add(float addX, float addY, float addZ, float addW) {
|
||||
return new Vector4f(x + addX, y + addY, z + addZ, w + addW);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>addLocal</code> adds the provided values to this vector internally,
|
||||
* and returns a handle to this vector for easy chaining of calls.
|
||||
*
|
||||
* @param addX value to add to x
|
||||
* @param addY value to add to y
|
||||
* @param addZ value to add to z
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f addLocal(float addX, float addY, float addZ, float addW) {
|
||||
x += addX;
|
||||
y += addY;
|
||||
z += addZ;
|
||||
w += addW;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>scaleAdd</code> multiplies this vector by a scalar then adds the
|
||||
* given Vector3f.
|
||||
*
|
||||
* @param scalar the value to multiply this vector by.
|
||||
* @param add the value to add
|
||||
*/
|
||||
public Vector4f scaleAdd(float scalar, Vector4f add) {
|
||||
x = x * scalar + add.x;
|
||||
y = y * scalar + add.y;
|
||||
z = z * scalar + add.z;
|
||||
w = w * scalar + add.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>scaleAdd</code> multiplies the given vector by a scalar then adds
|
||||
* the given vector.
|
||||
*
|
||||
* @param scalar the value to multiply this vector by.
|
||||
* @param mult the value to multiply the scalar by
|
||||
* @param add the value to add
|
||||
*/
|
||||
public Vector4f scaleAdd(float scalar, Vector4f mult, Vector4f add) {
|
||||
this.x = mult.x * scalar + add.x;
|
||||
this.y = mult.y * scalar + add.y;
|
||||
this.z = mult.z * scalar + add.z;
|
||||
this.w = mult.w * scalar + add.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>dot</code> calculates the dot product of this vector with a
|
||||
* provided vector. If the provided vector is null, 0 is returned.
|
||||
*
|
||||
* @param vec the vector to dot with this vector.
|
||||
* @return the resultant dot product of this vector and a given vector.
|
||||
*/
|
||||
public float dot(Vector4f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, 0 returned.");
|
||||
return 0;
|
||||
}
|
||||
return x * vec.x + y * vec.y + z * vec.z + w * vec.w;
|
||||
}
|
||||
|
||||
public Vector4f project(Vector4f other) {
|
||||
float n = this.dot(other); // A . B
|
||||
float d = other.lengthSquared(); // |B|^2
|
||||
return new Vector4f(other).normalizeLocal().multLocal(n / d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this vector is a unit vector (length() ~= 1), returns
|
||||
* false otherwise.
|
||||
*
|
||||
* @return true if this vector is a unit vector (length() ~= 1), or false
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean isUnitVector() {
|
||||
float len = length();
|
||||
return 0.99f < len && len < 1.01f;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>length</code> calculates the magnitude of this vector.
|
||||
*
|
||||
* @return the length or magnitude of the vector.
|
||||
*/
|
||||
public float length() {
|
||||
return FastMath.sqrt(lengthSquared());
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>lengthSquared</code> calculates the squared value of the magnitude
|
||||
* of the vector.
|
||||
*
|
||||
* @return the magnitude squared of the vector.
|
||||
*/
|
||||
public float lengthSquared() {
|
||||
return x * x + y * y + z * z + w * w;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>distanceSquared</code> calculates the distance squared between this
|
||||
* vector and vector v.
|
||||
*
|
||||
* @param v the second vector to determine the distance squared.
|
||||
* @return the distance squared between the two vectors.
|
||||
*/
|
||||
public float distanceSquared(Vector4f v) {
|
||||
double dx = x - v.x;
|
||||
double dy = y - v.y;
|
||||
double dz = z - v.z;
|
||||
double dw = w - v.w;
|
||||
return (float) (dx * dx + dy * dy + dz * dz + dw * dw);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>distance</code> calculates the distance between this vector and
|
||||
* vector v.
|
||||
*
|
||||
* @param v the second vector to determine the distance.
|
||||
* @return the distance between the two vectors.
|
||||
*/
|
||||
public float distance(Vector4f v) {
|
||||
return FastMath.sqrt(distanceSquared(v));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>mult</code> multiplies this vector by a scalar. The resultant
|
||||
* vector is returned.
|
||||
*
|
||||
* @param scalar the value to multiply this vector by.
|
||||
* @return the new vector.
|
||||
*/
|
||||
public Vector4f mult(float scalar) {
|
||||
return new Vector4f(x * scalar, y * scalar, z * scalar, w * scalar);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>mult</code> multiplies this vector by a scalar. The resultant
|
||||
* vector is supplied as the second parameter and returned.
|
||||
*
|
||||
* @param scalar the scalar to multiply this vector by.
|
||||
* @param product the product to store the result in.
|
||||
* @return product
|
||||
*/
|
||||
public Vector4f mult(float scalar, Vector4f product) {
|
||||
if (null == product) {
|
||||
product = new Vector4f();
|
||||
}
|
||||
|
||||
product.x = x * scalar;
|
||||
product.y = y * scalar;
|
||||
product.z = z * scalar;
|
||||
product.w = w * scalar;
|
||||
return product;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>multLocal</code> multiplies this vector by a scalar internally, and
|
||||
* returns a handle to this vector for easy chaining of calls.
|
||||
*
|
||||
* @param scalar the value to multiply this vector by.
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f multLocal(float scalar) {
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
z *= scalar;
|
||||
w *= scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>multLocal</code> multiplies a provided vector to this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls. If the provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec the vector to mult to this vector.
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f multLocal(Vector4f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
x *= vec.x;
|
||||
y *= vec.y;
|
||||
z *= vec.z;
|
||||
w *= vec.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>multLocal</code> multiplies this vector by 3 scalars internally,
|
||||
* and returns a handle to this vector for easy chaining of calls.
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @param z
|
||||
* @param w
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f multLocal(float x, float y, float z, float w) {
|
||||
this.x *= x;
|
||||
this.y *= y;
|
||||
this.z *= z;
|
||||
this.w *= w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>multLocal</code> multiplies a provided vector to this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls. If the provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec the vector to mult to this vector.
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f mult(Vector4f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
return mult(vec, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>multLocal</code> multiplies a provided vector to this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls. If the provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec the vector to mult to this vector.
|
||||
* @param store result vector (null to create a new vector)
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f mult(Vector4f vec, Vector4f store) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
if (store == null)
|
||||
store = new Vector4f();
|
||||
return store.set(x * vec.x, y * vec.y, z * vec.z, w * vec.w);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>divide</code> divides the values of this vector by a scalar and
|
||||
* returns the result. The values of this vector remain untouched.
|
||||
*
|
||||
* @param scalar the value to divide this vectors attributes by.
|
||||
* @return the result <code>Vector</code>.
|
||||
*/
|
||||
public Vector4f divide(float scalar) {
|
||||
scalar = 1f / scalar;
|
||||
return new Vector4f(x * scalar, y * scalar, z * scalar, w * scalar);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>divideLocal</code> divides this vector by a scalar internally, and
|
||||
* returns a handle to this vector for easy chaining of calls. Dividing by
|
||||
* zero will result in an exception.
|
||||
*
|
||||
* @param scalar the value to divides this vector by.
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f divideLocal(float scalar) {
|
||||
scalar = 1f / scalar;
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
z *= scalar;
|
||||
w *= scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>divide</code> divides the values of this vector by a scalar and
|
||||
* returns the result. The values of this vector remain untouched.
|
||||
*
|
||||
* @param scalar the value to divide this vectors attributes by.
|
||||
* @return the result <code>Vector</code>.
|
||||
*/
|
||||
public Vector4f divide(Vector4f scalar) {
|
||||
return new Vector4f(x / scalar.x, y / scalar.y, z / scalar.z, w / scalar.w);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>divideLocal</code> divides this vector by a scalar internally, and
|
||||
* returns a handle to this vector for easy chaining of calls. Dividing by
|
||||
* zero will result in an exception.
|
||||
*
|
||||
* @param scalar the value to divides this vector by.
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f divideLocal(Vector4f scalar) {
|
||||
x /= scalar.x;
|
||||
y /= scalar.y;
|
||||
z /= scalar.z;
|
||||
w /= scalar.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>negate</code> returns the negative of this vector. All values are
|
||||
* negated and set to a new vector.
|
||||
*
|
||||
* @return the negated vector.
|
||||
*/
|
||||
public Vector4f negate() {
|
||||
return new Vector4f(-x, -y, -z, -w);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>negateLocal</code> negates the internal values of this vector.
|
||||
*
|
||||
* @return this.
|
||||
*/
|
||||
public Vector4f negateLocal() {
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
w = -w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>subtract</code> subtracts the values of a given vector from those
|
||||
* of this vector creating a new vector object. If the provided vector is
|
||||
* null, null is returned.
|
||||
*
|
||||
* @param vec the vector to subtract from this vector.
|
||||
* @return the result vector.
|
||||
*/
|
||||
public Vector4f subtract(Vector4f vec) {
|
||||
return new Vector4f(x - vec.x, y - vec.y, z - vec.z, w - vec.w);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtractLocal</code> subtracts a provided vector to this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls. If the provided vector is null, null is returned.
|
||||
*
|
||||
* @param vec the vector to subtract
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f subtractLocal(Vector4f vec) {
|
||||
if (null == vec) {
|
||||
logger.warning("Provided vector is null, null returned.");
|
||||
return null;
|
||||
}
|
||||
x -= vec.x;
|
||||
y -= vec.y;
|
||||
z -= vec.z;
|
||||
w -= vec.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>subtract</code>
|
||||
*
|
||||
* @param vec the vector to subtract from this
|
||||
* @param result the vector to store the result in
|
||||
* @return result
|
||||
*/
|
||||
public Vector4f subtract(Vector4f vec, Vector4f result) {
|
||||
if (result == null) {
|
||||
result = new Vector4f();
|
||||
}
|
||||
result.x = x - vec.x;
|
||||
result.y = y - vec.y;
|
||||
result.z = z - vec.z;
|
||||
result.w = w - vec.w;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>subtract</code> subtracts the provided values from this vector,
|
||||
* creating a new vector that is then returned.
|
||||
*
|
||||
* @param subtractX the x value to subtract.
|
||||
* @param subtractY the y value to subtract.
|
||||
* @param subtractZ the z value to subtract.
|
||||
* @param subtractW the w value to subtract.
|
||||
* @return the result vector.
|
||||
*/
|
||||
public Vector4f subtract(float subtractX, float subtractY, float subtractZ, float subtractW) {
|
||||
return new Vector4f(x - subtractX, y - subtractY, z - subtractZ, w - subtractW);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>subtractLocal</code> subtracts the provided values from this vector
|
||||
* internally, and returns a handle to this vector for easy chaining of
|
||||
* calls.
|
||||
*
|
||||
* @param subtractX the x value to subtract.
|
||||
* @param subtractY the y value to subtract.
|
||||
* @param subtractZ the z value to subtract.
|
||||
* @param subtractW the w value to subtract.
|
||||
* @return this
|
||||
*/
|
||||
public Vector4f subtractLocal(
|
||||
float subtractX,
|
||||
float subtractY,
|
||||
float subtractZ,
|
||||
float subtractW
|
||||
) {
|
||||
x -= subtractX;
|
||||
y -= subtractY;
|
||||
z -= subtractZ;
|
||||
w -= subtractW;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>normalize</code> returns the unit vector of this vector.
|
||||
*
|
||||
* @return unit vector of this vector.
|
||||
*/
|
||||
public Vector4f normalize() {
|
||||
// float length = length();
|
||||
// if (length != 0) {
|
||||
// return divide(length);
|
||||
// }
|
||||
//
|
||||
// return divide(1);
|
||||
float length = x * x + y * y + z * z + w * w;
|
||||
if (length != 1f && length != 0f) {
|
||||
length = 1.0f / FastMath.sqrt(length);
|
||||
return new Vector4f(x * length, y * length, z * length, w * length);
|
||||
}
|
||||
return clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>normalizeLocal</code> makes this vector into a unit vector of
|
||||
* itself.
|
||||
*
|
||||
* @return this.
|
||||
*/
|
||||
public Vector4f normalizeLocal() {
|
||||
// NOTE: this implementation is more optimized
|
||||
// than the old jme normalize as this method
|
||||
// is commonly used.
|
||||
float length = x * x + y * y + z * z + w * w;
|
||||
if (length != 1f && length != 0f) {
|
||||
length = 1.0f / FastMath.sqrt(length);
|
||||
x *= length;
|
||||
y *= length;
|
||||
z *= length;
|
||||
w *= length;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>maxLocal</code> computes the maximum value for each component in
|
||||
* this and <code>other</code> vector. The result is stored in this vector.
|
||||
*
|
||||
* @param other
|
||||
*/
|
||||
public Vector4f maxLocal(Vector4f other) {
|
||||
x = other.x > x ? other.x : x;
|
||||
y = other.y > y ? other.y : y;
|
||||
z = other.z > z ? other.z : z;
|
||||
w = other.w > w ? other.w : w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>minLocal</code> computes the minimum value for each component in
|
||||
* this and <code>other</code> vector. The result is stored in this vector.
|
||||
*
|
||||
* @param other
|
||||
*/
|
||||
public Vector4f minLocal(Vector4f other) {
|
||||
x = other.x < x ? other.x : x;
|
||||
y = other.y < y ? other.y : y;
|
||||
z = other.z < z ? other.z : z;
|
||||
w = other.w < w ? other.w : w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>zero</code> resets this vector's data to zero internally.
|
||||
*/
|
||||
public Vector4f zero() {
|
||||
x = y = z = w = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>angleBetween</code> returns (in radians) the angle between two
|
||||
* vectors. It is assumed that both this vector and the given vector are
|
||||
* unit vectors (iow, normalized).
|
||||
*
|
||||
* @param otherVector a unit vector to find the angle against
|
||||
* @return the angle in radians.
|
||||
*/
|
||||
public float angleBetween(Vector4f otherVector) {
|
||||
float dotProduct = dot(otherVector);
|
||||
float angle = FastMath.acos(dotProduct);
|
||||
return angle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this vector to the interpolation by changeAmnt from this to the
|
||||
* finalVec this=(1-changeAmnt)*this + changeAmnt * finalVec
|
||||
*
|
||||
* @param finalVec The final vector to interpolate towards
|
||||
* @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
|
||||
* change from this towards finalVec
|
||||
*/
|
||||
public Vector4f interpolate(Vector4f finalVec, float changeAmnt) {
|
||||
this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
|
||||
this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
|
||||
this.z = (1 - changeAmnt) * this.z + changeAmnt * finalVec.z;
|
||||
this.w = (1 - changeAmnt) * this.w + changeAmnt * finalVec.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this vector to the interpolation by changeAmnt from beginVec to
|
||||
* finalVec this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
|
||||
*
|
||||
* @param beginVec the beging vector (changeAmnt=0)
|
||||
* @param finalVec The final vector to interpolate towards
|
||||
* @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
|
||||
* change from beginVec towards finalVec
|
||||
*/
|
||||
public Vector4f interpolate(Vector4f beginVec, Vector4f finalVec, float changeAmnt) {
|
||||
this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
|
||||
this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
|
||||
this.z = (1 - changeAmnt) * beginVec.z + changeAmnt * finalVec.z;
|
||||
this.w = (1 - changeAmnt) * beginVec.w + changeAmnt * finalVec.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a vector... if it is null or its floats are NaN or infinite, return
|
||||
* false. Else return true.
|
||||
*
|
||||
* @param vector the vector to check
|
||||
* @return true or false as stated above.
|
||||
*/
|
||||
public static boolean isValidVector(Vector4f vector) {
|
||||
if (vector == null)
|
||||
return false;
|
||||
if (
|
||||
Float.isNaN(vector.x)
|
||||
||
|
||||
Float.isNaN(vector.y)
|
||||
||
|
||||
Float.isNaN(vector.z)
|
||||
||
|
||||
Float.isNaN(vector.w)
|
||||
)
|
||||
return false;
|
||||
if (
|
||||
Float.isInfinite(vector.x)
|
||||
||
|
||||
Float.isInfinite(vector.y)
|
||||
||
|
||||
Float.isInfinite(vector.z)
|
||||
||
|
||||
Float.isInfinite(vector.w)
|
||||
)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector4f clone() {
|
||||
try {
|
||||
return (Vector4f) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError(); // can not happen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this Vector3f into the given float[] object.
|
||||
*
|
||||
* @param floats The float[] to take this Vector3f. If null, a new float[3]
|
||||
* is created.
|
||||
* @return The array, with X, Y, Z float values in that order
|
||||
*/
|
||||
public float[] toArray(float[] floats) {
|
||||
if (floats == null) {
|
||||
floats = new float[4];
|
||||
}
|
||||
floats[0] = x;
|
||||
floats[1] = y;
|
||||
floats[2] = z;
|
||||
floats[3] = w;
|
||||
return floats;
|
||||
}
|
||||
|
||||
/**
|
||||
* are these two vectors the same? they are is they both have the same x,y,
|
||||
* and z values.
|
||||
*
|
||||
* @param o the object to compare for equality
|
||||
* @return true if they are equal
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Vector4f)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector4f comp = (Vector4f) o;
|
||||
if (Float.compare(x, comp.x) != 0)
|
||||
return false;
|
||||
if (Float.compare(y, comp.y) != 0)
|
||||
return false;
|
||||
if (Float.compare(z, comp.z) != 0)
|
||||
return false;
|
||||
if (Float.compare(w, comp.w) != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>hashCode</code> returns a unique code for this vector object based
|
||||
* on it's values. If two vectors are logically equivalent, they will return
|
||||
* the same hash code value.
|
||||
*
|
||||
* @return the hash code value of this vector.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 37;
|
||||
hash += 37 * hash + Float.floatToIntBits(x);
|
||||
hash += 37 * hash + Float.floatToIntBits(y);
|
||||
hash += 37 * hash + Float.floatToIntBits(z);
|
||||
hash += 37 * hash + Float.floatToIntBits(w);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>toString</code> returns the string representation of this vector.
|
||||
* The format is:
|
||||
*
|
||||
* org.jme.math.Vector3f [X=XX.XXXX, Y=YY.YYYY, Z=ZZ.ZZZZ, W=WW.WWWW]
|
||||
*
|
||||
* @return the string representation of this vector.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + x + ", " + y + ", " + z + ", " + w + ")";
|
||||
}
|
||||
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public Vector4f setX(float x) {
|
||||
this.x = x;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public Vector4f setY(float y) {
|
||||
this.y = y;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public Vector4f setZ(float z) {
|
||||
this.z = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getW() {
|
||||
return w;
|
||||
}
|
||||
|
||||
public Vector4f setW(float w) {
|
||||
this.w = w;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index
|
||||
* @return x value if index == 0, y value if index == 1 or z value if index
|
||||
* == 2
|
||||
* @throws IllegalArgumentException if index is not one of 0, 1, 2.
|
||||
*/
|
||||
public float get(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return x;
|
||||
case 1:
|
||||
return y;
|
||||
case 2:
|
||||
return z;
|
||||
case 3:
|
||||
return w;
|
||||
}
|
||||
throw new IllegalArgumentException("index must be either 0, 1, 2 or 3");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index which field index in this vector to set.
|
||||
* @param value to set to one of x, y, z or w.
|
||||
* @throws IllegalArgumentException if index is not one of 0, 1, 2, 3.
|
||||
*/
|
||||
public void set(int index, float value) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
x = value;
|
||||
return;
|
||||
case 1:
|
||||
y = value;
|
||||
return;
|
||||
case 2:
|
||||
z = value;
|
||||
return;
|
||||
case 3:
|
||||
w = value;
|
||||
return;
|
||||
}
|
||||
throw new IllegalArgumentException("index must be either 0, 1, 2 or 3");
|
||||
}
|
||||
|
||||
}
|
||||
105
src/main/java/com/jme3/system/NanoTimer.java
Normal file
105
src/main/java/com/jme3/system/NanoTimer.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.system;
|
||||
|
||||
/**
|
||||
* <code>NanoTimer</code> is a System.nanoTime implementation of
|
||||
* <code>Timer</code>. This is primarily useful for headless applications
|
||||
* running on a server.
|
||||
*
|
||||
* @author Matthew D. Hicks
|
||||
*/
|
||||
public class NanoTimer extends Timer {
|
||||
|
||||
private static final long TIMER_RESOLUTION = 1000000000L;
|
||||
private static final float INVERSE_TIMER_RESOLUTION = 1f / TIMER_RESOLUTION;
|
||||
|
||||
private long startTime;
|
||||
private long previousTime;
|
||||
private float tpf;
|
||||
private float fps;
|
||||
private long currentTime;
|
||||
|
||||
public NanoTimer() {
|
||||
startTime = System.nanoTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time in seconds. The timer starts at 0.0 seconds.
|
||||
*
|
||||
* @return the current time in seconds
|
||||
*/
|
||||
|
||||
protected long getTimeInternal() {
|
||||
return System.nanoTime() - startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getTimeInSeconds() {
|
||||
return getTime() * INVERSE_TIMER_RESOLUTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTime() {
|
||||
return currentTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getResolution() {
|
||||
return TIMER_RESOLUTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFrameRate() {
|
||||
return fps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getTimePerFrame() {
|
||||
return tpf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
currentTime = getTimeInternal();
|
||||
tpf = (currentTime - previousTime) * (1.0f / TIMER_RESOLUTION);
|
||||
fps = 1.0f / tpf;
|
||||
previousTime = getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
startTime = System.nanoTime();
|
||||
currentTime = getTimeInternal();
|
||||
previousTime = getTime();
|
||||
}
|
||||
}
|
||||
93
src/main/java/com/jme3/system/Timer.java
Normal file
93
src/main/java/com/jme3/system/Timer.java
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.system;
|
||||
|
||||
/**
|
||||
* <code>Timer</code> is the base class for a high resolution timer. It is
|
||||
* created from getTimer("display system")
|
||||
*
|
||||
* @author Mark Powell
|
||||
* @version $Id: Timer.java,v 1.18 2007/03/09 10:19:34 rherlitz Exp $
|
||||
*/
|
||||
public abstract class Timer {
|
||||
|
||||
/**
|
||||
* Returns the current time in ticks. A tick is an arbitrary measure of time
|
||||
* defined by the timer implementation. The number of ticks per second is
|
||||
* given by <code>getResolution()</code>. The timer starts at 0 ticks.
|
||||
*
|
||||
* @return a long value representing the current time
|
||||
*/
|
||||
public abstract long getTime();
|
||||
|
||||
/**
|
||||
* Returns the time in seconds. The timer starts at 0.0 seconds.
|
||||
*
|
||||
* @return the current time in seconds
|
||||
*/
|
||||
public float getTimeInSeconds() {
|
||||
return getTime() / (float) getResolution();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resolution of the timer.
|
||||
*
|
||||
* @return the number of timer ticks per second
|
||||
*/
|
||||
public abstract long getResolution();
|
||||
|
||||
/**
|
||||
* Returns the "calls per second". If this is called every frame, then it
|
||||
* will return the "frames per second".
|
||||
*
|
||||
* @return The "calls per second".
|
||||
*/
|
||||
public abstract float getFrameRate();
|
||||
|
||||
/**
|
||||
* Returns the time, in seconds, between the last call and the current one.
|
||||
*
|
||||
* @return Time between this call and the last one.
|
||||
*/
|
||||
public abstract float getTimePerFrame();
|
||||
|
||||
/**
|
||||
* <code>update</code> recalculates the frame rate based on the previous
|
||||
* call to update. It is assumed that update is called each frame.
|
||||
*/
|
||||
public abstract void update();
|
||||
|
||||
/**
|
||||
* Reset the timer to 0. Clear any tpf history.
|
||||
*/
|
||||
public abstract void reset();
|
||||
}
|
||||
@@ -19,7 +19,6 @@ public class Main {
|
||||
|
||||
public static VRServer vrServer;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void main(String[] args) {
|
||||
System.setProperty("awt.useSystemAAFontSettings", "on");
|
||||
System.setProperty("swing.aatext", "true");
|
||||
|
||||
672
src/main/java/io/eiren/math/FloatMath.java
Normal file
672
src/main/java/io/eiren/math/FloatMath.java
Normal file
@@ -0,0 +1,672 @@
|
||||
package io.eiren.math;
|
||||
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.math.Vector4f;
|
||||
|
||||
|
||||
public class FloatMath {
|
||||
|
||||
public static final float PI = (float) Math.PI;
|
||||
public static final float TWO_PI = (float) (Math.PI * 2);
|
||||
public static final float ANGLE_EPSILON = 0.028f; // in degrees (float
|
||||
// epsilon for sin/cos)
|
||||
public static final float ANGLE_EPSILON_RAD = toRad(ANGLE_EPSILON);
|
||||
|
||||
public static final float ZERO_TOLERANCE_F = FastMath.ZERO_TOLERANCE;
|
||||
public static final double ZERO_TOLERANCE_D = 0.0001d;
|
||||
|
||||
public static final float SQRT_TWO = (float) Math.sqrt(2f);
|
||||
public static final float INV_SQRT_TWO = 1f / SQRT_TWO;
|
||||
public static final float SQRT_THREE = (float) Math.sqrt(3f);
|
||||
public static final float INV_SQRT_THREE = 1f / SQRT_THREE;
|
||||
public static final float TWO_FPI = PI * 2;
|
||||
|
||||
public static final float SIN_75_DEG = 0.965926f;
|
||||
public static final float SIN_60_DEG = 0.866025f;
|
||||
public static final float SIN_45_DEG = 0.707107f;
|
||||
public static final float SIN_30_DEG = 0.5f;
|
||||
public static final float SIN_15_DEG = 0.258819f;
|
||||
|
||||
public static final float COS_75_DEG = 0.258819f;
|
||||
public static final float COS_60_DEG = 0.5f;
|
||||
public static final float COS_45_DEG = 0.707107f;
|
||||
public static final float COS_30_DEG = 0.866025f;
|
||||
public static final float COS_15_DEG = 0.965926f;
|
||||
|
||||
public static final int TEN_BITS = ~(~0 << 10);
|
||||
public static final int TENTH_BIT = 1 << 10;
|
||||
public static final int TEN_BITS_MAX = ~(~0 << 9);
|
||||
public static final int TEN_BITS_MAX_UNSIGNED = ~(~0 << 10);
|
||||
public static final int TWO_BITS = ~(~0 << 2);
|
||||
public static final int SECOND_BIT = 1 << 2;
|
||||
public static final int TWO_BITS_MAX = ~(~0 << 1);
|
||||
public static final int TWO_BITS_MAX_UNSIGNED = ~(~0 << 2);
|
||||
|
||||
public static float roundIfZero(float x) {
|
||||
return Math.abs(x) < ZERO_TOLERANCE_F ? 0.0f : x;
|
||||
}
|
||||
|
||||
public static boolean equalsToZero(float x) {
|
||||
return Math.abs(x) < ZERO_TOLERANCE_F;
|
||||
}
|
||||
|
||||
public static boolean lessThanZero(float x) {
|
||||
return (x < -ZERO_TOLERANCE_F);
|
||||
}
|
||||
|
||||
public static boolean lessOrEqualsToZero(float x) {
|
||||
return (x < ZERO_TOLERANCE_F);
|
||||
}
|
||||
|
||||
public static boolean greaterThanZero(float x) {
|
||||
return (x > ZERO_TOLERANCE_F);
|
||||
}
|
||||
|
||||
public static boolean greaterOrEqualsToZero(float x) {
|
||||
return (x > -ZERO_TOLERANCE_F);
|
||||
}
|
||||
|
||||
public static boolean equalsToZero(float x, float epsilon) {
|
||||
return Math.abs(x) < epsilon;
|
||||
}
|
||||
|
||||
public static boolean equalsWithEpsilon(float x, float y) {
|
||||
return Math.abs(x - y) < ZERO_TOLERANCE_F;
|
||||
}
|
||||
|
||||
public static boolean equalsWithEpsilon(float x, float y, float epsilon) {
|
||||
return Math.abs(x - y) < epsilon;
|
||||
}
|
||||
|
||||
public static boolean lessWithEpsilon(float x, float y) {
|
||||
return (x < y - ZERO_TOLERANCE_F);
|
||||
}
|
||||
|
||||
public static boolean lessOrEqualsWithEpsilon(float x, float y) {
|
||||
return (x < y + ZERO_TOLERANCE_F);
|
||||
}
|
||||
|
||||
public static boolean lessWithEpsilon(float x, float y, float epsilon) {
|
||||
return (x < y - epsilon);
|
||||
}
|
||||
|
||||
public static boolean lessOrEqualsWithEpsilon(float x, float y, float epsilon) {
|
||||
return (x < y + epsilon);
|
||||
}
|
||||
|
||||
public static boolean greaterWithEpsilon(float x, float y) {
|
||||
return (x > y + ZERO_TOLERANCE_F);
|
||||
}
|
||||
|
||||
public static boolean greaterOrEqualsWithEpsilon(float x, float y) {
|
||||
return (x > y - ZERO_TOLERANCE_F);
|
||||
}
|
||||
|
||||
public static boolean greaterWithEpsilon(float x, float y, float epsilon) {
|
||||
return (x > y + epsilon);
|
||||
}
|
||||
|
||||
public static boolean greaterOrEqualsWithEpsilon(float x, float y, float epsilon) {
|
||||
return (x > y - epsilon);
|
||||
}
|
||||
|
||||
public static double roundIfZero(double x) {
|
||||
return Math.abs(x) < ZERO_TOLERANCE_D ? 0.0d : x;
|
||||
}
|
||||
|
||||
public static boolean equalsToZero(double x) {
|
||||
return Math.abs(x) < ZERO_TOLERANCE_D;
|
||||
}
|
||||
|
||||
public static boolean equalsWithEpsilon(double x, double y) {
|
||||
return Math.abs(x - y) < ZERO_TOLERANCE_D;
|
||||
}
|
||||
|
||||
public static boolean lessWithEpsilon(double x, double y) {
|
||||
return (x < y - ZERO_TOLERANCE_D);
|
||||
}
|
||||
|
||||
public static boolean lessOrEqualsWithEpsilon(double x, double y) {
|
||||
return (x < y + ZERO_TOLERANCE_D);
|
||||
}
|
||||
|
||||
public static boolean greaterWithEpsilon(double x, double y) {
|
||||
return (x > y + ZERO_TOLERANCE_D);
|
||||
}
|
||||
|
||||
public static boolean greaterOrEqualsWithEpsilon(double x, double y) {
|
||||
return (x > y - ZERO_TOLERANCE_D);
|
||||
}
|
||||
|
||||
public static float toDegrees(float angrad) {
|
||||
return angrad * 180.0f / PI;
|
||||
}
|
||||
|
||||
public static float toRad(float deg) {
|
||||
return deg / 180.0f * PI;
|
||||
}
|
||||
|
||||
public static boolean radEqual(float angle1, float angle2) {
|
||||
float diff = clampRad(angle1 - angle2);
|
||||
return Math.abs(diff) < ANGLE_EPSILON_RAD;
|
||||
}
|
||||
|
||||
public static boolean degreesEqual(float angle1, float angle2) {
|
||||
float diff = clampDegrees(angle1 - angle2);
|
||||
return Math.abs(diff) < ANGLE_EPSILON;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #normalizeRad(float)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static float clampRad(float angle) {
|
||||
return normalizeRad(angle);
|
||||
}
|
||||
|
||||
public static float normalizeRad(float angle) {
|
||||
return FastMath.normalize(angle, -FastMath.PI, FastMath.PI);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #normalizeDegrees(float)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static float clampDegrees(float angle) {
|
||||
return normalizeDegrees(angle);
|
||||
}
|
||||
|
||||
public static float normalizeDegrees(float angle) {
|
||||
return FastMath.normalize(angle, -180f, 180f);
|
||||
}
|
||||
|
||||
public static float animateEase(float t) {
|
||||
// Special case of Bezier interpolation (p0 = p1 = 0, p2 = p3 = 1)
|
||||
return (3.0f - 2.0f * t) * t * t;
|
||||
}
|
||||
|
||||
public static float animateEaseIn(float t) {
|
||||
return t * t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lineary remaps value from the source interval to the target interval.
|
||||
* <a href="https://en.wikipedia.org/wiki/Linear_interpolation">details</a>
|
||||
*/
|
||||
public static float mapValue(
|
||||
float value,
|
||||
float sourceStart,
|
||||
float sourceEnd,
|
||||
float targetStart,
|
||||
float targetEnd
|
||||
) {
|
||||
return targetStart
|
||||
+ (value - sourceStart) * (targetEnd - targetStart) / (sourceEnd - sourceStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamps the given value and remaps to the target interval.
|
||||
* <p>
|
||||
* Note the source interval values should be sorted.
|
||||
*/
|
||||
public static float mapValueWithClampBefore(
|
||||
float value,
|
||||
float sourceBottom,
|
||||
float sourceTop,
|
||||
float targetBottom,
|
||||
float targetTop
|
||||
) {
|
||||
return mapValue(
|
||||
clamp(value, sourceBottom, sourceTop),
|
||||
sourceBottom,
|
||||
sourceTop,
|
||||
targetBottom,
|
||||
targetTop
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remaps the given value to the target interval and clamps.
|
||||
* <p>
|
||||
* Note the target interval values should be sorted.
|
||||
*/
|
||||
public static float mapValueWithClampAfter(
|
||||
float value,
|
||||
float sourceBottom,
|
||||
float sourceTop,
|
||||
float targetBottom,
|
||||
float targetTop
|
||||
) {
|
||||
return clamp(
|
||||
mapValue(value, sourceBottom, sourceTop, targetBottom, targetTop),
|
||||
targetBottom,
|
||||
targetTop
|
||||
);
|
||||
}
|
||||
|
||||
public static float smoothstep(float edge0, float edge1, float x) {
|
||||
// Scale, bias and saturate x to 0..1 range
|
||||
x = FastMath.clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
|
||||
// Evaluate polynomial
|
||||
return x * x * (3f - 2f * x);
|
||||
}
|
||||
|
||||
public static float smootherstep(float edge0, float edge1, float x) {
|
||||
// Scale, and clamp x to 0..1 range
|
||||
x = FastMath.clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
|
||||
// Evaluate polynomial
|
||||
return x * x * x * (x * (x * 6f - 15f) + 10f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies linear contrast (with clamping).
|
||||
*
|
||||
* @param t - input value in range (0..1)
|
||||
* @param k - contrast factor in range (-1..1):
|
||||
* <ul>
|
||||
* <li>1.0 - maximal contrast</li>
|
||||
* <li><b>0.0</b> - bypass (returns input value)</li>
|
||||
* <li>-1.0 - minimal contrast (returns 0.5f for any input)</li>
|
||||
* </ul>
|
||||
* @return contrasted value in range (0..1)
|
||||
*/
|
||||
public static float contrastLinear(float t, float k) {
|
||||
float x = 2f * t - 1f; // -1..1
|
||||
float gamma = (1f + k) / (1f - k);
|
||||
float f = FastMath.clamp(gamma * x, -1f, 1f); // -1..1
|
||||
return 0.5f * (f + 1f); // 0..1
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies non-linear contrast by power function.
|
||||
*
|
||||
* @param t - input value in range (0..1)
|
||||
* @param k - contrast factor in range (-1..1) exclusive:
|
||||
* <ul>
|
||||
* <li>0.999 - maximal contrast</li>
|
||||
* <li>0.0 - bypass (returns input value)</li>
|
||||
* <li>-0.999 - minimal contrast</li>
|
||||
* </ul>
|
||||
* @return contrasted value in range (0..1)
|
||||
*/
|
||||
public static float contrastPower(float t, float k) {
|
||||
float x = 2f * t - 1f; // -1..1
|
||||
float gamma = (1f - k) / (1f + k);
|
||||
float f = FastMath.sign(x) * FastMath.pow(FastMath.abs(x), gamma); // -1..1
|
||||
return 0.5f * (f + 1f); // 0..1
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies non-linear contrast by square splines.
|
||||
*
|
||||
* @param t - input value in range (0..1)
|
||||
* @param k - contrast factor in range (-1..1):
|
||||
* <ul>
|
||||
* <li>1.0 - maximal contrast</li>
|
||||
* <li>0.0 - bypass (returns input value)</li>
|
||||
* <li>-1.0 - minimal contrast</li>
|
||||
* </ul>
|
||||
* @return contrasted value in range (0..1)
|
||||
*/
|
||||
public static float contrastQuadricSpline(float t, float k) {
|
||||
float x = 2f * t - 1f; // -1..1
|
||||
float f = x * (1f + k * (1f - FastMath.abs(x))); // -1..1
|
||||
return 0.5f * (f + 1f); // 0..1
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies non-linear contrast by square splines inverted function.
|
||||
*
|
||||
* @param t - input value in range (0..1)
|
||||
* @param k - contrast factor in range (-2..2):
|
||||
* <ul>
|
||||
* <li>2.0 - maximal contrast</li>
|
||||
* <li>0.0 - bypass (returns input value)</li>
|
||||
* <li>-2.0 - minimal contrast</li>
|
||||
* </ul>
|
||||
* @return contrasted value in range (0..1)
|
||||
*/
|
||||
public static float contrastInvertQuadricSpline(float t, float k) {
|
||||
float x = 2f * t - 1f; // -1..1
|
||||
float g;
|
||||
if (k > 0) {
|
||||
g = FastMath.sign(x) * FastMath.sqrt(FastMath.abs(x)) - 2f * x;
|
||||
} else {
|
||||
g = FastMath.sign(x) * (FastMath.sqrt(1f - FastMath.abs(x)) - 1f);
|
||||
}
|
||||
float f = (1f + k) * x + k * g; // -1..1
|
||||
return 0.5f * (f + 1f); // 0..1
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies non-linear contrast by cubic splines.
|
||||
*
|
||||
* @param t - input value in range (0..1)
|
||||
* @param k - contrast factor in range (-1..1):
|
||||
* <ul>
|
||||
* <li>1.0 - maximal contrast</li>
|
||||
* <li>0.0 - bypass (returns input value)</li>
|
||||
* <li>-1.0 - minimal contrast</li>
|
||||
* </ul>
|
||||
* @return contrasted value in range (0..1)
|
||||
*/
|
||||
public static float contrastCubicSpline(float t, float k) {
|
||||
float x = 2f * t - 1f; // -1..1
|
||||
float f = x * (1f + FastMath.abs(k) * (x * x - 1f));
|
||||
if (k < 0)
|
||||
f -= x * 3f * k * (1f - FastMath.abs(x));
|
||||
return 0.5f * (f + 1f); // 0..1
|
||||
}
|
||||
|
||||
public static float fraction(float f) {
|
||||
return f - (int) f;
|
||||
}
|
||||
|
||||
public static double fraction(double d) {
|
||||
return d - (long) d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Do not copy {@link Math} methods.
|
||||
*/
|
||||
@Deprecated
|
||||
public static float min(float a, float b) {
|
||||
return a > b ? b : a;
|
||||
}
|
||||
|
||||
public static float min(float a, float b, float c) {
|
||||
return Math.min(Math.min(a, b), c);
|
||||
}
|
||||
|
||||
public static float min(float a, float b, float c, float d) {
|
||||
return Math.min(Math.min(a, b), Math.min(c, d));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Do not copy {@link Math} methods.
|
||||
*/
|
||||
@Deprecated
|
||||
public static float max(float a, float b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
public static float max(float a, float b, float c) {
|
||||
return Math.max(Math.max(a, b), c);
|
||||
}
|
||||
|
||||
public static float max(float a, float b, float c, float d) {
|
||||
return Math.max(Math.max(a, b), Math.max(c, d));
|
||||
}
|
||||
|
||||
public static float cos(float value) {
|
||||
return (float) Math.cos(value);
|
||||
}
|
||||
|
||||
public static float sin(float value) {
|
||||
return (float) Math.sin(value);
|
||||
}
|
||||
|
||||
public static float ceil(float value) {
|
||||
return (float) Math.ceil(value);
|
||||
}
|
||||
|
||||
public static float floor(float value) {
|
||||
return (float) Math.floor(value);
|
||||
}
|
||||
|
||||
public static float pow(float value, float power) {
|
||||
return (float) Math.pow(value, power);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Do not copy {@link Math} methods.
|
||||
*/
|
||||
@Deprecated
|
||||
public static float abs(float value) {
|
||||
return (float) Math.abs(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Do not copy {@link Math} methods.
|
||||
*/
|
||||
@Deprecated
|
||||
public static float round(float value) {
|
||||
return (float) Math.round(value);
|
||||
}
|
||||
|
||||
public static float sqrt(float value) {
|
||||
return (float) Math.sqrt(value);
|
||||
}
|
||||
|
||||
public static float distance(float x0, float y0, float z0, float x1, float y1, float z1) {
|
||||
return distance(x1 - x0, y1 - y0, z1 - z0);
|
||||
}
|
||||
|
||||
public static float distance(float x, float y, float z) {
|
||||
return sqrt(sqrDistance(x, y, z));
|
||||
}
|
||||
|
||||
public static float sqrDistance(float x, float y, float z) {
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
public static float distance(float x, float y) {
|
||||
return sqrt(sqrDistance(x, y));
|
||||
}
|
||||
|
||||
public static float sqrDistance(float x, float y) {
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
public static float sqrDistance(Vector3f v, float x1, float y1, float z1) {
|
||||
return sqrDistance(x1 - v.x, y1 - v.y, z1 - v.z);
|
||||
}
|
||||
|
||||
public static float sqrDistance(float x0, float y0, float z0, float x1, float y1, float z1) {
|
||||
return sqrDistance(x1 - x0, y1 - y0, z1 - z0);
|
||||
}
|
||||
|
||||
public static float hypot(float x, float y) {
|
||||
return FastMath.sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
public static float hypot(float x, float y, float z) {
|
||||
return FastMath.sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as FastMath.clamp
|
||||
*/
|
||||
public static float clamp(float value, float min, float max) {
|
||||
if (value <= min)
|
||||
return min;
|
||||
if (value >= max)
|
||||
return max;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Vector3f int2101010RevToFloats(int packedValue, Vector3f store) {
|
||||
if (store == null)
|
||||
store = new Vector3f();
|
||||
store.x = packedValue & TEN_BITS_MAX;
|
||||
if ((packedValue & TENTH_BIT) != 0)
|
||||
store.x *= -1;
|
||||
store.y = (packedValue >>> 10) & TEN_BITS_MAX;
|
||||
if ((packedValue & (TENTH_BIT << 10)) != 0)
|
||||
store.y *= -1;
|
||||
store.z = (packedValue >>> 20) & TEN_BITS_MAX;
|
||||
if ((packedValue & (TENTH_BIT << 20)) != 0)
|
||||
store.z *= -1;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static int floatToInt210101Rev(Vector3f values) {
|
||||
int store = 0;
|
||||
store |= ((int) values.x) & TEN_BITS_MAX;
|
||||
if (values.x < 0)
|
||||
store |= TENTH_BIT;
|
||||
store |= (((int) values.y) & TEN_BITS_MAX) << 10;
|
||||
if (values.y < 0)
|
||||
store |= TENTH_BIT << 10;
|
||||
store |= (((int) values.z) & TEN_BITS_MAX) << 20;
|
||||
if (values.z < 0)
|
||||
store |= TENTH_BIT << 20;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static int floatToInt210101RevNormalized(Vector3f values) {
|
||||
int store = 0;
|
||||
store |= ((int) (values.x * TEN_BITS)) & TEN_BITS_MAX;
|
||||
if (values.x < 0)
|
||||
store |= TENTH_BIT;
|
||||
store |= (((int) (values.y * TEN_BITS)) & TEN_BITS_MAX) << 10;
|
||||
if (values.y < 0)
|
||||
store |= TENTH_BIT << 10;
|
||||
store |= (((int) (values.z * TEN_BITS)) & TEN_BITS_MAX) << 20;
|
||||
if (values.z < 0)
|
||||
store |= TENTH_BIT << 20;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static int floatToUnsignedInt210101Rev(Vector3f values) {
|
||||
int store = 0;
|
||||
store |= ((int) values.x) & TEN_BITS;
|
||||
store |= (((int) values.y) & TEN_BITS) << 10;
|
||||
store |= (((int) values.z) & TEN_BITS) << 20;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static int floatToUnsignedInt210101RevNormalized(Vector3f values) {
|
||||
int store = 0;
|
||||
store |= ((int) (values.x * TEN_BITS)) & TEN_BITS;
|
||||
store |= (((int) (values.y * TEN_BITS)) & TEN_BITS) << 10;
|
||||
store |= (((int) (values.z * TEN_BITS)) & TEN_BITS) << 20;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static int floatToInt210101Rev(float x, float y, float z) {
|
||||
int store = 0;
|
||||
store |= ((int) x) & TEN_BITS_MAX;
|
||||
if (x < 0)
|
||||
store |= TENTH_BIT;
|
||||
store |= (((int) y) & TEN_BITS_MAX) << 10;
|
||||
if (y < 0)
|
||||
store |= TENTH_BIT << 10;
|
||||
store |= (((int) z) & TEN_BITS_MAX) << 20;
|
||||
if (z < 0)
|
||||
store |= TENTH_BIT << 20;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static int floatToUnsignedInt210101Rev(float x, float y, float z) {
|
||||
int store = 0;
|
||||
store |= ((int) x) & TEN_BITS;
|
||||
store |= (((int) y) & TEN_BITS) << 10;
|
||||
store |= (((int) z) & TEN_BITS) << 20;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static Vector4f int2101010RevToFloats(int packedValue, Vector4f store) {
|
||||
if (store == null)
|
||||
store = new Vector4f();
|
||||
store.x = packedValue & TEN_BITS_MAX;
|
||||
if ((packedValue & TENTH_BIT) != 0)
|
||||
store.x *= -1;
|
||||
store.y = (packedValue >>> 10) & TEN_BITS_MAX;
|
||||
if ((packedValue & (TENTH_BIT << 10)) != 0)
|
||||
store.y *= -1;
|
||||
store.z = (packedValue >>> 20) & TEN_BITS_MAX;
|
||||
if ((packedValue & (TENTH_BIT << 20)) != 0)
|
||||
store.z *= -1;
|
||||
store.w = (packedValue >>> 30) & TWO_BITS_MAX;
|
||||
if ((packedValue & (SECOND_BIT << 30)) != 0)
|
||||
store.w *= -1;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static int floatToInt210101Rev(Vector4f values) {
|
||||
int store = 0;
|
||||
store |= ((int) values.x) & TEN_BITS_MAX;
|
||||
if (values.x < 0)
|
||||
store |= TENTH_BIT;
|
||||
store |= (((int) values.y) & TEN_BITS_MAX) << 10;
|
||||
if (values.y < 0)
|
||||
store |= TENTH_BIT << 10;
|
||||
store |= (((int) values.z) & TEN_BITS_MAX) << 20;
|
||||
if (values.z < 0)
|
||||
store |= TENTH_BIT << 20;
|
||||
store |= (((int) values.z) & TWO_BITS_MAX) << 30;
|
||||
if (values.w < 0)
|
||||
store |= SECOND_BIT << 30;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static int floatToUnsignedInt210101Rev(Vector4f values) {
|
||||
int store = 0;
|
||||
store |= ((int) values.x) & TEN_BITS;
|
||||
store |= (((int) values.y) & TEN_BITS) << 10;
|
||||
store |= (((int) values.z) & TEN_BITS) << 20;
|
||||
store |= (((int) values.z) & TWO_BITS) << 30;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static Vector3f unsignedInt2101010RevToFloats(int packedValue, Vector3f store) {
|
||||
if (store == null)
|
||||
store = new Vector3f();
|
||||
store.x = packedValue & TEN_BITS;
|
||||
store.y = (packedValue >>> 10) & TEN_BITS;
|
||||
store.z = (packedValue >>> 20) & TEN_BITS;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static Vector4f unsignedInt2101010RevToFloats(int packedValue, Vector4f store) {
|
||||
if (store == null)
|
||||
store = new Vector4f();
|
||||
store.x = packedValue & TEN_BITS;
|
||||
store.y = (packedValue >>> 10) & TEN_BITS;
|
||||
store.z = (packedValue >>> 20) & TEN_BITS;
|
||||
store.w = (packedValue >>> 30) & TWO_BITS;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static Vector3f int2101010RevNormalizedToFloats(int packedValue, Vector3f store) {
|
||||
store = int2101010RevToFloats(packedValue, store);
|
||||
store.x /= TEN_BITS_MAX;
|
||||
store.y /= TEN_BITS_MAX;
|
||||
store.z /= TEN_BITS_MAX;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static Vector4f int2101010RevNormalizedToFloats(int packedValue, Vector4f store) {
|
||||
store = int2101010RevToFloats(packedValue, store);
|
||||
store.x /= TEN_BITS_MAX;
|
||||
store.y /= TEN_BITS_MAX;
|
||||
store.z /= TEN_BITS_MAX;
|
||||
store.w /= TWO_BITS_MAX;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static Vector3f unsignedInt2101010RevNormalizedToFloats(
|
||||
int packedValue,
|
||||
Vector3f store
|
||||
) {
|
||||
store = unsignedInt2101010RevToFloats(packedValue, store);
|
||||
store.x /= TEN_BITS;
|
||||
store.y /= TEN_BITS;
|
||||
store.z /= TEN_BITS;
|
||||
return store;
|
||||
}
|
||||
|
||||
public static Vector4f unsignedInt2101010RevNormalizedToFloats(
|
||||
int packedValue,
|
||||
Vector4f store
|
||||
) {
|
||||
store = unsignedInt2101010RevToFloats(packedValue, store);
|
||||
store.x /= TEN_BITS;
|
||||
store.y /= TEN_BITS;
|
||||
store.z /= TEN_BITS;
|
||||
store.w /= TWO_BITS;
|
||||
return store;
|
||||
}
|
||||
}
|
||||
257
src/main/java/io/eiren/math/Vector3d.java
Normal file
257
src/main/java/io/eiren/math/Vector3d.java
Normal file
@@ -0,0 +1,257 @@
|
||||
package io.eiren.math;
|
||||
|
||||
import com.jme3.math.Vector3f;
|
||||
|
||||
|
||||
public class Vector3d implements Cloneable {
|
||||
|
||||
public double x;
|
||||
public double y;
|
||||
public double z;
|
||||
|
||||
public Vector3d() {
|
||||
}
|
||||
|
||||
public Vector3d(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public Vector3d(double x1, double y1, double z1, double x2, double y2, double z2) {
|
||||
this.x = x2 - x1;
|
||||
this.y = y2 - y1;
|
||||
this.z = z2 - z1;
|
||||
}
|
||||
|
||||
public Vector3d(Vector3f src) {
|
||||
this(src.x, src.y, src.z);
|
||||
}
|
||||
|
||||
public Vector3d set(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3d set(Vector3d v) {
|
||||
this.x = v.x;
|
||||
this.y = v.y;
|
||||
this.z = v.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3d add(double addX, double addY, double addZ) {
|
||||
return new Vector3d(this.x + addX, this.y + addY, this.z + addZ);
|
||||
}
|
||||
|
||||
public Vector3d addLocal(Vector3d vec) {
|
||||
return addLocal(vec.x, vec.y, vec.z);
|
||||
}
|
||||
|
||||
public Vector3d addLocal(double addX, double addY, double addZ) {
|
||||
x += addX;
|
||||
y += addY;
|
||||
z += addZ;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3d substract(double subX, double subY, double subZ) {
|
||||
return new Vector3d(this.x - subX, this.y - subY, this.z - subZ);
|
||||
}
|
||||
|
||||
public Vector3d substractLocal(Vector3d vec) {
|
||||
if (null == vec) {
|
||||
return null;
|
||||
}
|
||||
x -= vec.x;
|
||||
y -= vec.y;
|
||||
z -= vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3d substractLocal(double subX, double subY, double subZ) {
|
||||
x -= subX;
|
||||
y -= subY;
|
||||
z -= subZ;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3d negate() {
|
||||
return new Vector3d(-x, -y, -z);
|
||||
}
|
||||
|
||||
public Vector3d negateLocal() {
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3d mult(double scalar) {
|
||||
return new Vector3d(x * scalar, y * scalar, z * scalar);
|
||||
}
|
||||
|
||||
public Vector3d multLocal(double scalar) {
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
z *= scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3d divide(double scalar) {
|
||||
return new Vector3d(x / scalar, y / scalar, z / scalar);
|
||||
}
|
||||
|
||||
public Vector3d divideLocal(double scalar) {
|
||||
x /= scalar;
|
||||
y /= scalar;
|
||||
z /= scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double dot(Vector3d v) {
|
||||
return x * v.x + y * v.y + z * v.z;
|
||||
}
|
||||
|
||||
public double dot(double vx, double vy, double vz) {
|
||||
return x * vx + y * vy + z * vz;
|
||||
}
|
||||
|
||||
public Vector3d cross(Vector3d other, Vector3d result) {
|
||||
if (result == null)
|
||||
result = new Vector3d();
|
||||
double resX = ((y * other.z) - (z * other.y));
|
||||
double resY = ((z * other.x) - (x * other.z));
|
||||
double resZ = ((x * other.y) - (y * other.x));
|
||||
result.set(resX, resY, resZ);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3d clone() {
|
||||
return new Vector3d(this.x, this.y, this.z);
|
||||
}
|
||||
|
||||
public Vector3d normalize() {
|
||||
double length = x * x + y * y + z * z;
|
||||
if (length != 1.0 && length != 0.0) {
|
||||
double invLength = 1.0 / Math.sqrt(length);
|
||||
return mult(invLength);
|
||||
}
|
||||
return clone();
|
||||
}
|
||||
|
||||
public Vector3d normalizeLocal() {
|
||||
double length = x * x + y * y + z * z;
|
||||
if (length != 1.0 && length != 0.0) {
|
||||
length = Math.sqrt(length);
|
||||
double invLength = 1.0 / length;
|
||||
x *= invLength;
|
||||
z *= invLength;
|
||||
y *= invLength;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3f toVector3f() {
|
||||
return new Vector3f((float) x, (float) y, (float) z);
|
||||
}
|
||||
|
||||
public double length() {
|
||||
return Math.sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
public double lengthSquared() {
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder("Vector3D{")
|
||||
.append(x)
|
||||
.append(',')
|
||||
.append(y)
|
||||
.append(',')
|
||||
.append(z)
|
||||
.append('}')
|
||||
.toString();
|
||||
}
|
||||
|
||||
public void rotateAroundX(float f) {
|
||||
double f1 = Math.cos(f);
|
||||
double f2 = Math.sin(f);
|
||||
double d = x;
|
||||
double d1 = y * f1 + z * f2;
|
||||
double d2 = z * f1 - y * f2;
|
||||
x = (float) d;
|
||||
y = (float) d1;
|
||||
z = (float) d2;
|
||||
}
|
||||
|
||||
public void rotateAroundY(float f) {
|
||||
double f1 = Math.cos(f);
|
||||
double f2 = Math.sin(f);
|
||||
double d = x * f1 + z * f2;
|
||||
double d1 = y;
|
||||
double d2 = z * f1 - x * f2;
|
||||
x = (float) d;
|
||||
y = (float) d1;
|
||||
z = (float) d2;
|
||||
}
|
||||
|
||||
public double distanceTo(Vector3d vec3d) {
|
||||
return Math.sqrt(squaredDistance(vec3d));
|
||||
}
|
||||
|
||||
public double squaredDistance(Vector3d point) {
|
||||
return squaredDistance(point.x, point.y, point.z);
|
||||
}
|
||||
|
||||
public double squaredDistance(double toX, double toY, double toZ) {
|
||||
return (this.x - toX) * (this.x - toX)
|
||||
+ (this.y - toY) * (this.y - toY)
|
||||
+ (this.z - toZ) * (this.z - toZ);
|
||||
}
|
||||
|
||||
public Vector3d add(Vector3d dir) {
|
||||
return add(dir.x, dir.y, dir.z);
|
||||
}
|
||||
|
||||
public Vector3d substract(Vector3d dir) {
|
||||
return substract(dir.x, dir.y, dir.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(x);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(y);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(z);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Vector3d other = (Vector3d) obj;
|
||||
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
|
||||
return false;
|
||||
if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
|
||||
return false;
|
||||
if (Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
130
src/main/java/io/eiren/util/BufferedTimer.java
Normal file
130
src/main/java/io/eiren/util/BufferedTimer.java
Normal file
@@ -0,0 +1,130 @@
|
||||
package io.eiren.util;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
|
||||
import com.jme3.system.NanoTimer;
|
||||
|
||||
|
||||
/**
|
||||
* This timer accumulate measured TPF and returns average/min/max FPS value
|
||||
*/
|
||||
public class BufferedTimer extends NanoTimer {
|
||||
|
||||
private final float measureInterval;
|
||||
private float averageTpf;
|
||||
private float averageFps;
|
||||
private float averageFrameRenderTime;
|
||||
private float sumFrameRenderTime;
|
||||
private float sumTpf;
|
||||
private float minFpsCurrent;
|
||||
private float maxFpsCurrent;
|
||||
private float maxFps;
|
||||
private float minFps;
|
||||
private int count;
|
||||
private boolean measured = false;
|
||||
|
||||
/**
|
||||
* Measure average tpf over the provided inverval in seconds
|
||||
*
|
||||
* @param measureInterval interval to measure averages over
|
||||
*/
|
||||
public BufferedTimer(float measureInterval) {
|
||||
averageFps = 0;
|
||||
sumTpf = 0;
|
||||
count = 0;
|
||||
this.measureInterval = measureInterval;
|
||||
}
|
||||
|
||||
public float getAverageFPS() {
|
||||
return averageFps;
|
||||
}
|
||||
|
||||
public float getMinFPS() {
|
||||
return minFps;
|
||||
}
|
||||
|
||||
public float getMaxFPS() {
|
||||
return maxFps;
|
||||
}
|
||||
|
||||
public void addRenderTime(float renderTime) {
|
||||
sumFrameRenderTime += renderTime;
|
||||
}
|
||||
|
||||
public float getAverageFrameRenderTime() {
|
||||
return averageFrameRenderTime;
|
||||
}
|
||||
|
||||
public boolean isMeasured() {
|
||||
if (measured) {
|
||||
measured = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public TimerSample getCurrentData() {
|
||||
return new TimerSample(getFrameRate(), minFps, maxFps, averageFps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
super.update();
|
||||
// Accumulate instant rate
|
||||
sumTpf += getTimePerFrame();
|
||||
float fps = getFrameRate();
|
||||
if (fps < minFpsCurrent)
|
||||
minFpsCurrent = fps;
|
||||
if (fps > maxFpsCurrent)
|
||||
maxFpsCurrent = fps;
|
||||
++count;
|
||||
// Calculate results once per measure interval
|
||||
if (!measured || sumTpf > measureInterval) {
|
||||
// Average results
|
||||
averageTpf = sumTpf / count;
|
||||
averageFps = 1.0f / averageTpf;
|
||||
averageFrameRenderTime = sumFrameRenderTime / count;
|
||||
minFps = minFpsCurrent;
|
||||
maxFps = maxFpsCurrent;
|
||||
// Reset counter
|
||||
sumTpf = 0;
|
||||
sumFrameRenderTime = 0;
|
||||
minFpsCurrent = Float.MAX_VALUE;
|
||||
maxFpsCurrent = 0;
|
||||
count = 0;
|
||||
measured = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TimerSample {
|
||||
|
||||
public float fps;
|
||||
public float minFps;
|
||||
public float maxFps;
|
||||
public float averageFps;
|
||||
|
||||
@ConstructorProperties({ "fps", "minFps", "maxFps", "averageFps" })
|
||||
public TimerSample(float fps, float minFps, float maxFps, float averageFps) {
|
||||
this.fps = fps;
|
||||
this.minFps = minFps;
|
||||
this.maxFps = maxFps;
|
||||
this.averageFps = averageFps;
|
||||
}
|
||||
|
||||
public float getFps() {
|
||||
return fps;
|
||||
}
|
||||
|
||||
public float getMinFps() {
|
||||
return minFps;
|
||||
}
|
||||
|
||||
public float getMaxFps() {
|
||||
return maxFps;
|
||||
}
|
||||
|
||||
public float getAverageFps() {
|
||||
return averageFps;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/main/java/io/eiren/util/MacOSX.java
Normal file
41
src/main/java/io/eiren/util/MacOSX.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package io.eiren.util;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.Toolkit;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class MacOSX {
|
||||
|
||||
public static void setIcons(List<? extends Image> icons) {
|
||||
try {
|
||||
Class<?> applicationClass = Class.forName("com.apple.eawt.Application");
|
||||
Method m = applicationClass.getDeclaredMethod("getApplication");
|
||||
Object application = m.invoke(null);
|
||||
m = application.getClass().getDeclaredMethod("setDockIconImage", Image.class);
|
||||
m.invoke(application, icons.get(icons.size() - 1));
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
public static void setTitle(String title) {
|
||||
try {
|
||||
Class<?> applicationClass = Class.forName("com.apple.eawt.Application");
|
||||
Method m = applicationClass.getDeclaredMethod("getApplication");
|
||||
Object application = m.invoke(null);
|
||||
m = application.getClass().getDeclaredMethod("setDockIconImage", String.class);
|
||||
m.invoke(application, title);
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
public static boolean hasRetinaDisplay() {
|
||||
Object obj = Toolkit.getDefaultToolkit().getDesktopProperty("apple.awt.contentScaleFactor");
|
||||
if (obj instanceof Float) {
|
||||
Float f = (Float) obj;
|
||||
int scale = f.intValue();
|
||||
return (scale == 2); // 1 indicates a regular mac display.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
47
src/main/java/io/eiren/util/OperatingSystem.java
Normal file
47
src/main/java/io/eiren/util/OperatingSystem.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package io.eiren.util;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
public enum OperatingSystem {
|
||||
|
||||
//@formatter:off
|
||||
LINUX("linux", new String[]{"linux", "unix"}),
|
||||
WINDOWS("windows", new String[]{"win"}),
|
||||
OSX("osx", new String[]{"mac"}),
|
||||
UNKNOWN("unknown", new String[0]);
|
||||
//@fomatter: on
|
||||
|
||||
private final String[] aliases;
|
||||
public final String name;
|
||||
private static OperatingSystem currentPlatform;
|
||||
|
||||
private OperatingSystem(String name, String[] aliases) {
|
||||
this.aliases = aliases;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static String getJavaExecutable(boolean forceConsole) {
|
||||
String separator = System.getProperty("file.separator");
|
||||
String path = System.getProperty("java.home") + separator + "bin" + separator;
|
||||
if(getCurrentPlatform() == WINDOWS) {
|
||||
if(!forceConsole && new File(path + "javaw.exe").isFile())
|
||||
return path + "javaw.exe";
|
||||
return path + "java.exe";
|
||||
}
|
||||
return path + "java";
|
||||
}
|
||||
|
||||
public static OperatingSystem getCurrentPlatform() {
|
||||
if(currentPlatform != null)
|
||||
return currentPlatform;
|
||||
String osName = System.getProperty("os.name").toLowerCase();
|
||||
for(OperatingSystem os : values()) {
|
||||
for(String alias : os.aliases) {
|
||||
if(osName.contains(alias))
|
||||
return currentPlatform = os;
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
35
src/main/java/io/eiren/util/StringUtils.java
Normal file
35
src/main/java/io/eiren/util/StringUtils.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package io.eiren.util;
|
||||
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
public class StringUtils {
|
||||
|
||||
private static char DECIMAL_SEP;
|
||||
|
||||
public static char getDecimalSeparator() {
|
||||
if (DECIMAL_SEP == '\u0000') {
|
||||
final Locale l = Locale.getDefault(Locale.Category.FORMAT);
|
||||
// Formatter.java always use "." in the Locale.US
|
||||
DECIMAL_SEP = (l == null || l.equals(Locale.US)
|
||||
? '.'
|
||||
: DecimalFormatSymbols.getInstance(l).getDecimalSeparator());
|
||||
}
|
||||
return DECIMAL_SEP;
|
||||
}
|
||||
|
||||
public static String prettyNumber(float f) {
|
||||
return prettyNumber(f, 4);
|
||||
}
|
||||
|
||||
public static String prettyNumber(float f, int numDigits) {
|
||||
String str = String.format("%." + numDigits + "f", f);
|
||||
if (numDigits != 0)
|
||||
str = org.apache.commons.lang3.StringUtils.stripEnd(str, "0");
|
||||
char lastChar = str.charAt(str.length() - 1);
|
||||
if (lastChar == getDecimalSeparator())
|
||||
str = str.substring(0, str.length() - 1);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
176
src/main/java/io/eiren/util/Util.java
Normal file
176
src/main/java/io/eiren/util/Util.java
Normal file
@@ -0,0 +1,176 @@
|
||||
package io.eiren.util;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
||||
public class Util {
|
||||
|
||||
public static void close(Object r) {
|
||||
try {
|
||||
if (r != null) {
|
||||
if (r instanceof Closeable)
|
||||
((Closeable) r).close();
|
||||
else if (r instanceof AutoCloseable)
|
||||
((AutoCloseable) r).close();
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
public static void close(Object r1, Object r2) {
|
||||
close(r1);
|
||||
close(r2);
|
||||
}
|
||||
|
||||
public static void close(Object... r) {
|
||||
for (int i = 0; i < r.length; ++i)
|
||||
try {
|
||||
if (r[i] != null) {
|
||||
if (r[i] instanceof Closeable)
|
||||
((Closeable) r[i]).close();
|
||||
else if (r[i] instanceof AutoCloseable)
|
||||
((AutoCloseable) r[i]).close();
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
public static void close(AutoCloseable... r) {
|
||||
for (int i = 0; i < r.length; ++i)
|
||||
try {
|
||||
if (r[i] != null)
|
||||
r[i].close();
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
public static void close(Closeable... r) {
|
||||
for (int i = 0; i < r.length; ++i)
|
||||
try {
|
||||
if (r[i] != null)
|
||||
r[i].close();
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Performs a deep toString of provided object. It shows content of arrays,
|
||||
* collections and maps (trove not supported yet).
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Highly ineffective, use only for debug.</b>
|
||||
* </p>
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
public static String toString(Object object) {
|
||||
if (object == null)
|
||||
return "null";
|
||||
StringBuilder buf = new StringBuilder();
|
||||
elementToString(object, buf, new HashSet<Object>());
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static void deepToString(Map<Object, Object> m, StringBuilder buf, Set<Object> dejaVu) {
|
||||
if (m == null) {
|
||||
buf.append("null");
|
||||
return;
|
||||
}
|
||||
if (m.size() == 0) {
|
||||
buf.append("{}");
|
||||
return;
|
||||
}
|
||||
dejaVu.add(m);
|
||||
buf.append('{');
|
||||
Iterator<Entry<Object, Object>> iterator = m.entrySet().iterator();
|
||||
boolean has = false;
|
||||
while (iterator.hasNext()) {
|
||||
if (has)
|
||||
buf.append(',');
|
||||
Entry<Object, Object> e = iterator.next();
|
||||
elementToString(e.getKey(), buf, dejaVu);
|
||||
buf.append(':');
|
||||
elementToString(e.getValue(), buf, dejaVu);
|
||||
has = true;
|
||||
}
|
||||
buf.append('}');
|
||||
dejaVu.remove(m);
|
||||
}
|
||||
|
||||
private static void deepToString(
|
||||
Collection<Object> list,
|
||||
StringBuilder buf,
|
||||
Set<Object> dejaVu
|
||||
) {
|
||||
Object[] array = list.toArray();
|
||||
deepToString(array, buf, dejaVu);
|
||||
}
|
||||
|
||||
private static void deepToString(Object[] a, StringBuilder buf, Set<Object> dejaVu) {
|
||||
if (a == null) {
|
||||
buf.append("null");
|
||||
return;
|
||||
}
|
||||
if (a.length == 0) {
|
||||
buf.append("[]");
|
||||
return;
|
||||
}
|
||||
dejaVu.add(a);
|
||||
buf.append('[');
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
if (i != 0)
|
||||
buf.append(',');
|
||||
Object element = a[i];
|
||||
elementToString(element, buf, dejaVu);
|
||||
}
|
||||
buf.append(']');
|
||||
dejaVu.remove(a);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void elementToString(Object element, StringBuilder buf, Set<Object> dejaVu) {
|
||||
if (element == null) {
|
||||
buf.append("null");
|
||||
} else {
|
||||
Class<?> eClass = element.getClass();
|
||||
if (eClass.isArray()) {
|
||||
if (eClass == byte[].class)
|
||||
buf.append(Arrays.toString((byte[]) element));
|
||||
else if (eClass == short[].class)
|
||||
buf.append(Arrays.toString((short[]) element));
|
||||
else if (eClass == int[].class)
|
||||
buf.append(Arrays.toString((int[]) element));
|
||||
else if (eClass == long[].class)
|
||||
buf.append(Arrays.toString((long[]) element));
|
||||
else if (eClass == char[].class)
|
||||
buf.append(Arrays.toString((char[]) element));
|
||||
else if (eClass == float[].class)
|
||||
buf.append(Arrays.toString((float[]) element));
|
||||
else if (eClass == double[].class)
|
||||
buf.append(Arrays.toString((double[]) element));
|
||||
else if (eClass == boolean[].class)
|
||||
buf.append(Arrays.toString((boolean[]) element));
|
||||
else { // element is an array of object references
|
||||
if (dejaVu.contains(element))
|
||||
buf.append("[...]");
|
||||
else
|
||||
deepToString((Object[]) element, buf, dejaVu);
|
||||
}
|
||||
} else { // element is non-null and not an array
|
||||
if (element instanceof Collection)
|
||||
deepToString((Collection<Object>) element, buf, dejaVu);
|
||||
else if (element instanceof Map)
|
||||
deepToString((Map<Object, Object>) element, buf, dejaVu);
|
||||
else if (element instanceof CharSequence)
|
||||
buf.append('"').append(element.toString()).append('"');
|
||||
else
|
||||
buf.append(element.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/main/java/io/eiren/util/ann/AWTThread.java
Normal file
10
src/main/java/io/eiren/util/ann/AWTThread.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package io.eiren.util.ann;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
|
||||
@Retention(value = RetentionPolicy.SOURCE)
|
||||
public @interface AWTThread {
|
||||
|
||||
}
|
||||
9
src/main/java/io/eiren/util/ann/DebugSwitch.java
Normal file
9
src/main/java/io/eiren/util/ann/DebugSwitch.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package io.eiren.util.ann;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD })
|
||||
public @interface DebugSwitch {
|
||||
}
|
||||
19
src/main/java/io/eiren/util/ann/NativeUnsafe.java
Normal file
19
src/main/java/io/eiren/util/ann/NativeUnsafe.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package io.eiren.util.ann;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/**
|
||||
* Marks methods and classes that use unsafe or direct access to memory. Proceed
|
||||
* with caution.
|
||||
*
|
||||
* @author Rena
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
public @interface NativeUnsafe {
|
||||
|
||||
}
|
||||
30
src/main/java/io/eiren/util/ann/Synchronize.java
Normal file
30
src/main/java/io/eiren/util/ann/Synchronize.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package io.eiren.util.ann;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Означает необходимость обязательной синхронизации этого меcта во внешних
|
||||
* методах. В аргументах передаётся название поля для синхронизации.
|
||||
* </p>
|
||||
* <p>
|
||||
* Методы, помеченные данной аннотацией могут вызывать только Thread-Safe
|
||||
* методы, либо методы, помеченные такой же аннотацией с тем же полем
|
||||
* синхронизации.
|
||||
* </p>
|
||||
* <p>
|
||||
* Поля, помеченные данной аннотацией должны быть синхронизированны на указанное
|
||||
* поле при чтении или записи.
|
||||
* </p>
|
||||
*
|
||||
* @see {@link ThreadSafe}, {@link ThreadSecure}, {@link ThreadSafeSingle}
|
||||
* @author Rena
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.SOURCE)
|
||||
public @interface Synchronize {
|
||||
|
||||
String[] value();
|
||||
|
||||
}
|
||||
27
src/main/java/io/eiren/util/ann/ThreadSafe.java
Normal file
27
src/main/java/io/eiren/util/ann/ThreadSafe.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package io.eiren.util.ann;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Методы, помеченные этой аннотацией должны быть Thread-Safe.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Важно:</b> данные методы гарантированно должны обеспечивать потоковую
|
||||
* безопасность, но не обязаны обеспечивать концессивность (полноту данных или
|
||||
* точность синхронизации).
|
||||
* </p>
|
||||
* <p>
|
||||
* Для полностью потоко-безопасных методов можно использовать аннотацию
|
||||
* {@link ThreadSecure}.
|
||||
* </p>
|
||||
*
|
||||
* @see {@link ThreadSecure}, {@link Synchronize}, {@link ThreadSafeSingle}
|
||||
* @author Rena
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.SOURCE)
|
||||
public @interface ThreadSafe {
|
||||
|
||||
}
|
||||
17
src/main/java/io/eiren/util/ann/ThreadSafeSingle.java
Normal file
17
src/main/java/io/eiren/util/ann/ThreadSafeSingle.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package io.eiren.util.ann;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
|
||||
/**
|
||||
* Соблюдает те же требования что и {@link ThreadSafe} но при условии, что сам
|
||||
* метод вызывается только из одного потока одновременно.
|
||||
*
|
||||
* @see {@link ThreadSafe}, {@link ThreadSecure}, {@link Synchronize}
|
||||
* @author Rena
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.SOURCE)
|
||||
public @interface ThreadSafeSingle {
|
||||
|
||||
}
|
||||
22
src/main/java/io/eiren/util/ann/ThreadSecure.java
Normal file
22
src/main/java/io/eiren/util/ann/ThreadSecure.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package io.eiren.util.ann;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Методы, помеченные этой аннотацией должны быть полностью Thread-Safe.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Важно:</b> данные методы гарантированно должны обеспечивать потоковую
|
||||
* безопасность и консистентность (полноту данных и точность синхронизации).
|
||||
* </p>
|
||||
*
|
||||
* @see {@link ThreadSafe}, {@link Synchronize}, {@link ThreadSafeSingle}
|
||||
* @author Rena
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.SOURCE)
|
||||
public @interface ThreadSecure {
|
||||
|
||||
}
|
||||
39
src/main/java/io/eiren/util/ann/Transient.java
Normal file
39
src/main/java/io/eiren/util/ann/Transient.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package io.eiren.util.ann;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Означает что поле используется для временных или быстро изменяющихся
|
||||
* переменных.
|
||||
* </p>
|
||||
* <p>
|
||||
* Поле помеченное этой аннотацией не влияет на долгосрочное состояние объекта,
|
||||
* не участвует в сериализации, вычислении equals и hashCode, не определяет
|
||||
* поведение объекта для внешнего кода. Поэтому такие поля не должны
|
||||
* использоваться внешним кодом, их состояние имеет смысл только для самого
|
||||
* объекта в котором они объявлены.
|
||||
* </p>
|
||||
* Примеры:
|
||||
* <ul>
|
||||
* <li>Временный объект, который используется в методах для внутренних
|
||||
* вычислений. Например векторные и матричные вычисления.</li>
|
||||
* <li>Внутренний флаг для мультитрединга. Например, флаг апдейта графического
|
||||
* состояния взводимый из игрового потока.</li>
|
||||
* <li>Выведенное значение или структура, которое инициализируется самим
|
||||
* объектом по фиксированному правилу. Например, производное значение от
|
||||
* переменной параметризующей объект. Инициализируемый в конструкторе lookup
|
||||
* table.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author tort32
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.SOURCE)
|
||||
@Target({ ElementType.FIELD })
|
||||
public @interface Transient {
|
||||
|
||||
}
|
||||
547
src/main/java/io/eiren/util/collections/FastList.java
Normal file
547
src/main/java/io/eiren/util/collections/FastList.java
Normal file
@@ -0,0 +1,547 @@
|
||||
package io.eiren.util.collections;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class FastList<E> extends AbstractList<E>
|
||||
implements RandomAccess, Cloneable, RemoveAtSwapList<E> {
|
||||
|
||||
private static final Object[] emptyArray = new Object[0];
|
||||
|
||||
public static final int MAX_ARRAY_SIZE = 2147483639;
|
||||
|
||||
protected int size = 0;
|
||||
protected Object[] array;
|
||||
|
||||
public FastList(int capacity) {
|
||||
array = capacity == 0 ? emptyArray : new Object[capacity];
|
||||
}
|
||||
|
||||
public FastList() {
|
||||
this(5);
|
||||
}
|
||||
|
||||
public FastList(Collection<E> source) {
|
||||
this(source.size());
|
||||
addAll(source);
|
||||
}
|
||||
|
||||
public FastList(FastList<E> source) {
|
||||
this(source.size);
|
||||
addAllInternal(0, source.array, source.size);
|
||||
}
|
||||
|
||||
public FastList(E[] source) {
|
||||
this(source.length);
|
||||
addAll(source);
|
||||
}
|
||||
|
||||
public FastList(E source) {
|
||||
this();
|
||||
add(source);
|
||||
}
|
||||
|
||||
private FastList(Object[] arr, int size) {
|
||||
this(size);
|
||||
System.arraycopy(arr, 0, array, 0, size);
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
private FastList(boolean f) {
|
||||
}
|
||||
|
||||
public static <E> FastList<E> reuseArray(E[] source) {
|
||||
FastList<E> list = new FastList<>(true);
|
||||
list.array = source;
|
||||
list.size = source.length;
|
||||
return list;
|
||||
}
|
||||
|
||||
private void checkBounds(int index) {
|
||||
if (index < 0 || index >= size)
|
||||
throw new ArrayIndexOutOfBoundsException(
|
||||
new StringBuilder("Index: ")
|
||||
.append(index)
|
||||
.append(", size: ")
|
||||
.append(size)
|
||||
.toString()
|
||||
);
|
||||
}
|
||||
|
||||
public void ensureCapacity(int numToFit) {
|
||||
if (array.length < size + numToFit)
|
||||
grow(numToFit + size);
|
||||
}
|
||||
|
||||
private void grow(int i) {
|
||||
int j = array.length;
|
||||
int k = j + (j >> 1);
|
||||
if (k - i < 0)
|
||||
k = i;
|
||||
if (k - 2147483639 > 0)
|
||||
k = hugeCapacity(i);
|
||||
array = Arrays.copyOf(array, k);
|
||||
}
|
||||
|
||||
private static int hugeCapacity(int i) {
|
||||
if (i < 0)
|
||||
throw new OutOfMemoryError("Huge capacity negative: " + i);
|
||||
else
|
||||
return i <= MAX_ARRAY_SIZE ? MAX_ARRAY_SIZE : 2147483647;
|
||||
}
|
||||
|
||||
public void copyInto(Object[] anArray) {
|
||||
System.arraycopy(array, 0, anArray, 0, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(int index) {
|
||||
checkBounds(index);
|
||||
return (E) array[index];
|
||||
}
|
||||
|
||||
public E unsafeGet(int index) {
|
||||
return (E) array[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object obj) {
|
||||
for (int j = 0; j < size; j++)
|
||||
if (obj == array[j])
|
||||
return j;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object obj) {
|
||||
for (int j = size - 1; j >= 0; j--)
|
||||
if (obj == array[j])
|
||||
return j;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object obj) {
|
||||
return indexOf(obj) >= 0;
|
||||
}
|
||||
|
||||
public void trimToSize() {
|
||||
int i = array.length;
|
||||
if (size < i)
|
||||
array = Arrays.copyOf(array, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return Arrays.copyOf(array, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T aobj[]) {
|
||||
if (aobj.length < size)
|
||||
return (T[]) Arrays.copyOf(array, size, aobj.getClass());
|
||||
System.arraycopy(array, 0, aobj, 0, size);
|
||||
if (aobj.length > size)
|
||||
aobj[size] = null;
|
||||
return aobj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E e) {
|
||||
ensureCapacity(1);
|
||||
array[size++] = e;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E remove(int i) {
|
||||
checkBounds(i);
|
||||
E obj = (E) array[i];
|
||||
removeInternal(i);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object obj) {
|
||||
for (int j = 0; j < size; j++)
|
||||
if (obj == array[j]) {
|
||||
removeInternal(j);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean removeAll(Object[] toRemove) {
|
||||
boolean removed = false;
|
||||
for (int i = toRemove.length - 1; i >= 0; --i) {
|
||||
int index = indexOf(toRemove[i]);
|
||||
if (index != -1) {
|
||||
removeInternal(index);
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
protected void removeInternal(int i) {
|
||||
int j = size - i - 1;
|
||||
if (j > 0)
|
||||
System.arraycopy(array, i + 1, array, i, j);
|
||||
array[--size] = null;
|
||||
}
|
||||
|
||||
public void unsafeRemove(int i) {
|
||||
removeInternal(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
Objects.requireNonNull(c);
|
||||
return batchRemove(c, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
Objects.requireNonNull(c);
|
||||
return batchRemove(c, true);
|
||||
}
|
||||
|
||||
private boolean batchRemove(Collection<?> c, boolean complement) {
|
||||
final Object[] elementData = this.array;
|
||||
int r = 0, w = 0;
|
||||
boolean modified = false;
|
||||
try {
|
||||
for (; r < size; r++)
|
||||
if (c.contains(elementData[r]) == complement)
|
||||
elementData[w++] = elementData[r];
|
||||
} finally {
|
||||
// Preserve behavioral compatibility with AbstractCollection,
|
||||
// even if c.contains() throws.
|
||||
if (r != size) {
|
||||
System.arraycopy(elementData, r, elementData, w, size - r);
|
||||
w += size - r;
|
||||
}
|
||||
if (w != size) {
|
||||
for (int i = w; i < size; i++)
|
||||
elementData[i] = null;
|
||||
size = w;
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
for (int i = 0; i < size; i++)
|
||||
array[i] = null;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
public void fakeClear() {
|
||||
size = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends E> collection) {
|
||||
return addAll(size, collection);
|
||||
}
|
||||
|
||||
public void addAll(E[] arr) {
|
||||
addAllInternal(size, arr, arr.length);
|
||||
}
|
||||
|
||||
public void addAll(E[] arr, int limit) {
|
||||
addAllInternal(size, arr, limit);
|
||||
}
|
||||
|
||||
public void addAll(int index, E[] arr) {
|
||||
addAllInternal(index, arr, arr.length);
|
||||
}
|
||||
|
||||
public void addAll(int index, E[] arr, int limit) {
|
||||
addAllInternal(index, arr, limit);
|
||||
}
|
||||
|
||||
private void addAllInternal(int index, Object[] arr, int limit) {
|
||||
if (limit > arr.length)
|
||||
limit = arr.length;
|
||||
if (limit == 1) {
|
||||
add(index, (E) arr[0]);
|
||||
} else if (limit > 0) {
|
||||
if (index >= size) {
|
||||
ensureCapacity(size - index + limit);
|
||||
System.arraycopy(arr, 0, array, index, limit);
|
||||
size = index + limit;
|
||||
} else {
|
||||
if (array.length < size + limit) {
|
||||
Object[] newArray = new Object[size + limit];
|
||||
System.arraycopy(array, 0, newArray, 0, index);
|
||||
System.arraycopy(arr, 0, newArray, index, limit);
|
||||
System.arraycopy(array, index, newArray, index + limit, size - index);
|
||||
array = newArray;
|
||||
} else {
|
||||
System.arraycopy(array, index, array, index + 1, size - index);
|
||||
System.arraycopy(arr, 0, array, index, limit);
|
||||
}
|
||||
size += limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends E> collection) {
|
||||
if (collection.size() > 0) {
|
||||
if (collection instanceof FastList) {
|
||||
addAllInternal(
|
||||
index,
|
||||
((FastList<? extends E>) collection).array,
|
||||
collection.size()
|
||||
);
|
||||
} else if (collection instanceof RandomAccess) {
|
||||
Object[] arr = collection.toArray(new Object[collection.size()]);
|
||||
addAllInternal(index, arr, arr.length);
|
||||
} else {
|
||||
if (index >= size) {
|
||||
ensureCapacity(size - index + collection.size());
|
||||
Iterator<? extends E> iterator = collection.iterator();
|
||||
int i = index;
|
||||
while (iterator.hasNext())
|
||||
array[i++] = iterator.next();
|
||||
size = index + collection.size();
|
||||
} else {
|
||||
if (array.length < size + collection.size()) {
|
||||
Object[] newArray = new Object[size + collection.size()];
|
||||
System.arraycopy(array, 0, newArray, 0, index);
|
||||
Iterator<? extends E> iterator = collection.iterator();
|
||||
int i = index;
|
||||
while (iterator.hasNext())
|
||||
newArray[i++] = iterator.next();
|
||||
System
|
||||
.arraycopy(
|
||||
array,
|
||||
index,
|
||||
newArray,
|
||||
index + collection.size(),
|
||||
size - index
|
||||
);
|
||||
array = newArray;
|
||||
} else {
|
||||
System.arraycopy(array, index, array, index + 1, size - index);
|
||||
Iterator<? extends E> iterator = collection.iterator();
|
||||
while (iterator.hasNext())
|
||||
array[index++] = iterator.next();
|
||||
}
|
||||
size += collection.size();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, E element) {
|
||||
if (index >= size) {
|
||||
ensureCapacity(size - index + 1);
|
||||
size = index + 1;
|
||||
array[index] = element;
|
||||
} else {
|
||||
if (array.length < size + 1) {
|
||||
Object[] newArray = new Object[size + 1];
|
||||
System.arraycopy(array, 0, newArray, 0, index);
|
||||
newArray[index] = element;
|
||||
System.arraycopy(array, index, newArray, index + 1, size - index);
|
||||
array = newArray;
|
||||
} else {
|
||||
System.arraycopy(array, index, array, index + 1, size - index);
|
||||
array[index] = element;
|
||||
}
|
||||
size++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public E set(int index, E element) {
|
||||
checkBounds(index);
|
||||
E oldValue = (E) array[index];
|
||||
array[index] = element;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FastList<E> clone() {
|
||||
return new FastList<E>(array, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<? super E> action) {
|
||||
Objects.requireNonNull(action);
|
||||
final int expectedModCount = modCount;
|
||||
final E[] elementData = (E[]) this.array;
|
||||
final int size = this.size;
|
||||
for (int i = 0; modCount == expectedModCount && i < size; i++) {
|
||||
action.accept(elementData[i]);
|
||||
}
|
||||
if (modCount != expectedModCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public E removeAtSwap(int i) {
|
||||
checkBounds(i);
|
||||
E obj = (E) array[i];
|
||||
removeAtSwapInternal(i);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAtSwap(Object obj) {
|
||||
for (int j = 0; j < size; j++)
|
||||
if (obj == array[j]) {
|
||||
removeAtSwapInternal(j);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void removeAtSwapInternal(int i) {
|
||||
int j = size - i - 1;
|
||||
if (j > 0)
|
||||
array[i] = array[size - 1];
|
||||
array[--size] = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRange(int i, int toIndex) {
|
||||
checkBounds(i);
|
||||
checkBounds(toIndex);
|
||||
int j = size - toIndex - 1;
|
||||
if (j > 0)
|
||||
System.arraycopy(array, toIndex + 1, array, i, j);
|
||||
size -= (toIndex - i + 1);
|
||||
Arrays.fill(array, i, toIndex, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceAll(UnaryOperator<E> operator) {
|
||||
Objects.requireNonNull(operator);
|
||||
for (int i = 0; i < size; ++i)
|
||||
set(i, operator.apply(get(i)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sort(Comparator<? super E> c) {
|
||||
Arrays.sort((E[]) array, 0, size, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = 1;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
Object o = array[i];
|
||||
hashCode = 31 * hashCode + (o == null ? 0 : o.hashCode());
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<E> spliterator() {
|
||||
return Spliterators.spliterator(array, 0, size, Spliterator.ORDERED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special comodification iterator. <b>Use with caution.</b>
|
||||
* <p>
|
||||
* <i>To get element type correctly assign result to reference type
|
||||
* {@code FastList<T>.SkipFastListIterator}</i>
|
||||
*
|
||||
* @return skip iterator to iterate this list in thread-safe manner
|
||||
*/
|
||||
public SkipFastListIterator skipIterator() {
|
||||
return new SkipFastListIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeIf(Predicate<? super E> filter) {
|
||||
Objects.requireNonNull(filter);
|
||||
// figure out which elements are to be removed
|
||||
// any exception thrown from the filter predicate at this stage
|
||||
// will leave the collection unmodified
|
||||
int removeCount = 0;
|
||||
final BitSet removeSet = new BitSet(size);
|
||||
final int expectedModCount = modCount;
|
||||
final int size = this.size;
|
||||
for (int i = 0; modCount == expectedModCount && i < size; i++) {
|
||||
final E element = (E) array[i];
|
||||
if (filter.test(element)) {
|
||||
removeSet.set(i);
|
||||
removeCount++;
|
||||
}
|
||||
}
|
||||
if (modCount != expectedModCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
|
||||
// shift surviving elements left over the spaces left by removed
|
||||
// elements
|
||||
final boolean anyToRemove = removeCount > 0;
|
||||
if (anyToRemove) {
|
||||
final int newSize = size - removeCount;
|
||||
for (int i = 0, j = 0; (i < size) && (j < newSize); i++, j++) {
|
||||
i = removeSet.nextClearBit(i);
|
||||
array[j] = array[i];
|
||||
}
|
||||
for (int k = newSize; k < size; k++) {
|
||||
array[k] = null; // Let gc do its work
|
||||
}
|
||||
this.size = newSize;
|
||||
if (modCount != expectedModCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
modCount++;
|
||||
}
|
||||
|
||||
return anyToRemove;
|
||||
}
|
||||
|
||||
public class SkipFastListIterator implements ResettableIterator<E>, SkipIterator<E> {
|
||||
|
||||
public int position;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return position < size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
Object[] arr = array;
|
||||
if (arr.length > position) {
|
||||
return (E) arr[position++];
|
||||
}
|
||||
position++; // Increase position so hasNext() never loops infinitely
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
position = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package io.eiren.util.collections;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
/**
|
||||
* FastList that performs Remove-At-Swap on stanard remove() operations.
|
||||
*
|
||||
* <p>
|
||||
* Remove operations breaks ordering of this list
|
||||
*
|
||||
* @author Rena
|
||||
*
|
||||
* @param <E>
|
||||
*/
|
||||
public class RemoveAtSwapFastList<E> extends FastList<E> {
|
||||
|
||||
public RemoveAtSwapFastList(int capacity) {
|
||||
super(capacity);
|
||||
}
|
||||
|
||||
public RemoveAtSwapFastList() {
|
||||
}
|
||||
|
||||
public RemoveAtSwapFastList(Collection<E> source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
public RemoveAtSwapFastList(E[] source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
public RemoveAtSwapFastList(E source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeInternal(int i) {
|
||||
super.removeAtSwapInternal(i);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package io.eiren.util.collections;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public interface RemoveAtSwapList<E> extends List<E> {
|
||||
|
||||
public E removeAtSwap(int i);
|
||||
|
||||
public boolean removeAtSwap(Object object);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.eiren.util.collections;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
|
||||
/**
|
||||
* {@link Iterator} that can be reset and iterated from the start by using
|
||||
* {@link #reset()}
|
||||
*
|
||||
* @author Rena
|
||||
*
|
||||
* @param <E>
|
||||
*/
|
||||
public interface ResettableIterator<E> extends Iterator<E> {
|
||||
|
||||
public void reset();
|
||||
}
|
||||
16
src/main/java/io/eiren/util/collections/SkipIterator.java
Normal file
16
src/main/java/io/eiren/util/collections/SkipIterator.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package io.eiren.util.collections;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
|
||||
/**
|
||||
* {@link Iterator} that can return null on {@link #next()} or can lie on
|
||||
* {@link #hasNext()}. It is <b>not thread-secure!</b>
|
||||
*
|
||||
* @param <E> the type of elements returned by this iterator
|
||||
*/
|
||||
public interface SkipIterator<E> extends Iterator<E> {
|
||||
|
||||
@Override
|
||||
E next();
|
||||
}
|
||||
137
src/main/java/io/eiren/util/logging/DefaultGLog.java
Normal file
137
src/main/java/io/eiren/util/logging/DefaultGLog.java
Normal file
@@ -0,0 +1,137 @@
|
||||
package io.eiren.util.logging;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
public class DefaultGLog extends Thread implements IGLog {
|
||||
|
||||
private final Logger logger;
|
||||
|
||||
public static class LogEntry {
|
||||
|
||||
private Level level;
|
||||
private String message;
|
||||
private Throwable t;
|
||||
|
||||
public LogEntry(Level level, String message, Throwable t) {
|
||||
this(level, message);
|
||||
this.t = t;
|
||||
}
|
||||
|
||||
public LogEntry(Level level, String message) {
|
||||
this.level = level;
|
||||
this.message = message;
|
||||
this.t = null;
|
||||
}
|
||||
|
||||
public Level getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public Throwable getException() {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
private final ArrayBlockingQueue<LogEntry> queue = new ArrayBlockingQueue<LogEntry>(50000);
|
||||
private volatile LoggerRecorder recorder;
|
||||
|
||||
@Override
|
||||
public void info(String message) {
|
||||
add(new LogEntry(Level.INFO, message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String message, Throwable t) {
|
||||
add(new LogEntry(Level.INFO, message, t));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void severe(String message) {
|
||||
add(new LogEntry(Level.SEVERE, message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void severe(String message, Throwable t) {
|
||||
add(new LogEntry(Level.SEVERE, message, t));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warning(String message) {
|
||||
add(new LogEntry(Level.WARNING, message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warning(String message, Throwable t) {
|
||||
add(new LogEntry(Level.WARNING, message, t));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message) {
|
||||
add(new LogEntry(Level.INFO, "[DBG] " + message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message, Throwable t) {
|
||||
add(new LogEntry(Level.INFO, "[DBG] " + message, t));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String message) {
|
||||
add(new LogEntry(level, message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String message, Throwable t) {
|
||||
add(new LogEntry(level, message, t));
|
||||
}
|
||||
|
||||
private void add(LogEntry entry) {
|
||||
try {
|
||||
queue.put(entry);
|
||||
} catch (InterruptedException e) {}
|
||||
try {
|
||||
if (recorder != null)
|
||||
recorder.addEntry(entry);
|
||||
} catch (NullPointerException e) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecorder(LoggerRecorder recorder) {
|
||||
this.recorder = recorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoggerRecorder removeRecorder() {
|
||||
LoggerRecorder lr = this.recorder;
|
||||
this.recorder = null;
|
||||
return lr;
|
||||
}
|
||||
|
||||
public DefaultGLog(Logger logger) {
|
||||
super("Logger");
|
||||
this.logger = logger;
|
||||
this.setDaemon(true);
|
||||
this.setPriority(7);
|
||||
this.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
LogEntry log = queue.take();
|
||||
if (log.t != null)
|
||||
logger.log(log.level, log.message, log.t);
|
||||
else
|
||||
logger.log(log.level, log.message);
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
58
src/main/java/io/eiren/util/logging/FileLogFormatter.java
Normal file
58
src/main/java/io/eiren/util/logging/FileLogFormatter.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package io.eiren.util.logging;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
|
||||
public class FileLogFormatter extends Formatter {
|
||||
|
||||
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public String format(LogRecord record) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(dateFormat.format(record.getMillis()));
|
||||
Level localLevel = record.getLevel();
|
||||
if (localLevel == Level.FINEST)
|
||||
sb.append(" [FINEST] ");
|
||||
else if (localLevel == Level.FINER)
|
||||
sb.append(" [FINER] ");
|
||||
else if (localLevel == Level.FINE)
|
||||
sb.append(" [FINE] ");
|
||||
else if (localLevel == Level.INFO)
|
||||
sb.append(" [INFO] ");
|
||||
else if (localLevel == Level.WARNING)
|
||||
sb.append(" [WARNING] ");
|
||||
else if (localLevel == Level.SEVERE)
|
||||
sb.append(" [SEVERE] ");
|
||||
else
|
||||
sb.append(" [" + localLevel.getLocalizedName() + "] ");
|
||||
|
||||
sb.append(record.getMessage());
|
||||
sb.append('\n');
|
||||
|
||||
Throwable localThrowable = record.getThrown();
|
||||
if (localThrowable != null) {
|
||||
StringWriter localStringWriter = new StringWriter();
|
||||
localThrowable.printStackTrace(new PrintWriter(localStringWriter));
|
||||
sb.append(localStringWriter.toString());
|
||||
}
|
||||
|
||||
String message = sb.toString();
|
||||
Object parameters[] = record.getParameters();
|
||||
if (parameters == null || parameters.length == 0)
|
||||
return message;
|
||||
if (
|
||||
message.indexOf("{0") >= 0
|
||||
|| message.indexOf("{1") >= 0
|
||||
|| message.indexOf("{2") >= 0
|
||||
|| message.indexOf("{3") >= 0
|
||||
)
|
||||
return java.text.MessageFormat.format(message, parameters);
|
||||
return message;
|
||||
}
|
||||
}
|
||||
48
src/main/java/io/eiren/util/logging/IGLog.java
Normal file
48
src/main/java/io/eiren/util/logging/IGLog.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package io.eiren.util.logging;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
|
||||
public interface IGLog {
|
||||
|
||||
public void info(String message);
|
||||
|
||||
public void severe(String message);
|
||||
|
||||
public void warning(String message);
|
||||
|
||||
public void debug(String message);
|
||||
|
||||
public default void info(String message, Throwable t) {
|
||||
log(Level.INFO, message, t);
|
||||
}
|
||||
|
||||
public default void severe(String message, Throwable t) {
|
||||
log(Level.SEVERE, message, t);
|
||||
}
|
||||
|
||||
public default void warning(String message, Throwable t) {
|
||||
log(Level.WARNING, message, t);
|
||||
}
|
||||
|
||||
public default void debug(String message, Throwable t) {
|
||||
log(Level.INFO, "[DBG] " + message, t);
|
||||
}
|
||||
|
||||
public void log(Level level, String message);
|
||||
|
||||
public void log(Level level, String message, Throwable t);
|
||||
|
||||
public void setRecorder(LoggerRecorder recorder);
|
||||
|
||||
public LoggerRecorder removeRecorder();
|
||||
|
||||
static class GLevel extends Level {
|
||||
|
||||
private static final long serialVersionUID = -539856764608026895L;
|
||||
|
||||
private GLevel(String s, int i) {
|
||||
super(s, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
136
src/main/java/io/eiren/util/logging/LogManager.java
Normal file
136
src/main/java/io/eiren/util/logging/LogManager.java
Normal file
@@ -0,0 +1,136 @@
|
||||
package io.eiren.util.logging;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.FileHandler;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
public class LogManager {
|
||||
|
||||
private static AtomicBoolean initialized = new AtomicBoolean(false);
|
||||
|
||||
public static Logger global = Logger.getLogger("");
|
||||
public static final IGLog log = new DefaultGLog(global);
|
||||
public static ConsoleHandler handler;
|
||||
|
||||
public static void initialize(File logsDir, File mainLogDir)
|
||||
throws SecurityException, IOException {
|
||||
if (initialized.getAndSet(true))
|
||||
return;
|
||||
FileLogFormatter loc = new FileLogFormatter();
|
||||
if (mainLogDir != null) {
|
||||
if (!mainLogDir.exists())
|
||||
mainLogDir.mkdirs();
|
||||
File lastLogFile = new File(mainLogDir, "log_last.log");
|
||||
if (lastLogFile.exists())
|
||||
lastLogFile.delete();
|
||||
File mainLog = new File(mainLogDir, "log_main.log");
|
||||
FileHandler mHandler = new FileHandler(mainLog.getPath(), true);
|
||||
FileHandler filehandler = new FileHandler(lastLogFile.getPath(), true);
|
||||
mHandler.setFormatter(loc);
|
||||
filehandler.setFormatter(loc);
|
||||
global.addHandler(mHandler);
|
||||
global.addHandler(filehandler);
|
||||
}
|
||||
if (logsDir != null) {
|
||||
if (!logsDir.exists())
|
||||
logsDir.mkdir();
|
||||
if (!logsDir.isDirectory())
|
||||
System.out.println("*** WARNING *** LOG FOLDER IS NOT A DIRECTORY!");
|
||||
File currentLog = new File(
|
||||
logsDir,
|
||||
"log_"
|
||||
+ new SimpleDateFormat("yyyy-MM-dd")
|
||||
.format(Long.valueOf(System.currentTimeMillis()))
|
||||
+ ".log"
|
||||
);
|
||||
FileHandler filehandler2 = new FileHandler(currentLog.getPath(), true);
|
||||
filehandler2.setFormatter(loc);
|
||||
global.addHandler(filehandler2);
|
||||
}
|
||||
}
|
||||
|
||||
public static void replaceMainHandler(ConsoleHandler newHandler) {
|
||||
handler.close();
|
||||
global.removeHandler(handler);
|
||||
handler = newHandler;
|
||||
global.addHandler(newHandler);
|
||||
}
|
||||
|
||||
public static void addHandler(Handler add) {
|
||||
global.addHandler(add);
|
||||
}
|
||||
|
||||
public static void removeHandler(Handler remove) {
|
||||
global.removeHandler(remove);
|
||||
}
|
||||
|
||||
public static void enablePreciseTimestamp() {
|
||||
handler.setFormatter(new PreciseConsoleLogFormatter());
|
||||
}
|
||||
|
||||
public static void info(String message) {
|
||||
log.info(message);
|
||||
}
|
||||
|
||||
public static void severe(String message) {
|
||||
log.severe(message);
|
||||
}
|
||||
|
||||
public static void warning(String message) {
|
||||
log.warning(message);
|
||||
}
|
||||
|
||||
public static void debug(String message) {
|
||||
log.debug(message);
|
||||
}
|
||||
|
||||
public static void info(String message, Throwable t) {
|
||||
log.info(message, t);
|
||||
}
|
||||
|
||||
public static void severe(String message, Throwable t) {
|
||||
log.severe(message, t);
|
||||
}
|
||||
|
||||
public static void warning(String message, Throwable t) {
|
||||
log.warning(message, t);
|
||||
}
|
||||
|
||||
public static void debug(String message, Throwable t) {
|
||||
log.debug(message, t);
|
||||
}
|
||||
|
||||
public static void log(Level level, String message) {
|
||||
log.log(level, message);
|
||||
}
|
||||
|
||||
public static void log(Level level, String message, Throwable t) {
|
||||
log.log(level, message, t);
|
||||
}
|
||||
|
||||
static {
|
||||
boolean hasConsoleHandler = false;
|
||||
for (Handler h : global.getHandlers()) {
|
||||
if (h instanceof ConsoleHandler) {
|
||||
handler = (ConsoleHandler) h;
|
||||
hasConsoleHandler = true;
|
||||
}
|
||||
}
|
||||
if (!hasConsoleHandler) {
|
||||
handler = new ConsoleHandler();
|
||||
global.addHandler(handler);
|
||||
}
|
||||
handler.setFormatter(new ShortConsoleLogFormatter());
|
||||
|
||||
System.setOut(new PrintStream(new LoggerOutputStream(log, Level.INFO), true));
|
||||
System.setErr(new PrintStream(new LoggerOutputStream(log, Level.SEVERE), true));
|
||||
}
|
||||
}
|
||||
52
src/main/java/io/eiren/util/logging/LoggerOutputStream.java
Normal file
52
src/main/java/io/eiren/util/logging/LoggerOutputStream.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package io.eiren.util.logging;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
|
||||
public class LoggerOutputStream extends ByteArrayOutputStream {
|
||||
|
||||
private static final String separator = System.getProperty("line.separator");
|
||||
|
||||
private final IGLog logger;
|
||||
private final Level level;
|
||||
private final String prefix;
|
||||
private final StringBuilder buffer = new StringBuilder();
|
||||
|
||||
public LoggerOutputStream(IGLog logger, Level level) {
|
||||
this(logger, level, "");
|
||||
}
|
||||
|
||||
public LoggerOutputStream(IGLog logger, Level level, String prefix) {
|
||||
super();
|
||||
this.logger = logger;
|
||||
this.level = level;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
synchronized (this) {
|
||||
super.flush();
|
||||
String record = this.toString();
|
||||
super.reset();
|
||||
if (record.length() > 0) {
|
||||
buffer.append(record);
|
||||
if (record.contains(separator)) {
|
||||
String s = buffer.toString();
|
||||
String[] split = s.split(separator);
|
||||
for (int i = 0; i < split.length; ++i)
|
||||
logger.log(level, prefix + split[i]);
|
||||
buffer.setLength(0);
|
||||
// buffer.append(split[split.length - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
flush();
|
||||
}
|
||||
}
|
||||
23
src/main/java/io/eiren/util/logging/LoggerRecorder.java
Normal file
23
src/main/java/io/eiren/util/logging/LoggerRecorder.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package io.eiren.util.logging;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.eiren.util.collections.FastList;
|
||||
import io.eiren.util.logging.DefaultGLog.LogEntry;
|
||||
|
||||
|
||||
public class LoggerRecorder {
|
||||
|
||||
private final List<LogEntry> recorded = new FastList<LogEntry>();
|
||||
|
||||
public LoggerRecorder() {
|
||||
}
|
||||
|
||||
public synchronized void addEntry(LogEntry e) {
|
||||
recorded.add(e);
|
||||
}
|
||||
|
||||
public List<LogEntry> getEntries() {
|
||||
return recorded;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.eiren.util.logging;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
|
||||
/**
|
||||
* Format message timestamp as time passed from the start with milliseconds.
|
||||
*/
|
||||
public class PreciseConsoleLogFormatter extends ShortConsoleLogFormatter {
|
||||
|
||||
private final long startMills;
|
||||
|
||||
public PreciseConsoleLogFormatter() {
|
||||
startMills = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SimpleDateFormat createDateFormat() {
|
||||
return new SimpleDateFormat("mm:ss.SSS");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildMessage(StringBuilder builder, LogRecord record) {
|
||||
builder.append(date.format(record.getMillis() - startMills));
|
||||
builder.append(" [");
|
||||
builder.append(record.getLevel().getLocalizedName().toUpperCase());
|
||||
builder.append("] ");
|
||||
builder.append(record.getMessage());
|
||||
builder.append('\n');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package io.eiren.util.logging;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
|
||||
public class ShortConsoleLogFormatter extends Formatter {
|
||||
|
||||
protected final SimpleDateFormat date;
|
||||
|
||||
public ShortConsoleLogFormatter() {
|
||||
this.date = createDateFormat();
|
||||
}
|
||||
|
||||
protected SimpleDateFormat createDateFormat() {
|
||||
return new SimpleDateFormat("HH:mm:ss");
|
||||
}
|
||||
|
||||
protected void buildMessage(StringBuilder builder, LogRecord record) {
|
||||
builder.append(date.format(record.getMillis()));
|
||||
builder.append(" [");
|
||||
builder.append(record.getLevel().getLocalizedName().toUpperCase());
|
||||
builder.append("] ");
|
||||
builder.append(record.getMessage());
|
||||
builder.append('\n');
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(LogRecord record) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
Throwable ex = record.getThrown();
|
||||
|
||||
buildMessage(builder, record);
|
||||
|
||||
if (ex != null) {
|
||||
StringWriter writer = new StringWriter();
|
||||
ex.printStackTrace(new PrintWriter(writer));
|
||||
builder.append(writer);
|
||||
}
|
||||
|
||||
String message = builder.toString();
|
||||
Object parameters[] = record.getParameters();
|
||||
if (parameters == null || parameters.length == 0)
|
||||
return message;
|
||||
if (
|
||||
message.indexOf("{0") >= 0
|
||||
|| message.indexOf("{1") >= 0
|
||||
|| message.indexOf("{2") >= 0
|
||||
|| message.indexOf("{3") >= 0
|
||||
)
|
||||
return java.text.MessageFormat.format(message, parameters);
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
880
src/main/java/org/json/JSONArray.java
Normal file
880
src/main/java/org/json/JSONArray.java
Normal file
@@ -0,0 +1,880 @@
|
||||
package org.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* A JSONArray is an ordered sequence of values. Its external text form is a
|
||||
* string wrapped in square brackets with commas separating the values. The
|
||||
* internal form is an object having <code>get</code> and <code>opt</code>
|
||||
* methods for accessing the values by index, and <code>put</code> methods for
|
||||
* adding or replacing values. The values can be any of these types:
|
||||
* <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
|
||||
* <code>Number</code>, <code>String</code>, or the
|
||||
* <code>JSONObject.NULL object</code>.
|
||||
* <p>
|
||||
* The constructor can convert a JSON text into a Java object. The
|
||||
* <code>toString</code> method converts to JSON text.
|
||||
* <p>
|
||||
* A <code>get</code> method returns a value if one can be found, and throws an
|
||||
* exception if one cannot be found. An <code>opt</code> method returns a
|
||||
* default value instead of throwing an exception, and so is useful for
|
||||
* obtaining optional values.
|
||||
* <p>
|
||||
* The generic <code>get()</code> and <code>opt()</code> methods return an
|
||||
* object which you can cast or query for type. There are also typed
|
||||
* <code>get</code> and <code>opt</code> methods that do type checking and type
|
||||
* coercion for you.
|
||||
* <p>
|
||||
* The texts produced by the <code>toString</code> methods strictly conform to
|
||||
* JSON syntax rules. The constructors are more forgiving in the texts they will
|
||||
* accept:
|
||||
* <ul>
|
||||
* <li>An extra <code>,</code> <small>(comma)</small> may appear just
|
||||
* before the closing bracket.</li>
|
||||
* <li>The <code>null</code> value will be inserted when there is <code>,</code>
|
||||
* <small>(comma)</small> elision.</li>
|
||||
* <li>Strings may be quoted with <code>'</code> <small>(single
|
||||
* quote)</small>.</li>
|
||||
* <li>Strings do not need to be quoted at all if they do not begin with a quote
|
||||
* or single quote, and if they do not contain leading or trailing spaces, and
|
||||
* if they do not contain any of these characters:
|
||||
* <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
|
||||
* if they are not the reserved words <code>true</code>, <code>false</code>, or
|
||||
* <code>null</code>.</li>
|
||||
* <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as
|
||||
* well as by <code>,</code> <small>(comma)</small>.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2012-04-20
|
||||
*/
|
||||
public class JSONArray {
|
||||
|
||||
/**
|
||||
* The arrayList where the JSONArray's properties are kept.
|
||||
*/
|
||||
private final ArrayList<Object> myArrayList;
|
||||
|
||||
/**
|
||||
* Construct an empty JSONArray.
|
||||
*/
|
||||
public JSONArray() {
|
||||
this.myArrayList = new ArrayList<Object>();
|
||||
}
|
||||
|
||||
public JSONArray(int initialLength) {
|
||||
this.myArrayList = new ArrayList<Object>(initialLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from a JSONTokener.
|
||||
*
|
||||
* @param x A JSONTokener
|
||||
* @throws JSONException If there is a syntax error.
|
||||
*/
|
||||
public JSONArray(JSONTokener x) throws JSONException {
|
||||
this();
|
||||
if (x.nextClean() != '[') {
|
||||
throw x.syntaxError("A JSONArray text must start with '['");
|
||||
}
|
||||
if (x.nextClean() != ']') {
|
||||
x.back();
|
||||
for (;;) {
|
||||
if (x.nextClean() == ',') {
|
||||
x.back();
|
||||
this.myArrayList.add(JSONObject.NULL);
|
||||
} else {
|
||||
x.back();
|
||||
this.myArrayList.add(x.nextValue());
|
||||
}
|
||||
switch (x.nextClean()) {
|
||||
case ';':
|
||||
case ',':
|
||||
if (x.nextClean() == ']') {
|
||||
return;
|
||||
}
|
||||
x.back();
|
||||
break;
|
||||
case ']':
|
||||
return;
|
||||
default:
|
||||
throw x.syntaxError("Expected a ',' or ']'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from a source JSON text.
|
||||
*
|
||||
* @param source A string that begins with <code>[</code> <small>(left
|
||||
* bracket)</small> and ends with <code>]</code> <small>(right
|
||||
* bracket)</small>.
|
||||
* @throws JSONException If there is a syntax error.
|
||||
*/
|
||||
public JSONArray(String source) throws JSONException {
|
||||
this(new JSONTokener(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from a Collection.
|
||||
*
|
||||
* @param collection A Collection.
|
||||
*/
|
||||
public JSONArray(Collection<Object> collection) {
|
||||
this.myArrayList = new ArrayList<Object>();
|
||||
if (collection != null) {
|
||||
Iterator<Object> iter = collection.iterator();
|
||||
while (iter.hasNext()) {
|
||||
this.myArrayList.add(JSONObject.wrap(iter.next()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from an array
|
||||
*
|
||||
* @throws JSONException If not an array.
|
||||
*/
|
||||
public JSONArray(Object array) {
|
||||
this();
|
||||
if (array.getClass().isArray()) {
|
||||
int length = Array.getLength(array);
|
||||
for (int i = 0; i < length; i += 1) {
|
||||
this.put(JSONObject.wrap(Array.get(array, i)));
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"JSONArray initial value should be a string or collection or array."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return An object value.
|
||||
* @throws JSONException If there is no value for the index.
|
||||
*/
|
||||
public Object get(int index) throws JSONException {
|
||||
Object object = this.opt(index);
|
||||
if (object == null) {
|
||||
throw new JSONException("JSONArray[" + index + "] not found.");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the boolean value associated with an index. The string values "true"
|
||||
* and "false" are converted to boolean.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The truth.
|
||||
* @throws JSONException If there is no value for the index or if the value
|
||||
* is not convertible to boolean.
|
||||
*/
|
||||
public boolean getBoolean(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if (
|
||||
object.equals(Boolean.FALSE)
|
||||
|| (object instanceof String && ((String) object).equalsIgnoreCase("false"))
|
||||
) {
|
||||
return false;
|
||||
} else if (
|
||||
object.equals(Boolean.TRUE)
|
||||
|| (object instanceof String && ((String) object).equalsIgnoreCase("true"))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] is not a boolean.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the double value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
* @throws JSONException If the key is not found or if the value cannot be
|
||||
* converted to a number.
|
||||
*/
|
||||
public double getDouble(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
try {
|
||||
return object instanceof Number
|
||||
? ((Number) object).doubleValue()
|
||||
: Double.parseDouble((String) object);
|
||||
} catch (Exception e) {
|
||||
throw new JSONException("JSONArray[" + index + "] is not a number.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the int value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
* @throws JSONException If the key is not found or if the value is not a
|
||||
* number.
|
||||
*/
|
||||
public int getInt(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
try {
|
||||
return object instanceof Number
|
||||
? ((Number) object).intValue()
|
||||
: Integer.parseInt((String) object);
|
||||
} catch (Exception e) {
|
||||
throw new JSONException("JSONArray[" + index + "] is not a number.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSONArray associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A JSONArray value.
|
||||
* @throws JSONException If there is no value for the index. or if the value
|
||||
* is not a JSONArray
|
||||
*/
|
||||
public JSONArray getJSONArray(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if (object instanceof JSONArray) {
|
||||
return (JSONArray) object;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] is not a JSONArray.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSONObject associated with an index.
|
||||
*
|
||||
* @param index subscript
|
||||
* @return A JSONObject value.
|
||||
* @throws JSONException If there is no value for the index or if the value
|
||||
* is not a JSONObject
|
||||
*/
|
||||
public JSONObject getJSONObject(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if (object instanceof JSONObject) {
|
||||
return (JSONObject) object;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] is not a JSONObject.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the long value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
* @throws JSONException If the key is not found or if the value cannot be
|
||||
* converted to a number.
|
||||
*/
|
||||
public long getLong(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
try {
|
||||
return object instanceof Number
|
||||
? ((Number) object).longValue()
|
||||
: Long.parseLong((String) object);
|
||||
} catch (Exception e) {
|
||||
throw new JSONException("JSONArray[" + index + "] is not a number.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A string value.
|
||||
* @throws JSONException If there is no string value for the index.
|
||||
*/
|
||||
public String getString(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if (object instanceof String) {
|
||||
return (String) object;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] not a string.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the value is null.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return true if the value at the index is null, or if there is no value.
|
||||
*/
|
||||
public boolean isNull(int index) {
|
||||
return JSONObject.NULL.equals(this.opt(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a string from the contents of this JSONArray. The
|
||||
* <code>separator</code> string is inserted between each element. Warning:
|
||||
* This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @param separator A string that will be inserted between the elements.
|
||||
* @return a string.
|
||||
* @throws JSONException If the array contains an invalid number.
|
||||
*/
|
||||
public String join(String separator) throws JSONException {
|
||||
int len = this.length();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
for (int i = 0; i < len; i += 1) {
|
||||
if (i > 0) {
|
||||
sb.append(separator);
|
||||
}
|
||||
sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of elements in the JSONArray, included nulls.
|
||||
*
|
||||
* @return The length (or size).
|
||||
*/
|
||||
public int length() {
|
||||
return this.myArrayList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional object value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return An object value, or null if there is no object at that index.
|
||||
*/
|
||||
public Object opt(int index) {
|
||||
return (index < 0 || index >= this.length()) ? null : this.myArrayList.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional boolean value associated with an index. It returns false
|
||||
* if there is no value at that index, or if the value is not Boolean.TRUE
|
||||
* or the String "true".
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The truth.
|
||||
*/
|
||||
public boolean optBoolean(int index) {
|
||||
return this.optBoolean(index, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional boolean value associated with an index. It returns the
|
||||
* defaultValue if there is no value at that index or if it is not a Boolean
|
||||
* or the String "true" or "false" (case insensitive).
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue A boolean default.
|
||||
* @return The truth.
|
||||
*/
|
||||
public boolean optBoolean(int index, boolean defaultValue) {
|
||||
try {
|
||||
return this.getBoolean(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional double value associated with an index. NaN is returned
|
||||
* if there is no value for the index, or if the value is not a number and
|
||||
* cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
*/
|
||||
public double optDouble(int index) {
|
||||
return this.optDouble(index, Double.NaN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional double value associated with an index. The defaultValue
|
||||
* is returned if there is no value for the index, or if the value is not a
|
||||
* number and cannot be converted to a number.
|
||||
*
|
||||
* @param index subscript
|
||||
* @param defaultValue The default value.
|
||||
* @return The value.
|
||||
*/
|
||||
public double optDouble(int index, double defaultValue) {
|
||||
try {
|
||||
return this.getDouble(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional int value associated with an index. Zero is returned if
|
||||
* there is no value for the index, or if the value is not a number and
|
||||
* cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
*/
|
||||
public int optInt(int index) {
|
||||
return this.optInt(index, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional int value associated with an index. The defaultValue is
|
||||
* returned if there is no value for the index, or if the value is not a
|
||||
* number and cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue The default value.
|
||||
* @return The value.
|
||||
*/
|
||||
public int optInt(int index, int defaultValue) {
|
||||
try {
|
||||
return this.getInt(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional JSONArray associated with an index.
|
||||
*
|
||||
* @param index subscript
|
||||
* @return A JSONArray value, or null if the index has no value, or if the
|
||||
* value is not a JSONArray.
|
||||
*/
|
||||
public JSONArray optJSONArray(int index) {
|
||||
Object o = this.opt(index);
|
||||
return o instanceof JSONArray ? (JSONArray) o : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional JSONObject associated with an index. Null is returned if
|
||||
* the key is not found, or null if the index has no value, or if the value
|
||||
* is not a JSONObject.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A JSONObject value.
|
||||
*/
|
||||
public JSONObject optJSONObject(int index) {
|
||||
Object o = this.opt(index);
|
||||
return o instanceof JSONObject ? (JSONObject) o : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional long value associated with an index. Zero is returned if
|
||||
* there is no value for the index, or if the value is not a number and
|
||||
* cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
*/
|
||||
public long optLong(int index) {
|
||||
return this.optLong(index, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional long value associated with an index. The defaultValue is
|
||||
* returned if there is no value for the index, or if the value is not a
|
||||
* number and cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue The default value.
|
||||
* @return The value.
|
||||
*/
|
||||
public long optLong(int index, long defaultValue) {
|
||||
try {
|
||||
return this.getLong(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional string value associated with an index. It returns an
|
||||
* empty string if there is no value at that index. If the value is not a
|
||||
* string and is not null, then it is coverted to a string.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A String value.
|
||||
*/
|
||||
public String optString(int index) {
|
||||
return this.optString(index, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional string associated with an index. The defaultValue is
|
||||
* returned if the key is not found.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue The default value.
|
||||
* @return A String value.
|
||||
*/
|
||||
public String optString(int index, String defaultValue) {
|
||||
Object object = this.opt(index);
|
||||
return JSONObject.NULL.equals(object) ? defaultValue : object.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a boolean value. This increases the array's length by one.
|
||||
*
|
||||
* @param value A boolean value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(boolean value) {
|
||||
this.put(value ? Boolean.TRUE : Boolean.FALSE);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a JSONArray which
|
||||
* is produced from a Collection.
|
||||
*
|
||||
* @param value A Collection value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(Collection<Object> value) {
|
||||
this.put(new JSONArray(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a double value. This increases the array's length by one.
|
||||
*
|
||||
* @param value A double value.
|
||||
* @throws JSONException if the value is not finite.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(double value) throws JSONException {
|
||||
Double d = new Double(value);
|
||||
JSONObject.testValidity(d);
|
||||
this.put(d);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an int value. This increases the array's length by one.
|
||||
*
|
||||
* @param value An int value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(int value) {
|
||||
this.put(new Integer(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an long value. This increases the array's length by one.
|
||||
*
|
||||
* @param value A long value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(long value) {
|
||||
this.put(new Long(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a JSONObject which
|
||||
* is produced from a Map.
|
||||
*
|
||||
* @param value A Map value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(Map<Object, Object> value) {
|
||||
this.put(new JSONObject(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an object value. This increases the array's length by one.
|
||||
*
|
||||
* @param value An object value. The value should be a Boolean, Double,
|
||||
* Integer, JSONArray, JSONObject, Long, or String, or the JSONObject.NULL
|
||||
* object.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(Object value) {
|
||||
this.myArrayList.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace a boolean value in the JSONArray. If the index is greater
|
||||
* than the length of the JSONArray, then null elements will be added as
|
||||
* necessary to pad it out.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value A boolean value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative.
|
||||
*/
|
||||
public JSONArray put(int index, boolean value) throws JSONException {
|
||||
this.put(index, value ? Boolean.TRUE : Boolean.FALSE);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a JSONArray which
|
||||
* is produced from a Collection.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value A Collection value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the value is not
|
||||
* finite.
|
||||
*/
|
||||
public JSONArray put(int index, Collection<Object> value) throws JSONException {
|
||||
this.put(index, new JSONArray(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace a double value. If the index is greater than the length of
|
||||
* the JSONArray, then null elements will be added as necessary to pad it
|
||||
* out.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value A double value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the value is not
|
||||
* finite.
|
||||
*/
|
||||
public JSONArray put(int index, double value) throws JSONException {
|
||||
this.put(index, new Double(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace an int value. If the index is greater than the length of
|
||||
* the JSONArray, then null elements will be added as necessary to pad it
|
||||
* out.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value An int value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative.
|
||||
*/
|
||||
public JSONArray put(int index, int value) throws JSONException {
|
||||
this.put(index, new Integer(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace a long value. If the index is greater than the length of
|
||||
* the JSONArray, then null elements will be added as necessary to pad it
|
||||
* out.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value A long value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative.
|
||||
*/
|
||||
public JSONArray put(int index, long value) throws JSONException {
|
||||
this.put(index, new Long(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a JSONObject that
|
||||
* is produced from a Map.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value The Map value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the the value is an
|
||||
* invalid number.
|
||||
*/
|
||||
public JSONArray put(int index, Map<Object, Object> value) throws JSONException {
|
||||
this.put(index, new JSONObject(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace an object value in the JSONArray. If the index is greater
|
||||
* than the length of the JSONArray, then null elements will be added as
|
||||
* necessary to pad it out.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value The value to put into the array. The value should be a
|
||||
* Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
|
||||
* JSONObject.NULL object.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the the value is an
|
||||
* invalid number.
|
||||
*/
|
||||
public JSONArray put(int index, Object value) throws JSONException {
|
||||
JSONObject.testValidity(value);
|
||||
if (index < 0) {
|
||||
throw new JSONException("JSONArray[" + index + "] not found.");
|
||||
}
|
||||
if (index < this.length()) {
|
||||
this.myArrayList.set(index, value);
|
||||
} else {
|
||||
while (index != this.length()) {
|
||||
this.put(JSONObject.NULL);
|
||||
}
|
||||
this.put(value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an index and close the hole.
|
||||
*
|
||||
* @param index The index of the element to be removed.
|
||||
* @return The value that was associated with the index, or null if there
|
||||
* was no value.
|
||||
*/
|
||||
public Object remove(int index) {
|
||||
Object o = this.opt(index);
|
||||
this.myArrayList.remove(index);
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONObject by combining a JSONArray of names with the values of
|
||||
* this JSONArray.
|
||||
*
|
||||
* @param names A JSONArray containing a list of key strings. These will be
|
||||
* paired with the values.
|
||||
* @return A JSONObject, or null if there are no names or if this JSONArray
|
||||
* has no values.
|
||||
* @throws JSONException If any of the names are null.
|
||||
*/
|
||||
public JSONObject toJSONObject(JSONArray names) throws JSONException {
|
||||
if (names == null || names.length() == 0 || this.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
JSONObject jo = new JSONObject();
|
||||
for (int i = 0; i < names.length(); i += 1) {
|
||||
jo.put(names.getString(i), this.opt(i));
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a JSON text of this JSONArray. For compactness, no unnecessary
|
||||
* whitespace is added. If it is not possible to produce a syntactically
|
||||
* correct JSON text then null will be returned instead. This could occur if
|
||||
* the array contains an invalid number.
|
||||
* <p>
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @return a printable, displayable, transmittable representation of the
|
||||
* array.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return '[' + this.join(",") + ']';
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a prettyprinted JSON text of this JSONArray. Warning: This method
|
||||
* assumes that the data structure is acyclical.
|
||||
*
|
||||
* @param indentFactor The number of spaces to add to each level of
|
||||
* indentation.
|
||||
* @return a printable, displayable, transmittable representation of the
|
||||
* object, beginning with <code>[</code> <small>(left bracket)</small>
|
||||
* and ending with <code>]</code> <small>(right bracket)</small>.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public String toString(int indentFactor) throws JSONException {
|
||||
StringWriter sw = new StringWriter();
|
||||
synchronized (sw.getBuffer()) {
|
||||
return this.write(sw, indentFactor, 0).toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the contents of the JSONArray as JSON text to a writer. For
|
||||
* compactness, no whitespace is added.
|
||||
* <p>
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @return The writer.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public Writer write(Writer writer) throws JSONException {
|
||||
return this.write(writer, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the contents of the JSONArray as JSON text to a writer. For
|
||||
* compactness, no whitespace is added.
|
||||
* <p>
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @param indentFactor The number of spaces to add to each level of
|
||||
* indentation.
|
||||
* @param indent The indention of the top level.
|
||||
* @return The writer.
|
||||
* @throws JSONException
|
||||
*/
|
||||
Writer write(Writer writer, int indentFactor, int indent) throws JSONException {
|
||||
try {
|
||||
boolean commanate = false;
|
||||
int length = this.length();
|
||||
writer.write('[');
|
||||
|
||||
if (length == 1) {
|
||||
JSONObject.writeValue(writer, this.myArrayList.get(0), indentFactor, indent);
|
||||
} else if (length != 0) {
|
||||
final int newindent = indent + indentFactor;
|
||||
|
||||
for (int i = 0; i < length; i += 1) {
|
||||
if (commanate) {
|
||||
writer.write(',');
|
||||
}
|
||||
if (indentFactor > 0) {
|
||||
writer.write('\n');
|
||||
}
|
||||
JSONObject.indent(writer, newindent);
|
||||
JSONObject.writeValue(writer, this.myArrayList.get(i), indentFactor, newindent);
|
||||
commanate = true;
|
||||
}
|
||||
if (indentFactor > 0) {
|
||||
writer.write('\n');
|
||||
}
|
||||
JSONObject.indent(writer, indent);
|
||||
}
|
||||
writer.write(']');
|
||||
return writer;
|
||||
} catch (IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/main/java/org/json/JSONException.java
Normal file
26
src/main/java/org/json/JSONException.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package org.json;
|
||||
|
||||
/**
|
||||
* The JSONException is thrown by the JSON.org classes when things are amiss.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2010-12-24
|
||||
*/
|
||||
public class JSONException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
|
||||
/**
|
||||
* Constructs a JSONException with an explanatory message.
|
||||
*
|
||||
* @param message Detail about the reason for the exception.
|
||||
*/
|
||||
public JSONException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JSONException(Throwable cause) {
|
||||
super(cause.getMessage());
|
||||
initCause(cause);
|
||||
}
|
||||
}
|
||||
1590
src/main/java/org/json/JSONObject.java
Normal file
1590
src/main/java/org/json/JSONObject.java
Normal file
File diff suppressed because it is too large
Load Diff
19
src/main/java/org/json/JSONString.java
Normal file
19
src/main/java/org/json/JSONString.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package org.json;
|
||||
|
||||
/**
|
||||
* The <code>JSONString</code> interface allows a <code>toJSONString()</code>
|
||||
* method so that a class can change the behavior of
|
||||
* <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>, and
|
||||
* <code>JSONWriter.value(</code>Object<code>)</code>. The
|
||||
* <code>toJSONString</code> method will be used instead of the default behavior
|
||||
* of using the Object's <code>toString()</code> method and quoting the result.
|
||||
*/
|
||||
public interface JSONString {
|
||||
/**
|
||||
* The <code>toJSONString</code> method allows a class to produce its own
|
||||
* JSON serialization.
|
||||
*
|
||||
* @return A strictly syntactically correct JSON text.
|
||||
*/
|
||||
public String toJSONString();
|
||||
}
|
||||
434
src/main/java/org/json/JSONTokener.java
Normal file
434
src/main/java/org/json/JSONTokener.java
Normal file
@@ -0,0 +1,434 @@
|
||||
package org.json;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* A JSONTokener takes a source string and extracts characters and tokens from
|
||||
* it. It is used by the JSONObject and JSONArray constructors to parse JSON
|
||||
* source strings.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2012-02-16
|
||||
*/
|
||||
public class JSONTokener {
|
||||
|
||||
private long character;
|
||||
private boolean eof;
|
||||
private long index;
|
||||
private long line;
|
||||
private char previous;
|
||||
private Reader reader;
|
||||
private boolean usePrevious;
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from a Reader.
|
||||
*
|
||||
* @param reader A reader.
|
||||
*/
|
||||
public JSONTokener(Reader reader) {
|
||||
this.reader = reader.markSupported() ? reader : new BufferedReader(reader);
|
||||
this.eof = false;
|
||||
this.usePrevious = false;
|
||||
this.previous = 0;
|
||||
this.index = 0;
|
||||
this.character = 1;
|
||||
this.line = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from an InputStream.
|
||||
*/
|
||||
public JSONTokener(InputStream inputStream) throws JSONException {
|
||||
this(new InputStreamReader(inputStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from a string.
|
||||
*
|
||||
* @param s A source string.
|
||||
*/
|
||||
public JSONTokener(String s) {
|
||||
this(new StringReader(s));
|
||||
}
|
||||
|
||||
/**
|
||||
* Back up one character. This provides a sort of lookahead capability, so
|
||||
* that you can test for a digit or letter before attempting to parse the
|
||||
* next number or identifier.
|
||||
*/
|
||||
public void back() throws JSONException {
|
||||
if (this.usePrevious || this.index <= 0) {
|
||||
throw new JSONException("Stepping back two steps is not supported");
|
||||
}
|
||||
this.index -= 1;
|
||||
this.character -= 1;
|
||||
this.usePrevious = true;
|
||||
this.eof = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hex value of a character (base16).
|
||||
*
|
||||
* @param c A character between '0' and '9' or between 'A' and 'F' or
|
||||
* between 'a' and 'f'.
|
||||
* @return An int between 0 and 15, or -1 if c was not a hex digit.
|
||||
*/
|
||||
public static int dehexchar(char c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
return c - ('A' - 10);
|
||||
}
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
return c - ('a' - 10);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean end() {
|
||||
return this.eof && !this.usePrevious;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the source string still contains characters that next() can
|
||||
* consume.
|
||||
*
|
||||
* @return true if not yet at the end of the source.
|
||||
*/
|
||||
public boolean more() throws JSONException {
|
||||
this.next();
|
||||
if (this.end()) {
|
||||
return false;
|
||||
}
|
||||
this.back();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next character in the source string.
|
||||
*
|
||||
* @return The next character, or 0 if past the end of the source string.
|
||||
*/
|
||||
public char next() throws JSONException {
|
||||
int c;
|
||||
if (this.usePrevious) {
|
||||
this.usePrevious = false;
|
||||
c = this.previous;
|
||||
} else {
|
||||
try {
|
||||
c = this.reader.read();
|
||||
} catch (IOException exception) {
|
||||
throw new JSONException(exception);
|
||||
}
|
||||
|
||||
if (c <= 0) { // End of stream
|
||||
this.eof = true;
|
||||
c = 0;
|
||||
}
|
||||
}
|
||||
this.index += 1;
|
||||
if (this.previous == '\r') {
|
||||
this.line += 1;
|
||||
this.character = c == '\n' ? 0 : 1;
|
||||
} else if (c == '\n') {
|
||||
this.line += 1;
|
||||
this.character = 0;
|
||||
} else {
|
||||
this.character += 1;
|
||||
}
|
||||
this.previous = (char) c;
|
||||
return this.previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume the next character, and check that it matches a specified
|
||||
* character.
|
||||
*
|
||||
* @param c The character to match.
|
||||
* @return The character.
|
||||
* @throws JSONException if the character does not match.
|
||||
*/
|
||||
public char next(char c) throws JSONException {
|
||||
char n = this.next();
|
||||
if (n != c) {
|
||||
throw this.syntaxError("Expected '" + c + "' and instead saw '" + n + "'");
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next n characters.
|
||||
*
|
||||
* @param n The number of characters to take.
|
||||
* @return A string of n characters.
|
||||
* @throws JSONException Substring bounds error if there are not n
|
||||
* characters remaining in the source string.
|
||||
*/
|
||||
public String next(int n) throws JSONException {
|
||||
if (n == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
char[] chars = new char[n];
|
||||
int pos = 0;
|
||||
|
||||
while (pos < n) {
|
||||
chars[pos] = this.next();
|
||||
if (this.end()) {
|
||||
throw this.syntaxError("Substring bounds error");
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next char in the string, skipping whitespace.
|
||||
*
|
||||
* @throws JSONException
|
||||
* @return A character, or 0 if there are no more characters.
|
||||
*/
|
||||
public char nextClean() throws JSONException {
|
||||
for (;;) {
|
||||
char c = this.next();
|
||||
if (c == 0 || c > ' ') {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the characters up to the next close quote character. Backslash
|
||||
* processing is done. The formal JSON format does not allow strings in
|
||||
* single quotes, but an implementation is allowed to accept them.
|
||||
*
|
||||
* @param quote The quoting character, either <code>"</code>
|
||||
* <small>(double quote)</small> or <code>'</code>
|
||||
* <small>(single quote)</small>.
|
||||
* @return A String.
|
||||
* @throws JSONException Unterminated string.
|
||||
*/
|
||||
public String nextString(char quote) throws JSONException {
|
||||
char c;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (;;) {
|
||||
c = this.next();
|
||||
switch (c) {
|
||||
case 0:
|
||||
case '\n':
|
||||
case '\r':
|
||||
throw this.syntaxError("Unterminated string");
|
||||
case '\\':
|
||||
c = this.next();
|
||||
switch (c) {
|
||||
case 'b':
|
||||
sb.append('\b');
|
||||
break;
|
||||
case 't':
|
||||
sb.append('\t');
|
||||
break;
|
||||
case 'n':
|
||||
sb.append('\n');
|
||||
break;
|
||||
case 'f':
|
||||
sb.append('\f');
|
||||
break;
|
||||
case 'r':
|
||||
sb.append('\r');
|
||||
break;
|
||||
case 'u':
|
||||
sb.append((char) Integer.parseInt(this.next(4), 16));
|
||||
break;
|
||||
case '"':
|
||||
case '\'':
|
||||
case '\\':
|
||||
case '/':
|
||||
sb.append(c);
|
||||
break;
|
||||
default:
|
||||
throw this.syntaxError("Illegal escape.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (c == quote) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text up but not including the specified character or the end of
|
||||
* line, whichever comes first.
|
||||
*
|
||||
* @param delimiter A delimiter character.
|
||||
* @return A string.
|
||||
*/
|
||||
public String nextTo(char delimiter) throws JSONException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (;;) {
|
||||
char c = this.next();
|
||||
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
this.back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text up but not including one of the specified delimiter
|
||||
* characters or the end of line, whichever comes first.
|
||||
*
|
||||
* @param delimiters A set of delimiter characters.
|
||||
* @return A string, trimmed.
|
||||
*/
|
||||
public String nextTo(String delimiters) throws JSONException {
|
||||
char c;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (;;) {
|
||||
c = this.next();
|
||||
if (delimiters.indexOf(c) >= 0 || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
this.back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next value. The value can be a Boolean, Double, Integer,
|
||||
* JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
|
||||
*
|
||||
* @throws JSONException If syntax error.
|
||||
*
|
||||
* @return An object.
|
||||
*/
|
||||
public Object nextValue() throws JSONException {
|
||||
char c = this.nextClean();
|
||||
String string;
|
||||
|
||||
switch (c) {
|
||||
case '"':
|
||||
case '\'':
|
||||
return this.nextString(c);
|
||||
case '{':
|
||||
this.back();
|
||||
return new JSONObject(this);
|
||||
case '[':
|
||||
this.back();
|
||||
return new JSONArray(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle unquoted text. This could be the values true, false, or null,
|
||||
* or it can be a number. An implementation (such as this one) is
|
||||
* allowed to also accept non-standard forms.
|
||||
*
|
||||
* Accumulate characters until we reach the end of the text or a
|
||||
* formatting character.
|
||||
*/
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
|
||||
sb.append(c);
|
||||
c = this.next();
|
||||
}
|
||||
this.back();
|
||||
|
||||
string = sb.toString().trim();
|
||||
if ("".equals(string)) {
|
||||
throw this.syntaxError("Missing value");
|
||||
}
|
||||
return JSONObject.stringToValue(string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip characters until the next character is the requested character. If
|
||||
* the requested character is not found, no characters are skipped.
|
||||
*
|
||||
* @param to A character to skip to.
|
||||
* @return The requested character, or zero if the requested character is
|
||||
* not found.
|
||||
*/
|
||||
public char skipTo(char to) throws JSONException {
|
||||
char c;
|
||||
try {
|
||||
long startIndex = this.index;
|
||||
long startCharacter = this.character;
|
||||
long startLine = this.line;
|
||||
this.reader.mark(1000000);
|
||||
do {
|
||||
c = this.next();
|
||||
if (c == 0) {
|
||||
this.reader.reset();
|
||||
this.index = startIndex;
|
||||
this.character = startCharacter;
|
||||
this.line = startLine;
|
||||
return c;
|
||||
}
|
||||
} while (c != to);
|
||||
} catch (IOException exc) {
|
||||
throw new JSONException(exc);
|
||||
}
|
||||
|
||||
this.back();
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a JSONException to signal a syntax error.
|
||||
*
|
||||
* @param message The error message.
|
||||
* @return A JSONException object, suitable for throwing
|
||||
*/
|
||||
public JSONException syntaxError(String message) {
|
||||
return new JSONException(message + this.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a printable string of this JSONTokener.
|
||||
*
|
||||
* @return " at {index} [character {character} line {line}]"
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return " at " + this.index + " [character " + this.character + " line " + this.line + "]";
|
||||
}
|
||||
}
|
||||
57
src/main/java/org/json/JSONUtil.java
Normal file
57
src/main/java/org/json/JSONUtil.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package org.json;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
||||
public class JSONUtil {
|
||||
|
||||
public static JSONObject toJSON(JSONEntry... entries) {
|
||||
return o(entries);
|
||||
}
|
||||
|
||||
public static JSONObject o(JSONEntry... entries) {
|
||||
JSONObject object = new JSONObject();
|
||||
for (int i = 0; i < entries.length; ++i) {
|
||||
JSONEntry e = entries[i];
|
||||
object.put(e.getKey(), e.getValue());
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
public static JSONArray a(Object... values) {
|
||||
return new JSONArray(values);
|
||||
}
|
||||
|
||||
public static JSONEntry e(String k, Object v) {
|
||||
return new JSONEntry(k, v);
|
||||
}
|
||||
|
||||
public static class JSONEntry implements Entry<String, Object> {
|
||||
|
||||
private String key;
|
||||
private Object value;
|
||||
|
||||
public JSONEntry(String key, Object value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(Object value) {
|
||||
Object oldValue = this.value;
|
||||
this.value = value;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user