From c6b8619810c40e17545fb6efec7ac60d1cfff8c1 Mon Sep 17 00:00:00 2001
From: SeH <1s1e1h1@gmail.com>
Date: Sat, 13 Jun 2015 23:42:56 -0400
Subject: [PATCH] geometry lib stabilized

---
 .../com/syncleus/spangraph/geom/Axis.java     |   21 -
 .../spangraph/spacetime/AbstractOctree.java   |  467 +++
 .../spangraph => toxi}/geom/AABB.java         |   66 +-
 .../spangraph => toxi}/geom/Axis3D.java       |   10 +-
 .../java/toxi/geom/AxisAlignedCylinder.java   |  129 +
 .../java/toxi/geom/BernsteinPolynomial.java   |   63 +
 src/main/java/toxi/geom/BezierCurve2D.java    |  117 +
 src/main/java/toxi/geom/BezierCurve3D.java    |  111 +
 .../java/toxi/geom/BooleanShapeBuilder.java   |  135 +
 .../geom/BoxIntersector.java                  |    2 +-
 .../spangraph => toxi}/geom/Circle.java       |    6 +-
 .../geom/CircleIntersector.java               |    2 +-
 src/main/java/toxi/geom/Cone.java             |  111 +
 .../java/toxi/geom/ConvexPolygonClipper.java  |  108 +
 .../java/toxi/geom/CoordinateExtractor.java   |    6 +
 .../spangraph => toxi}/geom/Ellipse.java      |   19 +-
 src/main/java/toxi/geom/GMatrix.java          | 2644 +++++++++++++
 src/main/java/toxi/geom/GVector.java          |  879 +++++
 .../java/toxi/geom/GlobalGridTesselator.java  |   43 +
 src/main/java/toxi/geom/GridTesselator.java   |   93 +
 .../geom/Intersector2D.java                   |    2 +-
 .../geom/Intersector3D.java                   |    2 +-
 .../spangraph => toxi}/geom/IsectData2D.java  |    2 +-
 .../spangraph => toxi}/geom/IsectData3D.java  |    8 +-
 .../spangraph => toxi}/geom/Line2D.java       |   12 +-
 .../spangraph => toxi}/geom/Line3D.java       |   41 +-
 src/main/java/toxi/geom/LineStrip2D.java      |  319 ++
 src/main/java/toxi/geom/LineStrip3D.java      |  182 +
 .../java/toxi/geom/LocalGridTesselator.java   |   46 +
 src/main/java/toxi/geom/Matrix3d.java         | 3063 +++++++++++++++
 src/main/java/toxi/geom/Matrix4f.java         | 3419 +++++++++++++++++
 src/main/java/toxi/geom/Matrix4x4.java        |  815 ++++
 .../java/toxi/geom/MatrixSizeException.java   |   58 +
 .../geom/OctreeVisitor.java                   |    2 +-
 .../spangraph => toxi}/geom/Origin3D.java     |   20 +-
 .../spangraph => toxi}/geom/Plane.java        |   76 +-
 src/main/java/toxi/geom/PlaneIntersector.java |   47 +
 .../spangraph => toxi}/geom/PointCloud3D.java |   42 +-
 .../spangraph => toxi}/geom/PointOctree.java  |   69 +-
 .../geom/PointQuadtree.java                   |    2 +-
 .../spangraph => toxi}/geom/Polygon2D.java    |   46 +-
 src/main/java/toxi/geom/PolygonClipper2D.java |   45 +
 .../java/toxi/geom/PolygonTesselator.java     |   16 +
 .../geom/QuadtreeVisitor.java                 |    2 +-
 src/main/java/toxi/geom/Quaternion.java       |  511 +++
 .../spangraph => toxi}/geom/Ray2D.java        |    2 +-
 .../spangraph => toxi}/geom/Ray3D.java        |   10 +-
 .../geom/Ray3DIntersector.java                |    2 +-
 .../geom/ReadonlyVec2D.java                   |   51 +-
 .../geom/ReadonlyVec4D.java                   |    6 +-
 .../spangraph => toxi}/geom/Rect.java         |   10 +-
 src/main/java/toxi/geom/Reflector3D.java      |   58 +
 .../spangraph => toxi}/geom/Shape2D.java      |    2 +-
 .../spangraph => toxi}/geom/Shape3D.java      |    4 +-
 .../toxi/geom/SingularMatrixException.java    |   57 +
 src/main/java/toxi/geom/SpatialBins.java      |  123 +
 .../spangraph => toxi}/geom/SpatialIndex.java |    2 +-
 .../spangraph => toxi}/geom/Sphere.java       |   33 +-
 .../toxi/geom/SphereIntersectorReflector.java |  158 +
 .../spangraph => toxi}/geom/Spline2D.java     |    2 +-
 .../spangraph => toxi}/geom/Spline3D.java     |   61 +-
 .../toxi/geom/SutherlandHodgemanClipper.java  |  142 +
 .../spangraph => toxi}/geom/Triangle2D.java   |   24 +-
 .../spangraph => toxi}/geom/Triangle3D.java   |   32 +-
 .../geom/TriangleIntersector.java             |    2 +-
 .../spangraph => toxi}/geom/Vec2D.java        |   84 +-
 .../spangraph => toxi}/geom/Vec3D.java        |  325 +-
 .../spangraph => toxi}/geom/Vec4D.java        |   20 +-
 .../spangraph => toxi}/geom/VecMathUtil.java  |    2 +-
 src/main/java/toxi/geom/XAxisCylinder.java    |   52 +
 src/main/java/toxi/geom/XYZ.java              |   89 +
 src/main/java/toxi/geom/YAxisCylinder.java    |   53 +
 src/main/java/toxi/geom/ZAxisCylinder.java    |   52 +
 .../toxi/geom/mesh2d/DelaunayTriangle.java    |  192 +
 .../geom/mesh2d/DelaunayTriangulation.java    |  321 ++
 .../java/toxi/geom/mesh2d/DelaunayVertex.java |  546 +++
 src/main/java/toxi/geom/mesh2d/Voronoi.java   |  106 +
 .../java/toxi/geom/nurbs/BasicNurbsCurve.java |  179 +
 .../toxi/geom/nurbs/BasicNurbsSurface.java    |  211 +
 src/main/java/toxi/geom/nurbs/ControlNet.java |  140 +
 .../java/toxi/geom/nurbs/CurveCreator.java    |  112 +
 src/main/java/toxi/geom/nurbs/CurveUtils.java |  240 ++
 .../geom/nurbs/InterpolationException.java    |   49 +
 src/main/java/toxi/geom/nurbs/KnotVector.java |  321 ++
 .../java/toxi/geom/nurbs/NurbsCreator.java    |  815 ++++
 src/main/java/toxi/geom/nurbs/NurbsCurve.java |   98 +
 .../toxi/geom/nurbs/NurbsMeshCreator.java     |  103 +
 .../java/toxi/geom/nurbs/NurbsSurface.java    |  133 +
 .../geom/roVec3D.java}                        |  148 +-
 .../java/toxi/math/BezierInterpolation.java   |   83 +
 .../java/toxi/math/CircularInterpolation.java |   76 +
 .../java/toxi/math/CosineInterpolation.java   |   50 +
 .../toxi/math/DecimatedInterpolation.java     |   60 +
 .../toxi/math/ExponentialInterpolation.java   |   66 +
 .../java/toxi/math/InterpolateStrategy.java   |   60 +
 src/main/java/toxi/math/Interpolation2D.java  |   93 +
 .../java/toxi/math/LinearInterpolation.java   |   44 +
 .../geom => toxi/math}/MathUtils.java         |    2 +-
 .../java/toxi/math/NonLinearScaleMap.java     |   60 +
 src/main/java/toxi/math/ScaleMap.java         |  167 +
 .../java/toxi/math/SigmoidInterpolation.java  |   68 +
 src/main/java/toxi/math/SinCosLUT.java        |  118 +
 .../toxi/math/ThresholdInterpolation.java     |   49 +
 .../java/toxi/math/ZoomLensInterpolation.java |   91 +
 .../toxi/math/conversion/UnitTranslator.java  |  157 +
 .../java/toxi/math/noise/PerlinNoise.java     |  216 ++
 .../java/toxi/math/noise/SimplexNoise.java    |  542 +++
 .../java/toxi/math/waves/AMFMSineWave.java    |  123 +
 .../java/toxi/math/waves/AbstractWave.java    |  202 +
 .../java/toxi/math/waves/ConstantWave.java    |   43 +
 .../toxi/math/waves/FMHarmonicSquareWave.java |  125 +
 .../java/toxi/math/waves/FMSawtoothWave.java  |  114 +
 src/main/java/toxi/math/waves/FMSineWave.java |  105 +
 .../java/toxi/math/waves/FMSquareWave.java    |  113 +
 .../java/toxi/math/waves/FMTriangleWave.java  |   92 +
 src/main/java/toxi/math/waves/SineWave.java   |   77 +
 src/main/java/toxi/math/waves/Wave2D.java     |   47 +
 src/main/java/toxi/math/waves/WaveState.java  |   43 +
 src/main/java/toxi/util/DateUtils.java        |  133 +
 .../toxi/util/FileSequenceDescriptor.java     |  157 +
 src/main/java/toxi/util/FileUtils.java        |  480 +++
 .../java/toxi/util/datatypes/ArraySet.java    |  121 +
 .../java/toxi/util/datatypes/ArrayUtil.java   |  377 ++
 .../util/datatypes/BiasedDoubleRange.java     |  115 +
 .../toxi/util/datatypes/BiasedFloatRange.java |  115 +
 .../util/datatypes/BiasedIntegerRange.java    |  115 +
 .../java/toxi/util/datatypes/DoubleRange.java |  160 +
 .../java/toxi/util/datatypes/FloatRange.java  |  160 +
 .../java/toxi/util/datatypes/GenericSet.java  |  133 +
 .../toxi/util/datatypes/IntegerRange.java     |  157 +
 .../java/toxi/util/datatypes/IntegerSet.java  |  100 +
 .../java/toxi/util/datatypes/ItemIndex.java   |   25 +
 .../util/datatypes/SingletonRegistry.java     |   87 +
 .../toxi/util/datatypes/TypedProperties.java  |  269 ++
 .../toxi/util/datatypes/UndirectedGraph.java  |  126 +
 .../toxi/util/datatypes/UniqueItemIndex.java  |  140 +
 .../util/datatypes/WeightedRandomEntry.java   |   53 +
 .../util/datatypes/WeightedRandomSet.java     |  118 +
 .../toxi/util/events/EventDispatcher.java     |   58 +
 139 files changed, 24646 insertions(+), 690 deletions(-)
 delete mode 100644 src/main/java/com/syncleus/spangraph/geom/Axis.java
 create mode 100644 src/main/java/com/syncleus/spangraph/spacetime/AbstractOctree.java
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/AABB.java (92%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Axis3D.java (92%)
 create mode 100644 src/main/java/toxi/geom/AxisAlignedCylinder.java
 create mode 100644 src/main/java/toxi/geom/BernsteinPolynomial.java
 create mode 100644 src/main/java/toxi/geom/BezierCurve2D.java
 create mode 100644 src/main/java/toxi/geom/BezierCurve3D.java
 create mode 100644 src/main/java/toxi/geom/BooleanShapeBuilder.java
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/BoxIntersector.java (96%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Circle.java (99%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/CircleIntersector.java (98%)
 create mode 100644 src/main/java/toxi/geom/Cone.java
 create mode 100644 src/main/java/toxi/geom/ConvexPolygonClipper.java
 create mode 100644 src/main/java/toxi/geom/CoordinateExtractor.java
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Ellipse.java (92%)
 create mode 100644 src/main/java/toxi/geom/GMatrix.java
 create mode 100644 src/main/java/toxi/geom/GVector.java
 create mode 100644 src/main/java/toxi/geom/GlobalGridTesselator.java
 create mode 100644 src/main/java/toxi/geom/GridTesselator.java
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Intersector2D.java (97%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Intersector3D.java (97%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/IsectData2D.java (98%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/IsectData3D.java (93%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Line2D.java (95%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Line3D.java (91%)
 create mode 100644 src/main/java/toxi/geom/LineStrip2D.java
 create mode 100644 src/main/java/toxi/geom/LineStrip3D.java
 create mode 100644 src/main/java/toxi/geom/LocalGridTesselator.java
 create mode 100644 src/main/java/toxi/geom/Matrix3d.java
 create mode 100644 src/main/java/toxi/geom/Matrix4f.java
 create mode 100644 src/main/java/toxi/geom/Matrix4x4.java
 create mode 100644 src/main/java/toxi/geom/MatrixSizeException.java
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/OctreeVisitor.java (97%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Origin3D.java (89%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Plane.java (79%)
 create mode 100644 src/main/java/toxi/geom/PlaneIntersector.java
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/PointCloud3D.java (87%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/PointOctree.java (87%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/PointQuadtree.java (99%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Polygon2D.java (95%)
 create mode 100644 src/main/java/toxi/geom/PolygonClipper2D.java
 create mode 100644 src/main/java/toxi/geom/PolygonTesselator.java
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/QuadtreeVisitor.java (97%)
 create mode 100644 src/main/java/toxi/geom/Quaternion.java
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Ray2D.java (98%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Ray3D.java (92%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Ray3DIntersector.java (98%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/ReadonlyVec2D.java (94%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/ReadonlyVec4D.java (98%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Rect.java (99%)
 create mode 100644 src/main/java/toxi/geom/Reflector3D.java
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Shape2D.java (98%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Shape3D.java (94%)
 create mode 100644 src/main/java/toxi/geom/SingularMatrixException.java
 create mode 100644 src/main/java/toxi/geom/SpatialBins.java
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/SpatialIndex.java (89%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Sphere.java (90%)
 create mode 100644 src/main/java/toxi/geom/SphereIntersectorReflector.java
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Spline2D.java (99%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Spline3D.java (82%)
 create mode 100644 src/main/java/toxi/geom/SutherlandHodgemanClipper.java
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Triangle2D.java (93%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Triangle3D.java (93%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/TriangleIntersector.java (98%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Vec2D.java (94%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Vec3D.java (81%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/Vec4D.java (97%)
 rename src/main/java/{com/syncleus/spangraph => toxi}/geom/VecMathUtil.java (99%)
 create mode 100644 src/main/java/toxi/geom/XAxisCylinder.java
 create mode 100644 src/main/java/toxi/geom/XYZ.java
 create mode 100644 src/main/java/toxi/geom/YAxisCylinder.java
 create mode 100644 src/main/java/toxi/geom/ZAxisCylinder.java
 create mode 100644 src/main/java/toxi/geom/mesh2d/DelaunayTriangle.java
 create mode 100644 src/main/java/toxi/geom/mesh2d/DelaunayTriangulation.java
 create mode 100644 src/main/java/toxi/geom/mesh2d/DelaunayVertex.java
 create mode 100644 src/main/java/toxi/geom/mesh2d/Voronoi.java
 create mode 100644 src/main/java/toxi/geom/nurbs/BasicNurbsCurve.java
 create mode 100644 src/main/java/toxi/geom/nurbs/BasicNurbsSurface.java
 create mode 100644 src/main/java/toxi/geom/nurbs/ControlNet.java
 create mode 100644 src/main/java/toxi/geom/nurbs/CurveCreator.java
 create mode 100644 src/main/java/toxi/geom/nurbs/CurveUtils.java
 create mode 100644 src/main/java/toxi/geom/nurbs/InterpolationException.java
 create mode 100644 src/main/java/toxi/geom/nurbs/KnotVector.java
 create mode 100644 src/main/java/toxi/geom/nurbs/NurbsCreator.java
 create mode 100644 src/main/java/toxi/geom/nurbs/NurbsCurve.java
 create mode 100644 src/main/java/toxi/geom/nurbs/NurbsMeshCreator.java
 create mode 100644 src/main/java/toxi/geom/nurbs/NurbsSurface.java
 rename src/main/java/{com/syncleus/spangraph/geom/ReadonlyVec3D.java => toxi/geom/roVec3D.java} (76%)
 create mode 100644 src/main/java/toxi/math/BezierInterpolation.java
 create mode 100644 src/main/java/toxi/math/CircularInterpolation.java
 create mode 100644 src/main/java/toxi/math/CosineInterpolation.java
 create mode 100644 src/main/java/toxi/math/DecimatedInterpolation.java
 create mode 100644 src/main/java/toxi/math/ExponentialInterpolation.java
 create mode 100644 src/main/java/toxi/math/InterpolateStrategy.java
 create mode 100644 src/main/java/toxi/math/Interpolation2D.java
 create mode 100644 src/main/java/toxi/math/LinearInterpolation.java
 rename src/main/java/{com/syncleus/spangraph/geom => toxi/math}/MathUtils.java (99%)
 create mode 100644 src/main/java/toxi/math/NonLinearScaleMap.java
 create mode 100644 src/main/java/toxi/math/ScaleMap.java
 create mode 100644 src/main/java/toxi/math/SigmoidInterpolation.java
 create mode 100644 src/main/java/toxi/math/SinCosLUT.java
 create mode 100644 src/main/java/toxi/math/ThresholdInterpolation.java
 create mode 100644 src/main/java/toxi/math/ZoomLensInterpolation.java
 create mode 100644 src/main/java/toxi/math/conversion/UnitTranslator.java
 create mode 100644 src/main/java/toxi/math/noise/PerlinNoise.java
 create mode 100644 src/main/java/toxi/math/noise/SimplexNoise.java
 create mode 100644 src/main/java/toxi/math/waves/AMFMSineWave.java
 create mode 100644 src/main/java/toxi/math/waves/AbstractWave.java
 create mode 100644 src/main/java/toxi/math/waves/ConstantWave.java
 create mode 100644 src/main/java/toxi/math/waves/FMHarmonicSquareWave.java
 create mode 100644 src/main/java/toxi/math/waves/FMSawtoothWave.java
 create mode 100644 src/main/java/toxi/math/waves/FMSineWave.java
 create mode 100644 src/main/java/toxi/math/waves/FMSquareWave.java
 create mode 100644 src/main/java/toxi/math/waves/FMTriangleWave.java
 create mode 100644 src/main/java/toxi/math/waves/SineWave.java
 create mode 100644 src/main/java/toxi/math/waves/Wave2D.java
 create mode 100644 src/main/java/toxi/math/waves/WaveState.java
 create mode 100644 src/main/java/toxi/util/DateUtils.java
 create mode 100644 src/main/java/toxi/util/FileSequenceDescriptor.java
 create mode 100644 src/main/java/toxi/util/FileUtils.java
 create mode 100644 src/main/java/toxi/util/datatypes/ArraySet.java
 create mode 100644 src/main/java/toxi/util/datatypes/ArrayUtil.java
 create mode 100644 src/main/java/toxi/util/datatypes/BiasedDoubleRange.java
 create mode 100644 src/main/java/toxi/util/datatypes/BiasedFloatRange.java
 create mode 100644 src/main/java/toxi/util/datatypes/BiasedIntegerRange.java
 create mode 100644 src/main/java/toxi/util/datatypes/DoubleRange.java
 create mode 100644 src/main/java/toxi/util/datatypes/FloatRange.java
 create mode 100644 src/main/java/toxi/util/datatypes/GenericSet.java
 create mode 100644 src/main/java/toxi/util/datatypes/IntegerRange.java
 create mode 100644 src/main/java/toxi/util/datatypes/IntegerSet.java
 create mode 100644 src/main/java/toxi/util/datatypes/ItemIndex.java
 create mode 100644 src/main/java/toxi/util/datatypes/SingletonRegistry.java
 create mode 100644 src/main/java/toxi/util/datatypes/TypedProperties.java
 create mode 100644 src/main/java/toxi/util/datatypes/UndirectedGraph.java
 create mode 100644 src/main/java/toxi/util/datatypes/UniqueItemIndex.java
 create mode 100644 src/main/java/toxi/util/datatypes/WeightedRandomEntry.java
 create mode 100644 src/main/java/toxi/util/datatypes/WeightedRandomSet.java
 create mode 100644 src/main/java/toxi/util/events/EventDispatcher.java

diff --git a/src/main/java/com/syncleus/spangraph/geom/Axis.java b/src/main/java/com/syncleus/spangraph/geom/Axis.java
deleted file mode 100644
index 0418bd9..0000000
--- a/src/main/java/com/syncleus/spangraph/geom/Axis.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.syncleus.spangraph.geom;
-
-/**
- * Created by me on 6/13/15.
- */
-public enum Axis {
-
-    X(Vec3D.X_AXIS),
-    Y(Vec3D.Y_AXIS),
-    Z(Vec3D.Z_AXIS);
-
-    private final ReadonlyVec3D vector;
-
-    private Axis(ReadonlyVec3D v) {
-        this.vector = v;
-    }
-
-    public ReadonlyVec3D getVector() {
-        return vector;
-    }
-}
diff --git a/src/main/java/com/syncleus/spangraph/spacetime/AbstractOctree.java b/src/main/java/com/syncleus/spangraph/spacetime/AbstractOctree.java
new file mode 100644
index 0000000..7650bc3
--- /dev/null
+++ b/src/main/java/com/syncleus/spangraph/spacetime/AbstractOctree.java
@@ -0,0 +1,467 @@
+package com.syncleus.spangraph.spacetime;
+
+import toxi.geom.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Consumer;
+
+
+public class AbstractOctree<L> extends AABB implements Shape3D {
+
+    /**
+     * alternative tree recursion limit, number of world units when cells are
+     * not subdivided any further
+     */
+    protected float minNodeSize = 4;
+
+    /**
+     *
+     */
+    public final AbstractOctree parent;
+
+    protected AbstractOctree[] children;
+
+    protected byte numChildren;
+
+    protected Collection<XYZ> points;
+
+    protected float size, halfSize;
+
+    protected Vec3D center;
+
+    private int depth = 0;
+
+    private boolean isAutoReducing = true;
+
+    /**
+     * Constructs a new AbstractOctree node within the AABB cube volume: {o.x, o.y,
+     * o.z} ... {o.x+size, o.y+size, o.z+size}
+     *
+     * @param p
+     *            parent node
+     * @param o
+     *            tree origin
+     * @param halfSize
+     *            half length of the tree volume along a single axis
+     */
+    private AbstractOctree(AbstractOctree p, Vec3D o, float halfSize) {
+        super(o.add(halfSize, halfSize, halfSize), new Vec3D(halfSize,
+                halfSize, halfSize));
+        this.parent = p;
+        this.halfSize = halfSize;
+        this.size = halfSize * 2;
+        this.center = o;
+        this.numChildren = 0;
+        if (parent != null) {
+            depth = parent.depth + 1;
+            minNodeSize = parent.minNodeSize;
+        }
+    }
+
+    /**
+     * Constructs a new AbstractOctree node within the AABB cube volume: {o.x, o.y,
+     * o.z} ... {o.x+size, o.y+size, o.z+size}
+     *
+     * @param o
+     *            tree origin
+     * @param size
+     *            size of the tree volume along a single axis
+     */
+    public AbstractOctree(Vec3D o, float size) {
+        this(null, o, size / 2);
+    }
+
+    /**
+     * Adds all points of the collection to the octree. IMPORTANT: Points need
+     * be of type Vec3D or have subclassed it.
+     *
+     * @param points
+     *            point collection
+     * @return true, if all points have been added successfully.
+     */
+    public boolean addAll(Collection<XYZ> points) {
+        boolean addedAll = true;
+        for (XYZ p : points) {
+            addedAll &= addPoint(p);
+        }
+        return addedAll;
+    }
+
+    /**
+     * Adds a new point/particle to the tree structure. All points are stored
+     * within leaf nodes only. The tree implementation is using lazy
+     * instantiation for all intermediate tree levels.
+     *
+     * @param p
+     * @return true, if point has been added successfully
+     */
+    public boolean addPoint(final XYZ p) {
+        final float halfSize = this.halfSize;
+
+        AbstractOctree[] children = this.children;
+
+        // check if point is inside cube
+        if (containsPoint(p)) {
+            // only add points to leaves for now
+            if (halfSize <= minNodeSize) {
+                if (points == null) {
+                    points = newPointsCollection();
+                }
+                points.add(p);
+                return true;
+            } else {
+                if (children == null) {
+                    children = new AbstractOctree[8];
+                }
+                int octant = getOctantID(p, center);
+                if (children[octant] == null) {
+                    Vec3D off = center.add(new Vec3D(
+                            (octant & 1) != 0 ? halfSize : 0,
+                            (octant & 2) != 0 ? halfSize : 0,
+                            (octant & 4) != 0 ? halfSize : 0));
+                    children[octant] = new AbstractOctree(this, off,
+                            halfSize * 0.5f);
+                    numChildren++;
+                }
+                return children[octant].addPoint(p);
+            }
+        }
+        return false;
+    }
+
+    protected Collection<XYZ> newPointsCollection() {
+        return new ArrayList();
+    }
+
+
+    /**
+     * Applies the given {@link OctreeVisitor} implementation to this node and
+     * all of its children.
+     */
+    public void forEach(Consumer<AbstractOctree> visitor) {
+        visitor.accept(this);
+        if (numChildren > 0) {
+            for (AbstractOctree c : children) {
+                if (c != null) {
+                    c.forEach(visitor);
+                }
+            }
+        }
+    }
+
+    public boolean containsPoint(XYZ p) {
+        return p.isInAABB(this);
+    }
+
+    public AbstractOctree clear() {
+        numChildren = 0;
+        children = null;
+        points = null;
+        return this;
+    }
+
+    /**
+     * @return a copy of the child nodes array
+     */
+    public AbstractOctree[] getChildrenCopy() {
+        if (children != null) {
+            AbstractOctree[] clones = new AbstractOctree[8];
+            System.arraycopy(children, 0, clones, 0, 8);
+            return clones;
+        }
+        return null;
+    }
+
+    /**
+     * @return the depth
+     */
+    public int getDepth() {
+        return depth;
+    }
+
+    /**
+     * Finds the leaf node which spatially relates to the given point
+     *
+     * @return leaf node or null if point is outside the tree dimensions
+     */
+    public AbstractOctree getLeafForPoint(XYZ p) {
+        // if not a leaf node...
+        if (p.isInAABB(this)) {
+            if (numChildren > 0) {
+                int octant = getOctantID(p, center);
+                if (children[octant] != null) {
+                    return children[octant].getLeafForPoint(p);
+                }
+            } else if (points != null) {
+                return this;
+            }
+        }
+        return null;
+    }
+
+
+
+    /**
+     * Returns the minimum size of nodes (in world units). This value acts as
+     * tree recursion limit since nodes smaller than this size are not
+     * subdivided further. Leaf node are always smaller or equal to this size.
+     *
+     * @return the minimum size of tree nodes
+     */
+    public float getMinNodeSize() {
+        return minNodeSize;
+    }
+
+    public float getNodeSize() {
+        return size;
+    }
+
+    /**
+     * @return the number of child nodes (max. 8)
+     */
+    public int getNumChildren() {
+        return numChildren;
+    }
+
+    /**
+     * Computes the local child octant/cube index for the given point
+     *
+     * @param plocal
+     *            point in the node-local coordinate system
+     * @return octant index
+     */
+    protected final int getOctantID(final Vec3D plocal) {
+        float halfSize = this.halfSize;
+
+        return (plocal.x >= halfSize ? 1 : 0) + (plocal.y >= halfSize ? 2 : 0)
+                + (plocal.z >= halfSize ? 4 : 0);
+    }
+
+    /** computes getOctantID for the point subtracted by another point,
+     *  without needing to allocate a temporary object
+
+     */
+    private int getOctantID(final XYZ p, final Vec3D s) {
+        return ((p.x() - s.x) >= halfSize ? 1 : 0) + ((p.y() - s.y) >= halfSize ? 2 : 0)
+                + ((p.z() - s.z) >= halfSize ? 4 : 0);
+    }
+
+    /**
+     * @return the offset
+     */
+    public roVec3D getCenter() {
+        return center;
+    }
+
+    /**
+     * @return the parent
+     */
+    public AbstractOctree getParent() {
+        return parent;
+    }
+
+    public List<XYZ> getPoints() {
+        return getPoints(new ArrayList());
+    }
+
+    /**
+     * @return the points
+     */
+    public List<XYZ> getPoints(List<XYZ> results) {
+        if (points != null) {
+        } else if (numChildren > 0) {
+            for (int i = 0; i < 8; i++) {
+                if (children[i] != null) {
+                    List<XYZ> childPoints = children[i].getPoints();
+                    if (childPoints != null) {
+                        results.addAll(childPoints);
+                    }
+                }
+            }
+        }
+        return results;
+    }
+
+    /**
+     * Selects all stored points within the given axis-aligned bounding box.
+     *
+     * @param b
+     *            AABB
+     * @return all points with the box volume
+     */
+    public List<XYZ> getPointsWithinBox(AABB b) {
+        ArrayList<XYZ> results = null;
+        if (this.intersectsBox(b)) {
+            if (points != null) {
+                for (XYZ q : points) {
+                    if (q.isInAABB(b)) {
+                        if (results == null) {
+                            results = new ArrayList<XYZ>();
+                        }
+                        results.add(q);
+                    }
+                }
+            } else if (numChildren > 0) {
+                for (int i = 0; i < 8; i++) {
+                    if (children[i] != null) {
+                        List<XYZ> points = children[i].getPointsWithinBox(b);
+                        if (points != null) {
+                            if (results == null) {
+                                results = new ArrayList();
+                            }
+                            results.addAll(points);
+                        }
+                    }
+                }
+            }
+        }
+        return results;
+    }
+
+    /**
+     * Selects all stored points within the given sphere volume
+     *
+     * @param s
+     *            sphere
+     * @return selected points
+     */
+    @Deprecated public List<XYZ> getPointsWithinSphere(Sphere s) {
+        ArrayList<XYZ> results = null;
+        if (this.intersectsSphere(s)) {
+            if (points != null) {
+                for (XYZ q : points) {
+                    if (s.containsPoint(q)) {
+                        if (results == null) {
+                            results = new ArrayList();
+                        }
+                        results.add(q);
+                    }
+                }
+            } else if (numChildren > 0) {
+                for (int i = 0; i < 8; i++) {
+                    if (children[i] != null) {
+                        List<XYZ> points = children[i].getPointsWithinSphere(s);
+                        if (points != null) {
+                            if (results == null) {
+                                results = new ArrayList();
+                            }
+                            results.addAll(points);
+                        }
+                    }
+                }
+            }
+        }
+        return results;
+    }
+
+    public void forEachInSphere(Sphere s, Consumer<XYZ> c) {
+
+        if (this.intersectsSphere(s)) {
+            if (points != null) {
+                for (XYZ q : points) {
+                    if (s.containsPoint(q)) {
+                        c.accept(q);
+                    }
+                }
+            } else if (numChildren > 0) {
+                for (int i = 0; i < 8; i++) {
+                    AbstractOctree cc = children[i];
+                    if (cc != null) {
+                        cc.forEachInSphere(s, c);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Selects all stored points within the given sphere volume
+     *
+     * @param sphereOrigin
+     * @param clipRadius
+     * @return selected points
+     */
+    public void forEachInSphere(Vec3D sphereOrigin, float clipRadius, Consumer<XYZ> c) {
+        forEachInSphere(new Sphere(sphereOrigin, clipRadius), c);
+    }
+
+    /**
+     * @return the size
+     */
+    public float getSize() {
+        return size;
+    }
+
+    private void reduceBranch() {
+        if (points != null && points.size() == 0) {
+            points = null;
+        }
+        if (numChildren > 0) {
+            for (int i = 0; i < 8; i++) {
+                if (children[i] != null && children[i].points == null) {
+                    children[i] = null;
+                }
+            }
+        }
+        if (parent != null) {
+            parent.reduceBranch();
+        }
+    }
+
+    /**
+     * Removes a point from the tree and (optionally) tries to release memory by
+     * reducing now empty sub-branches.
+     *
+     * @param p
+     *            point to delete
+     * @return true, if the point was found & removed
+     */
+    public boolean remove(XYZ p) {
+        boolean found = false;
+        AbstractOctree leaf = getLeafForPoint(p);
+        if (leaf != null) {
+            if (leaf.points.remove(p)) {
+                found = true;
+                if (isAutoReducing && leaf.points.size() == 0) {
+                    leaf.reduceBranch();
+                }
+            }
+        }
+        return found;
+    }
+
+    public void removeAll(Collection<XYZ> points) {
+        for (XYZ p : points) {
+            remove(p);
+        }
+    }
+
+    /**
+     * @param minNodeSize
+     */
+    public void setMinNodeSize(float minNodeSize) {
+        this.minNodeSize = minNodeSize * 0.5f;
+    }
+
+    /**
+     * Enables/disables auto reduction of branches after points have been
+     * deleted from the tree. Turned off by default.
+     *
+     * @param state
+     *            true, to enable feature
+     */
+    public void setTreeAutoReduction(boolean state) {
+        isAutoReducing = state;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see toxi.geom.AABB#toString()
+     */
+    public String toString() {
+        return "<octree> offset: " + super.toString() + " size: " + size;
+    }
+}
diff --git a/src/main/java/com/syncleus/spangraph/geom/AABB.java b/src/main/java/toxi/geom/AABB.java
similarity index 92%
rename from src/main/java/com/syncleus/spangraph/geom/AABB.java
rename to src/main/java/toxi/geom/AABB.java
index 88ea269..d0a0017 100644
--- a/src/main/java/com/syncleus/spangraph/geom/AABB.java
+++ b/src/main/java/toxi/geom/AABB.java
@@ -25,7 +25,9 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
+
+import toxi.math.MathUtils;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
@@ -33,7 +35,6 @@ import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlTransient;
 import java.util.List;
 
-
 /**
  * Axis-aligned bounding box with basic intersection features for Ray, AABB and
  * Sphere classes.
@@ -65,16 +66,20 @@ public class AABB extends Vec3D implements Shape3D {
      * @param points
      * @return bounding rect
      */
-    public static final AABB getBoundingBox(List<? extends Vec3D> points) {
+    public static final AABB getBoundingBox(List<? extends XYZ> points) {
         if (points == null || points.size() == 0) {
             return null;
         }
-        Vec3D first = points.get(0);
-        Vec3D min = first.copy();
-        Vec3D max = first.copy();
-        for (Vec3D p : points) {
-            min.minSelf(p);
-            max.maxSelf(p);
+        XYZ first = points.get(0);
+        Vec3D min = new Vec3D(first);
+        Vec3D max = new Vec3D(first);
+        int n = points.size();
+        if (n > 1) {
+            for (int i = 1; i < n; i++) {
+                XYZ p = points.get(i);
+                min.minSelf(p);
+                max.maxSelf(p);
+            }
         }
         return fromMinMax(min, max);
     }
@@ -115,7 +120,7 @@ public class AABB extends Vec3D implements Shape3D {
      * @param pos
      * @param extent
      */
-    public AABB(ReadonlyVec3D pos, float extent) {
+    public AABB(roVec3D pos, float extent) {
         super(pos);
         setExtent(new Vec3D(extent, extent, extent));
     }
@@ -128,12 +133,12 @@ public class AABB extends Vec3D implements Shape3D {
      *            box dimensions (the box will be double the size in each
      *            direction)
      */
-    public AABB(ReadonlyVec3D pos, ReadonlyVec3D extent) {
+    public AABB(roVec3D pos, roVec3D extent) {
         super(pos);
         setExtent(extent);
     }
 
-    public boolean containsPoint(ReadonlyVec3D p) {
+    public boolean containsPoint(XYZ p) {
         return p.isInAABB(this);
     }
 
@@ -147,7 +152,7 @@ public class AABB extends Vec3D implements Shape3D {
 
     /**
      * Returns the current box size as new Vec3D instance (updating this vector
-     * will NOT update the box size! Use {@link #setExtent(ReadonlyVec3D)} for
+     * will NOT update the box size! Use {@link #setExtent(roVec3D)} for
      * those purposes)
      * 
      * @return box size
@@ -165,11 +170,11 @@ public class AABB extends Vec3D implements Shape3D {
         return min.copy();
     }
 
-    public Vec3D getNormalForPoint(ReadonlyVec3D p) {
+    public XYZ getNormalForPoint(roVec3D p) {
         p = p.sub(this);
         Vec3D pabs = extent.sub(p.getAbs());
         Vec3D psign = p.getSignum();
-        Vec3D normal = Vec3D.X_AXIS.scale(psign.x);
+        XYZ normal = Vec3D.X_AXIS.scale(psign.x);
         float minDist = pabs.x;
         if (pabs.y < minDist) {
             minDist = pabs.y;
@@ -188,7 +193,7 @@ public class AABB extends Vec3D implements Shape3D {
      *            point to include
      * @return itself
      */
-    public AABB growToContainPoint(ReadonlyVec3D p) {
+    public AABB growToContainPoint(roVec3D p) {
         min.minSelf(p);
         max.maxSelf(p);
         set(min.interpolateTo(max, 0.5f));
@@ -448,7 +453,7 @@ public class AABB extends Vec3D implements Shape3D {
 
     public AABB set(AABB box) {
         extent.set(box.extent);
-        return set((ReadonlyVec3D) box);
+        return set((XYZ) box);
     }
 
     /**
@@ -469,9 +474,8 @@ public class AABB extends Vec3D implements Shape3D {
      * Updates the position of the box in space and calls
      * {@link #updateBounds()} immediately
      * 
-     * @see toxi.geom.Vec3D#set(toxi.geom.Vec3D)
      */
-    public AABB set(ReadonlyVec3D v) {
+    public AABB set(XYZ v) {
         x = v.x();
         y = v.y();
         z = v.z();
@@ -486,7 +490,7 @@ public class AABB extends Vec3D implements Shape3D {
      *            new box size
      * @return itself, for method chaining
      */
-    public AABB setExtent(ReadonlyVec3D extent) {
+    public AABB setExtent(roVec3D extent) {
         this.extent = extent.copy();
         return updateBounds();
     }
@@ -582,4 +586,26 @@ public class AABB extends Vec3D implements Shape3D {
         }
         return this;
     }
+
+    public boolean contains(final XYZ v) {
+        final Vec3D min = this.min;
+        final Vec3D max = this.max;
+
+        final float x = v.x();
+        if (x < min.x || x > max.x) {
+            return false;
+        }
+
+        final float y = v.y();
+        if (y < min.y || y > max.y) {
+            return false;
+        }
+
+        final float z = v.z();
+        if (z < min.z || z > max.z) {
+            return false;
+        }
+
+        return true;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/com/syncleus/spangraph/geom/Axis3D.java b/src/main/java/toxi/geom/Axis3D.java
similarity index 92%
rename from src/main/java/com/syncleus/spangraph/geom/Axis3D.java
rename to src/main/java/toxi/geom/Axis3D.java
index 00fe4b4..aa899ab 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Axis3D.java
+++ b/src/main/java/toxi/geom/Axis3D.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 /**
  * An immutable origin + axis in 3D-Space.
@@ -53,8 +53,8 @@ public class Axis3D {
         return new Axis3D(Vec3D.Z_AXIS);
     }
 
-    public final ReadonlyVec3D origin;
-    public final ReadonlyVec3D dir;
+    public final roVec3D origin;
+    public final roVec3D dir;
 
     /**
      * Creates a new z-Axis3D object from the world origin.
@@ -77,7 +77,7 @@ public class Axis3D {
      * @param dir
      *            direction vector
      */
-    public Axis3D(ReadonlyVec3D dir) {
+    public Axis3D(roVec3D dir) {
         this(new Vec3D(), dir);
     }
 
@@ -89,7 +89,7 @@ public class Axis3D {
      * @param dir
      *            direction
      */
-    public Axis3D(ReadonlyVec3D o, ReadonlyVec3D dir) {
+    public Axis3D(roVec3D o, roVec3D dir) {
         this.origin = o;
         this.dir = dir.getNormalized();
     }
diff --git a/src/main/java/toxi/geom/AxisAlignedCylinder.java b/src/main/java/toxi/geom/AxisAlignedCylinder.java
new file mode 100644
index 0000000..3b82ca2
--- /dev/null
+++ b/src/main/java/toxi/geom/AxisAlignedCylinder.java
@@ -0,0 +1,129 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.geom;
+
+public abstract class AxisAlignedCylinder implements Shape3D {
+
+    protected Vec3D pos;
+    protected float radius;
+    protected float radiusSquared;
+    protected float length;
+
+    protected AxisAlignedCylinder(roVec3D pos, float radius, float length) {
+        this.pos = pos.copy();
+        setRadius(radius);
+        setLength(length);
+    }
+
+    /**
+     * Checks if the given point is inside the cylinder.
+     * 
+     * @param p
+     * @return true, if inside
+     */
+    public abstract boolean containsPoint(XYZ p);
+
+    /**
+     * @return the length
+     */
+    public float getLength() {
+        return length;
+    }
+
+    /**
+     * @return the cylinder's orientation axis
+     */
+    public abstract Vec3D.Axis getMajorAxis();
+
+    /**
+     * Returns the cylinder's position (centroid).
+     * 
+     * @return the pos
+     */
+    public XYZ getPosition() {
+        return pos.copy();
+    }
+
+    /**
+     * @return the cylinder radius
+     */
+    public float getRadius() {
+        return radius;
+    }
+
+    /**
+     * @param length
+     *            the length to set
+     */
+    public void setLength(float length) {
+        this.length = length;
+    }
+
+    /**
+     * @param pos
+     *            the pos to set
+     */
+    public void setPosition(Vec3D pos) {
+        this.pos.set(pos);
+    }
+
+    /**
+     * @param radius
+     */
+    public void setRadius(float radius) {
+        this.radius = radius;
+        this.radiusSquared = radius * radius;
+    }
+
+//    /**
+//     * Builds a TriangleMesh representation of the cylinder at a default
+//     * resolution 30 degrees.
+//     *
+//     * @return mesh instance
+//     */
+//    public Mesh3D toMesh() {
+//        return toMesh(12, 0);
+//    }
+//
+//    /**
+//     * Builds a TriangleMesh representation of the cylinder using the given
+//     * number of steps and start angle offset.
+//     *
+//     * @param steps
+//     * @param thetaOffset
+//     * @return mesh
+//     */
+//    public Mesh3D toMesh(int steps, float thetaOffset) {
+//        return toMesh(null, steps, thetaOffset);
+//    }
+//
+//    public Mesh3D toMesh(Mesh3D mesh, int steps, float thetaOffset) {
+//        return new Cone(pos, getMajorAxis().getVector(), radius, radius, length)
+//                .toMesh(mesh, steps, thetaOffset, true, true);
+//    }
+}
\ No newline at end of file
diff --git a/src/main/java/toxi/geom/BernsteinPolynomial.java b/src/main/java/toxi/geom/BernsteinPolynomial.java
new file mode 100644
index 0000000..33ff8e7
--- /dev/null
+++ b/src/main/java/toxi/geom/BernsteinPolynomial.java
@@ -0,0 +1,63 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.geom;
+
+/**
+ * Helper class for the spline3d classes in this package. Used to compute
+ * subdivision points of the curve.
+ */
+public class BernsteinPolynomial {
+
+    public float[] b0, b1, b2, b3;
+    public int resolution;
+
+    /**
+     * @param res
+     *            number of subdivision steps between each control point of the
+     *            spline3d
+     */
+    public BernsteinPolynomial(int res) {
+        resolution = res;
+        b0 = new float[res];
+        b1 = new float[res];
+        b2 = new float[res];
+        b3 = new float[res];
+        float t = 0;
+        float dt = 1.0f / (resolution - 1);
+        for (int i = 0; i < resolution; i++) {
+            float t1 = 1 - t;
+            float t12 = t1 * t1;
+            float t2 = t * t;
+            b0[i] = t1 * t12;
+            b1[i] = 3 * t * t12;
+            b2[i] = 3 * t2 * t1;
+            b3[i] = t * t2;
+            t += dt;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/toxi/geom/BezierCurve2D.java b/src/main/java/toxi/geom/BezierCurve2D.java
new file mode 100644
index 0000000..23690ee
--- /dev/null
+++ b/src/main/java/toxi/geom/BezierCurve2D.java
@@ -0,0 +1,117 @@
+package toxi.geom;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Standard multi-segment bezier curve implementation with optional automatic
+ * handle alignment between segments. Can be used to create closed curves which
+ * can then be converted into {@link Polygon2D} instances. Also provides curve
+ * tangent calculation feature. Usage of this class is very similar to
+ * {@link Spline2D}.
+ */
+public class BezierCurve2D {
+
+    public static Vec2D computePointInSegment(Vec2D a, Vec2D b, Vec2D c,
+            Vec2D d, float t) {
+        float it = 1.0f - t;
+        float it2 = it * it;
+        float t2 = t * t;
+        return a.scale(it2 * it).addSelf(b.scale(3 * t * it2))
+                .addSelf(c.scale(3 * t2 * it)).addSelf(d.scale(t2 * t));
+    }
+
+    public static Vec2D computeTangentInSegment(Vec2D a, Vec2D b, Vec2D c,
+            Vec2D d, float t) {
+        float t2 = t * t;
+        float x = (3 * t2 * (-a.x + 3 * b.x - 3 * c.x + d.x) + 6 * t
+                * (a.x - 2 * b.x + c.x) + 3 * (-a.x + b.x));
+        float y = (3 * t2 * (-a.y + 3 * b.y - 3 * c.y + d.y) + 6 * t
+                * (a.y - 2 * b.y + c.y) + 3 * (-a.y + b.y));
+        return new Vec2D(x, y).normalize();
+    }
+
+    private List<Vec2D> points;
+
+    public BezierCurve2D() {
+        points = new ArrayList<Vec2D>();
+    }
+
+    public BezierCurve2D(List<Vec2D> points) {
+        this.points = points;
+    }
+
+    public BezierCurve2D add(Vec2D p) {
+        points.add(p);
+        return this;
+    }
+
+    public void alignAllHandles() {
+        for (int i = 0, num = points.size() - 1; i < num; i += 3) {
+            alignHandlesForPoint(i);
+        }
+    }
+
+    public void alignHandlesForPoint(int id) {
+        if (id < points.size() - 1) {
+            Vec2D c;
+            if (id == 0 && isClosed()) {
+                c = points.get(points.size() - 2);
+            } else {
+                c = points.get(id - 1);
+            }
+            Vec2D d = points.get(id);
+            Vec2D e = points.get(id + 1);
+            Vec2D cd = d.sub(c);
+            Vec2D de = e.sub(d);
+            Vec2D cd2 = cd.interpolateTo(de, 0.5f);
+            c.set(d.sub(cd2));
+            e.set(d.add(de.interpolateToSelf(cd, 0.5f)));
+        } else {
+            throw new IllegalArgumentException("invalid point index");
+        }
+    }
+
+    /**
+     * @return true, if the curve is closed. I.e. the first and last control
+     *         point coincide.
+     */
+    public boolean isClosed() {
+        return points.get(0).equals(points.get(points.size() - 1));
+    }
+
+    /**
+     * Computes a list of intermediate curve points for all segments. For each
+     * curve segment the given number of points will be produced.
+     * 
+     * @param res
+     *            number of points per segment
+     * @return list of Vec2Ds
+     */
+    public LineStrip2D toLineStrip2D(int res) {
+        LineStrip2D strip = new LineStrip2D();
+        int i = 0;
+        int maxRes = res;
+        for (int num = points.size(); i < num - 3; i += 3) {
+            Vec2D a = points.get(i);
+            Vec2D b = points.get(i + 1);
+            Vec2D c = points.get(i + 2);
+            Vec2D d = points.get(i + 3);
+            if (i + 3 > num - 3) {
+                maxRes++;
+            }
+            for (int t = 0; t < maxRes; t++) {
+                strip.add(computePointInSegment(a, b, c, d, (float) t / res));
+            }
+        }
+        return strip;
+    }
+
+    public Polygon2D toPolygon2D(int res) {
+        Polygon2D poly = new Polygon2D(toLineStrip2D(res).getVertices());
+        if (isClosed()) {
+            poly.vertices.remove(poly.vertices.get(poly.vertices.size() - 1));
+        }
+        return poly;
+    }
+}
diff --git a/src/main/java/toxi/geom/BezierCurve3D.java b/src/main/java/toxi/geom/BezierCurve3D.java
new file mode 100644
index 0000000..409ab64
--- /dev/null
+++ b/src/main/java/toxi/geom/BezierCurve3D.java
@@ -0,0 +1,111 @@
+package toxi.geom;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Standard multi-segment bezier curve implementation with optional automatic
+ * handle alignment between segments. Also provides curve tangent calculation
+ * feature. Can be used to create closed curves. Usage of this class is very
+ * similar to {@link Spline3D}.
+ */
+public class BezierCurve3D {
+
+    public static XYZ computePointInSegment(Vec3D a, Vec3D b, Vec3D c,
+            Vec3D d, float t) {
+        float it = 1.0f - t;
+        float it2 = it * it;
+        float t2 = t * t;
+        return a.scale(it2 * it).addSelf(b.scale(3 * t * it2))
+                .addSelf(c.scale(3 * t2 * it)).addSelf(d.scale(t2 * t));
+    }
+
+    public static XYZ computeTangentInSegment(Vec3D a, Vec3D b, Vec3D c,
+            Vec3D d, float t) {
+        float t2 = t * t;
+        float x = (3 * t2 * (-a.x + 3 * b.x - 3 * c.x + d.x) + 6 * t
+                * (a.x - 2 * b.x + c.x) + 3 * (-a.x + b.x));
+        float y = (3 * t2 * (-a.y + 3 * b.y - 3 * c.y + d.y) + 6 * t
+                * (a.y - 2 * b.y + c.y) + 3 * (-a.y + b.y));
+        float z = (3 * t2 * (-a.z + 3 * b.z - 3 * c.z + d.z) + 6 * t
+                * (a.z - 2 * b.z + c.z) + 3 * (-a.z + b.z));
+        return new Vec3D(x, y, z).normalize();
+    }
+
+    private List<Vec3D> points;
+
+    public BezierCurve3D() {
+        points = new ArrayList();
+    }
+
+    public BezierCurve3D(List<Vec3D> points) {
+        this.points = points;
+    }
+
+    public BezierCurve3D add(Vec3D p) {
+        points.add(p);
+        return this;
+    }
+
+    public void alignAllHandles() {
+        for (int i = 0, num = points.size() - 1; i < num; i += 3) {
+            alignHandlesForPoint(i);
+        }
+    }
+
+    public void alignHandlesForPoint(int id) {
+        if (id < points.size() - 1) {
+            Vec3D c;
+            if (id == 0 && isClosed()) {
+                c = points.get(points.size() - 2);
+            } else {
+                c = points.get(id - 1);
+            }
+            Vec3D d = points.get(id);
+            Vec3D e = points.get(id + 1);
+            Vec3D cd = d.sub(c);
+            Vec3D de = e.sub(d);
+            Vec3D cd2 = cd.interpolateTo(de, 0.5f);
+            c.set(d.sub(cd2));
+            e.set(d.add(de.interpolateToSelf(cd, 0.5f)));
+        } else {
+            throw new IllegalArgumentException("invalid point index");
+        }
+    }
+
+    /**
+     * @return true, if the curve is closed. I.e. the first and last control
+     *         point coincide.
+     */
+    public boolean isClosed() {
+        return points.get(0).equals(points.get(points.size() - 1));
+    }
+
+    /**
+     * Computes a list of intermediate curve points for all segments. For each
+     * curve segment the given number of points will be produced.
+     * 
+     * @param res
+     *            number of points per segment
+     * @return list of Vec3Ds
+     */
+    public LineStrip3D toLineStrip3D(int res) {
+        LineStrip3D strip = new LineStrip3D();
+        int i = 0;
+        int maxRes = res;
+        for (int num = points.size(); i < num - 3; i += 3) {
+            Vec3D a = points.get(i);
+            Vec3D b = points.get(i + 1);
+            Vec3D c = points.get(i + 2);
+            Vec3D d = points.get(i + 3);
+            if (i + 3 > num - 3) {
+                maxRes++;
+            }
+            for (int t = 0; t < maxRes; t++) {
+                strip.add(computePointInSegment(a, b, c, d, (float) t / res));
+            }
+        }
+        return strip;
+    }
+
+}
diff --git a/src/main/java/toxi/geom/BooleanShapeBuilder.java b/src/main/java/toxi/geom/BooleanShapeBuilder.java
new file mode 100644
index 0000000..fdb66cb
--- /dev/null
+++ b/src/main/java/toxi/geom/BooleanShapeBuilder.java
@@ -0,0 +1,135 @@
+package toxi.geom;
+
+import java.awt.Shape;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+import java.util.List;
+
+public class BooleanShapeBuilder {
+
+    public enum Type {
+        UNION,
+        INTERSECTION,
+        DIFFERENCE,
+        XOR;
+    }
+
+    private int bezierRes;
+
+    private final Area area;
+    private final Type type;
+
+    public BooleanShapeBuilder(Type type) {
+        this(type, 8);
+    }
+
+    public BooleanShapeBuilder(Type type, int bezierRes) {
+        this.type = type;
+        this.bezierRes = bezierRes;
+        area = new Area();
+    }
+
+    public BooleanShapeBuilder addShape(Shape2D s) {
+        return combineWithArea(new Area(convertToAWTShape(s)));
+    }
+
+    public BooleanShapeBuilder combineWithArea(Area a) {
+        switch (type) {
+            case UNION:
+                area.add(a);
+                break;
+            case INTERSECTION:
+                area.intersect(a);
+                break;
+            case DIFFERENCE:
+                area.subtract(a);
+                break;
+            case XOR:
+                area.exclusiveOr(a);
+                break;
+        }
+        return this;
+    }
+
+    public List<Polygon2D> computeShapes() {
+        List<Polygon2D> shapes = new ArrayList<Polygon2D>();
+        PathIterator i = area.getPathIterator(null);
+        float[] buf = new float[6];
+        Vec2D prev = new Vec2D();
+        Polygon2D s = null;
+        while (!i.isDone()) {
+            int id = i.currentSegment(buf);
+            switch (id) {
+                case PathIterator.SEG_MOVETO:
+                    s = new Polygon2D();
+                    shapes.add(s);
+                    prev.set(buf[0], buf[1]);
+                    s.add(prev.copy());
+                    break;
+                case PathIterator.SEG_LINETO:
+                    prev.set(buf[0], buf[1]);
+                    s.add(prev.copy());
+                    break;
+                case PathIterator.SEG_CUBICTO:
+                    Vec2D pa = new Vec2D(buf[0], buf[1]);
+                    Vec2D pb = new Vec2D(buf[2], buf[3]);
+                    Vec2D pc = new Vec2D(buf[4], buf[5]);
+                    for (int t = 0; t <= bezierRes; t++) {
+                        s.add(BezierCurve2D.computePointInSegment(prev, pa, pb,
+                                pc, (float) t / bezierRes));
+                    }
+                    prev.set(pc);
+                    break;
+                case PathIterator.SEG_CLOSE:
+                    break;
+                default:
+                    throw new UnsupportedOperationException(
+                            "Unsupported path segment type: " + id);
+            }
+            i.next();
+        }
+        return shapes;
+    }
+
+    private Shape convertToAWTShape(Shape2D s) {
+        if (s instanceof Rect) {
+            Rect r = (Rect) s;
+            return new Rectangle2D.Float(r.x, r.y, r.width, r.height);
+        }
+        if (s instanceof Triangle2D) {
+            Triangle2D t = (Triangle2D) s;
+            Path2D path = new Path2D.Float();
+            path.moveTo(t.a.x, t.a.y);
+            path.lineTo(t.b.x, t.b.y);
+            path.lineTo(t.c.x, t.c.y);
+            path.closePath();
+            return path;
+        }
+        if (s instanceof Ellipse) {
+            Ellipse e = (Ellipse) s;
+            Vec2D r = e.getRadii();
+            return new Ellipse2D.Float(e.x - r.x, e.y - r.y, r.x * 2, r.y * 2);
+        }
+        if (!(s instanceof Polygon2D)) {
+            s = s.toPolygon2D();
+        }
+        Polygon2D poly = (Polygon2D) s;
+        Path2D path = new Path2D.Float();
+        Vec2D p = poly.get(0);
+        path.moveTo(p.x, p.y);
+        for (int i = 1, num = poly.getNumVertices(); i < num; i++) {
+            p = poly.get(i);
+            path.lineTo(p.x, p.y);
+        }
+        path.closePath();
+        return path;
+    }
+
+    public Area getArea() {
+        return area;
+    }
+}
diff --git a/src/main/java/com/syncleus/spangraph/geom/BoxIntersector.java b/src/main/java/toxi/geom/BoxIntersector.java
similarity index 96%
rename from src/main/java/com/syncleus/spangraph/geom/BoxIntersector.java
rename to src/main/java/toxi/geom/BoxIntersector.java
index 3c172a9..3b02acd 100644
--- a/src/main/java/com/syncleus/spangraph/geom/BoxIntersector.java
+++ b/src/main/java/toxi/geom/BoxIntersector.java
@@ -1,4 +1,4 @@
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 public class BoxIntersector implements Intersector3D {
 
diff --git a/src/main/java/com/syncleus/spangraph/geom/Circle.java b/src/main/java/toxi/geom/Circle.java
similarity index 99%
rename from src/main/java/com/syncleus/spangraph/geom/Circle.java
rename to src/main/java/toxi/geom/Circle.java
index 9762474..2127f17 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Circle.java
+++ b/src/main/java/toxi/geom/Circle.java
@@ -25,12 +25,11 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
-
-
+package toxi.geom;
 
 import java.util.List;
 
+import toxi.math.MathUtils;
 
 /**
  * This class overrides {@link Ellipse} to define a 2D circle and provides
@@ -39,7 +38,6 @@ import java.util.List;
  */
 public class Circle extends Ellipse {
 
-
     /**
      * Factory method to construct a circle which has the two given points lying
      * on its perimeter. If the points are coincident, the circle will have a
diff --git a/src/main/java/com/syncleus/spangraph/geom/CircleIntersector.java b/src/main/java/toxi/geom/CircleIntersector.java
similarity index 98%
rename from src/main/java/com/syncleus/spangraph/geom/CircleIntersector.java
rename to src/main/java/toxi/geom/CircleIntersector.java
index ba66378..70b470d 100644
--- a/src/main/java/com/syncleus/spangraph/geom/CircleIntersector.java
+++ b/src/main/java/toxi/geom/CircleIntersector.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 /**
  * This class handles Circle-Ray2D intersections by implementing the
diff --git a/src/main/java/toxi/geom/Cone.java b/src/main/java/toxi/geom/Cone.java
new file mode 100644
index 0000000..c769b72
--- /dev/null
+++ b/src/main/java/toxi/geom/Cone.java
@@ -0,0 +1,111 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.geom;
+
+/**
+ * A geometric definition of a cone (and cylinder as a special case) with
+ * support for mesh creation/representation. The class is currently still
+ * incomplete in that it doesn't provide any other features than the
+ * construction of a cone shaped mesh.
+ */
+public class Cone extends Vec3D {
+
+    public Vec3D dir;
+    public float radiusSouth;
+    public float radiusNorth;
+    public float length;
+
+    /**
+     * Constructs a new cone instance.
+     * 
+     * @param pos
+     *            centre position
+     * @param dir
+     *            direction
+     * @param rNorth
+     *            radius on the side in the forward direction
+     * @param rSouth
+     *            radius on the side in the opposite direction
+     * @param len
+     *            length of the cone
+     */
+    public Cone(roVec3D pos, roVec3D dir, float rNorth,
+            float rSouth, float len) {
+        super(pos);
+        this.dir = dir.getNormalized();
+        this.radiusNorth = rNorth;
+        this.radiusSouth = rSouth;
+        this.length = len;
+    }
+
+//    public Mesh3D toMesh(int steps) {
+//        return toMesh(steps, 0);
+//    }
+//
+//    public Mesh3D toMesh(int steps, float thetaOffset) {
+//        return toMesh(null, steps, thetaOffset, true, true);
+//    }
+//
+//    public Mesh3D toMesh(Mesh3D mesh, int steps, float thetaOffset,
+//            boolean topClosed, boolean bottomClosed) {
+//        roVec3D c = this.add(0.01f, 0.01f, 0.01f);
+//        roVec3D n = c.cross(dir.getNormalized()).normalize();
+//        Vec3D halfAxis = dir.scale(length * 0.5f);
+//        Vec3D p = sub(halfAxis);
+//        Vec3D q = add(halfAxis);
+//        Vec3D[] south = new Vec3D[steps];
+//        Vec3D[] north = new Vec3D[steps];
+//        float phi = MathUtils.TWO_PI / steps;
+//        for (int i = 0; i < steps; i++) {
+//            float theta = i * phi + thetaOffset;
+//            XYZ nr = n.getRotatedAroundAxis(dir, theta);
+//            south[i] = nr.scale(radiusSouth).addSelf(p);
+//            north[i] = nr.scale(radiusNorth).addSelf(q);
+//        }
+//        int numV = steps * 2 + 2;
+//        int numF = steps * 2 + (topClosed ? steps : 0)
+//                + (bottomClosed ? steps : 0);
+//        if (mesh == null) {
+//            mesh = new TriangleMesh("cone", numV, numF);
+//        }
+//        for (int i = 0, j = 1; i < steps; i++, j++) {
+//            if (j == steps) {
+//                j = 0;
+//            }
+//            mesh.addFace(south[i], north[i], south[j], null, null, null, null);
+//            mesh.addFace(south[j], north[i], north[j], null, null, null, null);
+//            if (bottomClosed) {
+//                mesh.addFace(p, south[i], south[j], null, null, null, null);
+//            }
+//            if (topClosed) {
+//                mesh.addFace(north[i], q, north[j], null, null, null, null);
+//            }
+//        }
+//        return mesh;
+//    }
+}
\ No newline at end of file
diff --git a/src/main/java/toxi/geom/ConvexPolygonClipper.java b/src/main/java/toxi/geom/ConvexPolygonClipper.java
new file mode 100644
index 0000000..7aa0f9c
--- /dev/null
+++ b/src/main/java/toxi/geom/ConvexPolygonClipper.java
@@ -0,0 +1,108 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.geom;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A more generic version of the Sutherland-Hodgeman algorithm to limit 2D
+ * polygons to convex clipping regions. Uses the clipping region's centroid and
+ * {@link Line2D#classifyPoint(ReadonlyVec2D)} to identify if an edge needs to
+ * be clipped or not.
+ * 
+ * More information: http://en.wikipedia.org/wiki/Sutherland-Hodgman_algorithm
+ * 
+ * @see SutherlandHodgemanClipper
+ * @since 0021
+ */
+public class ConvexPolygonClipper implements PolygonClipper2D {
+
+    protected Polygon2D bounds;
+    protected Vec2D boundsCentroid;
+
+    public ConvexPolygonClipper(Polygon2D bounds) {
+        setBounds(bounds);
+    }
+
+    public Polygon2D clipPolygon(Polygon2D poly) {
+        List<Vec2D> points = new ArrayList<Vec2D>(poly.vertices);
+        List<Vec2D> clipped = new ArrayList<Vec2D>();
+        points.add(points.get(0));
+        for (Line2D clipEdge : bounds.getEdges()) {
+            clipped.clear();
+            float sign = clipEdge.classifyPoint(boundsCentroid);
+            for (int i = 0, num = points.size() - 1; i < num; i++) {
+                Vec2D p = points.get(i);
+                Vec2D q = points.get(i + 1);
+                if (clipEdge.classifyPoint(p) == sign) {
+                    if (clipEdge.classifyPoint(q) == sign) {
+                        clipped.add(q.copy());
+                    } else {
+                        clipped.add(getClippedPosOnEdge(clipEdge, p, q));
+                    }
+                    continue;
+                }
+                if (clipEdge.classifyPoint(q) == sign) {
+                    clipped.add(getClippedPosOnEdge(clipEdge, p, q));
+                    clipped.add(q.copy());
+                }
+            }
+            if (clipped.size() > 0
+                    && clipped.get(0) != clipped.get(clipped.size() - 1)) {
+                clipped.add(clipped.get(0));
+            }
+            List<Vec2D> t = points;
+            points = clipped;
+            clipped = t;
+        }
+        return new Polygon2D(points).removeDuplicates(0.001f);
+    }
+
+    public Polygon2D getBounds() {
+        return bounds;
+    }
+
+    protected Vec2D getClippedPosOnEdge(Line2D clipEdge, Vec2D p, Vec2D q) {
+        return clipEdge.intersectLine(new Line2D(p, q)).getPos();
+    }
+
+    protected boolean isKnownVertex(List<Vec2D> list, Vec2D q) {
+        for (Vec2D p : list) {
+            if (p.equalsWithTolerance(q, 0.001f)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void setBounds(Polygon2D bounds) {
+        this.bounds = bounds;
+        this.boundsCentroid = bounds.getCentroid();
+    }
+}
diff --git a/src/main/java/toxi/geom/CoordinateExtractor.java b/src/main/java/toxi/geom/CoordinateExtractor.java
new file mode 100644
index 0000000..6cb3bfc
--- /dev/null
+++ b/src/main/java/toxi/geom/CoordinateExtractor.java
@@ -0,0 +1,6 @@
+package toxi.geom;
+
+public interface CoordinateExtractor<T> {
+
+    public float coordinate(T obj);
+}
diff --git a/src/main/java/com/syncleus/spangraph/geom/Ellipse.java b/src/main/java/toxi/geom/Ellipse.java
similarity index 92%
rename from src/main/java/com/syncleus/spangraph/geom/Ellipse.java
rename to src/main/java/toxi/geom/Ellipse.java
index bd817cc..cb3b17a 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Ellipse.java
+++ b/src/main/java/toxi/geom/Ellipse.java
@@ -25,7 +25,10 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
+
+import toxi.math.MathUtils;
+import toxi.util.datatypes.BiasedFloatRange;
 
 import java.util.List;
 
@@ -89,6 +92,7 @@ public class Ellipse extends Vec2D implements Shape2D {
      * Returns the ellipse's bounding rect.
      * 
      * @return bounding rect
+     * @see toxi.geom.Shape2D#getBounds()
      */
     public Rect getBounds() {
         return new Rect(sub(radius), add(radius));
@@ -141,15 +145,14 @@ public class Ellipse extends Vec2D implements Shape2D {
     /**
      * Creates a random point within the ellipse using a
      * {@link BiasedFloatRange} to create a more uniform distribution.
-     *
+     * 
      * @return Vec2D
      */
     public Vec2D getRandomPoint() {
-        throw new UnsupportedOperationException();
-//        float theta = MathUtils.random(MathUtils.TWO_PI);
-//        BiasedFloatRange rnd = new BiasedFloatRange(0f, 1f, 1f, MathUtils.SQRT2);
-//        return Vec2D.fromTheta(theta).scaleSelf(radius.scale(rnd.pickRandom()))
-//                .addSelf(this);
+        float theta = MathUtils.random(MathUtils.TWO_PI);
+        BiasedFloatRange rnd = new BiasedFloatRange(0f, 1f, 1f, MathUtils.SQRT2);
+        return Vec2D.fromTheta(theta).scaleSelf(radius.scale(rnd.pickRandom()))
+                .addSelf(this);
     }
 
     /**
@@ -171,7 +174,7 @@ public class Ellipse extends Vec2D implements Shape2D {
      * @param r
      * @return itself
      */
-    public Ellipse setRadii(ReadonlyVec3D r) {
+    public Ellipse setRadii(roVec3D r) {
         return setRadii(r.x(), r.y());
     }
 
diff --git a/src/main/java/toxi/geom/GMatrix.java b/src/main/java/toxi/geom/GMatrix.java
new file mode 100644
index 0000000..ff4998e
--- /dev/null
+++ b/src/main/java/toxi/geom/GMatrix.java
@@ -0,0 +1,2644 @@
+/*
+ * Copyright 1997-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package toxi.geom;
+
+import toxi.math.MathUtils;
+
+/**
+ * A double precision, row major, general and dynamically-resizable,
+ * two-dimensional matrix class. Row and column numbering begins with zero.
+ */
+public class GMatrix implements java.io.Serializable, Cloneable {
+
+    static final long serialVersionUID = 1L;
+
+    /**
+     * Solves a set of linear equations. The input parameters "matrix1", and
+     * "row_perm" come from luDecompostion and do not change here. The parameter
+     * "matrix2" is a set of column vectors assembled into a nxn matrix of
+     * floating-point values. The procedure takes each column of "matrix2" in
+     * turn and treats it as the right-hand side of the matrix equation Ax = LUx
+     * = b. The solution vector replaces the original column of the matrix.
+     * 
+     * If "matrix2" is the identity matrix, the procedure replaces its contents
+     * with the inverse of the matrix from which "matrix1" was originally
+     * derived.
+     */
+    //
+    // Reference: Press, Flannery, Teukolsky, Vetterling,
+    // _Numerical_Recipes_in_C_, Cambridge University Press,
+    // 1988, pp 44-45.
+    //
+    public static void backSubstituteLU(int dim, double[] matrix1,
+            int[] row_perm, double[] matrix2) {
+
+        int i, ii, ip, j, k;
+        int rp;
+        int cv, rv, ri;
+        double tt;
+
+        // rp = row_perm;
+        rp = 0;
+
+        // For each column vector of matrix2 ...
+        for (k = 0; k < dim; k++) {
+            // cv = &(matrix2[0][k]);
+            cv = k;
+            ii = -1;
+
+            // Forward substitution
+            for (i = 0; i < dim; i++) {
+                double sum;
+
+                ip = row_perm[rp + i];
+                sum = matrix2[cv + dim * ip];
+                matrix2[cv + dim * ip] = matrix2[cv + dim * i];
+                if (ii >= 0) {
+                    // rv = &(matrix1[i][0]);
+                    rv = i * dim;
+                    for (j = ii; j <= i - 1; j++) {
+                        sum -= matrix1[rv + j] * matrix2[cv + dim * j];
+                    }
+                } else if (sum != 0.0) {
+                    ii = i;
+                }
+                matrix2[cv + dim * i] = sum;
+            }
+
+            // Backsubstitution
+            for (i = 0; i < dim; i++) {
+                ri = (dim - 1 - i);
+                rv = dim * (ri);
+                tt = 0.0;
+                for (j = 1; j <= i; j++) {
+                    tt += matrix1[rv + dim - j] * matrix2[cv + dim * (dim - j)];
+                }
+                matrix2[cv + dim * ri] = (matrix2[cv + dim * ri] - tt)
+                        / matrix1[rv + ri];
+            }
+        }
+    }
+
+    private static void chase_across(double[] s, double[] e, int k, GMatrix u) {
+        double f, g, r;
+        double[] cosl = new double[1];
+        double[] sinl = new double[1];
+        int i;
+        GMatrix t = new GMatrix(u.nRow, u.nCol);
+        GMatrix m = new GMatrix(u.nRow, u.nCol);
+
+        g = e[k];
+        f = s[k + 1];
+
+        for (i = k; i < u.nCol - 2; i++) {
+            r = compute_rot(f, g, sinl, cosl);
+            g = -e[i + 1] * sinl[0];
+            f = s[i + 2];
+            s[i + 1] = r;
+            e[i + 1] = e[i + 1] * cosl[0];
+            update_u_split(k, i + 1, u, cosl, sinl, t, m);
+        }
+
+        s[i + 1] = compute_rot(f, g, sinl, cosl);
+        update_u_split(k, i + 1, u, cosl, sinl, t, m);
+    }
+
+    private static void chase_up(double[] s, double[] e, int k, GMatrix v) {
+        double f, g, r;
+        double[] cosr = new double[1];
+        double[] sinr = new double[1];
+        int i;
+        GMatrix t = new GMatrix(v.nRow, v.nCol);
+        GMatrix m = new GMatrix(v.nRow, v.nCol);
+
+        f = e[k];
+        g = s[k];
+
+        for (i = k; i > 0; i--) {
+            r = compute_rot(f, g, sinr, cosr);
+            f = -e[i - 1] * sinr[0];
+            g = s[i - 1];
+            s[i] = r;
+            e[i - 1] = e[i - 1] * cosr[0];
+            update_v_split(i, k + 1, v, cosr, sinr, t, m);
+        }
+
+        s[i + 1] = compute_rot(f, g, sinr, cosr);
+        update_v_split(i, k + 1, v, cosr, sinr, t, m);
+    }
+
+    private static void checkMatrix(GMatrix m) {
+        int i, j;
+
+        for (i = 0; i < m.nRow; i++) {
+            for (j = 0; j < m.nCol; j++) {
+                if (MathUtils.abs(m.values[i][j]) < 0.0000000001) {
+                    System.out.print(" 0.0     ");
+                } else {
+                    System.out.print(" " + m.values[i][j]);
+                }
+            }
+            System.out.print("\n");
+        }
+    }
+
+    private static int compute_2X2(double f, double g, double h,
+            double[] single_values, double[] snl, double[] csl, double[] snr,
+            double[] csr, int index) {
+
+        double c_b3 = 2.0;
+        double c_b4 = 1.0;
+
+        double d__1;
+        int pmax;
+        double temp;
+        boolean swap;
+        double a, d, l, m, r, s, t, tsign, fa, ga, ha;
+        double ft, gt, ht, mm;
+        boolean gasmal;
+        double tt, clt, crt, slt, srt;
+        double ssmin, ssmax;
+
+        ssmax = single_values[0];
+        ssmin = single_values[1];
+        clt = 0.0;
+        crt = 0.0;
+        slt = 0.0;
+        srt = 0.0;
+        tsign = 0.0;
+
+        ft = f;
+        fa = MathUtils.abs(ft);
+        ht = h;
+        ha = MathUtils.abs(h);
+
+        pmax = 1;
+        if (ha > fa) {
+            swap = true;
+        } else {
+            swap = false;
+        }
+
+        if (swap) {
+            pmax = 3;
+            temp = ft;
+            ft = ht;
+            ht = temp;
+            temp = fa;
+            fa = ha;
+            ha = temp;
+
+        }
+
+        gt = g;
+        ga = MathUtils.abs(gt);
+        if (ga == 0.0) {
+            single_values[1] = ha;
+            single_values[0] = fa;
+            clt = 1.0;
+            crt = 1.0;
+            slt = 0.0;
+            srt = 0.0;
+        } else {
+            gasmal = true;
+            if (ga > fa) {
+                pmax = 2;
+                if (fa / ga < EPS) {
+                    gasmal = false;
+                    ssmax = ga;
+
+                    if (ha > 1.0) {
+                        ssmin = fa / (ga / ha);
+                    } else {
+                        ssmin = fa / ga * ha;
+                    }
+                    clt = 1.0;
+                    slt = ht / gt;
+                    srt = 1.0;
+                    crt = ft / gt;
+                }
+            }
+            if (gasmal) {
+                d = fa - ha;
+                if (d == fa) {
+
+                    l = 1.0;
+                } else {
+                    l = d / fa;
+                }
+
+                m = gt / ft;
+                t = 2.0 - l;
+                mm = m * m;
+                tt = t * t;
+                s = Math.sqrt(tt + mm);
+
+                if (l == 0.0) {
+                    r = MathUtils.abs(m);
+                } else {
+                    r = Math.sqrt(l * l + mm);
+                }
+
+                a = (s + r) * 0.5;
+                if (ga > fa) {
+                    pmax = 2;
+                    if (fa / ga < EPS) {
+                        gasmal = false;
+                        ssmax = ga;
+                        if (ha > 1.0) {
+                            ssmin = fa / (ga / ha);
+                        } else {
+                            ssmin = fa / ga * ha;
+                        }
+                        clt = 1.0;
+                        slt = ht / gt;
+                        srt = 1.0;
+                        crt = ft / gt;
+                    }
+                }
+                if (gasmal) {
+                    d = fa - ha;
+                    if (d == fa) {
+                        l = 1.0;
+                    } else {
+                        l = d / fa;
+                    }
+
+                    m = gt / ft;
+                    t = 2.0 - l;
+
+                    mm = m * m;
+                    tt = t * t;
+                    s = Math.sqrt(tt + mm);
+
+                    if (l == 0.) {
+                        r = MathUtils.abs(m);
+                    } else {
+                        r = Math.sqrt(l * l + mm);
+                    }
+
+                    a = (s + r) * 0.5;
+                    ssmin = ha / a;
+                    ssmax = fa * a;
+
+                    if (mm == 0.0) {
+                        if (l == 0.0) {
+                            t = MathUtils.dualSign(c_b3, ft)
+                                    * MathUtils.dualSign(c_b4, gt);
+                        } else {
+                            t = gt / MathUtils.dualSign(d, ft) + m / t;
+                        }
+                    } else {
+                        t = (m / (s + t) + m / (r + l)) * (a + 1.0);
+                    }
+
+                    l = Math.sqrt(t * t + 4.0);
+                    crt = 2.0 / l;
+                    srt = t / l;
+                    clt = (crt + srt * m) / a;
+                    slt = ht / ft * srt / a;
+                }
+            }
+            if (swap) {
+                csl[0] = srt;
+                snl[0] = crt;
+                csr[0] = slt;
+                snr[0] = clt;
+            } else {
+                csl[0] = clt;
+                snl[0] = slt;
+                csr[0] = crt;
+                snr[0] = srt;
+            }
+
+            if (pmax == 1) {
+                tsign = MathUtils.dualSign(c_b4, csr[0])
+                        * MathUtils.dualSign(c_b4, csl[0])
+                        * MathUtils.dualSign(c_b4, f);
+            }
+            if (pmax == 2) {
+                tsign = MathUtils.dualSign(c_b4, snr[0])
+                        * MathUtils.dualSign(c_b4, csl[0])
+                        * MathUtils.dualSign(c_b4, g);
+            }
+            if (pmax == 3) {
+                tsign = MathUtils.dualSign(c_b4, snr[0])
+                        * MathUtils.dualSign(c_b4, snl[0])
+                        * MathUtils.dualSign(c_b4, h);
+            }
+
+            single_values[index] = MathUtils.dualSign(ssmax, tsign);
+            d__1 = tsign * MathUtils.dualSign(c_b4, f)
+                    * MathUtils.dualSign(c_b4, h);
+            single_values[index + 1] = MathUtils.dualSign(ssmin, d__1);
+        }
+
+        return 0;
+    }
+
+    private static double compute_rot(double f, double g, double[] sin,
+            double[] cos) {
+        double cs, sn;
+        int i;
+        double scale;
+        int count;
+        double f1, g1;
+        double r;
+        final double safmn2 = 2.002083095183101E-146;
+        final double safmx2 = 4.994797680505588E+145;
+
+        if (g == 0.0) {
+            cs = 1.0;
+            sn = 0.0;
+            r = f;
+        } else if (f == 0.0) {
+            cs = 0.0;
+            sn = 1.0;
+            r = g;
+        } else {
+            f1 = f;
+            g1 = g;
+            scale = MathUtils.max(MathUtils.abs(f1), MathUtils.abs(g1));
+            if (scale >= safmx2) {
+                count = 0;
+                while (scale >= safmx2) {
+                    ++count;
+                    f1 *= safmn2;
+                    g1 *= safmn2;
+                    scale = MathUtils.max(MathUtils.abs(f1), MathUtils.abs(g1));
+                }
+                r = Math.sqrt(f1 * f1 + g1 * g1);
+                cs = f1 / r;
+                sn = g1 / r;
+                for (i = 1; i <= count; ++i) {
+                    r *= safmx2;
+                }
+            } else if (scale <= safmn2) {
+                count = 0;
+                while (scale <= safmn2) {
+                    ++count;
+                    f1 *= safmx2;
+                    g1 *= safmx2;
+                    scale = MathUtils.max(MathUtils.abs(f1), MathUtils.abs(g1));
+                }
+                r = Math.sqrt(f1 * f1 + g1 * g1);
+                cs = f1 / r;
+                sn = g1 / r;
+                for (i = 1; i <= count; ++i) {
+                    r *= safmn2;
+                }
+            } else {
+                r = Math.sqrt(f1 * f1 + g1 * g1);
+                cs = f1 / r;
+                sn = g1 / r;
+            }
+            if (MathUtils.abs(f) > MathUtils.abs(g) && cs < 0.0) {
+                cs = -cs;
+                sn = -sn;
+                r = -r;
+            }
+        }
+        sin[0] = sn;
+        cos[0] = cs;
+        return r;
+    }
+
+    private static double compute_shift(double f, double g, double h) {
+        double d__1, d__2;
+        double fhmn, fhmx, c, fa, ga, ha, as, at, au;
+        double ssmin;
+
+        fa = MathUtils.abs(f);
+        ga = MathUtils.abs(g);
+        ha = MathUtils.abs(h);
+        fhmn = MathUtils.min(fa, ha);
+        fhmx = MathUtils.max(fa, ha);
+
+        if (fhmn == 0.0) {
+            ssmin = 0.0;
+            if (fhmx == 0.0) {
+            } else {
+                d__1 = MathUtils.min(fhmx, ga) / MathUtils.max(fhmx, ga);
+            }
+        } else {
+            if (ga < fhmx) {
+                as = fhmn / fhmx + 1.0;
+                at = (fhmx - fhmn) / fhmx;
+                d__1 = ga / fhmx;
+                au = d__1 * d__1;
+                c = 2.0 / (Math.sqrt(as * as + au) + Math.sqrt(at * at + au));
+                ssmin = fhmn * c;
+            } else {
+                au = fhmx / ga;
+                if (au == 0.0) {
+                    ssmin = fhmn * fhmx / ga;
+                } else {
+                    as = fhmn / fhmx + 1.0;
+                    at = (fhmx - fhmn) / fhmx;
+                    d__1 = as * au;
+                    d__2 = at * au;
+                    c = 1.0 / (Math.sqrt(d__1 * d__1 + 1.0) + Math.sqrt(d__2
+                            * d__2 + 1.0));
+                    ssmin = fhmn * c * au;
+                    ssmin += ssmin;
+                }
+            }
+        }
+
+        return ssmin;
+    }
+
+    public static void computeQR(int start, int end, double[] s, double[] e,
+            GMatrix u, GMatrix v) {
+
+        int i, k, n, sl;
+        double shift, r, f, g;
+        double[] cosl = new double[1];
+        double[] cosr = new double[1];
+        double[] sinl = new double[1];
+        double[] sinr = new double[1];
+
+        final int MAX_INTERATIONS = 2;
+        final double CONVERGE_TOL = 4.89E-15;
+
+        boolean converged = false;
+
+        f = 0.0;
+        g = 0.0;
+
+        for (k = 0; k < MAX_INTERATIONS && !converged; k++) {
+            for (i = start; i <= end; i++) {
+
+                // if at start of iterfaction compute shift
+                if (i == start) {
+                    if (e.length == s.length) {
+                        sl = end;
+                    } else {
+                        sl = end + 1;
+                    }
+
+                    shift = compute_shift(s[sl - 1], e[end], s[sl]);
+
+                    f = (MathUtils.abs(s[i]) - shift)
+                            * (MathUtils.dualSign(1.0, s[i]) + shift / s[i]);
+                    g = e[i];
+                }
+
+                r = compute_rot(f, g, sinr, cosr);
+                if (i != start) {
+                    e[i - 1] = r;
+                }
+
+                f = cosr[0] * s[i] + sinr[0] * e[i];
+                e[i] = cosr[0] * e[i] - sinr[0] * s[i];
+                g = sinr[0] * s[i + 1];
+                s[i + 1] = cosr[0] * s[i + 1];
+
+                update_v(i, v, cosr, sinr);
+
+                r = compute_rot(f, g, sinl, cosl);
+                s[i] = r;
+                f = cosl[0] * e[i] + sinl[0] * s[i + 1];
+                s[i + 1] = cosl[0] * s[i + 1] - sinl[0] * e[i];
+
+                if (i < end) {
+                    // if not last
+                    g = sinl[0] * e[i + 1];
+                    e[i + 1] = cosl[0] * e[i + 1];
+                }
+                update_u(i, u, cosl, sinl);
+            }
+
+            // if extra off diagonal perform one more right side rotation
+            if (s.length == e.length) {
+                r = compute_rot(f, g, sinr, cosr);
+                f = cosr[0] * s[i] + sinr[0] * e[i];
+                e[i] = cosr[0] * e[i] - sinr[0] * s[i];
+                s[i + 1] = cosr[0] * s[i + 1];
+
+                update_v(i, v, cosr, sinr);
+            }
+
+            // check for convergence on off diagonals and reduce
+            while ((end - start > 1) && (MathUtils.abs(e[end]) < CONVERGE_TOL)) {
+                end--;
+            }
+
+            // check if need to split
+            for (n = end - 2; n > start; n--) {
+                if (MathUtils.abs(e[n]) < CONVERGE_TOL) { // split
+                    computeQR(n + 1, end, s, e, u, v); // do lower matrix
+                    end = n - 1; // do upper matrix
+
+                    // check for convergence on off diagonals and reduce
+                    while ((end - start > 1)
+                            && (MathUtils.abs(e[end]) < CONVERGE_TOL)) {
+                        end--;
+                    }
+                }
+            }
+
+            if ((end - start <= 1)
+                    && (MathUtils.abs(e[start + 1]) < CONVERGE_TOL)) {
+                converged = true;
+            } else {
+                // check if zero on the diagonal
+            }
+
+        }
+
+        if (MathUtils.abs(e[1]) < CONVERGE_TOL) {
+            compute_2X2(s[start], e[start], s[start + 1], s, sinl, cosl, sinr,
+                    cosr, 0);
+            e[start] = 0.0;
+            e[start + 1] = 0.0;
+        }
+
+        i = start;
+        update_u(i, u, cosl, sinl);
+        update_v(i, v, cosr, sinr);
+    }
+
+    public static int computeSVD(GMatrix mat, GMatrix U, GMatrix W, GMatrix V) {
+        int i, j, k;
+        int nr, nc, si;
+
+        int rank;
+        double mag, scale, t;
+        int eLength, sLength, vecLength;
+
+        GMatrix tmp = new GMatrix(mat.nRow, mat.nCol);
+        GMatrix u = new GMatrix(mat.nRow, mat.nCol);
+        GMatrix v = new GMatrix(mat.nRow, mat.nCol);
+        GMatrix m = new GMatrix(mat);
+
+        // compute the number of singular values
+        if (m.nRow >= m.nCol) {
+            sLength = m.nCol;
+            eLength = m.nCol - 1;
+        } else {
+            sLength = m.nRow;
+            eLength = m.nRow;
+        }
+
+        if (m.nRow > m.nCol) {
+            vecLength = m.nRow;
+        } else {
+            vecLength = m.nCol;
+        }
+
+        double[] vec = new double[vecLength];
+        double[] single_values = new double[sLength];
+        double[] e = new double[eLength];
+
+        rank = 0;
+
+        U.identity();
+        V.identity();
+
+        nr = m.nRow;
+        nc = m.nCol;
+
+        // householder reduction
+        for (si = 0; si < sLength; si++) {
+            // for each singular value
+
+            if (nr > 1) {
+                // compute reflector
+                mag = 0.0;
+                for (i = 0; i < nr; i++) {
+                    mag += m.values[i + si][si] * m.values[i + si][si];
+                }
+
+                mag = Math.sqrt(mag);
+                if (m.values[si][si] == 0.0) {
+                    vec[0] = mag;
+                } else {
+                    vec[0] = m.values[si][si]
+                            + MathUtils.dualSign(mag, m.values[si][si]);
+                }
+
+                for (i = 1; i < nr; i++) {
+                    vec[i] = m.values[si + i][si];
+                }
+
+                scale = 0.0;
+                for (i = 0; i < nr; i++) {
+                    scale += vec[i] * vec[i];
+                }
+
+                scale = 2.0 / scale;
+                for (j = si; j < m.nRow; j++) {
+                    for (k = si; k < m.nRow; k++) {
+                        u.values[j][k] = -scale * vec[j - si] * vec[k - si];
+                    }
+                }
+
+                for (i = si; i < m.nRow; i++) {
+                    u.values[i][i] += 1.0;
+                }
+
+                // compute s
+                t = 0.0;
+                for (i = si; i < m.nRow; i++) {
+                    t += u.values[si][i] * m.values[i][si];
+                }
+                m.values[si][si] = t;
+
+                // apply reflector
+                for (j = si; j < m.nRow; j++) {
+                    for (k = si + 1; k < m.nCol; k++) {
+                        tmp.values[j][k] = 0.0;
+                        for (i = si; i < m.nCol; i++) {
+                            tmp.values[j][k] += u.values[j][i] * m.values[i][k];
+                        }
+                    }
+                }
+
+                for (j = si; j < m.nRow; j++) {
+                    for (k = si + 1; k < m.nCol; k++) {
+                        m.values[j][k] = tmp.values[j][k];
+                    }
+                }
+
+                // update U matrix
+                for (j = si; j < m.nRow; j++) {
+                    for (k = 0; k < m.nCol; k++) {
+                        tmp.values[j][k] = 0.0;
+                        for (i = si; i < m.nCol; i++) {
+                            tmp.values[j][k] += u.values[j][i] * U.values[i][k];
+                        }
+                    }
+                }
+
+                for (j = si; j < m.nRow; j++) {
+                    for (k = 0; k < m.nCol; k++) {
+                        U.values[j][k] = tmp.values[j][k];
+                    }
+                }
+                nr--;
+            }
+
+            if (nc > 2) {
+                mag = 0.0;
+                for (i = 1; i < nc; i++) {
+                    mag += m.values[si][si + i] * m.values[si][si + i];
+                }
+                // generate the reflection vector, compute the first entry and
+                // copy the rest from the row to be zeroed
+                mag = Math.sqrt(mag);
+                if (m.values[si][si + 1] == 0.0) {
+                    vec[0] = mag;
+                } else {
+                    vec[0] = m.values[si][si + 1]
+                            + MathUtils.dualSign(mag, m.values[si][si + 1]);
+                }
+
+                for (i = 1; i < nc - 1; i++) {
+                    vec[i] = m.values[si][si + i + 1];
+                }
+
+                // use reflection vector to compute v matrix
+                scale = 0.0;
+                for (i = 0; i < nc - 1; i++) {
+                    scale += vec[i] * vec[i];
+                }
+
+                scale = 2.0 / scale;
+                for (j = si + 1; j < nc; j++) {
+                    for (k = si + 1; k < m.nCol; k++) {
+                        v.values[j][k] = -scale * vec[j - si - 1]
+                                * vec[k - si - 1];
+                    }
+                }
+
+                for (i = si + 1; i < m.nCol; i++) {
+                    v.values[i][i] += 1.0;
+                }
+
+                t = 0.0;
+                for (i = si; i < m.nCol; i++) {
+                    t += v.values[i][si + 1] * m.values[si][i];
+                }
+                m.values[si][si + 1] = t;
+
+                // apply reflector
+                for (j = si + 1; j < m.nRow; j++) {
+                    for (k = si + 1; k < m.nCol; k++) {
+                        tmp.values[j][k] = 0.0;
+                        for (i = si + 1; i < m.nCol; i++) {
+                            tmp.values[j][k] += v.values[i][k] * m.values[j][i];
+                        }
+                    }
+                }
+
+                for (j = si + 1; j < m.nRow; j++) {
+                    for (k = si + 1; k < m.nCol; k++) {
+                        m.values[j][k] = tmp.values[j][k];
+                    }
+                }
+
+                // update V matrix
+                for (j = 0; j < m.nRow; j++) {
+                    for (k = si + 1; k < m.nCol; k++) {
+                        tmp.values[j][k] = 0.0;
+                        for (i = si + 1; i < m.nCol; i++) {
+                            tmp.values[j][k] += v.values[i][k] * V.values[j][i];
+                        }
+                    }
+                }
+
+                for (j = 0; j < m.nRow; j++) {
+                    for (k = si + 1; k < m.nCol; k++) {
+                        V.values[j][k] = tmp.values[j][k];
+                    }
+                }
+                nc--;
+            }
+        }
+
+        for (i = 0; i < sLength; i++) {
+            single_values[i] = m.values[i][i];
+        }
+
+        for (i = 0; i < eLength; i++) {
+            e[i] = m.values[i][i + 1];
+        }
+
+        // Fix ArrayIndexOutOfBounds for 2x2 matrices, which partially
+        // addresses bug 4348562 for J3D 1.2.1.
+        //
+        // Does *not* fix the following problems reported in 4348562,
+        // which will wait for J3D 1.3:
+        //
+        // 1) no output of W
+        // 2) wrong transposition of U
+        // 3) wrong results for 4x4 matrices
+        // 4) slow performance
+        if (m.nRow == 2 && m.nCol == 2) {
+            double[] cosl = new double[1];
+            double[] cosr = new double[1];
+            double[] sinl = new double[1];
+            double[] sinr = new double[1];
+
+            compute_2X2(single_values[0], e[0], single_values[1],
+                    single_values, sinl, cosl, sinr, cosr, 0);
+
+            update_u(0, U, cosl, sinl);
+            update_v(0, V, cosr, sinr);
+            return 2;
+        }
+
+        // compute_qr causes ArrayIndexOutOfBounds for 2x2 matrices
+        computeQR(0, e.length - 1, single_values, e, U, V);
+
+        // compute rank = number of non zero singular values
+        rank = single_values.length;
+
+        // sort by order of size of single values
+        // and check for zero's
+        return rank;
+    }
+
+    /**
+     * Given a nxn array "matrix0", this function replaces it with the LU
+     * decomposition of a row-wise permutation of itself. The input parameters
+     * are "matrix0" and "dim". The array "matrix0" is also an output parameter.
+     * The vector "row_perm[]" is an output parameter that contains the row
+     * permutations resulting from partial pivoting. The output parameter
+     * "even_row_xchg" is 1 when the number of row exchanges is even, or -1
+     * otherwise. Assumes data type is always double.
+     * 
+     * @return true if the matrix is nonsingular, or false otherwise.
+     */
+    //
+    // Reference: Press, Flannery, Teukolsky, Vetterling,
+    // _Numerical_Recipes_in_C_, Cambridge University Press,
+    // 1988, pp 40-45.
+    //
+    public static boolean decomposeLU(int dim, double[] matrix0,
+            int[] row_perm, int[] even_row_xchg) {
+
+        double row_scale[] = new double[dim];
+
+        // Determine implicit scaling information by looping over rows
+        int i, j;
+        int ptr, rs, mtx;
+        double big, temp;
+
+        ptr = 0;
+        rs = 0;
+        even_row_xchg[0] = 1;
+
+        // For each row ...
+        i = dim;
+        while (i-- != 0) {
+            big = 0.0;
+
+            // For each column, find the largest element in the row
+            j = dim;
+            while (j-- != 0) {
+                temp = matrix0[ptr++];
+                temp = MathUtils.abs(temp);
+                if (temp > big) {
+                    big = temp;
+                }
+            }
+
+            // Is the matrix singular?
+            if (big == 0.0) {
+                return false;
+            }
+            row_scale[rs++] = 1.0 / big;
+        }
+
+        // For all columns, execute Crout's method
+        mtx = 0;
+        for (j = 0; j < dim; j++) {
+            int imax, k;
+            int target, p1, p2;
+            double sum;
+
+            // Determine elements of upper diagonal matrix U
+            for (i = 0; i < j; i++) {
+                target = mtx + (dim * i) + j;
+                sum = matrix0[target];
+                k = i;
+                p1 = mtx + (dim * i);
+                p2 = mtx + j;
+                while (k-- != 0) {
+                    sum -= matrix0[p1] * matrix0[p2];
+                    p1++;
+                    p2 += dim;
+                }
+                matrix0[target] = sum;
+            }
+
+            // Search for largest pivot element and calculate
+            // intermediate elements of lower diagonal matrix L.
+            big = 0.0;
+            imax = -1;
+            for (i = j; i < dim; i++) {
+                target = mtx + (dim * i) + j;
+                sum = matrix0[target];
+                k = j;
+                p1 = mtx + (dim * i);
+                p2 = mtx + j;
+                while (k-- != 0) {
+                    sum -= matrix0[p1] * matrix0[p2];
+                    p1++;
+                    p2 += dim;
+                }
+                matrix0[target] = sum;
+
+                // Is this the best pivot so far?
+                if ((temp = row_scale[i] * MathUtils.abs(sum)) >= big) {
+                    big = temp;
+                    imax = i;
+                }
+            }
+
+            if (imax < 0) {
+                throw new RuntimeException();
+            }
+
+            // Is a row exchange necessary?
+            if (j != imax) {
+                // Yes: exchange rows
+                k = dim;
+                p1 = mtx + (dim * imax);
+                p2 = mtx + (dim * j);
+                while (k-- != 0) {
+                    temp = matrix0[p1];
+                    matrix0[p1++] = matrix0[p2];
+                    matrix0[p2++] = temp;
+                }
+
+                // Record change in scale factor
+                row_scale[imax] = row_scale[j];
+                even_row_xchg[0] = -even_row_xchg[0]; // change exchange parity
+            }
+
+            // Record row permutation
+            row_perm[j] = imax;
+
+            // Is the matrix singular
+            if (matrix0[(mtx + (dim * j) + j)] == 0.0) {
+                return false;
+            }
+
+            // Divide elements of lower diagonal matrix L by pivot
+            if (j != (dim - 1)) {
+                temp = 1.0 / (matrix0[(mtx + (dim * j) + j)]);
+                target = mtx + (dim * (j + 1)) + j;
+                i = (dim - 1) - j;
+                while (i-- != 0) {
+                    matrix0[target] *= temp;
+                    target += dim;
+                }
+            }
+
+        }
+
+        return true;
+    }
+
+    private static void print_m(GMatrix m, GMatrix u, GMatrix v) {
+        GMatrix mtmp = new GMatrix(m.nCol, m.nRow);
+
+        mtmp.mul(u, mtmp);
+        mtmp.mul(mtmp, v);
+        System.out.println("\n m = \n" + mtmp.toString(mtmp));
+
+    }
+
+    private static void print_se(double[] s, double[] e) {
+        System.out.println("\ns =" + s[0] + " " + s[1] + " " + s[2]);
+        System.out.println("e =" + e[0] + " " + e[1]);
+    }
+
+    private static void print_svd(double[] s, double[] e, GMatrix u, GMatrix v) {
+        int i;
+        GMatrix mtmp = new GMatrix(u.nCol, v.nRow);
+
+        System.out.println(" \ns = ");
+        for (i = 0; i < s.length; i++) {
+            System.out.println(" " + s[i]);
+        }
+
+        System.out.println(" \ne = ");
+        for (i = 0; i < e.length; i++) {
+            System.out.println(" " + e[i]);
+        }
+
+        System.out.println(" \nu  = \n" + u.toString());
+        System.out.println(" \nv  = \n" + v.toString());
+
+        mtmp.identity();
+        for (i = 0; i < s.length; i++) {
+            mtmp.values[i][i] = s[i];
+        }
+        for (i = 0; i < e.length; i++) {
+            mtmp.values[i][i + 1] = e[i];
+        }
+        System.out.println(" \nm  = \n" + mtmp.toString());
+
+        mtmp.mulTransposeLeft(u, mtmp);
+        mtmp.mulTransposeRight(mtmp, v);
+
+        System.out.println(" \n u.transpose*m*v.transpose  = \n"
+                + mtmp.toString());
+    }
+
+    private static String toString(GMatrix m) {
+        StringBuffer buffer = new StringBuffer(m.nRow * m.nCol * 8);
+        int i, j;
+
+        for (i = 0; i < m.nRow; i++) {
+            for (j = 0; j < m.nCol; j++) {
+                if (MathUtils.abs(m.values[i][j]) < MathUtils.EPS) {
+                    buffer.append("0.0000 ");
+                } else {
+                    buffer.append(m.values[i][j]).append(" ");
+                }
+            }
+            buffer.append("\n");
+        }
+        return buffer.toString();
+    }
+
+    private static void update_u(int index, GMatrix u, double[] cosl,
+            double[] sinl) {
+        int j;
+        double utemp;
+
+        for (j = 0; j < u.nCol; j++) {
+            utemp = u.values[index][j];
+            u.values[index][j] = cosl[0] * utemp + sinl[0]
+                    * u.values[index + 1][j];
+            u.values[index + 1][j] = -sinl[0] * utemp + cosl[0]
+                    * u.values[index + 1][j];
+        }
+    }
+
+    private static void update_u_split(int topr, int bottomr, GMatrix u,
+            double[] cosl, double[] sinl, GMatrix t, GMatrix m) {
+        int j;
+        double utemp;
+
+        for (j = 0; j < u.nCol; j++) {
+            utemp = u.values[topr][j];
+            u.values[topr][j] = cosl[0] * utemp - sinl[0]
+                    * u.values[bottomr][j];
+            u.values[bottomr][j] = sinl[0] * utemp + cosl[0]
+                    * u.values[bottomr][j];
+        }
+
+        checkMatrix(m);
+        checkMatrix(t);
+        m.mul(t, m);
+        checkMatrix(m);
+    }
+
+    private static void update_v(int index, GMatrix v, double[] cosr,
+            double[] sinr) {
+        int j;
+        double vtemp;
+
+        for (j = 0; j < v.nRow; j++) {
+            vtemp = v.values[j][index];
+            v.values[j][index] = cosr[0] * vtemp + sinr[0]
+                    * v.values[j][index + 1];
+            v.values[j][index + 1] = -sinr[0] * vtemp + cosr[0]
+                    * v.values[j][index + 1];
+        }
+    }
+
+    private static void update_v_split(int topr, int bottomr, GMatrix v,
+            double[] cosr, double[] sinr, GMatrix t, GMatrix m) {
+        int j;
+        double vtemp;
+
+        for (j = 0; j < v.nRow; j++) {
+            vtemp = v.values[j][topr];
+            v.values[j][topr] = cosr[0] * vtemp - sinr[0]
+                    * v.values[j][bottomr];
+            v.values[j][bottomr] = sinr[0] * vtemp + cosr[0]
+                    * v.values[j][bottomr];
+        }
+
+        checkMatrix(m);
+        checkMatrix(t);
+        m.mul(m, t);
+        checkMatrix(m);
+    }
+
+    int nRow;
+
+    int nCol;
+
+    // double dereference is slow
+    double[][] values;
+
+    private static final double EPS = 1.0E-10;
+
+    /**
+     * Constructs a new GMatrix and copies the initial values from the parameter
+     * matrix.
+     * 
+     * @param matrix
+     *            the source of the initial values of the new GMatrix
+     */
+    public GMatrix(GMatrix matrix) {
+        nRow = matrix.nRow;
+        nCol = matrix.nCol;
+        values = new double[nRow][nCol];
+
+        int i, j;
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                values[i][j] = matrix.values[i][j];
+            }
+        }
+    }
+
+    /**
+     * Constructs an nRow by NCol identity matrix. Note that because row and
+     * column numbering begins with zero, nRow and nCol will be one larger than
+     * the maximum possible matrix index values.
+     * 
+     * @param nRow
+     *            number of rows in this matrix.
+     * @param nCol
+     *            number of columns in this matrix.
+     */
+    public GMatrix(int nRow, int nCol) {
+        values = new double[nRow][nCol];
+        this.nRow = nRow;
+        this.nCol = nCol;
+
+        int i, j;
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                values[i][j] = 0.0;
+            }
+        }
+
+        int l;
+        if (nRow < nCol) {
+            l = nRow;
+        } else {
+            l = nCol;
+        }
+
+        for (i = 0; i < l; i++) {
+            values[i][i] = 1.0;
+        }
+    }
+
+    /**
+     * Constructs an nRow by nCol matrix initialized to the values in the matrix
+     * array. The array values are copied in one row at a time in row major
+     * fashion. The array should be at least nRow*nCol in length. Note that
+     * because row and column numbering begins with zero, nRow and nCol will be
+     * one larger than the maximum possible matrix index values.
+     * 
+     * @param nRow
+     *            number of rows in this matrix.
+     * @param nCol
+     *            number of columns in this matrix.
+     * @param matrix
+     *            a 1D array that specifies a matrix in row major fashion
+     */
+    public GMatrix(int nRow, int nCol, double[] matrix) {
+        values = new double[nRow][nCol];
+        this.nRow = nRow;
+        this.nCol = nCol;
+
+        int i, j;
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                values[i][j] = matrix[i * nCol + j];
+            }
+        }
+    }
+
+    /**
+     * Sets the value of this matrix to sum of itself and matrix m1.
+     * 
+     * @param m1
+     *            the other matrix
+     */
+    public final void add(GMatrix m1) {
+        int i, j;
+
+        if (nRow != m1.nRow) {
+            throw new MatrixSizeException();
+        }
+
+        if (nCol != m1.nCol) {
+            throw new MatrixSizeException();
+        }
+
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                values[i][j] = values[i][j] + m1.values[i][j];
+            }
+        }
+    }
+
+    /**
+     * Sets the value of this matrix to the matrix sum of matrices m1 and m2.
+     * 
+     * @param m1
+     *            the first matrix
+     * @param m2
+     *            the second matrix
+     */
+    public final void add(GMatrix m1, GMatrix m2) {
+        int i, j;
+
+        if (m2.nRow != m1.nRow) {
+            throw new MatrixSizeException();
+        }
+
+        if (m2.nCol != m1.nCol) {
+            throw new MatrixSizeException();
+        }
+
+        if (nCol != m1.nCol || nRow != m1.nRow) {
+            throw new MatrixSizeException();
+        }
+
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                values[i][j] = m1.values[i][j] + m2.values[i][j];
+            }
+        }
+    }
+
+    /**
+     * Creates a new object of the same class as this object.
+     * 
+     * @return a clone of this instance.
+     * @exception OutOfMemoryError
+     *                if there is not enough memory.
+     * @see Cloneable
+     * @since vecmath 1.3
+     */
+    public Object clone() {
+        GMatrix m1 = null;
+        try {
+            m1 = (GMatrix) super.clone();
+        } catch (CloneNotSupportedException e) {
+            // this shouldn't happen, since we are Cloneable
+            throw new InternalError();
+        }
+
+        // Also need to clone array of values
+        m1.values = new double[nRow][nCol];
+        for (int i = 0; i < nRow; i++) {
+            for (int j = 0; j < nCol; j++) {
+                m1.values[i][j] = values[i][j];
+            }
+        }
+
+        return m1;
+    }
+
+    /**
+     * LU Decomposition: this matrix must be a square matrix and the LU GMatrix
+     * parameter must be the same size as this matrix. The matrix LU will be
+     * overwritten as the combination of a lower diagonal and upper diagonal
+     * matrix decompostion of this matrix; the diagonal elements of L (unity)
+     * are not stored. The GVector parameter records the row permutation
+     * effected by the partial pivoting, and is used as a parameter to the
+     * GVector method LUDBackSolve to solve sets of linear equations. This
+     * method returns +/- 1 depending on whether the number of row interchanges
+     * was even or odd, respectively.
+     * 
+     * @param LU
+     *            The matrix into which the lower and upper decompositions will
+     *            be placed.
+     * @param permutation
+     *            The row permutation effected by the partial pivoting
+     * @return +-1 depending on whether the number of row interchanges was even
+     *         or odd respectively
+     */
+    public final int computeLUD(GMatrix LU, GVector permutation) {
+        int size = LU.nRow * LU.nCol;
+        double[] temp = new double[size];
+        int[] even_row_exchange = new int[1];
+        int[] row_perm = new int[LU.nRow];
+        int i, j;
+
+        if (nRow != nCol) {
+            throw new MatrixSizeException();
+        }
+
+        if (nRow != LU.nRow) {
+            throw new MatrixSizeException();
+        }
+
+        if (nCol != LU.nCol) {
+            throw new MatrixSizeException();
+        }
+
+        if (LU.nRow != permutation.size()) {
+            throw new MatrixSizeException();
+        }
+
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                temp[i * nCol + j] = values[i][j];
+            }
+        }
+
+        // Calculate LU decomposition: Is the matrix singular?
+        if (!decomposeLU(LU.nRow, temp, row_perm, even_row_exchange)) {
+            // Matrix has no inverse
+            throw new SingularMatrixException();
+        }
+
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                LU.values[i][j] = temp[i * nCol + j];
+            }
+        }
+
+        for (i = 0; i < LU.nRow; i++) {
+            permutation.values[i] = row_perm[i];
+        }
+
+        return even_row_exchange[0];
+    }
+
+    /**
+     * Finds the singular value decomposition (SVD) of this matrix such that
+     * this = U*W*transpose(V); and returns the rank of this matrix; the values
+     * of U,W,V are all overwritten. Note that the matrix V is output as V, and
+     * not transpose(V). If this matrix is mxn, then U is mxm, W is a diagonal
+     * matrix that is mxn, and V is nxn. Using the notation W = diag(w), then
+     * the inverse of this matrix is: inverse(this) = V*diag(1/w)*tranpose(U),
+     * where diag(1/w) is the same matrix as W except that the reciprocal of
+     * each of the diagonal components is used.
+     * 
+     * @param U
+     *            The computed U matrix in the equation this = U*W*transpose(V)
+     * @param W
+     *            The computed W matrix in the equation this = U*W*transpose(V)
+     * @param V
+     *            The computed V matrix in the equation this = U*W*transpose(V)
+     * @return The rank of this matrix.
+     */
+    public final int computeSVD(GMatrix U, GMatrix W, GMatrix V) {
+        // check for consistancy in dimensions
+        if (nCol != V.nCol || nCol != V.nRow) {
+            throw new MatrixSizeException();
+        }
+
+        if (nRow != U.nRow || nRow != U.nCol) {
+            throw new MatrixSizeException();
+        }
+
+        if (nRow != W.nRow || nCol != W.nCol) {
+            throw new MatrixSizeException();
+        }
+
+        // Fix ArrayIndexOutOfBounds for 2x2 matrices, which partially
+        // addresses bug 4348562 for J3D 1.2.1.
+        //
+        // Does *not* fix the following problems reported in 4348562,
+        // which will wait for J3D 1.3:
+        //
+        // 1) no output of W
+        // 2) wrong transposition of U
+        // 3) wrong results for 4x4 matrices
+        // 4) slow performance
+        if (nRow == 2 && nCol == 2) {
+            if (values[1][0] == 0.0) {
+                U.identity();
+                V.identity();
+
+                if (values[0][1] == 0.0) {
+                    return 2;
+                }
+
+                double[] sinl = new double[1];
+                double[] sinr = new double[1];
+                double[] cosl = new double[1];
+                double[] cosr = new double[1];
+                double[] single_values = new double[2];
+
+                single_values[0] = values[0][0];
+                single_values[1] = values[1][1];
+
+                compute_2X2(values[0][0], values[0][1], values[1][1],
+                        single_values, sinl, cosl, sinr, cosr, 0);
+
+                update_u(0, U, cosl, sinl);
+                update_v(0, V, cosr, sinr);
+
+                return 2;
+            }
+            // else call computeSVD() and check for 2x2 there
+        }
+
+        return computeSVD(this, U, W, V);
+    }
+
+    /**
+     * Copies a sub-matrix derived from this matrix into the target matrix. The
+     * upper left of the sub-matrix is located at (rowSource, colSource); the
+     * lower right of the sub-matrix is located at
+     * (lastRowSource,lastColSource). The sub-matrix is copied into the the
+     * target matrix starting at (rowDest, colDest).
+     * 
+     * @param rowSource
+     *            the top-most row of the sub-matrix
+     * @param colSource
+     *            the left-most column of the sub-matrix
+     * @param numRow
+     *            the number of rows in the sub-matrix
+     * @param numCol
+     *            the number of columns in the sub-matrix
+     * @param rowDest
+     *            the top-most row of the position of the copied sub-matrix
+     *            within the target matrix
+     * @param colDest
+     *            the left-most column of the position of the copied sub-matrix
+     *            within the target matrix
+     * @param target
+     *            the matrix into which the sub-matrix will be copied
+     */
+    public final void copySubMatrix(int rowSource, int colSource, int numRow,
+            int numCol, int rowDest, int colDest, GMatrix target) {
+        int i, j;
+
+        if (this != target) {
+            for (i = 0; i < numRow; i++) {
+                for (j = 0; j < numCol; j++) {
+                    target.values[rowDest + i][colDest + j] = values[rowSource
+                            + i][colSource + j];
+                }
+            }
+        } else {
+            double[][] tmp = new double[numRow][numCol];
+            for (i = 0; i < numRow; i++) {
+                for (j = 0; j < numCol; j++) {
+                    tmp[i][j] = values[rowSource + i][colSource + j];
+                }
+            }
+            for (i = 0; i < numRow; i++) {
+                for (j = 0; j < numCol; j++) {
+                    target.values[rowDest + i][colDest + j] = tmp[i][j];
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns true if the L-infinite distance between this matrix and matrix m1
+     * is less than or equal to the epsilon parameter, otherwise returns false.
+     * The L-infinite distance is equal to MAX[i=0,1,2, . . .n ; j=0,1,2, . . .n
+     * ; abs(this.m(i,j) - m1.m(i,j)]
+     * 
+     * @param m1
+     *            The matrix to be compared to this matrix
+     * @param epsilon
+     *            the threshold value
+     */
+    public boolean epsilonEquals(GMatrix m1, double epsilon) {
+        int i, j;
+        double diff;
+        if (nRow != m1.nRow || nCol != m1.nCol) {
+            return false;
+        }
+
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                diff = values[i][j] - m1.values[i][j];
+                if ((diff < 0 ? -diff : diff) > epsilon) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * @deprecated Use epsilonEquals(GMatrix, double) instead
+     */
+    @Deprecated
+    public boolean epsilonEquals(GMatrix m1, float epsilon) {
+        return epsilonEquals(m1, (double) epsilon);
+    }
+
+    /**
+     * Returns true if all of the data members of GMatrix m1 are equal to the
+     * corresponding data members in this GMatrix.
+     * 
+     * @param m1
+     *            The matrix with which the comparison is made.
+     * @return true or false
+     */
+    public boolean equals(GMatrix m1) {
+        try {
+            int i, j;
+
+            if (nRow != m1.nRow || nCol != m1.nCol) {
+                return false;
+            }
+
+            for (i = 0; i < nRow; i++) {
+                for (j = 0; j < nCol; j++) {
+                    if (values[i][j] != m1.values[i][j]) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        } catch (NullPointerException e2) {
+            return false;
+        }
+    }
+
+    /**
+     * Returns true if the Object o1 is of type GMatrix and all of the data
+     * members of o1 are equal to the corresponding data members in this
+     * GMatrix.
+     * 
+     * @param o1
+     *            The object with which the comparison is made.
+     * @return true or false
+     */
+    public boolean equals(Object o1) {
+        try {
+            GMatrix m2 = (GMatrix) o1;
+            int i, j;
+            if (nRow != m2.nRow || nCol != m2.nCol) {
+                return false;
+            }
+
+            for (i = 0; i < nRow; i++) {
+                for (j = 0; j < nCol; j++) {
+                    if (values[i][j] != m2.values[i][j]) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        } catch (ClassCastException e1) {
+            return false;
+        } catch (NullPointerException e2) {
+            return false;
+        }
+    }
+
+    /**
+     * Places the values in the this GMatrix into the matrix m1; m1 should be at
+     * least as large as this GMatrix.
+     * 
+     * @param m1
+     *            The matrix that will hold the new values
+     */
+    public final void get(GMatrix m1) {
+        int i, j, nc, nr;
+
+        if (nCol < m1.nCol) {
+            nc = nCol;
+        } else {
+            nc = m1.nCol;
+        }
+
+        if (nRow < m1.nRow) {
+            nr = nRow;
+        } else {
+            nr = m1.nRow;
+        }
+
+        for (i = 0; i < nr; i++) {
+            for (j = 0; j < nc; j++) {
+                m1.values[i][j] = values[i][j];
+            }
+        }
+        for (i = nr; i < m1.nRow; i++) {
+            for (j = 0; j < m1.nCol; j++) {
+                m1.values[i][j] = 0.0;
+            }
+        }
+        for (j = nc; j < m1.nCol; j++) {
+            for (i = 0; i < nr; i++) {
+                m1.values[i][j] = 0.0;
+            }
+        }
+    }
+
+    /**
+     * Places the values in the upper 3x3 of this GMatrix into the matrix m1.
+     * 
+     * @param m1
+     *            The matrix that will hold the new values
+     */
+    public final void get(Matrix3d m1) {
+        if (nRow < 3 || nCol < 3) {
+            m1.setZero();
+            if (nCol > 0) {
+                if (nRow > 0) {
+                    m1.m00 = values[0][0];
+                    if (nRow > 1) {
+                        m1.m10 = values[1][0];
+                        if (nRow > 2) {
+                            m1.m20 = values[2][0];
+                        }
+                    }
+                }
+                if (nCol > 1) {
+                    if (nRow > 0) {
+                        m1.m01 = values[0][1];
+                        if (nRow > 1) {
+                            m1.m11 = values[1][1];
+                            if (nRow > 2) {
+                                m1.m21 = values[2][1];
+                            }
+                        }
+                    }
+                    if (nCol > 2) {
+                        if (nRow > 0) {
+                            m1.m02 = values[0][2];
+                            if (nRow > 1) {
+                                m1.m12 = values[1][2];
+                                if (nRow > 2) {
+                                    m1.m22 = values[2][2];
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } else {
+            m1.m00 = values[0][0];
+            m1.m01 = values[0][1];
+            m1.m02 = values[0][2];
+
+            m1.m10 = values[1][0];
+            m1.m11 = values[1][1];
+            m1.m12 = values[1][2];
+
+            m1.m20 = values[2][0];
+            m1.m21 = values[2][1];
+            m1.m22 = values[2][2];
+        }
+    }
+
+    /**
+     * Places the values in the upper 4x4 of this GMatrix into the matrix m1.
+     * 
+     * @param m1
+     *            The matrix that will hold the new values
+     */
+    public final void get(Matrix4f m1) {
+
+        if (nRow < 4 || nCol < 4) {
+            m1.setZero();
+            if (nCol > 0) {
+                if (nRow > 0) {
+                    m1.m00 = (float) values[0][0];
+                    if (nRow > 1) {
+                        m1.m10 = (float) values[1][0];
+                        if (nRow > 2) {
+                            m1.m20 = (float) values[2][0];
+                            if (nRow > 3) {
+                                m1.m30 = (float) values[3][0];
+                            }
+                        }
+                    }
+                }
+                if (nCol > 1) {
+                    if (nRow > 0) {
+                        m1.m01 = (float) values[0][1];
+                        if (nRow > 1) {
+                            m1.m11 = (float) values[1][1];
+                            if (nRow > 2) {
+                                m1.m21 = (float) values[2][1];
+                                if (nRow > 3) {
+                                    m1.m31 = (float) values[3][1];
+                                }
+                            }
+                        }
+                    }
+                    if (nCol > 2) {
+                        if (nRow > 0) {
+                            m1.m02 = (float) values[0][2];
+                            if (nRow > 1) {
+                                m1.m12 = (float) values[1][2];
+                                if (nRow > 2) {
+                                    m1.m22 = (float) values[2][2];
+                                    if (nRow > 3) {
+                                        m1.m32 = (float) values[3][2];
+                                    }
+                                }
+                            }
+                        }
+                        if (nCol > 3) {
+                            if (nRow > 0) {
+                                m1.m03 = (float) values[0][3];
+                                if (nRow > 1) {
+                                    m1.m13 = (float) values[1][3];
+                                    if (nRow > 2) {
+                                        m1.m23 = (float) values[2][3];
+                                        if (nRow > 3) {
+                                            m1.m33 = (float) values[3][3];
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } else {
+            m1.m00 = (float) values[0][0];
+            m1.m01 = (float) values[0][1];
+            m1.m02 = (float) values[0][2];
+            m1.m03 = (float) values[0][3];
+
+            m1.m10 = (float) values[1][0];
+            m1.m11 = (float) values[1][1];
+            m1.m12 = (float) values[1][2];
+            m1.m13 = (float) values[1][3];
+
+            m1.m20 = (float) values[2][0];
+            m1.m21 = (float) values[2][1];
+            m1.m22 = (float) values[2][2];
+            m1.m23 = (float) values[2][3];
+
+            m1.m30 = (float) values[3][0];
+            m1.m31 = (float) values[3][1];
+            m1.m32 = (float) values[3][2];
+            m1.m33 = (float) values[3][3];
+        }
+    }
+
+    /**
+     * Places the values of the specified column into the array parameter.
+     * 
+     * @param col
+     *            the target column number
+     * @param array
+     *            the array into which the column values will be placed
+     */
+    public final void getColumn(int col, double[] array) {
+        for (int i = 0; i < nRow; i++) {
+            array[i] = values[i][col];
+        }
+
+    }
+
+    /**
+     * Places the values of the specified column into the vector parameter.
+     * 
+     * @param col
+     *            the target column number
+     * @param vector
+     *            the vector into which the column values will be placed
+     */
+    public final void getColumn(int col, GVector vector) {
+        if (vector.size() < nRow) {
+            vector.setSize(nRow);
+        }
+
+        for (int i = 0; i < nRow; i++) {
+            vector.values[i] = values[i][col];
+        }
+    }
+
+    /**
+     * Retrieves the value at the specified row and column of this matrix.
+     * 
+     * @param row
+     *            the row number to be retrieved (zero indexed)
+     * @param column
+     *            the column number to be retrieved (zero indexed)
+     * @return the value at the indexed element
+     */
+    public final double getElement(int row, int column) {
+        return (values[row][column]);
+    }
+
+    /**
+     * Returns the number of colmuns in this matrix.
+     * 
+     * @return number of columns in this matrix
+     */
+    public final int getNumCol() {
+        return (nCol);
+    }
+
+    /**
+     * Returns the number of rows in this matrix.
+     * 
+     * @return number of rows in this matrix
+     */
+    public final int getNumRow() {
+        return (nRow);
+    }
+
+    /**
+     * Places the values of the specified row into the array parameter.
+     * 
+     * @param row
+     *            the target row number
+     * @param array
+     *            the array into which the row values will be placed
+     */
+    public final void getRow(int row, double[] array) {
+        for (int i = 0; i < nCol; i++) {
+            array[i] = values[row][i];
+        }
+    }
+
+    /**
+     * Places the values of the specified row into the vector parameter.
+     * 
+     * @param row
+     *            the target row number
+     * @param vector
+     *            the vector into which the row values will be placed
+     */
+    public final void getRow(int row, GVector vector) {
+        if (vector.size() < nCol) {
+            vector.setSize(nCol);
+        }
+
+        for (int i = 0; i < nCol; i++) {
+            vector.values[i] = values[row][i];
+        }
+    }
+
+    /**
+     * Returns a hash code value based on the data values in this object. Two
+     * different GMatrix objects with identical data values (i.e.,
+     * GMatrix.equals returns true) will return the same hash number. Two
+     * GMatrix objects with different data members may return the same hash
+     * value, although this is not likely.
+     * 
+     * @return the integer hash code value
+     */
+    public int hashCode() {
+        long bits = 1L;
+
+        bits = 31L * bits + nRow;
+        bits = 31L * bits + nCol;
+
+        for (int i = 0; i < nRow; i++) {
+            for (int j = 0; j < nCol; j++) {
+                bits = 31L * bits + VecMathUtil.doubleToLongBits(values[i][j]);
+            }
+        }
+
+        return (int) (bits ^ (bits >> 32));
+    }
+
+    /**
+     * Sets this GMatrix to the identity matrix.
+     */
+    public final void identity() {
+        int i, j;
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                values[i][j] = 0.0;
+            }
+        }
+
+        int l;
+        if (nRow < nCol) {
+            l = nRow;
+        } else {
+            l = nCol;
+        }
+
+        for (i = 0; i < l; i++) {
+            values[i][i] = 1.0;
+        }
+    }
+
+    /**
+     * Subtracts this matrix from the identity matrix and puts the values back
+     * into this (this = I - this).
+     */
+    public final void identityMinus() {
+        int i, j;
+
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                values[i][j] = -values[i][j];
+            }
+        }
+
+        int l;
+        if (nRow < nCol) {
+            l = nRow;
+        } else {
+            l = nCol;
+        }
+
+        for (i = 0; i < l; i++) {
+            values[i][i] += 1.0;
+        }
+    }
+
+    /**
+     * Inverts this matrix in place.
+     */
+    public final void invert() {
+        invertGeneral(this);
+    }
+
+    /**
+     * Inverts matrix m1 and places the new values into this matrix. Matrix m1
+     * is not modified.
+     * 
+     * @param m1
+     *            the matrix to be inverted
+     */
+    public final void invert(GMatrix m1) {
+        invertGeneral(m1);
+    }
+
+    /**
+     * General invert routine. Inverts m1 and places the result in "this". Note
+     * that this routine handles both the "this" version and the non-"this"
+     * version.
+     * 
+     * Also note that since this routine is slow anyway, we won't worry about
+     * allocating a little bit of garbage.
+     */
+    final void invertGeneral(GMatrix m1) {
+        int size = m1.nRow * m1.nCol;
+        double temp[] = new double[size];
+        double result[] = new double[size];
+        int row_perm[] = new int[m1.nRow];
+        int[] even_row_exchange = new int[1];
+        int i, j;
+
+        // Use LU decomposition and backsubstitution code specifically
+        // for floating-point nxn matrices.
+        if (m1.nRow != m1.nCol) {
+            // Matrix is either under or over determined
+            throw new MatrixSizeException();
+        }
+
+        // Copy source matrix to temp
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                temp[i * nCol + j] = m1.values[i][j];
+            }
+        }
+
+        // Calculate LU decomposition: Is the matrix singular?
+        if (!decomposeLU(m1.nRow, temp, row_perm, even_row_exchange)) {
+            // Matrix has no inverse
+            throw new SingularMatrixException();
+        }
+
+        // Perform back substitution on the identity matrix
+        for (i = 0; i < size; i++) {
+            result[i] = 0.0;
+        }
+
+        for (i = 0; i < nCol; i++) {
+            result[i + i * nCol] = 1.0;
+        }
+
+        backSubstituteLU(m1.nRow, temp, row_perm, result);
+
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                values[i][j] = result[i * nCol + j];
+            }
+        }
+    }
+
+    /**
+     * Sets the value of this matrix to the result of multiplying itself with
+     * matrix m1 (this = this * m1).
+     * 
+     * @param m1
+     *            the other matrix
+     */
+    public final void mul(GMatrix m1) {
+        int i, j, k;
+
+        if (nCol != m1.nRow || nCol != m1.nCol) {
+            throw new MatrixSizeException();
+        }
+
+        double[][] tmp = new double[nRow][nCol];
+
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                tmp[i][j] = 0.0;
+                for (k = 0; k < nCol; k++) {
+                    tmp[i][j] += values[i][k] * m1.values[k][j];
+                }
+            }
+        }
+
+        values = tmp;
+    }
+
+    /**
+     * Sets the value of this matrix to the result of multiplying the two
+     * argument matrices together (this = m1 * m2).
+     * 
+     * @param m1
+     *            the first matrix
+     * @param m2
+     *            the second matrix
+     */
+    public final void mul(GMatrix m1, GMatrix m2) {
+        int i, j, k;
+
+        if (m1.nCol != m2.nRow || nRow != m1.nRow || nCol != m2.nCol) {
+            throw new MatrixSizeException();
+        }
+
+        double[][] tmp = new double[nRow][nCol];
+
+        for (i = 0; i < m1.nRow; i++) {
+            for (j = 0; j < m2.nCol; j++) {
+                tmp[i][j] = 0.0;
+                for (k = 0; k < m1.nCol; k++) {
+                    tmp[i][j] += m1.values[i][k] * m2.values[k][j];
+                }
+            }
+        }
+
+        values = tmp;
+    }
+
+    /**
+     * Computes the outer product of the two vectors; multiplies the the first
+     * vector by the transpose of the second vector and places the matrix result
+     * into this matrix. This matrix must be be as big or bigger than
+     * getSize(v1)xgetSize(v2).
+     * 
+     * @param v1
+     *            the first vector, treated as a row vector
+     * @param v2
+     *            the second vector, treated as a column vector
+     */
+    public final void mul(GVector v1, GVector v2) {
+        int i, j;
+
+        if (nRow < v1.size()) {
+            throw new MatrixSizeException();
+        }
+
+        if (nCol < v2.size()) {
+            throw new MatrixSizeException();
+        }
+
+        for (i = 0; i < v1.size(); i++) {
+            for (j = 0; j < v2.size(); j++) {
+                values[i][j] = v1.values[i] * v2.values[j];
+            }
+        }
+    }
+
+    /**
+     * Multiplies the transpose of matrix m1 times the transpose of matrix m2,
+     * and places the result into this.
+     * 
+     * @param m1
+     *            The matrix on the left hand side of the multiplication
+     * @param m2
+     *            The matrix on the right hand side of the multiplication
+     */
+    public final void mulTransposeBoth(GMatrix m1, GMatrix m2) {
+        int i, j, k;
+
+        if (m1.nRow != m2.nCol || nRow != m1.nCol || nCol != m2.nRow) {
+            throw new MatrixSizeException();
+        }
+
+        if (m1 == this || m2 == this) {
+            double[][] tmp = new double[nRow][nCol];
+            for (i = 0; i < nRow; i++) {
+                for (j = 0; j < nCol; j++) {
+                    tmp[i][j] = 0.0;
+                    for (k = 0; k < m1.nRow; k++) {
+                        tmp[i][j] += m1.values[k][i] * m2.values[j][k];
+                    }
+                }
+            }
+            values = tmp;
+        } else {
+            for (i = 0; i < nRow; i++) {
+                for (j = 0; j < nCol; j++) {
+                    values[i][j] = 0.0;
+                    for (k = 0; k < m1.nRow; k++) {
+                        values[i][j] += m1.values[k][i] * m2.values[j][k];
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Multiplies the transpose of matrix m1 times matrix m2, and places the
+     * result into this.
+     * 
+     * @param m1
+     *            The matrix on the left hand side of the multiplication
+     * @param m2
+     *            The matrix on the right hand side of the multiplication
+     */
+    public final void mulTransposeLeft(GMatrix m1, GMatrix m2) {
+        int i, j, k;
+
+        if (m1.nRow != m2.nRow || nCol != m2.nCol || nRow != m1.nCol) {
+            throw new MatrixSizeException();
+        }
+
+        if (m1 == this || m2 == this) {
+            double[][] tmp = new double[nRow][nCol];
+            for (i = 0; i < nRow; i++) {
+                for (j = 0; j < nCol; j++) {
+                    tmp[i][j] = 0.0;
+                    for (k = 0; k < m1.nRow; k++) {
+                        tmp[i][j] += m1.values[k][i] * m2.values[k][j];
+                    }
+                }
+            }
+            values = tmp;
+        } else {
+            for (i = 0; i < nRow; i++) {
+                for (j = 0; j < nCol; j++) {
+                    values[i][j] = 0.0;
+                    for (k = 0; k < m1.nRow; k++) {
+                        values[i][j] += m1.values[k][i] * m2.values[k][j];
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Multiplies matrix m1 times the transpose of matrix m2, and places the
+     * result into this.
+     * 
+     * @param m1
+     *            The matrix on the left hand side of the multiplication
+     * @param m2
+     *            The matrix on the right hand side of the multiplication
+     */
+    public final void mulTransposeRight(GMatrix m1, GMatrix m2) {
+        int i, j, k;
+
+        if (m1.nCol != m2.nCol || nCol != m2.nRow || nRow != m1.nRow) {
+            throw new MatrixSizeException();
+        }
+
+        if (m1 == this || m2 == this) {
+            double[][] tmp = new double[nRow][nCol];
+            for (i = 0; i < nRow; i++) {
+                for (j = 0; j < nCol; j++) {
+                    tmp[i][j] = 0.0;
+                    for (k = 0; k < m1.nCol; k++) {
+                        tmp[i][j] += m1.values[i][k] * m2.values[j][k];
+                    }
+                }
+            }
+            values = tmp;
+        } else {
+            for (i = 0; i < nRow; i++) {
+                for (j = 0; j < nCol; j++) {
+                    values[i][j] = 0.0;
+                    for (k = 0; k < m1.nCol; k++) {
+                        values[i][j] += m1.values[i][k] * m2.values[j][k];
+                    }
+                }
+            }
+        }
+
+    }
+
+    /**
+     * Negates the value of this matrix: this = -this.
+     */
+    public final void negate() {
+        int i, j;
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                values[i][j] = -values[i][j];
+            }
+        }
+    }
+
+    /**
+     * Sets the value of this matrix equal to the negation of of the GMatrix
+     * parameter.
+     * 
+     * @param m1
+     *            The source matrix
+     */
+    public final void negate(GMatrix m1) {
+        int i, j;
+        if (nRow != m1.nRow || nCol != m1.nCol) {
+            throw new MatrixSizeException();
+        }
+
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                values[i][j] = -m1.values[i][j];
+            }
+        }
+    }
+
+    /**
+     * Sets the value of this matrix to the values found in the array parameter.
+     * The values are copied in one row at a time, in row major fashion. The
+     * array should be at least equal in length to the number of matrix rows
+     * times the number of matrix columns in this matrix.
+     * 
+     * @param matrix
+     *            the row major source array
+     */
+    public final void set(double[] matrix) {
+        int i, j;
+
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                values[i][j] = matrix[nCol * i + j];
+            }
+        }
+    }
+
+    /**
+     * Sets the value of this matrix to the values found in matrix m1.
+     * 
+     * @param m1
+     *            the source matrix
+     */
+    public final void set(GMatrix m1) {
+        int i, j;
+
+        if (nRow < m1.nRow || nCol < m1.nCol) {
+            nRow = m1.nRow;
+            nCol = m1.nCol;
+            values = new double[nRow][nCol];
+        }
+
+        for (i = 0; i < Math.min(nRow, m1.nRow); i++) {
+            for (j = 0; j < Math.min(nCol, m1.nCol); j++) {
+                values[i][j] = m1.values[i][j];
+            }
+        }
+
+        for (i = m1.nRow; i < nRow; i++) { // pad rest or matrix with zeros
+            for (j = m1.nCol; j < nCol; j++) {
+                values[i][j] = 0.0;
+            }
+        }
+    }
+
+    /**
+     * Sets the value of this matrix to that of the Matrix3d provided.
+     * 
+     * @param m1
+     *            the matrix
+     */
+    public final void set(Matrix3d m1) {
+        if (nRow < 3 || nCol < 3) {
+            values = new double[3][3];
+            nRow = 3;
+            nCol = 3;
+        }
+
+        values[0][0] = m1.m00;
+        values[0][1] = m1.m01;
+        values[0][2] = m1.m02;
+
+        values[1][0] = m1.m10;
+        values[1][1] = m1.m11;
+        values[1][2] = m1.m12;
+
+        values[2][0] = m1.m20;
+        values[2][1] = m1.m21;
+        values[2][2] = m1.m22;
+
+        for (int i = 3; i < nRow; i++) { // pad rest or matrix with zeros
+            for (int j = 3; j < nCol; j++) {
+                values[i][j] = 0.0;
+            }
+        }
+
+    }
+
+    /**
+     * Sets the value of this matrix to that of the Matrix4f provided.
+     * 
+     * @param m1
+     *            the matrix
+     */
+    public final void set(Matrix4f m1) {
+        if (nRow < 4 || nCol < 4) {
+            values = new double[4][4];
+            nRow = 4;
+            nCol = 4;
+        }
+
+        values[0][0] = m1.m00;
+        values[0][1] = m1.m01;
+        values[0][2] = m1.m02;
+        values[0][3] = m1.m03;
+
+        values[1][0] = m1.m10;
+        values[1][1] = m1.m11;
+        values[1][2] = m1.m12;
+        values[1][3] = m1.m13;
+
+        values[2][0] = m1.m20;
+        values[2][1] = m1.m21;
+        values[2][2] = m1.m22;
+        values[2][3] = m1.m23;
+
+        values[3][0] = m1.m30;
+        values[3][1] = m1.m31;
+        values[3][2] = m1.m32;
+        values[3][3] = m1.m33;
+
+        for (int i = 4; i < nRow; i++) { // pad rest or matrix with zeros
+            for (int j = 4; j < nCol; j++) {
+                values[i][j] = 0.0;
+            }
+        }
+    }
+
+    /**
+     * Copy the values from the array into the specified column of this matrix.
+     * 
+     * @param col
+     *            the column of this matrix into which the array values will be
+     *            copied
+     * @param array
+     *            the source array
+     */
+    public final void setColumn(int col, double[] array) {
+        for (int i = 0; i < nRow; i++) {
+            values[i][col] = array[i];
+        }
+    }
+
+    /**
+     * Copy the values from the vector into the specified column of this matrix.
+     * 
+     * @param col
+     *            the column of this matrix into which the array values will be
+     *            copied
+     * @param vector
+     *            the source vector
+     */
+    public final void setColumn(int col, GVector vector) {
+        for (int i = 0; i < nRow; i++) {
+            values[i][col] = vector.values[i];
+        }
+
+    }
+
+    /**
+     * Modifies the value at the specified row and column of this matrix.
+     * 
+     * @param row
+     *            the row number to be modified (zero indexed)
+     * @param column
+     *            the column number to be modified (zero indexed)
+     * @param value
+     *            the new matrix element value
+     */
+    public final void setElement(int row, int column, double value) {
+        values[row][column] = value;
+    }
+
+    /**
+     * Copy the values from the array into the specified row of this matrix.
+     * 
+     * @param row
+     *            the row of this matrix into which the array values will be
+     *            copied.
+     * @param array
+     *            the source array
+     */
+    public final void setRow(int row, double[] array) {
+        for (int i = 0; i < nCol; i++) {
+            values[row][i] = array[i];
+        }
+    }
+
+    /**
+     * Copy the values from the vector into the specified row of this matrix.
+     * 
+     * @param row
+     *            the row of this matrix into which the array values will be
+     *            copied
+     * @param vector
+     *            the source vector
+     */
+    public final void setRow(int row, GVector vector) {
+        for (int i = 0; i < nCol; i++) {
+            values[row][i] = vector.values[i];
+        }
+    }
+
+    /**
+     * Sets this matrix to a uniform scale matrix; all of the values are reset.
+     * 
+     * @param scale
+     *            The new scale value
+     */
+    public final void setScale(double scale) {
+        int i, j, l;
+
+        if (nRow < nCol) {
+            l = nRow;
+        } else {
+            l = nCol;
+        }
+
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                values[i][j] = 0.0;
+            }
+        }
+
+        for (i = 0; i < l; i++) {
+            values[i][i] = scale;
+        }
+    }
+
+    /**
+     * Changes the size of this matrix dynamically. If the size is increased no
+     * data values will be lost. If the size is decreased, only those data
+     * values whose matrix positions were eliminated will be lost.
+     * 
+     * @param nRow
+     *            number of desired rows in this matrix
+     * @param nCol
+     *            number of desired columns in this matrix
+     */
+    public final void setSize(int nRow, int nCol) {
+        double[][] tmp = new double[nRow][nCol];
+        int i, j, maxRow, maxCol;
+
+        if (this.nRow < nRow) {
+            maxRow = this.nRow;
+        } else {
+            maxRow = nRow;
+        }
+
+        if (this.nCol < nCol) {
+            maxCol = this.nCol;
+        } else {
+            maxCol = nCol;
+        }
+
+        for (i = 0; i < maxRow; i++) {
+            for (j = 0; j < maxCol; j++) {
+                tmp[i][j] = values[i][j];
+            }
+        }
+
+        this.nRow = nRow;
+        this.nCol = nCol;
+
+        values = tmp;
+    }
+
+    /**
+     * Sets all the values in this matrix to zero.
+     */
+    public final void setZero() {
+        int i, j;
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                values[i][j] = 0.0;
+            }
+        }
+    }
+
+    /**
+     * Sets the value of this matrix to the matrix difference of itself and
+     * matrix m1 (this = this - m1).
+     * 
+     * @param m1
+     *            the other matrix
+     */
+    public final void sub(GMatrix m1) {
+        int i, j;
+        if (nRow != m1.nRow) {
+            throw new MatrixSizeException();
+        }
+
+        if (nCol != m1.nCol) {
+            throw new MatrixSizeException();
+        }
+
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                values[i][j] = values[i][j] - m1.values[i][j];
+            }
+        }
+    }
+
+    /**
+     * Sets the value of this matrix to the matrix difference of matrices m1 and
+     * m2 (this = m1 - m2).
+     * 
+     * @param m1
+     *            the first matrix
+     * @param m2
+     *            the second matrix
+     */
+    public final void sub(GMatrix m1, GMatrix m2) {
+        int i, j;
+        if (m2.nRow != m1.nRow) {
+            throw new MatrixSizeException();
+        }
+
+        if (m2.nCol != m1.nCol) {
+            throw new MatrixSizeException();
+        }
+
+        if (nRow != m1.nRow || nCol != m1.nCol) {
+            throw new MatrixSizeException();
+        }
+
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                values[i][j] = m1.values[i][j] - m2.values[i][j];
+            }
+        }
+    }
+
+    /**
+     * Returns a string that contains the values of this GMatrix.
+     * 
+     * @return the String representation
+     */
+    public String toString() {
+        StringBuffer buffer = new StringBuffer(nRow * nCol * 8);
+
+        int i, j;
+
+        for (i = 0; i < nRow; i++) {
+            for (j = 0; j < nCol; j++) {
+                buffer.append(values[i][j]).append(" ");
+            }
+            buffer.append("\n");
+        }
+
+        return buffer.toString();
+    }
+
+    /**
+     * Returns the trace of this matrix.
+     * 
+     * @return the trace of this matrix
+     */
+    public final double trace() {
+        int i, l;
+        double t;
+
+        if (nRow < nCol) {
+            l = nRow;
+        } else {
+            l = nCol;
+        }
+
+        t = 0.0;
+        for (i = 0; i < l; i++) {
+            t += values[i][i];
+        }
+        return t;
+    }
+
+    /**
+     * Transposes this matrix in place.
+     */
+    public final void transpose() {
+        int i, j;
+
+        if (nRow != nCol) {
+            double[][] tmp;
+            i = nRow;
+            nRow = nCol;
+            nCol = i;
+            tmp = new double[nRow][nCol];
+            for (i = 0; i < nRow; i++) {
+                for (j = 0; j < nCol; j++) {
+                    tmp[i][j] = values[j][i];
+                }
+            }
+            values = tmp;
+        } else {
+            double swap;
+            for (i = 0; i < nRow; i++) {
+                for (j = 0; j < i; j++) {
+                    swap = values[i][j];
+                    values[i][j] = values[j][i];
+                    values[j][i] = swap;
+                }
+            }
+        }
+    }
+
+    /**
+     * Places the matrix values of the transpose of matrix m1 into this matrix.
+     * 
+     * @param m1
+     *            the matrix to be transposed (but not modified)
+     */
+    public final void transpose(GMatrix m1) {
+        int i, j;
+
+        if (nRow != m1.nCol || nCol != m1.nRow) {
+            throw new MatrixSizeException();
+        }
+
+        if (m1 != this) {
+            for (i = 0; i < nRow; i++) {
+                for (j = 0; j < nCol; j++) {
+                    values[i][j] = m1.values[j][i];
+                }
+            }
+        } else {
+            transpose();
+        }
+    }
+
+}
diff --git a/src/main/java/toxi/geom/GVector.java b/src/main/java/toxi/geom/GVector.java
new file mode 100644
index 0000000..5e8844b
--- /dev/null
+++ b/src/main/java/toxi/geom/GVector.java
@@ -0,0 +1,879 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+package toxi.geom;
+
+import toxi.math.InterpolateStrategy;
+import toxi.math.MathUtils;
+
+/**
+ * A double precision, general, dynamically-resizable, one-dimensional vector
+ * class.
+ */
+public class GVector implements java.io.Serializable, Cloneable {
+
+    private int length;
+    public double[] values;
+
+    static final long serialVersionUID = 1L;
+
+    /**
+     * Constructs a new GVector of the specified length and initializes it by
+     * copying the specified number of elements from the specified array. The
+     * array must contain at least <code>length</code> elements (i.e.,
+     * <code>vector.length</code> >= <code>length</code>. The length of this new
+     * GVector is set to the specified length.
+     * 
+     * @param vector
+     *            The array from which the values will be copied.
+     * @param length
+     *            The number of values copied from the array.
+     */
+    public GVector(double vector[], int length) {
+        this.length = length;
+        values = new double[length];
+        for (int i = 0; i < length; i++) {
+            values[i] = vector[i];
+        }
+    }
+
+    /**
+     * Constructs a new GVector from the specified array elements. The length of
+     * this GVector is set to the length of the specified array. The array
+     * elements are copied into this new GVector.
+     * 
+     * @param vector
+     *            the values for the new GVector.
+     */
+    public GVector(double[] vector) {
+        length = vector.length;
+        values = new double[vector.length];
+        for (int i = 0; i < length; i++) {
+            values[i] = vector[i];
+        }
+    }
+
+    /**
+     * Constructs a new GVector from the specified vector. The vector elements
+     * are copied into this new GVector.
+     * 
+     * @param vector
+     *            the source GVector for this new GVector.
+     */
+    public GVector(GVector vector) {
+        values = new double[vector.length];
+        length = vector.length;
+        for (int i = 0; i < length; i++) {
+            values[i] = vector.values[i];
+        }
+    }
+
+    /**
+     * Constructs a new GVector of the specified length with all vector elements
+     * initialized to 0.
+     * 
+     * @param length
+     *            the number of elements in this GVector.
+     */
+    public GVector(int length) {
+        this.length = length;
+        values = new double[length];
+        for (int i = 0; i < length; i++) {
+            values[i] = 0.0;
+        }
+    }
+
+    /**
+     * Constructs a new GVector and copies the initial values from the specified
+     * tuple.
+     * 
+     * @param v
+     *            the source for the new GVector's initial values
+     */
+    public GVector(ReadonlyVec2D v) {
+        values = new double[] {
+                v.x(), v.y()
+        };
+        length = 2;
+    }
+
+    /**
+     * Constructs a new GVector and copies the initial values from the specified
+     * tuple.
+     * 
+     * @param v
+     *            the source for the new GVector's initial values
+     */
+    public GVector(roVec3D v) {
+        values = new double[] {
+                v.x(), v.y(), v.z()
+        };
+        length = 3;
+    }
+
+    /**
+     * Constructs a new GVector and copies the initial values from the specified
+     * tuple.
+     * 
+     * @param v
+     *            the source for the new GVector's initial values
+     */
+    public GVector(ReadonlyVec4D v) {
+        values = new double[] {
+                v.x(), v.y(), v.z(), v.w()
+        };
+        length = 4;
+    }
+
+    /**
+     * Creates the vector sum of this vector and the given one (must be equal
+     * sized). Returns result as new vector.
+     * 
+     * @param v
+     * @return new vector
+     */
+    public final GVector add(GVector v) {
+        if (length != v.length) {
+            throw new MatrixSizeException();
+        }
+        double[] tmp = new double[length];
+        for (int i = 0; i < length; i++) {
+            tmp[i] = values[i] + v.values[i];
+        }
+        return new GVector(tmp);
+    }
+
+    /**
+     * Sets the value of this vector to sum of itself and the specified vector
+     * 
+     * @param vector
+     *            the second vector
+     * @return itself
+     */
+    public final GVector addSelf(GVector vector) {
+        if (length != vector.length) {
+            throw new MatrixSizeException();
+        }
+        for (int i = 0; i < length; i++) {
+            this.values[i] += vector.values[i];
+        }
+        return this;
+    }
+
+    /**
+     * Returns the (n-space) angle in radians between this vector and the vector
+     * parameter; the return value is constrained to the range [0,PI].
+     * 
+     * @param v
+     *            The other vector
+     * @return The angle in radians in the range [0,PI]
+     */
+    public final double angleBetween(GVector v) {
+        return (Math.acos(this.dot(v) / (this.magnitude() * v.magnitude())));
+    }
+
+    /**
+     * LU Decomposition Back Solve; this method takes the LU matrix and the
+     * permutation vector produced by the GMatrix method LUD and solves the
+     * equation (LU)*x = b by placing the solution vector x into this vector.
+     * This vector should be the same length or longer than b.
+     * 
+     * @param LU
+     *            The matrix into which the lower and upper decompostions have
+     *            been placed
+     * @param b
+     *            The b vector in the equation (LU)*x = b
+     * @param permutation
+     *            The row permuations that were necessary to produce the LU
+     *            matrix parameter
+     */
+    public final void backSolveLUD(GMatrix LU, GVector b, GVector permutation) {
+        int size = LU.nRow * LU.nCol;
+
+        double[] temp = new double[size];
+        double[] result = new double[size];
+        int[] row_perm = new int[b.size()];
+        int i, j;
+
+        if (LU.nRow != b.size()) {
+            throw new MatrixSizeException();
+        }
+
+        if (LU.nRow != permutation.size()) {
+            throw new MatrixSizeException();
+        }
+
+        if (LU.nRow != LU.nCol) {
+            throw new MatrixSizeException();
+        }
+
+        for (i = 0; i < LU.nRow; i++) {
+            for (j = 0; j < LU.nCol; j++) {
+                temp[i * LU.nCol + j] = LU.values[i][j];
+            }
+        }
+
+        for (i = 0; i < LU.nRow; i++) {
+            result[i * LU.nCol] = b.values[i];
+        }
+        for (i = 0; i < LU.nCol; i++) {
+            row_perm[i] = (int) permutation.values[i];
+        }
+
+        GMatrix.backSubstituteLU(LU.nRow, temp, row_perm, result);
+
+        for (i = 0; i < LU.nRow; i++) {
+            this.values[i] = result[i * LU.nCol];
+        }
+    }
+
+    /**
+     * Solves for x in Ax = b, where x is this vector (nx1), A is mxn, b is mx1,
+     * and A = U*W*transpose(V); U,W,V must be precomputed and can be found by
+     * taking the singular value decomposition (SVD) of A using the method SVD
+     * found in the GMatrix class.
+     * 
+     * @param U
+     *            The U matrix produced by the GMatrix method SVD
+     * @param W
+     *            The W matrix produced by the GMatrix method SVD
+     * @param V
+     *            The V matrix produced by the GMatrix method SVD
+     * @param b
+     *            The b vector in the linear equation Ax = b
+     */
+    public final void backSolveSVD(GMatrix U, GMatrix W, GMatrix V, GVector b) {
+        if (!(U.nRow == b.size() && U.nRow == U.nCol && U.nRow == W.nRow)) {
+            throw new MatrixSizeException();
+        }
+        if (!(W.nCol == values.length && W.nCol == V.nCol && W.nCol == V.nRow)) {
+            throw new MatrixSizeException();
+        }
+        GMatrix tmp = new GMatrix(U.nRow, W.nCol);
+        tmp.mul(U, V);
+        tmp.mulTransposeRight(U, W);
+        tmp.invert();
+        mul(tmp, b);
+    }
+
+    /**
+     * Creates a new object of the same class as this object.
+     * 
+     * @return a clone of this instance.
+     * @exception OutOfMemoryError
+     *                if there is not enough memory.
+     * @see Cloneable
+     * @since vecmath 1.3
+     */
+    public Object clone() {
+        GVector v = null;
+        try {
+            v = (GVector) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new InternalError();
+        }
+        v.values = new double[length];
+        for (int i = 0; i < length; i++) {
+            v.values[i] = values[i];
+        }
+        return v;
+    }
+
+    /**
+     * Returns the dot product of this vector and vector v.
+     * 
+     * @param v
+     *            the other vector
+     * @return the dot product of this and v
+     */
+    public final double dot(GVector v) {
+        if (length != v.length) {
+            throw new MatrixSizeException();
+        }
+        double result = 0.0;
+        for (int i = 0; i < length; i++) {
+            result += values[i] * v.values[i];
+        }
+        return result;
+    }
+
+    /**
+     * Returns true if all of the data members of GVector vector1 are equal to
+     * the corresponding data members in this GVector.
+     * 
+     * @param vector1
+     *            The vector with which the comparison is made.
+     * @return true or false
+     */
+    public boolean equals(GVector vector1) {
+        try {
+            if (length != vector1.length) {
+                return false;
+            }
+            for (int i = 0; i < length; i++) {
+                if (values[i] != vector1.values[i]) {
+                    return false;
+                }
+            }
+            return true;
+        } catch (NullPointerException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Returns true if the Object o1 is of type GMatrix and all of the data
+     * members of o1 are equal to the corresponding data members in this
+     * GMatrix.
+     * 
+     * @param o1
+     *            The object with which the comparison is made.
+     * @return true or false
+     */
+    public boolean equals(Object o1) {
+        try {
+            GVector v2 = (GVector) o1;
+            if (length != v2.length) {
+                return false;
+            }
+            for (int i = 0; i < length; i++) {
+                if (values[i] != v2.values[i]) {
+                    return false;
+                }
+            }
+            return true;
+        } catch (ClassCastException e) {
+            return false;
+        } catch (NullPointerException e) {
+            return false;
+        }
+
+    }
+
+    /**
+     * Returns true if the L-infinite distance between this vector and vector v
+     * is less than or equal to the tolerance parameter, otherwise returns
+     * false. The L-infinite distance is equal to MAX[abs(x1-x2), abs(y1-y2), .
+     * . . ].
+     * 
+     * @param v
+     *            The vector to be compared to this vector
+     * @param tolerance
+     *            the threshold value
+     */
+    public boolean equalsWithTolerance(GVector v, double tolerance) {
+        try {
+            double diff;
+            if (length != v.length) {
+                return false;
+            }
+            for (int i = 0; i < length; i++) {
+                diff = values[i] - v.values[i];
+                if ((diff < 0 ? -diff : diff) > tolerance) {
+                    return false;
+                }
+            }
+            return true;
+        } catch (NullPointerException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Retrieves the value at the specified index value of this vector.
+     * 
+     * @param index
+     *            the index of the element to retrieve (zero indexed)
+     * @return the value at the indexed element
+     */
+    public final double get(int index) {
+        return values[index];
+    }
+
+    /**
+     * Returns a hash code value based on the data values in this object. Two
+     * different GVector objects with identical data values (i.e.,
+     * GVector.equals returns true) will return the same hash number. Two
+     * GVector objects with different data members may return the same hash
+     * value, although this is not likely.
+     * 
+     * @return the integer hash code value
+     */
+    public int hashCode() {
+        long bits = 1L;
+        for (int i = 0; i < length; i++) {
+            bits = 31L * bits + VecMathUtil.doubleToLongBits(values[i]);
+        }
+        return (int) (bits ^ (bits >> 32));
+    }
+
+    /**
+     * Linearly interpolates this vector to the target vector and places the
+     * result into a new instance: result = this + (target-this)*alpha. The
+     * target vector needs to be equal sized.
+     * 
+     * @param v
+     *            the target vector
+     * @param alpha
+     *            the alpha interpolation parameter
+     * @return result as new vector
+     */
+    public final GVector interpolateTo(GVector v, double alpha) {
+        if (length != v.length) {
+            throw new MatrixSizeException();
+        }
+        return new GVector(this).interpolateToSelf(v, alpha);
+    }
+
+    /**
+     * Interpolates the vector towards the given target vector, using the given
+     * {@link InterpolateStrategy}. The target vector needs to be equal sized.
+     * 
+     * @param v
+     *            target vector
+     * @param alpha
+     *            interpolation factor (should be in the range 0..1)
+     * @param strategy
+     *            InterpolateStrategy instance
+     * 
+     * @return result as new vector
+     */
+    public final GVector interpolateTo(GVector v, double alpha,
+            InterpolateStrategy strategy) {
+        if (length != v.length) {
+            throw new MatrixSizeException();
+        }
+        return new GVector(this).interpolateToSelf(v, alpha, strategy);
+    }
+
+    /**
+     * Linearly interpolates this vector to the target vector and places result
+     * in this vector. result = this + (target-this)*alpha. The target vector
+     * needs to be equal sized.
+     * 
+     * @param v
+     *            the target vector
+     * @param alpha
+     *            the alpha interpolation parameter
+     */
+    public final GVector interpolateToSelf(GVector v, double alpha) {
+        if (v.length != length) {
+            throw new MatrixSizeException();
+        }
+        for (int i = 0; i < length; i++) {
+            values[i] += (v.values[i] - values[i]) * alpha;
+        }
+        return this;
+    }
+
+    /**
+     * Interpolates the vector towards the given target vector, using the given
+     * {@link InterpolateStrategy}. The target vector needs to be equal sized.
+     * 
+     * @param v
+     *            target vector
+     * @param alpha
+     *            interpolation factor (should be in the range 0..1)
+     * @param strategy
+     *            InterpolateStrategy instance
+     * 
+     * @return itself, result overrides current vector
+     */
+    public final GVector interpolateToSelf(GVector v, double alpha,
+            InterpolateStrategy strategy) {
+        if (v.length != length) {
+            throw new MatrixSizeException();
+        }
+        for (int i = 0; i < length; i++) {
+            values[i] = strategy.interpolate(values[i], v.values[i], alpha);
+        }
+        return this;
+    }
+
+    /**
+     * Negates the value of this vector: this = -this.
+     */
+    public final void invert() {
+        for (int i = 0; i < length; i++) {
+            this.values[i] *= -1.0;
+        }
+    }
+
+    /**
+     * Returns the square root of the sum of the squares of this vector (its
+     * length in n-dimensional space).
+     * 
+     * @return length of this vector
+     */
+
+    public final double magnitude() {
+        double sq = 0.0;
+        for (int i = 0; i < length; i++) {
+            sq += values[i] * values[i];
+        }
+        return Math.sqrt(sq);
+    }
+
+    /**
+     * Returns the sum of the squares of this vector (its length squared in
+     * n-dimensional space).
+     * 
+     * @return length squared of this vector
+     */
+    public final double magSquared() {
+        double sq = 0.0;
+        for (int i = 0; i < length; i++) {
+            sq += values[i] * values[i];
+        }
+        return sq;
+    }
+
+    /**
+     * Multiplies matrix m1 times Vector v1 and places the result into this
+     * vector (this = m1*v1).
+     * 
+     * @param m1
+     *            The matrix in the multiplication
+     * @param v1
+     *            The vector that is multiplied
+     */
+    public final void mul(GMatrix m1, GVector v1) {
+        if (m1.getNumCol() != v1.length) {
+            throw new MatrixSizeException();
+        }
+
+        if (length != m1.getNumRow()) {
+            throw new MatrixSizeException();
+        }
+
+        double v[];
+        if (v1 != this) {
+            v = v1.values;
+        } else {
+            v = values.clone();
+        }
+
+        for (int j = length - 1; j >= 0; j--) {
+            values[j] = 0.0;
+            for (int i = v1.length - 1; i >= 0; i--) {
+                values[j] += m1.values[j][i] * v[i];
+            }
+        }
+    }
+
+    /**
+     * Multiplies the transpose of vector v1 (ie, v1 becomes a row vector with
+     * respect to the multiplication) times matrix m1 and places the result into
+     * this vector (this = transpose(v1)*m1). The result is technically a row
+     * vector, but the GVector class only knows about column vectors, and so the
+     * result is stored as a column vector.
+     * 
+     * @param m1
+     *            The matrix in the multiplication
+     * @param v1
+     *            The vector that is temporarily transposed
+     */
+    public final void mul(GVector v1, GMatrix m1) {
+        if (m1.getNumRow() != v1.length) {
+            throw new MatrixSizeException();
+        }
+
+        if (length != m1.getNumCol()) {
+            throw new MatrixSizeException();
+        }
+
+        double v[];
+        if (v1 != this) {
+            v = v1.values;
+        } else {
+            v = values.clone();
+        }
+
+        for (int j = length - 1; j >= 0; j--) {
+            values[j] = 0.0;
+            for (int i = v1.length - 1; i >= 0; i--) {
+                values[j] += m1.values[i][j] * v[i];
+            }
+        }
+    }
+
+    /**
+     * Normalizes this vector in place.
+     */
+    public final void normalize() {
+        double mag = magnitude();
+        if (mag > MathUtils.EPS) {
+            double invMag = 1.0 / mag;
+            for (int i = 0; i < length; i++) {
+                values[i] = values[i] * invMag;
+            }
+        }
+    }
+
+    /**
+     * Scales this vector by the scale factor s and returns result as new
+     * vector.
+     * 
+     * @param s
+     *            the scalar value
+     * @return new vector
+     */
+    public final GVector scale(double s) {
+        double[] tmp = new double[length];
+        for (int i = 0; i < length; i++) {
+            tmp[i] = values[i] * s;
+        }
+        return new GVector(tmp);
+    }
+
+    /**
+     * Scales the values of this vector with the values of the given vector
+     * vector (this = this * vector). Returns result as new vector.
+     * 
+     * @param v
+     *            scale vector
+     * @return new vector
+     */
+    public final GVector scale(GVector v) {
+        if (length != v.length) {
+            throw new MatrixSizeException();
+        }
+        double[] tmp = new double[length];
+        for (int i = 0; i < length; i++) {
+            tmp[i] = values[i] * v.values[i];
+        }
+        return new GVector(tmp);
+    }
+
+    /**
+     * Scales this vector by the scale factor s.
+     * 
+     * @param s
+     *            the scalar value
+     * @return itself
+     */
+    public final GVector scaleSelf(double s) {
+        for (int i = 0; i < length; i++) {
+            values[i] = values[i] * s;
+        }
+        return this;
+    }
+
+    /**
+     * Scales the values of this vector with the values of the given vector
+     * vector (this = this * vector).
+     * 
+     * @param v
+     *            scale vector
+     * @return itself
+     */
+    public final GVector scaleSelf(GVector v) {
+        if (length != v.length) {
+            throw new MatrixSizeException();
+        }
+        for (int i = 0; i < length; i++) {
+            this.values[i] *= v.values[i];
+        }
+        return this;
+    }
+
+    /**
+     * Sets the values of this vector to the values found in the array
+     * parameter. If the array is shorter than the number of values in this
+     * vector the remaining values are zeroed. If the array is longer, only the
+     * first values up to to the vector length are copied.
+     * 
+     * @param vector
+     *            the source array
+     */
+    public final GVector set(double[] vector) {
+        int i;
+        if (vector.length >= length) {
+            for (i = 0; i < length; i++) {
+                values[i] = vector[i];
+            }
+        } else {
+            for (i = 0; i < vector.length; i++) {
+                values[i] = vector[i];
+            }
+            for (i = vector.length; i < length; i++) {
+                values[i] = 0.0;
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Sets the value of this vector to the values found in vector vector.
+     * 
+     * @param vector
+     *            the source vector
+     */
+    public final GVector set(GVector vector) {
+        return set(vector.values);
+    }
+
+    /**
+     * Sets the value of this vector to the values in tuple
+     * 
+     * @param tuple
+     *            the source for the new GVector's new values
+     */
+    public final GVector set(ReadonlyVec2D tuple) {
+        return set(new double[] {
+                tuple.x(), tuple.y()
+        });
+    }
+
+    /**
+     * Sets the value of this vector to the values in tuple
+     * 
+     * @param tuple
+     *            the source for the new GVector's new values
+     */
+    public final GVector set(roVec3D tuple) {
+        return set(new double[] {
+                tuple.x(), tuple.y(), tuple.z()
+        });
+    }
+
+    /**
+     * Sets the value of this vector to the values in tuple
+     * 
+     * @param tuple
+     *            the source for the new GVector's new values
+     * @return itself
+     */
+    public final GVector set(ReadonlyVec4D tuple) {
+        return set(new double[] {
+                tuple.x(), tuple.y(), tuple.w()
+        });
+    }
+
+    /**
+     * Modifies the value at the specified index of this vector.
+     * 
+     * @param index
+     *            the index if the element to modify (zero indexed)
+     * @param value
+     *            the new vector element value
+     */
+    public final GVector setElement(int index, double value) {
+        values[index] = value;
+        return this;
+    }
+
+    /**
+     * Changes the size of this vector dynamically. If the size is increased no
+     * data values will be lost. If the size is decreased, only those data
+     * values whose vector positions were eliminated will be lost.
+     * 
+     * @param length
+     *            number of desired elements in this vector
+     */
+    public final GVector setSize(int length) {
+        double[] tmp = new double[length];
+        int max;
+        if (this.length < length) {
+            max = this.length;
+        } else {
+            max = length;
+        }
+        for (int i = 0; i < max; i++) {
+            tmp[i] = values[i];
+        }
+        this.length = length;
+        values = tmp;
+        return this;
+    }
+
+    /**
+     * Returns the number of elements in this vector.
+     * 
+     * @return number of elements in this vector
+     */
+    public final int size() {
+        return values.length;
+    }
+
+    /**
+     * Creates the vector difference of this vector and the given one (must be
+     * equal sized). Returns result as new vector.
+     * 
+     * @param v
+     * @return new vector
+     */
+    public final GVector sub(GVector v) {
+        if (length != v.length) {
+            throw new MatrixSizeException();
+        }
+        double[] tmp = new double[length];
+        for (int i = 0; i < length; i++) {
+            tmp[i] = values[i] - v.values[i];
+        }
+        return new GVector(tmp);
+    }
+
+    /**
+     * Sets the value of this vector to the vector difference of itself and
+     * vector (this = this - vector).
+     * 
+     * @param vector
+     *            the other vector
+     */
+    public final GVector subSelf(GVector vector) {
+        if (length != vector.length) {
+            throw new MatrixSizeException();
+        }
+        for (int i = 0; i < length; i++) {
+            this.values[i] -= vector.values[i];
+        }
+        return this;
+    }
+
+    /**
+     * Returns a string that contains the values of this GVector.
+     * 
+     * @return the String representation
+     */
+    public String toString() {
+        StringBuilder buffer = new StringBuilder(length * 8);
+        for (int i = 0; i < length; i++) {
+            buffer.append(values[i]).append(" ");
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Sets all the values in this vector to zero.
+     */
+    public final GVector zero() {
+        for (int i = 0; i < length; i++) {
+            this.values[i] = 0.0;
+        }
+        return this;
+    }
+}
diff --git a/src/main/java/toxi/geom/GlobalGridTesselator.java b/src/main/java/toxi/geom/GlobalGridTesselator.java
new file mode 100644
index 0000000..799e581
--- /dev/null
+++ b/src/main/java/toxi/geom/GlobalGridTesselator.java
@@ -0,0 +1,43 @@
+package toxi.geom;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import toxi.math.MathUtils;
+
+/**
+ * A concrete implementation of the abstract {@link GridTesselator} using a grid
+ * in global coordinate space for generating additional points within a polygon.
+ * The resolution setting of this class defines the axis-aligned distance
+ * between grid points. E.g. a resolution of 10 means grid points are created a
+ * world space positions of multiples of 10 (i.e. 0,10,20 etc.). This resolution
+ * is used independently on polygon size, so depending on the chosen resolution
+ * and polygon size no additional inliers MIGHT be created at all. This behavior
+ * property is useful in cases where you want to adjust the number of resulting
+ * triangles dynamically, e.g. based on polygon size. Use the
+ * {@link LocalGridTesselator} for an alternative behavior.
+ * 
+ * @see GridTesselator
+ * @see LocalGridTesselator
+ * @see PolygonTesselator
+ */
+public class GlobalGridTesselator extends GridTesselator {
+
+    public GlobalGridTesselator(float res) {
+        super(res);
+    }
+
+    protected List<Vec2D> createInsidePoints(Polygon2D poly, Rect bounds) {
+        List<Vec2D> points = new ArrayList<Vec2D>();
+        for (float y = bounds.y; y < bounds.getBottom(); y += res) {
+            float yy = MathUtils.roundTo(y, res);
+            for (float x = bounds.x; x < bounds.getRight(); x += res) {
+                Vec2D p = new Vec2D(MathUtils.roundTo(x, res), yy);
+                if (poly.containsPoint(p)) {
+                    points.add(p);
+                }
+            }
+        }
+        return points;
+    }
+}
diff --git a/src/main/java/toxi/geom/GridTesselator.java b/src/main/java/toxi/geom/GridTesselator.java
new file mode 100644
index 0000000..7f027ca
--- /dev/null
+++ b/src/main/java/toxi/geom/GridTesselator.java
@@ -0,0 +1,93 @@
+package toxi.geom;
+
+import toxi.geom.mesh2d.DelaunayTriangulation;
+import toxi.geom.mesh2d.Voronoi;
+import toxi.math.MathUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is an implementation of the {@link PolygonTesselator} interface and
+ * abstract parent class for tesselating 2D polygons using a grid of additional
+ * points created within the polygon. These inlier points are connected to the
+ * original polygon vertices using a {@link DelaunayTriangulation}. The quality
+ * and final amount of triangles used can be adjusted via the number of
+ * additional grid points. This class currently has two concrete
+ * implementations: {@link GlobalGridTesselator} and {@link LocalGridTesselator}
+ * .
+ */
+public abstract class GridTesselator implements PolygonTesselator {
+
+    protected float res;
+    private float rootSize;
+
+    /**
+     * Creates a new instance with the given grid resolution.
+     * 
+     * @param res
+     *            snap distance for grid points
+     */
+    public GridTesselator(float res) {
+        this(res, Voronoi.DEFAULT_SIZE);
+    }
+
+    /**
+     * Creates a new instance with the given grid resolution.
+     * 
+     * @param res
+     *            snap distance for grid points
+     */
+    public GridTesselator(float res, float rootSize) {
+        this.res = res;
+        this.rootSize = rootSize;
+    }
+
+    protected abstract List<Vec2D> createInsidePoints(Polygon2D poly,
+            Rect bounds);
+
+    public float getResolution() {
+        return res;
+    }
+
+    public void setResolution(float res) {
+        this.res = res;
+    }
+
+    /**
+     * Tesselates/decomposes the given polygon into a list of 2D triangles using
+     * the currently set grid resolution.
+     * 
+     * @param poly
+     *            polygon to be tesselated
+     * @return list of triangles
+     */
+    public List<Triangle2D> tesselatePolygon(Polygon2D poly) {
+        List<Triangle2D> triangles = new ArrayList<Triangle2D>();
+        Rect bounds = poly.getBounds();
+        // a Voronoi diagram relies on a Delaunay triangulation behind the
+        // scenes
+        Voronoi voronoi = new Voronoi(rootSize);
+        // add perimeter points
+        for (Vec2D v : poly.vertices) {
+            voronoi.addPoint(v);
+        }
+        // add random inliers
+        for (Vec2D v : createInsidePoints(poly, bounds)) {
+            voronoi.addPoint(v);
+        }
+        // get filtered delaunay triangles:
+        // ignore any triangles which share a vertex with the initial root
+        // triangle or whose centroid is outside the polygon
+        for (Triangle2D t : voronoi.getTriangles()) {
+            if (MathUtils.abs(t.a.x) != Voronoi.DEFAULT_SIZE
+                    && MathUtils.abs(t.a.y) != Voronoi.DEFAULT_SIZE) {
+                if (poly.containsPoint(t.computeCentroid())) {
+                    triangles.add(t);
+                }
+            }
+        }
+        return triangles;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/syncleus/spangraph/geom/Intersector2D.java b/src/main/java/toxi/geom/Intersector2D.java
similarity index 97%
rename from src/main/java/com/syncleus/spangraph/geom/Intersector2D.java
rename to src/main/java/toxi/geom/Intersector2D.java
index 26e623d..acf253f 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Intersector2D.java
+++ b/src/main/java/toxi/geom/Intersector2D.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 /**
  * Generic interface for ray intersection with 2D geometry
diff --git a/src/main/java/com/syncleus/spangraph/geom/Intersector3D.java b/src/main/java/toxi/geom/Intersector3D.java
similarity index 97%
rename from src/main/java/com/syncleus/spangraph/geom/Intersector3D.java
rename to src/main/java/toxi/geom/Intersector3D.java
index c8caf73..ee7f122 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Intersector3D.java
+++ b/src/main/java/toxi/geom/Intersector3D.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 /**
  * Generic interface for ray intersection with 3D geometry
diff --git a/src/main/java/com/syncleus/spangraph/geom/IsectData2D.java b/src/main/java/toxi/geom/IsectData2D.java
similarity index 98%
rename from src/main/java/com/syncleus/spangraph/geom/IsectData2D.java
rename to src/main/java/toxi/geom/IsectData2D.java
index 7cec6cd..3fe3511 100644
--- a/src/main/java/com/syncleus/spangraph/geom/IsectData2D.java
+++ b/src/main/java/toxi/geom/IsectData2D.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 public class IsectData2D {
 
diff --git a/src/main/java/com/syncleus/spangraph/geom/IsectData3D.java b/src/main/java/toxi/geom/IsectData3D.java
similarity index 93%
rename from src/main/java/com/syncleus/spangraph/geom/IsectData3D.java
rename to src/main/java/toxi/geom/IsectData3D.java
index 46d6b37..ef76813 100644
--- a/src/main/java/com/syncleus/spangraph/geom/IsectData3D.java
+++ b/src/main/java/toxi/geom/IsectData3D.java
@@ -25,15 +25,15 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 public class IsectData3D {
 
     public boolean isIntersection;
     public float dist;
-    public ReadonlyVec3D pos;
-    public ReadonlyVec3D dir;
-    public ReadonlyVec3D normal;
+    public Vec3D pos;
+    public Vec3D dir;
+    public XYZ normal;
 
     public IsectData3D() {
 
diff --git a/src/main/java/com/syncleus/spangraph/geom/Line2D.java b/src/main/java/toxi/geom/Line2D.java
similarity index 95%
rename from src/main/java/com/syncleus/spangraph/geom/Line2D.java
rename to src/main/java/toxi/geom/Line2D.java
index 07e417c..17c9c9f 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Line2D.java
+++ b/src/main/java/toxi/geom/Line2D.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -309,20 +309,20 @@ public class Line2D {
             float ub = nb / denom;
             final Vec2D i = a.interpolateTo(b, ua);
             if (ua >= 0.0f && ua <= 1.0 && ub >= 0.0 && ub <= 1.0) {
-                isec = new LineIntersection(LineIntersection.Type.INTERSECTING, i, ua, ub);
+                isec = new LineIntersection(Type.INTERSECTING, i, ua, ub);
             } else {
-                isec = new LineIntersection(LineIntersection.Type.NON_INTERSECTING, i, ua, ub);
+                isec = new LineIntersection(Type.NON_INTERSECTING, i, ua, ub);
             }
         } else {
             if (na == 0.0 && nb == 0.0) {
                 if (distanceToPoint(l.a) == 0.0) {
-                    isec = new LineIntersection(LineIntersection.Type.COINCIDENT, null);
+                    isec = new LineIntersection(Type.COINCIDENT, null);
                 } else {
-                    isec = new LineIntersection(LineIntersection.Type.COINCIDENT_NO_INTERSECT,
+                    isec = new LineIntersection(Type.COINCIDENT_NO_INTERSECT,
                             null);
                 }
             } else {
-                isec = new LineIntersection(LineIntersection.Type.PARALLEL, null);
+                isec = new LineIntersection(Type.PARALLEL, null);
             }
         }
         return isec;
diff --git a/src/main/java/com/syncleus/spangraph/geom/Line3D.java b/src/main/java/toxi/geom/Line3D.java
similarity index 91%
rename from src/main/java/com/syncleus/spangraph/geom/Line3D.java
rename to src/main/java/toxi/geom/Line3D.java
index 372a3cd..45af97a 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Line3D.java
+++ b/src/main/java/toxi/geom/Line3D.java
@@ -25,16 +25,15 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.xml.bind.annotation.XmlElement;
+package toxi.geom;
 
 import toxi.geom.Line3D.LineIntersection.Type;
 import toxi.math.MathUtils;
 
+import javax.xml.bind.annotation.XmlElement;
+import java.util.ArrayList;
+import java.util.List;
+
 public class Line3D {
 
     public static class LineIntersection {
@@ -112,10 +111,10 @@ public class Line3D {
      *            false, if A is NOT to be added to results
      * @return list of result vectors
      */
-    public static final List<Vec3D> splitIntoSegments(Vec3D a, Vec3D b,
-            float stepLength, List<Vec3D> segments, boolean addFirst) {
+    public static final List<XYZ> splitIntoSegments(Vec3D a, Vec3D b,
+            float stepLength, List<XYZ> segments, boolean addFirst) {
         if (segments == null) {
-            segments = new ArrayList<Vec3D>();
+            segments = new ArrayList();
         }
         if (addFirst) {
             segments.add(a.copy());
@@ -142,9 +141,9 @@ public class Line3D {
         this.b = new Vec3D(x2, y2, z2);
     }
 
-    public Line3D(ReadonlyVec3D a, ReadonlyVec3D b) {
-        this.a = a.copy();
-        this.b = b.copy();
+    public Line3D(XYZ a, XYZ b) {
+        this.a = new Vec3D(a);
+        this.b = new Vec3D(b);
     }
 
     public Line3D(Vec3D a, Vec3D b) {
@@ -168,11 +167,11 @@ public class Line3D {
     public LineIntersection closestLineTo(Line3D l) {
         Vec3D p43 = l.a.sub(l.b);
         if (p43.isZeroVector()) {
-            return new LineIntersection(LineIntersection.Type.NON_INTERSECTING);
+            return new LineIntersection(Type.NON_INTERSECTING);
         }
         Vec3D p21 = b.sub(a);
         if (p21.isZeroVector()) {
-            return new LineIntersection(LineIntersection.Type.NON_INTERSECTING);
+            return new LineIntersection(Type.NON_INTERSECTING);
         }
         Vec3D p13 = a.sub(l.a);
 
@@ -184,7 +183,7 @@ public class Line3D {
 
         double denom = d2121 * d4343 - d4321 * d4321;
         if (MathUtils.abs(denom) < MathUtils.EPS) {
-            return new LineIntersection(LineIntersection.Type.NON_INTERSECTING);
+            return new LineIntersection(Type.NON_INTERSECTING);
         }
         double numer = d1343 * d4321 - d1321 * d4343;
         float mua = (float) (numer / denom);
@@ -192,7 +191,7 @@ public class Line3D {
 
         Vec3D pa = a.add(p21.scaleSelf(mua));
         Vec3D pb = l.a.add(p43.scaleSelf(mub));
-        return new LineIntersection(LineIntersection.Type.INTERSECTING, new Line3D(pa, pb), mua,
+        return new LineIntersection(Type.INTERSECTING, new Line3D(pa, pb), mua,
                 mub);
     }
 
@@ -203,7 +202,7 @@ public class Line3D {
      *            point to check against
      * @return closest point on the line
      */
-    public Vec3D closestPointTo(ReadonlyVec3D p) {
+    public Vec3D closestPointTo(roVec3D p) {
         final Vec3D v = b.sub(a);
         final float t = p.sub(a).dot(v) / v.magSquared();
         // Check to see if t is beyond the extents of the line segment
@@ -240,7 +239,7 @@ public class Line3D {
      * Returns the line's axis-aligned bounding box.
      * 
      * @return aabb
-     * @see toxi.geom.AABB
+     * @see AABB
      */
     public AABB getBounds() {
         return AABB.fromMinMax(a, b);
@@ -262,7 +261,7 @@ public class Line3D {
         return a.add(b).scaleSelf(0.5f);
     }
 
-    public Vec3D getNormal() {
+    public XYZ getNormal() {
         return b.cross(a);
     }
 
@@ -323,7 +322,7 @@ public class Line3D {
         return this;
     }
 
-    public Line3D set(ReadonlyVec3D a, ReadonlyVec3D b) {
+    public Line3D set(roVec3D a, roVec3D b) {
         this.a = a.copy();
         this.b = b.copy();
         return this;
@@ -335,7 +334,7 @@ public class Line3D {
         return this;
     }
 
-    public List<Vec3D> splitIntoSegments(List<Vec3D> segments,
+    public List<XYZ> splitIntoSegments(List<XYZ> segments,
             float stepLength, boolean addFirst) {
         return splitIntoSegments(a, b, stepLength, segments, addFirst);
     }
diff --git a/src/main/java/toxi/geom/LineStrip2D.java b/src/main/java/toxi/geom/LineStrip2D.java
new file mode 100644
index 0000000..af80183
--- /dev/null
+++ b/src/main/java/toxi/geom/LineStrip2D.java
@@ -0,0 +1,319 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.geom;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlElement;
+
+import toxi.geom.Line2D.LineIntersection;
+import toxi.geom.Line2D.LineIntersection.Type;
+import toxi.math.MathUtils;
+
+public class LineStrip2D implements Iterable<Vec2D> {
+
+    @XmlElement(name = "v")
+    protected List<Vec2D> vertices = new ArrayList<Vec2D>();
+
+    protected float[] arcLenIndex;
+
+    public LineStrip2D() {
+    }
+
+    public LineStrip2D(Collection<? extends Vec2D> vertices) {
+        this.vertices = new ArrayList<Vec2D>(vertices);
+    }
+
+    public LineStrip2D add(float x, float y) {
+        vertices.add(new Vec2D(x, y));
+        return this;
+    }
+
+    public LineStrip2D add(ReadonlyVec2D p) {
+        vertices.add(p.copy());
+        return this;
+    }
+
+    public LineStrip2D add(Vec2D p) {
+        vertices.add(p);
+        return this;
+    }
+
+    /**
+     * Returns the vertex at the given index. This function follows Python
+     * convention, in that if the index is negative, it is considered relative
+     * to the list end. Therefore the vertex at index -1 is the last vertex in
+     * the list.
+     * 
+     * @param i
+     *            index
+     * @return vertex
+     */
+    public Vec2D get(int i) {
+        if (i < 0) {
+            i += vertices.size();
+        }
+        return vertices.get(i);
+    }
+
+    public Circle getBoundingCircle() {
+        return Circle.newBoundingCircle(vertices);
+    }
+
+    public Rect getBounds() {
+        return Rect.getBoundingRect(vertices);
+    }
+
+    public Vec2D getCentroid() {
+        int num = vertices.size();
+        if (num > 0) {
+            Vec2D centroid = new Vec2D();
+            for (Vec2D v : vertices) {
+                centroid.addSelf(v);
+            }
+            return centroid.scaleSelf(1f / num);
+        }
+        return null;
+    }
+
+    /**
+     * Computes a list of points along the spline which are uniformly separated
+     * by the given step distance.
+     * 
+     * @param step
+     * @return point list
+     */
+    public List<Vec2D> getDecimatedVertices(float step) {
+        return getDecimatedVertices(step, true);
+    }
+
+    /**
+     * Computes a list of points along the spline which are close to uniformly
+     * separated by the given step distance. The uniform distribution is only an
+     * approximation and is based on the estimated arc length of the polyline.
+     * The distance between returned points might vary in places, especially if
+     * there're sharp angles between line segments.
+     * 
+     * @param step
+     * @param doAddFinalVertex
+     *            true, if the last vertex computed should be added regardless
+     *            of its distance.
+     * @return point list
+     */
+    public List<Vec2D> getDecimatedVertices(float step, boolean doAddFinalVertex) {
+        ArrayList<Vec2D> uniform = new ArrayList<Vec2D>();
+        if (vertices.size() < 3) {
+            if (vertices.size() == 2) {
+                new Line2D(vertices.get(0), vertices.get(1)).splitIntoSegments(
+                        uniform, step, true);
+                if (!doAddFinalVertex) {
+                    uniform.remove(uniform.size() - 1);
+                }
+            } else {
+                return null;
+            }
+        }
+        float arcLen = getLength();
+        if (arcLen > 0) {
+            double delta = step / arcLen;
+            int currIdx = 0;
+            for (double t = 0; t < 1.0; t += delta) {
+                double currT = t * arcLen;
+                while (currT >= arcLenIndex[currIdx]) {
+                    currIdx++;
+                }
+                ReadonlyVec2D p = vertices.get(currIdx - 1);
+                ReadonlyVec2D q = vertices.get(currIdx);
+                float frac = (float) ((currT - arcLenIndex[currIdx - 1]) / (arcLenIndex[currIdx] - arcLenIndex[currIdx - 1]));
+                Vec2D i = p.interpolateTo(q, frac);
+                uniform.add(i);
+            }
+            if (doAddFinalVertex) {
+                uniform.add(vertices.get(vertices.size() - 1).copy());
+            }
+        }
+        return uniform;
+    }
+
+    /**
+     * Returns a list of {@link Line2D} segments representing the segments
+     * between the vertices of this strip.
+     * 
+     * @return list of lines
+     */
+    public List<Line2D> getEdges() {
+        int num = vertices.size();
+        List<Line2D> edges = new ArrayList<Line2D>(num - 1);
+        for (int i = 1; i < num; i++) {
+            edges.add(new Line2D(vertices.get(i - 1), vertices.get(i)));
+        }
+        return edges;
+    }
+
+    public float getLength() {
+        if (arcLenIndex == null
+                || (arcLenIndex != null && arcLenIndex.length != vertices
+                        .size())) {
+            arcLenIndex = new float[vertices.size()];
+        }
+        float arcLen = 0;
+        for (int i = 1; i < arcLenIndex.length; i++) {
+            ReadonlyVec2D p = vertices.get(i - 1);
+            ReadonlyVec2D q = vertices.get(i);
+            arcLen += p.distanceTo(q);
+            arcLenIndex[i] = arcLen;
+        }
+        return arcLen;
+    }
+
+    /**
+     * Computes point at position t, where t is the normalized position along
+     * the strip. If t&lt;0 then the first vertex of the strip is returned. If
+     * t&gt;=1.0 the last vertex is returned. If the strip contains less than 2
+     * vertices, this method returns null.
+     * 
+     * @param t
+     * @return
+     */
+    public Vec2D getPointAt(float t) {
+        int num = vertices.size();
+        if (num > 1) {
+            if (t <= 0.0) {
+                return vertices.get(0);
+            } else if (t >= 1.0) {
+                return vertices.get(num - 1);
+            }
+            float totalLength = this.getLength();
+            double offp = 0, offq = 0;
+            for (int i = 1; i < num; i++) {
+                Vec2D p = vertices.get(i - 1);
+                Vec2D q = vertices.get(i);
+                offq += q.distanceTo(p) / totalLength;
+                if (offp <= t && offq >= t) {
+                    return p.interpolateTo(q, (float) MathUtils.mapInterval(t,
+                            offp, offq, 0.0, 1.0));
+                }
+                offp = offq;
+            }
+        }
+        return null;
+    }
+
+    public List<Line2D> getSegments() {
+        final int num = vertices.size();
+        List<Line2D> segments = new ArrayList<Line2D>(num - 1);
+        for (int i = 1; i < num; i++) {
+            segments.add(new Line2D(vertices.get(i - 1), vertices.get(i)));
+        }
+        return segments;
+    }
+
+    /**
+     * @return the vertices
+     */
+    public List<Vec2D> getVertices() {
+        return vertices;
+    }
+
+    public LineIntersection intersectLine(Line2D line) {
+        Line2D l = new Line2D(new Vec2D(), new Vec2D());
+        for (int i = 1, num = vertices.size(); i < num; i++) {
+            l.set(vertices.get(i - 1), vertices.get(i));
+            LineIntersection isec = l.intersectLine(line);
+            if (isec.getType() == Type.INTERSECTING
+                    || isec.getType() == Type.COINCIDENT) {
+                return isec;
+            }
+        }
+        return null;
+    }
+
+    public Iterator<Vec2D> iterator() {
+        return vertices.iterator();
+    }
+
+    public LineStrip2D rotate(float theta) {
+        for (Vec2D v : vertices) {
+            v.rotate(theta);
+        }
+        return this;
+    }
+
+    public LineStrip2D scale(float scale) {
+        return scale(scale, scale);
+    }
+
+    public LineStrip2D scale(float x, float y) {
+        for (Vec2D v : vertices) {
+            v.scaleSelf(x, y);
+        }
+        return this;
+    }
+
+    public LineStrip2D scale(ReadonlyVec2D scale) {
+        return scale(scale.x(), scale.y());
+    }
+
+    public LineStrip2D scaleSize(float scale) {
+        return scaleSize(scale, scale);
+    }
+
+    public LineStrip2D scaleSize(float x, float y) {
+        Vec2D centroid = getCentroid();
+        for (Vec2D v : vertices) {
+            v.subSelf(centroid).scaleSelf(x, y).addSelf(centroid);
+        }
+        return this;
+    }
+
+    public LineStrip2D scaleSize(ReadonlyVec2D scale) {
+        return scaleSize(scale.x(), scale.y());
+    }
+
+    /**
+     * @param vertices
+     *            the vertices to set
+     */
+    public void setVertices(List<Vec2D> vertices) {
+        this.vertices = vertices;
+    }
+
+    public LineStrip2D translate(float x, float y) {
+        for (Vec2D v : vertices) {
+            v.addSelf(x, y);
+        }
+        return this;
+    }
+
+    public LineStrip2D translate(ReadonlyVec2D offset) {
+        return translate(offset.x(), offset.y());
+    }
+}
diff --git a/src/main/java/toxi/geom/LineStrip3D.java b/src/main/java/toxi/geom/LineStrip3D.java
new file mode 100644
index 0000000..d76121f
--- /dev/null
+++ b/src/main/java/toxi/geom/LineStrip3D.java
@@ -0,0 +1,182 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.geom;
+
+import javax.xml.bind.annotation.XmlElement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+public class LineStrip3D implements Iterable<XYZ> {
+
+    @XmlElement(name = "v")
+    protected List<XYZ> vertices = new ArrayList<XYZ>();
+
+    protected float[] arcLenIndex;
+
+    public LineStrip3D() {
+    }
+
+    public LineStrip3D(Collection<? extends XYZ> vertices) {
+        this.vertices = new ArrayList(vertices);
+    }
+
+    public LineStrip3D add(float x, float y, float z) {
+        vertices.add(new Vec3D(x, y, z));
+        return this;
+    }
+
+    public LineStrip3D add(roVec3D p) {
+        vertices.add(p.copy());
+        return this;
+    }
+
+    public LineStrip3D add(XYZ p) {
+        vertices.add(p);
+        return this;
+    }
+
+    /**
+     * Returns the vertex at the given index. This function follows Python
+     * convention, in that if the index is negative, it is considered relative
+     * to the list end. Therefore the vertex at index -1 is the last vertex in
+     * the list.
+     * 
+     * @param i
+     *            index
+     * @return vertex
+     */
+    public XYZ get(int i) {
+        if (i < 0) {
+            i += vertices.size();
+        }
+        return vertices.get(i);
+    }
+
+    /**
+     * Computes a list of points along the spline which are uniformly separated
+     * by the given step distance.
+     * 
+     * @param step
+     * @return point list
+     */
+    public List<XYZ> getDecimatedVertices(float step) {
+        return getDecimatedVertices(step, true);
+    }
+
+    /**
+     * Computes a list of points along the spline which are close to uniformly
+     * separated by the given step distance. The uniform distribution is only an
+     * approximation and is based on the estimated arc length of the polyline.
+     * The distance between returned points might vary in places, especially if
+     * there're sharp angles between line segments.
+     * 
+     * @param step
+     * @param doAddFinalVertex
+     *            true, if the last vertex computed should be added regardless
+     *            of its distance.
+     * @return point list
+     */
+    public List<XYZ> getDecimatedVertices(float step, boolean doAddFinalVertex) {
+        ArrayList<XYZ> uniform = new ArrayList();
+        if (vertices.size() < 3) {
+            if (vertices.size() == 2) {
+                new Line3D(vertices.get(0), vertices.get(1)).splitIntoSegments(
+                        uniform, step, true);
+                if (!doAddFinalVertex) {
+                    uniform.remove(uniform.size() - 1);
+                }
+            } else {
+                return null;
+            }
+        }
+        float arcLen = getEstimatedArcLength();
+        double delta = (double) step / arcLen;
+        int currIdx = 0;
+        for (double t = 0; t < 1.0; t += delta) {
+            double currT = t * arcLen;
+            while (currT >= arcLenIndex[currIdx]) {
+                currIdx++;
+            }
+            XYZ p = vertices.get(currIdx - 1);
+            XYZ q = vertices.get(currIdx);
+            float frac = (float) ((currT - arcLenIndex[currIdx - 1]) / (arcLenIndex[currIdx] - arcLenIndex[currIdx - 1]));
+            XYZ i = p.interpolateTo(q, frac);
+            uniform.add(i);
+        }
+        if (doAddFinalVertex) {
+            uniform.add(vertices.get(vertices.size() - 1).copy());
+        }
+        return uniform;
+    }
+
+    public float getEstimatedArcLength() {
+        if (arcLenIndex == null
+                || (arcLenIndex != null && arcLenIndex.length != vertices
+                        .size())) {
+            arcLenIndex = new float[vertices.size()];
+        }
+        float arcLen = 0;
+        for (int i = 1; i < arcLenIndex.length; i++) {
+            XYZ p = vertices.get(i - 1);
+            XYZ q = vertices.get(i);
+            arcLen += p.distanceTo(q);
+            arcLenIndex[i] = arcLen;
+        }
+        return arcLen;
+    }
+
+    public List<Line3D> getSegments() {
+        final int num = vertices.size();
+        List<Line3D> segments = new ArrayList<Line3D>(num - 1);
+        for (int i = 1; i < num; i++) {
+            segments.add(new Line3D(vertices.get(i - 1), vertices.get(i)));
+        }
+        return segments;
+    }
+
+    /**
+     * @return the vertices
+     */
+    public List<XYZ> getVertices() {
+        return vertices;
+    }
+
+    public Iterator<XYZ> iterator() {
+        return vertices.iterator();
+    }
+
+    /**
+     * @param vertices
+     *            the vertices to set
+     */
+    public void setVertices(List<XYZ> vertices) {
+        this.vertices = vertices;
+    }
+}
diff --git a/src/main/java/toxi/geom/LocalGridTesselator.java b/src/main/java/toxi/geom/LocalGridTesselator.java
new file mode 100644
index 0000000..8c8e358
--- /dev/null
+++ b/src/main/java/toxi/geom/LocalGridTesselator.java
@@ -0,0 +1,46 @@
+package toxi.geom;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import toxi.math.ScaleMap;
+
+/**
+ * A concrete implementation of the abstract {@link GridTesselator} using a grid
+ * in polygon-local coordinate space for generating additional points within a
+ * polygon. The resolution setting of this class defines number of desired grid
+ * points in X & Y direction. E.g. a resolution of 10 means up to 10x10 grid
+ * points are created a within the polygon bounding rect. For smaller polygons,
+ * the resulting triangles will simply be smaller too. This resolution is used
+ * independently on polygon size. Use the {@link GlobalGridTesselator} for an
+ * alternative behavior, resulting in more uniformly sized triangles.
+ * 
+ * @see GridTesselator
+ * @see GlobalGridTesselator
+ * @see PolygonTesselator
+ */
+public class LocalGridTesselator extends GridTesselator {
+
+    public LocalGridTesselator(int res) {
+        super(res);
+    }
+
+    protected List<Vec2D> createInsidePoints(Polygon2D poly, Rect bounds) {
+        List<Vec2D> points = new ArrayList<Vec2D>();
+        int ires = (int) res;
+        ScaleMap xmap = new ScaleMap(0, ires, bounds.getLeft(),
+                bounds.getRight());
+        ScaleMap ymap = new ScaleMap(0, ires, bounds.getTop(),
+                bounds.getBottom());
+        for (int y = 0; y < ires; y++) {
+            float yy = (float) ymap.getMappedValueFor(y);
+            for (int x = 0; x < ires; x++) {
+                Vec2D p = new Vec2D((float) xmap.getMappedValueFor(x), yy);
+                if (poly.containsPoint(p)) {
+                    points.add(p);
+                }
+            }
+        }
+        return points;
+    }
+}
diff --git a/src/main/java/toxi/geom/Matrix3d.java b/src/main/java/toxi/geom/Matrix3d.java
new file mode 100644
index 0000000..3e38371
--- /dev/null
+++ b/src/main/java/toxi/geom/Matrix3d.java
@@ -0,0 +1,3063 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * $Revision: 127 $
+ * $Date: 2008-02-28 20:18:51 +0000 (Thu, 28 Feb 2008) $
+ * $State$
+ */
+
+package toxi.geom;
+
+import toxi.math.MathUtils;
+
+/**
+ * A double precision floating point 3 by 3 matrix. Primarily to support 3D
+ * rotations.
+ */
+public class Matrix3d implements java.io.Serializable, Cloneable {
+
+    // Compatible with 1.1
+    static final long serialVersionUID = 6837536777072402710L;
+
+    private static final boolean almostEqual(double a, double b) {
+        if (a == b) {
+            return true;
+        }
+
+        final double EPSILON_ABSOLUTE = 1.0e-6;
+        final double EPSILON_RELATIVE = 1.0e-4;
+        double diff = Math.abs(a - b);
+        double absA = Math.abs(a);
+        double absB = Math.abs(b);
+        double max = (absA >= absB) ? absA : absB;
+
+        if (diff < EPSILON_ABSOLUTE) {
+            return true;
+        }
+
+        if ((diff / max) < EPSILON_RELATIVE) {
+            return true;
+        }
+
+        return false;
+    }
+
+    static int compute_2X2(double f, double g, double h,
+            double[] single_values, double[] snl, double[] csl, double[] snr,
+            double[] csr, int index) {
+
+        double c_b3 = 2.;
+        double c_b4 = 1.;
+
+        double d__1;
+        int pmax;
+        double temp;
+        boolean swap;
+        double a, d, l, m, r, s, t, tsign, fa, ga, ha;
+        double ft, gt, ht, mm;
+        boolean gasmal;
+        double tt, clt, crt, slt, srt;
+        double ssmin, ssmax;
+
+        ssmax = single_values[0];
+        ssmin = single_values[1];
+        clt = 0.0;
+        crt = 0.0;
+        slt = 0.0;
+        srt = 0.0;
+        tsign = 0.0;
+
+        ft = f;
+        fa = Math.abs(ft);
+        ht = h;
+        ha = Math.abs(h);
+
+        pmax = 1;
+        if (ha > fa) {
+            swap = true;
+        } else {
+            swap = false;
+        }
+
+        if (swap) {
+            pmax = 3;
+            temp = ft;
+            ft = ht;
+            ht = temp;
+            temp = fa;
+            fa = ha;
+            ha = temp;
+
+        }
+        gt = g;
+        ga = Math.abs(gt);
+        if (ga == 0.) {
+
+            single_values[1] = ha;
+            single_values[0] = fa;
+            clt = 1.;
+            crt = 1.;
+            slt = 0.;
+            srt = 0.;
+        } else {
+            gasmal = true;
+
+            if (ga > fa) {
+                pmax = 2;
+                if (fa / ga < EPS) {
+
+                    gasmal = false;
+                    ssmax = ga;
+                    if (ha > 1.) {
+                        ssmin = fa / (ga / ha);
+                    } else {
+                        ssmin = fa / ga * ha;
+                    }
+                    clt = 1.;
+                    slt = ht / gt;
+                    srt = 1.;
+                    crt = ft / gt;
+                }
+            }
+            if (gasmal) {
+
+                d = fa - ha;
+                if (d == fa) {
+
+                    l = 1.;
+                } else {
+                    l = d / fa;
+                }
+
+                m = gt / ft;
+
+                t = 2. - l;
+
+                mm = m * m;
+                tt = t * t;
+                s = Math.sqrt(tt + mm);
+
+                if (l == 0.) {
+                    r = Math.abs(m);
+                } else {
+                    r = Math.sqrt(l * l + mm);
+                }
+
+                a = (s + r) * .5;
+
+                if (ga > fa) {
+                    pmax = 2;
+                    if (fa / ga < EPS) {
+
+                        gasmal = false;
+                        ssmax = ga;
+                        if (ha > 1.) {
+                            ssmin = fa / (ga / ha);
+                        } else {
+                            ssmin = fa / ga * ha;
+                        }
+                        clt = 1.;
+                        slt = ht / gt;
+                        srt = 1.;
+                        crt = ft / gt;
+                    }
+                }
+                if (gasmal) {
+
+                    d = fa - ha;
+                    if (d == fa) {
+
+                        l = 1.;
+                    } else {
+                        l = d / fa;
+                    }
+
+                    m = gt / ft;
+
+                    t = 2. - l;
+
+                    mm = m * m;
+                    tt = t * t;
+                    s = Math.sqrt(tt + mm);
+
+                    if (l == 0.) {
+                        r = Math.abs(m);
+                    } else {
+                        r = Math.sqrt(l * l + mm);
+                    }
+
+                    a = (s + r) * .5;
+
+                    ssmin = ha / a;
+                    ssmax = fa * a;
+                    if (mm == 0.) {
+
+                        if (l == 0.) {
+                            t = MathUtils.dualSign(c_b3, ft)
+                                    * MathUtils.dualSign(c_b4, gt);
+                        } else {
+                            t = gt / MathUtils.dualSign(d, ft) + m / t;
+                        }
+                    } else {
+                        t = (m / (s + t) + m / (r + l)) * (a + 1.);
+                    }
+                    l = Math.sqrt(t * t + 4.);
+                    crt = 2. / l;
+                    srt = t / l;
+                    clt = (crt + srt * m) / a;
+                    slt = ht / ft * srt / a;
+                }
+            }
+            if (swap) {
+                csl[0] = srt;
+                snl[0] = crt;
+                csr[0] = slt;
+                snr[0] = clt;
+            } else {
+                csl[0] = clt;
+                snl[0] = slt;
+                csr[0] = crt;
+                snr[0] = srt;
+            }
+
+            if (pmax == 1) {
+                tsign = MathUtils.dualSign(c_b4, csr[0])
+                        * MathUtils.dualSign(c_b4, csl[0])
+                        * MathUtils.dualSign(c_b4, f);
+            }
+            if (pmax == 2) {
+                tsign = MathUtils.dualSign(c_b4, snr[0])
+                        * MathUtils.dualSign(c_b4, csl[0])
+                        * MathUtils.dualSign(c_b4, g);
+            }
+            if (pmax == 3) {
+                tsign = MathUtils.dualSign(c_b4, snr[0])
+                        * MathUtils.dualSign(c_b4, snl[0])
+                        * MathUtils.dualSign(c_b4, h);
+            }
+            single_values[index] = MathUtils.dualSign(ssmax, tsign);
+            d__1 = tsign * MathUtils.dualSign(c_b4, f)
+                    * MathUtils.dualSign(c_b4, h);
+            single_values[index + 1] = MathUtils.dualSign(ssmin, d__1);
+
+        }
+        return 0;
+    }
+
+    static int compute_qr(double[] s, double[] e, double[] u, double[] v) {
+        int k;
+        boolean converged;
+        double shift, r;
+        double[] cosl = new double[2];
+        double[] cosr = new double[2];
+        double[] sinl = new double[2];
+        double[] sinr = new double[2];
+        double[] m = new double[9];
+
+        double utemp, vtemp;
+        double f, g;
+
+        final int MAX_INTERATIONS = 10;
+        final double CONVERGE_TOL = 4.89E-15;
+
+        double c_b48 = 1.;
+        int first;
+        converged = false;
+
+        first = 1;
+
+        if (Math.abs(e[1]) < CONVERGE_TOL || Math.abs(e[0]) < CONVERGE_TOL) {
+            converged = true;
+        }
+
+        for (k = 0; k < MAX_INTERATIONS && !converged; k++) {
+            shift = compute_shift(s[1], e[1], s[2]);
+            f = (Math.abs(s[0]) - shift)
+                    * (MathUtils.dualSign(c_b48, s[0]) + shift / s[0]);
+            g = e[0];
+            r = compute_rot(f, g, sinr, cosr, 0, first);
+            f = cosr[0] * s[0] + sinr[0] * e[0];
+            e[0] = cosr[0] * e[0] - sinr[0] * s[0];
+            g = sinr[0] * s[1];
+            s[1] = cosr[0] * s[1];
+
+            r = compute_rot(f, g, sinl, cosl, 0, first);
+            first = 0;
+            s[0] = r;
+            f = cosl[0] * e[0] + sinl[0] * s[1];
+            s[1] = cosl[0] * s[1] - sinl[0] * e[0];
+            g = sinl[0] * e[1];
+            e[1] = cosl[0] * e[1];
+
+            r = compute_rot(f, g, sinr, cosr, 1, first);
+            e[0] = r;
+            f = cosr[1] * s[1] + sinr[1] * e[1];
+            e[1] = cosr[1] * e[1] - sinr[1] * s[1];
+            g = sinr[1] * s[2];
+            s[2] = cosr[1] * s[2];
+
+            r = compute_rot(f, g, sinl, cosl, 1, first);
+            s[1] = r;
+            f = cosl[1] * e[1] + sinl[1] * s[2];
+            s[2] = cosl[1] * s[2] - sinl[1] * e[1];
+            e[1] = f;
+
+            // update u matrices
+            utemp = u[0];
+            u[0] = cosl[0] * utemp + sinl[0] * u[3];
+            u[3] = -sinl[0] * utemp + cosl[0] * u[3];
+            utemp = u[1];
+            u[1] = cosl[0] * utemp + sinl[0] * u[4];
+            u[4] = -sinl[0] * utemp + cosl[0] * u[4];
+            utemp = u[2];
+            u[2] = cosl[0] * utemp + sinl[0] * u[5];
+            u[5] = -sinl[0] * utemp + cosl[0] * u[5];
+
+            utemp = u[3];
+            u[3] = cosl[1] * utemp + sinl[1] * u[6];
+            u[6] = -sinl[1] * utemp + cosl[1] * u[6];
+            utemp = u[4];
+            u[4] = cosl[1] * utemp + sinl[1] * u[7];
+            u[7] = -sinl[1] * utemp + cosl[1] * u[7];
+            utemp = u[5];
+            u[5] = cosl[1] * utemp + sinl[1] * u[8];
+            u[8] = -sinl[1] * utemp + cosl[1] * u[8];
+
+            // update v matrices
+
+            vtemp = v[0];
+            v[0] = cosr[0] * vtemp + sinr[0] * v[1];
+            v[1] = -sinr[0] * vtemp + cosr[0] * v[1];
+            vtemp = v[3];
+            v[3] = cosr[0] * vtemp + sinr[0] * v[4];
+            v[4] = -sinr[0] * vtemp + cosr[0] * v[4];
+            vtemp = v[6];
+            v[6] = cosr[0] * vtemp + sinr[0] * v[7];
+            v[7] = -sinr[0] * vtemp + cosr[0] * v[7];
+
+            vtemp = v[1];
+            v[1] = cosr[1] * vtemp + sinr[1] * v[2];
+            v[2] = -sinr[1] * vtemp + cosr[1] * v[2];
+            vtemp = v[4];
+            v[4] = cosr[1] * vtemp + sinr[1] * v[5];
+            v[5] = -sinr[1] * vtemp + cosr[1] * v[5];
+            vtemp = v[7];
+            v[7] = cosr[1] * vtemp + sinr[1] * v[8];
+            v[8] = -sinr[1] * vtemp + cosr[1] * v[8];
+
+            m[0] = s[0];
+            m[1] = e[0];
+            m[2] = 0.0;
+            m[3] = 0.0;
+            m[4] = s[1];
+            m[5] = e[1];
+            m[6] = 0.0;
+            m[7] = 0.0;
+            m[8] = s[2];
+
+            if (Math.abs(e[1]) < CONVERGE_TOL || Math.abs(e[0]) < CONVERGE_TOL) {
+                converged = true;
+            }
+        }
+
+        if (Math.abs(e[1]) < CONVERGE_TOL) {
+            compute_2X2(s[0], e[0], s[1], s, sinl, cosl, sinr, cosr, 0);
+
+            utemp = u[0];
+            u[0] = cosl[0] * utemp + sinl[0] * u[3];
+            u[3] = -sinl[0] * utemp + cosl[0] * u[3];
+            utemp = u[1];
+            u[1] = cosl[0] * utemp + sinl[0] * u[4];
+            u[4] = -sinl[0] * utemp + cosl[0] * u[4];
+            utemp = u[2];
+            u[2] = cosl[0] * utemp + sinl[0] * u[5];
+            u[5] = -sinl[0] * utemp + cosl[0] * u[5];
+
+            // update v matrices
+
+            vtemp = v[0];
+            v[0] = cosr[0] * vtemp + sinr[0] * v[1];
+            v[1] = -sinr[0] * vtemp + cosr[0] * v[1];
+            vtemp = v[3];
+            v[3] = cosr[0] * vtemp + sinr[0] * v[4];
+            v[4] = -sinr[0] * vtemp + cosr[0] * v[4];
+            vtemp = v[6];
+            v[6] = cosr[0] * vtemp + sinr[0] * v[7];
+            v[7] = -sinr[0] * vtemp + cosr[0] * v[7];
+        } else {
+            compute_2X2(s[1], e[1], s[2], s, sinl, cosl, sinr, cosr, 1);
+
+            utemp = u[3];
+            u[3] = cosl[0] * utemp + sinl[0] * u[6];
+            u[6] = -sinl[0] * utemp + cosl[0] * u[6];
+            utemp = u[4];
+            u[4] = cosl[0] * utemp + sinl[0] * u[7];
+            u[7] = -sinl[0] * utemp + cosl[0] * u[7];
+            utemp = u[5];
+            u[5] = cosl[0] * utemp + sinl[0] * u[8];
+            u[8] = -sinl[0] * utemp + cosl[0] * u[8];
+
+            // update v matrices
+
+            vtemp = v[1];
+            v[1] = cosr[0] * vtemp + sinr[0] * v[2];
+            v[2] = -sinr[0] * vtemp + cosr[0] * v[2];
+            vtemp = v[4];
+            v[4] = cosr[0] * vtemp + sinr[0] * v[5];
+            v[5] = -sinr[0] * vtemp + cosr[0] * v[5];
+            vtemp = v[7];
+            v[7] = cosr[0] * vtemp + sinr[0] * v[8];
+            v[8] = -sinr[0] * vtemp + cosr[0] * v[8];
+        }
+
+        return (0);
+    }
+
+    static double compute_rot(double f, double g, double[] sin, double[] cos,
+            int index, int first) {
+        double cs, sn;
+        int i;
+        double scale;
+        int count;
+        double f1, g1;
+        double r;
+        final double safmn2 = 2.002083095183101E-146;
+        final double safmx2 = 4.994797680505588E+145;
+
+        if (g == 0.) {
+            cs = 1.;
+            sn = 0.;
+            r = f;
+        } else if (f == 0.) {
+            cs = 0.;
+            sn = 1.;
+            r = g;
+        } else {
+            f1 = f;
+            g1 = g;
+            scale = MathUtils.max(MathUtils.abs(f1), MathUtils.abs(g1));
+            if (scale >= safmx2) {
+                count = 0;
+                while (scale >= safmx2) {
+                    ++count;
+                    f1 *= safmn2;
+                    g1 *= safmn2;
+                    scale = MathUtils.max(MathUtils.abs(f1), MathUtils.abs(g1));
+                }
+                r = Math.sqrt(f1 * f1 + g1 * g1);
+                cs = f1 / r;
+                sn = g1 / r;
+                for (i = 1; i <= count; ++i) {
+                    r *= safmx2;
+                }
+            } else if (scale <= safmn2) {
+                count = 0;
+                while (scale <= safmn2) {
+                    ++count;
+                    f1 *= safmx2;
+                    g1 *= safmx2;
+                    scale = MathUtils.max(MathUtils.abs(f1), MathUtils.abs(g1));
+                }
+                r = Math.sqrt(f1 * f1 + g1 * g1);
+                cs = f1 / r;
+                sn = g1 / r;
+                for (i = 1; i <= count; ++i) {
+                    r *= safmn2;
+                }
+            } else {
+                r = Math.sqrt(f1 * f1 + g1 * g1);
+                cs = f1 / r;
+                sn = g1 / r;
+            }
+            if (Math.abs(f) > Math.abs(g) && cs < 0.) {
+                cs = -cs;
+                sn = -sn;
+                r = -r;
+            }
+        }
+        sin[index] = sn;
+        cos[index] = cs;
+        return r;
+
+    }
+
+    static double compute_shift(double f, double g, double h) {
+        double d__1, d__2;
+        double fhmn, fhmx, c, fa, ga, ha, as, at, au;
+        double ssmin;
+
+        fa = Math.abs(f);
+        ga = Math.abs(g);
+        ha = Math.abs(h);
+        fhmn = MathUtils.min(fa, ha);
+        fhmx = MathUtils.max(fa, ha);
+        if (fhmn == 0.) {
+            ssmin = 0.;
+            if (fhmx == 0.) {
+            } else {
+                d__1 = MathUtils.min(fhmx, ga) / MathUtils.max(fhmx, ga);
+            }
+        } else {
+            if (ga < fhmx) {
+                as = fhmn / fhmx + 1.;
+                at = (fhmx - fhmn) / fhmx;
+                d__1 = ga / fhmx;
+                au = d__1 * d__1;
+                c = 2. / (Math.sqrt(as * as + au) + Math.sqrt(at * at + au));
+                ssmin = fhmn * c;
+            } else {
+                au = fhmx / ga;
+                if (au == 0.) {
+                    ssmin = fhmn * fhmx / ga;
+                } else {
+                    as = fhmn / fhmx + 1.;
+                    at = (fhmx - fhmn) / fhmx;
+                    d__1 = as * au;
+                    d__2 = at * au;
+                    c = 1. / (Math.sqrt(d__1 * d__1 + 1.) + Math.sqrt(d__2
+                            * d__2 + 1.));
+                    ssmin = fhmn * c * au;
+                    ssmin += ssmin;
+                }
+            }
+        }
+
+        return (ssmin);
+    }
+
+    static void compute_svd(double[] m, double[] outScale, double[] outRot) {
+        int i;
+        double g;
+        double[] u1 = new double[9];
+        double[] v1 = new double[9];
+        double[] t1 = new double[9];
+        double[] t2 = new double[9];
+
+        double[] tmp = t1;
+        double[] single_values = t2;
+
+        double[] rot = new double[9];
+        double[] e = new double[3];
+        double[] scales = new double[3];
+
+        int negCnt = 0;
+        double c1, c2, c3, c4;
+        double s1, s2, s3, s4;
+
+        for (i = 0; i < 9; i++) {
+            rot[i] = m[i];
+        }
+
+        // u1
+
+        if (m[3] * m[3] < EPS) {
+            u1[0] = 1.0;
+            u1[1] = 0.0;
+            u1[2] = 0.0;
+            u1[3] = 0.0;
+            u1[4] = 1.0;
+            u1[5] = 0.0;
+            u1[6] = 0.0;
+            u1[7] = 0.0;
+            u1[8] = 1.0;
+        } else if (m[0] * m[0] < EPS) {
+            tmp[0] = m[0];
+            tmp[1] = m[1];
+            tmp[2] = m[2];
+            m[0] = m[3];
+            m[1] = m[4];
+            m[2] = m[5];
+
+            m[3] = -tmp[0]; // zero
+            m[4] = -tmp[1];
+            m[5] = -tmp[2];
+
+            u1[0] = 0.0;
+            u1[1] = 1.0;
+            u1[2] = 0.0;
+            u1[3] = -1.0;
+            u1[4] = 0.0;
+            u1[5] = 0.0;
+            u1[6] = 0.0;
+            u1[7] = 0.0;
+            u1[8] = 1.0;
+        } else {
+            g = 1.0 / Math.sqrt(m[0] * m[0] + m[3] * m[3]);
+            c1 = m[0] * g;
+            s1 = m[3] * g;
+            tmp[0] = c1 * m[0] + s1 * m[3];
+            tmp[1] = c1 * m[1] + s1 * m[4];
+            tmp[2] = c1 * m[2] + s1 * m[5];
+
+            m[3] = -s1 * m[0] + c1 * m[3]; // zero
+            m[4] = -s1 * m[1] + c1 * m[4];
+            m[5] = -s1 * m[2] + c1 * m[5];
+
+            m[0] = tmp[0];
+            m[1] = tmp[1];
+            m[2] = tmp[2];
+            u1[0] = c1;
+            u1[1] = s1;
+            u1[2] = 0.0;
+            u1[3] = -s1;
+            u1[4] = c1;
+            u1[5] = 0.0;
+            u1[6] = 0.0;
+            u1[7] = 0.0;
+            u1[8] = 1.0;
+        }
+
+        // u2
+
+        if (m[6] * m[6] < EPS) {
+        } else if (m[0] * m[0] < EPS) {
+            tmp[0] = m[0];
+            tmp[1] = m[1];
+            tmp[2] = m[2];
+            m[0] = m[6];
+            m[1] = m[7];
+            m[2] = m[8];
+
+            m[6] = -tmp[0]; // zero
+            m[7] = -tmp[1];
+            m[8] = -tmp[2];
+
+            tmp[0] = u1[0];
+            tmp[1] = u1[1];
+            tmp[2] = u1[2];
+            u1[0] = u1[6];
+            u1[1] = u1[7];
+            u1[2] = u1[8];
+
+            u1[6] = -tmp[0]; // zero
+            u1[7] = -tmp[1];
+            u1[8] = -tmp[2];
+        } else {
+            g = 1.0 / Math.sqrt(m[0] * m[0] + m[6] * m[6]);
+            c2 = m[0] * g;
+            s2 = m[6] * g;
+            tmp[0] = c2 * m[0] + s2 * m[6];
+            tmp[1] = c2 * m[1] + s2 * m[7];
+            tmp[2] = c2 * m[2] + s2 * m[8];
+
+            m[6] = -s2 * m[0] + c2 * m[6];
+            m[7] = -s2 * m[1] + c2 * m[7];
+            m[8] = -s2 * m[2] + c2 * m[8];
+            m[0] = tmp[0];
+            m[1] = tmp[1];
+            m[2] = tmp[2];
+
+            tmp[0] = c2 * u1[0];
+            tmp[1] = c2 * u1[1];
+            u1[2] = s2;
+
+            tmp[6] = -u1[0] * s2;
+            tmp[7] = -u1[1] * s2;
+            u1[8] = c2;
+            u1[0] = tmp[0];
+            u1[1] = tmp[1];
+            u1[6] = tmp[6];
+            u1[7] = tmp[7];
+        }
+
+        // v1
+
+        if (m[2] * m[2] < EPS) {
+            v1[0] = 1.0;
+            v1[1] = 0.0;
+            v1[2] = 0.0;
+            v1[3] = 0.0;
+            v1[4] = 1.0;
+            v1[5] = 0.0;
+            v1[6] = 0.0;
+            v1[7] = 0.0;
+            v1[8] = 1.0;
+        } else if (m[1] * m[1] < EPS) {
+            tmp[2] = m[2];
+            tmp[5] = m[5];
+            tmp[8] = m[8];
+            m[2] = -m[1];
+            m[5] = -m[4];
+            m[8] = -m[7];
+
+            m[1] = tmp[2]; // zero
+            m[4] = tmp[5];
+            m[7] = tmp[8];
+
+            v1[0] = 1.0;
+            v1[1] = 0.0;
+            v1[2] = 0.0;
+            v1[3] = 0.0;
+            v1[4] = 0.0;
+            v1[5] = -1.0;
+            v1[6] = 0.0;
+            v1[7] = 1.0;
+            v1[8] = 0.0;
+        } else {
+            g = 1.0 / Math.sqrt(m[1] * m[1] + m[2] * m[2]);
+            c3 = m[1] * g;
+            s3 = m[2] * g;
+            tmp[1] = c3 * m[1] + s3 * m[2]; // can assign to m[1]?
+            m[2] = -s3 * m[1] + c3 * m[2]; // zero
+            m[1] = tmp[1];
+
+            tmp[4] = c3 * m[4] + s3 * m[5];
+            m[5] = -s3 * m[4] + c3 * m[5];
+            m[4] = tmp[4];
+
+            tmp[7] = c3 * m[7] + s3 * m[8];
+            m[8] = -s3 * m[7] + c3 * m[8];
+            m[7] = tmp[7];
+
+            v1[0] = 1.0;
+            v1[1] = 0.0;
+            v1[2] = 0.0;
+            v1[3] = 0.0;
+            v1[4] = c3;
+            v1[5] = -s3;
+            v1[6] = 0.0;
+            v1[7] = s3;
+            v1[8] = c3;
+        }
+
+        // u3
+
+        if (m[7] * m[7] < EPS) {
+        } else if (m[4] * m[4] < EPS) {
+            tmp[3] = m[3];
+            tmp[4] = m[4];
+            tmp[5] = m[5];
+            m[3] = m[6]; // zero
+            m[4] = m[7];
+            m[5] = m[8];
+
+            m[6] = -tmp[3]; // zero
+            m[7] = -tmp[4]; // zero
+            m[8] = -tmp[5];
+
+            tmp[3] = u1[3];
+            tmp[4] = u1[4];
+            tmp[5] = u1[5];
+            u1[3] = u1[6];
+            u1[4] = u1[7];
+            u1[5] = u1[8];
+
+            u1[6] = -tmp[3]; // zero
+            u1[7] = -tmp[4];
+            u1[8] = -tmp[5];
+
+        } else {
+            g = 1.0 / Math.sqrt(m[4] * m[4] + m[7] * m[7]);
+            c4 = m[4] * g;
+            s4 = m[7] * g;
+            tmp[3] = c4 * m[3] + s4 * m[6];
+            m[6] = -s4 * m[3] + c4 * m[6]; // zero
+            m[3] = tmp[3];
+
+            tmp[4] = c4 * m[4] + s4 * m[7];
+            m[7] = -s4 * m[4] + c4 * m[7];
+            m[4] = tmp[4];
+
+            tmp[5] = c4 * m[5] + s4 * m[8];
+            m[8] = -s4 * m[5] + c4 * m[8];
+            m[5] = tmp[5];
+
+            tmp[3] = c4 * u1[3] + s4 * u1[6];
+            u1[6] = -s4 * u1[3] + c4 * u1[6];
+            u1[3] = tmp[3];
+
+            tmp[4] = c4 * u1[4] + s4 * u1[7];
+            u1[7] = -s4 * u1[4] + c4 * u1[7];
+            u1[4] = tmp[4];
+
+            tmp[5] = c4 * u1[5] + s4 * u1[8];
+            u1[8] = -s4 * u1[5] + c4 * u1[8];
+            u1[5] = tmp[5];
+        }
+
+        single_values[0] = m[0];
+        single_values[1] = m[4];
+        single_values[2] = m[8];
+        e[0] = m[1];
+        e[1] = m[5];
+
+        if (e[0] * e[0] < EPS && e[1] * e[1] < EPS) {
+
+        } else {
+            compute_qr(single_values, e, u1, v1);
+        }
+
+        scales[0] = single_values[0];
+        scales[1] = single_values[1];
+        scales[2] = single_values[2];
+
+        // Do some optimization here. If scale is unity, simply return the
+        // rotation matric.
+        if (almostEqual(Math.abs(scales[0]), 1.0)
+                && almostEqual(Math.abs(scales[1]), 1.0)
+                && almostEqual(Math.abs(scales[2]), 1.0)) {
+            for (i = 0; i < 3; i++) {
+                if (scales[i] < 0.0) {
+                    negCnt++;
+                }
+            }
+
+            if ((negCnt == 0) || (negCnt == 2)) {
+                outScale[0] = outScale[1] = outScale[2] = 1.0;
+                for (i = 0; i < 9; i++) {
+                    outRot[i] = rot[i];
+                }
+
+                return;
+            }
+        }
+
+        transpose_mat(u1, t1);
+        transpose_mat(v1, t2);
+
+        svdReorder(m, t1, t2, scales, outRot, outScale);
+    }
+
+    /**
+     * Solves a set of linear equations. The input parameters "matrix1", and
+     * "row_perm" come from luDecompostionD3x3 and do not change here. The
+     * parameter "matrix2" is a set of column vectors assembled into a 3x3
+     * matrix of floating-point values. The procedure takes each column of
+     * "matrix2" in turn and treats it as the right-hand side of the matrix
+     * equation Ax = LUx = b. The solution vector replaces the original column
+     * of the matrix.
+     * 
+     * If "matrix2" is the identity matrix, the procedure replaces its contents
+     * with the inverse of the matrix from which "matrix1" was originally
+     * derived.
+     */
+    //
+    // Reference: Press, Flannery, Teukolsky, Vetterling,
+    // _Numerical_Recipes_in_C_, Cambridge University Press,
+    // 1988, pp 44-45.
+    //
+    static void luBacksubstitution(double[] matrix1, int[] row_perm,
+            double[] matrix2) {
+
+        int i, ii, ip, j, k;
+        int rp;
+        int cv, rv;
+
+        // rp = row_perm;
+        rp = 0;
+
+        // For each column vector of matrix2 ...
+        for (k = 0; k < 3; k++) {
+            // cv = &(matrix2[0][k]);
+            cv = k;
+            ii = -1;
+
+            // Forward substitution
+            for (i = 0; i < 3; i++) {
+                double sum;
+
+                ip = row_perm[rp + i];
+                sum = matrix2[cv + 3 * ip];
+                matrix2[cv + 3 * ip] = matrix2[cv + 3 * i];
+                if (ii >= 0) {
+                    // rv = &(matrix1[i][0]);
+                    rv = i * 3;
+                    for (j = ii; j <= i - 1; j++) {
+                        sum -= matrix1[rv + j] * matrix2[cv + 3 * j];
+                    }
+                } else if (sum != 0.0) {
+                    ii = i;
+                }
+                matrix2[cv + 3 * i] = sum;
+            }
+
+            // Backsubstitution
+            // rv = &(matrix1[3][0]);
+            rv = 2 * 3;
+            matrix2[cv + 3 * 2] /= matrix1[rv + 2];
+
+            rv -= 3;
+            matrix2[cv + 3 * 1] = (matrix2[cv + 3 * 1] - matrix1[rv + 2]
+                    * matrix2[cv + 3 * 2])
+                    / matrix1[rv + 1];
+
+            rv -= 3;
+            matrix2[cv + 4 * 0] = (matrix2[cv + 3 * 0] - matrix1[rv + 1]
+                    * matrix2[cv + 3 * 1] - matrix1[rv + 2]
+                    * matrix2[cv + 3 * 2])
+                    / matrix1[rv + 0];
+        }
+    }
+
+    static void mat_mul(double[] m1, double[] m2, double[] m3) {
+        int i;
+        double[] tmp = new double[9];
+
+        tmp[0] = m1[0] * m2[0] + m1[1] * m2[3] + m1[2] * m2[6];
+        tmp[1] = m1[0] * m2[1] + m1[1] * m2[4] + m1[2] * m2[7];
+        tmp[2] = m1[0] * m2[2] + m1[1] * m2[5] + m1[2] * m2[8];
+
+        tmp[3] = m1[3] * m2[0] + m1[4] * m2[3] + m1[5] * m2[6];
+        tmp[4] = m1[3] * m2[1] + m1[4] * m2[4] + m1[5] * m2[7];
+        tmp[5] = m1[3] * m2[2] + m1[4] * m2[5] + m1[5] * m2[8];
+
+        tmp[6] = m1[6] * m2[0] + m1[7] * m2[3] + m1[8] * m2[6];
+        tmp[7] = m1[6] * m2[1] + m1[7] * m2[4] + m1[8] * m2[7];
+        tmp[8] = m1[6] * m2[2] + m1[7] * m2[5] + m1[8] * m2[8];
+
+        for (i = 0; i < 9; i++) {
+            m3[i] = tmp[i];
+        }
+    }
+
+    static void print_det(double[] mat) {
+        double det;
+
+        det = mat[0] * mat[4] * mat[8] + mat[1] * mat[5] * mat[6] + mat[2]
+                * mat[3] * mat[7] - mat[2] * mat[4] * mat[6] - mat[0] * mat[5]
+                * mat[7] - mat[1] * mat[3] * mat[8];
+        System.out.println("det= " + det);
+    }
+
+    static void print_mat(double[] mat) {
+        int i;
+        for (i = 0; i < 3; i++) {
+            System.out.println(mat[i * 3 + 0] + " " + mat[i * 3 + 1] + " "
+                    + mat[i * 3 + 2] + "\n");
+        }
+
+    }
+
+    static void svdReorder(double[] m, double[] t1, double[] t2,
+            double[] scales, double[] outRot, double[] outScale) {
+
+        int[] out = new int[3];
+        int[] in = new int[3];
+        int in0, in1, in2, index, i;
+        double[] mag = new double[3];
+        double[] rot = new double[9];
+
+        // check for rotation information in the scales
+        if (scales[0] < 0.0) { // move the rotation info to rotation matrix
+            scales[0] = -scales[0];
+            t2[0] = -t2[0];
+            t2[1] = -t2[1];
+            t2[2] = -t2[2];
+        }
+        if (scales[1] < 0.0) { // move the rotation info to rotation matrix
+            scales[1] = -scales[1];
+            t2[3] = -t2[3];
+            t2[4] = -t2[4];
+            t2[5] = -t2[5];
+        }
+        if (scales[2] < 0.0) { // move the rotation info to rotation matrix
+            scales[2] = -scales[2];
+            t2[6] = -t2[6];
+            t2[7] = -t2[7];
+            t2[8] = -t2[8];
+        }
+
+        mat_mul(t1, t2, rot);
+
+        // check for equal scales case and do not reorder
+        if (almostEqual(Math.abs(scales[0]), Math.abs(scales[1]))
+                && almostEqual(Math.abs(scales[1]), Math.abs(scales[2]))) {
+            for (i = 0; i < 9; i++) {
+                outRot[i] = rot[i];
+            }
+            for (i = 0; i < 3; i++) {
+                outScale[i] = scales[i];
+            }
+
+        } else {
+
+            // sort the order of the results of SVD
+            if (scales[0] > scales[1]) {
+                if (scales[0] > scales[2]) {
+                    if (scales[2] > scales[1]) {
+                        out[0] = 0;
+                        out[1] = 2;
+                        out[2] = 1; // xzy
+                    } else {
+                        out[0] = 0;
+                        out[1] = 1;
+                        out[2] = 2; // xyz
+                    }
+                } else {
+                    out[0] = 2;
+                    out[1] = 0;
+                    out[2] = 1; // zxy
+                }
+            } else { // y > x
+                if (scales[1] > scales[2]) {
+                    if (scales[2] > scales[0]) {
+                        out[0] = 1;
+                        out[1] = 2;
+                        out[2] = 0; // yzx
+                    } else {
+                        out[0] = 1;
+                        out[1] = 0;
+                        out[2] = 2; // yxz
+                    }
+                } else {
+                    out[0] = 2;
+                    out[1] = 1;
+                    out[2] = 0; // zyx
+                }
+            }
+
+            /*
+             * System.out.println("\nscales="+scales[0]+" "+scales[1]+" "+scales[
+             * 2]); System.out.println("\nrot="+rot[0]+" "+rot[1]+" "+rot[2]);
+             * System.out.println("rot="+rot[3]+" "+rot[4]+" "+rot[5]);
+             * System.out.println("rot="+rot[6]+" "+rot[7]+" "+rot[8]);
+             */
+
+            // sort the order of the input matrix
+            mag[0] = (m[0] * m[0] + m[1] * m[1] + m[2] * m[2]);
+            mag[1] = (m[3] * m[3] + m[4] * m[4] + m[5] * m[5]);
+            mag[2] = (m[6] * m[6] + m[7] * m[7] + m[8] * m[8]);
+
+            if (mag[0] > mag[1]) {
+                if (mag[0] > mag[2]) {
+                    if (mag[2] > mag[1]) {
+                        // 0 - 2 - 1
+                        in0 = 0;
+                        in2 = 1;
+                        in1 = 2;// xzy
+                    } else {
+                        // 0 - 1 - 2
+                        in0 = 0;
+                        in1 = 1;
+                        in2 = 2; // xyz
+                    }
+                } else {
+                    // 2 - 0 - 1
+                    in2 = 0;
+                    in0 = 1;
+                    in1 = 2; // zxy
+                }
+            } else { // y > x 1>0
+                if (mag[1] > mag[2]) {
+                    if (mag[2] > mag[0]) {
+                        // 1 - 2 - 0
+                        in1 = 0;
+                        in2 = 1;
+                        in0 = 2; // yzx
+                    } else {
+                        // 1 - 0 - 2
+                        in1 = 0;
+                        in0 = 1;
+                        in2 = 2; // yxz
+                    }
+                } else {
+                    // 2 - 1 - 0
+                    in2 = 0;
+                    in1 = 1;
+                    in0 = 2; // zyx
+                }
+            }
+
+            index = out[in0];
+            outScale[0] = scales[index];
+
+            index = out[in1];
+            outScale[1] = scales[index];
+
+            index = out[in2];
+            outScale[2] = scales[index];
+
+            index = out[in0];
+            outRot[0] = rot[index];
+
+            index = out[in0] + 3;
+            outRot[0 + 3] = rot[index];
+
+            index = out[in0] + 6;
+            outRot[0 + 6] = rot[index];
+
+            index = out[in1];
+            outRot[1] = rot[index];
+
+            index = out[in1] + 3;
+            outRot[1 + 3] = rot[index];
+
+            index = out[in1] + 6;
+            outRot[1 + 6] = rot[index];
+
+            index = out[in2];
+            outRot[2] = rot[index];
+
+            index = out[in2] + 3;
+            outRot[2 + 3] = rot[index];
+
+            index = out[in2] + 6;
+            outRot[2 + 6] = rot[index];
+        }
+    }
+
+    static void transpose_mat(double[] in, double[] out) {
+        out[0] = in[0];
+        out[1] = in[3];
+        out[2] = in[6];
+
+        out[3] = in[1];
+        out[4] = in[4];
+        out[5] = in[7];
+
+        out[6] = in[2];
+        out[7] = in[5];
+        out[8] = in[8];
+    }
+
+    /**
+     * The first matrix element in the first row.
+     */
+    public double m00;
+
+    /**
+     * The second matrix element in the first row.
+     */
+    public double m01;
+
+    /**
+     * The third matrix element in the first row.
+     */
+    public double m02;
+
+    /**
+     * The first matrix element in the second row.
+     */
+    public double m10;
+
+    /**
+     * The second matrix element in the second row.
+     */
+    public double m11;
+
+    /**
+     * The third matrix element in the second row.
+     */
+    public double m12;
+
+    /**
+     * The first matrix element in the third row.
+     */
+    public double m20;
+
+    /**
+     * The second matrix element in the third row.
+     */
+    public double m21;
+
+    /**
+     * The third matrix element in the third row.
+     */
+    public double m22;
+
+    // double[] tmp = new double[9]; // scratch matrix
+    // double[] tmp_rot = new double[9]; // scratch matrix
+    // double[] tmp_scale = new double[3]; // scratch matrix
+    private static final double EPS = 1.110223024E-16;
+
+    private static final double ERR_EPS = 1.0E-8;
+
+    private static double xin, yin, zin, xout, yout, zout;
+
+    /**
+     * Constructs and initializes a Matrix3d to all zeros.
+     */
+    public Matrix3d() {
+        this.m00 = 0.0;
+        this.m01 = 0.0;
+        this.m02 = 0.0;
+
+        this.m10 = 0.0;
+        this.m11 = 0.0;
+        this.m12 = 0.0;
+
+        this.m20 = 0.0;
+        this.m21 = 0.0;
+        this.m22 = 0.0;
+
+    }
+
+    /**
+     * Constructs and initializes a Matrix3d from the specified nine values.
+     * 
+     * @param m00
+     *            the [0][0] element
+     * @param m01
+     *            the [0][1] element
+     * @param m02
+     *            the [0][2] element
+     * @param m10
+     *            the [1][0] element
+     * @param m11
+     *            the [1][1] element
+     * @param m12
+     *            the [1][2] element
+     * @param m20
+     *            the [2][0] element
+     * @param m21
+     *            the [2][1] element
+     * @param m22
+     *            the [2][2] element
+     */
+    public Matrix3d(double m00, double m01, double m02, double m10, double m11,
+            double m12, double m20, double m21, double m22) {
+        this.m00 = m00;
+        this.m01 = m01;
+        this.m02 = m02;
+
+        this.m10 = m10;
+        this.m11 = m11;
+        this.m12 = m12;
+
+        this.m20 = m20;
+        this.m21 = m21;
+        this.m22 = m22;
+
+    }
+
+    /**
+     * Constructs and initializes a Matrix3d from the specified nine- element
+     * array.
+     * 
+     * @param v
+     *            the array of length 9 containing in order
+     */
+    public Matrix3d(double[] v) {
+        this.m00 = v[0];
+        this.m01 = v[1];
+        this.m02 = v[2];
+
+        this.m10 = v[3];
+        this.m11 = v[4];
+        this.m12 = v[5];
+
+        this.m20 = v[6];
+        this.m21 = v[7];
+        this.m22 = v[8];
+
+    }
+
+    /**
+     * Constructs a new matrix with the same values as the Matrix3d parameter.
+     * 
+     * @param m1
+     *            the source matrix
+     */
+    public Matrix3d(Matrix3d m1) {
+        this.m00 = m1.m00;
+        this.m01 = m1.m01;
+        this.m02 = m1.m02;
+
+        this.m10 = m1.m10;
+        this.m11 = m1.m11;
+        this.m12 = m1.m12;
+
+        this.m20 = m1.m20;
+        this.m21 = m1.m21;
+        this.m22 = m1.m22;
+
+    }
+
+    /**
+     * Adds a scalar to each component of this matrix.
+     * 
+     * @param scalar
+     *            the scalar adder
+     */
+    public final void add(double scalar) {
+        m00 += scalar;
+        m01 += scalar;
+        m02 += scalar;
+
+        m10 += scalar;
+        m11 += scalar;
+        m12 += scalar;
+
+        m20 += scalar;
+        m21 += scalar;
+        m22 += scalar;
+
+    }
+
+    /**
+     * Adds a scalar to each component of the matrix m1 and places the result
+     * into this. Matrix m1 is not modified.
+     * 
+     * @param scalar
+     *            the scalar adder
+     * @param m1
+     *            the original matrix values
+     */
+    public final void add(double scalar, Matrix3d m1) {
+        this.m00 = m1.m00 + scalar;
+        this.m01 = m1.m01 + scalar;
+        this.m02 = m1.m02 + scalar;
+
+        this.m10 = m1.m10 + scalar;
+        this.m11 = m1.m11 + scalar;
+        this.m12 = m1.m12 + scalar;
+
+        this.m20 = m1.m20 + scalar;
+        this.m21 = m1.m21 + scalar;
+        this.m22 = m1.m22 + scalar;
+    }
+
+    /**
+     * Sets the value of this matrix to the sum of itself and matrix m1.
+     * 
+     * @param m1
+     *            the other matrix
+     */
+    public final void add(Matrix3d m1) {
+        this.m00 += m1.m00;
+        this.m01 += m1.m01;
+        this.m02 += m1.m02;
+
+        this.m10 += m1.m10;
+        this.m11 += m1.m11;
+        this.m12 += m1.m12;
+
+        this.m20 += m1.m20;
+        this.m21 += m1.m21;
+        this.m22 += m1.m22;
+    }
+
+    /**
+     * Sets the value of this matrix to the matrix sum of matrices m1 and m2.
+     * 
+     * @param m1
+     *            the first matrix
+     * @param m2
+     *            the second matrix
+     */
+    public final void add(Matrix3d m1, Matrix3d m2) {
+        this.m00 = m1.m00 + m2.m00;
+        this.m01 = m1.m01 + m2.m01;
+        this.m02 = m1.m02 + m2.m02;
+
+        this.m10 = m1.m10 + m2.m10;
+        this.m11 = m1.m11 + m2.m11;
+        this.m12 = m1.m12 + m2.m12;
+
+        this.m20 = m1.m20 + m2.m20;
+        this.m21 = m1.m21 + m2.m21;
+        this.m22 = m1.m22 + m2.m22;
+    }
+
+    /**
+     * Creates a new object of the same class as this object.
+     * 
+     * @return a clone of this instance.
+     * @exception OutOfMemoryError
+     *                if there is not enough memory.
+     * @see Cloneable
+     * @since vecmath 1.3
+     */
+    public Object clone() {
+        Matrix3d m1 = null;
+        try {
+            m1 = (Matrix3d) super.clone();
+        } catch (CloneNotSupportedException e) {
+            // this shouldn't happen, since we are Cloneable
+            throw new InternalError();
+        }
+
+        // Also need to create new tmp arrays (no need to actually clone them)
+        return m1;
+    }
+
+    /**
+     * Computes the determinant of this matrix.
+     * 
+     * @return the determinant of the matrix
+     */
+    public final double determinant() {
+        double total;
+
+        total = this.m00 * (this.m11 * this.m22 - this.m12 * this.m21)
+                + this.m01 * (this.m12 * this.m20 - this.m10 * this.m22)
+                + this.m02 * (this.m10 * this.m21 - this.m11 * this.m20);
+        return total;
+    }
+
+    /**
+     * Returns true if the L-infinite distance between this matrix and matrix m1
+     * is less than or equal to the epsilon parameter, otherwise returns false.
+     * The L-infinite distance is equal to MAX[i=0,1,2 ; j=0,1,2 ;
+     * abs(this.m(i,j) - m1.m(i,j)]
+     * 
+     * @param m1
+     *            the matrix to be compared to this matrix
+     * @param epsilon
+     *            the threshold value
+     */
+    public boolean epsilonEquals(Matrix3d m1, double epsilon) {
+        double diff;
+
+        diff = m00 - m1.m00;
+        if ((diff < 0 ? -diff : diff) > epsilon) {
+            return false;
+        }
+
+        diff = m01 - m1.m01;
+        if ((diff < 0 ? -diff : diff) > epsilon) {
+            return false;
+        }
+
+        diff = m02 - m1.m02;
+        if ((diff < 0 ? -diff : diff) > epsilon) {
+            return false;
+        }
+
+        diff = m10 - m1.m10;
+        if ((diff < 0 ? -diff : diff) > epsilon) {
+            return false;
+        }
+
+        diff = m11 - m1.m11;
+        if ((diff < 0 ? -diff : diff) > epsilon) {
+            return false;
+        }
+
+        diff = m12 - m1.m12;
+        if ((diff < 0 ? -diff : diff) > epsilon) {
+            return false;
+        }
+
+        diff = m20 - m1.m20;
+        if ((diff < 0 ? -diff : diff) > epsilon) {
+            return false;
+        }
+
+        diff = m21 - m1.m21;
+        if ((diff < 0 ? -diff : diff) > epsilon) {
+            return false;
+        }
+
+        diff = m22 - m1.m22;
+        if ((diff < 0 ? -diff : diff) > epsilon) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns true if all of the data members of Matrix3d m1 are equal to the
+     * corresponding data members in this Matrix3d.
+     * 
+     * @param m1
+     *            the matrix with which the comparison is made
+     * @return true or false
+     */
+    public boolean equals(Matrix3d m1) {
+        try {
+            return (this.m00 == m1.m00 && this.m01 == m1.m01
+                    && this.m02 == m1.m02 && this.m10 == m1.m10
+                    && this.m11 == m1.m11 && this.m12 == m1.m12
+                    && this.m20 == m1.m20 && this.m21 == m1.m21 && this.m22 == m1.m22);
+        } catch (NullPointerException e2) {
+            return false;
+        }
+
+    }
+
+    /**
+     * Returns true if the Object t1 is of type Matrix3d and all of the data
+     * members of t1 are equal to the corresponding data members in this
+     * Matrix3d.
+     * 
+     * @param t1
+     *            the matrix with which the comparison is made
+     * @return true or false
+     */
+    public boolean equals(Object t1) {
+        try {
+            Matrix3d m2 = (Matrix3d) t1;
+            return (this.m00 == m2.m00 && this.m01 == m2.m01
+                    && this.m02 == m2.m02 && this.m10 == m2.m10
+                    && this.m11 == m2.m11 && this.m12 == m2.m12
+                    && this.m20 == m2.m20 && this.m21 == m2.m21 && this.m22 == m2.m22);
+        } catch (ClassCastException e1) {
+            return false;
+        } catch (NullPointerException e2) {
+            return false;
+        }
+
+    }
+
+    /**
+     * Retrieves the value at the specified row and column of the specified
+     * matrix.
+     * 
+     * @param row
+     *            the row number to be retrieved (zero indexed)
+     * @param column
+     *            the column number to be retrieved (zero indexed)
+     * @return the value at the indexed element.
+     */
+    public final double get(int row, int column) {
+        switch (row) {
+            case 0:
+                switch (column) {
+                    case 0:
+                        return (this.m00);
+                    case 1:
+                        return (this.m01);
+                    case 2:
+                        return (this.m02);
+                    default:
+                        break;
+                }
+                break;
+            case 1:
+                switch (column) {
+                    case 0:
+                        return (this.m10);
+                    case 1:
+                        return (this.m11);
+                    case 2:
+                        return (this.m12);
+                    default:
+                        break;
+                }
+                break;
+
+            case 2:
+                switch (column) {
+                    case 0:
+                        return (this.m20);
+                    case 1:
+                        return (this.m21);
+                    case 2:
+                        return (this.m22);
+                    default:
+                        break;
+                }
+                break;
+
+            default:
+                break;
+        }
+        throw new ArrayIndexOutOfBoundsException();
+    }
+
+    /**
+     * Copies the matrix values in the specified column into the array
+     * parameter.
+     * 
+     * @param column
+     *            the matrix column
+     * @param v
+     *            the array into which the matrix row values will be copied
+     */
+    public final void getColumn(int column, double v[]) {
+        if (column == 0) {
+            v[0] = m00;
+            v[1] = m10;
+            v[2] = m20;
+        } else if (column == 1) {
+            v[0] = m01;
+            v[1] = m11;
+            v[2] = m21;
+        } else if (column == 2) {
+            v[0] = m02;
+            v[1] = m12;
+            v[2] = m22;
+        } else {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+
+    }
+
+    /**
+     * Copies the matrix values in the specified column into the vector
+     * parameter.
+     * 
+     * @param column
+     *            the matrix column
+     * @param v
+     *            the vector into which the matrix row values will be copied
+     */
+    public final void getColumn(int column, Vec3D v) {
+        if (column == 0) {
+            v.x = (float) m00;
+            v.y = (float) m10;
+            v.z = (float) m20;
+        } else if (column == 1) {
+            v.x = (float) m01;
+            v.y = (float) m11;
+            v.z = (float) m21;
+        } else if (column == 2) {
+            v.x = (float) m02;
+            v.y = (float) m12;
+            v.z = (float) m22;
+        } else {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+
+    }
+
+    /**
+     * Copies the matrix values in the specified row into the array parameter.
+     * 
+     * @param row
+     *            the matrix row
+     * @param v
+     *            the array into which the matrix row values will be copied
+     */
+    public final void getRow(int row, double v[]) {
+        if (row == 0) {
+            v[0] = m00;
+            v[1] = m01;
+            v[2] = m02;
+        } else if (row == 1) {
+            v[0] = m10;
+            v[1] = m11;
+            v[2] = m12;
+        } else if (row == 2) {
+            v[0] = m20;
+            v[1] = m21;
+            v[2] = m22;
+        } else {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Copies the matrix values in the specified row into the vector parameter.
+     * 
+     * @param row
+     *            the matrix row
+     * @param v
+     *            the vector into which the matrix row values will be copied
+     */
+    public final void getRow(int row, Vec3D v) {
+        if (row == 0) {
+            v.x = (float) m00;
+            v.y = (float) m01;
+            v.z = (float) m02;
+        } else if (row == 1) {
+            v.x = (float) m10;
+            v.y = (float) m11;
+            v.z = (float) m12;
+        } else if (row == 2) {
+            v.x = (float) m20;
+            v.y = (float) m21;
+            v.z = (float) m22;
+        } else {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+
+    }
+
+    /**
+     * Performs an SVD normalization of this matrix to calculate and return the
+     * uniform scale factor. If the matrix has non-uniform scale factors, the
+     * largest of the x, y, and z scale factors will be returned. This matrix is
+     * not modified.
+     * 
+     * @return the scale factor of this matrix
+     */
+    public final double getScale() {
+        double[] tmp_scale = new double[3];
+        double[] tmp_rot = new double[9];
+        getScaleRotate(tmp_scale, tmp_rot);
+        return (MathUtils.max(tmp_scale));
+    }
+
+    /**
+     * perform SVD (if necessary to get rotational component
+     */
+    final void getScaleRotate(double scales[], double rots[]) {
+        double[] tmp = new double[9];
+        tmp[0] = m00;
+        tmp[1] = m01;
+        tmp[2] = m02;
+
+        tmp[3] = m10;
+        tmp[4] = m11;
+        tmp[5] = m12;
+
+        tmp[6] = m20;
+        tmp[7] = m21;
+        tmp[8] = m22;
+        compute_svd(tmp, scales, rots);
+    }
+
+    /**
+     * Returns a hash code value based on the data values in this object. Two
+     * different Matrix3d objects with identical data values (i.e.,
+     * Matrix3d.equals returns true) will return the same hash code value. Two
+     * objects with different data members may return the same hash value,
+     * although this is not likely.
+     * 
+     * @return the integer hash code value
+     */
+    public int hashCode() {
+        long bits = 1L;
+        bits = 31L * bits + VecMathUtil.doubleToLongBits(m00);
+        bits = 31L * bits + VecMathUtil.doubleToLongBits(m01);
+        bits = 31L * bits + VecMathUtil.doubleToLongBits(m02);
+        bits = 31L * bits + VecMathUtil.doubleToLongBits(m10);
+        bits = 31L * bits + VecMathUtil.doubleToLongBits(m11);
+        bits = 31L * bits + VecMathUtil.doubleToLongBits(m12);
+        bits = 31L * bits + VecMathUtil.doubleToLongBits(m20);
+        bits = 31L * bits + VecMathUtil.doubleToLongBits(m21);
+        bits = 31L * bits + VecMathUtil.doubleToLongBits(m22);
+        return (int) (bits ^ (bits >> 32));
+    }
+
+    /**
+     * Inverts this matrix in place.
+     */
+    public final void invert() {
+        invertGeneral(this);
+    }
+
+    /**
+     * Sets the value of this matrix to the matrix inverse of the passed matrix
+     * m1.
+     * 
+     * @param m1
+     *            the matrix to be inverted
+     */
+    public final void invert(Matrix3d m1) {
+        invertGeneral(m1);
+    }
+
+    /**
+     * General invert routine. Inverts m1 and places the result in "this". Note
+     * that this routine handles both the "this" version and the non-"this"
+     * version.
+     * 
+     * Also note that since this routine is slow anyway, we won't worry about
+     * allocating a little bit of garbage.
+     */
+    private final void invertGeneral(Matrix3d m1) {
+        double result[] = new double[9];
+        int row_perm[] = new int[3];
+        int i, r, c;
+        double[] tmp = new double[9]; // scratch matrix
+
+        // Use LU decomposition and backsubstitution code specifically
+        // for floating-point 3x3 matrices.
+
+        // Copy source matrix to t1tmp
+        tmp[0] = m1.m00;
+        tmp[1] = m1.m01;
+        tmp[2] = m1.m02;
+
+        tmp[3] = m1.m10;
+        tmp[4] = m1.m11;
+        tmp[5] = m1.m12;
+
+        tmp[6] = m1.m20;
+        tmp[7] = m1.m21;
+        tmp[8] = m1.m22;
+
+        // Calculate LU decomposition: Is the matrix singular?
+        if (!Matrix4x4.LUDecomposition(tmp, row_perm, 3)) {
+            // Matrix has no inverse
+            throw new SingularMatrixException();
+        }
+
+        // Perform back substitution on the identity matrix
+        for (i = 0; i < 9; i++) {
+            result[i] = 0.0;
+        }
+        result[0] = 1.0;
+        result[4] = 1.0;
+        result[8] = 1.0;
+        luBacksubstitution(tmp, row_perm, result);
+
+        this.m00 = result[0];
+        this.m01 = result[1];
+        this.m02 = result[2];
+
+        this.m10 = result[3];
+        this.m11 = result[4];
+        this.m12 = result[5];
+
+        this.m20 = result[6];
+        this.m21 = result[7];
+        this.m22 = result[8];
+
+    }
+
+    /**
+     * Multiplies each element of this matrix by a scalar.
+     * 
+     * @param scalar
+     *            The scalar multiplier.
+     */
+    public final void mul(double scalar) {
+        m00 *= scalar;
+        m01 *= scalar;
+        m02 *= scalar;
+
+        m10 *= scalar;
+        m11 *= scalar;
+        m12 *= scalar;
+
+        m20 *= scalar;
+        m21 *= scalar;
+        m22 *= scalar;
+
+    }
+
+    /**
+     * Multiplies each element of matrix m1 by a scalar and places the result
+     * into this. Matrix m1 is not modified.
+     * 
+     * @param scalar
+     *            the scalar multiplier
+     * @param m1
+     *            the original matrix
+     */
+    public final void mul(double scalar, Matrix3d m1) {
+        this.m00 = scalar * m1.m00;
+        this.m01 = scalar * m1.m01;
+        this.m02 = scalar * m1.m02;
+
+        this.m10 = scalar * m1.m10;
+        this.m11 = scalar * m1.m11;
+        this.m12 = scalar * m1.m12;
+
+        this.m20 = scalar * m1.m20;
+        this.m21 = scalar * m1.m21;
+        this.m22 = scalar * m1.m22;
+
+    }
+
+    /**
+     * Sets the value of this matrix to the result of multiplying itself with
+     * matrix m1.
+     * 
+     * @param m1
+     *            the other matrix
+     */
+    public final void mul(Matrix3d m1) {
+        double m00, m01, m02, m10, m11, m12, m20, m21, m22;
+
+        m00 = this.m00 * m1.m00 + this.m01 * m1.m10 + this.m02 * m1.m20;
+        m01 = this.m00 * m1.m01 + this.m01 * m1.m11 + this.m02 * m1.m21;
+        m02 = this.m00 * m1.m02 + this.m01 * m1.m12 + this.m02 * m1.m22;
+
+        m10 = this.m10 * m1.m00 + this.m11 * m1.m10 + this.m12 * m1.m20;
+        m11 = this.m10 * m1.m01 + this.m11 * m1.m11 + this.m12 * m1.m21;
+        m12 = this.m10 * m1.m02 + this.m11 * m1.m12 + this.m12 * m1.m22;
+
+        m20 = this.m20 * m1.m00 + this.m21 * m1.m10 + this.m22 * m1.m20;
+        m21 = this.m20 * m1.m01 + this.m21 * m1.m11 + this.m22 * m1.m21;
+        m22 = this.m20 * m1.m02 + this.m21 * m1.m12 + this.m22 * m1.m22;
+
+        this.m00 = m00;
+        this.m01 = m01;
+        this.m02 = m02;
+        this.m10 = m10;
+        this.m11 = m11;
+        this.m12 = m12;
+        this.m20 = m20;
+        this.m21 = m21;
+        this.m22 = m22;
+    }
+
+    /**
+     * Sets the value of this matrix to the result of multiplying the two
+     * argument matrices together.
+     * 
+     * @param m1
+     *            the first matrix
+     * @param m2
+     *            the second matrix
+     */
+    public final void mul(Matrix3d m1, Matrix3d m2) {
+        if (this != m1 && this != m2) {
+            this.m00 = m1.m00 * m2.m00 + m1.m01 * m2.m10 + m1.m02 * m2.m20;
+            this.m01 = m1.m00 * m2.m01 + m1.m01 * m2.m11 + m1.m02 * m2.m21;
+            this.m02 = m1.m00 * m2.m02 + m1.m01 * m2.m12 + m1.m02 * m2.m22;
+
+            this.m10 = m1.m10 * m2.m00 + m1.m11 * m2.m10 + m1.m12 * m2.m20;
+            this.m11 = m1.m10 * m2.m01 + m1.m11 * m2.m11 + m1.m12 * m2.m21;
+            this.m12 = m1.m10 * m2.m02 + m1.m11 * m2.m12 + m1.m12 * m2.m22;
+
+            this.m20 = m1.m20 * m2.m00 + m1.m21 * m2.m10 + m1.m22 * m2.m20;
+            this.m21 = m1.m20 * m2.m01 + m1.m21 * m2.m11 + m1.m22 * m2.m21;
+            this.m22 = m1.m20 * m2.m02 + m1.m21 * m2.m12 + m1.m22 * m2.m22;
+        } else {
+            double m00, m01, m02, m10, m11, m12, m20, m21, m22; // vars for temp
+                                                                // result matrix
+
+            m00 = m1.m00 * m2.m00 + m1.m01 * m2.m10 + m1.m02 * m2.m20;
+            m01 = m1.m00 * m2.m01 + m1.m01 * m2.m11 + m1.m02 * m2.m21;
+            m02 = m1.m00 * m2.m02 + m1.m01 * m2.m12 + m1.m02 * m2.m22;
+
+            m10 = m1.m10 * m2.m00 + m1.m11 * m2.m10 + m1.m12 * m2.m20;
+            m11 = m1.m10 * m2.m01 + m1.m11 * m2.m11 + m1.m12 * m2.m21;
+            m12 = m1.m10 * m2.m02 + m1.m11 * m2.m12 + m1.m12 * m2.m22;
+
+            m20 = m1.m20 * m2.m00 + m1.m21 * m2.m10 + m1.m22 * m2.m20;
+            m21 = m1.m20 * m2.m01 + m1.m21 * m2.m11 + m1.m22 * m2.m21;
+            m22 = m1.m20 * m2.m02 + m1.m21 * m2.m12 + m1.m22 * m2.m22;
+
+            this.m00 = m00;
+            this.m01 = m01;
+            this.m02 = m02;
+            this.m10 = m10;
+            this.m11 = m11;
+            this.m12 = m12;
+            this.m20 = m20;
+            this.m21 = m21;
+            this.m22 = m22;
+        }
+    }
+
+    /**
+     * Multiplies this matrix by matrix m1, does an SVD normalization of the
+     * result, and places the result back into this matrix this =
+     * SVDnorm(this*m1).
+     * 
+     * @param m1
+     *            the matrix on the right hand side of the multiplication
+     */
+    public final void mulNormalize(Matrix3d m1) {
+
+        double[] tmp = new double[9]; // scratch matrix
+        double[] tmp_rot = new double[9]; // scratch matrix
+        double[] tmp_scale = new double[3]; // scratch matrix
+
+        tmp[0] = this.m00 * m1.m00 + this.m01 * m1.m10 + this.m02 * m1.m20;
+        tmp[1] = this.m00 * m1.m01 + this.m01 * m1.m11 + this.m02 * m1.m21;
+        tmp[2] = this.m00 * m1.m02 + this.m01 * m1.m12 + this.m02 * m1.m22;
+
+        tmp[3] = this.m10 * m1.m00 + this.m11 * m1.m10 + this.m12 * m1.m20;
+        tmp[4] = this.m10 * m1.m01 + this.m11 * m1.m11 + this.m12 * m1.m21;
+        tmp[5] = this.m10 * m1.m02 + this.m11 * m1.m12 + this.m12 * m1.m22;
+
+        tmp[6] = this.m20 * m1.m00 + this.m21 * m1.m10 + this.m22 * m1.m20;
+        tmp[7] = this.m20 * m1.m01 + this.m21 * m1.m11 + this.m22 * m1.m21;
+        tmp[8] = this.m20 * m1.m02 + this.m21 * m1.m12 + this.m22 * m1.m22;
+
+        compute_svd(tmp, tmp_scale, tmp_rot);
+
+        this.m00 = tmp_rot[0];
+        this.m01 = tmp_rot[1];
+        this.m02 = tmp_rot[2];
+
+        this.m10 = tmp_rot[3];
+        this.m11 = tmp_rot[4];
+        this.m12 = tmp_rot[5];
+
+        this.m20 = tmp_rot[6];
+        this.m21 = tmp_rot[7];
+        this.m22 = tmp_rot[8];
+
+    }
+
+    /**
+     * Multiplies matrix m1 by matrix m2, does an SVD normalization of the
+     * result, and places the result into this matrix this = SVDnorm(m1*m2).
+     * 
+     * @param m1
+     *            the matrix on the left hand side of the multiplication
+     * @param m2
+     *            the matrix on the right hand side of the multiplication
+     */
+    public final void mulNormalize(Matrix3d m1, Matrix3d m2) {
+
+        double[] tmp = new double[9]; // scratch matrix
+        double[] tmp_rot = new double[9]; // scratch matrix
+        double[] tmp_scale = new double[3]; // scratch matrix
+
+        tmp[0] = m1.m00 * m2.m00 + m1.m01 * m2.m10 + m1.m02 * m2.m20;
+        tmp[1] = m1.m00 * m2.m01 + m1.m01 * m2.m11 + m1.m02 * m2.m21;
+        tmp[2] = m1.m00 * m2.m02 + m1.m01 * m2.m12 + m1.m02 * m2.m22;
+
+        tmp[3] = m1.m10 * m2.m00 + m1.m11 * m2.m10 + m1.m12 * m2.m20;
+        tmp[4] = m1.m10 * m2.m01 + m1.m11 * m2.m11 + m1.m12 * m2.m21;
+        tmp[5] = m1.m10 * m2.m02 + m1.m11 * m2.m12 + m1.m12 * m2.m22;
+
+        tmp[6] = m1.m20 * m2.m00 + m1.m21 * m2.m10 + m1.m22 * m2.m20;
+        tmp[7] = m1.m20 * m2.m01 + m1.m21 * m2.m11 + m1.m22 * m2.m21;
+        tmp[8] = m1.m20 * m2.m02 + m1.m21 * m2.m12 + m1.m22 * m2.m22;
+
+        compute_svd(tmp, tmp_scale, tmp_rot);
+
+        this.m00 = tmp_rot[0];
+        this.m01 = tmp_rot[1];
+        this.m02 = tmp_rot[2];
+
+        this.m10 = tmp_rot[3];
+        this.m11 = tmp_rot[4];
+        this.m12 = tmp_rot[5];
+
+        this.m20 = tmp_rot[6];
+        this.m21 = tmp_rot[7];
+        this.m22 = tmp_rot[8];
+
+    }
+
+    /**
+     * Multiplies the transpose of matrix m1 times the transpose of matrix m2,
+     * and places the result into this.
+     * 
+     * @param m1
+     *            the matrix on the left hand side of the multiplication
+     * @param m2
+     *            the matrix on the right hand side of the multiplication
+     */
+    public final void mulTransposeBoth(Matrix3d m1, Matrix3d m2) {
+        if (this != m1 && this != m2) {
+            this.m00 = m1.m00 * m2.m00 + m1.m10 * m2.m01 + m1.m20 * m2.m02;
+            this.m01 = m1.m00 * m2.m10 + m1.m10 * m2.m11 + m1.m20 * m2.m12;
+            this.m02 = m1.m00 * m2.m20 + m1.m10 * m2.m21 + m1.m20 * m2.m22;
+
+            this.m10 = m1.m01 * m2.m00 + m1.m11 * m2.m01 + m1.m21 * m2.m02;
+            this.m11 = m1.m01 * m2.m10 + m1.m11 * m2.m11 + m1.m21 * m2.m12;
+            this.m12 = m1.m01 * m2.m20 + m1.m11 * m2.m21 + m1.m21 * m2.m22;
+
+            this.m20 = m1.m02 * m2.m00 + m1.m12 * m2.m01 + m1.m22 * m2.m02;
+            this.m21 = m1.m02 * m2.m10 + m1.m12 * m2.m11 + m1.m22 * m2.m12;
+            this.m22 = m1.m02 * m2.m20 + m1.m12 * m2.m21 + m1.m22 * m2.m22;
+        } else {
+            double m00, m01, m02, m10, m11, m12, m20, m21, m22; // vars for temp
+                                                                // result matrix
+
+            m00 = m1.m00 * m2.m00 + m1.m10 * m2.m01 + m1.m20 * m2.m02;
+            m01 = m1.m00 * m2.m10 + m1.m10 * m2.m11 + m1.m20 * m2.m12;
+            m02 = m1.m00 * m2.m20 + m1.m10 * m2.m21 + m1.m20 * m2.m22;
+
+            m10 = m1.m01 * m2.m00 + m1.m11 * m2.m01 + m1.m21 * m2.m02;
+            m11 = m1.m01 * m2.m10 + m1.m11 * m2.m11 + m1.m21 * m2.m12;
+            m12 = m1.m01 * m2.m20 + m1.m11 * m2.m21 + m1.m21 * m2.m22;
+
+            m20 = m1.m02 * m2.m00 + m1.m12 * m2.m01 + m1.m22 * m2.m02;
+            m21 = m1.m02 * m2.m10 + m1.m12 * m2.m11 + m1.m22 * m2.m12;
+            m22 = m1.m02 * m2.m20 + m1.m12 * m2.m21 + m1.m22 * m2.m22;
+
+            this.m00 = m00;
+            this.m01 = m01;
+            this.m02 = m02;
+            this.m10 = m10;
+            this.m11 = m11;
+            this.m12 = m12;
+            this.m20 = m20;
+            this.m21 = m21;
+            this.m22 = m22;
+        }
+
+    }
+
+    /**
+     * Multiplies the transpose of matrix m1 times matrix m2, and places the
+     * result into this.
+     * 
+     * @param m1
+     *            the matrix on the left hand side of the multiplication
+     * @param m2
+     *            the matrix on the right hand side of the multiplication
+     */
+    public final void mulTransposeLeft(Matrix3d m1, Matrix3d m2) {
+        if (this != m1 && this != m2) {
+            this.m00 = m1.m00 * m2.m00 + m1.m10 * m2.m10 + m1.m20 * m2.m20;
+            this.m01 = m1.m00 * m2.m01 + m1.m10 * m2.m11 + m1.m20 * m2.m21;
+            this.m02 = m1.m00 * m2.m02 + m1.m10 * m2.m12 + m1.m20 * m2.m22;
+
+            this.m10 = m1.m01 * m2.m00 + m1.m11 * m2.m10 + m1.m21 * m2.m20;
+            this.m11 = m1.m01 * m2.m01 + m1.m11 * m2.m11 + m1.m21 * m2.m21;
+            this.m12 = m1.m01 * m2.m02 + m1.m11 * m2.m12 + m1.m21 * m2.m22;
+
+            this.m20 = m1.m02 * m2.m00 + m1.m12 * m2.m10 + m1.m22 * m2.m20;
+            this.m21 = m1.m02 * m2.m01 + m1.m12 * m2.m11 + m1.m22 * m2.m21;
+            this.m22 = m1.m02 * m2.m02 + m1.m12 * m2.m12 + m1.m22 * m2.m22;
+        } else {
+            double m00, m01, m02, m10, m11, m12, m20, m21, m22; // vars for temp
+                                                                // result matrix
+
+            m00 = m1.m00 * m2.m00 + m1.m10 * m2.m10 + m1.m20 * m2.m20;
+            m01 = m1.m00 * m2.m01 + m1.m10 * m2.m11 + m1.m20 * m2.m21;
+            m02 = m1.m00 * m2.m02 + m1.m10 * m2.m12 + m1.m20 * m2.m22;
+
+            m10 = m1.m01 * m2.m00 + m1.m11 * m2.m10 + m1.m21 * m2.m20;
+            m11 = m1.m01 * m2.m01 + m1.m11 * m2.m11 + m1.m21 * m2.m21;
+            m12 = m1.m01 * m2.m02 + m1.m11 * m2.m12 + m1.m21 * m2.m22;
+
+            m20 = m1.m02 * m2.m00 + m1.m12 * m2.m10 + m1.m22 * m2.m20;
+            m21 = m1.m02 * m2.m01 + m1.m12 * m2.m11 + m1.m22 * m2.m21;
+            m22 = m1.m02 * m2.m02 + m1.m12 * m2.m12 + m1.m22 * m2.m22;
+
+            this.m00 = m00;
+            this.m01 = m01;
+            this.m02 = m02;
+            this.m10 = m10;
+            this.m11 = m11;
+            this.m12 = m12;
+            this.m20 = m20;
+            this.m21 = m21;
+            this.m22 = m22;
+        }
+    }
+
+    /**
+     * Multiplies matrix m1 times the transpose of matrix m2, and places the
+     * result into this.
+     * 
+     * @param m1
+     *            the matrix on the left hand side of the multiplication
+     * @param m2
+     *            the matrix on the right hand side of the multiplication
+     */
+    public final void mulTransposeRight(Matrix3d m1, Matrix3d m2) {
+        if (this != m1 && this != m2) {
+            this.m00 = m1.m00 * m2.m00 + m1.m01 * m2.m01 + m1.m02 * m2.m02;
+            this.m01 = m1.m00 * m2.m10 + m1.m01 * m2.m11 + m1.m02 * m2.m12;
+            this.m02 = m1.m00 * m2.m20 + m1.m01 * m2.m21 + m1.m02 * m2.m22;
+
+            this.m10 = m1.m10 * m2.m00 + m1.m11 * m2.m01 + m1.m12 * m2.m02;
+            this.m11 = m1.m10 * m2.m10 + m1.m11 * m2.m11 + m1.m12 * m2.m12;
+            this.m12 = m1.m10 * m2.m20 + m1.m11 * m2.m21 + m1.m12 * m2.m22;
+
+            this.m20 = m1.m20 * m2.m00 + m1.m21 * m2.m01 + m1.m22 * m2.m02;
+            this.m21 = m1.m20 * m2.m10 + m1.m21 * m2.m11 + m1.m22 * m2.m12;
+            this.m22 = m1.m20 * m2.m20 + m1.m21 * m2.m21 + m1.m22 * m2.m22;
+        } else {
+            double m00, m01, m02, m10, m11, m12, m20, m21, m22; // vars for temp
+                                                                // result matrix
+
+            m00 = m1.m00 * m2.m00 + m1.m01 * m2.m01 + m1.m02 * m2.m02;
+            m01 = m1.m00 * m2.m10 + m1.m01 * m2.m11 + m1.m02 * m2.m12;
+            m02 = m1.m00 * m2.m20 + m1.m01 * m2.m21 + m1.m02 * m2.m22;
+
+            m10 = m1.m10 * m2.m00 + m1.m11 * m2.m01 + m1.m12 * m2.m02;
+            m11 = m1.m10 * m2.m10 + m1.m11 * m2.m11 + m1.m12 * m2.m12;
+            m12 = m1.m10 * m2.m20 + m1.m11 * m2.m21 + m1.m12 * m2.m22;
+
+            m20 = m1.m20 * m2.m00 + m1.m21 * m2.m01 + m1.m22 * m2.m02;
+            m21 = m1.m20 * m2.m10 + m1.m21 * m2.m11 + m1.m22 * m2.m12;
+            m22 = m1.m20 * m2.m20 + m1.m21 * m2.m21 + m1.m22 * m2.m22;
+
+            this.m00 = m00;
+            this.m01 = m01;
+            this.m02 = m02;
+            this.m10 = m10;
+            this.m11 = m11;
+            this.m12 = m12;
+            this.m20 = m20;
+            this.m21 = m21;
+            this.m22 = m22;
+        }
+    }
+
+    /**
+     * Negates the value of this matrix: this = -this.
+     */
+    public final void negate() {
+        this.m00 = -this.m00;
+        this.m01 = -this.m01;
+        this.m02 = -this.m02;
+
+        this.m10 = -this.m10;
+        this.m11 = -this.m11;
+        this.m12 = -this.m12;
+
+        this.m20 = -this.m20;
+        this.m21 = -this.m21;
+        this.m22 = -this.m22;
+
+    }
+
+    /**
+     * Sets the value of this matrix equal to the negation of of the Matrix3d
+     * parameter.
+     * 
+     * @param m1
+     *            the source matrix
+     */
+    public final void negate(Matrix3d m1) {
+        this.m00 = -m1.m00;
+        this.m01 = -m1.m01;
+        this.m02 = -m1.m02;
+
+        this.m10 = -m1.m10;
+        this.m11 = -m1.m11;
+        this.m12 = -m1.m12;
+
+        this.m20 = -m1.m20;
+        this.m21 = -m1.m21;
+        this.m22 = -m1.m22;
+
+    }
+
+    /**
+     * Performs singular value decomposition normalization of this matrix.
+     */
+    public final void normalize() {
+        double[] tmp_rot = new double[9]; // scratch matrix
+        double[] tmp_scale = new double[3]; // scratch matrix
+
+        getScaleRotate(tmp_scale, tmp_rot);
+
+        this.m00 = tmp_rot[0];
+        this.m01 = tmp_rot[1];
+        this.m02 = tmp_rot[2];
+
+        this.m10 = tmp_rot[3];
+        this.m11 = tmp_rot[4];
+        this.m12 = tmp_rot[5];
+
+        this.m20 = tmp_rot[6];
+        this.m21 = tmp_rot[7];
+        this.m22 = tmp_rot[8];
+
+    }
+
+    /**
+     * Perform singular value decomposition normalization of matrix m1 and place
+     * the normalized values into this.
+     * 
+     * @param m1
+     *            Provides the matrix values to be normalized
+     */
+    public final void normalize(Matrix3d m1) {
+
+        double[] tmp = new double[9]; // scratch matrix
+        double[] tmp_rot = new double[9]; // scratch matrix
+        double[] tmp_scale = new double[3]; // scratch matrix
+
+        tmp[0] = m1.m00;
+        tmp[1] = m1.m01;
+        tmp[2] = m1.m02;
+
+        tmp[3] = m1.m10;
+        tmp[4] = m1.m11;
+        tmp[5] = m1.m12;
+
+        tmp[6] = m1.m20;
+        tmp[7] = m1.m21;
+        tmp[8] = m1.m22;
+
+        compute_svd(tmp, tmp_scale, tmp_rot);
+
+        this.m00 = tmp_rot[0];
+        this.m01 = tmp_rot[1];
+        this.m02 = tmp_rot[2];
+
+        this.m10 = tmp_rot[3];
+        this.m11 = tmp_rot[4];
+        this.m12 = tmp_rot[5];
+
+        this.m20 = tmp_rot[6];
+        this.m21 = tmp_rot[7];
+        this.m22 = tmp_rot[8];
+    }
+
+    /**
+     * Perform cross product normalization of this matrix.
+     */
+
+    public final void normalizeCP() {
+        double mag = 1.0 / Math.sqrt(m00 * m00 + m10 * m10 + m20 * m20);
+        m00 = m00 * mag;
+        m10 = m10 * mag;
+        m20 = m20 * mag;
+
+        mag = 1.0 / Math.sqrt(m01 * m01 + m11 * m11 + m21 * m21);
+        m01 = m01 * mag;
+        m11 = m11 * mag;
+        m21 = m21 * mag;
+
+        m02 = m10 * m21 - m11 * m20;
+        m12 = m01 * m20 - m00 * m21;
+        m22 = m00 * m11 - m01 * m10;
+    }
+
+    /**
+     * Perform cross product normalization of matrix m1 and place the normalized
+     * values into this.
+     * 
+     * @param m1
+     *            Provides the matrix values to be normalized
+     */
+    public final void normalizeCP(Matrix3d m1) {
+        double mag = 1.0 / Math.sqrt(m1.m00 * m1.m00 + m1.m10 * m1.m10 + m1.m20
+                * m1.m20);
+        m00 = m1.m00 * mag;
+        m10 = m1.m10 * mag;
+        m20 = m1.m20 * mag;
+
+        mag = 1.0 / Math.sqrt(m1.m01 * m1.m01 + m1.m11 * m1.m11 + m1.m21
+                * m1.m21);
+        m01 = m1.m01 * mag;
+        m11 = m1.m11 * mag;
+        m21 = m1.m21 * mag;
+
+        m02 = m10 * m21 - m11 * m20;
+        m12 = m01 * m20 - m00 * m21;
+        m22 = m00 * m11 - m01 * m10;
+    }
+
+    /**
+     * Sets the value of this matrix to a counter clockwise rotation about the x
+     * axis.
+     * 
+     * @param angle
+     *            the angle to rotate about the X axis in radians
+     */
+    public final void rotX(double angle) {
+        double sinAngle, cosAngle;
+
+        sinAngle = Math.sin(angle);
+        cosAngle = Math.cos(angle);
+
+        this.m00 = 1.0;
+        this.m01 = 0.0;
+        this.m02 = 0.0;
+
+        this.m10 = 0.0;
+        this.m11 = cosAngle;
+        this.m12 = -sinAngle;
+
+        this.m20 = 0.0;
+        this.m21 = sinAngle;
+        this.m22 = cosAngle;
+    }
+
+    /**
+     * Sets the value of this matrix to a counter clockwise rotation about the y
+     * axis.
+     * 
+     * @param angle
+     *            the angle to rotate about the Y axis in radians
+     */
+    public final void rotY(double angle) {
+        double sinAngle, cosAngle;
+
+        sinAngle = Math.sin(angle);
+        cosAngle = Math.cos(angle);
+
+        this.m00 = cosAngle;
+        this.m01 = 0.0;
+        this.m02 = sinAngle;
+
+        this.m10 = 0.0;
+        this.m11 = 1.0;
+        this.m12 = 0.0;
+
+        this.m20 = -sinAngle;
+        this.m21 = 0.0;
+        this.m22 = cosAngle;
+    }
+
+    /**
+     * Sets the value of this matrix to a counter clockwise rotation about the z
+     * axis.
+     * 
+     * @param angle
+     *            the angle to rotate about the Z axis in radians
+     */
+    public final void rotZ(double angle) {
+        double sinAngle, cosAngle;
+
+        sinAngle = Math.sin(angle);
+        cosAngle = Math.cos(angle);
+
+        this.m00 = cosAngle;
+        this.m01 = -sinAngle;
+        this.m02 = 0.0;
+
+        this.m10 = sinAngle;
+        this.m11 = cosAngle;
+        this.m12 = 0.0;
+
+        this.m20 = 0.0;
+        this.m21 = 0.0;
+        this.m22 = 1.0;
+    }
+
+    /**
+     * Sets the value of this matrix to a scale matrix with the passed scale
+     * amount.
+     * 
+     * @param scale
+     *            the scale factor for the matrix
+     */
+    public final void set(double scale) {
+        this.m00 = scale;
+        this.m01 = 0.0;
+        this.m02 = 0.0;
+
+        this.m10 = 0.0;
+        this.m11 = scale;
+        this.m12 = 0.0;
+
+        this.m20 = 0.0;
+        this.m21 = 0.0;
+        this.m22 = scale;
+    }
+
+    /**
+     * Sets the values in this Matrix3d equal to the row-major array parameter
+     * (ie, the first three elements of the array will be copied into the first
+     * row of this matrix, etc.).
+     * 
+     * @param m
+     *            the double precision array of length 9
+     */
+    public final void set(double[] m) {
+        m00 = m[0];
+        m01 = m[1];
+        m02 = m[2];
+
+        m10 = m[3];
+        m11 = m[4];
+        m12 = m[5];
+
+        m20 = m[6];
+        m21 = m[7];
+        m22 = m[8];
+
+    }
+
+    /**
+     * Sets the value of this matrix to the value of the Matrix3d argument.
+     * 
+     * @param m1
+     *            the source matrix3d
+     */
+    public final void set(Matrix3d m1) {
+        this.m00 = m1.m00;
+        this.m01 = m1.m01;
+        this.m02 = m1.m02;
+
+        this.m10 = m1.m10;
+        this.m11 = m1.m11;
+        this.m12 = m1.m12;
+
+        this.m20 = m1.m20;
+        this.m21 = m1.m21;
+        this.m22 = m1.m22;
+    }
+
+    /**
+     * Sets the value of this matrix to the matrix conversion of the single
+     * precision quaternion argument.
+     * 
+     * @param q1
+     *            the quaternion to be converted
+     */
+    public final void set(Quaternion q1) {
+        this.m00 = (1.0 - 2.0 * q1.y * q1.y - 2.0 * q1.z * q1.z);
+        this.m10 = (2.0 * (q1.x * q1.y + q1.w * q1.z));
+        this.m20 = (2.0 * (q1.x * q1.z - q1.w * q1.y));
+
+        this.m01 = (2.0 * (q1.x * q1.y - q1.w * q1.z));
+        this.m11 = (1.0 - 2.0 * q1.x * q1.x - 2.0 * q1.z * q1.z);
+        this.m21 = (2.0 * (q1.y * q1.z + q1.w * q1.x));
+
+        this.m02 = (2.0 * (q1.x * q1.z + q1.w * q1.y));
+        this.m12 = (2.0 * (q1.y * q1.z - q1.w * q1.x));
+        this.m22 = (1.0 - 2.0 * q1.x * q1.x - 2.0 * q1.y * q1.y);
+    }
+
+    /**
+     * Sets the specified column of this matrix3d to the three values provided.
+     * 
+     * @param column
+     *            the column number to be modified (zero indexed)
+     * @param v
+     *            the replacement column
+     */
+    public final void setColumn(int column, double v[]) {
+        switch (column) {
+            case 0:
+                this.m00 = v[0];
+                this.m10 = v[1];
+                this.m20 = v[2];
+                break;
+
+            case 1:
+                this.m01 = v[0];
+                this.m11 = v[1];
+                this.m21 = v[2];
+                break;
+
+            case 2:
+                this.m02 = v[0];
+                this.m12 = v[1];
+                this.m22 = v[2];
+                break;
+
+            default:
+                throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Sets the specified column of this matrix3d to the three values provided.
+     * 
+     * @param column
+     *            the column number to be modified (zero indexed)
+     * @param x
+     *            the first row element
+     * @param y
+     *            the second row element
+     * @param z
+     *            the third row element
+     */
+    public final void setColumn(int column, double x, double y, double z) {
+        switch (column) {
+            case 0:
+                this.m00 = x;
+                this.m10 = y;
+                this.m20 = z;
+                break;
+
+            case 1:
+                this.m01 = x;
+                this.m11 = y;
+                this.m21 = z;
+                break;
+
+            case 2:
+                this.m02 = x;
+                this.m12 = y;
+                this.m22 = z;
+                break;
+
+            default:
+                throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Sets the specified column of this matrix3d to the vector provided.
+     * 
+     * @param column
+     *            the column number to be modified (zero indexed)
+     * @param v
+     *            the replacement column
+     */
+    public final void setColumn(int column, Vec3D v) {
+        switch (column) {
+            case 0:
+                this.m00 = v.x;
+                this.m10 = v.y;
+                this.m20 = v.z;
+                break;
+
+            case 1:
+                this.m01 = v.x;
+                this.m11 = v.y;
+                this.m21 = v.z;
+                break;
+
+            case 2:
+                this.m02 = v.x;
+                this.m12 = v.y;
+                this.m22 = v.z;
+                break;
+
+            default:
+                throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Sets the specified element of this matrix3f to the value provided.
+     * 
+     * @param row
+     *            the row number to be modified (zero indexed)
+     * @param column
+     *            the column number to be modified (zero indexed)
+     * @param value
+     *            the new value
+     */
+    public final void setElement(int row, int column, double value) {
+        switch (row) {
+            case 0:
+                switch (column) {
+                    case 0:
+                        this.m00 = value;
+                        break;
+                    case 1:
+                        this.m01 = value;
+                        break;
+                    case 2:
+                        this.m02 = value;
+                        break;
+                    default:
+                        throw new ArrayIndexOutOfBoundsException();
+                }
+                break;
+
+            case 1:
+                switch (column) {
+                    case 0:
+                        this.m10 = value;
+                        break;
+                    case 1:
+                        this.m11 = value;
+                        break;
+                    case 2:
+                        this.m12 = value;
+                        break;
+                    default:
+                        throw new ArrayIndexOutOfBoundsException();
+                }
+                break;
+
+            case 2:
+                switch (column) {
+                    case 0:
+                        this.m20 = value;
+                        break;
+                    case 1:
+                        this.m21 = value;
+                        break;
+                    case 2:
+                        this.m22 = value;
+                        break;
+                    default:
+                        throw new ArrayIndexOutOfBoundsException();
+                }
+                break;
+
+            default:
+                throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Sets this Matrix3d to identity.
+     */
+    public final void setIdentity() {
+        this.m00 = 1.0;
+        this.m01 = 0.0;
+        this.m02 = 0.0;
+
+        this.m10 = 0.0;
+        this.m11 = 1.0;
+        this.m12 = 0.0;
+
+        this.m20 = 0.0;
+        this.m21 = 0.0;
+        this.m22 = 1.0;
+    }
+
+    /**
+     * Set the first matrix element in the first row.
+     * 
+     * @param m00
+     *            The m00 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM00(double m00) {
+        this.m00 = m00;
+    }
+
+    /**
+     * Set the second matrix element in the first row.
+     * 
+     * @param m01
+     *            The m01 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM01(double m01) {
+        this.m01 = m01;
+    }
+
+    /**
+     * Set the third matrix element in the first row.
+     * 
+     * @param m02
+     *            The m02 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM02(double m02) {
+        this.m02 = m02;
+    }
+
+    /**
+     * Set first matrix element in the second row.
+     * 
+     * @param m10
+     *            The m10 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM10(double m10) {
+        this.m10 = m10;
+    }
+
+    /**
+     * Set the second matrix element in the second row.
+     * 
+     * @param m11
+     *            The m11 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM11(double m11) {
+        this.m11 = m11;
+    }
+
+    /**
+     * Set the third matrix element in the second row.
+     * 
+     * @param m12
+     *            The m12 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM12(double m12) {
+        this.m12 = m12;
+    }
+
+    /**
+     * Set the first matrix element in the third row.
+     * 
+     * @param m20
+     *            The m20 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM20(double m20) {
+        this.m20 = m20;
+    }
+
+    /**
+     * Set the second matrix element in the third row.
+     * 
+     * @param m21
+     *            The m21 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM21(double m21) {
+        this.m21 = m21;
+    }
+
+    /**
+     * Set the third matrix element in the third row.
+     * 
+     * @param m22
+     *            The m22 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM22(double m22) {
+        this.m22 = m22;
+    }
+
+    /**
+     * Sets the specified row of this matrix3d to the three values provided.
+     * 
+     * @param row
+     *            the row number to be modified (zero indexed)
+     * @param v
+     *            the replacement row
+     */
+    public final void setRow(int row, double v[]) {
+        switch (row) {
+            case 0:
+                this.m00 = v[0];
+                this.m01 = v[1];
+                this.m02 = v[2];
+                break;
+
+            case 1:
+                this.m10 = v[0];
+                this.m11 = v[1];
+                this.m12 = v[2];
+                break;
+
+            case 2:
+                this.m20 = v[0];
+                this.m21 = v[1];
+                this.m22 = v[2];
+                break;
+
+            default:
+                throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Sets the specified row of this matrix3d to the 4 values provided.
+     * 
+     * @param row
+     *            the row number to be modified (zero indexed)
+     * @param x
+     *            the first column element
+     * @param y
+     *            the second column element
+     * @param z
+     *            the third column element
+     */
+    public final void setRow(int row, double x, double y, double z) {
+        switch (row) {
+            case 0:
+                this.m00 = x;
+                this.m01 = y;
+                this.m02 = z;
+                break;
+
+            case 1:
+                this.m10 = x;
+                this.m11 = y;
+                this.m12 = z;
+                break;
+
+            case 2:
+                this.m20 = x;
+                this.m21 = y;
+                this.m22 = z;
+                break;
+
+            default:
+                throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Sets the specified row of this matrix3d to the Vector provided.
+     * 
+     * @param row
+     *            the row number to be modified (zero indexed)
+     * @param v
+     *            the replacement row
+     */
+    public final void setRow(int row, Vec3D v) {
+        switch (row) {
+            case 0:
+                this.m00 = v.x;
+                this.m01 = v.y;
+                this.m02 = v.z;
+                break;
+
+            case 1:
+                this.m10 = v.x;
+                this.m11 = v.y;
+                this.m12 = v.z;
+                break;
+
+            case 2:
+                this.m20 = v.x;
+                this.m21 = v.y;
+                this.m22 = v.z;
+                break;
+
+            default:
+                throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Sets the scale component of the current matrix by factoring out the
+     * current scale (by doing an SVD) and multiplying by the new scale.
+     * 
+     * @param scale
+     *            the new scale amount
+     */
+    public final void setScale(double scale) {
+
+        double[] tmp_rot = new double[9]; // scratch matrix
+        double[] tmp_scale = new double[3]; // scratch matrix
+
+        getScaleRotate(tmp_scale, tmp_rot);
+
+        this.m00 = tmp_rot[0] * scale;
+        this.m01 = tmp_rot[1] * scale;
+        this.m02 = tmp_rot[2] * scale;
+
+        this.m10 = tmp_rot[3] * scale;
+        this.m11 = tmp_rot[4] * scale;
+        this.m12 = tmp_rot[5] * scale;
+
+        this.m20 = tmp_rot[6] * scale;
+        this.m21 = tmp_rot[7] * scale;
+        this.m22 = tmp_rot[8] * scale;
+    }
+
+    /**
+     * Sets this matrix to all zeros.
+     */
+    public final void setZero() {
+        m00 = 0.0;
+        m01 = 0.0;
+        m02 = 0.0;
+
+        m10 = 0.0;
+        m11 = 0.0;
+        m12 = 0.0;
+
+        m20 = 0.0;
+        m21 = 0.0;
+        m22 = 0.0;
+
+    }
+
+    /**
+     * Sets the value of this matrix to the matrix difference of itself and
+     * matrix m1 (this = this - m1).
+     * 
+     * @param m1
+     *            the other matrix
+     */
+    public final void sub(Matrix3d m1) {
+        this.m00 -= m1.m00;
+        this.m01 -= m1.m01;
+        this.m02 -= m1.m02;
+
+        this.m10 -= m1.m10;
+        this.m11 -= m1.m11;
+        this.m12 -= m1.m12;
+
+        this.m20 -= m1.m20;
+        this.m21 -= m1.m21;
+        this.m22 -= m1.m22;
+    }
+
+    /**
+     * Sets the value of this matrix to the matrix difference of matrices m1 and
+     * m2.
+     * 
+     * @param m1
+     *            the first matrix
+     * @param m2
+     *            the second matrix
+     */
+    public final void sub(Matrix3d m1, Matrix3d m2) {
+        this.m00 = m1.m00 - m2.m00;
+        this.m01 = m1.m01 - m2.m01;
+        this.m02 = m1.m02 - m2.m02;
+
+        this.m10 = m1.m10 - m2.m10;
+        this.m11 = m1.m11 - m2.m11;
+        this.m12 = m1.m12 - m2.m12;
+
+        this.m20 = m1.m20 - m2.m20;
+        this.m21 = m1.m21 - m2.m21;
+        this.m22 = m1.m22 - m2.m22;
+    }
+
+    /**
+     * Returns a string that contains the values of this Matrix3d.
+     * 
+     * @return the String representation
+     */
+    public String toString() {
+        return this.m00 + ", " + this.m01 + ", " + this.m02 + "\n" + this.m10
+                + ", " + this.m11 + ", " + this.m12 + "\n" + this.m20 + ", "
+                + this.m21 + ", " + this.m22 + "\n";
+    }
+
+    /**
+     * Multiply this matrix by the tuple t and place the result back into the
+     * tuple (t = this*t).
+     * 
+     * @param t
+     *            the tuple to be multiplied by this matrix and then replaced
+     */
+    public final void transform(Vec3D t) {
+        float x, y, z;
+        x = (float) (m00 * t.x + m01 * t.y + m02 * t.z);
+        y = (float) (m10 * t.x + m11 * t.y + m12 * t.z);
+        z = (float) (m20 * t.x + m21 * t.y + m22 * t.z);
+        t.set(x, y, z);
+    }
+
+    /**
+     * Multiply this matrix by the tuple t and and place the result into the
+     * tuple "result" (result = this*t).
+     * 
+     * @param t
+     *            the tuple to be multiplied by this matrix
+     * @param result
+     *            the tuple into which the product is placed
+     */
+    public final void transform(Vec3D t, Vec3D result) {
+        float x, y, z;
+        x = (float) (m00 * t.x + m01 * t.y + m02 * t.z);
+        y = (float) (m10 * t.x + m11 * t.y + m12 * t.z);
+        result.z = (float) (m20 * t.x + m21 * t.y + m22 * t.z);
+        result.x = x;
+        result.y = y;
+    }
+
+    /**
+     * Sets the value of this matrix to its transpose.
+     */
+    public final void transpose() {
+        double temp;
+
+        temp = this.m10;
+        this.m10 = this.m01;
+        this.m01 = temp;
+
+        temp = this.m20;
+        this.m20 = this.m02;
+        this.m02 = temp;
+
+        temp = this.m21;
+        this.m21 = this.m12;
+        this.m12 = temp;
+    }
+
+    /**
+     * Sets the value of this matrix to the transpose of the argument matrix.
+     * 
+     * @param m1
+     *            the matrix to be transposed
+     */
+    public final void transpose(Matrix3d m1) {
+        if (this != m1) {
+            this.m00 = m1.m00;
+            this.m01 = m1.m10;
+            this.m02 = m1.m20;
+
+            this.m10 = m1.m01;
+            this.m11 = m1.m11;
+            this.m12 = m1.m21;
+
+            this.m20 = m1.m02;
+            this.m21 = m1.m12;
+            this.m22 = m1.m22;
+        } else {
+            this.transpose();
+        }
+    }
+
+}
diff --git a/src/main/java/toxi/geom/Matrix4f.java b/src/main/java/toxi/geom/Matrix4f.java
new file mode 100644
index 0000000..bc054e0
--- /dev/null
+++ b/src/main/java/toxi/geom/Matrix4f.java
@@ -0,0 +1,3419 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * $Revision: 127 $
+ * $Date: 2008-02-28 20:18:51 +0000 (Thu, 28 Feb 2008) $
+ * $State$
+ */
+
+package toxi.geom;
+
+import toxi.math.MathUtils;
+
+/**
+ * A single precision floating point 4 by 4 matrix. Primarily to support 3D
+ * rotations.
+ * 
+ */
+public class Matrix4f implements java.io.Serializable, Cloneable {
+
+    // Compatible with 1.1
+    static final long serialVersionUID = -8405036035410109353L;
+
+    /**
+     * Solves a set of linear equations. The input parameters "matrix1", and
+     * "row_perm" come from luDecompostionD4x4 and do not change here. The
+     * parameter "matrix2" is a set of column vectors assembled into a 4x4
+     * matrix of floating-point values. The procedure takes each column of
+     * "matrix2" in turn and treats it as the right-hand side of the matrix
+     * equation Ax = LUx = b. The solution vector replaces the original column
+     * of the matrix.
+     * 
+     * If "matrix2" is the identity matrix, the procedure replaces its contents
+     * with the inverse of the matrix from which "matrix1" was originally
+     * derived.
+     */
+    //
+    // Reference: Press, Flannery, Teukolsky, Vetterling,
+    // _Numerical_Recipes_in_C_, Cambridge University Press,
+    // 1988, pp 44-45.
+    //
+    static void luBacksubstitution(double[] matrix1, int[] row_perm,
+            double[] matrix2) {
+
+        int i, ii, ip, j, k;
+        int rp;
+        int cv, rv;
+
+        // rp = row_perm;
+        rp = 0;
+
+        // For each column vector of matrix2 ...
+        for (k = 0; k < 4; k++) {
+            // cv = &(matrix2[0][k]);
+            cv = k;
+            ii = -1;
+
+            // Forward substitution
+            for (i = 0; i < 4; i++) {
+                double sum;
+
+                ip = row_perm[rp + i];
+                sum = matrix2[cv + 4 * ip];
+                matrix2[cv + 4 * ip] = matrix2[cv + 4 * i];
+                if (ii >= 0) {
+                    // rv = &(matrix1[i][0]);
+                    rv = i * 4;
+                    for (j = ii; j <= i - 1; j++) {
+                        sum -= matrix1[rv + j] * matrix2[cv + 4 * j];
+                    }
+                } else if (sum != 0.0) {
+                    ii = i;
+                }
+                matrix2[cv + 4 * i] = sum;
+            }
+
+            // Backsubstitution
+            // rv = &(matrix1[3][0]);
+            rv = 3 * 4;
+            matrix2[cv + 4 * 3] /= matrix1[rv + 3];
+
+            rv -= 4;
+            matrix2[cv + 4 * 2] = (matrix2[cv + 4 * 2] - matrix1[rv + 3]
+                    * matrix2[cv + 4 * 3])
+                    / matrix1[rv + 2];
+
+            rv -= 4;
+            matrix2[cv + 4 * 1] = (matrix2[cv + 4 * 1] - matrix1[rv + 2]
+                    * matrix2[cv + 4 * 2] - matrix1[rv + 3]
+                    * matrix2[cv + 4 * 3])
+                    / matrix1[rv + 1];
+
+            rv -= 4;
+            matrix2[cv + 4 * 0] = (matrix2[cv + 4 * 0] - matrix1[rv + 1]
+                    * matrix2[cv + 4 * 1] - matrix1[rv + 2]
+                    * matrix2[cv + 4 * 2] - matrix1[rv + 3]
+                    * matrix2[cv + 4 * 3])
+                    / matrix1[rv + 0];
+        }
+    }
+
+    /**
+     * The first element of the first row.
+     */
+    public float m00;
+
+    /**
+     * The second element of the first row.
+     */
+    public float m01;
+
+    /**
+     * The third element of the first row.
+     */
+    public float m02;
+
+    /**
+     * The fourth element of the first row.
+     */
+    public float m03;
+
+    /**
+     * The first element of the second row.
+     */
+    public float m10;
+
+    /**
+     * The second element of the second row.
+     */
+    public float m11;
+
+    /**
+     * The third element of the second row.
+     */
+    public float m12;
+
+    /**
+     * The fourth element of the second row.
+     */
+    public float m13;
+
+    /**
+     * The first element of the third row.
+     */
+    public float m20;
+
+    /**
+     * The second element of the third row.
+     */
+    public float m21;
+
+    /**
+     * The third element of the third row.
+     */
+    public float m22;
+
+    /**
+     * The fourth element of the third row.
+     */
+    public float m23;
+
+    /**
+     * The first element of the fourth row.
+     */
+    public float m30;
+
+    /**
+     * The second element of the fourth row.
+     */
+    public float m31;
+    /**
+     * The third element of the fourth row.
+     */
+    public float m32;
+
+    /**
+     * The fourth element of the fourth row.
+     */
+    public float m33;
+
+    /*
+     * double[] tmp = new double[9]; double[] tmp_scale = new double[3];
+     * double[] tmp_rot = new double[9];
+     */
+    private static final double EPS = 1.0E-8;
+
+    /**
+     * Constructs and initializes a Matrix4f to all zeros.
+     */
+    public Matrix4f() {
+        this.m00 = (float) 0.0;
+        this.m01 = (float) 0.0;
+        this.m02 = (float) 0.0;
+        this.m03 = (float) 0.0;
+
+        this.m10 = (float) 0.0;
+        this.m11 = (float) 0.0;
+        this.m12 = (float) 0.0;
+        this.m13 = (float) 0.0;
+
+        this.m20 = (float) 0.0;
+        this.m21 = (float) 0.0;
+        this.m22 = (float) 0.0;
+        this.m23 = (float) 0.0;
+
+        this.m30 = (float) 0.0;
+        this.m31 = (float) 0.0;
+        this.m32 = (float) 0.0;
+        this.m33 = (float) 0.0;
+
+    }
+
+    /**
+     * Constructs and initializes a Matrix4f from the specified 16 values.
+     * 
+     * @param m00
+     *            the [0][0] element
+     * @param m01
+     *            the [0][1] element
+     * @param m02
+     *            the [0][2] element
+     * @param m03
+     *            the [0][3] element
+     * @param m10
+     *            the [1][0] element
+     * @param m11
+     *            the [1][1] element
+     * @param m12
+     *            the [1][2] element
+     * @param m13
+     *            the [1][3] element
+     * @param m20
+     *            the [2][0] element
+     * @param m21
+     *            the [2][1] element
+     * @param m22
+     *            the [2][2] element
+     * @param m23
+     *            the [2][3] element
+     * @param m30
+     *            the [3][0] element
+     * @param m31
+     *            the [3][1] element
+     * @param m32
+     *            the [3][2] element
+     * @param m33
+     *            the [3][3] element
+     */
+    public Matrix4f(float m00, float m01, float m02, float m03, float m10,
+            float m11, float m12, float m13, float m20, float m21, float m22,
+            float m23, float m30, float m31, float m32, float m33) {
+        this.m00 = m00;
+        this.m01 = m01;
+        this.m02 = m02;
+        this.m03 = m03;
+
+        this.m10 = m10;
+        this.m11 = m11;
+        this.m12 = m12;
+        this.m13 = m13;
+
+        this.m20 = m20;
+        this.m21 = m21;
+        this.m22 = m22;
+        this.m23 = m23;
+
+        this.m30 = m30;
+        this.m31 = m31;
+        this.m32 = m32;
+        this.m33 = m33;
+
+    }
+
+    /**
+     * Constructs and initializes a Matrix4f from the specified 16 element
+     * array. this.m00 =v[0], this.m01=v[1], etc.
+     * 
+     * @param v
+     *            the array of length 16 containing in order
+     */
+    public Matrix4f(float[] v) {
+        this.m00 = v[0];
+        this.m01 = v[1];
+        this.m02 = v[2];
+        this.m03 = v[3];
+
+        this.m10 = v[4];
+        this.m11 = v[5];
+        this.m12 = v[6];
+        this.m13 = v[7];
+
+        this.m20 = v[8];
+        this.m21 = v[9];
+        this.m22 = v[10];
+        this.m23 = v[11];
+
+        this.m30 = v[12];
+        this.m31 = v[13];
+        this.m32 = v[14];
+        this.m33 = v[15];
+
+    }
+
+    /**
+     * Constructs and initializes a Matrix4f from the rotation matrix,
+     * translation, and scale values; the scale is applied only to the
+     * rotational components of the matrix (upper 3x3) and not to the
+     * translational components of the matrix.
+     * 
+     * @param m1
+     *            the rotation matrix representing the rotational components
+     * @param t1
+     *            the translational components of the matrix
+     * @param s
+     *            the scale value applied to the rotational components
+     */
+    public Matrix4f(Matrix3d m1, Vec3D t1, float s) {
+        this.m00 = (float) (m1.m00 * s);
+        this.m01 = (float) (m1.m01 * s);
+        this.m02 = (float) (m1.m02 * s);
+        this.m03 = t1.x;
+
+        this.m10 = (float) (m1.m10 * s);
+        this.m11 = (float) (m1.m11 * s);
+        this.m12 = (float) (m1.m12 * s);
+        this.m13 = t1.y;
+
+        this.m20 = (float) (m1.m20 * s);
+        this.m21 = (float) (m1.m21 * s);
+        this.m22 = (float) (m1.m22 * s);
+        this.m23 = t1.z;
+
+        this.m30 = 0.0f;
+        this.m31 = 0.0f;
+        this.m32 = 0.0f;
+        this.m33 = 1.0f;
+
+    }
+
+    /**
+     * Constructs a new matrix with the same values as the Matrix4f parameter.
+     * 
+     * @param m1
+     *            the source matrix
+     */
+    public Matrix4f(Matrix4f m1) {
+        this.m00 = m1.m00;
+        this.m01 = m1.m01;
+        this.m02 = m1.m02;
+        this.m03 = m1.m03;
+
+        this.m10 = m1.m10;
+        this.m11 = m1.m11;
+        this.m12 = m1.m12;
+        this.m13 = m1.m13;
+
+        this.m20 = m1.m20;
+        this.m21 = m1.m21;
+        this.m22 = m1.m22;
+        this.m23 = m1.m23;
+
+        this.m30 = m1.m30;
+        this.m31 = m1.m31;
+        this.m32 = m1.m32;
+        this.m33 = m1.m33;
+
+    }
+
+    /**
+     * Constructs and initializes a Matrix4f from the quaternion, translation,
+     * and scale values; the scale is applied only to the rotational components
+     * of the matrix (upper 3x3) and not to the translational components.
+     * 
+     * @param q1
+     *            the quaternion value representing the rotational component
+     * @param t1
+     *            the translational component of the matrix
+     * @param s
+     *            the scale value applied to the rotational components
+     */
+    public Matrix4f(Quaternion q1, Vec3D t1, float s) {
+        m00 = (float) (s * (1.0 - 2.0 * q1.y * q1.y - 2.0 * q1.z * q1.z));
+        m10 = (float) (s * (2.0 * (q1.x * q1.y + q1.w * q1.z)));
+        m20 = (float) (s * (2.0 * (q1.x * q1.z - q1.w * q1.y)));
+
+        m01 = (float) (s * (2.0 * (q1.x * q1.y - q1.w * q1.z)));
+        m11 = (float) (s * (1.0 - 2.0 * q1.x * q1.x - 2.0 * q1.z * q1.z));
+        m21 = (float) (s * (2.0 * (q1.y * q1.z + q1.w * q1.x)));
+
+        m02 = (float) (s * (2.0 * (q1.x * q1.z + q1.w * q1.y)));
+        m12 = (float) (s * (2.0 * (q1.y * q1.z - q1.w * q1.x)));
+        m22 = (float) (s * (1.0 - 2.0 * q1.x * q1.x - 2.0 * q1.y * q1.y));
+
+        m03 = t1.x;
+        m13 = t1.y;
+        m23 = t1.z;
+
+        m30 = 0.0f;
+        m31 = 0.0f;
+        m32 = 0.0f;
+        m33 = 1.0f;
+
+    }
+
+    /**
+     * Adds a scalar to each component of this matrix.
+     * 
+     * @param scalar
+     *            the scalar adder
+     */
+    public final void add(float scalar) {
+        m00 += scalar;
+        m01 += scalar;
+        m02 += scalar;
+        m03 += scalar;
+        m10 += scalar;
+        m11 += scalar;
+        m12 += scalar;
+        m13 += scalar;
+        m20 += scalar;
+        m21 += scalar;
+        m22 += scalar;
+        m23 += scalar;
+        m30 += scalar;
+        m31 += scalar;
+        m32 += scalar;
+        m33 += scalar;
+    }
+
+    /**
+     * Adds a scalar to each component of the matrix m1 and places the result
+     * into this. Matrix m1 is not modified.
+     * 
+     * @param scalar
+     *            the scalar adder
+     * @param m1
+     *            the original matrix values
+     */
+    public final void add(float scalar, Matrix4f m1) {
+        this.m00 = m1.m00 + scalar;
+        this.m01 = m1.m01 + scalar;
+        this.m02 = m1.m02 + scalar;
+        this.m03 = m1.m03 + scalar;
+        this.m10 = m1.m10 + scalar;
+        this.m11 = m1.m11 + scalar;
+        this.m12 = m1.m12 + scalar;
+        this.m13 = m1.m13 + scalar;
+        this.m20 = m1.m20 + scalar;
+        this.m21 = m1.m21 + scalar;
+        this.m22 = m1.m22 + scalar;
+        this.m23 = m1.m23 + scalar;
+        this.m30 = m1.m30 + scalar;
+        this.m31 = m1.m31 + scalar;
+        this.m32 = m1.m32 + scalar;
+        this.m33 = m1.m33 + scalar;
+    }
+
+    /**
+     * Sets the value of this matrix to the sum of itself and matrix m1.
+     * 
+     * @param m1
+     *            the other matrix
+     */
+    public final void add(Matrix4f m1) {
+        this.m00 += m1.m00;
+        this.m01 += m1.m01;
+        this.m02 += m1.m02;
+        this.m03 += m1.m03;
+
+        this.m10 += m1.m10;
+        this.m11 += m1.m11;
+        this.m12 += m1.m12;
+        this.m13 += m1.m13;
+
+        this.m20 += m1.m20;
+        this.m21 += m1.m21;
+        this.m22 += m1.m22;
+        this.m23 += m1.m23;
+
+        this.m30 += m1.m30;
+        this.m31 += m1.m31;
+        this.m32 += m1.m32;
+        this.m33 += m1.m33;
+    }
+
+    /**
+     * Sets the value of this matrix to the matrix sum of matrices m1 and m2.
+     * 
+     * @param m1
+     *            the first matrix
+     * @param m2
+     *            the second matrix
+     */
+    public final void add(Matrix4f m1, Matrix4f m2) {
+        this.m00 = m1.m00 + m2.m00;
+        this.m01 = m1.m01 + m2.m01;
+        this.m02 = m1.m02 + m2.m02;
+        this.m03 = m1.m03 + m2.m03;
+
+        this.m10 = m1.m10 + m2.m10;
+        this.m11 = m1.m11 + m2.m11;
+        this.m12 = m1.m12 + m2.m12;
+        this.m13 = m1.m13 + m2.m13;
+
+        this.m20 = m1.m20 + m2.m20;
+        this.m21 = m1.m21 + m2.m21;
+        this.m22 = m1.m22 + m2.m22;
+        this.m23 = m1.m23 + m2.m23;
+
+        this.m30 = m1.m30 + m2.m30;
+        this.m31 = m1.m31 + m2.m31;
+        this.m32 = m1.m32 + m2.m32;
+        this.m33 = m1.m33 + m2.m33;
+    }
+
+    /**
+     * Creates a new object of the same class as this object.
+     * 
+     * @return a clone of this instance.
+     * @exception OutOfMemoryError
+     *                if there is not enough memory.
+     * @see Cloneable
+     * @since vecmath 1.3
+     */
+    public Object clone() {
+        Matrix4f m1 = null;
+        try {
+            m1 = (Matrix4f) super.clone();
+        } catch (CloneNotSupportedException e) {
+            // this shouldn't happen, since we are Cloneable
+            throw new InternalError();
+        }
+
+        return m1;
+    }
+
+    /**
+     * Computes the determinate of this matrix.
+     * 
+     * @return the determinate of the matrix
+     */
+    public final float determinant() {
+        float det;
+
+        // cofactor exapainsion along first row
+
+        det = m00
+                * (m11 * m22 * m33 + m12 * m23 * m31 + m13 * m21 * m32 - m13
+                        * m22 * m31 - m11 * m23 * m32 - m12 * m21 * m33);
+        det -= m01
+                * (m10 * m22 * m33 + m12 * m23 * m30 + m13 * m20 * m32 - m13
+                        * m22 * m30 - m10 * m23 * m32 - m12 * m20 * m33);
+        det += m02
+                * (m10 * m21 * m33 + m11 * m23 * m30 + m13 * m20 * m31 - m13
+                        * m21 * m30 - m10 * m23 * m31 - m11 * m20 * m33);
+        det -= m03
+                * (m10 * m21 * m32 + m11 * m22 * m30 + m12 * m20 * m31 - m12
+                        * m21 * m30 - m10 * m22 * m31 - m11 * m20 * m32);
+
+        return (det);
+    }
+
+    /**
+     * Returns true if the L-infinite distance between this matrix and matrix m1
+     * is less than or equal to the epsilon parameter, otherwise returns false.
+     * The L-infinite distance is equal to MAX[i=0,1,2,3 ; j=0,1,2,3 ;
+     * abs(this.m(i,j) - m1.m(i,j)]
+     * 
+     * @param m1
+     *            the matrix to be compared to this matrix
+     * @param epsilon
+     *            the threshold value
+     */
+    public boolean epsilonEquals(Matrix4f m1, float epsilon) {
+
+        boolean status = true;
+
+        if (Math.abs(this.m00 - m1.m00) > epsilon) {
+            status = false;
+        }
+        if (Math.abs(this.m01 - m1.m01) > epsilon) {
+            status = false;
+        }
+        if (Math.abs(this.m02 - m1.m02) > epsilon) {
+            status = false;
+        }
+        if (Math.abs(this.m03 - m1.m03) > epsilon) {
+            status = false;
+        }
+
+        if (Math.abs(this.m10 - m1.m10) > epsilon) {
+            status = false;
+        }
+        if (Math.abs(this.m11 - m1.m11) > epsilon) {
+            status = false;
+        }
+        if (Math.abs(this.m12 - m1.m12) > epsilon) {
+            status = false;
+        }
+        if (Math.abs(this.m13 - m1.m13) > epsilon) {
+            status = false;
+        }
+
+        if (Math.abs(this.m20 - m1.m20) > epsilon) {
+            status = false;
+        }
+        if (Math.abs(this.m21 - m1.m21) > epsilon) {
+            status = false;
+        }
+        if (Math.abs(this.m22 - m1.m22) > epsilon) {
+            status = false;
+        }
+        if (Math.abs(this.m23 - m1.m23) > epsilon) {
+            status = false;
+        }
+
+        if (Math.abs(this.m30 - m1.m30) > epsilon) {
+            status = false;
+        }
+        if (Math.abs(this.m31 - m1.m31) > epsilon) {
+            status = false;
+        }
+        if (Math.abs(this.m32 - m1.m32) > epsilon) {
+            status = false;
+        }
+        if (Math.abs(this.m33 - m1.m33) > epsilon) {
+            status = false;
+        }
+
+        return (status);
+
+    }
+
+    /**
+     * Returns true if all of the data members of Matrix4f m1 are equal to the
+     * corresponding data members in this Matrix4f.
+     * 
+     * @param m1
+     *            the matrix with which the comparison is made.
+     * @return true or false
+     */
+    public boolean equals(Matrix4f m1) {
+        try {
+            return (this.m00 == m1.m00 && this.m01 == m1.m01
+                    && this.m02 == m1.m02 && this.m03 == m1.m03
+                    && this.m10 == m1.m10 && this.m11 == m1.m11
+                    && this.m12 == m1.m12 && this.m13 == m1.m13
+                    && this.m20 == m1.m20 && this.m21 == m1.m21
+                    && this.m22 == m1.m22 && this.m23 == m1.m23
+                    && this.m30 == m1.m30 && this.m31 == m1.m31
+                    && this.m32 == m1.m32 && this.m33 == m1.m33);
+        } catch (NullPointerException e2) {
+            return false;
+        }
+
+    }
+
+    /**
+     * Returns true if the Object t1 is of type Matrix4f and all of the data
+     * members of t1 are equal to the corresponding data members in this
+     * Matrix4f.
+     * 
+     * @param t1
+     *            the matrix with which the comparison is made.
+     * @return true or false
+     */
+    public boolean equals(Object t1) {
+        try {
+            Matrix4f m2 = (Matrix4f) t1;
+            return (this.m00 == m2.m00 && this.m01 == m2.m01
+                    && this.m02 == m2.m02 && this.m03 == m2.m03
+                    && this.m10 == m2.m10 && this.m11 == m2.m11
+                    && this.m12 == m2.m12 && this.m13 == m2.m13
+                    && this.m20 == m2.m20 && this.m21 == m2.m21
+                    && this.m22 == m2.m22 && this.m23 == m2.m23
+                    && this.m30 == m2.m30 && this.m31 == m2.m31
+                    && this.m32 == m2.m32 && this.m33 == m2.m33);
+        } catch (ClassCastException e1) {
+            return false;
+        } catch (NullPointerException e2) {
+            return false;
+        }
+    }
+
+    /**
+     * Performs an SVD normalization of this matrix in order to acquire the
+     * normalized rotational component; the values are placed into the Matrix3d
+     * parameter.
+     * 
+     * @param m1
+     *            matrix into which the rotational component is placed
+     */
+    public final void get(Matrix3d m1) {
+
+        double[] tmp_rot = new double[9]; // scratch matrix
+        double[] tmp_scale = new double[3]; // scratch matrix
+
+        getScaleRotate(tmp_scale, tmp_rot);
+
+        m1.m00 = tmp_rot[0];
+        m1.m01 = tmp_rot[1];
+        m1.m02 = tmp_rot[2];
+
+        m1.m10 = tmp_rot[3];
+        m1.m11 = tmp_rot[4];
+        m1.m12 = tmp_rot[5];
+
+        m1.m20 = tmp_rot[6];
+        m1.m21 = tmp_rot[7];
+        m1.m22 = tmp_rot[8];
+
+    }
+
+    /**
+     * Performs an SVD normalization of this matrix to calculate the rotation as
+     * a 3x3 matrix, the translation, and the scale. None of the matrix values
+     * are modified.
+     * 
+     * @param m1
+     *            the normalized matrix representing the rotation
+     * @param t1
+     *            the translation component
+     * @return the scale component of this transform
+     */
+    public final double get(Matrix3d m1, Vec3D t1) {
+        double[] tmp_rot = new double[9];
+        double[] tmp_scale = new double[3];
+        getScaleRotate(tmp_scale, tmp_rot);
+
+        m1.m00 = tmp_rot[0];
+        m1.m01 = tmp_rot[1];
+        m1.m02 = tmp_rot[2];
+
+        m1.m10 = tmp_rot[3];
+        m1.m11 = tmp_rot[4];
+        m1.m12 = tmp_rot[5];
+
+        m1.m20 = tmp_rot[6];
+        m1.m21 = tmp_rot[7];
+        m1.m22 = tmp_rot[8];
+
+        t1.x = m03;
+        t1.y = m13;
+        t1.z = m23;
+
+        return MathUtils.max(tmp_scale);
+
+    }
+
+    /**
+     * Performs an SVD normalization of this matrix in order to acquire the
+     * normalized rotational component; the values are placed into the Quat4f
+     * parameter.
+     * 
+     * @param q1
+     *            quaternion into which the rotation component is placed
+     */
+    public final void get(Quaternion q1) {
+        double[] tmp_rot = new double[9]; // scratch matrix
+        double[] tmp_scale = new double[3]; // scratch matrix
+        getScaleRotate(tmp_scale, tmp_rot);
+
+        double ww;
+
+        ww = 0.25 * (1.0 + tmp_rot[0] + tmp_rot[4] + tmp_rot[8]);
+        if (!((ww < 0 ? -ww : ww) < 1.0e-30)) {
+            q1.w = (float) Math.sqrt(ww);
+            ww = 0.25 / q1.w;
+            q1.x = (float) ((tmp_rot[7] - tmp_rot[5]) * ww);
+            q1.y = (float) ((tmp_rot[2] - tmp_rot[6]) * ww);
+            q1.z = (float) ((tmp_rot[3] - tmp_rot[1]) * ww);
+            return;
+        }
+
+        q1.w = 0.0f;
+        ww = -0.5 * (tmp_rot[4] + tmp_rot[8]);
+        if (!((ww < 0 ? -ww : ww) < 1.0e-30)) {
+            q1.x = (float) Math.sqrt(ww);
+            ww = 0.5 / q1.x;
+            q1.y = (float) (tmp_rot[3] * ww);
+            q1.z = (float) (tmp_rot[6] * ww);
+            return;
+        }
+
+        q1.x = 0.0f;
+        ww = 0.5 * (1.0 - tmp_rot[8]);
+        if (!((ww < 0 ? -ww : ww) < 1.0e-30)) {
+            q1.y = (float) (Math.sqrt(ww));
+            q1.z = (float) (tmp_rot[7] / (2.0 * q1.y));
+            return;
+        }
+
+        q1.y = 0.0f;
+        q1.z = 1.0f;
+
+    }
+
+    /**
+     * Retrieves the translational components of this matrix.
+     * 
+     * @param trans
+     *            the vector that will receive the translational component
+     */
+    public final void get(Vec3D trans) {
+        trans.x = m03;
+        trans.y = m13;
+        trans.z = m23;
+    }
+
+    /**
+     * Copies the matrix values in the specified column into the array
+     * parameter.
+     * 
+     * @param column
+     *            the matrix column
+     * @param v
+     *            the array into which the matrix row values will be copied
+     */
+    public final void getColumn(int column, float v[]) {
+        if (column == 0) {
+            v[0] = m00;
+            v[1] = m10;
+            v[2] = m20;
+            v[3] = m30;
+        } else if (column == 1) {
+            v[0] = m01;
+            v[1] = m11;
+            v[2] = m21;
+            v[3] = m31;
+        } else if (column == 2) {
+            v[0] = m02;
+            v[1] = m12;
+            v[2] = m22;
+            v[3] = m32;
+        } else if (column == 3) {
+            v[0] = m03;
+            v[1] = m13;
+            v[2] = m23;
+            v[3] = m33;
+        } else {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+
+    }
+
+    /**
+     * Copies the matrix values in the specified column into the vector
+     * parameter.
+     * 
+     * @param column
+     *            the matrix column
+     * @param v
+     *            the vector into which the matrix row values will be copied
+     */
+    public final void getColumn(int column, Vec4D v) {
+        if (column == 0) {
+            v.x = m00;
+            v.y = m10;
+            v.z = m20;
+            v.w = m30;
+        } else if (column == 1) {
+            v.x = m01;
+            v.y = m11;
+            v.z = m21;
+            v.w = m31;
+        } else if (column == 2) {
+            v.x = m02;
+            v.y = m12;
+            v.z = m22;
+            v.w = m32;
+        } else if (column == 3) {
+            v.x = m03;
+            v.y = m13;
+            v.z = m23;
+            v.w = m33;
+        } else {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+
+    }
+
+    /**
+     * Retrieves the value at the specified row and column of this matrix.
+     * 
+     * @param row
+     *            the row number to be retrieved (zero indexed)
+     * @param column
+     *            the column number to be retrieved (zero indexed)
+     * @return the value at the indexed element
+     */
+    public final float getElement(int row, int column) {
+        switch (row) {
+            case 0:
+                switch (column) {
+                    case 0:
+                        return (this.m00);
+                    case 1:
+                        return (this.m01);
+                    case 2:
+                        return (this.m02);
+                    case 3:
+                        return (this.m03);
+                    default:
+                        break;
+                }
+                break;
+            case 1:
+                switch (column) {
+                    case 0:
+                        return (this.m10);
+                    case 1:
+                        return (this.m11);
+                    case 2:
+                        return (this.m12);
+                    case 3:
+                        return (this.m13);
+                    default:
+                        break;
+                }
+                break;
+
+            case 2:
+                switch (column) {
+                    case 0:
+                        return (this.m20);
+                    case 1:
+                        return (this.m21);
+                    case 2:
+                        return (this.m22);
+                    case 3:
+                        return (this.m23);
+                    default:
+                        break;
+                }
+                break;
+
+            case 3:
+                switch (column) {
+                    case 0:
+                        return (this.m30);
+                    case 1:
+                        return (this.m31);
+                    case 2:
+                        return (this.m32);
+                    case 3:
+                        return (this.m33);
+                    default:
+                        break;
+                }
+                break;
+
+            default:
+                break;
+        }
+        throw new ArrayIndexOutOfBoundsException();
+    }
+
+    /**
+     * Get the first matrix element in the first row.
+     * 
+     * @return Returns the m00.
+     * 
+     * @since vecmath 1.5
+     */
+    public final float getM00() {
+        return m00;
+    }
+
+    /**
+     * Get the second matrix element in the first row.
+     * 
+     * @return Returns the m01.
+     * 
+     * @since vecmath 1.5
+     */
+    public final float getM01() {
+        return m01;
+    }
+
+    /**
+     * Get the third matrix element in the first row.
+     * 
+     * @return Returns the m02.
+     * 
+     * @since vecmath 1.5
+     */
+    public final float getM02() {
+        return m02;
+    }
+
+    /**
+     * Get the fourth element of the first row.
+     * 
+     * @return Returns the m03.
+     * 
+     * @since vecmath 1.5
+     */
+    public final float getM03() {
+        return m03;
+    }
+
+    /**
+     * Get first matrix element in the second row.
+     * 
+     * @return Returns the m10.
+     * 
+     * @since vecmath 1.5
+     */
+    public final float getM10() {
+        return m10;
+    }
+
+    /**
+     * Get second matrix element in the second row.
+     * 
+     * @return Returns the m11.
+     * 
+     * @since vecmath 1.5
+     */
+    public final float getM11() {
+        return m11;
+    }
+
+    /**
+     * Get the third matrix element in the second row.
+     * 
+     * @return Returns the m12.
+     * 
+     * @since vecmath 1.5
+     */
+    public final float getM12() {
+        return m12;
+    }
+
+    /**
+     * Get the fourth element of the second row.
+     * 
+     * @return Returns the m13.
+     * 
+     * @since vecmath 1.5
+     */
+    public final float getM13() {
+        return m13;
+    }
+
+    /**
+     * Get the first matrix element in the third row.
+     * 
+     * @return Returns the m20.
+     * 
+     * @since vecmath 1.5
+     */
+    public final float getM20() {
+        return m20;
+    }
+
+    /**
+     * Get the second matrix element in the third row.
+     * 
+     * @return Returns the m21.
+     * 
+     * @since vecmath 1.5
+     */
+    public final float getM21() {
+        return m21;
+    }
+
+    /**
+     * Get the third matrix element in the third row.
+     * 
+     * @return Returns the m22.
+     * 
+     * @since vecmath 1.5
+     */
+    public final float getM22() {
+        return m22;
+    }
+
+    /**
+     * Get the fourth element of the third row.
+     * 
+     * @return Returns the m23.
+     * 
+     * @since vecmath 1.5
+     */
+    public final float getM23() {
+        return m23;
+    }
+
+    /**
+     * Get the first element of the fourth row.
+     * 
+     * @return Returns the m30.
+     * 
+     * @since vecmath 1.5
+     */
+    public final float getM30() {
+        return m30;
+    }
+
+    /**
+     * Get the second element of the fourth row.
+     * 
+     * @return Returns the m31.
+     * 
+     * @since vecmath 1.5
+     */
+    public final float getM31() {
+        return m31;
+    }
+
+    /**
+     * Get the third element of the fourth row.
+     * 
+     * @return Returns the m32.
+     * 
+     * @since vecmath 1.5
+     */
+    public final float getM32() {
+        return m32;
+    }
+
+    /**
+     * Get the fourth element of the fourth row.
+     * 
+     * @return Returns the m33.
+     * 
+     * @since vecmath 1.5
+     */
+    public final float getM33() {
+        return m33;
+    }
+
+    /**
+     * Gets the upper 3x3 values of this matrix and places them into the matrix
+     * m1.
+     * 
+     * @param m1
+     *            the matrix that will hold the values
+     */
+    public final void getRotationScale(Matrix3d m1) {
+        m1.m00 = m00;
+        m1.m01 = m01;
+        m1.m02 = m02;
+        m1.m10 = m10;
+        m1.m11 = m11;
+        m1.m12 = m12;
+        m1.m20 = m20;
+        m1.m21 = m21;
+        m1.m22 = m22;
+    }
+
+    /**
+     * Copies the matrix values in the specified row into the array parameter.
+     * 
+     * @param row
+     *            the matrix row
+     * @param v
+     *            the array into which the matrix row values will be copied
+     */
+    public final void getRow(int row, float v[]) {
+        if (row == 0) {
+            v[0] = m00;
+            v[1] = m01;
+            v[2] = m02;
+            v[3] = m03;
+        } else if (row == 1) {
+            v[0] = m10;
+            v[1] = m11;
+            v[2] = m12;
+            v[3] = m13;
+        } else if (row == 2) {
+            v[0] = m20;
+            v[1] = m21;
+            v[2] = m22;
+            v[3] = m23;
+        } else if (row == 3) {
+            v[0] = m30;
+            v[1] = m31;
+            v[2] = m32;
+            v[3] = m33;
+        } else {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+
+    }
+
+    /**
+     * Copies the matrix values in the specified row into the vector parameter.
+     * 
+     * @param row
+     *            the matrix row
+     * @param v
+     *            the vector into which the matrix row values will be copied
+     */
+    public final void getRow(int row, Vec4D v) {
+        if (row == 0) {
+            v.x = m00;
+            v.y = m01;
+            v.z = m02;
+            v.w = m03;
+        } else if (row == 1) {
+            v.x = m10;
+            v.y = m11;
+            v.z = m12;
+            v.w = m13;
+        } else if (row == 2) {
+            v.x = m20;
+            v.y = m21;
+            v.z = m22;
+            v.w = m23;
+        } else if (row == 3) {
+            v.x = m30;
+            v.y = m31;
+            v.z = m32;
+            v.w = m33;
+        } else {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+
+    }
+
+    /**
+     * Performs an SVD normalization of this matrix to calculate and return the
+     * uniform scale factor. If the matrix has non-uniform scale factors, the
+     * largest of the x, y, and z scale factors will be returned. This matrix is
+     * not modified.
+     * 
+     * @return the scale factor of this matrix
+     */
+    public final float getScale() {
+        double[] tmp_scale = new double[3];
+        getScaleRotate(tmp_scale, new double[9]);
+        return ((float) MathUtils.max(tmp_scale));
+    }
+
+    private final void getScaleRotate(double scales[], double rots[]) {
+
+        double[] tmp = new double[9]; // scratch matrix
+        tmp[0] = m00;
+        tmp[1] = m01;
+        tmp[2] = m02;
+
+        tmp[3] = m10;
+        tmp[4] = m11;
+        tmp[5] = m12;
+
+        tmp[6] = m20;
+        tmp[7] = m21;
+        tmp[8] = m22;
+
+        Matrix3d.compute_svd(tmp, scales, rots);
+    }
+
+    /**
+     * Returns a hash code value based on the data values in this object. Two
+     * different Matrix4f objects with identical data values (i.e.,
+     * Matrix4f.equals returns true) will return the same hash code value. Two
+     * objects with different data members may return the same hash value,
+     * although this is not likely.
+     * 
+     * @return the integer hash code value
+     */
+    public int hashCode() {
+        long bits = 1L;
+        bits = 31L * bits + VecMathUtil.floatToIntBits(m00);
+        bits = 31L * bits + VecMathUtil.floatToIntBits(m01);
+        bits = 31L * bits + VecMathUtil.floatToIntBits(m02);
+        bits = 31L * bits + VecMathUtil.floatToIntBits(m03);
+        bits = 31L * bits + VecMathUtil.floatToIntBits(m10);
+        bits = 31L * bits + VecMathUtil.floatToIntBits(m11);
+        bits = 31L * bits + VecMathUtil.floatToIntBits(m12);
+        bits = 31L * bits + VecMathUtil.floatToIntBits(m13);
+        bits = 31L * bits + VecMathUtil.floatToIntBits(m20);
+        bits = 31L * bits + VecMathUtil.floatToIntBits(m21);
+        bits = 31L * bits + VecMathUtil.floatToIntBits(m22);
+        bits = 31L * bits + VecMathUtil.floatToIntBits(m23);
+        bits = 31L * bits + VecMathUtil.floatToIntBits(m30);
+        bits = 31L * bits + VecMathUtil.floatToIntBits(m31);
+        bits = 31L * bits + VecMathUtil.floatToIntBits(m32);
+        bits = 31L * bits + VecMathUtil.floatToIntBits(m33);
+        return (int) (bits ^ (bits >> 32));
+    }
+
+    /**
+     * Inverts this matrix in place.
+     */
+    public final void invert() {
+        invertGeneral(this);
+    }
+
+    /**
+     * Sets the value of this matrix to the matrix inverse of the passed (user
+     * declared) matrix m1.
+     * 
+     * @param m1
+     *            the matrix to be inverted
+     */
+    public final void invert(Matrix4f m1) {
+
+        invertGeneral(m1);
+    }
+
+    /**
+     * General invert routine. Inverts m1 and places the result in "this". Note
+     * that this routine handles both the "this" version and the non-"this"
+     * version.
+     * 
+     * Also note that since this routine is slow anyway, we won't worry about
+     * allocating a little bit of garbage.
+     */
+    final void invertGeneral(Matrix4f m1) {
+        double temp[] = new double[16];
+        double result[] = new double[16];
+        int row_perm[] = new int[4];
+        int i, r, c;
+
+        // Use LU decomposition and backsubstitution code specifically
+        // for floating-point 4x4 matrices.
+
+        // Copy source matrix to t1tmp
+        temp[0] = m1.m00;
+        temp[1] = m1.m01;
+        temp[2] = m1.m02;
+        temp[3] = m1.m03;
+
+        temp[4] = m1.m10;
+        temp[5] = m1.m11;
+        temp[6] = m1.m12;
+        temp[7] = m1.m13;
+
+        temp[8] = m1.m20;
+        temp[9] = m1.m21;
+        temp[10] = m1.m22;
+        temp[11] = m1.m23;
+
+        temp[12] = m1.m30;
+        temp[13] = m1.m31;
+        temp[14] = m1.m32;
+        temp[15] = m1.m33;
+
+        // Calculate LU decomposition: Is the matrix singular?
+        if (!Matrix4x4.LUDecomposition(temp, row_perm, 4)) {
+            // Matrix has no inverse
+            throw new SingularMatrixException();
+        }
+
+        // Perform back substitution on the identity matrix
+        for (i = 0; i < 16; i++) {
+            result[i] = 0.0;
+        }
+        result[0] = 1.0;
+        result[5] = 1.0;
+        result[10] = 1.0;
+        result[15] = 1.0;
+        luBacksubstitution(temp, row_perm, result);
+
+        this.m00 = (float) result[0];
+        this.m01 = (float) result[1];
+        this.m02 = (float) result[2];
+        this.m03 = (float) result[3];
+
+        this.m10 = (float) result[4];
+        this.m11 = (float) result[5];
+        this.m12 = (float) result[6];
+        this.m13 = (float) result[7];
+
+        this.m20 = (float) result[8];
+        this.m21 = (float) result[9];
+        this.m22 = (float) result[10];
+        this.m23 = (float) result[11];
+
+        this.m30 = (float) result[12];
+        this.m31 = (float) result[13];
+        this.m32 = (float) result[14];
+        this.m33 = (float) result[15];
+
+    }
+
+    /**
+     * Multiplies each element of this matrix by a scalar.
+     * 
+     * @param scalar
+     *            the scalar multiplier.
+     */
+    public final void mul(float scalar) {
+        m00 *= scalar;
+        m01 *= scalar;
+        m02 *= scalar;
+        m03 *= scalar;
+        m10 *= scalar;
+        m11 *= scalar;
+        m12 *= scalar;
+        m13 *= scalar;
+        m20 *= scalar;
+        m21 *= scalar;
+        m22 *= scalar;
+        m23 *= scalar;
+        m30 *= scalar;
+        m31 *= scalar;
+        m32 *= scalar;
+        m33 *= scalar;
+    }
+
+    /**
+     * Multiplies each element of matrix m1 by a scalar and places the result
+     * into this. Matrix m1 is not modified.
+     * 
+     * @param scalar
+     *            the scalar multiplier.
+     * @param m1
+     *            the original matrix.
+     */
+    public final void mul(float scalar, Matrix4f m1) {
+        this.m00 = m1.m00 * scalar;
+        this.m01 = m1.m01 * scalar;
+        this.m02 = m1.m02 * scalar;
+        this.m03 = m1.m03 * scalar;
+        this.m10 = m1.m10 * scalar;
+        this.m11 = m1.m11 * scalar;
+        this.m12 = m1.m12 * scalar;
+        this.m13 = m1.m13 * scalar;
+        this.m20 = m1.m20 * scalar;
+        this.m21 = m1.m21 * scalar;
+        this.m22 = m1.m22 * scalar;
+        this.m23 = m1.m23 * scalar;
+        this.m30 = m1.m30 * scalar;
+        this.m31 = m1.m31 * scalar;
+        this.m32 = m1.m32 * scalar;
+        this.m33 = m1.m33 * scalar;
+    }
+
+    /**
+     * Sets the value of this matrix to the result of multiplying itself with
+     * matrix m1.
+     * 
+     * @param m1
+     *            the other matrix
+     */
+    public final void mul(Matrix4f m1) {
+        float m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33; // vars
+                                                                                              // for
+                                                                                              // temp
+                                                                                              // result
+                                                                                              // matrix
+
+        m00 = this.m00 * m1.m00 + this.m01 * m1.m10 + this.m02 * m1.m20
+                + this.m03 * m1.m30;
+        m01 = this.m00 * m1.m01 + this.m01 * m1.m11 + this.m02 * m1.m21
+                + this.m03 * m1.m31;
+        m02 = this.m00 * m1.m02 + this.m01 * m1.m12 + this.m02 * m1.m22
+                + this.m03 * m1.m32;
+        m03 = this.m00 * m1.m03 + this.m01 * m1.m13 + this.m02 * m1.m23
+                + this.m03 * m1.m33;
+
+        m10 = this.m10 * m1.m00 + this.m11 * m1.m10 + this.m12 * m1.m20
+                + this.m13 * m1.m30;
+        m11 = this.m10 * m1.m01 + this.m11 * m1.m11 + this.m12 * m1.m21
+                + this.m13 * m1.m31;
+        m12 = this.m10 * m1.m02 + this.m11 * m1.m12 + this.m12 * m1.m22
+                + this.m13 * m1.m32;
+        m13 = this.m10 * m1.m03 + this.m11 * m1.m13 + this.m12 * m1.m23
+                + this.m13 * m1.m33;
+
+        m20 = this.m20 * m1.m00 + this.m21 * m1.m10 + this.m22 * m1.m20
+                + this.m23 * m1.m30;
+        m21 = this.m20 * m1.m01 + this.m21 * m1.m11 + this.m22 * m1.m21
+                + this.m23 * m1.m31;
+        m22 = this.m20 * m1.m02 + this.m21 * m1.m12 + this.m22 * m1.m22
+                + this.m23 * m1.m32;
+        m23 = this.m20 * m1.m03 + this.m21 * m1.m13 + this.m22 * m1.m23
+                + this.m23 * m1.m33;
+
+        m30 = this.m30 * m1.m00 + this.m31 * m1.m10 + this.m32 * m1.m20
+                + this.m33 * m1.m30;
+        m31 = this.m30 * m1.m01 + this.m31 * m1.m11 + this.m32 * m1.m21
+                + this.m33 * m1.m31;
+        m32 = this.m30 * m1.m02 + this.m31 * m1.m12 + this.m32 * m1.m22
+                + this.m33 * m1.m32;
+        m33 = this.m30 * m1.m03 + this.m31 * m1.m13 + this.m32 * m1.m23
+                + this.m33 * m1.m33;
+
+        this.m00 = m00;
+        this.m01 = m01;
+        this.m02 = m02;
+        this.m03 = m03;
+        this.m10 = m10;
+        this.m11 = m11;
+        this.m12 = m12;
+        this.m13 = m13;
+        this.m20 = m20;
+        this.m21 = m21;
+        this.m22 = m22;
+        this.m23 = m23;
+        this.m30 = m30;
+        this.m31 = m31;
+        this.m32 = m32;
+        this.m33 = m33;
+    }
+
+    /**
+     * Sets the value of this matrix to the result of multiplying the two
+     * argument matrices together.
+     * 
+     * @param m1
+     *            the first matrix
+     * @param m2
+     *            the second matrix
+     */
+    public final void mul(Matrix4f m1, Matrix4f m2) {
+        if (this != m1 && this != m2) {
+
+            this.m00 = m1.m00 * m2.m00 + m1.m01 * m2.m10 + m1.m02 * m2.m20
+                    + m1.m03 * m2.m30;
+            this.m01 = m1.m00 * m2.m01 + m1.m01 * m2.m11 + m1.m02 * m2.m21
+                    + m1.m03 * m2.m31;
+            this.m02 = m1.m00 * m2.m02 + m1.m01 * m2.m12 + m1.m02 * m2.m22
+                    + m1.m03 * m2.m32;
+            this.m03 = m1.m00 * m2.m03 + m1.m01 * m2.m13 + m1.m02 * m2.m23
+                    + m1.m03 * m2.m33;
+
+            this.m10 = m1.m10 * m2.m00 + m1.m11 * m2.m10 + m1.m12 * m2.m20
+                    + m1.m13 * m2.m30;
+            this.m11 = m1.m10 * m2.m01 + m1.m11 * m2.m11 + m1.m12 * m2.m21
+                    + m1.m13 * m2.m31;
+            this.m12 = m1.m10 * m2.m02 + m1.m11 * m2.m12 + m1.m12 * m2.m22
+                    + m1.m13 * m2.m32;
+            this.m13 = m1.m10 * m2.m03 + m1.m11 * m2.m13 + m1.m12 * m2.m23
+                    + m1.m13 * m2.m33;
+
+            this.m20 = m1.m20 * m2.m00 + m1.m21 * m2.m10 + m1.m22 * m2.m20
+                    + m1.m23 * m2.m30;
+            this.m21 = m1.m20 * m2.m01 + m1.m21 * m2.m11 + m1.m22 * m2.m21
+                    + m1.m23 * m2.m31;
+            this.m22 = m1.m20 * m2.m02 + m1.m21 * m2.m12 + m1.m22 * m2.m22
+                    + m1.m23 * m2.m32;
+            this.m23 = m1.m20 * m2.m03 + m1.m21 * m2.m13 + m1.m22 * m2.m23
+                    + m1.m23 * m2.m33;
+
+            this.m30 = m1.m30 * m2.m00 + m1.m31 * m2.m10 + m1.m32 * m2.m20
+                    + m1.m33 * m2.m30;
+            this.m31 = m1.m30 * m2.m01 + m1.m31 * m2.m11 + m1.m32 * m2.m21
+                    + m1.m33 * m2.m31;
+            this.m32 = m1.m30 * m2.m02 + m1.m31 * m2.m12 + m1.m32 * m2.m22
+                    + m1.m33 * m2.m32;
+            this.m33 = m1.m30 * m2.m03 + m1.m31 * m2.m13 + m1.m32 * m2.m23
+                    + m1.m33 * m2.m33;
+        } else {
+            float m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33; // vars
+                                                                                                  // for
+                                                                                                  // temp
+                                                                                                  // result
+                                                                                                  // matrix
+            m00 = m1.m00 * m2.m00 + m1.m01 * m2.m10 + m1.m02 * m2.m20 + m1.m03
+                    * m2.m30;
+            m01 = m1.m00 * m2.m01 + m1.m01 * m2.m11 + m1.m02 * m2.m21 + m1.m03
+                    * m2.m31;
+            m02 = m1.m00 * m2.m02 + m1.m01 * m2.m12 + m1.m02 * m2.m22 + m1.m03
+                    * m2.m32;
+            m03 = m1.m00 * m2.m03 + m1.m01 * m2.m13 + m1.m02 * m2.m23 + m1.m03
+                    * m2.m33;
+
+            m10 = m1.m10 * m2.m00 + m1.m11 * m2.m10 + m1.m12 * m2.m20 + m1.m13
+                    * m2.m30;
+            m11 = m1.m10 * m2.m01 + m1.m11 * m2.m11 + m1.m12 * m2.m21 + m1.m13
+                    * m2.m31;
+            m12 = m1.m10 * m2.m02 + m1.m11 * m2.m12 + m1.m12 * m2.m22 + m1.m13
+                    * m2.m32;
+            m13 = m1.m10 * m2.m03 + m1.m11 * m2.m13 + m1.m12 * m2.m23 + m1.m13
+                    * m2.m33;
+
+            m20 = m1.m20 * m2.m00 + m1.m21 * m2.m10 + m1.m22 * m2.m20 + m1.m23
+                    * m2.m30;
+            m21 = m1.m20 * m2.m01 + m1.m21 * m2.m11 + m1.m22 * m2.m21 + m1.m23
+                    * m2.m31;
+            m22 = m1.m20 * m2.m02 + m1.m21 * m2.m12 + m1.m22 * m2.m22 + m1.m23
+                    * m2.m32;
+            m23 = m1.m20 * m2.m03 + m1.m21 * m2.m13 + m1.m22 * m2.m23 + m1.m23
+                    * m2.m33;
+
+            m30 = m1.m30 * m2.m00 + m1.m31 * m2.m10 + m1.m32 * m2.m20 + m1.m33
+                    * m2.m30;
+            m31 = m1.m30 * m2.m01 + m1.m31 * m2.m11 + m1.m32 * m2.m21 + m1.m33
+                    * m2.m31;
+            m32 = m1.m30 * m2.m02 + m1.m31 * m2.m12 + m1.m32 * m2.m22 + m1.m33
+                    * m2.m32;
+            m33 = m1.m30 * m2.m03 + m1.m31 * m2.m13 + m1.m32 * m2.m23 + m1.m33
+                    * m2.m33;
+
+            this.m00 = m00;
+            this.m01 = m01;
+            this.m02 = m02;
+            this.m03 = m03;
+            this.m10 = m10;
+            this.m11 = m11;
+            this.m12 = m12;
+            this.m13 = m13;
+            this.m20 = m20;
+            this.m21 = m21;
+            this.m22 = m22;
+            this.m23 = m23;
+            this.m30 = m30;
+            this.m31 = m31;
+            this.m32 = m32;
+            this.m33 = m33;
+        }
+    }
+
+    /**
+     * Multiplies the transpose of matrix m1 times the transpose of matrix m2,
+     * and places the result into this.
+     * 
+     * @param m1
+     *            the matrix on the left hand side of the multiplication
+     * @param m2
+     *            the matrix on the right hand side of the multiplication
+     */
+    public final void mulTransposeBoth(Matrix4f m1, Matrix4f m2) {
+        if (this != m1 && this != m2) {
+            this.m00 = m1.m00 * m2.m00 + m1.m10 * m2.m01 + m1.m20 * m2.m02
+                    + m1.m30 * m2.m03;
+            this.m01 = m1.m00 * m2.m10 + m1.m10 * m2.m11 + m1.m20 * m2.m12
+                    + m1.m30 * m2.m13;
+            this.m02 = m1.m00 * m2.m20 + m1.m10 * m2.m21 + m1.m20 * m2.m22
+                    + m1.m30 * m2.m23;
+            this.m03 = m1.m00 * m2.m30 + m1.m10 * m2.m31 + m1.m20 * m2.m32
+                    + m1.m30 * m2.m33;
+
+            this.m10 = m1.m01 * m2.m00 + m1.m11 * m2.m01 + m1.m21 * m2.m02
+                    + m1.m31 * m2.m03;
+            this.m11 = m1.m01 * m2.m10 + m1.m11 * m2.m11 + m1.m21 * m2.m12
+                    + m1.m31 * m2.m13;
+            this.m12 = m1.m01 * m2.m20 + m1.m11 * m2.m21 + m1.m21 * m2.m22
+                    + m1.m31 * m2.m23;
+            this.m13 = m1.m01 * m2.m30 + m1.m11 * m2.m31 + m1.m21 * m2.m32
+                    + m1.m31 * m2.m33;
+
+            this.m20 = m1.m02 * m2.m00 + m1.m12 * m2.m01 + m1.m22 * m2.m02
+                    + m1.m32 * m2.m03;
+            this.m21 = m1.m02 * m2.m10 + m1.m12 * m2.m11 + m1.m22 * m2.m12
+                    + m1.m32 * m2.m13;
+            this.m22 = m1.m02 * m2.m20 + m1.m12 * m2.m21 + m1.m22 * m2.m22
+                    + m1.m32 * m2.m23;
+            this.m23 = m1.m02 * m2.m30 + m1.m12 * m2.m31 + m1.m22 * m2.m32
+                    + m1.m32 * m2.m33;
+
+            this.m30 = m1.m03 * m2.m00 + m1.m13 * m2.m01 + m1.m23 * m2.m02
+                    + m1.m33 * m2.m03;
+            this.m31 = m1.m03 * m2.m10 + m1.m13 * m2.m11 + m1.m23 * m2.m12
+                    + m1.m33 * m2.m13;
+            this.m32 = m1.m03 * m2.m20 + m1.m13 * m2.m21 + m1.m23 * m2.m22
+                    + m1.m33 * m2.m23;
+            this.m33 = m1.m03 * m2.m30 + m1.m13 * m2.m31 + m1.m23 * m2.m32
+                    + m1.m33 * m2.m33;
+        } else {
+            float m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, // vars
+                                                                              // for
+                                                                              // temp
+                                                                              // result
+                                                                              // matrix
+            m30, m31, m32, m33;
+
+            m00 = m1.m00 * m2.m00 + m1.m10 * m2.m01 + m1.m20 * m2.m02 + m1.m30
+                    * m2.m03;
+            m01 = m1.m00 * m2.m10 + m1.m10 * m2.m11 + m1.m20 * m2.m12 + m1.m30
+                    * m2.m13;
+            m02 = m1.m00 * m2.m20 + m1.m10 * m2.m21 + m1.m20 * m2.m22 + m1.m30
+                    * m2.m23;
+            m03 = m1.m00 * m2.m30 + m1.m10 * m2.m31 + m1.m20 * m2.m32 + m1.m30
+                    * m2.m33;
+
+            m10 = m1.m01 * m2.m00 + m1.m11 * m2.m01 + m1.m21 * m2.m02 + m1.m31
+                    * m2.m03;
+            m11 = m1.m01 * m2.m10 + m1.m11 * m2.m11 + m1.m21 * m2.m12 + m1.m31
+                    * m2.m13;
+            m12 = m1.m01 * m2.m20 + m1.m11 * m2.m21 + m1.m21 * m2.m22 + m1.m31
+                    * m2.m23;
+            m13 = m1.m01 * m2.m30 + m1.m11 * m2.m31 + m1.m21 * m2.m32 + m1.m31
+                    * m2.m33;
+
+            m20 = m1.m02 * m2.m00 + m1.m12 * m2.m01 + m1.m22 * m2.m02 + m1.m32
+                    * m2.m03;
+            m21 = m1.m02 * m2.m10 + m1.m12 * m2.m11 + m1.m22 * m2.m12 + m1.m32
+                    * m2.m13;
+            m22 = m1.m02 * m2.m20 + m1.m12 * m2.m21 + m1.m22 * m2.m22 + m1.m32
+                    * m2.m23;
+            m23 = m1.m02 * m2.m30 + m1.m12 * m2.m31 + m1.m22 * m2.m32 + m1.m32
+                    * m2.m33;
+
+            m30 = m1.m03 * m2.m00 + m1.m13 * m2.m01 + m1.m23 * m2.m02 + m1.m33
+                    * m2.m03;
+            m31 = m1.m03 * m2.m10 + m1.m13 * m2.m11 + m1.m23 * m2.m12 + m1.m33
+                    * m2.m13;
+            m32 = m1.m03 * m2.m20 + m1.m13 * m2.m21 + m1.m23 * m2.m22 + m1.m33
+                    * m2.m23;
+            m33 = m1.m03 * m2.m30 + m1.m13 * m2.m31 + m1.m23 * m2.m32 + m1.m33
+                    * m2.m33;
+
+            this.m00 = m00;
+            this.m01 = m01;
+            this.m02 = m02;
+            this.m03 = m03;
+            this.m10 = m10;
+            this.m11 = m11;
+            this.m12 = m12;
+            this.m13 = m13;
+            this.m20 = m20;
+            this.m21 = m21;
+            this.m22 = m22;
+            this.m23 = m23;
+            this.m30 = m30;
+            this.m31 = m31;
+            this.m32 = m32;
+            this.m33 = m33;
+        }
+
+    }
+
+    /**
+     * Multiplies the transpose of matrix m1 times matrix m2, and places the
+     * result into this.
+     * 
+     * @param m1
+     *            the matrix on the left hand side of the multiplication
+     * @param m2
+     *            the matrix on the right hand side of the multiplication
+     */
+    public final void mulTransposeLeft(Matrix4f m1, Matrix4f m2) {
+        if (this != m1 && this != m2) {
+            this.m00 = m1.m00 * m2.m00 + m1.m10 * m2.m10 + m1.m20 * m2.m20
+                    + m1.m30 * m2.m30;
+            this.m01 = m1.m00 * m2.m01 + m1.m10 * m2.m11 + m1.m20 * m2.m21
+                    + m1.m30 * m2.m31;
+            this.m02 = m1.m00 * m2.m02 + m1.m10 * m2.m12 + m1.m20 * m2.m22
+                    + m1.m30 * m2.m32;
+            this.m03 = m1.m00 * m2.m03 + m1.m10 * m2.m13 + m1.m20 * m2.m23
+                    + m1.m30 * m2.m33;
+
+            this.m10 = m1.m01 * m2.m00 + m1.m11 * m2.m10 + m1.m21 * m2.m20
+                    + m1.m31 * m2.m30;
+            this.m11 = m1.m01 * m2.m01 + m1.m11 * m2.m11 + m1.m21 * m2.m21
+                    + m1.m31 * m2.m31;
+            this.m12 = m1.m01 * m2.m02 + m1.m11 * m2.m12 + m1.m21 * m2.m22
+                    + m1.m31 * m2.m32;
+            this.m13 = m1.m01 * m2.m03 + m1.m11 * m2.m13 + m1.m21 * m2.m23
+                    + m1.m31 * m2.m33;
+
+            this.m20 = m1.m02 * m2.m00 + m1.m12 * m2.m10 + m1.m22 * m2.m20
+                    + m1.m32 * m2.m30;
+            this.m21 = m1.m02 * m2.m01 + m1.m12 * m2.m11 + m1.m22 * m2.m21
+                    + m1.m32 * m2.m31;
+            this.m22 = m1.m02 * m2.m02 + m1.m12 * m2.m12 + m1.m22 * m2.m22
+                    + m1.m32 * m2.m32;
+            this.m23 = m1.m02 * m2.m03 + m1.m12 * m2.m13 + m1.m22 * m2.m23
+                    + m1.m32 * m2.m33;
+
+            this.m30 = m1.m03 * m2.m00 + m1.m13 * m2.m10 + m1.m23 * m2.m20
+                    + m1.m33 * m2.m30;
+            this.m31 = m1.m03 * m2.m01 + m1.m13 * m2.m11 + m1.m23 * m2.m21
+                    + m1.m33 * m2.m31;
+            this.m32 = m1.m03 * m2.m02 + m1.m13 * m2.m12 + m1.m23 * m2.m22
+                    + m1.m33 * m2.m32;
+            this.m33 = m1.m03 * m2.m03 + m1.m13 * m2.m13 + m1.m23 * m2.m23
+                    + m1.m33 * m2.m33;
+        } else {
+            float m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, // vars
+                                                                              // for
+                                                                              // temp
+                                                                              // result
+                                                                              // matrix
+            m30, m31, m32, m33;
+
+            m00 = m1.m00 * m2.m00 + m1.m10 * m2.m10 + m1.m20 * m2.m20 + m1.m30
+                    * m2.m30;
+            m01 = m1.m00 * m2.m01 + m1.m10 * m2.m11 + m1.m20 * m2.m21 + m1.m30
+                    * m2.m31;
+            m02 = m1.m00 * m2.m02 + m1.m10 * m2.m12 + m1.m20 * m2.m22 + m1.m30
+                    * m2.m32;
+            m03 = m1.m00 * m2.m03 + m1.m10 * m2.m13 + m1.m20 * m2.m23 + m1.m30
+                    * m2.m33;
+
+            m10 = m1.m01 * m2.m00 + m1.m11 * m2.m10 + m1.m21 * m2.m20 + m1.m31
+                    * m2.m30;
+            m11 = m1.m01 * m2.m01 + m1.m11 * m2.m11 + m1.m21 * m2.m21 + m1.m31
+                    * m2.m31;
+            m12 = m1.m01 * m2.m02 + m1.m11 * m2.m12 + m1.m21 * m2.m22 + m1.m31
+                    * m2.m32;
+            m13 = m1.m01 * m2.m03 + m1.m11 * m2.m13 + m1.m21 * m2.m23 + m1.m31
+                    * m2.m33;
+
+            m20 = m1.m02 * m2.m00 + m1.m12 * m2.m10 + m1.m22 * m2.m20 + m1.m32
+                    * m2.m30;
+            m21 = m1.m02 * m2.m01 + m1.m12 * m2.m11 + m1.m22 * m2.m21 + m1.m32
+                    * m2.m31;
+            m22 = m1.m02 * m2.m02 + m1.m12 * m2.m12 + m1.m22 * m2.m22 + m1.m32
+                    * m2.m32;
+            m23 = m1.m02 * m2.m03 + m1.m12 * m2.m13 + m1.m22 * m2.m23 + m1.m32
+                    * m2.m33;
+
+            m30 = m1.m03 * m2.m00 + m1.m13 * m2.m10 + m1.m23 * m2.m20 + m1.m33
+                    * m2.m30;
+            m31 = m1.m03 * m2.m01 + m1.m13 * m2.m11 + m1.m23 * m2.m21 + m1.m33
+                    * m2.m31;
+            m32 = m1.m03 * m2.m02 + m1.m13 * m2.m12 + m1.m23 * m2.m22 + m1.m33
+                    * m2.m32;
+            m33 = m1.m03 * m2.m03 + m1.m13 * m2.m13 + m1.m23 * m2.m23 + m1.m33
+                    * m2.m33;
+
+            this.m00 = m00;
+            this.m01 = m01;
+            this.m02 = m02;
+            this.m03 = m03;
+            this.m10 = m10;
+            this.m11 = m11;
+            this.m12 = m12;
+            this.m13 = m13;
+            this.m20 = m20;
+            this.m21 = m21;
+            this.m22 = m22;
+            this.m23 = m23;
+            this.m30 = m30;
+            this.m31 = m31;
+            this.m32 = m32;
+            this.m33 = m33;
+        }
+
+    }
+
+    /**
+     * Multiplies matrix m1 times the transpose of matrix m2, and places the
+     * result into this.
+     * 
+     * @param m1
+     *            the matrix on the left hand side of the multiplication
+     * @param m2
+     *            the matrix on the right hand side of the multiplication
+     */
+    public final void mulTransposeRight(Matrix4f m1, Matrix4f m2) {
+        if (this != m1 && this != m2) {
+            this.m00 = m1.m00 * m2.m00 + m1.m01 * m2.m01 + m1.m02 * m2.m02
+                    + m1.m03 * m2.m03;
+            this.m01 = m1.m00 * m2.m10 + m1.m01 * m2.m11 + m1.m02 * m2.m12
+                    + m1.m03 * m2.m13;
+            this.m02 = m1.m00 * m2.m20 + m1.m01 * m2.m21 + m1.m02 * m2.m22
+                    + m1.m03 * m2.m23;
+            this.m03 = m1.m00 * m2.m30 + m1.m01 * m2.m31 + m1.m02 * m2.m32
+                    + m1.m03 * m2.m33;
+
+            this.m10 = m1.m10 * m2.m00 + m1.m11 * m2.m01 + m1.m12 * m2.m02
+                    + m1.m13 * m2.m03;
+            this.m11 = m1.m10 * m2.m10 + m1.m11 * m2.m11 + m1.m12 * m2.m12
+                    + m1.m13 * m2.m13;
+            this.m12 = m1.m10 * m2.m20 + m1.m11 * m2.m21 + m1.m12 * m2.m22
+                    + m1.m13 * m2.m23;
+            this.m13 = m1.m10 * m2.m30 + m1.m11 * m2.m31 + m1.m12 * m2.m32
+                    + m1.m13 * m2.m33;
+
+            this.m20 = m1.m20 * m2.m00 + m1.m21 * m2.m01 + m1.m22 * m2.m02
+                    + m1.m23 * m2.m03;
+            this.m21 = m1.m20 * m2.m10 + m1.m21 * m2.m11 + m1.m22 * m2.m12
+                    + m1.m23 * m2.m13;
+            this.m22 = m1.m20 * m2.m20 + m1.m21 * m2.m21 + m1.m22 * m2.m22
+                    + m1.m23 * m2.m23;
+            this.m23 = m1.m20 * m2.m30 + m1.m21 * m2.m31 + m1.m22 * m2.m32
+                    + m1.m23 * m2.m33;
+
+            this.m30 = m1.m30 * m2.m00 + m1.m31 * m2.m01 + m1.m32 * m2.m02
+                    + m1.m33 * m2.m03;
+            this.m31 = m1.m30 * m2.m10 + m1.m31 * m2.m11 + m1.m32 * m2.m12
+                    + m1.m33 * m2.m13;
+            this.m32 = m1.m30 * m2.m20 + m1.m31 * m2.m21 + m1.m32 * m2.m22
+                    + m1.m33 * m2.m23;
+            this.m33 = m1.m30 * m2.m30 + m1.m31 * m2.m31 + m1.m32 * m2.m32
+                    + m1.m33 * m2.m33;
+        } else {
+            float m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, // vars
+                                                                              // for
+                                                                              // temp
+                                                                              // result
+                                                                              // matrix
+            m30, m31, m32, m33;
+
+            m00 = m1.m00 * m2.m00 + m1.m01 * m2.m01 + m1.m02 * m2.m02 + m1.m03
+                    * m2.m03;
+            m01 = m1.m00 * m2.m10 + m1.m01 * m2.m11 + m1.m02 * m2.m12 + m1.m03
+                    * m2.m13;
+            m02 = m1.m00 * m2.m20 + m1.m01 * m2.m21 + m1.m02 * m2.m22 + m1.m03
+                    * m2.m23;
+            m03 = m1.m00 * m2.m30 + m1.m01 * m2.m31 + m1.m02 * m2.m32 + m1.m03
+                    * m2.m33;
+
+            m10 = m1.m10 * m2.m00 + m1.m11 * m2.m01 + m1.m12 * m2.m02 + m1.m13
+                    * m2.m03;
+            m11 = m1.m10 * m2.m10 + m1.m11 * m2.m11 + m1.m12 * m2.m12 + m1.m13
+                    * m2.m13;
+            m12 = m1.m10 * m2.m20 + m1.m11 * m2.m21 + m1.m12 * m2.m22 + m1.m13
+                    * m2.m23;
+            m13 = m1.m10 * m2.m30 + m1.m11 * m2.m31 + m1.m12 * m2.m32 + m1.m13
+                    * m2.m33;
+
+            m20 = m1.m20 * m2.m00 + m1.m21 * m2.m01 + m1.m22 * m2.m02 + m1.m23
+                    * m2.m03;
+            m21 = m1.m20 * m2.m10 + m1.m21 * m2.m11 + m1.m22 * m2.m12 + m1.m23
+                    * m2.m13;
+            m22 = m1.m20 * m2.m20 + m1.m21 * m2.m21 + m1.m22 * m2.m22 + m1.m23
+                    * m2.m23;
+            m23 = m1.m20 * m2.m30 + m1.m21 * m2.m31 + m1.m22 * m2.m32 + m1.m23
+                    * m2.m33;
+
+            m30 = m1.m30 * m2.m00 + m1.m31 * m2.m01 + m1.m32 * m2.m02 + m1.m33
+                    * m2.m03;
+            m31 = m1.m30 * m2.m10 + m1.m31 * m2.m11 + m1.m32 * m2.m12 + m1.m33
+                    * m2.m13;
+            m32 = m1.m30 * m2.m20 + m1.m31 * m2.m21 + m1.m32 * m2.m22 + m1.m33
+                    * m2.m23;
+            m33 = m1.m30 * m2.m30 + m1.m31 * m2.m31 + m1.m32 * m2.m32 + m1.m33
+                    * m2.m33;
+
+            this.m00 = m00;
+            this.m01 = m01;
+            this.m02 = m02;
+            this.m03 = m03;
+            this.m10 = m10;
+            this.m11 = m11;
+            this.m12 = m12;
+            this.m13 = m13;
+            this.m20 = m20;
+            this.m21 = m21;
+            this.m22 = m22;
+            this.m23 = m23;
+            this.m30 = m30;
+            this.m31 = m31;
+            this.m32 = m32;
+            this.m33 = m33;
+        }
+
+    }
+
+    /**
+     * Negates the value of this matrix: this = -this.
+     */
+    public final void negate() {
+        m00 = -m00;
+        m01 = -m01;
+        m02 = -m02;
+        m03 = -m03;
+        m10 = -m10;
+        m11 = -m11;
+        m12 = -m12;
+        m13 = -m13;
+        m20 = -m20;
+        m21 = -m21;
+        m22 = -m22;
+        m23 = -m23;
+        m30 = -m30;
+        m31 = -m31;
+        m32 = -m32;
+        m33 = -m33;
+    }
+
+    /**
+     * Sets the value of this matrix equal to the negation of of the Matrix4f
+     * parameter.
+     * 
+     * @param m1
+     *            the source matrix
+     */
+    public final void negate(Matrix4f m1) {
+        this.m00 = -m1.m00;
+        this.m01 = -m1.m01;
+        this.m02 = -m1.m02;
+        this.m03 = -m1.m03;
+        this.m10 = -m1.m10;
+        this.m11 = -m1.m11;
+        this.m12 = -m1.m12;
+        this.m13 = -m1.m13;
+        this.m20 = -m1.m20;
+        this.m21 = -m1.m21;
+        this.m22 = -m1.m22;
+        this.m23 = -m1.m23;
+        this.m30 = -m1.m30;
+        this.m31 = -m1.m31;
+        this.m32 = -m1.m32;
+        this.m33 = -m1.m33;
+    }
+
+    /**
+     * Sets the value of this matrix to a counter clockwise rotation about the x
+     * axis.
+     * 
+     * @param angle
+     *            the angle to rotate about the X axis in radians
+     */
+    public final void rotX(float angle) {
+        float sinAngle, cosAngle;
+
+        sinAngle = (float) Math.sin(angle);
+        cosAngle = (float) Math.cos(angle);
+
+        this.m00 = (float) 1.0;
+        this.m01 = (float) 0.0;
+        this.m02 = (float) 0.0;
+        this.m03 = (float) 0.0;
+
+        this.m10 = (float) 0.0;
+        this.m11 = cosAngle;
+        this.m12 = -sinAngle;
+        this.m13 = (float) 0.0;
+
+        this.m20 = (float) 0.0;
+        this.m21 = sinAngle;
+        this.m22 = cosAngle;
+        this.m23 = (float) 0.0;
+
+        this.m30 = (float) 0.0;
+        this.m31 = (float) 0.0;
+        this.m32 = (float) 0.0;
+        this.m33 = (float) 1.0;
+    }
+
+    /**
+     * Sets the value of this matrix to a counter clockwise rotation about the y
+     * axis.
+     * 
+     * @param angle
+     *            the angle to rotate about the Y axis in radians
+     */
+    public final void rotY(float angle) {
+        float sinAngle, cosAngle;
+
+        sinAngle = (float) Math.sin(angle);
+        cosAngle = (float) Math.cos(angle);
+
+        this.m00 = cosAngle;
+        this.m01 = (float) 0.0;
+        this.m02 = sinAngle;
+        this.m03 = (float) 0.0;
+
+        this.m10 = (float) 0.0;
+        this.m11 = (float) 1.0;
+        this.m12 = (float) 0.0;
+        this.m13 = (float) 0.0;
+
+        this.m20 = -sinAngle;
+        this.m21 = (float) 0.0;
+        this.m22 = cosAngle;
+        this.m23 = (float) 0.0;
+
+        this.m30 = (float) 0.0;
+        this.m31 = (float) 0.0;
+        this.m32 = (float) 0.0;
+        this.m33 = (float) 1.0;
+    }
+
+    /**
+     * Sets the value of this matrix to a counter clockwise rotation about the z
+     * axis.
+     * 
+     * @param angle
+     *            the angle to rotate about the Z axis in radians
+     */
+    public final void rotZ(float angle) {
+        float sinAngle, cosAngle;
+
+        sinAngle = (float) Math.sin(angle);
+        cosAngle = (float) Math.cos(angle);
+
+        this.m00 = cosAngle;
+        this.m01 = -sinAngle;
+        this.m02 = (float) 0.0;
+        this.m03 = (float) 0.0;
+
+        this.m10 = sinAngle;
+        this.m11 = cosAngle;
+        this.m12 = (float) 0.0;
+        this.m13 = (float) 0.0;
+
+        this.m20 = (float) 0.0;
+        this.m21 = (float) 0.0;
+        this.m22 = (float) 1.0;
+        this.m23 = (float) 0.0;
+
+        this.m30 = (float) 0.0;
+        this.m31 = (float) 0.0;
+        this.m32 = (float) 0.0;
+        this.m33 = (float) 1.0;
+    }
+
+    /**
+     * Sets the value of this matrix to a scale matrix with the the passed scale
+     * amount.
+     * 
+     * @param scale
+     *            the scale factor for the matrix
+     */
+    public final void set(float scale) {
+        this.m00 = scale;
+        this.m01 = (float) 0.0;
+        this.m02 = (float) 0.0;
+        this.m03 = (float) 0.0;
+
+        this.m10 = (float) 0.0;
+        this.m11 = scale;
+        this.m12 = (float) 0.0;
+        this.m13 = (float) 0.0;
+
+        this.m20 = (float) 0.0;
+        this.m21 = (float) 0.0;
+        this.m22 = scale;
+        this.m23 = (float) 0.0;
+
+        this.m30 = (float) 0.0;
+        this.m31 = (float) 0.0;
+        this.m32 = (float) 0.0;
+        this.m33 = (float) 1.0;
+    }
+
+    /**
+     * Sets the value of this transform to a scale and translation matrix; the
+     * scale is not applied to the translation and all of the matrix values are
+     * modified.
+     * 
+     * @param scale
+     *            the scale factor for the matrix
+     * @param t1
+     *            the translation amount
+     */
+    public final void set(float scale, Vec3D t1) {
+        this.m00 = scale;
+        this.m01 = (float) 0.0;
+        this.m02 = (float) 0.0;
+        this.m03 = t1.x;
+
+        this.m10 = (float) 0.0;
+        this.m11 = scale;
+        this.m12 = (float) 0.0;
+        this.m13 = t1.y;
+
+        this.m20 = (float) 0.0;
+        this.m21 = (float) 0.0;
+        this.m22 = scale;
+        this.m23 = t1.z;
+
+        this.m30 = (float) 0.0;
+        this.m31 = (float) 0.0;
+        this.m32 = (float) 0.0;
+        this.m33 = (float) 1.0;
+    }
+
+    /**
+     * Sets the values in this Matrix4f equal to the row-major array parameter
+     * (ie, the first four elements of the array will be copied into the first
+     * row of this matrix, etc.).
+     * 
+     * @param m
+     *            the single precision array of length 16
+     */
+    public final void set(float[] m) {
+        m00 = m[0];
+        m01 = m[1];
+        m02 = m[2];
+        m03 = m[3];
+        m10 = m[4];
+        m11 = m[5];
+        m12 = m[6];
+        m13 = m[7];
+        m20 = m[8];
+        m21 = m[9];
+        m22 = m[10];
+        m23 = m[11];
+        m30 = m[12];
+        m31 = m[13];
+        m32 = m[14];
+        m33 = m[15];
+    }
+
+    /**
+     * Sets the rotational component (upper 3x3) of this matrix to the matrix
+     * values in the double precision Matrix3d argument; the other elements of
+     * this matrix are initialized as if this were an identity matrix (i.e.,
+     * affine matrix with no translational component).
+     * 
+     * @param m1
+     *            the double-precision 3x3 matrix
+     */
+    public final void set(Matrix3d m1) {
+        m00 = (float) m1.m00;
+        m01 = (float) m1.m01;
+        m02 = (float) m1.m02;
+        m03 = 0.0f;
+        m10 = (float) m1.m10;
+        m11 = (float) m1.m11;
+        m12 = (float) m1.m12;
+        m13 = 0.0f;
+        m20 = (float) m1.m20;
+        m21 = (float) m1.m21;
+        m22 = (float) m1.m22;
+        m23 = 0.0f;
+        m30 = 0.0f;
+        m31 = 0.0f;
+        m32 = 0.0f;
+        m33 = 1.0f;
+    }
+
+    /**
+     * Sets the value of this matrix from the rotation expressed by the rotation
+     * matrix m1, the translation t1, and the scale factor. The translation is
+     * not modified by the scale.
+     * 
+     * @param m1
+     *            the rotation component
+     * @param t1
+     *            the translation component
+     * @param scale
+     *            the scale factor
+     */
+    public final void set(Matrix3d m1, Vec3D t1, double scale) {
+        this.m00 = (float) (m1.m00 * scale);
+        this.m01 = (float) (m1.m01 * scale);
+        this.m02 = (float) (m1.m02 * scale);
+        this.m03 = t1.x;
+
+        this.m10 = (float) (m1.m10 * scale);
+        this.m11 = (float) (m1.m11 * scale);
+        this.m12 = (float) (m1.m12 * scale);
+        this.m13 = t1.y;
+
+        this.m20 = (float) (m1.m20 * scale);
+        this.m21 = (float) (m1.m21 * scale);
+        this.m22 = (float) (m1.m22 * scale);
+        this.m23 = t1.z;
+
+        this.m30 = 0.0f;
+        this.m31 = 0.0f;
+        this.m32 = 0.0f;
+        this.m33 = 1.0f;
+    }
+
+    /**
+     * Sets the value of this matrix to a copy of the passed matrix m1.
+     * 
+     * @param m1
+     *            the matrix to be copied
+     */
+    public final void set(Matrix4f m1) {
+        this.m00 = m1.m00;
+        this.m01 = m1.m01;
+        this.m02 = m1.m02;
+        this.m03 = m1.m03;
+
+        this.m10 = m1.m10;
+        this.m11 = m1.m11;
+        this.m12 = m1.m12;
+        this.m13 = m1.m13;
+
+        this.m20 = m1.m20;
+        this.m21 = m1.m21;
+        this.m22 = m1.m22;
+        this.m23 = m1.m23;
+
+        this.m30 = m1.m30;
+        this.m31 = m1.m31;
+        this.m32 = m1.m32;
+        this.m33 = m1.m33;
+    }
+
+    /**
+     * Sets the value of this matrix to the matrix conversion of the single
+     * precision quaternion argument.
+     * 
+     * @param q1
+     *            the quaternion to be converted
+     */
+    public final void set(Quaternion q1) {
+        this.m00 = (1.0f - 2.0f * q1.y * q1.y - 2.0f * q1.z * q1.z);
+        this.m10 = (2.0f * (q1.x * q1.y + q1.w * q1.z));
+        this.m20 = (2.0f * (q1.x * q1.z - q1.w * q1.y));
+
+        this.m01 = (2.0f * (q1.x * q1.y - q1.w * q1.z));
+        this.m11 = (1.0f - 2.0f * q1.x * q1.x - 2.0f * q1.z * q1.z);
+        this.m21 = (2.0f * (q1.y * q1.z + q1.w * q1.x));
+
+        this.m02 = (2.0f * (q1.x * q1.z + q1.w * q1.y));
+        this.m12 = (2.0f * (q1.y * q1.z - q1.w * q1.x));
+        this.m22 = (1.0f - 2.0f * q1.x * q1.x - 2.0f * q1.y * q1.y);
+
+        this.m03 = (float) 0.0;
+        this.m13 = (float) 0.0;
+        this.m23 = (float) 0.0;
+
+        this.m30 = (float) 0.0;
+        this.m31 = (float) 0.0;
+        this.m32 = (float) 0.0;
+        this.m33 = (float) 1.0;
+    }
+
+    /**
+     * Sets the value of this matrix from the rotation expressed by the
+     * quaternion q1, the translation t1, and the scale s.
+     * 
+     * @param q1
+     *            the rotation expressed as a quaternion
+     * @param t1
+     *            the translation
+     * @param s
+     *            the scale value
+     */
+    public final void set(Quaternion q1, Vec3D t1, double s) {
+        this.m00 = (float) (s * (1.0 - 2.0 * q1.y * q1.y - 2.0 * q1.z * q1.z));
+        this.m10 = (float) (s * (2.0 * (q1.x * q1.y + q1.w * q1.z)));
+        this.m20 = (float) (s * (2.0 * (q1.x * q1.z - q1.w * q1.y)));
+
+        this.m01 = (float) (s * (2.0 * (q1.x * q1.y - q1.w * q1.z)));
+        this.m11 = (float) (s * (1.0 - 2.0 * q1.x * q1.x - 2.0 * q1.z * q1.z));
+        this.m21 = (float) (s * (2.0 * (q1.y * q1.z + q1.w * q1.x)));
+
+        this.m02 = (float) (s * (2.0 * (q1.x * q1.z + q1.w * q1.y)));
+        this.m12 = (float) (s * (2.0 * (q1.y * q1.z - q1.w * q1.x)));
+        this.m22 = (float) (s * (1.0 - 2.0 * q1.x * q1.x - 2.0 * q1.y * q1.y));
+
+        this.m03 = t1.x;
+        this.m13 = t1.y;
+        this.m23 = t1.z;
+
+        this.m30 = (float) 0.0;
+        this.m31 = (float) 0.0;
+        this.m32 = (float) 0.0;
+        this.m33 = (float) 1.0;
+    }
+
+    /**
+     * Sets the value of this matrix from the rotation expressed by the
+     * quaternion q1, the translation t1, and the scale s.
+     * 
+     * @param q1
+     *            the rotation expressed as a quaternion
+     * @param t1
+     *            the translation
+     * @param s
+     *            the scale value
+     */
+    public final void set(Quaternion q1, Vec3D t1, float s) {
+        this.m00 = (s * (1.0f - 2.0f * q1.y * q1.y - 2.0f * q1.z * q1.z));
+        this.m10 = (s * (2.0f * (q1.x * q1.y + q1.w * q1.z)));
+        this.m20 = (s * (2.0f * (q1.x * q1.z - q1.w * q1.y)));
+
+        this.m01 = (s * (2.0f * (q1.x * q1.y - q1.w * q1.z)));
+        this.m11 = (s * (1.0f - 2.0f * q1.x * q1.x - 2.0f * q1.z * q1.z));
+        this.m21 = (s * (2.0f * (q1.y * q1.z + q1.w * q1.x)));
+
+        this.m02 = (s * (2.0f * (q1.x * q1.z + q1.w * q1.y)));
+        this.m12 = (s * (2.0f * (q1.y * q1.z - q1.w * q1.x)));
+        this.m22 = (s * (1.0f - 2.0f * q1.x * q1.x - 2.0f * q1.y * q1.y));
+
+        this.m03 = t1.x;
+        this.m13 = t1.y;
+        this.m23 = t1.z;
+
+        this.m30 = (float) 0.0;
+        this.m31 = (float) 0.0;
+        this.m32 = (float) 0.0;
+        this.m33 = (float) 1.0;
+    }
+
+    /**
+     * Sets the value of this matrix to a translate matrix with the passed
+     * translation value.
+     * 
+     * @param v1
+     *            the translation amount
+     */
+    public final void set(Vec3D v1) {
+        this.m00 = (float) 1.0;
+        this.m01 = (float) 0.0;
+        this.m02 = (float) 0.0;
+        this.m03 = v1.x;
+
+        this.m10 = (float) 0.0;
+        this.m11 = (float) 1.0;
+        this.m12 = (float) 0.0;
+        this.m13 = v1.y;
+
+        this.m20 = (float) 0.0;
+        this.m21 = (float) 0.0;
+        this.m22 = (float) 1.0;
+        this.m23 = v1.z;
+
+        this.m30 = (float) 0.0;
+        this.m31 = (float) 0.0;
+        this.m32 = (float) 0.0;
+        this.m33 = (float) 1.0;
+    }
+
+    /**
+     * Sets the value of this transform to a scale and translation matrix; the
+     * translation is scaled by the scale factor and all of the matrix values
+     * are modified.
+     * 
+     * @param t1
+     *            the translation amount
+     * @param scale
+     *            the scale factor for the matrix
+     */
+    public final void set(Vec3D t1, float scale) {
+        this.m00 = scale;
+        this.m01 = (float) 0.0;
+        this.m02 = (float) 0.0;
+        this.m03 = scale * t1.x;
+
+        this.m10 = (float) 0.0;
+        this.m11 = scale;
+        this.m12 = (float) 0.0;
+        this.m13 = scale * t1.y;
+
+        this.m20 = (float) 0.0;
+        this.m21 = (float) 0.0;
+        this.m22 = scale;
+        this.m23 = scale * t1.z;
+
+        this.m30 = (float) 0.0;
+        this.m31 = (float) 0.0;
+        this.m32 = (float) 0.0;
+        this.m33 = (float) 1.0;
+    }
+
+    /**
+     * Sets the specified column of this matrix4f to the four values provided.
+     * 
+     * @param column
+     *            the column number to be modified (zero indexed)
+     * @param v
+     *            the replacement column
+     */
+    public final void setColumn(int column, float v[]) {
+        switch (column) {
+            case 0:
+                this.m00 = v[0];
+                this.m10 = v[1];
+                this.m20 = v[2];
+                this.m30 = v[3];
+                break;
+
+            case 1:
+                this.m01 = v[0];
+                this.m11 = v[1];
+                this.m21 = v[2];
+                this.m31 = v[3];
+                break;
+
+            case 2:
+                this.m02 = v[0];
+                this.m12 = v[1];
+                this.m22 = v[2];
+                this.m32 = v[3];
+                break;
+
+            case 3:
+                this.m03 = v[0];
+                this.m13 = v[1];
+                this.m23 = v[2];
+                this.m33 = v[3];
+                break;
+
+            default:
+                throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Sets the specified column of this matrix4f to the four values provided.
+     * 
+     * @param column
+     *            the column number to be modified (zero indexed)
+     * @param x
+     *            the first row element
+     * @param y
+     *            the second row element
+     * @param z
+     *            the third row element
+     * @param w
+     *            the fourth row element
+     */
+    public final void setColumn(int column, float x, float y, float z, float w) {
+        switch (column) {
+            case 0:
+                this.m00 = x;
+                this.m10 = y;
+                this.m20 = z;
+                this.m30 = w;
+                break;
+
+            case 1:
+                this.m01 = x;
+                this.m11 = y;
+                this.m21 = z;
+                this.m31 = w;
+                break;
+
+            case 2:
+                this.m02 = x;
+                this.m12 = y;
+                this.m22 = z;
+                this.m32 = w;
+                break;
+
+            case 3:
+                this.m03 = x;
+                this.m13 = y;
+                this.m23 = z;
+                this.m33 = w;
+                break;
+
+            default:
+                throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Sets the specified column of this matrix4f to the vector provided.
+     * 
+     * @param column
+     *            the column number to be modified (zero indexed)
+     * @param v
+     *            the replacement column
+     */
+    public final void setColumn(int column, Vec4D v) {
+        switch (column) {
+            case 0:
+                this.m00 = v.x;
+                this.m10 = v.y;
+                this.m20 = v.z;
+                this.m30 = v.w;
+                break;
+
+            case 1:
+                this.m01 = v.x;
+                this.m11 = v.y;
+                this.m21 = v.z;
+                this.m31 = v.w;
+                break;
+
+            case 2:
+                this.m02 = v.x;
+                this.m12 = v.y;
+                this.m22 = v.z;
+                this.m32 = v.w;
+                break;
+
+            case 3:
+                this.m03 = v.x;
+                this.m13 = v.y;
+                this.m23 = v.z;
+                this.m33 = v.w;
+                break;
+
+            default:
+                throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Sets the specified element of this matrix4f to the value provided.
+     * 
+     * @param row
+     *            the row number to be modified (zero indexed)
+     * @param column
+     *            the column number to be modified (zero indexed)
+     * @param value
+     *            the new value
+     */
+    public final void setElement(int row, int column, float value) {
+        switch (row) {
+            case 0:
+                switch (column) {
+                    case 0:
+                        this.m00 = value;
+                        break;
+                    case 1:
+                        this.m01 = value;
+                        break;
+                    case 2:
+                        this.m02 = value;
+                        break;
+                    case 3:
+                        this.m03 = value;
+                        break;
+                    default:
+                        throw new ArrayIndexOutOfBoundsException();
+                }
+                break;
+
+            case 1:
+                switch (column) {
+                    case 0:
+                        this.m10 = value;
+                        break;
+                    case 1:
+                        this.m11 = value;
+                        break;
+                    case 2:
+                        this.m12 = value;
+                        break;
+                    case 3:
+                        this.m13 = value;
+                        break;
+                    default:
+                        throw new ArrayIndexOutOfBoundsException();
+                }
+                break;
+
+            case 2:
+                switch (column) {
+                    case 0:
+                        this.m20 = value;
+                        break;
+                    case 1:
+                        this.m21 = value;
+                        break;
+                    case 2:
+                        this.m22 = value;
+                        break;
+                    case 3:
+                        this.m23 = value;
+                        break;
+                    default:
+                        throw new ArrayIndexOutOfBoundsException();
+                }
+                break;
+
+            case 3:
+                switch (column) {
+                    case 0:
+                        this.m30 = value;
+                        break;
+                    case 1:
+                        this.m31 = value;
+                        break;
+                    case 2:
+                        this.m32 = value;
+                        break;
+                    case 3:
+                        this.m33 = value;
+                        break;
+                    default:
+                        throw new ArrayIndexOutOfBoundsException();
+                }
+                break;
+
+            default:
+                throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Sets this Matrix4f to identity.
+     */
+    public final void setIdentity() {
+        this.m00 = (float) 1.0;
+        this.m01 = (float) 0.0;
+        this.m02 = (float) 0.0;
+        this.m03 = (float) 0.0;
+
+        this.m10 = (float) 0.0;
+        this.m11 = (float) 1.0;
+        this.m12 = (float) 0.0;
+        this.m13 = (float) 0.0;
+
+        this.m20 = (float) 0.0;
+        this.m21 = (float) 0.0;
+        this.m22 = (float) 1.0;
+        this.m23 = (float) 0.0;
+
+        this.m30 = (float) 0.0;
+        this.m31 = (float) 0.0;
+        this.m32 = (float) 0.0;
+        this.m33 = (float) 1.0;
+    }
+
+    /**
+     * Set the first matrix element in the first row.
+     * 
+     * @param m00
+     *            The m00 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM00(float m00) {
+        this.m00 = m00;
+    }
+
+    /**
+     * Set the second matrix element in the first row.
+     * 
+     * @param m01
+     *            The m01 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM01(float m01) {
+        this.m01 = m01;
+    }
+
+    /**
+     * Set the third matrix element in the first row.
+     * 
+     * @param m02
+     *            The m02 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM02(float m02) {
+        this.m02 = m02;
+    }
+
+    /**
+     * Set the fourth element of the first row.
+     * 
+     * @param m03
+     *            The m03 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM03(float m03) {
+        this.m03 = m03;
+    }
+
+    /**
+     * Set first matrix element in the second row.
+     * 
+     * @param m10
+     *            The m10 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM10(float m10) {
+        this.m10 = m10;
+    }
+
+    /**
+     * Set the second matrix element in the second row.
+     * 
+     * @param m11
+     *            The m11 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM11(float m11) {
+        this.m11 = m11;
+    }
+
+    /**
+     * Set the third matrix element in the second row.
+     * 
+     * @param m12
+     *            The m12 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM12(float m12) {
+        this.m12 = m12;
+    }
+
+    /**
+     * Set the fourth element of the second row.
+     * 
+     * @param m13
+     *            The m13 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM13(float m13) {
+        this.m13 = m13;
+    }
+
+    /**
+     * Set the first matrix element in the third row.
+     * 
+     * @param m20
+     *            The m20 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM20(float m20) {
+        this.m20 = m20;
+    }
+
+    /**
+     * Set the second matrix element in the third row.
+     * 
+     * @param m21
+     *            The m21 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM21(float m21) {
+        this.m21 = m21;
+    }
+
+    /**
+     * Set the third matrix element in the third row.
+     * 
+     * @param m22
+     *            The m22 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM22(float m22) {
+        this.m22 = m22;
+    }
+
+    /**
+     * Set the fourth element of the third row.
+     * 
+     * @param m23
+     *            The m23 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM23(float m23) {
+        this.m23 = m23;
+    }
+
+    /**
+     * Set the first element of the fourth row.
+     * 
+     * @param m30
+     *            The m30 to set.
+     * 
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM30(float m30) {
+        this.m30 = m30;
+    }
+
+    /**
+     * Set the second element of the fourth row.
+     * 
+     * @param m31
+     *            The m31 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM31(float m31) {
+        this.m31 = m31;
+    }
+
+    /**
+     * Set the third element of the fourth row.
+     * 
+     * @param m32
+     *            The m32 to set.
+     * 
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM32(float m32) {
+        this.m32 = m32;
+    }
+
+    /**
+     * Set the fourth element of the fourth row.
+     * 
+     * @param m33
+     *            The m33 to set.
+     * 
+     * @since vecmath 1.5
+     */
+    public final void setM33(float m33) {
+        this.m33 = m33;
+    }
+
+    /**
+     * Sets the rotational component (upper 3x3) of this matrix to the matrix
+     * values in the double precision Matrix3d argument; the other elements of
+     * this matrix are unchanged; a singular value decomposition is performed on
+     * this object's upper 3x3 matrix to factor out the scale, then this
+     * object's upper 3x3 matrix components are replaced by the passed rotation
+     * components, and then the scale is reapplied to the rotational components.
+     * 
+     * @param m1
+     *            double precision 3x3 matrix
+     */
+    public final void setRotation(Matrix3d m1) {
+        double[] tmp_rot = new double[9];
+        double[] tmp_scale = new double[3];
+
+        getScaleRotate(tmp_scale, tmp_rot);
+
+        m00 = (float) (m1.m00 * tmp_scale[0]);
+        m01 = (float) (m1.m01 * tmp_scale[1]);
+        m02 = (float) (m1.m02 * tmp_scale[2]);
+
+        m10 = (float) (m1.m10 * tmp_scale[0]);
+        m11 = (float) (m1.m11 * tmp_scale[1]);
+        m12 = (float) (m1.m12 * tmp_scale[2]);
+
+        m20 = (float) (m1.m20 * tmp_scale[0]);
+        m21 = (float) (m1.m21 * tmp_scale[1]);
+        m22 = (float) (m1.m22 * tmp_scale[2]);
+
+    }
+
+    /**
+     * Sets the rotational component (upper 3x3) of this matrix to the matrix
+     * equivalent values of the quaternion argument; the other elements of this
+     * matrix are unchanged; a singular value decomposition is performed on this
+     * object's upper 3x3 matrix to factor out the scale, then this object's
+     * upper 3x3 matrix components are replaced by the matrix equivalent of the
+     * quaternion, and then the scale is reapplied to the rotational components.
+     * 
+     * @param q1
+     *            the quaternion that specifies the rotation
+     */
+    public final void setRotation(Quaternion q1) {
+        double[] tmp_rot = new double[9]; // scratch matrix
+        double[] tmp_scale = new double[3]; // scratch matrix
+        getScaleRotate(tmp_scale, tmp_rot);
+
+        m00 = (float) ((1.0f - 2.0f * q1.y * q1.y - 2.0f * q1.z * q1.z) * tmp_scale[0]);
+        m10 = (float) ((2.0f * (q1.x * q1.y + q1.w * q1.z)) * tmp_scale[0]);
+        m20 = (float) ((2.0f * (q1.x * q1.z - q1.w * q1.y)) * tmp_scale[0]);
+
+        m01 = (float) ((2.0f * (q1.x * q1.y - q1.w * q1.z)) * tmp_scale[1]);
+        m11 = (float) ((1.0f - 2.0f * q1.x * q1.x - 2.0f * q1.z * q1.z) * tmp_scale[1]);
+        m21 = (float) ((2.0f * (q1.y * q1.z + q1.w * q1.x)) * tmp_scale[1]);
+
+        m02 = (float) ((2.0f * (q1.x * q1.z + q1.w * q1.y)) * tmp_scale[2]);
+        m12 = (float) ((2.0f * (q1.y * q1.z - q1.w * q1.x)) * tmp_scale[2]);
+        m22 = (float) ((1.0f - 2.0f * q1.x * q1.x - 2.0f * q1.y * q1.y) * tmp_scale[2]);
+
+    }
+
+    /**
+     * Replaces the upper 3x3 matrix values of this matrix with the values in
+     * the matrix m1.
+     * 
+     * @param m1
+     *            the matrix that will be the new upper 3x3
+     */
+    public final void setRotationScale(Matrix3d m1) {
+        m00 = (float) m1.m00;
+        m01 = (float) m1.m01;
+        m02 = (float) m1.m02;
+        m10 = (float) m1.m10;
+        m11 = (float) m1.m11;
+        m12 = (float) m1.m12;
+        m20 = (float) m1.m20;
+        m21 = (float) m1.m21;
+        m22 = (float) m1.m22;
+    }
+
+    /**
+     * Sets the specified row of this matrix4f to the four values provided in
+     * the passed array.
+     * 
+     * @param row
+     *            the row number to be modified (zero indexed)
+     * @param v
+     *            the replacement row
+     */
+    public final void setRow(int row, float v[]) {
+        switch (row) {
+            case 0:
+                this.m00 = v[0];
+                this.m01 = v[1];
+                this.m02 = v[2];
+                this.m03 = v[3];
+                break;
+
+            case 1:
+                this.m10 = v[0];
+                this.m11 = v[1];
+                this.m12 = v[2];
+                this.m13 = v[3];
+                break;
+
+            case 2:
+                this.m20 = v[0];
+                this.m21 = v[1];
+                this.m22 = v[2];
+                this.m23 = v[3];
+                break;
+
+            case 3:
+                this.m30 = v[0];
+                this.m31 = v[1];
+                this.m32 = v[2];
+                this.m33 = v[3];
+                break;
+
+            default:
+                throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Sets the specified row of this matrix4f to the four values provided.
+     * 
+     * @param row
+     *            the row number to be modified (zero indexed)
+     * @param x
+     *            the first column element
+     * @param y
+     *            the second column element
+     * @param z
+     *            the third column element
+     * @param w
+     *            the fourth column element
+     */
+    public final void setRow(int row, float x, float y, float z, float w) {
+        switch (row) {
+            case 0:
+                this.m00 = x;
+                this.m01 = y;
+                this.m02 = z;
+                this.m03 = w;
+                break;
+
+            case 1:
+                this.m10 = x;
+                this.m11 = y;
+                this.m12 = z;
+                this.m13 = w;
+                break;
+
+            case 2:
+                this.m20 = x;
+                this.m21 = y;
+                this.m22 = z;
+                this.m23 = w;
+                break;
+
+            case 3:
+                this.m30 = x;
+                this.m31 = y;
+                this.m32 = z;
+                this.m33 = w;
+                break;
+
+            default:
+                throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Sets the specified row of this matrix4f to the Vector provided.
+     * 
+     * @param row
+     *            the row number to be modified (zero indexed)
+     * @param v
+     *            the replacement row
+     */
+    public final void setRow(int row, Vec4D v) {
+        switch (row) {
+            case 0:
+                this.m00 = v.x;
+                this.m01 = v.y;
+                this.m02 = v.z;
+                this.m03 = v.w;
+                break;
+
+            case 1:
+                this.m10 = v.x;
+                this.m11 = v.y;
+                this.m12 = v.z;
+                this.m13 = v.w;
+                break;
+
+            case 2:
+                this.m20 = v.x;
+                this.m21 = v.y;
+                this.m22 = v.z;
+                this.m23 = v.w;
+                break;
+
+            case 3:
+                this.m30 = v.x;
+                this.m31 = v.y;
+                this.m32 = v.z;
+                this.m33 = v.w;
+                break;
+
+            default:
+                throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Sets the scale component of the current matrix by factoring out the
+     * current scale (by doing an SVD) from the rotational component and
+     * multiplying by the new scale.
+     * 
+     * @param scale
+     *            the new scale amount
+     */
+    public final void setScale(float scale) {
+
+        double[] tmp_rot = new double[9]; // scratch matrix
+        double[] tmp_scale = new double[3]; // scratch matrix
+        getScaleRotate(tmp_scale, tmp_rot);
+
+        m00 = (float) (tmp_rot[0] * scale);
+        m01 = (float) (tmp_rot[1] * scale);
+        m02 = (float) (tmp_rot[2] * scale);
+
+        m10 = (float) (tmp_rot[3] * scale);
+        m11 = (float) (tmp_rot[4] * scale);
+        m12 = (float) (tmp_rot[5] * scale);
+
+        m20 = (float) (tmp_rot[6] * scale);
+        m21 = (float) (tmp_rot[7] * scale);
+        m22 = (float) (tmp_rot[8] * scale);
+
+    }
+
+    /**
+     * Modifies the translational components of this matrix to the values of the
+     * Vector3f argument; the other values of this matrix are not modified.
+     * 
+     * @param trans
+     *            the translational component
+     */
+    public final void setTranslation(Vec3D trans) {
+        m03 = trans.x;
+        m13 = trans.y;
+        m23 = trans.z;
+    }
+
+    /**
+     * Sets this matrix to all zeros.
+     */
+    public final void setZero() {
+        m00 = 0.0f;
+        m01 = 0.0f;
+        m02 = 0.0f;
+        m03 = 0.0f;
+        m10 = 0.0f;
+        m11 = 0.0f;
+        m12 = 0.0f;
+        m13 = 0.0f;
+        m20 = 0.0f;
+        m21 = 0.0f;
+        m22 = 0.0f;
+        m23 = 0.0f;
+        m30 = 0.0f;
+        m31 = 0.0f;
+        m32 = 0.0f;
+        m33 = 0.0f;
+    }
+
+    /**
+     * Sets this matrix to the matrix difference of itself and matrix m1 (this =
+     * this - m1).
+     * 
+     * @param m1
+     *            the other matrix
+     */
+    public final void sub(Matrix4f m1) {
+        this.m00 -= m1.m00;
+        this.m01 -= m1.m01;
+        this.m02 -= m1.m02;
+        this.m03 -= m1.m03;
+
+        this.m10 -= m1.m10;
+        this.m11 -= m1.m11;
+        this.m12 -= m1.m12;
+        this.m13 -= m1.m13;
+
+        this.m20 -= m1.m20;
+        this.m21 -= m1.m21;
+        this.m22 -= m1.m22;
+        this.m23 -= m1.m23;
+
+        this.m30 -= m1.m30;
+        this.m31 -= m1.m31;
+        this.m32 -= m1.m32;
+        this.m33 -= m1.m33;
+    }
+
+    /**
+     * Performs an element-by-element subtraction of matrix m2 from matrix m1
+     * and places the result into matrix this (this = m2 - m1).
+     * 
+     * @param m1
+     *            the first matrix
+     * @param m2
+     *            the second matrix
+     */
+    public final void sub(Matrix4f m1, Matrix4f m2) {
+        this.m00 = m1.m00 - m2.m00;
+        this.m01 = m1.m01 - m2.m01;
+        this.m02 = m1.m02 - m2.m02;
+        this.m03 = m1.m03 - m2.m03;
+
+        this.m10 = m1.m10 - m2.m10;
+        this.m11 = m1.m11 - m2.m11;
+        this.m12 = m1.m12 - m2.m12;
+        this.m13 = m1.m13 - m2.m13;
+
+        this.m20 = m1.m20 - m2.m20;
+        this.m21 = m1.m21 - m2.m21;
+        this.m22 = m1.m22 - m2.m22;
+        this.m23 = m1.m23 - m2.m23;
+
+        this.m30 = m1.m30 - m2.m30;
+        this.m31 = m1.m31 - m2.m31;
+        this.m32 = m1.m32 - m2.m32;
+        this.m33 = m1.m33 - m2.m33;
+    }
+
+    /**
+     * Returns a string that contains the values of this Matrix4f.
+     * 
+     * @return the String representation
+     */
+    public String toString() {
+        return this.m00 + ", " + this.m01 + ", " + this.m02 + ", " + this.m03
+                + "\n" + this.m10 + ", " + this.m11 + ", " + this.m12 + ", "
+                + this.m13 + "\n" + this.m20 + ", " + this.m21 + ", "
+                + this.m22 + ", " + this.m23 + "\n" + this.m30 + ", "
+                + this.m31 + ", " + this.m32 + ", " + this.m33 + "\n";
+    }
+
+    /**
+     * Transform the vector vec using this Transform and place the result back
+     * into vec.
+     * 
+     * @param vec
+     *            the single precision vector to be transformed
+     */
+    public final void transform(Vec4D vec) {
+        float x, y, z;
+
+        x = m00 * vec.x + m01 * vec.y + m02 * vec.z + m03 * vec.w;
+        y = m10 * vec.x + m11 * vec.y + m12 * vec.z + m13 * vec.w;
+        z = m20 * vec.x + m21 * vec.y + m22 * vec.z + m23 * vec.w;
+        vec.w = m30 * vec.x + m31 * vec.y + m32 * vec.z + m33 * vec.w;
+        vec.x = x;
+        vec.y = y;
+        vec.z = z;
+    }
+
+    /**
+     * Transform the vector vec using this Matrix4f and place the result into
+     * vecOut.
+     * 
+     * @param vec
+     *            the single precision vector to be transformed
+     * @param vecOut
+     *            the vector into which the transformed values are placed
+     */
+    public final void transform(Vec4D vec, Vec4D vecOut) {
+        float x, y, z;
+        x = m00 * vec.x + m01 * vec.y + m02 * vec.z + m03 * vec.w;
+        y = m10 * vec.x + m11 * vec.y + m12 * vec.z + m13 * vec.w;
+        z = m20 * vec.x + m21 * vec.y + m22 * vec.z + m23 * vec.w;
+        vecOut.w = m30 * vec.x + m31 * vec.y + m32 * vec.z + m33 * vec.w;
+        vecOut.x = x;
+        vecOut.y = y;
+        vecOut.z = z;
+    }
+
+    /**
+     * Transforms the point parameter with this Matrix4f and places the result
+     * back into point. The fourth element of the point input paramter is
+     * assumed to be one.
+     * 
+     * @param point
+     *            the input point to be transformed.
+     */
+    public final void transformOne(Vec3D point) {
+        float x, y;
+        x = m00 * point.x + m01 * point.y + m02 * point.z + m03;
+        y = m10 * point.x + m11 * point.y + m12 * point.z + m13;
+        point.z = m20 * point.x + m21 * point.y + m22 * point.z + m23;
+        point.x = x;
+        point.y = y;
+    }
+
+    /**
+     * Transforms the point parameter with this Matrix4f and places the result
+     * into pointOut. The fourth element of the point input paramter is assumed
+     * to be one.
+     * 
+     * @param point
+     *            the input point to be transformed.
+     * @param pointOut
+     *            the transformed point
+     */
+    public final void transformOne(Vec3D point, Vec3D pointOut) {
+        float x, y;
+        x = m00 * point.x + m01 * point.y + m02 * point.z + m03;
+        y = m10 * point.x + m11 * point.y + m12 * point.z + m13;
+        pointOut.z = m20 * point.x + m21 * point.y + m22 * point.z + m23;
+        pointOut.x = x;
+        pointOut.y = y;
+    }
+
+    /**
+     * Transforms the normal parameter by this transform and places the value
+     * back into normal. The fourth element of the normal is assumed to be zero.
+     * 
+     * @param normal
+     *            the input normal to be transformed.
+     */
+    public final void transformZero(Vec3D normal) {
+        float x, y;
+
+        x = m00 * normal.x + m01 * normal.y + m02 * normal.z;
+        y = m10 * normal.x + m11 * normal.y + m12 * normal.z;
+        normal.z = m20 * normal.x + m21 * normal.y + m22 * normal.z;
+        normal.x = x;
+        normal.y = y;
+    }
+
+    /**
+     * Transforms the normal parameter by this Matrix4f and places the value
+     * into normalOut. The fourth element of the normal is assumed to be zero.
+     * 
+     * @param normal
+     *            the input normal to be transformed.
+     * @param normalOut
+     *            the transformed normal
+     */
+    public final void transformZero(Vec3D normal, Vec3D normalOut) {
+        float x, y;
+        x = m00 * normal.x + m01 * normal.y + m02 * normal.z;
+        y = m10 * normal.x + m11 * normal.y + m12 * normal.z;
+        normalOut.z = m20 * normal.x + m21 * normal.y + m22 * normal.z;
+        normalOut.x = x;
+        normalOut.y = y;
+    }
+
+    /**
+     * Sets the value of this matrix to its transpose in place.
+     */
+    public final void transpose() {
+        float temp;
+
+        temp = this.m10;
+        this.m10 = this.m01;
+        this.m01 = temp;
+
+        temp = this.m20;
+        this.m20 = this.m02;
+        this.m02 = temp;
+
+        temp = this.m30;
+        this.m30 = this.m03;
+        this.m03 = temp;
+
+        temp = this.m21;
+        this.m21 = this.m12;
+        this.m12 = temp;
+
+        temp = this.m31;
+        this.m31 = this.m13;
+        this.m13 = temp;
+
+        temp = this.m32;
+        this.m32 = this.m23;
+        this.m23 = temp;
+    }
+
+    /**
+     * Sets the value of this matrix to the transpose of the argument matrix.
+     * 
+     * @param m1
+     *            the matrix to be transposed
+     */
+    public final void transpose(Matrix4f m1) {
+        if (this != m1) {
+            this.m00 = m1.m00;
+            this.m01 = m1.m10;
+            this.m02 = m1.m20;
+            this.m03 = m1.m30;
+
+            this.m10 = m1.m01;
+            this.m11 = m1.m11;
+            this.m12 = m1.m21;
+            this.m13 = m1.m31;
+
+            this.m20 = m1.m02;
+            this.m21 = m1.m12;
+            this.m22 = m1.m22;
+            this.m23 = m1.m32;
+
+            this.m30 = m1.m03;
+            this.m31 = m1.m13;
+            this.m32 = m1.m23;
+            this.m33 = m1.m33;
+        } else {
+            this.transpose();
+        }
+    }
+}
diff --git a/src/main/java/toxi/geom/Matrix4x4.java b/src/main/java/toxi/geom/Matrix4x4.java
new file mode 100644
index 0000000..e847b73
--- /dev/null
+++ b/src/main/java/toxi/geom/Matrix4x4.java
@@ -0,0 +1,815 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.geom;
+
+import toxi.math.MathUtils;
+
+/**
+ * Implements a simple row-major 4x4 matrix class, all matrix operations are
+ * applied to new instances. Use {@link #transpose()} to convert from
+ * column-major formats...
+ */
+public class Matrix4x4 {
+
+    private static final Matrix4x4 TEMP = new Matrix4x4();
+
+    /**
+     * Given a MxM array "matrix0", this function replaces it with the LU
+     * decomposition of a row-wise permutation of itself. The input parameters
+     * are "matrix0" and "dimen". The array "matrix0" is also an output
+     * parameter. The vector "row_perm[4]" is an output parameter that contains
+     * the row permutations resulting from partial pivoting. The output
+     * parameter "even_row_xchg" is 1 when the number of row exchanges is even,
+     * or -1 otherwise. Assumes data type is always double.
+     * 
+     * This function is similar to luDecomposition, except that it is tuned
+     * specifically for 4x4 matrices.
+     * 
+     * Reference: Press, Flannery, Teukolsky, Vetterling,
+     * _Numerical_Recipes_in_C_, Cambridge University Press, 1988, pp 40-45.
+     * 
+     * @param matrix0
+     * @param row_perm
+     * @param width
+     * @return true if the matrix is nonsingular, or false otherwise.
+     */
+    static boolean LUDecomposition(double[] matrix0, int[] row_perm, int width) {
+        double row_scale[] = new double[width];
+        // Determine implicit scaling information by looping over rows
+        {
+            int i, j;
+            int ptr, rs;
+            double big, temp;
+
+            ptr = 0;
+            rs = 0;
+
+            // For each row ...
+            i = width;
+            while (i-- != 0) {
+                big = 0.0;
+
+                // For each column, find the largest element in the row
+                j = width;
+                while (j-- != 0) {
+                    temp = matrix0[ptr++];
+                    temp = Math.abs(temp);
+                    if (temp > big) {
+                        big = temp;
+                    }
+                }
+                // Is the matrix singular?
+                if (big == 0.0) {
+                    return false;
+                }
+                row_scale[rs++] = 1.0 / big;
+            }
+        }
+
+        {
+            int j;
+            int mtx = 0;
+
+            // For all columns, execute Crout's method
+            for (j = 0; j < width; j++) {
+                int i, imax, k;
+                int target, p1, p2;
+                double sum, big, temp;
+
+                // Determine elements of upper diagonal matrix U
+                for (i = 0; i < j; i++) {
+                    target = mtx + (width * i) + j;
+                    sum = matrix0[target];
+                    k = i;
+                    p1 = mtx + (width * i);
+                    p2 = mtx + j;
+                    while (k-- != 0) {
+                        sum -= matrix0[p1] * matrix0[p2];
+                        p1++;
+                        p2 += width;
+                    }
+                    matrix0[target] = sum;
+                }
+
+                // Search for largest pivot element and calculate
+                // intermediate elements of lower diagonal matrix L.
+                big = 0.0;
+                imax = -1;
+                for (i = j; i < width; i++) {
+                    target = mtx + (width * i) + j;
+                    sum = matrix0[target];
+                    k = j;
+                    p1 = mtx + (width * i);
+                    p2 = mtx + j;
+                    while (k-- != 0) {
+                        sum -= matrix0[p1] * matrix0[p2];
+                        p1++;
+                        p2 += width;
+                    }
+                    matrix0[target] = sum;
+
+                    // Is this the best pivot so far?
+                    if ((temp = row_scale[i] * Math.abs(sum)) >= big) {
+                        big = temp;
+                        imax = i;
+                    }
+                }
+
+                if (imax < 0) {
+                    throw new RuntimeException();
+                }
+
+                // Is a row exchange necessary?
+                if (j != imax) {
+                    // Yes: exchange rows
+                    k = width;
+                    p1 = mtx + (width * imax);
+                    p2 = mtx + (width * j);
+                    while (k-- != 0) {
+                        temp = matrix0[p1];
+                        matrix0[p1++] = matrix0[p2];
+                        matrix0[p2++] = temp;
+                    }
+
+                    // Record change in scale factor
+                    row_scale[imax] = row_scale[j];
+                }
+
+                // Record row permutation
+                row_perm[j] = imax;
+
+                // Is the matrix singular
+                if (matrix0[(mtx + (width * j) + j)] == 0.0) {
+                    return false;
+                }
+
+                // Divide elements of lower diagonal matrix L by pivot
+                if (j != width - 1) {
+                    temp = 1.0 / (matrix0[(mtx + (width * j) + j)]);
+                    target = mtx + (width * (j + 1)) + j;
+                    i = (width - 1) - j;
+                    while (i-- != 0) {
+                        matrix0[target] *= temp;
+                        target += width;
+                    }
+                }
+            }
+        }
+        return true;
+    }
+
+    public double[][] matrix;
+    protected double[] temp = new double[4];
+
+    public Matrix4x4() {
+        init();
+        matrix[0][0] = 1;
+        matrix[1][1] = 1;
+        matrix[2][2] = 1;
+        matrix[3][3] = 1;
+    }
+
+    public Matrix4x4(double v11, double v12, double v13, double v14,
+            double v21, double v22, double v23, double v24, double v31,
+            double v32, double v33, double v34, double v41, double v42,
+            double v43, double v44) {
+        init();
+        double[] m = matrix[0];
+        m[0] = v11;
+        m[1] = v12;
+        m[2] = v13;
+        m[3] = v14;
+        m = matrix[1];
+        m[0] = v21;
+        m[1] = v22;
+        m[2] = v23;
+        m[3] = v24;
+        m = matrix[2];
+        m[0] = v31;
+        m[1] = v32;
+        m[2] = v33;
+        m[3] = v34;
+        m = matrix[3];
+        m[0] = v41;
+        m[1] = v42;
+        m[2] = v43;
+        m[3] = v44;
+    }
+
+    /**
+     * Initialising constructor from a 1d array. Assumes row-major ordering
+     * (column index increases faster).
+     * 
+     * @param array
+     */
+    public Matrix4x4(double[] array) {
+        if (array.length != 9 && array.length != 16) {
+            throw new RuntimeException("Array.length must == 9 or 16");
+        }
+        init();
+        if (array.length == 16) {
+            matrix[0][0] = array[0];
+            matrix[0][1] = array[1];
+            matrix[0][2] = array[2];
+            matrix[0][3] = array[3];
+
+            matrix[1][0] = array[4];
+            matrix[1][1] = array[5];
+            matrix[1][2] = array[6];
+            matrix[1][3] = array[7];
+
+            matrix[2][0] = array[8];
+            matrix[2][1] = array[9];
+            matrix[2][2] = array[10];
+            matrix[2][3] = array[11];
+
+            matrix[3][0] = array[12];
+            matrix[3][1] = array[13];
+            matrix[3][2] = array[14];
+            matrix[3][3] = array[15];
+        } else {
+            matrix[0][0] = array[0];
+            matrix[0][1] = array[1];
+            matrix[0][2] = array[2];
+
+            matrix[1][0] = array[3];
+            matrix[1][1] = array[4];
+            matrix[1][2] = array[5];
+
+            matrix[2][0] = array[6];
+            matrix[2][1] = array[7];
+            matrix[2][2] = array[8];
+
+            matrix[3][0] = array[9];
+            matrix[3][1] = array[10];
+            matrix[3][2] = array[11];
+            matrix[3][3] = 1;
+        }
+    }
+
+    public Matrix4x4(Matrix4x4 m) {
+        init();
+        for (int i = 0; i < 4; i++) {
+            double[] mi = matrix[i];
+            double[] mmi = m.matrix[i];
+            mi[0] = mmi[0];
+            mi[1] = mmi[1];
+            mi[2] = mmi[2];
+            mi[3] = mmi[3];
+        }
+    }
+
+    public Matrix4x4 add(Matrix4x4 rhs) {
+        Matrix4x4 result = new Matrix4x4(this);
+        return result.addSelf(rhs);
+    }
+
+    public Matrix4x4 addSelf(Matrix4x4 m) {
+        for (int i = 0; i < 4; i++) {
+            double[] mi = matrix[i];
+            double[] rhsm = m.matrix[i];
+            mi[0] += rhsm[0];
+            mi[1] += rhsm[1];
+            mi[2] += rhsm[2];
+            mi[3] += rhsm[3];
+        }
+        return this;
+    }
+
+    /**
+     * Creates a copy of the given vector, transformed by this matrix.
+     * 
+     * @param v
+     * @return transformed vector
+     */
+    public Vec3D applyTo(XYZ v) {
+        return applyToSelf(new Vec3D(v));
+    }
+
+    public Vec3D applyToSelf(Vec3D v) {
+        for (int i = 0; i < 4; i++) {
+            double[] m = matrix[i];
+            temp[i] = v.x * m[0] + v.y * m[1] + v.z * m[2] + m[3];
+        }
+        v.set((float) temp[0], (float) temp[1], (float) temp[2]).scaleSelf(
+                (float) (1.0 / temp[3]));
+        return v;
+    }
+
+    public Matrix4x4 copy() {
+        return new Matrix4x4(this);
+    }
+
+    public Matrix4x4 getInverted() {
+        return new Matrix4x4(this).invert();
+    }
+
+    public Matrix4x4 getRotatedAroundAxis(roVec3D axis, double theta) {
+        return new Matrix4x4(this).rotateAroundAxis(axis, theta);
+    }
+
+    public Matrix4x4 getRotatedX(double theta) {
+        return new Matrix4x4(this).rotateX(theta);
+    }
+
+    public Matrix4x4 getRotatedY(double theta) {
+        return new Matrix4x4(this).rotateY(theta);
+    }
+
+    public Matrix4x4 getRotatedZ(double theta) {
+        return new Matrix4x4(this).rotateZ(theta);
+    }
+
+    public Matrix4x4 getTransposed() {
+        return new Matrix4x4(this).transpose();
+    }
+
+    public Matrix4x4 identity() {
+        double[] m;
+        m = matrix[0];
+        m[1] = m[2] = m[3] = 0;
+        m = matrix[1];
+        m[0] = m[2] = m[3] = 0;
+        m = matrix[2];
+        m[0] = m[1] = m[3] = 0;
+        m = matrix[3];
+        m[0] = m[1] = m[2] = 0;
+        matrix[0][0] = 1;
+        matrix[1][1] = 1;
+        matrix[2][2] = 1;
+        matrix[3][3] = 1;
+        return this;
+    }
+
+    private final void init() {
+        matrix = new double[][] {
+                new double[4], new double[4], new double[4], new double[4]
+        };
+    }
+
+    /**
+     * Matrix Inversion using Cramer's Method Computes Adjoint matrix divided by
+     * determinant Code modified from
+     * http://www.intel.com/design/pentiumiii/sml/24504301.pdf
+     * 
+     * @return itself
+     */
+    public Matrix4x4 invert() {
+        final double[] tmp = new double[12];
+        final double[] src = new double[16];
+        final double[] dst = new double[16];
+        final double[] mat = toArray(null);
+
+        for (int i = 0; i < 4; i++) {
+            int i4 = i << 2;
+            src[i] = mat[i4];
+            src[i + 4] = mat[i4 + 1];
+            src[i + 8] = mat[i4 + 2];
+            src[i + 12] = mat[i4 + 3];
+        }
+
+        // calculate pairs for first 8 elements (cofactors)
+        tmp[0] = src[10] * src[15];
+        tmp[1] = src[11] * src[14];
+        tmp[2] = src[9] * src[15];
+        tmp[3] = src[11] * src[13];
+        tmp[4] = src[9] * src[14];
+        tmp[5] = src[10] * src[13];
+        tmp[6] = src[8] * src[15];
+        tmp[7] = src[11] * src[12];
+        tmp[8] = src[8] * src[14];
+        tmp[9] = src[10] * src[12];
+        tmp[10] = src[8] * src[13];
+        tmp[11] = src[9] * src[12];
+
+        // calculate first 8 elements (cofactors)
+        double src0 = src[0];
+        double src1 = src[1];
+        double src2 = src[2];
+        double src3 = src[3];
+        double src4 = src[4];
+        double src5 = src[5];
+        double src6 = src[6];
+        double src7 = src[7];
+        dst[0] = tmp[0] * src5 + tmp[3] * src6 + tmp[4] * src7;
+        dst[0] -= tmp[1] * src5 + tmp[2] * src6 + tmp[5] * src7;
+        dst[1] = tmp[1] * src4 + tmp[6] * src6 + tmp[9] * src7;
+        dst[1] -= tmp[0] * src4 + tmp[7] * src6 + tmp[8] * src7;
+        dst[2] = tmp[2] * src4 + tmp[7] * src5 + tmp[10] * src7;
+        dst[2] -= tmp[3] * src4 + tmp[6] * src5 + tmp[11] * src7;
+        dst[3] = tmp[5] * src4 + tmp[8] * src5 + tmp[11] * src6;
+        dst[3] -= tmp[4] * src4 + tmp[9] * src5 + tmp[10] * src6;
+        dst[4] = tmp[1] * src1 + tmp[2] * src2 + tmp[5] * src3;
+        dst[4] -= tmp[0] * src1 + tmp[3] * src2 + tmp[4] * src3;
+        dst[5] = tmp[0] * src0 + tmp[7] * src2 + tmp[8] * src3;
+        dst[5] -= tmp[1] * src0 + tmp[6] * src2 + tmp[9] * src3;
+        dst[6] = tmp[3] * src0 + tmp[6] * src1 + tmp[11] * src3;
+        dst[6] -= tmp[2] * src0 + tmp[7] * src1 + tmp[10] * src3;
+        dst[7] = tmp[4] * src0 + tmp[9] * src1 + tmp[10] * src2;
+        dst[7] -= tmp[5] * src0 + tmp[8] * src1 + tmp[11] * src2;
+
+        // calculate pairs for second 8 elements (cofactors)
+        tmp[0] = src2 * src7;
+        tmp[1] = src3 * src6;
+        tmp[2] = src1 * src7;
+        tmp[3] = src3 * src5;
+        tmp[4] = src1 * src6;
+        tmp[5] = src2 * src5;
+        tmp[6] = src0 * src7;
+        tmp[7] = src3 * src4;
+        tmp[8] = src0 * src6;
+        tmp[9] = src2 * src4;
+        tmp[10] = src0 * src5;
+        tmp[11] = src1 * src4;
+
+        // calculate second 8 elements (cofactors)
+        src0 = src[8];
+        src1 = src[9];
+        src2 = src[10];
+        src3 = src[11];
+        src4 = src[12];
+        src5 = src[13];
+        src6 = src[14];
+        src7 = src[15];
+        dst[8] = tmp[0] * src5 + tmp[3] * src6 + tmp[4] * src7;
+        dst[8] -= tmp[1] * src5 + tmp[2] * src6 + tmp[5] * src7;
+        dst[9] = tmp[1] * src4 + tmp[6] * src6 + tmp[9] * src7;
+        dst[9] -= tmp[0] * src4 + tmp[7] * src6 + tmp[8] * src7;
+        dst[10] = tmp[2] * src4 + tmp[7] * src5 + tmp[10] * src7;
+        dst[10] -= tmp[3] * src4 + tmp[6] * src5 + tmp[11] * src7;
+        dst[11] = tmp[5] * src4 + tmp[8] * src5 + tmp[11] * src6;
+        dst[11] -= tmp[4] * src4 + tmp[9] * src5 + tmp[10] * src6;
+        dst[12] = tmp[2] * src2 + tmp[5] * src3 + tmp[1] * src1;
+        dst[12] -= tmp[4] * src3 + tmp[0] * src1 + tmp[3] * src2;
+        dst[13] = tmp[8] * src3 + tmp[0] * src0 + tmp[7] * src2;
+        dst[13] -= tmp[6] * src2 + tmp[9] * src3 + tmp[1] * src0;
+        dst[14] = tmp[6] * src1 + tmp[11] * src3 + tmp[3] * src0;
+        dst[14] -= tmp[10] * src3 + tmp[2] * src0 + tmp[7] * src1;
+        dst[15] = tmp[10] * src2 + tmp[4] * src0 + tmp[9] * src1;
+        dst[15] -= tmp[8] * src1 + tmp[11] * src2 + tmp[5] * src0;
+
+        double det = 1.0 / (src[0] * dst[0] + src[1] * dst[1] + src[2] * dst[2] + src[3]
+                * dst[3]);
+
+        for (int i = 0, k = 0; i < 4; i++) {
+            double[] m = matrix[i];
+            for (int j = 0; j < 4; j++) {
+                m[j] = dst[k++] * det;
+            }
+        }
+        return this;
+    }
+
+    public Matrix4x4 lookAt(roVec3D eye, roVec3D target,
+            roVec3D up) {
+        Vec3D f = eye.sub(target).normalize();
+        Vec3D s = up.cross(f).normalize();
+        Vec3D t = f.cross(s).normalize();
+        return set(s.x, s.y, s.z, -s.dot(eye), t.x, t.y, t.z, -t.dot(eye), f.x,
+                f.y, f.z, -f.dot(eye), 0, 0, 0, 1);
+    }
+
+    public Matrix4x4 multiply(double factor) {
+        return new Matrix4x4(this).multiply(factor);
+    }
+
+    /**
+     * Matrix-Matrix Right-multiplication.
+     * 
+     * @param mat
+     * @return product as new matrix
+     */
+    public Matrix4x4 multiply(Matrix4x4 mat) {
+        return new Matrix4x4(this).multiplySelf(mat);
+    }
+
+    /**
+     * In-place matrix-scalar multiplication.
+     * 
+     * @param factor
+     * @return product applied to this matrix.
+     */
+    public Matrix4x4 multiplySelf(double factor) {
+        for (int i = 0; i < 4; i++) {
+            double[] m = matrix[i];
+            m[0] *= factor;
+            m[1] *= factor;
+            m[2] *= factor;
+            m[3] *= factor;
+        }
+        return this;
+    }
+
+    public Matrix4x4 multiplySelf(Matrix4x4 mat) {
+        double[] mm0 = mat.matrix[0];
+        double[] mm1 = mat.matrix[1];
+        double[] mm2 = mat.matrix[2];
+        double[] mm3 = mat.matrix[3];
+        for (int i = 0; i < 4; i++) {
+            double[] m = matrix[i];
+            for (int j = 0; j < 4; j++) {
+                temp[j] = m[0] * mm0[j] + m[1] * mm1[j] + m[2] * mm2[j] + m[3]
+                        * mm3[j];
+            }
+            m[0] = temp[0];
+            m[1] = temp[1];
+            m[2] = temp[2];
+            m[3] = temp[3];
+        }
+        return this;
+    }
+
+    /**
+     * Applies rotation about arbitrary axis to matrix
+     * 
+     * @param axis
+     * @param theta
+     * @return rotation applied to this matrix
+     */
+    public Matrix4x4 rotateAroundAxis(roVec3D axis, double theta) {
+        double x, y, z, s, c, t, tx, ty;
+        x = axis.x();
+        y = axis.y();
+        z = axis.z();
+        s = Math.sin(theta);
+        c = Math.cos(theta);
+        t = 1 - c;
+        tx = t * x;
+        ty = t * y;
+        TEMP.set(tx * x + c, tx * y + s * z, tx * z - s * y, 0, tx * y - s * z,
+                ty * y + c, ty * z + s * x, 0, tx * z + s * y, ty * z - s * x,
+                t * z * z + c, 0, 0, 0, 0, 1);
+        return this.multiplySelf(TEMP);
+    }
+
+    /**
+     * Applies rotation about X to this matrix.
+     * 
+     * @param theta
+     *            rotation angle in radians
+     * @return itself
+     */
+    public Matrix4x4 rotateX(double theta) {
+        TEMP.identity();
+        TEMP.matrix[1][1] = TEMP.matrix[2][2] = Math.cos(theta);
+        TEMP.matrix[2][1] = Math.sin(theta);
+        TEMP.matrix[1][2] = -TEMP.matrix[2][1];
+        return this.multiplySelf(TEMP);
+    }
+
+    /**
+     * Applies rotation about Y to this matrix.
+     * 
+     * @param theta
+     *            rotation angle in radians
+     * @return itself
+     */
+    public Matrix4x4 rotateY(double theta) {
+        TEMP.identity();
+        TEMP.matrix[0][0] = TEMP.matrix[2][2] = Math.cos(theta);
+        TEMP.matrix[0][2] = Math.sin(theta);
+        TEMP.matrix[2][0] = -TEMP.matrix[0][2];
+        return this.multiplySelf(TEMP);
+    }
+
+    // Apply Rotation about Z to Matrix
+    public Matrix4x4 rotateZ(double theta) {
+        TEMP.identity();
+        TEMP.matrix[0][0] = TEMP.matrix[1][1] = Math.cos(theta);
+        TEMP.matrix[1][0] = Math.sin(theta);
+        TEMP.matrix[0][1] = -TEMP.matrix[1][0];
+        return this.multiplySelf(TEMP);
+    }
+
+    public Matrix4x4 scale(double scale) {
+        return new Matrix4x4(this).scaleSelf(scale);
+    }
+
+    public Matrix4x4 scale(double scaleX, double scaleY, double scaleZ) {
+        return new Matrix4x4(this).scaleSelf(scaleX, scaleY, scaleZ);
+    }
+
+    public Matrix4x4 scale(roVec3D scale) {
+        return new Matrix4x4(this).scaleSelf(scale.x(), scale.y(), scale.z());
+    }
+
+    public Matrix4x4 scaleSelf(double scale) {
+        return scaleSelf(scale, scale, scale);
+    }
+
+    public Matrix4x4 scaleSelf(double scaleX, double scaleY, double scaleZ) {
+        TEMP.identity();
+        TEMP.setScale(scaleX, scaleY, scaleZ);
+        return this.multiplySelf(TEMP);
+    }
+
+    public Matrix4x4 scaleSelf(roVec3D scale) {
+        return scaleSelf(scale.x(), scale.y(), scale.z());
+    }
+
+    public Matrix4x4 set(double a, double b, double c, double d, double e,
+            double f, double g, double h, double i, double j, double k,
+            double l, double m, double n, double o, double p) {
+        double[] mat = matrix[0];
+        mat[0] = a;
+        mat[1] = b;
+        mat[2] = c;
+        mat[3] = d;
+        mat = matrix[1];
+        mat[0] = e;
+        mat[1] = f;
+        mat[2] = g;
+        mat[3] = h;
+        mat = matrix[2];
+        mat[0] = i;
+        mat[1] = j;
+        mat[2] = k;
+        mat[3] = l;
+        mat = matrix[3];
+        mat[0] = m;
+        mat[1] = n;
+        mat[2] = o;
+        mat[3] = p;
+        return this;
+    }
+
+    public Matrix4x4 set(Matrix4x4 mat) {
+        for (int i = 0; i < 4; i++) {
+            double[] m = matrix[i];
+            double[] n = mat.matrix[i];
+            m[0] = n[0];
+            m[1] = n[1];
+            m[2] = n[2];
+            m[3] = n[3];
+        }
+        return this;
+    }
+
+    public Matrix4x4 setFrustum(double left, double right, double top,
+            double bottom, double near, double far) {
+        return set((2.0 * near) / (right - left), 0, (left + right)
+                / (right - left), 0, 0, (2.0 * near) / (top - bottom),
+                (top + bottom) / (top - bottom), 0, 0, 0, -(near + far)
+                        / (far - near), (-2 * near * far) / (far - near), 0, 0,
+                -1, 0);
+    }
+
+    public Matrix4x4 setOrtho(double left, double right, double top,
+            double bottom, double near, double far) {
+        return set(2.0 / (right - left), 0, 0, (left + right) / (right - left),
+                0, 2.0 / (top - bottom), 0, (top + bottom) / (top - bottom), 0,
+                0, -2.0 / (far - near), (far + near) / (far - near), 0, 0, 0, 1);
+    }
+
+    public Matrix4x4 setPerspective(double fov, double aspect, double near,
+            double far) {
+        double y = near * Math.tan(0.5 * MathUtils.radians(fov));
+        double x = aspect * y;
+        return setFrustum(-x, x, y, -y, near, far);
+    }
+
+    public Matrix4x4 setPosition(double x, double y, double z) {
+        matrix[0][3] = x;
+        matrix[1][3] = y;
+        matrix[2][3] = z;
+        return this;
+    }
+
+    public Matrix4x4 setScale(double scaleX, double scaleY, double scaleZ) {
+        matrix[0][0] = scaleX;
+        matrix[1][1] = scaleY;
+        matrix[2][2] = scaleZ;
+        return this;
+    }
+
+    public Matrix4x4 sub(Matrix4x4 m) {
+        return new Matrix4x4(this).subSelf(m);
+    }
+
+    public Matrix4x4 subSelf(Matrix4x4 mat) {
+        for (int i = 0; i < 4; i++) {
+            double[] m = matrix[i];
+            double[] n = mat.matrix[i];
+            m[0] -= n[0];
+            m[1] -= n[1];
+            m[2] -= n[2];
+            m[3] -= n[3];
+        }
+        return this;
+    }
+
+    /**
+     * Copies all matrix elements into an linear array.
+     * 
+     * @param result
+     *            array (or null to create a new one)
+     * @return matrix as 16 element array
+     */
+    public double[] toArray(double[] result) {
+        if (result == null) {
+            result = new double[16];
+        }
+        for (int i = 0, k = 0; i < 4; i++) {
+            double[] m = matrix[i];
+            for (int j = 0; j < 4; j++) {
+                result[k++] = m[j];
+            }
+        }
+        return result;
+    }
+
+    public float[] toFloatArray(float[] result) {
+        if (result == null) {
+            result = new float[16];
+        }
+        double[] tmp = toArray(null);
+        for (int i = 0; i < 16; i++) {
+            result[i] = (float) tmp[i];
+        }
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    public String toString() {
+        return "| " + matrix[0][0] + " " + matrix[0][1] + " " + matrix[0][2]
+                + " " + matrix[0][3] + " |\n" + "| " + matrix[1][0] + " "
+                + matrix[1][1] + " " + matrix[1][2] + " " + matrix[1][3]
+                + " |\n" + "| " + matrix[2][0] + " " + matrix[2][1] + " "
+                + matrix[2][2] + " " + matrix[2][3] + " |\n" + "| "
+                + matrix[3][0] + " " + matrix[3][1] + " " + matrix[3][2] + " "
+                + matrix[3][3] + " |";
+    }
+
+    public float[] toTransposedFloatArray(float[] result) {
+        if (result == null) {
+            result = new float[16];
+        }
+        for (int i = 0, k = 0; i < 4; i++) {
+            for (int j = 0; j < 4; j++) {
+                result[k++] = (float) matrix[j][i];
+            }
+        }
+        return result;
+    }
+
+    public Matrix4x4 translate(double dx, double dy, double dz) {
+        return new Matrix4x4(this).translateSelf(dx, dy, dz);
+    }
+
+    public Matrix4x4 translate(roVec3D trans) {
+        return new Matrix4x4(this).translateSelf(trans.x(), trans.y(),
+                trans.z());
+    }
+
+    public Matrix4x4 translateSelf(double dx, double dy, double dz) {
+        TEMP.identity();
+        TEMP.setPosition(dx, dy, dz);
+        return this.multiplySelf(TEMP);
+    }
+
+    public Matrix4x4 translateSelf(roVec3D trans) {
+        return translateSelf(trans.x(), trans.y(), trans.z());
+    }
+
+    /**
+     * Converts the matrix (in-place) between column-major to row-major order
+     * (and vice versa).
+     * 
+     * @return itself
+     */
+    public Matrix4x4 transpose() {
+        return set(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0],
+                matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1],
+                matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2],
+                matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/toxi/geom/MatrixSizeException.java b/src/main/java/toxi/geom/MatrixSizeException.java
new file mode 100644
index 0000000..105ca7a
--- /dev/null
+++ b/src/main/java/toxi/geom/MatrixSizeException.java
@@ -0,0 +1,58 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright 1997-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * $Revision: 127 $
+ * $Date: 2008-02-28 20:18:51 +0000 (Thu, 28 Feb 2008) $
+ * $State$
+ */
+
+package toxi.geom;
+
+/**
+ * Indicates that an operation cannot be completed properly because of a
+ * mismatch in the sizes of object attributes.
+ */
+public class MatrixSizeException extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Create the exception object with default values.
+     */
+    public MatrixSizeException() {
+    }
+
+    /**
+     * Create the exception object that outputs a message.
+     * 
+     * @param str
+     *            the message string to be output.
+     */
+    public MatrixSizeException(String str) {
+        super(str);
+    }
+
+}
diff --git a/src/main/java/com/syncleus/spangraph/geom/OctreeVisitor.java b/src/main/java/toxi/geom/OctreeVisitor.java
similarity index 97%
rename from src/main/java/com/syncleus/spangraph/geom/OctreeVisitor.java
rename to src/main/java/toxi/geom/OctreeVisitor.java
index 9b9f28d..cc9becb 100644
--- a/src/main/java/com/syncleus/spangraph/geom/OctreeVisitor.java
+++ b/src/main/java/toxi/geom/OctreeVisitor.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 /**
  * This interface is the core part of the visitor pattern application for
diff --git a/src/main/java/com/syncleus/spangraph/geom/Origin3D.java b/src/main/java/toxi/geom/Origin3D.java
similarity index 89%
rename from src/main/java/com/syncleus/spangraph/geom/Origin3D.java
rename to src/main/java/toxi/geom/Origin3D.java
index 5e5598c..622bacf 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Origin3D.java
+++ b/src/main/java/toxi/geom/Origin3D.java
@@ -25,7 +25,9 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
+
+import toxi.geom.Vec3D.Axis;
 
 /**
  * This class defines an origin and set of axis vectors for a 3D cartesian
@@ -33,8 +35,8 @@ package com.syncleus.spangraph.geom;
  */
 public class Origin3D {
 
-    public ReadonlyVec3D origin;
-    public ReadonlyVec3D xAxis, yAxis, zAxis;
+    public roVec3D origin;
+    public roVec3D xAxis, yAxis, zAxis;
 
     /**
      * Creates a new origin at the world origin using the standard XYZ axes
@@ -47,12 +49,12 @@ public class Origin3D {
         this(new Vec3D(x, y, z));
     }
 
-//    public Origin3D(Matrix4x4 mat) {
-//        this.origin = mat.applyToSelf(new Vec3D());
-//        this.xAxis = mat.applyTo(Vec3D.X_AXIS).subSelf(origin).normalize();
-//        this.yAxis = mat.applyTo(Vec3D.Y_AXIS).subSelf(origin).normalize();
-//        zAxis = xAxis.crossInto(yAxis, new Vec3D());
-//    }
+    public Origin3D(Matrix4x4 mat) {
+        this.origin = mat.applyToSelf(new Vec3D());
+        this.xAxis = mat.applyTo(Vec3D.X_AXIS).subSelf(origin).normalize();
+        this.yAxis = mat.applyTo(Vec3D.Y_AXIS).subSelf(origin).normalize();
+        zAxis = xAxis.crossInto(yAxis, new Vec3D());
+    }
 
     /**
      * Creates a new origin at the given origin using the standard XYZ axes
diff --git a/src/main/java/com/syncleus/spangraph/geom/Plane.java b/src/main/java/toxi/geom/Plane.java
similarity index 79%
rename from src/main/java/com/syncleus/spangraph/geom/Plane.java
rename to src/main/java/toxi/geom/Plane.java
index 2cdb7eb..dae1260 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Plane.java
+++ b/src/main/java/toxi/geom/Plane.java
@@ -25,14 +25,12 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 
-import toxi.geom.mesh.Mesh3D;
-import toxi.geom.mesh.TriangleMesh;
 import toxi.math.MathUtils;
 
 /**
@@ -43,7 +41,7 @@ import toxi.math.MathUtils;
 public class Plane extends Vec3D implements Shape3D {
 
     /**
-     * Classifier constant for {@link Plane#classifyPoint(ReadonlyVec3D, float)}
+     * Classifier constant for {@link Plane#classifyPoint(roVec3D, float)}
      */
     public enum Classifier {
         FRONT,
@@ -63,7 +61,7 @@ public class Plane extends Vec3D implements Shape3D {
         normal = Vec3D.Y_AXIS.copy();
     }
 
-    public Plane(ReadonlyVec3D origin, ReadonlyVec3D norm) {
+    public Plane(roVec3D origin, roVec3D norm) {
         super(origin);
         normal = norm.getNormalized();
     }
@@ -78,7 +76,7 @@ public class Plane extends Vec3D implements Shape3D {
      * 
      * @return One of the 3 classification types: FRONT, BACK, ON_PLANE
      */
-    public Classifier classifyPoint(ReadonlyVec3D p, float tolerance) {
+    public Classifier classifyPoint(XYZ p, float tolerance) {
         float d = this.sub(p).normalize().dot(normal);
         if (d < -tolerance) {
             return Classifier.FRONT;
@@ -88,7 +86,7 @@ public class Plane extends Vec3D implements Shape3D {
         return Classifier.ON_PLANE;
     }
 
-    public boolean containsPoint(ReadonlyVec3D p) {
+    public boolean containsPoint(XYZ p) {
         return classifyPoint(p, MathUtils.EPS) == Classifier.ON_PLANE;
     }
 
@@ -115,7 +113,7 @@ public class Plane extends Vec3D implements Shape3D {
      * @param r
      * @return intersection point or null if ray doesn't intersect plane
      */
-    public ReadonlyVec3D getIntersectionWithRay(Ray3D r) {
+    public roVec3D getIntersectionWithRay(Ray3D r) {
         float denom = normal.dot(r.getDirection());
         if (denom > MathUtils.EPS) {
             float u = normal.dot(this.sub(r)) / denom;
@@ -125,14 +123,14 @@ public class Plane extends Vec3D implements Shape3D {
         }
     }
 
-    public Vec3D getProjectedPoint(Vec3D p) {
+    public XYZ getProjectedPoint(Vec3D p) {
         Vec3D dir;
         if (normal.dot(sub(p)) < 0) {
             dir = normal.getInverted();
         } else {
             dir = normal;
         }
-        Vec3D proj = new Ray3D(p, dir)
+        XYZ proj = new Ray3D(p, dir)
                 .getPointAtDistance(getDistanceToPoint(p));
         return proj;
     }
@@ -193,35 +191,35 @@ public class Plane extends Vec3D implements Shape3D {
         return new Ray3D(anchor, dir);
     }
 
-    /**
-     * Creates a TriangleMesh representation of the plane as a finite, squared
-     * quad of the requested size, centred around the current plane point.
-     * 
-     * @param size
-     *            desired edge length
-     * @return mesh
-     */
-    public Mesh3D toMesh(float size) {
-        return toMesh(null, size);
-    }
-
-    public Mesh3D toMesh(Mesh3D mesh, float size) {
-        if (mesh == null) {
-            mesh = new TriangleMesh("plane", 4, 2);
-        }
-        ReadonlyVec3D p = equalsWithTolerance(Vec3D.ZERO, 0.01f) ? add(0.01f,
-                0.01f, 0.01f) : this;
-        size *= 0.5f;
-        Vec3D n = p.cross(normal).normalizeTo(size);
-        Vec3D m = n.cross(normal).normalizeTo(size);
-        Vec3D a = this.add(n).addSelf(m);
-        Vec3D b = this.add(n).subSelf(m);
-        Vec3D c = this.sub(n).subSelf(m);
-        Vec3D d = this.sub(n).addSelf(m);
-        mesh.addFace(a, d, b, null, null, null, null);
-        mesh.addFace(b, d, c, null, null, null, null);
-        return mesh;
-    }
+//    /**
+//     * Creates a TriangleMesh representation of the plane as a finite, squared
+//     * quad of the requested size, centred around the current plane point.
+//     *
+//     * @param size
+//     *            desired edge length
+//     * @return mesh
+//     */
+//    public Mesh3D toMesh(float size) {
+//        return toMesh(null, size);
+//    }
+//
+//    public Mesh3D toMesh(Mesh3D mesh, float size) {
+//        if (mesh == null) {
+//            mesh = new TriangleMesh("plane", 4, 2);
+//        }
+//        roVec3D p = equalsWithTolerance(Vec3D.ZERO, 0.01f) ? add(0.01f,
+//                0.01f, 0.01f) : this;
+//        size *= 0.5f;
+//        Vec3D n = p.cross(normal).normalizeTo(size);
+//        Vec3D m = n.cross(normal).normalizeTo(size);
+//        Vec3D a = this.add(n).addSelf(m);
+//        Vec3D b = this.add(n).subSelf(m);
+//        Vec3D c = this.sub(n).subSelf(m);
+//        Vec3D d = this.sub(n).addSelf(m);
+//        mesh.addFace(a, d, b, null, null, null, null);
+//        mesh.addFace(b, d, c, null, null, null, null);
+//        return mesh;
+//    }
 
     public String toString() {
         StringBuffer sb = new StringBuffer();
diff --git a/src/main/java/toxi/geom/PlaneIntersector.java b/src/main/java/toxi/geom/PlaneIntersector.java
new file mode 100644
index 0000000..bcfff06
--- /dev/null
+++ b/src/main/java/toxi/geom/PlaneIntersector.java
@@ -0,0 +1,47 @@
+package toxi.geom;
+
+import toxi.math.MathUtils;
+
+public class PlaneIntersector implements Intersector3D {
+
+    private Plane plane;
+    private final IsectData3D isec;
+
+    public PlaneIntersector(Plane p) {
+        this.plane = p;
+        this.isec = new IsectData3D();
+    }
+
+    public IsectData3D getIntersectionData() {
+        return isec;
+    }
+
+    /**
+     * @return the box
+     */
+    public Plane getPlane() {
+        return plane;
+    }
+
+    public boolean intersectsRay(Ray3D ray) {
+        float d = -plane.normal.dot(plane);
+        float numer = plane.normal.dot(ray) + d;
+        float denom = plane.normal.dot(ray.dir);
+
+        // normal is orthogonal to vector, can't intersect
+        if (isec.isIntersection = (MathUtils.abs(denom) >= MathUtils.EPS)) {
+            isec.dist = -(numer / denom);
+            isec.pos = ray.getPointAtDistance(isec.dist);
+            isec.normal = plane.normal;
+        }
+        return isec.isIntersection;
+    }
+
+    /**
+     * @param p
+     *            the plane to set
+     */
+    public void setPlane(Plane p) {
+        this.plane = p;
+    }
+}
diff --git a/src/main/java/com/syncleus/spangraph/geom/PointCloud3D.java b/src/main/java/toxi/geom/PointCloud3D.java
similarity index 87%
rename from src/main/java/com/syncleus/spangraph/geom/PointCloud3D.java
rename to src/main/java/toxi/geom/PointCloud3D.java
index 59d180a..dbe8a46 100644
--- a/src/main/java/com/syncleus/spangraph/geom/PointCloud3D.java
+++ b/src/main/java/toxi/geom/PointCloud3D.java
@@ -25,7 +25,9 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
+
+import toxi.math.MathUtils;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -45,7 +47,7 @@ public class PointCloud3D implements Iterable<Vec3D> {
     }
 
     public PointCloud3D(int numPoints) {
-        points = new ArrayList<Vec3D>(numPoints);
+        points = new ArrayList(numPoints);
         clear();
     }
 
@@ -66,20 +68,20 @@ public class PointCloud3D implements Iterable<Vec3D> {
         return this;
     }
 
-//    /**
-//     * Applies the given transformation matrix to all points in the cloud.
-//     *
-//     * @param m
-//     *            transformation matrix
-//     * @return itself
-//     */
-//    public PointCloud3D applyMatrix(Matrix4x4 m) {
-//        for (Vec3D p : points) {
-//            p.set(m.applyTo(p));
-//        }
-//        updateBounds();
-//        return this;
-//    }
+    /**
+     * Applies the given transformation matrix to all points in the cloud.
+     *
+     * @param m
+     *            transformation matrix
+     * @return itself
+     */
+    public PointCloud3D applyMatrix(Matrix4x4 m) {
+        for (Vec3D p : points) {
+            p.set(m.applyTo(p));
+        }
+        updateBounds();
+        return this;
+    }
 
     /**
      * Updates all points in the cloud so that their new centroid is at the
@@ -99,7 +101,7 @@ public class PointCloud3D implements Iterable<Vec3D> {
      *            new centroid
      * @return itself
      */
-    public PointCloud3D center(ReadonlyVec3D origin) {
+    public PointCloud3D center(roVec3D origin) {
         getCentroid();
         Vec3D delta = origin != null ? origin.sub(centroid) : centroid
                 .getInverted();
@@ -132,7 +134,7 @@ public class PointCloud3D implements Iterable<Vec3D> {
      */
     public PointCloud3D copy() {
         PointCloud3D c = new PointCloud3D(points.size());
-        for (ReadonlyVec3D p : points) {
+        for (roVec3D p : points) {
             c.addPoint(p.copy());
         }
         return c;
@@ -169,7 +171,7 @@ public class PointCloud3D implements Iterable<Vec3D> {
      * @param p
      * @return true, if point has been removed.
      */
-    public boolean removePoint(ReadonlyVec3D p) {
+    public boolean removePoint(roVec3D p) {
         return points.remove(p);
     }
 
@@ -194,7 +196,7 @@ public class PointCloud3D implements Iterable<Vec3D> {
         }
         centroid.set(min.add(max).scaleSelf(0.5f));
         radiusSquared = 0;
-        for (ReadonlyVec3D p : points) {
+        for (roVec3D p : points) {
             radiusSquared = MathUtils.max(radiusSquared,
                     p.distanceToSquared(centroid));
         }
diff --git a/src/main/java/com/syncleus/spangraph/geom/PointOctree.java b/src/main/java/toxi/geom/PointOctree.java
similarity index 87%
rename from src/main/java/com/syncleus/spangraph/geom/PointOctree.java
rename to src/main/java/toxi/geom/PointOctree.java
index c646e09..c5de07f 100644
--- a/src/main/java/com/syncleus/spangraph/geom/PointOctree.java
+++ b/src/main/java/toxi/geom/PointOctree.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -45,24 +45,24 @@ public class PointOctree extends AABB implements Shape3D {
      * alternative tree recursion limit, number of world units when cells are
      * not subdivided any further
      */
-    protected float minNodeSize = 4;
+    public float minNodeSize = 4;
 
     /**
 	 * 
 	 */
-    protected PointOctree parent;
+    public final PointOctree parent;
 
-    protected PointOctree[] children;
+    public PointOctree[] children;
 
     protected byte numChildren;
 
-    protected ArrayList<Vec3D> points;
+    protected ArrayList<XYZ> points;
 
     protected float size, halfSize;
 
     protected Vec3D offset;
 
-    private int depth = 0;
+    public int depth = 0;
 
     private boolean isAutoReducing = false;
 
@@ -80,6 +80,7 @@ public class PointOctree extends AABB implements Shape3D {
     private PointOctree(PointOctree p, Vec3D o, float halfSize) {
         super(o.add(halfSize, halfSize, halfSize), new Vec3D(halfSize,
                 halfSize, halfSize));
+
         this.parent = p;
         this.halfSize = halfSize;
         this.size = halfSize * 2;
@@ -112,9 +113,9 @@ public class PointOctree extends AABB implements Shape3D {
      *            point collection
      * @return true, if all points have been added successfully.
      */
-    public boolean addAll(Collection<Vec3D> points) {
+    public boolean addAll(Collection<XYZ> points) {
         boolean addedAll = true;
-        for (Vec3D p : points) {
+        for (XYZ p : points) {
             addedAll &= addPoint(p);
         }
         return addedAll;
@@ -128,13 +129,13 @@ public class PointOctree extends AABB implements Shape3D {
      * @param p
      * @return true, if point has been added successfully
      */
-    public boolean addPoint(Vec3D p) {
+    public boolean addPoint(XYZ p) {
         // check if point is inside cube
         if (containsPoint(p)) {
             // only add points to leaves for now
             if (halfSize <= minNodeSize) {
                 if (points == null) {
-                    points = new ArrayList<Vec3D>();
+                    points = new ArrayList();
                 }
                 points.add(p);
                 return true;
@@ -174,7 +175,7 @@ public class PointOctree extends AABB implements Shape3D {
         }
     }
 
-    public boolean containsPoint(ReadonlyVec3D p) {
+    public boolean containsPoint(roVec3D p) {
         return p.isInAABB(this);
     }
 
@@ -210,7 +211,7 @@ public class PointOctree extends AABB implements Shape3D {
      *            point to check
      * @return leaf node or null if point is outside the tree dimensions
      */
-    public PointOctree getLeafForPoint(ReadonlyVec3D p) {
+    public PointOctree getLeafForPoint(XYZ p) {
         // if not a leaf node...
         if (p.isInAABB(this)) {
             if (numChildren > 0) {
@@ -262,7 +263,7 @@ public class PointOctree extends AABB implements Shape3D {
     /**
      * @return the offset
      */
-    public ReadonlyVec3D getOffset() {
+    public roVec3D getOffset() {
         return offset;
     }
 
@@ -276,17 +277,17 @@ public class PointOctree extends AABB implements Shape3D {
     /**
      * @return the points
      */
-    public List<Vec3D> getPoints() {
-        List<Vec3D> results = null;
+    public List<XYZ> getPoints() {
+        List<XYZ> results = null;
         if (points != null) {
-            results = new ArrayList<Vec3D>(points);
+            results = new ArrayList(points);
         } else if (numChildren > 0) {
             for (int i = 0; i < 8; i++) {
                 if (children[i] != null) {
-                    List<Vec3D> childPoints = children[i].getPoints();
+                    List<XYZ> childPoints = children[i].getPoints();
                     if (childPoints != null) {
                         if (results == null) {
-                            results = new ArrayList<Vec3D>();
+                            results = new ArrayList();
                         }
                         results.addAll(childPoints);
                     }
@@ -303,14 +304,14 @@ public class PointOctree extends AABB implements Shape3D {
      *            AABB
      * @return all points with the box volume
      */
-    public List<Vec3D> getPointsWithinBox(AABB b) {
-        ArrayList<Vec3D> results = null;
+    public List<XYZ> getPointsWithinBox(AABB b) {
+        ArrayList<XYZ> results = null;
         if (this.intersectsBox(b)) {
             if (points != null) {
-                for (Vec3D q : points) {
+                for (XYZ q : points) {
                     if (q.isInAABB(b)) {
                         if (results == null) {
-                            results = new ArrayList<Vec3D>();
+                            results = new ArrayList();
                         }
                         results.add(q);
                     }
@@ -318,10 +319,10 @@ public class PointOctree extends AABB implements Shape3D {
             } else if (numChildren > 0) {
                 for (int i = 0; i < 8; i++) {
                     if (children[i] != null) {
-                        List<Vec3D> points = children[i].getPointsWithinBox(b);
+                        List<XYZ> points = children[i].getPointsWithinBox(b);
                         if (points != null) {
                             if (results == null) {
-                                results = new ArrayList<Vec3D>();
+                                results = new ArrayList();
                             }
                             results.addAll(points);
                         }
@@ -339,14 +340,14 @@ public class PointOctree extends AABB implements Shape3D {
      *            sphere
      * @return selected points
      */
-    public List<Vec3D> getPointsWithinSphere(Sphere s) {
-        ArrayList<Vec3D> results = null;
+    public List<XYZ> getPointsWithinSphere(Sphere s) {
+        ArrayList<XYZ> results = null;
         if (this.intersectsSphere(s)) {
             if (points != null) {
-                for (Vec3D q : points) {
+                for (XYZ q : points) {
                     if (s.containsPoint(q)) {
                         if (results == null) {
-                            results = new ArrayList<Vec3D>();
+                            results = new ArrayList();
                         }
                         results.add(q);
                     }
@@ -354,11 +355,11 @@ public class PointOctree extends AABB implements Shape3D {
             } else if (numChildren > 0) {
                 for (int i = 0; i < 8; i++) {
                     if (children[i] != null) {
-                        List<Vec3D> points = children[i]
+                        List<XYZ> points = children[i]
                                 .getPointsWithinSphere(s);
                         if (points != null) {
                             if (results == null) {
-                                results = new ArrayList<Vec3D>();
+                                results = new ArrayList();
                             }
                             results.addAll(points);
                         }
@@ -376,7 +377,7 @@ public class PointOctree extends AABB implements Shape3D {
      * @param clipRadius
      * @return selected points
      */
-    public List<Vec3D> getPointsWithinSphere(Vec3D sphereOrigin,
+    public List<XYZ> getPointsWithinSphere(Vec3D sphereOrigin,
             float clipRadius) {
         return getPointsWithinSphere(new Sphere(sphereOrigin, clipRadius));
     }
@@ -412,7 +413,7 @@ public class PointOctree extends AABB implements Shape3D {
      *            point to delete
      * @return true, if the point was found & removed
      */
-    public boolean remove(ReadonlyVec3D p) {
+    public boolean remove(XYZ p) {
         boolean found = false;
         PointOctree leaf = getLeafForPoint(p);
         if (leaf != null) {
@@ -426,8 +427,8 @@ public class PointOctree extends AABB implements Shape3D {
         return found;
     }
 
-    public void removeAll(Collection<Vec3D> points) {
-        for (ReadonlyVec3D p : points) {
+    public void removeAll(Collection<XYZ> points) {
+        for (XYZ p : points) {
             remove(p);
         }
     }
diff --git a/src/main/java/com/syncleus/spangraph/geom/PointQuadtree.java b/src/main/java/toxi/geom/PointQuadtree.java
similarity index 99%
rename from src/main/java/com/syncleus/spangraph/geom/PointQuadtree.java
rename to src/main/java/toxi/geom/PointQuadtree.java
index 048ce79..05db672 100644
--- a/src/main/java/com/syncleus/spangraph/geom/PointQuadtree.java
+++ b/src/main/java/toxi/geom/PointQuadtree.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/src/main/java/com/syncleus/spangraph/geom/Polygon2D.java b/src/main/java/toxi/geom/Polygon2D.java
similarity index 95%
rename from src/main/java/com/syncleus/spangraph/geom/Polygon2D.java
rename to src/main/java/toxi/geom/Polygon2D.java
index 58977cf..d0bf866 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Polygon2D.java
+++ b/src/main/java/toxi/geom/Polygon2D.java
@@ -25,21 +25,19 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
+import javafx.scene.shape.TriangleMesh;
+import toxi.geom.Line2D.LineIntersection;
+import toxi.geom.Line2D.LineIntersection.Type;
+import toxi.math.MathUtils;
+
+import javax.xml.bind.annotation.XmlElement;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
-import javax.xml.bind.annotation.XmlElement;
-
-import toxi.geom.Line2D.LineIntersection;
-import toxi.geom.Line2D.LineIntersection.Type;
-import toxi.geom.mesh.Mesh3D;
-import toxi.geom.mesh.TriangleMesh;
-import toxi.math.MathUtils;
-
 /**
  * Container type for convex polygons. Implements {@link Shape2D}.
  */
@@ -267,7 +265,7 @@ public class Polygon2D implements Shape2D, Iterable<Vec2D> {
      * Returns the polygon's bounding rect.
      * 
      * @return bounding rect
-     * @see toxi.geom.Shape2D#getBounds()
+     * @see Shape2D#getBounds()
      */
     public Rect getBounds() {
         return Rect.getBoundingRect(vertices);
@@ -296,7 +294,7 @@ public class Polygon2D implements Shape2D, Iterable<Vec2D> {
      * 
      * @return perimiter length
      * 
-     * @see toxi.geom.Shape2D#getCircumference()
+     * @see Shape2D#getCircumference()
      */
     public float getCircumference() {
         float circ = 0;
@@ -728,32 +726,6 @@ public class Polygon2D implements Shape2D, Iterable<Vec2D> {
         return this;
     }
 
-    public Mesh3D toMesh(Mesh3D mesh) {
-        return toMesh(mesh, null, 0);
-    }
-
-    public Mesh3D toMesh(Mesh3D mesh, Vec2D centroid2D, float extrude) {
-        if (mesh == null) {
-            mesh = new TriangleMesh();
-        }
-        final int num = vertices.size();
-        if (centroid2D == null) {
-            centroid2D = getCentroid();
-        }
-        Vec3D centroid = centroid2D.to3DXY();
-        centroid.z = extrude;
-        Rect bounds = getBounds();
-        Vec2D boundScale = new Vec2D(1f / bounds.width, 1f / bounds.height);
-        Vec2D uvC = centroid2D.sub(bounds.getTopLeft()).scaleSelf(boundScale);
-        for (int i = 1; i <= num; i++) {
-            Vec2D a = vertices.get(i % num);
-            Vec2D b = vertices.get(i - 1);
-            Vec2D uvA = a.sub(bounds.getTopLeft()).scaleSelf(boundScale);
-            Vec2D uvB = b.sub(bounds.getTopLeft()).scaleSelf(boundScale);
-            mesh.addFace(centroid, a.to3DXY(), b.to3DXY(), uvC, uvA, uvB);
-        }
-        return mesh;
-    }
 
     /**
      * Attempts to remove all internal self-intersections and creates a new
diff --git a/src/main/java/toxi/geom/PolygonClipper2D.java b/src/main/java/toxi/geom/PolygonClipper2D.java
new file mode 100644
index 0000000..99a9722
--- /dev/null
+++ b/src/main/java/toxi/geom/PolygonClipper2D.java
@@ -0,0 +1,45 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.geom;
+
+/**
+ * Defines an interface for clipping 2D polygons. Currently the only
+ * implementation for this available is {@link SutherlandHodgemanClipper}.
+ */
+public interface PolygonClipper2D {
+
+    /**
+     * Creates a clipped version of the polygon to the boundary shape set.
+     * 
+     * @param poly
+     *            polygon to be clipped
+     * @return clipped poly
+     */
+    public Polygon2D clipPolygon(Polygon2D poly);
+
+}
\ No newline at end of file
diff --git a/src/main/java/toxi/geom/PolygonTesselator.java b/src/main/java/toxi/geom/PolygonTesselator.java
new file mode 100644
index 0000000..299c894
--- /dev/null
+++ b/src/main/java/toxi/geom/PolygonTesselator.java
@@ -0,0 +1,16 @@
+package toxi.geom;
+
+import java.util.List;
+
+public interface PolygonTesselator {
+
+    /**
+     * Tesselates the given polygon into a set of triangles.
+     * 
+     * @param poly
+     *            polygon
+     * @return list of triangles
+     */
+    public List<Triangle2D> tesselatePolygon(Polygon2D poly);
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/syncleus/spangraph/geom/QuadtreeVisitor.java b/src/main/java/toxi/geom/QuadtreeVisitor.java
similarity index 97%
rename from src/main/java/com/syncleus/spangraph/geom/QuadtreeVisitor.java
rename to src/main/java/toxi/geom/QuadtreeVisitor.java
index 6b3b09c..98cde8c 100644
--- a/src/main/java/com/syncleus/spangraph/geom/QuadtreeVisitor.java
+++ b/src/main/java/toxi/geom/QuadtreeVisitor.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 /**
  * This interface is the core part of the visitor pattern application for
diff --git a/src/main/java/toxi/geom/Quaternion.java b/src/main/java/toxi/geom/Quaternion.java
new file mode 100644
index 0000000..b49511a
--- /dev/null
+++ b/src/main/java/toxi/geom/Quaternion.java
@@ -0,0 +1,511 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.geom;
+
+import toxi.math.InterpolateStrategy;
+import toxi.math.MathUtils;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+
+/**
+ * Quaternion implementation with SLERP based on http://is.gd/2n9s
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Quaternion {
+
+    public static final float DOT_THRESHOLD = 0.9995f;
+
+    /**
+     * Creates a Quaternion from a axis and a angle.
+     * 
+     * @param axis
+     *            axis vector (will be normalized)
+     * @param angle
+     *            angle in radians.
+     * 
+     * @return new quaternion
+     */
+    public static Quaternion createFromAxisAngle(roVec3D axis, float angle) {
+        angle *= 0.5;
+        float sin = MathUtils.sin(angle);
+        float cos = MathUtils.cos(angle);
+        Quaternion q = new Quaternion(cos, axis.getNormalizedTo(sin));
+        return q;
+    }
+
+    /**
+     * Creates a Quaternion from Euler angles.
+     * 
+     * @param pitch
+     *            X-angle in radians.
+     * @param yaw
+     *            Y-angle in radians.
+     * @param roll
+     *            Z-angle in radians.
+     * 
+     * @return new quaternion
+     */
+    public static Quaternion createFromEuler(float pitch, float yaw, float roll) {
+        pitch *= 0.5;
+        yaw *= 0.5;
+        roll *= 0.5;
+        float sinPitch = MathUtils.sin(pitch);
+        float cosPitch = MathUtils.cos(pitch);
+        float sinYaw = MathUtils.sin(yaw);
+        float cosYaw = MathUtils.cos(yaw);
+        float sinRoll = MathUtils.sin(roll);
+        float cosRoll = MathUtils.cos(roll);
+        float cosPitchCosYaw = cosPitch * cosYaw;
+        float sinPitchSinYaw = sinPitch * sinYaw;
+
+        Quaternion q = new Quaternion();
+
+        q.x = sinRoll * cosPitchCosYaw - cosRoll * sinPitchSinYaw;
+        q.y = cosRoll * sinPitch * cosYaw + sinRoll * cosPitch * sinYaw;
+        q.z = cosRoll * cosPitch * sinYaw - sinRoll * sinPitch * cosYaw;
+        q.w = cosRoll * cosPitchCosYaw + sinRoll * sinPitchSinYaw;
+
+        // alternative solution from:
+        // http://is.gd/6HdEB
+        //
+        // double c1 = Math.cos(yaw/2);
+        // double s1 = Math.sin(yaw/2);
+        // double c2 = Math.cos(pitch/2);
+        // double s2 = Math.sin(pitch/2);
+        // double c3 = Math.cos(roll/2);
+        // double s3 = Math.sin(roll/2);
+        // double c1c2 = c1*c2;
+        // double s1s2 = s1*s2;
+        // w =c1c2*c3 - s1s2*s3;
+        // x =c1c2*s3 + s1s2*c3;
+        // y =s1*c2*c3 + c1*s2*s3;
+        // z =c1*s2*c3 - s1*c2*s3;
+
+        return q;
+    }
+
+    /**
+     * Creates a quaternion from a rotation matrix. The algorithm used is from
+     * Allan and Mark Watt's "Advanced Animation and Rendering Techniques" (ACM
+     * Press 1992).
+     * 
+     * @param m
+     *            rotation matrix
+     * @return quaternion
+     */
+    public static Quaternion createFromMatrix(Matrix4x4 m) {
+
+        double s = 0.0f;
+        double[] q = new double[4];
+        double trace = m.matrix[0][0] + m.matrix[1][1] + m.matrix[2][2];
+        if (trace > 0.0f) {
+            s = 0.5 / Math.sqrt(trace + 1.0);
+            q[0] = (m.matrix[2][1] - m.matrix[1][2]) * s;
+            q[1] = (m.matrix[0][2] - m.matrix[2][0]) * s;
+            q[2] = (m.matrix[1][0] - m.matrix[0][1]) * s;
+            q[3] = 0.25 / s;
+        } else {
+            int[] nxt = new int[] {
+                    1, 2, 0
+            };
+            int i = 0, j = 0, k = 0;
+
+            if (m.matrix[1][1] > m.matrix[0][0]) {
+                i = 1;
+            }
+
+            if (m.matrix[2][2] > m.matrix[i][i]) {
+                i = 2;
+            }
+
+            j = nxt[i];
+            k = nxt[j];
+            s = 2.0f * Math
+                    .sqrt((m.matrix[i][i] - m.matrix[j][j] - m.matrix[k][k]) + 1.0f);
+
+            double ss = 1.0 / s;
+            q[i] = s * 0.25f;
+            q[j] = (m.matrix[j][i] + m.matrix[i][j]) * ss;
+            q[k] = (m.matrix[k][i] + m.matrix[i][k]) * ss;
+            q[3] = (m.matrix[k][j] - m.matrix[j][k]) * ss;
+        }
+
+        return new Quaternion((float) q[3], (float) q[0], (float) q[1],
+                (float) q[2]);
+    }
+
+    /**
+     * Constructs a quaternion that rotates the vector given by the "forward"
+     * param into the direction given by the "dir" param.
+     * 
+     * @param dir
+     * @param forward
+     * @return quaternion
+     */
+    public static Quaternion getAlignmentQuat(roVec3D dir,
+            roVec3D forward) {
+        Vec3D target = dir.getNormalized();
+        roVec3D axis = forward.cross(target);
+        float length = axis.magnitude() + 0.0001f;
+        float angle = (float) Math.atan2(length, forward.dot(target));
+        return createFromAxisAngle(axis, angle);
+    }
+
+    @XmlAttribute(required = true)
+    public float x, y, z, w;
+
+    public Quaternion() {
+        identity();
+    }
+
+    public Quaternion(float w, float x, float y, float z) {
+        this.w = w;
+        this.x = x;
+        this.y = y;
+        this.z = z;
+    }
+
+    public Quaternion(float w, roVec3D v) {
+        this.x = v.x();
+        this.y = v.y();
+        this.z = v.z();
+        this.w = w;
+    }
+
+    public Quaternion(Quaternion q) {
+        this.w = q.w;
+        this.x = q.x;
+        this.y = q.y;
+        this.z = q.z;
+    }
+
+    public Quaternion add(Quaternion q) {
+        return new Quaternion(x + q.x, y + q.y, z + q.z, w + q.w);
+    }
+
+    public Quaternion addSelf(Quaternion q) {
+        x += q.x;
+        y += q.y;
+        z += q.z;
+        w += q.w;
+        return this;
+    }
+
+    public XYZ applyTo(Vec3D v) {
+        float ix = w * v.x + y * v.z - z * v.y;
+        float iy = w * v.y + z * v.x - x * v.z;
+        float iz = w * v.z + x * v.y - y * v.x;
+        float iw = -x * v.x - y * v.y - z * v.z;
+        float xx = ix * w - iw * x - iy * z + iz * y;
+        float yy = iy * w - iw * y - iz * x + ix * z;
+        float zz = iz * w - iw * z - ix * y + iy * x;
+        v.set(xx, yy, zz);
+        return v;
+    }
+
+    public Quaternion copy() {
+        return new Quaternion(w, x, y, z);
+    }
+
+    /**
+     * Computes the dot product with the given quaternion.
+     * 
+     * @param q
+     * @return dot product
+     */
+    public float dot(Quaternion q) {
+        return (x * q.x) + (y * q.y) + (z * q.z) + (w * q.w);
+    }
+
+    /**
+     * Computes this quaternion's conjugate, defined as the same w around the
+     * inverted axis.
+     * 
+     * @return new conjugate quaternion
+     */
+    public Quaternion getConjugate() {
+        Quaternion q = new Quaternion();
+        q.x = -x;
+        q.y = -y;
+        q.z = -z;
+        q.w = w;
+        return q;
+    }
+
+    /**
+     * @deprecated use {@link #toMatrix4x4()} instead
+     * @return result matrix
+     */
+    @Deprecated
+    public Matrix4x4 getMatrix() {
+        return toMatrix4x4();
+    }
+
+    /**
+     * Computes normalized version of this quaternion.
+     * 
+     * @return new normalized quaternion
+     */
+    public Quaternion getNormalized() {
+        return new Quaternion(this).normalize();
+    }
+
+    public Quaternion identity() {
+        w = 1.0f;
+        x = 0.0f;
+        y = 0.0f;
+        z = 0.0f;
+        return this;
+    }
+
+    /**
+     * Spherical interpolation to target quaternion (code ported from <a href=
+     * "http://www.gamasutra.com/view/feature/3278/rotating_objects_using_quaternions.php"
+     * >GamaSutra</a>)
+     * 
+     * @param target
+     *            quaternion
+     * @param t
+     *            interpolation factor (0..1)
+     * @return new interpolated quat
+     */
+    public Quaternion interpolateTo(Quaternion target, float t) {
+        return copy().interpolateToSelf(target, t);
+    }
+
+    /**
+     * @param target
+     * @param t
+     * @param is
+     * @return interpolated quaternion as new instance
+     */
+    public Quaternion interpolateTo(Quaternion target, float t,
+            InterpolateStrategy is) {
+        return copy().interpolateToSelf(target, is.interpolate(0, 1, t));
+    }
+
+    /**
+     * Spherical interpolation to target quaternion (code ported from <a href=
+     * "http://www.gamasutra.com/view/feature/3278/rotating_objects_using_quaternions.php"
+     * >GamaSutra</a>)
+     * 
+     * @param target
+     *            quaternion
+     * @param t
+     *            interpolation factor (0..1)
+     * @return new interpolated quat
+     */
+    public Quaternion interpolateToSelf(Quaternion target, double t) {
+        double scale;
+        double invscale;
+        float dot = dot(target);
+        double theta = Math.acos(dot);
+        double sintheta = Math.sin(theta);
+        if (sintheta > 0.001f) {
+            scale = Math.sin(theta * (1.0 - t)) / sintheta;
+            invscale = Math.sin(theta * t) / sintheta;
+        } else {
+            scale = 1 - t;
+            invscale = t;
+        }
+        if (dot < 0) {
+            w = (float) (scale * w - invscale * target.w);
+            x = (float) (scale * x - invscale * target.x);
+            y = (float) (scale * y - invscale * target.y);
+            z = (float) (scale * z - invscale * target.z);
+        } else {
+            w = (float) (scale * w + invscale * target.w);
+            x = (float) (scale * x + invscale * target.x);
+            y = (float) (scale * y + invscale * target.y);
+            z = (float) (scale * z + invscale * target.z);
+        }
+        return normalize();
+    }
+
+    /**
+     * Uses spherical interpolation to approach the target quaternion. The
+     * interpolation factor is manipulated by the chosen
+     * {@link InterpolateStrategy} first.
+     * 
+     * @param target
+     * @param t
+     * @param is
+     * @return itself
+     */
+    public Quaternion interpolateToSelf(Quaternion target, float t,
+            InterpolateStrategy is) {
+        return interpolateToSelf(target, is.interpolate(0, 1, t));
+    }
+
+    public float magnitude() {
+        return (float) Math.sqrt(x * x + y * y + z * z + w * w);
+    }
+
+    public Quaternion multiply(Quaternion q2) {
+        Quaternion res = new Quaternion();
+        res.w = w * q2.w - x * q2.x - y * q2.y - z * q2.z;
+        res.x = w * q2.x + x * q2.w + y * q2.z - z * q2.y;
+        res.y = w * q2.y + y * q2.w + z * q2.x - x * q2.z;
+        res.z = w * q2.z + z * q2.w + x * q2.y - y * q2.x;
+
+        return res;
+    }
+
+    public Quaternion normalize() {
+        double mag = Math.sqrt(x * x + y * y + z * z + w * w);
+        if (mag > MathUtils.EPS) {
+            mag = 1.0 / mag;
+            x *= mag;
+            y *= mag;
+            z *= mag;
+            w *= mag;
+        }
+        return this;
+    }
+
+    public Quaternion scale(float t) {
+        return new Quaternion(x * t, y * t, z * t, w * t);
+    }
+
+    public Quaternion scaleSelf(float t) {
+        x *= t;
+        y *= t;
+        z *= t;
+        w *= t;
+        return this;
+    }
+
+    public Quaternion set(float w, float x, float y, float z) {
+        this.w = w;
+        this.x = x;
+        this.y = y;
+        this.z = z;
+        return this;
+    }
+
+    public Quaternion set(float w, Vec3D v) {
+        this.w = w;
+        x = v.x;
+        y = v.y;
+        z = v.z;
+        return this;
+    }
+
+    public Quaternion set(Quaternion q) {
+        w = q.w;
+        x = q.x;
+        y = q.y;
+        z = q.z;
+        return this;
+    }
+
+    public Quaternion sub(Quaternion q) {
+        return new Quaternion(x - q.x, y - q.y, z - q.z, w - q.w);
+    }
+
+    public Quaternion subSelf(Quaternion q) {
+        x -= q.x;
+        y -= q.y;
+        z -= q.z;
+        w -= q.w;
+        return this;
+    }
+
+    public float[] toArray() {
+        return new float[] {
+                w, x, y, z
+        };
+    }
+
+    /**
+     * Converts the quaternion into a float array consisting of: rotation angle
+     * in radians, rotation axis x,y,z
+     * 
+     * @return 4-element float array
+     */
+    public float[] toAxisAngle() {
+        float[] res = new float[4];
+        float sa = (float) Math.sqrt(1.0f - w * w);
+        if (sa < MathUtils.EPS) {
+            sa = 1.0f;
+        } else {
+            sa = 1.0f / sa;
+        }
+        res[0] = (float) Math.acos(w) * 2.0f;
+        res[1] = x * sa;
+        res[2] = y * sa;
+        res[3] = z * sa;
+        return res;
+    }
+
+    /**
+     * Converts the quat to a 4x4 rotation matrix (in row-major format). Assumes
+     * the quat is currently normalized (if not, you'll need to call
+     * {@link #normalize()} first).
+     * 
+     * @return result matrix
+     */
+    public Matrix4x4 toMatrix4x4() {
+        return toMatrix4x4(new Matrix4x4());
+    }
+
+    public Matrix4x4 toMatrix4x4(Matrix4x4 result) {
+        // Converts this quaternion to a rotation matrix.
+        //
+        // | 1 - 2(y^2 + z^2) 2(xy + wz) 2(xz - wy) 0 |
+        // | 2(xy - wz) 1 - 2(x^2 + z^2) 2(yz + wx) 0 |
+        // | 2(xz + wy) 2(yz - wx) 1 - 2(x^2 + y^2) 0 |
+        // | 0 0 0 1 |
+
+        float x2 = x + x;
+        float y2 = y + y;
+        float z2 = z + z;
+        float xx = x * x2;
+        float xy = x * y2;
+        float xz = x * z2;
+        float yy = y * y2;
+        float yz = y * z2;
+        float zz = z * z2;
+        float wx = w * x2;
+        float wy = w * y2;
+        float wz = w * z2;
+
+        return result.set(1 - (yy + zz), xy - wz, xz + wy, 0, xy + wz,
+                1 - (xx + zz), yz - wx, 0, xz - wy, yz + wx, 1 - (xx + yy), 0,
+                0, 0, 0, 1);
+    }
+
+    public String toString() {
+        StringBuffer sb = new StringBuffer(48);
+        sb.append("{axis: [").append(x).append(",").append(y).append(",")
+                .append(z).append("], w: ").append(w).append("}");
+        return sb.toString();
+    }
+}
diff --git a/src/main/java/com/syncleus/spangraph/geom/Ray2D.java b/src/main/java/toxi/geom/Ray2D.java
similarity index 98%
rename from src/main/java/com/syncleus/spangraph/geom/Ray2D.java
rename to src/main/java/toxi/geom/Ray2D.java
index 1e430a1..1902a53 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Ray2D.java
+++ b/src/main/java/toxi/geom/Ray2D.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
diff --git a/src/main/java/com/syncleus/spangraph/geom/Ray3D.java b/src/main/java/toxi/geom/Ray3D.java
similarity index 92%
rename from src/main/java/com/syncleus/spangraph/geom/Ray3D.java
rename to src/main/java/toxi/geom/Ray3D.java
index 3fa0d99..fadef96 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Ray3D.java
+++ b/src/main/java/toxi/geom/Ray3D.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
@@ -45,12 +45,12 @@ public class Ray3D extends Vec3D {
         dir = Vec3D.Y_AXIS.copy();
     }
 
-    public Ray3D(float x, float y, float z, ReadonlyVec3D d) {
+    public Ray3D(float x, float y, float z, XYZ d) {
         super(x, y, z);
         dir = d.getNormalized();
     }
 
-    public Ray3D(ReadonlyVec3D o, ReadonlyVec3D d) {
+    public Ray3D(XYZ o, XYZ d) {
         this(o.x(), o.y(), o.z(), d);
     }
 
@@ -93,12 +93,12 @@ public class Ray3D extends Vec3D {
      *            new direction
      * @return itself
      */
-    public Ray3D setDirection(ReadonlyVec3D d) {
+    public Ray3D setDirection(roVec3D d) {
         dir.set(d).normalize();
         return this;
     }
 
-    public Ray3D setNormalizedDirection(ReadonlyVec3D d) {
+    public Ray3D setNormalizedDirection(roVec3D d) {
         dir.set(d);
         return this;
     }
diff --git a/src/main/java/com/syncleus/spangraph/geom/Ray3DIntersector.java b/src/main/java/toxi/geom/Ray3DIntersector.java
similarity index 98%
rename from src/main/java/com/syncleus/spangraph/geom/Ray3DIntersector.java
rename to src/main/java/toxi/geom/Ray3DIntersector.java
index dcf1208..3b72cc5 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Ray3DIntersector.java
+++ b/src/main/java/toxi/geom/Ray3DIntersector.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 import toxi.math.MathUtils;
 
diff --git a/src/main/java/com/syncleus/spangraph/geom/ReadonlyVec2D.java b/src/main/java/toxi/geom/ReadonlyVec2D.java
similarity index 94%
rename from src/main/java/com/syncleus/spangraph/geom/ReadonlyVec2D.java
rename to src/main/java/toxi/geom/ReadonlyVec2D.java
index 9684d10..26d1375 100644
--- a/src/main/java/com/syncleus/spangraph/geom/ReadonlyVec2D.java
+++ b/src/main/java/toxi/geom/ReadonlyVec2D.java
@@ -25,7 +25,12 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
+
+import toxi.geom.Vec2D.Axis;
+import toxi.math.InterpolateStrategy;
+import toxi.math.MathUtils;
+import toxi.math.ScaleMap;
 
 /**
  * Readonly, immutable interface wrapper for Vec2D instances. Used throughout
@@ -218,14 +223,14 @@ public interface ReadonlyVec2D {
      */
     public Vec2D getLimited(float lim);
 
-//    /**
-//     * Produces a new vector with its coordinates passed through the given
-//     * {@link ScaleMap}.
-//     *
-//     * @param map
-//     * @return mapped vector
-//     */
-//    public Vec2D getMapped(ScaleMap map);
+    /**
+     * Produces a new vector with its coordinates passed through the given
+     * {@link ScaleMap}.
+     * 
+     * @param map
+     * @return mapped vector
+     */
+    public Vec2D getMapped(ScaleMap map);
 
     /**
      * Produces the normalized version as a new vector
@@ -305,19 +310,19 @@ public interface ReadonlyVec2D {
      */
     public Vec2D interpolateTo(ReadonlyVec2D v, float f);
 
-//    /**
-//     * Interpolates the vector towards the given target vector, using the given
-//     * {@link InterpolateStrategy}
-//     *
-//     * @param v
-//     *            target vector
-//     * @param f
-//     *            interpolation factor (should be in the range 0..1)
-//     * @param s
-//     *            InterpolateStrategy instance
-//     * @return result as new vector
-//     */
-//    public Vec2D interpolateTo(ReadonlyVec2D v, float f, InterpolateStrategy s);
+    /**
+     * Interpolates the vector towards the given target vector, using the given
+     * {@link InterpolateStrategy}
+     * 
+     * @param v
+     *            target vector
+     * @param f
+     *            interpolation factor (should be in the range 0..1)
+     * @param s
+     *            InterpolateStrategy instance
+     * @return result as new vector
+     */
+    public Vec2D interpolateTo(ReadonlyVec2D v, float f, InterpolateStrategy s);
 
     /**
      * Checks if the point is inside the given sphere.
@@ -495,7 +500,7 @@ public interface ReadonlyVec2D {
      * 
      * @return 3D vector
      */
-    public Vec3D to3DYZ();
+    public XYZ to3DYZ();
 
     /*
      * (non-Javadoc)
diff --git a/src/main/java/com/syncleus/spangraph/geom/ReadonlyVec4D.java b/src/main/java/toxi/geom/ReadonlyVec4D.java
similarity index 98%
rename from src/main/java/com/syncleus/spangraph/geom/ReadonlyVec4D.java
rename to src/main/java/toxi/geom/ReadonlyVec4D.java
index dd9ad4a..cdc7aea 100644
--- a/src/main/java/com/syncleus/spangraph/geom/ReadonlyVec4D.java
+++ b/src/main/java/toxi/geom/ReadonlyVec4D.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 import toxi.math.InterpolateStrategy;
 import toxi.math.MathUtils;
@@ -207,7 +207,7 @@ public interface ReadonlyVec4D {
      * 
      * @return new result vector
      */
-    public Vec4D getRotatedAroundAxis(ReadonlyVec3D axis, float theta);
+    public Vec4D getRotatedAroundAxis(roVec3D axis, float theta);
 
     /**
      * Creates a new vector rotated by the given angle around the X axis.
@@ -368,7 +368,7 @@ public interface ReadonlyVec4D {
 
     public float w();
 
-    public Vec3D weightInto(Vec3D out);
+    public XYZ weightInto(Vec3D out);
 
     public float x();
 
diff --git a/src/main/java/com/syncleus/spangraph/geom/Rect.java b/src/main/java/toxi/geom/Rect.java
similarity index 99%
rename from src/main/java/com/syncleus/spangraph/geom/Rect.java
rename to src/main/java/toxi/geom/Rect.java
index af5263a..64e229e 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Rect.java
+++ b/src/main/java/toxi/geom/Rect.java
@@ -25,14 +25,16 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
-import javax.xml.bind.annotation.XmlAccessType;
+import java.util.ArrayList;
+import java.util.List;
 
+import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
-import java.util.ArrayList;
-import java.util.List;
+
+import toxi.math.MathUtils;
 
 @XmlAccessorType(XmlAccessType.FIELD)
 public class Rect implements Shape2D {
diff --git a/src/main/java/toxi/geom/Reflector3D.java b/src/main/java/toxi/geom/Reflector3D.java
new file mode 100644
index 0000000..c7532d8
--- /dev/null
+++ b/src/main/java/toxi/geom/Reflector3D.java
@@ -0,0 +1,58 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.geom;
+
+/**
+ * Generic interface for ray reflection with 3D geometry
+ */
+public interface Reflector3D extends Intersector3D {
+
+    /**
+     * Returns the point on the reflected ray at given distance from the
+     * intersection point
+     * 
+     * @param dist
+     *            distance from isect position
+     * @return point on reflected ray
+     */
+    public roVec3D getReflectedRayPointAtDistance(float dist);
+
+    /**
+     * @return angle between incident ray and surface normal
+     */
+    public float getReflectionAngle();
+
+    /**
+     * Reflects given ray on the entity's surface
+     * 
+     * @param ray
+     *            incident ray
+     * @return reflected ray starting from intersection point
+     */
+    public Ray3D reflectRay(Ray3D ray);
+}
\ No newline at end of file
diff --git a/src/main/java/com/syncleus/spangraph/geom/Shape2D.java b/src/main/java/toxi/geom/Shape2D.java
similarity index 98%
rename from src/main/java/com/syncleus/spangraph/geom/Shape2D.java
rename to src/main/java/toxi/geom/Shape2D.java
index e304ead..869b548 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Shape2D.java
+++ b/src/main/java/toxi/geom/Shape2D.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 import java.util.List;
 
diff --git a/src/main/java/com/syncleus/spangraph/geom/Shape3D.java b/src/main/java/toxi/geom/Shape3D.java
similarity index 94%
rename from src/main/java/com/syncleus/spangraph/geom/Shape3D.java
rename to src/main/java/toxi/geom/Shape3D.java
index b2b5ce7..f044756 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Shape3D.java
+++ b/src/main/java/toxi/geom/Shape3D.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 /**
  * Interface description of common operations supported by 3D geometry types.
@@ -37,5 +37,5 @@ public interface Shape3D {
      * 
      * @return true, if inside
      */
-    boolean containsPoint(ReadonlyVec3D p);
+    boolean containsPoint(XYZ p);
 }
diff --git a/src/main/java/toxi/geom/SingularMatrixException.java b/src/main/java/toxi/geom/SingularMatrixException.java
new file mode 100644
index 0000000..1aa8ad9
--- /dev/null
+++ b/src/main/java/toxi/geom/SingularMatrixException.java
@@ -0,0 +1,57 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright 1997-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * $Revision: 127 $
+ * $Date: 2008-02-28 20:18:51 +0000 (Thu, 28 Feb 2008) $
+ * $State$
+ */
+
+package toxi.geom;
+
+/**
+ * Indicates that inverse of a matrix can not be computed.
+ */
+public class SingularMatrixException extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Create the exception object with default values.
+     */
+    public SingularMatrixException() {
+    }
+
+    /**
+     * Create the exception object that outputs message.
+     * 
+     * @param str
+     *            the message string to be output.
+     */
+    public SingularMatrixException(String str) {
+        super(str);
+    }
+
+}
diff --git a/src/main/java/toxi/geom/SpatialBins.java b/src/main/java/toxi/geom/SpatialBins.java
new file mode 100644
index 0000000..b4bcfe9
--- /dev/null
+++ b/src/main/java/toxi/geom/SpatialBins.java
@@ -0,0 +1,123 @@
+package toxi.geom;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import toxi.math.MathUtils;
+
+public class SpatialBins<T> implements SpatialIndex<T> {
+
+    private final float invBinWidth;
+    private final float minOffset;
+    private final int numBins;
+    private int numItems;
+
+    private final List<HashSet<T>> bins;
+    private final CoordinateExtractor<T> extractor;
+
+    public SpatialBins(float min, float max, int numBins,
+            CoordinateExtractor<T> extractor) {
+        this.extractor = extractor;
+        this.bins = new ArrayList<HashSet<T>>();
+        for (int i = 0; i < numBins; i++) {
+            bins.add(new HashSet<T>());
+        }
+        this.minOffset = min;
+        this.numBins = numBins;
+        this.invBinWidth = numBins / (max - min);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.geom.SpatialIndex#clear()
+     */
+    public void clear() {
+        for (HashSet<T> bin : bins) {
+            bin.clear();
+        }
+        numItems = 0;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.geom.SpatialIndex#index(T)
+     */
+    public boolean index(T p) {
+        int id = (int) MathUtils.clip((extractor.coordinate(p) - minOffset)
+                * invBinWidth, 0, numBins - 1);
+        if (bins.get(id).add(p)) {
+            numItems++;
+            return true;
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.geom.SpatialIndex#isIndexed(T)
+     */
+    public boolean isIndexed(T item) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    public List<T> itemsWithinRadius(T p, float radius, List<T> results) {
+        int id = (int) MathUtils.clip((extractor.coordinate(p) - minOffset)
+                * invBinWidth, 0, numBins);
+        int tol = (int) Math.ceil(radius * invBinWidth);
+        for (int i = Math.max(id - tol, 0), n = Math.min(
+                Math.min(id + tol, numBins), numBins - 1); i <= n; i++) {
+            if (results == null) {
+                results = new ArrayList<T>();
+            }
+            results.addAll(bins.get(i));
+        }
+        return results;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.geom.SpatialIndex#reindex(float, T)
+     */
+    public boolean reindex(T p, T q) {
+        int id1 = (int) MathUtils.clip((extractor.coordinate(p) - minOffset)
+                * invBinWidth, 0, numBins);
+        int id2 = (int) MathUtils.clip((extractor.coordinate(q) - minOffset)
+                * invBinWidth, 0, numBins);
+        if (id1 != id2) {
+            if (bins.get(id1).remove(p)) {
+                numItems--;
+            }
+            if (bins.get(id2).add(q)) {
+                numItems++;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.geom.SpatialIndex#size()
+     */
+    public int size() {
+        return numItems;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.geom.SpatialIndex#unindex(T)
+     */
+    public boolean unindex(T p) {
+        int id = (int) MathUtils.clip((extractor.coordinate(p) - minOffset)
+                * invBinWidth, 0, numBins);
+        return bins.get(id).remove(p);
+    }
+}
diff --git a/src/main/java/com/syncleus/spangraph/geom/SpatialIndex.java b/src/main/java/toxi/geom/SpatialIndex.java
similarity index 89%
rename from src/main/java/com/syncleus/spangraph/geom/SpatialIndex.java
rename to src/main/java/toxi/geom/SpatialIndex.java
index f0a30db..36dbdb7 100644
--- a/src/main/java/com/syncleus/spangraph/geom/SpatialIndex.java
+++ b/src/main/java/toxi/geom/SpatialIndex.java
@@ -1,4 +1,4 @@
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 import java.util.List;
 
diff --git a/src/main/java/com/syncleus/spangraph/geom/Sphere.java b/src/main/java/toxi/geom/Sphere.java
similarity index 90%
rename from src/main/java/com/syncleus/spangraph/geom/Sphere.java
rename to src/main/java/toxi/geom/Sphere.java
index 2aa2ae0..3e7d658 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Sphere.java
+++ b/src/main/java/toxi/geom/Sphere.java
@@ -25,16 +25,14 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
-
-
+package toxi.geom;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
 
 @XmlAccessorType(XmlAccessType.FIELD)
-public class Sphere extends Vec3D  {
+public class Sphere extends Vec3D implements Shape3D {
 
     /**
      * Earth's mean radius in km
@@ -53,7 +51,7 @@ public class Sphere extends Vec3D  {
         this(new Vec3D(), radius);
     }
 
-    public Sphere(ReadonlyVec3D v, float r) {
+    public Sphere(roVec3D v, float r) {
         super(v);
         radius = r;
     }
@@ -62,9 +60,22 @@ public class Sphere extends Vec3D  {
         this(s, s.radius);
     }
 
-    public boolean containsPoint(ReadonlyVec3D p) {
-        float d = this.sub(p).magSquared();
-        return (d <= radius * radius);
+    public boolean containsPoint(final XYZ p) {
+        float d = 0;
+        float rsquare = radius*radius;
+
+        float dx = x() - p.x(); dx*=dx;
+        if (dx > rsquare) return false;
+
+        float dy = y() - p.y(); dy*=dy;
+        d = dx + dy;
+        if (d > rsquare) return false;
+
+        float dz = z() - p.z(); dz*=dz;
+        d += dz;
+        if (d > rsquare) return false;
+
+        return true;
     }
 
     /**
@@ -84,7 +95,7 @@ public class Sphere extends Vec3D  {
      */
     public float[] intersectRay(Ray3D ray) {
         float[] result = null;
-        ReadonlyVec3D q = ray.sub(this);
+        roVec3D q = ray.sub(this);
         float distSquared = q.magSquared();
         float v = -q.dot(ray.getDirection());
         float d = radius * radius - (distSquared - v * v);
@@ -132,7 +143,7 @@ public class Sphere extends Vec3D  {
 
         // Sphere and triangle intersect if the (squared) distance from sphere
         // center to Vec3D p is less than the (squared) sphere radius
-        ReadonlyVec3D v = result.sub(this);
+        roVec3D v = result.sub(this);
         return v.magSquared() <= radius * radius;
     }
 
@@ -171,7 +182,7 @@ public class Sphere extends Vec3D  {
      * @return a unit normal vector to the tangent plane of the ellipsoid in the
      *         point.
      */
-    public Vec3D tangentPlaneNormalAt(ReadonlyVec3D q) {
+    public Vec3D tangentPlaneNormalAt(XYZ q) {
         return q.sub(this).normalize();
     }
 
diff --git a/src/main/java/toxi/geom/SphereIntersectorReflector.java b/src/main/java/toxi/geom/SphereIntersectorReflector.java
new file mode 100644
index 0000000..caad360
--- /dev/null
+++ b/src/main/java/toxi/geom/SphereIntersectorReflector.java
@@ -0,0 +1,158 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.geom;
+
+import toxi.math.MathUtils;
+
+public class SphereIntersectorReflector implements Intersector3D, Reflector3D {
+
+    protected Sphere sphere;
+    protected IsectData3D isectData;
+
+    protected XYZ reflectedDir, reflectedPos;
+    protected float reflectTheta;
+
+    public SphereIntersectorReflector(Sphere s) {
+        sphere = s;
+        isectData = new IsectData3D();
+    }
+
+    public SphereIntersectorReflector(Vec3D o, float r) {
+        this(new Sphere(o, r));
+    }
+
+    public IsectData3D getIntersectionData() {
+        return isectData;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.geom.Reflector3D#getReflectedRayPointAtDistance(float)
+     */
+    public roVec3D getReflectedRayPointAtDistance(float dist) {
+        if (reflectedDir != null) {
+            return isectData.pos.add(reflectedDir.scale(dist));
+        } else {
+            return null;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.geom.Reflector3D#getReflectionAngle()
+     */
+    public float getReflectionAngle() {
+        return reflectTheta;
+    }
+
+    /**
+     * @return the sphere
+     */
+    public Sphere getSphere() {
+        return sphere;
+    }
+
+    /**
+     * Calculates the distance of the vector to the given sphere in the
+     * specified direction. A sphere is defined by a 3D point and a radius.
+     * Normalized directional vectors expected.
+     * 
+     * @param ray
+     *            intersection ray
+     * @return distance to sphere in world units, -1 if no intersection.
+     */
+
+    public float intersectRayDistance(Ray3D ray) {
+        roVec3D q = sphere.sub(ray);
+        float distSquared = q.magSquared();
+        float v = q.dot(ray.dir);
+        float d = sphere.radius * sphere.radius - (distSquared - v * v);
+
+        // If there was no intersection, return -1
+        if (d < 0.0) {
+            return -1;
+        }
+
+        // Return the distance to the [first] intersecting point
+        return v - (float) Math.sqrt(d);
+    }
+
+    public boolean intersectsRay(Ray3D ray) {
+        isectData.dist = intersectRayDistance(ray);
+        isectData.isIntersection = isectData.dist >= 0;
+        if (isectData.isIntersection) {
+            // get the intersection point
+            isectData.pos = ray.add(ray.getDirection().scale(isectData.dist));
+            // calculate the direction from our point to the intersection pos
+            isectData.dir = isectData.pos.sub(ray);
+            isectData.normal = sphere.tangentPlaneNormalAt(isectData.pos);
+        }
+        return isectData.isIntersection;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.geom.Reflector3D#reflectRay(toxi.geom.Vec3D, toxi.geom.Vec3D)
+     */
+    public Ray3D reflectRay(Ray3D ray) {
+        if (intersectsRay(ray)) {
+            // compute the normal vector of the sphere at the intersection
+            // position
+            // compute the reflection angle
+            reflectTheta = isectData.dir.angleBetween(isectData.normal, true)
+                    * 2 + MathUtils.PI;
+            // then form a perpendicular vector standing on the plane spanned by
+            // isectDir and sphereNormal
+            // this vector will be used to mirror the ray around the
+            // intersection point
+            Vec3D reflectNormal = isectData.dir.getNormalized()
+                    .cross(isectData.normal).normalize();
+            if (!reflectNormal.isZeroVector()) {
+                // compute the reflected ray direction
+                reflectedDir = isectData.dir.getNormalized().rotateAroundAxis(
+                        reflectNormal, reflectTheta);
+            } else {
+                reflectedDir = isectData.dir.getInverted();
+            }
+            return new Ray3D(isectData.pos, reflectedDir);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * @param sphere
+     *            the sphere to set
+     */
+    public void setSphere(Sphere sphere) {
+        this.sphere = sphere;
+    }
+}
diff --git a/src/main/java/com/syncleus/spangraph/geom/Spline2D.java b/src/main/java/toxi/geom/Spline2D.java
similarity index 99%
rename from src/main/java/com/syncleus/spangraph/geom/Spline2D.java
rename to src/main/java/toxi/geom/Spline2D.java
index 9417348..dbbd978 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Spline2D.java
+++ b/src/main/java/toxi/geom/Spline2D.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/src/main/java/com/syncleus/spangraph/geom/Spline3D.java b/src/main/java/toxi/geom/Spline3D.java
similarity index 82%
rename from src/main/java/com/syncleus/spangraph/geom/Spline3D.java
rename to src/main/java/toxi/geom/Spline3D.java
index 98e61af..0c2b298 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Spline3D.java
+++ b/src/main/java/toxi/geom/Spline3D.java
@@ -25,18 +25,13 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
+import javax.xml.bind.annotation.*;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlTransient;
-
 /**
  * <p>
  * This is a generic 3D B-Spline class for curves of arbitrary length, control
@@ -68,10 +63,10 @@ public class Spline3D {
     public static final float DEFAULT_TIGHTNESS = 0.25f;
 
     @XmlTransient
-    protected Vec3D[] points;
+    protected XYZ[] points;
 
     @XmlElement(name = "p")
-    public List<Vec3D> pointList = new ArrayList<Vec3D>();
+    public List<XYZ> pointList = new ArrayList();
 
     @XmlTransient
     public BernsteinPolynomial bernstein;
@@ -93,7 +88,7 @@ public class Spline3D {
 
     /**
      * Constructs an empty spline container with default curve tightness. You
-     * need to populate the spline manually by using {@link #add(ReadonlyVec3D)}
+     * need to populate the spline manually by using {@link #add(roVec3D)}
      * .
      */
     public Spline3D() {
@@ -104,7 +99,7 @@ public class Spline3D {
      * @param rawPoints
      *            list of control point vectors
      */
-    public Spline3D(List<Vec3D> rawPoints) {
+    public Spline3D(List<XYZ> rawPoints) {
         this(rawPoints, null, DEFAULT_TIGHTNESS);
     }
 
@@ -117,7 +112,7 @@ public class Spline3D {
      *            default curve tightness used for the interpolated vertices
      *            {@linkplain #setTightness(float)}
      */
-    public Spline3D(List<Vec3D> rawPoints, BernsteinPolynomial b,
+    public Spline3D(List<XYZ> rawPoints, BernsteinPolynomial b,
             float tightness) {
         pointList.addAll(rawPoints);
         bernstein = b;
@@ -128,7 +123,7 @@ public class Spline3D {
      * @param pointArray
      *            array of control point vectors
      */
-    public Spline3D(Vec3D[] pointArray) {
+    public Spline3D(XYZ[] pointArray) {
         this(pointArray, null, DEFAULT_TIGHTNESS);
     }
 
@@ -141,7 +136,7 @@ public class Spline3D {
      *            default curve tightness used for the interpolated vertices
      *            {@linkplain #setTightness(float)}
      */
-    public Spline3D(Vec3D[] pointArray, BernsteinPolynomial b, float tightness) {
+    public Spline3D(XYZ[] pointArray, BernsteinPolynomial b, float tightness) {
         this(Arrays.asList(pointArray), b, tightness);
     }
 
@@ -155,25 +150,25 @@ public class Spline3D {
      * @param p
      * @return itself
      */
-    public Spline3D add(ReadonlyVec3D p) {
+    public Spline3D add(roVec3D p) {
         pointList.add(p.copy());
         return this;
     }
 
     protected void findCPoints() {
         bi[1] = -tightness;
-        coeffA[1].set((points[2].x - points[0].x - delta[0].x) * tightness,
-                (points[2].y - points[0].y - delta[0].y) * tightness,
-                (points[2].z - points[0].z - delta[0].z) * tightness);
+        coeffA[1].set((points[2].x() - points[0].x() - delta[0].x) * tightness,
+                (points[2].y() - points[0].y() - delta[0].y) * tightness,
+                (points[2].z() - points[0].z() - delta[0].z) * tightness);
         final int numP = getNumPoints();
         for (int i = 2; i < numP - 1; i++) {
             bi[i] = -1 / (invTightness + bi[i - 1]);
             coeffA[i].set(
-                    -(points[i + 1].x - points[i - 1].x - coeffA[i - 1].x)
+                    -(points[i + 1].x() - points[i - 1].x() - coeffA[i - 1].x)
                             * bi[i],
-                    -(points[i + 1].y - points[i - 1].y - coeffA[i - 1].y)
+                    -(points[i + 1].y() - points[i - 1].y() - coeffA[i - 1].y)
                             * bi[i],
-                    -(points[i + 1].z - points[i - 1].z - coeffA[i - 1].z)
+                    -(points[i + 1].z() - points[i - 1].z() - coeffA[i - 1].z)
                             * bi[i]);
         }
         for (int i = numP - 2; i > 0; i--) {
@@ -195,7 +190,7 @@ public class Spline3D {
     /**
      * @return the pointList
      */
-    public List<Vec3D> getPointList() {
+    public List<XYZ> getPointList() {
         return pointList;
     }
 
@@ -215,9 +210,9 @@ public class Spline3D {
      *            the pointList to set
      * @return itself
      */
-    public Spline3D setPointList(List<Vec3D> plist) {
+    public Spline3D setPointList(List<XYZ> plist) {
         pointList.clear();
-        for (ReadonlyVec3D p : plist) {
+        for (XYZ p : plist) {
             pointList.add(p.copy());
         }
         return this;
@@ -276,17 +271,17 @@ public class Spline3D {
         Vec3D deltaQ = new Vec3D();
         res--;
         for (int i = 0, numP = getNumPoints(); i < numP - 1; i++) {
-            Vec3D p = points[i];
-            Vec3D q = points[i + 1];
+            XYZ p = points[i];
+            XYZ q = points[i + 1];
             deltaP.set(delta[i]).addSelf(p);
             deltaQ.set(q).subSelf(delta[i + 1]);
             for (int k = 0; k < res; k++) {
-                float x = p.x * bernstein.b0[k] + deltaP.x * bernstein.b1[k]
-                        + deltaQ.x * bernstein.b2[k] + q.x * bernstein.b3[k];
-                float y = p.y * bernstein.b0[k] + deltaP.y * bernstein.b1[k]
-                        + deltaQ.y * bernstein.b2[k] + q.y * bernstein.b3[k];
-                float z = p.z * bernstein.b0[k] + deltaP.z * bernstein.b1[k]
-                        + deltaQ.z * bernstein.b2[k] + q.z * bernstein.b3[k];
+                float x = p.x() * bernstein.b0[k] + deltaP.x * bernstein.b1[k]
+                        + deltaQ.x * bernstein.b2[k] + q.x() * bernstein.b3[k];
+                float y = p.y() * bernstein.b0[k] + deltaP.y * bernstein.b1[k]
+                        + deltaQ.y * bernstein.b2[k] + q.y() * bernstein.b3[k];
+                float z = p.z() * bernstein.b0[k] + deltaP.z * bernstein.b1[k]
+                        + deltaQ.z * bernstein.b2[k] + q.z() * bernstein.b3[k];
                 strip.add(new Vec3D(x, y, z));
             }
         }
@@ -306,6 +301,6 @@ public class Spline3D {
             }
         }
         setTightness(tightness);
-        points = pointList.toArray(new Vec3D[numP]);
+        points = pointList.toArray(new XYZ[numP]);
     }
 }
\ No newline at end of file
diff --git a/src/main/java/toxi/geom/SutherlandHodgemanClipper.java b/src/main/java/toxi/geom/SutherlandHodgemanClipper.java
new file mode 100644
index 0000000..03eadc3
--- /dev/null
+++ b/src/main/java/toxi/geom/SutherlandHodgemanClipper.java
@@ -0,0 +1,142 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.geom;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A version of the Sutherland-Hodgeman algorithm to clip 2D polygons optimized
+ * for rectangular clipping regions.
+ * 
+ * More information: http://en.wikipedia.org/wiki/Sutherland-Hodgman_algorithm
+ * 
+ * @see ConvexPolygonClipper
+ */
+public class SutherlandHodgemanClipper implements PolygonClipper2D {
+
+    protected Rect bounds;
+
+    public SutherlandHodgemanClipper(Rect bounds) {
+        this.bounds = bounds;
+    }
+
+    public Polygon2D clipPolygon(Polygon2D poly) {
+        List<Vec2D> points = new ArrayList<Vec2D>(poly.vertices);
+        List<Vec2D> clipped = new ArrayList<Vec2D>();
+        points.add(points.get(0));
+        for (int edgeID = 0; edgeID < 4; edgeID++) {
+            clipped.clear();
+            for (int i = 0, num = points.size() - 1; i < num; i++) {
+                Vec2D p = points.get(i);
+                Vec2D q = points.get(i + 1);
+                if (isInsideEdge(p, edgeID)) {
+                    if (isInsideEdge(q, edgeID)) {
+                        clipped.add(q.copy());
+                    } else {
+                        clipped.add(getClippedPosOnEdge(edgeID, p, q));
+                    }
+                    continue;
+                }
+                if (isInsideEdge(q, edgeID)) {
+                    clipped.add(getClippedPosOnEdge(edgeID, p, q));
+                    clipped.add(q.copy());
+                }
+            }
+            if (clipped.size() > 0
+                    && clipped.get(0) != clipped.get(clipped.size() - 1)) {
+                clipped.add(clipped.get(0));
+            }
+            List<Vec2D> t = points;
+            points = clipped;
+            clipped = t;
+        }
+        return new Polygon2D(points).removeDuplicates(0.001f);
+    }
+
+    /**
+     * @return the bounding rect
+     */
+    public Rect getBounds() {
+        return bounds;
+    }
+
+    private final Vec2D getClippedPosOnEdge(int edgeID, Vec2D p1, Vec2D p2) {
+        switch (edgeID) {
+            case 0:
+                return new Vec2D(p1.x + ((bounds.y - p1.y) * (p2.x - p1.x))
+                        / (p2.y - p1.y), bounds.y);
+            case 2:
+                float by = bounds.y + bounds.height;
+                return new Vec2D(p1.x + ((by - p1.y) * (p2.x - p1.x))
+                        / (p2.y - p1.y), by);
+            case 1:
+                float bx = bounds.x + bounds.width;
+                return new Vec2D(bx, p1.y + ((bx - p1.x) * (p2.y - p1.y))
+                        / (p2.x - p1.x));
+
+            case 3:
+                return new Vec2D(bounds.x, p1.y
+                        + ((bounds.x - p1.x) * (p2.y - p1.y)) / (p2.x - p1.x));
+            default:
+                return null;
+        }
+    }
+
+    private final boolean isInsideEdge(Vec2D p, int edgeID) {
+        switch (edgeID) {
+            case 0:
+                return p.y >= bounds.y;
+            case 2:
+                return p.y < bounds.y + bounds.height;
+            case 3:
+                return p.x >= bounds.x;
+            case 1:
+                return p.x < bounds.x + bounds.width;
+            default:
+                return false;
+        }
+    }
+
+    protected boolean isKnownVertex(List<Vec2D> list, Vec2D q) {
+        for (Vec2D p : list) {
+            if (p.equalsWithTolerance(q, 0.0001f)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @param bounds
+     *            the bounding rect to set
+     */
+    public void setBounds(Rect bounds) {
+        this.bounds = bounds;
+    }
+}
diff --git a/src/main/java/com/syncleus/spangraph/geom/Triangle2D.java b/src/main/java/toxi/geom/Triangle2D.java
similarity index 93%
rename from src/main/java/com/syncleus/spangraph/geom/Triangle2D.java
rename to src/main/java/toxi/geom/Triangle2D.java
index a0e6682..7cb17ba 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Triangle2D.java
+++ b/src/main/java/toxi/geom/Triangle2D.java
@@ -25,14 +25,21 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
-
-import javax.xml.bind.annotation.XmlTransient;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+import toxi.geom.Line2D.LineIntersection.Type;
+import toxi.math.MathUtils;
+
+@XmlAccessorType(XmlAccessType.FIELD)
 public class Triangle2D implements Shape2D {
 
     public static Triangle2D createEquilateralFrom(ReadonlyVec2D a,
@@ -49,6 +56,7 @@ public class Triangle2D implements Shape2D {
         return (determ > 0.0);
     }
 
+    @XmlElement(required = true)
     public Vec2D a, b, c;
 
     @XmlTransient
@@ -120,7 +128,7 @@ public class Triangle2D implements Shape2D {
         return this;
     }
 
-    public Vec2D fromBarycentric(ReadonlyVec3D p) {
+    public Vec2D fromBarycentric(roVec3D p) {
         return new Vec2D(a.x * p.x() + b.x * p.y() + c.x * p.z(), a.y * p.x()
                 + b.y * p.y() + c.y * p.z());
     }
@@ -246,8 +254,8 @@ public class Triangle2D implements Shape2D {
         };
         for (Line2D la : ea) {
             for (Line2D lb : eb) {
-                Line2D.LineIntersection.Type type = la.intersectLine(lb).getType();
-                if (type != Line2D.LineIntersection.Type.NON_INTERSECTING && type != Line2D.LineIntersection.Type.PARALLEL) {
+                Type type = la.intersectLine(lb).getType();
+                if (type != Type.NON_INTERSECTING && type != Type.PARALLEL) {
                     return true;
                 }
             }
@@ -268,14 +276,14 @@ public class Triangle2D implements Shape2D {
     /**
      * Produces the barycentric coordinates of the given point within this
      * triangle. These coordinates can then be used to re-project the point into
-     * a different triangle using its {@link #fromBarycentric(ReadonlyVec3D)}
+     * a different triangle using its {@link #fromBarycentric(roVec3D)}
      * method.
      * 
      * @param p
      *            point in world space
      * @return barycentric coords as {@link Vec3D}
      */
-    public Vec3D toBarycentric(ReadonlyVec2D p) {
+    public XYZ toBarycentric(ReadonlyVec2D p) {
         return new Triangle3D(a.to3DXY(), b.to3DXY(), c.to3DXY())
                 .toBarycentric(p.to3DXY());
     }
diff --git a/src/main/java/com/syncleus/spangraph/geom/Triangle3D.java b/src/main/java/toxi/geom/Triangle3D.java
similarity index 93%
rename from src/main/java/com/syncleus/spangraph/geom/Triangle3D.java
rename to src/main/java/toxi/geom/Triangle3D.java
index b50475d..44c787b 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Triangle3D.java
+++ b/src/main/java/toxi/geom/Triangle3D.java
@@ -25,15 +25,15 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
+
+import toxi.math.MathUtils;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlTransient;
 
-import toxi.math.MathUtils;
-
 @XmlAccessorType(XmlAccessType.FIELD)
 public class Triangle3D implements Shape3D {
 
@@ -92,9 +92,9 @@ public class Triangle3D implements Shape3D {
         Vec3D ac = c.sub(a);
         Vec3D bc = c.sub(b);
 
-        ReadonlyVec3D pa = p.sub(a);
-        ReadonlyVec3D pb = p.sub(b);
-        ReadonlyVec3D pc = p.sub(c);
+        roVec3D pa = p.sub(a);
+        roVec3D pb = p.sub(b);
+        roVec3D pc = p.sub(c);
 
         Vec3D ap = a.sub(p);
         Vec3D bp = b.sub(p);
@@ -128,7 +128,7 @@ public class Triangle3D implements Shape3D {
         }
 
         // P is outside (or on) AB if the triple scalar product [N PA PB] <= 0
-        ReadonlyVec3D n = ab.cross(ac);
+        roVec3D n = ab.cross(ac);
         float vc = n.dot(ap.crossSelf(bp));
 
         // If P outside AB and within feature region of AB,
@@ -183,7 +183,7 @@ public class Triangle3D implements Shape3D {
      * 
      * @return true, if point is in triangle.
      */
-    public boolean containsPoint(ReadonlyVec3D p) {
+    public boolean containsPoint(XYZ p) {
         Vec3D v0 = c.sub(a);
         Vec3D v1 = b.sub(a);
         Vec3D v2 = p.sub(a);
@@ -211,7 +211,7 @@ public class Triangle3D implements Shape3D {
         return this;
     }
 
-    public Vec3D fromBarycentric(ReadonlyVec3D p) {
+    public XYZ fromBarycentric(roVec3D p) {
         return new Vec3D(a.x * p.x() + b.x * p.y() + c.x * p.z(), a.y * p.x()
                 + b.y * p.y() + c.y * p.z(), a.z * p.x() + b.z * p.y() + c.z
                 * p.z());
@@ -232,7 +232,7 @@ public class Triangle3D implements Shape3D {
      * @return closest point
      */
 
-    public Vec3D getClosestPointTo(ReadonlyVec3D p) {
+    public XYZ getClosestPointTo(roVec3D p) {
         Line3D edge = new Line3D(a, b);
         final Vec3D Rab = edge.closestPointTo(p);
         final Vec3D Rbc = edge.set(b, c).closestPointTo(p);
@@ -243,7 +243,7 @@ public class Triangle3D implements Shape3D {
         final float dCA = p.sub(Rca).magSquared();
 
         float min = dAB;
-        Vec3D result = Rab;
+        XYZ result = Rab;
 
         if (dBC < min) {
             min = dBC;
@@ -256,13 +256,13 @@ public class Triangle3D implements Shape3D {
         return result;
     }
 
-    public Vec3D[] getVertexArray() {
+    public XYZ[] getVertexArray() {
         return getVertexArray(null, 0);
     }
 
-    public Vec3D[] getVertexArray(Vec3D[] array, int offset) {
+    public XYZ[] getVertexArray(XYZ[] array, int offset) {
         if (array == null) {
-            array = new Vec3D[3];
+            array = new XYZ[3];
         }
         array[offset++] = a;
         array[offset++] = b;
@@ -282,7 +282,7 @@ public class Triangle3D implements Shape3D {
         return Triangle3D.isClockwiseInXY(a, b, c);
     }
 
-    private boolean isSameClockDir(Vec3D a, Vec3D b, ReadonlyVec3D p, Vec3D norm) {
+    private boolean isSameClockDir(Vec3D a, Vec3D b, roVec3D p, Vec3D norm) {
         float bax = b.x - a.x;
         float bay = b.y - a.y;
         float baz = b.z - a.z;
@@ -302,7 +302,7 @@ public class Triangle3D implements Shape3D {
         c = c2;
     }
 
-    public Vec3D toBarycentric(ReadonlyVec3D p) {
+    public XYZ toBarycentric(roVec3D p) {
         Vec3D e = b.sub(a).cross(c.sub(a));
         Vec3D n = e.getNormalized();
 
diff --git a/src/main/java/com/syncleus/spangraph/geom/TriangleIntersector.java b/src/main/java/toxi/geom/TriangleIntersector.java
similarity index 98%
rename from src/main/java/com/syncleus/spangraph/geom/TriangleIntersector.java
rename to src/main/java/toxi/geom/TriangleIntersector.java
index 3a5c6be..fbd8b09 100644
--- a/src/main/java/com/syncleus/spangraph/geom/TriangleIntersector.java
+++ b/src/main/java/toxi/geom/TriangleIntersector.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 import toxi.math.MathUtils;
 
diff --git a/src/main/java/com/syncleus/spangraph/geom/Vec2D.java b/src/main/java/toxi/geom/Vec2D.java
similarity index 94%
rename from src/main/java/com/syncleus/spangraph/geom/Vec2D.java
rename to src/main/java/toxi/geom/Vec2D.java
index 0370221..469bbe7 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Vec2D.java
+++ b/src/main/java/toxi/geom/Vec2D.java
@@ -25,17 +25,37 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
-import javax.xml.bind.annotation.XmlAttribute;
 import java.util.Random;
 
+import javax.xml.bind.annotation.XmlAttribute;
+
+import toxi.math.InterpolateStrategy;
+import toxi.math.MathUtils;
+import toxi.math.ScaleMap;
+
 /**
  * Comprehensive 2D vector class with additional basic intersection and
  * collision detection features.
  */
 public class Vec2D implements Comparable<ReadonlyVec2D>, ReadonlyVec2D {
 
+    public static enum Axis {
+
+        X(Vec2D.X_AXIS),
+        Y(Vec2D.Y_AXIS);
+
+        private final ReadonlyVec2D vector;
+
+        private Axis(ReadonlyVec2D v) {
+            this.vector = v;
+        }
+
+        public ReadonlyVec2D getVector() {
+            return vector;
+        }
+    }
 
     /**
      * Defines positive X axis
@@ -490,11 +510,11 @@ public class Vec2D implements Comparable<ReadonlyVec2D>, ReadonlyVec2D {
         }
         return new Vec2D(this);
     }
-//
-//    public Vec2D getMapped(ScaleMap map) {
-//        return new Vec2D((float) map.getClippedValueFor(x),
-//                (float) map.getClippedValueFor(y));
-//    }
+
+    public Vec2D getMapped(ScaleMap map) {
+        return new Vec2D((float) map.getClippedValueFor(x),
+                (float) map.getClippedValueFor(y));
+    }
 
     public final Vec2D getNormalized() {
         return new Vec2D(this).normalize();
@@ -556,17 +576,17 @@ public class Vec2D implements Comparable<ReadonlyVec2D>, ReadonlyVec2D {
         return new Vec2D(x + (v.x() - x) * f, y + (v.y() - y) * f);
     }
 
-//    public Vec2D interpolateTo(ReadonlyVec2D v, float f, InterpolateStrategy s) {
-//        return new Vec2D(s.interpolate(x, v.x(), f), s.interpolate(y, v.y(), f));
-//    }
+    public Vec2D interpolateTo(ReadonlyVec2D v, float f, InterpolateStrategy s) {
+        return new Vec2D(s.interpolate(x, v.x(), f), s.interpolate(y, v.y(), f));
+    }
 
     public final Vec2D interpolateTo(Vec2D v, float f) {
         return new Vec2D(x + (v.x - x) * f, y + (v.y - y) * f);
     }
 
-//    public Vec2D interpolateTo(Vec2D v, float f, InterpolateStrategy s) {
-//        return new Vec2D(s.interpolate(x, v.x, f), s.interpolate(y, v.y, f));
-//    }
+    public Vec2D interpolateTo(Vec2D v, float f, InterpolateStrategy s) {
+        return new Vec2D(s.interpolate(x, v.x, f), s.interpolate(y, v.y, f));
+    }
 
     /**
      * Interpolates the vector towards the given target vector, using linear
@@ -584,24 +604,24 @@ public class Vec2D implements Comparable<ReadonlyVec2D>, ReadonlyVec2D {
         return this;
     }
 
-//    /**
-//     * Interpolates the vector towards the given target vector, using the given
-//     * {@link InterpolateStrategy}
-//     *
-//     * @param v
-//     *            target vector
-//     * @param f
-//     *            interpolation factor (should be in the range 0..1)
-//     * @param s
-//     *            InterpolateStrategy instance
-//     * @return itself, result overrides current vector
-//     */
-//    public Vec2D interpolateToSelf(ReadonlyVec2D v, float f,
-//            InterpolateStrategy s) {
-//        x = s.interpolate(x, v.x(), f);
-//        y = s.interpolate(y, v.y(), f);
-//        return this;
-//    }
+    /**
+     * Interpolates the vector towards the given target vector, using the given
+     * {@link InterpolateStrategy}
+     * 
+     * @param v
+     *            target vector
+     * @param f
+     *            interpolation factor (should be in the range 0..1)
+     * @param s
+     *            InterpolateStrategy instance
+     * @return itself, result overrides current vector
+     */
+    public Vec2D interpolateToSelf(ReadonlyVec2D v, float f,
+            InterpolateStrategy s) {
+        x = s.interpolate(x, v.x(), f);
+        y = s.interpolate(y, v.y(), f);
+        return this;
+    }
 
     /**
      * Scales vector uniformly by factor -1 ( v = -v ), overrides coordinates
@@ -1051,7 +1071,7 @@ public class Vec2D implements Comparable<ReadonlyVec2D>, ReadonlyVec2D {
         return new Vec3D(x, 0, y);
     }
 
-    public final Vec3D to3DYZ() {
+    public final XYZ to3DYZ() {
         return new Vec3D(0, x, y);
     }
 
diff --git a/src/main/java/com/syncleus/spangraph/geom/Vec3D.java b/src/main/java/toxi/geom/Vec3D.java
similarity index 81%
rename from src/main/java/com/syncleus/spangraph/geom/Vec3D.java
rename to src/main/java/toxi/geom/Vec3D.java
index bb4018a..6d650ef 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Vec3D.java
+++ b/src/main/java/toxi/geom/Vec3D.java
@@ -25,44 +25,67 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
-import java.util.Random;
+import toxi.math.InterpolateStrategy;
+import toxi.math.MathUtils;
+import toxi.math.ScaleMap;
 
+import javax.xml.bind.annotation.XmlAttribute;
+import java.util.Random;
 
 /**
  * Comprehensive 3D vector class with additional basic intersection and
  * collision detection features.
  */
-public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
+public class Vec3D implements Comparable<roVec3D>, roVec3D {
+
+
+
+    public static enum Axis {
+
+        X(Vec3D.X_AXIS),
+        Y(Vec3D.Y_AXIS),
+        Z(Vec3D.Z_AXIS);
+
+        private final roVec3D vector;
+
+        private Axis(roVec3D v) {
+            this.vector = v;
+        }
+
+        public roVec3D getVector() {
+            return vector;
+        }
+    }
 
     /** Defines positive X axis. */
-    public static final ReadonlyVec3D X_AXIS = new Vec3D(1, 0, 0);
+    public static final roVec3D X_AXIS = new Vec3D(1, 0, 0);
 
     /** Defines positive Y axis. */
-    public static final ReadonlyVec3D Y_AXIS = new Vec3D(0, 1, 0);
+    public static final roVec3D Y_AXIS = new Vec3D(0, 1, 0);
 
     /** Defines positive Z axis. */
-    public static final ReadonlyVec3D Z_AXIS = new Vec3D(0, 0, 1);
+    public static final roVec3D Z_AXIS = new Vec3D(0, 0, 1);
 
     /** Defines the zero vector. */
-    public static final ReadonlyVec3D ZERO = new Vec3D();;
+    public static final roVec3D ZERO = new Vec3D();;
 
     /**
      * Defines vector with all coords set to Float.MIN_VALUE. Useful for
      * bounding box operations.
      */
-    public static final ReadonlyVec3D MIN_VALUE = new Vec3D(Float.MIN_VALUE,
+    public static final roVec3D MIN_VALUE = new Vec3D(Float.MIN_VALUE,
             Float.MIN_VALUE, Float.MIN_VALUE);
 
     /**
      * Defines vector with all coords set to Float.MAX_VALUE. Useful for
      * bounding box operations.
      */
-    public static final ReadonlyVec3D MAX_VALUE = new Vec3D(Float.MAX_VALUE,
+    public static final roVec3D MAX_VALUE = new Vec3D(Float.MAX_VALUE,
             Float.MAX_VALUE, Float.MAX_VALUE);
 
-    public static final ReadonlyVec3D NEG_MAX_VALUE = new Vec3D(
+    public static final roVec3D NEG_MAX_VALUE = new Vec3D(
             -Float.MAX_VALUE, -Float.MAX_VALUE, -Float.MAX_VALUE);
 
     /**
@@ -76,7 +99,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return new vector in the XY plane
      */
-    public static final Vec3D fromXYTheta(float theta) {
+    public static final XYZ fromXYTheta(float theta) {
         return new Vec3D((float) Math.cos(theta), (float) Math.sin(theta), 0);
     }
 
@@ -91,7 +114,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return new vector in the XZ plane
      */
-    public static final Vec3D fromXZTheta(float theta) {
+    public static final XYZ fromXZTheta(float theta) {
         return new Vec3D((float) Math.cos(theta), 0, (float) Math.sin(theta));
     }
 
@@ -106,7 +129,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return new vector in the YZ plane
      */
-    public static final Vec3D fromYZTheta(float theta) {
+    public static final XYZ fromYZTheta(float theta) {
         return new Vec3D(0, (float) Math.cos(theta), (float) Math.sin(theta));
     }
 
@@ -121,7 +144,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return result as new vector
      */
-    public static final Vec3D max(ReadonlyVec3D a, ReadonlyVec3D b) {
+    public static final Vec3D max(roVec3D a, roVec3D b) {
         return new Vec3D(MathUtils.max(a.x(), b.x()), MathUtils.max(a.y(),
                 b.y()), MathUtils.max(a.z(), b.z()));
     }
@@ -137,7 +160,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return result as new vector
      */
-    public static final Vec3D min(ReadonlyVec3D a, ReadonlyVec3D b) {
+    public static final Vec3D min(roVec3D a, roVec3D b) {
         return new Vec3D(MathUtils.min(a.x(), b.x()), MathUtils.min(a.y(),
                 b.y()), MathUtils.min(a.z(), b.z()));
     }
@@ -148,7 +171,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return a new random normalized unit vector.
      */
-    public static final Vec3D randomVector() {
+    public static final XYZ randomVector() {
         return randomVector(MathUtils.RND);
     }
 
@@ -163,19 +186,22 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return a new random normalized unit vector.
      */
-    public static final Vec3D randomVector(Random rnd) {
+    public static final XYZ randomVector(Random rnd) {
         Vec3D v = new Vec3D(rnd.nextFloat() * 2 - 1, rnd.nextFloat() * 2 - 1,
                 rnd.nextFloat() * 2 - 1);
         return v.normalize();
     }
 
     /** X coordinate. */
+    @XmlAttribute(required = true)
     public float x;
 
     /** Y coordinate. */
+    @XmlAttribute(required = true)
     public float y;
 
     /** Z coordinate. */
+    @XmlAttribute(required = true)
     public float z;
 
     /**
@@ -208,11 +234,11 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
 
     /**
      * Creates a new vector with the coordinates of the given vector.
-     * 
+     *
      * @param v
      *            vector to be copied
      */
-    public Vec3D(ReadonlyVec3D v) {
+    public Vec3D(XYZ v) {
         this.x = v.x();
         this.y = v.y();
         this.z = v.z();
@@ -234,7 +260,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         return new Vec3D(x + a, y + b, z + c);
     }
 
-    public Vec3D add(ReadonlyVec3D v) {
+    public Vec3D add(roVec3D v) {
         return new Vec3D(x + v.x(), y + v.y(), z + v.z());
     }
 
@@ -254,14 +280,14 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public final Vec3D addSelf(float a, float b, float c) {
+    public final XYZ addSelf(float a, float b, float c) {
         x += a;
         y += b;
         z += c;
         return this;
     }
 
-    public final Vec3D addSelf(ReadonlyVec3D v) {
+    public final Vec3D addSelf(XYZ v) {
         x += v.x();
         y += v.y();
         z += v.z();
@@ -283,11 +309,11 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         return this;
     }
 
-    public final float angleBetween(ReadonlyVec3D v) {
+    public final float angleBetween(XYZ v) {
         return (float) Math.acos(dot(v));
     }
 
-    public final float angleBetween(ReadonlyVec3D v, boolean forceNormalize) {
+    public final float angleBetween(XYZ v, boolean forceNormalize) {
         float theta;
         if (forceNormalize) {
             theta = getNormalized().dot(v.getNormalized());
@@ -302,12 +328,12 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public final ReadonlyVec3D clear() {
+    public roVec3D clear() {
         x = y = z = 0;
         return this;
     }
 
-    public int compareTo(ReadonlyVec3D v) {
+    public int compareTo(roVec3D v) {
         if (x == v.x() && y == v.y() && z == v.z()) {
             return 0;
         }
@@ -327,7 +353,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public Vec3D constrain(AABB box) {
+    public XYZ constrain(AABB box) {
         return constrain(box.getMin(), box.getMax());
     }
 
@@ -339,7 +365,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * @param max
      * @return itself
      */
-    public Vec3D constrain(Vec3D min, Vec3D max) {
+    public XYZ constrain(Vec3D min, Vec3D max) {
         x = MathUtils.clip(x, min.x, max.x);
         y = MathUtils.clip(y, min.y, max.y);
         z = MathUtils.clip(z, min.z, max.z);
@@ -350,7 +376,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         return new Vec3D(this);
     }
 
-    public final Vec3D cross(ReadonlyVec3D v) {
+    public final Vec3D cross(XYZ v) {
         return new Vec3D(y * v.z() - v.y() * z, z * v.x() - v.z() * x, x
                 * v.y() - v.x() * y);
     }
@@ -360,7 +386,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
                 * y);
     }
 
-    public final Vec3D crossInto(ReadonlyVec3D v, Vec3D result) {
+    public final Vec3D crossInto(XYZ v, Vec3D result) {
         final float vx = v.x();
         final float vy = v.y();
         final float vz = v.z();
@@ -389,29 +415,9 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         return this;
     }
 
-    public final float distanceTo(ReadonlyVec3D v) {
-        if (v != null) {
-            final float dx = x - v.x();
-            final float dy = y - v.y();
-            final float dz = z - v.z();
-            return (float) Math.sqrt(dx * dx + dy * dy + dz * dz);
-        } else {
-            return Float.NaN;
-        }
-    }
 
-    public final float distanceToSquared(ReadonlyVec3D v) {
-        if (v != null) {
-            final float dx = x - v.x();
-            final float dy = y - v.y();
-            final float dz = z - v.z();
-            return dx * dx + dy * dy + dz * dz;
-        } else {
-            return Float.NaN;
-        }
-    }
 
-    public final float dot(ReadonlyVec3D v) {
+    public final float dot(XYZ v) {
         return x * v.x() + y * v.y() + z * v.z();
     }
 
@@ -429,7 +435,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      */
     public boolean equals(Object v) {
         try {
-            ReadonlyVec3D vv = (ReadonlyVec3D) v;
+            roVec3D vv = (roVec3D) v;
             return (x == vv.x() && y == vv.y() && z == vv.z());
         } catch (NullPointerException e) {
             return false;
@@ -446,7 +452,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      *            the vector with which the comparison is made
      * @return true or false
      */
-    public boolean equals(ReadonlyVec3D v) {
+    public boolean equals(roVec3D v) {
         try {
             return (x == v.x() && y == v.y() && z == v.z());
         } catch (NullPointerException e) {
@@ -454,7 +460,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         }
     }
 
-    public boolean equalsWithTolerance(ReadonlyVec3D v, float tolerance) {
+    public boolean equalsWithTolerance(roVec3D v, float tolerance) {
         try {
             float diff = x - v.x();
             if (Float.isNaN(diff)) {
@@ -489,7 +495,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public final Vec3D floor() {
+    public final XYZ floor() {
         x = MathUtils.floor(x);
         y = MathUtils.floor(y);
         z = MathUtils.floor(z);
@@ -502,7 +508,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public final Vec3D frac() {
+    public final XYZ frac() {
         x -= MathUtils.floor(x);
         y -= MathUtils.floor(y);
         z -= MathUtils.floor(z);
@@ -513,7 +519,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         return new Vec3D(this).abs();
     }
 
-    public Vec3D getCartesian() {
+    public XYZ getCartesian() {
         return copy().toCartesian();
     }
 
@@ -569,7 +575,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @see toxi.geom.ReadonlyVec3D#getConstrained(toxi.geom.AABB)
      */
-    public final Vec3D getConstrained(AABB box) {
+    public final XYZ getConstrained(AABB box) {
         return new Vec3D(this).constrain(box);
     }
 
@@ -578,7 +584,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @see toxi.geom.ReadonlyVec3D#getFloored()
      */
-    public final Vec3D getFloored() {
+    public final XYZ getFloored() {
         return new Vec3D(this).floor();
     }
 
@@ -587,7 +593,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @see toxi.geom.ReadonlyVec3D#getFrac()
      */
-    public final Vec3D getFrac() {
+    public final XYZ getFrac() {
         return new Vec3D(this).frac();
     }
 
@@ -605,26 +611,17 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @see toxi.geom.ReadonlyVec3D#getLimited(float)
      */
-    public final Vec3D getLimited(float lim) {
+    public final XYZ getLimited(float lim) {
         if (magSquared() > lim * lim) {
             return getNormalizedTo(lim);
         }
         return new Vec3D(this);
     }
 
-    /*public Vec3D getMapped(ScaleMap map) {
+    public XYZ getMapped(ScaleMap map) {
         return new Vec3D((float) map.getClippedValueFor(x),
                 (float) map.getClippedValueFor(y),
                 (float) map.getClippedValueFor(z));
-    }*/
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see toxi.geom.ReadonlyVec3D#getNormalized()
-     */
-    public final Vec3D getNormalized() {
-        return new Vec3D(this).normalize();
     }
 
     /*
@@ -641,11 +638,11 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @see toxi.geom.ReadonlyVec3D#getReciprocal()
      */
-    public final Vec3D getReciprocal() {
+    public final XYZ getReciprocal() {
         return copy().reciprocal();
     }
 
-    public final Vec3D getReflected(ReadonlyVec3D normal) {
+    public final XYZ getReflected(roVec3D normal) {
         return copy().reflect(normal);
     }
 
@@ -654,7 +651,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @see toxi.geom.ReadonlyVec3D#getRotatedAroundAxis(toxi.geom.Vec3D, float)
      */
-    public final Vec3D getRotatedAroundAxis(ReadonlyVec3D axis, float theta) {
+    public final XYZ getRotatedAroundAxis(roVec3D axis, float theta) {
         return new Vec3D(this).rotateAroundAxis(axis, theta);
     }
 
@@ -663,7 +660,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @see toxi.geom.ReadonlyVec3D#getRotatedX(float)
      */
-    public final Vec3D getRotatedX(float theta) {
+    public final XYZ getRotatedX(float theta) {
         return new Vec3D(this).rotateX(theta);
     }
 
@@ -672,7 +669,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @see toxi.geom.ReadonlyVec3D#getRotatedY(float)
      */
-    public final Vec3D getRotatedY(float theta) {
+    public final XYZ getRotatedY(float theta) {
         return new Vec3D(this).rotateY(theta);
     }
 
@@ -681,11 +678,11 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @see toxi.geom.ReadonlyVec3D#getRotatedZ(float)
      */
-    public final Vec3D getRotatedZ(float theta) {
+    public final XYZ getRotatedZ(float theta) {
         return new Vec3D(this).rotateZ(theta);
     }
 
-    public Vec3D getRoundedTo(float prec) {
+    public XYZ getRoundedTo(float prec) {
         return copy().roundTo(prec);
     }
 
@@ -698,7 +695,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         return new Vec3D(this).signum();
     }
 
-    public Vec3D getSpherical() {
+    public XYZ getSpherical() {
         return copy().toSpherical();
     }
 
@@ -746,69 +743,47 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         return (float) Math.atan2(y, z);
     }
 
-    public ReadonlyVec3D immutable() {
+    public roVec3D immutable() {
         return this;
     }
 
-    public final Vec3D interpolateTo(ReadonlyVec3D v, float f) {
-        return new Vec3D(x + (v.x() - x) * f, y + (v.y() - y) * f, z
-                + (v.z() - z) * f);
-    }
-
-//    public final Vec3D interpolateTo(ReadonlyVec3D v, float f,
-//            InterpolateStrategy s) {
-//        return new Vec3D(s.interpolate(x, v.x(), f),
-//                s.interpolate(y, v.y(), f), s.interpolate(z, v.z(), f));
-//    }
-//
-    public final Vec3D interpolateTo(Vec3D v, float f) {
-        return new Vec3D(x + (v.x - x) * f, y + (v.y - y) * f, z + (v.z - z)
-                * f);
-    }
-//
-//    public final Vec3D interpolateTo(Vec3D v, float f, InterpolateStrategy s) {
-//        return new Vec3D(s.interpolate(x, v.x, f), s.interpolate(y, v.y, f),
-//                s.interpolate(z, v.z, f));
-//    }
-
     /**
      * Interpolates the vector towards the given target vector, using linear
      * interpolation.
-     * 
+     *
      * @param v
      *            target vector
      * @param f
      *            interpolation factor (should be in the range 0..1)
-     * 
+     *
      * @return itself, result overrides current vector
      */
-    public final Vec3D interpolateToSelf(ReadonlyVec3D v, float f) {
+    public final Vec3D interpolateToSelf(roVec3D v, float f) {
         x += (v.x() - x) * f;
         y += (v.y() - y) * f;
         z += (v.z() - z) * f;
         return this;
     }
-
-//    /**
-//     * Interpolates the vector towards the given target vector, using the given
-//     * {@link InterpolateStrategy}.
-//     *
-//     * @param v
-//     *            target vector
-//     * @param f
-//     *            interpolation factor (should be in the range 0..1)
-//     * @param s
-//     *            InterpolateStrategy instance
-//     *
-//     * @return itself, result overrides current vector
-//     */
-//    public final Vec3D interpolateToSelf(ReadonlyVec3D v, float f,
-//            InterpolateStrategy s) {
-//        x = s.interpolate(x, v.x(), f);
-//        y = s.interpolate(y, v.y(), f);
-//        z = s.interpolate(z, v.z(), f);
-//        return this;
-//    }
+    /**
+     * Interpolates the vector towards the given target vector, using the given
+     * {@link InterpolateStrategy}.
+     * 
+     * @param v
+     *            target vector
+     * @param f
+     *            interpolation factor (should be in the range 0..1)
+     * @param s
+     *            InterpolateStrategy instance
+     * 
+     * @return itself, result overrides current vector
+     */
+    public final XYZ interpolateToSelf(roVec3D v, float f,
+            InterpolateStrategy s) {
+        x = s.interpolate(x, v.x(), f);
+        y = s.interpolate(y, v.y(), f);
+        z = s.interpolate(z, v.z(), f);
+        return this;
+    }
 
     /**
      * Scales vector uniformly by factor -1 ( v = -v ), overrides coordinates
@@ -816,32 +791,14 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public final Vec3D invert() {
+    public final XYZ invert() {
         x *= -1;
         y *= -1;
         z *= -1;
         return this;
     }
 
-    /*
-     * (non-Javadoc)
-     * 
-     * @see toxi.geom.ReadonlyVec3D#isInAABB(toxi.geom.AABB)
-     */
-    public boolean isInAABB(AABB box) {
-        final Vec3D min = box.getMin();
-        final Vec3D max = box.getMax();
-        if (x < min.x || x > max.x) {
-            return false;
-        }
-        if (y < min.y || y > max.y) {
-            return false;
-        }
-        if (z < min.z || z > max.z) {
-            return false;
-        }
-        return true;
-    }
+
 
     public boolean isInAABB(Vec3D boxOrigin, Vec3D boxExtent) {
         float w = boxExtent.x;
@@ -895,7 +852,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return the vec3 d
      */
-    public final Vec3D jitter(float j) {
+    public final XYZ jitter(float j) {
         return jitter(j, j, j);
     }
 
@@ -912,25 +869,25 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public final Vec3D jitter(float jx, float jy, float jz) {
+    public final XYZ jitter(float jx, float jy, float jz) {
         x += MathUtils.normalizedRandom() * jx;
         y += MathUtils.normalizedRandom() * jy;
         z += MathUtils.normalizedRandom() * jz;
         return this;
     }
 
-    public final Vec3D jitter(Random rnd, float j) {
+    public final XYZ jitter(Random rnd, float j) {
         return jitter(rnd, j, j, j);
     }
 
-    public final Vec3D jitter(Random rnd, float jx, float jy, float jz) {
+    public final XYZ jitter(Random rnd, float jx, float jy, float jz) {
         x += MathUtils.normalizedRandom(rnd) * jx;
         y += MathUtils.normalizedRandom(rnd) * jy;
         z += MathUtils.normalizedRandom(rnd) * jz;
         return this;
     }
 
-    public final Vec3D jitter(Random rnd, Vec3D jitterVec) {
+    public final XYZ jitter(Random rnd, Vec3D jitterVec) {
         return jitter(rnd, jitterVec.x, jitterVec.y, jitterVec.z);
     }
 
@@ -944,7 +901,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public final Vec3D jitter(Vec3D jitterVec) {
+    public final XYZ jitter(Vec3D jitterVec) {
         return jitter(jitterVec.x, jitterVec.y, jitterVec.z);
     }
 
@@ -979,7 +936,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return the vec3 d
      */
-    public final Vec3D maxSelf(ReadonlyVec3D b) {
+    public final XYZ maxSelf(XYZ b) {
         x = MathUtils.max(x, b.x());
         y = MathUtils.max(y, b.y());
         z = MathUtils.max(z, b.z());
@@ -994,7 +951,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return the vec3 d
      */
-    public final Vec3D minSelf(ReadonlyVec3D b) {
+    public final XYZ minSelf(XYZ b) {
         x = MathUtils.min(x, b.x());
         y = MathUtils.min(y, b.y());
         z = MathUtils.min(z, b.z());
@@ -1010,7 +967,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public final Vec3D modSelf(float base) {
+    public final XYZ modSelf(float base) {
         x %= base;
         y %= base;
         z %= base;
@@ -1030,7 +987,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * @return itself
      */
 
-    public final Vec3D modSelf(float bx, float by, float bz) {
+    public final XYZ modSelf(float bx, float by, float bz) {
         x %= bx;
         y %= by;
         z %= bz;
@@ -1083,7 +1040,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         return this;
     }
 
-    public final Vec3D reflect(ReadonlyVec3D normal) {
+    public final XYZ reflect(roVec3D normal) {
         return set(normal.scale(this.dot(normal) * 2).subSelf(this));
     }
 
@@ -1097,7 +1054,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public final Vec3D rotateAroundAxis(ReadonlyVec3D axis, float theta) {
+    public final XYZ rotateAroundAxis(XYZ axis, float theta) {
         final float ax = axis.x();
         final float ay = axis.y();
         final float az = axis.z();
@@ -1135,7 +1092,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public final Vec3D rotateX(float theta) {
+    public final XYZ rotateX(float theta) {
         final float co = (float) Math.cos(theta);
         final float si = (float) Math.sin(theta);
         final float zz = co * z - si * y;
@@ -1152,7 +1109,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public final Vec3D rotateY(float theta) {
+    public final XYZ rotateY(float theta) {
         final float co = (float) Math.cos(theta);
         final float si = (float) Math.sin(theta);
         final float xx = co * x - si * z;
@@ -1169,7 +1126,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public final Vec3D rotateZ(float theta) {
+    public final XYZ rotateZ(float theta) {
         final float co = (float) Math.cos(theta);
         final float si = (float) Math.sin(theta);
         final float xx = co * x - si * y;
@@ -1178,26 +1135,23 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         return this;
     }
 
-    public Vec3D roundTo(float prec) {
+    public XYZ roundTo(float prec) {
         x = MathUtils.roundTo(x, prec);
         y = MathUtils.roundTo(y, prec);
         z = MathUtils.roundTo(z, prec);
         return this;
     }
 
-    public Vec3D scale(float s) {
-        return new Vec3D(x * s, y * s, z * s);
-    }
 
-    public Vec3D scale(float a, float b, float c) {
+    public XYZ scale(float a, float b, float c) {
         return new Vec3D(x * a, y * b, z * c);
     }
 
-    public Vec3D scale(ReadonlyVec3D s) {
+    public XYZ scale(roVec3D s) {
         return new Vec3D(x * s.x(), y * s.y(), z * s.z());
     }
 
-    public Vec3D scale(Vec3D s) {
+    public XYZ scale(Vec3D s) {
         return new Vec3D(x * s.x, y * s.y, z * s.z);
     }
 
@@ -1229,14 +1183,14 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public Vec3D scaleSelf(float a, float b, float c) {
+    public XYZ scaleSelf(float a, float b, float c) {
         x *= a;
         y *= b;
         z *= c;
         return this;
     }
 
-    public Vec3D scaleSelf(ReadonlyVec3D s) {
+    public XYZ scaleSelf(roVec3D s) {
         x *= s.x();
         y *= s.y();
         z *= s.z();
@@ -1279,7 +1233,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         return this;
     }
 
-    public Vec3D set(ReadonlyVec3D v) {
+    public Vec3D set(XYZ v) {
         x = v.x();
         y = v.y();
         z = v.z();
@@ -1301,7 +1255,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         return this;
     }
 
-    public final Vec3D setComponent(Axis id, float val) {
+    public final XYZ setComponent(Axis id, float val) {
         switch (id) {
             case X:
                 x = val;
@@ -1316,7 +1270,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         return this;
     }
 
-    public final Vec3D setComponent(int id, float val) {
+    public final XYZ setComponent(int id, float val) {
         switch (id) {
             case 0:
                 x = val;
@@ -1331,7 +1285,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         return this;
     }
 
-    public Vec3D setX(float x) {
+    public XYZ setX(float x) {
         this.x = x;
         return this;
     }
@@ -1344,23 +1298,23 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public Vec3D setXY(Vec2D v) {
+    public XYZ setXY(Vec2D v) {
         x = v.x;
         y = v.y;
         return this;
     }
 
-    public Vec3D setY(float y) {
+    public XYZ setY(float y) {
         this.y = y;
         return this;
     }
 
-    public Vec3D setZ(float z) {
+    public XYZ setZ(float z) {
         this.z = z;
         return this;
     }
 
-    public Vec3D shuffle(int iterations) {
+    public XYZ shuffle(int iterations) {
         float t;
         for (int i = 0; i < iterations; i++) {
             switch (MathUtils.random(3)) {
@@ -1404,7 +1358,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public final Vec3D snapToAxis() {
+    public final XYZ snapToAxis() {
         if (MathUtils.abs(x) < 0.5f) {
             x = 0;
         } else {
@@ -1426,17 +1380,14 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         return this;
     }
 
-    public final Vec3D sub(float a, float b, float c) {
+    public final XYZ sub(float a, float b, float c) {
         return new Vec3D(x - a, y - b, z - c);
     }
 
-    public final Vec3D sub(ReadonlyVec3D v) {
+    public final Vec3D sub(roVec3D v) {
         return new Vec3D(x - v.x(), y - v.y(), z - v.z());
     }
 
-    public final Vec3D sub(Vec3D v) {
-        return new Vec3D(x - v.x, y - v.y, z - v.z);
-    }
 
     /**
      * Subtracts vector {a,b,c} and overrides coordinates with result.
@@ -1450,14 +1401,14 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
      * 
      * @return itself
      */
-    public final Vec3D subSelf(float a, float b, float c) {
+    public final XYZ subSelf(float a, float b, float c) {
         x -= a;
         y -= b;
         z -= c;
         return this;
     }
 
-    public final Vec3D subSelf(ReadonlyVec3D v) {
+    public final Vec3D subSelf(roVec3D v) {
         x -= v.x();
         y -= v.y();
         z -= v.z();
@@ -1511,7 +1462,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         };
     }
 
-    public final Vec3D toCartesian() {
+    public final XYZ toCartesian() {
         final float a = (float) (x * Math.cos(z));
         final float xx = (float) (a * Math.cos(y));
         final float yy = (float) (x * Math.sin(z));
@@ -1522,7 +1473,7 @@ public class Vec3D implements Comparable<ReadonlyVec3D>, ReadonlyVec3D {
         return this;
     }
 
-    public final Vec3D toSpherical() {
+    public final XYZ toSpherical() {
         final float xx = Math.abs(x) <= MathUtils.EPS ? MathUtils.EPS : x;
         final float zz = z;
 
diff --git a/src/main/java/com/syncleus/spangraph/geom/Vec4D.java b/src/main/java/toxi/geom/Vec4D.java
similarity index 97%
rename from src/main/java/com/syncleus/spangraph/geom/Vec4D.java
rename to src/main/java/toxi/geom/Vec4D.java
index 4712c7d..c673691 100644
--- a/src/main/java/com/syncleus/spangraph/geom/Vec4D.java
+++ b/src/main/java/toxi/geom/Vec4D.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 import javax.xml.bind.annotation.XmlAttribute;
 
@@ -61,7 +61,7 @@ public class Vec4D implements ReadonlyVec4D, Cloneable {
         this.w = w;
     }
 
-    public Vec4D(ReadonlyVec3D v, float w) {
+    public Vec4D(roVec3D v, float w) {
         this.x = v.x();
         this.y = v.y();
         this.z = v.z();
@@ -108,7 +108,7 @@ public class Vec4D implements ReadonlyVec4D, Cloneable {
         return new Vec4D(x + xx, y + yy, z + zz, w);
     }
 
-    public final Vec4D addXYZ(ReadonlyVec3D v) {
+    public final Vec4D addXYZ(roVec3D v) {
         return new Vec4D(x + v.x(), y + v.y(), z + v.z(), w);
     }
 
@@ -119,7 +119,7 @@ public class Vec4D implements ReadonlyVec4D, Cloneable {
         return this;
     }
 
-    public final Vec4D addXYZSelf(ReadonlyVec3D v) {
+    public final Vec4D addXYZSelf(roVec3D v) {
         this.x += v.x();
         this.y += v.y();
         this.z += v.z();
@@ -297,7 +297,7 @@ public class Vec4D implements ReadonlyVec4D, Cloneable {
         return copy().normalizeTo(len);
     }
 
-    public Vec4D getRotatedAroundAxis(ReadonlyVec3D axis, float theta) {
+    public Vec4D getRotatedAroundAxis(roVec3D axis, float theta) {
         return copy().rotateAroundAxis(axis, theta);
     }
 
@@ -439,7 +439,7 @@ public class Vec4D implements ReadonlyVec4D, Cloneable {
      * 
      * @return itself
      */
-    public final Vec4D rotateAroundAxis(ReadonlyVec3D axis, float theta) {
+    public final Vec4D rotateAroundAxis(roVec3D axis, float theta) {
         final float ax = axis.x();
         final float ay = axis.y();
         final float az = axis.z();
@@ -609,7 +609,7 @@ public class Vec4D implements ReadonlyVec4D, Cloneable {
         return this;
     }
 
-    public final Vec4D setXYZ(ReadonlyVec3D v) {
+    public final Vec4D setXYZ(roVec3D v) {
         this.x = v.x();
         this.y = v.y();
         this.z = v.z();
@@ -642,7 +642,7 @@ public class Vec4D implements ReadonlyVec4D, Cloneable {
         return new Vec4D(x - xx, y - yy, z - zz, w);
     }
 
-    public final Vec4D subXYZ(ReadonlyVec3D v) {
+    public final Vec4D subXYZ(roVec3D v) {
         return new Vec4D(x - v.x(), y - v.y(), z - v.z(), w);
     }
 
@@ -653,7 +653,7 @@ public class Vec4D implements ReadonlyVec4D, Cloneable {
         return this;
     }
 
-    public final Vec4D subXYZSelf(ReadonlyVec3D v) {
+    public final Vec4D subXYZSelf(roVec3D v) {
         this.x -= v.x();
         this.y -= v.y();
         this.z -= v.z();
@@ -721,7 +721,7 @@ public class Vec4D implements ReadonlyVec4D, Cloneable {
         return this;
     }
 
-    public final Vec3D weightInto(Vec3D out) {
+    public final XYZ weightInto(Vec3D out) {
         out.x = x * w;
         out.y = y * w;
         out.z = z * w;
diff --git a/src/main/java/com/syncleus/spangraph/geom/VecMathUtil.java b/src/main/java/toxi/geom/VecMathUtil.java
similarity index 99%
rename from src/main/java/com/syncleus/spangraph/geom/VecMathUtil.java
rename to src/main/java/toxi/geom/VecMathUtil.java
index 77a13ae..2038bc4 100644
--- a/src/main/java/com/syncleus/spangraph/geom/VecMathUtil.java
+++ b/src/main/java/toxi/geom/VecMathUtil.java
@@ -29,7 +29,7 @@
  * $State$
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
 /**
  * Utility vecmath class used when computing the hash code for vecmath objects
diff --git a/src/main/java/toxi/geom/XAxisCylinder.java b/src/main/java/toxi/geom/XAxisCylinder.java
new file mode 100644
index 0000000..a19a721
--- /dev/null
+++ b/src/main/java/toxi/geom/XAxisCylinder.java
@@ -0,0 +1,52 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.geom;
+
+import toxi.math.MathUtils;
+
+public class XAxisCylinder extends AxisAlignedCylinder {
+
+    public XAxisCylinder(roVec3D pos, float radius, float length) {
+        super(pos, radius, length);
+    }
+
+    public boolean containsPoint(XYZ p) {
+        if (MathUtils.abs(p.x() - pos.x) < length * 0.5) {
+            float dy = p.y() - pos.y;
+            float dz = p.z() - pos.z;
+            if (Math.abs(dz * dz + dy * dy) < radiusSquared) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public Vec3D.Axis getMajorAxis() {
+        return Vec3D.Axis.X;
+    }
+}
diff --git a/src/main/java/toxi/geom/XYZ.java b/src/main/java/toxi/geom/XYZ.java
new file mode 100644
index 0000000..91c52c8
--- /dev/null
+++ b/src/main/java/toxi/geom/XYZ.java
@@ -0,0 +1,89 @@
+package toxi.geom;
+
+import toxi.math.InterpolateStrategy;
+
+/**
+ * Created by me on 6/13/15.
+ */
+public interface XYZ {
+
+    public float x();
+
+    public float y();
+
+    public float z();
+
+    default public XYZ interpolateTo(XYZ v, float f) {
+        return new Vec3D(x() + (v.x() - x()) * f, y() + (v.y() - y()) * f, z()
+                + (v.z() - z()) * f);
+    }
+
+    default public XYZ interpolateTo(XYZ v, float f,
+                                   InterpolateStrategy s) {
+        return new Vec3D(s.interpolate(x(), v.x(), f),
+                s.interpolate(y(), v.y(), f), s.interpolate(z(), v.z(), f));
+    }
+
+    default public Vec3D interpolateTo(Vec3D v, float f) {
+        return new Vec3D(x() + (v.x - x()) * f, y() + (v.y - y()) * f, z() + (v.z - z())
+                * f);
+    }
+
+    default public XYZ interpolateTo(Vec3D v, float f, InterpolateStrategy s) {
+        return new Vec3D(s.interpolate(x(), v.x, f), s.interpolate(y(), v.y, f),
+                s.interpolate(z(), v.z, f));
+    }
+
+
+    default public Vec3D scale(float s) {
+        return new Vec3D(x() * s, y() * s, z() * s);
+    }
+
+    default public Vec3D sub(XYZ v) {
+        return new Vec3D(x() - v.x(), y() - v.y(), z() - v.z());
+    }
+
+    default public float distanceTo(XYZ v) {
+        if (v != null) {
+            final float dx = x() - v.x();
+            final float dy = y() - v.y();
+            final float dz = z() - v.z();
+            return (float) Math.sqrt(dx * dx + dy * dy + dz * dz);
+        } else {
+            return Float.NaN;
+        }
+    }
+
+    default public float distanceToSquared(roVec3D v) {
+        if (v != null) {
+            final float dx = x() - v.x();
+            final float dy = y() - v.y();
+            final float dz = z() - v.z();
+            return dx * dx + dy * dy + dz * dz;
+        } else {
+            return Float.NaN;
+        }
+    }
+
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see toxi.geom.ReadonlyVec3D#getNormalized()
+     */
+    default public Vec3D getNormalized() {
+        return new Vec3D(this).normalize();
+    }
+
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see toxi.geom.ReadonlyVec3D#isInAABB(toxi.geom.AABB)
+     */
+    default public boolean isInAABB(final AABB box) {
+        return box.contains(this);
+    }
+
+    default XYZ copy() { return new Vec3D(this); }
+}
diff --git a/src/main/java/toxi/geom/YAxisCylinder.java b/src/main/java/toxi/geom/YAxisCylinder.java
new file mode 100644
index 0000000..092bd08
--- /dev/null
+++ b/src/main/java/toxi/geom/YAxisCylinder.java
@@ -0,0 +1,53 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.geom;
+
+import toxi.math.MathUtils;
+
+public class YAxisCylinder extends AxisAlignedCylinder {
+
+    public YAxisCylinder(roVec3D pos, float radius, float length) {
+        super(pos, radius, length);
+    }
+
+    public boolean containsPoint(XYZ p) {
+        if (MathUtils.abs(p.y() - pos.y) < length * 0.5) {
+            float dx = p.x() - pos.x;
+            float dz = p.z() - pos.z;
+            if (Math.abs(dz * dz + dx * dx) < radiusSquared) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public Vec3D.Axis getMajorAxis() {
+        return Vec3D.Axis.Y;
+    }
+
+}
diff --git a/src/main/java/toxi/geom/ZAxisCylinder.java b/src/main/java/toxi/geom/ZAxisCylinder.java
new file mode 100644
index 0000000..6f47bc1
--- /dev/null
+++ b/src/main/java/toxi/geom/ZAxisCylinder.java
@@ -0,0 +1,52 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.geom;
+
+import toxi.math.MathUtils;
+
+public class ZAxisCylinder extends AxisAlignedCylinder {
+
+    public ZAxisCylinder(roVec3D pos, float radius, float length) {
+        super(pos, radius, length);
+    }
+
+    public boolean containsPoint(XYZ p) {
+        if (MathUtils.abs(p.z() - pos.z) < length * 0.5) {
+            float dx = p.x() - pos.x;
+            float dy = p.y() - pos.y;
+            if (Math.abs(dx * dx + dy * dy) < radiusSquared) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public Vec3D.Axis getMajorAxis() {
+        return Vec3D.Axis.Z;
+    }
+}
diff --git a/src/main/java/toxi/geom/mesh2d/DelaunayTriangle.java b/src/main/java/toxi/geom/mesh2d/DelaunayTriangle.java
new file mode 100644
index 0000000..cf43526
--- /dev/null
+++ b/src/main/java/toxi/geom/mesh2d/DelaunayTriangle.java
@@ -0,0 +1,192 @@
+package toxi.geom.mesh2d;
+
+/*
+ * Copyright (c) 2007 by L. Paul Chew.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, 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 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.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import toxi.util.datatypes.ArraySet;
+
+/**
+ * A DelaunayTriangle is an immutable Set of exactly three Pnts.
+ * 
+ * All Set operations are available. Individual vertices can be accessed via
+ * iterator() and also via triangle.get(index).
+ * 
+ * Note that, even if two triangles have the same vertex set, they are
+ * *different* triangles. Methods equals() and hashCode() are consistent with
+ * this rule.
+ * 
+ * @author Paul Chew
+ * 
+ *         Created December 2007. Replaced general simplices with geometric
+ *         triangle.
+ * 
+ */
+public class DelaunayTriangle extends ArraySet<DelaunayVertex> {
+
+    private int idNumber; // The id number
+    private DelaunayVertex circumcenter = null; // The triangle's circumcenter
+
+    private static int idGenerator = 0; // Used to create id numbers
+    public static boolean moreInfo = false; // True if more info in toString
+
+    /**
+     * @param collection
+     *            a Collection holding the Simplex vertices
+     * @throws IllegalArgumentException
+     *             if there are not three distinct vertices
+     */
+    public DelaunayTriangle(Collection<? extends DelaunayVertex> collection) {
+        super(collection);
+        idNumber = idGenerator++;
+        if (this.size() != 3) {
+            throw new IllegalArgumentException(
+                    "DelaunayTriangle must have 3 vertices");
+        }
+    }
+
+    /**
+     * @param vertices
+     *            the vertices of the DelaunayTriangle.
+     * @throws IllegalArgumentException
+     *             if there are not three distinct vertices
+     */
+    public DelaunayTriangle(DelaunayVertex... vertices) {
+        this(Arrays.asList(vertices));
+    }
+
+    @Override
+    public boolean add(DelaunayVertex vertex) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return (this == o);
+    }
+
+    /**
+     * Report the facet opposite vertex.
+     * 
+     * @param vertex
+     *            a vertex of this DelaunayTriangle
+     * @return the facet opposite vertex
+     * @throws IllegalArgumentException
+     *             if the vertex is not in triangle
+     */
+    public ArraySet<DelaunayVertex> facetOpposite(DelaunayVertex vertex) {
+        ArraySet<DelaunayVertex> facet = new ArraySet<DelaunayVertex>(this);
+        if (!facet.remove(vertex)) {
+            throw new IllegalArgumentException("Vertex not in triangle");
+        }
+        return facet;
+    }
+
+    /**
+     * @return the triangle's circumcenter
+     */
+    public DelaunayVertex getCircumcenter() {
+        if (circumcenter == null) {
+            circumcenter = DelaunayVertex.circumcenter(this
+                    .toArray(new DelaunayVertex[0]));
+        }
+        return circumcenter;
+    }
+
+    /**
+     * Get arbitrary vertex of this triangle, but not any of the bad vertices.
+     * 
+     * @param badVertices
+     *            one or more bad vertices
+     * @return a vertex of this triangle, but not one of the bad vertices
+     * @throws NoSuchElementException
+     *             if no vertex found
+     */
+    public DelaunayVertex getVertexButNot(DelaunayVertex... badVertices) {
+        Collection<DelaunayVertex> bad = Arrays.asList(badVertices);
+        for (DelaunayVertex v : this) {
+            if (!bad.contains(v)) {
+                return v;
+            }
+        }
+        throw new NoSuchElementException("No vertex found");
+    }
+
+    /* The following two methods ensure that a DelaunayTriangle is immutable */
+
+    @Override
+    public int hashCode() {
+        return (idNumber ^ (idNumber >>> 32));
+    }
+
+    /**
+     * True iff triangles are neighbors. Two triangles are neighbors if they
+     * share a facet.
+     * 
+     * @param triangle
+     *            the other DelaunayTriangle
+     * @return true iff this DelaunayTriangle is a neighbor of triangle
+     */
+    public boolean isNeighbor(DelaunayTriangle triangle) {
+        int count = 0;
+        for (DelaunayVertex vertex : this) {
+            if (!triangle.contains(vertex)) {
+                count++;
+            }
+        }
+        return count == 1;
+    }
+
+    /* The following two methods ensure that all triangles are different. */
+
+    @Override
+    public Iterator<DelaunayVertex> iterator() {
+        return new Iterator<DelaunayVertex>() {
+
+            private Iterator<DelaunayVertex> it = DelaunayTriangle.super
+                    .iterator();
+
+            public boolean hasNext() {
+                return it.hasNext();
+            }
+
+            public DelaunayVertex next() {
+                return it.next();
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    @Override
+    public String toString() {
+        if (!moreInfo) {
+            return "DelaunayTriangle" + idNumber;
+        }
+        return "DelaunayTriangle" + idNumber + super.toString();
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/toxi/geom/mesh2d/DelaunayTriangulation.java b/src/main/java/toxi/geom/mesh2d/DelaunayTriangulation.java
new file mode 100644
index 0000000..9fcb2b4
--- /dev/null
+++ b/src/main/java/toxi/geom/mesh2d/DelaunayTriangulation.java
@@ -0,0 +1,321 @@
+package toxi.geom.mesh2d;
+
+/*
+ * Copyright (c) 2005, 2007 by L. Paul Chew.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, 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 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 toxi.util.datatypes.UndirectedGraph;
+
+import java.util.*;
+
+/**
+ * A 2D Delaunay DelaunayTriangulation (DT) with incremental site insertion.
+ *
+ * This is not the fastest way to build a DT, but it's a reasonable way to build
+ * a DT incrementally and it makes a nice interactive display. There are several
+ * O(n log n) methods, but they require that the sites are all known initially.
+ *
+ * A DelaunayTriangulation is a Set of Triangles. A DelaunayTriangulation is
+ * unmodifiable as a Set; the only way to change it is to add sites (via
+ * delaunayPlace).
+ *
+ * @author Paul Chew
+ *
+ *         Created July 2005. Derived from an earlier, messier version.
+ *
+ *         Modified November 2007. Rewrote to use AbstractSet as parent class
+ *         and to use the UndirectedGraph class internally. Tried to make the DT
+ *         algorithm clearer by explicitly creating a cavity. Added code needed
+ *         to find a Voronoi cell.
+ *
+ * @author Karsten Schmidt
+ *
+ *         Ported to use toxiclibs classes (June 2010).
+ */
+public class DelaunayTriangulation extends AbstractSet<DelaunayTriangle> {
+
+    private DelaunayTriangle mostRecent = null;
+
+    private UndirectedGraph<DelaunayTriangle> triGraph;
+
+    /**
+     * All sites must fall within the initial triangle.
+     *
+     * @param triangle
+     *            the initial triangle
+     */
+    public DelaunayTriangulation(DelaunayTriangle triangle) {
+        triGraph = new UndirectedGraph<DelaunayTriangle>();
+        triGraph.add(triangle);
+        mostRecent = triangle;
+    }
+
+    /**
+     * True iff triangle is a member of this triangulation. This method isn't
+     * required by AbstractSet, but it improves efficiency.
+     *
+     * @param triangle
+     *            the object to check for membership
+     */
+    public boolean contains(Object triangle) {
+        return triGraph.getNodes().contains(triangle);
+    }
+
+    /**
+     * Place a new site into the DT. Nothing happens if the site matches an
+     * existing DT vertex.
+     *
+     * @param site
+     *            the new DelaunayVertex
+     * @throws IllegalArgumentException
+     *             if site does not lie in any triangle
+     */
+    public void delaunayPlace(DelaunayVertex site) {
+        // Uses straightforward scheme rather than best asymptotic time
+        // Locate containing triangle
+        DelaunayTriangle triangle = locate(site);
+        // Give up if no containing triangle or if site is already in DT
+        if (triangle == null) {
+            throw new IllegalArgumentException("No containing triangle");
+        }
+        if (triangle.contains(site)) {
+            return;
+        }
+        // Determine the cavity and update the triangulation
+        Set<DelaunayTriangle> cavity = getCavity(site, triangle);
+        mostRecent = update(site, cavity);
+    }
+
+    /**
+     * Determine the cavity caused by site.
+     *
+     * @param site
+     *            the site causing the cavity
+     * @param triangle
+     *            the triangle containing site
+     * @return set of all triangles that have site in their circumcircle
+     */
+    private Set<DelaunayTriangle> getCavity(DelaunayVertex site,
+            DelaunayTriangle triangle) {
+        Set<DelaunayTriangle> encroached = new HashSet<DelaunayTriangle>();
+        Queue<DelaunayTriangle> toBeChecked = new LinkedList<DelaunayTriangle>();
+        Set<DelaunayTriangle> marked = new HashSet<DelaunayTriangle>();
+        toBeChecked.add(triangle);
+        marked.add(triangle);
+        while (!toBeChecked.isEmpty()) {
+            triangle = toBeChecked.remove();
+            if (site.vsCircumcircle(triangle.toArray(new DelaunayVertex[0])) == 1) {
+                // Site outside triangle => triangle not in cavity
+                continue;
+            }
+            encroached.add(triangle);
+            // Check the neighbors
+            for (DelaunayTriangle neighbor : triGraph
+                    .getConnectedNodesFor(triangle)) {
+                if (marked.contains(neighbor)) {
+                    continue;
+                }
+                marked.add(neighbor);
+                toBeChecked.add(neighbor);
+            }
+        }
+        return encroached;
+    }
+
+    @Override
+    public Iterator<DelaunayTriangle> iterator() {
+        return triGraph.getNodes().iterator();
+    }
+
+    /**
+     * Locate the triangle with point inside it or on its boundary.
+     *
+     * @param point
+     *            the point to locate
+     * @return the triangle that holds point; null if no such triangle
+     */
+    public DelaunayTriangle locate(DelaunayVertex point) {
+        DelaunayTriangle triangle = mostRecent;
+        if (!this.contains(triangle)) {
+            triangle = null;
+        }
+
+        // Try a directed walk (this works fine in 2D, but can fail in 3D)
+        Set<DelaunayTriangle> visited = new HashSet<DelaunayTriangle>();
+        while (triangle != null) {
+            if (visited.contains(triangle)) { // This should never happen
+                System.out.println("Warning: Caught in a locate loop");
+                break;
+            }
+            visited.add(triangle);
+            // Corner opposite point
+            DelaunayVertex corner = point.isOutside(triangle
+                    .toArray(new DelaunayVertex[0]));
+            if (corner == null) {
+                return triangle;
+            }
+            triangle = this.neighborOpposite(corner, triangle);
+        }
+        // No luck; try brute force
+        System.out.println("Warning: Checking all triangles for " + point);
+        for (DelaunayTriangle tri : this) {
+            if (point.isOutside(tri.toArray(new DelaunayVertex[0])) == null) {
+                return tri;
+            }
+        }
+        // No such triangle
+        System.out.println("Warning: No triangle holds " + point);
+        return null;
+    }
+
+    /**
+     * Report neighbor opposite the given vertex of triangle.
+     *
+     * @param site
+     *            a vertex of triangle
+     * @param triangle
+     *            we want the neighbor of this triangle
+     * @return the neighbor opposite site in triangle; null if none
+     * @throws IllegalArgumentException
+     *             if site is not in this triangle
+     */
+    public DelaunayTriangle neighborOpposite(DelaunayVertex site,
+            DelaunayTriangle triangle) {
+        if (!triangle.contains(site)) {
+            throw new IllegalArgumentException("Bad vertex; not in triangle");
+        }
+        for (DelaunayTriangle neighbor : triGraph
+                .getConnectedNodesFor(triangle)) {
+            if (!neighbor.contains(site)) {
+                return neighbor;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Return the set of triangles adjacent to triangle.
+     *
+     * @param triangle
+     *            the triangle to check
+     * @return the neighbors of triangle
+     */
+    public Set<DelaunayTriangle> neighbors(DelaunayTriangle triangle) {
+        return triGraph.getConnectedNodesFor(triangle);
+    }
+
+    @Override
+    public int size() {
+        return triGraph.getNodes().size();
+    }
+
+    /**
+     * Report triangles surrounding site in order (cw or ccw).
+     *
+     * @param site
+     *            we want the surrounding triangles for this site
+     * @param triangle
+     *            a "starting" triangle that has site as a vertex
+     * @return all triangles surrounding site in order (cw or ccw)
+     * @throws IllegalArgumentException
+     *             if site is not in triangle
+     */
+    public List<DelaunayTriangle> surroundingTriangles(DelaunayVertex site,
+            DelaunayTriangle triangle) {
+        if (!triangle.contains(site)) {
+            throw new IllegalArgumentException("Site not in triangle");
+        }
+        List<DelaunayTriangle> list = new ArrayList<DelaunayTriangle>();
+        DelaunayTriangle start = triangle;
+        DelaunayVertex guide = triangle.getVertexButNot(site); // Affects cw or
+        // ccw
+        while (true) {
+            list.add(triangle);
+            DelaunayTriangle previous = triangle;
+            triangle = this.neighborOpposite(guide, triangle); // Next triangle
+            guide = previous.getVertexButNot(site, guide); // Update guide
+            if (triangle == start) {
+                break;
+            }
+        }
+        return list;
+    }
+
+    @Override
+    public String toString() {
+        return "DelaunayTriangulation with " + size() + " triangles";
+    }
+
+    /**
+     * Update the triangulation by removing the cavity triangles and then
+     * filling the cavity with new triangles.
+     *
+     * @param site
+     *            the site that created the cavity
+     * @param cavity
+     *            the triangles with site in their circumcircle
+     * @return one of the new triangles
+     */
+    private DelaunayTriangle update(DelaunayVertex site,
+            Set<DelaunayTriangle> cavity) {
+        Set<Set<DelaunayVertex>> boundary = new HashSet<Set<DelaunayVertex>>();
+        Set<DelaunayTriangle> theTriangles = new HashSet<DelaunayTriangle>();
+
+        // Find boundary facets and adjacent triangles
+        for (DelaunayTriangle triangle : cavity) {
+            theTriangles.addAll(neighbors(triangle));
+            for (DelaunayVertex vertex : triangle) {
+                Set<DelaunayVertex> facet = triangle.facetOpposite(vertex);
+                if (boundary.contains(facet)) {
+                    boundary.remove(facet);
+                } else {
+                    boundary.add(facet);
+                }
+            }
+        }
+        theTriangles.removeAll(cavity); // Adj triangles only
+
+        // Remove the cavity triangles from the triangulation
+        for (DelaunayTriangle triangle : cavity) {
+            triGraph.remove(triangle);
+        }
+
+        // Build each new triangle and add it to the triangulation
+        Set<DelaunayTriangle> newTriangles = new HashSet<DelaunayTriangle>();
+        for (Set<DelaunayVertex> vertices : boundary) {
+            vertices.add(site);
+            DelaunayTriangle tri = new DelaunayTriangle(vertices);
+            triGraph.add(tri);
+            newTriangles.add(tri);
+        }
+
+        // Update the graph links for each new triangle
+        theTriangles.addAll(newTriangles); // Adj triangle + new triangles
+        for (DelaunayTriangle triangle : newTriangles) {
+            for (DelaunayTriangle other : theTriangles) {
+                if (triangle.isNeighbor(other)) {
+                    triGraph.connect(triangle, other);
+                }
+            }
+        }
+
+        // Return one of the new triangles
+        return newTriangles.iterator().next();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/toxi/geom/mesh2d/DelaunayVertex.java b/src/main/java/toxi/geom/mesh2d/DelaunayVertex.java
new file mode 100644
index 0000000..be95cdf
--- /dev/null
+++ b/src/main/java/toxi/geom/mesh2d/DelaunayVertex.java
@@ -0,0 +1,546 @@
+package toxi.geom.mesh2d;
+
+import toxi.geom.Vec2D;
+
+/*
+ * Copyright (c) 2005, 2007 by L. Paul Chew.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, 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 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.
+ */
+
+/**
+ * Points in Euclidean space, implemented as double[].
+ * 
+ * Includes simple geometric operations. Uses matrices; a matrix is represented
+ * as an array of Pnts. Uses simplices; a simplex is represented as an array of
+ * Pnts.
+ * 
+ * @author Paul Chew Created July 2005. Derived from an earlier, messier
+ *         version. Modified Novemeber 2007. Minor clean up.
+ */
+public class DelaunayVertex {
+
+    /**
+     * Circumcenter of a simplex.
+     * 
+     * @param simplex
+     *            the simplex (as an array of Pnts)
+     * @return the circumcenter (a DelaunayVertex) of simplex
+     */
+    public static DelaunayVertex circumcenter(DelaunayVertex[] simplex) {
+        int dim = simplex[0].dimension();
+        if (simplex.length - 1 != dim) {
+            throw new IllegalArgumentException("Dimension mismatch");
+        }
+        DelaunayVertex[] matrix = new DelaunayVertex[dim];
+        for (int i = 0; i < dim; i++) {
+            matrix[i] = simplex[i].bisector(simplex[i + 1]);
+        }
+        DelaunayVertex hCenter = cross(matrix); // Center in homogeneous
+                                                // coordinates
+        double last = hCenter.coordinates[dim];
+        double[] result = new double[dim];
+        for (int i = 0; i < dim; i++) {
+            result[i] = hCenter.coordinates[i] / last;
+        }
+        return new DelaunayVertex(result);
+    }
+
+    /**
+     * Determine the signed content (i.e., area or volume, etc.) of a simplex.
+     * 
+     * @param simplex
+     *            the simplex (as an array of Pnts)
+     * @return the signed content of the simplex
+     */
+    public static double content(DelaunayVertex[] simplex) {
+        DelaunayVertex[] matrix = new DelaunayVertex[simplex.length];
+        for (int i = 0; i < matrix.length; i++) {
+            matrix[i] = simplex[i].extend(1);
+        }
+        int fact = 1;
+        for (int i = 1; i < matrix.length; i++) {
+            fact = fact * i;
+        }
+        return determinant(matrix) / fact;
+    }
+
+    /**
+     * Compute generalized cross-product of the rows of a matrix. The result is
+     * a DelaunayVertex perpendicular (as a vector) to each row of the matrix.
+     * This is not an efficient implementation, but should be adequate for low
+     * dimension.
+     * 
+     * @param matrix
+     *            the matrix of Pnts (one less row than the DelaunayVertex
+     *            dimension)
+     * @return a DelaunayVertex perpendicular to each row DelaunayVertex
+     * @throws IllegalArgumentException
+     *             if matrix is wrong shape
+     */
+    public static DelaunayVertex cross(DelaunayVertex[] matrix) {
+        int len = matrix.length + 1;
+        if (len != matrix[0].dimension()) {
+            throw new IllegalArgumentException("Dimension mismatch");
+        }
+        boolean[] columns = new boolean[len];
+        for (int i = 0; i < len; i++) {
+            columns[i] = true;
+        }
+        double[] result = new double[len];
+        int sign = 1;
+        try {
+            for (int i = 0; i < len; i++) {
+                columns[i] = false;
+                result[i] = sign * determinant(matrix, 0, columns);
+                columns[i] = true;
+                sign = -sign;
+            }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            throw new IllegalArgumentException("Matrix is wrong shape");
+        }
+        return new DelaunayVertex(result);
+    }
+
+    /**
+     * Compute the determinant of a matrix (array of Pnts). This is not an
+     * efficient implementation, but should be adequate for low dimension.
+     * 
+     * @param matrix
+     *            the matrix as an array of Pnts
+     * @return the determinnant of the input matrix
+     * @throws IllegalArgumentException
+     *             if dimensions are wrong
+     */
+    public static double determinant(DelaunayVertex[] matrix) {
+        if (matrix.length != matrix[0].dimension()) {
+            throw new IllegalArgumentException("Matrix is not square");
+        }
+        boolean[] columns = new boolean[matrix.length];
+        for (int i = 0; i < matrix.length; i++) {
+            columns[i] = true;
+        }
+        try {
+            return determinant(matrix, 0, columns);
+        } catch (ArrayIndexOutOfBoundsException e) {
+            throw new IllegalArgumentException("Matrix is wrong shape");
+        }
+    }
+
+    /**
+     * Compute the determinant of a submatrix specified by starting row and by
+     * "active" columns.
+     * 
+     * @param matrix
+     *            the matrix as an array of Pnts
+     * @param row
+     *            the starting row
+     * @param columns
+     *            a boolean array indicating the "active" columns
+     * @return the determinant of the specified submatrix
+     * @throws ArrayIndexOutOfBoundsException
+     *             if dimensions are wrong
+     */
+    private static double determinant(DelaunayVertex[] matrix, int row,
+            boolean[] columns) {
+        if (row == matrix.length) {
+            return 1;
+        }
+        double sum = 0;
+        int sign = 1;
+        for (int col = 0; col < columns.length; col++) {
+            if (!columns[col]) {
+                continue;
+            }
+            columns[col] = false;
+            sum += sign * matrix[row].coordinates[col]
+                    * determinant(matrix, row + 1, columns);
+            columns[col] = true;
+            sign = -sign;
+        }
+        return sum;
+    }
+
+    /**
+     * Create a String for a matrix.
+     * 
+     * @param matrix
+     *            the matrix (an array of Pnts)
+     * @return a String represenation of the matrix
+     */
+    public static String toString(DelaunayVertex[] matrix) {
+        StringBuilder buf = new StringBuilder("{");
+        for (DelaunayVertex row : matrix) {
+            buf.append(" " + row);
+        }
+        buf.append(" }");
+        return buf.toString();
+    }
+
+    private double[] coordinates; // The point's coordinates
+
+    /**
+     * Constructor.
+     * 
+     * @param coords
+     *            the coordinates
+     */
+    public DelaunayVertex(double... coords) {
+        // Copying is done here to ensure that DelaunayVertex's coords cannot be
+        // altered.
+        // This is necessary because the double... notation actually creates a
+        // constructor with double[] as its argument.
+        coordinates = new double[coords.length];
+        System.arraycopy(coords, 0, coordinates, 0, coords.length);
+    }
+
+    /**
+     * Add.
+     * 
+     * @param p
+     *            the other DelaunayVertex
+     * @return a new DelaunayVertex = this + p
+     */
+    public DelaunayVertex add(DelaunayVertex p) {
+        int len = dimCheck(p);
+        double[] coords = new double[len];
+        for (int i = 0; i < len; i++) {
+            coords[i] = this.coordinates[i] + p.coordinates[i];
+        }
+        return new DelaunayVertex(coords);
+    }
+
+    /**
+     * Angle (in radians) between two Pnts (treated as vectors).
+     * 
+     * @param p
+     *            the other DelaunayVertex
+     * @return the angle (in radians) between the two Pnts
+     */
+    public double angle(DelaunayVertex p) {
+        return Math.acos(this.dot(p) / (this.magnitude() * p.magnitude()));
+    }
+
+    /**
+     * Perpendicular bisector of two Pnts. Works in any dimension. The
+     * coefficients are returned as a DelaunayVertex of one higher dimension
+     * (e.g., (A,B,C,D) for an equation of the form Ax + By + Cz + D = 0).
+     * 
+     * @param point
+     *            the other point
+     * @return the coefficients of the perpendicular bisector
+     */
+    public DelaunayVertex bisector(DelaunayVertex point) {
+        dimCheck(point);
+        DelaunayVertex diff = this.subtract(point);
+        DelaunayVertex sum = this.add(point);
+        double dot = diff.dot(sum);
+        return diff.extend(-dot / 2);
+    }
+
+    /**
+     * @return the specified coordinate of this DelaunayVertex
+     * @throws ArrayIndexOutOfBoundsException
+     *             for bad coordinate
+     */
+    public double coord(int i) {
+        return this.coordinates[i];
+    }
+
+    /**
+     * Check that dimensions match.
+     * 
+     * @param p
+     *            the DelaunayVertex to check (against this DelaunayVertex)
+     * @return the dimension of the Pnts
+     * @throws IllegalArgumentException
+     *             if dimension fail to match
+     */
+    public int dimCheck(DelaunayVertex p) {
+        int len = this.coordinates.length;
+        if (len != p.coordinates.length) {
+            throw new IllegalArgumentException("Dimension mismatch");
+        }
+        return len;
+    }
+
+    /**
+     * @return this DelaunayVertex's dimension.
+     */
+    public int dimension() {
+        return coordinates.length;
+    }
+
+    /* Pnts as matrices */
+
+    /**
+     * Dot product.
+     * 
+     * @param p
+     *            the other DelaunayVertex
+     * @return dot product of this DelaunayVertex and p
+     */
+    public double dot(DelaunayVertex p) {
+        int len = dimCheck(p);
+        double sum = 0;
+        for (int i = 0; i < len; i++) {
+            sum += this.coordinates[i] * p.coordinates[i];
+        }
+        return sum;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof DelaunayVertex)) {
+            return false;
+        }
+        DelaunayVertex p = (DelaunayVertex) other;
+        if (this.coordinates.length != p.coordinates.length) {
+            return false;
+        }
+        for (int i = 0; i < this.coordinates.length; i++) {
+            if (this.coordinates[i] != p.coordinates[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Create a new DelaunayVertex by adding additional coordinates to this
+     * DelaunayVertex.
+     * 
+     * @param coords
+     *            the new coordinates (added on the right end)
+     * @return a new DelaunayVertex with the additional coordinates
+     */
+    public DelaunayVertex extend(double... coords) {
+        double[] result = new double[coordinates.length + coords.length];
+        System.arraycopy(coordinates, 0, result, 0, coordinates.length);
+        System.arraycopy(coords, 0, result, coordinates.length, coords.length);
+        return new DelaunayVertex(result);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 0;
+        for (double c : this.coordinates) {
+            long bits = Double.doubleToLongBits(c);
+            hash = (31 * hash) ^ (int) (bits ^ (bits >> 32));
+        }
+        return hash;
+    }
+
+    /* Pnts as simplices */
+
+    /**
+     * Test if this DelaunayVertex is inside a simplex.
+     * 
+     * @param simplex
+     *            the simplex (an arary of Pnts)
+     * @return true iff this DelaunayVertex is inside simplex.
+     */
+    public boolean isInside(DelaunayVertex[] simplex) {
+        int[] result = this.relation(simplex);
+        for (int r : result) {
+            if (r >= 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Test if this DelaunayVertex is on a simplex.
+     * 
+     * @param simplex
+     *            the simplex (an array of Pnts)
+     * @return the simplex DelaunayVertex that "witnesses" on-ness (or null if
+     *         not on)
+     */
+    public DelaunayVertex isOn(DelaunayVertex[] simplex) {
+        int[] result = this.relation(simplex);
+        DelaunayVertex witness = null;
+        for (int i = 0; i < result.length; i++) {
+            if (result[i] == 0) {
+                witness = simplex[i];
+            } else if (result[i] > 0) {
+                return null;
+            }
+        }
+        return witness;
+    }
+
+    /**
+     * Test if this DelaunayVertex is outside of simplex.
+     * 
+     * @param simplex
+     *            the simplex (an array of Pnts)
+     * @return simplex DelaunayVertex that "witnesses" outsideness (or null if
+     *         not outside)
+     */
+    public DelaunayVertex isOutside(DelaunayVertex[] simplex) {
+        int[] result = this.relation(simplex);
+        for (int i = 0; i < result.length; i++) {
+            if (result[i] > 0) {
+                return simplex[i];
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Magnitude (as a vector).
+     * 
+     * @return the Euclidean length of this vector
+     */
+    public double magnitude() {
+        return Math.sqrt(this.dot(this));
+    }
+
+    /**
+     * Relation between this DelaunayVertex and a simplex (represented as an
+     * array of Pnts). Result is an array of signs, one for each vertex of the
+     * simplex, indicating the relation between the vertex, the vertex's
+     * opposite facet, and this DelaunayVertex.
+     * 
+     * <pre>
+     *   -1 means DelaunayVertex is on same side of facet
+     *    0 means DelaunayVertex is on the facet
+     *   +1 means DelaunayVertex is on opposite side of facet
+     * </pre>
+     * 
+     * @param simplex
+     *            an array of Pnts representing a simplex
+     * @return an array of signs showing relation between this DelaunayVertex
+     *         and simplex
+     * @throws IllegalArgumentExcpetion
+     *             if the simplex is degenerate
+     */
+    public int[] relation(DelaunayVertex[] simplex) {
+        /*
+         * In 2D, we compute the cross of this matrix: 1 1 1 1 p0 a0 b0 c0 p1 a1
+         * b1 c1 where (a, b, c) is the simplex and p is this DelaunayVertex.
+         * The result is a vector in which the first coordinate is the signed
+         * area (all signed areas are off by the same constant factor) of the
+         * simplex and the remaining coordinates are the *negated* signed areas
+         * for the simplices in which p is substituted for each of the vertices.
+         * Analogous results occur in higher dimensions.
+         */
+        int dim = simplex.length - 1;
+        if (this.dimension() != dim) {
+            throw new IllegalArgumentException("Dimension mismatch");
+        }
+
+        /* Create and load the matrix */
+        DelaunayVertex[] matrix = new DelaunayVertex[dim + 1];
+        /* First row */
+        double[] coords = new double[dim + 2];
+        for (int j = 0; j < coords.length; j++) {
+            coords[j] = 1;
+        }
+        matrix[0] = new DelaunayVertex(coords);
+        /* Other rows */
+        for (int i = 0; i < dim; i++) {
+            coords[0] = this.coordinates[i];
+            for (int j = 0; j < simplex.length; j++) {
+                coords[j + 1] = simplex[j].coordinates[i];
+            }
+            matrix[i + 1] = new DelaunayVertex(coords);
+        }
+
+        /* Compute and analyze the vector of areas/volumes/contents */
+        DelaunayVertex vector = cross(matrix);
+        double content = vector.coordinates[0];
+        int[] result = new int[dim + 1];
+        for (int i = 0; i < result.length; i++) {
+            double value = vector.coordinates[i + 1];
+            if (Math.abs(value) <= 1.0e-6 * Math.abs(content)) {
+                result[i] = 0;
+            } else if (value < 0) {
+                result[i] = -1;
+            } else {
+                result[i] = 1;
+            }
+        }
+        if (content < 0) {
+            for (int i = 0; i < result.length; i++) {
+                result[i] = -result[i];
+            }
+        }
+        if (content == 0) {
+            for (int i = 0; i < result.length; i++) {
+                result[i] = Math.abs(result[i]);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Subtract.
+     * 
+     * @param p
+     *            the other DelaunayVertex
+     * @return a new DelaunayVertex = this - p
+     */
+    public DelaunayVertex subtract(DelaunayVertex p) {
+        int len = dimCheck(p);
+        double[] coords = new double[len];
+        for (int i = 0; i < len; i++) {
+            coords[i] = this.coordinates[i] - p.coordinates[i];
+        }
+        return new DelaunayVertex(coords);
+    }
+
+    @Override
+    public String toString() {
+        if (coordinates.length == 0) {
+            return "DelaunayVertex()";
+        }
+        String result = "DelaunayVertex(" + coordinates[0];
+        for (int i = 1; i < coordinates.length; i++) {
+            result = result + "," + coordinates[i];
+        }
+        result = result + ")";
+        return result;
+    }
+
+    public Vec2D toVec2D() {
+        return new Vec2D((float) coordinates[0], (float) coordinates[1]);
+    }
+
+    /**
+     * Test relation between this DelaunayVertex and circumcircle of a simplex.
+     * 
+     * @param simplex
+     *            the simplex (as an array of Pnts)
+     * @return -1, 0, or +1 for inside, on, or outside of circumcircle
+     */
+    public int vsCircumcircle(DelaunayVertex[] simplex) {
+        DelaunayVertex[] matrix = new DelaunayVertex[simplex.length + 1];
+        for (int i = 0; i < simplex.length; i++) {
+            matrix[i] = simplex[i].extend(1, simplex[i].dot(simplex[i]));
+        }
+        matrix[simplex.length] = this.extend(1, this.dot(this));
+        double d = determinant(matrix);
+        int result = (d < 0) ? -1 : ((d > 0) ? +1 : 0);
+        if (content(simplex) < 0) {
+            result = -result;
+        }
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/toxi/geom/mesh2d/Voronoi.java b/src/main/java/toxi/geom/mesh2d/Voronoi.java
new file mode 100644
index 0000000..6f5f641
--- /dev/null
+++ b/src/main/java/toxi/geom/mesh2d/Voronoi.java
@@ -0,0 +1,106 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.geom.mesh2d;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import toxi.geom.Polygon2D;
+import toxi.geom.Triangle2D;
+import toxi.geom.Vec2D;
+
+public class Voronoi {
+
+    public static float DEFAULT_SIZE = 10000;
+
+    protected DelaunayTriangulation delaunay;
+    protected DelaunayTriangle initialTriangle;
+    protected List<Vec2D> sites = new ArrayList<Vec2D>();
+
+    public Voronoi() {
+        this(DEFAULT_SIZE);
+    }
+
+    public Voronoi(float size) {
+        initialTriangle = new DelaunayTriangle(
+                new DelaunayVertex(-size, -size), new DelaunayVertex(size,
+                        -size), new DelaunayVertex(0, size));
+        this.delaunay = new DelaunayTriangulation(initialTriangle);
+    }
+
+    public void addPoint(Vec2D p) {
+        sites.add(p.copy());
+        delaunay.delaunayPlace(new DelaunayVertex(p.x, p.y));
+    }
+
+    public void addPoints(Collection<? extends Vec2D> points) {
+        for (Vec2D p : points) {
+            addPoint(p);
+        }
+    }
+
+    public List<Polygon2D> getRegions() {
+        List<Polygon2D> regions = new LinkedList<Polygon2D>();
+        HashSet<DelaunayVertex> done = new HashSet<DelaunayVertex>(
+                initialTriangle);
+        for (DelaunayTriangle triangle : delaunay) {
+            for (DelaunayVertex site : triangle) {
+                if (done.contains(site)) {
+                    continue;
+                }
+                done.add(site);
+                List<DelaunayTriangle> list = delaunay.surroundingTriangles(
+                        site, triangle);
+                Polygon2D poly = new Polygon2D();
+                for (DelaunayTriangle tri : list) {
+                    DelaunayVertex circumeter = tri.getCircumcenter();
+                    poly.add(new Vec2D((float) circumeter.coord(0),
+                            (float) circumeter.coord(1)));
+                }
+                regions.add(poly);
+            }
+        }
+        return regions;
+    }
+
+    public List<Vec2D> getSites() {
+        return sites;
+    }
+
+    public List<Triangle2D> getTriangles() {
+        List<Triangle2D> tris = new ArrayList<Triangle2D>();
+        for (DelaunayTriangle t : delaunay) {
+            tris.add(new Triangle2D(t.get(0).toVec2D(), t.get(1).toVec2D(), t
+                    .get(2).toVec2D()));
+        }
+        return tris;
+    }
+}
diff --git a/src/main/java/toxi/geom/nurbs/BasicNurbsCurve.java b/src/main/java/toxi/geom/nurbs/BasicNurbsCurve.java
new file mode 100644
index 0000000..1884e12
--- /dev/null
+++ b/src/main/java/toxi/geom/nurbs/BasicNurbsCurve.java
@@ -0,0 +1,179 @@
+/*
+ * jgeom: Geometry Library fo Java
+ * 
+ * Copyright (C) 2005  Samuel Gerber
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package toxi.geom.nurbs;
+
+import toxi.geom.Polygon2D;
+import toxi.geom.Vec3D;
+import toxi.geom.Vec4D;
+import toxi.geom.XYZ;
+
+/**
+ * A Basic implementation of a NurbsCurve.
+ * 
+ * @author sg
+ * @version 1.0
+ */
+public class BasicNurbsCurve implements NurbsCurve, Cloneable {
+
+    private Vec4D[] cpoly;
+    private KnotVector uKnots;
+
+    /**
+     * Create a Nurbs Curve from the given Controlpoints, Knots and degree.<br>
+     * [TODO Validate Input, part of it is done by creating the KnotVector]
+     * 
+     * @param cps
+     *            Array of Controlpoints
+     * @param uK
+     *            Knot values
+     * @param degree
+     *            Degree of the Nurbs Curve
+     */
+    public BasicNurbsCurve(Vec4D[] cps, float[] uK, int degree) {
+        this(cps, new KnotVector(uK, degree));
+    }
+
+    /**
+     * Generate a Nurbs from the given Controlpoints and the given Knotvector.<br>
+     * [TODO validate input]
+     * 
+     * @param cps
+     *            Array of Controlpoints
+     * @param uKnots
+     *            Knotvector of the Nurbs
+     */
+    public BasicNurbsCurve(Vec4D[] cps, KnotVector uKnots) {
+        cpoly = cps;
+        this.uKnots = uKnots;
+        if (uKnots.length() != uKnots.getDegree() + cpoly.length + 1) {
+            throw new IllegalArgumentException(
+                    "Nurbs Curve has wrong knot number");
+        }
+    }
+
+    public Vec4D[][] curveDerivCpts(int d, int r1, int r2) {
+
+        Vec4D[][] result = new Vec4D[d + 1][r2 - r1 + 1];
+
+        int degree = uKnots.getDegree();
+
+        // k=0 => control points
+        int r = r2 - r1;
+        for (int i = 0; i <= r; i++) {
+            result[0][i] = cpoly[i];
+        }
+
+        // k=1 => 1st derivative, k=2 => 2nd derivative, etc...
+        for (int k = 1; k <= d; k++) {
+            int tmp = degree - k + 1;
+            for (int i = 0; i <= (r - k); i++) {
+                Vec4D cw = new Vec4D(result[k - 1][i + 1]);
+                cw.subSelf(result[k - 1][i]);
+                cw.scaleSelf(tmp);
+                cw.scaleSelf(1 / (uKnots.get(r1 + i + degree + 1) - uKnots
+                        .get(r1 + i + k)));
+                result[k][i] = cw;
+            }
+        }
+        return result;
+    }
+
+    public XYZ[] derivativesOnCurve(float u, int grade) {
+        return derivativesOnCurve(u, grade, new XYZ[grade + 1]);
+    }
+
+    public XYZ[] derivativesOnCurve(float u, int grade, XYZ[] derivs) {
+
+        int span = uKnots.findSpan(u);
+        int degree = uKnots.getDegree();
+
+        // TODO: compute derivatives also for NURBS
+        // currently supports only non-rational B-Splines
+        float derivVals[][] = uKnots.derivBasisFunctions(span, u, grade);
+
+        // Zero values
+        for (int k = (degree + 1); k <= grade; k++) {
+            derivs[k] = new Vec3D();
+        }
+
+        for (int k = 0; k <= grade; k++) {
+            Vec3D d = new Vec3D();
+            for (int j = 0; j <= degree; j++) {
+                Vec4D v = cpoly[(span - degree) + j];
+                float s = derivVals[k][j];
+                d.addSelf(v.x * s, v.y * s, v.z * s);
+            }
+            derivs[k] = d;
+        }
+        return derivs;
+    }
+
+    public Vec4D[] getControlPoints() {
+        return cpoly;
+    }
+
+    public int getDegree() {
+        return uKnots.getDegree();
+    }
+
+    public float[] getKnots() {
+        return uKnots.getArray();
+    }
+
+    public KnotVector getKnotVector() {
+        return uKnots;
+    }
+
+    public Vec3D pointOnCurve(float u) {
+        return pointOnCurve(u, new Vec3D());
+    }
+
+    public Vec3D pointOnCurve(float u, Vec3D out) {
+        int span = uKnots.findSpan(u);
+        int degree = uKnots.getDegree();
+
+        // for periodic knot vectors the usable parameter range is
+        // span >= degree and span <= no control points (n+1)
+        if (span < degree) {
+            return out;
+        }
+
+        if (span > uKnots.getN()) {
+            return out;
+        }
+
+        double[] bf = uKnots.basisFunctions(span, u);
+        Vec4D cw = new Vec4D();
+        for (int i = 0; i <= degree; i++) {
+            cw.addSelf(cpoly[(span - degree) + i].getWeighted().scaleSelf(
+                    (float) bf[i]));
+        }
+        return cw.unweightInto(out);
+    }
+
+    public Polygon2D toPolygon2D(int res) {
+        float delta = 1f / (res - 1);
+        Polygon2D poly = new Polygon2D();
+        for (int i = 0; i < res; i++) {
+            poly.add(pointOnCurve(i * delta).to2DXY());
+        }
+        return poly;
+    }
+}
diff --git a/src/main/java/toxi/geom/nurbs/BasicNurbsSurface.java b/src/main/java/toxi/geom/nurbs/BasicNurbsSurface.java
new file mode 100644
index 0000000..cb6ec24
--- /dev/null
+++ b/src/main/java/toxi/geom/nurbs/BasicNurbsSurface.java
@@ -0,0 +1,211 @@
+/*
+ * jgeom: Geometry Library fo Java
+ * 
+ * Copyright (C) 2005  Samuel Gerber
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package toxi.geom.nurbs;
+
+import toxi.geom.Vec3D;
+import toxi.geom.Vec4D;
+import toxi.geom.XYZ;
+
+/**
+ * A Basic NurbsSurface implementation.
+ * 
+ * @author sg
+ * @version 1.2
+ */
+public class BasicNurbsSurface implements NurbsSurface {
+
+    private KnotVector uKnots;
+    private KnotVector vKnots;
+    private ControlNet cpnet;
+
+    // private List<TrimCurve> trimms = new LinkedList<TrimCurve>();
+
+    /**
+     * Create a Nurbs Surface from the given {@link ControlNet} and the knot
+     * values of degree p in u direction and of degree q in v direction.
+     * 
+     * @param cps
+     *            ControlNet of the Nurbs
+     * @param uK
+     *            Knot values in u direction
+     * @param vK
+     *            Knot values in v direction
+     * @param p
+     *            degree in u direction
+     * @param q
+     *            degree in v direction
+     */
+    public BasicNurbsSurface(ControlNet cps, float[] uK, float[] vK, int p,
+            int q) throws IllegalArgumentException {
+        this(cps, new KnotVector(uK, p), new KnotVector(vK, q));
+    }
+
+    /**
+     * Create a Nurbs form the Controlnet and the two Knot vectors.
+     * 
+     * @param net
+     *            Contorl net of Nurbs
+     * @param u
+     *            KnotVector in u direction
+     * @param v
+     *            KnotVector in v direction
+     */
+    public BasicNurbsSurface(ControlNet net, KnotVector u, KnotVector v)
+            throws IllegalArgumentException {
+        cpnet = net;
+        uKnots = u;
+        vKnots = v;
+        validate();
+    }
+
+    // public void addTrimCurve(TrimCurve tc) {
+    // trimms.add(tc);
+    // }
+
+    public ControlNet getControlNet() {
+        return cpnet;
+    }
+
+    // public List<TrimCurve> getTrimCurves() {
+    // return trimms;
+    // }
+
+    public int getUDegree() {
+        return uKnots.getDegree();
+    }
+
+    public float[] getUKnots() {
+        return uKnots.getArray();
+    }
+
+    public KnotVector getUKnotVector() {
+        return uKnots;
+    }
+
+    public int getVDegree() {
+        return vKnots.getDegree();
+    }
+
+    public float[] getVKnots() {
+        return vKnots.getArray();
+    }
+
+    public KnotVector getVKnotVector() {
+        return vKnots;
+    }
+
+    public Vec3D pointOnSurface(double u, double v) {
+        return pointOnSurface((float) u, (float) v, new Vec3D());
+    }
+
+    public XYZ pointOnSurface(float u, float v) {
+        return pointOnSurface(u, v, new Vec3D());
+    }
+
+    public Vec3D pointOnSurface(float u, float v, Vec3D out) {
+
+        // Piegl -> Algorithm A4.3 -> page 134
+
+        int uspan = uKnots.findSpan(u);
+        double[] bfu = uKnots.basisFunctions(uspan, u);
+
+        int vspan = vKnots.findSpan(v);
+        double[] bfv = vKnots.basisFunctions(vspan, v);
+
+        int p = uKnots.getDegree();
+        int q = vKnots.getDegree();
+        Vec4D[] tmp = new Vec4D[q + 1];
+        for (int l = 0; l <= q; l++) {
+            Vec4D pw = new Vec4D();
+            for (int k = 0; k <= p; k++) {
+                pw.addSelf(cpnet.get(uspan - p + k, vspan - q + l)
+                        .getWeighted().scaleSelf((float) bfu[k]));
+            }
+            tmp[l] = pw;
+        }
+        Vec4D sw = new Vec4D();
+        for (int l = 0; l <= q; l++) {
+            sw.addSelf(tmp[l].scaleSelf((float) bfv[l]));
+        }
+        return sw.unweightInto(out);
+    }
+
+    public Vec4D[][][][] surfaceDerivCpts(int d, int r1, int r2, int s1, int s2) {
+
+        Vec4D[][][][] result = new Vec4D[d + 1][d + 1][r2 - r1 + 1][s2 - s1 + 1];
+        int degreeU = uKnots.getDegree();
+        int degreeV = vKnots.getDegree();
+
+        int du = d < degreeU ? d : degreeU;
+        int dv = d < degreeV ? d : degreeV;
+        int r = r2 - r1;
+        int s = s2 - s1;
+        Vec4D[][] cps = cpnet.getControlPoints();
+        Vec4D[] ucps = new Vec4D[cpnet.uLength()];
+        for (int j = s1; j <= s2; j++) {
+            for (int idxu = 0; idxu < cpnet.uLength(); idxu++) {
+                ucps[idxu] = cps[idxu][j];
+            }
+            Vec4D[][] tmp = new BasicNurbsCurve(ucps, uKnots).curveDerivCpts(
+                    du, r1, r2);
+            for (int k = 0; k <= du; k++) {
+                final Vec4D[][] resk0 = result[k][0];
+                for (int i = 0; i <= (r - k); i++) {
+                    resk0[i][j - s1] = tmp[k][i];
+                }
+            }
+        }
+
+        for (int k = 0; k <= du; k++) {
+            final Vec4D[][] resk0 = result[k][0];
+            for (int i = 0; i <= (r - k); i++) {
+                final int length = resk0[i].length;
+                final Vec4D[] resk0i = resk0[i];
+                Vec4D[] vcps = new Vec4D[length];
+                for (int idx = 0; idx < length; idx++) {
+                    vcps[idx] = resk0i[idx];
+                }
+                final int dd = (d - k) < dv ? (d - k) : dv;
+                Vec4D[][] tmp = new BasicNurbsCurve(vcps, vKnots)
+                        .curveDerivCpts(dd, 0, s);
+                for (int l = 1; l <= dd; l++) {
+                    final Vec4D[] reskli = result[k][l][i];
+                    final Vec4D[] tmpL = tmp[l];
+                    for (int j = 0; j <= (s - 1); j++) {
+                        reskli[j] = tmpL[j];
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+    private void validate() {
+        if (uKnots.length() != uKnots.getDegree() + cpnet.uLength() + 1) {
+            throw new IllegalArgumentException(
+                    "Nurbs Surface has wrong Knot number in u Direction");
+        }
+        if (vKnots.length() != vKnots.getDegree() + cpnet.vLength() + 1) {
+            throw new IllegalArgumentException(
+                    "Nurbs Surface has wrong Knot number in v Direction");
+        }
+
+    }
+}
diff --git a/src/main/java/toxi/geom/nurbs/ControlNet.java b/src/main/java/toxi/geom/nurbs/ControlNet.java
new file mode 100644
index 0000000..8141781
--- /dev/null
+++ b/src/main/java/toxi/geom/nurbs/ControlNet.java
@@ -0,0 +1,140 @@
+/*
+ * jgeom: Geometry Library fo Java
+ * 
+ * Copyright (C) 2005  Samuel Gerber
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package toxi.geom.nurbs;
+
+import toxi.geom.roVec3D;
+import toxi.geom.Vec3D;
+import toxi.geom.Vec4D;
+
+/**
+ * A ControlNet for a NurbsSurface
+ * 
+ * @author sg
+ * @version 1.0
+ */
+public class ControlNet {
+
+    private int nU, nV;
+    private Vec4D[][] cps;
+
+    /**
+     * Create a ControlNet from the given points the two dimensional array must
+     * be a Matrix else an IllegalArgumentException is thrown.
+     * 
+     * @param cpnet
+     *            "Matrix" of ControlPoints
+     */
+    public ControlNet(Vec4D[][] cpnet) throws IllegalArgumentException {
+        if (cpnet.length < 1) {
+            throw new IllegalArgumentException(
+                    "Nurbs is not a Surface, to few ControlPoints in u Direction");
+        }
+        if (cpnet[0].length < 1) {
+            throw new IllegalArgumentException(
+                    "Nurbs is not a Surface, to few ControlPoints in v Direction");
+        }
+        for (int i = 1; i < cpnet.length; i++) {
+            if (cpnet[i].length != cpnet[i - 1].length) {
+                throw new IllegalArgumentException(
+                        "ControlPoint net is not a Matrix");
+            }
+        }
+
+        nU = cpnet.length;
+        nV = cpnet[0].length;
+        cps = cpnet;
+    }
+
+    public void center(roVec3D origin) {
+        Vec3D centroid = computeCentroid();
+        Vec3D delta = origin != null ? origin.sub(centroid) : centroid
+                .getInverted();
+        for (int i = 0; i < nU; i++) {
+            for (int j = 0; j < nV; j++) {
+                cps[i][j].addXYZSelf(delta);
+            }
+        }
+    }
+
+    public Vec3D computeCentroid() {
+        Vec4D centroid = new Vec4D();
+        for (int i = 0; i < nU; i++) {
+            for (int j = 0; j < nV; j++) {
+                centroid.addSelf(cps[i][j]);
+            }
+        }
+        return centroid.scaleSelf(1f / (nU * nV)).to3D();
+    }
+
+    /**
+     * Get the ControlPoint at the position u,v
+     * 
+     * @param u
+     *            index in u direction
+     * @param v
+     *            index in v direction
+     * @return The by u and v indexed ControlPoint
+     */
+    public Vec4D get(int u, int v) {
+        return cps[u][v];
+    }
+
+    /**
+     * Get all the control points
+     * 
+     * @return 2D array
+     */
+    public Vec4D[][] getControlPoints() {
+        return cps;
+    }
+
+    /**
+     * Set the ControlPoint at the position u,v
+     * 
+     * @param u
+     *            index in u direction
+     * @param v
+     *            index in v direction
+     * @param cp
+     *            ControlPoint to set at the indexed position
+     */
+    public void set(int u, int v, Vec4D cp) {
+        cps[u][v].set(cp);
+    }
+
+    /**
+     * Get number of ControlPoints in u direction
+     * 
+     * @return number of ControlPoints in u direction
+     */
+    public int uLength() {
+        return nU;
+    }
+
+    /**
+     * Get number of ControlPoints in v direction
+     * 
+     * @return number of ControlPoints in v direction
+     */
+    public int vLength() {
+        return nV;
+    }
+
+}
diff --git a/src/main/java/toxi/geom/nurbs/CurveCreator.java b/src/main/java/toxi/geom/nurbs/CurveCreator.java
new file mode 100644
index 0000000..95f9b85
--- /dev/null
+++ b/src/main/java/toxi/geom/nurbs/CurveCreator.java
@@ -0,0 +1,112 @@
+/*
+ * jgeom: Geometry Library fo Java
+ * 
+ * Copyright (C) 2005  Samuel Gerber
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package toxi.geom.nurbs;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import toxi.geom.Vec3D;
+import toxi.geom.Vec4D;
+
+/**
+ * Convenience class to create dynamically NurbsCurves.
+ * 
+ * @author sg
+ * @version 1.0
+ */
+public class CurveCreator {
+
+    private List<Vec4D> cps = new LinkedList<Vec4D>();
+    private NurbsCurve curve = null;
+
+    private int degree;
+    private int incp = 0;
+
+    /**
+     * Create a new CurveCretor object which can create Curves from the given
+     * degree
+     * 
+     * @param degree
+     *            Degree the created NurbsCurve will have.
+     */
+    public CurveCreator(int degree) {
+        this.degree = degree;
+    }
+
+    /**
+     * Add a normal Point as ControlPoint to the CurveCreator. The newly added
+     * Point has automatically the weight one.
+     * 
+     * @param cp
+     *            Controlpoint to add with weight one.
+     * @return the actual NurbsCurve if any exists, or null otherwise (to less
+     *         control for the given degree).
+     */
+    public NurbsCurve addControlPoint(Vec3D cp) {
+        return addControlPoint(new Vec4D(cp, 1));
+    }
+
+    /**
+     * Add a new Controlpoint to the current CurveCreator.
+     * 
+     * @param cp
+     *            ControlPoint to add.
+     * @return the actual NurbsCurve if any exists, or null otherwise (to less
+     *         control for the given degree).
+     */
+    public NurbsCurve addControlPoint(Vec4D cp) {
+        cps.add(cp);
+        int np = cps.size();
+        int tmp = degree;
+        if (np <= degree) {
+            if (incp == 0) {
+                incp++;
+                return null;
+            }
+            tmp = incp++;
+        }
+
+        float[] u = new float[np + tmp + 1];
+        for (int i = 0; i <= tmp; i++) {
+            u[u.length - 1 - i] = 1;
+        }
+        if (np > degree + 1) {
+            float val = 1.0f / (np - degree);
+            float step = val;
+            for (int i = degree + 1; i < u.length - 1 - degree; i++) {
+                u[i] = val;
+                val += step;
+            }
+        }
+        curve = new BasicNurbsCurve(cps.toArray(new Vec4D[cps.size()]), u, tmp);
+        return curve;
+    }
+
+    /**
+     * Get the curve NurbsCurve of the CurveCreator
+     * 
+     * @return the actual NurbsCurve if any exists, or null otherwise (to less
+     *         control for the given degree).
+     */
+    public NurbsCurve getCurve() {
+        return curve;
+    }
+
+}
diff --git a/src/main/java/toxi/geom/nurbs/CurveUtils.java b/src/main/java/toxi/geom/nurbs/CurveUtils.java
new file mode 100644
index 0000000..98e32f0
--- /dev/null
+++ b/src/main/java/toxi/geom/nurbs/CurveUtils.java
@@ -0,0 +1,240 @@
+package toxi.geom.nurbs;
+
+import java.util.LinkedList;
+
+import toxi.geom.Vec4D;
+import toxi.math.MathUtils;
+
+/**
+ * @author sg
+ */
+public final class CurveUtils {
+
+    private static int[] binomials(int x, int y) {
+        int[] bin = new int[y + 1];
+        bin[0] = 1;
+        for (int i = 1; i <= y; i++) {
+            bin[i] = (bin[i - 1] * x) / i;
+            x--;
+        }
+        return bin;
+    };
+
+    public static NurbsCurve connectCurves(NurbsCurve curve1, NurbsCurve curve2) {
+        return connectCurves(new NurbsCurve[] {
+                curve1, curve2
+        });
+    }
+
+    public static NurbsCurve connectCurves(NurbsCurve[] curves) {
+        if (curves.length < 2) {
+            throw new IllegalArgumentException("Must be at least 2 curves");
+        }
+        int degree = curves[0].getDegree();
+        LinkedList<Float> knots = new LinkedList<Float>();
+        for (int i = 0; i <= degree; i++) {
+            knots.add(0f);
+        }
+        LinkedList<Vec4D> cps = new LinkedList<Vec4D>();
+        cps.add(curves[0].getControlPoints()[0]);
+        for (int i = 0; i < curves.length; i++) {
+            float[] u = curves[i].getKnots();
+            if (degree != curves[i].getDegree()) {
+                throw new IllegalArgumentException(
+                        "Curves must have equal degrees");
+            }
+            float start = knots.getLast() - u[0];
+            for (int j = degree + 1; j < u.length - degree - 1; j++) {
+                knots.addLast(start + u[j]);
+            }
+            final float lastU = start + u[u.length - 1];
+            for (int j = 0; j < degree; j++) {
+                knots.addLast(lastU);
+            }
+
+            Vec4D[] pts = curves[i].getControlPoints();
+            for (int j = 1; j < pts.length; j++) {
+                cps.addLast(pts[j]);
+            }
+            // TODO check start and end point equality
+        }
+        knots.addLast(knots.getLast());
+
+        float[] u = new float[knots.size()];
+        for (int i = 0; i < u.length; i++) {
+            u[i] = knots.get(i);
+        }
+        return new BasicNurbsCurve(cps.toArray(new Vec4D[cps.size()]), u,
+                degree);
+    }
+
+    public static NurbsCurve increaseDegree(NurbsCurve curve, int t) {
+        if (t <= 0) {
+            throw new IllegalArgumentException(
+                    "New degree smaller or equal degree of curve");
+        }
+        Vec4D[] cp = curve.getControlPoints();
+        float[] u = curve.getKnots();
+        int p = curve.getDegree();
+        int seg = curve.getKnotVector().getNumberOfSegments() + 1;
+        Vec4D[] cph = new Vec4D[cp.length + t * seg];
+        float[] uh = new float[u.length + t * seg];
+
+        float[][] bezalfs = new float[p + t + 1][p + 1];
+        Vec4D[] bpts = new Vec4D[p + 1];
+        Vec4D[] ebpts = new Vec4D[p + t + 1];
+        Vec4D[] nextbpts = new Vec4D[p - 1];
+        float[] alfs = new float[p - 1];
+
+        double m = u.length - 1;
+        int ph = p + t;
+        int ph2 = ph / 2;
+        bezalfs[0][0] = bezalfs[p + t][p] = 1;
+        int[] binph = binomials(ph, ph2);
+        int[] binp = binomials(p, p);
+        int[] bint = binomials(t, t);
+        for (int i = 1; i <= ph2; i++) {
+            float inv = 1f / binph[i];
+            int mpi = MathUtils.min(p, i);
+            for (int j = MathUtils.max(0, i - t); j <= mpi; j++) {
+                bezalfs[i][j] = inv * binp[j] * bint[i - j];
+            }
+        }
+        for (int i = ph2 + 1; i < ph; i++) {
+            double mpi = MathUtils.min(p, i);
+            for (int j = MathUtils.max(0, i - t); j <= mpi; j++) {
+                bezalfs[i][j] = bezalfs[ph - i][p - j];
+            }
+        }
+        int mh = ph;
+        int kind = ph + 1;
+        int r = -1;
+        int a = p;
+        int b = p + 1;
+        int cind = 1;
+        float ua = u[0];
+        cph[0] = new Vec4D(cp[0]);
+        for (int i = 0; i <= ph; i++) {
+            uh[i] = ua;
+        }
+        for (int i = 0; i <= p; i++) {
+            bpts[i] = new Vec4D(cp[i]);
+        }
+
+        while (b < m) {
+            int i = b;
+            while (b < m && u[b] == u[b + 1]) {
+                b++;
+            }
+            int mul = b - i + 1;
+            mh += mul + t;
+            float ub = u[b];
+            int oldr = r;
+            r = p - mul;
+            int lbz = 1;
+            if (oldr > 0) {
+                lbz = (oldr + 2) / 2;
+            }
+            int rbz = ph;
+            if (r > 0) {
+                rbz = ph - (r + 1) / 2;
+                float numer = ub - ua;
+                for (int k = p; k > mul; k--) {
+                    alfs[k - mul - 1] = numer / (u[a + k] - ua);
+                }
+                for (int j = 1; j <= r; j++) {
+                    int save = r - j;
+                    int s = mul + j;
+                    for (int k = p; k >= s; k--) {
+                        bpts[k].interpolateToSelf(bpts[k - 1], 1 - alfs[k - s]);
+                    }
+                    nextbpts[save] = bpts[p];
+                }
+            }
+            for (i = lbz; i <= ph; i++) {
+                ebpts[i] = new Vec4D();
+                int mpi = MathUtils.min(p, i);
+                for (int j = MathUtils.max(0, i - t); j <= mpi; j++) {
+                    ebpts[i].addScaledSelf(bpts[j], bezalfs[i][j]);
+                }
+            }
+            if (oldr > 1) {
+                int first = kind - 2;
+                int last = kind;
+                float den = ua - ub;
+                for (int tr = 1; tr < oldr; tr++) {
+                    i = first;
+                    int j = last;
+                    int kj = j - kind + 1;
+                    while (j - i > tr) {
+                        if (i < cind) {
+                            float alf = (ub - uh[i]) / (ua - uh[i]);
+                            cph[i].interpolateToSelf(cph[i - 1], 1 - alf);
+                        }
+                        if (j >= lbz) {
+                            if (j - tr <= kind - ph + oldr) {
+                                float gam = (ub - uh[j - tr]) / den;
+                                ebpts[kj].interpolateToSelf(ebpts[kj + 1],
+                                        1 - gam);
+                            } else {
+                                float bet = (ub - uh[kind - 1]) / den;
+                                ebpts[kj].interpolateToSelf(ebpts[kj + 1],
+                                        1 - bet);
+                            }
+                        }
+                        i++;
+                        j--;
+                        kj--;
+                    }
+                    first--;
+                    last++;
+                }
+            }
+            if (a != p) {
+                for (i = 0; i < ph - oldr; i++) {
+                    uh[kind] = ua;
+                    kind++;
+                }
+            }
+            for (int j = lbz; j <= rbz; j++) {
+                cph[cind] = new Vec4D(ebpts[j]);
+                cind++;
+            }
+            if (b < m) {
+                for (int j = 0; j < r; j++) {
+                    bpts[j].set(nextbpts[j]);
+                }
+                for (int j = r; j <= p; j++) {
+                    bpts[j].set(cp[b - p + j]);
+                }
+                a = b;
+                b++;
+                ua = ub;
+            } else {
+                for (i = 0; i <= ph; i++) {
+                    uh[kind + i] = ub;
+                }
+            }
+        }
+        int nh = mh - ph - 1;
+        float[] uNew = new float[mh + 1];
+        for (int i = 0; i < uNew.length; i++) {
+            uNew[i] = uh[i];
+        }
+        Vec4D[] cpNew = new Vec4D[nh + 1];
+        for (int i = 0; i < cpNew.length; i++) {
+            cpNew[i] = new Vec4D(cph[i]);
+        }
+        return new BasicNurbsCurve(cpNew, uNew, p + t);
+    }
+
+    private CurveUtils() {
+    }
+
+    public NurbsCurve equalizeConnectCurves(NurbsCurve[] curves) {
+        // TODO equalize degrees and movw curves so that end of curves[i-1] is
+        // the same as start of curves[i];
+        return connectCurves(curves);
+    }
+
+}
diff --git a/src/main/java/toxi/geom/nurbs/InterpolationException.java b/src/main/java/toxi/geom/nurbs/InterpolationException.java
new file mode 100644
index 0000000..12bc60d
--- /dev/null
+++ b/src/main/java/toxi/geom/nurbs/InterpolationException.java
@@ -0,0 +1,49 @@
+/*
+ * jgeom: Geometry Library fo Java
+ * 
+ * Copyright (C) 2005  Samuel Gerber
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package toxi.geom.nurbs;
+
+/**
+ * An InterpolationException is thrown if Nurbs could not be interpolated from
+ * the given points.
+ * 
+ * @author sg
+ * @version 1.0
+ */
+public class InterpolationException extends Exception {
+
+    private static final long serialVersionUID = 1L;
+
+    public InterpolationException() {
+        super();
+    }
+
+    public InterpolationException(String arg0) {
+        super(arg0);
+    }
+
+    public InterpolationException(String arg0, Throwable arg1) {
+        super(arg0, arg1);
+    }
+
+    public InterpolationException(Throwable arg0) {
+        super(arg0);
+    }
+
+}
diff --git a/src/main/java/toxi/geom/nurbs/KnotVector.java b/src/main/java/toxi/geom/nurbs/KnotVector.java
new file mode 100644
index 0000000..de47d62
--- /dev/null
+++ b/src/main/java/toxi/geom/nurbs/KnotVector.java
@@ -0,0 +1,321 @@
+/*
+ * jgeom: Geometry Library fo Java
+ * 
+ * Copyright (C) 2005  Samuel Gerber
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package toxi.geom.nurbs;
+
+/**
+ * KnotVector, assembles the knots values of a NURBS and its degree.
+ * 
+ * @author sg
+ * @version 1.0
+ */
+public class KnotVector {
+
+    private boolean isOpen;
+    private float knots[];
+    private int degree;
+    private int n;
+
+    /**
+     * Create a Knotvector from the given knot values of the desired degree.
+     * 
+     * @param knots
+     *            knot values
+     * @param degree
+     *            degree of Nurbs
+     */
+    public KnotVector(float knots[], int degree)
+            throws IllegalArgumentException {
+        this.knots = knots;
+        this.degree = degree;
+        n = knots.length - degree - 2;
+        for (int i = 1; i < knots.length; i++) {
+            if (knots[i - 1] > knots[i]) {
+                throw new IllegalArgumentException("Knots not valid knot["
+                        + (i - 1) + "] > knot[" + i + "]: knot[" + (i - 1)
+                        + "]=" + knots[i - 1] + " > knot[" + i + "]="
+                        + knots[i]);
+            }
+        }
+
+        int m = knots.length - 1;
+
+        // Check if it is an open knot vector
+        isOpen = true;
+        for (int k = 0; k < degree && isOpen; k++) {
+            if (knots[k] != knots[k + 1]) {
+                isOpen = false;
+            }
+        }
+        for (int k = m; k > m - degree && isOpen; k--) {
+            if (knots[k] != knots[k - 1]) {
+                isOpen = false;
+            }
+        }
+
+    }
+
+    /**
+     * Gets the basis function values for the given u value. This function
+     * calculates firstly the span which is needed in order to calculate the
+     * basis functions values.
+     * 
+     * @param u
+     *            Value to calculate basis functions for.
+     * @return basis function values
+     */
+    public double[] basisFunctions(float u) {
+        return basisFunctions(findSpan(u), u);
+    }
+
+    /**
+     * Calculates the basis function values for the given u value, when it's
+     * already known in which span u lies.
+     * 
+     * @param span
+     *            The span u lies in
+     * @param u
+     *            Value to calculate basis functions for.
+     * @return basis function values
+     */
+    public double[] basisFunctions(int span, float u) {
+        final int d1 = degree + 1;
+        double res[] = new double[d1];
+        double left[] = new double[d1];
+        double right[] = new double[d1];
+        res[0] = 1;
+        for (int j = 1; j < d1; j++) {
+            left[j] = u - knots[span + 1 - j];
+            right[j] = knots[span + j] - u;
+            double saved = 0;
+            for (int r = 0; r < j; r++) {
+                double tmp = res[r] / (right[r + 1] + left[j - r]);
+                res[r] = saved + right[r + 1] * tmp;
+                saved = left[j - r] * tmp;
+            }
+            res[j] = saved;
+        }
+        return res;
+    }
+
+    /**
+     * Calculates the basis functions and its derivatives up to the given grade.
+     * 
+     * @param u
+     *            Value to calculate basis functions and derivatives for.
+     * @param grade
+     *            grade to calculate derivations for.
+     * @return an array of basis function values or derivated basis functions
+     *         values. The first array is the degree of dderivation in the
+     *         second array rhe values are stored. <br>
+     *         Example: <br>
+     *         <code>
+     * float[]][] f=dersBasisFuns(0.1f, 3);
+     * float value=f[0][1]; //In value is know the second value of the basis function derived 0 times stored.
+     * </code>
+     * 
+     */
+    public float[][] derivBasisFunctions(float u, int grade) {
+        int span = findSpan(u);
+        return derivBasisFunctions(span, u, grade);
+    }
+
+    /**
+     * Calculates the basis functions and its derivatives up to the given grade.
+     * 
+     * @param span
+     *            Span the given value lies in.
+     * @param u
+     *            Value to calculate basis functions and derivatives for.
+     * @param grade
+     *            grade to calculate derivations for.
+     * @return an array of basis function values or derivated basis functions
+     *         values
+     * @see KnotVector#derivBasisFunctions(float, int)
+     */
+    public float[][] derivBasisFunctions(int span, float u, int grade) {
+        float[][] ders = new float[grade + 1][degree + 1];
+        float[][] ndu = new float[degree + 1][degree + 1];
+        ndu[0][0] = 1.0f;
+        float[] left = new float[degree + 1];
+        float[] right = new float[degree + 1];
+        int j1, j2;
+        for (int j = 1; j <= degree; j++) {
+            left[j] = u - knots[span + 1 - j];
+            right[j] = knots[span + j] - u;
+            float saved = 0.0f;
+            for (int r = 0; r < j; r++) {
+                ndu[j][r] = right[r + 1] + left[j - r];
+                float temp = ndu[r][j - 1] / ndu[j][r];
+                ndu[r][j] = saved + right[r + 1] * temp;
+                saved = left[j - r] * temp;
+            }
+            ndu[j][j] = saved;
+        }
+        for (int j = 0; j <= degree; j++) {
+            ders[0][j] = ndu[j][degree];
+        }
+        for (int r = 0; r <= degree; r++) {
+            int s1 = 0;
+            int s2 = 1;
+            float[][] a = new float[2][degree + 1];
+            a[0][0] = 1.0f;
+            for (int k = 1; k <= grade; k++) {
+                float d = 0.0f;
+                final int rk = r - k;
+                final int pk = degree - k;
+                final float[] as1 = a[s1];
+                final float[] as2 = a[s2];
+                if (r >= k) {
+                    as2[0] = d = as1[0] / ndu[pk + 1][rk];
+                    d *= ndu[rk][pk];
+                }
+                if (rk >= -1) {
+                    j1 = 1;
+                } else {
+                    j1 = -rk;
+                }
+                if (r - 1 <= pk) {
+                    j2 = k - 1;
+                } else {
+                    j2 = degree - r;
+                }
+                for (int j = j1; j <= j2; j++) {
+                    as2[j] = (as1[j] - as1[j - 1]) / ndu[pk + 1][rk + j];
+                    d += as2[j] * ndu[rk + j][pk];
+                }
+                if (r <= pk) {
+                    as2[k] = -as1[k - 1] / ndu[pk + 1][r];
+                    d += as2[k] * ndu[r][pk];
+                }
+                ders[k][r] = d;
+                int j = s1;
+                s1 = s2;
+                s2 = j;
+            }
+        }
+        int r = degree;
+        for (int k = 1; k <= grade; k++) {
+            for (int j = 0; j <= degree; j++) {
+                ders[k][j] *= r;
+            }
+            r *= (degree - k);
+        }
+        return ders;
+    }
+
+    /**
+     * Finds the span (Position of corresponding knot values in knot vector) a
+     * given value belongs to.
+     * 
+     * @param u
+     *            value to find span for
+     * @return Position of span.
+     */
+    public int findSpan(float u) {
+        if (u >= knots[n + 1]) {
+            return n;
+        }
+        int low = degree;
+        int high = n + 1;
+        int mid = (low + high) / 2;
+        while ((u < knots[mid] || u >= knots[mid + 1]) && low < high) {
+            if (u < knots[mid]) {
+                high = mid;
+            } else {
+                low = mid;
+            }
+            mid = (low + high) / 2;
+        }
+        return mid;
+    }
+
+    /**
+     * Get the knot value at a specific index.
+     * 
+     * @param i
+     *            Index to get knot value for
+     * @return the knot value
+     */
+    public float get(int i) {
+        return knots[i];
+    }
+
+    /**
+     * get the knot values as float array
+     * 
+     * @return the knot values
+     */
+    public float[] getArray() {
+        return knots;
+    }
+
+    /**
+     * Get the degree of the KnotVector
+     * 
+     * @return Degree of the Knotvector
+     */
+    public int getDegree() {
+        return degree;
+    }
+
+    /**
+     * Return the nu
+     * 
+     * @return Length of the KnotVector
+     */
+    public int getN() {
+        return n;
+    }
+
+    public int getNumberOfSegments() {
+        int seg = 0;
+        float u = knots[0];
+        for (int i = 1; i < knots.length; i++) {
+            if (u != knots[i]) {
+                seg++;
+                u = knots[i];
+            }
+        }
+        return seg;
+    }
+
+    public synchronized boolean isOpen() {
+        return isOpen;
+    }
+
+    public int length() {
+        return knots.length;
+    }
+
+    /**
+     * Set the knot value at a specific index. After this operation a call to
+     * isValid may be needed if one is not sure if the KnotVector with the
+     * changed value is valid for a Nurbs.
+     * 
+     * @param i
+     *            Index to set knot value
+     * @param val
+     *            value to set the knot too
+     */
+    public void set(int i, float val) {
+        knots[i] = val;
+    }
+
+}
diff --git a/src/main/java/toxi/geom/nurbs/NurbsCreator.java b/src/main/java/toxi/geom/nurbs/NurbsCreator.java
new file mode 100644
index 0000000..c44f092
--- /dev/null
+++ b/src/main/java/toxi/geom/nurbs/NurbsCreator.java
@@ -0,0 +1,815 @@
+/*
+ * jgeom: Geometry Library fo Java
+ * 
+ * Copyright (C) 2005  Samuel Gerber
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package toxi.geom.nurbs;
+
+import toxi.geom.Axis3D;
+import toxi.geom.GMatrix;
+import toxi.geom.GVector;
+import toxi.geom.Origin3D;
+import toxi.geom.roVec3D;
+import toxi.geom.SingularMatrixException;
+import toxi.geom.Vec3D;
+import toxi.geom.Vec4D;
+import toxi.math.MathUtils;
+
+/**
+ * This class offers some static methods to create NurbsSurfaces and NurbsCurves
+ * from diffrent inputs.
+ * 
+ * @author sg
+ * @version 1.0
+ */
+public final class NurbsCreator {
+
+    private static KnotVector averaging(final float[] uk, final int p) {
+        int m = uk.length + p;
+        int n = uk.length - 1;
+        float ip = 1f / p;
+        float[] u = new float[m + 1];
+        for (int i = 0; i <= p; i++) {
+            u[m - i] = 1;
+        }
+        for (int j = 1; j <= n - p; j++) {
+            float sum = 0;
+            for (int i = j; i <= j + p - 1; i++) {
+                sum += uk[i];
+            }
+            u[j + p] = sum * ip;
+        }
+        return new KnotVector(u, p);
+    }
+
+    private static float[] centripetal(Vec3D[] points) {
+        int n = points.length - 1;
+        float d = 0;
+        float[] uk = new float[n + 1];
+        uk[n] = 1;
+        double[] tmp = new double[n];
+        for (int k = 1; k <= n; k++) {
+            tmp[k - 1] = Math.sqrt(points[k].distanceTo(points[k - 1]));
+            d += tmp[k - 1];
+        }
+        d = 1f / d;
+        for (int i = 1; i < n; i++) {
+            uk[i] = uk[i - 1] + (float) (tmp[i - 1] * d);
+        }
+        return uk;
+    }
+
+    /**
+     * Create an Arc.
+     * 
+     * @param o
+     *            Origin to creat arc around
+     * @param r
+     *            Radius of the arc.
+     * @param thetaStart
+     *            Start angle of the arc in radians
+     * @param thetaEnd
+     *            End angle of the arc in radians. If end angle is smaller than
+     *            start angle, the end angle is increased by 2*PI.
+     * @return A NurbsCurve for the Arc.
+     */
+    public static NurbsCurve createArc(Origin3D o, float r, float thetaStart,
+            float thetaEnd) {
+        Vec3D tmp = new Vec3D();
+
+        if (thetaEnd < thetaStart) {
+            thetaEnd += MathUtils.TWO_PI;
+        }
+        double theta = thetaEnd - thetaStart;
+
+        int narcs = 4;
+        if (theta <= MathUtils.HALF_PI) {
+            narcs = 1;
+        } else if (theta <= MathUtils.PI) {
+            narcs = 2;
+        } else if (theta <= MathUtils.THREE_HALVES_PI) {
+            narcs = 3;
+        }
+        double dtheta = theta / narcs;
+        int n = 2 * narcs;
+        double w1 = Math.cos(dtheta / 2);
+
+        final float sinStart = (float) Math.sin(thetaStart);
+        final float cosStart = (float) Math.cos(thetaStart);
+        tmp.set(o.xAxis).scaleSelf(r * cosStart);
+        Vec3D p0 = new Vec3D(o.origin).addSelf(tmp);
+        tmp.set(o.yAxis).scaleSelf(r * sinStart);
+        p0.addSelf(tmp);
+
+        tmp.set(o.yAxis).scaleSelf(cosStart);
+        Vec3D t0 = new Vec3D(o.xAxis).scaleSelf(-sinStart).addSelf(tmp);
+
+        Vec4D[] cps = new Vec4D[n + 1];
+        cps[0] = new Vec4D(p0, 1);
+        int index = 0;
+        double angle = thetaStart;
+
+        Vec3D p1 = new Vec3D();
+        Vec3D p2 = new Vec3D();
+        Vec3D t2 = new Vec3D();
+        for (int i = 1; i <= narcs; i++) {
+            angle += dtheta;
+            final double sin = Math.sin(angle);
+            final double cos = Math.cos(angle);
+
+            tmp.set(o.xAxis).scaleSelf((float) (r * cos));
+            p2.set(o.origin).addSelf(tmp);
+            tmp.set(o.yAxis).scaleSelf((float) (r * sin));
+            p2.addSelf(tmp);
+
+            cps[index + 2] = new Vec4D(p2, 1);
+
+            t2.set(o.xAxis).scaleSelf((float) -sin);
+            tmp.set(o.yAxis).scaleSelf((float) cos);
+            t2.addSelf(tmp);
+
+            lineIntersect3D(p0, t0, p2, t2, p1, p1);
+
+            cps[index + 1] = new Vec4D(p1, (float) w1);
+            index += 2;
+            if (i < narcs) {
+                p0.set(p2);
+                t0.set(t2);
+            }
+        }
+        int j = n + 1;
+        float[] uKnot = new float[j + 3];
+        for (int i = 0; i < 3; i++) {
+            uKnot[i + j] = 1;
+        }
+        switch (narcs) {
+            case 2:
+                uKnot[3] = 0.5f;
+                uKnot[4] = 0.5f;
+                break;
+            case 3:
+                uKnot[3] = uKnot[4] = MathUtils.THIRD;
+                uKnot[5] = uKnot[6] = 2 * MathUtils.THIRD;
+                break;
+            case 4:
+                uKnot[3] = 0.25f;
+                uKnot[4] = 0.25f;
+                uKnot[5] = 0.5f;
+                uKnot[6] = 0.5f;
+                uKnot[7] = 0.75f;
+                uKnot[8] = 0.75f;
+                break;
+        }
+
+        return new BasicNurbsCurve(cps, uKnot, 2);
+    }
+
+    /**
+     * Create a full-circle NurbsCurve around the given Origin with radius r.
+     * The NurbsCurve has controlpolygon which has 7 controlpoints and the shape
+     * of quadrat.
+     * 
+     * @param o
+     *            Origin to create the full-circle around
+     * @param r
+     *            Radius of the full-circle
+     * @return A NurbsCurve for a full-circle
+     */
+    public static NurbsCurve createFullCircleQuad7(Origin3D o, float r) {
+
+        Vec4D[] cp = new Vec4D[7];
+        cp[0] = new Vec4D(o.xAxis.scale(r), 1);
+        cp[3] = cp[0].getInvertedXYZ();
+        cp[6] = cp[0].copy();
+
+        cp[1] = new Vec4D(o.yAxis.add(o.xAxis).scaleSelf(r), 0.5f);
+        cp[4] = cp[1].getInvertedXYZ();
+
+        cp[2] = new Vec4D(o.xAxis.getInverted().addSelf(o.yAxis).scaleSelf(r),
+                0.5f);
+        cp[5] = cp[2].getInvertedXYZ();
+
+        for (int i = 0; i < 7; i++) {
+            cp[i].addXYZSelf(o.origin);
+        }
+        float[] u = {
+                0, 0, 0, 0.25f, 0.5f, 0.5f, 0.75f, 1, 1, 1
+        };
+        return new BasicNurbsCurve(cp, u, 2);
+    }
+
+    /**
+     * Create a full-circle NurbsCurve around the given Origin with radius r.
+     * The NurbsCurve has controlpolygon which has 9 controlpoints and the shape
+     * of quadrat.
+     * 
+     * @param o
+     *            Origin to create the full-circle around
+     * @param r
+     *            Radius of the full-circle
+     * @return A NurbsCurve for a full-circle
+     */
+    public static NurbsCurve createFullCircleQuad9(Origin3D o, float r) {
+        final float w = MathUtils.SQRT2 / 2;
+
+        Vec4D[] cp = new Vec4D[9];
+        cp[0] = new Vec4D(o.xAxis.scale(r), 1);
+        cp[4] = cp[0].getInvertedXYZ();
+        cp[8] = cp[0].copy();
+
+        cp[1] = new Vec4D(o.xAxis.add(o.yAxis).scaleSelf(r), w);
+        cp[5] = cp[1].getInvertedXYZ();
+
+        cp[2] = new Vec4D(o.yAxis.scale(r), 1);
+        cp[6] = cp[2].getInvertedXYZ();
+
+        cp[3] = new Vec4D(o.xAxis.getInverted().addSelf(o.yAxis).scaleSelf(r),
+                w);
+        cp[7] = cp[3].getInvertedXYZ();
+
+        for (int i = 0; i < 9; i++) {
+            cp[i].addXYZSelf(o.origin);
+        }
+        float[] u = {
+                0, 0, 0, 0.25f, 0.25f, 0.5f, 0.5f, 0.75f, 0.75f, 1, 1, 1
+        };
+        return new BasicNurbsCurve(cp, u, 2);
+    }
+
+    /**
+     * Create a revolved NurbsSurface from the given NurbsCurve around the given
+     * axis whith the angle theta.
+     * 
+     * @param a
+     *            Axis to revolve around.
+     * @param curve
+     *            NurbsCurve to revolve
+     * @param theta
+     *            Angle to revolve
+     * @return The revolved NurbsSurface
+     */
+    // TODO:call createRevolvedSurface(Axis3D a, NurbsCurve curve, double
+    // thetaStart, double thetaEnd) as as it is tested
+    public static NurbsSurface createRevolvedSurface(Axis3D a,
+            NurbsCurve curve, double theta) {
+        int narcs = 4;
+        if (theta <= MathUtils.HALF_PI) {
+            narcs = 1;
+        } else if (theta <= MathUtils.PI) {
+            narcs = 2;
+        } else if (theta <= MathUtils.THREE_HALVES_PI) {
+            narcs = 3;
+        }
+
+        int j = 3 + 2 * (narcs - 1);
+        final double dtheta = theta / narcs;
+        final float[] uKnot = new float[j + 3];
+        for (int i = 0; i < 3; i++) {
+            uKnot[j + i] = 1;
+        }
+        switch (narcs) {
+            case 2:
+                uKnot[3] = 0.5f;
+                uKnot[4] = 0.5f;
+                break;
+            case 3:
+                uKnot[3] = uKnot[4] = MathUtils.THIRD;
+                uKnot[5] = uKnot[6] = 2 * MathUtils.THIRD;
+                break;
+            case 4:
+                uKnot[3] = 0.25f;
+                uKnot[4] = 0.25f;
+                uKnot[5] = 0.5f;
+                uKnot[6] = 0.5f;
+                uKnot[7] = 0.75f;
+                uKnot[8] = 0.75f;
+                break;
+        }
+
+        double angle = 0;
+        final double[] cos = new double[narcs + 1];
+        final double[] sin = new double[narcs + 1];
+        for (int i = 0; i <= narcs; i++) {
+            cos[i] = Math.cos(angle);
+            sin[i] = Math.sin(angle);
+            angle += dtheta;
+        }
+
+        Vec4D[] pj = curve.getControlPoints();
+        Vec3D P0 = new Vec3D();
+        final Vec3D P2 = new Vec3D();
+        final Vec3D O = new Vec3D();
+        final Vec3D T2 = new Vec3D();
+        final Vec3D T0 = new Vec3D();
+        final Vec3D tmp = new Vec3D();
+        final Vec3D X = new Vec3D();
+        final Vec3D Y = new Vec3D();
+        final Vec4D[][] pij = new Vec4D[2 * narcs + 1][pj.length];
+        final double wm = Math.cos(dtheta / 2);
+        for (j = 0; j < pj.length; j++) {
+            pointToLine3D(a.origin, a.dir, pj[j].to3D(), O);
+            X.set(pj[j].to3D().subSelf(O));
+            final double r = X.magnitude();
+            if (r == 0) {
+                X.set(O);
+            }
+            X.normalize();
+            a.dir.crossInto(X, Y);
+            pij[0][j] = new Vec4D(pj[j]);
+            P0 = pj[j].to3D();
+            T0.set(Y);
+            int index = 0;
+            for (int i = 1; i <= narcs; i++) {
+                tmp.set(X).scaleSelf((float) (r * cos[i]));
+                P2.set(O).addSelf(tmp);
+                tmp.set(Y).scaleSelf((float) (r * sin[i]));
+                P2.addSelf(tmp);
+
+                pij[index + 2][j] = new Vec4D(P2, pj[j].w);
+
+                tmp.set(Y).scaleSelf((float) cos[i]);
+                T2.set(X).scaleSelf((float) -sin[i]).addSelf(tmp);
+
+                lineIntersect3D(P0, T0, P2, T2, tmp, tmp);
+                pij[index + 1][j] = new Vec4D(tmp, (float) (wm * pj[j].w));
+
+                index += 2;
+                if (i < narcs) {
+                    P0.set(P2);
+                    T0.set(T2);
+                }
+
+            }
+        }
+        ControlNet cnet = new ControlNet(pij);
+        return new BasicNurbsSurface(cnet, uKnot, curve.getKnots(), 2,
+                curve.getDegree());
+    }
+
+    /**
+     * Create a revolved NurbsSurface from the given NurbsCurve around the given
+     * axis whith the angle theta.
+     * 
+     * @param a
+     *            Axis to revolve around.
+     * @param curve
+     *            NurbsCurve to revolve
+     * @param thetaStart
+     *            Angle to start revolution
+     * @param thetaEnd
+     *            Angle to end revolution
+     * @return The revolved NurbsSurface
+     */
+    public static NurbsSurface createRevolvedSurface(Axis3D a,
+            NurbsCurve curve, double thetaStart, double thetaEnd) {
+        int narcs = 4;
+        if (thetaStart > thetaEnd) {
+            double tmp = thetaEnd;
+            thetaEnd = thetaStart;
+            thetaStart = tmp;
+        }
+        double theta = thetaEnd - thetaStart;
+        if (theta <= MathUtils.HALF_PI) {
+            narcs = 1;
+        } else if (theta <= MathUtils.PI) {
+            narcs = 2;
+        } else if (theta <= MathUtils.THREE_HALVES_PI) {
+            narcs = 3;
+        }
+
+        int j = 3 + 2 * (narcs - 1);
+        final double dtheta = theta / narcs;
+        final float[] uKnot = new float[j + 3];
+        for (int i = 0; i < 3; i++) {
+            uKnot[i] = 0;
+            uKnot[j + i] = 1;
+        }
+        switch (narcs) {
+            case 2:
+                uKnot[3] = 0.5f;
+                uKnot[4] = 0.5f;
+                break;
+            case 3:
+                uKnot[3] = uKnot[4] = MathUtils.THIRD;
+                uKnot[5] = uKnot[6] = 2 * MathUtils.THIRD;
+                break;
+            case 4:
+                uKnot[3] = 0.25f;
+                uKnot[4] = 0.25f;
+                uKnot[5] = 0.5f;
+                uKnot[6] = 0.5f;
+                uKnot[7] = 0.75f;
+                uKnot[8] = 0.75f;
+                break;
+        }
+
+        double angle = thetaStart;
+        final double[] cos = new double[narcs + 1];
+        final double[] sin = new double[narcs + 1];
+        for (int i = 0; i <= narcs; i++) {
+            cos[i] = Math.cos(angle);
+            sin[i] = Math.sin(angle);
+            angle += dtheta;
+        }
+
+        final Vec4D[] pj = curve.getControlPoints();
+        Vec3D P0 = new Vec3D();
+        final Vec3D O = new Vec3D();
+        final Vec3D P2 = new Vec3D();
+        final Vec3D T2 = new Vec3D();
+        final Vec3D T0 = new Vec3D();
+        final Vec3D tmp = new Vec3D();
+        final Vec3D X = new Vec3D();
+        final Vec3D Y = new Vec3D();
+        final Vec4D[][] pij = new Vec4D[2 * narcs + 1][pj.length];
+        final double wm = Math.cos(dtheta / 2);
+        for (j = 0; j < pj.length; j++) {
+            pointToLine3D(a.origin, a.dir, pj[j].to3D(), O);
+            X.set(pj[j].to3D().subSelf(O));
+            final double r = X.magnitude();
+            if (r == 0) {
+                X.set(O);
+            }
+            X.normalize();
+            a.dir.crossInto(X, Y);
+            pij[0][j] = new Vec4D(pj[j]);
+            P0 = pj[j].to3D();
+            T0.set(Y);
+            int index = 0;
+            for (int i = 1; i <= narcs; i++) {
+                tmp.set(X).scaleSelf((float) (r * cos[i]));
+                P2.set(O).addSelf(tmp);
+                tmp.set(Y).scaleSelf((float) (r * sin[i]));
+                P2.addSelf(tmp);
+
+                pij[index + 2][j] = new Vec4D(P2, pj[j].w);
+
+                tmp.set(Y).scaleSelf((float) cos[i]);
+                T2.set(X).scaleSelf((float) -sin[i]).addSelf(tmp);
+
+                lineIntersect3D(P0, T0, P2, T2, tmp, tmp);
+                pij[index + 1][j] = new Vec4D(tmp, (float) (wm * pj[j].w));
+
+                index += 2;
+                if (i < narcs) {
+                    P0.set(P2);
+                    T0.set(T2);
+                }
+            }
+        }
+        ControlNet cnet = new ControlNet(pij);
+        return new BasicNurbsSurface(cnet, uKnot, curve.getKnots(), 2,
+                curve.getDegree());
+    }
+
+    /**
+     * Create a semi-circle NurbsCurve around the given Origin with radius r.
+     * 
+     * @param o
+     *            Origin to create semi-circle around.
+     * @param r
+     *            Radius of the semi-circle
+     * @return A NurbsCurve for a semi-circle
+     */
+    public static NurbsCurve createSemiCircle(Origin3D o, float r) {
+        Vec4D[] cp = new Vec4D[4];
+        cp[0] = new Vec4D(o.xAxis.scale(r), 1);
+        cp[3] = cp[0].getInvertedXYZ();
+        cp[0].addXYZSelf(o.origin);
+        cp[3].addXYZSelf(o.origin);
+        cp[1] = new Vec4D(o.xAxis.add(o.yAxis).scaleSelf(r).addSelf(o.origin),
+                0.5f);
+        cp[2] = new Vec4D(o.xAxis.getInverted().addSelf(o.yAxis).scaleSelf(r)
+                .addSelf(o.origin), 0.5f);
+
+        float[] u = {
+                0, 0, 0, 0.5f, 1, 1, 1
+        };
+        return new BasicNurbsCurve(cp, u, 2);
+    }
+
+    /**
+     * Creates a {@link NurbsSurface} by swinging a profile {@link NurbsCurve}
+     * in the XZ plane around a trajectory curve in the XY plane. Both curves
+     * MUST be offset from the major axes (i.e. their control points should have
+     * non-zero coordinates for the Y coordinates of the profile curve and the Z
+     * coordinates of the trajectory).
+     * 
+     * @param proj
+     *            profile curve in XZ
+     * @param traj
+     *            trajectory curve in XY
+     * @param alpha
+     *            scale factor
+     * @return 3D NURBS surface
+     */
+    public static NurbsSurface createSwungSurface(NurbsCurve proj,
+            NurbsCurve traj, float alpha) {
+        Vec4D[] cpProj = proj.getControlPoints();
+        Vec4D[] cpTraj = traj.getControlPoints();
+
+        // The NURBS Book, Piegl, p.455,456
+        // http://books.google.co.uk/books?id=7dqY5dyAwWkC&pg=PA455&lpg=PA455
+        // fixed Z handling (was wrong in original jgeom version)
+        Vec4D[][] cps = new Vec4D[cpProj.length][cpTraj.length];
+        for (int i = 0; i < cpProj.length; i++) {
+            for (int j = 0; j < cpTraj.length; j++) {
+                Vec4D cp = new Vec4D();
+                cp.x = cpProj[i].x * cpTraj[j].x * alpha;
+                cp.y = cpProj[i].y * cpTraj[j].y * alpha;
+                cp.z = (cpProj[i].z + cpTraj[j].z) * alpha;
+                cp.w = cpProj[i].w * cpTraj[j].w;
+                cps[i][j] = cp;
+            }
+        }
+        return new BasicNurbsSurface(new ControlNet(cps), proj.getKnots(),
+                traj.getKnots(), proj.getDegree(), traj.getDegree());
+
+    }
+
+    /**
+     * Perform a linear extrusion of the given {@link NurbsCurve} along the
+     * supplied vector to produce a new {@link NurbsSurface}. The extrusion
+     * length is the length of the vector given.
+     * 
+     * @param curve
+     *            NURBS curve instance
+     * @param extrude
+     *            a extrusion vector
+     * @return a NurbsSurface.
+     */
+    public static NurbsSurface extrudeCurve(NurbsCurve curve, Vec3D extrude) {
+
+        // Curve and Surface Construction using Rational B-splines
+        // Piegl and Tiller CAD Vol 19 #9 November 1987 pp 485-498
+        KnotVector vKnot = new KnotVector(new float[] {
+                0f, 0f, 1f, 1f
+        }, 1);
+
+        Vec4D[][] cpoints = new Vec4D[curve.getControlPoints().length][2];
+        Vec4D[] curvePoints = curve.getControlPoints();
+        for (int i = 0; i < cpoints.length; i++) {
+            for (int j = 0; j < 2; j++) {
+                /*
+                 * Change added 11/02/90 Steve Larkin : Have multiplied the term
+                 * wcoord to the extrusion vector before adding to the curve
+                 * coordinates. Not really sure this is the correct fix, but it
+                 * works !
+                 */
+                Vec4D cp = new Vec4D();
+                cp.x = curvePoints[i].x + j * extrude.x;
+                cp.y = curvePoints[i].y + j * extrude.y;
+                cp.z = curvePoints[i].z + j * extrude.z;
+                cp.w = curvePoints[i].w;
+                cpoints[i][j] = cp;
+            }
+        }
+        ControlNet cnet = new ControlNet(cpoints);
+        return new BasicNurbsSurface(cnet, curve.getKnots(), vKnot.getArray(),
+                curve.getDegree(), vKnot.getDegree());
+    }
+
+    /**
+     * Interpolates a NurbCurve form the given Points using a global
+     * interpolation technique.
+     * 
+     * @param points
+     *            Points to interpolate
+     * @param degree
+     *            degree of the interpolated NurbsCurve
+     * @return A NurbsCurve interpolating the given Points
+     * @throws InterpolationException
+     *             thrown if interpolation failed or is not possible.
+     */
+    public static NurbsCurve globalCurveInterpolation(Vec3D[] points, int degree)
+            throws InterpolationException {
+        try {
+            final int n = points.length;
+            final double[] A = new double[n * n];
+
+            final float[] uk = centripetal(points);
+            KnotVector uKnots = averaging(uk, degree);
+            for (int i = 0; i < n; i++) {
+                int span = uKnots.findSpan(uk[i]);
+                double[] tmp = uKnots.basisFunctions(span, uk[i]);
+                System.arraycopy(tmp, 0, A, i * n + span - degree, tmp.length);
+            }
+            final GMatrix a = new GMatrix(n, n, A);
+            final GVector perm = new GVector(n);
+            final GMatrix lu = new GMatrix(n, n);
+            a.computeLUD(lu, perm);
+
+            final Vec4D[] cps = new Vec4D[n];
+            for (int i = 0; i < cps.length; i++) {
+                cps[i] = new Vec4D(0, 0, 0, 1);
+            }
+
+            // x-ccordinate
+            final GVector b = new GVector(n);
+            for (int j = 0; j < n; j++) {
+                b.setElement(j, points[j].x);
+            }
+            final GVector sol = new GVector(n);
+            sol.backSolveLUD(lu, b, perm);
+            for (int j = 0; j < n; j++) {
+                cps[j].x = (float) sol.get(j);
+            }
+
+            // y-ccordinate
+            for (int j = 0; j < n; j++) {
+                b.setElement(j, points[j].y);
+            }
+            sol.zero();
+            sol.backSolveLUD(lu, b, perm);
+            for (int j = 0; j < n; j++) {
+                cps[j].y = (float) sol.get(j);
+            }
+
+            // z-ccordinate
+            for (int j = 0; j < n; j++) {
+                b.setElement(j, points[j].z);
+            }
+            sol.zero();
+            sol.backSolveLUD(lu, b, perm);
+            for (int j = 0; j < n; j++) {
+                cps[j].z = (float) sol.get(j);
+            }
+            return new BasicNurbsCurve(cps, uKnots);
+        } catch (SingularMatrixException ex) {
+            throw new InterpolationException(ex);
+        }
+
+    }
+
+    /**
+     * Interpolates a NurbsSurface from the given points using a gloabl
+     * interpolation technique.
+     * 
+     * @param points
+     *            Points arranged in a net (matrix) to interpolate
+     * @param uDegrees
+     *            degree in u direction
+     * @param vDegrees
+     *            degree in v direction
+     * @return A NurbsSurface interpolating the given points.
+     * @throws InterpolationException
+     *             thrown if interpolation failed or is not possible.
+     */
+    public static NurbsSurface globalSurfaceInterpolation(Vec3D[][] points,
+            int uDegrees, int vDegrees) throws InterpolationException {
+        final int n = points.length;
+        final int m = points[0].length;
+        float[][] uv = surfaceMeshParameters(points, n - 1, m - 1);
+        KnotVector u = averaging(uv[0], uDegrees);
+        KnotVector v = averaging(uv[1], vDegrees);
+
+        Vec4D[][] r = new Vec4D[m][n];
+        Vec3D[] tmp = new Vec3D[n];
+        for (int l = 0; l < m; l++) {
+            for (int i = 0; i < n; i++) {
+                tmp[i] = points[i][l];
+            }
+            try {
+                NurbsCurve curve = globalCurveInterpolation(tmp, uDegrees);
+                r[l] = curve.getControlPoints();
+            } catch (InterpolationException ex) {
+                for (int i = 0; i < tmp.length; i++) {
+                    r[l][i] = new Vec4D(tmp[i], 1);
+                }
+            }
+
+        }
+
+        Vec4D[][] cp = new Vec4D[n][m];
+        tmp = new Vec3D[m];
+        for (int i = 0; i < n; i++) {
+            for (int j = 0; j < m; j++) {
+                tmp[j] = r[j][i].to3D();
+            }
+            try {
+                NurbsCurve curve = globalCurveInterpolation(tmp, vDegrees);
+                cp[i] = curve.getControlPoints();
+            } catch (InterpolationException ex) {
+                for (int j = 0; j < tmp.length; j++) {
+                    cp[i][j] = new Vec4D(tmp[j], 1);
+                }
+            }
+        }
+
+        return new BasicNurbsSurface(new ControlNet(cp), u, v);
+    }
+
+    private static void lineIntersect3D(Vec3D p0, Vec3D t0, Vec3D p2, Vec3D t2,
+            Vec3D out0, Vec3D out2) {
+        Vec3D v02 = p0.sub(p2);
+
+        double a = t0.dot(t0);
+        double b = t0.dot(t2);
+        double c = t2.dot(t2);
+        double d = t0.dot(v02);
+        double e = t2.dot(v02);
+        double denom = a * c - b * b;
+
+        double mu0, mu2;
+
+        if (denom < MathUtils.EPS) {
+            mu0 = 0;
+            mu2 = b > c ? d / b : e / c;
+        } else {
+            mu0 = (b * e - c * d) / denom;
+            mu2 = (a * e - b * d) / denom;
+        }
+
+        out0.set(t0.scale((float) mu0).addSelf(p0));
+        out2.set(t2.scale((float) mu2).addSelf(p2));
+    }
+
+    private static void pointToLine3D(roVec3D p, roVec3D t,
+            Vec3D top, Vec3D out) {
+        Vec3D dir = top.sub(p);
+        float hyp = dir.magnitude();
+        out.set(p.add(t.scale(t.dot(dir.normalize()) * hyp)));
+    }
+
+    private static float[][] surfaceMeshParameters(Vec3D points[][], int n,
+            int m) {
+        final float[][] res = new float[2][];
+        int num = m + 1;
+        final float[] cds = new float[(n + 1) * (m + 1)];
+        final float[] uk = new float[n + 1];
+        uk[n] = 1;
+        for (int l = 0; l <= m; l++) {
+            float total = 0;
+            for (int k = 1; k <= n; k++) {
+                cds[k] = points[k][l].distanceTo(points[k - 1][l]);
+                total += cds[k];
+            }
+            if (total == 0) {
+                num = num - 1;
+            } else {
+                float d = 0;
+                total = 1f / total;
+                for (int k = 1; k <= n; k++) {
+                    d += cds[k];
+                    uk[k] += d * total;
+                }
+            }
+        }
+        if (num == 0) {
+            return null;
+        }
+        float inum = 1f / num;
+        for (int k = 1; k < n; k++) {
+            uk[k] *= inum;
+        }
+
+        num = n + 1;
+        final float[] vk = new float[m + 1];
+        vk[m] = 1;
+        for (int l = 0; l <= n; l++) {
+            float total = 0;
+            Vec3D[] pl = points[l];
+            for (int k = 1; k <= m; k++) {
+                cds[k] = pl[k].distanceTo(pl[k - 1]);
+                total += cds[k];
+            }
+            if (total == 0) {
+                num = num - 1;
+            } else {
+                float d = 0;
+                total = 1f / total;
+                for (int k = 1; k <= m; k++) {
+                    d += cds[k];
+                    vk[k] += d * total;
+                }
+            }
+        }
+        if (num == 0) {
+            return null;
+        }
+        inum = 1f / num;
+        for (int k = 1; k < m; k++) {
+            vk[k] *= inum;
+        }
+        res[0] = uk;
+        res[1] = vk;
+        return res;
+    }
+
+    private NurbsCreator() {
+    }
+}
diff --git a/src/main/java/toxi/geom/nurbs/NurbsCurve.java b/src/main/java/toxi/geom/nurbs/NurbsCurve.java
new file mode 100644
index 0000000..72b6db3
--- /dev/null
+++ b/src/main/java/toxi/geom/nurbs/NurbsCurve.java
@@ -0,0 +1,98 @@
+/*
+ * jgeom: Geometry Library fo Java
+ * 
+ * Copyright (C) 2005  Samuel Gerber
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package toxi.geom.nurbs;
+
+import toxi.geom.Polygon2D;
+import toxi.geom.Vec3D;
+import toxi.geom.Vec4D;
+import toxi.geom.XYZ;
+
+/**
+ * Interface for Nurbs Curves
+ * 
+ * @author sg
+ * @version 1.0
+ */
+public interface NurbsCurve {
+
+    /**
+     * Computes control points of dth derivative<br />
+     * Piegel, L. The Nurbs Book<br />
+     * Algorithm A3.3 -> Page 98<br />
+     * <br />
+     * 
+     * @param d
+     *            - dth derivative<br />
+     * @param r1
+     *            - from control point (0 for all control points)<br />
+     * @param r2
+     *            - to control point (n for all control points)<br />
+     * @return Vec4D[k][i] kth derivative, ith control point
+     */
+    public Vec4D[][] curveDerivCpts(int d, int r1, int r2);
+
+    public abstract XYZ[] derivativesOnCurve(float u, int d);
+
+    public abstract XYZ[] derivativesOnCurve(float u, int d, XYZ[] ders);
+
+    /**
+     * Get the ControlPoints of this curve
+     * 
+     * @return the ordered ControlPoints
+     */
+    Vec4D[] getControlPoints();
+
+    /**
+     * Get the Degree of the curve
+     * 
+     * @return degree of curve
+     */
+    int getDegree();
+
+    /**
+     * Gets the Knot values of the Nurbs curve
+     * 
+     * @return knot values
+     */
+    float[] getKnots();
+
+    KnotVector getKnotVector();
+
+    /**
+     * Calculate point on surface for the given u value
+     * 
+     * @param u
+     *            value to calculate point of
+     * @return calculated Point
+     */
+    Vec3D pointOnCurve(float u);
+
+    /**
+     * Calculate point on surface for the given u value
+     * 
+     * @param u
+     *            value to calculate point of
+     * @param out
+     *            Point to place result in
+     */
+    Vec3D pointOnCurve(float u, Vec3D out);
+
+    Polygon2D toPolygon2D(int res);
+}
diff --git a/src/main/java/toxi/geom/nurbs/NurbsMeshCreator.java b/src/main/java/toxi/geom/nurbs/NurbsMeshCreator.java
new file mode 100644
index 0000000..9ec5c9d
--- /dev/null
+++ b/src/main/java/toxi/geom/nurbs/NurbsMeshCreator.java
@@ -0,0 +1,103 @@
+//package toxi.geom.nurbs;
+//
+//import toxi.geom.Vec2D;
+//import toxi.geom.Vec3D;
+//import toxi.geom.mesh.Mesh3D;
+//import toxi.geom.mesh.TriangleMesh;
+//
+//public class NurbsMeshCreator {
+//
+//    private NurbsSurface surf;
+//    private Vec2D maxUV;
+//
+//    public NurbsMeshCreator(NurbsSurface surf) {
+//        this(surf, new Vec2D(1, 1));
+//    }
+//
+//    public NurbsMeshCreator(NurbsSurface surf, Vec2D maxUV) {
+//        this.surf = surf;
+//        this.maxUV = maxUV;
+//    }
+//
+//    public Mesh3D createControlMesh(Mesh3D mesh) {
+//        Vec3D[] prev = null;
+//        if (mesh == null) {
+//            mesh = new TriangleMesh();
+//        }
+//        int resU = surf.getControlNet().uLength();
+//        int resV = surf.getControlNet().vLength();
+//        Vec2D dUV = maxUV.scale(1f / resU, 1f / resV);
+//        for (int u = 0; u < resU; u++) {
+//            Vec3D[] curr = new Vec3D[resV + 1];
+//            for (int v = 0; v < resV; v++) {
+//                Vec3D vert = surf.getControlNet().get(u, v).to3D();
+//                if (v > 0 && u > 0) {
+//                    mesh.addFace(curr[v - 1], vert, prev[v - 1],
+//                            dUV.scale(u, v - 1), dUV.scale(u, v),
+//                            dUV.scale(u - 1, v - 1));
+//                    mesh.addFace(vert, prev[v], prev[v - 1], dUV.scale(u, v),
+//                            dUV.scale(u - 1, v), dUV.scale(u - 1, v - 1));
+//                }
+//                curr[v] = vert;
+//            }
+//            prev = curr;
+//        }
+//        mesh.computeVertexNormals();
+//        return mesh;
+//    }
+//
+//    public Mesh3D createMesh(Mesh3D mesh, int resU, int resV, boolean isClosed) {
+//        final KnotVector knotU = surf.getUKnotVector();
+//        final KnotVector knotV = surf.getVKnotVector();
+//        double iresU = knotU.get(knotU.length() - 1) / resU;
+//        double iresV = knotV.get(knotV.length() - 1) / resV;
+//        Vec3D[] prev = null;
+//        Vec3D[] first = null;
+//        if (mesh == null) {
+//            mesh = new TriangleMesh();
+//        }
+//        Vec2D dUV = maxUV.scale(1f / resU, 1f / resV);
+//        for (int u = 0; u <= resU; u++) {
+//            Vec3D[] curr = new Vec3D[resV + 1];
+//            for (int v = 0; v <= resV; v++) {
+//                Vec3D vert = null;
+//                if (isClosed) {
+//                    vert = u < resU ? surf.pointOnSurface(u * iresU, v * iresV)
+//                            : first[v];
+//                } else {
+//                    vert = surf.pointOnSurface(u * iresU, v * iresV);
+//                }
+//                if (v > 0 && u > 0) {
+//                    mesh.addFace(curr[v - 1], vert, prev[v - 1],
+//                            dUV.scale(u, v - 1), dUV.scale(u, v),
+//                            dUV.scale(u - 1, v - 1));
+//                    mesh.addFace(vert, prev[v], prev[v - 1], dUV.scale(u, v),
+//                            dUV.scale(u - 1, v), dUV.scale(u - 1, v - 1));
+//                }
+//                curr[v] = vert;
+//            }
+//            prev = curr;
+//            if (u == 0) {
+//                first = curr;
+//            }
+//        }
+//        mesh.computeVertexNormals();
+//        return mesh;
+//    }
+//
+//    public NurbsSurface getSurface() {
+//        return surf;
+//    }
+//
+//    public Vec2D getUVScale() {
+//        return maxUV;
+//    }
+//
+//    public void setSurface(NurbsSurface surf) {
+//        this.surf = surf;
+//    }
+//
+//    public void setUVScale(Vec2D maxUV) {
+//        this.maxUV = maxUV;
+//    }
+//}
diff --git a/src/main/java/toxi/geom/nurbs/NurbsSurface.java b/src/main/java/toxi/geom/nurbs/NurbsSurface.java
new file mode 100644
index 0000000..54ac235
--- /dev/null
+++ b/src/main/java/toxi/geom/nurbs/NurbsSurface.java
@@ -0,0 +1,133 @@
+/*
+ * jgeom: Geometry Library fo Java
+ * 
+ * Copyright (C) 2005  Samuel Gerber
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package toxi.geom.nurbs;
+
+import toxi.geom.Vec3D;
+import toxi.geom.Vec4D;
+import toxi.geom.XYZ;
+
+/**
+ * Interface for Nurbs Surfaces.
+ * 
+ * @author sg
+ * @version 1.2
+ */
+public interface NurbsSurface {
+
+    /**
+     * Get the Contol points of the NurbsSurface
+     * 
+     * @return ControlNet of the Nurbs
+     */
+    ControlNet getControlNet();
+
+    /**
+     * Get a List of all TrimCurves asociated with this Nurbs Surface
+     * 
+     * @return List of TrimCurves
+     */
+    // List<TrimCurve> getTrimCurves();
+
+    /**
+     * Get the degree in u direction
+     * 
+     * @return degree in u direction
+     */
+    int getUDegree();
+
+    /**
+     * Get the knot values in u direction
+     * 
+     * @return knot values in u direction
+     */
+    float[] getUKnots();
+
+    public abstract KnotVector getUKnotVector();
+
+    /**
+     * Get the degree in v direction
+     * 
+     * @return degree in v direction
+     */
+    int getVDegree();
+
+    /**
+     * Get the knot values in v direction
+     * 
+     * @return knot values in v direction
+     */
+    float[] getVKnots();
+
+    public abstract KnotVector getVKnotVector();
+
+    public abstract Vec3D pointOnSurface(double u, double v);
+
+    /**
+     * Calculate point on surface for the given u and v values
+     * 
+     * @param u
+     *            u value to caculate point from
+     * @param v
+     *            v value to caculate point from
+     * @return calculated point
+     */
+    XYZ pointOnSurface(float u, float v);
+
+    /**
+     * Add a TrimCurve to this Nurbs Surface
+     * 
+     * @param tc
+     *            TrimCurve to add.
+     */
+    // void addTrimCurve(TrimCurve tc);
+
+    /**
+     * Calculate point on surface for the given u and v values
+     * 
+     * @param u
+     *            u value to caculate point from
+     * @param v
+     *            v value to caculate point from
+     * @param out
+     *            point to place result in.
+     */
+    Vec3D pointOnSurface(float u, float v, Vec3D out);
+
+    /**
+     * Computes control points of dth derivative<br />
+     * Piegel, L. The Nurbs Book, Algorithm A3.7 -> Page 114<br />
+     * 
+     * @param d
+     *            - dth derivative (0<=k+l<=d)<br />
+     * @param r1
+     *            - from control point (u direction; 0 for all control points)<br />
+     * @param r2
+     *            - to control point (u direction; n for all control points)<br />
+     * @param s1
+     *            - from control point (v direction; 0 for all control points)<br />
+     * @param s2
+     *            - to control point (v direction; n for all control points)<br />
+     * @return ControlPoint4f[k][l][i][j] i,jth control point, differentiated k
+     *         times<br />
+     *         with respect to u and l times with respect to v
+     */
+    public Vec4D[][][][] surfaceDerivCpts(int d, int r1, int r2, int s1, int s2);
+
+}
diff --git a/src/main/java/com/syncleus/spangraph/geom/ReadonlyVec3D.java b/src/main/java/toxi/geom/roVec3D.java
similarity index 76%
rename from src/main/java/com/syncleus/spangraph/geom/ReadonlyVec3D.java
rename to src/main/java/toxi/geom/roVec3D.java
index 60c46bb..ebe2f31 100644
--- a/src/main/java/com/syncleus/spangraph/geom/ReadonlyVec3D.java
+++ b/src/main/java/toxi/geom/roVec3D.java
@@ -25,14 +25,17 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.geom;
 
+import toxi.geom.Vec3D.Axis;
+import toxi.math.MathUtils;
+import toxi.math.ScaleMap;
 
 /**
  * Readonly, immutable interface wrapper for Vec3D instances. Used throughout
  * the library for safety purposes.
  */
-public interface ReadonlyVec3D {
+public interface roVec3D extends XYZ {
 
     /**
      * Adds vector {a,b,c} and returns result as new vector.
@@ -48,7 +51,7 @@ public interface ReadonlyVec3D {
      */
     public Vec3D add(float a, float b, float c);
 
-    public Vec3D add(ReadonlyVec3D v);
+    public Vec3D add(roVec3D v);
 
     /**
      * Add vector v and returns result as new vector.
@@ -63,14 +66,14 @@ public interface ReadonlyVec3D {
     /**
      * Computes the angle between this vector and vector V. This function
      * assumes both vectors are normalized, if this can't be guaranteed, use the
-     * alternative implementation {@link #angleBetween(ReadonlyVec3D, boolean)}
+     * alternative implementation {@link #angleBetween(roVec3D, boolean)}
      * 
      * @param v
      *            vector
      * 
      * @return angle in radians, or NaN if vectors are parallel
      */
-    public float angleBetween(ReadonlyVec3D v);
+    public float angleBetween(XYZ v);
 
     /**
      * Computes the angle between this vector and vector V.
@@ -84,7 +87,7 @@ public interface ReadonlyVec3D {
      * 
      * @return angle in radians, or NaN if vectors are parallel
      */
-    public float angleBetween(ReadonlyVec3D v, boolean forceNormalize);
+    public float angleBetween(XYZ v, boolean forceNormalize);
 
     /**
      * Compares the length of the vector with another one.
@@ -94,7 +97,7 @@ public interface ReadonlyVec3D {
      * 
      * @return -1 if other vector is longer, 0 if both are equal or else +1
      */
-    public int compareTo(ReadonlyVec3D v);
+    public int compareTo(roVec3D v);
 
     /**
      * Copy.
@@ -106,13 +109,13 @@ public interface ReadonlyVec3D {
     /**
      * Calculates cross-product with vector v. The resulting vector is
      * perpendicular to both the current and supplied vector.
-     * 
+     *
      * @param v
      *            vector to cross
-     * 
+     *
      * @return cross-product as new vector
      */
-    public Vec3D cross(ReadonlyVec3D v);
+    public Vec3D cross(XYZ v);
 
     /**
      * Calculates cross-product with vector v. The resulting vector is
@@ -126,29 +129,8 @@ public interface ReadonlyVec3D {
      * 
      * @return result vector
      */
-    public Vec3D crossInto(ReadonlyVec3D v, Vec3D result);
+    public Vec3D crossInto(XYZ v, Vec3D result);
 
-    /**
-     * Calculates distance to another vector.
-     * 
-     * @param v
-     *            non-null vector
-     * 
-     * @return distance or Float.NaN if v=null
-     */
-    public float distanceTo(ReadonlyVec3D v);
-
-    /**
-     * Calculates the squared distance to another vector.
-     * 
-     * @param v
-     *            non-null vector
-     * 
-     * @return distance or NaN if v=null
-     * 
-     * @see #magSquared()
-     */
-    public float distanceToSquared(ReadonlyVec3D v);
 
     /**
      * Computes the scalar product (dot product) with the given vector.
@@ -161,7 +143,7 @@ public interface ReadonlyVec3D {
      * @see <a href="http://en.wikipedia.org/wiki/Dot_product">Wikipedia
      *      entry</a>
      */
-    public float dot(ReadonlyVec3D v);
+    public float dot(XYZ v);
 
     /*
      * (non-Javadoc)
@@ -182,7 +164,7 @@ public interface ReadonlyVec3D {
      * 
      * @return true, if equal
      */
-    public boolean equalsWithTolerance(ReadonlyVec3D v, float tolerance);
+    public boolean equalsWithTolerance(roVec3D v, float tolerance);
 
     /**
      * Gets the abs.
@@ -196,7 +178,7 @@ public interface ReadonlyVec3D {
      * 
      * @return new vector
      */
-    public Vec3D getCartesian();
+    public XYZ getCartesian();
 
     public Axis getClosestAxis();
 
@@ -212,7 +194,7 @@ public interface ReadonlyVec3D {
      * 
      * @return fitted vector
      */
-    public Vec3D getConstrained(AABB box);
+    public XYZ getConstrained(AABB box);
 
     /**
      * Creates a new vector whose components are the integer value of their
@@ -220,7 +202,7 @@ public interface ReadonlyVec3D {
      * 
      * @return result as new vector
      */
-    public Vec3D getFloored();
+    public XYZ getFloored();
 
     /**
      * Creates a new vector whose components are the fractional part of their
@@ -228,7 +210,7 @@ public interface ReadonlyVec3D {
      * 
      * @return result as new vector
      */
-    public Vec3D getFrac();
+    public XYZ getFrac();
 
     /**
      * Scales vector uniformly by factor -1 ( v = -v ).
@@ -246,7 +228,7 @@ public interface ReadonlyVec3D {
      * 
      * @return result as new vector
      */
-    public Vec3D getLimited(float lim);
+    public XYZ getLimited(float lim);
 
     /**
      * Produces a new vector with its coordinates passed through the given
@@ -255,14 +237,8 @@ public interface ReadonlyVec3D {
      * @param map
      * @return mapped vector
      */
-    //public Vec3D getMapped(ScaleMap map);
+    public XYZ getMapped(ScaleMap map);
 
-    /**
-     * Produces the normalized version as a new vector.
-     * 
-     * @return new vector
-     */
-    public Vec3D getNormalized();
 
     /**
      * Produces a new vector normalized to the given length.
@@ -279,9 +255,9 @@ public interface ReadonlyVec3D {
      * 
      * @return new vector
      */
-    public Vec3D getReciprocal();
+    public XYZ getReciprocal();
 
-    public Vec3D getReflected(ReadonlyVec3D normal);
+    public XYZ getReflected(roVec3D normal);
 
     /**
      * Gets the rotated around axis.
@@ -293,7 +269,7 @@ public interface ReadonlyVec3D {
      * 
      * @return new result vector
      */
-    public Vec3D getRotatedAroundAxis(ReadonlyVec3D axis, float theta);
+    public XYZ getRotatedAroundAxis(roVec3D axis, float theta);
 
     /**
      * Creates a new vector rotated by the given angle around the X axis.
@@ -303,7 +279,7 @@ public interface ReadonlyVec3D {
      * 
      * @return rotated vector
      */
-    public Vec3D getRotatedX(float theta);
+    public XYZ getRotatedX(float theta);
 
     /**
      * Creates a new vector rotated by the given angle around the Y axis.
@@ -313,7 +289,7 @@ public interface ReadonlyVec3D {
      * 
      * @return rotated vector
      */
-    public Vec3D getRotatedY(float theta);
+    public XYZ getRotatedY(float theta);
 
     /**
      * Creates a new vector rotated by the given angle around the Z axis.
@@ -323,7 +299,7 @@ public interface ReadonlyVec3D {
      * 
      * @return rotated vector
      */
-    public Vec3D getRotatedZ(float theta);
+    public XYZ getRotatedZ(float theta);
 
     /**
      * Creates a new vector with its coordinates rounded to the given precision
@@ -332,7 +308,7 @@ public interface ReadonlyVec3D {
      * @param prec
      * @return grid aligned vector
      */
-    public Vec3D getRoundedTo(float prec);
+    public XYZ getRoundedTo(float prec);
 
     /**
      * Creates a new vector in which all components are replaced with the signum
@@ -354,7 +330,7 @@ public interface ReadonlyVec3D {
      * 
      * @return new vector
      */
-    public Vec3D getSpherical();
+    public XYZ getSpherical();
 
     /**
      * Computes the vector's direction in the XY plane (for example for 2D
@@ -380,33 +356,6 @@ public interface ReadonlyVec3D {
      */
     public float headingYZ();
 
-    /**
-     * Interpolates the vector towards the given target vector, using linear
-     * interpolation.
-     * 
-     * @param v
-     *            target vector
-     * @param f
-     *            interpolation factor (should be in the range 0..1)
-     * 
-     * @return result as new vector
-     */
-    public Vec3D interpolateTo(ReadonlyVec3D v, float f);
-
-    /**
-     * Interpolates the vector towards the given target vector, using the given
-     * {@link InterpolateStrategy}.
-     * 
-     * @param v
-     *            target vector
-     * @param f
-     *            interpolation factor (should be in the range 0..1)
-     * @param s
-     *            InterpolateStrategy instance
-     * 
-     * @return result as new vector
-     */
-    //public Vec3D interpolateTo(ReadonlyVec3D v, float f, InterpolateStrategy s);
 
     /**
      * Checks if the point is inside the given AABB.
@@ -416,7 +365,7 @@ public interface ReadonlyVec3D {
      * 
      * @return true, if point is inside
      */
-    public boolean isInAABB(AABB box);
+
 
     /**
      * Checks if the point is inside the given axis-aligned bounding box.
@@ -464,29 +413,9 @@ public interface ReadonlyVec3D {
      */
     public float magSquared();
 
-    /**
-     * Scales vector uniformly and returns result as new vector.
-     * 
-     * @param s
-     *            scale factor
-     * 
-     * @return new vector
-     */
-    public Vec3D scale(float s);
 
-    /**
-     * Scales vector non-uniformly and returns result as new vector.
-     * 
-     * @param a
-     *            scale factor for X coordinate
-     * @param b
-     *            scale factor for Y coordinate
-     * @param c
-     *            scale factor for Z coordinate
-     * 
-     * @return new vector
-     */
-    public Vec3D scale(float a, float b, float c);
+
+
 
     /**
      * Scales vector non-uniformly by vector v and returns result as new vector.
@@ -496,7 +425,7 @@ public interface ReadonlyVec3D {
      * 
      * @return new vector
      */
-    public Vec3D scale(ReadonlyVec3D s);
+    public XYZ scale(roVec3D s);
 
     /**
      * Subtracts vector {a,b,c} and returns result as new vector.
@@ -510,7 +439,7 @@ public interface ReadonlyVec3D {
      * 
      * @return result as new vector
      */
-    public Vec3D sub(float a, float b, float c);
+    public XYZ sub(float a, float b, float c);
 
     /**
      * Subtracts vector v and returns result as new vector.
@@ -520,7 +449,7 @@ public interface ReadonlyVec3D {
      * 
      * @return result as new vector
      */
-    public Vec3D sub(ReadonlyVec3D v);
+    public Vec3D sub(roVec3D v);
 
     /**
      * Creates a new 2D vector of the XY components.
@@ -563,9 +492,4 @@ public interface ReadonlyVec3D {
 
     public float[] toArray4(float w);
 
-    public float x();
-
-    public float y();
-
-    public float z();
 }
\ No newline at end of file
diff --git a/src/main/java/toxi/math/BezierInterpolation.java b/src/main/java/toxi/math/BezierInterpolation.java
new file mode 100644
index 0000000..c133c39
--- /dev/null
+++ b/src/main/java/toxi/math/BezierInterpolation.java
@@ -0,0 +1,83 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math;
+
+/**
+ * Bezier curve interpolation with configurable coefficients. The curve
+ * parameters need to be normalized offsets relative to the start and end values
+ * passed to the {@link #interpolate(float, float, float)} method, but can
+ * exceed the normal 0 .. 1.0 interval. Use symmetrical offsets to create a
+ * symmetrical curve, e.g. this will create a curve with 2 dips reaching the
+ * minimum and maximum values at 25% and 75% of the interval...
+ * 
+ * <p>
+ * <code>BezierInterpolation b=new BezierInterpolation(3,-3);</code>
+ * </p>
+ * 
+ * The curve will be a straight line with this configuration:
+ * 
+ * <p>
+ * <code>BezierInterpolation b=new BezierInterpolation(1f/3,-1f/3);</code>
+ * </p>
+ */
+public class BezierInterpolation implements InterpolateStrategy {
+
+    public float c1;
+    public float c2;
+
+    public BezierInterpolation(float h1, float h2) {
+        this.c1 = h1;
+        this.c2 = h2;
+    }
+
+    public double interpolate(double a, double b, double t) {
+        double tSquared = t * t;
+        double invT = 1.0f - t;
+        double invTSquared = invT * invT;
+        return (a * invTSquared * invT)
+                + (3 * (c1 * (b - a) + a) * t * invTSquared)
+                + (3 * (c2 * (b - a) + b) * tSquared * invT)
+                + (b * tSquared * t);
+    }
+
+    public float interpolate(float a, float b, float t) {
+        float tSquared = t * t;
+        float invT = 1.0f - t;
+        float invTSquared = invT * invT;
+        return (a * invTSquared * invT)
+                + (3 * (c1 * (b - a) + a) * t * invTSquared)
+                + (3 * (c2 * (b - a) + b) * tSquared * invT)
+                + (b * tSquared * t);
+    }
+
+    public void setCoefficients(float a, float b) {
+        c1 = a;
+        c2 = b;
+    }
+
+}
diff --git a/src/main/java/toxi/math/CircularInterpolation.java b/src/main/java/toxi/math/CircularInterpolation.java
new file mode 100644
index 0000000..b4cf6b2
--- /dev/null
+++ b/src/main/java/toxi/math/CircularInterpolation.java
@@ -0,0 +1,76 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math;
+
+/**
+ * Implementation of the circular interpolation function.
+ * 
+ * i = a-(b-a) * (sqrt(1 - (1 - f) * (1 - f) ))
+ */
+public class CircularInterpolation implements InterpolateStrategy {
+
+    protected boolean isFlipped;
+
+    public CircularInterpolation() {
+        this(false);
+    }
+
+    /**
+     * The interpolation slope can be flipped to have its steepest ascent
+     * towards the end value, rather than at the beginning in the default
+     * configuration.
+     * 
+     * @param isFlipped
+     *            true, if slope is inverted
+     */
+    public CircularInterpolation(boolean isFlipped) {
+        this.isFlipped = isFlipped;
+    }
+
+    public double interpolate(double a, double b, double f) {
+        if (isFlipped) {
+            return a - (b - a) * (Math.sqrt(1 - f * f) - 1);
+        } else {
+            f = 1 - f;
+            return a + (b - a) * (Math.sqrt(1 - f * f));
+        }
+    }
+
+    public float interpolate(float a, float b, float f) {
+        if (isFlipped) {
+            return a - (b - a) * ((float) Math.sqrt(1 - f * f) - 1);
+        } else {
+            f = 1 - f;
+            return a + (b - a) * ((float) Math.sqrt(1 - f * f));
+        }
+    }
+
+    public void setFlipped(boolean isFlipped) {
+        this.isFlipped = isFlipped;
+    }
+}
diff --git a/src/main/java/toxi/math/CosineInterpolation.java b/src/main/java/toxi/math/CosineInterpolation.java
new file mode 100644
index 0000000..a071fd1
--- /dev/null
+++ b/src/main/java/toxi/math/CosineInterpolation.java
@@ -0,0 +1,50 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math;
+
+/**
+ * Implementation of the cosine interpolation function:
+ * 
+ * i = b+(a-b)*(0.5+0.5*cos(f*PI))
+ */
+public class CosineInterpolation implements InterpolateStrategy {
+
+    public double interpolate(double a, double b, double f) {
+        return b + (a - b) * (0.5 + 0.5 * Math.cos(f * Math.PI));
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.math.InterpolateStrategy#interpolate(float, float, float)
+     */
+    public final float interpolate(float a, float b, float f) {
+        return b + (a - b) * (float) (0.5 + 0.5 * Math.cos(f * MathUtils.PI));
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/toxi/math/DecimatedInterpolation.java b/src/main/java/toxi/math/DecimatedInterpolation.java
new file mode 100644
index 0000000..b15229d
--- /dev/null
+++ b/src/main/java/toxi/math/DecimatedInterpolation.java
@@ -0,0 +1,60 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math;
+
+/**
+ * Delivers a number of decimated/stepped values for a given interval. E.g. by
+ * using 5 steps the interpolation factor is decimated to: 0, 20, 40, 60, 80 and
+ * 100%. By default {@link LinearInterpolation} is used, however any other
+ * {@link InterpolateStrategy} can be specified via the constructor.
+ */
+public class DecimatedInterpolation implements InterpolateStrategy {
+
+    public int numSteps;
+    public InterpolateStrategy strategy;
+
+    public DecimatedInterpolation(int steps) {
+        this(steps, new LinearInterpolation());
+    }
+
+    public DecimatedInterpolation(int steps, InterpolateStrategy strategy) {
+        this.numSteps = steps;
+        this.strategy = strategy;
+    }
+
+    public double interpolate(double a, double b, double f) {
+        double fd = (int) (f * numSteps) / (double) numSteps;
+        return strategy.interpolate(a, b, fd);
+    }
+
+    public float interpolate(float a, float b, float f) {
+        float fd = (int) (f * numSteps) / (float) numSteps;
+        return strategy.interpolate(a, b, fd);
+    }
+
+}
diff --git a/src/main/java/toxi/math/ExponentialInterpolation.java b/src/main/java/toxi/math/ExponentialInterpolation.java
new file mode 100644
index 0000000..cf41eeb
--- /dev/null
+++ b/src/main/java/toxi/math/ExponentialInterpolation.java
@@ -0,0 +1,66 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math;
+
+/**
+ * Exponential curve interpolation with adjustable exponent. Use exp in the
+ * following ranges to achieve these effects:
+ * <ul>
+ * <li>0.0 &lt; x &lt; 1.0 : ease in (steep changes towards b)</li>
+ * <li>1.0 : same as {@link LinearInterpolation}</li>
+ * <li>&gt; 1.0 : ease-out (steep changes from a)</li>
+ * </ul>
+ */
+public class ExponentialInterpolation implements InterpolateStrategy {
+
+    private float exponent;
+
+    /**
+     * Default constructor uses square parabola (exp=2)
+     */
+    public ExponentialInterpolation() {
+        this(2);
+    }
+
+    /**
+     * @param exp
+     *            curve exponent
+     */
+    public ExponentialInterpolation(float exp) {
+        this.exponent = exp;
+    }
+
+    public double interpolate(double a, double b, double f) {
+        return a + (b - a) * Math.pow(f, exponent);
+    }
+
+    public float interpolate(float a, float b, float f) {
+        return a + (b - a) * (float) Math.pow(f, exponent);
+    }
+
+}
diff --git a/src/main/java/toxi/math/InterpolateStrategy.java b/src/main/java/toxi/math/InterpolateStrategy.java
new file mode 100644
index 0000000..1ffdc0b
--- /dev/null
+++ b/src/main/java/toxi/math/InterpolateStrategy.java
@@ -0,0 +1,60 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math;
+
+/**
+ * Defines a generic function to interpolate 2 float values.
+ */
+public interface InterpolateStrategy {
+
+    /**
+     * Implements an interpolation equation using double precision values.
+     * 
+     * @param a
+     *            current value
+     * @param b
+     *            target value
+     * @param f
+     *            normalized interpolation factor (0.0 .. 1.0)
+     * @return interpolated value
+     */
+    public double interpolate(double a, double b, double f);
+
+    /**
+     * Implements an interpolation equation using float values.
+     * 
+     * @param a
+     *            current value
+     * @param b
+     *            target value
+     * @param f
+     *            normalized interpolation factor (0.0 .. 1.0)
+     * @return interpolated value
+     */
+    public float interpolate(float a, float b, float f);
+}
diff --git a/src/main/java/toxi/math/Interpolation2D.java b/src/main/java/toxi/math/Interpolation2D.java
new file mode 100644
index 0000000..9d81f7d
--- /dev/null
+++ b/src/main/java/toxi/math/Interpolation2D.java
@@ -0,0 +1,93 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math;
+
+import toxi.geom.Vec2D;
+
+/**
+ * Implementations of 2D interpolation functions (currently only bilinear).
+ */
+public class Interpolation2D {
+
+    /**
+     * @param x
+     *            x coord of point to filter
+     * @param y
+     *            y coord of point to filter
+     * @param x1
+     *            x coord of top-left corner
+     * @param y1
+     *            y coord of top-left corner
+     * @param x2
+     *            x coord of bottom-right corner
+     * @param y2
+     *            y coord of bottom-right corner
+     * @param tl
+     *            top-left value
+     * @param tr
+     *            top-right value
+     * @param bl
+     *            bottom-left value
+     * @param br
+     *            bottom-right value
+     * @return interpolated value
+     */
+    public static float bilinear(double x, double y, double x1, double y1,
+            double x2, double y2, float tl, float tr, float bl, float br) {
+        double denom = 1.0 / ((x2 - x1) * (y2 - y1));
+        double dx1 = (x - x1) * denom;
+        double dx2 = (x2 - x) * denom;
+        double dy1 = y - y1;
+        double dy2 = y2 - y;
+        return (float) (tl * dx2 * dy2 + tr * dx1 * dy2 + bl * dx2 * dy1 + br
+                * dx1 * dy1);
+    }
+
+    /**
+     * @param p
+     *            point to filter
+     * @param p1
+     *            top-left corner
+     * @param p2
+     *            bottom-right corner
+     * @param tl
+     *            top-left value
+     * @param tr
+     *            top-right value
+     * @param bl
+     *            bottom-left value
+     * @param br
+     *            bottom-right value
+     * @return interpolated value
+     */
+    public static float bilinear(Vec2D p, Vec2D p1, Vec2D p2, float tl,
+            float tr, float bl, float br) {
+        return bilinear(p.x, p.y, p1.x, p1.y, p2.x, p2.y, tl, tr, bl, br);
+    }
+
+}
diff --git a/src/main/java/toxi/math/LinearInterpolation.java b/src/main/java/toxi/math/LinearInterpolation.java
new file mode 100644
index 0000000..665a19d
--- /dev/null
+++ b/src/main/java/toxi/math/LinearInterpolation.java
@@ -0,0 +1,44 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math;
+
+/**
+ * Implementation of the linear interpolation function
+ * 
+ * i = a + ( b - a ) * f
+ */
+public class LinearInterpolation implements InterpolateStrategy {
+
+    public double interpolate(double a, double b, double f) {
+        return a + (b - a) * f;
+    }
+
+    public final float interpolate(float a, float b, float f) {
+        return a + (b - a) * f;
+    }
+}
diff --git a/src/main/java/com/syncleus/spangraph/geom/MathUtils.java b/src/main/java/toxi/math/MathUtils.java
similarity index 99%
rename from src/main/java/com/syncleus/spangraph/geom/MathUtils.java
rename to src/main/java/toxi/math/MathUtils.java
index 63d02c8..ee12397 100644
--- a/src/main/java/com/syncleus/spangraph/geom/MathUtils.java
+++ b/src/main/java/toxi/math/MathUtils.java
@@ -25,7 +25,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-package com.syncleus.spangraph.geom;
+package toxi.math;
 
 import java.util.Random;
 
diff --git a/src/main/java/toxi/math/NonLinearScaleMap.java b/src/main/java/toxi/math/NonLinearScaleMap.java
new file mode 100644
index 0000000..b93e8aa
--- /dev/null
+++ b/src/main/java/toxi/math/NonLinearScaleMap.java
@@ -0,0 +1,60 @@
+package toxi.math;
+
+import java.util.NavigableSet;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+public class NonLinearScaleMap {
+
+    public class Sample implements Comparable<Sample> {
+
+        public final double x, y;
+
+        public Sample(double x, double y) {
+            this.x = x;
+            this.y = y;
+        }
+
+        public int compareTo(Sample b) {
+            return (int) Math.signum(x - b.x);
+        }
+    }
+
+    private TreeSet<Sample> samples;
+
+    private double rangeMin = Float.MAX_VALUE;
+    private double rangeMax = Float.MIN_VALUE;
+
+    public NonLinearScaleMap() {
+        samples = new TreeSet<Sample>();
+    }
+
+    public NonLinearScaleMap addSample(double x, double y) {
+        samples.add(new Sample(x, y));
+        rangeMin = MathUtils.min(y, rangeMin);
+        rangeMax = MathUtils.max(y, rangeMax);
+        return this;
+    }
+
+    public NavigableSet<Sample> getSamples() {
+        return samples;
+    }
+
+    public double map(double x) {
+        Sample t = new Sample(x, 0);
+        SortedSet<Sample> aset = samples.headSet(t);
+        SortedSet<Sample> bset = samples.tailSet(t);
+        if (aset.isEmpty()) {
+            return bset.first().y;
+        } else {
+            if (bset.isEmpty()) {
+                return aset.last().y;
+            } else {
+                Sample a = aset.last();
+                Sample b = bset.first();
+                return MathUtils.lerp(a.y, b.y, (x - a.x) / (b.x - a.x));
+            }
+        }
+    }
+
+}
diff --git a/src/main/java/toxi/math/ScaleMap.java b/src/main/java/toxi/math/ScaleMap.java
new file mode 100644
index 0000000..e858c08
--- /dev/null
+++ b/src/main/java/toxi/math/ScaleMap.java
@@ -0,0 +1,167 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math;
+
+import toxi.util.datatypes.DoubleRange;
+
+/**
+ * This class maps values from one interval into another. By default the mapping
+ * is using linear projection, but can be changed by using alternative
+ * {@link toxi.math.InterpolateStrategy} implementations to achieve a
+ * non-regular mapping.
+ */
+public class ScaleMap {
+
+    /**
+     * The actual mapping behaviour function.
+     * 
+     * @see toxi.math.InterpolateStrategy
+     */
+    protected InterpolateStrategy mapFunction = new LinearInterpolation();
+
+    protected double interval;
+    protected double mapRange;
+
+    protected DoubleRange in, out;
+
+    /**
+     * Creates a new instance to map values between the 2 number ranges
+     * specified. By default linear projection is used.
+     * 
+     * @param minIn
+     * @param maxIn
+     * @param minOut
+     * @param maxOut
+     */
+    public ScaleMap(double minIn, double maxIn, double minOut, double maxOut) {
+        setInputRange(minIn, maxIn);
+        setOutputRange(minOut, maxOut);
+    }
+
+    /**
+     * Computes mapped value in the target interval and ensures the input value
+     * is clipped to source interval.
+     * 
+     * @param val
+     * @return mapped value
+     */
+    public double getClippedValueFor(double val) {
+        double t = MathUtils.clipNormalized((val - in.min) / interval);
+        if (Double.isNaN(t)) {
+            t = 0;
+        }
+        return mapFunction.interpolate(out.min, out.max, t);
+    }
+
+    /**
+     * @return the middle value of the input range.
+     */
+    public double getInputMedian() {
+        return (in.min + in.max) * 0.5;
+    }
+
+    /**
+     * @return the in
+     */
+    public DoubleRange getInputRange() {
+        return in;
+    }
+
+    /**
+     * @return the mapped middle value of the output range. Depending on the
+     *         mapping function used, this value might be different to the one
+     *         returned by {@link #getOutputMedian()}.
+     */
+    public double getMappedMedian() {
+        return getMappedValueFor(0.5);
+    }
+
+    /**
+     * Computes mapped value in the target interval. Does check if input value
+     * is outside the input range.
+     * 
+     * @param val
+     * @return mapped value
+     */
+    public double getMappedValueFor(double val) {
+        double t = ((val - in.min) / interval);
+        if (Double.isNaN(t)) {
+            t = 0;
+        }
+        return mapFunction.interpolate(out.min, out.max, t);
+    }
+
+    /**
+     * @return the middle value of the output range
+     */
+    public double getOutputMedian() {
+        return (out.min + out.max) * 0.5;
+    }
+
+    /**
+     * @return the output range
+     */
+    public DoubleRange getOutputRange() {
+        return out;
+    }
+
+    /**
+     * Sets new minimum & maximum values for the input range
+     * 
+     * @param min
+     * @param max
+     */
+    public void setInputRange(double min, double max) {
+        in = new DoubleRange(min, max);
+        interval = max - min;
+    }
+
+    /**
+     * Overrides the mapping function used for the scale conversion. By default
+     * a linear mapping is used: {@link LinearInterpolation}.
+     * 
+     * @param func
+     *            interpolate strategy implementation
+     */
+    public void setMapFunction(InterpolateStrategy func) {
+        mapFunction = func;
+    }
+
+    /**
+     * Sets new minimum & maximum values for the output/target range
+     * 
+     * @param min
+     *            new min output value
+     * @param max
+     *            new max output value
+     */
+    public void setOutputRange(double min, double max) {
+        out = new DoubleRange(min, max);
+        mapRange = max - min;
+    }
+}
diff --git a/src/main/java/toxi/math/SigmoidInterpolation.java b/src/main/java/toxi/math/SigmoidInterpolation.java
new file mode 100644
index 0000000..46890f6
--- /dev/null
+++ b/src/main/java/toxi/math/SigmoidInterpolation.java
@@ -0,0 +1,68 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math;
+
+/**
+ * Implements the sigmoid interpolation function with adjustable curve sharpness
+ */
+public class SigmoidInterpolation implements InterpolateStrategy {
+
+    private float sharpness;
+
+    private float sharpPremult;
+
+    /**
+     * Initializes the s-curve with default sharpness = 2
+     */
+    public SigmoidInterpolation() {
+        this(2f);
+    }
+
+    public SigmoidInterpolation(float s) {
+        setSharpness(s);
+    }
+
+    public float getSharpness() {
+        return sharpness;
+    }
+
+    public double interpolate(double a, double b, double f) {
+        f = 1.0 + Math.exp(-((f * 2 - 1) * sharpPremult));
+        return a + (b - a) / f;
+    }
+
+    public float interpolate(float a, float b, float f) {
+        f = (float) (1.0 + Math.exp(-((f * 2 - 1) * sharpPremult)));
+        return a + (b - a) / f;
+    }
+
+    private void setSharpness(float s) {
+        sharpness = s;
+        sharpPremult = 5 * s;
+    }
+}
diff --git a/src/main/java/toxi/math/SinCosLUT.java b/src/main/java/toxi/math/SinCosLUT.java
new file mode 100644
index 0000000..efad551
--- /dev/null
+++ b/src/main/java/toxi/math/SinCosLUT.java
@@ -0,0 +1,118 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math;
+
+/**
+ * Lookup table for fast sine & cosine computations. Tables with varying
+ * precisions can be created to which input angles will be rounded to. The
+ * sin/cos methods can be used with both positive and negative input angles as
+ * with the normal Math.sin()/Math.cos() versions.
+ */
+public final class SinCosLUT {
+
+    /**
+     * default precision
+     */
+    public static final float DEFAULT_PRECISION = 0.25f;
+
+    private static SinCosLUT DEFAULT_INSTANCE;
+
+    public static final SinCosLUT getDefaultInstance() {
+        if (DEFAULT_INSTANCE == null) {
+            DEFAULT_INSTANCE = new SinCosLUT();
+        }
+        return DEFAULT_INSTANCE;
+    }
+
+    /**
+     * Lookup table for sine values
+     */
+    private final float[] sinLUT;
+
+    private final float precision;
+
+    private final int period;
+    private final int quadrant;
+
+    private final float deg2rad;
+    private final float rad2deg;
+
+    public SinCosLUT() {
+        this(DEFAULT_PRECISION);
+    }
+
+    public SinCosLUT(float precision) {
+        this.precision = precision;
+        this.period = (int) (360 / precision);
+        this.quadrant = period >> 2;
+        this.deg2rad = (float) (Math.PI / 180.0) * precision;
+        this.rad2deg = (float) (180.0 / Math.PI) / precision;
+        this.sinLUT = new float[period];
+        for (int i = 0; i < period; i++) {
+            sinLUT[i] = (float) Math.sin(i * deg2rad);
+        }
+    }
+
+    /**
+     * Calculate cosine for the passed in angle in radians.
+     * 
+     * @param theta
+     * @return cosine value for theta
+     */
+    public final float cos(float theta) {
+        while (theta < 0) {
+            theta += MathUtils.TWO_PI;
+        }
+        return sinLUT[((int) (theta * rad2deg) + quadrant) % period];
+    }
+
+    public int getPeriod() {
+        return period;
+    }
+
+    public float getPrecision() {
+        return precision;
+    }
+
+    public float[] getSinLUT() {
+        return sinLUT;
+    }
+
+    /**
+     * Calculates sine for the passed angle in radians.
+     * 
+     * @param theta
+     * @return sine value for theta
+     */
+    public final float sin(float theta) {
+        while (theta < 0) {
+            theta += MathUtils.TWO_PI;
+        }
+        return sinLUT[(int) (theta * rad2deg) % period];
+    }
+}
diff --git a/src/main/java/toxi/math/ThresholdInterpolation.java b/src/main/java/toxi/math/ThresholdInterpolation.java
new file mode 100644
index 0000000..5369717
--- /dev/null
+++ b/src/main/java/toxi/math/ThresholdInterpolation.java
@@ -0,0 +1,49 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math;
+
+/**
+ * Defines a single step/threshold function which returns the min value for all
+ * factors &lt; threshold and the max value for all others.
+ */
+public class ThresholdInterpolation implements InterpolateStrategy {
+
+    public float threshold;
+
+    public ThresholdInterpolation(float threshold) {
+        this.threshold = threshold;
+    }
+
+    public double interpolate(double a, double b, double f) {
+        return f < threshold ? a : b;
+    }
+
+    public float interpolate(float a, float b, float f) {
+        return f < threshold ? a : b;
+    }
+}
diff --git a/src/main/java/toxi/math/ZoomLensInterpolation.java b/src/main/java/toxi/math/ZoomLensInterpolation.java
new file mode 100644
index 0000000..3bfa876
--- /dev/null
+++ b/src/main/java/toxi/math/ZoomLensInterpolation.java
@@ -0,0 +1,91 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math;
+
+/**
+ * This class provides an adjustable zoom lens to either bundle or dilate values
+ * around a focal point within a given interval. For a example use cases, please
+ * have a look at the provided ScaleMapDataViz and ZoomLens examples.
+ */
+public class ZoomLensInterpolation implements InterpolateStrategy {
+
+    protected CircularInterpolation leftImpl = new CircularInterpolation();
+    protected CircularInterpolation rightImpl = new CircularInterpolation();
+
+    protected float lensPos;
+    protected float lensStrength, absStrength;
+
+    public ZoomLensInterpolation() {
+        this(0.5f, 1);
+    }
+
+    public ZoomLensInterpolation(float lensPos, float lensStrength) {
+        this.lensPos = lensPos;
+        this.lensStrength = lensStrength;
+        this.absStrength = MathUtils.abs(lensStrength);
+        leftImpl.setFlipped(lensStrength > 0);
+        rightImpl.setFlipped(lensStrength < 0);
+    }
+
+    public double interpolate(double min, double max, double t) {
+        double val = min + (max - min) * t;
+        if (t < lensPos) {
+            val += (leftImpl.interpolate(min, min + (max - min) * lensPos, t
+                    / lensPos) - val)
+                    * absStrength;
+        } else {
+            val += (rightImpl.interpolate(min + (max - min) * lensPos, max,
+                    (t - lensPos) / (1 - lensPos)) - val) * absStrength;
+        }
+        return val;
+    }
+
+    public float interpolate(float min, float max, float t) {
+        float val = min + (max - min) * t;
+        if (t < lensPos) {
+            val += (leftImpl.interpolate(min, min + (max - min) * lensPos, t
+                    / lensPos) - val)
+                    * absStrength;
+        } else {
+            val += (rightImpl.interpolate(min + (max - min) * lensPos, max,
+                    (t - lensPos) / (1 - lensPos)) - val) * absStrength;
+        }
+        return val;
+    }
+
+    public void setLensPos(float pos, float smooth) {
+        lensPos += (MathUtils.clipNormalized(pos) - lensPos) * smooth;
+    }
+
+    public void setLensStrength(float str, float smooth) {
+        lensStrength += (MathUtils.clip(str, -1, 1) - lensStrength) * smooth;
+        absStrength = MathUtils.abs(lensStrength);
+        leftImpl.setFlipped(lensStrength > 0);
+        rightImpl.setFlipped(lensStrength < 0);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/toxi/math/conversion/UnitTranslator.java b/src/main/java/toxi/math/conversion/UnitTranslator.java
new file mode 100644
index 0000000..13fd907
--- /dev/null
+++ b/src/main/java/toxi/math/conversion/UnitTranslator.java
@@ -0,0 +1,157 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math.conversion;
+
+public class UnitTranslator {
+
+    /**
+     * Number of millimeters per inch
+     */
+    public static final double INCH_MM = 25.4;
+
+    /**
+     * Number of points per inch
+     */
+    public static final double POINT_POSTSCRIPT = 72.0;
+
+    /**
+     * Converts millimeters into pixels.
+     * 
+     * @param mm
+     *            millimeters
+     * @param dpi
+     *            DPI resolution
+     * @return number of pixels
+     */
+    public static int millisToPixels(double mm, int dpi) {
+        return (int) (mm / INCH_MM * dpi);
+    }
+
+    /**
+     * Converts millimeters into PostScript points.
+     * 
+     * @param mm
+     *            millimeters
+     * @return number of points
+     */
+    public static double millisToPoints(double mm) {
+        return mm / INCH_MM * POINT_POSTSCRIPT;
+    }
+
+    /**
+     * Converts pixels into inches.
+     * 
+     * @param pix
+     *            pixels
+     * @param dpi
+     *            DPI resolution to use
+     * @return number of inches
+     */
+    public static double pixelsToInch(int pix, int dpi) {
+        return (double) pix / dpi;
+    }
+
+    /**
+     * Converts pixels into millimeters.
+     * 
+     * @param pix
+     *            pixels
+     * @param dpi
+     *            DPI resolution
+     * @return number of millimeters
+     */
+    public static double pixelsToMillis(int pix, int dpi) {
+        return pixelsToInch(pix, dpi) * INCH_MM;
+    }
+
+    /**
+     * Converts pixels into points.
+     * 
+     * @param pix
+     *            pixels
+     * @param dpi
+     *            DPI resolution
+     * @return number of points
+     */
+    public static double pixelsToPoints(int pix, int dpi) {
+        return pixelsToInch(pix, dpi) * POINT_POSTSCRIPT;
+    }
+
+    /**
+     * Converts points into millimeters.
+     * 
+     * @param pt
+     * @return number of millimeters
+     */
+    public static double pointsToMillis(double pt) {
+        return pt / POINT_POSTSCRIPT * INCH_MM;
+    }
+
+    /**
+     * Converts points into pixels.
+     * 
+     * @param pt
+     *            points
+     * @param dpi
+     *            DPI resolution
+     * @return number of pixels
+     */
+    public static int pointsToPixels(double pt, int dpi) {
+        return millisToPixels(pointsToMillis(pt), dpi);
+    }
+
+    /**
+     * Converts an area measure in square inch to square millimeters.
+     * 
+     * @param area
+     * @return square mm
+     */
+    public static double squareInchToMillis(double area) {
+        return area * INCH_MM * INCH_MM;
+    }
+
+    /**
+     * Converts an area measure in points to square inch.
+     * 
+     * @param area
+     * @return square inch
+     */
+    public static double squarePointsToInch(double area) {
+        return area / (POINT_POSTSCRIPT * POINT_POSTSCRIPT);
+    }
+
+    /**
+     * Converts an area measure in points to square millimeters.
+     * 
+     * @param area
+     * @return square mm
+     */
+    public static double squarePointsToMillis(double area) {
+        return squareInchToMillis(squarePointsToInch(area));
+    }
+}
diff --git a/src/main/java/toxi/math/noise/PerlinNoise.java b/src/main/java/toxi/math/noise/PerlinNoise.java
new file mode 100644
index 0000000..12f9d02
--- /dev/null
+++ b/src/main/java/toxi/math/noise/PerlinNoise.java
@@ -0,0 +1,216 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math.noise;
+
+import java.util.Random;
+
+import toxi.math.SinCosLUT;
+
+/**
+ * PERLIN NOISE taken from the java port I originally did for PApplet, based on
+ * an implementation by the german demo group farbrausch as used in their demo
+ * "Art": http://www.farb-rausch.de/fr010src.zip
+ */
+public class PerlinNoise {
+
+    protected static final int PERLIN_YWRAPB = 4;
+
+    protected static final int PERLIN_YWRAP = 1 << PERLIN_YWRAPB;
+
+    protected static final int PERLIN_ZWRAPB = 8;
+
+    protected static final int PERLIN_ZWRAP = 1 << PERLIN_ZWRAPB;
+
+    protected static final int PERLIN_SIZE = 4095;
+
+    private static final float PERLIN_MIN_AMPLITUDE = 0.001f;
+
+    protected int perlin_octaves = 4; // default to medium smooth
+
+    protected float perlin_amp_falloff = 0.5f; // 50% reduction/octave
+
+    // [toxi 031112]
+    // new vars needed due to recent change of cos table in PGraphics
+    protected int perlin_TWOPI, perlin_PI;
+
+    protected float[] perlin_cosTable;
+
+    protected float perlin[];
+
+    protected Random perlinRandom;
+
+    public PerlinNoise() {
+        noiseSeed(System.nanoTime());
+    }
+
+    /**
+     * Computes the Perlin noise function value at point x.
+     */
+    public float noise(float x) {
+        // is this legit? it's a dumb way to do it (but repair it later)
+        return noise(x, 0f, 0f);
+    }
+
+    /**
+     * Computes the Perlin noise function value at the point x, y.
+     */
+    public float noise(float x, float y) {
+        return noise(x, y, 0f);
+    }
+
+    /**
+     * Computes the Perlin noise function value at x, y, z.
+     */
+    public float noise(float x, float y, float z) {
+        if (perlin == null) {
+            if (perlinRandom == null) {
+                perlinRandom = new Random();
+            }
+            perlin = new float[PERLIN_SIZE + 1];
+            for (int i = 0; i < PERLIN_SIZE + 1; i++) {
+                perlin[i] = perlinRandom.nextFloat(); // (float)Math.random();
+            }
+            // [toxi 031112]
+            // noise broke due to recent change of cos table in PGraphics
+            // this will take care of it
+            perlin_cosTable = SinCosLUT.getDefaultInstance().getSinLUT();
+            perlin_TWOPI = perlin_PI = SinCosLUT.getDefaultInstance()
+                    .getPeriod();
+            perlin_PI >>= 1;
+        }
+
+        if (x < 0) {
+            x = -x;
+        }
+        if (y < 0) {
+            y = -y;
+        }
+        if (z < 0) {
+            z = -z;
+        }
+
+        int xi = (int) x, yi = (int) y, zi = (int) z;
+        float xf = (x - xi);
+        float yf = (y - yi);
+        float zf = (z - zi);
+        float rxf, ryf;
+
+        float r = 0;
+        float ampl = 0.5f;
+
+        float n1, n2, n3;
+
+        for (int i = 0; i < perlin_octaves; i++) {
+            int of = xi + (yi << PERLIN_YWRAPB) + (zi << PERLIN_ZWRAPB);
+
+            rxf = noise_fsc(xf);
+            ryf = noise_fsc(yf);
+
+            n1 = perlin[of & PERLIN_SIZE];
+            n1 += rxf * (perlin[(of + 1) & PERLIN_SIZE] - n1);
+            n2 = perlin[(of + PERLIN_YWRAP) & PERLIN_SIZE];
+            n2 += rxf * (perlin[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n2);
+            n1 += ryf * (n2 - n1);
+
+            of += PERLIN_ZWRAP;
+            n2 = perlin[of & PERLIN_SIZE];
+            n2 += rxf * (perlin[(of + 1) & PERLIN_SIZE] - n2);
+            n3 = perlin[(of + PERLIN_YWRAP) & PERLIN_SIZE];
+            n3 += rxf * (perlin[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n3);
+            n2 += ryf * (n3 - n2);
+
+            n1 += noise_fsc(zf) * (n2 - n1);
+
+            r += n1 * ampl;
+            ampl *= perlin_amp_falloff;
+
+            // break if amp has no more impact
+            if (ampl < PERLIN_MIN_AMPLITUDE) {
+                break;
+            }
+
+            xi <<= 1;
+            xf *= 2;
+            yi <<= 1;
+            yf *= 2;
+            zi <<= 1;
+            zf *= 2;
+
+            if (xf >= 1.0f) {
+                xi++;
+                xf--;
+            }
+            if (yf >= 1.0f) {
+                yi++;
+                yf--;
+            }
+            if (zf >= 1.0f) {
+                zi++;
+                zf--;
+            }
+        }
+        return r;
+    }
+
+    // [toxi 031112]
+    // now adjusts to the size of the cosLUT used via
+    // the new variables, defined above
+    private float noise_fsc(float i) {
+        // using bagel's cosine table instead
+        return 0.5f * (1.0f - perlin_cosTable[(int) ((i + 0.5f) * perlin_PI)
+                % perlin_TWOPI]);
+    }
+
+    // [toxi 040903]
+    // make perlin noise quality user controlled to allow
+    // for different levels of detail. lower values will produce
+    // smoother results as higher octaves are surpressed
+
+    public void noiseDetail(int lod) {
+        if (lod > 0) {
+            perlin_octaves = lod;
+        }
+    }
+
+    public void noiseDetail(int lod, float falloff) {
+        if (lod > 0) {
+            perlin_octaves = lod;
+        }
+        if (falloff > 0) {
+            perlin_amp_falloff = falloff;
+        }
+    }
+
+    public void noiseSeed(long what) {
+        if (perlinRandom == null) {
+            perlinRandom = new Random();
+        }
+        perlinRandom.setSeed(what);
+        perlin = null;
+    }
+}
diff --git a/src/main/java/toxi/math/noise/SimplexNoise.java b/src/main/java/toxi/math/noise/SimplexNoise.java
new file mode 100644
index 0000000..25b2beb
--- /dev/null
+++ b/src/main/java/toxi/math/noise/SimplexNoise.java
@@ -0,0 +1,542 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math.noise;
+
+/**
+ * Simplex Noise in 2D, 3D and 4D. Based on the example code of this paper:
+ * http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
+ * 
+ * @author Stefan Gustavson, Linkping University, Sweden (stegu at itn dot liu
+ *         dot se)
+ * @author Karsten Schmidt (slight optimizations & restructuring)
+ */
+public class SimplexNoise {
+
+    private static final double SQRT3 = Math.sqrt(3.0);
+    private static final double SQRT5 = Math.sqrt(5.0);
+
+    /**
+     * Skewing and unskewing factors for 2D, 3D and 4D, some of them
+     * pre-multiplied.
+     */
+    private static final double F2 = 0.5 * (SQRT3 - 1.0);
+    private static final double G2 = (3.0 - SQRT3) / 6.0;
+    private static final double G22 = G2 * 2.0 - 1;
+
+    private static final double F3 = 1.0 / 3.0;
+    private static final double G3 = 1.0 / 6.0;
+
+    private static final double F4 = (SQRT5 - 1.0) / 4.0;
+    private static final double G4 = (5.0 - SQRT5) / 20.0;
+    private static final double G42 = G4 * 2.0;
+    private static final double G43 = G4 * 3.0;
+    private static final double G44 = G4 * 4.0 - 1.0;
+
+    /**
+     * Gradient vectors for 3D (pointing to mid points of all edges of a unit
+     * cube)
+     */
+    private static final int[][] grad3 = { { 1, 1, 0 }, { -1, 1, 0 },
+            { 1, -1, 0 }, { -1, -1, 0 }, { 1, 0, 1 }, { -1, 0, 1 },
+            { 1, 0, -1 }, { -1, 0, -1 }, { 0, 1, 1 }, { 0, -1, 1 },
+            { 0, 1, -1 }, { 0, -1, -1 } };
+
+    /**
+     * Gradient vectors for 4D (pointing to mid points of all edges of a unit 4D
+     * hypercube)
+     */
+    private static final int[][] grad4 = { { 0, 1, 1, 1 }, { 0, 1, 1, -1 },
+            { 0, 1, -1, 1 }, { 0, 1, -1, -1 }, { 0, -1, 1, 1 },
+            { 0, -1, 1, -1 }, { 0, -1, -1, 1 }, { 0, -1, -1, -1 },
+            { 1, 0, 1, 1 }, { 1, 0, 1, -1 }, { 1, 0, -1, 1 }, { 1, 0, -1, -1 },
+            { -1, 0, 1, 1 }, { -1, 0, 1, -1 }, { -1, 0, -1, 1 },
+            { -1, 0, -1, -1 }, { 1, 1, 0, 1 }, { 1, 1, 0, -1 },
+            { 1, -1, 0, 1 }, { 1, -1, 0, -1 }, { -1, 1, 0, 1 },
+            { -1, 1, 0, -1 }, { -1, -1, 0, 1 }, { -1, -1, 0, -1 },
+            { 1, 1, 1, 0 }, { 1, 1, -1, 0 }, { 1, -1, 1, 0 }, { 1, -1, -1, 0 },
+            { -1, 1, 1, 0 }, { -1, 1, -1, 0 }, { -1, -1, 1, 0 },
+            { -1, -1, -1, 0 } };
+
+    /**
+     * Permutation table
+     */
+    private static final int[] p = { 151, 160, 137, 91, 90, 15, 131, 13, 201,
+            95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37,
+            240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62,
+            94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56,
+            87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139,
+            48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133,
+            230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25,
+            63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200,
+            196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3,
+            64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255,
+            82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42,
+            223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153,
+            101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79,
+            113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242,
+            193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249,
+            14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204,
+            176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222,
+            114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 };
+
+    /**
+     * To remove the need for index wrapping, double the permutation table
+     * length
+     */
+    private static int[] perm = new int[0x200];
+    /**
+     * A lookup table to traverse the simplex around a given point in 4D.
+     * Details can be found where this table is used, in the 4D noise method.
+     */
+    private static final int[][] simplex = { { 0, 1, 2, 3 }, { 0, 1, 3, 2 },
+            { 0, 0, 0, 0 }, { 0, 2, 3, 1 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+            { 0, 0, 0, 0 }, { 1, 2, 3, 0 }, { 0, 2, 1, 3 }, { 0, 0, 0, 0 },
+            { 0, 3, 1, 2 }, { 0, 3, 2, 1 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+            { 0, 0, 0, 0 }, { 1, 3, 2, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+            { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+            { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 2, 0, 3 }, { 0, 0, 0, 0 },
+            { 1, 3, 0, 2 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+            { 2, 3, 0, 1 }, { 2, 3, 1, 0 }, { 1, 0, 2, 3 }, { 1, 0, 3, 2 },
+            { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 2, 0, 3, 1 },
+            { 0, 0, 0, 0 }, { 2, 1, 3, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+            { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+            { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 2, 0, 1, 3 }, { 0, 0, 0, 0 },
+            { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 3, 0, 1, 2 }, { 3, 0, 2, 1 },
+            { 0, 0, 0, 0 }, { 3, 1, 2, 0 }, { 2, 1, 0, 3 }, { 0, 0, 0, 0 },
+            { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 3, 1, 0, 2 }, { 0, 0, 0, 0 },
+            { 3, 2, 0, 1 }, { 3, 2, 1, 0 } };
+
+    static {
+        for (int i = 0; i < 0x200; i++) {
+            perm[i] = p[i & 0xff];
+        }
+    }
+
+    /**
+     * Computes dot product in 2D.
+     * 
+     * @param g
+     *            2-vector (grid offset)
+     * @param x
+     * @param y
+     * @return dot product
+     */
+    private static double dot(int g[], double x, double y) {
+        return g[0] * x + g[1] * y;
+    }
+
+    /**
+     * Computes dot product in 3D.
+     * 
+     * @param g
+     *            3-vector (grid offset)
+     * @param x
+     * @param y
+     * @param z
+     * @return dot product
+     */
+    private static double dot(int g[], double x, double y, double z) {
+        return g[0] * x + g[1] * y + g[2] * z;
+    }
+
+    /**
+     * Computes dot product in 4D.
+     * 
+     * @param g
+     *            4-vector (grid offset)
+     * @param x
+     * @param y
+     * @param z
+     * @param w
+     * @return dot product
+     */
+    private static double dot(int g[], double x, double y, double z, double w) {
+        return g[0] * x + g[1] * y + g[2] * z + g[3] * w;
+    }
+
+    /**
+     * This method is a *lot* faster than using (int)Math.floor(x).
+     * 
+     * @param x
+     *            value to be floored
+     * @return
+     */
+    private static final int fastfloor(double x) {
+        return x >= 0 ? (int) x : (int) x - 1;
+    }
+
+    /**
+     * Computes 2D Simplex Noise.
+     * 
+     * @param x
+     *            coordinate
+     * @param y
+     *            coordinate
+     * @return noise value in range -1 ... +1.
+     */
+    public static double noise(double x, double y) {
+        double n0 = 0, n1 = 0, n2 = 0; // Noise contributions from the three
+        // corners
+        // Skew the input space to determine which simplex cell we're in
+        double s = (x + y) * F2; // Hairy factor for 2D
+        int i = fastfloor(x + s);
+        int j = fastfloor(y + s);
+        double t = (i + j) * G2;
+        double x0 = x - (i - t); // The x,y distances from the cell origin
+        double y0 = y - (j - t);
+        // For the 2D case, the simplex shape is an equilateral triangle.
+        // Determine which simplex we are in.
+        int i1, j1; // Offsets for second (middle) corner of simplex in (i,j)
+        if (x0 > y0) {
+            i1 = 1;
+            j1 = 0;
+        } // lower triangle, XY order: (0,0)->(1,0)->(1,1)
+        else {
+            i1 = 0;
+            j1 = 1;
+        } // upper triangle, YX order: (0,0)->(0,1)->(1,1)
+          // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
+          // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
+          // c = (3-sqrt(3))/6
+        double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed
+        double y1 = y0 - j1 + G2;
+        double x2 = x0 + G22; // Offsets for last corner in (x,y) unskewed
+        double y2 = y0 + G22;
+        // Work out the hashed gradient indices of the three simplex corners
+        int ii = i & 0xff;
+        int jj = j & 0xff;
+        // Calculate the contribution from the three corners
+        double t0 = 0.5 - x0 * x0 - y0 * y0;
+        if (t0 > 0) {
+            t0 *= t0;
+            int gi0 = perm[ii + perm[jj]] % 12;
+            n0 = t0 * t0 * dot(grad3[gi0], x0, y0); // (x,y) of grad3 used for
+            // 2D gradient
+        }
+        double t1 = 0.5 - x1 * x1 - y1 * y1;
+        if (t1 > 0) {
+            t1 *= t1;
+            int gi1 = perm[ii + i1 + perm[jj + j1]] % 12;
+            n1 = t1 * t1 * dot(grad3[gi1], x1, y1);
+        }
+        double t2 = 0.5 - x2 * x2 - y2 * y2;
+        if (t2 > 0) {
+            t2 *= t2;
+            int gi2 = perm[ii + 1 + perm[jj + 1]] % 12;
+            n2 = t2 * t2 * dot(grad3[gi2], x2, y2);
+        }
+        // Add contributions from each corner to get the final noise value.
+        // The result is scaled to return values in the interval [-1,1].
+        return 70.0 * (n0 + n1 + n2);
+    }
+
+    /**
+     * Computes 3D Simplex Noise.
+     * 
+     * @param x
+     *            coordinate
+     * @param y
+     *            coordinate
+     * @param z
+     *            coordinate
+     * @return noise value in range -1 ... +1
+     */
+    public static double noise(double x, double y, double z) {
+        double n0 = 0, n1 = 0, n2 = 0, n3 = 0;
+        // Noise contributions from the
+        // four corners
+        // Skew the input space to determine which simplex cell we're in
+        // final double F3 = 1.0 / 3.0;
+        double s = (x + y + z) * F3; // Very nice and simple skew factor
+        // for 3D
+        int i = fastfloor(x + s);
+        int j = fastfloor(y + s);
+        int k = fastfloor(z + s);
+        // final double G3 = 1.0 / 6.0; // Very nice and simple unskew factor,
+        // too
+        double t = (i + j + k) * G3;
+        double x0 = x - (i - t); // The x,y,z distances from the cell origin
+        double y0 = y - (j - t);
+        double z0 = z - (k - t);
+        // For the 3D case, the simplex shape is a slightly irregular
+        // tetrahedron.
+        // Determine which simplex we are in.
+        int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k)
+        // coords
+        int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
+        if (x0 >= y0) {
+            if (y0 >= z0) {
+                i1 = 1;
+                j1 = 0;
+                k1 = 0;
+                i2 = 1;
+                j2 = 1;
+                k2 = 0;
+            } // X Y Z order
+            else if (x0 >= z0) {
+                i1 = 1;
+                j1 = 0;
+                k1 = 0;
+                i2 = 1;
+                j2 = 0;
+                k2 = 1;
+            } // X Z Y order
+            else {
+                i1 = 0;
+                j1 = 0;
+                k1 = 1;
+                i2 = 1;
+                j2 = 0;
+                k2 = 1;
+            } // Z X Y order
+        } else { // x0<y0
+            if (y0 < z0) {
+                i1 = 0;
+                j1 = 0;
+                k1 = 1;
+                i2 = 0;
+                j2 = 1;
+                k2 = 1;
+            } // Z Y X order
+            else if (x0 < z0) {
+                i1 = 0;
+                j1 = 1;
+                k1 = 0;
+                i2 = 0;
+                j2 = 1;
+                k2 = 1;
+            } // Y Z X order
+            else {
+                i1 = 0;
+                j1 = 1;
+                k1 = 0;
+                i2 = 1;
+                j2 = 1;
+                k2 = 0;
+            } // Y X Z order
+        }
+        // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
+        // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z),
+        // and
+        // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z),
+        // where
+        // c = 1/6.
+        double x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
+        double y1 = y0 - j1 + G3;
+        double z1 = z0 - k1 + G3;
+
+        double x2 = x0 - i2 + F3; // Offsets for third corner in (x,y,z)
+        double y2 = y0 - j2 + F3;
+        double z2 = z0 - k2 + F3;
+
+        double x3 = x0 - 0.5; // Offsets for last corner in (x,y,z)
+        double y3 = y0 - 0.5;
+        double z3 = z0 - 0.5;
+        // Work out the hashed gradient indices of the four simplex corners
+        int ii = i & 0xff;
+        int jj = j & 0xff;
+        int kk = k & 0xff;
+
+        // Calculate the contribution from the four corners
+        double t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;
+        if (t0 > 0) {
+            t0 *= t0;
+            int gi0 = perm[ii + perm[jj + perm[kk]]] % 12;
+            n0 = t0 * t0 * dot(grad3[gi0], x0, y0, z0);
+        }
+        double t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
+        if (t1 > 0) {
+            t1 *= t1;
+            int gi1 = perm[ii + i1 + perm[jj + j1 + perm[kk + k1]]] % 12;
+            n1 = t1 * t1 * dot(grad3[gi1], x1, y1, z1);
+        }
+        double t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
+        if (t2 > 0) {
+            t2 *= t2;
+            int gi2 = perm[ii + i2 + perm[jj + j2 + perm[kk + k2]]] % 12;
+            n2 = t2 * t2 * dot(grad3[gi2], x2, y2, z2);
+        }
+        double t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
+        if (t3 > 0) {
+            t3 *= t3;
+            int gi3 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1]]] % 12;
+            n3 = t3 * t3 * dot(grad3[gi3], x3, y3, z3);
+        }
+        // Add contributions from each corner to get the final noise value.
+        // The result is scaled to stay just inside [-1,1]
+        return 32.0 * (n0 + n1 + n2 + n3);
+    }
+
+    /**
+     * Computes 4D Simplex Noise.
+     * 
+     * @param x
+     *            coordinate
+     * @param y
+     *            coordinate
+     * @param z
+     *            coordinate
+     * @param w
+     *            coordinate
+     * @return noise value in range -1 ... +1
+     */
+    public static double noise(double x, double y, double z, double w) {
+        // The skewing and unskewing factors are hairy again for the 4D case
+        double n0 = 0, n1 = 0, n2 = 0, n3 = 0, n4 = 0; // Noise contributions
+        // from the five corners
+        // Skew the (x,y,z,w) space to determine which cell of 24 simplices
+        double s = (x + y + z + w) * F4; // Factor for 4D skewing
+        int i = fastfloor(x + s);
+        int j = fastfloor(y + s);
+        int k = fastfloor(z + s);
+        int l = fastfloor(w + s);
+        double t = (i + j + k + l) * G4; // Factor for 4D unskewing
+        double x0 = x - (i - t); // The x,y,z,w distances from the cell origin
+        double y0 = y - (j - t);
+        double z0 = z - (k - t);
+        double w0 = w - (l - t);
+        // For the 4D case, the simplex is a 4D shape I won't even try to
+        // describe.
+        // To find out which of the 24 possible simplices we're in, we need to
+        // determine the magnitude ordering of x0, y0, z0 and w0.
+        // The method below is a good way of finding the ordering of x,y,z,w and
+        // then find the correct traversal order for the simplex were in.
+        // First, six pair-wise comparisons are performed between each possible
+        // pair of the four coordinates, and the results are used to add up
+        // binary bits for an integer index.
+        int c = 0;
+        if (x0 > y0) {
+            c = 0x20;
+        }
+        if (x0 > z0) {
+            c |= 0x10;
+        }
+        if (y0 > z0) {
+            c |= 0x08;
+        }
+        if (x0 > w0) {
+            c |= 0x04;
+        }
+        if (y0 > w0) {
+            c |= 0x02;
+        }
+        if (z0 > w0) {
+            c |= 0x01;
+        }
+        int i1, j1, k1, l1; // The integer offsets for the second simplex corner
+        int i2, j2, k2, l2; // The integer offsets for the third simplex corner
+        int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
+        // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some
+        // order. Many values of c will never occur, since e.g. x>y>z>w makes
+        // x<z, y<w and x<w impossible. Only the 24 indices which have non-zero
+        // entries make any sense. We use a thresholding to set the coordinates
+        // in turn from the largest magnitude. The number 3 in the "simplex"
+        // array is at the position of the largest coordinate.
+        int[] sc = simplex[c];
+        i1 = sc[0] >= 3 ? 1 : 0;
+        j1 = sc[1] >= 3 ? 1 : 0;
+        k1 = sc[2] >= 3 ? 1 : 0;
+        l1 = sc[3] >= 3 ? 1 : 0;
+        // The number 2 in the "simplex" array is at the second largest
+        // coordinate.
+        i2 = sc[0] >= 2 ? 1 : 0;
+        j2 = sc[1] >= 2 ? 1 : 0;
+        k2 = sc[2] >= 2 ? 1 : 0;
+        l2 = sc[3] >= 2 ? 1 : 0;
+        // The number 1 in the "simplex" array is at the second smallest
+        // coordinate.
+        i3 = sc[0] >= 1 ? 1 : 0;
+        j3 = sc[1] >= 1 ? 1 : 0;
+        k3 = sc[2] >= 1 ? 1 : 0;
+        l3 = sc[3] >= 1 ? 1 : 0;
+        // The fifth corner has all coordinate offsets = 1, so no need to look
+        // that up.
+        double x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w)
+        double y1 = y0 - j1 + G4;
+        double z1 = z0 - k1 + G4;
+        double w1 = w0 - l1 + G4;
+
+        double x2 = x0 - i2 + G42; // Offsets for third corner in (x,y,z,w)
+        double y2 = y0 - j2 + G42;
+        double z2 = z0 - k2 + G42;
+        double w2 = w0 - l2 + G42;
+
+        double x3 = x0 - i3 + G43; // Offsets for fourth corner in (x,y,z,w)
+        double y3 = y0 - j3 + G43;
+        double z3 = z0 - k3 + G43;
+        double w3 = w0 - l3 + G43;
+
+        double x4 = x0 + G44; // Offsets for last corner in (x,y,z,w)
+        double y4 = y0 + G44;
+        double z4 = z0 + G44;
+        double w4 = w0 + G44;
+
+        // Work out the hashed gradient indices of the five simplex corners
+        int ii = i & 0xff;
+        int jj = j & 0xff;
+        int kk = k & 0xff;
+        int ll = l & 0xff;
+
+        // Calculate the contribution from the five corners
+        double t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;
+        if (t0 > 0) {
+            t0 *= t0;
+            int gi0 = perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32;
+            n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0);
+        }
+        double t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;
+        if (t1 > 0) {
+            t1 *= t1;
+            int gi1 = perm[ii + i1
+                    + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32;
+            n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1);
+        }
+        double t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;
+        if (t2 > 0) {
+            t2 *= t2;
+            int gi2 = perm[ii + i2
+                    + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32;
+            n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2);
+        }
+        double t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;
+        if (t3 > 0) {
+            t3 *= t3;
+            int gi3 = perm[ii + i3
+                    + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32;
+            n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3);
+        }
+        double t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;
+        if (t4 > 0) {
+            t4 *= t4;
+            int gi4 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32;
+            n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4);
+        }
+        // Sum up and scale the result to cover the range [-1,1]
+        return 27.0 * (n0 + n1 + n2 + n3 + n4);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/toxi/math/waves/AMFMSineWave.java b/src/main/java/toxi/math/waves/AMFMSineWave.java
new file mode 100644
index 0000000..de6a983
--- /dev/null
+++ b/src/main/java/toxi/math/waves/AMFMSineWave.java
@@ -0,0 +1,123 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math.waves;
+
+/**
+ * <p>
+ * Amplitude and frequency modulated sine wave. Uses 2 secondary waves to
+ * modulate the shape of the main wave.
+ * </p>
+ * 
+ * <p>
+ * <strong>Note:</strong> You must NEVER call the update() method on the
+ * modulating waves.
+ * </p>
+ */
+public class AMFMSineWave extends AbstractWave {
+
+    public AbstractWave fmod;
+    public AbstractWave amod;
+
+    /**
+     * Creates a new instance from
+     * 
+     * @param phase
+     * @param freq
+     * @param fmod
+     * @param amod
+     */
+    public AMFMSineWave(float phase, float freq, AbstractWave fmod,
+            AbstractWave amod) {
+        super(phase, freq);
+        this.amod = amod;
+        this.fmod = fmod;
+    }
+
+    /**
+     * @param phase
+     * @param freq
+     * @param offset
+     * @param fmod
+     * @param amod
+     */
+    public AMFMSineWave(float phase, float freq, float offset,
+            AbstractWave fmod, AbstractWave amod) {
+        super(phase, freq, 1, offset);
+        this.amod = amod;
+        this.fmod = fmod;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.math.waves.AbstractWave#pop()
+     */
+    @Override
+    public void pop() {
+        super.pop();
+        amod.pop();
+        fmod.pop();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.math.waves.AbstractWave#push()
+     */
+    @Override
+    public void push() {
+        super.push();
+        amod.push();
+        fmod.push();
+    }
+
+    /**
+     * Resets this wave and its modulating waves as well.
+     * 
+     * @see toxi.math.waves.AbstractWave#reset()
+     */
+    public void reset() {
+        super.reset();
+        fmod.reset();
+        amod.reset();
+    }
+
+    /**
+     * Progresses the wave and updates the result value. You must NEVER call the
+     * update() method on the 2 modulating wave since this is handled
+     * automatically by this method.
+     * 
+     * @see toxi.math.waves.AbstractWave#update()
+     */
+    public float update() {
+        amp = amod.update();
+        value = amp * (float) Math.sin(phase) + offset;
+        cyclePhase(frequency + fmod.update());
+        return value;
+    }
+}
diff --git a/src/main/java/toxi/math/waves/AbstractWave.java b/src/main/java/toxi/math/waves/AbstractWave.java
new file mode 100644
index 0000000..0da01ac
--- /dev/null
+++ b/src/main/java/toxi/math/waves/AbstractWave.java
@@ -0,0 +1,202 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math.waves;
+
+import java.util.Stack;
+
+/**
+ * Abstract wave oscillator type which needs to be subclassed to implement
+ * different waveforms. Please note that the frequency unit is radians, but
+ * conversion methods to & from Hertz ({@link #hertzToRadians(float, float)})
+ * are included in this base class.
+ */
+public abstract class AbstractWave {
+
+    public static final float PI = 3.14159265358979323846f;
+    public static final float TWO_PI = 2 * PI;
+
+    /**
+     * Converts a frequency in Hertz into radians.
+     * 
+     * @param hz
+     *            frequency to convert (in Hz)
+     * @param sampleRate
+     *            sampling rate in Hz (equals period length @ 1 Hz)
+     * @return frequency in radians
+     */
+    public static final float hertzToRadians(float hz, float sampleRate) {
+        return hz / sampleRate * TWO_PI;
+    }
+
+    /**
+     * Converts a frequency from radians to Hertz.
+     * 
+     * @param f
+     *            frequency in radians
+     * @param sampleRate
+     *            sampling rate in Hz (equals period length @ 1 Hz)
+     * @return freq in Hz
+     */
+    public static final float radiansToHertz(float f, float sampleRate) {
+        return f / TWO_PI * sampleRate;
+    }
+
+    /**
+     * Current wave phase
+     */
+    public float phase;
+    public float frequency;
+    public float amp;
+    public float offset;
+    public float value;
+
+    protected float origPhase;
+    protected Stack<WaveState> stateStack;
+
+    public AbstractWave() {
+    }
+
+    /**
+     * @param phase
+     */
+    public AbstractWave(float phase) {
+        this(phase, 0, 1, 0);
+    }
+
+    /**
+     * 
+     * @param phase
+     * @param freq
+     */
+    public AbstractWave(float phase, float freq) {
+        this(phase, freq, 1, 0);
+    }
+
+    /**
+     * @param phase
+     * @param freq
+     * @param amp
+     * @param offset
+     */
+    public AbstractWave(float phase, float freq, float amp, float offset) {
+        setPhase(phase);
+        this.frequency = freq;
+        this.amp = amp;
+        this.offset = offset;
+    }
+
+    /**
+     * Ensures phase remains in the 0...TWO_PI interval.
+     * 
+     * @return current phase
+     */
+    public final float cyclePhase() {
+        phase %= TWO_PI;
+        if (phase < 0) {
+            phase += TWO_PI;
+        }
+        return phase;
+    }
+
+    /**
+     * Progresses phase and ensures it remains in the 0...TWO_PI interval.
+     * 
+     * @param freq
+     *            normalized progress frequency
+     * @return update phase value
+     */
+    public final float cyclePhase(float freq) {
+        phase = (phase + freq) % TWO_PI;
+        if (phase < 0) {
+            phase += TWO_PI;
+        }
+        return phase;
+    }
+
+    public void pop() {
+        if (stateStack == null || (stateStack != null && stateStack.empty())) {
+            throw new IllegalStateException("no wave states on stack");
+        }
+        WaveState s = stateStack.pop();
+        phase = s.phase;
+        frequency = s.frequency;
+        amp = s.amp;
+        offset = s.offset;
+    }
+
+    public void push() {
+        if (stateStack == null) {
+            stateStack = new Stack<WaveState>();
+        }
+        stateStack.push(new WaveState(phase, frequency, amp, offset));
+    }
+
+    /**
+     * Resets the wave phase to the last set phase value (via
+     * {@link #setPhase(float)}.
+     */
+    public void reset() {
+        phase = origPhase;
+    }
+
+    /**
+     * Starts the wave from a new phase. The new phase position will also be
+     * used for any later call to {{@link #reset()}
+     * 
+     * @param phase
+     *            new phase
+     */
+    public void setPhase(float phase) {
+        this.phase = phase;
+        cyclePhase();
+        this.origPhase = phase;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append(this.getClass().getName()).append(" phase: ").append(phase);
+        sb.append(" frequency: ").append(frequency);
+        sb.append(" amp: ").append(amp);
+        sb.append(" offset: ").append(offset);
+        return sb.toString();
+    }
+
+    /**
+     * Updates the wave and returns new value. Implementing classes should
+     * manually ensure the phase remains in the 0...TWO_PI interval or by
+     * calling {@link #cyclePhase()}.
+     * 
+     * @return current (newly calculated) wave value
+     */
+    public abstract float update();
+}
\ No newline at end of file
diff --git a/src/main/java/toxi/math/waves/ConstantWave.java b/src/main/java/toxi/math/waves/ConstantWave.java
new file mode 100644
index 0000000..1d45541
--- /dev/null
+++ b/src/main/java/toxi/math/waves/ConstantWave.java
@@ -0,0 +1,43 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math.waves;
+
+/**
+ * Implements a constant value as waveform.
+ */
+public class ConstantWave extends AbstractWave {
+
+    public ConstantWave(float value) {
+        super();
+        this.value = value;
+    }
+
+    public final float update() {
+        return value;
+    }
+}
diff --git a/src/main/java/toxi/math/waves/FMHarmonicSquareWave.java b/src/main/java/toxi/math/waves/FMHarmonicSquareWave.java
new file mode 100644
index 0000000..11ecf6c
--- /dev/null
+++ b/src/main/java/toxi/math/waves/FMHarmonicSquareWave.java
@@ -0,0 +1,125 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math.waves;
+
+/**
+ * <p>
+ * Frequency modulated <strong>bandwidth-limited</strong> square wave using a
+ * fourier series of harmonics. Also uses a secondary wave to modulate the
+ * frequency of the main wave.
+ * </p>
+ * 
+ * <p>
+ * <strong>Note:</strong> You must NEVER call the update() method on the
+ * modulating wave.
+ * </p>
+ */
+public class FMHarmonicSquareWave extends AbstractWave {
+
+    public AbstractWave fmod;
+
+    /**
+     * Maximum harmonics to add (make sure you stay under Nyquist freq), default
+     * = 9
+     */
+    public int maxHarmonics = 3;
+
+    public FMHarmonicSquareWave(float phase, float freq, AbstractWave fmod) {
+        super(phase, freq);
+        this.fmod = fmod;
+    }
+
+    /**
+     * Convenience constructor to create a non frequency modulated square wave
+     * 
+     * @param phase
+     * @param freq
+     *            base frequency (in radians)
+     * @param amp
+     * @param offset
+     */
+    public FMHarmonicSquareWave(float phase, float freq, float amp, float offset) {
+        this(phase, freq, amp, offset, new ConstantWave(0));
+    }
+
+    public FMHarmonicSquareWave(float phase, float freq, float amp,
+            float offset, AbstractWave fmod) {
+        super(phase, freq, amp, offset);
+        this.fmod = fmod;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.math.waves.AbstractWave#pop()
+     */
+    @Override
+    public void pop() {
+        super.pop();
+        fmod.pop();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.math.waves.AbstractWave#push()
+     */
+    @Override
+    public void push() {
+        super.push();
+        fmod.push();
+    }
+
+    /**
+     * Resets this wave and its modulating wave as well.
+     * 
+     * @see AbstractWave#reset()
+     */
+    public void reset() {
+        super.reset();
+        fmod.reset();
+    }
+
+    /**
+     * Progresses the wave and updates the result value. You must NEVER call the
+     * update() method on the modulating wave since this is handled
+     * automatically by this method.
+     * 
+     * @see AbstractWave#update()
+     */
+    public float update() {
+        value = 0;
+        for (int i = 1; i <= maxHarmonics; i += 2) {
+            value += 1.0 / i * (float) Math.sin(i * phase);
+        }
+        value *= amp;
+        value += offset;
+        cyclePhase(frequency + fmod.update());
+        return value;
+    }
+}
diff --git a/src/main/java/toxi/math/waves/FMSawtoothWave.java b/src/main/java/toxi/math/waves/FMSawtoothWave.java
new file mode 100644
index 0000000..5291863
--- /dev/null
+++ b/src/main/java/toxi/math/waves/FMSawtoothWave.java
@@ -0,0 +1,114 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math.waves;
+
+/**
+ * <p>
+ * Frequency modulated bandwidth unlimited pure sawtooth wave. Uses a secondary
+ * wave to modulate the frequency of the main wave.
+ * </p>
+ * 
+ * <p>
+ * <strong>Note:</strong> You must NEVER call the update() method on the
+ * modulating wave.
+ * </p>
+ */
+public class FMSawtoothWave extends AbstractWave {
+
+    public AbstractWave fmod;
+
+    public FMSawtoothWave(float phase, float freq, AbstractWave fmod) {
+        super(phase, freq);
+        this.fmod = fmod;
+    }
+
+    /**
+     * Convenience constructor to create a non frequency modulated sawtooth.
+     * 
+     * @param phase
+     * @param freq
+     *            base frequency (in radians)
+     * @param amp
+     * @param offset
+     */
+    public FMSawtoothWave(float phase, float freq, float amp, float offset) {
+        this(phase, freq, amp, offset, new ConstantWave(0));
+    }
+
+    public FMSawtoothWave(float phase, float freq, float amp, float offset,
+            AbstractWave fmod) {
+        super(phase, freq, amp, offset);
+        this.fmod = fmod;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.math.waves.AbstractWave#pop()
+     */
+    @Override
+    public void pop() {
+        super.pop();
+        fmod.pop();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.math.waves.AbstractWave#push()
+     */
+    @Override
+    public void push() {
+        super.push();
+        fmod.push();
+    }
+
+    /**
+     * Resets this wave and its modulating wave as well.
+     * 
+     * @see AbstractWave#reset()
+     */
+    public void reset() {
+        super.reset();
+        fmod.reset();
+    }
+
+    /**
+     * Progresses the wave and updates the result value. You must NEVER call the
+     * update() method on the modulating wave since this is handled
+     * automatically by this method.
+     * 
+     * @see AbstractWave#update()
+     */
+    public float update() {
+        value = ((phase / TWO_PI) * 2 - 1) * amp + offset;
+        cyclePhase(frequency + fmod.update());
+        return value;
+    }
+
+}
diff --git a/src/main/java/toxi/math/waves/FMSineWave.java b/src/main/java/toxi/math/waves/FMSineWave.java
new file mode 100644
index 0000000..57267e5
--- /dev/null
+++ b/src/main/java/toxi/math/waves/FMSineWave.java
@@ -0,0 +1,105 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math.waves;
+
+/**
+ * <p>
+ * Frequency modulated sine wave. Uses a secondary wave to modulate the
+ * frequency of the main wave.
+ * </p>
+ * 
+ * <p>
+ * <strong>Note:</strong> You must NEVER call the update() method on the
+ * modulating wave.
+ * </p>
+ */
+public class FMSineWave extends AbstractWave {
+
+    public AbstractWave fmod;
+
+    public FMSineWave(float phase, float freq, AbstractWave fmod) {
+        super(phase, freq);
+        this.fmod = fmod;
+    }
+
+    public FMSineWave(float phase, float freq, float amp, float offset) {
+        this(phase, freq, amp, offset, new ConstantWave(0));
+    }
+
+    public FMSineWave(float phase, float freq, float amp, float offset,
+            AbstractWave fmod) {
+        super(phase, freq, amp, offset);
+        this.fmod = fmod;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.math.waves.AbstractWave#pop()
+     */
+    @Override
+    public void pop() {
+        super.pop();
+        fmod.pop();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.math.waves.AbstractWave#push()
+     */
+    @Override
+    public void push() {
+        super.push();
+        fmod.push();
+    }
+
+    /**
+     * Resets this wave and its modulating wave as well.
+     * 
+     * @see toxi.math.waves.AbstractWave#reset()
+     */
+    public void reset() {
+        super.reset();
+        fmod.reset();
+    }
+
+    /**
+     * Progresses the wave and updates the result value. You must NEVER call the
+     * update() method on the modulating wave since this is handled
+     * automatically by this method.
+     * 
+     * @see toxi.math.waves.AbstractWave#update()
+     */
+    public float update() {
+        value = (float) (Math.sin(phase) * amp) + offset;
+        cyclePhase(frequency + fmod.update());
+        return value;
+    }
+
+}
diff --git a/src/main/java/toxi/math/waves/FMSquareWave.java b/src/main/java/toxi/math/waves/FMSquareWave.java
new file mode 100644
index 0000000..d286f79
--- /dev/null
+++ b/src/main/java/toxi/math/waves/FMSquareWave.java
@@ -0,0 +1,113 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math.waves;
+
+/**
+ * <p>
+ * Frequency modulated bandwidth unlimited pure digital square wave. Uses a
+ * secondary wave to modulate the frequency of the main wave.
+ * </p>
+ * 
+ * <p>
+ * <strong>Note:</strong> You must NEVER call the update() method on the
+ * modulating wave.
+ * </p>
+ */
+public class FMSquareWave extends AbstractWave {
+
+    public AbstractWave fmod;
+
+    public FMSquareWave(float phase, float freq, AbstractWave fmod) {
+        super(phase, freq);
+        this.fmod = fmod;
+    }
+
+    /**
+     * Convenience constructor to create a non frequency modulated square wave
+     * 
+     * @param phase
+     * @param freq
+     *            base frequency (in radians)
+     * @param amp
+     * @param offset
+     */
+    public FMSquareWave(float phase, float freq, float amp, float offset) {
+        this(phase, freq, amp, offset, new ConstantWave(0));
+    }
+
+    public FMSquareWave(float phase, float freq, float amp, float offset,
+            AbstractWave fmod) {
+        super(phase, freq, amp, offset);
+        this.fmod = fmod;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.math.waves.AbstractWave#pop()
+     */
+    @Override
+    public void pop() {
+        super.pop();
+        fmod.pop();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.math.waves.AbstractWave#push()
+     */
+    @Override
+    public void push() {
+        super.push();
+        fmod.push();
+    }
+
+    /**
+     * Resets this wave and its modulating wave as well.
+     * 
+     * @see AbstractWave#reset()
+     */
+    public void reset() {
+        super.reset();
+        fmod.reset();
+    }
+
+    /**
+     * Progresses the wave and updates the result value. You must NEVER call the
+     * update() method on the modulating wave since this is handled
+     * automatically by this method.
+     * 
+     * @see AbstractWave#update()
+     */
+    public float update() {
+        value = (phase / TWO_PI < 0.5 ? 1 : -1) * amp + offset;
+        cyclePhase(frequency + fmod.update());
+        return value;
+    }
+}
diff --git a/src/main/java/toxi/math/waves/FMTriangleWave.java b/src/main/java/toxi/math/waves/FMTriangleWave.java
new file mode 100644
index 0000000..2b2d4aa
--- /dev/null
+++ b/src/main/java/toxi/math/waves/FMTriangleWave.java
@@ -0,0 +1,92 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math.waves;
+
+import toxi.math.MathUtils;
+
+/**
+ * Implements a frequency modulated triangular wave with its peak at PI: "/\"
+ */
+public class FMTriangleWave extends AbstractWave {
+
+    public AbstractWave fmod;
+
+    public FMTriangleWave(float phase, float freq) {
+        this(phase, freq, 1, 0);
+    }
+
+    public FMTriangleWave(float phase, float freq, float amp, float offset) {
+        this(phase, freq, amp, offset, new ConstantWave(0));
+    }
+
+    public FMTriangleWave(float phase, float freq, float amp, float offset,
+            AbstractWave fmod) {
+        super(phase, freq, amp, offset);
+        this.fmod = fmod;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.math.waves.AbstractWave#pop()
+     */
+    @Override
+    public void pop() {
+        super.pop();
+        fmod.pop();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.math.waves.AbstractWave#push()
+     */
+    @Override
+    public void push() {
+        super.push();
+        fmod.push();
+    }
+
+    /**
+     * Resets this wave and its modulating wave as well.
+     * 
+     * @see AbstractWave#reset()
+     */
+    public void reset() {
+        super.reset();
+        fmod.reset();
+    }
+
+    @Override
+    public float update() {
+        value = 2 * amp * (MathUtils.abs(PI - phase) * MathUtils.INV_PI - 0.5f)
+                + offset;
+        cyclePhase(frequency + fmod.update());
+        return value;
+    }
+}
diff --git a/src/main/java/toxi/math/waves/SineWave.java b/src/main/java/toxi/math/waves/SineWave.java
new file mode 100644
index 0000000..4d60279
--- /dev/null
+++ b/src/main/java/toxi/math/waves/SineWave.java
@@ -0,0 +1,77 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math.waves;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Standard Sine wave at fixed frequency and values normalized to the given
+ * amplitude.
+ */
+@XmlRootElement
+public class SineWave extends AbstractWave {
+
+    public SineWave() {
+        super();
+    }
+
+    /**
+     * @param phase
+     *            starting phase
+     * @param freq
+     *            in radians (not Hertz)
+     */
+    public SineWave(float phase, float freq) {
+        super(phase, freq);
+    }
+
+    /**
+     * @param phase
+     *            starting phase
+     * @param freq
+     *            in radians (not Hertz)
+     * @param amp
+     *            amplitude factor
+     * @param offset
+     *            centre oscillation value
+     */
+    public SineWave(float phase, float freq, float amp, float offset) {
+        super(phase, freq, amp, offset);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.math.waves.AbstractWave#update()
+     */
+    public float update() {
+        value = (float) (Math.sin(phase) * amp) + offset;
+        cyclePhase(frequency);
+        return value;
+    }
+}
diff --git a/src/main/java/toxi/math/waves/Wave2D.java b/src/main/java/toxi/math/waves/Wave2D.java
new file mode 100644
index 0000000..75c9aa3
--- /dev/null
+++ b/src/main/java/toxi/math/waves/Wave2D.java
@@ -0,0 +1,47 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math.waves;
+
+import toxi.geom.Vec2D;
+
+public class Wave2D {
+
+    public AbstractWave xmod, ymod;
+    public Vec2D pos;
+
+    public Wave2D(AbstractWave x, AbstractWave y) {
+        xmod = x;
+        ymod = y;
+        pos = new Vec2D();
+    }
+
+    public void update() {
+        pos.x = xmod.update();
+        pos.y = ymod.update();
+    }
+}
diff --git a/src/main/java/toxi/math/waves/WaveState.java b/src/main/java/toxi/math/waves/WaveState.java
new file mode 100644
index 0000000..1d54791
--- /dev/null
+++ b/src/main/java/toxi/math/waves/WaveState.java
@@ -0,0 +1,43 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.math.waves;
+
+public class WaveState {
+
+    public float phase;
+    public float frequency;
+    public float amp;
+    public float offset;
+
+    public WaveState(float phase, float frequency, float amp, float offset) {
+        this.phase = phase;
+        this.frequency = frequency;
+        this.amp = amp;
+        this.offset = offset;
+    }
+}
diff --git a/src/main/java/toxi/util/DateUtils.java b/src/main/java/toxi/util/DateUtils.java
new file mode 100644
index 0000000..e1c438e
--- /dev/null
+++ b/src/main/java/toxi/util/DateUtils.java
@@ -0,0 +1,133 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * A Simple timestamp generator/formatter with timezone support.
+ */
+public class DateUtils {
+
+    public static final TimeZone GMT = TimeZone.getTimeZone("GMT");
+
+    public static final SimpleDateFormat FORMAT = new SimpleDateFormat(
+            "yyyyMMdd-HHmmss");
+
+    /**
+     * Creates a formatted timestamp string of the current datetime using the
+     * local host timezone.
+     * 
+     * @return timestamp
+     */
+    public static final String timeStamp() {
+        return timeStamp(new Date(), null);
+    }
+
+    /**
+     * Creates a formatted timestamp string of the given date using the local
+     * host timezone.
+     * 
+     * @param date
+     * @return timestamp
+     */
+    public static final String timeStamp(Date date) {
+        return timeStamp(date, null);
+    }
+
+    /**
+     * Creates a formatted timestamp string of the given date using the
+     * specified timezone.
+     * 
+     * @param date
+     * @param zone
+     * @return timestamp
+     */
+    public static final String timeStamp(Date date, TimeZone zone) {
+        if (zone == null) {
+            zone = TimeZone.getDefault();
+        }
+        FORMAT.setTimeZone(zone);
+        return FORMAT.format(date);
+    }
+
+    /**
+     * Creates a formatted timestamp string of the given epoch using the local
+     * host timezone.
+     * 
+     * @param t
+     *            unix epoch timestamp
+     * @return timestamp
+     */
+    public static final String timeStamp(long t) {
+        return timeStamp(new Date(t), null);
+    }
+
+    /**
+     * Creates a formatted timestamp string of the given date using the given
+     * timezone ID.
+     * 
+     * @see TimeZone#getTimeZone(String)
+     * 
+     * @param zoneID
+     * @param date
+     * @return timestamp
+     */
+    public static final String timeStampForZone(String zoneID, Date date) {
+        return timeStamp(date, TimeZone.getTimeZone(zoneID));
+    }
+
+    /**
+     * Creates a formatted timestamp string of the current date in GMT.
+     * 
+     * @return timestamp
+     */
+    public static final String timeStampGMT() {
+        return timeStamp(new Date(), GMT);
+    }
+
+    /**
+     * Creates a formatted timestamp string of the given date in GMT.
+     * 
+     * @return timestamp
+     */
+    public static final String timeStampGMT(Date date) {
+        return timeStamp(date, GMT);
+    }
+
+    /**
+     * Creates a formatted timestamp string of the given epoch in GMT.
+     * 
+     * @return timestamp
+     */
+    public static final String timeStampGMT(long t) {
+        return timeStamp(new Date(t), GMT);
+    }
+}
diff --git a/src/main/java/toxi/util/FileSequenceDescriptor.java b/src/main/java/toxi/util/FileSequenceDescriptor.java
new file mode 100644
index 0000000..4758d24
--- /dev/null
+++ b/src/main/java/toxi/util/FileSequenceDescriptor.java
@@ -0,0 +1,157 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util;
+
+import java.io.File;
+import java.util.Iterator;
+
+/**
+ * A descriptor and iterator for handling file sequences.
+ */
+public class FileSequenceDescriptor implements Iterable<String> {
+
+    private class SequenceIterator implements Iterator<String> {
+
+        private int curr;
+        private int end;
+
+        public SequenceIterator(int start, int end) {
+            this.curr = start;
+            this.end = end;
+        }
+
+        public boolean hasNext() {
+            return curr < end;
+        }
+
+        public String next() {
+            String path = getPathForIndex(curr);
+            curr++;
+            return path;
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException("remove() not supported");
+        }
+
+    }
+
+    public String filePattern;
+    public String extension;
+    public int numDigits;
+    public int start;
+    public int end = -1;
+
+    /**
+     * Creates a new descriptor from the given sequence details.
+     * 
+     * @param filePattern
+     *            file pattern in the format: e.g. "path/basename%d04.ext"
+     * @param extension
+     *            file extension (e.g. ".tga")
+     * @param numDigits
+     *            number of digits used for the index
+     * @param start
+     *            start index
+     */
+    public FileSequenceDescriptor(String filePattern, String extension,
+            int numDigits, int start) {
+        this.filePattern = filePattern;
+        this.extension = extension;
+        this.numDigits = numDigits;
+        this.start = start;
+    }
+
+    /**
+     * Returns the base path of the sequence, i.e. the substring of the
+     * sequence's file pattern from the beginning until the first occurence of
+     * the % sign indicating the frame numbers.
+     * 
+     * @return path string
+     */
+    public String getBasePath() {
+        return filePattern.substring(0, filePattern.indexOf('%'));
+    }
+
+    /**
+     * Calculates sequence duration
+     * 
+     * @return number of files in sequence
+     */
+    public int getDuration() {
+        return getFinalIndex() - start;
+    }
+
+    /**
+     * Identifies the index of the last file of the sequence.
+     * 
+     * @return final index
+     */
+    public int getFinalIndex() {
+        if (end == -1) {
+            end = start;
+            while (true) {
+                if (!new File(getPathForIndex(end)).canRead()) {
+                    break;
+                } else {
+                    end++;
+                }
+            }
+        }
+        return end;
+    }
+
+    /**
+     * Constructs the file path for the given absolute index
+     * 
+     * @param i
+     *            index
+     * @return path
+     */
+    public String getPathForIndex(int i) {
+        return String.format(filePattern, i);
+    }
+
+    /**
+     * Returns the index of the first file of the sequence.
+     * 
+     * @return start index
+     */
+    public int getStartIndex() {
+        return start;
+    }
+
+    /**
+     * Creates an iterator providing paths for each file in the sequence. The
+     * iterator does not support the remove() method and attempts to use it
+     * results in an {@link UnsupportedOperationException} being thrown.
+     */
+    public Iterator<String> iterator() {
+        return new SequenceIterator(start, getFinalIndex());
+    }
+}
diff --git a/src/main/java/toxi/util/FileUtils.java b/src/main/java/toxi/util/FileUtils.java
new file mode 100644
index 0000000..1bd8179
--- /dev/null
+++ b/src/main/java/toxi/util/FileUtils.java
@@ -0,0 +1,480 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util;
+
+import java.awt.FileDialog;
+import java.awt.Frame;
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * A collection of file handling utilities.
+ */
+public class FileUtils {
+
+    /**
+     * {@link FileDialog} constant
+     */
+    public static final int LOAD = FileDialog.LOAD;
+
+    /**
+     * {@link FileDialog} constant
+     */
+    public static final int SAVE = FileDialog.SAVE;
+
+    /**
+     * Attempts to create the full path of directories as specified by the given
+     * target path. The path is assumed to be a directory, NOT a file in a
+     * directory. For the latter, use {@link #createDirectoriesForFile(File)}.
+     * 
+     * @param path
+     * @return true, if the operation succeeded
+     */
+    static public boolean createDirectories(File path) {
+        try {
+            if (!path.exists()) {
+                path.mkdirs();
+            }
+            return true;
+        } catch (SecurityException se) {
+            System.err.println("No permissions to create "
+                    + path.getAbsolutePath());
+        }
+        return false;
+    }
+
+    /**
+     * Attempts to create the full path of directories as specified by the given
+     * target file.
+     * 
+     * @param file
+     * @return true, if the operation succeeded
+     */
+    static public boolean createDirectoriesForFile(File file) {
+        try {
+            String parentName = file.getParent();
+            if (parentName != null) {
+                File parent = new File(parentName);
+                if (!parent.exists()) {
+                    parent.mkdirs();
+                }
+            }
+            return true;
+        } catch (SecurityException se) {
+            System.err.println("No permissions to create "
+                    + file.getAbsolutePath());
+        }
+        return false;
+    }
+
+    /**
+     * Creates an {@link InputStream} for the given file. If the file extension
+     * ends with ".gz" the stream is automatically wrapped in a
+     * {@link GZIPInputStream} as well.
+     * 
+     * @param file
+     *            input file
+     * @return input stream
+     * @throws IOException
+     */
+    static public InputStream createInputStream(File file) throws IOException {
+        if (file == null) {
+            throw new IllegalArgumentException("file can't be null");
+        }
+        InputStream stream = new FileInputStream(file);
+        if (file.getName().toLowerCase().endsWith(".gz")) {
+            stream = new GZIPInputStream(stream);
+        }
+        return stream;
+    }
+
+    /**
+     * Creates an {@link OutputStream} for the given file. If the file extension
+     * ends with ".gz" the stream is automatically wrapped in a
+     * {@link GZIPOutputStream} as well. Also attempts to create any
+     * intermediate directories for the file using
+     * {@link #createDirectoriesForFile(File)}.
+     * 
+     * @param file
+     *            output file
+     * @return output stream
+     * @throws IOException
+     */
+    static public OutputStream createOutputStream(File file) throws IOException {
+        if (file == null) {
+            throw new IllegalArgumentException("file can't be null");
+        }
+        createDirectoriesForFile(file);
+        OutputStream stream = new FileOutputStream(file);
+        if (file.getName().toLowerCase().endsWith(".gz")) {
+            stream = new GZIPOutputStream(stream);
+        }
+        return stream;
+    }
+
+    /**
+     * Creates a {@link BufferedReader} for the given file using UTF-8 encoding.
+     * 
+     * @param file
+     * @return reader instance
+     * @throws IOException
+     */
+    public static BufferedReader createReader(File file) throws IOException {
+        return createReader(createInputStream(file));
+    }
+
+    /**
+     * Creates a {@link BufferedReader} for the given {@link InputStream} using
+     * UTF-8 encoding.
+     * 
+     * @param input
+     *            stream
+     * @return reader instance
+     * @throws IOException
+     */
+    public static BufferedReader createReader(InputStream input) {
+        return createReader(input, "UTF-8");
+    }
+
+    /**
+     * Creates a {@link BufferedReader} for the given {@link InputStream} and
+     * using the specified encoding.
+     * 
+     * @param input
+     *            stream
+     * @param encoding
+     *            text encoding to use
+     * @return reader instance
+     * @throws IOException
+     */
+    public static BufferedReader createReader(InputStream input, String encoding) {
+        InputStreamReader isr = null;
+        try {
+            isr = new InputStreamReader(input, encoding);
+        } catch (UnsupportedEncodingException e) {
+        }
+        return new BufferedReader(isr, 0x10000);
+    }
+
+    /**
+     * Creates a {@link BufferedWriter} for the given file using UTF-8 encoding.
+     * 
+     * @param file
+     * @return writer instance
+     * @throws IOException
+     */
+    public static BufferedWriter createWriter(File file) throws IOException {
+        return createWriter(createOutputStream(file));
+    }
+
+    /**
+     * Creates a {@link BufferedWriter} for the given {@link OutputStream} using
+     * UTF-8 encoding.
+     * 
+     * @param out
+     * @return writer instance
+     * @throws IOException
+     */
+    public static BufferedWriter createWriter(OutputStream out) {
+        return createWriter(out, "UTF-8");
+    }
+
+    /**
+     * Creates a {@link BufferedWriter} for the given {@link OutputStream} and
+     * using the specified encoding.
+     * 
+     * @param out
+     *            stream
+     * @param encoding
+     *            text encoding to use
+     * @return writer instance
+     * @throws IOException
+     */
+    public static BufferedWriter createWriter(OutputStream out, String encoding) {
+        OutputStreamWriter w = null;
+        try {
+            w = new OutputStreamWriter(out, encoding);
+        } catch (UnsupportedEncodingException e) {
+        }
+        return new BufferedWriter(w, 0x10000);
+    }
+
+    /**
+     * <p>
+     * Analyses the given file path for a file sequence pattern and returns a
+     * {@link FileSequenceDescriptor} instance for further use to handle this
+     * sequence. The file pattern should be in one of these formats:
+     * </p>
+     * <ul>
+     * <li>base_path-00001.ext</li>
+     * <li>base_path001.ext</li>
+     * </ul>
+     * <p>
+     * The sequence index should be using leading zeros, but the number of
+     * digits will be identified automatically.
+     * </p>
+     * 
+     * @param path
+     *            file path of the first file in the sequence
+     * @return descriptor, or null, if the path could not be analysed
+     */
+    public static FileSequenceDescriptor getFileSequenceDescriptorFor(
+            String path) {
+        int dotIndex = path.lastIndexOf('.');
+        int zeroIndex = path.lastIndexOf('-') + 1;
+        if (zeroIndex == 0) {
+            zeroIndex = dotIndex - 1;
+            while (path.charAt(zeroIndex) >= '0'
+                    && path.charAt(zeroIndex) <= '9') {
+                zeroIndex--;
+            }
+            zeroIndex++;
+        }
+        int numDigits = dotIndex - zeroIndex;
+        if (dotIndex != -1 && numDigits > 0) {
+            String base = path.substring(0, zeroIndex);
+            String extension = path.substring(dotIndex);
+            String filePattern = base + "%0" + numDigits + "d" + extension;
+            int start = Integer.parseInt(path.substring(zeroIndex, dotIndex));
+            return new FileSequenceDescriptor(filePattern, extension, dotIndex
+                    - zeroIndex, start);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Loads the given {@link InputStream} into a byte array buffer.
+     * 
+     * @param stream
+     * @return byte array
+     * @throws IOException
+     */
+    static public byte[] loadBytes(InputStream stream) throws IOException {
+        BufferedInputStream input = new BufferedInputStream(stream);
+        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+        int c;
+        while ((c = input.read()) != -1) {
+            buffer.write(c);
+        }
+        return buffer.toByteArray();
+    }
+
+    /**
+     * Loads the given {@link BufferedReader} as text, line by line into a
+     * {@link String}.
+     * 
+     * @param reader
+     * @return reader contents as string
+     * @throws IOException
+     */
+    public static String loadText(BufferedReader reader) throws IOException {
+        StringBuilder result = new StringBuilder();
+        String line;
+        while ((line = reader.readLine()) != null) {
+            result.append(line).append("\n");
+        }
+        return result.toString();
+    }
+
+    /**
+     * Loads the given {@link InputStream} as text, line by line into a
+     * {@link String} using UTF-8 encoding.
+     * 
+     * @param input
+     *            stream
+     * @return stream contents as string
+     * @throws IOException
+     */
+    public static String loadText(InputStream input) throws IOException {
+        return loadText(input, "UTF-8");
+    }
+
+    /**
+     * Loads the given {@link InputStream} as text, line by line into a
+     * {@link String} using the specified encoding.
+     * 
+     * @param input
+     *            stream
+     * @param encoding
+     * @return stream contents as single string
+     * @throws IOException
+     */
+    public static String loadText(InputStream input, String encoding)
+            throws IOException {
+        byte[] raw = loadBytes(input);
+        return new String(raw, encoding);
+    }
+
+    /**
+     * Saves the given text to the specified {@link BufferedWriter} instance,
+     * then closes the writer afterwards.
+     * 
+     * @param writer
+     * @param string
+     * @throws IOException
+     */
+    static public void saveText(BufferedWriter writer, String string)
+            throws IOException {
+        writer.write(string);
+        writer.flush();
+        writer.close();
+    }
+
+    /**
+     * Saves the given text to the specified {@link OutputStream} instance, then
+     * closes the underlying writer afterwards.
+     * 
+     * @param output
+     * @param string
+     * @throws IOException
+     */
+    static public void saveText(OutputStream output, String string)
+            throws IOException {
+        saveText(createWriter(output), string);
+    }
+
+    /**
+     * Displays a standard AWT file dialog for choosing a folder (no files!).
+     * 
+     * @param frame
+     *            parent frame
+     * @param title
+     *            dialog title
+     * @param path
+     *            base directory (or null)
+     * @return path to chosen directory or null, if user has canceled
+     */
+    public static String showDirectoryChooser(final Frame frame,
+            final String title, String path) {
+        System.setProperty("apple.awt.fileDialogForDirectories", "true");
+        String result = showFileDialog(frame, title, path,
+                new FilenameFilter() {
+
+                    public boolean accept(File dir, String name) {
+                        return new File(dir + "/" + name).isDirectory();
+                    }
+                }, LOAD);
+        System.setProperty("apple.awt.fileDialogForDirectories", "false");
+        return result;
+    }
+
+    /**
+     * Displays a standard AWT file dialog for choosing a file for loading or
+     * saving.
+     * 
+     * @param frame
+     *            parent frame
+     * @param title
+     *            dialog title
+     * @param path
+     *            base directory (or null)
+     * @param filter
+     *            a FilenameFilter implementation (or null)
+     * @param mode
+     *            either FileUtils.LOAD or FileUtils.SAVE
+     * @return path to chosen file or null, if user has canceled
+     */
+    public static String showFileDialog(final Frame frame, final String title,
+            String path, FilenameFilter filter, final int mode) {
+        String fileID = null;
+        FileDialog fd = new FileDialog(frame, title, mode);
+        if (path != null) {
+            fd.setDirectory(path);
+        }
+        if (filter != null) {
+            fd.setFilenameFilter(filter);
+        }
+        fd.setVisible(true);
+        if (fd.getFile() != null) {
+            fileID = fd.getFile();
+            fileID = fd.getDirectory() + fileID;
+        }
+        return fileID;
+    }
+
+    /**
+     * Displays a standard AWT file dialog for choosing a file for loading or
+     * saving.
+     * 
+     * @param frame
+     *            parent frame
+     * @param title
+     *            dialog title
+     * @param path
+     *            base directory (or null)
+     * @param formats
+     *            an array of allowed file extensions (or null to allow all)
+     * @param mode
+     *            either FileUtils.LOAD or FileUtils.SAVE
+     * @return path to chosen file or null, if user has canceled
+     */
+    public static String showFileDialog(final Frame frame, final String title,
+            String path, final String[] formats, final int mode) {
+        String fileID = null;
+        FileDialog fd = new FileDialog(frame, title, mode);
+        if (path != null) {
+            fd.setDirectory(path);
+        }
+        if (formats != null) {
+            fd.setFilenameFilter(new FilenameFilter() {
+
+                public boolean accept(File dir, String name) {
+                    boolean isAccepted = false;
+                    for (String ext : formats) {
+                        if (name.indexOf(ext) != -1) {
+                            isAccepted = true;
+                            break;
+                        }
+                    }
+                    return isAccepted;
+                }
+            });
+        }
+        fd.setVisible(true);
+        if (fd.getFile() != null) {
+            fileID = fd.getFile();
+            fileID = fd.getDirectory() + fileID;
+        }
+        return fileID;
+    }
+}
diff --git a/src/main/java/toxi/util/datatypes/ArraySet.java b/src/main/java/toxi/util/datatypes/ArraySet.java
new file mode 100644
index 0000000..c0d2c55
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/ArraySet.java
@@ -0,0 +1,121 @@
+package toxi.util.datatypes;
+
+/*
+ * Copyright (c) 2007 by L. Paul Chew.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, 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 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.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * An ArrayList implementation of Set. An ArraySet is good for small sets; it
+ * has less overhead than a HashSet or a TreeSet.
+ * 
+ * @author Paul Chew
+ * 
+ *         Created December 2007. For use with Voronoi/Delaunay applet.
+ * 
+ */
+public class ArraySet<E> extends AbstractSet<E> {
+
+    private ArrayList<E> items; // Items of the set
+
+    /**
+     * Create an empty set (default initial capacity is 3).
+     */
+    public ArraySet() {
+        this(3);
+    }
+
+    /**
+     * Create a set containing the items of the collection. Any duplicate items
+     * are discarded.
+     * 
+     * @param collection
+     *            the source for the items of the small set
+     */
+    public ArraySet(Collection<? extends E> collection) {
+        items = new ArrayList<E>(collection.size());
+        for (E item : collection) {
+            if (!items.contains(item)) {
+                items.add(item);
+            }
+        }
+    }
+
+    /**
+     * Create an empty set with the specified initial capacity.
+     * 
+     * @param initialCapacity
+     *            the initial capacity
+     */
+    public ArraySet(int initialCapacity) {
+        items = new ArrayList<E>(initialCapacity);
+    }
+
+    @Override
+    public boolean add(E item) {
+        if (items.contains(item)) {
+            return false;
+        }
+        return items.add(item);
+    }
+
+    /**
+     * True if any member of the collection is also in the ArraySet.
+     * 
+     * @param collection
+     *            the Collection to check
+     * @return true if any member of collection appears in this ArraySet
+     */
+    public boolean containsAny(Collection<?> collection) {
+        for (Object item : collection) {
+            if (this.contains(item)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Get the item at the specified index.
+     * 
+     * @param index
+     *            where the item is located in the ListSet
+     * @return the item at the specified index
+     * @throws IndexOutOfBoundsException
+     *             if the index is out of bounds
+     */
+    public E get(int index) throws IndexOutOfBoundsException {
+        return items.get(index);
+    }
+
+    @Override
+    public Iterator<E> iterator() {
+        return items.iterator();
+    }
+
+    @Override
+    public int size() {
+        return items.size();
+    }
+
+}
diff --git a/src/main/java/toxi/util/datatypes/ArrayUtil.java b/src/main/java/toxi/util/datatypes/ArrayUtil.java
new file mode 100644
index 0000000..adfae6e
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/ArrayUtil.java
@@ -0,0 +1,377 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util.datatypes;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Random;
+
+/**
+ * A collection of array utilities.
+ */
+public class ArrayUtil {
+
+    /**
+     * Adds all array elements to the given collection of the same type.
+     * 
+     * @param <T>
+     * @param array
+     *            array
+     * @param collection
+     *            existing collection or null (to create a new {@link ArrayList}
+     *            automatically)
+     */
+    public static <T> void addArrayToCollection(T[] array,
+            Collection<T> collection) {
+        if (collection == null) {
+            collection = new ArrayList<T>();
+        }
+        for (T o : array) {
+            collection.add(o);
+        }
+    }
+
+    /**
+     * Converts the generic array into an {@link ArrayList} of the same type.
+     * 
+     * @param array
+     * @return array list version
+     */
+    public static <T> ArrayList<T> arrayToList(T[] array) {
+        ArrayList<T> list = new ArrayList<T>(array.length);
+        for (T element : array) {
+            list.add(element);
+        }
+        return list;
+    }
+
+    /**
+     * Creates a normalized version of the values of the given int[] array.
+     * Supports packed integers (e.g. ARGB data) by allowing to specify a
+     * bitshift amount & bitmask, e.g. do this to get the normalized
+     * representation of the red channel of an ARGB array:
+     * 
+     * <pre>
+     * 
+     * // use 16 bits as shift offset for accessing red channel
+     * float[] red = ArrayUtil.getAsNormalizedFloatArray(argbPixels, 16, 255, 255);
+     * </pre>
+     * 
+     * @param source
+     *            source data
+     * @param bits
+     *            number of bits to right shift each value
+     * @param mask
+     *            bitmask to apply after bitshifting
+     * @param peak
+     *            peak value (in the source domain) to normalize against
+     * @param target
+     *            peak of the normalized values
+     * @return normalized values
+     */
+    public static float[] getAsNormalizedFloatArray(int[] source, int bits,
+            int mask, int peak, float target) {
+        float invPeak = target / peak;
+        float[] normalized = new float[source.length];
+        for (int i = 0; i < source.length; i++) {
+            int val = source[i];
+            if (bits > 0) {
+                val >>= bits;
+            }
+            val &= mask;
+            normalized[i] = val * invPeak;
+        }
+        return normalized;
+    }
+
+    /**
+     * Returns the index of the element where the given value is found in the
+     * array.
+     * 
+     * @param needle
+     *            number to find
+     * @param stack
+     *            array to search
+     * @param maxLen
+     *            number of elements to search
+     * @return array index or -1 if value couldn't be found in array
+     */
+    public static int indexInArray(float needle, float[] stack, int maxLen) {
+        for (int i = 0; i < maxLen; i++) {
+            if (stack[i] == needle) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Returns the index of the element where the given value is found in the
+     * array.
+     * 
+     * @param needle
+     *            number to find
+     * @param stack
+     *            array to search
+     * @param maxLen
+     *            number of elements to search
+     * @return array index or -1 if value couldn't be found in array
+     */
+    public static int indexInArray(int needle, int[] stack, int maxLen) {
+        for (int i = 0; i < maxLen; i++) {
+            if (stack[i] == needle) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Returns the index of the element where the given value is found in the
+     * array. The comparison uses {@link Object#equals(Object)}.
+     * 
+     * @param needle
+     *            number to find
+     * @param stack
+     *            array to search
+     * @param maxLen
+     *            number of elements to search
+     * @return array index or -1 if value couldn't be found in array
+     */
+    public static int indexInArray(Object needle, Object[] stack, int maxLen) {
+        for (int i = 0; i < maxLen; i++) {
+            if (stack[i].equals(needle)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Normalizes the values in the given array to the new absolute target
+     * value. The original values are overridden.
+     * 
+     * @param buffer
+     *            array
+     * @param peak
+     *            current peak in the source domain
+     * @param target
+     *            new peak in the target domain
+     * @return normalized array
+     */
+    public static float[] normalizeFloatArray(float[] buffer, float peak,
+            float target) {
+        float invPeak = target / peak;
+        for (int i = 0; i < buffer.length; i++) {
+            buffer[i] *= invPeak;
+        }
+        return buffer;
+    }
+
+    /**
+     * Reverses the item order of the supplied byte array.
+     * 
+     * @param array
+     */
+    public static void reverse(byte[] array) {
+        int len = array.length - 1;
+        int len2 = array.length / 2;
+        for (int i = 0; i < len2; i++) {
+            byte tmp = array[i];
+            array[i] = array[len - i];
+            array[len - i] = tmp;
+        }
+    }
+
+    /**
+     * Reverses the item order of the supplied char array.
+     * 
+     * @param array
+     */
+    public static void reverse(char[] array) {
+        int len = array.length - 1;
+        int len2 = array.length / 2;
+        for (int i = 0; i < len2; i++) {
+            char tmp = array[i];
+            array[i] = array[len - i];
+            array[len - i] = tmp;
+        }
+    }
+
+    /**
+     * Reverses the item order of the supplied float array.
+     * 
+     * @param array
+     */
+    public static void reverse(float[] array) {
+        int len = array.length - 1;
+        int len2 = array.length / 2;
+        for (int i = 0; i < len2; i++) {
+            float tmp = array[i];
+            array[i] = array[len - i];
+            array[len - i] = tmp;
+        }
+    }
+
+    /**
+     * Reverses the item order of the supplied int array.
+     * 
+     * @param array
+     */
+    public static void reverse(int[] array) {
+        int len = array.length - 1;
+        int len2 = array.length / 2;
+        for (int i = 0; i < len2; i++) {
+            int tmp = array[i];
+            array[i] = array[len - i];
+            array[len - i] = tmp;
+        }
+    }
+
+    /**
+     * Reverses the item order of the supplied short array.
+     * 
+     * @param array
+     */
+    public static void reverse(short[] array) {
+        int len = array.length - 1;
+        int len2 = array.length / 2;
+        for (int i = 0; i < len2; i++) {
+            short tmp = array[i];
+            array[i] = array[len - i];
+            array[len - i] = tmp;
+        }
+    }
+
+    /**
+     * Reverses the item order of the supplied array (generic types).
+     * 
+     * @param array
+     */
+    public static <T> void reverse(T[] array) {
+        int len = array.length - 1;
+        int len2 = array.length / 2;
+        for (int i = 0; i < len2; i++) {
+            T tmp = array[i];
+            array[i] = array[len - i];
+            array[len - i] = tmp;
+        }
+    }
+
+    /**
+     * Rearranges the array items in random order using the default
+     * java.util.Random generator. Operation is in-place, no copy is created.
+     * 
+     * @param array
+     */
+    public static <T> void shuffle(T[] array) {
+        shuffle(array, new Random());
+    }
+
+    /**
+     * Rearranges the array items in random order using the given RNG. Operation
+     * is in-place, no copy is created.
+     * 
+     * @param array
+     * @param rnd
+     */
+    public static <T> void shuffle(T[] array, Random rnd) {
+        int N = array.length;
+        for (int i = 0; i < N; i++) {
+            int r = i + rnd.nextInt(N - i); // between i and N-1
+            T swap = array[i];
+            array[i] = array[r];
+            array[r] = swap;
+        }
+    }
+
+    public static String toString(byte[] array) {
+        StringBuilder s = new StringBuilder();
+        s.append('{');
+        final int max = array.length - 1;
+        for (int i = 0; i < array.length; i++) {
+            s.append(array[i]);
+            if (i < max) {
+                s.append(',');
+            }
+        }
+        return s.append('}').toString();
+    }
+
+    public static String toString(double[] array) {
+        StringBuilder s = new StringBuilder();
+        s.append('{');
+        final int max = array.length - 1;
+        for (int i = 0; i < array.length; i++) {
+            s.append(array[i]);
+            if (i < max) {
+                s.append(',');
+            }
+        }
+        return s.append('}').toString();
+    }
+
+    public static String toString(float[] array) {
+        StringBuilder s = new StringBuilder();
+        s.append('{');
+        final int max = array.length - 1;
+        for (int i = 0; i < array.length; i++) {
+            s.append(array[i]);
+            if (i < max) {
+                s.append(',');
+            }
+        }
+        return s.append('}').toString();
+    }
+
+    public static String toString(int[] array) {
+        StringBuilder s = new StringBuilder();
+        s.append('{');
+        final int max = array.length - 1;
+        for (int i = 0; i < array.length; i++) {
+            s.append(array[i]);
+            if (i < max) {
+                s.append(',');
+            }
+        }
+        return s.append('}').toString();
+    }
+
+    public static <T> String toString(T[] array) {
+        StringBuilder s = new StringBuilder();
+        s.append('{');
+        final int max = array.length - 1;
+        for (int i = 0; i < array.length; i++) {
+            s.append(array[i]);
+            if (i < max) {
+                s.append(',');
+            }
+        }
+        return s.append('}').toString();
+    }
+}
diff --git a/src/main/java/toxi/util/datatypes/BiasedDoubleRange.java b/src/main/java/toxi/util/datatypes/BiasedDoubleRange.java
new file mode 100644
index 0000000..744e45a
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/BiasedDoubleRange.java
@@ -0,0 +1,115 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util.datatypes;
+
+import javax.xml.bind.annotation.XmlAttribute;
+
+import toxi.math.MathUtils;
+
+public class BiasedDoubleRange extends DoubleRange {
+
+    @XmlAttribute
+    protected double bias = 0.5;
+
+    @XmlAttribute
+    protected double standardDeviation = bias * 0.5;
+
+    public BiasedDoubleRange() {
+        this(0, 1, 0.5, 1);
+    }
+
+    /**
+     * @param min
+     *            min value (inclusive)
+     * @param max
+     *            max value (inclusive)
+     * @param bias
+     *            bias value (can be outside the min/max range, but values will
+     *            be clipped)
+     * @param sd
+     *            standard deviation (if bias at range mean sd=1.0, the entire
+     *            range will be covered)
+     */
+    public BiasedDoubleRange(double min, double max, double bias, double sd) {
+        super(min, max);
+        setBias(bias);
+        setStandardDeviation(sd);
+    }
+
+    public BiasedDoubleRange copy() {
+        BiasedDoubleRange r = new BiasedDoubleRange(min, max, bias,
+                standardDeviation * 2);
+        r.currValue = currValue;
+        return r;
+    }
+
+    /**
+     * @return the bias
+     */
+    public double getBias() {
+        return bias;
+    }
+
+    /**
+     * @return the standardDeviation
+     */
+    public double getStandardDeviation() {
+        return standardDeviation;
+    }
+
+    @Override
+    public double pickRandom() {
+        do {
+            currValue = (random.nextGaussian() * standardDeviation * (max - min))
+                    + bias;
+        } while (currValue < min || currValue >= max);
+        return currValue;
+    }
+
+    /**
+     * @param bias
+     *            the bias to set
+     */
+    public void setBias(double bias) {
+        this.bias = MathUtils.clip(bias, min, max);
+    }
+
+    /**
+     * @param sd
+     *            the standardDeviation to set
+     */
+    public void setStandardDeviation(double sd) {
+        this.standardDeviation = MathUtils.clip(sd, 0, 1.0) * 0.5;
+    }
+
+    @Override
+    public String toString() {
+        return "BiasedFloatRange: " + min + " -> " + max + " bias: " + bias
+                + " q: " + standardDeviation;
+    }
+}
diff --git a/src/main/java/toxi/util/datatypes/BiasedFloatRange.java b/src/main/java/toxi/util/datatypes/BiasedFloatRange.java
new file mode 100644
index 0000000..c2b3c6a
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/BiasedFloatRange.java
@@ -0,0 +1,115 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util.datatypes;
+
+import javax.xml.bind.annotation.XmlAttribute;
+
+import toxi.math.MathUtils;
+
+public class BiasedFloatRange extends FloatRange {
+
+    @XmlAttribute
+    protected float bias = 0.5f;
+
+    @XmlAttribute
+    protected float standardDeviation = bias * 0.5f;
+
+    public BiasedFloatRange() {
+        this(0, 1, 0.5f, 1);
+    }
+
+    /**
+     * @param min
+     *            min value (inclusive)
+     * @param max
+     *            max value (inclusive)
+     * @param bias
+     *            bias value (can be outside the min/max range, but values will
+     *            be clipped)
+     * @param sd
+     *            standard deviation (if bias at range mean sd=1.0, the entire
+     *            range will be covered)
+     */
+    public BiasedFloatRange(float min, float max, float bias, float sd) {
+        super(min, max);
+        setBias(bias);
+        setStandardDeviation(sd);
+    }
+
+    public BiasedFloatRange copy() {
+        BiasedFloatRange r = new BiasedFloatRange(min, max, bias,
+                standardDeviation * 2);
+        r.currValue = currValue;
+        return r;
+    }
+
+    /**
+     * @return the bias
+     */
+    public float getBias() {
+        return bias;
+    }
+
+    /**
+     * @return the standardDeviation
+     */
+    public float getStandardDeviation() {
+        return standardDeviation;
+    }
+
+    @Override
+    public float pickRandom() {
+        do {
+            currValue = (float) (random.nextGaussian() * standardDeviation * (max - min))
+                    + bias;
+        } while (currValue < min || currValue >= max);
+        return currValue;
+    }
+
+    /**
+     * @param bias
+     *            the bias to set
+     */
+    public void setBias(float bias) {
+        this.bias = MathUtils.clip(bias, min, max);
+    }
+
+    /**
+     * @param sd
+     *            the standardDeviation to set
+     */
+    public void setStandardDeviation(float sd) {
+        this.standardDeviation = MathUtils.clip(sd, 0, 1.0f) * 0.5f;
+    }
+
+    @Override
+    public String toString() {
+        return "BiasedFloatRange: " + min + " -> " + max + " bias: " + bias
+                + " q: " + standardDeviation;
+    }
+}
diff --git a/src/main/java/toxi/util/datatypes/BiasedIntegerRange.java b/src/main/java/toxi/util/datatypes/BiasedIntegerRange.java
new file mode 100644
index 0000000..ab7e2eb
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/BiasedIntegerRange.java
@@ -0,0 +1,115 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util.datatypes;
+
+import javax.xml.bind.annotation.XmlAttribute;
+
+import toxi.math.MathUtils;
+
+public class BiasedIntegerRange extends IntegerRange {
+
+    @XmlAttribute
+    public int bias;
+
+    @XmlAttribute
+    public float standardDeviation;
+
+    public BiasedIntegerRange() {
+        this(0, 100, 50, 1.0f);
+    }
+
+    /**
+     * @param min
+     *            min value (inclusive)
+     * @param max
+     *            max value (inclusive)
+     * @param bias
+     *            bias value (can be outside the min/max range, but values will
+     *            be clipped)
+     * @param sd
+     *            standard deviation (if bias at range means sd=1.0, the entire
+     *            range will be covered)
+     */
+    public BiasedIntegerRange(int min, int max, int bias, float sd) {
+        super(min, max);
+        this.bias = bias;
+        this.standardDeviation = sd * 0.5f;
+    }
+
+    public BiasedIntegerRange copy() {
+        BiasedIntegerRange r = new BiasedIntegerRange(min, max, bias,
+                standardDeviation * 2);
+        r.currValue = currValue;
+        return r;
+    }
+
+    /**
+     * @return the bias
+     */
+    public int getBias() {
+        return bias;
+    }
+
+    /**
+     * @return the standardDeviation
+     */
+    public float getStandardDeviation() {
+        return standardDeviation;
+    }
+
+    @Override
+    public int pickRandom() {
+        do {
+            currValue = (int) (random.nextGaussian() * standardDeviation * (max - min))
+                    + bias;
+        } while (currValue < min || currValue >= max);
+        return currValue;
+    }
+
+    /**
+     * @param bias
+     *            the bias to set
+     */
+    public void setBias(int bias) {
+        this.bias = MathUtils.clip(bias, min, max);
+    }
+
+    /**
+     * @param sd
+     *            the standardDeviation to set
+     */
+    public void setStandardDeviation(float sd) {
+        this.standardDeviation = MathUtils.clip(sd, 0, 1.0f) * 0.5f;
+    }
+
+    @Override
+    public String toString() {
+        return "BiasedIntegerRange: " + min + " -> " + max + " bias: " + bias
+                + " q: " + standardDeviation;
+    }
+}
diff --git a/src/main/java/toxi/util/datatypes/DoubleRange.java b/src/main/java/toxi/util/datatypes/DoubleRange.java
new file mode 100644
index 0000000..ed21c25
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/DoubleRange.java
@@ -0,0 +1,160 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util.datatypes;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import javax.xml.bind.annotation.XmlAttribute;
+
+import toxi.math.MathUtils;
+
+public class DoubleRange {
+
+    public static DoubleRange fromSamples(double... samples) {
+        double min = Double.MAX_VALUE;
+        double max = Double.MIN_VALUE;
+        for (double s : samples) {
+            min = MathUtils.min(min, s);
+            max = MathUtils.max(max, s);
+        }
+        return new DoubleRange(min, max);
+    }
+
+    public static DoubleRange fromSamples(List<Double> samples) {
+        double min = Double.MAX_VALUE;
+        double max = Double.MIN_VALUE;
+        for (double s : samples) {
+            min = MathUtils.min(min, s);
+            max = MathUtils.max(max, s);
+        }
+        return new DoubleRange(min, max);
+    }
+
+    @XmlAttribute
+    public double min, max;
+
+    @XmlAttribute(name = "default")
+    public double currValue;
+
+    protected Random random = new Random();
+
+    public DoubleRange() {
+        this(0d, 1d);
+    }
+
+    public DoubleRange(double min, double max) {
+        // swap if necessary...
+        if (min > max) {
+            double t = max;
+            max = min;
+            min = t;
+        }
+        this.min = min;
+        this.max = max;
+        this.currValue = min;
+    }
+
+    public double adjustCurrentBy(double val) {
+        return setCurrent(currValue + val);
+    }
+
+    public DoubleRange copy() {
+        DoubleRange range = new DoubleRange(min, max);
+        range.currValue = currValue;
+        range.random = random;
+        return range;
+    }
+
+    /**
+     * Returns the value at the normalized position <code>(0.0 = min ... 1.0 =
+     * max-EPS)</code> within the range. Since the max value is exclusive, the
+     * value returned for position 1.0 is the range max value minus
+     * {@link MathUtils#EPS}. Also note the given position is not being clipped
+     * to the 0.0-1.0 interval, so when passing in values outside that interval
+     * will produce out-of-range values too.
+     * 
+     * @param perc
+     * @return value within the range
+     */
+    public final double getAt(double perc) {
+        return min + (max - min - MathUtils.EPS) * perc;
+    }
+
+    public double getCurrent() {
+        return currValue;
+    }
+
+    public double getMedian() {
+        return (min + max) * 0.5f;
+    }
+
+    public double getRange() {
+        return max - min;
+    }
+
+    public boolean isValueInRange(float val) {
+        return val >= min && val <= max;
+    }
+
+    public double pickRandom() {
+        currValue = MathUtils.random(random, (float) min, (float) max);
+        return currValue;
+    }
+
+    public DoubleRange seed(long seed) {
+        random.setSeed(seed);
+        return this;
+    }
+
+    public double setCurrent(double val) {
+        currValue = MathUtils.clip(val, min, max);
+        return currValue;
+    }
+
+    public DoubleRange setRandom(Random rnd) {
+        random = rnd;
+        return this;
+    }
+
+    public Double[] toArray(double step) {
+        List<Double> range = new LinkedList<Double>();
+        double v = min;
+        while (v < max) {
+            range.add(v);
+            v += step;
+        }
+        return range.toArray(new Double[0]);
+    }
+
+    @Override
+    public String toString() {
+        return "DoubleRange: " + min + " -> " + max;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/toxi/util/datatypes/FloatRange.java b/src/main/java/toxi/util/datatypes/FloatRange.java
new file mode 100644
index 0000000..8499be4
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/FloatRange.java
@@ -0,0 +1,160 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util.datatypes;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import javax.xml.bind.annotation.XmlAttribute;
+
+import toxi.math.MathUtils;
+
+public class FloatRange {
+
+    public static FloatRange fromSamples(float... samples) {
+        float min = Float.MAX_VALUE;
+        float max = Float.MIN_VALUE;
+        for (float s : samples) {
+            min = MathUtils.min(min, s);
+            max = MathUtils.max(max, s);
+        }
+        return new FloatRange(min, max);
+    }
+
+    public static FloatRange fromSamples(List<Float> samples) {
+        float min = Float.MAX_VALUE;
+        float max = Float.MIN_VALUE;
+        for (float s : samples) {
+            min = MathUtils.min(min, s);
+            max = MathUtils.max(max, s);
+        }
+        return new FloatRange(min, max);
+    }
+
+    @XmlAttribute
+    public float min, max;
+
+    @XmlAttribute(name = "default")
+    public float currValue;
+
+    protected Random random = new Random();
+
+    public FloatRange() {
+        this(0f, 1f);
+    }
+
+    public FloatRange(float min, float max) {
+        // swap if necessary...
+        if (min > max) {
+            float t = max;
+            max = min;
+            min = t;
+        }
+        this.min = min;
+        this.max = max;
+        this.currValue = min;
+    }
+
+    public float adjustCurrentBy(float val) {
+        return setCurrent(currValue + val);
+    }
+
+    public FloatRange copy() {
+        FloatRange range = new FloatRange(min, max);
+        range.currValue = currValue;
+        range.random = random;
+        return range;
+    }
+
+    /**
+     * Returns the value at the normalized position <code>(0.0 = min ... 1.0 =
+     * max-EPS)</code> within the range. Since the max value is exclusive, the
+     * value returned for position 1.0 is the range max value minus
+     * {@link MathUtils#EPS}. Also note the given position is not being clipped
+     * to the 0.0-1.0 interval, so when passing in values outside that interval
+     * will produce out-of-range values too.
+     * 
+     * @param perc
+     * @return value within the range
+     */
+    public final float getAt(float perc) {
+        return min + (max - min - MathUtils.EPS) * perc;
+    }
+
+    public float getCurrent() {
+        return currValue;
+    }
+
+    public float getMedian() {
+        return (min + max) * 0.5f;
+    }
+
+    public float getRange() {
+        return max - min;
+    }
+
+    public boolean isValueInRange(float val) {
+        return val >= min && val <= max;
+    }
+
+    public float pickRandom() {
+        currValue = MathUtils.random(random, min, max);
+        return currValue;
+    }
+
+    public FloatRange seed(long seed) {
+        random.setSeed(seed);
+        return this;
+    }
+
+    public float setCurrent(float val) {
+        currValue = MathUtils.clip(val, min, max);
+        return currValue;
+    }
+
+    public FloatRange setRandom(Random rnd) {
+        random = rnd;
+        return this;
+    }
+
+    public Float[] toArray(float step) {
+        List<Float> range = new LinkedList<Float>();
+        double v = min;
+        while (v < max) {
+            range.add((float) v);
+            v += step;
+        }
+        return range.toArray(new Float[0]);
+    }
+
+    @Override
+    public String toString() {
+        return "FloatRange: " + min + " -> " + max;
+    }
+}
diff --git a/src/main/java/toxi/util/datatypes/GenericSet.java b/src/main/java/toxi/util/datatypes/GenericSet.java
new file mode 100644
index 0000000..10c4924
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/GenericSet.java
@@ -0,0 +1,133 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util.datatypes;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Random;
+
+import toxi.math.MathUtils;
+
+public class GenericSet<T> implements Iterable<T> {
+
+    protected ArrayList<T> items;
+    protected int currID = -1;
+    protected T current;
+
+    protected Random random = new Random();
+
+    public GenericSet(Collection<T> items) {
+        this.items = new ArrayList<T>(items);
+        pickRandom();
+    }
+
+    public GenericSet(T... obj) {
+        items = new ArrayList<T>(obj.length);
+        for (int i = 0; i < obj.length; i++) {
+            items.add(obj[i]);
+        }
+        if (items.size() > 0) {
+            pickRandom();
+        }
+    }
+
+    public boolean add(T obj) {
+        boolean isAdded = items.add(obj);
+        if (items.size() == 1) {
+            pickRandom();
+        }
+        return isAdded;
+    }
+
+    public boolean addAll(Collection<T> coll) {
+        return items.addAll(coll);
+    }
+
+    public void clear() {
+        items.clear();
+    }
+
+    public boolean contains(T obj) {
+        return items.contains(obj);
+    }
+
+    public GenericSet<T> copy() {
+        GenericSet<T> set = new GenericSet<T>(items);
+        set.current = current;
+        set.currID = currID;
+        set.random = random;
+        return set;
+    }
+
+    public T getCurrent() {
+        return current;
+    }
+
+    public ArrayList<T> getItems() {
+        return items;
+    }
+
+    public Iterator<T> iterator() {
+        return items.iterator();
+    }
+
+    public T pickRandom() {
+        currID = MathUtils.random(random, items.size());
+        current = items.get(currID);
+        return current;
+    }
+
+    public T pickRandomUnique() {
+        int size = items.size();
+        if (size > 1) {
+            int newID = currID;
+            while (newID == currID) {
+                newID = MathUtils.random(random, size);
+            }
+            currID = newID;
+        } else {
+            currID = 0;
+        }
+        current = items.get(currID);
+        return current;
+    }
+
+    public GenericSet<T> seed(long seed) {
+        random.setSeed(seed);
+        return this;
+    }
+
+    public void setRandom(Random rnd) {
+        random = rnd;
+    }
+
+    public int size() {
+        return items.size();
+    }
+}
diff --git a/src/main/java/toxi/util/datatypes/IntegerRange.java b/src/main/java/toxi/util/datatypes/IntegerRange.java
new file mode 100644
index 0000000..796d7b1
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/IntegerRange.java
@@ -0,0 +1,157 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util.datatypes;
+
+import java.util.List;
+import java.util.Random;
+
+import javax.xml.bind.annotation.XmlAttribute;
+
+import toxi.math.MathUtils;
+
+public class IntegerRange {
+
+    public static IntegerRange fromSamples(int... samples) {
+        int min = Integer.MAX_VALUE;
+        int max = Integer.MIN_VALUE;
+        for (int s : samples) {
+            min = MathUtils.min(min, s);
+            max = MathUtils.max(max, s);
+        }
+        return new IntegerRange(min, max);
+    }
+
+    public static IntegerRange fromSamples(List<Integer> samples) {
+        int min = Integer.MAX_VALUE;
+        int max = Integer.MIN_VALUE;
+        for (int s : samples) {
+            min = MathUtils.min(min, s);
+            max = MathUtils.max(max, s);
+        }
+        return new IntegerRange(min, max);
+    }
+
+    @XmlAttribute
+    public int min, max;
+
+    @XmlAttribute(name = "default")
+    public int currValue;
+
+    protected Random random = new Random();
+
+    public IntegerRange() {
+        this(0, 100);
+    }
+
+    public IntegerRange(int min, int max) {
+        // swap if necessary...
+        if (min > max) {
+            max ^= min;
+            min ^= max;
+            max ^= min;
+        }
+        this.min = min;
+        this.max = max;
+        this.currValue = min;
+    }
+
+    public int adjustCurrentBy(int val) {
+        return setCurrent(currValue + val);
+    }
+
+    public IntegerRange copy() {
+        IntegerRange range = new IntegerRange(min, max);
+        range.currValue = currValue;
+        range.random = random;
+        return range;
+    }
+
+    /**
+     * Returns the value at the normalized position
+     * <code>(0.0 = min ... 1.0 = max-1)</code> within the range. Since the max
+     * value is exclusive, the value returned for position 1.0 is the range max
+     * value minus 1. Also note the given position is not being clipped to the
+     * 0.0-1.0 interval, so when passing in values outside that interval will
+     * produce out-of-range values too.
+     * 
+     * @param perc
+     * @return value within the range
+     */
+    public final int getAt(float perc) {
+        return (int) (min + (max - min - 1) * perc);
+    }
+
+    public final int getCurrent() {
+        return currValue;
+    }
+
+    public final int getMedian() {
+        return (min + max) / 2;
+    }
+
+    public final int getRange() {
+        return max - min;
+    }
+
+    public final boolean isValueInRange(int val) {
+        return val >= min && val < max;
+    }
+
+    public int pickRandom() {
+        currValue = MathUtils.random(random, min, max);
+        return currValue;
+    }
+
+    public IntegerRange seed(long seed) {
+        random.setSeed(seed);
+        return this;
+    }
+
+    public int setCurrent(int val) {
+        currValue = MathUtils.clip(val, min, max);
+        return currValue;
+    }
+
+    public IntegerRange setRandom(Random rnd) {
+        random = rnd;
+        return this;
+    }
+
+    public Integer[] toArray() {
+        Integer[] range = new Integer[max - min];
+        for (int i = 0, j = min; j < max; i++, j++) {
+            range[i] = j;
+        }
+        return range;
+    }
+
+    @Override
+    public String toString() {
+        return "IntegerRange: " + min + " -> " + max;
+    }
+}
diff --git a/src/main/java/toxi/util/datatypes/IntegerSet.java b/src/main/java/toxi/util/datatypes/IntegerSet.java
new file mode 100644
index 0000000..5a4792d
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/IntegerSet.java
@@ -0,0 +1,100 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util.datatypes;
+
+import java.util.Random;
+
+import toxi.math.MathUtils;
+
+public class IntegerSet {
+
+    public int[] items;
+    public int currID = -1;
+    public int current;
+
+    private Random random = new Random();
+
+    public IntegerSet(int[] items) {
+        this.items = items;
+        pickRandom();
+    }
+
+    public IntegerSet(Integer... items) {
+        if (items.length > 0) {
+            this.items = new int[items.length];
+            for (int i = 0; i < items.length; i++) {
+                this.items[i] = items[i];
+            }
+            pickRandom();
+        } else {
+            throw new IllegalArgumentException("can't create empty IntegerSet");
+        }
+    }
+
+    public boolean contains(int value) {
+        for (int i = 0; i < items.length; i++) {
+            if (items[i] == value) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public int getCurrent() {
+        return current;
+    }
+
+    public int pickRandom() {
+        currID = MathUtils.random(random, items.length);
+        current = items[currID];
+        return current;
+    }
+
+    public int pickRandomUnique() {
+        if (items.length > 1) {
+            int newID = currID;
+            while (newID == currID) {
+                newID = MathUtils.random(random, items.length);
+            }
+            currID = newID;
+        } else {
+            currID = 0;
+        }
+        current = items[currID];
+        return current;
+    }
+
+    public IntegerSet seed(long seed) {
+        random.setSeed(seed);
+        return this;
+    }
+
+    public void setRandom(Random rnd) {
+        random = rnd;
+    }
+}
diff --git a/src/main/java/toxi/util/datatypes/ItemIndex.java b/src/main/java/toxi/util/datatypes/ItemIndex.java
new file mode 100644
index 0000000..4f09e85
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/ItemIndex.java
@@ -0,0 +1,25 @@
+package toxi.util.datatypes;
+
+import java.util.List;
+
+public interface ItemIndex<T> {
+
+    public void clear();
+
+    public T forID(int id);
+
+    public int getID(T item);
+
+    public List<T> getItems();
+
+    public int index(T item);
+
+    public boolean isIndexed(T item);
+
+    public int reindex(T item, T newItem);
+
+    public int size();
+
+    public int unindex(T item);
+
+}
\ No newline at end of file
diff --git a/src/main/java/toxi/util/datatypes/SingletonRegistry.java b/src/main/java/toxi/util/datatypes/SingletonRegistry.java
new file mode 100644
index 0000000..18b8b23
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/SingletonRegistry.java
@@ -0,0 +1,87 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util.datatypes;
+
+import java.util.HashMap;
+import java.util.logging.Logger;
+
+/**
+ * Implements a registry for dynamic singleton management. Use this registry
+ * instead of using "new" to enforce singletons of any class with a visible
+ * default constructor. The registry itself is implemented as singleton.
+ */
+public class SingletonRegistry {
+
+    /**
+     * The singleton instance of the registry itself.
+     */
+    public static final SingletonRegistry REGISTRY = new SingletonRegistry();
+
+    private static HashMap<String, Object> map = new HashMap<String, Object>();
+
+    private static Logger logger = Logger.getLogger(SingletonRegistry.class
+            .getName());
+
+    /**
+     * Creates or returns an instance of the class requested by name.
+     * 
+     * @param className
+     * @return class singleton instance
+     */
+    public static synchronized Object getInstanceOf(String className) {
+        Object instance = map.get(className);
+        if (instance != null) {
+            return instance;
+        }
+        try {
+            instance = Class.forName(className).newInstance();
+            map.put(className, instance);
+            logger.info("Created singleton: " + instance);
+        } catch (ClassNotFoundException cnf) {
+            logger.severe("Couldn't find class: " + className);
+        } catch (InstantiationException ie) {
+            logger.severe("Couldn't instantiate the class: " + className);
+        } catch (IllegalAccessException ia) {
+            logger.severe("Couldn't access class: " + className);
+        }
+        return instance;
+    }
+
+    /**
+     * Alternative, more conventional accessor to the singleton instance of the
+     * registry itself.
+     * 
+     * @return registry instance
+     */
+    public static SingletonRegistry getRegistry() {
+        return REGISTRY;
+    }
+
+    protected SingletonRegistry() {
+    }
+}
diff --git a/src/main/java/toxi/util/datatypes/TypedProperties.java b/src/main/java/toxi/util/datatypes/TypedProperties.java
new file mode 100644
index 0000000..56fd592
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/TypedProperties.java
@@ -0,0 +1,269 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util.datatypes;
+
+import java.io.FileInputStream;
+import java.util.HashMap;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.logging.Logger;
+
+/**
+ * Convenience wrapper providing typed access to Java {@link Properties} files.
+ */
+@SuppressWarnings("serial")
+public class TypedProperties extends Properties {
+
+    public static final String DELIM = "\t\n\r\f\u00A0,";
+
+    private static final Logger logger = Logger.getLogger(TypedProperties.class
+            .getName());
+
+    /**
+     * Returns a property as boolean.
+     * 
+     * @param id
+     *            property name
+     * @param defaultState
+     * @return prop value
+     */
+    public boolean getBoolean(String id, boolean defaultState) {
+        return Boolean.parseBoolean(getProperty(id, "" + defaultState));
+    }
+
+    /**
+     * Shorthand wrapper for {@link #getByteArray(String, byte[])} automatically
+     * supplying an empty byte[] as default value.
+     * 
+     * @param id
+     * @return prop values as array
+     */
+    public byte[] getByteArray(String id) {
+        return getByteArray(id, new byte[0]);
+    }
+
+    /**
+     * Returns a comma delimited property value as byte[] array. Non-byte values
+     * will be ignored.
+     * 
+     * @param id
+     *            prop name
+     * @return prop values as array
+     */
+    public byte[] getByteArray(String id, byte[] defaultArray) {
+        StringTokenizer tokenizer = new StringTokenizer(getProperty(id, ""),
+                DELIM);
+        byte[] pieces = new byte[tokenizer.countTokens()];
+        int index = 0;
+        while (tokenizer.hasMoreTokens()) {
+            try {
+                pieces[index] = Byte.parseByte(tokenizer.nextToken());
+                index++;
+            } catch (NumberFormatException e) {
+                // ignore non-integer items
+            }
+        }
+        if (index > 0) {
+            byte[] result = new byte[index];
+            System.arraycopy(pieces, 0, result, 0, index);
+            return result;
+        } else {
+            return defaultArray;
+        }
+    }
+
+    /**
+     * Returns a property as float.
+     * 
+     * @param id
+     * @param defaultValue
+     * @return prop value
+     */
+    public float getFloat(String id, float defaultValue) {
+        return Float.parseFloat(getProperty(id, "" + defaultValue));
+    }
+
+    /**
+     * Shorthand wrapper for {@link #getFloatArray(String, float[])}
+     * automatically supplying an empty float[] array as default value.
+     * 
+     * @param id
+     * @return prop values as array
+     */
+    public float[] getFloatArray(String id) {
+        return getFloatArray(id, new float[0]);
+    }
+
+    /**
+     * Returns a comma delimited property value as float[] array.
+     * 
+     * @param id
+     *            prop name
+     * @return prop items as array
+     */
+    public float[] getFloatArray(String id, float[] defaultArray) {
+        StringTokenizer tokenizer = new StringTokenizer(getProperty(id, ""),
+                DELIM);
+        float pieces[] = new float[tokenizer.countTokens()];
+        int index = 0;
+        while (tokenizer.hasMoreTokens()) {
+            try {
+                pieces[index] = Float.parseFloat(tokenizer.nextToken());
+                index++;
+            } catch (NumberFormatException e) {
+                // ignore NaN items
+            }
+        }
+        if (index > 0) {
+            float[] result = new float[index];
+            System.arraycopy(pieces, 0, result, 0, index);
+            return result;
+        } else {
+            return defaultArray;
+        }
+    }
+
+    /**
+     * Returns a hexadecimal property as integer
+     * 
+     * @param id
+     *            prop name
+     * @param defaultValue
+     * @return prop value
+     */
+    public int getHexInt(String id, int defaultValue) {
+        return Integer.parseInt(
+                getProperty(id, Integer.toHexString(defaultValue)), 16);
+    }
+
+    /**
+     * Returns a property as integer.
+     * 
+     * @param id
+     *            property name
+     * @param defaultValue
+     * @return prop value
+     */
+    public int getInt(String id, int defaultValue) {
+        return Integer.parseInt(getProperty(id, "" + defaultValue));
+    }
+
+    /**
+     * Shorthand wrapper for {{@link #getIntArray(String, int[])} automatically
+     * supplying an empty int[] array as default value.
+     * 
+     * @param id
+     * @return prop values as array
+     */
+    public int[] getIntArray(String id) {
+        return getIntArray(id, new int[0]);
+    }
+
+    /**
+     * Returns a comma delimited property value as int[] array. Non-integer
+     * items will be ignored.
+     * 
+     * @param id
+     *            prop name
+     * @return prop items as array
+     */
+    public int[] getIntArray(String id, int[] defaultArray) {
+        StringTokenizer tokenizer = new StringTokenizer(getProperty(id, ""),
+                DELIM);
+        int pieces[] = new int[tokenizer.countTokens()];
+        int index = 0;
+        while (tokenizer.hasMoreTokens()) {
+            try {
+                pieces[index] = Integer.parseInt(tokenizer.nextToken());
+                index++;
+            } catch (NumberFormatException e) {
+                // ignore non-integer items
+            }
+        }
+        if (index > 0) {
+            int[] result = new int[index];
+            System.arraycopy(pieces, 0, result, 0, index);
+            return result;
+        } else {
+            return defaultArray;
+        }
+    }
+
+    public String[] getStringArray(String id) {
+        return getStringArray(id, new String[0]);
+    }
+
+    public String[] getStringArray(String id, String[] defaultArray) {
+        StringTokenizer tokenizer = new StringTokenizer(getProperty(id, ""),
+                DELIM);
+        int index = 0;
+        String[] pieces = null;
+        while (tokenizer.hasMoreTokens()) {
+            if (pieces == null) {
+                pieces = new String[tokenizer.countTokens()];
+            }
+            String token = tokenizer.nextToken();
+            if (token.length() > 0) {
+                pieces[index++] = token;
+            }
+        }
+        if (index > 0) {
+            String[] result = new String[index];
+            System.arraycopy(pieces, 0, result, 0, index);
+            return result;
+        } else {
+            return defaultArray;
+        }
+    }
+
+    /**
+     * Attempts to load properties from the specified (absolute) file path (In
+     * Processing use sketchPath() or dataPath() to build absolute path).
+     * 
+     * @param path
+     *            config file
+     * @return true, if successful.
+     */
+    public boolean load(String path) {
+        try {
+            load(new FileInputStream(path));
+            return true;
+        } catch (Exception e) {
+            logger.warning("error opening config file: " + path);
+            return false;
+        }
+    }
+
+    public HashMap<String, String> toHashMap() {
+        HashMap<String, String> map = new HashMap<String, String>();
+        for (String id : stringPropertyNames()) {
+            map.put(id, getProperty(id));
+        }
+        return map;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/toxi/util/datatypes/UndirectedGraph.java b/src/main/java/toxi/util/datatypes/UndirectedGraph.java
new file mode 100644
index 0000000..856c07b
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/UndirectedGraph.java
@@ -0,0 +1,126 @@
+package toxi.util.datatypes;
+
+/*
+ * Copyright (c) 2007 by L. Paul Chew.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, 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 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.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Straightforward undirected graph implementation. Nodes are generic type N.
+ * 
+ * @author Paul Chew Created November, December 2007. For use in
+ *         Delaunay/Voronoi code.
+ * @author toxi Updated July 2010, minor API changes to be better suited for
+ *         toxiclibs
+ */
+public class UndirectedGraph<N> {
+
+    protected Map<N, Set<N>> nodeLinks = new HashMap<N, Set<N>>();
+    protected Set<N> nodeIDs = Collections.unmodifiableSet(nodeLinks.keySet());
+
+    /**
+     * Add a node. If node is already in graph then no change.
+     * 
+     * @param node
+     *            the node to add
+     */
+    public void add(N node) {
+        if (nodeLinks.containsKey(node)) {
+            return;
+        }
+        nodeLinks.put(node, new ArraySet<N>());
+    }
+
+    /**
+     * Add a link. If the link is already in graph then no change.
+     * 
+     * @param nodeA
+     *            one end of the link
+     * @param nodeB
+     *            the other end of the link
+     * @throws NullPointerException
+     *             if either endpoint is not in graph
+     */
+    public void connect(N nodeA, N nodeB) throws NullPointerException {
+        nodeLinks.get(nodeA).add(nodeB);
+        nodeLinks.get(nodeB).add(nodeA);
+    }
+
+    /**
+     * Remove the specified link. If link not in graph, nothing happens.
+     * 
+     * @param nodeA
+     *            one end of the link
+     * @param nodeB
+     *            the other end of the link
+     * @throws NullPointerException
+     *             if either endpoint is not in graph
+     */
+    public void disconnect(N nodeA, N nodeB) throws NullPointerException {
+        nodeLinks.get(nodeA).remove(nodeB);
+        nodeLinks.get(nodeB).remove(nodeA);
+    }
+
+    /**
+     * Report all the neighbors of node.
+     * 
+     * @param node
+     *            the node
+     * @return the neighbors of node
+     * @throws NullPointerException
+     *             if node does not appear in graph
+     */
+    public Set<N> getConnectedNodesFor(N node) throws NullPointerException {
+        return Collections.unmodifiableSet(nodeLinks.get(node));
+    }
+
+    /**
+     * Returns an unmodifiable Set view of the nodes contained in this graph.
+     * The set is backed by the graph, so changes to the graph are reflected in
+     * the set.
+     * 
+     * @return a Set view of the graph's node set
+     */
+    public Set<N> getNodes() {
+        return nodeIDs;
+    }
+
+    /**
+     * Remove node and any links that use node. If node not in graph, nothing
+     * happens.
+     * 
+     * @param node
+     *            the node to remove.
+     */
+    public void remove(N node) {
+        if (!nodeLinks.containsKey(node)) {
+            return;
+        }
+        for (N neighbor : nodeLinks.get(node)) {
+            nodeLinks.get(neighbor).remove(node);
+        }
+        nodeLinks.get(node).clear();
+        nodeLinks.remove(node);
+    }
+
+}
diff --git a/src/main/java/toxi/util/datatypes/UniqueItemIndex.java b/src/main/java/toxi/util/datatypes/UniqueItemIndex.java
new file mode 100644
index 0000000..e8b8f3e
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/UniqueItemIndex.java
@@ -0,0 +1,140 @@
+package toxi.util.datatypes;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This class is used to build an unique set of items and offers a
+ * bi-directional mapping between items and their associated ID values. Items
+ * are added via the {@link #index(Object)} method and removed via
+ * {@link #unindex(Object)}. The item's {@link #hashCode()} is used as unique
+ * identifier, so you MUST ensure the item class satisfies the contract of
+ * {@link #hashCode()} and {@link #equals(Object)}.
+ */
+public class UniqueItemIndex<T> implements ItemIndex<T> {
+
+    protected final HashMap<T, Integer> uniqueItems = new HashMap<T, Integer>();
+    protected final ArrayList<T> index = new ArrayList<T>();
+
+    public UniqueItemIndex() {
+    }
+
+    public UniqueItemIndex(Collection<T> items) {
+        for (T item : items) {
+            index(item);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.util.ItemIndex#clear()
+     */
+    public void clear() {
+        uniqueItems.clear();
+        index.clear();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.util.ItemIndex#forID(int)
+     */
+    public T forID(int id) {
+        return index.get(id);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.util.ItemIndex#getID(T)
+     */
+    public int getID(T item) {
+        Integer id = uniqueItems.get(item);
+        return (id != null) ? id : -1;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.util.ItemIndex#getItems()
+     */
+    public List<T> getItems() {
+        return index;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.util.ItemIndex#index(T)
+     */
+    public int index(T item) {
+        Integer id = uniqueItems.get(item);
+        if (id == null) {
+            id = index.size();
+            uniqueItems.put(item, id);
+            index.add(item);
+        }
+        return id;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.util.ItemIndex#isIndexed(T)
+     */
+    public boolean isIndexed(T item) {
+        return uniqueItems.get(item) != null;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.util.ItemIndex#reindex(T, T)
+     */
+    public int reindex(T item, T newItem) {
+        int id = getID(item);
+        if (id != -1) {
+            int newID = getID(newItem);
+            if (newID != -1) {
+                unindex(item);
+                id = getID(newItem);
+            } else {
+                index.set(id, newItem);
+                uniqueItems.remove(item);
+                uniqueItems.put(newItem, id);
+            }
+        }
+        return id;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.util.ItemIndex#size()
+     */
+    public int size() {
+        return index.size();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see toxi.util.ItemIndex#unindex(T)
+     */
+    public int unindex(T item) {
+        Integer id = uniqueItems.get(item);
+        if (id != null) {
+            uniqueItems.remove(item);
+            index.remove(item);
+            for (int i = id, num = index.size(); i < num; i++) {
+                uniqueItems.put(index.get(i), i);
+            }
+        } else {
+            id = -1;
+        }
+        return id;
+    }
+}
diff --git a/src/main/java/toxi/util/datatypes/WeightedRandomEntry.java b/src/main/java/toxi/util/datatypes/WeightedRandomEntry.java
new file mode 100644
index 0000000..4cd2a03
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/WeightedRandomEntry.java
@@ -0,0 +1,53 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util.datatypes;
+
+/**
+ * This class encapsulates a single element within a {@link WeightedRandomSet}.
+ * 
+ * @param <T>
+ */
+public class WeightedRandomEntry<T> implements
+        Comparable<WeightedRandomEntry<T>> {
+
+    protected T item;
+    protected int weight;
+
+    public WeightedRandomEntry(T item, int weight) {
+        this.item = item;
+        this.weight = weight;
+    }
+
+    public int compareTo(WeightedRandomEntry<T> e) {
+        return (e.weight - weight);
+    }
+
+    public String toString() {
+        return item.toString() + ": " + weight;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/toxi/util/datatypes/WeightedRandomSet.java b/src/main/java/toxi/util/datatypes/WeightedRandomSet.java
new file mode 100644
index 0000000..a53bb42
--- /dev/null
+++ b/src/main/java/toxi/util/datatypes/WeightedRandomSet.java
@@ -0,0 +1,118 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util.datatypes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import toxi.math.MathUtils;
+
+/**
+ * This class provides a generic random-weight distribution of arbitary objects.
+ * Add elements with their weight to the set and then use the
+ * {@link #getRandom()} method to retrieve objects. The frequency of returned
+ * elements is based on their relative weight. This makes it easy to provide
+ * biased preferences.
+ * 
+ * http://www.electricmonk.nl/log/2009/12/23/weighted-random-distribution/
+ */
+public class WeightedRandomSet<T> {
+
+    protected List<WeightedRandomEntry<T>> elements = new ArrayList<WeightedRandomEntry<T>>();
+
+    protected int totalWeight;
+
+    /**
+     * Add a new element of type T to the set.
+     * 
+     * @param item
+     * @param weight
+     * @return itself
+     */
+    public WeightedRandomSet<T> add(T item, int weight) {
+        WeightedRandomEntry<T> e = new WeightedRandomEntry<T>(item, weight);
+        int num = elements.size();
+        boolean isInserted = false;
+        if (num > 0) {
+            for (int i = 0; i < num; i++) {
+                if (weight < elements.get(i).weight) {
+                    elements.add(i, e);
+                    isInserted = true;
+                    break;
+                }
+            }
+        }
+        if (!isInserted) {
+            elements.add(e);
+        }
+        totalWeight += weight;
+        return this;
+    }
+
+    /**
+     * @return the elements
+     */
+    public List<WeightedRandomEntry<T>> getElements() {
+        return elements;
+    }
+
+    /**
+     * Returns a randomly picked element from the set. The frequency of
+     * occurance depends on the relative weight of each item.
+     * 
+     * @return picked element
+     */
+    public T getRandom() {
+        int rnd = MathUtils.random(totalWeight);
+        T choice = null;
+        int sum = totalWeight;
+        for (WeightedRandomEntry<T> e : elements) {
+            sum -= e.weight;
+            if (sum <= rnd) {
+                choice = e.item;
+                break;
+            }
+        }
+        return choice;
+    }
+
+    /**
+     * Removes the given item from the set.
+     * 
+     * @param item
+     */
+    public void remove(T item) {
+        for (WeightedRandomEntry<T> e : elements) {
+            if (e.item.equals(item)) {
+                elements.remove(e);
+                totalWeight -= e.weight;
+                return;
+            }
+        }
+    }
+}
diff --git a/src/main/java/toxi/util/events/EventDispatcher.java b/src/main/java/toxi/util/events/EventDispatcher.java
new file mode 100644
index 0000000..14e5a4b
--- /dev/null
+++ b/src/main/java/toxi/util/events/EventDispatcher.java
@@ -0,0 +1,58 @@
+/*
+ *   __               .__       .__  ._____.           
+ * _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
+ * \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
+ *  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \ 
+ *  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
+ *                   \/       \/             \/     \/ 
+ *
+ * Copyright (c) 2006-2011 Karsten Schmidt
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * http://creativecommons.org/licenses/LGPL/2.1/
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package toxi.util.events;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+public class EventDispatcher<T> implements Iterable<T> {
+
+    protected List<T> listeners = new LinkedList<T>();
+
+    public EventDispatcher() {
+    }
+
+    public void addListener(T listener) {
+        if (!listeners.contains(listener)) {
+            listeners.add(listener);
+        }
+    }
+
+    public List<T> getListeners() {
+        return listeners;
+    }
+
+    public Iterator<T> iterator() {
+        return listeners.iterator();
+    }
+
+    public void removeListener(T listener) {
+        listeners.remove(listener);
+    }
+}
-- 
GitLab