Remove Slime Java Commons submodule, (#236)

This commit is contained in:
Eiren Rain
2022-09-13 03:13:06 +03:00
committed by GitHub
parent 426f914f4d
commit 1b5a4bf369
98 changed files with 32855 additions and 6 deletions

3
.gitmodules vendored
View File

@@ -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

View File

@@ -42,7 +42,6 @@ allprojects {
}
dependencies {
implementation project(':slime-java-commons')
implementation project(":solarxr-protocol")

View 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);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

View 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>,&nbsp;<code>y</code>) to polar coordinates
* (r,&nbsp;<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;
}
}

File diff suppressed because it is too large Load Diff

View 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");
}
}

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

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

View 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];
}

View 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;
}
}

View 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;
}
}

View 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;
}
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

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

View 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 {
}

View 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 {
}

View 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 {
}

View File

@@ -0,0 +1,30 @@
package io.eiren.util.ann;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* <p>
* Означает необходимость обязательной синхронизации этого меа во внешних
* методах. В аргументах передаётся название поля для синхронизации.
* </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();
}

View 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 {
}

View 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 {
}

View 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 {
}

View 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 {
}

View 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;
}
}
}

View 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);
}
}

View 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);
}

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

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

View 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) {}
}
}
}

View 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;
}
}

View 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);
}
}
}

View 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));
}
}

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

View 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;
}
}

View 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');
}
}

View 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;
}
}

View 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);
}
}

View 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;
}
}

View 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);
}
}

View 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>&nbsp;<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>
* &nbsp;<small>(comma)</small> elision.</li>
* <li>Strings may be quoted with <code>'</code>&nbsp;<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>&nbsp;<small>(left
* bracket)</small> and ends with <code>]</code> &nbsp;<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>&nbsp;<small>(left bracket)</small>
* and ending with <code>]</code> &nbsp;<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);
}
}
}

View 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);
}
}

File diff suppressed because it is too large Load Diff

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

View 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>
* &nbsp;<small>(double quote)</small> or <code>'</code>
* &nbsp;<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 + "]";
}
}

View 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;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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];
}

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

View 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>,&nbsp;<code>y</code>) to polar coordinates
* (r,&nbsp;<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;
}
}

File diff suppressed because it is too large Load Diff

View 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");
}
}

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

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

View File

@@ -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");

View 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;
}
}

View 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;
}
}

View 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;
}
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

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

View 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 {
}

View 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 {
}

View 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 {
}

View File

@@ -0,0 +1,30 @@
package io.eiren.util.ann;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* <p>
* Означает необходимость обязательной синхронизации этого меа во внешних
* методах. В аргументах передаётся название поля для синхронизации.
* </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();
}

View 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 {
}

View 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 {
}

View 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 {
}

View 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 {
}

View 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;
}
}
}

View 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);
}
}

View 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);
}

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

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

View 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) {}
}
}
}

View 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;
}
}

View 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);
}
}
}

View 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));
}
}

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

View 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;
}
}

View 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');
}
}

View 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;
}
}

View 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>&nbsp;<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>
* &nbsp;<small>(comma)</small> elision.</li>
* <li>Strings may be quoted with <code>'</code>&nbsp;<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>&nbsp;<small>(left
* bracket)</small> and ends with <code>]</code> &nbsp;<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>&nbsp;<small>(left bracket)</small>
* and ending with <code>]</code> &nbsp;<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);
}
}
}

View 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);
}
}

File diff suppressed because it is too large Load Diff

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

View 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>
* &nbsp;<small>(double quote)</small> or <code>'</code>
* &nbsp;<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 + "]";
}
}

View 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;
}
}
}