[Arakhnę-Dev] [388] * Add the new modules: math, util, attrs, ui, ui-awt, ui-base, ui-swing, ui-vector, ui-vector-awt, ui-vector-android.

[ Thread Index | Date Index | More arakhne.org/dev Archives ]


Revision: 388
Author:   galland
Date:     2013-03-20 15:54:50 +0100 (Wed, 20 Mar 2013)
Log Message:
-----------
* Add the new modules: math, util, attrs, ui, ui-awt, ui-base, ui-swing, ui-vector, ui-vector-awt, ui-vector-android.

Modified Paths:
--------------
    trunk/arakhneVmutils/arakhneVmutils-java/src/main/java/org/arakhne/vmutil/URLHandlerUtil.java
    trunk/arakhneVmutils/arakhneVmutils-java/src/main/java/org/arakhne/vmutil/file/HandlerFactory.java
    trunk/pom.xml

Added Paths:
-----------
    trunk/attrs/
    trunk/attrs/pom.xml
    trunk/attrs/src/
    trunk/attrs/src/main/
    trunk/attrs/src/main/java/
    trunk/attrs/src/main/java/org/
    trunk/attrs/src/main/java/org/arakhne/
    trunk/attrs/src/main/java/org/arakhne/afc/
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/Attribute.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeComparator.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeConstants.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeError.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeException.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeImpl.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeNameComparator.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeNotInitializedException.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeType.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeValue.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeValueComparator.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeValueImpl.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/InvalidAttributeTypeException.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/NullAttribute.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/Timestamp.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AbstractAttributeCollection.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AbstractAttributeProvider.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AbstractBufferedAttributeProvider.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeChangeEvent.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeChangeListener.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeCollection.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeIterator.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeNameStringComparator.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeProvider.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/BufferedAttributeCollection.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/HeapAttributeCollection.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/MultiAttributeCollection.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/MultiAttributeProvider.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/NoAttributeFoundException.java
    trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/ROMBasedAttributeCollection.java
    trunk/attrs/src/main/resources/
    trunk/attrs/src/main/resources/org/
    trunk/attrs/src/main/resources/org/arakhne/
    trunk/attrs/src/main/resources/org/arakhne/afc/
    trunk/attrs/src/main/resources/org/arakhne/afc/attrs/
    trunk/attrs/src/main/resources/org/arakhne/afc/attrs/types.properties
    trunk/attrs/src/main/resources/org/arakhne/afc/attrs/types_es.properties
    trunk/attrs/src/main/resources/org/arakhne/afc/attrs/types_fr.properties
    trunk/attrs/src/test/
    trunk/attrs/src/test/java/
    trunk/attrs/src/test/java/org/
    trunk/attrs/src/test/java/org/arakhne/
    trunk/attrs/src/test/java/org/arakhne/afc/
    trunk/attrs/src/test/java/org/arakhne/afc/attrs/
    trunk/attrs/src/test/java/org/arakhne/afc/attrs/AbstractAttrTestCase.java
    trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/
    trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeComparatorTest.java
    trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeTest.java
    trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeTypeTest.java
    trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeValueComparatorTest.java
    trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeValueTest.java
    trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/
    trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/AbstractAttributeCollectionTest.java
    trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/AbstractAttributeProviderTest.java
    trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/AbstractBufferedAttributeProviderTest.java
    trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/BufferedAttributeCollectionTest.java
    trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/HeapAttributeCollectionTest.java
    trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/MultiAttributeCollectionTest.java
    trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/MultiAttributeProviderTest.java
    trunk/math/
    trunk/math/pom.xml
    trunk/math/src/
    trunk/math/src/javadoc/
    trunk/math/src/javadoc/org/
    trunk/math/src/javadoc/org/arakhne/
    trunk/math/src/javadoc/org/arakhne/math/
    trunk/math/src/javadoc/org/arakhne/math/discrete/
    trunk/math/src/javadoc/org/arakhne/math/discrete/object2d/
    trunk/math/src/javadoc/org/arakhne/math/discrete/object2d/doc-files/
    trunk/math/src/javadoc/org/arakhne/math/discrete/object2d/doc-files/crossing_circle.png
    trunk/math/src/javadoc/org/arakhne/math/discrete/object2d/doc-files/crossing_point.png
    trunk/math/src/javadoc/org/arakhne/math/discrete/object2d/doc-files/crossing_rect.png
    trunk/math/src/javadoc/org/arakhne/math/discrete/object2d/doc-files/crossing_segment.png
    trunk/math/src/javadoc/org/arakhne/math/generic/
    trunk/math/src/javadoc/org/arakhne/math/generic/doc-files/
    trunk/math/src/javadoc/org/arakhne/math/generic/doc-files/fillrule-evenodd.png
    trunk/math/src/javadoc/org/arakhne/math/generic/doc-files/fillrule-nonzero.png
    trunk/math/src/main/
    trunk/math/src/main/java/
    trunk/math/src/main/java/org/
    trunk/math/src/main/java/org/arakhne/
    trunk/math/src/main/java/org/arakhne/afc/
    trunk/math/src/main/java/org/arakhne/afc/math/
    trunk/math/src/main/java/org/arakhne/afc/math/MathConstants.java
    trunk/math/src/main/java/org/arakhne/afc/math/MathUtil.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/AbstractRectangularShape2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/AbstractShape2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Circle2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Ellipse2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Path2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/PathElement2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/PathElementType.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/PathIterator2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/PathWindingRule.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Point2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Rectangle2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/RoundRectangle2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Segment2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Shape2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Tuple2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Tuple2fComparator.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/UnmodifiablePoint2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Vector2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Point3f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Quaternion.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Tuple3f.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Tuple3fComparator.java
    trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Vector3f.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/AbstractRectangularShape2i.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/AbstractShape2i.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Circle2i.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Path2i.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/PathElement2i.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/PathIterator2i.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Point2i.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Rectangle2i.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Segment2i.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Shape2i.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Tuple2i.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Tuple2iComparator.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Vector2i.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/Point3i.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/Tuple3i.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/Tuple3iComparator.java
    trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/Vector3i.java
    trunk/math/src/main/java/org/arakhne/afc/math/generic/
    trunk/math/src/main/java/org/arakhne/afc/math/generic/PathElementType.java
    trunk/math/src/main/java/org/arakhne/afc/math/generic/PathWindingRule.java
    trunk/math/src/main/java/org/arakhne/afc/math/generic/Point2D.java
    trunk/math/src/main/java/org/arakhne/afc/math/generic/Point3D.java
    trunk/math/src/main/java/org/arakhne/afc/math/generic/Shape2D.java
    trunk/math/src/main/java/org/arakhne/afc/math/generic/Tuple2D.java
    trunk/math/src/main/java/org/arakhne/afc/math/generic/Tuple3D.java
    trunk/math/src/main/java/org/arakhne/afc/math/generic/Vector2D.java
    trunk/math/src/main/java/org/arakhne/afc/math/generic/Vector3D.java
    trunk/math/src/main/java/org/arakhne/afc/math/matrix/
    trunk/math/src/main/java/org/arakhne/afc/math/matrix/Matrix2f.java
    trunk/math/src/main/java/org/arakhne/afc/math/matrix/Matrix3f.java
    trunk/math/src/main/java/org/arakhne/afc/math/matrix/Matrix4f.java
    trunk/math/src/main/java/org/arakhne/afc/math/matrix/SingularMatrixException.java
    trunk/math/src/main/java/org/arakhne/afc/math/matrix/Transform2D.java
    trunk/math/src/main/java/org/arakhne/afc/math/matrix/Transform3D.java
    trunk/math/src/main/resources/
    trunk/math/src/main/resources/org/
    trunk/math/src/main/resources/org/arakhne/
    trunk/math/src/main/resources/org/arakhne/afc/
    trunk/math/src/main/resources/org/arakhne/afc/math/
    trunk/math/src/main/resources/org/arakhne/afc/math/continuous/
    trunk/math/src/main/resources/org/arakhne/afc/math/continuous/Matrix3f.properties
    trunk/math/src/main/resources/org/arakhne/afc/math/continuous/Matrix3f_fr.properties
    trunk/math/src/test/
    trunk/math/src/test/java/
    trunk/math/src/test/java/org/
    trunk/math/src/test/java/org/arakhne/
    trunk/math/src/test/java/org/arakhne/afc/
    trunk/math/src/test/java/org/arakhne/afc/math/
    trunk/math/src/test/java/org/arakhne/afc/math/AbstractMathTestCase.java
    trunk/math/src/test/java/org/arakhne/afc/math/continous/
    trunk/math/src/test/java/org/arakhne/afc/math/continous/matrix/
    trunk/math/src/test/java/org/arakhne/afc/math/continous/matrix/Matrix2fTest.java
    trunk/math/src/test/java/org/arakhne/afc/math/continous/matrix/Transform2DTest.java
    trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/
    trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/AbstractRectangularShape2fTestCase.java
    trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/AbstractShape2fTestCase.java
    trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Circle2fTest.java
    trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Ellipse2fTest.java
    trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Path2fPointCollectionTest.java
    trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Path2fTest.java
    trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Rectangle2fTest.java
    trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/RoundRectangle2fTest.java
    trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Segment2fTest.java
    trunk/math/src/test/java/org/arakhne/afc/math/discrete/
    trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/
    trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/AbstractRectangularShape2iTestCase.java
    trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/AbstractShape2iTestCase.java
    trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/Circle2iTest.java
    trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/Path2iTest.java
    trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/Rectangle2iTest.java
    trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/Segment2iTest.java
    trunk/math/src/test/java/org/arakhne/afc/math/matrix/
    trunk/math/src/test/java/org/arakhne/afc/math/matrix/Matrix2fTest.java
    trunk/math/src/test/java/org/arakhne/afc/math/matrix/Transform2DTest.java
    trunk/math/src/test/resources/
    trunk/math/src/test/resources/org/
    trunk/math/src/test/resources/org/arakhne/
    trunk/math/src/test/resources/org/arakhne/afc/
    trunk/math/src/test/resources/org/arakhne/afc/math/
    trunk/math/src/test/resources/org/arakhne/afc/math/discrete/
    trunk/math/src/test/resources/org/arakhne/afc/math/discrete/object2d/
    trunk/math/src/test/resources/org/arakhne/afc/math/discrete/object2d/Segment2i.ods
    trunk/ui/
    trunk/ui/pom.xml
    trunk/ui/ui-awt/
    trunk/ui/ui-awt/pom.xml
    trunk/ui/ui-awt/src/
    trunk/ui/ui-awt/src/main/
    trunk/ui/ui-awt/src/main/java/
    trunk/ui/ui-awt/src/main/java/org/
    trunk/ui/ui-awt/src/main/java/org/arakhne/
    trunk/ui/ui-awt/src/main/java/org/arakhne/afc/
    trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/
    trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/
    trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/AbstractLODGraphics2D.java
    trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/AwtUtil.java
    trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/DefaultLODGraphics2D.java
    trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/DoubleDimension.java
    trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/ExceptionListener.java
    trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/FloatDimension.java
    trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/FontComparator.java
    trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/LODGraphics2D.java
    trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/SupportedShape.java
    trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/UnsupportedShapeException.java
    trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/VirtualScreenGraphics2D.java
    trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/VirtualizableShape.java
    trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/ZoomableContextUtil.java
    trunk/ui/ui-awt/src/main/resources/
    trunk/ui/ui-awt/src/test/
    trunk/ui/ui-awt/src/test/java/
    trunk/ui/ui-awt/src/test/resources/
    trunk/ui/ui-base/
    trunk/ui/ui-base/pom.xml
    trunk/ui/ui-base/src/
    trunk/ui/ui-base/src/main/
    trunk/ui/ui-base/src/main/java/
    trunk/ui/ui-base/src/main/java/org/
    trunk/ui/ui-base/src/main/java/org/arakhne/
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/Graphics2DLOD.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/MouseCursor.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/StringAnchor.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/TextAlignment.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/ZoomableContext.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionMode.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeAdapter.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeException.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeListener.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeManager.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeManagerOwner.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/SelectableInteractionAdapter.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/SelectableInteractionEvent.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/SelectableInteractionListener.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/event/
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/event/InputEvent.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/event/KeyEvent.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/event/PointerEvent.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/Selectable.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/SelectionEvent.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/SelectionListener.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/SelectionManager.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/TreeSetSelectionManager.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/AbstractUndoable.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/DefaultUndoManager.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/UndoListener.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/UndoManager.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/Undoable.java
    trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/UndoableGroup.java
    trunk/ui/ui-base/src/main/resources/
    trunk/ui/ui-base/src/main/resources/org/
    trunk/ui/ui-base/src/main/resources/org/arakhne/
    trunk/ui/ui-base/src/main/resources/org/arakhne/afc/
    trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/
    trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/
    trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/ActionMode.properties
    trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/ActionModeManager.properties
    trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/ActionModeManager_fr.properties
    trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/ActionMode_fr.properties
    trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/undo/
    trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/undo/DefaultUndoManager.properties
    trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/undo/DefaultUndoManager_fr.properties
    trunk/ui/ui-base/src/test/
    trunk/ui/ui-base/src/test/java/
    trunk/ui/ui-base/src/test/resources/
    trunk/ui/ui-swing/
    trunk/ui/ui-swing/pom.xml
    trunk/ui/ui-swing/src/
    trunk/ui/ui-swing/src/main/
    trunk/ui/ui-swing/src/main/java/
    trunk/ui/ui-swing/src/main/java/org/
    trunk/ui/ui-swing/src/main/java/org/arakhne/
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/FileFilterSwing.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/JColorSelector.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/JGroupButton.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/JPopupTextField.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/StandardAction.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/event/
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/event/KeyEventSwing.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/event/PointerEventSwing.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/progress/
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/progress/ProgressBarModel.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/progress/ProgressMonitor.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/progress/ProgressionProgressBarWrapper.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/AbstractCallableUndoableEdit.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/UndoManagerSwing.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/UndoableAction.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/UndoableGroupSwing.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/InternalZoomablePanel.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/ScrollingMethod.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/ZoomableGraphics2D.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/ZoomablePanel.java
    trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/ZoomableViewport.java
    trunk/ui/ui-swing/src/main/resources/
    trunk/ui/ui-swing/src/test/
    trunk/ui/ui-swing/src/test/java/
    trunk/ui/ui-swing/src/test/resources/
    trunk/ui/ui-vector/
    trunk/ui/ui-vector/pom.xml
    trunk/ui/ui-vector/src/
    trunk/ui/ui-vector/src/main/
    trunk/ui/ui-vector/src/main/java/
    trunk/ui/ui-vector/src/main/java/org/
    trunk/ui/ui-vector/src/main/java/org/arakhne/
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/AbstractVectorGraphics2D.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Color.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Composite.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Dimension.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Font.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/FontComparator.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/FontMetrics.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/FontStyle.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Image.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/ImageObserver.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Margins.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Paint.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/PathUtil.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Pdf.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Raster.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Stroke.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/VectorGraphics2D.java
    trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/VectorToolkit.java
    trunk/ui/ui-vector/src/main/resources/
    trunk/ui/ui-vector/src/main/resources/org/
    trunk/ui/ui-vector/src/main/resources/org/arakhne/
    trunk/ui/ui-vector/src/main/resources/org/arakhne/afc/
    trunk/ui/ui-vector/src/main/resources/org/arakhne/afc/ui/
    trunk/ui/ui-vector/src/main/resources/org/arakhne/afc/ui/vector/
    trunk/ui/ui-vector/src/main/resources/org/arakhne/afc/ui/vector/VectorToolkit.properties
    trunk/ui/ui-vector/src/main/resources/org/arakhne/afc/ui/vector/no_picture.png
    trunk/ui/ui-vector/src/test/
    trunk/ui/ui-vector/src/test/java/
    trunk/ui/ui-vector/src/test/resources/
    trunk/ui/ui-vector-android/
    trunk/ui/ui-vector-android/pom.xml
    trunk/ui/ui-vector-android/src/
    trunk/ui/ui-vector-android/src/main/
    trunk/ui/ui-vector-android/src/main/java/
    trunk/ui/ui-vector-android/src/main/java/org/
    trunk/ui/ui-vector-android/src/main/java/org/arakhne/
    trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/
    trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/
    trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/
    trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/
    trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidColor.java
    trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidComposite.java
    trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidDimension.java
    trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidImage.java
    trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidMargins.java
    trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidPaint.java
    trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidPdf.java
    trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidRaster.java
    trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidVectorToolkit.java
    trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/DelegatedVectorGraphics2D.java
    trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/NativeWrapper.java
    trunk/ui/ui-vector-android/src/main/javadoc/
    trunk/ui/ui-vector-android/src/main/resources/
    trunk/ui/ui-vector-android/src/test/
    trunk/ui/ui-vector-android/src/test/java/
    trunk/ui/ui-vector-android/src/test/resources/
    trunk/ui/ui-vector-awt/
    trunk/ui/ui-vector-awt/pom.xml
    trunk/ui/ui-vector-awt/src/
    trunk/ui/ui-vector-awt/src/main/
    trunk/ui/ui-vector-awt/src/main/java/
    trunk/ui/ui-vector-awt/src/main/java/org/
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtBufferedImage.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtColor.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtComposite.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtDimension.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtFont.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtFontMetrics.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtImage.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtImageObserver.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtMargins.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtPaint.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtPath.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtPathIterator.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtPdf.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtRaster.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtStroke.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtVectorToolkit.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/DelegatedVectorGraphics2D.java
    trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/NativeWrapper.java
    trunk/ui/ui-vector-awt/src/main/resources/
    trunk/ui/ui-vector-awt/src/test/
    trunk/ui/ui-vector-awt/src/test/java/
    trunk/ui/ui-vector-awt/src/test/resources/
    trunk/util/
    trunk/util/pom.xml
    trunk/util/src/
    trunk/util/src/main/
    trunk/util/src/main/java/
    trunk/util/src/main/java/org/
    trunk/util/src/main/java/org/arakhne/
    trunk/util/src/main/java/org/arakhne/afc/
    trunk/util/src/main/java/org/arakhne/afc/io/
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/AbstractFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/BMPFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/DOTFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/EPSFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/FileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/GIFFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/GMLFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/GXLFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/GraphMLFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/ImageFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/JPEGFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/JavaFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/MultiFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/NGRFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/PDFFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/PNGFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/SVGFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/filefilter/XMLFileFilter.java
    trunk/util/src/main/java/org/arakhne/afc/io/stream/
    trunk/util/src/main/java/org/arakhne/afc/io/stream/ReaderInputStream.java
    trunk/util/src/main/java/org/arakhne/afc/io/stream/UnclosableInputStream.java
    trunk/util/src/main/java/org/arakhne/afc/io/stream/UnclosableOutputStream.java
    trunk/util/src/main/java/org/arakhne/afc/io/stream/WriterOutputStream.java
    trunk/util/src/main/java/org/arakhne/afc/progress/
    trunk/util/src/main/java/org/arakhne/afc/progress/DefaultProgression.java
    trunk/util/src/main/java/org/arakhne/afc/progress/Progression.java
    trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionAdapter.java
    trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionConsoleMonitor.java
    trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionEvent.java
    trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionInputStream.java
    trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionListener.java
    trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionOuputStream.java
    trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionUtil.java
    trunk/util/src/main/java/org/arakhne/afc/progress/SubProgressionModel.java
    trunk/util/src/main/java/org/arakhne/afc/sizediterator/
    trunk/util/src/main/java/org/arakhne/afc/sizediterator/EmptyIterator.java
    trunk/util/src/main/java/org/arakhne/afc/sizediterator/ModifiableCollectionSizedIterator.java
    trunk/util/src/main/java/org/arakhne/afc/sizediterator/ModifiableCollectionSizedIteratorOwner.java
    trunk/util/src/main/java/org/arakhne/afc/sizediterator/MultiSizedIterator.java
    trunk/util/src/main/java/org/arakhne/afc/sizediterator/SizedIterator.java
    trunk/util/src/main/java/org/arakhne/afc/sizediterator/UnmodifiableCollectionSizedIterator.java
    trunk/util/src/main/java/org/arakhne/afc/sizediterator/UnmodifiableMapKeySizedIterator.java
    trunk/util/src/main/java/org/arakhne/afc/sizediterator/UnmodifiableMapValueSizedIterator.java
    trunk/util/src/main/java/org/arakhne/afc/sizediterator/UnmodifiableSizedIterator.java
    trunk/util/src/main/java/org/arakhne/afc/text/
    trunk/util/src/main/java/org/arakhne/afc/text/TextUtil.java
    trunk/util/src/main/java/org/arakhne/afc/util/
    trunk/util/src/main/java/org/arakhne/afc/util/HashCodeUtil.java
    trunk/util/src/main/java/org/arakhne/afc/util/ListUtil.java
    trunk/util/src/main/java/org/arakhne/afc/util/ListenerCollection.java
    trunk/util/src/main/java/org/arakhne/afc/util/MultiValue.java
    trunk/util/src/main/java/org/arakhne/afc/util/Pair.java
    trunk/util/src/main/java/org/arakhne/afc/util/PropertyOwner.java
    trunk/util/src/main/java/org/arakhne/afc/util/UnmodifiableIterator.java
    trunk/util/src/main/resources/
    trunk/util/src/main/resources/org/
    trunk/util/src/main/resources/org/arakhne/
    trunk/util/src/main/resources/org/arakhne/afc/
    trunk/util/src/main/resources/org/arakhne/afc/io/
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/BMPFileFilter.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/BMPFileFilter_fr.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/DOTFileFilter.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/DOTFileFilter_fr.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/EPSFileFilter.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/EPSFileFilter_fr.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GIFFileFilter.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GIFFileFilter_fr.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GMLFileFilter.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GMLFileFilter_fr.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GXLFileFilter.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GXLFileFilter_fr.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GraphMLFileFilter.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GraphMLFileFilter_fr.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/ImageFileFilter.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/ImageFileFilter_fr.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/JPEGFileFilter.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/JPEGFileFilter_fr.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/JavaFileFilter.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/JavaFileFilter_fr.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/NGRFileFilter.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/NGRFileFilter_fr.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/PDFFileFilter.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/PDFFileFilter_fr.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/PNGFileFilter.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/PNGFileFilter_fr.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/SVGFileFilter.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/SVGFileFilter_fr.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/XMLFileFilter.properties
    trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/XMLFileFilter_fr.properties
    trunk/util/src/main/resources/org/arakhne/afc/util/
    trunk/util/src/main/resources/org/arakhne/afc/util/MultiValue.properties
    trunk/util/src/main/resources/org/arakhne/afc/util/MultiValue_fr.properties
    trunk/util/src/test/
    trunk/util/src/test/java/
    trunk/util/src/test/resources/

Modified: trunk/arakhneVmutils/arakhneVmutils-java/src/main/java/org/arakhne/vmutil/URLHandlerUtil.java
===================================================================
--- trunk/arakhneVmutils/arakhneVmutils-java/src/main/java/org/arakhne/vmutil/URLHandlerUtil.java	2013-01-14 15:11:56 UTC (rev 387)
+++ trunk/arakhneVmutils/arakhneVmutils-java/src/main/java/org/arakhne/vmutil/URLHandlerUtil.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -2,7 +2,7 @@
  * $Id$
  * 
  * Copyright (C) 2004-2009 Stephane GALLAND.
- * Copyright (C) 2012 Stephane GALLAND.
+ * Copyright (C) 2012-13 Stephane GALLAND.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -22,9 +22,12 @@
 
 package org.arakhne.vmutil;
 
+import java.net.URLStreamHandler;
 import java.util.Arrays;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.NoSuchElementException;
 
 /** Utilities around URLHandler.
  * 
@@ -38,6 +41,16 @@
 
 	private static final String HANDLER_PACKAGES = "java.protocol.handler.pkgs"; //$NON-NLS-1$
 	
+	/** Replies an iterator on the handlers that are supporting the given protocol.
+	 * 
+	 * @param protocol
+	 * @return the iterator.
+	 * @since 7.2
+	 */
+	public static Iterator<Class<? extends URLStreamHandler>> getHandlersFor(String protocol) {
+		return new HandlerIterator(protocol);
+	}
+	
 	private static void install(String... packageNames) {
 		List<String> array = new LinkedList<String>();
 
@@ -116,5 +129,73 @@
 		uninstall(URLHandlerUtil.class.getPackage().getName());
 	}
 
+	/**
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 * @since 7.2
+	 */
+	private static class HandlerIterator implements Iterator<Class<? extends URLStreamHandler>> {
+
+		private final String protocol;
+		private final String[] packages;
+		private int position = 0;
+		
+		private Class<? extends URLStreamHandler> next;
+		
+		/**
+		 * @param protocol
+		 */
+		public HandlerIterator(String protocol) {
+			this.protocol = protocol;
+			String str = System.getProperty(HANDLER_PACKAGES);
+			if (str==null)
+				this.packages = new String[0];
+			else
+				this.packages = str.split("\\|"); //$NON-NLS-1$
+			
+			searchNext();
+		}
+		
+		@SuppressWarnings("unchecked")
+		private void searchNext() {
+			this.next = null;
+			ClassLoader clsLoader = getClass().getClassLoader();
+			while (this.next==null && this.position<this.packages.length) {
+				String typename = this.packages[this.position++];
+
+				try {
+					Class<?> type = clsLoader.loadClass(typename+"."+this.protocol+".Handler"); //$NON-NLS-1$ //$NON-NLS-2$
+					if (type!=null && URLStreamHandler.class.isAssignableFrom(type)) {
+						this.next = (Class<? extends URLStreamHandler>)type;
+					}
+				}
+				catch(Throwable _) {
+					//
+				}
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.next!=null;
+		}
+
+		@Override
+		public Class<? extends URLStreamHandler> next() {
+			Class<? extends URLStreamHandler> n = this.next;
+			if (n==null) throw new NoSuchElementException();
+			searchNext();
+			return n;
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+		
+	}
+	
 }
 

Modified: trunk/arakhneVmutils/arakhneVmutils-java/src/main/java/org/arakhne/vmutil/file/HandlerFactory.java
===================================================================
--- trunk/arakhneVmutils/arakhneVmutils-java/src/main/java/org/arakhne/vmutil/file/HandlerFactory.java	2013-01-14 15:11:56 UTC (rev 387)
+++ trunk/arakhneVmutils/arakhneVmutils-java/src/main/java/org/arakhne/vmutil/file/HandlerFactory.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -1,7 +1,7 @@
 /* 
  * $Id$
  * 
- * Copyright (C) 2010 Stephane GALLAND.
+ * Copyright (C) 2010-13 Stephane GALLAND.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public


Property changes on: trunk/attrs
___________________________________________________________________
Added: svn:ignore
   + .classpath
..metadata
..settings
..project
target
bin


Added: trunk/attrs/pom.xml
===================================================================
--- trunk/attrs/pom.xml	                        (rev 0)
+++ trunk/attrs/pom.xml	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,46 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<artifactId>afc</artifactId>
+		<groupId>org.arakhne.afc</groupId>
+		<version>4.5-SNAPSHOT</version>
+	</parent>
+
+	<groupId>org.arakhne.afc</groupId>
+	<artifactId>attrs</artifactId>
+	<version>1.0-SNAPSHOT</version>
+	<name>Arakhne Attribute API</name>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.arakhne.afc</groupId>
+			<artifactId>arakhneVmutils</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.arakhne.afc</groupId>
+			<artifactId>arakhneRefs</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.arakhne.afc</groupId>
+			<artifactId>math</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.arakhne.afc.ui</groupId>
+			<artifactId>ui-vector</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+			<optional>true</optional>
+		</dependency>
+		<dependency>
+			<groupId>org.arakhne.afc.ui</groupId>
+			<artifactId>ui-vector-awt</artifactId>
+			<scope>test</scope>
+			<optional>true</optional>
+		</dependency>
+	</dependencies>
+
+</project>

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/Attribute.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/Attribute.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/Attribute.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,79 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+import java.util.Comparator;
+
+
+/**
+ * This interface contains a metadata with a name.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Attribute extends AttributeValue, AttributeConstants {
+
+	/**
+	 * Replies a comparator suitable for attribute based on the names
+	 * of the attributes only.
+	 * 
+	 * @return a comparator, never <code>null</code>
+	 * @see #valueComparator()
+	 * @see #comparator()
+	 */
+	public Comparator<? extends Attribute> nameComparator();
+
+	/**
+	 * Replies a comparator suitable for attribute based on the names
+	 * of the attributes only.
+	 * 
+	 * @return a comparator, never <code>null</code>
+	 * @see #nameComparator()
+	 * @see #valueComparator()
+	 */
+	public Comparator<? extends Attribute> comparator();
+
+	/** The this value with the content of the specified one.
+	 * 
+	 * @param value
+	 * @throws InvalidAttributeTypeException
+	 */
+	public void setAttribute(Attribute value) throws InvalidAttributeTypeException;
+
+	/**
+	 * Replies the name of the metadata.
+	 * 
+	 * @return the name of the attribute.
+	 */
+	public String getName() ;
+
+	/**
+	 * Set the name of this metadata.
+	 * 
+	 * @param name
+	 */
+	public void setName(String name) ;
+	
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeComparator.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeComparator.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeComparator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,90 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+import java.util.Comparator;
+
+
+
+/**
+ * This class permits to compare to {@link Attribute}.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AttributeComparator implements Comparator<Attribute> {
+	
+    /**
+     * Compares its two arguments for order.  Returns a negative integer,
+     * zero, or a positive integer as the first argument is less than, equal
+     * to, or greater than the second.<p>
+     *
+     * In the foregoing description, the notation
+     * <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
+     * <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
+     * <tt>0</tt>, or <tt>1</tt> according to whether the value of
+     * <i>expression</i> is negative, zero or positive.<p>
+     *
+     * The implementor must ensure that <tt>sgn(compare(x, y)) ==
+     * -sgn(compare(y, x))</tt> for all <tt>x</tt> and <tt>y</tt>.  (This
+     * implies that <tt>compare(x, y)</tt> must throw an exception if and only
+     * if <tt>compare(y, x)</tt> throws an exception.)<p>
+     *
+     * The implementor must also ensure that the relation is transitive:
+     * <tt>((compare(x, y)&gt;0) &amp;&amp; (compare(y, z)&gt;0))</tt> implies
+     * <tt>compare(x, z)&gt;0</tt>.<p>
+     *
+     * Finally, the implementor must ensure that <tt>compare(x, y)==0</tt>
+     * implies that <tt>sgn(compare(x, z))==sgn(compare(y, z))</tt> for all
+     * <tt>z</tt>.<p>
+     *
+     * It is generally the case, but <i>not</i> strictly required that
+     * <tt>(compare(x, y)==0) == (x.equals(y))</tt>.  Generally speaking,
+     * any comparator that violates this condition should clearly indicate
+     * this fact.  The recommended language is "Note: this comparator
+     * imposes orderings that are inconsistent with equals."
+     *
+     * @param arg0 the first object to be compared.
+     * @param arg1 the second object to be compared.
+     * @return a negative integer, zero, or a positive integer as the
+     * 	       first argument is less than, equal to, or greater than the
+     *	       second.
+     * @throws ClassCastException if the arguments' types prevent them from
+     * 	       being compared by this comparator.
+     */
+	public int compare(AttributeValue arg0, AttributeValue arg1) {
+		if ((arg0 instanceof Attribute)&&(arg1 instanceof Attribute))
+			return compare((Attribute)arg0,(Attribute)arg1);
+		return AttributeValueImpl.compareValues(arg0,arg1);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public int compare(Attribute arg0, Attribute arg1) {
+		return AttributeImpl.compareAttrs(arg0, arg1);
+	}
+
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeConstants.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeConstants.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeConstants.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,93 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+import org.arakhne.vmutil.URISchemeType;
+
+/**
+ * This interface contains several constant names.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface AttributeConstants {
+
+	/** Default URI and URL scheme used to build URLs and URIs.
+	 */
+	public static final URISchemeType DEFAULT_SCHEME = URISchemeType.HTTP;
+
+	/** String constant for the <code>true</code> boolean constant.
+	 */
+	public static final String TRUE_CONSTANT = "true"; //$NON-NLS-1$
+	
+	/** String constant for the <code>true</code> boolean constant.
+	 */
+	public static final String YES_CONSTANT = "yes"; //$NON-NLS-1$
+
+	/** String constant for the <code>true</code> boolean constant.
+	 */
+	public static final String OUI_CONSTANT = "oui"; //$NON-NLS-1$
+
+	/** String constant for the <code>true</code> boolean constant.
+	 */
+	public static final String T_CONSTANT = "t"; //$NON-NLS-1$
+
+	/** String constant for the <code>true</code> boolean constant.
+	 */
+	public static final String O_CONSTANT = "o"; //$NON-NLS-1$
+
+	/** String constant for the <code>true</code> boolean constant.
+	 */
+	public static final String Y_CONSTANT = "y"; //$NON-NLS-1$
+
+	/** String constant for the <code>false</code> boolean constant.
+	 */
+	public static final String FALSE_CONSTANT = "false"; //$NON-NLS-1$
+	
+	/** String constant for the <code>true</code> boolean constant.
+	 */
+	public static final String NO_CONSTANT = "no"; //$NON-NLS-1$
+
+	/** String constant for the <code>true</code> boolean constant.
+	 */
+	public static final String NON_CONSTANT = "non"; //$NON-NLS-1$
+
+	/** String constant for the <code>true</code> boolean constant.
+	 */
+	public static final String F_CONSTANT = "f"; //$NON-NLS-1$
+
+	/** String constant for the <code>true</code> boolean constant.
+	 */
+	public static final String N_CONSTANT = "n"; //$NON-NLS-1$
+
+	/** Attribute "name".
+	 */
+	public static final String ATTR_NAME = "name"; //$NON-NLS-1$
+	
+	/** Attirbute "id".
+	 */
+	public static final String ATTR_IDENTIFIER = "id"; //$NON-NLS-1$
+	
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeError.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeError.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeError.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,58 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+/**
+ * This exception is generated each time something wrong append
+ * with attributes.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AttributeError extends RuntimeException {
+	
+	private static final long serialVersionUID = 3139198792703646207L;
+
+	/**
+	 */
+	public AttributeError() {
+		//
+	}
+	
+	/**
+	 * @param name is the name of the attribute on which something wrong appended.
+	 */
+	public AttributeError(String name) {
+		super(name);
+	}
+
+	/**
+	 * @param e is the exception to forward.
+	 */
+	public AttributeError(Throwable e) {
+		super(e);
+	}
+
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeException.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeException.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeException.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,58 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+/**
+ * This exception is generated each time something wrong append
+ * with attributes.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AttributeException extends Exception {
+	
+	private static final long serialVersionUID = 3139198792703646207L;
+
+	/**
+	 */
+	public AttributeException() {
+		//
+	}
+	
+	/**
+	 * @param name is the name of the attribute on which something wrong appended.
+	 */
+	public AttributeException(String name) {
+		super(name);
+	}
+
+	/**
+	 * @param e is the exception to forward.
+	 */
+	public AttributeException(Throwable e) {
+		super(e);
+	}
+
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeImpl.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeImpl.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeImpl.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,492 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URL;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.UUID;
+
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Point3D;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Image;
+
+/**
+ * This class contains an attribute value.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AttributeImpl extends AttributeValueImpl implements Attribute {
+	
+	/** Compare the two specified attributes.
+	 *
+	 * @param arg0
+	 * @param arg1
+	 * @return replies a negative value if <var>arg0</var> is lesser than
+	 * <var>arg1</var>, a positive value if <var>arg0</var> is greater than
+	 * <var>arg1</var>, or <code>0</code> if they are equal.
+	 * @see AttributeComparator
+	 */
+	public static int compareAttrs(Attribute arg0, Attribute arg1) {
+		if (arg0==arg1) return 0;
+		if (arg0==null) return 1;
+		if (arg1==null) return -1;
+		
+		String n0 = arg0.getName();
+		String n1 = arg1.getName();
+		int cmp = compareAttrNames(n0, n1);
+		
+		if (cmp==0)
+			return compareValues(arg0,arg1);
+		
+		return cmp;
+	}
+	
+	/** Compare the two specified attribute names.
+	 *
+	 * @param arg0
+	 * @param arg1
+	 * @return replies a negative value if <var>arg0</var> is lesser than
+	 * <var>arg1</var>, a positive value if <var>arg0</var> is greater than
+	 * <var>arg1</var>, or <code>0</code> if they are equal.
+	 * @see AttributeNameComparator
+	 */
+	public static int compareAttrNames(String arg0, String arg1) {
+		if (arg0==arg1) return 0;
+		if (arg0==null) return Integer.MAX_VALUE;
+		if (arg1==null) return Integer.MIN_VALUE;
+		return arg0.compareToIgnoreCase(arg1);
+	}
+
+	/**
+	 * Name of the metadata.
+	 */
+	private String name = null;
+
+	/**
+	 * Uninitialized attribute.
+	 */
+	public AttributeImpl() {
+		//
+	}
+
+	/**
+	 * Uninitialized attribute.
+	 * 
+	 * @param type is the type of the attribute.
+	 */
+	public AttributeImpl(AttributeType type) {
+		super(type);
+	}
+	
+	/**
+	 * Uninitialized attribute.
+	 * 
+	 * @param name is the name of the attribute
+	 */
+	public AttributeImpl(String name) {
+		this.name = name;
+	}
+
+	/**
+	 * Uninitialized attribute.
+	 * 
+	 * @param name is the name of the attribute
+	 * @param type is the type of the attribute.
+	 */
+	public AttributeImpl(String name, AttributeType type) {
+		super(type);
+		this.name = name;
+	}
+
+	/**
+	 * Initialized attribute with the given raw value.
+	 * The raw value must be compatible with the internal
+	 * representation of the value.
+	 * 
+	 * @param name is the name of the attribute
+	 * @param type is the type of the attribute.
+	 * @param rawValue is the rawValue.
+	 */
+	public AttributeImpl(String name, AttributeType type, Object rawValue) {
+		super(type, rawValue);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the vaule of this new attribute.
+	 */
+	public AttributeImpl(String name, AttributeValue value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param value is the vaule of this new attribute.
+	 */
+	public AttributeImpl(Attribute value) {
+		super(value);
+		this.name = value.getName();
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the vaule of this new attribute.
+	 */
+	public AttributeImpl(String name, boolean value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the vaule of this new attribute.
+	 */
+	public AttributeImpl(String name, Color value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the vaule of this new attribute.
+	 */
+	public AttributeImpl(String name, Date value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the vaule of this new attribute.
+	 */
+	public AttributeImpl(String name, float value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the vaule of this new attribute.
+	 */
+	public AttributeImpl(String name, double value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the vaule of this new attribute.
+	 */
+	public AttributeImpl(String name, Image value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the vaule of this new attribute.
+	 */
+	public AttributeImpl(String name, int value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the vaule of this new attribute.
+	 */
+	public AttributeImpl(String name, long value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the vaule of this new attribute.
+	 */
+	public AttributeImpl(String name, Object value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the vaule of this new attribute.
+	 */
+	public AttributeImpl(String name, Point2D value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param x is the value of this new attribute.
+	 * @param y is the value of this new attribute.
+	 */
+	public AttributeImpl(String name, float x, float y) {
+		super(x,y);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param x is the value of this new attribute.
+	 * @param y is the value of this new attribute.
+	 */
+	public AttributeImpl(String name, double x, double y) {
+		super(x,y);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the vaule of this new attribute.
+	 */
+	public AttributeImpl(String name, Point3D value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param x is the value of this new attribute.
+	 * @param y is the value of this new attribute.
+	 * @param z is the value of this new attribute.
+	 */
+	public AttributeImpl(String name, float x, float y, float z) {
+		super(x,y,z);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param x is the value of this new attribute.
+	 * @param y is the value of this new attribute.
+	 * @param z is the value of this new attribute.
+	 */
+	public AttributeImpl(String name, double x, double y, double z) {
+		super(x,y,z);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the value of this new attribute.
+	 */
+	public AttributeImpl(String name, String value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the value of this new attribute.
+	 */
+	public AttributeImpl(String name, Point2D[] value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the value of this new attribute.
+	 */
+	public AttributeImpl(String name, Point3D[] value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the value of this new attribute.
+	 */
+	public AttributeImpl(String name, Enum<?> value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the value of this new attribute.
+	 */
+	public AttributeImpl(String name, InetAddress value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the value of this new attribute.
+	 */
+	public AttributeImpl(String name, InetSocketAddress value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the value of this new attribute.
+	 */
+	public AttributeImpl(String name, URI value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the value of this new attribute.
+	 */
+	public AttributeImpl(String name, URL value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the value of this new attribute.
+	 */
+	public AttributeImpl(String name, UUID value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * @param name is the name of the attribute
+	 * @param value is the value of this new attribute.
+	 */
+	public AttributeImpl(String name, Class<?> value) {
+		super(value);
+		this.name = name;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @param o {@inheritDoc}
+	 * @return {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(Object o) {
+		if (o instanceof Attribute) {
+			return compareAttrs(this, (Attribute)o)==0;
+		}
+		return super.equals(o);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int hashCode() {
+        final int PRIME = 31;
+        int result = 1;
+        result = PRIME * result + (this.name!=null ? this.name.hashCode() : 0);
+        result = PRIME * result + super.hashCode();
+        return result;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @return {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		StringBuilder str = new StringBuilder();
+		str.append('[');
+		str.append((this.name==null)
+				? "???" //$NON-NLS-1$
+				: this.name);
+		str.append('=');
+		try {
+			str.append((getValue()==null)
+					? "???" //$NON-NLS-1$
+					: getValue().toString());
+		}
+		catch (AttributeException e) {
+			str.append("???"); //$NON-NLS-1$
+		}
+		str.append(':');
+		str.append(getType().toString());
+		str.append(']');
+		return str.toString();
+	}
+	
+	/** Assert that the attribute value was assigned and not <code>null</code>.
+	 */
+	@Override
+	protected void assertAssignedAndNotNull() throws AttributeNotInitializedException {
+		try {
+			super.assertAssignedAndNotNull();
+		}
+		catch(AttributeNotInitializedException _) {
+			throw new AttributeNotInitializedException(this.name);
+		}
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public String getName() {
+		return this.name;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setName(String name) {
+		this.name = name;
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setAttribute(Attribute value) throws InvalidAttributeTypeException {
+		setValue(value);
+		this.name = value.getName();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Comparator<? extends Attribute> nameComparator() {
+		return new AttributeNameComparator();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Comparator<? extends Attribute> comparator() {
+		return new AttributeComparator();
+	}
+
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeNameComparator.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeNameComparator.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeNameComparator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,46 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+import java.util.Comparator;
+
+
+
+/**
+ * Comparator for attribute names.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AttributeNameComparator implements Comparator<Attribute> {
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public int compare(Attribute arg0, Attribute arg1) {
+		return AttributeImpl.compareAttrNames(arg0.getName(), arg1.getName());
+	}
+
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeNotInitializedException.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeNotInitializedException.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeNotInitializedException.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,51 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+/**
+ * This exception is generated each time an uninitialized
+ * attribute value was accessed.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AttributeNotInitializedException extends AttributeException {
+	
+	private static final long serialVersionUID = -8155807821254667201L;
+
+	/**
+	 */
+	public AttributeNotInitializedException() {
+		//
+	}
+
+	/**
+	 * @param name is the name of the uninitialized attribute.
+	 */
+	public AttributeNotInitializedException(String name) {
+		super(name);
+	}
+
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeType.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeType.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeType.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,719 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+import java.lang.reflect.Array;
+import java.math.BigInteger;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.UnknownHostException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object3d.Point3f;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Point3D;
+import org.arakhne.afc.math.generic.Tuple2D;
+import org.arakhne.afc.math.generic.Tuple3D;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Image;
+import org.arakhne.afc.ui.vector.VectorToolkit;
+import org.arakhne.vmutil.locale.Locale;
+
+/**
+ * List of supported types for the metadata.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public enum AttributeType {
+
+	/** Represents an enumeration.
+	 * @see Enum
+	 */
+	ENUMERATION,
+
+	/** Represents a Java type.
+	 */
+	TYPE,
+
+	/** Represents an unique universal identifier.
+	 */
+	UUID,
+
+	/** Represents an integer.
+	 */
+	INTEGER,
+	
+	/** Represents a floating number.
+	 */
+	REAL,
+	
+	/** Represents a date.
+	 */
+	DATE,
+	
+	/** Represents a boolean value.
+	 */
+	BOOLEAN,
+	
+	/** Represents an Internet address.
+	 * @see Inet4Address
+	 * @see Inet6Address
+	 * @see InetSocketAddress
+	 */
+	INET_ADDRESS,
+
+	/** Represents a color value.
+	 */
+	COLOR,
+
+	/** Represents an URL.
+	 * @see java.net.URL
+	 */
+	URL,
+
+	/** Represents an URI.
+	 * @see java.net.URI
+	 */
+	URI,
+
+	/** Represents a timestamp value.
+	 */
+	TIMESTAMP,
+
+	/** Represents a 3d point value.
+	 */
+	POINT3D,
+
+	/** Represents a 2d point value.
+	 */
+	POINT,
+
+	/** Represents a list of 3d points.
+	 */
+	POLYLINE3D,
+
+	/** Represents a list of 2d points.
+	 */
+	POLYLINE,
+
+	/** Represents an image value.
+	 */
+	IMAGE,
+		
+	/** Represents a string.
+	 */
+	STRING,
+	
+	/** Represents a java-object value.
+	 */
+	OBJECT;	
+	
+	private static final String NAME_RESOURCE_FILE;
+	
+	static {
+		String pName = AttributeType.class.getPackage().getName();
+		NAME_RESOURCE_FILE = pName+".types"; //$NON-NLS-1$
+	}
+	
+	/** Replies the name of this type (localized).
+	 * 
+	 * @return the localized name of this type.
+	 */
+	public String getName() {
+		switch(this) {
+		case INTEGER:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"INTEGER"); //$NON-NLS-1$ 
+		case REAL:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"FLOAT"); //$NON-NLS-1$ 
+		case STRING:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"STRING"); //$NON-NLS-1$ 
+		case BOOLEAN:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"BOOLEAN"); //$NON-NLS-1$ 
+		case DATE:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"DATE"); //$NON-NLS-1$ 
+		case TIMESTAMP:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"TIMESTAMP"); //$NON-NLS-1$ 
+		case OBJECT:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"OBJECT"); //$NON-NLS-1$ 
+		case POINT3D:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"POINT3D"); //$NON-NLS-1$ 
+		case POINT:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"POINT2D"); //$NON-NLS-1$ 
+		case COLOR:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"COLOR"); //$NON-NLS-1$ 
+		case IMAGE:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"ICON"); //$NON-NLS-1$ 
+		case POLYLINE:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"POLYLINE"); //$NON-NLS-1$ 
+		case POLYLINE3D:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"POLYLINE3D"); //$NON-NLS-1$ 
+		case UUID:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"UUID"); //$NON-NLS-1$ 
+		case URL:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"URL"); //$NON-NLS-1$ 
+		case URI:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"URI"); //$NON-NLS-1$ 
+		case INET_ADDRESS:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"INET_ADDRESS"); //$NON-NLS-1$ 
+		case ENUMERATION:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"ENUMERATION"); //$NON-NLS-1$ 
+		case TYPE:
+			return Locale.getStringFrom(NAME_RESOURCE_FILE,"TYPE"); //$NON-NLS-1$ 
+		default:
+		}		
+		return Locale.getStringFrom(NAME_RESOURCE_FILE,"OTHER"); //$NON-NLS-1$ 
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @return {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		return getName();
+	}
+
+	/** Replies the Attribute type that corresponds to the
+	 * specified internal code.
+	 * 
+	 * @param type is an integer representing an attribute type.
+	 * @return the type that corresponds to the given integer.
+	 */
+	public static AttributeType fromInteger(int type) {
+		AttributeType[] vals = values();
+		if ((vals!=null)&&(type>=0)&&(type<vals.length))
+			return vals[type];
+		return OBJECT;
+	}
+
+	/** Replies the Attribute type that corresponds to the
+	 * specified value.
+	 * 
+	 *  @param value is the value to test.
+	 *  @return the type that corresponds to the given value.
+	 */
+	public static AttributeType fromValue(Object value) {
+		if (value!=null) {
+			if (value instanceof NullAttribute)
+				return ((NullAttribute)value).getType();
+			return fromClass(value.getClass());
+		}
+		return OBJECT;
+	}
+
+	/** Replies the Attribute type that corresponds to the
+	 * specified type.
+	 * 
+	 *  @param type is the type to test.
+	 *  @return the type that corresponds to the given value.
+	 */
+	public static AttributeType fromClass(Class<?> type) {
+		if (type!=null) {
+			if (byte.class.isAssignableFrom(type)) return INTEGER;
+			if (Byte.class.isAssignableFrom(type)) return INTEGER;
+			if (short.class.isAssignableFrom(type)) return INTEGER;
+			if (Short.class.isAssignableFrom(type)) return INTEGER;
+			if (int.class.isAssignableFrom(type)) return INTEGER;
+			if (Integer.class.isAssignableFrom(type)) return INTEGER;
+			if (long.class.isAssignableFrom(type)) return INTEGER;
+			if (Long.class.isAssignableFrom(type)) return INTEGER;
+			if (BigInteger.class.isAssignableFrom(type)) return INTEGER;
+			if (AtomicInteger.class.isAssignableFrom(type)) return INTEGER;
+			if (AtomicLong.class.isAssignableFrom(type)) return INTEGER;
+
+			if (Timestamp.class.isAssignableFrom(type)) return TIMESTAMP;
+			
+			if (float.class.isAssignableFrom(type)) return REAL;
+			if (double.class.isAssignableFrom(type)) return REAL;
+			if (Number.class.isAssignableFrom(type)) return REAL;
+
+			if (char.class.isAssignableFrom(type)) return STRING;
+			if (Character.class.isAssignableFrom(type)) return STRING;
+			if (CharSequence.class.isAssignableFrom(type)) return STRING;
+	
+			if (boolean.class.isAssignableFrom(type)) return BOOLEAN;
+			if (Boolean.class.isAssignableFrom(type)) return BOOLEAN;
+	
+			if (Date.class.isAssignableFrom(type)) return DATE;
+			if (Calendar.class.isAssignableFrom(type)) return DATE;
+	
+			if (Tuple3D.class.isAssignableFrom(type)) return POINT3D;
+
+			if (Tuple2D.class.isAssignableFrom(type)) return POINT;
+
+			if (Color.class.isAssignableFrom(type)) return COLOR;
+
+			if (UUID.class.isAssignableFrom(type)) return UUID;
+
+			if (java.net.URL.class.isAssignableFrom(type)) return URL;
+
+			if (java.net.URI.class.isAssignableFrom(type)) return URI;
+
+			if (Image.class.isAssignableFrom(type)) return IMAGE;
+
+			if (InetAddress.class.isAssignableFrom(type)) return INET_ADDRESS;
+			if (InetSocketAddress.class.isAssignableFrom(type)) return INET_ADDRESS;
+
+			if (type.isArray()) {
+				Class<?> elementType = type.getComponentType();
+				if (Point2D.class.isAssignableFrom(elementType)) return POLYLINE;
+				if (Point3D.class.isAssignableFrom(elementType)) return POLYLINE3D;
+			}
+
+
+			if (Enum.class.isAssignableFrom(type)) return ENUMERATION;
+
+			if (Class.class.isAssignableFrom(type)) return TYPE;
+		}
+		return OBJECT;
+	}
+
+	/** Replies if the specified value is an instanceof the type..
+	 * 
+	 *  @param value is the value to test.
+	 *  @return <code>true</code> if the given value is an instance of
+	 *  this attribute type, otherwise <code>false</code>.
+	 */
+	public boolean instanceOf(Object value) {
+		return this == fromValue(value);
+	}
+
+	/** Replies the default value for the specified type.
+	 * 
+	 * @return the default value.
+	 */
+	public Object getDefaultValue() {
+		switch(this) {
+		case INTEGER:
+			return new Long(0);
+		case REAL:
+			return new Double(0);
+		case STRING:
+			return new String();
+		case BOOLEAN:
+			return Boolean.FALSE;
+		case DATE:
+			return new Date();
+		case TIMESTAMP:
+			return new Timestamp(System.currentTimeMillis());
+		case POINT3D:
+			return new Point3f();
+		case POINT:
+			return new Point2f();
+		case COLOR:
+			return VectorToolkit.BLACK;
+		case UUID:
+			return java.util.UUID.fromString("00000000-0000-0000-0000-000000000000"); //$NON-NLS-1$
+		case URL:
+			return null;
+		case URI:
+			return null;
+		case POLYLINE3D:
+			return new Point3D[0];
+		case POLYLINE:
+			return new Point2D[0];
+		case OBJECT:
+			return null;
+		case IMAGE:
+			return null;
+		case INET_ADDRESS:
+			try {
+				return InetAddress.getLocalHost();
+			}
+			catch (UnknownHostException _) {
+				return null;
+			}
+		case ENUMERATION:
+			return null;
+		case TYPE:
+		default:
+			return Object.class;
+		}		
+	}
+
+	/**
+	 * Replies if this attribute type is
+	 * a base type, ie. a number, a boolean
+	 * or a string.
+	 * 
+	 * @return <code>true</code> if this type is a base type,
+	 * otherwise <code>false</code>
+	 */
+	public boolean isBaseType() {
+		return this==INTEGER
+			|| this==REAL
+			|| this==TIMESTAMP
+			|| this==BOOLEAN
+			|| this==STRING;
+	}
+
+	/**
+	 * Replies if this attribute type is
+	 * a number type.
+	 * A number type is always a base type.
+	 * 
+	 * @return <code>true</code> if this type is a number type,
+	 * otherwise <code>false</code>
+	 * @since 4.0
+	 */
+	public boolean isNumberType() {
+		return this==INTEGER
+			|| this==REAL
+			|| this==TIMESTAMP;
+	}
+
+	/**
+	 * Replies if a null value is allowed for this attribute type.
+	 * 
+	 * @return <code>true</code> if this type allows <code>null</code> value,
+	 * otherwise <code>false</code>
+	 */
+	public boolean isNullAllowed() {
+		return this==OBJECT
+			|| this==IMAGE
+			|| this==URI
+			|| this==URL
+			|| this==INET_ADDRESS
+			|| this==ENUMERATION;
+	}
+	
+	/** Replies if a value of the given attribute type may
+	 * be cast to a value of this attribute type.
+	 * <p>
+	 * Caution: even if isAssignableFrom is replying <code>true</code>,
+	 * the {@link AttributeValue#cast(AttributeType)} and
+	 * {@link AttributeValue#castAndSet(AttributeType, Object)} may fail
+	 * if the target type does not support a specifical value of the
+	 * source type. The isAssignableFrom function replies <code>true</code>
+	 * if a least one value of the source type is assignable to a value
+	 * of the target type.
+	 * 
+	 * @param type
+	 * @return <code>true</code> if a value of the given
+	 * <var>type</var> may be cast to a value of <code>this</code>;
+	 * otherwise <code>false</code>.
+	 * @since 4.0
+	 */
+	public boolean isAssignableFrom(AttributeType type) {
+		switch(this) {
+		case INTEGER:
+		case REAL:
+			return type==INTEGER || type==REAL || type==TIMESTAMP || type==STRING || type==DATE || type==BOOLEAN || type==COLOR || type==ENUMERATION ||  type==OBJECT;
+		case TIMESTAMP:
+			return type==INTEGER || type==REAL || type==TIMESTAMP || type==STRING || type==DATE || type==BOOLEAN || type==COLOR || type==OBJECT;
+		case BOOLEAN:
+			return type==BOOLEAN || type==STRING || type==INTEGER || type==TIMESTAMP || type==REAL || type==OBJECT;
+		case DATE:
+			return type==DATE || type==REAL || type==INTEGER || type==TIMESTAMP || type==STRING || type==OBJECT;
+		case POINT3D:
+		case POINT:
+			return type==POINT || type==POINT3D || type==COLOR || type==REAL || type==INTEGER || type==TIMESTAMP || type==DATE || type==STRING || type==OBJECT;
+		case COLOR:
+			return type==COLOR || type==POINT || type==POINT3D || type==STRING || type==INTEGER || type==REAL || type==TIMESTAMP || type==DATE || type==OBJECT;
+		case URL:
+			return type==URI || type==URL || type==INET_ADDRESS || type==STRING || type==OBJECT;
+		case URI:
+			return type==URI || type==URL || type==INET_ADDRESS || type==STRING || type==UUID || type==OBJECT;
+		case POLYLINE3D:
+		case POLYLINE:
+			return type==POLYLINE || type==POLYLINE3D || type==POINT || type==POINT3D || type==STRING || type==OBJECT;
+		case IMAGE:
+			return type==IMAGE || type==OBJECT;
+		case INET_ADDRESS:
+			return type==INET_ADDRESS || type==STRING || type==URL || type==URI  || type==OBJECT;
+		case ENUMERATION:
+			return type==ENUMERATION || type==STRING || type==OBJECT;
+		case TYPE:
+			return type==TYPE || type==STRING || type==OBJECT;
+		case UUID:
+		case STRING:
+		case OBJECT:
+			return true;
+		default:
+		}
+		return false;
+	}
+
+	/** Cast the specified value to corresponds to the
+	 * default storage standard for attributes.
+	 * 
+	 * @param obj is the object to cast
+	 * @return the casted value
+	 * @throws ClassCastException if is impossible to cast.
+	 * @throws NullPointerException if null value is not allowed.
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	public Object cast(Object obj) {
+		if (obj instanceof NullAttribute) return null;
+		switch(this) {
+		case INTEGER:
+			// Possible ClassCastException
+			if (obj==null) throw new NullPointerException();
+			if (obj instanceof Enum<?>) return (long)((Enum<?>)obj).ordinal();
+			return ((Number)obj).longValue();
+		case REAL:
+			// Possible ClassCastException
+			if (obj==null) throw new NullPointerException();
+			if (obj instanceof Enum<?>) return (double)((Enum<?>)obj).ordinal();
+			return ((Number)obj).doubleValue();
+		case STRING:
+			if (obj==null) return ""; //$NON-NLS-1$
+			if (obj instanceof Enum<?>) {
+				Enum<?> enumValue = (Enum<?>)obj;
+				return enumValue.getClass().getCanonicalName()
+						+"." //$NON-NLS-1$
+						+enumValue.name();
+			}
+			return obj.toString();
+		case BOOLEAN:
+			// Possible ClassCastException
+			if (obj==null) throw new NullPointerException();
+			return Boolean.class.cast(obj);
+		case DATE:
+			// Possible ClassCastException
+			if (obj==null) throw new NullPointerException();
+			if (obj instanceof Number)
+				return new Date(((Number)obj).longValue());
+			if (obj instanceof Calendar) return ((Calendar)obj).getTime();
+			return Date.class.cast(obj);
+		case TIMESTAMP:
+			// Possible ClassCastException
+			if (obj==null) throw new NullPointerException();
+			if (obj instanceof Calendar) return ((Calendar)obj).getTimeInMillis();
+			if (obj instanceof Date) return ((Date)obj).getTime();
+			if (obj instanceof Number && !(obj instanceof Timestamp)) {
+				return new Timestamp(((Number)obj).longValue());
+			}
+			return Timestamp.class.cast(obj);
+		case POINT3D:
+			// Possible ClassCastException
+			if (obj==null) throw new NullPointerException();
+			if (obj instanceof Tuple3D && !(obj instanceof Point3D)) {
+				return new Point3f((Tuple3D)obj);
+			}
+			return Point3D.class.cast(obj);
+		case POINT:
+			// Possible ClassCastException
+			if (obj==null) throw new NullPointerException();
+			if (obj instanceof Tuple2D && !(obj instanceof Point2D)) {
+				return new Point2f((Tuple2D)obj);
+			}
+			return Point2D.class.cast(obj);
+		case COLOR:
+			// Possible ClassCastException
+			if (obj==null) throw new NullPointerException();
+			if (obj instanceof Number) {
+				return VectorToolkit.color(((Number)obj).intValue());
+			}
+			return Color.class.cast(obj);
+		case UUID:
+			// Possible ClassCastException
+			if (obj==null) throw new NullPointerException();
+			return UUID.class.cast(obj);
+		case URL:
+			// Possible ClassCastException
+			if (obj==null) return null;
+			if (obj instanceof java.net.URI) {
+				try {
+					return ((java.net.URI)obj).toURL();
+				}
+				catch (MalformedURLException e) {
+					//
+				}
+			}
+			if (obj instanceof InetAddress) {
+				try {
+					return new java.net.URL(AttributeConstants.DEFAULT_SCHEME.name(), ((InetAddress)obj).getHostAddress(), ""); //$NON-NLS-1$
+				}
+				catch (MalformedURLException e) {
+					//
+				}
+			}
+			if (obj instanceof InetSocketAddress) {
+				try {
+					return new java.net.URL(AttributeConstants.DEFAULT_SCHEME.name(), ((InetSocketAddress)obj).getAddress().getHostAddress(), ""); //$NON-NLS-1$
+				}
+				catch (MalformedURLException e) {
+					//
+				}
+			}
+			return java.net.URL.class.cast(obj);
+		case URI:
+			// Possible ClassCastException
+			if (obj==null) return null;
+			if (obj instanceof java.net.URL) {
+				try {
+					return ((java.net.URL)obj).toURI();
+				}
+				catch (URISyntaxException e) {
+					//
+				}
+			}
+			if (obj instanceof InetAddress) {
+				try {
+					return new java.net.URI(AttributeConstants.DEFAULT_SCHEME.name(), ((InetAddress)obj).getHostAddress(), ""); //$NON-NLS-1$
+				}
+				catch (URISyntaxException e) {
+					//
+				}
+			}
+			if (obj instanceof InetSocketAddress) {
+				try {
+					return new java.net.URI(AttributeConstants.DEFAULT_SCHEME.name(), ((InetSocketAddress)obj).getAddress().getHostAddress(), ""); //$NON-NLS-1$
+				}
+				catch (URISyntaxException e) {
+					//
+				}
+			}
+			return java.net.URI.class.cast(obj);
+		case POLYLINE3D:
+			// Possible ClassCastException
+			if (obj==null) throw new NullPointerException();
+			if (obj.getClass().isArray()) {
+				Class<?> elementType = obj.getClass().getComponentType();
+				if (Tuple3D.class.isAssignableFrom(elementType) &&
+					!Point3D.class.isAssignableFrom(elementType)) {
+					int length = Array.getLength(obj);
+					Point3D[] tab = new Point3D[length];
+					for(int i=0; i<length; ++i)
+						tab[i] = new Point3f((Tuple3D)Array.get(obj, i));
+					return tab;
+				}
+			}
+			return Point3D[].class.cast(obj);
+		case POLYLINE:
+			// Possible ClassCastException
+			if (obj==null) throw new NullPointerException();
+			if (obj.getClass().isArray()) {
+				Class<?> elementType = obj.getClass().getComponentType();
+				if (Tuple2D.class.isAssignableFrom(elementType) &&
+					!Point2D.class.isAssignableFrom(elementType)) {
+					int length = Array.getLength(obj);
+					Point2D[] tab = new Point2D[length];
+					for(int i=0; i<length; ++i)
+						tab[i] = new Point2f((Tuple2D)Array.get(obj, i));
+					return tab;
+				}
+			}
+			return Point2D[].class.cast(obj);
+		case IMAGE:
+			if (obj==null) return null;
+			return Image.class.cast(obj);
+		case OBJECT:
+			if (obj==null) return null;
+			break;
+		case INET_ADDRESS:
+			if (obj==null) return null;
+			if (obj instanceof InetSocketAddress) {
+				return ((InetSocketAddress)obj).getAddress();
+			}
+			if (obj instanceof java.net.URL) {
+				java.net.URL url = (java.net.URL)obj;
+				try {
+					return InetAddress.getByName(url.getHost());
+				}
+				catch (UnknownHostException _) {
+					//
+				}
+			}
+			if (obj instanceof java.net.URI) {
+				java.net.URI uri = (java.net.URI)obj;
+				try {
+					return InetAddress.getByName(uri.getHost());
+				}
+				catch (UnknownHostException _) {
+					//
+				}
+			}
+			if (obj instanceof CharSequence) {
+				try {
+					String ipStr = obj.toString();
+					int index = ipStr.lastIndexOf("/"); //$NON-NLS-1$
+					if (index>=0) {
+						try {
+							return InetAddress.getByName(ipStr.substring(index+1));
+						}
+						catch (UnknownHostException _) {
+							//
+						}
+					}
+					return InetAddress.getByName(ipStr);
+				}
+				catch (UnknownHostException _) {
+					//
+				}
+			}
+			return InetAddress.class.cast(obj);
+		case ENUMERATION:
+			if (obj==null) return null;
+			if (obj instanceof CharSequence) {
+				String enumStr = obj.toString();
+				int index = enumStr.lastIndexOf('.');
+				if (index>0) {
+					String enumName = enumStr.substring(0, index);
+					String constantName = enumStr.substring(index+1);
+					try {
+						Class type = Class.forName(enumName);
+						if (Enum.class.isAssignableFrom(type)) {
+							 Enum<?> v = Enum.valueOf(type, constantName.toUpperCase());
+							 if (v!=null) return v;
+						}
+					}
+					catch(Throwable _) {
+						//
+					}
+				}
+			}
+			return Enum.class.cast(obj);
+		case TYPE:
+			// Possible ClassCastException
+			if (obj==null) throw new NullPointerException();
+			if (obj instanceof CharSequence) {
+				try {
+					return Class.forName(((CharSequence)obj).toString());
+				}
+				catch (ClassNotFoundException e) {
+					//
+				}
+			}
+			return Class.class.cast(obj);
+		default:
+			throw new ClassCastException();
+		}
+		return obj;
+	}
+	
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeValue.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeValue.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeValue.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,674 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.UUID;
+
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Point3D;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Image;
+
+/**
+ * This class contains a metadata value.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface AttributeValue {
+
+	/**
+	 * Replies a comparator suitable for attribute values.
+	 * 
+	 * @return a comparator, never <code>null</code>
+	 */
+	public Comparator<? extends AttributeValue> valueComparator();
+
+	/**
+	 * Replies if this attribute type is
+	 * a base type, ie. a number, a boolean
+	 * or a string.
+	 * <p>
+	 * The following code is always <code>true</code>:<br>
+	 * <code>isObjectValue() == !isBaseValue()</code>
+	 * 
+	 * @return <code>true</code> if this attribute is containing a base type value,
+	 * otherwise <code>false</code>
+	 * @see #isNullAllowed()
+	 * @see #isObjectValue()
+	 */
+	public boolean isBaseType();
+
+	/**
+	 * Replies the type of this metadata.
+	 * 
+	 * @return the type of the attribute
+	 */
+	public AttributeType getType() ; 
+	
+	/**
+	 * Change the type of this attribute.
+	 * <p>
+	 * The exception will be generated in case
+	 * the current value could not be casted
+	 * to the new type.
+	 * 
+	 * @param type is the new type of this attribute
+	 * @throws InvalidAttributeTypeException if the current value was incompatible with the given type. 
+	 */
+	public void setType(AttributeType type) throws InvalidAttributeTypeException; 
+
+	/**
+	 * Change the type of this attribute.
+	 * <p>
+	 * The value could be lost in case the type was incompatible
+	 * with the value.
+	 * 
+	 * @param type is the new type of this attribute
+	 * @return <code>true</code> if the cast was sucessfully done,
+	 *         otherwhise, if the value was lost because of the
+	 *         cast operation.
+	 */
+	public boolean cast(AttributeType type); 
+
+	/**
+	 * Change the type of this attribute and set its value.
+	 * 
+	 * @param type is the new type of this attribute
+	 * @param value is the new value.
+	 */
+	public void castAndSet(AttributeType type, Object value); 
+
+	/** Replies the value attribute stored in the implementation of this interface.
+	 * In opposite than {@link #getJavaObject()}, this function replies
+	 * the value for all attribute type.
+	 * 
+	 * @return the raw value of this attribute
+	 * @throws InvalidAttributeTypeException
+	 * @throws AttributeNotInitializedException
+	 */
+	public Object getValue() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/** Replies the type of the internal value of this implementation of AttributeValue.
+	 * 
+	 * @return the type of the value stored inside this attribute value implementation.
+	 * @since 4.0
+	 */
+	public Class<?> getInternalStorageType();
+
+	/** The this value with the content of the specified one.
+	 * 
+	 * @param value
+	 */
+	public void setValue(AttributeValue value);
+	
+	/** The this value with the content of the specified one.
+	 * <p>
+	 * The type of the attribute will be detected from the type
+	 * of the object.
+	 * 
+	 * @param value
+	 */
+	public void setValue(Object value);
+
+	/** Set the value to its default.
+	 */
+	public void setToDefault() ;
+	
+	/** Set the value to its default if not init.
+	 */
+	public void setToDefaultIfUninitialized() ;
+
+	/**
+	 * Replies the value of this metadata.
+	 *
+	 * @return the value
+	 * @throws InvalidAttributeTypeException
+	 * @throws AttributeNotInitializedException
+	 */
+	public long getInteger() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param value
+	 */
+	public void setInteger(int value);
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param value
+	 */
+	public void setInteger(long value);
+
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return the value
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 */
+	public double getReal() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param value
+	 */
+	public void setReal(double value);
+
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return the value
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 */
+	public String getString() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+	
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param value
+	 */
+	public void setString(String value);
+
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return the value
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 */
+	public Date getDate() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param value
+	 */
+	public void setDate(Date value);
+
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return the value
+	 * @throws InvalidAttributeTypeException
+	 * @throws AttributeNotInitializedException
+	 */
+	public boolean getBoolean() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param value
+	 */
+	public void setBoolean(boolean value);
+
+	/**
+	 * Replies of the value of this attribute is
+	 * a data object ie, java object or icon.
+	 * <p>
+	 * The following code is always <code>true</code>:<br>
+	 * <code>isObjectValue() == !isBaseValue()</code>
+	 * 
+	 * @return <code>true</code> if this attribute contains a object as value (ie, not a base type),
+	 * otherwise <code>false</code>
+	 * @see #isBaseType()
+	 * @see #isNullAllowed()
+	 */
+	public boolean isObjectValue();
+
+	/**
+	 * Replies the value of this metadata.
+	 * In opposite than {@link #getValue()}, this function replies
+	 * the value only if this attribute value if of type
+	 * {@link AttributeType#OBJECT}.
+	 *
+	 * @param <T> is the type of the value to reply
+	 * @return the value
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 * @see #getValue()
+	 */
+	public <T extends Object> T getJavaObject() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param <T> is the type of the new value
+	 * @param value
+	 */
+	public <T extends Object> void setJavaObject(T value);
+
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return a timestamp with a precision in milliseconds
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 */
+	public long getTimestamp() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param value
+	 */
+	public void setTimestamp(long value);
+
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return a 3d point
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 */
+	public Point3D getPoint3D() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param p
+	 */
+	public void setPoint3D(Point3D p);
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public void setPoint3D(float x, float y, float z);
+
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return a 2d point
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 */
+	public Point2D getPoint() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param p
+	 */
+	public void setPoint(Point2D p);
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param x
+	 * @param y
+	 */
+	public void setPoint(float x, float y);
+
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return a color
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 */
+	public Color getColor() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param c
+	 */
+	public void setColor(Color c);
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param r
+	 * @param g
+	 * @param b
+	 */
+	public void setColor(float r, float g, float b);
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param r
+	 * @param g
+	 * @param b
+	 * @param a
+	 */
+	public void setColor(float r, float g, float b, float a);
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param r
+	 * @param g
+	 * @param b
+	 */
+	public void setColor(int r, int g, int b);
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param r
+	 * @param g
+	 * @param b
+	 * @param a
+	 */
+	public void setColor(int r, int g, int b, int a);
+
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return an icon
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 */
+	public Image getImage() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param c
+	 */
+	public void setImage(Image c);
+	
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return an uuid
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 * @since 4.0
+	 */
+	public UUID getUUID() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param u
+	 * @since 4.0
+	 */
+	public void setUUID(UUID u);
+	
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return an url
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 * @since 4.0
+	 */
+	public URL getURL() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param u
+	 * @since 4.0
+	 */
+	public void setURL(URL u);
+
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return an uri
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 * @since 4.0
+	 */
+	public URI getURI() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param u
+	 * @since 4.0
+	 */
+	public void setURI(URI u);
+
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return a list of 3d points
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 */
+	public Point3D[] getPolyline3D() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param p
+	 */
+	public void setPolyline3D(Point3D... p);
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param p
+	 */
+	public void setPolyline3D(Collection<? extends Point3D> p);
+
+	/**
+	 * Add a point to the end of the polyline
+	 * 
+	 * @param p
+	 */
+	public void addToPolyline3D(Point3D... p);
+
+	/**
+	 * Add a point to the end of the polyline
+	 * 
+	 * @param p
+	 */
+	public void addToPolyline3D(Collection<? extends Point3D> p);
+
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return a list of 2d points
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 */
+	public Point2D[] getPolyline() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param p
+	 */
+	public void setPolyline(Point2D... p);
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param p
+	 */
+	public void setPolyline(Collection<? extends Point2D> p);
+
+	/**
+	 * Add a point to the end of the polyline
+	 * 
+	 * @param p
+	 */
+	public void addToPolyline(Point2D... p);
+
+	/**
+	 * Add a point to the end of the polyline
+	 * 
+	 * @param p
+	 */
+	public void addToPolyline(Collection<? extends Point2D> p);
+
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return an Internet address.
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 */
+	public InetAddress getInetAddress() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param a
+	 */
+	public void setInetAddress(InetAddress a);
+
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return an enumeration.
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 */
+	public Enum<?> getEnumeration() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @param <T> is the type of the enumeration to reply.
+	 * @param type is the type of the enumeration to reply.
+	 * @return an enumeration.
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 */
+	public <T extends Enum<T>> T getEnumeration(Class<T> type) throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param e
+	 */
+	public void setEnumeration(Enum<?> e);
+
+	/**
+	 * Replies the value of this metadata.
+	 * 
+	 * @return a Java type.
+	 * @throws AttributeNotInitializedException
+	 * @throws InvalidAttributeTypeException
+	 */
+	public Class<?> getJavaClass() throws InvalidAttributeTypeException, AttributeNotInitializedException;
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param e
+	 */
+	public void setJavaClass(Class<?> e);
+
+	/**
+	 * Replies if a value was affected to this attribute.
+	 * 
+	 * @return <code>true</code> if this attribute is containing a value,
+	 * otherwise <code>false</code>
+	 */
+	public boolean isAssigned();
+
+	/**
+	 * Replies if a null value is allowed for this attribute.
+	 * <p>
+	 * If {@link #isBaseType()} replies <code>true</code>,
+	 * this function must always replies <code>false</code>.
+	 * 
+	 * @return <code>true</code> if <code>null</code> is assigned to this attribute,
+	 * otherwise <code>false</code>
+	 * @see #isBaseType()
+	 * @see #isObjectValue()
+	 */
+	public boolean isNullAllowed();
+
+	/**
+	 * Set this attribute value uninitialized.
+	 */
+	public void uninitializeValue();
+
+	/** Force this attribute to put its value into a storage system.
+	 * <p>
+	 * By default, this function does nothing. It is dependant of the application
+	 * implementation.
+	 * 
+	 * @return <code>true</code> if the value was written, otherwhise <code>false</code>
+	 */
+	public boolean flush();
+
+	/** Replies if a value of the given attribute type may
+	 * be cast to a value of this attribute type.
+	 * <p>
+	 * Caution: even if isAssignableFrom is replying <code>true</code>,
+	 * the {@link AttributeValue#cast(AttributeType)} and
+	 * {@link AttributeValue#castAndSet(AttributeType, Object)} may fail
+	 * if the target type does not support a specifical value of the
+	 * source type. The isAssignableFrom function replies <code>true</code>
+	 * if a least one value of the source type is assignable to a value
+	 * of the target type.
+	 * <p>
+	 * This function is equivalent to:
+	 * <code>this.getType().isAssignableFrom(type)</code>
+	 * 
+	 * @param type
+	 * @return <code>true</code> if a value of the given
+	 * <var>type</var> may be cast to a value of the same type as this;
+	 * otherwise <code>false</code>.
+	 * @since 4.0
+	 */
+	public boolean isAssignableFrom(AttributeType type);
+	
+	/** Replies if a value of the given attribute type may
+	 * be cast to a value of this attribute type.
+	 * <p>
+	 * Caution: even if isAssignableFrom is replying <code>true</code>,
+	 * the {@link AttributeValue#cast(AttributeType)} and
+	 * {@link AttributeValue#castAndSet(AttributeType, Object)} may fail
+	 * if the target type does not support a specifical value of the
+	 * source type. The isAssignableFrom function replies <code>true</code>
+	 * if a least one value of the source type is assignable to a value
+	 * of the target type.
+	 * <p>
+	 * This function is equivalent to:
+	 * <code>this.getType().isAssignableFrom(value.getType())</code>
+	 * 
+	 * @param value
+	 * @return <code>true</code> if the given value may be cast to
+	 * a value of the same type as this;
+	 * otherwise <code>false</code>.
+	 * @since 4.0
+	 */
+	public boolean isAssignableFrom(AttributeValue value);
+
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeValueComparator.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeValueComparator.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeValueComparator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,82 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+import java.util.Comparator;
+
+
+
+/**
+ * This class permits to compare to {@link AttributeValue}.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AttributeValueComparator implements Comparator<AttributeValue> {
+	
+    /**
+     * Compares its two arguments for order.  Returns a negative integer,
+     * zero, or a positive integer as the first argument is less than, equal
+     * to, or greater than the second.<p>
+     *
+     * In the foregoing description, the notation
+     * <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
+     * <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
+     * <tt>0</tt>, or <tt>1</tt> according to whether the value of
+     * <i>expression</i> is negative, zero or positive.<p>
+     *
+     * The implementor must ensure that <tt>sgn(compare(x, y)) ==
+     * -sgn(compare(y, x))</tt> for all <tt>x</tt> and <tt>y</tt>.  (This
+     * implies that <tt>compare(x, y)</tt> must throw an exception if and only
+     * if <tt>compare(y, x)</tt> throws an exception.)<p>
+     *
+     * The implementor must also ensure that the relation is transitive:
+     * <tt>((compare(x, y)&gt;0) &amp;&amp; (compare(y, z)&gt;0))</tt> implies
+     * <tt>compare(x, z)&gt;0</tt>.<p>
+     *
+     * Finally, the implementor must ensure that <tt>compare(x, y)==0</tt>
+     * implies that <tt>sgn(compare(x, z))==sgn(compare(y, z))</tt> for all
+     * <tt>z</tt>.<p>
+     *
+     * It is generally the case, but <i>not</i> strictly required that
+     * <tt>(compare(x, y)==0) == (x.equals(y))</tt>.  Generally speaking,
+     * any comparator that violates this condition should clearly indicate
+     * this fact.  The recommended language is "Note: this comparator
+     * imposes orderings that are inconsistent with equals."
+     *
+     * @param arg0 the first object to be compared.
+     * @param arg1 the second object to be compared.
+     * @return a negative integer, zero, or a positive integer as the
+     * 	       first argument is less than, equal to, or greater than the
+     *	       second.
+     * @throws ClassCastException if the arguments' types prevent them from
+     * 	       being compared by this comparator.
+     */
+	@Override
+	public int compare(AttributeValue arg0, AttributeValue arg1) {
+		return AttributeValueImpl.compareValues(arg0, arg1);
+	}
+
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeValueImpl.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeValueImpl.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/AttributeValueImpl.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,2737 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+import java.lang.reflect.Array;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Locale;
+import java.util.UUID;
+
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object3d.Point3f;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Point3D;
+import org.arakhne.afc.math.generic.Tuple2D;
+import org.arakhne.afc.math.generic.Tuple3D;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Image;
+import org.arakhne.afc.ui.vector.VectorToolkit;
+
+/**
+ * This class contains an attribute value.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AttributeValueImpl implements AttributeValue, AttributeConstants {
+
+	/** Replies the best attribute value that is representing
+	 * the given text.
+	 * 
+	 * @param text
+	 * @return the attribute value, never <code>null</code>.
+	 */
+	public static AttributeValueImpl parse(String text) {
+		AttributeValueImpl value = new AttributeValueImpl(text);
+		if (text!=null && !text.isEmpty()) {
+			Object binValue;
+			for(AttributeType type : AttributeType.values()) {
+				try {
+					binValue = null;
+					switch(type) {
+					case BOOLEAN:
+						binValue = value.getBoolean();
+						break;
+					case COLOR:
+						binValue = parseColor((String)value.value, true);
+						break;
+					case DATE:
+						binValue = value.getDate();
+						break;
+					case ENUMERATION:
+						binValue = value.getEnumeration();
+						break;
+					case IMAGE:
+						binValue = value.getImage();
+						break;
+					case INET_ADDRESS:
+						binValue = value.getInetAddress();
+						break;
+					case INTEGER:
+						binValue = value.getInteger();
+						break;
+					case OBJECT:
+						binValue = value.getJavaObject();
+						break;
+					case POINT:
+						binValue = parsePoint((String)value.value, true);
+						break;
+					case POINT3D:
+						binValue = parsePoint3D((String)value.value, true);
+						break;
+					case POLYLINE:
+						binValue = parsePolyline((String)value.value, true);
+						break;
+					case POLYLINE3D:
+						binValue = parsePolyline3D((String)value.value, true);
+						break;
+					case REAL:
+						binValue = value.getReal();
+						break;
+					case STRING:
+						binValue = value.getString();
+						break;
+					case TIMESTAMP:
+						binValue = value.getTimestamp();
+						break;
+					case TYPE:
+						binValue = value.getJavaClass();
+						break;
+					case URI:
+						binValue = parseURI((String)value.value);
+						break;
+					case URL:
+						binValue = value.getURL();
+						break;
+					case UUID:
+						binValue = parseUUID((String)value.value);
+						break;
+					default:
+						//
+					}
+					if (binValue!=null) {
+						return new AttributeValueImpl(type, binValue);
+					}
+				}
+				catch(Throwable _) {
+					//
+				}
+			}
+		}
+		return value;
+	}
+	
+	/** Compare the two specified values.
+	 * 
+	 * @param arg0
+	 * @param arg1
+	 * @return replies a negative value if <var>arg0</var> is lesser than
+	 * <var>arg1</var>, a positive value if <var>arg0</var> is greater than
+	 * <var>arg1</var>, or <code>0</code> if they are equal.
+	 * @see AttributeValueComparator
+	 */
+	public static int compareValues(AttributeValue arg0, AttributeValue arg1) {
+		if (arg0==arg1) return 0;
+		if (arg0==null) return Integer.MAX_VALUE;
+		if (arg1==null) return Integer.MIN_VALUE;
+		
+		Object v0, v1;
+		
+		try {
+			v0 = arg0.getValue();
+		}
+		catch (Exception _) {
+			v0 = null;
+		}
+
+		try {
+			v1 = arg1.getValue();
+		}
+		catch (Exception _) {
+			v1 = null;
+		}
+
+		return compareRawValues(v0, v1);
+	}
+	
+	/** Compare the internal objects of two specified values
+	 * 
+	 * @param arg0
+	 * @param arg1
+	 * @return replies a negative value if <var>arg0</var> is lesser than
+	 * <var>arg1</var>, a positive value if <var>arg0</var> is greater than
+	 * <var>arg1</var>, or <code>0</code> if they are equal.
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	private static int compareRawValues(Object arg0, Object arg1) {
+		if (arg0==arg1) return 0;
+		if (arg0==null) return Integer.MAX_VALUE;
+		if (arg1==null) return Integer.MIN_VALUE;
+		
+		if ((arg0 instanceof Number)&&(arg1 instanceof Number))
+			return Double.compare(((Number)arg0).doubleValue(),((Number)arg1).doubleValue());
+
+		try {
+			if (arg0 instanceof Comparable<?>)
+				return ((Comparable)arg0).compareTo(arg1);
+		}
+		catch(RuntimeException _) {
+			//
+		}
+		
+		try {
+			if (arg1 instanceof Comparable<?>)
+				return - ((Comparable)arg1).compareTo(arg0);
+		}
+		catch(RuntimeException _) {
+			//
+		}
+		
+		if (arg0.equals(arg1)) return 0;
+		
+		String sv0 = arg0.toString();
+		String sv1 = arg1.toString();
+
+		if (sv0==sv1) return 0;
+		if (sv0==null) return Integer.MAX_VALUE;
+		if (sv1==null) return Integer.MIN_VALUE;
+		
+		return sv0.compareTo(sv1);
+	}
+
+	/**
+	 * Type of the metadata.
+	 */
+	private AttributeType type;
+	
+	/**
+	 * Value of the metadata.
+	 */
+	private Object value;
+	
+	/** Indicates if this attribute was assigned.
+	 */
+	private boolean assigned = false;
+
+	/**
+	 * Uninitialized value.
+	 */
+	public AttributeValueImpl() {
+		this.type = AttributeType.OBJECT;
+		this.value = this.type.getDefaultValue();
+	}
+
+	/**
+	 * Uninitialized value.
+	 * 
+	 * @param type is the type of the value.
+	 */
+	public AttributeValueImpl(AttributeType type) {
+		this.type = type;
+		this.value = null;
+		this.assigned = false;
+	}
+	
+	/**
+	 * @param value is the value to initialize this new instance.
+	 */
+	public AttributeValueImpl(AttributeValue value) {
+		if (value!=null) {
+			this.type = value.getType();
+			try {
+				this.value = value.getValue();
+				this.assigned = isNullAllowed() || (this.value!=null);
+			}
+			catch (AttributeException _) {
+				this.value = null;
+				this.assigned = false;
+			}
+		}
+		else {
+			this.value = null;
+			this.assigned = false;
+		}		
+	}
+
+	/**
+	 * @param type is the type of this value.
+	 * @param rawValue is the value.
+	 */
+	public AttributeValueImpl(AttributeType type, Object rawValue) {
+		this.type = type;
+		if (rawValue==null) {
+			this.value = rawValue;
+		}
+		else {
+			try {
+				this.value = type.cast(rawValue);
+			}
+			catch(Exception _) {
+				this.value = null;
+			}
+		}
+		this.assigned = this.value!=null;
+	}
+	
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(boolean value) {
+		this.type = AttributeType.BOOLEAN;
+		this.value = new Boolean(value);
+		this.assigned = true;
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(Color value) {
+		this.type = AttributeType.COLOR;
+		this.value = (value!=null) ? VectorToolkit.color(value.getRed(), value.getGreen(), value.getBlue(), value.getAlpha()) : null;
+		this.assigned = (this.value!=null);
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(UUID value) {
+		this.type = AttributeType.UUID;
+		this.value = (value!=null) ? new UUID(value.getMostSignificantBits(), value.getLeastSignificantBits()) : null;
+		this.assigned = (this.value!=null);
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(URL value) {
+		this.type = AttributeType.URL;
+		try {
+			this.value = (value!=null) ? new URL(value.toExternalForm()) : null;
+		}
+		catch (MalformedURLException e) {
+			this.value = null;
+		}
+		this.assigned = (this.value!=null);
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(URI value) {
+		this.type = AttributeType.URI;
+		try {
+			this.value = (value!=null) ? new URI(value.toASCIIString()) : null;
+		}
+		catch (URISyntaxException e) {
+			this.value = null;
+		}
+		this.assigned = (this.value!=null);
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(InetAddress value) {
+		this.type = AttributeType.INET_ADDRESS;
+		this.value = (value!=null) ? value : null;
+		this.assigned = (this.value!=null);
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(InetSocketAddress value) {
+		this.type = AttributeType.INET_ADDRESS;
+		this.value = (value!=null) ? value.getAddress() : null;
+		this.assigned = (this.value!=null);
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(Enum<?> value) {
+		this.type = AttributeType.ENUMERATION;
+		this.value = (value!=null) ? value : null;
+		this.assigned = (this.value!=null);
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(Class<?> value) {
+		this.type = AttributeType.TYPE;
+		this.value = (value!=null) ? value : null;
+		this.assigned = (this.value!=null);
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(Date value) {
+		this.type = AttributeType.DATE;
+		this.value = (value!=null) ? new Date(value.getTime()) : null;
+		this.assigned = (this.value!=null);
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(float value) {
+		this.type = AttributeType.REAL;
+		this.value = new Double(value);
+		this.assigned = true;
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(double value) {
+		this.type = AttributeType.REAL;
+		this.value = new Double(value);
+		this.assigned = true;
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(Image value) {
+		this.type = AttributeType.IMAGE;
+		this.value = value;
+		this.assigned = true;
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(int value) {
+		this.type = AttributeType.INTEGER;
+		this.value = new Long(value);
+		this.assigned = true;
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(long value) {
+		this.type = AttributeType.INTEGER;
+		this.value = new Long(value);
+		this.assigned = true;
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(Point2D value) {
+		this.type = AttributeType.POINT;
+		this.value = value;
+		this.assigned = (this.value!=null);
+	}
+
+	/**
+	 * @param x is the value.
+	 * @param y is the value.
+	 */
+	public AttributeValueImpl(float x, float y) {
+		this.type = AttributeType.POINT;
+		this.value = new Point2f(x,y);
+		this.assigned = true;
+	}
+
+	/**
+	 * @param x is the value.
+	 * @param y is the value.
+	 */
+	public AttributeValueImpl(double x, double y) {
+		this.type = AttributeType.POINT;
+		this.value = new Point2f(x,y);
+		this.assigned = true;
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(Point3D value) {
+		this.type = AttributeType.POINT3D;
+		this.value = value;
+		this.assigned = (this.value!=null);
+	}
+
+	/**
+	 * @param x is the value.
+	 * @param y is the value.
+	 * @param z is the value.
+	 */
+	public AttributeValueImpl(float x, float y, float z) {
+		this.type = AttributeType.POINT3D;
+		this.value = new Point3f(x,y,z);
+		this.assigned = true;
+	}
+
+	/**
+	 * @param x is the value.
+	 * @param y is the value.
+	 * @param z is the value.
+	 */
+	public AttributeValueImpl(double x, double y, double z) {
+		this.type = AttributeType.POINT3D;
+		this.value = new Point3f(x,y,z);
+		this.assigned = true;
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(String value) {
+		this.type = AttributeType.STRING;
+		this.value = value;
+		this.assigned = (value!=null);
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(Point2D[] value) {
+		this.type = AttributeType.POLYLINE;
+		this.value = value;
+		this.assigned = (this.value!=null);
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(Point3D[] value) {
+		this.type = AttributeType.POLYLINE3D;
+		this.value = value;
+		this.assigned = (this.value!=null);
+	}
+
+	/**
+	 * @param value is the value.
+	 */
+	public AttributeValueImpl(Object value) {
+		AttributeType detectedType = AttributeType.fromValue(value);
+		this.type = detectedType;
+		this.value = detectedType.cast(value);
+		this.assigned = isNullAllowed() || (this.value!=null);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @param o {@inheritDoc}
+	 * @return {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(Object o) {
+		if (o instanceof AttributeValue) {
+			return compareValues(this, (AttributeValue)o)==0;
+		}
+		return compareRawValues(this.value, o) == 0;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int hashCode() {
+        int PRIME = 31;
+        int result = 1;
+        result = PRIME * result + (this.value!=null ? this.value.hashCode() : 0);
+        return result;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @return {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		StringBuilder str = new StringBuilder();
+		str.append("["); //$NON-NLS-1$
+		str.append((this.value==null)
+				? "???" //$NON-NLS-1$
+				: this.value.toString());
+		str.append(":"); //$NON-NLS-1$
+		str.append(this.type.toString());
+		str.append("]"); //$NON-NLS-1$
+		return str.toString();
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean isAssignableFrom(AttributeType type) {
+		return getType().isAssignableFrom(type);
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean isAssignableFrom(AttributeValue value) {
+		return getType().isAssignableFrom(value.getType());
+	}
+
+	/** Replies if this value was assigned and
+	 * supposes that the <code>null</code> value is
+	 * allowed.
+	 *
+	 * @return <code>true</code> if the value is not assigned or equals to <code>null</code>.
+	 */
+	private boolean isNotAssignedOrNull() {
+		return ((!this.assigned)||(this.value==null));
+	}
+	
+	/** Assert that the attribute value was assigned.
+	 */
+	private void assertAssigned() throws AttributeNotInitializedException {
+		if ((this.type==null)||(!this.assigned)) throw new AttributeNotInitializedException();
+	}
+
+	/** Assert that the attribute value was assigned and not <code>null</code>.
+	 * 
+	 * @throws AttributeNotInitializedException
+	 */
+	protected void assertAssignedAndNotNull() throws AttributeNotInitializedException {
+		if ((this.type==null)||(!this.assigned)||(this.value==null)) throw new AttributeNotInitializedException();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean isBaseType() {
+		return this.type.isBaseType();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean isNullAllowed() {
+		return this.type.isNullAllowed();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public AttributeType getType() {
+		return this.type;
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setType(AttributeType type) throws InvalidAttributeTypeException {
+		try {
+			switch(type) {
+			case INTEGER:
+				this.value = new Long(getInteger()); break;
+			case REAL:
+				this.value = new Double(getReal()); break;
+			case STRING:
+				this.value = getString(); break;
+			case BOOLEAN:
+				this.value = new Boolean(getBoolean()); break;
+			case DATE:
+				this.value = getDate(); break;
+			case TIMESTAMP:
+				this.value = new Timestamp(getTimestamp()); break;
+			case OBJECT:
+				this.value = getJavaObject(); break;
+			case POINT:
+				this.value = getPoint(); break;
+			case POINT3D:
+				this.value = getPoint3D(); break;
+			case COLOR:
+				this.value = getColor(); break;
+			case UUID:
+				this.value = getUUID(); break;
+			case IMAGE:
+				this.value = getImage(); break;
+			case POLYLINE:
+				this.value = getPolyline(); break;
+			case POLYLINE3D:
+				this.value = getPolyline3D(); break;
+			case URI:
+				this.value = getURI(); break;
+			case URL:
+				this.value = getURL(); break;
+			case INET_ADDRESS:
+				this.value = getInetAddress(); break;
+			case ENUMERATION:
+				this.value = getEnumeration(); break;
+			case TYPE:
+				this.value = getJavaClass(); break;
+			default:
+				throw new InvalidAttributeTypeException();
+			}			
+		}
+		catch(NumberFormatException ex) {
+			throw new InvalidAttributeTypeException();
+		}
+		catch (AttributeNotInitializedException e) {
+			this.value = type.getDefaultValue();
+		}
+		this.type = type;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean cast(AttributeType attrType) {
+		boolean b = true;
+		try {
+			setType(attrType);
+		}
+		catch(InvalidAttributeTypeException ex) {
+			this.value = attrType.getDefaultValue();
+			b = false;
+		}
+		this.type = attrType;
+		return b;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void castAndSet(AttributeType attrType, Object attrValue) {
+		try {
+			if (attrValue instanceof AttributeValue) {
+				this.type = ((AttributeValue)attrValue).getType();
+				try {
+					this.value = ((AttributeValue)attrValue).getValue();
+					this.assigned = true;
+				}
+				catch (AttributeNotInitializedException e) {
+					this.value = attrType.getDefaultValue();
+					this.assigned = true;
+				}
+			}
+			else {
+				this.type = attrType;
+				this.value = attrType.cast(attrValue);
+				this.assigned = true;
+			}
+			if (attrValue!=null) {
+				setType(attrType);
+			}
+			else {
+				this.value = attrType.getDefaultValue();
+				this.assigned = true;
+			}
+		}
+		catch(InvalidAttributeTypeException ex) {
+			this.value = attrType.getDefaultValue();
+			this.assigned = true;
+		}
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Object getValue() throws InvalidAttributeTypeException, AttributeNotInitializedException{
+		assertAssigned();
+		return this.value;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public final Class<?> getInternalStorageType() {
+		switch(this.type) {
+		case BOOLEAN:
+			return Boolean.class;
+		case COLOR:
+			return Color.class;
+		case DATE:
+			return Date.class;
+		case ENUMERATION:
+			return Enum.class;
+		case IMAGE:
+			return Image.class;
+		case INET_ADDRESS:
+			return InetAddress.class;
+		case INTEGER:
+			return Long.class;
+		case OBJECT:
+			return Object.class;
+		case POINT:
+			return Point2D.class;
+		case POINT3D:
+			return Point3D.class;
+		case POLYLINE:
+			return Point2D[].class;
+		case POLYLINE3D:
+			return Point3D[].class;
+		case REAL:
+			return Double.class;
+		case STRING:
+			return String.class;
+		case TIMESTAMP:
+			return Timestamp.class;
+		case TYPE:
+			return Class.class;
+		case URI:
+			return URI.class;
+		case URL:
+			return URL.class;
+		case UUID:
+			return UUID.class;
+		default:
+		}
+		return null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setValue(AttributeValue value) {
+		this.type = value.getType();
+		try {
+			this.assigned = value.isAssigned();
+			this.value = value.getValue();
+		}
+		catch (AttributeException e) {
+			this.value = this.type.getDefaultValue();
+		}
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setValue(Object value) {
+		AttributeType detectedType = AttributeType.fromValue(value);
+		this.value = detectedType.cast(value);
+		this.assigned = (this.value!=null);
+	}
+
+	/** Set this value with the content of the specified one.
+	 * <p>
+	 * The type of the attribute will be NOT detected from the type
+	 * of the object. It means that it is not changed by this function.
+	 * The given value must be compatible with internal representation
+	 * of the attribute implementation.
+	 * 
+	 * @param value is the raw value to put inside this attribute value.
+	 */
+	protected void setInternalValue(Object value) {
+		this.value = value;
+		this.assigned = (this.value!=null);
+	}
+
+	/** Set this value with the content of the specified one.
+	 * <p>
+	 * The type of the attribute will be NOT detected from the type
+	 * of the object. It means that it will be equal to the given type,
+	 * even if the given value is incompatible.
+	 * The given value must be compatible with internal representation
+	 * of the attribute implementation.
+	 * 
+	 * @param value is the raw value to put inside this attribute value.
+	 * @param type is the type of the value.
+	 */
+	protected void setInternalValue(Object value, AttributeType type) {
+		this.value = value;
+		this.assigned = (this.value!=null);
+		this.type = type;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setToDefault() {
+		this.assigned = true;
+		this.value = this.type.getDefaultValue();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setToDefaultIfUninitialized() {
+		if (!isAssigned()) setToDefault();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public long getInteger() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case INTEGER:
+				return ((Long)this.value).longValue();
+			case TIMESTAMP:
+				return ((Timestamp)this.value).longValue();
+			case REAL:
+				return ((Double)this.value).longValue();
+			case STRING:
+				return Long.parseLong((String)this.value);
+			case DATE:
+				return ((Date)this.value).getTime();
+			case BOOLEAN:
+				return ((Boolean)this.value).booleanValue() ? 1 : 0;
+			case COLOR:
+				return ((Color)this.value).getRGB();
+			case OBJECT:
+				if (this.value instanceof Number)
+					return ((Number)this.value).longValue();
+				break;
+			case ENUMERATION:
+				if (this.value instanceof Enum<?>)
+					return ((Enum<?>)this.value).ordinal();
+				break;
+			case IMAGE:
+			case POINT:
+			case POINT3D:
+			case POLYLINE:
+			case POLYLINE3D:
+			case URI:
+			case URL:
+			case UUID:
+			case INET_ADDRESS:
+			case TYPE:
+				break;
+			default:
+				throw new InvalidAttributeTypeException();
+			}
+		}
+		catch(NumberFormatException _) {
+			//
+		}
+		throw new InvalidAttributeTypeException();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setInteger(int value) {
+		this.value = new Long(value);
+		this.type = AttributeType.INTEGER;
+		this.assigned = true;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setInteger(long value) {
+		this.value = new Long(value);
+		this.type = AttributeType.INTEGER;
+		this.assigned = true;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public double getReal() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case INTEGER:
+				return ((Long)this.value).doubleValue();
+			case TIMESTAMP:
+				return ((Timestamp)this.value).doubleValue();
+			case REAL:
+				return ((Double)this.value).doubleValue();
+			case STRING:
+				return Double.parseDouble((String)this.value);
+			case DATE:
+				return ((Date)this.value).getTime();
+			case BOOLEAN:
+				return ((Boolean)this.value).booleanValue() ? 1. : 0.;
+			case COLOR:
+				return ((Color)this.value).getRGB();
+			case OBJECT:
+				if (this.value instanceof Number)
+					return ((Number)this.value).doubleValue();
+				break;
+			case ENUMERATION:
+				if (this.value instanceof Enum<?>)
+					return ((Enum<?>)this.value).ordinal();
+				break;
+			case IMAGE:
+			case POINT:
+			case POINT3D:
+			case POLYLINE:
+			case POLYLINE3D:
+			case URI:
+			case URL:
+			case UUID:
+			case INET_ADDRESS:
+			case TYPE:
+			default:
+				throw new InvalidAttributeTypeException();
+			}
+		}
+		catch(ClassCastException _) {
+			//
+		}
+		catch(NumberFormatException _) {
+			//
+		}
+		throw new InvalidAttributeTypeException();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setReal(double value) {
+		this.value = new Double(value);
+		this.type = AttributeType.REAL;
+		this.assigned = true;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public String getString() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case STRING:
+				return (String)this.value;
+			case BOOLEAN:
+				return ((Boolean)this.value).toString();
+			case COLOR:
+			{
+				Color col = ((Color)this.value);
+				return Integer.toString(col.getRed())
+					+';'+col.getGreen()
+					+';'+col.getBlue()
+					+';'+col.getAlpha();
+			}
+			case UUID:
+			{
+				UUID uuid = ((UUID)this.value);
+				return uuid.toString();
+			}
+			case URL:
+			{
+				URL url = ((URL)this.value);
+				return url.toExternalForm();
+			}
+			case URI:
+			{
+				URI uri = ((URI)this.value);
+				return uri.toASCIIString();
+			}
+			case TIMESTAMP:
+			{
+				SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$
+				return format.format(new Date(((Timestamp)this.value).longValue()));
+			}
+			case INTEGER:
+				return ((Long)this.value).toString();
+			case REAL:
+				return ((Double)this.value).toString();
+			case POINT:
+				Point2D pt2 = ((Point2D)this.value);
+				StringBuilder buffer = new StringBuilder();
+				buffer.append(pt2.getX());
+				buffer.append(";"); //$NON-NLS-1$
+				buffer.append(pt2.getY());
+				return buffer.toString();
+			case POINT3D:
+				Point3D pt3 = ((Point3D)this.value);
+				buffer = new StringBuilder();
+				buffer.append(pt3.getX());
+				buffer.append(";"); //$NON-NLS-1$
+				buffer.append(pt3.getY());
+				buffer.append(";"); //$NON-NLS-1$
+				buffer.append(pt3.getZ());
+				return buffer.toString();
+			case DATE:
+			{
+				SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); //$NON-NLS-1$
+				return format.format((Date)this.value);
+			}
+			case POLYLINE:
+			{ 
+				buffer = new StringBuilder();
+				Point2D[] lstpt2 = ((Point2D[])this.value);
+				for(int i=0; i<lstpt2.length; ++i) {
+					if (lstpt2[i]!=null) {
+						if (buffer.length()>0) buffer.append(";"); //$NON-NLS-1$
+						buffer.append(lstpt2[i].getX());
+						buffer.append(";"); //$NON-NLS-1$
+						buffer.append(lstpt2[i].getY());
+					}
+				}
+				return buffer.toString();
+			}
+			case POLYLINE3D:
+			{ 
+				buffer = new StringBuilder();
+				Point3D[] lstpt3 = ((Point3D[])this.value);
+				for(int i=0; i<lstpt3.length; ++i) {
+					if (lstpt3[i]!=null) {
+						if (buffer.length()>0) buffer.append(";"); //$NON-NLS-1$
+						buffer.append(lstpt3[i].getX());
+						buffer.append(";"); //$NON-NLS-1$
+						buffer.append(lstpt3[i].getY());
+						buffer.append(";"); //$NON-NLS-1$
+						buffer.append(lstpt3[i].getZ());
+					}
+				}
+				return buffer.toString();
+			}
+			case ENUMERATION:
+			{
+				StringBuilder b = new StringBuilder();
+				Enum<?> enumeration = (Enum<?>)this.value;
+				Class<?> enumerationType = enumeration.getDeclaringClass();
+				String typeName = enumerationType.getCanonicalName();
+				b.append(typeName);
+				b.append("."); //$NON-NLS-1$
+				b.append(((Enum<?>)this.value).name());
+				return b.toString();
+			}
+			case TYPE:
+			{
+				return ((Class<?>)this.value).getCanonicalName();
+			}
+			case INET_ADDRESS:
+			case OBJECT:
+			case IMAGE:
+				return this.value.toString();
+			default:
+				throw new InvalidAttributeTypeException();
+			}
+		}
+		catch(ClassCastException _) {
+			//
+		}
+		throw new InvalidAttributeTypeException();
+	}
+		
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setString(String value) {
+		this.value = value;
+		this.type = AttributeType.STRING;
+		this.assigned = this.value!=null;
+	}
+	
+	/** Parse a date according to the specified locale.
+	 */
+	private static Date extractDate(String text, Locale locale) {
+		DateFormat fmt;
+		for(int style=0; style<=3; ++style) {
+			// Date and time parsing
+			for(int style2=0; style2<=3; ++style2) {
+				fmt = DateFormat.getDateTimeInstance(style,style2,locale);
+				try {
+					return fmt.parse(text);
+				}
+				catch(ParseException _) {
+					//
+				}
+			}
+			// Date only parsing
+			fmt = DateFormat.getDateInstance(style,locale);
+			try {
+				return fmt.parse(text);
+			}
+			catch(ParseException _) {
+				//
+			}
+			// Time only parsing
+			fmt = DateFormat.getTimeInstance(style,locale);
+			try {
+				return fmt.parse(text);
+			}
+			catch(ParseException _) {
+				//
+			}
+		}
+		return null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Date getDate() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case DATE:
+				return (Date)((Date)this.value).clone();
+			case REAL:
+				 return new Date(((Double)this.value).longValue());				
+			case INTEGER:
+				 return new Date(((Long)this.value).longValue());				
+			case TIMESTAMP:
+				 return new Date(((Timestamp)this.value).longValue());				
+			case STRING:
+				String txt = (String)this.value;
+				DateFormat fmt;
+				Date dt;
+				
+				dt = extractDate(txt, Locale.getDefault());
+				if (dt!=null) return dt;
+				
+				dt = extractDate(txt, Locale.US);
+				if (dt!=null) return dt;
+				
+				dt = extractDate(txt, Locale.UK);
+				if (dt!=null) return dt;
+
+				try {
+					fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$
+					return fmt.parse(txt);
+				}
+				catch(ParseException _) {
+					//
+				}
+				fmt = new SimpleDateFormat("yyyy-MM-dd"); //$NON-NLS-1$
+				return fmt.parse(txt);
+			case OBJECT:
+				if (this.value instanceof Date)
+					return (Date)this.value;
+				if (this.value instanceof Calendar)
+					return ((Calendar)this.value).getTime();
+				if (this.value instanceof Number)
+					return new Date(((Number)this.value).longValue());
+				break;
+			case BOOLEAN:
+			case COLOR:
+			case IMAGE:
+			case POINT:
+			case POINT3D:
+			case POLYLINE:
+			case POLYLINE3D:
+			case URI:
+			case URL:
+			case UUID:
+			case INET_ADDRESS:
+			case ENUMERATION:
+			case TYPE:
+			default:
+			}
+		}
+		catch(Exception e) {
+			//
+		}
+		throw new InvalidAttributeTypeException();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setDate(Date value) {
+		this.value = value;
+		this.type = AttributeType.DATE;
+		this.assigned = this.value!=null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean getBoolean() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case BOOLEAN:
+				return ((Boolean)this.value).booleanValue();
+			case STRING:
+				// Do not use the function Boolean.parseBoolean() because
+				// it replies false when the string does not contains "true"
+				if (TRUE_CONSTANT.compareToIgnoreCase((String)this.value)==0)
+					return true;
+				if (YES_CONSTANT.compareToIgnoreCase((String)this.value)==0)
+					return true;
+				if (OUI_CONSTANT.compareToIgnoreCase((String)this.value)==0)
+					return true;
+				if (T_CONSTANT.compareToIgnoreCase((String)this.value)==0)
+					return true;
+				if (Y_CONSTANT.compareToIgnoreCase((String)this.value)==0)
+					return true;
+				if (O_CONSTANT.compareToIgnoreCase((String)this.value)==0)
+					return true;
+
+				if (FALSE_CONSTANT.compareToIgnoreCase((String)this.value)==0)
+					return false;
+				if (NO_CONSTANT.compareToIgnoreCase((String)this.value)==0)
+					return false;
+				if (NON_CONSTANT.compareToIgnoreCase((String)this.value)==0)
+					return false;
+				if (F_CONSTANT.compareToIgnoreCase((String)this.value)==0)
+					return false;
+				if (N_CONSTANT.compareToIgnoreCase((String)this.value)==0)
+					return false;
+
+				break;
+			case INTEGER:
+				return ((Long)this.value).longValue() != 0;
+			case TIMESTAMP:
+				return ((Timestamp)this.value).longValue() != 0;
+			case REAL:
+				return ((Double)this.value).doubleValue() != 0.;
+			case OBJECT:
+				if (this.value instanceof Boolean)
+					return ((Boolean)this.value).booleanValue();
+				break;
+			case COLOR:
+			case DATE:
+			case IMAGE:
+			case POINT:
+			case POINT3D:
+			case POLYLINE:
+			case POLYLINE3D:
+			case URI:
+			case URL:
+			case UUID:
+			case INET_ADDRESS:
+			case ENUMERATION:
+			case TYPE:
+			default:
+			}
+		}
+		catch(ClassCastException _) {
+			//
+		}			
+		throw new InvalidAttributeTypeException();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setBoolean(boolean value) {
+		this.value = new Boolean(value);
+		this.type = AttributeType.BOOLEAN;
+		this.assigned = true;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean isObjectValue() {
+		return !isBaseType();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	@SuppressWarnings("unchecked")
+	public <T> T getJavaObject() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		if (!isObjectValue())
+			throw new InvalidAttributeTypeException();
+		if (isNotAssignedOrNull()) return null;
+		try {
+			return (T)this.value;
+		}
+		catch(ClassCastException _) {
+			throw new InvalidAttributeTypeException();
+		}			
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setJavaObject(Object value) {
+		this.value = value;
+		this.type = AttributeType.OBJECT;
+		this.assigned = true;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public long getTimestamp() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case INTEGER:
+				return ((Long)this.value).longValue();
+			case TIMESTAMP:
+				return ((Timestamp)this.value).longValue();
+			case REAL:
+				return ((Double)this.value).longValue();
+			case STRING:
+				return Long.parseLong((String)this.value);
+			case DATE:
+				return ((Date)this.value).getTime();
+			case BOOLEAN:
+				return ((Boolean)this.value).booleanValue() ? 1 : 0;
+			case COLOR:
+				return ((Color)this.value).getRGB();
+			case OBJECT:
+				if (this.value instanceof Number)
+					return ((Number)this.value).longValue();
+				if (this.value instanceof Date)
+					return ((Date)this.value).getTime();
+				if (this.value instanceof Calendar)
+					return ((Calendar)this.value).getTimeInMillis();
+				break;
+			case IMAGE:
+			case POINT:
+			case POINT3D:
+			case POLYLINE:
+			case POLYLINE3D:
+			case URI:
+			case URL:
+			case UUID:
+			case INET_ADDRESS:
+			case ENUMERATION:
+			case TYPE:
+			default:
+			}
+		}
+		catch(ClassCastException _) {
+			//
+		}
+		catch(NumberFormatException _) {
+			//
+		}
+		throw new InvalidAttributeTypeException();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setTimestamp(long value) {
+		this.value = new Timestamp(value);
+		this.type = AttributeType.TIMESTAMP;
+		this.assigned = true;
+	}
+	
+	private static Point3D parsePoint3D(String text, boolean isStrict) {
+		String[] comp = text.split(";"); //$NON-NLS-1$
+		if (isStrict && comp.length!=3) {
+			return null;
+		}
+		Point3f pt3 = new Point3f();
+		if (comp.length>0) pt3.setX(Float.parseFloat(comp[0]));
+		if (comp.length>1) pt3.setY(Float.parseFloat(comp[1]));
+		if (comp.length>2) pt3.setZ(Float.parseFloat(comp[2]));
+		return pt3;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Point3D getPoint3D() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case COLOR:
+				Color col = (Color)this.value;
+				return new Point3f(col.getRed(),col.getGreen(),col.getBlue()); 
+			case REAL:
+				Double flt = (Double)this.value;
+				return new Point3f(flt.floatValue(),0,0); 
+			case INTEGER:
+				Long lg = (Long)this.value;
+				return new Point3f(lg.floatValue(),0,0); 
+			case TIMESTAMP:
+				Timestamp ts = (Timestamp)this.value;
+				return new Point3f(ts.floatValue(),0,0); 
+			case DATE:
+				Date dt = (Date)this.value;
+				return new Point3f(dt.getTime(),0,0); 
+			case POINT:
+				Point2D pt2 = (Point2D)this.value;
+				return new Point3f(pt2.getX(), pt2.getY(),0); 
+			case POINT3D:
+				return ((Point3D)this.value).clone(); 
+			case STRING:
+				return parsePoint3D((String)this.value, false);
+			case OBJECT:
+				if (this.value instanceof Tuple3D) {
+					Tuple3D<?> t3 = (Tuple3D<?>)this.value;
+					return new Point3f(t3.getX(), t3.getY(), t3.getZ()); 
+				}
+				if (this.value instanceof Tuple2D) {
+					Tuple2D<?> t2 = (Tuple2D<?>)this.value;
+					return new Point3f(t2.getX(), t2.getY(), 0); 
+				}
+				break;
+			case BOOLEAN:
+			case IMAGE:
+			case POLYLINE:
+			case POLYLINE3D:
+			case URI:
+			case URL:
+			case UUID:
+			case INET_ADDRESS:
+			case ENUMERATION:
+			case TYPE:
+			default:
+			}
+		}
+		catch(ClassCastException _) {
+			//
+		}
+		catch(NumberFormatException _) {
+			//
+		}			
+		throw new InvalidAttributeTypeException();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setPoint3D(Point3D value) {
+		this.value = value;
+		this.type = AttributeType.POINT3D;
+		this.assigned = this.value!=null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setPoint3D(float x, float y, float z) {
+		this.value = new Point3f(x,y,z);
+		this.type = AttributeType.POINT3D;
+		this.assigned = true;
+	}
+	
+	private static Point2D parsePoint(String text, boolean isStrict) {
+		String[] comp = text.split(";"); //$NON-NLS-1$
+		if (isStrict && comp.length!=2) {
+			return null;
+		}
+		float x=0,y=0;
+		if (comp.length>0) x = Float.parseFloat(comp[0]);
+		if (comp.length>1) y = Float.parseFloat(comp[1]);
+		return new Point2f(x,y);
+		
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Point2D getPoint() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case COLOR:
+				Color col = (Color)this.value;
+				return new Point2f(col.getRed(),col.getGreen()); 
+			case REAL:
+				Double flt = (Double)this.value;
+				return new Point2f(flt.floatValue(),0f); 
+			case INTEGER:
+				Long lg = (Long)this.value;
+				return new Point2f(lg.floatValue(),0f); 
+			case TIMESTAMP:
+				Timestamp ts = (Timestamp)this.value;
+				return new Point2f(ts.floatValue(),0f); 
+			case DATE:
+				Date dt = (Date)this.value;
+				return new Point2f(dt.getTime(),0f); 
+			case POINT:
+				return new Point2f(((Point2D)this.value).getX(),
+						((Point2D)this.value).getY());
+			case POINT3D:
+				Point3D pt3 = (Point3D)this.value;
+				return new Point2f(pt3.getX(),pt3.getY()); 
+			case STRING:
+				return parsePoint((String)this.value, false);
+			case OBJECT:
+				if (this.value instanceof Tuple3D) {
+					Tuple3D<?> t3 = (Tuple3D<?>)this.value;
+					return new Point2f(t3.getX(), t3.getY()); 
+				}
+				if (this.value instanceof Tuple2D) {
+					Tuple2D<?> t2 = (Tuple2D<?>)this.value;
+					return new Point2f(t2.getX(), t2.getY()); 
+				}
+				break;
+			case BOOLEAN:
+			case IMAGE:
+			case POLYLINE:
+			case POLYLINE3D:
+			case URI:
+			case URL:
+			case UUID:
+			case INET_ADDRESS:
+			case ENUMERATION:
+			case TYPE:
+			default:
+			}
+		}
+		catch(ClassCastException _) {
+			//
+		}			
+		catch(NumberFormatException _) {
+			//
+		}			
+		throw new InvalidAttributeTypeException();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setPoint(Point2D value) {
+		this.value = value;
+		this.type = AttributeType.POINT;
+		this.assigned = this.value!=null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setPoint(float x, float y) {
+		this.value = new Point2f(x,y);
+		this.type = AttributeType.POINT;
+		this.assigned = true;
+	}
+	
+	private static Color parseColor(String text, boolean isStrict) {
+		String[] comp = text.split(";"); //$NON-NLS-1$
+		if (isStrict && comp.length!=3 && comp.length!=4) {
+			return null;
+		}
+		int r=0,g=0,b=0,a=255;
+		if (comp.length>0) r = Integer.parseInt(comp[0]);
+		if (comp.length>1) g = Integer.parseInt(comp[1]);
+		if (comp.length>2) b = Integer.parseInt(comp[2]);
+		if (comp.length>3) a = Integer.parseInt(comp[3]);
+		if ((r<256)&&(r>=0)&&(g<256)&&(g>=0)&&(b<256)&&(b>=0)&&
+			(a<256)&&(a>=0))
+			return VectorToolkit.color(r,g,b,a);
+		return null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Color getColor() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case COLOR:
+				Color col = (Color)this.value;
+				return VectorToolkit.color(col.getRed(),col.getGreen(),col.getBlue(),col.getAlpha());
+			case POINT:
+				Point2D pt2 = (Point2D)this.value;
+				return VectorToolkit.color(pt2.getX(),pt2.getY(),0f, 1f);
+			case POINT3D:
+				Point3D pt3 = (Point3D)this.value;
+				return VectorToolkit.color(pt3.getX(),pt3.getY(),pt3.getZ(), 1f);
+			case STRING:
+				Color color = parseColor((String)this.value, false);
+				if (color!=null) return color;
+				break;
+			case INTEGER:
+				return VectorToolkit.color(((Long)this.value).intValue());
+			case TIMESTAMP:
+				return VectorToolkit.color(((Timestamp)this.value).intValue());
+			case REAL:
+				return VectorToolkit.color(((Double)this.value).intValue());
+			case DATE:
+				return VectorToolkit.color((int)((Date)this.value).getTime());
+			case OBJECT:
+				if (this.value instanceof Color)
+					return (Color)this.value;
+				if (this.value instanceof Number)
+					return VectorToolkit.color(((Number)this.value).intValue());
+				if (this.value instanceof Date)
+					return VectorToolkit.color((int)((Date)this.value).getTime());
+				if (this.value instanceof Calendar)
+					return VectorToolkit.color((int)((Calendar)this.value).getTimeInMillis());
+				break;
+			case BOOLEAN:
+			case IMAGE:
+			case POLYLINE:
+			case POLYLINE3D:
+			case URI:
+			case URL:
+			case UUID:
+			case INET_ADDRESS:
+			case ENUMERATION:
+			case TYPE:
+			default:
+			}
+		}
+		catch(ClassCastException _) {
+			//
+		}
+		catch(NumberFormatException _) {
+			//
+		}
+		throw new InvalidAttributeTypeException();		
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setColor(Color c) {
+		this.value = c;
+		this.type = AttributeType.COLOR;		
+		this.assigned = this.value!=null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setColor(float r, float g, float b) {
+		this.value = VectorToolkit.color(r,g,b, 1f);
+		this.type = AttributeType.COLOR;				
+		this.assigned = true;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setColor(float r, float g, float b, float a) {
+		this.value = VectorToolkit.color(r,g,b,a);
+		this.type = AttributeType.COLOR;				
+		this.assigned = true;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setColor(int r, int g, int b) {
+		this.value = VectorToolkit.color(r,g,b,255);
+		this.type = AttributeType.COLOR;				
+		this.assigned = true;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setColor(int r, int g, int b, int a) {
+		this.value = VectorToolkit.color(r,g,b,a);
+		this.type = AttributeType.COLOR;				
+		this.assigned = true;
+	}
+
+	private static UUID parseUUID(String text) {
+		try {
+			URI uri = new URI(text);
+			if ("uuid".equalsIgnoreCase(uri.getScheme())) { //$NON-NLS-1$
+				return UUID.fromString(uri.getHost());
+			}
+		}
+		catch(Throwable _) {
+			//
+		}
+		try {
+			return UUID.fromString(text);
+		}
+		catch(Throwable _) {
+			//
+		}
+		return null;
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public UUID getUUID() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case UUID:
+				UUID id = (UUID)this.value;
+				return new UUID(id.getMostSignificantBits(), id.getLeastSignificantBits());
+			case URI:
+				URI uri = (URI)this.value;
+				if ("uuid".equalsIgnoreCase(uri.getScheme())) { //$NON-NLS-1$
+					try {
+						return UUID.fromString(uri.getHost());
+					}
+					catch(AssertionError e) {
+						throw e;
+					}
+					catch(Throwable _) {
+						//
+					}
+				}
+				break; // see below for the other cases
+			case OBJECT:
+				if (this.value instanceof UUID) {
+					return (UUID)this.value;
+				}
+				if (this.value instanceof URI
+					&& "uuid".equalsIgnoreCase(((URI)this.value).getScheme())) { //$NON-NLS-1$
+					try {
+						return UUID.fromString(((URI)this.value).getHost());
+					}
+					catch(AssertionError e) {
+						throw e;
+					}
+					catch(Throwable _) {
+						//
+					}
+				}
+				break; // see below for the other cases
+			case STRING:
+			case BOOLEAN:
+			case COLOR:
+			case DATE:
+			case IMAGE:
+			case INTEGER:
+			case POINT:
+			case POINT3D:
+			case POLYLINE:
+			case POLYLINE3D:
+			case REAL:
+			case TIMESTAMP:
+			case URL:
+			case INET_ADDRESS:
+			case ENUMERATION:
+			case TYPE:
+			default:
+				break; // see below, for the computation of an UUID from the value
+			}
+		}
+		catch(ClassCastException _) {
+			//
+		}
+		catch(NumberFormatException _) {
+			//
+		}
+		if (this.value==null)
+			return (UUID)AttributeType.UUID.getDefaultValue();
+		String s = this.value.toString();
+		if (s==null || "".equals(s)) //$NON-NLS-1$
+			return (UUID)AttributeType.UUID.getDefaultValue();
+		try {
+			return UUID.fromString(s);
+		}
+		catch(AssertionError e) {
+			throw e;
+		}
+		catch(Throwable _) {
+			//
+		}
+		return UUID.nameUUIDFromBytes(s.getBytes());
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public URL getURL() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case URL:
+				return new URL(((URL)this.value).toExternalForm());
+			case URI:
+				return ((URI)this.value).toURL();
+			case STRING:
+				return new URL((String)this.value);
+			case OBJECT:
+				if (this.value instanceof URL)
+					return (URL)this.value;
+				if (this.value instanceof URI)
+					return ((URI)this.value).toURL();
+				break; // Other objects, see below
+			case INET_ADDRESS:
+				return new URL(DEFAULT_SCHEME.name(), ((InetAddress)this.value).getHostAddress(), ""); //$NON-NLS-1$
+			case UUID:
+			case BOOLEAN:
+			case COLOR:
+			case DATE:
+			case IMAGE:
+			case INTEGER:
+			case POINT:
+			case POINT3D:
+			case POLYLINE:
+			case POLYLINE3D:
+			case REAL:
+			case TIMESTAMP:
+			case ENUMERATION:
+			case TYPE:
+			default:
+			}
+		}
+		catch(ClassCastException _) {
+			//
+		}
+		catch(MalformedURLException _) {
+			//
+		}
+		if (this.value==null)
+			return (URL)AttributeType.URL.getDefaultValue();
+		String s = this.value.toString();
+		if (s==null)
+			return (URL)AttributeType.URL.getDefaultValue();
+		try {
+			return new URL(s);
+		}
+		catch (MalformedURLException _) {
+			throw new InvalidAttributeTypeException();
+		}
+	}
+	
+	private static URI parseURI(String text) {
+		try {
+			URI uri = new URI(text);
+			if (uri.getScheme()!=null && !uri.getScheme().isEmpty())
+				return uri;
+		}
+		catch(Throwable _) {
+			//
+		}
+		return null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public URI getURI() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case URL:
+				return ((URL)this.value).toURI();
+			case URI:
+				return new URI(((URI)this.value).toASCIIString());
+			case STRING:
+				return new URI((String)this.value);
+			case UUID:
+				return new URI("uuid:"+((UUID)this.value).toString()); //$NON-NLS-1$
+			case OBJECT:
+				if (this.value instanceof URI)
+					return (URI)this.value;
+				if (this.value instanceof URL)
+					return ((URL)this.value).toURI();
+				if (this.value instanceof UUID)
+					return new URI("uuid:"+((UUID)this.value).toString()); //$NON-NLS-1$
+				break; // Other objects, see below
+			case INET_ADDRESS:
+				return new URI(DEFAULT_SCHEME.name(), ((InetAddress)this.value).getHostAddress(), ""); //$NON-NLS-1$
+			case BOOLEAN:
+			case COLOR:
+			case DATE:
+			case IMAGE:
+			case INTEGER:
+			case POINT:
+			case POINT3D:
+			case POLYLINE:
+			case POLYLINE3D:
+			case REAL:
+			case TIMESTAMP:
+			case ENUMERATION:
+			case TYPE:
+			default:
+			}
+		}
+		catch(ClassCastException _) {
+			//
+		}
+		catch(URISyntaxException _) {
+			//
+		}
+		if (this.value==null)
+			return (URI)AttributeType.URI.getDefaultValue();
+		String s = this.value.toString();
+		if (s==null)
+			return (URI)AttributeType.URI.getDefaultValue();
+		try {
+			return new URI(s);
+		}
+		catch (URISyntaxException _) {
+			throw new InvalidAttributeTypeException();
+		}
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setUUID(UUID u) {
+		this.value = (u==null) ? AttributeType.UUID.getDefaultValue() : u;
+		this.type = AttributeType.UUID;		
+		this.assigned = this.value!=null;
+	}
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param id
+	 */
+	public void setUUID(String id) {
+		try {
+			this.value = (id!=null) ? UUID.fromString(id) : null;
+		}
+		catch(Throwable _) {
+			assert(id!=null);
+			this.value = UUID.nameUUIDFromBytes(id.getBytes());
+		}
+		this.type = AttributeType.UUID;				
+		this.assigned = this.value!=null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setURL(URL u) {
+		this.value = (u==null) ? AttributeType.URL.getDefaultValue() : u;
+		this.type = AttributeType.URL;		
+		this.assigned = this.value!=null;
+	}
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param url
+	 */
+	public void setURL(String url) {
+		try {
+			this.value = (url!=null) ? new URL(url) : null;
+		}
+		catch(Throwable _) {
+			this.value = null;
+		}
+		this.type = AttributeType.URL;				
+		this.assigned = this.value!=null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setURI(URI u) {
+		this.value = (u==null) ? AttributeType.URI.getDefaultValue() : u;
+		this.type = AttributeType.URI;		
+		this.assigned = this.value!=null;
+	}
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param uri
+	 */
+	public void setURI(String uri) {
+		try {
+			this.value = (uri!=null) ? new URI(uri) : null;
+		}
+		catch(Throwable _) {
+			this.value = null;
+		}
+		this.type = AttributeType.URI;				
+		this.assigned = this.value!=null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Image getImage() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		try {
+			assertAssigned();
+			switch(this.type) {
+			case IMAGE:
+				assertAssignedAndNotNull();
+				return (Image)this.value;
+			case OBJECT:
+				if ((this.value==null)||(this.value instanceof Image)) return (Image)this.value;
+				break;
+			case BOOLEAN:
+			case COLOR:
+			case DATE:
+			case INTEGER:
+			case POINT:
+			case POINT3D:
+			case POLYLINE:
+			case POLYLINE3D:
+			case REAL:
+			case STRING:
+			case TIMESTAMP:
+			case URI:
+			case URL:
+			case UUID:
+			case INET_ADDRESS:
+			case ENUMERATION:
+			case TYPE:
+			default:
+			}
+		}
+		catch(ClassCastException _) {
+			//
+		}
+		throw new InvalidAttributeTypeException();		
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setImage(Image c) {
+		this.value = c;
+		this.type = AttributeType.IMAGE;		
+		this.assigned = true;
+	}
+
+	private static Point3D[] parsePolyline3D(String text, boolean isStrict) {
+		String[] comp = text.split(";"); //$NON-NLS-1$
+		if (isStrict && (comp.length%3)!=0) {
+			return null;
+		}
+		int fullPoints = comp.length/3;
+		boolean addPt = (fullPoints*3!=comp.length); 
+		Point3D[] tab = new Point3D[addPt ? fullPoints+1 : fullPoints];
+		for(int i=2,j=0; (i<comp.length)&&(j<tab.length); i+=3,++j) {
+			tab[j] = new Point3f(
+					Float.parseFloat(comp[i-2]),
+					Float.parseFloat(comp[i-1]),
+					Float.parseFloat(comp[i]));
+		}
+		if (addPt) {
+			float x = Float.parseFloat(comp[fullPoints*3]);
+			int idx = fullPoints*3+1;
+			float y = idx<comp.length ? Float.parseFloat(comp[idx]) : 0;
+			tab[tab.length-1]  = new Point3f(x, y, 0);
+		}
+		return tab;
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Point3D[] getPolyline3D() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case POINT:
+				Point2D pt2 = (Point2D)this.value;
+				return new Point3D[] {
+						new Point3f(pt2.getX(),pt2.getY(),0f) 
+				};
+			case POINT3D:
+				return new Point3D[] {
+					((Point3D)this.value).clone() 
+				};
+			case POLYLINE:
+			{
+				Point2D[] current = (Point2D[])this.value;
+				Point3D[] tab = new Point3D[current.length];
+				for(int i=0; i<current.length; ++i) {
+					tab[i] = new Point3f(
+								current[i].getX(),
+								current[i].getY(),
+								0f); 
+				}
+				return tab;
+			}
+			case POLYLINE3D:
+				return (Point3D[])this.value;
+			case STRING:
+				return parsePolyline3D((String)this.value, false);
+			case OBJECT:
+				if (this.value instanceof Tuple2D<?>) {
+					Tuple2D<?> t2 = (Tuple2D<?>)this.value;
+					return new Point3D[] {
+						new Point3f(t2.getX(),t2.getY(),0) 
+					};
+				}
+				else if (this.value instanceof Tuple3D<?>) {
+					Tuple3D<?> t2 = (Tuple3D<?>)this.value;
+					return new Point3D[] {
+							new Point3f(t2.getX(), t2.getY(), t2.getZ())
+					};
+				}
+				else if (this.value.getClass().isArray()) {
+					Class<?> elementType = this.value.getClass().getComponentType();
+					if (Point3D.class.equals(elementType)) {
+						return (Point3D[])this.value;
+					}
+					int size = Array.getLength(this.value);
+					if (Tuple3D.class.isAssignableFrom(elementType)) {
+						Point3D[] pa3 = new Point3D[size];
+						for(int i=0; i<pa3.length; ++i) {
+							Tuple3D<?> t = (Tuple3D<?>)Array.get(this.value, i);
+							pa3[i] = new Point3f(t);
+						}
+						return pa3;
+					}
+					if (Tuple2D.class.isAssignableFrom(elementType)) {
+						Point3D[] pa3 = new Point3D[size];
+						for(int i=0; i<pa3.length; ++i) {
+							Tuple2D<?> t = (Tuple2D<?>)Array.get(this.value, i);
+							pa3[i] = new Point3f(t.getX(), t.getY(), 0);
+						}
+						return pa3;
+					}
+				}
+				break;
+			case BOOLEAN:
+			case COLOR:
+			case DATE:
+			case IMAGE:
+			case INTEGER:
+			case REAL:
+			case TIMESTAMP:
+			case URI:
+			case URL:
+			case UUID:
+			case INET_ADDRESS:
+			case ENUMERATION:
+			case TYPE:
+			default:
+			}
+		}
+		catch(ClassCastException _) {
+			//
+		}
+		catch(NumberFormatException _) {
+			//
+		}			
+		throw new InvalidAttributeTypeException();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setPolyline3D(Point3D... value) {
+		this.value = value;
+		this.type = AttributeType.POLYLINE3D;
+		this.assigned = this.value!=null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setPolyline3D(Collection<? extends Point3D> value) {
+		if (value==null) {
+			this.value = null;
+		}
+		else {
+			Point3D[] tab = new Point3D[value.size()];
+			value.toArray(tab);
+			this.value = tab;
+		}
+		this.type = AttributeType.POLYLINE3D;
+		this.assigned = this.value!=null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void addToPolyline3D(Point3D... p) {
+		Point3D[] tab;
+		if (this.value instanceof Point3D[]) {
+			int size = ((Point3D[])this.value).length;
+			tab = new Point3D[size+p.length];
+			System.arraycopy(this.value,0,tab,0,size);
+			System.arraycopy(p,0,tab,size,p.length);
+		}
+		else {
+			tab = p;
+		}
+		this.value = tab;
+		this.assigned = this.value!=null;
+		this.type = AttributeType.POLYLINE3D;		
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void addToPolyline3D(Collection<? extends Point3D> p) {
+		Point3D[] tab;
+		if (this.value instanceof Point3D[]) {
+			int size = ((Point3D[])this.value).length;
+			tab = new Point3D[size+p.size()];
+			System.arraycopy(this.value,0,tab,0,size);
+			System.arraycopy(p.toArray(),0,tab,size,p.size());
+		}
+		else {
+			tab = new Point3D[p.size()];
+			p.toArray(tab);
+		}
+		this.value = tab;
+		this.assigned = this.value!=null;
+		this.type = AttributeType.POLYLINE3D;		
+	}
+
+	private static Point2D[] parsePolyline(String text, boolean isStrict) {
+		String[] comp = text.split(";"); //$NON-NLS-1$
+		if (isStrict && (comp.length%2)!=0) {
+			return null;
+		}
+		int fullPoints = comp.length/2;
+		boolean addPt = (fullPoints*2!=comp.length); 
+		Point2D[] tab = new Point2D[addPt ? fullPoints+1 : fullPoints];
+		for(int i=1,j=0; (i<comp.length)&&(j<fullPoints); i+=2,++j) {
+			tab[j] = new Point2f(
+					Float.parseFloat(comp[i-1]),
+					Float.parseFloat(comp[i]));
+		}
+		if (addPt) {
+			tab[tab.length-1]  = new Point2f(
+					Float.parseFloat(comp[comp.length-1]),
+					0);
+		}
+		return tab;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Point2D[] getPolyline() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case POINT:
+				return new Point2D[] {
+						new Point2f(((Point2D)this.value).getX(),
+								((Point2D)this.value).getY())
+				};
+			case POINT3D:
+				Point3D pt3 = (Point3D)this.value;
+				return new Point2D[] {
+					new Point2f(pt3.getX(),pt3.getY()) 
+				};
+			case POLYLINE:
+				return (Point2D[])this.value;
+			case POLYLINE3D:
+			{
+				Point3D[] current = (Point3D[])this.value;
+				Point2D[] tab = new Point2D[current.length];
+				for(int i=0; i<current.length; ++i) {
+					tab[i] = new Point2f(
+								current[i].getX(),
+								current[i].getY()); 
+				}
+				return tab;
+			}
+			case STRING:
+				return parsePolyline((String)this.value, false);
+			case OBJECT:
+				if (this.value instanceof Tuple2D) {
+					return new Point2D[] {
+							new Point2f(((Tuple2D<?>)this.value).getX(),
+									((Tuple2D<?>)this.value).getY())
+					};
+				}
+				else if (this.value instanceof Tuple3D) {
+					Tuple3D<?> t3 = (Tuple3D<?>)this.value;
+					return new Point2D[] {
+						new Point2f(t3.getX(),t3.getY()) 
+					};
+				}
+				else if (this.value instanceof Point2D[]) {
+					return (Point2D[])this.value;
+				}
+				else if (this.value instanceof Tuple2D[]) {
+					Tuple2D<?>[] ta2 = (Tuple2D[])this.value;
+					Point2D[] pa2 = new Point2D[ta2.length];
+					for(int i=0; i<pa2.length; ++i) pa2[i] = new Point2f(ta2[i]);
+				}
+				else if (this.value instanceof Tuple3D[]) {
+					Tuple3D<?>[] ta3 = (Tuple3D[])this.value;
+					Point2D[] pa2 = new Point2D[ta3.length];
+					for(int i=0; i<pa2.length; ++i) pa2[i] = new Point2f(ta3[i].getX(), ta3[i].getY());
+				}
+				break;
+			case BOOLEAN:
+			case COLOR:
+			case DATE:
+			case IMAGE:
+			case INTEGER:
+			case REAL:
+			case TIMESTAMP:
+			case URI:
+			case URL:
+			case UUID:
+			case INET_ADDRESS:
+			case ENUMERATION:
+			case TYPE:
+			default:
+			}
+		}
+		catch(ClassCastException _) {
+			//
+		}
+		catch(NumberFormatException _) {
+			//
+		}			
+		throw new InvalidAttributeTypeException();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setPolyline(Point2D... value) {
+		this.value = value;
+		this.type = AttributeType.POLYLINE;
+		this.assigned = this.value!=null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setPolyline(Collection<? extends Point2D> value) {
+		if (value==null) {
+			this.value = null;
+		}
+		else {
+			Point2D[] tab = new Point2D[value.size()];
+			value.toArray(tab);
+			this.value = tab;
+		}
+		this.value = value;
+		this.type = AttributeType.POLYLINE;
+		this.assigned = this.value!=null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void addToPolyline(Point2D... p) {
+		Point2D[] tab;
+		if (this.value instanceof Point2D[]) {
+			int size = ((Point2D[])this.value).length;
+			tab = new Point2D[size+p.length];
+			System.arraycopy(this.value,0,tab,0,size);
+			System.arraycopy(this.value,0,tab,size,p.length);
+		}
+		else {
+			tab = p;
+		}
+		this.value = tab;
+		this.assigned = this.value!=null;
+		this.type = AttributeType.POLYLINE;		
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void addToPolyline(Collection<? extends Point2D> p) {
+		Point2D[] tab;
+		if (this.value instanceof Point2D[]) {
+			int size = ((Point2D[])this.value).length;
+			tab = new Point2D[size+p.size()];
+			System.arraycopy(this.value,0,tab,0,size);
+			System.arraycopy(this.value,0,tab,size,p.size());
+		}
+		else {
+			tab = new Point2D[p.size()];
+			p.toArray(tab);
+		}
+		this.value = tab;
+		this.assigned = this.value!=null;
+		this.type = AttributeType.POLYLINE;		
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public InetAddress getInetAddress() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case INET_ADDRESS:
+				return (InetAddress)this.value;
+			case STRING:
+				try {
+					InetAddress adr = InetAddress.getByName(this.value.toString());
+					if (adr!=null) return adr;
+				}
+				catch(Throwable _) {
+					//
+				}
+				break;
+			case OBJECT:
+				if (this.value instanceof InetAddress)
+					return (InetAddress)this.value;
+				if (this.value instanceof InetSocketAddress)
+					return ((InetSocketAddress)this.value).getAddress();
+				if (this.value!=null) {
+					InetAddress adr;
+					try {
+						adr = InetAddress.getByName(this.value.toString());
+						if (adr!=null) return adr;
+					}
+					catch(Throwable _) {
+						//
+					}
+				}
+				else {
+					return null;
+				}
+				break;
+			case URI:
+				URI uri = (URI)this.value;
+				return InetAddress.getByName(uri.getHost());
+			case URL:
+				URL url = (URL)this.value;
+				return InetAddress.getByName(url.getHost());
+			case COLOR:
+			case POINT:
+			case POINT3D:
+			case INTEGER:
+			case TIMESTAMP:
+			case REAL:
+			case DATE:
+			case BOOLEAN:
+			case IMAGE:
+			case POLYLINE:
+			case POLYLINE3D:
+			case UUID:
+			case ENUMERATION:
+			case TYPE:
+			default:
+			}
+		}
+		catch(ClassCastException _) {
+			//
+		}
+		catch(NumberFormatException _) {
+			//
+		}
+		catch (UnknownHostException e) {
+			//
+		}
+		throw new InvalidAttributeTypeException();		
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setInetAddress(InetAddress a) {
+		this.value = a;
+		this.type = AttributeType.INET_ADDRESS;		
+		this.assigned = this.value!=null;
+	}
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param a
+	 */
+	public void setInetAddress(InetSocketAddress a) {
+		this.value = a.getAddress();
+		this.type = AttributeType.INET_ADDRESS;				
+		this.assigned = this.value!=null;
+	}
+
+	/**
+	 * Set the value of this metadata.
+	 * 
+	 * @param hostname
+	 */
+	public void setInetAddress(String hostname) {
+		this.type = AttributeType.INET_ADDRESS;				
+		try {
+			this.value = InetAddress.getByName(hostname);
+		}
+		catch(Throwable _) {
+			this.value = null;
+		}
+		this.assigned = this.value!=null;
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	public Enum<?> getEnumeration() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		if (this.value==null) return null;
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case ENUMERATION:
+				return (Enum<?>)this.value;
+			case STRING:
+				int index = ((String)this.value).lastIndexOf('.');
+				if (index>=0) {
+					String classname = ((String)this.value).substring(0, index);
+					String enumName = ((String)this.value).substring(index+1);
+					Class classType = Class.forName(classname);
+					if (Enum.class.isAssignableFrom(classType)) {
+						return Enum.valueOf(classType, enumName);
+					}
+				}
+				break;
+			case OBJECT:
+				if (this.value instanceof Enum<?>)
+					return (Enum<?>)this.value;
+				
+				break;
+			case REAL:
+			case INTEGER:
+				break;
+			case INET_ADDRESS:
+			case COLOR:
+			case POINT:
+			case POINT3D:
+			case TIMESTAMP:
+			case DATE:
+			case BOOLEAN:
+			case IMAGE:
+			case POLYLINE:
+			case POLYLINE3D:
+			case URI:
+			case URL:
+			case UUID:
+			case TYPE:
+			default:
+			}
+		}
+		catch(Throwable _) {
+			//
+		}
+		throw new InvalidAttributeTypeException();		
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public <T extends Enum<T>> T getEnumeration(Class<T> type) throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		if (this.value==null) return null;
+		try {
+			assertAssignedAndNotNull();
+			switch(this.type) {
+			case ENUMERATION:
+				return type.cast(this.value);
+			case STRING:
+				int index = ((String)this.value).lastIndexOf('.');
+				if (index>=0) {
+					String classname = ((String)this.value).substring(0, index);
+					String enumName = ((String)this.value).substring(index+1);
+					Class<?> classType = Class.forName(classname);
+					assert(type.equals(classType));
+					return Enum.valueOf(type, enumName);
+				}
+				break;
+			case OBJECT:
+				if (this.value instanceof Enum<?>)
+					return type.cast(this.value);
+				
+				break;
+			case REAL:
+			case INTEGER:
+				break;
+			case INET_ADDRESS:
+			case COLOR:
+			case POINT:
+			case POINT3D:
+			case TIMESTAMP:
+			case DATE:
+			case BOOLEAN:
+			case IMAGE:
+			case POLYLINE:
+			case POLYLINE3D:
+			case URI:
+			case URL:
+			case UUID:
+			case TYPE:
+			default:
+			}
+		}
+		catch(Throwable _) {
+			//
+		}
+		throw new InvalidAttributeTypeException();		
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setEnumeration(Enum<?> e) {
+		this.value = e;
+		this.type = AttributeType.ENUMERATION;		
+		this.assigned = this.value!=null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Class<?> getJavaClass() throws InvalidAttributeTypeException, AttributeNotInitializedException {
+		assertAssignedAndNotNull();
+		try {
+			switch(this.type) {
+			case TYPE:
+			case OBJECT:
+				return (Class<?>)this.value;
+			case STRING:
+				return Class.forName((String)this.value);
+			case ENUMERATION:
+			case REAL:
+			case INTEGER:
+			case INET_ADDRESS:
+			case COLOR:
+			case POINT:
+			case POINT3D:
+			case TIMESTAMP:
+			case DATE:
+			case BOOLEAN:
+			case IMAGE:
+			case POLYLINE:
+			case POLYLINE3D:
+			case URI:
+			case URL:
+			case UUID:
+			default:
+			}
+		}
+		catch(Throwable _) {
+			//
+		}
+		throw new InvalidAttributeTypeException();		
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setJavaClass(Class<?> e) {
+		this.value = e;
+		this.type = AttributeType.TYPE;		
+		this.assigned = this.value!=null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean isAssigned() {
+		return this.assigned;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void uninitializeValue() {
+		this.value = null;
+		this.assigned = false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean flush() {
+		return true;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Comparator<? extends AttributeValue> valueComparator() {
+		return new AttributeValueComparator();
+	}
+
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/InvalidAttributeTypeException.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/InvalidAttributeTypeException.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/InvalidAttributeTypeException.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,52 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+
+/**
+ * This exception is generated each time the an invalid type
+ * was used for metadata.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class InvalidAttributeTypeException extends AttributeException {
+	
+	private static final long serialVersionUID = -4301702969671847467L;
+
+	/**
+	 */
+	public InvalidAttributeTypeException() {
+		//
+	}
+
+	/**
+	 * @param name is the name of the attribute for which the type was invalid.
+	 */
+	public InvalidAttributeTypeException(String name) {
+		super(name);
+	}
+
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/NullAttribute.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/NullAttribute.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/NullAttribute.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,99 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+import java.io.Serializable;
+
+
+/**
+ * Class that is representing a <code>null</code> value
+ * for the embedded type of attribute.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public final class NullAttribute implements Serializable, Cloneable {
+
+	private static final long serialVersionUID = 259205366482306499L;
+	
+	private final AttributeType type;
+	
+	/**
+	 * @param type
+	 */
+	public NullAttribute(AttributeType type) {
+		this.type = type;
+	}
+
+	/** Replies the type.
+	 * 
+	 * @return the type.
+	 */
+	public AttributeType getType() {
+		return this.type;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public NullAttribute clone() {
+		try {
+			return (NullAttribute)super.clone();
+		}
+		catch (CloneNotSupportedException e) {
+			throw new Error(e);
+		}
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(Object obj) {
+		if (obj==null) return true;
+		if (obj instanceof NullAttribute) {
+			return this.type==((NullAttribute)obj).type;
+		}
+		return false;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int hashCode() {
+		return this.type.hashCode();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		return "null"; //$NON-NLS-1$
+	}
+	
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/Timestamp.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/Timestamp.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/attr/Timestamp.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,109 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+import java.text.MessageFormat;
+import java.util.Date;
+
+/**
+ * Container of timestamp.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class Timestamp extends Number {
+
+	private static final long serialVersionUID = 499564400887069856L;
+	
+	private final long time;
+	
+	/**
+	 * @param time
+	 */
+	public Timestamp(long time) {
+		this.time = time;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public double doubleValue() {
+		return this.time;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float floatValue() {
+		return this.time;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int intValue() {
+		return (int)this.time;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public long longValue() {
+		return this.time;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(Object obj) {
+		if (obj instanceof Number) {
+			return this.time==((Number)obj).longValue();
+		}
+		return false;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int hashCode() {
+		return Long.valueOf(this.time).hashCode();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		return MessageFormat.format("{0,date,full} {0,time,full}", //$NON-NLS-1$
+				new Date(this.time));
+	}
+	
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AbstractAttributeCollection.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AbstractAttributeCollection.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AbstractAttributeCollection.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,223 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeException;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+import org.arakhne.afc.attrs.collection.AttributeChangeEvent.Type;
+
+/**
+ * This class implements an abstract object with attributes.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractAttributeCollection extends AbstractAttributeProvider implements AttributeCollection {
+	
+	private List<AttributeChangeListener> listenerList = null;
+	private boolean isEventFirable = true;
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized boolean isEventFirable() {
+		return this.isEventFirable;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized void setEventFirable(boolean isFirable) {
+		this.isEventFirable = isFirable;
+	}
+	
+	/** Make a deep copy of this object and replies the copy.
+	 * 
+	 * @return the deep copy.
+	 */
+	@Override
+	public AttributeCollection clone() {
+		AbstractAttributeCollection clone = (AbstractAttributeCollection)super.clone();
+		clone.listenerList = null;
+		return clone;
+	}
+
+	/** Fire the addition event.
+	 * 
+	 * @param name is the name of the attribute for which the event occured.
+	 * @param attr is the value of the attribute.
+	 */
+	protected synchronized void fireAttributeAddedEvent(String name, AttributeValue attr) {
+		if (this.listenerList!=null && isEventFirable()) {
+			AttributeChangeListener[] list = new AttributeChangeListener[this.listenerList.size()];
+			this.listenerList.toArray(list);
+			AttributeChangeEvent event = new AttributeChangeEvent(
+					this, 				//source
+					Type.ADDITION,		//type
+					null,				//old name
+					null,				//old value
+					name,				//current name
+					attr);				//current value
+			for(AttributeChangeListener listener : list) {
+				listener.onAttributeChangeEvent(event);
+			}
+		}
+	}
+
+	/** Fire the attribute change event.
+	 * 
+	 * @param name is the name of the attribute for which the event occured.
+	 * @param oldValue is the previous value of the attribute
+	 * @param currentValue is the current value of the attribute
+	 */
+	protected synchronized void fireAttributeChangedEvent(String name, AttributeValue oldValue, AttributeValue currentValue) {
+		if (this.listenerList!=null && isEventFirable()) {
+			AttributeChangeListener[] list = new AttributeChangeListener[this.listenerList.size()];
+			this.listenerList.toArray(list);
+			AttributeChangeEvent event = new AttributeChangeEvent(
+					this, 				//source
+					Type.VALUE_UPDATE,	//type
+					name,				//old name
+					oldValue,			//old value
+					name,				//current name
+					currentValue);		//current value
+			for(AttributeChangeListener listener : list) {
+				listener.onAttributeChangeEvent(event);
+			}
+		}
+	}
+
+	/** Fire the all attribute removal event.
+	 */
+	protected synchronized void fireAttributeClearedEvent() {
+		if (this.listenerList!=null && isEventFirable()) {
+			AttributeChangeListener[] list = new AttributeChangeListener[this.listenerList.size()];
+			this.listenerList.toArray(list);
+			AttributeChangeEvent event = new AttributeChangeEvent(
+					this, 				//source
+					Type.REMOVE_ALL,	//type
+					null,				//old name
+					null,				//old value
+					null,				//current name
+					null);				//current value
+			for(AttributeChangeListener listener : list) {
+				listener.onAttributeChangeEvent(event);
+			}
+		}
+	}
+
+	/** Fire the an attribute removal event.
+	 * 
+	 * @param name is the name of the attribute for which the event occured.
+	 * @param oldValue is the previous value of the attribute
+	 */
+	protected synchronized void fireAttributeRemovedEvent(String name, AttributeValue oldValue) {
+		if (this.listenerList!=null && isEventFirable()) {
+			AttributeChangeListener[] list = new AttributeChangeListener[this.listenerList.size()];
+			this.listenerList.toArray(list);
+			AttributeChangeEvent event = new AttributeChangeEvent(
+					this, 				//source
+					Type.REMOVAL,		//type
+					name,				//old name
+					oldValue,			//old value
+					name,				//current name
+					oldValue);			//current value
+			for(AttributeChangeListener listener : list) {
+				listener.onAttributeChangeEvent(event);
+			}
+		}
+	}
+
+	/** Fire the renaming event.
+	 * 
+	 * @param oldName is the previous name of the attribute (before renaming)
+	 * @param newName is the new name of the attribute (after renaming)
+	 * @param attr is the value of the attribute.
+	 */
+	protected synchronized void fireAttributeRenamedEvent(String oldName, String newName, AttributeValue attr) {
+		if (this.listenerList!=null && isEventFirable()) {
+			AttributeChangeListener[] list = new AttributeChangeListener[this.listenerList.size()];
+			this.listenerList.toArray(list);
+			AttributeChangeEvent event = new AttributeChangeEvent(
+					this, 				//source
+					Type.RENAME,		//type
+					oldName,			//old name
+					attr,				//old value
+					newName,			//current name
+					attr);				//current value
+			for(AttributeChangeListener listener : list) {
+				listener.onAttributeChangeEvent(event);
+			}
+		}
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public synchronized void addAttributeChangeListener(AttributeChangeListener listener) {
+		if (listener!=null) {
+			if (this.listenerList==null)
+				this.listenerList = new LinkedList<AttributeChangeListener>();
+			this.listenerList.add(listener);
+		}
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public synchronized void removeAttributeChangeListener(AttributeChangeListener listener) {
+		if (listener!=null) {
+			if (this.listenerList!=null) {
+				this.listenerList.remove(listener);
+				if (this.listenerList.isEmpty())
+					this.listenerList = null;
+			}
+		}
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public final boolean renameAttribute(String oldname, String newname) {
+		return renameAttribute(oldname, newname, false);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public final void copyFrom(AttributeProvider otherContainer) throws AttributeException {
+		for (Attribute attr : otherContainer.attributes()) {
+			setAttribute(attr);
+		}
+	}
+	
+}
+

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AbstractAttributeProvider.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AbstractAttributeProvider.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AbstractAttributeProvider.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,529 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URL;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.UUID;
+
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeException;
+import org.arakhne.afc.attrs.attr.AttributeType;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+import org.arakhne.afc.attrs.attr.NullAttribute;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Image;
+
+/**
+ * This class implements an abstract attribute provider.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractAttributeProvider implements AttributeProvider, Iterable<Attribute>, Cloneable {
+		
+	/** Ensure that the <code>null</code> value for <var>rawAttributeValue</var>
+	 * is catched and replaced by a dedicated representant object.
+	 * This function permits to keep the type of a value even if it is <code>null</code>.
+	 * 
+	 * @param rawAttributeValue is the value to protect.
+	 * @param type is the type of the attribute to preserve over time.
+	 * @return the value, or the representant of the java <code>null</code> value.
+	 * @see #unprotectNull(Object)
+	 */
+	protected static Object protectNull(Object rawAttributeValue, AttributeType type) {
+		if (rawAttributeValue==null) {
+			if (type.isNullAllowed())
+				return new NullAttribute(type);
+			throw new NullPointerException();
+		}
+		return rawAttributeValue;
+	}
+
+	/** Ensure that the <code>null</code> value for <var>rawAttributeValue</var>
+	 * is catched and the dedicated representant object for <code>null</code>
+	 * if replace by the real <code>null</code> java value.
+	 * 
+	 * @param rawAttributeValue is the value to protect.
+	 * @return the value.
+	 * @see #protectNull(Object, AttributeType)
+	 */
+	protected static Object unprotectNull(Object rawAttributeValue) {
+		if (rawAttributeValue instanceof NullAttribute)
+			return null;
+		return rawAttributeValue;
+	}
+
+	@Override
+	public final Map<String, Object> toMap() {
+		Map<String,Object> map = new TreeMap<String,Object>();
+		toMap(map);
+		return map;
+	}
+
+	/** Replies an iterator on the attributes.
+	 * 
+	 * @return {@inheritDoc}
+	 */
+	@Override
+	public Iterator<Attribute> iterator() {
+		return new AttributeIterator(this);
+	}
+	
+	/** Make a deep copy of this object and replies the copy.
+	 * 
+	 * @return the deep copy.
+	 */
+	@Override
+	public AttributeProvider clone() {
+		try {
+			return (AttributeProvider)super.clone();
+		}
+		catch(CloneNotSupportedException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Iterable<Attribute> attributes() {
+		return this;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean getAttributeAsBool(String name) throws AttributeException {
+		AttributeValue value = getAttribute(name);
+		if (value==null) throw new NoAttributeFoundException(name);
+		return value.getBoolean();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public int getAttributeAsInt(String name) throws AttributeException {
+		AttributeValue value = getAttribute(name);
+		if (value==null) throw new NoAttributeFoundException(name);
+		return (int)value.getInteger();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public long getAttributeAsLong(String name) throws AttributeException {
+		AttributeValue value = getAttribute(name);
+		if (value==null) throw new NoAttributeFoundException(name);
+		return value.getInteger();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float getAttributeAsFloat(String name) throws AttributeException {
+		AttributeValue value = getAttribute(name);
+		if (value==null) throw new NoAttributeFoundException(name);
+		return (float)value.getReal();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public double getAttributeAsDouble(String name) throws AttributeException {
+		AttributeValue value = getAttribute(name);
+		if (value==null) throw new NoAttributeFoundException(name);
+		return value.getReal();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public String getAttributeAsString(String name) throws AttributeException {
+		AttributeValue value = getAttribute(name);
+		if (value==null) throw new NoAttributeFoundException(name);
+		return value.getString();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public UUID getAttributeAsUUID(String name) throws AttributeException {
+		AttributeValue value = getAttribute(name);
+		if (value==null) throw new NoAttributeFoundException(name);
+		return value.getUUID();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public URL getAttributeAsURL(String name) throws AttributeException {
+		AttributeValue value = getAttribute(name);
+		if (value==null) throw new NoAttributeFoundException(name);
+		return value.getURL();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public URI getAttributeAsURI(String name) throws AttributeException {
+		AttributeValue value = getAttribute(name);
+		if (value==null) throw new NoAttributeFoundException(name);
+		return value.getURI();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Image getAttributeAsImage(String name) throws AttributeException {
+		AttributeValue value = getAttribute(name);
+		if (value==null) throw new NoAttributeFoundException(name);
+		return value.getImage();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Date getAttributeAsDate(String name) throws AttributeException {
+		AttributeValue value = getAttribute(name);
+		if (value==null) throw new NoAttributeFoundException(name);
+		return value.getDate();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Color getAttributeAsColor(String name) throws AttributeException {
+		AttributeValue value = getAttribute(name);
+		if (value==null) throw new NoAttributeFoundException(name);
+		return value.getColor();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public InetAddress getAttributeAsInetAddress(String name) throws AttributeException {
+		AttributeValue value = getAttribute(name);
+		if (value==null) throw new NoAttributeFoundException(name);
+		return value.getInetAddress();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Enum<?> getAttributeAsEnumeration(String name) throws AttributeException {
+		AttributeValue value = getAttribute(name);
+		if (value==null) throw new NoAttributeFoundException(name);
+		return value.getEnumeration();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public <T extends Enum<T>> T getAttributeAsEnumeration(String name, Class<T> type) throws AttributeException {
+		AttributeValue value = getAttribute(name);
+		if (value==null) throw new NoAttributeFoundException(name);
+		return value.getEnumeration(type);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Class<?> getAttributeAsJavaClass(String name) throws AttributeException {
+		AttributeValue value = getAttribute(name);
+		if (value==null) throw new NoAttributeFoundException(name);
+		return value.getJavaClass();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean getAttribute(String name, boolean defaultValue) {
+		AttributeValue value = getAttribute(name);
+		if (value!=null) {
+			try {
+				return value.getBoolean();
+			}
+			catch(AttributeException _) {
+				//
+			}
+		}
+		return defaultValue;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public int getAttribute(String name, int defaultValue) {
+		AttributeValue value = getAttribute(name);
+		if (value!=null) {
+			try {
+				return (int)value.getInteger();
+			}
+			catch(AttributeException _) {
+				//
+			}
+		}
+		return defaultValue;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public long getAttribute(String name, long defaultValue) {
+		AttributeValue value = getAttribute(name);
+		if (value!=null) {
+			try {
+				return value.getInteger();
+			}
+			catch(AttributeException _) {
+				//
+			}
+		}
+		return defaultValue;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float getAttribute(String name, float defaultValue) {
+		AttributeValue value = getAttribute(name);
+		if (value!=null) {
+			try {
+				return (float)value.getReal();
+			}
+			catch(AttributeException _) {
+				//
+			}
+		}
+		return defaultValue;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public double getAttribute(String name, double defaultValue) {
+		AttributeValue value = getAttribute(name);
+		if (value!=null) {
+			try {
+				return value.getReal();
+			}
+			catch(AttributeException _) {
+				//
+			}
+		}
+		return defaultValue;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public String getAttribute(String name, String defaultValue) {
+		AttributeValue value = getAttribute(name);
+		if (value!=null) {
+			try {
+				return value.getString();
+			}
+			catch(AttributeException _) {
+				//
+			}
+		}
+		return defaultValue;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public UUID getAttribute(String name, UUID defaultValue) {
+		AttributeValue value = getAttribute(name);
+		if (value!=null) {
+			try {
+				return value.getUUID();
+			}
+			catch(AttributeException _) {
+				//
+			}
+		}
+		return defaultValue;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public URL getAttribute(String name, URL defaultValue) {
+		AttributeValue value = getAttribute(name);
+		if (value!=null) {
+			try {
+				return value.getURL();
+			}
+			catch(AttributeException _) {
+				//
+			}
+		}
+		return defaultValue;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public URI getAttribute(String name, URI defaultValue) {
+		AttributeValue value = getAttribute(name);
+		if (value!=null) {
+			try {
+				return value.getURI();
+			}
+			catch(AttributeException _) {
+				//
+			}
+		}
+		return defaultValue;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Image getAttribute(String name, Image defaultValue) {
+		AttributeValue value = getAttribute(name);
+		if (value!=null) {
+			try {
+				return value.getImage();
+			}
+			catch(AttributeException _) {
+				//
+			}
+		}
+		return defaultValue;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Date getAttribute(String name, Date defaultValue) {
+		AttributeValue value = getAttribute(name);
+		if (value!=null) {
+			try {
+				return value.getDate();
+			}
+			catch(AttributeException _) {
+				//
+			}
+		}
+		return defaultValue;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Color getAttribute(String name, Color defaultValue) {
+		AttributeValue value = getAttribute(name);
+		if (value!=null) {
+			try {
+				return value.getColor();
+			}
+			catch(AttributeException _) {
+				//
+			}
+		}
+		return defaultValue;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public InetAddress getAttribute(String name, InetAddress defaultValue) {
+		AttributeValue value = getAttribute(name);
+		if (value!=null) {
+			try {
+				return value.getInetAddress();
+			}
+			catch(AttributeException _) {
+				//
+			}
+		}
+		return defaultValue;
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public InetAddress getAttribute(String name, InetSocketAddress defaultValue) {
+		AttributeValue value = getAttribute(name);
+		if (value!=null) {
+			try {
+				return value.getInetAddress();
+			}
+			catch(AttributeException _) {
+				//
+			}
+		}
+		return defaultValue==null ? null : defaultValue.getAddress();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	@SuppressWarnings("unchecked")
+	public <T extends Enum<T>> T getAttribute(String name, T defaultValue) {
+		AttributeValue value = getAttribute(name);
+		if (value!=null) {
+			try {
+				return (T)value.getEnumeration();
+			}
+			catch(AttributeException _) {
+				//
+			}
+			catch(ClassCastException _) {
+				//
+			}
+		}
+		return defaultValue;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Class<?> getAttribute(String name, Class<?> defaultValue) {
+		AttributeValue value = getAttribute(name);
+		if (value!=null) {
+			try {
+				return value.getJavaClass();
+			}
+			catch(AttributeException _) {
+				//
+			}
+			catch(ClassCastException _) {
+				//
+			}
+		}
+		return defaultValue;
+	}
+
+}

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AbstractBufferedAttributeProvider.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AbstractBufferedAttributeProvider.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AbstractBufferedAttributeProvider.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,198 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeException;
+import org.arakhne.afc.attrs.attr.AttributeImpl;
+import org.arakhne.afc.attrs.attr.AttributeType;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+import org.arakhne.afc.attrs.attr.AttributeValueImpl;
+import org.arakhne.util.ref.SoftValueTreeMap;
+
+/**
+ * This class implements an abstract attribute container that use
+ * a memory cache.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractBufferedAttributeProvider extends AbstractAttributeProvider {
+	
+	private transient Map<String,AttributeValue> cache = new SoftValueTreeMap<String,AttributeValue>();
+	
+	/** Make a deep copy of this object and replies the copy.
+	 * 
+	 * @return the deep copy.
+	 */
+	@Override
+	public AbstractBufferedAttributeProvider clone() {
+		AbstractBufferedAttributeProvider clone = (AbstractBufferedAttributeProvider)super.clone();
+		clone.cache = new SoftValueTreeMap<String,AttributeValue>(this.cache);
+		return clone;
+	}
+
+	/** Load a value from the data source.
+	 * 
+	 * @param name is the name of the attribute to load
+	 * @return the value of the attribute.
+	 * @throws AttributeException on error or when the attribute does not exist
+	 */
+	protected abstract AttributeValue loadValue(String name) throws AttributeException;
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public abstract Collection<String> getAllAttributeNames();
+	
+	/** Replies the value associated to the specified name.
+	 */
+	private AttributeValue extractValueFor(String name) throws AttributeException {
+		AttributeValue value = null;
+		if (this.cache.containsKey(name)) {
+			value = this.cache.get(name);
+		}
+		else {
+			value = loadValue(name);
+			this.cache.put(name, value);
+		}
+		return value;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean hasAttribute(String name) {
+		return getAllAttributeNames().contains(name);
+	}	
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Collection<Attribute> getAllAttributes() {
+		ArrayList<Attribute> list = new ArrayList<Attribute>(getAttributeCount());
+		Attribute newAttr;
+		for(String name : getAllAttributeNames()) {
+			if (name!=null) {
+				try {
+					newAttr = new AttributeImpl(name, extractValueFor(name));
+					list.add(newAttr);
+				}
+				catch(AttributeException _) {
+					//
+				}
+			}
+		}
+		return list;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Map<AttributeType,Collection<Attribute>> getAllAttributesByType() {
+		TreeMap<AttributeType,Collection<Attribute>> map = new TreeMap<AttributeType,Collection<Attribute>>();
+		Attribute newAttr;
+		for(String name : getAllAttributeNames()) {
+			if (name!=null) {
+				try {
+					newAttr = new AttributeImpl(name, extractValueFor(name));
+					Collection<Attribute> list = map.get(newAttr.getType());
+					if (list==null) {
+						list = new ArrayList<Attribute>();
+						map.put(newAttr.getType(), list);
+					}
+					list.add(newAttr);
+				}
+				catch(AttributeException _) {
+					//
+				}
+			}
+		}
+		return map;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public AttributeValue getAttribute(String name) {
+		try {
+			return new AttributeValueImpl(extractValueFor(name));
+		}
+		catch(AttributeException _) {
+			//
+		}
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public AttributeValue getAttribute(String name, AttributeValue default_value) {
+		AttributeValue value;
+		try {
+			value = new AttributeValueImpl(extractValueFor(name));
+		}
+		catch(AttributeException _) {
+			value = default_value;
+		}
+		return value;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute getAttributeObject(String name) {
+		try {
+			return new AttributeImpl(name,extractValueFor(name));
+		}
+		catch(AttributeException _) {
+			//
+		}
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void freeMemory() {
+		this.cache.clear();
+	}
+	
+}
+

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeChangeEvent.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeChangeEvent.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeChangeEvent.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,143 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.util.EventObject;
+
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeImpl;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+
+/**
+ * This interface representes a listener on the attribute changes
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AttributeChangeEvent extends EventObject { 
+
+	private static final long serialVersionUID = -3573147826440564995L;
+	
+	private final String name;
+	private final String oldName;
+	private final AttributeValue oldValue;
+	private final AttributeValue newValue;
+	private final Type type; 
+
+	/** Create an event that describes a value update only.
+	 * 
+	 * @param source
+	 * @param type
+	 * @param oldName
+	 * @param oldValue
+	 * @param currentName
+	 * @param currentValue
+	 */
+	public AttributeChangeEvent(Object source, Type type, String oldName, AttributeValue oldValue, String currentName, AttributeValue currentValue) {
+		super(source);
+		this.name = currentName;
+		this.oldName = oldName;
+		this.oldValue = oldValue;
+		this.newValue = currentValue;
+		this.type = type;
+	}
+
+	/** Replies the name of the changed attributes.
+	 * 
+	 * @return the name attribute, or <code>null</code> if this
+	 * event has no name attribute.
+	 */
+	public String getName() {
+		return this.name;
+	}
+
+	/** Replies the old name of the changed attributes.
+	 * 
+	 * @return the old name attribute, or <code>null</code> if this
+	 * event has no old name attribute.
+	 */
+	public String getOldName() {
+		return this.oldName;
+	}
+
+	/** Replies the old value of the attribute.
+	 * 
+	 * @return the attribute value or <code>null</code>
+	 */
+	public AttributeValue getOldValue() {
+		return this.oldValue;
+	}
+
+	/** Replies the new value of the attribute.
+	 * 
+	 * @return the attribute value, never <code>null</code>
+	 */
+	public AttributeValue getValue() {
+		return this.newValue;
+	}
+
+	/** Replies the changed attribute.
+	 * 
+	 * @return the attribute, never <code>null</code>
+	 */
+	public Attribute getAttribute() {
+		return new AttributeImpl(this.name,this.newValue);
+	}
+	
+	/** Replies the type of event.
+	 * 
+	 * @return the type of the event, neither <code>null</code>
+	 */
+	public Type getType() {
+		return this.type;
+	}
+
+	/**
+	 * This interface representes a listener on the attribute changes
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public enum Type {
+		/** An attribute value has changed.
+		 */
+		VALUE_UPDATE,
+		/** An attribute was removed.
+		 */
+		REMOVAL,
+		/** an attribute was added.
+		 */
+		ADDITION,
+		/** An attribute was renamed.
+		 */
+		RENAME,
+		/** All attributes were removed.
+		 */
+		REMOVE_ALL;
+	}
+
+}

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeChangeListener.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeChangeListener.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeChangeListener.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,43 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.util.EventListener;
+
+/**
+ * This interface representes a listener on the attribute changes
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface AttributeChangeListener extends EventListener { 
+
+	/** Invoked when the attribute's value changed.
+	 * 
+	 * @param event describes the changes.
+	 */
+	public void onAttributeChangeEvent(AttributeChangeEvent event);
+
+}

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeCollection.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeCollection.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeCollection.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,341 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URL;
+import java.util.Date;
+import java.util.Map;
+import java.util.UUID;
+
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeException;
+import org.arakhne.afc.attrs.attr.AttributeType;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Image;
+
+/**
+ * This interface representes an object that owns a
+ * collection of attributes with a reading and a
+ * writing API.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface AttributeCollection extends AttributeProvider { 
+
+	/** Make a deep copy of this object and replies the copy.
+	 * 
+	 * @return the deep copy.
+	 */
+	@Override
+	public AttributeCollection clone();
+	
+	/** Set the content of this collection from the given map.
+	 * Any previous content of this attribute collection will
+	 * be lost.
+	 * This function is equivalent to:
+	 * <pre><code>
+	 * this.removeAllAttributes();
+	 * this.addAttributes(content);
+	 * </code></pre>
+	 * 
+	 * @param content is the content.
+	 * @see #addAttributes(Map)
+	 */
+	public void setAttributes(Map<String,Object> content);
+
+	/** Set the content of this collection from the given map.
+	 * Any previous content of this attribute collection will
+	 * be lost.
+	 * This function is equivalent to:
+	 * <pre><code>
+	 * this.removeAllAttributes();
+	 * this.addAttributes(content);
+	 * </code></pre>
+	 * 
+	 * @param content is the content.
+	 * @throws AttributeException if one attribute from the content cannot be inserted. 
+	 * @see #addAttributes(AttributeProvider)
+	 */
+	public void setAttributes(AttributeProvider content) throws AttributeException;
+
+	/** Put the values given as parameter in this attribute provider.
+	 * Any previous content of this attribute collection will remain
+	 * if the keys are not inside the given content.
+	 * If the values from the given content will be used to overwrite
+	 * any existing value.
+	 * 
+	 * @param content is the content to add inside.
+	 * @see #setAttributes(Map)
+	 */
+	public void addAttributes(Map<String,Object> content);
+
+	/** Put the values given as parameter in this attribute provider.
+	 * Any previous content of this attribute collection will remain
+	 * if the keys are not inside the given content.
+	 * If the values from the given content will be used to overwrite
+	 * any existing value.
+	 * 
+	 * @param content is the content to add inside.
+	 * @see #addAttributes(AttributeProvider)
+	 * @throws AttributeException if one attribute from the content cannot be inserted. 
+	 */
+	public void addAttributes(AttributeProvider content) throws AttributeException;
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 * @throws AttributeException on error.
+	 */
+	public Attribute setAttribute(String name, AttributeValue value) throws AttributeException;
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 */
+	public Attribute setAttribute(String name, boolean value);
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 */
+	public Attribute setAttribute(String name, int value);
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 */
+	public Attribute setAttribute(String name, long value);
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 */
+	public Attribute setAttribute(String name, float value);
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 */
+	public Attribute setAttribute(String name, double value);
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 */
+	public Attribute setAttribute(String name, String value);
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 */
+	public Attribute setAttribute(String name, UUID value);
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 */
+	public Attribute setAttribute(String name, URL value);
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 */
+	public Attribute setAttribute(String name, URI value);
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 */
+	public Attribute setAttribute(String name, Image value);
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 */
+	public Attribute setAttribute(String name, Date value);
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 */
+	public Attribute setAttribute(String name, Color value);
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 */
+	public Attribute setAttribute(String name, InetAddress value);
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 */
+	public Attribute setAttribute(String name, InetSocketAddress value);
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 */
+	public Attribute setAttribute(String name, Enum<?> value);
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param name is the name of the attribute to set.
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 */
+	public Attribute setAttribute(String name, Class<?> value);
+
+	/** Set the value for the given attribute.
+	 * 
+	 * @param value is the value to store.
+	 * @return the changed attribute or <code>null</code>
+	 * @throws AttributeException 
+	 */
+	public Attribute setAttribute(Attribute value) throws AttributeException;
+
+	/** Set the type of the attribute with the given name.
+	 * 
+	 * @param name is the name of the attribute
+	 * @param type is the desired type.
+	 * @return the changed attribute or <code>null</code>
+	 * @throws AttributeException 
+	 * @since 4.0
+	 */
+	public Attribute setAttributeType(String name, AttributeType type) throws AttributeException;
+
+	/** Remove the given attribute.
+	 * 
+	 * @param name is the name of the attribute to remove.
+	 * @return <code>true</code> on success, otherwhise <code>false</code>
+	 */
+	public boolean removeAttribute(String name);
+
+	/** Remove all the attributes.
+	 * 
+	 * @return <code>false</code> if something wrong appends
+	 */
+	public boolean removeAllAttributes();
+	
+	/** Rename the attribute.
+	 * <p>
+	 * If a attribute named <code>newname</code> already exists,
+	 * this function will reply <code>false</code>.
+	 * <p>
+	 * This function is equivalent to {@code renameAttribute(oldname,newname,false)}.
+	 * 
+	 * @param oldname is the name of the attribute to rename.
+	 * @param newname is the new name of the attribute.
+	 * @return <code>false</code> if something wrong appends
+	 */
+	public boolean renameAttribute(String oldname, String newname);
+
+	/** Rename the attribute .
+	 *
+	 * @param oldname is the name of the attribute to rename.
+	 * @param newname is the new name of the attribute.
+	 * @param overwrite must be <code>true</code> if the value of an
+	 * existing attribute named by <code>newname</code> must be
+	 * overwritten by the value of the attribute named <code>oldname</code>. 
+	 * @return <code>false</code> if something wrong appends
+	 */
+	public boolean renameAttribute(String oldname, String newname, boolean overwrite);
+
+	/** Copy the attributes from another container into this provider.
+	 * 
+	 * @param otherContainer is the container of attributes to copy.
+	 * @throws AttributeException on error.
+	 */
+	public void copyFrom(AttributeProvider otherContainer) throws AttributeException;
+
+	/** Add a listener on the attribute value changes.
+	 * 
+	 * @param listener
+	 */
+	public void addAttributeChangeListener(AttributeChangeListener listener);
+
+	/** Remove a listener on the attribute value changes.
+	 * 
+	 * @param listener
+	 */
+	public void removeAttributeChangeListener(AttributeChangeListener listener);
+	
+	/** Replies if the events are fired by this container.
+	 * 
+	 * @return <code>true</code> if the events are fired; otherwise <code>false</code>
+	 * if events are not fired.
+	 */
+	public boolean isEventFirable();
+	
+	/** Set if the events are fired by this container.
+	 * 
+	 * @param isFirable is <code>true</code> if the events are fired; otherwise <code>false</code>
+	 * if events are not fired.
+	 */
+	public void setEventFirable(boolean isFirable);
+
+	/** Force this provider to synchronized the memory state of the attributes
+	 * with a remote storage area.
+	 */
+	public void flush();
+
+}

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeIterator.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeIterator.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeIterator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,100 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.arakhne.afc.attrs.attr.Attribute;
+
+/**
+ * Iterator on attributes.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AttributeIterator implements Iterator<Attribute> {
+	
+	private AttributeProvider provider;
+	private final ArrayList<String> names = new ArrayList<String>();
+	private String lastName = null; 
+	
+	/**
+	 * Create an iterator on the given container.
+	 * 
+	 * @param provider is the container of attributes.
+	 */
+	public AttributeIterator(AttributeProvider provider) {
+		this.provider = provider;
+		this.names.addAll(provider.getAllAttributeNames());
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean hasNext() {
+		if ((this.provider==null)||(this.names.isEmpty())) {
+			this.provider = null;
+			return false;
+		}
+		return true;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Attribute next() {
+		if ((this.provider==null)||(this.names.isEmpty()))
+			throw new NoSuchElementException();
+		
+		String name = this.names.remove(0);
+		if (name==null)
+			throw new NoSuchElementException();
+		
+		Attribute attr = this.provider.getAttributeObject(name);
+		if (attr==null)
+			throw new NoSuchElementException();
+		
+		this.lastName = name;
+		
+		if (this.names.isEmpty())
+			this.provider = null;
+		
+		return attr;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void remove() {
+		if ((this.provider==null)||(this.lastName==null))
+			throw new NoSuchElementException();
+		if (this.provider instanceof AttributeCollection)
+			((AttributeCollection)this.provider).removeAttribute(this.lastName);
+		this.lastName = null;
+	}
+	
+}

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeNameStringComparator.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeNameStringComparator.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeNameStringComparator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,47 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.util.Comparator;
+
+import org.arakhne.afc.attrs.attr.AttributeImpl;
+
+
+/**
+ * Comparator for attribute names as strings.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AttributeNameStringComparator implements Comparator<String> {
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public int compare(String arg0, String arg1) {
+		return AttributeImpl.compareAttrNames(arg0, arg1);
+	}
+
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeProvider.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeProvider.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/AttributeProvider.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,421 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+import java.util.UUID;
+
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeException;
+import org.arakhne.afc.attrs.attr.AttributeType;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Image;
+
+/**
+ * This interface representes a provider of attributes
+ * that provide a reading API only.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface AttributeProvider { 
+
+	/** Make a deep copy of this object and replies the copy.
+	 * 
+	 * @return the deep copy.
+	 */
+	public AttributeProvider clone();
+
+	/** Replies the count of attributes
+	 * 
+	 * @return the count of attributes.
+	 */
+	public int getAttributeCount();
+
+	/** Replies if the given attribute exists.
+	 * 
+	 * @param name
+	 * @return <code>true</code> is an attribute with the given name exists, otherwise <code>false</code>
+	 */
+	public boolean hasAttribute(String name);
+
+	/** Replies all the attributes.
+	 * 
+	 * @return the list of all attributes
+	 */
+	public Collection<Attribute> getAllAttributes();
+	
+	/** Replies all the attributes.
+	 * 
+	 * @return an iterable object that contains the attributes.
+	 */
+	public Iterable<Attribute> attributes();
+
+	/** Replies all the attributes sorted by type.
+	 * <p>
+	 * The keys of the returned hashtable are the types and
+	 * the values are array of attributes ({@link java.util.Vector}).
+	 * 
+	 * @return the attributes grouped by type.
+	 */
+	public Map<AttributeType,Collection<Attribute>> getAllAttributesByType();
+
+	/** Replies all the attribute names.
+	 * This function never load the attribute values even
+	 * if they are not inside the storage layer.
+	 * 
+	 * @return the list of all attribute names.
+	 */
+	public Collection<String> getAllAttributeNames();
+
+	/** Replies the value for the given attribute.
+	 *
+	 * @param name
+	 * @return the value or <code>null</code>
+	 */
+	public AttributeValue getAttribute(String name);
+
+	/** Replies the attribute with the given name.
+	 * 
+	 * @param name
+	 * @return the attribute or <code>null</code>
+	 */
+	public Attribute getAttributeObject(String name);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @return the value
+	 * @throws AttributeException if the attribute was never set.
+	 */
+	public boolean getAttributeAsBool(String name) throws AttributeException;
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @return the value
+	 * @throws AttributeException if the attribute was never set.
+	 */
+	public int getAttributeAsInt(String name) throws AttributeException;
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @return the value
+	 * @throws AttributeException if the attribute was never set.
+	 */
+	public long getAttributeAsLong(String name) throws AttributeException;
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @return the value
+	 * @throws AttributeException if the attribute was never set.
+	 */
+	public float getAttributeAsFloat(String name) throws AttributeException;
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @return the value
+	 * @throws AttributeException if the attribute was never set.
+	 */
+	public double getAttributeAsDouble(String name) throws AttributeException;
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @return the value
+	 * @throws AttributeException if the attribute was never set.
+	 */
+	public String getAttributeAsString(String name) throws AttributeException;
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @return the value
+	 * @throws AttributeException if the attribute was never set.
+	 */
+	public UUID getAttributeAsUUID(String name) throws AttributeException;
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @return the value
+	 * @throws AttributeException if the attribute was never set.
+	 */
+	public URL getAttributeAsURL(String name) throws AttributeException;
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @return the value
+	 * @throws AttributeException if the attribute was never set.
+	 */
+	public URI getAttributeAsURI(String name) throws AttributeException;
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @return the value
+	 * @throws AttributeException if the attribute was never set.
+	 */
+	public Image getAttributeAsImage(String name) throws AttributeException;
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @return the value
+	 * @throws AttributeException if the attribute was never set.
+	 */
+	public Date getAttributeAsDate(String name) throws AttributeException;
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @return the value
+	 * @throws AttributeException if the attribute was never set.
+	 */
+	public Color getAttributeAsColor(String name) throws AttributeException;
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @return the value
+	 * @throws AttributeException if the attribute was never set.
+	 */
+	public InetAddress getAttributeAsInetAddress(String name) throws AttributeException;
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @return the value
+	 * @throws AttributeException if the attribute was never set.
+	 */
+	public Enum<?> getAttributeAsEnumeration(String name) throws AttributeException;
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param <T> is the type of the enumeration to retreive.
+	 * @param name
+	 * @param type is the type of the enumeration to retreive.
+	 * @return the value
+	 * @throws AttributeException if the attribute was never set.
+	 */
+	public <T extends Enum<T>> T getAttributeAsEnumeration(String name, Class<T> type) throws AttributeException;
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @return the value
+	 * @throws AttributeException if the attribute was never set.
+	 */
+	public Class<?> getAttributeAsJavaClass(String name) throws AttributeException;
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value
+	 */
+	public boolean getAttribute(String name, boolean defaultValue);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value
+	 */
+	public int getAttribute(String name, int defaultValue);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value
+	 */
+	public long getAttribute(String name, long defaultValue);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value
+	 */
+	public float getAttribute(String name, float defaultValue);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value
+	 */
+	public double getAttribute(String name, double defaultValue);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value
+	 */
+	public String getAttribute(String name, String defaultValue);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value
+	 */
+	public UUID getAttribute(String name, UUID defaultValue);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value
+	 */
+	public URL getAttribute(String name, URL defaultValue);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value
+	 */
+	public URI getAttribute(String name, URI defaultValue);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value
+	 */
+	public Image getAttribute(String name, Image defaultValue);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value
+	 */
+	public Date getAttribute(String name, Date defaultValue);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value
+	 */
+	public Color getAttribute(String name, Color defaultValue);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value or <code>null</code>
+	 */
+	public AttributeValue getAttribute(String name, AttributeValue defaultValue);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value
+	 */
+	public InetAddress getAttribute(String name, InetAddress defaultValue);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value
+	 */
+	public InetAddress getAttribute(String name, InetSocketAddress defaultValue);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param <T> is the type of the enumeration.
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value
+	 */
+	public <T extends Enum<T>> T getAttribute(String name, T defaultValue);
+
+	/** Replies the value for the given attribute.
+	 * 
+	 * @param name
+	 * @param defaultValue is the default value replied if the attribute
+	 * has no value.
+	 * @return the value
+	 */
+	public Class<?> getAttribute(String name, Class<?> defaultValue);
+
+	/** Clean the internal memory-storage structures if they exist.
+	 * <p>
+	 * This function permits to limit the memory usage without
+	 * removing the attribute value from a hard storage area (database,
+	 * files...). The attribute which are freed by this method could
+	 * be reloaded in memory with a call to a getting method.
+	 */
+	public void freeMemory();
+
+	/** Replies the map of the values stored in this attribute provider.
+	 * The replied map is a copy of or an unmodifiable version
+	 * of the internal map, if it exists.q
+	 * 
+	 * @return the map, never <code>null</code>.
+	 */
+	public Map<String,Object> toMap();
+	
+	/** Fill the given map with the values stored in this attribute provider.
+	 * 
+	 * @param mapToFill is the map to fill, never <code>null</code>.
+	 */
+	public void toMap(Map<String,Object> mapToFill);
+
+}

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/BufferedAttributeCollection.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/BufferedAttributeCollection.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/BufferedAttributeCollection.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,627 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.UUID;
+
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeException;
+import org.arakhne.afc.attrs.attr.AttributeImpl;
+import org.arakhne.afc.attrs.attr.AttributeType;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+import org.arakhne.afc.attrs.attr.AttributeValueImpl;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Image;
+import org.arakhne.util.ref.SoftValueTreeMap;
+
+/**
+ * This class implements an abstract attribute provider that use
+ * a memory cache.
+ * 
+ * XXX: Make this provider to save asynchronously on the remote storage area.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class BufferedAttributeCollection extends AbstractAttributeCollection {
+	
+	private transient Map<String,AttributeValue> cache = new SoftValueTreeMap<String,AttributeValue>();
+	
+	/** Make a deep copy of this object and replies the copy.
+	 * 
+	 * @return the deep copy.
+	 */
+	@Override
+	public BufferedAttributeCollection clone() {
+		BufferedAttributeCollection clone = (BufferedAttributeCollection)super.clone();
+		this.cache = new SoftValueTreeMap<String,AttributeValue>();
+		return clone;
+	}
+
+	/** Load a value from the data source.
+	 * 
+	 * @param name is the name of the attribute to load
+	 * @return the value of the attribute.
+	 * @throws AttributeException on error or when the attribute does not exist
+	 */
+	protected abstract AttributeValue loadValue(String name) throws AttributeException;
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public abstract Collection<String> getAllAttributeNames();
+	
+	/** Save a value into the data source.
+	 * 
+	 * @param name is the name of the attribute to save
+	 * @param value is the value of the attribute.
+	 * @throws AttributeException on error.
+	 */
+	protected abstract void saveValue(String name, AttributeValue value) throws AttributeException;
+
+	/** Remove a value from the data source.
+	 * 
+	 * @param name is the name of the attribute to remove
+	 * @return the removed value
+	 * @throws AttributeException on error
+	 */
+	protected abstract AttributeValue removeValue(String name) throws AttributeException;
+
+	/** Remove all the values from the data source.
+	 * 
+	 * @return <code>true</code> on success, otherwhise <code>false</code>
+	 * @throws AttributeException on error
+	 */
+	protected abstract boolean removeAllValues() throws AttributeException;
+
+	/** Replies the value associated to the specified name.
+	 * @throws AttributeException 
+	 */
+	private AttributeValue extractValueFor(String name) throws AttributeException {
+		AttributeValue value = null;
+		if (this.cache.containsKey(name)) {
+			value = this.cache.get(name);
+		}
+		else {
+			value = loadValue(name);
+			this.cache.put(name, value);
+		}
+		return value;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean hasAttribute(String name) {
+		return getAllAttributeNames().contains(name);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Collection<Attribute> getAllAttributes() {
+		ArrayList<Attribute> list = new ArrayList<Attribute>(getAttributeCount());
+		Attribute newAttr;
+		for(String name : getAllAttributeNames()) {
+			if (name!=null) {
+				try {
+					newAttr = new AttributeImpl(name, extractValueFor(name));
+					list.add(newAttr);
+				}
+				catch(AttributeException _) {
+					//
+				}
+			}
+		}
+		return list;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Map<AttributeType,Collection<Attribute>> getAllAttributesByType() {
+		TreeMap<AttributeType,Collection<Attribute>> map = new TreeMap<AttributeType,Collection<Attribute>>();
+		Attribute newAttr;
+		for(String name : getAllAttributeNames()) {
+			if (name!=null) {
+				try {
+					newAttr = new AttributeImpl(name, extractValueFor(name));
+					Collection<Attribute> list = map.get(newAttr.getType());
+					if (list==null) {
+						list = new ArrayList<Attribute>();
+						map.put(newAttr.getType(), list);
+					}
+					list.add(newAttr);
+				}
+				catch(AttributeException _) {
+					//
+				}
+			}
+		}
+		return map;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public AttributeValue getAttribute(String name) {
+		try {
+			return new AttributeValueImpl(extractValueFor(name));
+		}
+		catch(AttributeException _) {
+			//
+		}
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public AttributeValue getAttribute(String name, AttributeValue default_value) {
+		AttributeValue value;
+		try {
+			value = new AttributeValueImpl(extractValueFor(name));
+		}
+		catch(AttributeException _) {
+			value = default_value;
+		}
+		return value;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute getAttributeObject(String name) {
+		try {
+			return new AttributeImpl(name,extractValueFor(name));
+		}
+		catch(AttributeException _) {
+			//
+		}
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void freeMemory() {
+		this.cache.clear();
+	}
+	
+	/** Set the attribute value.
+	 * 
+	 * @param name is the name of the attribute
+	 * @param value is the raw value to store.
+	 * @return the new created attribute
+	 * @throws AttributeException 
+	 */
+	protected Attribute setAttributeFromRawValue(String name, AttributeValue value) throws AttributeException {
+		return setAttributeFromRawValue(name, value.getType(), value.getValue());
+	}
+
+	/** Set the attribute value.
+	 * 
+	 * @param name is the name of the attribute
+	 * @param type is the type of the attribute
+	 * @param value is the raw value to store.
+	 * @return the new created attribute
+	 * @throws AttributeException 
+	 */
+	protected Attribute setAttributeFromRawValue(String name, AttributeType type, Object value) throws AttributeException {
+		AttributeValue oldValue;
+		try {
+			oldValue = new AttributeValueImpl(extractValueFor(name));
+		}
+		catch(AttributeException _) {
+			oldValue = null;
+		}
+				
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		Attribute attr = new AttributeImpl(name,type);
+		attr.setValue(type.cast(value));
+
+		saveValue(name,attr);
+	
+		this.cache.put(name, attr);	
+		
+		if (oldValue!=null)
+			fireAttributeChangedEvent(name, oldValue, attr);
+		else
+			fireAttributeAddedEvent(name, attr);
+		
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttributeType(String name, AttributeType type) throws AttributeException {
+		AttributeValue oldValue;
+		try {
+			oldValue = new AttributeValueImpl(extractValueFor(name));
+		}
+		catch(AttributeException _) {
+			oldValue = null;
+		}
+		AttributeType oldType = (oldValue==null) ? null : oldValue.getType();
+
+		if (oldValue==null || oldType==null || type==null || type==oldType) return null;
+		
+		Attribute attr = new AttributeImpl(name,oldValue.getValue());
+		attr.cast(type);
+		
+		this.cache.put(name, attr);
+		
+		fireAttributeChangedEvent(name, oldValue, attr);
+		
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, AttributeValue value) throws AttributeException {
+		return setAttributeFromRawValue(name, value);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, boolean value) {
+		try {
+			return setAttributeFromRawValue(name, AttributeType.BOOLEAN, Boolean.valueOf(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, int value) {
+		try {
+			return setAttributeFromRawValue(name, AttributeType.INTEGER, Long.valueOf(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, long value) {
+		try {
+			return setAttributeFromRawValue(name, AttributeType.INTEGER, Long.valueOf(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, float value) {
+		try {
+			return setAttributeFromRawValue(name, AttributeType.REAL, Double.valueOf(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, double value) {
+		try {
+			return setAttributeFromRawValue(name, AttributeType.REAL, Double.valueOf(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, String value) {
+		try {
+			return setAttributeFromRawValue(name, AttributeType.STRING, value);
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, UUID value) {
+		try {
+			return setAttributeFromRawValue(name, AttributeType.UUID, value);
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, URL value) {
+		try {
+			return setAttributeFromRawValue(name, AttributeType.URL, value);
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, URI value) {
+		try {
+			return setAttributeFromRawValue(name, AttributeType.URI, value);
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, Image value) {
+		try {
+			return setAttributeFromRawValue(name, AttributeType.IMAGE, value);
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, Date value) {
+		try {
+			return setAttributeFromRawValue(name, AttributeType.DATE, value);
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, Color value) {
+		try {
+			return setAttributeFromRawValue(name, AttributeType.COLOR, value);
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, InetAddress value) {
+		try {
+			return setAttributeFromRawValue(name, AttributeType.INET_ADDRESS, value);
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, InetSocketAddress value) {
+		try {
+			return setAttributeFromRawValue(name, AttributeType.INET_ADDRESS, value==null ? null : value.getAddress());
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, Enum<?> value) {
+		try {
+			return setAttributeFromRawValue(name, AttributeType.ENUMERATION, value);
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, Class<?> value) {
+		try {
+			return setAttributeFromRawValue(name, AttributeType.TYPE, value);
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(Attribute value) throws AttributeException {
+		return setAttributeFromRawValue(value.getName(), value);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean removeAttribute(String name) {
+		try {
+			if (hasAttribute(name)) {
+				AttributeValue currentValue = extractValueFor(name);
+				this.cache.remove(name);
+				removeValue(name);
+				fireAttributeRemovedEvent(name,currentValue);
+				return true;
+			}
+		}
+		catch(AttributeException _) {
+			//
+		}
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean removeAllAttributes() {
+		try {
+			if (getAttributeCount()>0) {
+				this.cache.clear();
+				if (removeAllValues()) {
+					fireAttributeClearedEvent();
+					return true;
+				}
+			}
+		}
+		catch(AttributeException _) {
+			//
+		}
+		return false;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean renameAttribute(String oldname, String newname, boolean overwrite) {
+		try {
+			AttributeValue valueForOldName = null;
+			
+			try {
+				valueForOldName = extractValueFor(oldname);
+			}
+			catch(AttributeException _) {
+				//
+			}
+
+			// The source attribute does not exist.
+			if (valueForOldName==null) return false;
+			
+			AttributeValue oldValueForNewName = null;
+			
+			try {
+				oldValueForNewName = extractValueFor(newname);
+			}
+			catch(AttributeException _) {
+				//
+			}
+			
+			// Target attribute is existing and overwrite was disabled.
+			if ((!overwrite)&&(oldValueForNewName!=null)) return false;
+			
+			AttributeValue oldValueCopyForNewName = new AttributeValueImpl(oldValueForNewName);
+			
+			removeValue(oldname);
+			this.cache.remove(oldname);
+			if (valueForOldName instanceof Attribute) {
+				((Attribute)valueForOldName).setName(newname);
+			}
+			saveValue(newname, valueForOldName);
+			this.cache.put(newname, valueForOldName);
+			
+			if (oldValueForNewName!=null)
+				fireAttributeRemovedEvent(newname,oldValueCopyForNewName);
+			
+			fireAttributeRenamedEvent(oldname,newname,valueForOldName);
+			
+			return true;
+		}
+		catch(AttributeException _) {
+			//
+		}
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void flush() {
+		//
+	}
+
+}
+

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/HeapAttributeCollection.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/HeapAttributeCollection.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/HeapAttributeCollection.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,873 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import java.util.UUID;
+
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeException;
+import org.arakhne.afc.attrs.attr.AttributeImpl;
+import org.arakhne.afc.attrs.attr.AttributeNotInitializedException;
+import org.arakhne.afc.attrs.attr.AttributeType;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+import org.arakhne.afc.attrs.attr.AttributeValueImpl;
+import org.arakhne.afc.attrs.attr.InvalidAttributeTypeException;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Image;
+
+/**
+ * This class implements an attribute provider which
+ * only works with the Heap space.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class HeapAttributeCollection extends AbstractAttributeCollection {
+	
+	private Map<String,Object> heap = new TreeMap<String,Object>(new AttributeNameStringComparator());
+	
+	/** Make a deep copy of this object and replies the copy.
+	 * 
+	 * @return the deep copy.
+	 */
+	@Override
+	public HeapAttributeCollection clone() {
+		HeapAttributeCollection clone = (HeapAttributeCollection)super.clone();
+		clone.heap = new TreeMap<String,Object>(new AttributeNameStringComparator());
+		clone.heap.putAll(this.heap);
+		return clone;
+	}
+
+	@Override
+	public void addAttributes(Map<String, Object> content) {
+		Object value, oldValue;
+		AttributeType type;
+		for(Entry<String,Object> pair : content.entrySet()) {
+			value = pair.getValue();
+			type = AttributeType.fromValue(value);
+			value = type.cast(value);
+			oldValue = this.heap.put(pair.getKey(), value);
+			if (oldValue==null) {
+				fireAttributeAddedEvent(pair.getKey(), new AttributeValueImpl(type, value));
+			}
+			else {
+				fireAttributeChangedEvent(pair.getKey(),
+						new AttributeValueImpl(type, oldValue),
+						new AttributeValueImpl(type, value));
+			}
+		}
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addAttributes(AttributeProvider content) throws AttributeException {
+		Object value, oldValue;
+		for(Attribute attr : content.attributes()) {
+			value = attr.getValue();
+			oldValue = this.heap.put(attr.getName(), value);
+			if (oldValue==null) {
+				fireAttributeAddedEvent(attr.getName(),
+						new AttributeValueImpl(attr.getType(), value));
+			}
+			else {
+				fireAttributeChangedEvent(attr.getName(),
+						new AttributeValueImpl(AttributeType.fromValue(oldValue), oldValue),
+						new AttributeValueImpl(attr.getType(), value));
+			}
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setAttributes(Map<String, Object> content) {
+		setAttributesInternal(new TreeMap<String,Object>(content));
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setAttributes(AttributeProvider content) throws AttributeException {
+		Map<String,Object> newAttributes = new TreeMap<String,Object>();
+		content.toMap(newAttributes);
+		setAttributesInternal(newAttributes);
+	}
+
+	private void setAttributesInternal(Map<String, Object> newAttributes) {
+		Iterator<Entry<String,Object>> iterator = this.heap.entrySet().iterator();
+		Entry<String,Object> entry;
+		Object newValue;
+		AttributeType type;
+		while (iterator.hasNext()) {
+			entry = iterator.next();
+			newValue = newAttributes.remove(entry.getKey());
+			if (newValue==null) {
+				iterator.remove();
+				fireAttributeRemovedEvent(entry.getKey(),
+						new AttributeValueImpl(AttributeType.fromValue(entry.getValue()), entry.getValue()));
+			}
+			else {
+				type = AttributeType.fromValue(newValue);
+				newValue = type.cast(newValue);
+				entry.setValue(newValue);
+				fireAttributeChangedEvent(entry.getKey(),
+						new AttributeValueImpl(AttributeType.fromValue(entry.getValue()), entry.getValue()),
+						new AttributeValueImpl(type, newValue));
+			}
+		}
+		
+		for(Entry<String,Object> e : newAttributes.entrySet()) {
+			newValue = e.getValue();
+			type = AttributeType.fromValue(newValue);
+			newValue = type.cast(newValue);
+			if (newValue!=null) {
+				this.heap.put(e.getKey(), newValue);
+				fireAttributeAddedEvent(e.getKey(),
+						new AttributeValueImpl(type, newValue));
+			}
+		}
+	}
+			
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void toMap(Map<String,Object> mapToFill) {
+		mapToFill.putAll(this.heap);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int getAttributeCount() {
+		return this.heap.size();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean hasAttribute(String name) {
+		return this.heap.containsKey(name);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Collection<Attribute> getAllAttributes() {
+		ArrayList<Attribute> list = new ArrayList<Attribute>(getAttributeCount());
+		String name;
+		AttributeImpl newAttr;
+		Object rawValue;
+		for(Entry<String, Object> entry : this.heap.entrySet()) {
+			name = entry.getKey();
+			if (name!=null) {
+				rawValue = entry.getValue();
+				newAttr = new AttributeImpl(name);
+				newAttr.castAndSet(
+						AttributeType.fromValue(rawValue),
+						unprotectNull(rawValue));
+				list.add(newAttr);
+			}
+		}
+		return list;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Map<AttributeType,Collection<Attribute>> getAllAttributesByType() {
+		TreeMap<AttributeType,Collection<Attribute>> map = new TreeMap<AttributeType,Collection<Attribute>>();
+		AttributeType type;
+		Attribute attr;
+		Object value;
+		for(Entry<String,Object> entry : this.heap.entrySet()) {
+			value = entry.getValue(); 
+			if (value!=null) {
+				type = AttributeType.fromValue(value);
+				value = unprotectNull(value);
+				Collection<Attribute> list = map.get(type);
+				if (list==null) {
+					list = new ArrayList<Attribute>();
+					map.put(type, list);
+				}
+				attr = new AttributeImpl(entry.getKey());
+				attr.castAndSet(type, value);
+				list.add(attr);
+			}
+		}
+		return map;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Collection<String> getAllAttributeNames() {
+		return Collections.unmodifiableCollection(this.heap.keySet());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public AttributeValue getAttribute(String name) {
+		return getStoredAttributeValue(name,null);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public AttributeValue getAttribute(String name, AttributeValue default_value) {
+		AttributeValue value = getStoredAttributeValue(name,
+				default_value==null ? null : default_value.getType());
+		if (value==null) return default_value;
+		return value;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute getAttributeObject(String name) {
+		return getStoredAttribute(name,null);
+	}
+
+	/** Replies the attribute with the given name.
+	 * 
+	 * @param name is the name of the attribute to retreive
+	 * @param expectedType is the expected type for the attribute.
+	 * @return the value or <code>null</code>
+	 */
+	protected Attribute getStoredAttribute(String name, AttributeType expectedType) {
+		Object val = this.heap.get(name);
+		if (val!=null) {
+			AttributeType currentType = AttributeType.fromValue(val);
+			val = unprotectNull(val);
+			Attribute attr = new AttributeImpl(name);
+			if (expectedType==null)
+				attr.castAndSet(currentType,val);
+			else
+				attr.castAndSet(expectedType,val);
+			return attr;
+		}
+		return null;
+	}
+
+	/** Replies the attribute with the given name.
+	 * 
+	 * @param name is the name of the attribute to retreive
+	 * @param expectedType is the expected type for the attribute.
+	 * @return the value or <code>null</code>
+	 */
+	protected AttributeValue getStoredAttributeValue(String name, AttributeType expectedType) {
+		Object val = this.heap.get(name);
+		if (val!=null) {
+			AttributeType currentType = AttributeType.fromValue(val);
+			val = unprotectNull(val);
+			AttributeValue attr = new AttributeValueImpl(name);
+			if (expectedType==null)
+				attr.castAndSet(currentType, val);
+			else
+				attr.castAndSet(expectedType,val);
+			return attr;
+		}
+		return null;
+	}
+	
+	private AttributeValue copyValue(String name) {
+		AttributeValue oldValue = null;
+		Object currentValue = this.heap.get(name);
+		if (currentValue!=null) {
+			AttributeType oldType = AttributeType.fromValue(currentValue);
+			oldValue = new AttributeValueImpl();
+			oldValue.castAndSet(oldType, currentValue);
+			return oldValue;
+		}
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttributeType(String name, AttributeType type) throws AttributeException {
+		assert(name!=null);
+
+		AttributeValue oldValue = copyValue(name);
+		AttributeType oldType = (oldValue==null) ? null : oldValue.getType();
+	
+		if (oldType==null || type==null || oldType==type) return null;
+		
+		Attribute attr = new AttributeImpl(name,(oldValue==null) ? null : oldValue.getValue());
+		attr.cast(type);
+		
+		this.heap.put(name, protectNull(attr.getValue(), type));
+		
+		fireAttributeChangedEvent(name, oldValue, attr);
+
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, AttributeValue value) throws AttributeException {
+		assert(name!=null && value!=null);
+		AttributeValue oldValue = copyValue(name);
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		this.heap.put(name, protectNull(value.getValue(), value.getType()));
+		
+		Attribute attr = new AttributeImpl(name,value.getValue());
+
+		if (oldValue!=null)
+			fireAttributeChangedEvent(name, oldValue, value);
+		else
+			fireAttributeAddedEvent(name, attr);
+
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, boolean value) {
+		assert(name!=null);
+
+		AttributeValue oldValue = copyValue(name);
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		this.heap.put(name, Boolean.valueOf(value));
+		
+		Attribute attr = new AttributeImpl(name,value);
+		
+		if (oldValue!=null)
+			fireAttributeChangedEvent(name, oldValue, attr);
+		else
+			fireAttributeAddedEvent(name, attr);
+		
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, int value) {
+		assert(name!=null);
+
+		AttributeValue oldValue = copyValue(name);
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		this.heap.put(name, Long.valueOf(value));
+		
+		Attribute attr = new AttributeImpl(name,value);		
+		
+		if (oldValue!=null)
+			fireAttributeChangedEvent(name, oldValue, attr);
+		else
+			fireAttributeAddedEvent(name, attr);
+		
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, long value) {
+		assert(name!=null);
+
+		AttributeValue oldValue = copyValue(name);
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		this.heap.put(name, Long.valueOf(value));
+		
+		Attribute attr = new AttributeImpl(name,value);		
+		
+		if (oldValue!=null)
+			fireAttributeChangedEvent(name, oldValue, attr);
+		else
+			fireAttributeAddedEvent(name, attr);
+		
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, float value) {
+		assert(name!=null);
+
+		AttributeValue oldValue;
+		Object currentValue = this.heap.get(name);
+		if (currentValue!=null) {
+			oldValue = new AttributeValueImpl();
+			oldValue.castAndSet(AttributeType.fromValue(currentValue), currentValue);
+		}
+		else {
+			oldValue = null;
+		}
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		this.heap.put(name, Double.valueOf(value));
+		
+		Attribute attr = new AttributeImpl(name,value);		
+		
+		if (currentValue!=null)
+			fireAttributeChangedEvent(name, oldValue, attr);
+		else
+			fireAttributeAddedEvent(name, attr);
+		
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, double value) {
+		assert(name!=null);
+
+		AttributeValue oldValue = copyValue(name);
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		this.heap.put(name, Double.valueOf(value));
+		
+		Attribute attr = new AttributeImpl(name,value);		
+		
+		if (oldValue!=null)
+			fireAttributeChangedEvent(name, oldValue, attr);
+		else
+			fireAttributeAddedEvent(name, attr);
+		
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, String value) {
+		assert(name!=null);
+
+		AttributeValue oldValue = copyValue(name);
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		Object rv = (value==null) ? AttributeType.STRING.getDefaultValue() : value;
+		this.heap.put(name, rv);
+		
+		Attribute attr = new AttributeImpl(name,rv);
+		
+		if (oldValue!=null) {
+			fireAttributeChangedEvent(name, oldValue, attr);
+		}
+		else {
+			fireAttributeAddedEvent(name, attr);
+		}
+		
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, UUID value) {
+		assert(name!=null);
+
+		AttributeValue oldValue = copyValue(name);
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		Object rv = (value==null) ? AttributeType.UUID.getDefaultValue() : value;
+		this.heap.put(name, rv);
+		
+		Attribute attr = new AttributeImpl(name,rv);
+		
+		if (oldValue!=null) {
+			fireAttributeChangedEvent(name, oldValue, attr);
+		}
+		else {
+			fireAttributeAddedEvent(name, attr);
+		}
+		
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, URL value) {
+		assert(name!=null);
+
+		AttributeValue oldValue = copyValue(name);
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		this.heap.put(name, protectNull(value, AttributeType.URL));
+		
+		Attribute attr = new AttributeImpl(name,value);
+		
+		if (oldValue!=null) {
+			fireAttributeChangedEvent(name, oldValue, attr);
+		}
+		else {
+			fireAttributeAddedEvent(name, attr);
+		}
+		
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, URI value) {
+		assert(name!=null);
+
+		AttributeValue oldValue = copyValue(name);
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		this.heap.put(name, protectNull(value, AttributeType.URI));
+		
+		Attribute attr = new AttributeImpl(name,value);
+		
+		if (oldValue!=null) {
+			fireAttributeChangedEvent(name, oldValue, attr);
+		}
+		else {
+			fireAttributeAddedEvent(name, attr);
+		}
+		
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, Image value) {
+		assert(name!=null);
+
+		AttributeValue oldValue = copyValue(name);
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		this.heap.put(name, protectNull(value, AttributeType.IMAGE));
+		
+		Attribute attr = new AttributeImpl(name,value);
+		
+		if (oldValue!=null) {
+			fireAttributeChangedEvent(name, oldValue, attr);
+		}
+		else {
+			fireAttributeAddedEvent(name, attr);
+		}
+		
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, Date value) {
+		assert(name!=null);
+
+		AttributeValue oldValue = copyValue(name);
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		Object rv = (value==null) ? AttributeType.DATE.getDefaultValue() : value;
+		this.heap.put(name, rv);
+		
+		Attribute attr = new AttributeImpl(name,rv);
+		
+		if (oldValue!=null) {
+			fireAttributeChangedEvent(name, oldValue, attr);
+		}
+		else {
+			fireAttributeAddedEvent(name, attr);
+		}
+		
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, Color value) {
+		assert(name!=null);
+
+		AttributeValue oldValue = copyValue(name);
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		Object rv = (value==null) ? AttributeType.COLOR.getDefaultValue() : value;
+		this.heap.put(name, rv);
+		
+		Attribute attr = new AttributeImpl(name,rv);
+		
+		if (oldValue!=null) {
+			fireAttributeChangedEvent(name, oldValue, attr);
+		}
+		else {
+			fireAttributeAddedEvent(name, attr);
+		}
+		
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(Attribute value) throws AttributeException {
+		assert(value!=null);
+		String name = value.getName();
+		AttributeValue oldValue = copyValue(name);
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+		
+		this.heap.put(name, protectNull(value.getValue(), value.getType()));
+
+		if (oldValue!=null)
+			fireAttributeChangedEvent(name, oldValue, value);
+		else
+			fireAttributeAddedEvent(name, value);
+		
+		return value;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, InetAddress value) {
+		assert(name!=null);
+
+		AttributeValue oldValue = copyValue(name);
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		this.heap.put(name, value);
+		
+		Attribute attr = new AttributeImpl(name,value);
+		
+		if (oldValue!=null)
+			fireAttributeChangedEvent(name, oldValue, attr);
+		else
+			fireAttributeAddedEvent(name, attr);
+		
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, InetSocketAddress value) {
+		return setAttribute(name, (value==null) ? null : value.getAddress());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, Enum<?> value) {
+		assert(name!=null);
+
+		AttributeValue oldValue = copyValue(name);
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		this.heap.put(name, value);
+		
+		Attribute attr = new AttributeImpl(name,value);
+		
+		if (oldValue!=null)
+			fireAttributeChangedEvent(name, oldValue, attr);
+		else
+			fireAttributeAddedEvent(name, attr);
+		
+		return attr;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, Class<?> value) {
+		assert(name!=null);
+
+		AttributeValue oldValue = copyValue(name);
+		
+		if (oldValue!=null && oldValue.equals(value)) return null;
+
+		this.heap.put(name, value);
+		
+		Attribute attr = new AttributeImpl(name,value);
+		
+		if (oldValue!=null)
+			fireAttributeChangedEvent(name, oldValue, attr);
+		else
+			fireAttributeAddedEvent(name, attr);
+		
+		return attr;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean removeAttribute(String name) {
+		assert(name!=null);
+
+		AttributeValue oldValue;
+		Object currentValue = this.heap.remove(name);
+		if (currentValue!=null) {
+			oldValue = new AttributeValueImpl();
+			oldValue.castAndSet(AttributeType.fromValue(currentValue), currentValue);
+			fireAttributeRemovedEvent(name,oldValue);
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean removeAllAttributes() {
+		if (!this.heap.isEmpty()) {
+			this.heap.clear();
+			fireAttributeClearedEvent();
+			return true;
+		}
+		return false;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean renameAttribute(String oldname, String newname, boolean overwrite) {
+		if (oldname==null || newname==null || oldname.equals(newname)) return false;
+		
+		AttributeValue valueOfOldName = copyValue(oldname);
+		
+		// The attribute does not exist.
+		if (valueOfOldName==null) return false;
+		
+		AttributeValue oldValueOfNewName = copyValue(newname);
+		
+		// The target attribute is existing and overwrite was disabled
+		if ((!overwrite)&&(oldValueOfNewName!=null)) return false;
+		
+		Object rawValue;
+		
+		try {
+			rawValue = valueOfOldName.getValue();
+		}
+		catch (InvalidAttributeTypeException e) {
+			rawValue = null;
+		}
+		catch (AttributeNotInitializedException e) {
+			rawValue = null;
+		}
+
+		this.heap.remove(oldname);
+		this.heap.put(newname, protectNull(rawValue, valueOfOldName.getType()));
+		
+		if (oldValueOfNewName!=null)
+			fireAttributeRemovedEvent(newname,oldValueOfNewName);
+		
+		fireAttributeRenamedEvent(oldname,newname,valueOfOldName);
+		
+		return true;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void freeMemory() {
+		// Do nothing
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void flush() {
+		// Do nothing
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		return this.heap.toString();
+	}
+
+}
+

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/MultiAttributeCollection.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/MultiAttributeCollection.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/MultiAttributeCollection.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,732 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeError;
+import org.arakhne.afc.attrs.attr.AttributeException;
+import org.arakhne.afc.attrs.attr.AttributeImpl;
+import org.arakhne.afc.attrs.attr.AttributeType;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+import org.arakhne.afc.attrs.attr.AttributeValueImpl;
+import org.arakhne.afc.attrs.collection.AttributeChangeEvent.Type;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Image;
+
+/**
+ * This class contains a collection of attribute providers and
+ * tries to gather the data.
+ * This class contains a collection of AttributeProviders and
+ * exhibites the values of the attributes of all these providers.
+ * This class follows the following rules (in that order)
+ * to retreive the value of an attribute:<ol>
+ * <li>If the attribute is defined in none of the containers, throws the standard exception;</li>
+ * <li>If the attribute is defined in only one of the containers, replies the attribute value itself;</li>
+ * <li>If the attribute is defined in more than one container:<ol>
+ * 		<li>if all the values are equal, then replies one of the attribute values;</li>
+ * 		<li>if the values are not equal and all the values have equivalent types (as replied
+ * 			by {@link AttributeType#isAssignableFrom(AttributeType)}), then replies an
+ * 			attribute value with a "undefined" value and of the type of one of the values;</li>
+ * 		<li>if the values are not equal and one of the value has not an equivalent type to
+ * 			the others (as replied by {@link AttributeType#isAssignableFrom(AttributeType)}),
+ * 			then replies an attribute value with a "undefined" value and of the type OBJECT.</li>
+ * 		</ol></li>
+ * </ol>
+ * <p>
+ * If an attribute is set from this AttributeProviderContainer, all the containers inside it
+ * are changed.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @since 4.0
+ */
+public class MultiAttributeCollection extends MultiAttributeProvider implements AttributeCollection {
+
+	/**
+	 * Boolean indicates that enable event handling from the providers, or not.
+	 */
+	AtomicBoolean runProviderEvents = new AtomicBoolean(true);
+
+	private Handler eventHandler = new Handler();
+	private Collection<AttributeChangeListener> listeners = null;
+	private boolean isEventFirable = true;
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized boolean isEventFirable() {
+		return this.isEventFirable;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized void setEventFirable(boolean isFirable) {
+		this.isEventFirable = isFirable;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public MultiAttributeCollection clone() {
+		MultiAttributeCollection clone = (MultiAttributeCollection)super.clone();
+		clone.runProviderEvents = new AtomicBoolean(true);
+		clone.eventHandler = new Handler();
+		clone.listeners = (this.listeners==null) ? null : new ArrayList<AttributeChangeListener>(this.listeners);
+		for(AttributeProvider c : clone.containers()) {
+			if (c instanceof AttributeCollection) {
+				((AttributeCollection)c).addAttributeChangeListener(clone.eventHandler);
+			}
+		}
+		return clone;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean addAttributeContainer(AttributeProvider container) {
+		if (super.addAttributeContainer(container)) {
+			if (container instanceof AttributeCollection)
+				((AttributeCollection)container).addAttributeChangeListener(this.eventHandler);
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean removeAttributeContainer(AttributeProvider container) {
+		if (super.removeAttributeContainer(container)) {
+			if (container instanceof AttributeCollection)
+				((AttributeCollection)container).removeAttributeChangeListener(this.eventHandler);
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addAttributeChangeListener(AttributeChangeListener listener) {
+		if (this.listeners==null)
+			this.listeners = new ArrayList<AttributeChangeListener>();
+		this.listeners.add(listener);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void removeAttributeChangeListener(AttributeChangeListener listener) {
+		if (this.listeners!=null) {
+			this.listeners.add(listener);
+			if (this.listeners.isEmpty())
+				this.listeners = null;
+		}
+	}
+
+	/** Notifies the listeners about the change of an attribute.
+	 * 
+	 * @param event
+	 */
+	protected void fireAttributeChange(AttributeChangeEvent event) {
+		if (this.listeners!=null && isEventFirable()) {
+			AttributeChangeListener[] list = new AttributeChangeListener[this.listeners.size()];
+			this.listeners.toArray(list);
+			for(AttributeChangeListener listener : list) {
+				listener.onAttributeChangeEvent(event);
+			}
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void copyFrom(AttributeProvider otherContainer) throws AttributeException {
+		if (otherContainer!=null) {
+			for(Attribute attr : otherContainer.attributes()) {
+				setAttribute(attr);
+			}
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void flush() {
+		for(AttributeProvider c : containers()) {
+			if (c instanceof AttributeCollection) {
+				((AttributeCollection)c).flush();
+			}
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean removeAllAttributes() {
+		boolean b = this.runProviderEvents.getAndSet(false);
+		try {
+			boolean changed = false;
+			for(AttributeProvider c : containers()) {
+				if (c instanceof AttributeCollection) {
+					changed = ((AttributeCollection)c).removeAllAttributes() | changed;
+				}
+			}
+			if (changed) {
+				this.cache.clear();
+				this.names = null;
+				fireAttributeChange(new AttributeChangeEvent(this, Type.REMOVE_ALL, null, null, null, null));
+			}		
+			return changed;
+		}
+		finally {
+			this.runProviderEvents.set(b);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean removeAttribute(String name) {
+		boolean b = this.runProviderEvents.getAndSet(false);
+		try {
+			boolean changed = false;
+			ManyValueAttributeValue oldValue = new ManyValueAttributeValue();
+			for(AttributeProvider c : containers()) {
+				assign(oldValue, c.getAttribute(name));
+				if (c instanceof AttributeCollection) {
+					changed = ((AttributeCollection)c).removeAttribute(name) | changed;
+				}
+			}
+			if (changed) {
+				AttributeValue value = canonize(oldValue);
+				this.cache.remove(name);
+				if (this.names!=null) {
+					this.names.remove(name);
+					if (this.names.isEmpty()) this.names = null;
+				}
+				fireAttributeChange(new AttributeChangeEvent(this, Type.REMOVAL, name, value, null, null));
+			}
+			return changed;
+		}
+		finally {
+			this.runProviderEvents.set(b);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean renameAttribute(String oldname, String newname) {
+		boolean b = this.runProviderEvents.getAndSet(false);
+		try {
+			boolean changed = false;
+			ManyValueAttributeValue currentValue = new ManyValueAttributeValue();
+			for(AttributeProvider c : containers()) {
+				assign(currentValue, c.getAttribute(oldname));
+				if (c instanceof AttributeCollection) {
+					changed = ((AttributeCollection)c).renameAttribute(oldname, newname) | changed;
+				}
+			}
+			if (changed) {
+				AttributeValue cValue = canonize(currentValue);
+				this.cache.remove(oldname);
+				this.cache.remove(newname);
+				if (this.names!=null) {
+					this.names.remove(oldname);
+					this.names.add(newname);
+				}
+				fireAttributeChange(new AttributeChangeEvent(this, Type.RENAME, oldname, cValue, newname, cValue));
+			}
+			return changed;
+		}
+		finally {
+			this.runProviderEvents.set(b);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean renameAttribute(String oldname, String newname, boolean overwrite) {
+		boolean b = this.runProviderEvents.getAndSet(false);
+		try {
+			boolean changed = false;
+			ManyValueAttributeValue currentValue = new ManyValueAttributeValue();
+			for(AttributeProvider c : containers()) {
+				assign(currentValue, c.getAttribute(oldname));
+				if (c instanceof AttributeCollection) {
+					changed = ((AttributeCollection)c).renameAttribute(oldname, newname, overwrite) | changed;
+				}
+			}
+			if (changed) {
+				AttributeValue cValue = canonize(currentValue);
+				this.cache.remove(oldname);
+				if (this.names!=null) {
+					this.names.remove(oldname);
+					this.names.add(newname);
+				}
+				fireAttributeChange(new AttributeChangeEvent(this, Type.RENAME, oldname, cValue, newname, cValue));
+			}
+			return changed;
+		}
+		finally {
+			this.runProviderEvents.set(b);
+		}
+	}
+	
+	@Override
+	public void addAttributes(AttributeProvider content)
+			throws AttributeException {
+		for(Attribute attr : content.attributes()) {
+			setAttribute(attr);
+		}
+	}
+	
+	@Override
+	public void addAttributes(Map<String, Object> content) {
+		AttributeType type;
+		Object rawValue;
+		for(Entry<String,Object> entry : content.entrySet()) {
+			rawValue = entry.getValue();
+			type = AttributeType.fromValue(rawValue);
+			rawValue = type.cast(rawValue);
+			try {
+				setAttribute(entry.getKey(), new AttributeValueImpl(type, rawValue));
+			}
+			catch (AttributeException _) {
+				// should never occur
+			}
+		}
+	}
+	
+	@Override
+	public void setAttributes(AttributeProvider content)
+			throws AttributeException {
+		removeAllAttributes();
+		for(Attribute attr : content.attributes()) {
+			setAttribute(attr);
+		}
+	}
+	
+	@Override
+	public void setAttributes(Map<String, Object> content) {
+		removeAllAttributes();
+		AttributeType type;
+		Object value;
+		for(Entry<String,Object> entry : content.entrySet()) {
+			value = entry.getValue();
+			type = AttributeType.fromValue(value);
+			value = type.cast(value);
+			try {
+				setAttribute(entry.getKey(), new AttributeValueImpl(type, value));
+			}
+			catch (AttributeException e) {
+				throw new AttributeError(e);
+			}
+		}
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, AttributeValue value) throws AttributeException {
+		boolean b = this.runProviderEvents.getAndSet(false);
+		try {
+			boolean changed = false;
+			Attribute attr;
+			ManyValueAttributeValue oldValue = new ManyValueAttributeValue();
+			ManyValueAttributeValue newValue = new ManyValueAttributeValue();
+			for(AttributeProvider c : containers()) {
+				assign(oldValue, c.getAttribute(name));
+				if (c instanceof AttributeCollection) {
+					attr = ((AttributeCollection)c).setAttribute(name, value);
+					assign(newValue, attr);
+					if (attr!=null) changed = true;
+				}
+				else {
+					assign(newValue, null);
+				}
+			}
+			if (changed) {
+				AttributeValue oValue = canonize(oldValue);
+				AttributeValue nValue = canonize(newValue);
+				this.cache.put(name, nValue);
+				AttributeChangeEvent event;
+				if (nValue==null) {
+					event = new AttributeChangeEvent(this, Type.REMOVAL, name, oValue, null, null);
+				}
+				else if (oValue!=null && oValue.isAssigned()) {
+					event = new AttributeChangeEvent(this, Type.VALUE_UPDATE, name, oValue, name, nValue);
+				}
+				else {
+					event = new AttributeChangeEvent(this, Type.ADDITION, null, null, name, nValue);
+				}
+				fireAttributeChange(event);
+				return new AttributeImpl(name, value);
+			}
+			return null;
+		}
+		finally {
+			this.runProviderEvents.set(b);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, boolean value) {
+		try {
+			return setAttribute(name, new AttributeValueImpl(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, int value) {
+		try {
+			return setAttribute(name, new AttributeValueImpl(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, long value) {
+		try {
+			return setAttribute(name, new AttributeValueImpl(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, float value) {
+		try {
+			return setAttribute(name, new AttributeValueImpl(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, double value) {
+		try {
+			return setAttribute(name, new AttributeValueImpl(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, String value) {
+		try {
+			return setAttribute(name, new AttributeValueImpl(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, InetAddress value) {
+		try {
+			return setAttribute(name, new AttributeValueImpl(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, InetSocketAddress value) {
+		try {
+			return setAttribute(name, new AttributeValueImpl(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, Enum<?> value) {
+		try {
+			return setAttribute(name, new AttributeValueImpl(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, Class<?> value) {
+		try {
+			return setAttribute(name, new AttributeValueImpl(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, UUID value) {
+		try {
+			return setAttribute(name, new AttributeValueImpl(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, URL value) {
+		try {
+			return setAttribute(name, new AttributeValueImpl(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, URI value) {
+		try {
+			return setAttribute(name, new AttributeValueImpl(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, Image value) {
+		try {
+			return setAttribute(name, new AttributeValueImpl(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, Date value) {
+		try {
+			return setAttribute(name, new AttributeValueImpl(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(String name, Color value) {
+		try {
+			return setAttribute(name, new AttributeValueImpl(value));
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttribute(Attribute value) throws AttributeException {
+		if (value==null) return null;
+		try {
+			return setAttribute(value.getName(), value);
+		}
+		catch (AttributeException _) {
+			return null;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute setAttributeType(String name, AttributeType type) throws AttributeException {
+		boolean b = this.runProviderEvents.getAndSet(false);
+		try {
+			ManyValueAttributeValue oldValue = new ManyValueAttributeValue();
+			ManyValueAttributeValue attr = new ManyValueAttributeValue();
+			Attribute a;
+			boolean changed = false;
+			for(AttributeProvider c : containers()) {
+				assign(oldValue, c.getAttribute(name));
+				if (c instanceof AttributeCollection) {
+					a = ((AttributeCollection)c).setAttributeType(name, type);
+					assign(attr, a);
+					if (a!=null) changed = true;
+				}
+			}
+			AttributeValue cValue = canonize(attr);
+			if (changed) {
+				AttributeValue oValue = canonize(oldValue);
+				this.cache.put(name, cValue);
+				fireAttributeChange(new AttributeChangeEvent(this, Type.VALUE_UPDATE, name, oValue, name, cValue));
+			}
+			return new AttributeImpl(name, cValue);
+		}
+		finally {
+			this.runProviderEvents.set(b);
+		}
+	}
+
+	/**
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 * @since 4.0
+	 */
+	private class Handler implements AttributeChangeListener {
+
+		/**
+		 */
+		public Handler() {
+			//
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void onAttributeChangeEvent(AttributeChangeEvent event) {
+			if (MultiAttributeCollection.this.runProviderEvents.get()) {
+				switch(event.getType()) {
+				case ADDITION:
+					MultiAttributeCollection.this.cache.remove(event.getName());
+					if (MultiAttributeCollection.this.names!=null)
+						MultiAttributeCollection.this.names.add(event.getName());
+					break;
+				case REMOVAL:
+					MultiAttributeCollection.this.cache.remove(event.getOldName());
+					if (MultiAttributeCollection.this.names!=null)
+						MultiAttributeCollection.this.names.remove(event.getOldName());
+					break;
+				case REMOVE_ALL:
+					freeMemory();
+					break;
+				case RENAME:
+					MultiAttributeCollection.this.cache.remove(event.getOldName());
+					MultiAttributeCollection.this.cache.remove(event.getName());
+					if (MultiAttributeCollection.this.names!=null) {
+						MultiAttributeCollection.this.names.remove(event.getOldName());
+						MultiAttributeCollection.this.names.remove(event.getName());
+					}
+					break;
+				case VALUE_UPDATE:
+					MultiAttributeCollection.this.cache.remove(event.getName());
+					break;
+				default:
+				}
+			}
+		}
+
+	}
+
+}
+

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/MultiAttributeProvider.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/MultiAttributeProvider.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/MultiAttributeProvider.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,400 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.Map.Entry;
+
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeImpl;
+import org.arakhne.afc.attrs.attr.AttributeType;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+import org.arakhne.afc.attrs.attr.AttributeValueImpl;
+import org.arakhne.util.ref.SoftValueTreeMap;
+
+/**
+ * This class contains a collection of attribute containers and
+ * tries to gather the data.
+ * This class contains a collection of AttributeContainers and
+ * exhibites the values of the attributes of all these containers.
+ * This class follows the following rules (in that order)
+ * to retreive the value of an attribute:<ol>
+ * <li>If the attribute is defined in none of the containers, throws the standard exception;</li>
+ * <li>If the attribute is defined in only one of the containers, replies the attribute value itself;</li>
+ * <li>If the attribute is defined in more than one container:<ol>
+ * 		<li>if all the values are equal, then replies one of the attribute values;</li>
+ * 		<li>if the values are not equal and all the values have equivalent types (as replied
+ * 			by {@link AttributeType#isAssignableFrom(AttributeType)}), then replies an
+ * 			attribute value with a "undefined" value and of the type of one of the values;</li>
+ * 		<li>if the values are not equal and one of the value has not an equivalent type to
+ * 			the others (as replied by {@link AttributeType#isAssignableFrom(AttributeType)}),
+ * 			then replies an attribute value with a "undefined" value and of the type OBJECT.</li>
+ * 		</ol></li>
+ * </ol>
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @since 4.0
+ */
+public class MultiAttributeProvider extends AbstractAttributeProvider {
+
+	/** Cache of the attribute values.
+	 */
+	transient Map<String,AttributeValue> cache = new SoftValueTreeMap<String,AttributeValue>();
+
+	/** Cache of the names of the attributes.
+	 */
+	Set<String> names = null;
+
+	private Collection<AttributeProvider> containers = new ArrayList<AttributeProvider>();
+	
+	@Override
+	public void toMap(Map<String, Object> mapToFill) {
+		for(AttributeProvider provider : this.containers) {
+			mapToFill.putAll(provider.toMap());
+		}
+	}
+	
+	/** Replies the value associated to the specified name.
+	 */
+	private Attribute extract(String name) {
+		AttributeValue value = null;
+		if (this.cache.containsKey(name)) {
+			value = this.cache.get(name);
+		}
+		else {
+			ManyValueAttributeValue result = new ManyValueAttributeValue();
+			AttributeValue attrValue;
+			for(AttributeProvider c : this.containers) {
+				attrValue = c.getAttribute(name);
+				assign(result, attrValue);
+			}
+			value = canonize(result);
+			this.cache.put(name, value);
+		}
+		return (value!=null) ? new AttributeImpl(name, value) : null;
+	}
+
+	/** Assign the value v2 to v1 and change v1 according
+	 * to the types of v1 and v2.
+	 * Do not forget to invoke {@link #canonize(ManyValueAttributeValue)} on
+	 * <var>v1</var>.
+	 * 
+	 * @param v1
+	 * @param v2
+	 */
+	static void assign(ManyValueAttributeValue v1, AttributeValue v2) {
+		if (v2!=null) v1.setTopType(v2.getType());
+		if (v2==null || !v2.isAssigned()) {
+			v1.setMultipleValues(true);
+		}
+		else {
+			assert(v2.isAssigned());
+			if (!v1.isAssigned()) {
+				v1.setValue(v2);
+			}
+			else if (!v2.equals(v1)) {
+				v1.setMultipleValues(true);
+				if (!v1.isAssignableFrom(v2)) {
+					if (!v2.isAssignableFrom(v1))
+						v1.setInternalValue(null, AttributeType.OBJECT);
+					else
+						v1.setInternalValue(null, v2.getType());
+				}
+			}
+		}
+	}
+	
+	/** Replace any indicator put by {@link #assign(ManyValueAttributeValue, AttributeValue)}
+	 * to retreive a standard attribute value.
+	 * 
+	 * @param v is the value to canonize.
+	 * @return the canonized value.
+	 */
+	static AttributeValue canonize(ManyValueAttributeValue v) {
+		AttributeValueImpl attr;
+		if (v.hasMultipleValues()) {
+			if (v.getTopType()==null) return null;
+			attr = new AttributeValueImpl(v.getTopType(), null);
+		}
+		else {
+			attr = new AttributeValueImpl();
+			attr.setValue(v);
+		}
+		return attr;
+	}
+
+	/** Replies a collection on the containers.
+	 * 
+	 * @return a collection on the containers.
+	 */
+	protected Collection<AttributeProvider> containers() {
+		return Collections.unmodifiableCollection(this.containers);
+	}
+
+	/** Add a container in this set.
+	 * 
+	 * @param container
+	 * @return <code>true</code> if the container has been added,
+	 *  otherwise <code>false</code> 
+	 */
+	public boolean addAttributeContainer(AttributeProvider container) {
+		if (this.containers.add(container)) {
+			freeMemory();
+			return true;
+		}
+		return false;
+	}
+
+	/** Remove a container in this set.
+	 * 
+	 * @param container 
+	 * @return <code>true</code> if the container has been removed,
+	 *  otherwise <code>false</code> 
+	 */
+	public boolean removeAttributeContainer(AttributeProvider container) {
+		if (this.containers.remove(container)) {
+			freeMemory();
+			return true;
+		}
+		return false;
+	}
+	
+	/** Replies the number of attribute containers in this MultiAttributeContainer.
+	 * 
+	 * @return the number of attribute containers in this MultiAttributeContainer.
+	 */
+	public int getAttributeContainerCount() {
+		return this.containers.size();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public MultiAttributeProvider clone() {
+		MultiAttributeProvider clone = (MultiAttributeProvider)super.clone();
+		clone.cache = new SoftValueTreeMap<String,AttributeValue>();
+		for(Entry<String,AttributeValue> e : this.cache.entrySet()) {
+			clone.cache.put(e.getKey(), new AttributeValueImpl(e.getValue()));
+		}
+		clone.containers = new ArrayList<AttributeProvider>(this.containers);
+		if (this.names!=null) {
+			clone.names = new TreeSet<String>(new AttributeNameStringComparator());
+			clone.names.addAll(this.names);
+		}
+		return clone;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void freeMemory() {
+		this.cache.clear();
+		if (this.names!=null) this.names.clear();
+		this.names = null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Collection<Attribute> getAllAttributes() {
+		ArrayList<Attribute> list = new ArrayList<Attribute>(getAttributeCount());
+		Attribute newAttr;
+		for(String name : getAllAttributeNames()) {
+			if (name!=null) {
+				newAttr = extract(name);
+				if (newAttr!=null) list.add(newAttr);
+			}
+		}
+		return list;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Map<AttributeType, Collection<Attribute>> getAllAttributesByType() {
+		TreeMap<AttributeType,Collection<Attribute>> map = new TreeMap<AttributeType,Collection<Attribute>>();
+		Attribute newAttr;
+		for(String name : getAllAttributeNames()) {
+			if (name!=null) {
+				newAttr = extract(name);
+				if (newAttr!=null) {
+					Collection<Attribute> list = map.get(newAttr.getType());
+					if (list==null) {
+						list = new ArrayList<Attribute>();
+						map.put(newAttr.getType(), list);
+					}
+					list.add(newAttr);
+				}
+			}
+		}
+		return map;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public AttributeValue getAttribute(String name) {
+		return extract(name);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public AttributeValue getAttribute(String name, AttributeValue defaultValue) {
+		AttributeValue value = extract(name);
+		if (value==null) value = defaultValue;
+		return value;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int getAttributeCount() {
+		return getAllAttributeNames().size();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Collection<String> getAllAttributeNames() {
+		if (this.names==null) {
+			Set<String> names = new TreeSet<String>(new AttributeNameStringComparator());
+			for(AttributeProvider c : this.containers) {
+				names.addAll(c.getAllAttributeNames());
+			}
+			this.names = names;
+		}
+		return Collections.unmodifiableSet(this.names);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Attribute getAttributeObject(String name) {
+		return extract(name);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean hasAttribute(String name) {
+		for(AttributeProvider c : this.containers) {
+			if (c.hasAttribute(name)) return true;
+		}
+		return false;
+	}
+
+	/** This class provides an implementation of attribute value
+	 * that may be marked with an indicators that many
+	 * values are possibles for the attribute.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 * @since 4.0
+	 */
+	static class ManyValueAttributeValue extends AttributeValueImpl {
+
+		private boolean hasMultipleValues = false;
+		private AttributeType topType = null;
+		
+		/** Replies the type type associated to this attribute value.
+		 * 
+		 * @return the top type associated to this attribute value.
+		 */
+		public AttributeType getTopType() {
+			return this.topType;
+		}
+		
+		/** Set the top type associated to this attribute value.
+		 * 
+		 * @param type is the top type associated to this attribute value.
+		 */
+		public void setTopType(AttributeType type) {
+			if (this.topType==null) {
+				this.topType = type;
+			}
+			else if (this.topType!=type) {
+				if (!this.topType.isAssignableFrom(type)) {
+					if (type.isAssignableFrom(this.topType)) {
+						this.topType = type;
+					}
+					else {
+						this.topType = AttributeType.OBJECT;
+					}
+				}
+			}
+		}
+		
+		@Override
+		public final void setInternalValue(Object value) {
+			super.setInternalValue(value);
+		}
+		
+		@Override
+		public final void setInternalValue(Object value, AttributeType type) {
+			super.setInternalValue(value, type);
+		}
+
+		/** Replies if this attribute has multiple values.
+		 * 
+		 * @return <code>true</code> if this attribute has multiple
+		 * values, otherwise <code>false</code>.
+		 */
+		public boolean hasMultipleValues() {
+			return this.hasMultipleValues;
+		}
+		
+		/** Set if this attribute has multiple values.
+		 * 
+		 * @param v is <code>true</code> if this attribute has multiple
+		 * values, otherwise <code>false</code>.
+		 */
+		public void setMultipleValues(boolean v) {
+			this.hasMultipleValues = v;
+		}
+
+	}
+	
+}
+

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/NoAttributeFoundException.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/NoAttributeFoundException.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/NoAttributeFoundException.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,60 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import org.arakhne.afc.attrs.attr.AttributeException;
+
+/**
+ * This exception is generated each time an attribute
+ * was not found.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class NoAttributeFoundException extends AttributeException {
+	
+	private static final long serialVersionUID = 6988928120286302957L;
+
+	/**
+	 */
+	public NoAttributeFoundException() {
+		super();
+	}
+
+	/**
+	 * @param attrname is the name of the not-found attribute.
+	 */
+	public NoAttributeFoundException(String attrname) {
+		super(attrname);
+	}
+
+	/**
+	 * @param e is the exception to forward
+	 */
+	public NoAttributeFoundException(Exception e) {
+		super(e);
+	}
+
+}
\ No newline at end of file

Added: trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/ROMBasedAttributeCollection.java
===================================================================
--- trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/ROMBasedAttributeCollection.java	                        (rev 0)
+++ trunk/attrs/src/main/java/org/arakhne/afc/attrs/collection/ROMBasedAttributeCollection.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,72 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.util.Collection;
+
+import org.arakhne.afc.attrs.attr.Attribute;
+
+/**
+ * This interface representes a provider of attributes that is
+ * partly based on data stored on a ROM (read-only memory).
+ * <p>
+ * The changed values are stored inside the memory and never
+ * written back into the ROM.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface ROMBasedAttributeCollection extends AttributeCollection { 
+
+	/** Replies the list of attributes that have
+	 * changed.
+	 * 
+	 * @return the list of the names of the attributes which are stored inside the memory buffer.
+	 */
+	public Collection<String> getAllBufferedAttributeNames();
+
+	/** Replies the list of attributes that have
+	 * changed.
+	 * 
+	 * @return the list of attributes stored inside the memory buffer.
+	 */
+	public Collection<Attribute> getAllBufferedAttributes();
+
+	/** Replies if the specified attribute name corresponds to
+	 * a buffered attribute value.
+	 * 
+	 * @param attributeName
+	 * @return <code>true</code> if an attribute with the given name
+	 * is stored inside the memory buffer, otherwise <code>false</code>
+	 */
+	public boolean isBufferedAttribute(String attributeName);
+
+	/** Replies the count of buffered attributes.
+	 * 
+	 * @return the count of buffered attributes.
+	 */
+	public int getBufferedAttributeCount();
+
+}

Added: trunk/attrs/src/main/resources/org/arakhne/afc/attrs/types.properties
===================================================================
--- trunk/attrs/src/main/resources/org/arakhne/afc/attrs/types.properties	                        (rev 0)
+++ trunk/attrs/src/main/resources/org/arakhne/afc/attrs/types.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,41 @@
+# $Id$
+# 
+# Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+#                        Universite de Technologie de Belfort-Montbeliard.
+# Copyright (C) 2013 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+BOOLEAN = Boolean
+COLOR = Color
+DATE = Date
+ENUMERATION = Enumeration
+FLOAT = Float
+ICON = Icon
+INET_ADDRESS = Internet Address
+INTEGER = Integer
+OBJECT = Object
+OTHER = Other
+POINT2D = Point 2d
+POINT3D = Point 3d
+POLYLINE = Polyline 2d
+POLYLINE3D = Polyline 3d
+STRING = String
+TIMESTAMP = Timestamp
+TYPE = Java Type
+UUID = Unique Universal Identifier
+URI = Uniform resource identifier
+URL = Uniform resource locator
\ No newline at end of file

Added: trunk/attrs/src/main/resources/org/arakhne/afc/attrs/types_es.properties
===================================================================
--- trunk/attrs/src/main/resources/org/arakhne/afc/attrs/types_es.properties	                        (rev 0)
+++ trunk/attrs/src/main/resources/org/arakhne/afc/attrs/types_es.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,41 @@
+# $Id$
+# 
+# Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+#                        Universite de Technologie de Belfort-Montbeliard.
+# Copyright (C) 2013 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+BOOLEAN = Booleano
+COLOR = Color
+DATE = Fecha
+ENUMERATION = Enumeraci\xF3n
+FLOAT = N\xFAmero decimal
+ICON = Icono
+INET_ADDRESS = N\xFAmero Internet
+INTEGER = N\xFAmero entero
+OBJECT = Asunto
+OTHER = Otro
+POINT2D = Punto 2d
+POINT3D = Punto 3d
+POLYLINE = Poly-l\xEDnea 2d
+POLYLINE3D = Poly-l\xEDnea 3d
+STRING = Cadena de caracteres
+TIMESTAMP = Estampilla
+TYPE = Tipo Java
+UUID = Identificaci\xF3n \xFAnico universal
+URI = Identificador uniforme de recursos
+URL = Localizador de recurso uniforme
\ No newline at end of file

Added: trunk/attrs/src/main/resources/org/arakhne/afc/attrs/types_fr.properties
===================================================================
--- trunk/attrs/src/main/resources/org/arakhne/afc/attrs/types_fr.properties	                        (rev 0)
+++ trunk/attrs/src/main/resources/org/arakhne/afc/attrs/types_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,41 @@
+# $Id$
+# 
+# Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+#                        Universite de Technologie de Belfort-Montbeliard.
+# Copyright (C) 2013 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+BOOLEAN = Bool\xE9en
+COLOR = Couleur
+DATE = Date
+ENUMERATION = Enumeration
+FLOAT = Nombre d\xE9cimal
+ICON = Ic\xF4ne
+INET_ADDRESS = Addresse Internet
+INTEGER = Nombre entier
+OBJECT = Objet
+OTHER = Autre
+POINT2D = Point 2d
+POINT3D = Point 3d
+POLYLINE = Poly-ligne 2d
+POLYLINE3D = Poly-ligne 3d
+STRING = Cha\xEEne de caract\xE8res
+TIMESTAMP = Estampille
+TYPE = Type Java
+UUID = Identification universel unique
+URI = Identificateur uniforme de ressource
+URL = Adresse uniforme de ressource
\ No newline at end of file

Added: trunk/attrs/src/test/java/org/arakhne/afc/attrs/AbstractAttrTestCase.java
===================================================================
--- trunk/attrs/src/test/java/org/arakhne/afc/attrs/AbstractAttrTestCase.java	                        (rev 0)
+++ trunk/attrs/src/test/java/org/arakhne/afc/attrs/AbstractAttrTestCase.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,434 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Random;
+import java.util.UUID;
+
+import junit.framework.TestCase;
+
+/**
+ * Test of Attribute.
+ * 
+ * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AbstractAttrTestCase extends TestCase {
+
+	/**
+	 */
+	protected final Random RANDOM = new Random();
+	
+	/**
+	 * @param message is the message to format
+	 * @param expected is the expected value
+	 * @param actual is the current value
+	 * @return the formated message
+	 */
+	static String formatMsg(String message, Object expected, Object actual) {
+		String formatted= ""; //$NON-NLS-1$
+		if (message != null)
+			formatted= message+" "; //$NON-NLS-1$
+		return formatted+"expected:<"+expected+"> but was:<"+actual+">"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+	}
+	
+	/** Create a string.
+	 * @return a string
+	 */
+	protected static String randomString() {
+		return UUID.randomUUID().toString();
+	}
+	
+	/** Random a value from an enumeration.
+	 * 
+	 * @param <E> is the type of the enumeration
+	 * @param type is the type of the enumeration
+	 * @return a value from the specified enumeration.
+	 */
+	protected <E extends Enum<E>> E randomEnum(Class<E> type) {
+		E[] constants = type.getEnumConstants();
+		return constants[this.RANDOM.nextInt(constants.length)];
+	}
+	
+	/** Test if the two date are equals with an epsilon.
+	 * 
+	 * @param expected
+	 * @param actual
+	 */
+	protected static void assertEpsilonEquals(Date expected, Date actual) {
+		if (expected == null && actual == null) return;
+		String message = null;
+		if (expected!=null) {
+			DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");   //$NON-NLS-1$
+			String expectedStr = fmt.format(expected);
+			String actualStr = fmt.format(expected);
+			if (expectedStr.equals(actualStr)) return;
+			message = "expected <"+expectedStr+">, actual: <"+actualStr+">"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		}
+		fail(formatMsg(message, expected, actual));
+	}
+	
+	/** Test if the two collections contain the same elements and
+	 * taking into account the order of the elements in the collections.
+	 *
+	 * @param <T>
+	 * @param expected
+	 * @param actual
+	 */
+	protected static <T> void assertEquals(T[] expected, T[] actual) {
+		assertEquals(null, expected, actual);
+	}
+	
+	/** Test if the two collections contain the same elements and
+	 * taking into account the order of the elements in the collections.
+	 *
+	 * @param <T>
+	 * @param message
+	 * @param expected
+	 * @param actual
+	 */
+	protected static <T> void assertEquals(String message, T[] expected, T[] actual) {
+		if (actual==expected) return;
+		if (actual==null) {
+			fail(formatMsg((message==null ? "" : (message+": "))  //$NON-NLS-1$//$NON-NLS-2$
+					+"not same arrays, not same size",//$NON-NLS-1$
+					expected.length, 0));
+		}
+		else {
+			if (expected==null) {
+				fail(formatMsg((message==null ? "" : (message+": "))  //$NON-NLS-1$//$NON-NLS-2$
+						+"not same arrays, not same size",//$NON-NLS-1$
+						0, actual.length));
+			}
+			else {
+				if (actual.length!=expected.length) {
+					fail(formatMsg((message==null ? "" : (message+": "))  //$NON-NLS-1$//$NON-NLS-2$
+							+"not same arrays, not same size",//$NON-NLS-1$
+							expected.length, actual.length));
+				}
+				T o1, o2;
+				for(int i=0; i<expected.length; i++) {
+					o1 = expected[i];
+					o2 = actual[i];
+					if ((o1!=o2)&&
+						((o1==null)||(o2==null)||
+						 (!o1.equals(o2)))) {
+						fail(formatMsg((message==null ? "" : (message+": "))  //$NON-NLS-1$//$NON-NLS-2$
+								+"not same arrays, expected element:", //$NON-NLS-1$
+								o1, o2));
+					}
+				}
+			}
+		}
+	}	
+
+	/** Test if the given method entity throws the specified exception on the given entity.
+	 * 
+	 * @param expectedException
+	 * @param object
+	 * @param methodName
+	 * @param values are the values to pass to the methods.
+	 */
+	protected static void assertException(Class<? extends Throwable> expectedException,
+			Object object, String methodName, Object... values) {
+		assertException(null, expectedException, object, methodName, values);
+	}
+
+	/** Test if the given method entity throws the specified exception on the given entity.
+	 * 
+	 * @param message
+	 * @param expectedException
+	 * @param object
+	 * @param methodName
+	 * @param values are the values to pass to the methods.
+	 */
+	protected static void assertException(String message, Class<? extends Throwable> expectedException,
+			Object object, String methodName, Object... values) {
+		Class<?>[] types = new Class<?>[values.length];
+		for(int idx=0; idx<types.length; idx++) {
+			types[idx] = values[idx].getClass();
+		}
+		assertException(message, expectedException, object, methodName, types, values);
+	}
+
+	/** Test if the given method entity throws the specified exception on the given entity.
+	 * 
+	 * @param expectedException
+	 * @param object
+	 * @param methodName
+	 * @param types are the types of the method parameters.
+	 * @param values are the values to pass to the methods.
+	 */
+	protected static void assertException(Class<? extends Throwable> expectedException,
+			Object object, String methodName, Class<?>[] types, Object... values) {
+		assertException(null, expectedException, object, methodName, types, values);
+	}
+
+	/** Test if the given method entity throws the specified exception on the given entity.
+	 * 
+	 * @param message
+	 * @param expectedException
+	 * @param object
+	 * @param methodName
+	 * @param types are the types of the method parameters.
+	 * @param values are the values to pass to the methods.
+	 */
+	protected static void assertException(String message, Class<? extends Throwable> expectedException,
+			Object object, String methodName, Class<?>[] types, Object... values) {
+		assert(object!=null);
+		Class<?> objType;
+		Object obj = object;
+		if (obj instanceof Class<?>) {
+			objType = (Class<?>)obj;
+			obj = null;
+		}
+		else {
+			objType = obj.getClass();
+		}
+		Method method = null;
+		Throwable t = null;
+		try {
+			method = objType.getMethod(methodName, types);
+			if (method==null) {
+				fail((message!=null?(message+", ") //$NON-NLS-1$
+						:"") //$NON-NLS-1$
+						+"unable to find the method "+methodName); //$NON-NLS-1$
+				return;
+			}
+		}
+		catch(Exception _) {
+			fail((message!=null?(message+", ") //$NON-NLS-1$
+					:"") //$NON-NLS-1$
+					+"unable to find the method "+methodName); //$NON-NLS-1$
+			return;
+		}
+		try {
+			method.invoke(obj, values);
+		}
+		catch(InvocationTargetException e) {
+			if (expectedException.equals(e.getCause().getClass())) return;
+			t = e.getCause();
+		}
+		catch(Throwable e) {
+			if (expectedException.equals(e.getClass())) return;
+			t = e;
+		}
+		if (t!=null) {
+			fail((message!=null?(message+", ") //$NON-NLS-1$
+					:"") //$NON-NLS-1$
+					+"the method "+methodName //$NON-NLS-1$
+					+" does not thrown the expected exception of type "+expectedException //$NON-NLS-1$
+					+". An exception of type "+t.getClass().getName()+" is thrown insteed."); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		else {
+			fail((message!=null?(message+", ") //$NON-NLS-1$
+					:"") //$NON-NLS-1$
+					+"the method "+methodName //$NON-NLS-1$
+					+" does not thrown the expected exception of type "+expectedException //$NON-NLS-1$
+					+". No exception was thrown insteed."); //$NON-NLS-1$
+		}
+	}
+
+	/** Test if the value is strictly negative.
+	 * 
+	 * @param value
+	 */
+	protected static void assertStrictlyNegative(double value) {
+		assertStrictlyNegative(null, value);
+	}
+
+	/** Test if the value is strictly negative.
+	 * 
+	 * @param message
+	 * @param value
+	 */
+	protected static void assertStrictlyNegative(String message, double value) {
+		if (value>=0) {
+			StringBuilder msg = new StringBuilder();
+			if (message!=null) msg.append(message);
+			if (msg.length()>0) msg.append(' ');
+			msg.append("the value is expected to be stricly negative, but it is equals to "); //$NON-NLS-1$
+			msg.append(value);
+			fail(msg.toString());
+		}
+	}
+	
+	/** Test if the value is strictly positive.
+	 * 
+	 * @param value
+	 */
+	protected static void assertStrictlyPositive(double value) {
+		assertStrictlyPositive(null, value);
+	}
+
+	/** Test if the value is strictly positive.
+	 * 
+	 * @param message
+	 * @param value
+	 */
+	protected static void assertStrictlyPositive(String message, double value) {
+		if (value<=0) {
+			StringBuilder msg = new StringBuilder();
+			if (message!=null) msg.append(message);
+			if (msg.length()>0) msg.append(' ');
+			msg.append("the value is expected to be stricly positive, but it is equals to "); //$NON-NLS-1$
+			msg.append(value);
+			fail(msg.toString());
+		}
+	}
+
+	/** Test if the value is negative or zero.
+	 * 
+	 * @param value
+	 */
+	protected static void assertNegative(double value) {
+		assertNegative(null, value);
+	}
+
+	/** Test if the value is negative or zero.
+	 * 
+	 * @param message
+	 * @param value
+	 */
+	protected static void assertNegative(String message, double value) {
+		if (value>0) {
+			StringBuilder msg = new StringBuilder();
+			if (message!=null) msg.append(message);
+			if (msg.length()>0) msg.append(' ');
+			msg.append("the value is expected to be negative or nul, but it is equals to "); //$NON-NLS-1$
+			msg.append(value);
+			fail(msg.toString());
+		}
+	}
+	
+	/** Test if the value is positive or zero.
+	 * 
+	 * @param value
+	 */
+	protected static void assertPositive(double value) {
+		assertPositive(null, value);
+	}
+
+	/** Test if the value is positive or zero.
+	 * 
+	 * @param message
+	 * @param value
+	 */
+	protected static void assertPositive(String message, double value) {
+		if (value<0) {
+			StringBuilder msg = new StringBuilder();
+			if (message!=null) msg.append(message);
+			if (msg.length()>0) msg.append(' ');
+			msg.append("the value is expected to be positive or nul, but it is equals to "); //$NON-NLS-1$
+			msg.append(value);
+			fail(msg.toString());
+		}
+	}
+
+	/** Test if the two collections contain the same elements without
+	 * taking into account the order of the elements in the collections.
+	 *
+	 * @param <T>
+	 * @param expected
+	 * @param actual
+	 */
+	protected static <T> void assertEpsilonEquals(T[] expected, T[] actual) {
+		assertEpsilonEquals(null, expected, actual);
+	}
+
+	/** Test if the two collections contain the same elements without
+	 * taking into account the order of the elements in the collections.
+	 *
+	 * @param <T>
+	 * @param message
+	 * @param expected
+	 * @param actual
+	 */
+	protected static <T> void assertEpsilonEquals(String message, T[] expected, T[] actual) {
+		ArrayList<T> l = new ArrayList<T>(Arrays.asList(actual));
+		for(T e : expected) {
+			if (!l.remove(e)) {
+				fail((message==null ? "" : (message+": "))  //$NON-NLS-1$//$NON-NLS-2$
+						+"not similar collections, expected element:"+ //$NON-NLS-1$
+						expected.toString());
+			}
+		}
+		if (!l.isEmpty()) {
+			fail((message==null ? "" : (message+": "))  //$NON-NLS-1$//$NON-NLS-2$
+					+"not similar collections, not expected elements:"+ //$NON-NLS-1$
+					l.toString());
+		}
+	}
+
+	/** Test if the two collections contain the same elements without
+	 * taking into account the order of the elements in the collections.
+	 * 
+	 * @param <T>
+	 * @param expected
+	 * @param actual
+	 */
+	protected static <T> void assertEpsilonEquals(Collection<? extends T> expected, Collection<? extends T> actual) {
+		assertEpsilonEquals(null, expected, actual);
+	}
+
+	/** Test if the two collections contain the same elements without
+	 * taking into account the order of the elements in the collections.
+	 *
+	 * @param <T>
+	 * @param message
+	 * @param expected
+	 * @param actual
+	 */
+	protected static <T> void assertEpsilonEquals(String message, Collection<? extends T> expected, Collection<? extends T> actual) {
+		ArrayList<T> l = new ArrayList<T>(actual);
+		for(T e : expected) {
+			if (!l.remove(e)) {
+				fail((message==null ? "" : (message+": "))  //$NON-NLS-1$//$NON-NLS-2$
+						+"not similar collections, expected element:"+ //$NON-NLS-1$
+						expected);
+			}
+		}
+		if (!l.isEmpty()) {
+			fail((message==null ? "" : (message+": "))  //$NON-NLS-1$//$NON-NLS-2$
+					+"not similar collections, not expected elements:"+ //$NON-NLS-1$
+					l);
+		}
+	}
+
+	/**
+	 */
+	public void testIddle() {
+		//
+	}	
+
+}

Added: trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeComparatorTest.java
===================================================================
--- trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeComparatorTest.java	                        (rev 0)
+++ trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeComparatorTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,147 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+import org.arakhne.afc.attrs.AbstractAttrTestCase;
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeComparator;
+import org.arakhne.afc.attrs.attr.AttributeException;
+import org.arakhne.afc.attrs.attr.AttributeImpl;
+import org.arakhne.afc.attrs.attr.AttributeValueImpl;
+
+/**
+ * Test of AttributeComparator.
+ * 
+ * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AttributeComparatorTest extends AbstractAttrTestCase {
+
+	/**
+	 * @throws AttributeException
+	 */
+	public static void testCompareValues() throws AttributeException {
+		double base_d = Math.random();
+		long base_l = (long)base_d;
+		AttributeValueImpl attr1 = new AttributeValueImpl(base_d);		
+		AttributeValueImpl attr2 = new AttributeValueImpl(base_d+1);
+		AttributeValueImpl attr3 = new AttributeValueImpl(base_d-1);
+		
+		AttributeValueImpl attr4 = new AttributeValueImpl(base_l);		
+		AttributeValueImpl attr5 = new AttributeValueImpl(attr1.getInteger());
+		
+		AttributeValueImpl attr6 = new AttributeValueImpl("bonjour"); //$NON-NLS-1$
+		
+		AttributeComparator comp = new AttributeComparator();
+			
+		//----------- attr1 -> *
+		assertEquals(0, comp.compare(attr1,attr1));
+		assertStrictlyNegative(comp.compare(attr1,attr2));
+		assertStrictlyPositive(comp.compare(attr1,attr3));
+		assertPositive(comp.compare(attr1,attr4));
+		assertPositive(comp.compare(attr1,attr5));
+		assertStrictlyNegative(comp.compare(attr1,attr6));
+
+		//----------- attr2 -> *
+		assertStrictlyPositive(comp.compare(attr2,attr1));
+		assertEquals(0, comp.compare(attr2,attr2));
+		assertStrictlyPositive(comp.compare(attr2,attr3));
+		assertStrictlyPositive(comp.compare(attr2,attr4));
+		assertStrictlyPositive(comp.compare(attr2,attr5));
+		assertStrictlyNegative(comp.compare(attr2,attr6));
+
+		//----------- attr3 -> *
+		assertStrictlyNegative(comp.compare(attr3,attr1));
+		assertStrictlyNegative(comp.compare(attr3,attr2));
+		assertEquals(0, comp.compare(attr3,attr3));
+		assertStrictlyNegative(comp.compare(attr3,attr4));
+		assertStrictlyNegative(comp.compare(attr3,attr5));
+		assertStrictlyNegative(comp.compare(attr3,attr6));
+		
+
+		//----------- attr4 -> *
+		assertNegative(comp.compare(attr4,attr1));
+		assertStrictlyNegative(comp.compare(attr4,attr2));
+		assertStrictlyPositive(comp.compare(attr4,attr3));
+		assertEquals(0, comp.compare(attr4,attr4));
+		assertEquals(0,comp.compare(attr4,attr5));
+		assertStrictlyNegative(comp.compare(attr4,attr6));
+
+		//----------- attr5 -> *
+		assertNegative(comp.compare(attr5,attr1));
+		assertStrictlyNegative(comp.compare(attr5,attr2));
+		assertStrictlyPositive(comp.compare(attr5,attr3));
+		assertEquals(0,comp.compare(attr5,attr4));
+		assertEquals(0, comp.compare(attr5,attr5));
+		assertStrictlyNegative(comp.compare(attr5,attr6));
+
+		//----------- attr6 -> *
+		assertStrictlyPositive(comp.compare(attr6,attr1));
+		assertStrictlyPositive(comp.compare(attr6,attr2));
+		assertStrictlyPositive(comp.compare(attr6,attr3));
+		assertStrictlyPositive(comp.compare(attr6,attr4));
+		assertStrictlyPositive(comp.compare(attr6,attr5));
+		assertEquals(0, comp.compare(attr6,attr6));
+	}
+
+	/**
+	 */
+	public static void testCompare() {
+		AttributeComparator comp = new AttributeComparator();
+		
+		for(int i=5; i<50; ++i) {
+			String name1 = randomString();
+			String name2 = randomString();
+			String msg = "("+name1+"<=>"+name2+")";  //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
+			int cmpResult = name1.compareTo(name2);
+			
+			Attribute attr1 = new AttributeImpl(name1,1);
+			Attribute attr2 = new AttributeImpl(name2,1);
+			Attribute attr3 = new AttributeImpl(name1,1);
+			Attribute attr4 = new AttributeImpl(name1,2);
+			
+			assertEquals(msg,0,comp.compare(attr1, attr1));
+			assertEquals(msg,cmpResult,comp.compare(attr1, attr2));
+			assertEquals(msg,0,comp.compare(attr1, attr3));
+			assertStrictlyNegative(msg,comp.compare(attr1, attr4));
+			
+			assertEquals(msg,-cmpResult,comp.compare(attr2, attr1));
+			assertEquals(msg,0,comp.compare(attr2, attr2));
+			assertEquals(msg,-cmpResult,comp.compare(attr2, attr3));
+			assertEquals(msg,-cmpResult,comp.compare(attr2, attr4));
+
+			assertEquals(msg,0,comp.compare(attr3, attr1));
+			assertEquals(msg,cmpResult,comp.compare(attr3, attr2));
+			assertEquals(msg,0,comp.compare(attr3, attr3));
+			assertStrictlyNegative(msg,comp.compare(attr3, attr4));
+
+			assertStrictlyPositive(msg,comp.compare(attr4, attr1));
+			assertEquals(msg,cmpResult,comp.compare(attr4, attr2));
+			assertStrictlyPositive(msg,comp.compare(attr4, attr3));
+			assertEquals(msg,0,comp.compare(attr4, attr4));
+		}
+	}
+
+}

Added: trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeTest.java
===================================================================
--- trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeTest.java	                        (rev 0)
+++ trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,1125 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Random;
+
+import org.arakhne.afc.attrs.AbstractAttrTestCase;
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeException;
+import org.arakhne.afc.attrs.attr.AttributeImpl;
+import org.arakhne.afc.attrs.attr.AttributeNotInitializedException;
+import org.arakhne.afc.attrs.attr.AttributeType;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+import org.arakhne.afc.attrs.attr.AttributeValueImpl;
+import org.arakhne.afc.attrs.attr.InvalidAttributeTypeException;
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object3d.Point3f;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Point3D;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Image;
+import org.arakhne.afc.ui.vector.VectorToolkit;
+
+/**
+ * Test of Attribute.
+ * 
+ * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AttributeTest extends AbstractAttrTestCase {
+
+	/**
+	 * @param attr
+	 * @param type
+	 */
+	protected static void assertAllGetFailed(AttributeValue attr, AttributeType type) {
+		try {
+			attr.getValue();
+			fail("getValue: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getBoolean();
+			fail("getBoolean: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getColor();
+			fail("getColor: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getDate();
+			fail("getDate: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getImage();
+			fail("getImage: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getInteger();
+			fail("getInteger: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getJavaObject();
+			if (type.isBaseType())
+				fail("getJavaObject: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeNotInitializedException _) {
+			// expected case
+		}
+		catch(InvalidAttributeTypeException _) {
+			if (attr.isObjectValue())
+				fail("getJavaObject: unexpected exception InvalidAttributeTypeException for "+type); //$NON-NLS-1$
+		}
+
+		try {
+			attr.getPoint();
+			fail("getPoint: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getPoint3D();
+			fail("getPoint3D: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getPoint3D();
+			fail("getPoint3D: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getPolyline3D();
+			fail("getPolyline3D: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getReal();
+			fail("getReal: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getString();
+			fail("getString: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeNotInitializedException _) {
+			// expected case
+		}
+		catch(InvalidAttributeTypeException _) {
+			if (!attr.isObjectValue())
+				fail("getString: unexpected exception InvalidAttributeTypeException for "+type); //$NON-NLS-1$
+		}
+
+		try {
+			attr.getTimestamp();
+			fail("getTimestamp: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getURI();
+			fail("getURI: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getURL();
+			fail("getURL: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getUUID();
+			fail("getUUID: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+}
+
+	/**
+	 * @param attr
+	 * @param methodName
+	 * @throws Exception
+	 */
+	protected static void assertAttributeException(AttributeValue attr, String methodName) throws Exception {
+		try {
+			Class<? extends AttributeValue> clazz = attr.getClass();
+			Method method = clazz.getMethod(methodName);
+			method.invoke(attr);
+			fail("the exception AttributeException was not thrown"); //$NON-NLS-1$
+		}
+		catch(InvocationTargetException e) {
+			Throwable ex = e.getTargetException();
+			if (ex instanceof AttributeException) {
+				//
+			}
+			else {
+				fail("the exception AttributeException was not thrown"); //$NON-NLS-1$
+			}
+		}
+	}
+
+	
+	/**
+	 */
+	public static void testAttributeImpl() {
+		Attribute attr = new AttributeImpl();
+		
+		assertFalse(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(AttributeType.OBJECT, attr.getType());
+		
+		try {
+			attr.getValue();
+			fail("the exception AttributeNotInitializedException was not thrown"); //$NON-NLS-1$
+		}
+		catch(AttributeNotInitializedException _) {
+			// expected case
+		}
+		catch(InvalidAttributeTypeException _) {
+			fail("unexpected exception InvalidAttributeTypeException"); //$NON-NLS-1$
+		}
+
+		try {
+			attr.getBoolean();
+			fail("the exception AttributeNotInitializedException was not thrown"); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplAttributeType() throws Exception {
+		AttributeType[] values = AttributeType.values();
+		//AttributeType[] values = new AttributeType[] {AttributeType.OBJECT};
+		for (AttributeType type : values) {
+			Attribute attr = new AttributeImpl(type);
+			
+			assertEquals(type, attr.getType());
+
+			assertFalse(attr.isAssigned());
+			assertEquals(type.isBaseType(),attr.isBaseType());
+			assertEquals("on type "+type, //$NON-NLS-1$
+					!type.isBaseType(),
+					attr.isObjectValue());
+			
+			if (type.isNullAllowed()) {
+				assertAttributeException(attr, "getBoolean"); //$NON-NLS-1$
+				assertAttributeException(attr, "getColor"); //$NON-NLS-1$
+				assertAttributeException(attr, "getDate"); //$NON-NLS-1$
+				assertAttributeException(attr, "getImage"); //$NON-NLS-1$
+				assertAttributeException(attr, "getInteger"); //$NON-NLS-1$
+				assertNull(attr.getJavaObject());
+				assertAttributeException(attr, "getPoint"); //$NON-NLS-1$
+				assertAttributeException(attr, "getPoint3D"); //$NON-NLS-1$
+				assertAttributeException(attr, "getPolyline"); //$NON-NLS-1$
+				assertAttributeException(attr, "getPolyline3D"); //$NON-NLS-1$
+				assertAttributeException(attr, "getReal"); //$NON-NLS-1$
+				assertAttributeException(attr, "getString"); //$NON-NLS-1$
+				assertAttributeException(attr, "getTimestamp"); //$NON-NLS-1$
+				assertAttributeException(attr, "getURI"); //$NON-NLS-1$
+				assertAttributeException(attr, "getURL"); //$NON-NLS-1$
+				assertAttributeException(attr, "getUUID"); //$NON-NLS-1$
+				assertAttributeException(attr, "getValue"); //$NON-NLS-1$
+			}
+			else {
+				assertAllGetFailed(attr, type);
+			}
+		}
+	}
+	
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplBoolean() throws Exception {
+		Attribute attr = new AttributeImpl(randomString(),false);
+		
+		assertEquals(AttributeType.BOOLEAN, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertFalse((Boolean)attr.getValue());
+		assertFalse(attr.getBoolean());
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals(0, attr.getInteger());
+		assertEquals(0., attr.getReal());
+		assertEquals(0, attr.getTimestamp());
+		assertEquals(Boolean.toString(false),attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint3D"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplColor() throws Exception {
+		String txt = "255;0;0;255"; //$NON-NLS-1$
+		Attribute attr = new AttributeImpl(randomString(),VectorToolkit.RED);
+		
+		assertEquals(AttributeType.COLOR, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(VectorToolkit.RED,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertEquals(VectorToolkit.RED,attr.getColor());
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals(VectorToolkit.RED.getRGB(), attr.getInteger());
+		assertEquals((double)VectorToolkit.RED.getRGB(), attr.getReal());
+		assertEquals(VectorToolkit.RED.getRGB(), attr.getTimestamp());
+		assertEquals(txt,attr.getString());
+		assertEquals(VectorToolkit.color(255,0,0), attr.getJavaObject());
+		assertEquals(new Point2f(255,0),attr.getPoint());
+		assertEquals(new Point3f(255,0,0),attr.getPoint3D());
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplDate() throws Exception {
+		Date currentDate = new Date();
+		SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); //$NON-NLS-1$
+		String txt = fmt.format(currentDate);
+		Attribute attr = new AttributeImpl(randomString(),currentDate);
+		
+		assertEquals(AttributeType.DATE, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(currentDate,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertEquals(VectorToolkit.color((int)currentDate.getTime()), attr.getColor());
+		assertEquals(currentDate,attr.getDate());
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals(currentDate.getTime(),attr.getInteger());
+		assertEquals((double)currentDate.getTime(),attr.getReal());
+		assertEquals(currentDate.getTime(),attr.getTimestamp());
+		assertEquals(txt,attr.getString());
+		assertEquals(currentDate, attr.getJavaObject());
+		assertEquals(new Point2f(currentDate.getTime(), 0), attr.getPoint());
+		assertEquals(new Point3f(currentDate.getTime(), 0, 0), attr.getPoint3D());
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplFloat() throws Exception {
+		float nb = (float)Math.random();
+		String txt = Double.toString(nb);
+		Attribute attr = new AttributeImpl(randomString(),nb);
+		
+		assertEquals(AttributeType.REAL, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(nb,((Number)attr.getValue()).floatValue());
+		assertEquals(nb!=0f, attr.getBoolean());
+		assertEquals(VectorToolkit.color((int)nb), attr.getColor());
+		assertEquals(new Date((long)nb),attr.getDate());
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals((long)nb,attr.getInteger());
+		assertEquals((double)nb,attr.getReal());
+		assertEquals((long)nb,attr.getTimestamp());
+		assertEquals(txt,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(new Point2f(nb,0),attr.getPoint());
+		assertEquals(new Point3f(nb,0,0),attr.getPoint3D());
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplDouble() throws Exception {
+		/**
+		 * @throws Exception
+		 */
+		double nb = Math.random();
+		String txt = Double.toString(nb);
+		Attribute attr = new AttributeImpl(randomString(),nb);
+		
+		assertEquals(AttributeType.REAL, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(nb,((Number)attr.getValue()).doubleValue());
+		assertEquals(nb!=0., attr.getBoolean());
+		assertEquals(VectorToolkit.color((int)nb), attr.getColor());
+		assertEquals(new Date((long)nb),attr.getDate());
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals((long)nb,attr.getInteger());
+		assertEquals(nb,attr.getReal());
+		assertEquals((long)nb,attr.getTimestamp());
+		assertEquals(txt,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(new Point2f(nb,0),attr.getPoint());
+		assertEquals(new Point3f(nb,0,0),attr.getPoint3D());
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplIcon() throws Exception {
+		Image ic = VectorToolkit.image(1,1,false);
+		Attribute attr = new AttributeImpl(randomString(),ic);
+		
+		assertEquals(AttributeType.IMAGE, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(ic,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertEquals(ic,attr.getImage());
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(ic.toString(), attr.getString());
+		assertEquals(ic,attr.getJavaObject());
+		assertAttributeException(attr,"getPoint"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint3D"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplInt() throws Exception {
+		int nb = new Random().nextInt();
+		String txt = Long.toString(nb);
+		Attribute attr = new AttributeImpl(randomString(),nb);
+		
+		assertEquals(AttributeType.INTEGER, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(nb,((Number)attr.getValue()).intValue());
+		assertEquals(nb!=0, attr.getBoolean());
+		assertEquals(VectorToolkit.color(nb), attr.getColor());
+		assertEquals(new Date(nb),attr.getDate());
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals(nb,attr.getInteger());
+		assertEquals(nb,(int)attr.getReal());
+		assertEquals(nb,attr.getTimestamp());
+		assertEquals(txt,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(new Point2f(nb,0),attr.getPoint());
+		assertEquals(new Point3f(nb,0,0),attr.getPoint3D());
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplLong() throws Exception {
+		long nb = new Random().nextLong();
+		String txt = Long.toString(nb);
+		Attribute attr = new AttributeImpl(randomString(),nb);
+		
+		assertEquals(AttributeType.INTEGER, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(nb,((Number)attr.getValue()).longValue());
+		assertEquals(nb!=0, attr.getBoolean());
+		assertEquals(VectorToolkit.color((int)nb), attr.getColor());
+		assertEquals(new Date(nb),attr.getDate());
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals(nb,attr.getInteger());
+		assertEquals((double)nb,attr.getReal());
+		assertEquals(nb,attr.getTimestamp());
+		assertEquals(txt,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(new Point2f(nb,0),attr.getPoint());
+		assertEquals(new Point3f(nb,0,0),attr.getPoint3D());
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplPoint2d() throws Exception {
+		Point2D pt = new Point2f(Math.random(),Math.random());
+		Point3D pt3d = new Point3f(pt.getX(),pt.getY(),0.);
+		String str = pt.getX()+";"+pt.getY(); //$NON-NLS-1$
+		Attribute attr = new AttributeImpl(randomString(),pt);
+		
+		assertEquals(AttributeType.POINT, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(pt,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertEquals(VectorToolkit.color(pt.getX(),pt.getY(),0f),attr.getColor());
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertEquals(pt, attr.getJavaObject());
+		assertEquals(pt,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+	
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplDoubleDouble() throws Exception {
+		double x = Math.random();
+		double y = Math.random();
+		Point2D pt = new Point2f(x,y);
+		Point3D pt3d = new Point3f(x,y,0.);
+		String str = ((float)x)+";"+((float)y); //$NON-NLS-1$
+		Attribute attr = new AttributeImpl(randomString(),x,y);
+		
+		assertEquals(AttributeType.POINT, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(pt,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertEquals(VectorToolkit.color(pt.getX(),pt.getY(),0f),attr.getColor());
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertEquals(pt, attr.getJavaObject());
+		assertEquals(pt,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+	
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplPoint3d() throws Exception {
+		double x = Math.random();
+		double y = Math.random();
+		double z = Math.random();
+		Point3D pt = new Point3f(x,y,z);
+		Point2D pt2d = new Point2f(x,y);
+		String str = ((float)x)+";"+((float)y)+";"+((float)z); //$NON-NLS-1$ //$NON-NLS-2$
+		Attribute attr = new AttributeImpl(randomString(),pt);
+		
+		assertEquals(AttributeType.POINT3D, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(pt,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertEquals(VectorToolkit.color(pt.getX(),pt.getY(),pt.getZ()),attr.getColor());
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertEquals(pt, attr.getJavaObject());
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt2d},attr.getPolyline());
+		assertEquals(new Point3D[]{pt},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplDoubleDoubleDouble() throws Exception {
+		double x = Math.random();
+		double y = Math.random();
+		double z = Math.random();
+		Point3D pt = new Point3f(x,y,z);
+		Point2D pt2d = new Point2f(x,y);
+		String str = ((float)x)+";"+((float)y)+";"+((float)z); //$NON-NLS-1$ //$NON-NLS-2$
+		Attribute attr = new AttributeImpl(randomString(),x,y,z);
+		
+		assertEquals(AttributeType.POINT3D, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(pt,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertEquals(VectorToolkit.color(pt.getX(),pt.getY(),pt.getZ()),attr.getColor());
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertEquals(pt, attr.getJavaObject());
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt2d},attr.getPolyline());
+		assertEquals(new Point3D[]{pt},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplString_random() throws Exception {
+		double x = Math.random();
+		Point2D pt2d = new Point2f(x,0);
+		Point3D pt3d = new Point3f(x,0,0);
+		String str = Double.toHexString(x);
+		Attribute attr = new AttributeImpl(randomString(),str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertEquals(x,attr.getReal());
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(new Point2D[]{pt2d},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplString_boolean() throws Exception {
+		String str = Boolean.toString(true);
+		Attribute attr = new AttributeImpl(randomString(),str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertTrue(attr.getBoolean());
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint3D"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplString_Color() throws Exception {
+		Color c = VectorToolkit.RED;
+		Point2D pt2d = new Point2f(c.getRed(),c.getGreen());
+		Point2D pt2d2 = new Point2f(c.getBlue(),0);
+		Point3D pt3d = new Point3f(c.getRed(),c.getGreen(),c.getBlue());
+		String str = c.getRed()+";"+c.getGreen()+";"+c.getBlue();  //$NON-NLS-1$//$NON-NLS-2$
+		Attribute attr = new AttributeImpl(randomString(),str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertEquals(c,attr.getColor());
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt2d,pt2d2},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplString_Date() throws Exception {
+		Date currentDate = new Date();
+		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); //$NON-NLS-1$
+		String str = format.format(currentDate); 
+		Attribute attr = new AttributeImpl(randomString(),str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertEpsilonEquals(currentDate,attr.getDate());
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint3D"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplString_JDate() throws Exception {
+		Date currentDate = new Date();
+		String str = DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.FULL).format(currentDate); 
+		Attribute attr = new AttributeImpl(randomString(),str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertEpsilonEquals(currentDate,attr.getDate());
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint3D"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplString_Integer() throws Exception {
+		int nb = new Random().nextInt(20000)+256;
+		Point2D pt2d = new Point2f(nb,0);
+		Point3D pt3d = new Point3f(nb,0,0);
+		String str = Integer.toString(nb);
+		Attribute attr = new AttributeImpl(randomString(),str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals(nb,attr.getInteger());
+		assertEquals((double)nb,attr.getReal());
+		assertEquals(nb,attr.getTimestamp());
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt2d},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplString_Long() throws Exception {
+		long nb = new Random().nextInt(20000)+256;
+		Point2D pt2d = new Point2f(nb,0);
+		Point3D pt3d = new Point3f(nb,0,0);
+		String str = Long.toString(nb);
+		Attribute attr = new AttributeImpl(randomString(),str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals(nb,attr.getInteger());
+		assertEquals((double)nb,attr.getReal());
+		assertEquals(nb,attr.getTimestamp());
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt2d},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplString_Double() throws Exception {
+		double nb = Math.random()+256;
+		Point2D pt2d = new Point2f(nb,0);
+		Point3D pt3d = new Point3f(nb,0,0);
+		String str = Double.toString(nb);
+		Attribute attr = new AttributeImpl(randomString(),str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertEquals(nb,attr.getReal());
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt2d},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplString_Point2D() throws Exception {
+		double x = Math.random()+256;
+		double y = Math.random()+256;
+		Point2D pt2d = new Point2f(x,y);
+		Point3D pt3d = new Point3f(x,y,0);
+		String str = x+";"+y; //$NON-NLS-1$
+		Attribute attr = new AttributeImpl(randomString(),str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt2d},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplString_Point3D() throws Exception {
+		double x = Math.random()+256;
+		double y = Math.random()+256;
+		double z = Math.random()+256;
+		Point2D pt2d = new Point2f(x,y);
+		Point2D pt2d2 = new Point2f(z,0);
+		Point3D pt3d = new Point3f(x,y,z);
+		String str = x+";"+y+";"+z; //$NON-NLS-1$ //$NON-NLS-2$
+		Attribute attr = new AttributeImpl(randomString(),str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt2d,pt2d2},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplPoint2DArray() throws Exception {
+		double x1 = Math.random();
+		double y1 = Math.random();
+		double x2 = Math.random();
+		double y2 = Math.random();
+
+		Point2D pt1 = new Point2f(x1,y1);
+		Point2D pt2 = new Point2f(x2,y2);
+		
+		Point2D[] list = new Point2D[]{ pt1, pt2 };
+		Point3D[] list2 = new Point3D[]{ new Point3f(x1,y1,0), new Point3f(x2,y2,0) };
+
+		String str = ((float)x1)+";"+((float)y1)+";"+((float)x2)+";"+((float)y2); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+		Attribute attr = new AttributeImpl(randomString(),list);
+		
+		assertEquals(AttributeType.POLYLINE, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(list,(Point2D[])attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertEquals(list, attr.getJavaObject());
+		assertAttributeException(attr,"getPoint"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint3D"); //$NON-NLS-1$
+		assertEquals(list,attr.getPolyline());
+		assertEquals(list2,attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeImplPoint3DArray() throws Exception {
+		double x1 = Math.random();
+		double y1 = Math.random();
+		double z1 = Math.random();
+		double x2 = Math.random();
+		double y2 = Math.random();
+		double z2 = Math.random();
+
+		Point3D pt1 = new Point3f(x1,y1,z1);
+		Point3D pt2 = new Point3f(x2,y2,z2);
+		
+		Point3D[] list = new Point3D[]{ pt1, pt2 };
+		Point2D[] list2 = new Point2D[]{ new Point2f(x1,y1), new Point2f(x2,y2) };
+
+		String str = ((float)x1)+";"+((float)y1)+";"+((float)z1)+";"+((float)x2)+";"+((float)y2)+";"+((float)z2); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+
+		Attribute attr = new AttributeImpl(randomString(),list);
+		
+		assertEquals(AttributeType.POLYLINE3D, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(list,(Point3D[])attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertEquals(list, attr.getJavaObject());
+		assertAttributeException(attr,"getPoint"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint3D"); //$NON-NLS-1$
+		assertEquals(list2,attr.getPolyline());
+		assertEquals(list,attr.getPolyline3D());
+	}
+
+	/**
+	 */
+	public static void testEquals() {
+		AttributeImpl attr = new AttributeImpl("A1"); //$NON-NLS-1$
+		
+		attr.setBoolean(true);
+		assertTrue(attr.equals(new AttributeImpl("A1", true))); //$NON-NLS-1$
+		assertFalse(attr.equals(new AttributeImpl("A1", false))); //$NON-NLS-1$
+		assertFalse(attr.equals(new AttributeImpl("A2", true))); //$NON-NLS-1$
+		assertFalse(attr.equals(new AttributeImpl("A2", false))); //$NON-NLS-1$
+		assertTrue(attr.equals(new AttributeValueImpl(true)));
+		assertFalse(attr.equals(new AttributeValueImpl(false)));
+		assertTrue(attr.equals(true));
+		assertFalse(attr.equals(false));
+		assertTrue(attr.equals(new AttributeValueImpl("true"))); //$NON-NLS-1$
+		assertFalse(attr.equals(new AttributeValueImpl("false"))); //$NON-NLS-1$
+		assertTrue(attr.equals("true")); //$NON-NLS-1$
+		assertFalse(attr.equals("false")); //$NON-NLS-1$
+		assertFalse(attr.equals(new AttributeValueImpl(1.)));
+		assertFalse(attr.equals(new AttributeValueImpl("1."))); //$NON-NLS-1$
+		assertFalse(attr.equals(1.));
+		assertFalse(attr.equals("toto")); //$NON-NLS-1$
+
+		attr.setBoolean(false);
+		assertFalse(attr.equals(new AttributeImpl("A1", true))); //$NON-NLS-1$
+		assertTrue(attr.equals(new AttributeImpl("A1", false))); //$NON-NLS-1$
+		assertFalse(attr.equals(new AttributeImpl("A2", true))); //$NON-NLS-1$
+		assertFalse(attr.equals(new AttributeImpl("A2", false))); //$NON-NLS-1$
+		assertFalse(attr.equals(new AttributeValueImpl(true)));
+		assertTrue(attr.equals(new AttributeValueImpl(false)));
+		assertFalse(attr.equals(true));
+		assertTrue(attr.equals(false));
+		assertFalse(attr.equals(new AttributeValueImpl("true"))); //$NON-NLS-1$
+		assertTrue(attr.equals(new AttributeValueImpl("false"))); //$NON-NLS-1$
+		assertFalse(attr.equals("true")); //$NON-NLS-1$
+		assertTrue(attr.equals("false")); //$NON-NLS-1$
+		assertFalse(attr.equals(new AttributeValueImpl(1.)));
+		assertFalse(attr.equals(new AttributeValueImpl("1."))); //$NON-NLS-1$
+		assertFalse(attr.equals(1.));
+		assertFalse(attr.equals("toto")); //$NON-NLS-1$
+	}
+	
+}

Added: trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeTypeTest.java
===================================================================
--- trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeTypeTest.java	                        (rev 0)
+++ trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeTypeTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,1194 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.UUID;
+
+import org.arakhne.afc.attrs.AbstractAttrTestCase;
+import org.arakhne.afc.attrs.attr.AttributeConstants;
+import org.arakhne.afc.attrs.attr.AttributeType;
+import org.arakhne.afc.attrs.attr.NullAttribute;
+import org.arakhne.afc.attrs.attr.Timestamp;
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object3d.Point3f;
+import org.arakhne.afc.math.discrete.object2d.Point2i;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Point3D;
+import org.arakhne.afc.math.generic.Tuple2D;
+import org.arakhne.afc.math.generic.Tuple3D;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Image;
+import org.arakhne.afc.ui.vector.VectorToolkit;
+
+/**
+ * Test of AttributeType.
+ * 
+ * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AttributeTypeTest extends AbstractAttrTestCase {
+
+	/** 
+	 * @param type
+	 * @param value
+	 */
+	protected static void assertCastException(AttributeType type, Object value) {
+		assertException(ClassCastException.class, type, "cast", new Class<?>[] {Object.class}, new Object[] {value}); //$NON-NLS-1$
+	}
+
+	/**
+	 * @param type
+	 * @param value
+	 */
+	protected static void assertNullException(AttributeType type, Object value) {
+		assertException(NullPointerException.class, type, "cast", new Class<?>[] {Object.class}, new Object[] {value}); //$NON-NLS-1$
+	}
+
+	/**
+	 */
+	public static void testGetName() {
+		for(AttributeType type : AttributeType.values()) {
+			String name = type.getName();
+			assertNotNull(name);
+			assertNotSame("", name); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 */
+	public static void testIsNumberType() {
+		for(AttributeType t : AttributeType.values()) {
+			assertEquals(t==AttributeType.INTEGER || t==AttributeType.REAL || t==AttributeType.TIMESTAMP,
+					t.isNumberType());
+			if (t.isNumberType()) assertTrue(t.isBaseType());
+		}
+	}
+
+	/**
+	 */
+	public static void testFromInteger() {
+		AttributeType[] types = AttributeType.values();
+		assertNotNull(types);
+		assertFalse(0==types.length);
+		for(int i=-1; i<types.length+10; ++i) {
+			AttributeType type = AttributeType.fromInteger(i);
+			assertNotNull(type);
+			if ((i<0)||(i>=types.length)) {
+				assertEquals(AttributeType.OBJECT, type);
+			}
+			else {
+				assertEquals(i,type.ordinal());
+				assertEquals(types[i],type);
+			}
+		}
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testFromValue() throws Exception {
+		assertEquals(AttributeType.OBJECT, AttributeType.fromValue(null));
+		assertEquals(AttributeType.BOOLEAN, AttributeType.fromValue(true));
+		assertEquals(AttributeType.STRING, AttributeType.fromValue('c'));
+		assertEquals(AttributeType.INTEGER, AttributeType.fromValue((byte)1));
+		assertEquals(AttributeType.INTEGER, AttributeType.fromValue((short)1));
+		assertEquals(AttributeType.INTEGER, AttributeType.fromValue(1));
+		assertEquals(AttributeType.INTEGER, AttributeType.fromValue(1l));
+		assertEquals(AttributeType.REAL, AttributeType.fromValue(1f));
+		assertEquals(AttributeType.REAL, AttributeType.fromValue(1.));
+		assertEquals(AttributeType.STRING, AttributeType.fromValue("Hello")); //$NON-NLS-1$
+		assertEquals(AttributeType.STRING, AttributeType.fromValue(new StringBuffer()));
+		assertEquals(AttributeType.STRING, AttributeType.fromValue(new StringBuilder()));
+		assertEquals(AttributeType.DATE, AttributeType.fromValue(Calendar.getInstance()));
+		assertEquals(AttributeType.DATE, AttributeType.fromValue(new Date()));
+		assertEquals(AttributeType.COLOR, AttributeType.fromValue(VectorToolkit.RED));
+		assertEquals(AttributeType.UUID, AttributeType.fromValue(UUID.randomUUID()));
+		assertEquals(AttributeType.POINT, AttributeType.fromValue(new Point2f(0,0)));
+		assertEquals(AttributeType.POINT, AttributeType.fromValue(new Point2i(0,0)));
+		assertEquals(AttributeType.POINT3D, AttributeType.fromValue(new Point3f(0,0,0)));
+		assertEquals(AttributeType.POLYLINE, AttributeType.fromValue(new Point2D[0]));
+		assertEquals(AttributeType.POLYLINE3D, AttributeType.fromValue(new Point3D[0]));
+		assertEquals(AttributeType.IMAGE, AttributeType.fromValue(VectorToolkit.image(1,1,false)));
+		assertEquals(AttributeType.UUID, AttributeType.fromValue(UUID.randomUUID()));
+		assertEquals(AttributeType.URL, AttributeType.fromValue(new URL("http://set.utbm.fr";))); //$NON-NLS-1$
+		assertEquals(AttributeType.URI, AttributeType.fromValue(new URI("http://set.utbm.fr";))); //$NON-NLS-1$
+		assertEquals(AttributeType.INET_ADDRESS, AttributeType.fromValue(InetAddress.getLocalHost()));
+		AttributeType randomType = randomEnum(AttributeType.class);
+		assertEquals(AttributeType.ENUMERATION, AttributeType.fromValue(randomType));
+		assertEquals(AttributeType.OBJECT, AttributeType.fromValue(new int[0]));
+		assertEquals(AttributeType.OBJECT, AttributeType.fromValue(Locale.getDefault()));
+		assertEquals(AttributeType.TYPE, AttributeType.fromValue(AttributeTypeTest.class));
+		assertEquals(AttributeType.TYPE, AttributeType.fromValue(double.class));
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testFromClass() throws Exception {
+		assertEquals(AttributeType.OBJECT, AttributeType.fromClass(null));
+		assertEquals(AttributeType.BOOLEAN, AttributeType.fromClass(boolean.class));
+		assertEquals(AttributeType.BOOLEAN, AttributeType.fromClass(Boolean.class));
+		assertEquals(AttributeType.STRING, AttributeType.fromClass(char.class));
+		assertEquals(AttributeType.STRING, AttributeType.fromClass(Character.class));
+		assertEquals(AttributeType.INTEGER, AttributeType.fromClass(byte.class));
+		assertEquals(AttributeType.INTEGER, AttributeType.fromClass(Byte.class));
+		assertEquals(AttributeType.INTEGER, AttributeType.fromClass(short.class));
+		assertEquals(AttributeType.INTEGER, AttributeType.fromClass(Short.class));
+		assertEquals(AttributeType.INTEGER, AttributeType.fromClass(int.class));
+		assertEquals(AttributeType.INTEGER, AttributeType.fromClass(Integer.class));
+		assertEquals(AttributeType.INTEGER, AttributeType.fromClass(long.class));
+		assertEquals(AttributeType.INTEGER, AttributeType.fromClass(Long.class));
+		assertEquals(AttributeType.REAL, AttributeType.fromClass(float.class));
+		assertEquals(AttributeType.REAL, AttributeType.fromClass(Float.class));
+		assertEquals(AttributeType.REAL, AttributeType.fromClass(double.class));
+		assertEquals(AttributeType.REAL, AttributeType.fromClass(Double.class));
+		assertEquals(AttributeType.STRING, AttributeType.fromClass(String.class));
+		assertEquals(AttributeType.STRING, AttributeType.fromClass(StringBuffer.class));
+		assertEquals(AttributeType.STRING, AttributeType.fromClass(StringBuilder.class));
+		assertEquals(AttributeType.DATE, AttributeType.fromClass(Calendar.class));
+		assertEquals(AttributeType.DATE, AttributeType.fromClass(Date.class));
+		assertEquals(AttributeType.COLOR, AttributeType.fromClass(Color.class));
+		assertEquals(AttributeType.UUID, AttributeType.fromClass(UUID.class));
+		assertEquals(AttributeType.POINT, AttributeType.fromClass(Point2D.class));
+		assertEquals(AttributeType.POINT, AttributeType.fromClass(Point2D.class));
+		assertEquals(AttributeType.POINT3D, AttributeType.fromClass(Point3D.class));
+		assertEquals(AttributeType.OBJECT, AttributeType.fromClass(Tuple2D[].class));
+		assertEquals(AttributeType.POLYLINE, AttributeType.fromClass(Point2D[].class));
+		assertEquals(AttributeType.OBJECT, AttributeType.fromClass(Tuple3D[].class));
+		assertEquals(AttributeType.POLYLINE3D, AttributeType.fromClass(Point3D[].class));
+		assertEquals(AttributeType.IMAGE, AttributeType.fromClass(Image.class));
+		assertEquals(AttributeType.UUID, AttributeType.fromClass(UUID.class));
+		assertEquals(AttributeType.URL, AttributeType.fromClass(URL.class));
+		assertEquals(AttributeType.URI, AttributeType.fromClass(URI.class));
+		assertEquals(AttributeType.INET_ADDRESS, AttributeType.fromClass(InetAddress.class));
+		assertEquals(AttributeType.ENUMERATION, AttributeType.fromClass(AttributeType.class));
+		assertEquals(AttributeType.OBJECT, AttributeType.fromClass(int[].class));
+		assertEquals(AttributeType.OBJECT, AttributeType.fromClass(Locale.class));
+		assertEquals(AttributeType.TYPE, AttributeType.fromClass(Class.class));
+	}
+
+	/**
+	 */
+	public static void testIsBaseType() {
+		assertTrue(AttributeType.BOOLEAN.isBaseType());
+		assertTrue(AttributeType.INTEGER.isBaseType());
+		assertTrue(AttributeType.REAL.isBaseType());
+		assertTrue(AttributeType.STRING.isBaseType());
+		assertTrue(AttributeType.TIMESTAMP.isBaseType());
+		
+		assertFalse(AttributeType.COLOR.isBaseType());
+		assertFalse(AttributeType.DATE.isBaseType());
+		assertFalse(AttributeType.IMAGE.isBaseType());
+		assertFalse(AttributeType.OBJECT.isBaseType());
+		assertFalse(AttributeType.POINT.isBaseType());
+		assertFalse(AttributeType.POINT3D.isBaseType());
+		assertFalse(AttributeType.POLYLINE.isBaseType());
+		assertFalse(AttributeType.POLYLINE3D.isBaseType());
+		assertFalse(AttributeType.URL.isBaseType());
+		assertFalse(AttributeType.URI.isBaseType());
+		assertFalse(AttributeType.UUID.isBaseType());
+		assertFalse(AttributeType.INET_ADDRESS.isBaseType());
+		assertFalse(AttributeType.ENUMERATION.isBaseType());
+		assertFalse(AttributeType.TYPE.isBaseType());
+	}
+	
+	/**
+	 */
+	public static void testIsNullAllowed() {
+		assertFalse(AttributeType.BOOLEAN.isNullAllowed());
+		assertFalse(AttributeType.INTEGER.isNullAllowed());
+		assertFalse(AttributeType.REAL.isNullAllowed());
+		assertFalse(AttributeType.STRING.isNullAllowed());
+		assertFalse(AttributeType.TIMESTAMP.isNullAllowed());
+		
+		assertFalse(AttributeType.COLOR.isNullAllowed());
+		assertFalse(AttributeType.DATE.isNullAllowed());
+		assertTrue(AttributeType.IMAGE.isNullAllowed());
+		assertTrue(AttributeType.OBJECT.isNullAllowed());
+		assertFalse(AttributeType.POINT.isNullAllowed());
+		assertFalse(AttributeType.POINT3D.isNullAllowed());
+		assertFalse(AttributeType.POLYLINE.isNullAllowed());
+		assertFalse(AttributeType.POLYLINE3D.isNullAllowed());
+		assertTrue(AttributeType.URL.isNullAllowed());
+		assertTrue(AttributeType.URI.isNullAllowed());
+		assertFalse(AttributeType.UUID.isNullAllowed());
+		assertTrue(AttributeType.INET_ADDRESS.isNullAllowed());
+		assertTrue(AttributeType.ENUMERATION.isNullAllowed());
+		assertFalse(AttributeType.TYPE.isNullAllowed());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testGetDefaultValue() throws Exception {
+		assertEquals(Boolean.FALSE, AttributeType.BOOLEAN.getDefaultValue());
+		assertEquals(new Long(0), AttributeType.INTEGER.getDefaultValue());
+		assertEquals(new Double(0.), AttributeType.REAL.getDefaultValue());
+		assertEquals(new String(), AttributeType.STRING.getDefaultValue());
+		assertNotNull(AttributeType.TIMESTAMP.getDefaultValue());
+		
+		assertEquals(VectorToolkit.color(0,0,0), AttributeType.COLOR.getDefaultValue());
+		assertNotNull(AttributeType.DATE.getDefaultValue());
+		assertNull(AttributeType.IMAGE.getDefaultValue());
+		assertNull(AttributeType.OBJECT.getDefaultValue());
+		assertEquals(new Point2f(), AttributeType.POINT.getDefaultValue());
+		assertEquals(new Point3f(), AttributeType.POINT3D.getDefaultValue());
+		assertTrue(Arrays.equals(new Point2D[0], (Point2D[])AttributeType.POLYLINE.getDefaultValue()));
+		assertTrue(Arrays.equals(new Point3D[0], (Point3D[])AttributeType.POLYLINE3D.getDefaultValue()));
+		assertNull(AttributeType.URL.getDefaultValue());
+		assertNull(AttributeType.URI.getDefaultValue());
+		assertEquals(java.util.UUID.fromString("00000000-0000-0000-0000-000000000000"), AttributeType.UUID.getDefaultValue()); //$NON-NLS-1$
+		assertEquals(InetAddress.getLocalHost(), AttributeType.INET_ADDRESS.getDefaultValue());
+		assertNull(AttributeType.ENUMERATION.getDefaultValue());
+		assertEquals(Object.class, AttributeType.TYPE.getDefaultValue());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testCast() throws Exception {
+		AttributeType type;
+		char vChar = 'c';
+		byte vByte = 1;
+		short vShort = 1;
+		int vInt = 1;
+		long vLong = 1;
+		float vFloat = 1f;
+		double vDouble = 1.;
+		String vStr = "Hello"; //$NON-NLS-1$
+		StringBuilder vStrB = new StringBuilder("World"); //$NON-NLS-1$
+		Calendar cal = Calendar.getInstance();
+		Date dt = new Date();
+		Point2f pt2d1 = new Point2f(0,0);
+		Image img = VectorToolkit.image(1,1,false);
+		Color col = VectorToolkit.RED;
+		UUID uuid = UUID.nameUUIDFromBytes("abcd".getBytes()); //$NON-NLS-1$
+		Point3D pt3d = new Point3f(0,0,0);
+		Point2D[] tabpt2d1 = new Point2D[0];
+		Point3D[] tabpt3d = new Point3D[0];
+		int[] tabint = new int[0];
+		Object obj = Locale.getDefault();
+		URL url = new URL("http://set.utbm.fr";); //$NON-NLS-1$
+		URI uri = new URI("http://set.utbm.fr";); //$NON-NLS-1$
+		InetAddress ipAddress = InetAddress.getLocalHost();
+		String vIpStr = ipAddress.toString();
+		AttributeType enumeration = randomEnum(AttributeType.class);
+		String vEnumStr = AttributeType.class.getCanonicalName()+"."+AttributeType.values()[1].name(); //$NON-NLS-1$
+		
+		// BOOLEAN
+		type = AttributeType.BOOLEAN;
+		assertNullException(type,null);
+		assertEquals(Boolean.TRUE, type.cast(true));
+		assertEquals(Boolean.FALSE, type.cast(false));
+		assertCastException(type,vChar);
+		assertCastException(type,vByte);
+		assertCastException(type,vShort);
+		assertCastException(type,vInt);
+		assertCastException(type,vLong);
+		assertCastException(type,vFloat);
+		assertCastException(type,vDouble);
+		assertCastException(type,vStr);
+		assertCastException(type,vStrB);
+		assertCastException(type,cal);
+		assertCastException(type,dt);
+		assertCastException(type,col);
+		assertCastException(type,uuid);
+		assertCastException(type,pt2d1);
+		assertCastException(type,pt3d);
+		assertCastException(type,tabpt2d1);
+		assertCastException(type,tabpt3d);
+		assertCastException(type,img);
+		assertCastException(type,tabint);
+		assertCastException(type,obj);
+		assertCastException(type,url);
+		assertCastException(type,uri);
+		assertCastException(type,ipAddress);
+		assertCastException(type,enumeration);
+
+		// COLOR
+		type = AttributeType.COLOR;
+		assertNullException(type,null);
+		assertCastException(type,true);
+		assertCastException(type,false);
+		assertCastException(type,vChar);
+		assertEquals(VectorToolkit.color(vByte), type.cast(vByte));
+		assertEquals(VectorToolkit.color(vShort), type.cast(vShort));
+		assertEquals(VectorToolkit.color(vInt), type.cast(vInt));
+		assertEquals(VectorToolkit.color((int)vLong), type.cast(vLong));
+		assertEquals(VectorToolkit.color((int)vFloat), type.cast(vFloat));
+		assertEquals(VectorToolkit.color((int)vDouble), type.cast(vDouble));
+		assertCastException(type,vStr);
+		assertCastException(type,vStrB);
+		assertCastException(type,cal);
+		assertCastException(type,dt);
+		assertSame(col,type.cast(col));
+		assertCastException(type,uuid);
+		assertCastException(type,pt2d1);
+		assertCastException(type,pt3d);
+		assertCastException(type,tabpt2d1);
+		assertCastException(type,tabpt3d);
+		assertCastException(type,img);
+		assertCastException(type,tabint);
+		assertCastException(type,obj);
+		assertCastException(type,url);
+		assertCastException(type,uri);
+		assertCastException(type,ipAddress);
+		assertCastException(type,enumeration);
+
+		// UUID
+		type = AttributeType.UUID;
+		assertNullException(type,null);
+		assertCastException(type,true);
+		assertCastException(type,false);
+		assertCastException(type,vChar);
+		assertCastException(type,vByte);
+		assertCastException(type,vShort);
+		assertCastException(type,vInt);
+		assertCastException(type,vLong);
+		assertCastException(type,vFloat);
+		assertCastException(type,vDouble);
+		assertCastException(type,vStr);
+		assertCastException(type,vStrB);
+		assertCastException(type,cal);
+		assertCastException(type,dt);
+		assertCastException(type,col);
+		assertSame(uuid,type.cast(uuid));
+		assertCastException(type,pt2d1);
+		assertCastException(type,pt3d);
+		assertCastException(type,tabpt2d1);
+		assertCastException(type,tabpt3d);
+		assertCastException(type,img);
+		assertCastException(type,tabint);
+		assertCastException(type,obj);
+		assertCastException(type,url);
+		assertCastException(type,uri);
+		assertCastException(type,ipAddress);
+		assertCastException(type,enumeration);
+
+		// DATE
+		type = AttributeType.DATE;
+		assertNullException(type,null);
+		assertCastException(type,true);
+		assertCastException(type,false);
+		assertCastException(type,vChar);
+		assertEquals(new Date(vByte),type.cast(vByte));
+		assertEquals(new Date(vShort),type.cast(vShort));
+		assertEquals(new Date(vInt),type.cast(vInt));
+		assertEquals(new Date(vLong),type.cast(vLong));
+		assertEquals(new Date((long)vFloat),type.cast(vFloat));
+		assertEquals(new Date((long)vDouble),type.cast(vDouble));
+		assertCastException(type,vStr);
+		assertCastException(type,vStrB);
+		assertEquals(cal.getTime(),type.cast(cal));
+		assertSame(dt,type.cast(dt));
+		assertCastException(type,col);
+		assertCastException(type,uuid);
+		assertCastException(type,pt2d1);
+		assertCastException(type,pt3d);
+		assertCastException(type,tabpt2d1);
+		assertCastException(type,tabpt3d);
+		assertCastException(type,img);
+		assertCastException(type,tabint);
+		assertCastException(type,obj);
+		assertCastException(type,url);
+		assertCastException(type,uri);
+		assertCastException(type,ipAddress);
+		assertCastException(type,enumeration);
+
+		// ICON
+		type = AttributeType.IMAGE;
+		assertEquals(new NullAttribute(type),type.cast(null));
+		assertCastException(type,true);
+		assertCastException(type,false);
+		assertCastException(type,vChar);
+		assertCastException(type,vByte);
+		assertCastException(type,vShort);
+		assertCastException(type,vInt);
+		assertCastException(type,vLong);
+		assertCastException(type,vFloat);
+		assertCastException(type,vDouble);
+		assertCastException(type,vStr);
+		assertCastException(type,vStrB);
+		assertCastException(type,cal);
+		assertCastException(type,dt);
+		assertCastException(type,col);
+		assertCastException(type,uuid);
+		assertCastException(type,pt2d1);
+		assertCastException(type,pt3d);
+		assertCastException(type,tabpt2d1);
+		assertCastException(type,tabpt3d);
+		assertNotNull(type.cast(img));
+		assertCastException(type,tabint);
+		assertCastException(type,obj);
+		assertCastException(type,url);
+		assertCastException(type,uri);
+		assertCastException(type,ipAddress);
+		assertCastException(type,enumeration);
+
+		// INTEGER
+		type = AttributeType.INTEGER;
+		assertNullException(type,null);
+		assertCastException(type,true);
+		assertCastException(type,false);
+		assertCastException(type,vChar);
+		assertEquals((long)vByte,type.cast(vByte));
+		assertEquals((long)vShort,type.cast(vShort));
+		assertEquals((long)vInt,type.cast(vInt));
+		assertEquals(vLong,type.cast(vLong));
+		assertEquals((long)vFloat,type.cast(vFloat));
+		assertEquals((long)vDouble,type.cast(vDouble));
+		assertCastException(type,vStr);
+		assertCastException(type,vStrB);
+		assertCastException(type,cal);
+		assertCastException(type,dt);
+		assertCastException(type,col);
+		assertCastException(type,uuid);
+		assertCastException(type,pt2d1);
+		assertCastException(type,pt3d);
+		assertCastException(type,tabpt2d1);
+		assertCastException(type,tabpt3d);
+		assertCastException(type,img);
+		assertCastException(type,tabint);
+		assertCastException(type,obj);
+		assertCastException(type,url);
+		assertCastException(type,uri);
+		assertCastException(type,ipAddress);
+		assertEquals((long)enumeration.ordinal(), type.cast(enumeration));
+
+		// OBJECT
+		type = AttributeType.OBJECT;
+		assertEquals(new NullAttribute(type),type.cast(null));
+		assertEquals(true,type.cast(true));
+		assertEquals(false,type.cast(false));
+		assertEquals(vChar,type.cast(vChar));
+		assertEquals(vByte,type.cast(vByte));
+		assertEquals(vShort,type.cast(vShort));
+		assertEquals(vInt,type.cast(vInt));
+		assertEquals(vLong,type.cast(vLong));
+		assertEquals(vFloat, type.cast(vFloat));
+		assertEquals(vDouble,type.cast(vDouble));
+		assertEquals(vStr,type.cast(vStr));
+		assertEquals(vStrB,type.cast(vStrB));
+		assertSame(cal,type.cast(cal));
+		assertSame(dt,type.cast(dt));
+		assertSame(col,type.cast(col));
+		assertSame(uuid,type.cast(uuid));
+		assertSame(pt2d1,type.cast(pt2d1));
+		assertSame(pt3d,type.cast(pt3d));
+		assertSame(tabpt2d1,type.cast(tabpt2d1));
+		assertSame(tabpt3d,type.cast(tabpt3d));
+		assertSame(img,type.cast(img));
+		assertSame(tabint,type.cast(tabint));
+		assertSame(obj,type.cast(obj));
+		assertSame(url,type.cast(url));
+		assertSame(uri,type.cast(uri));
+		assertSame(ipAddress, type.cast(ipAddress));
+		assertSame(enumeration,type.cast(enumeration));
+
+		// POINT
+		type = AttributeType.POINT;
+		assertNullException(type,null);
+		assertCastException(type,true);
+		assertCastException(type,false);
+		assertCastException(type,vChar);
+		assertCastException(type,vByte);
+		assertCastException(type,vShort);
+		assertCastException(type,vInt);
+		assertCastException(type,vLong);
+		assertCastException(type,vFloat);
+		assertCastException(type,vDouble);
+		assertCastException(type,vStr);
+		assertCastException(type,vStrB);
+		assertCastException(type,cal);
+		assertCastException(type,dt);
+		assertCastException(type,col);
+		assertCastException(type,uuid);
+		assertSame(pt2d1,type.cast(pt2d1));
+		assertCastException(type,pt3d);
+		assertCastException(type,tabpt2d1);
+		assertCastException(type,tabpt3d);
+		assertCastException(type,img);
+		assertCastException(type,tabint);
+		assertCastException(type,obj);
+		assertCastException(type,url);
+		assertCastException(type,uri);
+		assertCastException(type,ipAddress);
+		assertCastException(type,enumeration);
+
+		// POINT3D
+		type = AttributeType.POINT3D;
+		assertNullException(type,null);
+		assertCastException(type,true);
+		assertCastException(type,false);
+		assertCastException(type,vChar);
+		assertCastException(type,vByte);
+		assertCastException(type,vShort);
+		assertCastException(type,vInt);
+		assertCastException(type,vLong);
+		assertCastException(type,vFloat);
+		assertCastException(type,vDouble);
+		assertCastException(type,vStr);
+		assertCastException(type,vStrB);
+		assertCastException(type,cal);
+		assertCastException(type,dt);
+		assertCastException(type,col);
+		assertCastException(type,uuid);
+		assertCastException(type,pt2d1);
+		assertSame(pt3d,type.cast(pt3d));
+		assertCastException(type,tabpt2d1);
+		assertCastException(type,tabpt3d);
+		assertCastException(type,img);
+		assertCastException(type,tabint);
+		assertCastException(type,obj);
+		assertCastException(type,url);
+		assertCastException(type,uri);
+		assertCastException(type,ipAddress);
+		assertCastException(type,enumeration);
+
+		// POLYLINE
+		type = AttributeType.POLYLINE;
+		assertNullException(type,null);
+		assertCastException(type,true);
+		assertCastException(type,false);
+		assertCastException(type,vChar);
+		assertCastException(type,vByte);
+		assertCastException(type,vShort);
+		assertCastException(type,vInt);
+		assertCastException(type,vLong);
+		assertCastException(type,vFloat);
+		assertCastException(type,vDouble);
+		assertCastException(type,vStr);
+		assertCastException(type,vStrB);
+		assertCastException(type,cal);
+		assertCastException(type,dt);
+		assertCastException(type,col);
+		assertCastException(type,uuid);
+		assertCastException(type,pt2d1);
+		assertCastException(type,pt3d);
+		assertSame(tabpt2d1,type.cast(tabpt2d1));
+		assertCastException(type,tabpt3d);
+		assertCastException(type,img);
+		assertCastException(type,tabint);
+		assertCastException(type,obj);
+		assertCastException(type,url);
+		assertCastException(type,uri);
+		assertCastException(type,ipAddress);
+		assertCastException(type,enumeration);
+
+		// POLYINE3D
+		type = AttributeType.POLYLINE3D;
+		assertNullException(type,null);
+		assertCastException(type,true);
+		assertCastException(type,false);
+		assertCastException(type,vChar);
+		assertCastException(type,vByte);
+		assertCastException(type,vShort);
+		assertCastException(type,vInt);
+		assertCastException(type,vLong);
+		assertCastException(type,vFloat);
+		assertCastException(type,vDouble);
+		assertCastException(type,vStr);
+		assertCastException(type,vStrB);
+		assertCastException(type,cal);
+		assertCastException(type,dt);
+		assertCastException(type,col);
+		assertCastException(type,uuid);
+		assertCastException(type,pt2d1);
+		assertCastException(type,pt3d);
+		assertCastException(type,tabpt2d1);
+		assertSame(tabpt3d,type.cast(tabpt3d));
+		assertCastException(type,img);
+		assertCastException(type,tabint);
+		assertCastException(type,obj);
+		assertCastException(type,url);
+		assertCastException(type,uri);
+		assertCastException(type,ipAddress);
+		assertCastException(type,enumeration);
+
+		// REAL
+		type = AttributeType.REAL;
+		assertNullException(type,null);
+		assertCastException(type,true);
+		assertCastException(type,false);
+		assertCastException(type,vChar);
+		assertEquals((double)vByte,type.cast(vByte));
+		assertEquals((double)vShort,type.cast(vShort));
+		assertEquals((double)vInt,type.cast(vInt));
+		assertEquals((double)vLong,type.cast(vLong));
+		assertEquals((double)vFloat,type.cast(vFloat));
+		assertEquals(vDouble,type.cast(vDouble));
+		assertCastException(type,vStr);
+		assertCastException(type,vStrB);
+		assertCastException(type,cal);
+		assertCastException(type,dt);
+		assertCastException(type,col);
+		assertCastException(type,uuid);
+		assertCastException(type,pt2d1);
+		assertCastException(type,pt3d);
+		assertCastException(type,tabpt2d1);
+		assertCastException(type,tabpt3d);
+		assertCastException(type,img);
+		assertCastException(type,tabint);
+		assertCastException(type,obj);
+		assertCastException(type,url);
+		assertCastException(type,uri);
+		assertCastException(type,ipAddress);
+		assertEquals((double)enumeration.ordinal(), type.cast(enumeration));
+
+		// STRING
+		type = AttributeType.STRING;
+		assertEquals(type.getDefaultValue(),type.cast(null));
+		assertEquals(Boolean.toString(true),type.cast(true));
+		assertEquals(Boolean.toString(false),type.cast(false));
+		assertEquals(Character.toString(vChar),type.cast(vChar));
+		assertEquals(Byte.toString(vByte),type.cast(vByte));
+		assertEquals(Short.toString(vShort),type.cast(vShort));
+		assertEquals(Integer.toString(vInt),type.cast(vInt));
+		assertEquals(Long.toString(vLong),type.cast(vLong));
+		assertEquals(Float.toString(vFloat),type.cast(vFloat));
+		assertEquals(Double.toString(vDouble),type.cast(vDouble));
+		assertEquals(vStr,type.cast(vStr));
+		assertEquals(vStrB.toString(),type.cast(vStrB));
+		assertEquals(cal.toString(),type.cast(cal));
+		assertEquals(dt.toString(),type.cast(dt));
+		assertEquals(col.toString(),type.cast(col));
+		assertEquals(uuid.toString(),type.cast(uuid));
+		assertEquals(pt2d1.toString(),type.cast(pt2d1));
+		assertEquals(pt3d.toString(),type.cast(pt3d));
+		assertEquals(tabpt2d1.toString(),type.cast(tabpt2d1));
+		assertEquals(tabpt3d.toString(),type.cast(tabpt3d));
+		assertEquals(img.toString(),type.cast(img));
+		assertEquals(tabint.toString(),type.cast(tabint));
+		assertEquals(obj.toString(),type.cast(obj));
+		assertEquals(url.toString(), type.cast(url));
+		assertEquals(uri.toString(), type.cast(uri));
+		assertEquals(ipAddress.toString(), type.cast(ipAddress));
+		assertEquals(enumeration.getClass().getCanonicalName()+"."+enumeration.name(), type.cast(enumeration)); //$NON-NLS-1$
+
+		// TIMESTAMP
+		type = AttributeType.TIMESTAMP;
+		assertNullException(type,null);
+		assertCastException(type,true);
+		assertCastException(type,false);
+		assertCastException(type,vChar);
+		assertEquals(new Timestamp(vByte),type.cast(vByte));
+		assertEquals(new Timestamp(vShort),type.cast(vShort));
+		assertEquals(new Timestamp(vInt),type.cast(vInt));
+		assertEquals(new Timestamp(vLong),type.cast(vLong));
+		assertEquals(new Timestamp((int)vFloat),type.cast(vFloat));
+		assertEquals(new Timestamp((int)vDouble),type.cast(vDouble));
+		assertCastException(type,vStr);
+		assertCastException(type,vStrB);
+		assertEquals(cal.getTimeInMillis(),type.cast(cal));
+		assertEquals(dt.getTime(),type.cast(dt));
+		assertCastException(type,col);
+		assertCastException(type,uuid);
+		assertCastException(type,pt2d1);
+		assertCastException(type,pt3d);
+		assertCastException(type,tabpt2d1);
+		assertCastException(type,tabpt3d);
+		assertCastException(type,img);
+		assertCastException(type,tabint);
+		assertCastException(type,obj);
+		assertCastException(type,url);
+		assertCastException(type,uri);
+		assertCastException(type,ipAddress);
+		assertCastException(type,enumeration);
+
+		// URI
+		type = AttributeType.URI;
+		assertEquals(new NullAttribute(type),type.cast(null));
+		assertCastException(type,true);
+		assertCastException(type,false);
+		assertCastException(type,vChar);
+		assertCastException(type,vByte);
+		assertCastException(type,vShort);
+		assertCastException(type,vInt);
+		assertCastException(type,vLong);
+		assertCastException(type,vFloat);
+		assertCastException(type,vDouble);
+		assertCastException(type,vStr);
+		assertCastException(type,vStrB);
+		assertCastException(type,cal);
+		assertCastException(type,dt);
+		assertCastException(type,col);
+		assertCastException(type,uuid);
+		assertCastException(type,pt2d1);
+		assertCastException(type,pt3d);
+		assertCastException(type,tabpt2d1);
+		assertCastException(type,tabpt3d);
+		assertCastException(type,img);
+		assertCastException(type,tabint);
+		assertCastException(type,obj);
+		assertEquals(url.toURI(), type.cast(url));
+		assertSame(uri, type.cast(uri));
+		assertEquals(new URI(AttributeConstants.DEFAULT_SCHEME.name(), ipAddress.getHostAddress(), ""), type.cast(ipAddress)); //$NON-NLS-1$
+		assertCastException(type,enumeration);
+
+		// URL
+		type = AttributeType.URL;
+		assertEquals(new NullAttribute(type),type.cast(null));
+		assertCastException(type,true);
+		assertCastException(type,false);
+		assertCastException(type,vChar);
+		assertCastException(type,vByte);
+		assertCastException(type,vShort);
+		assertCastException(type,vInt);
+		assertCastException(type,vLong);
+		assertCastException(type,vFloat);
+		assertCastException(type,vDouble);
+		assertCastException(type,vStr);
+		assertCastException(type,vStrB);
+		assertCastException(type,cal);
+		assertCastException(type,dt);
+		assertCastException(type,col);
+		assertCastException(type,uuid);
+		assertCastException(type,pt2d1);
+		assertCastException(type,pt3d);
+		assertCastException(type,tabpt2d1);
+		assertCastException(type,tabpt3d);
+		assertCastException(type,img);
+		assertCastException(type,tabint);
+		assertCastException(type,obj);
+		assertSame(url, type.cast(url));
+		assertEquals(uri.toURL(), type.cast(uri));
+		assertEquals(new URL(AttributeConstants.DEFAULT_SCHEME.name(), ipAddress.getHostAddress(), ""), type.cast(ipAddress)); //$NON-NLS-1$
+		assertCastException(type,enumeration);
+
+		// INET_ADDRESS
+		type = AttributeType.INET_ADDRESS;
+		assertEquals(new NullAttribute(type),type.cast(null));
+		assertCastException(type,true);
+		assertCastException(type,false);
+		assertCastException(type,vChar);
+		assertCastException(type,vByte);
+		assertCastException(type,vShort);
+		assertCastException(type,vInt);
+		assertCastException(type,vLong);
+		assertCastException(type,vFloat);
+		assertCastException(type,vDouble);
+		assertCastException(type,vStr);
+		assertEquals(ipAddress,type.cast(vIpStr));
+		assertCastException(type,vStrB);
+		assertCastException(type,cal);
+		assertCastException(type,dt);
+		assertCastException(type,col);
+		assertCastException(type,uuid);
+		assertCastException(type,pt2d1);
+		assertCastException(type,pt3d);
+		assertCastException(type,tabpt2d1);
+		assertCastException(type,tabpt3d);
+		assertCastException(type,img);
+		assertCastException(type,tabint);
+		assertCastException(type,obj);
+		assertEquals(InetAddress.getByName("set.utbm.fr"), type.cast(url)); //$NON-NLS-1$
+		assertEquals(InetAddress.getByName("set.utbm.fr"), type.cast(uri)); //$NON-NLS-1$
+		assertSame(ipAddress, type.cast(ipAddress));
+		assertCastException(type,enumeration);
+
+		// ENUMERATION
+		type = AttributeType.ENUMERATION;
+		assertEquals(new NullAttribute(type),type.cast(null));
+		assertCastException(type,true);
+		assertCastException(type,false);
+		assertCastException(type,vChar);
+		assertCastException(type,vByte);
+		assertCastException(type,vShort);
+		assertCastException(type,vInt);
+		assertCastException(type,vLong);
+		assertCastException(type,vFloat);
+		assertCastException(type,vDouble);
+		assertCastException(type,vStr);
+		assertSame(AttributeType.values()[1],type.cast(vEnumStr));
+		assertCastException(type,vStrB);
+		assertCastException(type,cal);
+		assertCastException(type,dt);
+		assertCastException(type,col);
+		assertCastException(type,uuid);
+		assertCastException(type,pt2d1);
+		assertCastException(type,pt3d);
+		assertCastException(type,tabpt2d1);
+		assertCastException(type,tabpt3d);
+		assertCastException(type,img);
+		assertCastException(type,tabint);
+		assertCastException(type,obj);
+		assertCastException(type,url);
+		assertCastException(type,uri);
+		assertCastException(type,ipAddress);
+		assertSame(enumeration,type.cast(enumeration));
+	}
+
+	/**
+	 */
+	public static void testIsAssignableFrom() {
+		assertTrue(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.BOOLEAN));
+		assertFalse(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.COLOR));
+		assertFalse(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.DATE));
+		assertFalse(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.IMAGE));
+		assertTrue(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.OBJECT));
+		assertFalse(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.POINT));
+		assertFalse(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.POINT3D));
+		assertFalse(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.POLYLINE));
+		assertFalse(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertTrue(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.STRING));
+		assertTrue(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertFalse(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.URI));
+		assertFalse(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.URL));
+		assertFalse(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.UUID));
+		assertFalse(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertFalse(AttributeType.BOOLEAN.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertFalse(AttributeType.COLOR.isAssignableFrom(AttributeType.BOOLEAN));
+		assertTrue(AttributeType.COLOR.isAssignableFrom(AttributeType.COLOR));
+		assertTrue(AttributeType.COLOR.isAssignableFrom(AttributeType.DATE));
+		assertFalse(AttributeType.COLOR.isAssignableFrom(AttributeType.IMAGE));
+		assertTrue(AttributeType.COLOR.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.COLOR.isAssignableFrom(AttributeType.OBJECT));
+		assertTrue(AttributeType.COLOR.isAssignableFrom(AttributeType.POINT));
+		assertTrue(AttributeType.COLOR.isAssignableFrom(AttributeType.POINT3D));
+		assertFalse(AttributeType.COLOR.isAssignableFrom(AttributeType.POLYLINE));
+		assertFalse(AttributeType.COLOR.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertTrue(AttributeType.COLOR.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.COLOR.isAssignableFrom(AttributeType.STRING));
+		assertTrue(AttributeType.COLOR.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertFalse(AttributeType.COLOR.isAssignableFrom(AttributeType.URI));
+		assertFalse(AttributeType.COLOR.isAssignableFrom(AttributeType.URL));
+		assertFalse(AttributeType.COLOR.isAssignableFrom(AttributeType.UUID));
+		assertFalse(AttributeType.COLOR.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertFalse(AttributeType.COLOR.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertFalse(AttributeType.DATE.isAssignableFrom(AttributeType.BOOLEAN));
+		assertFalse(AttributeType.DATE.isAssignableFrom(AttributeType.COLOR));
+		assertTrue(AttributeType.DATE.isAssignableFrom(AttributeType.DATE));
+		assertFalse(AttributeType.DATE.isAssignableFrom(AttributeType.IMAGE));
+		assertTrue(AttributeType.DATE.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.DATE.isAssignableFrom(AttributeType.OBJECT));
+		assertFalse(AttributeType.DATE.isAssignableFrom(AttributeType.POINT));
+		assertFalse(AttributeType.DATE.isAssignableFrom(AttributeType.POINT3D));
+		assertFalse(AttributeType.DATE.isAssignableFrom(AttributeType.POLYLINE));
+		assertFalse(AttributeType.DATE.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertTrue(AttributeType.DATE.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.DATE.isAssignableFrom(AttributeType.STRING));
+		assertTrue(AttributeType.DATE.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertFalse(AttributeType.DATE.isAssignableFrom(AttributeType.URI));
+		assertFalse(AttributeType.DATE.isAssignableFrom(AttributeType.URL));
+		assertFalse(AttributeType.DATE.isAssignableFrom(AttributeType.UUID));
+		assertFalse(AttributeType.DATE.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertFalse(AttributeType.DATE.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertFalse(AttributeType.IMAGE.isAssignableFrom(AttributeType.BOOLEAN));
+		assertFalse(AttributeType.IMAGE.isAssignableFrom(AttributeType.COLOR));
+		assertFalse(AttributeType.IMAGE.isAssignableFrom(AttributeType.DATE));
+		assertTrue(AttributeType.IMAGE.isAssignableFrom(AttributeType.IMAGE));
+		assertFalse(AttributeType.IMAGE.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.IMAGE.isAssignableFrom(AttributeType.OBJECT));
+		assertFalse(AttributeType.IMAGE.isAssignableFrom(AttributeType.POINT));
+		assertFalse(AttributeType.IMAGE.isAssignableFrom(AttributeType.POINT3D));
+		assertFalse(AttributeType.IMAGE.isAssignableFrom(AttributeType.POLYLINE));
+		assertFalse(AttributeType.IMAGE.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertFalse(AttributeType.IMAGE.isAssignableFrom(AttributeType.REAL));
+		assertFalse(AttributeType.IMAGE.isAssignableFrom(AttributeType.STRING));
+		assertFalse(AttributeType.IMAGE.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertFalse(AttributeType.IMAGE.isAssignableFrom(AttributeType.URI));
+		assertFalse(AttributeType.IMAGE.isAssignableFrom(AttributeType.URL));
+		assertFalse(AttributeType.IMAGE.isAssignableFrom(AttributeType.UUID));
+		assertFalse(AttributeType.IMAGE.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertFalse(AttributeType.IMAGE.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertTrue(AttributeType.INTEGER.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.INTEGER.isAssignableFrom(AttributeType.COLOR));
+		assertTrue(AttributeType.INTEGER.isAssignableFrom(AttributeType.DATE));
+		assertFalse(AttributeType.INTEGER.isAssignableFrom(AttributeType.IMAGE));
+		assertTrue(AttributeType.INTEGER.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.INTEGER.isAssignableFrom(AttributeType.OBJECT));
+		assertFalse(AttributeType.INTEGER.isAssignableFrom(AttributeType.POINT));
+		assertFalse(AttributeType.INTEGER.isAssignableFrom(AttributeType.POINT3D));
+		assertFalse(AttributeType.INTEGER.isAssignableFrom(AttributeType.POLYLINE));
+		assertFalse(AttributeType.INTEGER.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertTrue(AttributeType.INTEGER.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.INTEGER.isAssignableFrom(AttributeType.STRING));
+		assertTrue(AttributeType.INTEGER.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertFalse(AttributeType.INTEGER.isAssignableFrom(AttributeType.URI));
+		assertFalse(AttributeType.INTEGER.isAssignableFrom(AttributeType.URL));
+		assertFalse(AttributeType.INTEGER.isAssignableFrom(AttributeType.UUID));
+		assertFalse(AttributeType.INTEGER.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertTrue(AttributeType.INTEGER.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.BOOLEAN));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.COLOR));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.DATE));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.IMAGE));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.OBJECT));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.POINT));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.POINT3D));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.POLYLINE));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.STRING));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.URI));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.URL));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.UUID));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertTrue(AttributeType.OBJECT.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertFalse(AttributeType.POINT.isAssignableFrom(AttributeType.BOOLEAN));
+		assertTrue(AttributeType.POINT.isAssignableFrom(AttributeType.COLOR));
+		assertTrue(AttributeType.POINT.isAssignableFrom(AttributeType.DATE));
+		assertFalse(AttributeType.POINT.isAssignableFrom(AttributeType.IMAGE));
+		assertTrue(AttributeType.POINT.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.POINT.isAssignableFrom(AttributeType.OBJECT));
+		assertTrue(AttributeType.POINT.isAssignableFrom(AttributeType.POINT));
+		assertTrue(AttributeType.POINT.isAssignableFrom(AttributeType.POINT3D));
+		assertFalse(AttributeType.POINT.isAssignableFrom(AttributeType.POLYLINE));
+		assertFalse(AttributeType.POINT.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertTrue(AttributeType.POINT.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.POINT.isAssignableFrom(AttributeType.STRING));
+		assertTrue(AttributeType.POINT.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertFalse(AttributeType.POINT.isAssignableFrom(AttributeType.URI));
+		assertFalse(AttributeType.POINT.isAssignableFrom(AttributeType.URL));
+		assertFalse(AttributeType.POINT.isAssignableFrom(AttributeType.UUID));
+		assertFalse(AttributeType.POINT.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertFalse(AttributeType.POINT.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertFalse(AttributeType.POINT3D.isAssignableFrom(AttributeType.BOOLEAN));
+		assertTrue(AttributeType.POINT3D.isAssignableFrom(AttributeType.COLOR));
+		assertTrue(AttributeType.POINT3D.isAssignableFrom(AttributeType.DATE));
+		assertFalse(AttributeType.POINT3D.isAssignableFrom(AttributeType.IMAGE));
+		assertTrue(AttributeType.POINT3D.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.POINT3D.isAssignableFrom(AttributeType.OBJECT));
+		assertTrue(AttributeType.POINT3D.isAssignableFrom(AttributeType.POINT));
+		assertTrue(AttributeType.POINT3D.isAssignableFrom(AttributeType.POINT3D));
+		assertFalse(AttributeType.POINT3D.isAssignableFrom(AttributeType.POLYLINE));
+		assertFalse(AttributeType.POINT3D.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertTrue(AttributeType.POINT3D.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.POINT3D.isAssignableFrom(AttributeType.STRING));
+		assertTrue(AttributeType.POINT3D.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertFalse(AttributeType.POINT3D.isAssignableFrom(AttributeType.URI));
+		assertFalse(AttributeType.POINT3D.isAssignableFrom(AttributeType.URL));
+		assertFalse(AttributeType.POINT3D.isAssignableFrom(AttributeType.UUID));
+		assertFalse(AttributeType.POINT3D.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertFalse(AttributeType.POINT3D.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertFalse(AttributeType.POLYLINE.isAssignableFrom(AttributeType.BOOLEAN));
+		assertFalse(AttributeType.POLYLINE.isAssignableFrom(AttributeType.COLOR));
+		assertFalse(AttributeType.POLYLINE.isAssignableFrom(AttributeType.DATE));
+		assertFalse(AttributeType.POLYLINE.isAssignableFrom(AttributeType.IMAGE));
+		assertFalse(AttributeType.POLYLINE.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.POLYLINE.isAssignableFrom(AttributeType.OBJECT));
+		assertTrue(AttributeType.POLYLINE.isAssignableFrom(AttributeType.POINT));
+		assertTrue(AttributeType.POLYLINE.isAssignableFrom(AttributeType.POINT3D));
+		assertTrue(AttributeType.POLYLINE.isAssignableFrom(AttributeType.POLYLINE));
+		assertTrue(AttributeType.POLYLINE.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertFalse(AttributeType.POLYLINE.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.POLYLINE.isAssignableFrom(AttributeType.STRING));
+		assertFalse(AttributeType.POLYLINE.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertFalse(AttributeType.POLYLINE.isAssignableFrom(AttributeType.URI));
+		assertFalse(AttributeType.POLYLINE.isAssignableFrom(AttributeType.URL));
+		assertFalse(AttributeType.POLYLINE.isAssignableFrom(AttributeType.UUID));
+		assertFalse(AttributeType.POLYLINE.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertFalse(AttributeType.POLYLINE.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertFalse(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.BOOLEAN));
+		assertFalse(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.COLOR));
+		assertFalse(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.DATE));
+		assertFalse(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.IMAGE));
+		assertFalse(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.OBJECT));
+		assertTrue(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.POINT));
+		assertTrue(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.POINT3D));
+		assertTrue(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.POLYLINE));
+		assertTrue(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertFalse(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.STRING));
+		assertFalse(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertFalse(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.URI));
+		assertFalse(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.URL));
+		assertFalse(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.UUID));
+		assertFalse(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertFalse(AttributeType.POLYLINE3D.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertTrue(AttributeType.REAL.isAssignableFrom(AttributeType.BOOLEAN));
+		assertTrue(AttributeType.REAL.isAssignableFrom(AttributeType.COLOR));
+		assertTrue(AttributeType.REAL.isAssignableFrom(AttributeType.DATE));
+		assertFalse(AttributeType.REAL.isAssignableFrom(AttributeType.IMAGE));
+		assertTrue(AttributeType.REAL.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.REAL.isAssignableFrom(AttributeType.OBJECT));
+		assertFalse(AttributeType.REAL.isAssignableFrom(AttributeType.POINT));
+		assertFalse(AttributeType.REAL.isAssignableFrom(AttributeType.POINT3D));
+		assertFalse(AttributeType.REAL.isAssignableFrom(AttributeType.POLYLINE));
+		assertFalse(AttributeType.REAL.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertTrue(AttributeType.REAL.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.REAL.isAssignableFrom(AttributeType.STRING));
+		assertTrue(AttributeType.REAL.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertFalse(AttributeType.REAL.isAssignableFrom(AttributeType.URI));
+		assertFalse(AttributeType.REAL.isAssignableFrom(AttributeType.URL));
+		assertFalse(AttributeType.REAL.isAssignableFrom(AttributeType.UUID));
+		assertFalse(AttributeType.REAL.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertTrue(AttributeType.REAL.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.BOOLEAN));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.COLOR));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.DATE));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.IMAGE));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.OBJECT));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.POINT));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.POINT3D));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.POLYLINE));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.STRING));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.URI));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.URL));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.UUID));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertTrue(AttributeType.STRING.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertTrue(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.BOOLEAN));
+		assertTrue(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.COLOR));
+		assertTrue(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.DATE));
+		assertFalse(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.IMAGE));
+		assertTrue(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.OBJECT));
+		assertFalse(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.POINT));
+		assertFalse(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.POINT3D));
+		assertFalse(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.POLYLINE));
+		assertFalse(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertTrue(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.STRING));
+		assertTrue(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertFalse(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.URI));
+		assertFalse(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.URL));
+		assertFalse(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.UUID));
+		assertFalse(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertFalse(AttributeType.TIMESTAMP.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertFalse(AttributeType.URI.isAssignableFrom(AttributeType.BOOLEAN));
+		assertFalse(AttributeType.URI.isAssignableFrom(AttributeType.COLOR));
+		assertFalse(AttributeType.URI.isAssignableFrom(AttributeType.DATE));
+		assertFalse(AttributeType.URI.isAssignableFrom(AttributeType.IMAGE));
+		assertFalse(AttributeType.URI.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.URI.isAssignableFrom(AttributeType.OBJECT));
+		assertFalse(AttributeType.URI.isAssignableFrom(AttributeType.POINT));
+		assertFalse(AttributeType.URI.isAssignableFrom(AttributeType.POINT3D));
+		assertFalse(AttributeType.URI.isAssignableFrom(AttributeType.POLYLINE));
+		assertFalse(AttributeType.URI.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertFalse(AttributeType.URI.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.URI.isAssignableFrom(AttributeType.STRING));
+		assertFalse(AttributeType.URI.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertTrue(AttributeType.URI.isAssignableFrom(AttributeType.URI));
+		assertTrue(AttributeType.URI.isAssignableFrom(AttributeType.URL));
+		assertTrue(AttributeType.URI.isAssignableFrom(AttributeType.UUID));
+		assertTrue(AttributeType.URI.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertFalse(AttributeType.URI.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertFalse(AttributeType.URL.isAssignableFrom(AttributeType.BOOLEAN));
+		assertFalse(AttributeType.URL.isAssignableFrom(AttributeType.COLOR));
+		assertFalse(AttributeType.URL.isAssignableFrom(AttributeType.DATE));
+		assertFalse(AttributeType.URL.isAssignableFrom(AttributeType.IMAGE));
+		assertFalse(AttributeType.URL.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.URL.isAssignableFrom(AttributeType.OBJECT));
+		assertFalse(AttributeType.URL.isAssignableFrom(AttributeType.POINT));
+		assertFalse(AttributeType.URL.isAssignableFrom(AttributeType.POINT3D));
+		assertFalse(AttributeType.URL.isAssignableFrom(AttributeType.POLYLINE));
+		assertFalse(AttributeType.URL.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertFalse(AttributeType.URL.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.URL.isAssignableFrom(AttributeType.STRING));
+		assertFalse(AttributeType.URL.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertTrue(AttributeType.URL.isAssignableFrom(AttributeType.URI));
+		assertTrue(AttributeType.URL.isAssignableFrom(AttributeType.URL));
+		assertFalse(AttributeType.URL.isAssignableFrom(AttributeType.UUID));
+		assertTrue(AttributeType.URL.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertFalse(AttributeType.URL.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.BOOLEAN));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.COLOR));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.DATE));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.IMAGE));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.OBJECT));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.POINT));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.POINT3D));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.POLYLINE));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.STRING));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.URI));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.URL));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.UUID));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertTrue(AttributeType.UUID.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertFalse(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.BOOLEAN));
+		assertFalse(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.COLOR));
+		assertFalse(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.DATE));
+		assertFalse(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.IMAGE));
+		assertFalse(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.OBJECT));
+		assertFalse(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.POINT));
+		assertFalse(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.POINT3D));
+		assertFalse(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.POLYLINE));
+		assertFalse(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertFalse(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.STRING));
+		assertFalse(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertTrue(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.URI));
+		assertTrue(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.URL));
+		assertTrue(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertFalse(AttributeType.INET_ADDRESS.isAssignableFrom(AttributeType.ENUMERATION));
+
+		assertFalse(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.BOOLEAN));
+		assertFalse(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.COLOR));
+		assertFalse(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.DATE));
+		assertFalse(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.IMAGE));
+		assertFalse(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.INTEGER));
+		assertTrue(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.OBJECT));
+		assertFalse(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.POINT));
+		assertFalse(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.POINT3D));
+		assertFalse(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.POLYLINE));
+		assertFalse(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.POLYLINE3D));
+		assertFalse(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.REAL));
+		assertTrue(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.STRING));
+		assertFalse(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.TIMESTAMP));
+		assertFalse(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.URI));
+		assertFalse(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.URL));
+		assertFalse(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.INET_ADDRESS));
+		assertTrue(AttributeType.ENUMERATION.isAssignableFrom(AttributeType.ENUMERATION));
+	}
+
+}

Added: trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeValueComparatorTest.java
===================================================================
--- trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeValueComparatorTest.java	                        (rev 0)
+++ trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeValueComparatorTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,107 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+import org.arakhne.afc.attrs.AbstractAttrTestCase;
+import org.arakhne.afc.attrs.attr.AttributeException;
+import org.arakhne.afc.attrs.attr.AttributeValueComparator;
+import org.arakhne.afc.attrs.attr.AttributeValueImpl;
+
+/**
+ * Test of AttributeValueComparator.
+ * 
+ * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AttributeValueComparatorTest extends AbstractAttrTestCase {
+
+	/**
+	 * @throws AttributeException
+	 */
+	public static void testCompare() throws AttributeException {
+		double base_d = Math.random();
+		long base_l = (long)base_d;
+		AttributeValueImpl attr1 = new AttributeValueImpl(base_d);		
+		AttributeValueImpl attr2 = new AttributeValueImpl(base_d+1);
+		AttributeValueImpl attr3 = new AttributeValueImpl(base_d-1);
+		
+		AttributeValueImpl attr4 = new AttributeValueImpl(base_l);		
+		AttributeValueImpl attr5 = new AttributeValueImpl(attr1.getInteger());
+		
+		AttributeValueImpl attr6 = new AttributeValueImpl("bonjour"); //$NON-NLS-1$
+		
+		AttributeValueComparator comp = new AttributeValueComparator();
+			
+		//----------- attr1 -> *
+		assertEquals(0, comp.compare(attr1,attr1));
+		assertStrictlyNegative(comp.compare(attr1,attr2));
+		assertStrictlyPositive(comp.compare(attr1,attr3));
+		assertPositive(comp.compare(attr1,attr4));
+		assertPositive(comp.compare(attr1,attr5));
+		assertStrictlyNegative(comp.compare(attr1,attr6));
+
+		//----------- attr2 -> *
+		assertStrictlyPositive(comp.compare(attr2,attr1));
+		assertEquals(0, comp.compare(attr2,attr2));
+		assertStrictlyPositive(comp.compare(attr2,attr3));
+		assertStrictlyPositive(comp.compare(attr2,attr4));
+		assertStrictlyPositive(comp.compare(attr2,attr5));
+		assertStrictlyNegative(comp.compare(attr2,attr6));
+
+		//----------- attr3 -> *
+		assertStrictlyNegative(comp.compare(attr3,attr1));
+		assertStrictlyNegative(comp.compare(attr3,attr2));
+		assertEquals(0, comp.compare(attr3,attr3));
+		assertStrictlyNegative(comp.compare(attr3,attr4));
+		assertStrictlyNegative(comp.compare(attr3,attr5));
+		assertStrictlyNegative(comp.compare(attr3,attr6));
+		
+
+		//----------- attr4 -> *
+		assertNegative(comp.compare(attr4,attr1));
+		assertStrictlyNegative(comp.compare(attr4,attr2));
+		assertStrictlyPositive(comp.compare(attr4,attr3));
+		assertEquals(0, comp.compare(attr4,attr4));
+		assertEquals(0,comp.compare(attr4,attr5));
+		assertStrictlyNegative(comp.compare(attr4,attr6));
+
+		//----------- attr5 -> *
+		assertNegative(comp.compare(attr5,attr1));
+		assertStrictlyNegative(comp.compare(attr5,attr2));
+		assertStrictlyPositive(comp.compare(attr5,attr3));
+		assertEquals(0,comp.compare(attr5,attr4));
+		assertEquals(0, comp.compare(attr5,attr5));
+		assertStrictlyNegative(comp.compare(attr5,attr6));
+
+		//----------- attr6 -> *
+		assertStrictlyPositive(comp.compare(attr6,attr1));
+		assertStrictlyPositive(comp.compare(attr6,attr2));
+		assertStrictlyPositive(comp.compare(attr6,attr3));
+		assertStrictlyPositive(comp.compare(attr6,attr4));
+		assertStrictlyPositive(comp.compare(attr6,attr5));
+		assertEquals(0, comp.compare(attr6,attr6));
+	}
+
+}

Added: trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeValueTest.java
===================================================================
--- trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeValueTest.java	                        (rev 0)
+++ trunk/attrs/src/test/java/org/arakhne/afc/attrs/attr/AttributeValueTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,3494 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.attr;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Random;
+
+import org.arakhne.afc.attrs.AbstractAttrTestCase;
+import org.arakhne.afc.attrs.attr.AttributeException;
+import org.arakhne.afc.attrs.attr.AttributeNotInitializedException;
+import org.arakhne.afc.attrs.attr.AttributeType;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+import org.arakhne.afc.attrs.attr.AttributeValueImpl;
+import org.arakhne.afc.attrs.attr.InvalidAttributeTypeException;
+import org.arakhne.afc.attrs.attr.Timestamp;
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object3d.Point3f;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Point3D;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Image;
+import org.arakhne.afc.ui.vector.VectorToolkit;
+
+/**
+ * Test of AttributeValue.
+ * 
+ * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AttributeValueTest extends AbstractAttrTestCase {
+
+	/**
+	 * @param attr
+	 * @param type
+	 */
+	protected static void assertAllGetFailed(AttributeValue attr, AttributeType type) {
+		try {
+			attr.getValue();
+			fail("getValue: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getBoolean();
+			fail("getBoolean: the exception InvalidAttributeTypeException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getColor();
+			fail("getColor: the exception InvalidAttributeTypeException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getDate();
+			fail("getDate: the exception InvalidAttributeTypeException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getImage();
+			fail("getImage: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getInteger();
+			fail("getInteger: the exception InvalidAttributeTypeException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getJavaObject();
+			if (type.isBaseType())
+				fail("getJavaObject: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getPoint();
+			fail("getPoint: the exception InvalidAttributeTypeException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getPoint3D();
+			fail("getPoint3D: the exception InvalidAttributeTypeException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getPolyline();
+			fail("getPolyline: the exception InvalidAttributeTypeException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getPolyline3D();
+			fail("getPolyline3D: the exception InvalidAttributeTypeException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getReal();
+			fail("getReal: the exception InvalidAttributeTypeException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getString();
+			fail("getString: the exception AttributeNotInitializedException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeNotInitializedException _) {
+			//
+		}
+		catch(InvalidAttributeTypeException _) {
+			if (!attr.isObjectValue())
+				fail("unexpected exception InvalidAttributeTypeException for "+type); //$NON-NLS-1$
+		}
+
+		try {
+			attr.getTimestamp();
+			fail("getTimestamp: the exception InvalidAttributeTypeException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getUUID();
+			fail("getUUID: the exception InvalidAttributeTypeException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getURI();
+			fail("getURI: the exception InvalidAttributeTypeException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+
+		try {
+			attr.getURL();
+			fail("getURL: the exception InvalidAttributeTypeException was not thrown for "+type); //$NON-NLS-1$
+		}
+		catch(AttributeException _) {
+			// expected case
+		}
+	}
+
+	/**
+	 * @param attr
+	 * @param methodName
+	 * @throws Exception
+	 */
+	protected static void assertAttributeException(AttributeValue attr, String methodName) throws Exception {
+		try {
+			Class<? extends AttributeValue> clazz = attr.getClass();
+			Method method = clazz.getMethod(methodName);
+			method.invoke(attr);
+			fail("the exception AttributeException was not thrown"); //$NON-NLS-1$
+		}
+		catch(InvocationTargetException e) {
+			Throwable ex = e.getTargetException();
+			if (ex instanceof AssertionError) {
+				throw (AssertionError)ex;
+			}
+			if (ex instanceof AttributeException) {
+				//
+			}
+			else {
+				fail("the exception AttributeException was not thrown"); //$NON-NLS-1$
+			}
+		}
+	}
+
+	/**
+	 */
+	public static void testAttributeValueImpl() {
+		AttributeValue attr = new AttributeValueImpl();
+		
+		assertFalse(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(AttributeType.OBJECT, attr.getType());
+		
+		try {
+			attr.getValue();
+			fail("the exception AttributeNotInitializedException was not thrown"); //$NON-NLS-1$
+		}
+		catch(AttributeNotInitializedException _) {
+			// expected case
+		}
+		catch(InvalidAttributeTypeException _) {
+			fail("unexpected exception InvalidAttributeTypeException"); //$NON-NLS-1$
+		}
+
+		try {
+			attr.getBoolean();
+			fail("the exception AttributeNotInitializedException was not thrown"); //$NON-NLS-1$
+		}
+		catch(AttributeNotInitializedException _) {
+			// expected case
+		}
+		catch(InvalidAttributeTypeException _) {
+			fail("unexpected exception InvalidAttributeTypeException"); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplAttributeType() throws Exception {
+		AttributeType[] values = AttributeType.values();
+		//AttributeType[] values = new AttributeType[] {AttributeType.OBJECT};
+		for (AttributeType type : values) {
+			AttributeValue attr = new AttributeValueImpl(type);
+			
+			assertEquals(type, attr.getType());
+
+			assertFalse(attr.isAssigned());
+			assertEquals(type.isBaseType(),attr.isBaseType());
+			assertEquals("on type "+type, //$NON-NLS-1$
+					!type.isBaseType(),
+					attr.isObjectValue());
+			
+			if (type.isNullAllowed()) {
+				assertAttributeException(attr, "getBoolean"); //$NON-NLS-1$
+				assertAttributeException(attr, "getColor"); //$NON-NLS-1$
+				assertAttributeException(attr, "getDate"); //$NON-NLS-1$
+				assertAttributeException(attr, "getImage"); //$NON-NLS-1$
+				assertAttributeException(attr, "getInteger"); //$NON-NLS-1$
+				assertNull(attr.getJavaObject());
+				assertAttributeException(attr, "getPoint"); //$NON-NLS-1$
+				assertAttributeException(attr, "getPoint3D"); //$NON-NLS-1$
+				assertAttributeException(attr, "getPolyline"); //$NON-NLS-1$
+				assertAttributeException(attr, "getPolyline3D"); //$NON-NLS-1$
+				assertAttributeException(attr, "getReal"); //$NON-NLS-1$
+				assertAttributeException(attr, "getString"); //$NON-NLS-1$
+				assertAttributeException(attr, "getTimestamp"); //$NON-NLS-1$
+				assertAttributeException(attr, "getURI"); //$NON-NLS-1$
+				assertAttributeException(attr, "getURL"); //$NON-NLS-1$
+				assertAttributeException(attr, "getUUID"); //$NON-NLS-1$
+				assertAttributeException(attr, "getValue"); //$NON-NLS-1$
+			}
+			else {
+				assertAllGetFailed(attr, type);
+			}
+		}
+	}
+	
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplBoolean() throws Exception {
+		AttributeValue attr = new AttributeValueImpl(false);
+		
+		assertEquals(AttributeType.BOOLEAN, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertFalse((Boolean)attr.getValue());
+		assertFalse(attr.getBoolean());
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals(0, attr.getInteger());
+		assertEquals(0., attr.getReal());
+		assertEquals(0, attr.getTimestamp());
+		assertEquals(Boolean.toString(false),attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint3D"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplColor() throws Exception {
+		String txt = "255;0;0;255"; //$NON-NLS-1$
+		AttributeValue attr = new AttributeValueImpl(VectorToolkit.RED);
+		
+		assertEquals(AttributeType.COLOR, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(VectorToolkit.RED,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertEquals(VectorToolkit.RED,attr.getColor());
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals(VectorToolkit.RED.getRGB(), attr.getInteger());
+		assertEquals((double)VectorToolkit.RED.getRGB(), attr.getReal());
+		assertEquals(VectorToolkit.RED.getRGB(), attr.getTimestamp());
+		assertEquals(txt,attr.getString());
+		assertEquals(VectorToolkit.RED, attr.getJavaObject());
+		assertEquals(new Point2f(255,0),attr.getPoint());
+		assertEquals(new Point3f(255,0,0),attr.getPoint3D());
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplDate() throws Exception {
+		Date currentDate = new Date();
+		SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); //$NON-NLS-1$
+		String txt = fmt.format(currentDate);
+		AttributeValue attr = new AttributeValueImpl(currentDate);
+		
+		assertEquals(AttributeType.DATE, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(currentDate,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertEquals(VectorToolkit.color((int)currentDate.getTime()), attr.getColor());
+		assertEquals(currentDate,attr.getDate());
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals(currentDate.getTime(),attr.getInteger());
+		assertEquals((double)currentDate.getTime(),attr.getReal());
+		assertEquals(currentDate.getTime(),attr.getTimestamp());
+		assertEquals(txt,attr.getString());
+		assertEquals(currentDate, attr.getJavaObject());
+		assertEquals(new Point2f(currentDate.getTime(), 0), attr.getPoint());
+		assertEquals(new Point3f(currentDate.getTime(), 0, 0), attr.getPoint3D());
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplFloat() throws Exception {
+		float nb = (float)Math.random();
+		String txt = Double.toString(nb);
+		AttributeValue attr = new AttributeValueImpl(nb);
+		
+		assertEquals(AttributeType.REAL, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(nb,((Number)attr.getValue()).floatValue());
+		assertEquals(nb!=0f, attr.getBoolean());
+		assertEquals(VectorToolkit.color((int)nb), attr.getColor());
+		assertEquals(new Date((long)nb),attr.getDate());
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals((long)nb,attr.getInteger());
+		assertEquals((double)nb,attr.getReal());
+		assertEquals((long)nb,attr.getTimestamp());
+		assertEquals(txt,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(new Point2f(nb,0),attr.getPoint());
+		assertEquals(new Point3f(nb,0,0),attr.getPoint3D());
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplDouble() throws Exception {
+		double nb = Math.random();
+		String txt = Double.toString(nb);
+		AttributeValue attr = new AttributeValueImpl(nb);
+		
+		assertEquals(AttributeType.REAL, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(nb,((Number)attr.getValue()).doubleValue());
+		assertEquals(nb!=0., attr.getBoolean());
+		assertEquals(VectorToolkit.color((int)nb), attr.getColor());
+		assertEquals(new Date((long)nb),attr.getDate());
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals((long)nb,attr.getInteger());
+		assertEquals(nb,attr.getReal());
+		assertEquals((long)nb,attr.getTimestamp());
+		assertEquals(txt,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(new Point2f(nb,0),attr.getPoint());
+		assertEquals(new Point3f(nb,0,0),attr.getPoint3D());
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplIcon() throws Exception {
+		Image ic = VectorToolkit.image(1,1,false);
+		AttributeValue attr = new AttributeValueImpl(ic);
+		
+		assertEquals(AttributeType.IMAGE, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(ic,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertEquals(ic,attr.getImage());
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(ic.toString(), attr.getString());
+		assertEquals(ic,attr.getJavaObject());
+		assertAttributeException(attr,"getPoint"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint3D"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplInt() throws Exception {
+		int nb = new Random().nextInt();
+		String txt = Long.toString(nb);
+		AttributeValue attr = new AttributeValueImpl(nb);
+		
+		assertEquals(AttributeType.INTEGER, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(nb,((Number)attr.getValue()).intValue());
+		assertEquals(nb!=0, attr.getBoolean());
+		assertEquals(VectorToolkit.color(nb), attr.getColor());
+		assertEquals(new Date(nb),attr.getDate());
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals(nb,attr.getInteger());
+		assertEquals(nb,(int)attr.getReal());
+		assertEquals(nb,attr.getTimestamp());
+		assertEquals(txt,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(new Point2f(nb,0),attr.getPoint());
+		assertEquals(new Point3f(nb,0,0),attr.getPoint3D());
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplLong() throws Exception {
+		long nb = new Random().nextLong();
+		String txt = Long.toString(nb);
+		AttributeValue attr = new AttributeValueImpl(nb);
+		
+		assertEquals(AttributeType.INTEGER, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(nb,((Number)attr.getValue()).longValue());
+		assertEquals(nb!=0, attr.getBoolean());
+		assertEquals(VectorToolkit.color((int)nb), attr.getColor());
+		assertEquals(new Date(nb),attr.getDate());
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals(nb,attr.getInteger());
+		assertEquals((double)nb,attr.getReal());
+		assertEquals(nb,attr.getTimestamp());
+		assertEquals(txt,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(new Point2f(nb,0),attr.getPoint());
+		assertEquals(new Point3f(nb,0,0),attr.getPoint3D());
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplPoint2d() throws Exception {
+		Point2f pt = new Point2f((float)Math.random(),(float)Math.random());
+		Point3f pt3d = new Point3f(pt.getX(),pt.getY(),0);
+		String str = pt.getX()+";"+pt.getY(); //$NON-NLS-1$
+		AttributeValue attr = new AttributeValueImpl(pt);
+		
+		assertEquals(AttributeType.POINT, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(pt,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertEquals(VectorToolkit.color(pt.getX(),pt.getY(),0f),attr.getColor());
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertEquals(pt, attr.getJavaObject());
+		assertEquals(pt,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+	
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplDoubleDouble() throws Exception {
+		float x = (float)Math.random();
+		float y = (float)Math.random();
+		Point2f pt = new Point2f(x,y);
+		Point3f pt3d = new Point3f(x,y,0);
+		String str = x+";"+y; //$NON-NLS-1$
+		AttributeValue attr = new AttributeValueImpl(x,y);
+		
+		assertEquals(AttributeType.POINT, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(pt,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertEquals(VectorToolkit.color(pt.getX(),pt.getY(),0f),attr.getColor());
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertEquals(pt, attr.getJavaObject());
+		assertEquals(pt,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+	
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplPoint3d() throws Exception {
+		float x = (float)Math.random();
+		float y = (float)Math.random();
+		float z = (float)Math.random();
+		Point3f pt = new Point3f(x,y,z);
+		Point2f pt2d = new Point2f(x,y);
+		String str = x+";"+y+";"+z; //$NON-NLS-1$ //$NON-NLS-2$
+		AttributeValue attr = new AttributeValueImpl(pt);
+		
+		assertEquals(AttributeType.POINT3D, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(pt,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertEquals(VectorToolkit.color(pt.getX(),pt.getY(),pt.getZ()),attr.getColor());
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertEquals(pt, attr.getJavaObject());
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt2d},attr.getPolyline());
+		assertEquals(new Point3D[]{pt},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplDoubleDoubleDouble() throws Exception {
+		double x = Math.random();
+		double y = Math.random();
+		double z = Math.random();
+		Point3f pt = new Point3f(x,y,z);
+		Point2f pt2d = new Point2f(x,y);
+		String str = ((float)x)+";"+((float)y)+";"+((float)z); //$NON-NLS-1$ //$NON-NLS-2$
+		AttributeValue attr = new AttributeValueImpl(x,y,z);
+		
+		assertEquals(AttributeType.POINT3D, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(pt,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertEquals(VectorToolkit.color(pt.getX(),pt.getY(),pt.getZ()),attr.getColor());
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertEquals(pt, attr.getJavaObject());
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt2d},attr.getPolyline());
+		assertEquals(new Point3D[]{pt},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplString_random() throws Exception {
+		double x = Math.random();
+		Point2f pt2d = new Point2f(x,0);
+		Point3f pt3d = new Point3f(x,0,0);
+		String str = Double.toHexString(x);
+		AttributeValue attr = new AttributeValueImpl(str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertEquals(x,attr.getReal());
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(new Point2D[]{pt2d},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplString_boolean() throws Exception {
+		String str = Boolean.toString(true);
+		AttributeValue attr = new AttributeValueImpl(str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertTrue(attr.getBoolean());
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint3D"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplString_Color() throws Exception {
+		Color c = VectorToolkit.RED;
+		Point2D pt2d = new Point2f(c.getRed(),c.getGreen());
+		Point2D pt2d2 = new Point2f(c.getBlue(),0);
+		Point3D pt3d = new Point3f(c.getRed(),c.getGreen(),c.getBlue());
+		String str = c.getRed()+";"+c.getGreen()+";"+c.getBlue();  //$NON-NLS-1$//$NON-NLS-2$
+		AttributeValue attr = new AttributeValueImpl(str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertEquals(c,attr.getColor());
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt2d,pt2d2},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplString_Date() throws Exception {
+		Date currentDate = new Date();
+		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); //$NON-NLS-1$
+		String str = format.format(currentDate); 
+		AttributeValue attr = new AttributeValueImpl(str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertEpsilonEquals(currentDate,attr.getDate());
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint3D"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplString_JDate() throws Exception {
+		Date currentDate = new Date();
+		String str = DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.FULL).format(currentDate); 
+		AttributeValue attr = new AttributeValueImpl(str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertEpsilonEquals(currentDate,attr.getDate());
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint3D"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPolyline3D"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplString_Integer() throws Exception {
+		int nb = new Random().nextInt(20000)+256;
+		Point2D pt2d = new Point2f(nb,0);
+		Point3D pt3d = new Point3f(nb,0,0);
+		String str = Integer.toString(nb);
+		AttributeValue attr = new AttributeValueImpl(str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals(nb,attr.getInteger());
+		assertEquals((double)nb,attr.getReal());
+		assertEquals(nb,attr.getTimestamp());
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt2d},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplString_Long() throws Exception {
+		long nb = new Random().nextInt(20000)+256;
+		Point2D pt2d = new Point2f(nb,0);
+		Point3D pt3d = new Point3f(nb,0,0);
+		String str = Long.toString(nb);
+		AttributeValue attr = new AttributeValueImpl(str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertEquals(nb,attr.getInteger());
+		assertEquals((double)nb,attr.getReal());
+		assertEquals(nb,attr.getTimestamp());
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt2d},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplString_Double() throws Exception {
+		double nb = Math.random()+256;
+		Point2D pt2d = new Point2f(nb,0);
+		Point3D pt3d = new Point3f(nb,0,0);
+		String str = Double.toString(nb);
+		AttributeValue attr = new AttributeValueImpl(str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertEquals(nb,attr.getReal());
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt2d},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplString_Point2D() throws Exception {
+		double x = Math.random()+256;
+		double y = Math.random()+256;
+		Point2D pt2d = new Point2f(x,y);
+		Point3D pt3d = new Point3f(x,y,0);
+		String str = x+";"+y; //$NON-NLS-1$
+		AttributeValue attr = new AttributeValueImpl(str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt2d},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplString_Point3D() throws Exception {
+		double x = Math.random()+256;
+		double y = Math.random()+256;
+		double z = Math.random()+256;
+		Point2D pt2d = new Point2f(x,y);
+		Point2D pt2d2 = new Point2f(z,0);
+		Point3D pt3d = new Point3f(x,y,z);
+		String str = x+";"+y+";"+z; //$NON-NLS-1$ //$NON-NLS-2$
+		AttributeValue attr = new AttributeValueImpl(str);
+		
+		assertEquals(AttributeType.STRING, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertTrue(attr.isBaseType());
+		assertFalse(attr.isObjectValue());
+		
+		assertEquals(str,attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertAttributeException(attr,"getJavaObject"); //$NON-NLS-1$
+		assertEquals(pt2d,attr.getPoint());
+		assertEquals(pt3d,attr.getPoint3D());
+		assertEquals(new Point2D[]{pt2d,pt2d2},attr.getPolyline());
+		assertEquals(new Point3D[]{pt3d},attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplPoint2DArray() throws Exception {
+		double x1 = Math.random();
+		double y1 = Math.random();
+		double x2 = Math.random();
+		double y2 = Math.random();
+
+		Point2D pt1 = new Point2f(x1,y1);
+		Point2D pt2 = new Point2f(x2,y2);
+		
+		Point2D[] list = new Point2D[]{ pt1, pt2 };
+		Point3D[] list2 = new Point3D[]{ new Point3f(x1,y1,0), new Point3f(x2,y2,0) };
+
+		String str = ((float)x1)+";"+((float)y1)+";"+((float)x2)+";"+((float)y2); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+		AttributeValue attr = new AttributeValueImpl(list);
+		
+		assertEquals(AttributeType.POLYLINE, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(list,(Point2D[])attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertTrue(Arrays.equals(list, (Point2D[])attr.getJavaObject()));
+		assertAttributeException(attr,"getPoint"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint3D"); //$NON-NLS-1$
+		assertEquals(list,attr.getPolyline());
+		assertEquals(list2,attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public static void testAttributeValueImplPoint3DArray() throws Exception {
+		double x1 = Math.random();
+		double y1 = Math.random();
+		double z1 = Math.random();
+		double x2 = Math.random();
+		double y2 = Math.random();
+		double z2 = Math.random();
+
+		Point3D pt1 = new Point3f(x1,y1,z1);
+		Point3D pt2 = new Point3f(x2,y2,z2);
+		
+		Point3D[] list = new Point3D[]{ pt1, pt2 };
+		Point2D[] list2 = new Point2D[]{ new Point2f(x1,y1), new Point2f(x2,y2) };
+
+		String str = ((float)x1)+";"+((float)y1)+";"+((float)z1)+";"+((float)x2)+";"+((float)y2)+";"+((float)z2); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+
+		AttributeValue attr = new AttributeValueImpl(list);
+		
+		assertEquals(AttributeType.POLYLINE3D, attr.getType());
+
+		assertTrue(attr.isAssigned());
+		assertFalse(attr.isBaseType());
+		assertTrue(attr.isObjectValue());
+		
+		assertEquals(list,(Point3D[])attr.getValue());
+		assertAttributeException(attr,"getBoolean"); //$NON-NLS-1$
+		assertAttributeException(attr,"getColor"); //$NON-NLS-1$
+		assertAttributeException(attr,"getDate"); //$NON-NLS-1$
+		assertAttributeException(attr,"getImage"); //$NON-NLS-1$
+		assertAttributeException(attr,"getInteger"); //$NON-NLS-1$
+		assertAttributeException(attr,"getReal"); //$NON-NLS-1$
+		assertAttributeException(attr,"getTimestamp"); //$NON-NLS-1$
+		assertEquals(str,attr.getString());
+		assertTrue(Arrays.equals(list, (Point3D[])attr.getJavaObject()));
+		assertAttributeException(attr,"getPoint"); //$NON-NLS-1$
+		assertAttributeException(attr,"getPoint3D"); //$NON-NLS-1$
+		assertEquals(list2,attr.getPolyline());
+		assertEquals(list,attr.getPolyline3D());
+	}
+
+	/**
+	 * @throws AttributeException
+	 */
+	public static void testCast() throws AttributeException {
+		AttributeValue attr1, attr2;
+		String msg, str;
+		AttributeType source, target;
+		long time;
+		Color col;
+		Date dt;
+		DateFormat format;
+		Point2D pt2d;
+		Point3D pt3d;
+		
+		//
+		// SOURCE: BOOLEAN
+		//
+		
+		source = AttributeType.BOOLEAN;
+		target = AttributeType.BOOLEAN;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getBoolean());
+
+		source = AttributeType.BOOLEAN;
+		target = AttributeType.COLOR;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getColor());
+
+		source = AttributeType.BOOLEAN;
+		target = AttributeType.DATE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Date)attr2.getValue()).getTime()<=time);
+		assertTrue(msg,attr2.getDate().getTime()<=time);
+
+		source = AttributeType.BOOLEAN;
+		target = AttributeType.IMAGE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertException(msg, AttributeNotInitializedException.class, attr2, "getImage"); //$NON-NLS-1$
+
+		source = AttributeType.BOOLEAN;
+		target = AttributeType.INTEGER;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getInteger());
+
+		source = AttributeType.BOOLEAN;
+		target = AttributeType.OBJECT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getJavaObject());
+
+		source = AttributeType.BOOLEAN;
+		target = AttributeType.POINT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint());
+
+		source = AttributeType.BOOLEAN;
+		target = AttributeType.POINT3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint3D());
+
+		source = AttributeType.BOOLEAN;
+		target = AttributeType.POLYLINE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), (Point2D[])attr2.getValue());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), attr2.getPolyline());
+
+		source = AttributeType.BOOLEAN;
+		target = AttributeType.POLYLINE3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), (Point3D[])attr2.getValue());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), attr2.getPolyline3D());
+
+		source = AttributeType.BOOLEAN;
+		target = AttributeType.REAL;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getReal());
+
+		source = AttributeType.BOOLEAN;
+		target = AttributeType.STRING;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,Boolean.toString((Boolean)AttributeType.BOOLEAN.getDefaultValue()), attr2.getValue());
+		assertEquals(msg,Boolean.toString((Boolean)AttributeType.BOOLEAN.getDefaultValue()), attr2.getString());
+
+		source = AttributeType.BOOLEAN;
+		target = AttributeType.TIMESTAMP;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertTrue(msg,attr2.getValue() instanceof Timestamp);
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Number)attr2.getValue()).longValue()<=time);
+		assertTrue(msg,attr2.getTimestamp()<=time);
+
+		//
+		// SOURCE: COLOR
+		//
+		
+		source = AttributeType.COLOR;
+		target = AttributeType.BOOLEAN;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getBoolean());
+
+		source = AttributeType.COLOR;
+		target = AttributeType.COLOR;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getColor());
+
+		source = AttributeType.COLOR;
+		target = AttributeType.DATE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Date)attr2.getValue()).getTime()<=time);
+		assertTrue(msg,attr2.getDate().getTime()<=time);
+
+		source = AttributeType.COLOR;
+		target = AttributeType.IMAGE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertException(msg, AttributeNotInitializedException.class, attr2, "getImage"); //$NON-NLS-1$
+
+		source = AttributeType.COLOR;
+		target = AttributeType.INTEGER;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,((Color)source.getDefaultValue()).getRGB(), ((Long)attr2.getValue()).intValue());
+		assertEquals(msg,((Color)source.getDefaultValue()).getRGB(), (int)attr2.getInteger());
+
+		source = AttributeType.COLOR;
+		target = AttributeType.OBJECT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,VectorToolkit.color(0,0,0), attr2.getValue());
+		assertEquals(msg,VectorToolkit.color(0,0,0), attr2.getJavaObject());
+
+		source = AttributeType.COLOR;
+		target = AttributeType.POINT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint());
+
+		source = AttributeType.COLOR;
+		target = AttributeType.POINT3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint3D());
+
+		source = AttributeType.COLOR;
+		target = AttributeType.POLYLINE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), (Point2D[])attr2.getValue());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), attr2.getPolyline());
+
+		source = AttributeType.COLOR;
+		target = AttributeType.POLYLINE3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), (Point3D[])attr2.getValue());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), attr2.getPolyline3D());
+
+		source = AttributeType.COLOR;
+		target = AttributeType.REAL;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,((Color)source.getDefaultValue()).getRGB(), ((Number)attr2.getValue()).intValue());
+		assertEquals(msg,((Color)source.getDefaultValue()).getRGB(), (int)attr2.getReal());
+
+		source = AttributeType.COLOR;
+		target = AttributeType.STRING;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		col = (Color)source.getDefaultValue();
+		str = col.getRed()+";"+col.getGreen()+";"+col.getBlue()+";"+col.getAlpha(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		assertEquals(msg,str, attr2.getValue());
+		assertEquals(msg,str, attr2.getString());
+
+		source = AttributeType.COLOR;
+		target = AttributeType.TIMESTAMP;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertTrue(msg,attr2.getValue() instanceof Timestamp);
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Number)attr2.getValue()).longValue()<=time);
+		assertTrue(msg,attr2.getTimestamp()<=time);
+
+		//
+		// SOURCE: DATE
+		//
+		
+		source = AttributeType.DATE;
+		target = AttributeType.BOOLEAN;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getBoolean());
+
+		source = AttributeType.DATE;
+		target = AttributeType.DATE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Date)attr2.getValue()).getTime()<=time);
+		assertTrue(msg,attr2.getDate().getTime()<=time);
+
+		source = AttributeType.DATE;
+		target = AttributeType.IMAGE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertException(msg, AttributeNotInitializedException.class, attr2, "getImage"); //$NON-NLS-1$
+
+		source = AttributeType.DATE;
+		target = AttributeType.INTEGER;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		dt = attr2.getDate();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,dt.getTime(), attr2.getValue());
+		assertEquals(msg,dt.getTime(), attr2.getInteger());
+
+		source = AttributeType.DATE;
+		target = AttributeType.OBJECT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertNotNull(msg, attr2.getValue());
+		assertNotNull(msg,attr2.getJavaObject());
+
+		source = AttributeType.DATE;
+		target = AttributeType.POINT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		Point2D pt = attr2.getPoint();
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,pt, attr2.getValue());
+		assertEquals(msg,pt, attr2.getPoint());
+
+		source = AttributeType.DATE;
+		target = AttributeType.POINT3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		Point3D pt3 = attr2.getPoint3D();
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,pt3, attr2.getValue());
+		assertEquals(msg,pt3, attr2.getPoint3D());
+
+		source = AttributeType.DATE;
+		target = AttributeType.POLYLINE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), (Point2D[])attr2.getValue());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), attr2.getPolyline());
+
+		source = AttributeType.DATE;
+		target = AttributeType.POLYLINE3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), (Point3D[])attr2.getValue());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), attr2.getPolyline3D());
+
+		source = AttributeType.DATE;
+		target = AttributeType.REAL;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		dt = attr2.getDate();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,dt.getTime(), ((Double)attr2.getValue()).longValue());
+		assertEquals(msg,dt.getTime(), (long)attr2.getReal());
+
+		source = AttributeType.DATE;
+		target = AttributeType.STRING;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		dt = (Date)source.getDefaultValue();
+		format = new SimpleDateFormat("yyyy-MM-dd"); //$NON-NLS-1$
+		str = format.format(dt);
+		assertEquals(msg,str, attr2.getValue());
+		assertEquals(msg,str, attr2.getString());
+
+		source = AttributeType.DATE;
+		target = AttributeType.TIMESTAMP;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertTrue(msg,attr2.getValue() instanceof Timestamp);
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Number)attr2.getValue()).longValue()<=time);
+		assertTrue(msg,attr2.getTimestamp()<=time);
+
+		//
+		// SOURCE: ICON
+		//
+		
+		source = AttributeType.IMAGE;
+		target = AttributeType.BOOLEAN;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getBoolean());
+
+		source = AttributeType.IMAGE;
+		target = AttributeType.COLOR;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getColor());
+
+		source = AttributeType.IMAGE;
+		target = AttributeType.DATE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Date)attr2.getValue()).getTime()<=time);
+		assertTrue(msg,attr2.getDate().getTime()<=time);
+
+		source = AttributeType.IMAGE;
+		target = AttributeType.IMAGE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertException(msg, AttributeNotInitializedException.class, attr2, "getImage"); //$NON-NLS-1$
+
+		source = AttributeType.IMAGE;
+		target = AttributeType.INTEGER;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(),attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(),attr2.getInteger());
+
+		source = AttributeType.IMAGE;
+		target = AttributeType.OBJECT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertNull(msg,attr2.getValue());
+		assertNull(msg,attr2.getJavaObject());
+
+		source = AttributeType.IMAGE;
+		target = AttributeType.POINT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint());
+
+		source = AttributeType.IMAGE;
+		target = AttributeType.POINT3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint3D());
+
+		source = AttributeType.IMAGE;
+		target = AttributeType.POLYLINE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), (Point2D[])attr2.getValue());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), attr2.getPolyline());
+
+		source = AttributeType.IMAGE;
+		target = AttributeType.POLYLINE3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), (Point3D[])attr2.getValue());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), attr2.getPolyline3D());
+
+		source = AttributeType.IMAGE;
+		target = AttributeType.REAL;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getReal());
+
+		source = AttributeType.IMAGE;
+		target = AttributeType.STRING;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue().toString(), attr2.getString());
+
+		source = AttributeType.IMAGE;
+		target = AttributeType.TIMESTAMP;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertTrue(msg,attr2.getValue() instanceof Timestamp);
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Number)attr2.getValue()).longValue()<=time);
+		assertTrue(msg,attr2.getTimestamp()<=time);
+
+		//
+		// SOURCE: INTEGER
+		//
+		
+		source = AttributeType.INTEGER;
+		target = AttributeType.BOOLEAN;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getBoolean());
+
+		source = AttributeType.INTEGER;
+		target = AttributeType.COLOR;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getColor());
+
+		source = AttributeType.INTEGER;
+		target = AttributeType.DATE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Date)attr2.getValue()).getTime()<=time);
+		assertTrue(msg,attr2.getDate().getTime()<=time);
+
+		source = AttributeType.INTEGER;
+		target = AttributeType.IMAGE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertException(msg, AttributeNotInitializedException.class, attr2, "getImage"); //$NON-NLS-1$
+
+		source = AttributeType.INTEGER;
+		target = AttributeType.INTEGER;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(),attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(),attr2.getInteger());
+
+		source = AttributeType.INTEGER;
+		target = AttributeType.OBJECT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertNull(msg,attr2.getValue());
+		assertNull(msg,attr2.getJavaObject());
+
+		source = AttributeType.INTEGER;
+		target = AttributeType.POINT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint());
+
+		source = AttributeType.INTEGER;
+		target = AttributeType.POINT3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint3D());
+
+		source = AttributeType.INTEGER;
+		target = AttributeType.POLYLINE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), (Point2D[])attr2.getValue());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), attr2.getPolyline());
+
+		source = AttributeType.INTEGER;
+		target = AttributeType.POLYLINE3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), (Point3D[])attr2.getValue());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), attr2.getPolyline3D());
+
+		source = AttributeType.INTEGER;
+		target = AttributeType.REAL;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getReal());
+
+		source = AttributeType.INTEGER;
+		target = AttributeType.STRING;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,"0", attr2.getValue()); //$NON-NLS-1$
+		assertEquals(msg,"0", attr2.getString()); //$NON-NLS-1$
+
+		source = AttributeType.INTEGER;
+		target = AttributeType.TIMESTAMP;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertTrue(msg,attr2.getValue() instanceof Timestamp);
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Number)attr2.getValue()).longValue()<=time);
+		assertTrue(msg,attr2.getTimestamp()<=time);
+
+		//
+		// SOURCE: OBJECT
+		//
+		
+		source = AttributeType.OBJECT;
+		target = AttributeType.BOOLEAN;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getBoolean());
+
+		source = AttributeType.OBJECT;
+		target = AttributeType.COLOR;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getColor());
+
+		source = AttributeType.OBJECT;
+		target = AttributeType.DATE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Date)attr2.getValue()).getTime()<=time);
+		assertTrue(msg,attr2.getDate().getTime()<=time);
+
+		source = AttributeType.OBJECT;
+		target = AttributeType.IMAGE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertException(msg, AttributeNotInitializedException.class, attr2, "getImage"); //$NON-NLS-1$
+
+		source = AttributeType.OBJECT;
+		target = AttributeType.INTEGER;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(),attr2.getValue());
+		assertException(msg,InvalidAttributeTypeException.class, attr2,"getImage"); //$NON-NLS-1$
+
+		source = AttributeType.OBJECT;
+		target = AttributeType.OBJECT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertNull(msg,attr2.getValue());
+		assertNull(msg,attr2.getJavaObject());
+
+		source = AttributeType.OBJECT;
+		target = AttributeType.POINT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint());
+
+		source = AttributeType.OBJECT;
+		target = AttributeType.POINT3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint3D());
+
+		source = AttributeType.OBJECT;
+		target = AttributeType.POLYLINE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), (Point2D[])attr2.getValue());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), attr2.getPolyline());
+
+		source = AttributeType.OBJECT;
+		target = AttributeType.POLYLINE3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), (Point3D[])attr2.getValue());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), attr2.getPolyline3D());
+
+		source = AttributeType.OBJECT;
+		target = AttributeType.REAL;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getReal());
+
+		source = AttributeType.OBJECT;
+		target = AttributeType.STRING;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getString());
+
+		source = AttributeType.OBJECT;
+		target = AttributeType.TIMESTAMP;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertTrue(msg,attr2.getValue() instanceof Timestamp);
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Number)attr2.getValue()).longValue()<=time);
+		assertTrue(msg,attr2.getTimestamp()<=time);
+
+		//
+		// SOURCE: POINT
+		//
+		
+		source = AttributeType.POINT;
+		target = AttributeType.BOOLEAN;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getBoolean());
+
+		source = AttributeType.POINT;
+		target = AttributeType.COLOR;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getColor());
+
+		source = AttributeType.POINT;
+		target = AttributeType.DATE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Date)attr2.getValue()).getTime()<=time);
+		assertTrue(msg,attr2.getDate().getTime()<=time);
+
+		source = AttributeType.POINT;
+		target = AttributeType.IMAGE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertException(msg, AttributeNotInitializedException.class, attr2, "getImage"); //$NON-NLS-1$
+
+		source = AttributeType.POINT;
+		target = AttributeType.INTEGER;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(),attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(),attr2.getInteger());
+
+		source = AttributeType.POINT;
+		target = AttributeType.OBJECT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,new Point2f(), attr2.getValue());
+		assertEquals(msg,new Point2f(), attr2.getJavaObject());
+
+		source = AttributeType.POINT;
+		target = AttributeType.POINT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint());
+
+		source = AttributeType.POINT;
+		target = AttributeType.POINT3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint3D());
+
+		source = AttributeType.POINT;
+		target = AttributeType.POLYLINE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,new Point2D[] {(Point2D)source.getDefaultValue()}, (Point2D[])attr2.getValue());
+		assertEquals(msg,new Point2D[] {(Point2D)source.getDefaultValue()}, attr2.getPolyline());
+
+		source = AttributeType.POINT;
+		target = AttributeType.POLYLINE3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,new Point3D[] {(Point3D)AttributeType.POINT3D.getDefaultValue()}, (Point3D[])attr2.getValue());
+		assertEquals(msg,new Point3D[] {(Point3D)AttributeType.POINT3D.getDefaultValue()}, attr2.getPolyline3D());
+
+		source = AttributeType.POINT;
+		target = AttributeType.REAL;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getReal());
+
+		source = AttributeType.POINT;
+		target = AttributeType.STRING;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		pt2d = (Point2D)source.getDefaultValue();
+		str = pt2d.getX()+";"+pt2d.getY(); //$NON-NLS-1$
+		assertEquals(msg,str, attr2.getValue());
+		assertEquals(msg,str, attr2.getString());
+
+		source = AttributeType.POINT;
+		target = AttributeType.TIMESTAMP;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertTrue(msg,attr2.getValue() instanceof Timestamp);
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Number)attr2.getValue()).longValue()<=time);
+		assertTrue(msg,attr2.getTimestamp()<=time);
+
+		//
+		// SOURCE: POINT3D
+		//
+		
+		source = AttributeType.POINT3D;
+		target = AttributeType.BOOLEAN;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getBoolean());
+
+		source = AttributeType.POINT3D;
+		target = AttributeType.COLOR;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getColor());
+
+		source = AttributeType.POINT3D;
+		target = AttributeType.DATE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Date)attr2.getValue()).getTime()<=time);
+		assertTrue(msg,attr2.getDate().getTime()<=time);
+
+		source = AttributeType.POINT3D;
+		target = AttributeType.IMAGE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertException(msg, AttributeNotInitializedException.class, attr2, "getImage"); //$NON-NLS-1$
+
+		source = AttributeType.POINT3D;
+		target = AttributeType.INTEGER;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(),attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(),attr2.getInteger());
+
+		source = AttributeType.POINT3D;
+		target = AttributeType.OBJECT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,new Point3f(), attr2.getValue());
+		assertEquals(msg,new Point3f(), attr2.getJavaObject());
+
+		source = AttributeType.POINT3D;
+		target = AttributeType.POINT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint());
+
+		source = AttributeType.POINT3D;
+		target = AttributeType.POINT3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint3D());
+
+		source = AttributeType.POINT3D;
+		target = AttributeType.POLYLINE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,new Point2D[] {(Point2D)AttributeType.POINT.getDefaultValue()}, (Point2D[])attr2.getValue());
+		assertEquals(msg,new Point2D[] {(Point2D)AttributeType.POINT.getDefaultValue()}, attr2.getPolyline());
+
+		source = AttributeType.POINT3D;
+		target = AttributeType.POLYLINE3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,new Point3D[] {(Point3D)AttributeType.POINT3D.getDefaultValue()}, (Point3D[])attr2.getValue());
+		assertEquals(msg,new Point3D[] {(Point3D)AttributeType.POINT3D.getDefaultValue()}, attr2.getPolyline3D());
+
+		source = AttributeType.POINT3D;
+		target = AttributeType.REAL;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getReal());
+
+		source = AttributeType.POINT3D;
+		target = AttributeType.STRING;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		pt3d = (Point3D)source.getDefaultValue();
+		str = pt2d.getX()+";"+pt2d.getY()+";"+pt3d.getZ(); //$NON-NLS-1$ //$NON-NLS-2$
+		assertEquals(msg,str, attr2.getValue());
+		assertEquals(msg,str, attr2.getString());
+
+		source = AttributeType.POINT3D;
+		target = AttributeType.TIMESTAMP;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertTrue(msg,attr2.getValue() instanceof Timestamp);
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Number)attr2.getValue()).longValue()<=time);
+		assertTrue(msg,attr2.getTimestamp()<=time);
+
+		//
+		// SOURCE: POLYLINE
+		//
+		
+		source = AttributeType.POLYLINE;
+		target = AttributeType.BOOLEAN;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getBoolean());
+
+		source = AttributeType.POLYLINE;
+		target = AttributeType.COLOR;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getColor());
+
+		source = AttributeType.POLYLINE;
+		target = AttributeType.DATE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Date)attr2.getValue()).getTime()<=time);
+		assertTrue(msg,attr2.getDate().getTime()<=time);
+
+		source = AttributeType.POLYLINE;
+		target = AttributeType.IMAGE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertException(msg, AttributeNotInitializedException.class, attr2, "getImage"); //$NON-NLS-1$
+
+		source = AttributeType.POLYLINE;
+		target = AttributeType.INTEGER;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(),attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(),attr2.getInteger());
+
+		source = AttributeType.POLYLINE;
+		target = AttributeType.OBJECT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertTrue(msg,Arrays.equals(new Point2f[0], (Point2D[])attr2.getValue()));
+		assertTrue(msg,Arrays.equals(new Point2f[0], (Point2D[])attr2.getJavaObject()));
+
+		source = AttributeType.POLYLINE;
+		target = AttributeType.POINT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint());
+
+		source = AttributeType.POLYLINE;
+		target = AttributeType.POINT3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint3D());
+
+		source = AttributeType.POLYLINE;
+		target = AttributeType.POLYLINE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point2D[])source.getDefaultValue(), (Point2D[])attr2.getValue());
+		assertEquals(msg,(Point2D[])source.getDefaultValue(), attr2.getPolyline());
+		
+		source = AttributeType.POLYLINE;
+		target = AttributeType.POLYLINE3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point3D[])AttributeType.POLYLINE3D.getDefaultValue(), (Point3D[])attr2.getValue());
+		assertEquals(msg,(Point3D[])AttributeType.POLYLINE3D.getDefaultValue(), attr2.getPolyline3D());
+
+		source = AttributeType.POLYLINE;
+		target = AttributeType.REAL;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getReal());
+
+		source = AttributeType.POLYLINE;
+		target = AttributeType.STRING;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		str = ""; //$NON-NLS-1$
+		assertEquals(msg,str, attr2.getValue());
+		assertEquals(msg,str, attr2.getString());
+
+		source = AttributeType.POLYLINE;
+		target = AttributeType.TIMESTAMP;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertTrue(msg,attr2.getValue() instanceof Timestamp);
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Number)attr2.getValue()).longValue()<=time);
+		assertTrue(msg,attr2.getTimestamp()<=time);
+
+		//
+		// SOURCE: POLYLINE3D
+		//
+		
+		source = AttributeType.POLYLINE3D;
+		target = AttributeType.BOOLEAN;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getBoolean());
+
+		source = AttributeType.POLYLINE3D;
+		target = AttributeType.COLOR;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getColor());
+
+		source = AttributeType.POLYLINE3D;
+		target = AttributeType.DATE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Date)attr2.getValue()).getTime()<=time);
+		assertTrue(msg,attr2.getDate().getTime()<=time);
+
+		source = AttributeType.POLYLINE3D;
+		target = AttributeType.IMAGE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertException(msg, AttributeNotInitializedException.class, attr2, "getImage"); //$NON-NLS-1$
+
+		source = AttributeType.POLYLINE3D;
+		target = AttributeType.INTEGER;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(),attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(),attr2.getInteger());
+
+		source = AttributeType.POLYLINE3D;
+		target = AttributeType.OBJECT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertTrue(msg,Arrays.equals(new Point3f[0], (Point3D[])attr2.getValue()));
+		assertTrue(msg,Arrays.equals(new Point3f[0], (Point3D[])attr2.getJavaObject()));
+
+		source = AttributeType.POLYLINE3D;
+		target = AttributeType.POINT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint());
+
+		source = AttributeType.POLYLINE3D;
+		target = AttributeType.POINT3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint3D());
+
+		source = AttributeType.POLYLINE3D;
+		target = AttributeType.POLYLINE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point2D[])AttributeType.POLYLINE.getDefaultValue(), (Point2D[])attr2.getValue());
+		assertEquals(msg,(Point2D[])AttributeType.POLYLINE.getDefaultValue(), attr2.getPolyline());
+		
+		source = AttributeType.POLYLINE3D;
+		target = AttributeType.POLYLINE3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point3D[])AttributeType.POLYLINE3D.getDefaultValue(), (Point3D[])attr2.getValue());
+		assertEquals(msg,(Point3D[])AttributeType.POLYLINE3D.getDefaultValue(), attr2.getPolyline3D());
+
+		source = AttributeType.POLYLINE3D;
+		target = AttributeType.REAL;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getReal());
+
+		source = AttributeType.POLYLINE3D;
+		target = AttributeType.STRING;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		str = ""; //$NON-NLS-1$
+		assertEquals(msg,str, attr2.getValue());
+		assertEquals(msg,str, attr2.getString());
+
+		source = AttributeType.POLYLINE3D;
+		target = AttributeType.TIMESTAMP;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertTrue(msg,attr2.getValue() instanceof Timestamp);
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Number)attr2.getValue()).longValue()<=time);
+		assertTrue(msg,attr2.getTimestamp()<=time);
+
+		//
+		// SOURCE: REAL
+		//
+		
+		source = AttributeType.REAL;
+		target = AttributeType.BOOLEAN;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getBoolean());
+
+		source = AttributeType.REAL;
+		target = AttributeType.COLOR;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getColor());
+
+		source = AttributeType.REAL;
+		target = AttributeType.DATE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Date)attr2.getValue()).getTime()<=time);
+		assertTrue(msg,attr2.getDate().getTime()<=time);
+
+		source = AttributeType.REAL;
+		target = AttributeType.IMAGE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertException(msg, AttributeNotInitializedException.class, attr2, "getImage"); //$NON-NLS-1$
+
+		source = AttributeType.REAL;
+		target = AttributeType.INTEGER;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(),attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(),attr2.getInteger());
+
+		source = AttributeType.REAL;
+		target = AttributeType.OBJECT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertNull(msg,attr2.getValue());
+		assertNull(msg,attr2.getJavaObject());
+
+		source = AttributeType.REAL;
+		target = AttributeType.POINT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint());
+
+		source = AttributeType.REAL;
+		target = AttributeType.POINT3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint3D());
+
+		source = AttributeType.REAL;
+		target = AttributeType.POLYLINE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), (Point2D[])attr2.getValue());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), attr2.getPolyline());
+
+		source = AttributeType.REAL;
+		target = AttributeType.POLYLINE3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), (Point3D[])attr2.getValue());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), attr2.getPolyline3D());
+
+		source = AttributeType.REAL;
+		target = AttributeType.REAL;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getReal());
+
+		source = AttributeType.REAL;
+		target = AttributeType.STRING;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,Double.toString(0), attr2.getValue());
+		assertEquals(msg,Double.toString(0), attr2.getString());
+
+		source = AttributeType.REAL;
+		target = AttributeType.TIMESTAMP;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertTrue(msg,attr2.getValue() instanceof Timestamp);
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Number)attr2.getValue()).longValue()<=time);
+		assertTrue(msg,attr2.getTimestamp()<=time);
+
+		//
+		// SOURCE: STRING
+		//
+		
+		source = AttributeType.STRING;
+		target = AttributeType.BOOLEAN;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getBoolean());
+
+		source = AttributeType.STRING;
+		target = AttributeType.COLOR;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getColor());
+
+		source = AttributeType.STRING;
+		target = AttributeType.DATE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Date)attr2.getValue()).getTime()<=time);
+		assertTrue(msg,attr2.getDate().getTime()<=time);
+
+		source = AttributeType.STRING;
+		target = AttributeType.IMAGE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertException(msg, AttributeNotInitializedException.class, attr2, "getImage"); //$NON-NLS-1$
+
+		source = AttributeType.STRING;
+		target = AttributeType.INTEGER;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(),attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(),attr2.getInteger());
+
+		source = AttributeType.STRING;
+		target = AttributeType.OBJECT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertNull(msg,attr2.getValue());
+		assertNull(msg,attr2.getJavaObject());
+
+		source = AttributeType.STRING;
+		target = AttributeType.POINT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint());
+
+		source = AttributeType.STRING;
+		target = AttributeType.POINT3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getPoint3D());
+
+		source = AttributeType.STRING;
+		target = AttributeType.POLYLINE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), (Point2D[])attr2.getValue());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), attr2.getPolyline());
+
+		source = AttributeType.STRING;
+		target = AttributeType.POLYLINE3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), (Point3D[])attr2.getValue());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), attr2.getPolyline3D());
+
+		source = AttributeType.STRING;
+		target = AttributeType.REAL;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertEquals(msg,target.getDefaultValue(), attr2.getReal());
+
+		source = AttributeType.STRING;
+		target = AttributeType.STRING;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		str = ""; //$NON-NLS-1$
+		assertEquals(msg,str, attr2.getValue());
+		assertEquals(msg,str, attr2.getString());
+
+		source = AttributeType.STRING;
+		target = AttributeType.TIMESTAMP;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertTrue(msg,attr2.getValue() instanceof Timestamp);
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Number)attr2.getValue()).longValue()<=time);
+		assertTrue(msg,attr2.getTimestamp()<=time);
+
+		//
+		// SOURCE: TIMESTAMP
+		//
+		
+		source = AttributeType.TIMESTAMP;
+		target = AttributeType.BOOLEAN;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,true, attr2.getValue());
+		assertEquals(msg,true, attr2.getBoolean());
+
+		source = AttributeType.TIMESTAMP;
+		target = AttributeType.DATE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Date)attr2.getValue()).getTime()<=time);
+		assertTrue(msg,attr2.getDate().getTime()<=time);
+
+		source = AttributeType.TIMESTAMP;
+		target = AttributeType.IMAGE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,target.getDefaultValue(), attr2.getValue());
+		assertException(msg, AttributeNotInitializedException.class, attr2, "getImage"); //$NON-NLS-1$
+
+		source = AttributeType.TIMESTAMP;
+		target = AttributeType.INTEGER;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		time = System.currentTimeMillis();
+		assertTrue(msg,(Long)attr2.getValue()<=time);
+		assertTrue(msg,attr2.getTimestamp()<=time);
+
+		source = AttributeType.TIMESTAMP;
+		target = AttributeType.OBJECT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertNull(msg,attr2.getValue());
+		assertNull(msg,attr2.getJavaObject());
+
+		source = AttributeType.TIMESTAMP;
+		target = AttributeType.POINT;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		time = attr2.getTimestamp();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,new Point2f(time,0), attr2.getValue());
+		assertEquals(msg,new Point2f(time,0), attr2.getPoint());
+
+		source = AttributeType.TIMESTAMP;
+		target = AttributeType.POINT3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		time = attr2.getTimestamp();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,new Point3f(time,0,0), attr2.getValue());
+		assertEquals(msg,new Point3f(time,0,0), attr2.getPoint3D());
+
+		source = AttributeType.TIMESTAMP;
+		target = AttributeType.POLYLINE;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), (Point2D[])attr2.getValue());
+		assertEquals(msg,(Point2D[])target.getDefaultValue(), attr2.getPolyline());
+
+		source = AttributeType.TIMESTAMP;
+		target = AttributeType.POLYLINE3D;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), (Point3D[])attr2.getValue());
+		assertEquals(msg,(Point3D[])target.getDefaultValue(), attr2.getPolyline3D());
+
+		source = AttributeType.TIMESTAMP;
+		target = AttributeType.REAL;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		time = attr2.getTimestamp();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertEquals(msg,Double.valueOf(time), attr2.getValue());
+		assertEquals(msg,Long.valueOf(time).doubleValue(), attr2.getReal());
+
+		source = AttributeType.TIMESTAMP;
+		target = AttributeType.STRING;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();
+		time = attr2.getTimestamp();
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$
+		str = format.format(new Date(time));
+		assertEquals(msg,str, attr2.getValue());
+		assertEquals(msg,str, attr2.getString());
+
+		source = AttributeType.TIMESTAMP;
+		target = AttributeType.TIMESTAMP;
+		msg = "from '"+source.toString()+"' to '"+target.toString()+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		attr1 = new AttributeValueImpl(source);
+		attr2 = new AttributeValueImpl(source);
+		attr2.setToDefault();		
+		attr1.cast(target);
+		assertFalse(msg,attr1.isAssigned());
+		attr2.cast(target);
+		assertTrue(msg,attr2.isAssigned());
+		assertTrue(msg,attr2.getValue() instanceof Timestamp);
+		time = System.currentTimeMillis();
+		assertTrue(msg,((Number)attr2.getValue()).longValue()<=time);
+		assertTrue(msg,attr2.getTimestamp()<=time);
+
+	}
+	
+	/**
+	 */
+	public static void testEquals() {
+		AttributeValueImpl attr = new AttributeValueImpl();
+		
+		attr.setBoolean(true);
+		assertTrue(attr.equals(new AttributeValueImpl(true)));
+		assertFalse(attr.equals(new AttributeValueImpl(false)));
+		assertTrue(attr.equals(true));
+		assertFalse(attr.equals(false));
+		assertTrue(attr.equals(new AttributeValueImpl("true"))); //$NON-NLS-1$
+		assertFalse(attr.equals(new AttributeValueImpl("false"))); //$NON-NLS-1$
+		assertTrue(attr.equals("true")); //$NON-NLS-1$
+		assertFalse(attr.equals("false")); //$NON-NLS-1$
+		assertFalse(attr.equals(new AttributeValueImpl(1.)));
+		assertFalse(attr.equals(new AttributeValueImpl("1."))); //$NON-NLS-1$
+		assertFalse(attr.equals(1.));
+		assertFalse(attr.equals("toto")); //$NON-NLS-1$
+
+		attr.setBoolean(false);
+		assertFalse(attr.equals(new AttributeValueImpl(true)));
+		assertTrue(attr.equals(new AttributeValueImpl(false)));
+		assertFalse(attr.equals(true));
+		assertTrue(attr.equals(false));
+		assertFalse(attr.equals(new AttributeValueImpl("true"))); //$NON-NLS-1$
+		assertTrue(attr.equals(new AttributeValueImpl("false"))); //$NON-NLS-1$
+		assertFalse(attr.equals("true")); //$NON-NLS-1$
+		assertTrue(attr.equals("false")); //$NON-NLS-1$
+		assertFalse(attr.equals(new AttributeValueImpl(1.)));
+		assertFalse(attr.equals(new AttributeValueImpl("1."))); //$NON-NLS-1$
+		assertFalse(attr.equals(1.));
+		assertFalse(attr.equals("toto")); //$NON-NLS-1$
+	}
+	
+	/** 
+	 */
+	public static void testParse() {
+		AttributeValueImpl v;
+		
+		v = AttributeValueImpl.parse("127.0.0.1"); //$NON-NLS-1$
+		assertSame(AttributeType.INET_ADDRESS, v.getType());
+
+		v = AttributeValueImpl.parse("localhost"); //$NON-NLS-1$
+		assertSame(AttributeType.INET_ADDRESS, v.getType());
+
+		v = AttributeValueImpl.parse("java.lang.String"); //$NON-NLS-1$
+		assertSame(AttributeType.TYPE, v.getType());
+
+		v = AttributeValueImpl.parse(AttributeType.class.getName()+"."+AttributeType.ENUMERATION.toString()); //$NON-NLS-1$
+		assertSame(AttributeType.ENUMERATION, v.getType());
+
+		v = AttributeValueImpl.parse("3eade434-b267-4ffa-a574-2e2cbff0151a"); //$NON-NLS-1$
+		assertSame(AttributeType.UUID, v.getType());
+
+		v = AttributeValueImpl.parse("134"); //$NON-NLS-1$
+		assertSame(AttributeType.INTEGER, v.getType());
+
+		v = AttributeValueImpl.parse("-134"); //$NON-NLS-1$
+		assertSame(AttributeType.INTEGER, v.getType());
+
+		v = AttributeValueImpl.parse("134e34"); //$NON-NLS-1$
+		assertSame(AttributeType.REAL, v.getType());
+
+		v = AttributeValueImpl.parse("-134.5"); //$NON-NLS-1$
+		assertSame(AttributeType.REAL, v.getType());
+
+		v = AttributeValueImpl.parse("2012-11-30 18:22:34"); //$NON-NLS-1$
+		assertSame(AttributeType.DATE, v.getType());
+
+		v = AttributeValueImpl.parse("Fri, 30 Nov 2012 18:22:42 +0100"); //$NON-NLS-1$
+		assertSame(AttributeType.DATE, v.getType());
+
+		v = AttributeValueImpl.parse("True"); //$NON-NLS-1$
+		assertSame(AttributeType.BOOLEAN, v.getType());
+		
+		v = AttributeValueImpl.parse("False"); //$NON-NLS-1$
+		assertSame(AttributeType.BOOLEAN, v.getType());
+
+		v = AttributeValueImpl.parse("TrUe"); //$NON-NLS-1$
+		assertSame(AttributeType.BOOLEAN, v.getType());
+
+		v = AttributeValueImpl.parse("http://www.multiagent.fr";); //$NON-NLS-1$
+		assertSame(AttributeType.URL, v.getType());
+
+		v = AttributeValueImpl.parse("mailto:stephane.galland@xxxxxxx";); //$NON-NLS-1$
+		assertSame(AttributeType.URL, v.getType());
+
+		v = AttributeValueImpl.parse("urn:isbn:096139210x"); //$NON-NLS-1$
+		assertSame(AttributeType.URI, v.getType());
+
+		v = AttributeValueImpl.parse("1;2;3;4;5;6;7;8;9"); //$NON-NLS-1$
+		assertSame(AttributeType.POLYLINE3D, v.getType());
+
+		v = AttributeValueImpl.parse("1;2;3;4;5;6;7;8"); //$NON-NLS-1$
+		assertSame(AttributeType.POLYLINE, v.getType());
+
+		v = AttributeValueImpl.parse("1;2;3;4"); //$NON-NLS-1$
+		assertSame(AttributeType.COLOR, v.getType());
+
+		v = AttributeValueImpl.parse("1;2;3"); //$NON-NLS-1$
+		assertSame(AttributeType.COLOR, v.getType());
+
+		v = AttributeValueImpl.parse("1;2;300"); //$NON-NLS-1$
+		assertSame(AttributeType.POINT3D, v.getType());
+
+		v = AttributeValueImpl.parse("1;2"); //$NON-NLS-1$
+		assertSame(AttributeType.POINT, v.getType());
+
+		v = AttributeValueImpl.parse("blablabla"); //$NON-NLS-1$
+		assertSame(AttributeType.STRING, v.getType());
+	}
+
+}

Added: trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/AbstractAttributeCollectionTest.java
===================================================================
--- trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/AbstractAttributeCollectionTest.java	                        (rev 0)
+++ trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/AbstractAttributeCollectionTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,601 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeImpl;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+import org.arakhne.afc.attrs.attr.AttributeValueImpl;
+import org.arakhne.afc.attrs.collection.AttributeChangeEvent;
+import org.arakhne.afc.attrs.collection.AttributeChangeListener;
+import org.arakhne.afc.attrs.collection.AttributeCollection;
+
+/**
+ * Test of AbstractAttributeProvider.
+ * 
+ * @param <T>
+ * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractAttributeCollectionTest<T extends AttributeCollection> extends AbstractAttributeProviderTest<T> {
+
+	private Attribute[] newValues;
+	
+	/**
+	 */
+	protected ListenerStub listenerStub;
+	
+	/**
+	 * @param id
+	 */
+	public AbstractAttributeCollectionTest(String id) {
+		super(id);
+	}
+	
+	@Override
+	public void setUp() throws Exception {
+		super.setUp();
+		
+		this.newValues = new Attribute[] {
+			new AttributeImpl("A",false),	 //$NON-NLS-1$
+			new AttributeImpl("D","34"),	 //$NON-NLS-1$ //$NON-NLS-2$
+			new AttributeImpl("Z",17f),	 //$NON-NLS-1$
+		};
+		
+		this.listenerStub = new ListenerStub();
+		this.testData.addAttributeChangeListener(this.listenerStub);
+	}
+	
+	@Override
+	public void tearDown() throws Exception {
+		this.newValues = null;
+		this.listenerStub.reset();
+		this.listenerStub = null;
+		super.tearDown();
+	}
+	
+	private void runSetAttributeValue(Class<?>[] types, Object[] parameters, Attribute attr) throws Exception {
+		String name = attr.getName();
+		
+		boolean attrExists = this.testData.hasAttribute(name);
+		AttributeValue oldValue = null;
+		if (attrExists) {
+			oldValue = this.testData.getAttribute(name);
+		}
+	
+		Method method = this.testData.getClass().getMethod("setAttribute", types); //$NON-NLS-1$
+		Object o = method.invoke(this.testData, parameters);
+
+		assertTrue(this.id, o instanceof Attribute);
+		assertEquals(this.id, attr,o);
+		
+		assertNotNull(this.id, this.testData.getAttribute(name));
+		assertEquals(this.id, attr.getType(),this.testData.getAttribute(name).getType());
+		assertEquals(this.id, attr,this.testData.getAttribute(name));
+		
+		// Test events
+		String message = this.id+": set attribute "+name; //$NON-NLS-1$
+		this.listenerStub.assertNames(message, name);
+		this.listenerStub.assertValues(message, attr);
+		if (!attrExists) {
+			this.listenerStub.assertTypes(message,
+					AttributeChangeEvent.Type.ADDITION);
+			this.listenerStub.assertOldNames(message, new String[]{null});
+			this.listenerStub.assertOldValues(message, new AttributeValue[]{null});
+		}
+		else {
+			this.listenerStub.assertTypes(message,
+					AttributeChangeEvent.Type.VALUE_UPDATE);
+			this.listenerStub.assertOldNames(message, name);
+			this.listenerStub.assertOldValues(message, oldValue);
+		}
+		
+		this.listenerStub.reset();
+	}
+	
+	private void runSetAttributeValue(Class<?> type, Object parameter, Attribute attr) throws Exception {
+		runSetAttributeValue(
+				new Class<?>[] {String.class, type},
+				new Object[] {attr.getName(), parameter}, attr);
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testSetAttributeAttribute() throws Exception {
+		for (Attribute attr : this.newValues) {
+			runSetAttributeValue(
+					new Class<?>[] {Attribute.class},
+					new Object[] {attr},
+					attr);
+		}
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testSetAttributeStringAttributeValue() throws Exception {
+		for (Attribute attr : this.newValues) {
+			runSetAttributeValue(
+					AttributeValue.class,
+					attr,
+					attr);
+		}
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testSetAttributeStringBoolean() throws Exception {
+		Attribute attr = new AttributeImpl("A", false); //$NON-NLS-1$
+		runSetAttributeValue(
+				boolean.class,
+				attr.getBoolean(),
+				attr);
+		attr = new AttributeImpl("X", false); //$NON-NLS-1$
+		runSetAttributeValue(
+				boolean.class,
+				attr.getBoolean(),
+				attr);
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testSetAttributeStringInt() throws Exception {
+		Attribute attr = new AttributeImpl("E", 34); //$NON-NLS-1$
+		runSetAttributeValue(
+				int.class,
+				(int)attr.getInteger(),
+				attr);
+		attr = new AttributeImpl("X", 34); //$NON-NLS-1$
+		runSetAttributeValue(
+				int.class,
+				(int)attr.getInteger(),
+				attr);
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testSetAttributeStringLong() throws Exception {
+		Attribute attr = new AttributeImpl("E", 34); //$NON-NLS-1$
+		runSetAttributeValue(
+				long.class,
+				attr.getInteger(),
+				attr);
+		attr = new AttributeImpl("X", 34); //$NON-NLS-1$
+		runSetAttributeValue(
+				long.class,
+				attr.getInteger(),
+				attr);
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testSetAttributeStringFloat() throws Exception {
+		Attribute attr = new AttributeImpl("E", 34f); //$NON-NLS-1$
+		runSetAttributeValue(
+				float.class,
+				(float)attr.getReal(),
+				attr);
+		attr = new AttributeImpl("X", 34f); //$NON-NLS-1$
+		runSetAttributeValue(
+				float.class,
+				(float)attr.getReal(),
+				attr);
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testSetAttributeStringDouble() throws Exception {
+		Attribute attr = new AttributeImpl("E", 34.); //$NON-NLS-1$
+		runSetAttributeValue(
+				double.class,
+				attr.getReal(),
+				attr);
+		attr = new AttributeImpl("X", 34.); //$NON-NLS-1$
+		runSetAttributeValue(
+				double.class,
+				attr.getReal(),
+				attr);
+	}
+	
+	/**
+	 * @throws Exception
+	 */
+	public void testSetAttributeStringString() throws Exception {
+		Attribute attr = new AttributeImpl("E", "Toto"); //$NON-NLS-1$ //$NON-NLS-2$
+		runSetAttributeValue(
+				String.class,
+				attr.getString(),
+				attr);
+		attr = new AttributeImpl("X", "Titi et Rominet"); //$NON-NLS-1$ //$NON-NLS-2$
+		runSetAttributeValue(
+				String.class,
+				attr.getString(),
+				attr);
+	}
+
+	/**
+	 */
+	public void testRemoveAttributeString() {
+		String message;
+		
+		assertFalse(this.id, this.testData.removeAttribute("Y")); //$NON-NLS-1$
+		// Testing events
+		message = this.id+": removing Y"; //$NON-NLS-1$
+		this.listenerStub.assertEmpty(message);
+		this.listenerStub.reset();
+		
+		assertTrue(this.id, this.testData.removeAttribute("C")); //$NON-NLS-1$
+		// Testing events
+		message = "removing C"; //$NON-NLS-1$
+		this.listenerStub.assertTypes(message, AttributeChangeEvent.Type.REMOVAL);
+		this.listenerStub.assertNames(message, "C"); //$NON-NLS-1$
+		this.listenerStub.assertOldNames(message, "C"); //$NON-NLS-1$
+		this.listenerStub.assertValues(message, new AttributeValueImpl(true));
+		this.listenerStub.assertOldValues(message, new AttributeValueImpl(true));
+		this.listenerStub.reset();
+
+		assertFalse(this.id, this.testData.removeAttribute("X")); //$NON-NLS-1$
+		// Testing events
+		message = this.id+": removing X"; //$NON-NLS-1$
+		this.listenerStub.assertEmpty(message);
+		this.listenerStub.reset();
+		
+		assertTrue(this.id, this.testData.hasAttribute("A")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("X")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("B")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Y")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("C")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("D")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Z")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("E")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("F")); //$NON-NLS-1$
+		
+	}
+
+	/**
+	 */
+	public void testRemoveAllAttributes() {
+		String message;
+		
+		assertTrue(this.id, this.testData.hasAttribute("A")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("X")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("B")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Y")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("C")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("D")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Z")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("E")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("F")); //$NON-NLS-1$
+
+		assertTrue(this.id, this.testData.removeAllAttributes());
+		message = this.id+": removing all attributes"; //$NON-NLS-1$
+		this.listenerStub.assertTypes(message, AttributeChangeEvent.Type.REMOVE_ALL);
+		this.listenerStub.assertNames(message, new String[]{null});
+		this.listenerStub.assertOldNames(message, new String[]{null});
+		this.listenerStub.assertValues(message, new AttributeValue[]{null});
+		this.listenerStub.assertOldValues(message, new AttributeValue[]{null});
+		this.listenerStub.reset();
+
+		assertFalse(this.id, this.testData.hasAttribute("A")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("X")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("B")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Y")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("C")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("D")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Z")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("E")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("F")); //$NON-NLS-1$
+
+		assertFalse(this.id, this.testData.removeAllAttributes());
+		this.listenerStub.assertEmpty(message);
+		this.listenerStub.reset();
+
+		assertFalse(this.id, this.testData.hasAttribute("A")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("X")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("B")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Y")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("C")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("D")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Z")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("E")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("F")); //$NON-NLS-1$
+	}
+	
+	/**
+	 */
+	public void testRenameAttribute() {
+		String message;
+		
+		assertTrue(this.id, this.testData.hasAttribute("A")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("X")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("B")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Y")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("C")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("D")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Z")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("E")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("F")); //$NON-NLS-1$
+		
+		AttributeValue oldValue = this.testData.getAttribute("B"); //$NON-NLS-1$
+
+		assertTrue(this.id, this.testData.renameAttribute("B", "ZZZ", false)); //$NON-NLS-1$ //$NON-NLS-2$
+		// Testing events
+		message = this.id+": renaming B to ZZZ"; //$NON-NLS-1$
+		this.listenerStub.assertTypes(message, AttributeChangeEvent.Type.RENAME);
+		this.listenerStub.assertNames(message, "ZZZ"); //$NON-NLS-1$
+		this.listenerStub.assertOldNames(message, "B"); //$NON-NLS-1$
+		this.listenerStub.assertValues(message, oldValue);
+		this.listenerStub.assertOldValues(message, oldValue);
+		this.listenerStub.reset();
+
+		assertTrue(this.id, this.testData.hasAttribute("A")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("X")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("B")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("ZZZ")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Y")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("C")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("D")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Z")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("E")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("F")); //$NON-NLS-1$
+		
+		assertEquals(this.id, oldValue, this.testData.getAttribute("ZZZ")); //$NON-NLS-1$
+
+		assertFalse(this.id, this.testData.renameAttribute("toto", "XXX", false)); //$NON-NLS-1$ //$NON-NLS-2$
+		// Testing events
+		message = this.id+": renaming toto to XXX"; //$NON-NLS-1$
+		this.listenerStub.assertEmpty(message);
+		this.listenerStub.reset();
+
+		assertTrue(this.id, this.testData.hasAttribute("A")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("X")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("B")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("ZZZ")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Y")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("C")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("D")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Z")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("E")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("F")); //$NON-NLS-1$
+		
+		oldValue = this.testData.getAttribute("F"); //$NON-NLS-1$
+		AttributeValue oldValue2 = this.testData.getAttribute("A"); //$NON-NLS-1$
+
+		assertFalse(this.id, this.testData.renameAttribute("F", "A", false)); //$NON-NLS-1$ //$NON-NLS-2$
+		// Testing events
+		message = this.id+": renaming F to A"; //$NON-NLS-1$
+		this.listenerStub.assertEmpty(message);
+		this.listenerStub.reset();
+
+		assertTrue(this.id, this.testData.hasAttribute("A")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("X")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("B")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("ZZZ")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Y")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("C")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("D")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Z")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("E")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("F")); //$NON-NLS-1$
+		
+		assertEquals(this.id, oldValue, this.testData.getAttribute("F")); //$NON-NLS-1$
+		assertEquals(this.id, oldValue2, this.testData.getAttribute("A")); //$NON-NLS-1$
+	}
+	
+	/**
+	 */
+	public void testRenameAttributeOverwrite() {
+		String message;
+		
+		assertTrue(this.id, this.testData.hasAttribute("A")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("X")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("B")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Y")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("C")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("D")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Z")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("E")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("F")); //$NON-NLS-1$
+		
+		AttributeValue oldValue = this.testData.getAttribute("B"); //$NON-NLS-1$
+
+		assertTrue(this.id, this.testData.renameAttribute("B", "ZZZ", true)); //$NON-NLS-1$ //$NON-NLS-2$
+		// Testing events
+		message = this.id+": renaming B to ZZZ"; //$NON-NLS-1$
+		this.listenerStub.assertTypes(message, AttributeChangeEvent.Type.RENAME);
+		this.listenerStub.assertNames(message, "ZZZ"); //$NON-NLS-1$
+		this.listenerStub.assertOldNames(message, "B"); //$NON-NLS-1$
+		this.listenerStub.assertValues(message, oldValue);
+		this.listenerStub.assertOldValues(message, oldValue);
+		this.listenerStub.reset();
+
+		assertTrue(this.id, this.testData.hasAttribute("A")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("X")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("B")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("ZZZ")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Y")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("C")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("D")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Z")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("E")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("F")); //$NON-NLS-1$
+		
+		assertEquals(this.id, oldValue, this.testData.getAttribute("ZZZ")); //$NON-NLS-1$
+
+		assertFalse(this.id, this.testData.renameAttribute("toto", "XXX", true)); //$NON-NLS-1$ //$NON-NLS-2$
+		// Testing events
+		message = this.id+": renaming toto to XXX"; //$NON-NLS-1$
+		this.listenerStub.assertEmpty(message);
+		this.listenerStub.reset();
+
+		assertTrue(this.id, this.testData.hasAttribute("A")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("X")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("B")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("ZZZ")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Y")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("C")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("D")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Z")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("E")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("F")); //$NON-NLS-1$
+		
+		oldValue = this.testData.getAttribute("F"); //$NON-NLS-1$
+		AttributeValue oldValue2 = this.testData.getAttribute("A"); //$NON-NLS-1$
+
+		assertTrue(this.id, this.testData.renameAttribute("F", "A", true)); //$NON-NLS-1$ //$NON-NLS-2$
+		// Testing events
+		message = this.id+": renaming F to A"; //$NON-NLS-1$
+		this.listenerStub.assertTypes(message, AttributeChangeEvent.Type.REMOVAL, AttributeChangeEvent.Type.RENAME);
+		this.listenerStub.assertNames(message, "A","A"); //$NON-NLS-1$ //$NON-NLS-2$
+		this.listenerStub.assertOldNames(message, "A", "F"); //$NON-NLS-1$ //$NON-NLS-2$
+		this.listenerStub.assertValues(message, oldValue2, oldValue);
+		this.listenerStub.assertOldValues(message, oldValue2, oldValue);
+		this.listenerStub.reset();
+
+		assertTrue(this.id, this.testData.hasAttribute("A")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("X")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("B")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("ZZZ")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Y")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("C")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("D")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Z")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("E")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("F")); //$NON-NLS-1$
+		
+		assertEquals(this.id, oldValue, this.testData.getAttribute("A")); //$NON-NLS-1$
+	}
+	
+	/**
+	 * Stub for AttributeChangeListener.
+	 * 
+	 * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	protected class ListenerStub implements AttributeChangeListener {
+
+		private final ArrayList<AttributeChangeEvent> eventList = new ArrayList<AttributeChangeEvent>();
+
+		/**
+		 */
+		public void reset() {
+			this.eventList.clear();
+		}
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void onAttributeChangeEvent(AttributeChangeEvent event) {
+			this.eventList.add(event);
+		}
+		
+		/**
+		 * @param message
+		 */
+		public void assertEmpty(String message) {
+			assertEquals(message, 0,this.eventList.size());
+		}
+
+		/**
+		 * @param message
+		 * @param desiredTypes
+		 */
+		public void assertTypes(String message, AttributeChangeEvent.Type... desiredTypes) {
+			assertEquals(message, desiredTypes.length, this.eventList.size());
+			for(int i=0; i<desiredTypes.length; ++i) {
+				assertEquals(message+" at index "+i, desiredTypes[i], this.eventList.get(i).getType()); //$NON-NLS-1$
+			}
+		}
+
+		/**
+		 * @param message
+		 * @param desiredNames
+		 */
+		public void assertNames(String message, String... desiredNames) {
+			assertEquals(message, desiredNames.length, this.eventList.size());
+			for(int i=0; i<desiredNames.length; ++i) {
+				assertEquals(message+" at index "+i, desiredNames[i], this.eventList.get(i).getName()); //$NON-NLS-1$
+			}
+		}
+
+		/**
+		 * @param message
+		 * @param desiredNames
+		 */
+		public void assertOldNames(String message, String... desiredNames) {
+			assertEquals(message, desiredNames.length, this.eventList.size());
+			for(int i=0; i<desiredNames.length; ++i) {
+				assertEquals(message+" at index "+i, desiredNames[i], this.eventList.get(i).getOldName()); //$NON-NLS-1$
+			}
+		}
+
+		/**
+		 * @param message
+		 * @param desiredValues
+		 */
+		public void assertValues(String message, AttributeValue... desiredValues) {
+			assertEquals(message, desiredValues.length, this.eventList.size());
+			for(int i=0; i<desiredValues.length; ++i) {
+				assertEquals(message+" at index "+i, desiredValues[i], this.eventList.get(i).getValue()); //$NON-NLS-1$
+			}
+		}
+
+		/**
+		 * @param message
+		 * @param desiredValues
+		 */
+		public void assertOldValues(String message, AttributeValue... desiredValues) {
+			assertEquals(message, desiredValues.length, this.eventList.size());
+			for(int i=0; i<desiredValues.length; ++i) {
+				assertEquals(message+" at index "+i, desiredValues[i], this.eventList.get(i).getOldValue()); //$NON-NLS-1$
+			}
+		}
+
+		/**
+		 * @param message
+		 * @param desiredAttributes
+		 */
+		public void assertAttributes(String message, Attribute... desiredAttributes) {
+			assertEquals(message, desiredAttributes.length, this.eventList.size());
+			for(int i=0; i<desiredAttributes.length; ++i) {
+				assertEquals(message+" at index "+i, desiredAttributes[i], this.eventList.get(i).getAttribute()); //$NON-NLS-1$
+			}
+		}
+
+	}
+
+}

Added: trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/AbstractAttributeProviderTest.java
===================================================================
--- trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/AbstractAttributeProviderTest.java	                        (rev 0)
+++ trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/AbstractAttributeProviderTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,436 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.arakhne.afc.attrs.AbstractAttrTestCase;
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeException;
+import org.arakhne.afc.attrs.attr.AttributeImpl;
+import org.arakhne.afc.attrs.attr.AttributeType;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+import org.arakhne.afc.attrs.attr.AttributeValueImpl;
+import org.arakhne.afc.attrs.attr.InvalidAttributeTypeException;
+import org.arakhne.afc.attrs.collection.AttributeCollection;
+import org.arakhne.afc.attrs.collection.AttributeProvider;
+import org.arakhne.afc.math.continous.object2d.Point2f;
+
+/**
+ * Test of AbstractAttributeContainer.
+ * 
+ * @param <T>
+ * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractAttributeProviderTest<T extends AttributeProvider> extends AbstractAttrTestCase {
+
+	/**
+	 */
+	protected final String id;
+	
+	/**
+	 * @param id
+	 */
+	public AbstractAttributeProviderTest(String id) {
+		super();
+		this.id = id;
+	}
+	
+	/**
+	 * Assertion on invalid value exception.
+	 * 
+	 * @param provider
+	 * @param methodName
+	 * @param parameters
+	 * @throws Exception
+	 */
+	protected static void assertInvalidValue(AttributeProvider provider, String methodName, Object... parameters) throws Exception {
+		assertInvalidValue(null, provider, methodName, parameters);
+	}
+
+	/**
+	 * Assertion on invalid value exception.
+	 * 
+	 * @param message
+	 * @param provider
+	 * @param methodName
+	 * @param parameters
+	 * @throws Exception
+	 */
+	protected static void assertInvalidValue(String message, AttributeProvider provider, String methodName, Object... parameters) throws Exception {
+		StringBuilder msg = new StringBuilder();
+		if (message!=null && !message.isEmpty()) msg.append(": "); //$NON-NLS-1$
+		try {
+			Class<?>[] classTab = new Class<?>[parameters.length];
+			for(int i=0; i<parameters.length; ++i) {
+				classTab[i] = parameters[i].getClass();
+			}
+			Class<? extends AttributeProvider> clazz = provider.getClass();
+			Method method = clazz.getMethod(methodName,classTab);
+			method.invoke(provider,parameters);
+			msg.append("the exception InvalidAttributeTypeException was not thrown: standard return from the function "); //$NON-NLS-1$
+			msg.append(methodName);
+			fail(msg.toString());
+		}
+		catch(InvocationTargetException e) {
+			Throwable ex = e.getTargetException();
+			if (ex instanceof InvalidAttributeTypeException) {
+				// normal case
+			}
+			else {
+				msg.append("the exception InvalidAttributeTypeException was not thrown, exception: "); //$NON-NLS-1$
+				msg.append(ex);
+				msg.append(", file: "); //$NON-NLS-1$
+				msg.append(ex.getStackTrace()[0].getFileName());
+				msg.append(", line: "); //$NON-NLS-1$
+				msg.append(ex.getStackTrace()[0].getLineNumber());
+				fail(msg.toString());
+			}
+		}
+	}	
+	
+	/**
+	 */
+	protected T testData;
+	
+	/**
+	 */
+	protected Attribute[] baseData;
+
+	/**
+	 * Fill the attribute provider with test case data.
+	 * 
+	 * @param provider
+	 * @throws AttributeException
+	 */
+	protected void createTestCaseData(AttributeCollection provider) throws AttributeException {
+		for (Attribute a : this.baseData) {
+			provider.setAttribute(a);
+		}
+	}
+	
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		this.baseData = new Attribute[] {
+				new AttributeImpl("A",1), //$NON-NLS-1$
+				new AttributeImpl("B",2.), //$NON-NLS-1$
+				new AttributeImpl("C",true), //$NON-NLS-1$
+				new AttributeImpl("D","Hello"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E",new Point2f(1,2)), //$NON-NLS-1$
+				new AttributeImpl("F","false"), //$NON-NLS-1$ //$NON-NLS-2$
+		};
+		this.testData = setUpTestCase();
+		if (this.testData instanceof AttributeCollection) {
+			createTestCaseData((AttributeCollection)this.testData);
+		}
+	}
+
+	/** Initialize the test case.
+	 * @return the set up test case.
+	 * @throws Exception
+	 */
+	protected abstract T setUpTestCase() throws Exception;
+
+	@Override
+	protected void tearDown() throws Exception {
+		this.testData = null;
+		this.baseData = null;
+		super.tearDown();
+	}
+
+	/**
+	 */
+	public void testIterator() {
+		ArrayList<Attribute> ref = new ArrayList<Attribute>();
+		ref.addAll(Arrays.asList(this.baseData));
+
+		Iterator<Attribute> it = this.testData.attributes().iterator();
+		while(!ref.isEmpty()) {
+			assertTrue(this.id, it.hasNext());
+			Attribute attr = it.next();
+			assertNotNull(this.id, attr);
+			assertTrue(this.id, ref.remove(attr));
+		}
+		
+		assertFalse(this.id, it.hasNext());
+	}
+
+	/**
+	 */
+	public void testHasAttribute() {
+		assertTrue(this.id, this.testData.hasAttribute("A")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("X")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("B")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Y")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("C")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("D")); //$NON-NLS-1$
+		assertFalse(this.id, this.testData.hasAttribute("Z")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("E")); //$NON-NLS-1$
+		assertTrue(this.id, this.testData.hasAttribute("F")); //$NON-NLS-1$
+	}
+
+	/**
+	 */
+	public void testGetAllAttributes() {
+		assertEpsilonEquals(this.id, this.baseData, this.testData.getAllAttributes().toArray());
+	}
+
+	/**
+	 */
+	public void testGetAllAttributesByType() {
+		HashMap<AttributeType,Collection<Attribute>> map = new HashMap<AttributeType,Collection<Attribute>>();
+		for(Attribute data : this.baseData) {
+			AttributeType type = data.getType();
+			Collection<Attribute> col = map.get(type);
+			if (col==null) {
+				col = new ArrayList<Attribute>();
+				map.put(type,col);
+			}
+			col.add(data);
+		}
+		
+		assertEquals(this.id, map, this.testData.getAllAttributesByType());
+	}
+
+
+	/**
+	 */
+	public void testGetAllAttributeNames() {
+		assertEpsilonEquals(this.id, new String[] {
+				"A", //$NON-NLS-1$
+				"B", //$NON-NLS-1$
+				"C", //$NON-NLS-1$
+				"D", //$NON-NLS-1$
+				"E", //$NON-NLS-1$
+				"F", //$NON-NLS-1$
+		}, this.testData.getAllAttributeNames().toArray());
+	}
+
+	/**
+	 */
+	public void testGetAttributeString() {
+		assertEquals(this.id, this.baseData[0],this.testData.getAttribute("A")); //$NON-NLS-1$
+		assertNull(this.id, this.testData.getAttribute("X")); //$NON-NLS-1$
+		assertEquals(this.id, this.baseData[1],this.testData.getAttribute("B")); //$NON-NLS-1$
+		assertNull(this.id, this.testData.getAttribute("Y")); //$NON-NLS-1$
+		assertEquals(this.id, this.baseData[2],this.testData.getAttribute("C")); //$NON-NLS-1$
+		assertNull(this.id, this.testData.getAttribute("Z")); //$NON-NLS-1$
+		assertEquals(this.id, this.baseData[3],this.testData.getAttribute("D")); //$NON-NLS-1$
+		assertNull(this.id, this.testData.getAttribute("W")); //$NON-NLS-1$
+		assertEquals(this.id, this.baseData[4],this.testData.getAttribute("E")); //$NON-NLS-1$
+		assertEquals(this.id, this.baseData[5],this.testData.getAttribute("F")); //$NON-NLS-1$
+	}
+
+	/**
+	 */
+	public void testGetAttributeStringAttributeValue() {
+		AttributeValue defaultValue = new AttributeValueImpl();
+		assertEquals(this.id, this.baseData[0],this.testData.getAttribute("A",defaultValue)); //$NON-NLS-1$
+		assertSame(this.id, defaultValue, this.testData.getAttribute("X", defaultValue)); //$NON-NLS-1$
+		assertEquals(this.id, this.baseData[1],this.testData.getAttribute("B", defaultValue)); //$NON-NLS-1$
+		assertSame(this.id, defaultValue, this.testData.getAttribute("Y", defaultValue)); //$NON-NLS-1$
+		assertEquals(this.id, this.baseData[2],this.testData.getAttribute("C", defaultValue)); //$NON-NLS-1$
+		assertSame(this.id, defaultValue, this.testData.getAttribute("Z", defaultValue)); //$NON-NLS-1$
+		assertEquals(this.id, this.baseData[3],this.testData.getAttribute("D", defaultValue)); //$NON-NLS-1$
+		assertSame(this.id, defaultValue, this.testData.getAttribute("W", defaultValue)); //$NON-NLS-1$
+		assertEquals(this.id, this.baseData[4],this.testData.getAttribute("E", defaultValue)); //$NON-NLS-1$
+		assertEquals(this.id, this.baseData[5],this.testData.getAttribute("F", defaultValue)); //$NON-NLS-1$
+	}
+
+	/**
+	 */
+	public void testGetAttributeObject() {
+		assertEquals(this.id, this.baseData[0],this.testData.getAttributeObject("A")); //$NON-NLS-1$
+		assertNull(this.id, this.testData.getAttribute("X")); //$NON-NLS-1$
+		assertEquals(this.id, this.baseData[1],this.testData.getAttributeObject("B")); //$NON-NLS-1$
+		assertNull(this.id, this.testData.getAttribute("Y")); //$NON-NLS-1$
+		assertEquals(this.id, this.baseData[2],this.testData.getAttributeObject("C")); //$NON-NLS-1$
+		assertNull(this.id, this.testData.getAttribute("Z")); //$NON-NLS-1$
+		assertEquals(this.id, this.baseData[3],this.testData.getAttributeObject("D")); //$NON-NLS-1$
+		assertNull(this.id, this.testData.getAttribute("W")); //$NON-NLS-1$
+		assertEquals(this.id, this.baseData[4],this.testData.getAttributeObject("E")); //$NON-NLS-1$
+		assertEquals(this.id, this.baseData[5],this.testData.getAttributeObject("F")); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testGetAttributeAsBoolString() throws Exception {
+		assertTrue(this.id, this.testData.getAttributeAsBool("A"));//$NON-NLS-1$
+		assertTrue(this.id, this.testData.getAttributeAsBool("B"));//$NON-NLS-1$
+		assertTrue(this.id, this.testData.getAttributeAsBool("C"));//$NON-NLS-1$
+		assertInvalidValue(this.id, this.testData,"getAttributeAsBool","D");//$NON-NLS-1$ //$NON-NLS-2$
+		assertInvalidValue(this.id, this.testData,"getAttributeAsBool","E");//$NON-NLS-1$ //$NON-NLS-2$
+		assertFalse(this.id, this.testData.getAttributeAsBool("F"));//$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testGetAttributeStringBoolean() throws Exception {
+		assertTrue(this.id, this.testData.getAttribute("A",true));//$NON-NLS-1$
+		assertTrue(this.id, this.testData.getAttribute("B",false));//$NON-NLS-1$
+		assertTrue(this.id, this.testData.getAttribute("C",false));//$NON-NLS-1$
+		assertTrue(this.id, this.testData.getAttribute("D",true));//$NON-NLS-1$
+		assertFalse(this.id, this.testData.getAttribute("E",false));//$NON-NLS-1$
+		assertFalse(this.id, this.testData.getAttribute("F",true));//$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testGetAttributeAsIntString() throws Exception {
+		assertEquals(this.id, 1,this.testData.getAttributeAsInt("A"));//$NON-NLS-1$
+		assertEquals(this.id, 2,this.testData.getAttributeAsInt("B"));//$NON-NLS-1$
+		assertEquals(this.id, 1, this.testData.getAttributeAsInt("C"));//$NON-NLS-1$
+		assertInvalidValue(this.id, this.testData,"getAttributeAsInt","D");//$NON-NLS-1$ //$NON-NLS-2$
+		assertInvalidValue(this.id, this.testData,"getAttributeAsInt","E");//$NON-NLS-1$ //$NON-NLS-2$
+		assertInvalidValue(this.id, this.testData,"getAttributeAsInt","F");//$NON-NLS-1$ //$NON-NLS-2$
+	}
+	
+	/**
+	 * @throws Exception
+	 */
+	public void testGetAttributeStringInt() throws Exception {
+		assertEquals(this.id, 1,this.testData.getAttribute("A",5));//$NON-NLS-1$
+		assertEquals(this.id, 2,this.testData.getAttribute("B",34));//$NON-NLS-1$
+		assertEquals(this.id, 1,this.testData.getAttribute("C",18));//$NON-NLS-1$
+		assertEquals(this.id, 24,this.testData.getAttribute("D",24));//$NON-NLS-1$
+		assertEquals(this.id, -34,this.testData.getAttribute("E",-34));//$NON-NLS-1$
+		assertEquals(this.id, 18,this.testData.getAttribute("F",18));//$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testGetAttributeAsLongString() throws Exception {
+		assertEquals(this.id, 1,this.testData.getAttributeAsLong("A"));//$NON-NLS-1$
+		assertEquals(this.id, 2,this.testData.getAttributeAsLong("B"));//$NON-NLS-1$
+		assertEquals(this.id, 1, this.testData.getAttributeAsLong("C"));//$NON-NLS-1$
+		assertInvalidValue(this.id, this.testData,"getAttributeAsLong","D");//$NON-NLS-1$ //$NON-NLS-2$
+		assertInvalidValue(this.id, this.testData,"getAttributeAsLong","E");//$NON-NLS-1$ //$NON-NLS-2$
+		assertInvalidValue(this.id, this.testData,"getAttributeAsLong","F");//$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testGetAttributegStringLong() throws Exception {
+		assertEquals(this.id, 1,this.testData.getAttribute("A",5));//$NON-NLS-1$
+		assertEquals(this.id, 2,this.testData.getAttribute("B",34));//$NON-NLS-1$
+		assertEquals(this.id, 1,this.testData.getAttribute("C",18));//$NON-NLS-1$
+		assertEquals(this.id, 24,this.testData.getAttribute("D",24));//$NON-NLS-1$
+		assertEquals(this.id, -34,this.testData.getAttribute("E",-34));//$NON-NLS-1$
+		assertEquals(this.id, 18,this.testData.getAttribute("F",18));//$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testGetAttributeAsFloatString() throws Exception {
+		assertEquals(this.id, 1f,this.testData.getAttributeAsFloat("A"));//$NON-NLS-1$
+		assertEquals(this.id, 2f,this.testData.getAttributeAsFloat("B"));//$NON-NLS-1$
+		assertEquals(this.id, 1f, this.testData.getAttributeAsFloat("C"));//$NON-NLS-1$
+		assertInvalidValue(this.id, this.testData,"getAttributeAsFloat","D");//$NON-NLS-1$ //$NON-NLS-2$
+		assertInvalidValue(this.id, this.testData,"getAttributeAsFloat","E");//$NON-NLS-1$ //$NON-NLS-2$
+		assertInvalidValue(this.id, this.testData,"getAttributeAsFloat","F");//$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testGetAttributeStringFloat() throws Exception {
+		assertEquals(this.id, 1f,this.testData.getAttribute("A",5f));//$NON-NLS-1$
+		assertEquals(this.id, 2f,this.testData.getAttribute("B",34f));//$NON-NLS-1$
+		assertEquals(this.id, 1f,this.testData.getAttribute("C",18f));//$NON-NLS-1$
+		assertEquals(this.id, 24f,this.testData.getAttribute("D",24f));//$NON-NLS-1$
+		assertEquals(this.id, -34f,this.testData.getAttribute("E",-34f));//$NON-NLS-1$
+		assertEquals(this.id, 18f,this.testData.getAttribute("F",18f));//$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testGetAttributeAsDoubleString() throws Exception {
+		assertEquals(this.id, 1.,this.testData.getAttributeAsDouble("A"));//$NON-NLS-1$
+		assertEquals(this.id, 2.,this.testData.getAttributeAsDouble("B"));//$NON-NLS-1$
+		assertEquals(this.id, 1., this.testData.getAttributeAsDouble("C"));//$NON-NLS-1$
+		assertInvalidValue(this.id, this.testData,"getAttributeAsDouble","D");//$NON-NLS-1$ //$NON-NLS-2$
+		assertInvalidValue(this.id, this.testData,"getAttributeAsDouble","E");//$NON-NLS-1$ //$NON-NLS-2$
+		assertInvalidValue(this.id, this.testData,"getAttributeAsDouble","F");//$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testGetAttributeStringDouble() throws Exception {
+		assertEquals(this.id, 1.,this.testData.getAttribute("A",5.));//$NON-NLS-1$
+		assertEquals(this.id, 2.,this.testData.getAttribute("B",34.));//$NON-NLS-1$
+		assertEquals(this.id, 1.,this.testData.getAttribute("C",18.));//$NON-NLS-1$
+		assertEquals(this.id, 24.,this.testData.getAttribute("D",24.));//$NON-NLS-1$
+		assertEquals(this.id, -34.,this.testData.getAttribute("E",-34.));//$NON-NLS-1$
+		assertEquals(this.id, 18.,this.testData.getAttribute("F",18.));//$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testGetAttributeAsStringString() throws Exception {
+		assertEquals(this.id, Long.toString(1),this.testData.getAttributeAsString("A"));//$NON-NLS-1$
+		assertEquals(this.id, Double.toString(2.),this.testData.getAttributeAsString("B"));//$NON-NLS-1$
+		assertEquals(this.id, Boolean.toString(true),this.testData.getAttributeAsString("C"));//$NON-NLS-1$
+		assertEquals(this.id, "Hello",this.testData.getAttributeAsString("D"));//$NON-NLS-1$ //$NON-NLS-2$
+		assertEquals(this.id, 1.+";"+2.,this.testData.getAttributeAsString("E"));//$NON-NLS-1$ //$NON-NLS-2$		
+		assertEquals(this.id, Boolean.toString(false),this.testData.getAttributeAsString("F"));//$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testGetAttributeStringString() throws Exception {
+		assertEquals(this.id, Long.toString(1),this.testData.getAttribute("A","default"));//$NON-NLS-1$ //$NON-NLS-2$
+		assertEquals(this.id, Double.toString(2.),this.testData.getAttribute("B","default"));//$NON-NLS-1$ //$NON-NLS-2$
+		assertEquals(this.id, Boolean.toString(true),this.testData.getAttribute("C","default"));//$NON-NLS-1$ //$NON-NLS-2$
+		assertEquals(this.id, "Hello",this.testData.getAttribute("D","default"));//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		assertEquals(this.id, 1.+";"+2.,this.testData.getAttribute("E","default"));//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		assertEquals(this.id, Boolean.toString(false),this.testData.getAttribute("F","default"));//$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	/**
+	 */
+	public void testFreeMemory() {
+		this.testData.freeMemory();
+		testIterator();
+	}
+
+}

Added: trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/AbstractBufferedAttributeProviderTest.java
===================================================================
--- trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/AbstractBufferedAttributeProviderTest.java	                        (rev 0)
+++ trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/AbstractBufferedAttributeProviderTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,124 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeException;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+
+/**
+ * Stub for BufferedAttributeContrainer.
+ * 
+ * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class ReadOnlyCacheProviderStub extends AbstractBufferedAttributeProvider {
+
+	private final Attribute[] attributes;
+	
+	/**
+	 */
+	public ReadOnlyCacheProviderStub() {
+		this.attributes = new Attribute[0];
+	}
+
+	/**
+	 * @param attributes
+	 */
+	public ReadOnlyCacheProviderStub(Attribute...attributes) {
+		this.attributes = attributes;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int getAttributeCount() {
+		return this.attributes.length;
+	}
+
+	@Override
+	public Collection<String> getAllAttributeNames() {
+		ArrayList<String> list = new ArrayList<String>();
+		for (Attribute attr : this.attributes) {
+			list.add(attr.getName());
+		}
+		return list;
+	}
+
+	@Override
+	protected AttributeValue loadValue(String name) throws AttributeException {
+		for (Attribute attr : this.attributes) {
+			if (attr.getName().equals(name)) {
+				return attr;
+			}
+		}
+		throw new NoAttributeFoundException(name);
+	}
+
+	@Override
+	public void toMap(Map<String, Object> mapToFill) {
+		for(Attribute attr : this.attributes) {
+			if (attr.isAssigned()) {
+				try {
+					mapToFill.put(attr.getName(), attr.getValue());
+				}
+				catch (Exception e) {
+					throw new RuntimeException(e);
+				}
+			}
+		}
+	}
+	
+}
+
+/**
+ * Test for BufferedAttributeContainer.
+ * 
+ * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AbstractBufferedAttributeProviderTest extends AbstractAttributeProviderTest<ReadOnlyCacheProviderStub> {
+
+	/**
+	 */
+	public AbstractBufferedAttributeProviderTest() {
+		super("BufferedAttributeContainerTest"); //$NON-NLS-1$
+	}
+	
+	@Override
+	protected ReadOnlyCacheProviderStub setUpTestCase() throws Exception {
+		return new ReadOnlyCacheProviderStub(this.baseData);
+	}
+	
+}
+
+	
\ No newline at end of file

Added: trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/BufferedAttributeCollectionTest.java
===================================================================
--- trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/BufferedAttributeCollectionTest.java	                        (rev 0)
+++ trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/BufferedAttributeCollectionTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,165 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeException;
+import org.arakhne.afc.attrs.attr.AttributeImpl;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+
+/**
+ * Stub for CacheProvider.
+ * 
+ * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class CacheProviderStub extends BufferedAttributeCollection {
+
+	private final Map<String, Attribute> attributes = new HashMap<String,Attribute>();
+	
+	/**
+	 */
+	public CacheProviderStub() {
+		//
+	}
+
+	/**
+	 * @param attributes
+	 */
+	public CacheProviderStub(Attribute...attributes) {
+		for (Attribute attr : attributes) {
+			this.attributes.put(attr.getName(), attr);
+		}
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int getAttributeCount() {
+		return this.attributes.size();
+	}
+
+	@Override
+	public Collection<String> getAllAttributeNames() {
+		return new ArrayList<String>(this.attributes.keySet());
+	}
+
+	@Override
+	protected AttributeValue loadValue(String name) throws AttributeException {
+		Attribute attr = this.attributes.get(name);
+		if (attr!=null) return attr;
+		throw new NoAttributeFoundException(name);
+	}
+
+	@Override
+	protected boolean removeAllValues() throws AttributeException {
+		this.attributes.clear();
+		return true;
+	}
+
+	@Override
+	protected AttributeValue removeValue(String name) throws AttributeException {
+		return this.attributes.remove(name);
+	}
+
+	@Override
+	protected void saveValue(String name, AttributeValue value) throws AttributeException {
+		Attribute attr = this.attributes.get(name);
+		if (attr!=null) {
+			attr.setValue(value);
+		}
+		else {
+			this.attributes.put(name, new AttributeImpl(name,value));
+		}
+	}
+
+	@Override
+	public void setAttributes(Map<String, Object> content) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void setAttributes(AttributeProvider content)
+			throws AttributeException {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void addAttributes(Map<String, Object> content) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void addAttributes(AttributeProvider content)
+			throws AttributeException {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void toMap(Map<String, Object> mapToFill) {
+		for(Attribute attr : this.attributes.values()) {
+			if (attr.isAssigned()) {
+				try {
+					mapToFill.put(attr.getName(), attr.getValue());
+				}
+				catch (Exception e) {
+					throw new RuntimeException(e);
+				}
+			}
+		}
+	}
+
+}
+
+/**
+ * Test for BufferedAttributeProvider.
+ * 
+ * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class BufferedAttributeCollectionTest extends AbstractAttributeCollectionTest<CacheProviderStub> {
+
+	/**
+	 */
+	public BufferedAttributeCollectionTest() {
+		super("BufferedAttributeProviderTest"); //$NON-NLS-1$
+	}
+	
+	@Override
+	protected CacheProviderStub setUpTestCase() throws Exception {
+		return new CacheProviderStub(this.baseData);
+	}
+	
+}
+
+	
\ No newline at end of file

Added: trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/HeapAttributeCollectionTest.java
===================================================================
--- trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/HeapAttributeCollectionTest.java	                        (rev 0)
+++ trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/HeapAttributeCollectionTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,49 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import org.arakhne.afc.attrs.collection.HeapAttributeCollection;
+
+
+/**
+ * Test for HeapAttributeProvider.
+ * 
+ * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class HeapAttributeCollectionTest extends AbstractAttributeCollectionTest<HeapAttributeCollection> {
+
+	/**
+	 */
+	public HeapAttributeCollectionTest() {
+		super("HeapAttributeProviderTest"); //$NON-NLS-1$
+	}
+	
+	@Override
+	protected HeapAttributeCollection setUpTestCase() throws Exception {
+		return new HeapAttributeCollection();
+	}
+	
+}

Added: trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/MultiAttributeCollectionTest.java
===================================================================
--- trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/MultiAttributeCollectionTest.java	                        (rev 0)
+++ trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/MultiAttributeCollectionTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,1024 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import org.arakhne.afc.attrs.AbstractAttrTestCase;
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeImpl;
+import org.arakhne.afc.attrs.attr.AttributeType;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+import org.arakhne.afc.attrs.attr.AttributeValueImpl;
+import org.arakhne.afc.attrs.collection.AbstractAttributeProvider;
+import org.arakhne.afc.attrs.collection.HeapAttributeCollection;
+import org.arakhne.afc.attrs.collection.MultiAttributeCollection;
+
+/**
+ * Test of MultiAttributeProvider.
+ * 
+ * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @since 4.0
+ */
+public class MultiAttributeCollectionTest extends AbstractAttrTestCase {
+
+	private MultiAttributeCollection provider;
+	private HeapAttributeCollection subprovider1;
+	private HeapAttributeCollection subprovider2;
+	private HeapAttributeCollection subprovider3;
+	private AttributeContainerStub subcontainer4;
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		this.provider = new MultiAttributeCollection();
+		this.subprovider1 = new HeapAttributeCollection();
+		this.subprovider2 = new HeapAttributeCollection();
+		this.subprovider3 = new HeapAttributeCollection();
+		this.subcontainer4 = new AttributeContainerStub();
+		
+		this.subprovider1.setAttribute("A", true); //$NON-NLS-1$
+		this.subprovider1.setAttribute("B", 1); //$NON-NLS-1$
+		this.subprovider1.setAttribute("C", new URL("http://www.multiagent.fr";)); //$NON-NLS-1$ //$NON-NLS-2$
+		this.subprovider1.setAttribute("E", new URL("http://www.multiagent.fr";)); //$NON-NLS-1$ //$NON-NLS-2$
+		this.subprovider1.setAttribute("Z1", "Z1"); //$NON-NLS-1$ //$NON-NLS-2$
+
+		this.subprovider2.setAttribute("A", true); //$NON-NLS-1$
+		this.subprovider2.setAttribute("B", 1.); //$NON-NLS-1$
+		this.subprovider2.setAttribute("D", "abc"); //$NON-NLS-1$ //$NON-NLS-2$
+		this.subprovider2.setAttribute("E", 1); //$NON-NLS-1$
+		this.subprovider2.setAttribute("Z2", "Z2"); //$NON-NLS-1$ //$NON-NLS-2$
+
+		this.subprovider3.setAttribute("A", false); //$NON-NLS-1$
+		this.subprovider3.setAttribute("B", 1); //$NON-NLS-1$
+		this.subprovider3.setAttribute("C", new URL("http://www.multiagent.fr";)); //$NON-NLS-1$ //$NON-NLS-2$
+		this.subprovider3.setAttribute("D", "abc"); //$NON-NLS-1$ //$NON-NLS-2$
+		this.subprovider3.setAttribute("E", true); //$NON-NLS-1$
+		this.subprovider3.setAttribute("Z3", "Z3"); //$NON-NLS-1$ //$NON-NLS-2$
+		
+		this.subcontainer4.provider.setAttribute("A", true); //$NON-NLS-1$
+		this.subcontainer4.provider.setAttribute("Z4", "Z4"); //$NON-NLS-1$ //$NON-NLS-2$
+		
+		this.provider.addAttributeContainer(this.subprovider1);
+		this.provider.addAttributeContainer(this.subprovider2);
+		this.provider.addAttributeContainer(this.subprovider3);
+		this.provider.addAttributeContainer(this.subcontainer4);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void tearDown() throws Exception {
+		this.provider = null;
+		this.subprovider1 = this.subprovider2 = this.subprovider3 = null;
+		this.subcontainer4 = null;
+		super.tearDown();
+	}
+	
+	private static Attribute makeUninitialized(String name, AttributeType type) {
+		return new AttributeImpl(name, type);
+	}
+
+	/**
+	 */
+	public void testGetAttributeContainerCount() {
+		assertEquals(4, this.provider.getAttributeContainerCount());
+	}	
+
+	/**
+	 */
+	public void testRemoveAllAttributes() {
+		assertTrue(this.provider.removeAllAttributes());
+		assertEquals(0, this.subprovider1.getAttributeCount());
+		assertEquals(0, this.subprovider2.getAttributeCount());
+		assertEquals(0, this.subprovider3.getAttributeCount());
+		assertEquals(2, this.subcontainer4.getAttributeCount());
+		assertEquals(2, this.provider.getAttributeCount());
+
+		assertEpsilonEquals(Collections.emptyList(), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Collections.emptyList(), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Collections.emptyList(), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("A", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+
+		assertFalse(this.provider.removeAllAttributes());
+
+		assertEpsilonEquals(Collections.emptyList(), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Collections.emptyList(), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Collections.emptyList(), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("A", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testRemoveAttributeString() throws Exception {
+		//
+		// Remove A
+		//
+		assertTrue(this.provider.removeAttribute("A")); //$NON-NLS-1$
+		assertEquals(4, this.subprovider1.getAttributeCount());
+		assertEquals(4, this.subprovider2.getAttributeCount());
+		assertEquals(5, this.subprovider3.getAttributeCount());
+		assertEquals(2, this.subcontainer4.getAttributeCount());
+		assertEquals(9, this.provider.getAttributeCount());		
+		
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("Z1", "Z1") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", 1), //$NON-NLS-1$
+				new AttributeImpl("Z2", "Z2") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("B", 1.), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", true), //$NON-NLS-1$
+				new AttributeImpl("Z3", "Z3") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("A", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("B", AttributeType.INTEGER), //$NON-NLS-1$
+				makeUninitialized("C", AttributeType.URL), //$NON-NLS-1$
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("E", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z3", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+
+		//
+		// Failure on remove of ZZZ
+		//
+		assertFalse(this.provider.removeAttribute("ZZZ")); //$NON-NLS-1$
+
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("Z1", "Z1") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", 1), //$NON-NLS-1$
+				new AttributeImpl("Z2", "Z2") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("B", 1.), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", true), //$NON-NLS-1$
+				new AttributeImpl("Z3", "Z3") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("A", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("B", AttributeType.INTEGER), //$NON-NLS-1$
+				makeUninitialized("C", AttributeType.URL), //$NON-NLS-1$
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("E", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z3", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+
+		//
+		// Remove Z3
+		//
+		assertTrue(this.provider.removeAttribute("Z3")); //$NON-NLS-1$
+		assertEquals(4, this.subprovider1.getAttributeCount());
+		assertEquals(4, this.subprovider2.getAttributeCount());
+		assertEquals(4, this.subprovider3.getAttributeCount());
+		assertEquals(2, this.subcontainer4.getAttributeCount());
+		assertEquals(8, this.provider.getAttributeCount());
+
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("Z1", "Z1") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", 1), //$NON-NLS-1$
+				new AttributeImpl("Z2", "Z2") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("B", 1.), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", true) //$NON-NLS-1$
+		), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("A", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("B", AttributeType.INTEGER), //$NON-NLS-1$
+				makeUninitialized("C", AttributeType.URL), //$NON-NLS-1$
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("E", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testRenameAttributeStringString() throws Exception {
+		//
+		// Rename A to ZZZ
+		//
+		assertTrue(this.provider.renameAttribute("A", "ZZZ")); //$NON-NLS-1$ //$NON-NLS-2$
+		assertEquals(5, this.subprovider1.getAttributeCount());
+		assertEquals(5, this.subprovider2.getAttributeCount());
+		assertEquals(6, this.subprovider3.getAttributeCount());
+		assertEquals(2, this.subcontainer4.getAttributeCount());
+		assertEquals(10, this.provider.getAttributeCount());		
+
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", true), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("Z1", "Z1") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", true), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", 1), //$NON-NLS-1$
+				new AttributeImpl("Z2", "Z2") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", false), //$NON-NLS-1$
+				new AttributeImpl("B", 1.), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", true), //$NON-NLS-1$
+				new AttributeImpl("Z3", "Z3") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("ZZZ", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("A", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("B", AttributeType.INTEGER), //$NON-NLS-1$
+				makeUninitialized("C", AttributeType.URL), //$NON-NLS-1$
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("E", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z3", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+
+		//
+		// Rename TOTOZZZ to A
+		//
+		assertFalse(this.provider.renameAttribute("TOTOZZZ", "A")); //$NON-NLS-1$ //$NON-NLS-2$
+		assertEquals(5, this.subprovider1.getAttributeCount());
+		assertEquals(5, this.subprovider2.getAttributeCount());
+		assertEquals(6, this.subprovider3.getAttributeCount());
+		assertEquals(2, this.subcontainer4.getAttributeCount());
+		assertEquals(10, this.provider.getAttributeCount());		
+
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", true), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("Z1", "Z1") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", true), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", 1), //$NON-NLS-1$
+				new AttributeImpl("Z2", "Z2") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", false), //$NON-NLS-1$
+				new AttributeImpl("B", 1.), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", true), //$NON-NLS-1$
+				new AttributeImpl("Z3", "Z3") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("ZZZ", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("A", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("B", AttributeType.INTEGER), //$NON-NLS-1$
+				makeUninitialized("C", AttributeType.URL), //$NON-NLS-1$
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("E", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z3", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+
+		//
+		// Rename Z4 to C
+		//
+		assertFalse(this.provider.renameAttribute("TOTOZZZ", "A")); //$NON-NLS-1$ //$NON-NLS-2$
+		assertEquals(5, this.subprovider1.getAttributeCount());
+		assertEquals(5, this.subprovider2.getAttributeCount());
+		assertEquals(6, this.subprovider3.getAttributeCount());
+		assertEquals(2, this.subcontainer4.getAttributeCount());
+		assertEquals(10, this.provider.getAttributeCount());		
+
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", true), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("Z1", "Z1") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", true), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", 1), //$NON-NLS-1$
+				new AttributeImpl("Z2", "Z2") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", false), //$NON-NLS-1$
+				new AttributeImpl("B", 1.), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", true), //$NON-NLS-1$
+				new AttributeImpl("Z3", "Z3") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("ZZZ", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("A", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("B", AttributeType.INTEGER), //$NON-NLS-1$
+				makeUninitialized("C", AttributeType.URL), //$NON-NLS-1$
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("E", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z3", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testRenameAttributeStringStringBoolean() throws Exception {
+		//
+		// Rename A to ZZZ
+		//
+		assertTrue(this.provider.renameAttribute("A", "ZZZ")); //$NON-NLS-1$ //$NON-NLS-2$
+		assertEquals(5, this.subprovider1.getAttributeCount());
+		assertEquals(5, this.subprovider2.getAttributeCount());
+		assertEquals(6, this.subprovider3.getAttributeCount());
+		assertEquals(2, this.subcontainer4.getAttributeCount());
+		assertEquals(10, this.provider.getAttributeCount());		
+
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", true), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("Z1", "Z1") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", true), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", 1), //$NON-NLS-1$
+				new AttributeImpl("Z2", "Z2") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", false), //$NON-NLS-1$
+				new AttributeImpl("B", 1.), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", true), //$NON-NLS-1$
+				new AttributeImpl("Z3", "Z3") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("ZZZ", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("A", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("B", AttributeType.INTEGER), //$NON-NLS-1$
+				makeUninitialized("C", AttributeType.URL), //$NON-NLS-1$
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("E", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z3", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+
+		//
+		// Rename TOTOZZZ to A
+		//
+		assertFalse(this.provider.renameAttribute("TOTOZZZ", "A")); //$NON-NLS-1$ //$NON-NLS-2$
+		assertEquals(5, this.subprovider1.getAttributeCount());
+		assertEquals(5, this.subprovider2.getAttributeCount());
+		assertEquals(6, this.subprovider3.getAttributeCount());
+		assertEquals(2, this.subcontainer4.getAttributeCount());
+		assertEquals(10, this.provider.getAttributeCount());		
+
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", true), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("Z1", "Z1") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", true), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", 1), //$NON-NLS-1$
+				new AttributeImpl("Z2", "Z2") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", false), //$NON-NLS-1$
+				new AttributeImpl("B", 1.), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", true), //$NON-NLS-1$
+				new AttributeImpl("Z3", "Z3") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("ZZZ", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("A", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("B", AttributeType.INTEGER), //$NON-NLS-1$
+				makeUninitialized("C", AttributeType.URL), //$NON-NLS-1$
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("E", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z3", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+
+		//
+		// Rename Z4 to C
+		//
+		assertFalse(this.provider.renameAttribute("TOTOZZZ", "A")); //$NON-NLS-1$ //$NON-NLS-2$
+		assertEquals(5, this.subprovider1.getAttributeCount());
+		assertEquals(5, this.subprovider2.getAttributeCount());
+		assertEquals(6, this.subprovider3.getAttributeCount());
+		assertEquals(2, this.subcontainer4.getAttributeCount());
+		assertEquals(10, this.provider.getAttributeCount());		
+
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", true), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("Z1", "Z1") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", true), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", 1), //$NON-NLS-1$
+				new AttributeImpl("Z2", "Z2") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", false), //$NON-NLS-1$
+				new AttributeImpl("B", 1.), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", true), //$NON-NLS-1$
+				new AttributeImpl("Z3", "Z3") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("ZZZ", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("A", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("B", AttributeType.INTEGER), //$NON-NLS-1$
+				makeUninitialized("C", AttributeType.URL), //$NON-NLS-1$
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("E", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z3", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testSetAttributeStringAttributeValue() throws Exception {
+		//
+		// Set ZZZ
+		//
+		assertEquals(
+				new AttributeImpl("ZZZ", "xyz"), //$NON-NLS-1$ //$NON-NLS-2$
+				this.provider.setAttribute("ZZZ", new AttributeValueImpl("xyz"))); //$NON-NLS-1$ //$NON-NLS-2$
+		assertEquals(6, this.subprovider1.getAttributeCount());
+		assertEquals(6, this.subprovider2.getAttributeCount());
+		assertEquals(7, this.subprovider3.getAttributeCount());
+		assertEquals(2, this.subcontainer4.getAttributeCount());
+		assertEquals(10, this.provider.getAttributeCount());		
+
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", "xyz"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("Z1", "Z1") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", "xyz"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", 1), //$NON-NLS-1$
+				new AttributeImpl("Z2", "Z2") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", "xyz"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("A", false), //$NON-NLS-1$
+				new AttributeImpl("B", 1.), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", true), //$NON-NLS-1$
+				new AttributeImpl("Z3", "Z3") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("ZZZ", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("A", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("B", AttributeType.INTEGER), //$NON-NLS-1$
+				makeUninitialized("C", AttributeType.URL), //$NON-NLS-1$
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("E", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z3", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+
+		//
+		// Set B
+		//
+		assertEquals(
+				new AttributeImpl("B", "def"), //$NON-NLS-1$ //$NON-NLS-2$
+				this.provider.setAttribute("B", new AttributeValueImpl("def"))); //$NON-NLS-1$ //$NON-NLS-2$
+		assertEquals(6, this.subprovider1.getAttributeCount());
+		assertEquals(6, this.subprovider2.getAttributeCount());
+		assertEquals(7, this.subprovider3.getAttributeCount());
+		assertEquals(2, this.subcontainer4.getAttributeCount());
+		assertEquals(10, this.provider.getAttributeCount());		
+
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", "xyz"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("B", "def"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("Z1", "Z1") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", "xyz"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("B", "def"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", 1), //$NON-NLS-1$
+				new AttributeImpl("Z2", "Z2") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", "xyz"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("A", false), //$NON-NLS-1$
+				new AttributeImpl("B", "def"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", true), //$NON-NLS-1$
+				new AttributeImpl("Z3", "Z3") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("ZZZ", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("A", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("B", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("C", AttributeType.URL), //$NON-NLS-1$
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("E", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z3", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testSetAttributeAttribute() throws Exception {
+		//
+		// Set ZZZ
+		//
+		assertEquals(
+				new AttributeImpl("ZZZ", "xyz"), //$NON-NLS-1$ //$NON-NLS-2$
+				this.provider.setAttribute(new AttributeImpl("ZZZ", "xyz"))); //$NON-NLS-1$ //$NON-NLS-2$
+		assertEquals(6, this.subprovider1.getAttributeCount());
+		assertEquals(6, this.subprovider2.getAttributeCount());
+		assertEquals(7, this.subprovider3.getAttributeCount());
+		assertEquals(2, this.subcontainer4.getAttributeCount());
+		assertEquals(10, this.provider.getAttributeCount());		
+
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", "xyz"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("Z1", "Z1") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", "xyz"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", 1), //$NON-NLS-1$
+				new AttributeImpl("Z2", "Z2") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", "xyz"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("A", false), //$NON-NLS-1$
+				new AttributeImpl("B", 1.), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", true), //$NON-NLS-1$
+				new AttributeImpl("Z3", "Z3") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("ZZZ", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("A", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("B", AttributeType.INTEGER), //$NON-NLS-1$
+				makeUninitialized("C", AttributeType.URL), //$NON-NLS-1$
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("E", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z3", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+
+		//
+		// Set B
+		//
+		assertEquals(
+				new AttributeImpl("B", "def"), //$NON-NLS-1$ //$NON-NLS-2$
+				this.provider.setAttribute(new AttributeImpl("B", "def"))); //$NON-NLS-1$ //$NON-NLS-2$
+		assertEquals(6, this.subprovider1.getAttributeCount());
+		assertEquals(6, this.subprovider2.getAttributeCount());
+		assertEquals(7, this.subprovider3.getAttributeCount());
+		assertEquals(2, this.subcontainer4.getAttributeCount());
+		assertEquals(10, this.provider.getAttributeCount());		
+
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", "xyz"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("B", "def"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("Z1", "Z1") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", "xyz"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("B", "def"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", 1), //$NON-NLS-1$
+				new AttributeImpl("Z2", "Z2") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("ZZZ", "xyz"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("A", false), //$NON-NLS-1$
+				new AttributeImpl("B", "def"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", true), //$NON-NLS-1$
+				new AttributeImpl("Z3", "Z3") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("ZZZ", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("A", AttributeType.BOOLEAN), //$NON-NLS-1$
+				makeUninitialized("B", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("C", AttributeType.URL), //$NON-NLS-1$
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("E", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z3", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testSetAttributeTypeStringAttributeType() throws Exception {
+		//
+		// Set type of A to STRING
+		//
+		assertEquals(
+				makeUninitialized("A", AttributeType.STRING), //$NON-NLS-1$
+				this.provider.setAttributeType("A", AttributeType.STRING)); //$NON-NLS-1$
+
+		assertEquals(5, this.subprovider1.getAttributeCount());
+		assertEquals(5, this.subprovider2.getAttributeCount());
+		assertEquals(6, this.subprovider3.getAttributeCount());
+		assertEquals(2, this.subcontainer4.getAttributeCount());
+		assertEquals(9, this.provider.getAttributeCount());		
+
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", Boolean.TRUE.toString()), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("Z1", "Z1") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", Boolean.TRUE.toString()), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", 1), //$NON-NLS-1$
+				new AttributeImpl("Z2", "Z2") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", Boolean.FALSE.toString()), //$NON-NLS-1$
+				new AttributeImpl("B", 1.), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", true), //$NON-NLS-1$
+				new AttributeImpl("Z3", "Z3") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("A", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("B", AttributeType.INTEGER), //$NON-NLS-1$
+				makeUninitialized("C", AttributeType.URL), //$NON-NLS-1$
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("E", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z3", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+
+		//
+		// Set type of B to STRING
+		//
+		assertEquals(
+				makeUninitialized("B", AttributeType.STRING), //$NON-NLS-1$
+				this.provider.setAttributeType("B", AttributeType.STRING)); //$NON-NLS-1$
+
+		assertEquals(5, this.subprovider1.getAttributeCount());
+		assertEquals(5, this.subprovider2.getAttributeCount());
+		assertEquals(6, this.subprovider3.getAttributeCount());
+		assertEquals(2, this.subcontainer4.getAttributeCount());
+		assertEquals(9, this.provider.getAttributeCount());		
+
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", Boolean.TRUE.toString()), //$NON-NLS-1$
+				new AttributeImpl("B", Long.toString(1)), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("Z1", "Z1") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider1.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", Boolean.TRUE.toString()), //$NON-NLS-1$
+				new AttributeImpl("B", Double.toString(1.)), //$NON-NLS-1$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", 1), //$NON-NLS-1$
+				new AttributeImpl("Z2", "Z2") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider2.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", Boolean.FALSE.toString()), //$NON-NLS-1$
+				new AttributeImpl("B", Long.toString(1)), //$NON-NLS-1$
+				new AttributeImpl("C", new URL("http://www.multiagent.fr";)), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("D", "abc"), //$NON-NLS-1$ //$NON-NLS-2$
+				new AttributeImpl("E", true), //$NON-NLS-1$
+				new AttributeImpl("Z3", "Z3") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subprovider3.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("A", true), //$NON-NLS-1$
+				new AttributeImpl("Z4", "Z4") //$NON-NLS-1$ //$NON-NLS-2$
+		), this.subcontainer4.getAllAttributes());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("A", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("B", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("C", AttributeType.URL), //$NON-NLS-1$
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("E", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z3", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z4", AttributeType.STRING) //$NON-NLS-1$
+		), this.provider.getAllAttributes());
+	}
+
+	/**
+	 * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 * @since 4.0
+	 */
+	private static class AttributeContainerStub extends AbstractAttributeProvider {
+
+		/** Attribute provider wrapped by this stub.
+		 */
+		public final HeapAttributeCollection provider = new HeapAttributeCollection();
+		
+		/**
+		 */
+		public AttributeContainerStub() {
+			//
+		}
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void freeMemory() {
+			this.provider.freeMemory();
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public Collection<String> getAllAttributeNames() {
+			return this.provider.getAllAttributeNames();
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public Collection<Attribute> getAllAttributes() {
+			return this.provider.getAllAttributes();
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public Map<AttributeType, Collection<Attribute>> getAllAttributesByType() {
+			return this.provider.getAllAttributesByType();
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public AttributeValue getAttribute(String name) {
+			return this.provider.getAttribute(name);
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public AttributeValue getAttribute(String name, AttributeValue defaultValue) {
+			return this.provider.getAttribute(name, defaultValue);
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public int getAttributeCount() {
+			return this.provider.getAttributeCount();
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public Attribute getAttributeObject(String name) {
+			return this.provider.getAttributeObject(name);
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public boolean hasAttribute(String name) {
+			return this.provider.hasAttribute(name);
+		}
+
+		@Override
+		public void toMap(Map<String, Object> mapToFill) {
+			this.provider.toMap(mapToFill);
+		}
+		
+	}
+	
+}

Added: trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/MultiAttributeProviderTest.java
===================================================================
--- trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/MultiAttributeProviderTest.java	                        (rev 0)
+++ trunk/attrs/src/test/java/org/arakhne/afc/attrs/collection/MultiAttributeProviderTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,325 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.attrs.collection;
+
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+
+import org.arakhne.afc.attrs.AbstractAttrTestCase;
+import org.arakhne.afc.attrs.attr.Attribute;
+import org.arakhne.afc.attrs.attr.AttributeImpl;
+import org.arakhne.afc.attrs.attr.AttributeType;
+import org.arakhne.afc.attrs.attr.AttributeValue;
+import org.arakhne.afc.attrs.attr.AttributeValueImpl;
+import org.arakhne.afc.attrs.collection.HeapAttributeCollection;
+import org.arakhne.afc.attrs.collection.MultiAttributeProvider;
+
+/**
+ * Test of MultiAttributeContainer.
+ * 
+ * @author St&eacute;phane GALLAND &lt;stephane.galland@xxxxxxx&gt;
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @since 4.0
+ */
+public class MultiAttributeProviderTest extends AbstractAttrTestCase {
+
+	private MultiAttributeProvider container;
+	private HeapAttributeCollection subcontainer1;
+	private HeapAttributeCollection subcontainer2;
+	private HeapAttributeCollection subcontainer3;
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		this.container = new MultiAttributeProvider();
+		this.subcontainer1 = new HeapAttributeCollection();
+		this.subcontainer2 = new HeapAttributeCollection();
+		this.subcontainer3 = new HeapAttributeCollection();
+		
+		this.subcontainer1.setAttribute("A", true); //$NON-NLS-1$
+		this.subcontainer1.setAttribute("B", 1); //$NON-NLS-1$
+		this.subcontainer1.setAttribute("C", new URL("http://www.multiagent.fr";)); //$NON-NLS-1$ //$NON-NLS-2$
+		this.subcontainer1.setAttribute("E", new URL("http://www.multiagent.fr";)); //$NON-NLS-1$ //$NON-NLS-2$
+		this.subcontainer1.setAttribute("Z1", "Z1"); //$NON-NLS-1$ //$NON-NLS-2$
+
+		this.subcontainer2.setAttribute("A", true); //$NON-NLS-1$
+		this.subcontainer2.setAttribute("B", 1.); //$NON-NLS-1$
+		this.subcontainer2.setAttribute("D", "abc"); //$NON-NLS-1$ //$NON-NLS-2$
+		this.subcontainer2.setAttribute("E", 1); //$NON-NLS-1$
+		this.subcontainer2.setAttribute("Z2", "Z2"); //$NON-NLS-1$ //$NON-NLS-2$
+
+		this.subcontainer3.setAttribute("A", false); //$NON-NLS-1$
+		this.subcontainer3.setAttribute("B", 1); //$NON-NLS-1$
+		this.subcontainer3.setAttribute("C", new URL("http://www.multiagent.fr";)); //$NON-NLS-1$ //$NON-NLS-2$
+		this.subcontainer3.setAttribute("D", "abc"); //$NON-NLS-1$ //$NON-NLS-2$
+		this.subcontainer3.setAttribute("E", true); //$NON-NLS-1$
+		this.subcontainer3.setAttribute("Z3", "Z3"); //$NON-NLS-1$ //$NON-NLS-2$
+		
+		this.container.addAttributeContainer(this.subcontainer1);
+		this.container.addAttributeContainer(this.subcontainer2);
+		this.container.addAttributeContainer(this.subcontainer3);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void tearDown() throws Exception {
+		this.container = null;
+		this.subcontainer1 = this.subcontainer2 = this.subcontainer3 = null;
+		super.tearDown();
+	}
+	
+	/**
+	 */
+	public void testGetAttributeCount() {
+		assertEquals(8, this.container.getAttributeCount());
+	}
+	
+	/**
+	 */
+	public void testGetAttributeContainerCount() {
+		assertEquals(3, this.container.getAttributeContainerCount());
+	}	
+
+	/**
+	 */
+	public void testGetAllAttributeNames() {
+		Collection<String> names = this.container.getAllAttributeNames();
+		assertNotNull(names);
+		assertEquals(8, names.size());
+		assertEpsilonEquals(Arrays.asList(
+				"A", //$NON-NLS-1$
+				"B", //$NON-NLS-1$
+				"C", //$NON-NLS-1$
+				"D", //$NON-NLS-1$
+				"E", //$NON-NLS-1$
+				"Z1", //$NON-NLS-1$
+				"Z2", //$NON-NLS-1$
+				"Z3" //$NON-NLS-1$
+				), names);
+	}
+	
+	private static void assertUninitialized(AttributeType type, AttributeValue v) {
+		assertNotNull(v);
+		assertEquals(type, v.getType());
+		assertFalse(v.isAssigned());
+	}
+
+	private static Attribute makeUninitialized(String name, AttributeType type) {
+		return new AttributeImpl(name, type);
+	}
+
+	/**
+	 */
+	public void testHasAttributeString() {
+		assertTrue(this.container.hasAttribute("A")); //$NON-NLS-1$
+		assertTrue(this.container.hasAttribute("B")); //$NON-NLS-1$
+		assertTrue(this.container.hasAttribute("C")); //$NON-NLS-1$
+		assertTrue(this.container.hasAttribute("D")); //$NON-NLS-1$
+		assertTrue(this.container.hasAttribute("E")); //$NON-NLS-1$
+		assertFalse(this.container.hasAttribute("F")); //$NON-NLS-1$
+		assertTrue(this.container.hasAttribute("Z1")); //$NON-NLS-1$
+		assertTrue(this.container.hasAttribute("Z2")); //$NON-NLS-1$
+		assertTrue(this.container.hasAttribute("Z3")); //$NON-NLS-1$
+		assertFalse(this.container.hasAttribute("Z4")); //$NON-NLS-1$
+	}
+
+	/**
+	 */
+	public void testGetAttributeString() {
+		assertUninitialized(AttributeType.BOOLEAN,
+				this.container.getAttribute("A")); //$NON-NLS-1$
+		assertEquals(new AttributeValueImpl(1),
+				this.container.getAttribute("B")); //$NON-NLS-1$
+		assertUninitialized(AttributeType.URL,
+				this.container.getAttribute("C")); //$NON-NLS-1$
+		assertUninitialized(AttributeType.STRING,
+				this.container.getAttribute("D")); //$NON-NLS-1$
+		assertUninitialized(AttributeType.OBJECT,
+				this.container.getAttribute("E")); //$NON-NLS-1$
+		assertNull(this.container.getAttribute("F")); //$NON-NLS-1$
+		assertUninitialized(AttributeType.STRING,
+				this.container.getAttribute("Z1")); //$NON-NLS-1$
+		assertUninitialized(AttributeType.STRING,
+				this.container.getAttribute("Z2")); //$NON-NLS-1$
+		assertUninitialized(AttributeType.STRING,
+				this.container.getAttribute("Z3")); //$NON-NLS-1$
+		assertNull(this.container.getAttribute("Z4")); //$NON-NLS-1$
+	}
+
+	/**
+	 */
+	public void testGetAttributeStringAttributeValue() {
+		AttributeValue defaultValue = new AttributeValueImpl(456);
+		assertUninitialized(AttributeType.BOOLEAN,
+				this.container.getAttribute("A", defaultValue)); //$NON-NLS-1$
+		assertEquals(new AttributeValueImpl(1),
+				this.container.getAttribute("B", defaultValue)); //$NON-NLS-1$
+		assertUninitialized(AttributeType.URL,
+				this.container.getAttribute("C", defaultValue)); //$NON-NLS-1$
+		assertUninitialized(AttributeType.STRING,
+				this.container.getAttribute("D", defaultValue)); //$NON-NLS-1$
+		assertUninitialized(AttributeType.OBJECT,
+				this.container.getAttribute("E", defaultValue)); //$NON-NLS-1$
+		assertSame(defaultValue, this.container.getAttribute("F", defaultValue)); //$NON-NLS-1$
+		assertUninitialized(AttributeType.STRING,
+				this.container.getAttribute("Z1", defaultValue)); //$NON-NLS-1$
+		assertUninitialized(AttributeType.STRING,
+				this.container.getAttribute("Z2", defaultValue)); //$NON-NLS-1$
+		assertUninitialized(AttributeType.STRING,
+				this.container.getAttribute("Z3", defaultValue)); //$NON-NLS-1$
+		assertSame(defaultValue, this.container.getAttribute("Z4", defaultValue)); //$NON-NLS-1$
+	}
+
+	/**
+	 */
+	public void testGetAttributeObjectString() {
+		assertUninitialized(AttributeType.BOOLEAN,
+				this.container.getAttributeObject("A")); //$NON-NLS-1$
+		assertEquals(new AttributeImpl("B",1), //$NON-NLS-1$
+				this.container.getAttributeObject("B")); //$NON-NLS-1$
+		assertUninitialized(AttributeType.URL,
+				this.container.getAttributeObject("C")); //$NON-NLS-1$
+		assertUninitialized(AttributeType.STRING,
+				this.container.getAttributeObject("D")); //$NON-NLS-1$
+		assertUninitialized(AttributeType.OBJECT,
+				this.container.getAttributeObject("E")); //$NON-NLS-1$
+		assertNull(this.container.getAttribute("F")); //$NON-NLS-1$
+		assertUninitialized(AttributeType.STRING,
+				this.container.getAttributeObject("Z1")); //$NON-NLS-1$
+		assertUninitialized(AttributeType.STRING,
+				this.container.getAttributeObject("Z2")); //$NON-NLS-1$
+		assertUninitialized(AttributeType.STRING,
+				this.container.getAttributeObject("Z3")); //$NON-NLS-1$
+		assertNull(this.container.getAttributeObject("Z4")); //$NON-NLS-1$
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testGetAllAttributes() throws Exception {
+		Collection<Attribute> attrs = this.container.getAllAttributes();
+		assertNotNull(attrs);
+		assertEquals(8, attrs.size());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("A", AttributeType.BOOLEAN), //$NON-NLS-1$
+				new AttributeImpl("B", 1), //$NON-NLS-1$
+				makeUninitialized("C", AttributeType.URL), //$NON-NLS-1$
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("E", AttributeType.OBJECT), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z3", AttributeType.STRING) //$NON-NLS-1$
+		), attrs);
+	}
+
+	/**
+	 */
+	public void testGetAllAttributesByType() {
+		Map<AttributeType,Collection<Attribute>> attrsbytype = this.container.getAllAttributesByType();
+		assertNotNull(attrsbytype);
+		assertEquals(5, attrsbytype.size());
+		
+		Collection<Attribute> attrs;
+		
+		attrs = attrsbytype.get(AttributeType.BOOLEAN);
+		assertNotNull(attrs);
+		assertEquals(1, attrs.size());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("A", AttributeType.BOOLEAN) //$NON-NLS-1$
+		), attrs);
+		
+		attrs = attrsbytype.get(AttributeType.COLOR);
+		assertNull(attrs);
+
+		attrs = attrsbytype.get(AttributeType.DATE);
+		assertNull(attrs);
+
+		attrs = attrsbytype.get(AttributeType.IMAGE);
+		assertNull(attrs);
+
+		attrs = attrsbytype.get(AttributeType.INTEGER);
+		assertNotNull(attrs);
+		assertEquals(1, attrs.size());
+		assertEpsilonEquals(Arrays.asList(
+				new AttributeImpl("B", 1) //$NON-NLS-1$
+		), attrs);
+
+		attrs = attrsbytype.get(AttributeType.OBJECT);
+		assertNotNull(attrs);
+		assertEquals(1, attrs.size());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("E", AttributeType.OBJECT) //$NON-NLS-1$
+		), attrs);
+
+		attrs = attrsbytype.get(AttributeType.POINT);
+		assertNull(attrs);
+
+		attrs = attrsbytype.get(AttributeType.POINT3D);
+		assertNull(attrs);
+
+		attrs = attrsbytype.get(AttributeType.POLYLINE);
+		assertNull(attrs);
+
+		attrs = attrsbytype.get(AttributeType.POLYLINE3D);
+		assertNull(attrs);
+
+		attrs = attrsbytype.get(AttributeType.REAL);
+		assertNull(attrs);
+
+		attrs = attrsbytype.get(AttributeType.STRING);
+		assertNotNull(attrs);
+		assertEquals(4, attrs.size());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("D", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z1", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z2", AttributeType.STRING), //$NON-NLS-1$
+				makeUninitialized("Z3", AttributeType.STRING) //$NON-NLS-1$
+		), attrs);
+
+		attrs = attrsbytype.get(AttributeType.TIMESTAMP);
+		assertNull(attrs);
+
+		attrs = attrsbytype.get(AttributeType.URI);
+		assertNull(attrs);
+
+		attrs = attrsbytype.get(AttributeType.URL);
+		assertNotNull(attrs);
+		assertEquals(1, attrs.size());
+		assertEpsilonEquals(Arrays.asList(
+				makeUninitialized("C", AttributeType.URL) //$NON-NLS-1$
+		), attrs);
+
+		attrs = attrsbytype.get(AttributeType.UUID);
+		assertNull(attrs);
+	}
+
+}


Property changes on: trunk/math
___________________________________________________________________
Added: svn:ignore
   + .classpath
..metadata
..settings
..project
target
bin


Added: trunk/math/pom.xml
===================================================================
--- trunk/math/pom.xml	                        (rev 0)
+++ trunk/math/pom.xml	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,26 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+		<artifactId>afc</artifactId>
+		<groupId>org.arakhne.afc</groupId>
+		<version>4.5-SNAPSHOT</version>
+  </parent>
+  
+  <groupId>org.arakhne.afc</groupId>
+  <artifactId>math</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <name>Arakhne Math Tools</name>
+  
+  <dependencies>
+  	<dependency>
+  		<groupId>org.arakhne.afc</groupId>
+  		<artifactId>arakhneVmutils</artifactId>
+  	</dependency>
+  	<dependency>
+  		<groupId>junit</groupId>
+  		<artifactId>junit</artifactId>
+  		<scope>test</scope>
+  	</dependency>
+  </dependencies>
+  
+</project>

Added: trunk/math/src/javadoc/org/arakhne/math/discrete/object2d/doc-files/crossing_circle.png
===================================================================
(Binary files differ)


Property changes on: trunk/math/src/javadoc/org/arakhne/math/discrete/object2d/doc-files/crossing_circle.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: trunk/math/src/javadoc/org/arakhne/math/discrete/object2d/doc-files/crossing_point.png
===================================================================
(Binary files differ)


Property changes on: trunk/math/src/javadoc/org/arakhne/math/discrete/object2d/doc-files/crossing_point.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: trunk/math/src/javadoc/org/arakhne/math/discrete/object2d/doc-files/crossing_rect.png
===================================================================
(Binary files differ)


Property changes on: trunk/math/src/javadoc/org/arakhne/math/discrete/object2d/doc-files/crossing_rect.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: trunk/math/src/javadoc/org/arakhne/math/discrete/object2d/doc-files/crossing_segment.png
===================================================================
(Binary files differ)


Property changes on: trunk/math/src/javadoc/org/arakhne/math/discrete/object2d/doc-files/crossing_segment.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: trunk/math/src/javadoc/org/arakhne/math/generic/doc-files/fillrule-evenodd.png
===================================================================
(Binary files differ)


Property changes on: trunk/math/src/javadoc/org/arakhne/math/generic/doc-files/fillrule-evenodd.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: trunk/math/src/javadoc/org/arakhne/math/generic/doc-files/fillrule-nonzero.png
===================================================================
(Binary files differ)


Property changes on: trunk/math/src/javadoc/org/arakhne/math/generic/doc-files/fillrule-nonzero.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: trunk/math/src/main/java/org/arakhne/afc/math/MathConstants.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/MathConstants.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/MathConstants.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,145 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math;
+
+/** Several mathematical constants.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface MathConstants {
+
+	/** Inside zone according to the Cohen-Sutherland algorithm.
+	 */
+	public static final int COHEN_SUTHERLAND_INSIDE = 0; // 0000
+	/** Left zone according to the Cohen-Sutherland algorithm.
+	 */
+	public static final int COHEN_SUTHERLAND_LEFT = 1;   // 0001
+	/** Right zone according to the Cohen-Sutherland algorithm.
+	 */
+	public static final int COHEN_SUTHERLAND_RIGHT = 2;  // 0010
+	/** Bottom zone according to the Cohen-Sutherland algorithm.
+	 */
+	public static final int COHEN_SUTHERLAND_BOTTOM = 4; // 0100
+	/** Top zone according to the Cohen-Sutherland algorithm.
+	 */
+	public static final int COHEN_SUTHERLAND_TOP = 8;    // 1000
+	
+	/** PI
+	 */
+	public static final float PI = (float)Math.PI;
+
+	/** E
+	 */
+	public static final float E = (float)Math.E;
+
+	/** Epsilon value, smallest such that 1.0+EPSILON != 1.0
+	 * <p>
+	 * Given by the Java3D's implementation of the Matrix3d class.
+	 */
+	public static final double EPSILON = 1.110223024E-16;
+
+	/** 2 * PI
+	 */
+	public static final float TWO_PI = 2f * PI;
+
+	/** PI + PI/2
+	 */
+	public static final float ONE_HALF_PI = 1.5f * PI;
+
+	/** PI/2
+	 */
+	public static final float DEMI_PI = .5f * PI;
+
+	/** PI/4
+	 */
+	public static final float QUARTER_PI = .25f * PI;
+
+	/** 3*PI/4
+	 */
+	public static final float THREE_QUARTER_PI = .75f * PI;
+
+	/** Square root of 2
+	 */
+	public static final float SQRT_2 = (float)Math.sqrt(2.);
+
+	/**
+	 * Max sweeps in the Jacoby's algorithms.
+	 */
+	public static final int JACOBI_MAX_SWEEPS = 32;
+	
+	/**
+	 * Max sweeps in the Ellipse's algorithms.
+	 */
+	public static final int ELLIPSE_MAX_SWEEPS = 32;
+
+	/** This is the maximale distance that
+	 *  permits to detect hits.
+	 */
+	public static final float HIT_DISTANCE = 5f ;
+
+	/** The maximum distance that the line 
+	 *  segments used to approximate the 
+	 *  curved segments are allowed to 
+	 *  deviate from any point on the 
+	 *  original curve.
+	 *  <p>
+	 *  This attributes is used to parameter the approximation
+	 *  of the curve rendering.
+	 */
+	public static final float SPLINE_APPROXIMATION_RATIO = .1f;
+	
+	/**
+     * The rectangle intersection test counts the number of times
+     * that the path crosses through the shadow that the rectangle
+     * projects to the right towards (x => +INFINITY).
+     *
+     * During processing of the path it actually counts every time
+     * the path crosses either or both of the top and bottom edges
+     * of that shadow.  If the path enters from the top, the count
+     * is incremented.  If it then exits back through the top, the
+     * same way it came in, the count is decremented and there is
+     * no impact on the winding count.  If, instead, the path exits
+     * out the bottom, then the count is incremented again and a
+     * full pass through the shadow is indicated by the winding count
+     * having been incremented by 2.
+     *
+     * Thus, the winding count that it accumulates is actually double
+     * the real winding count.  Since the path is continuous, the
+     * final answer should be a multiple of 2, otherwise there is a
+     * logic error somewhere.
+     *
+     * If the path ever has a direct hit on the rectangle, then a
+     * special value is returned.  This special value terminates
+     * all ongoing accumulation on up through the call chain and
+     * ends up getting returned to the calling function which can
+     * then produce an answer directly.  For intersection tests,
+     * the answer is always "true" if the path intersects the
+     * rectangle.  For containment tests, the answer is always
+     * "false" if the path intersects the rectangle.  Thus, no
+     * further processing is ever needed if an intersection occurs.
+     */
+    public static final int SHAPE_INTERSECTS = 0x80000000;
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/MathUtil.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/MathUtil.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/MathUtil.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,1678 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2011 Janus Core Developers
+ * Copyright (C) 2012 Stéphane GALLAND
+ * 
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+package org.arakhne.afc.math;
+
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Vector2D;
+
+/** Mathematic and geometric utilities.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class MathUtil implements MathConstants {
+
+	/** Clamp the given angle in radians to {@code [0;2PI)}.
+	 * 
+	 * @param radians is the angle to clamp
+	 * @return the angle in {@code [0;2PI)} range.
+	 */
+	public static float clampRadian(float radians) {
+		float r = radians;
+		while (r<0f) r += TWO_PI;
+		while (r>=TWO_PI) r -= TWO_PI;
+		return r;
+	}
+
+	/** Clamp the given value to the given range.
+	 * <p>
+	 * If the value is outside the {@code [min;max]}
+	 * range, it is clamp to the nearest bounding value
+	 * <var>min</var> or <var>max</var>.
+	 * 
+	 * @param v is the value to clamp.
+	 * @param min is the min value of the range.
+	 * @param max is the max value of the range.
+	 * @return the value in {@code [min;max]} range.
+	 */
+	public static float clamp(float v, float min, float max) {
+		if (min<max) {
+			if (v<min) return min;
+			if (v>max) return max;
+		}
+		else {
+			if (v>min) return min;
+			if (v<max) return max;
+		}
+		return v;
+	}	
+
+	/** Replies if the given value is near zero.
+	 * 
+	 * @param value is the value to test.
+	 * @return <code>true</code> if the given <var>value</var>
+	 * is near zero, otherwise <code>false</code>.
+	 */
+	public static boolean isEpsilonZero(float value) {
+		return Math.abs(value) <= EPSILON;
+	}
+
+	/** Replies if the given values are near.
+	 * 
+	 * @param v1
+	 * @param v2
+	 * @return <code>true</code> if the given <var>v1</var>
+	 * is near <var>v2</var>, otherwise <code>false</code>.
+	 */
+	public static boolean isEpsilonEqual(float v1, float v2) {
+		return Math.abs(v1 - v2) <= EPSILON;
+	}
+
+	/** Replies the max value.
+	 * 
+	 * @param values are the values to scan.
+	 * @return the max value.
+	 */
+	public static float max(float... values) {
+		if (values==null || values.length==0) return Float.NaN;
+		float m = values[0];
+		for(float v : values) {
+			if (v>m) m = v;
+		}
+		return m;
+	}
+
+	/** Replies the max value.
+	 * 
+	 * @param values are the values to scan.
+	 * @return the max value.
+	 */
+	public static int max(int... values) {
+		if (values==null || values.length==0) return 0;
+		int m = values[0];
+		for(int v : values) {
+			if (v>m) m = v;
+		}
+		return m;
+	}
+
+	/** Replies the min value.
+	 * 
+	 * @param values are the values to scan.
+	 * @return the min value.
+	 */
+	public static float min(float... values) {
+		if (values==null || values.length==0) return Float.NaN;
+		float m = values[0];
+		for(float v : values) {
+			if (v<m) m = v;
+		}
+		return m;
+	}
+
+	/** Replies the min value.
+	 * 
+	 * @param values are the values to scan.
+	 * @return the min value.
+	 */
+	public static int min(int... values) {
+		if (values==null || values.length==0) return 0;
+		int m = values[0];
+		for(int v : values) {
+			if (v<m) m = v;
+		}
+		return m;
+	}
+
+	/** Compute the distance between a point and a segment.
+	 *
+	 * @param px horizontal position of the point.
+	 * @param py vertical position of the point.
+	 * @param x1 horizontal position of the first point of the segment.
+	 * @param y1 vertical position of the first point of the segment.
+	 * @param x2 horizontal position of the second point of the segment.
+	 * @param y2 vertical position of the second point of the segment.
+	 * @return the distance beetween the point and the segment.
+	 */
+	public static final float distancePointToSegment(float px, float py, float x1, float y1, float x2, float y2) {
+		return distancePointToSegment(px, py, x1, y1, x2, y2, null);
+	}
+
+	/** Compute the square distance between a point and a segment.
+	 *
+	 * @param px horizontal position of the point.
+	 * @param py vertical position of the point.
+	 * @param x1 horizontal position of the first point of the segment.
+	 * @param y1 vertical position of the first point of the segment.
+	 * @param x2 horizontal position of the second point of the segment.
+	 * @param y2 vertical position of the second point of the segment.
+	 * @return the distance beetween the point and the segment.
+	 */
+	public static final float distanceSquaredPointToSegment(float px, float py, float x1, float y1, float x2, float y2) {
+		return distanceSquaredPointToSegment(px, py, x1, y1, x2, y2, null);
+	}
+
+	/** Compute the distance between a point and a segment.
+	 *
+	 * @param px horizontal position of the point.
+	 * @param py vertical position of the point.
+	 * @param x1 horizontal position of the first point of the segment.
+	 * @param y1 vertical position of the first point of the segment.
+	 * @param x2 horizontal position of the second point of the segment.
+	 * @param y2 vertical position of the second point of the segment.
+	 * @param pts is the point that will be set with the coordinates of the nearest point,
+	 * if not <code>null</code>.
+	 * @return the distance beetween the point and the segment.
+	 */
+	public static final float distancePointToSegment(float px, float py, float x1, float y1, float x2, float y2, Point2D pts) {
+		float r_denomenator = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
+		if (r_denomenator==0.) return distancePointToPoint(px, py, x1, y1);
+		float r_numerator = (px-x1)*(x2-x1) + (py-y1)*(y2-y1);
+		float ratio = r_numerator / r_denomenator;
+
+		if (ratio<=0.) {
+			if (pts!=null) pts.set(x1, y1);
+			return (float)Math.sqrt((px-x1)*(px-x1) + (py-y1)*(py-y1));
+		}
+
+		if (ratio>=1.) {
+			if (pts!=null) pts.set(x2, y2);
+			return (float)Math.sqrt((px-x2)*(px-x2) + (py-y2)*(py-y2));
+		}
+
+		if (pts!=null) pts.set(
+				ratio * (x2-x1),
+				ratio * (y2-y1));
+
+		float s =  ((y1-py)*(x2-x1)-(x1-px)*(y2-y1) ) / r_denomenator;
+		return (float)(Math.abs(s) * Math.sqrt(r_denomenator));
+	}
+
+	/** Compute the square distance between a point and a segment.
+	 *
+	 * @param px horizontal position of the point.
+	 * @param py vertical position of the point.
+	 * @param x1 horizontal position of the first point of the segment.
+	 * @param y1 vertical position of the first point of the segment.
+	 * @param x2 horizontal position of the second point of the segment.
+	 * @param y2 vertical position of the second point of the segment.
+	 * @param pts is the point that will be set with the coordinates of the nearest point,
+	 * if not <code>null</code>.
+	 * @return the distance beetween the point and the segment.
+	 */
+	public static final float distanceSquaredPointToSegment(float px, float py, float x1, float y1, float x2, float y2, Point2D pts) {
+		float r_denomenator = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
+		if (r_denomenator==0f) return distanceSquaredPointToPoint(px, py, x1, y1);
+		float r_numerator = (px-x1)*(x2-x1) + (py-y1)*(y2-y1);
+		float ratio = r_numerator / r_denomenator;
+
+		if (ratio<=0f) {
+			if (pts!=null) pts.set(x1, y1);
+			return Math.abs((px-x1)*(px-x1) + (py-y1)*(py-y1));
+		}
+
+		if (ratio>=1f) {
+			if (pts!=null) pts.set(x2, y2);
+			return Math.abs((px-x2)*(px-x2) + (py-y2)*(py-y2));
+		}
+
+		if (pts!=null) pts.set(
+				ratio * (x2-x1),
+				ratio * (y2-y1));
+
+		float s =  ((y1-py)*(x2-x1)-(x1-px)*(y2-y1) ) / r_denomenator;
+		return (s * s) * Math.abs(r_denomenator);
+	}
+
+	/** Compute the distance between a point and a line.
+	 *
+	 * @param px horizontal position of the point.
+	 * @param py vertical position of the point.
+	 * @param x1 horizontal position of the first point of the line.
+	 * @param y1 vertical position of the first point of the line.
+	 * @param x2 horizontal position of the second point of the line.
+	 * @param y2 vertical position of the second point of the line.
+	 * @return the distance beetween the point and the line.
+	 * @see #distanceSquaredPointToLine(float, float, float, float, float, float)
+	 * @see #relativeDistancePointToLine(float, float, float, float, float, float)
+	 */
+	public static final float distancePointToLine(float px, float py, float x1, float y1, float x2, float y2) {
+		float r_denomenator = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
+		if (r_denomenator==0.) return distancePointToPoint(px, py, x1, y1);
+		float s = ((y1-py)*(x2-x1)-(x1-px)*(y2-y1) ) / r_denomenator;
+		return (float)(Math.abs(s) * Math.sqrt(r_denomenator));
+	}
+
+	/** Compute the distance between a point and a line.
+	 *
+	 * @param px horizontal position of the point.
+	 * @param py vertical position of the point.
+	 * @param x1 horizontal position of the first point of the line.
+	 * @param y1 vertical position of the first point of the line.
+	 * @param x2 horizontal position of the second point of the line.
+	 * @param y2 vertical position of the second point of the line.
+	 * @return the distance beetween the point and the line.
+	 * @see #distancePointToLine(float, float, float, float, float, float)
+	 */
+	public static final float distanceSquaredPointToLine(float px, float py, float x1, float y1, float x2, float y2) {
+		float r_denomenator = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
+		if (r_denomenator==0.) return distanceSquaredPointToPoint(px, py, x1, y1);
+		float s = ((y1-py)*(x2-x1)-(x1-px)*(y2-y1) ) / r_denomenator;
+		return (s * s) * Math.abs(r_denomenator);
+	}
+
+	/** Compute the distance between 2 points.
+	 *
+	 * @param x1 horizontal position of the first point.
+	 * @param y1 vertical position of the first point.
+	 * @param x2 horizontal position of the second point.
+	 * @param y2 vertical position of the second point.
+	 * @return the distance between given points.
+	 * @see #distanceSquaredPointToPoint(float, float, float, float)
+	 */
+	public static final float distancePointToPoint(float x1, float y1, float x2, float y2) {
+		float dx, dy;
+		dx = x1 - x2;
+		dy = y1 - y2;
+		return (float)Math.sqrt(dx*dx+dy*dy);
+	}
+
+	/** Compute the squared distance between 2 points.
+	 *
+	 * @param x1 horizontal position of the first point.
+	 * @param y1 vertical position of the first point.
+	 * @param x2 horizontal position of the second point.
+	 * @param y2 vertical position of the second point.
+	 * @return the squared distance between given points.
+	 * @see #distancePointToPoint(float, float, float, float)
+	 */
+	public static final float distanceSquaredPointToPoint(float x1, float y1, float x2, float y2) {
+		float dx, dy;
+		dx = x1 - x2;
+		dy = y1 - y2;
+		return dx*dx+dy*dy;
+	}
+
+	/**
+	 * Replies the specified angle translated between 0 and 2PI.
+	 * 
+	 * @param radian
+	 *            is an angle
+	 * @return normalized angle
+	 */
+	public static float clampRadian0To2PI(float radian) {
+		if ((!Float.isNaN(radian)) && (!Float.isInfinite(radian))) {
+			return clampTrigo(radian, 0, 2f*PI);
+		}
+		return radian;
+	}
+
+	/**
+	 * Replies the specified angle translated between -PI and PI.
+	 * 
+	 * @param radian
+	 *            is an angle
+	 * @return normalized angle
+	 */
+	public static float clampRadianMinusPIToPI(float radian) {
+		if ((!Float.isNaN(radian)) && (!Float.isInfinite(radian))) {
+			return clampTrigo(radian, -PI, PI);
+		}
+		return radian;
+	}
+
+	/**
+	 * Replies the specified angle translated between 0 and 360.
+	 * 
+	 * @param degree
+	 *            is an angle
+	 * @return normalized angle
+	 */
+	public static float clampDegree0To360(float degree) {
+		if ((!Float.isNaN(degree)) && (!Float.isInfinite(degree))) {
+			return clampTrigo(degree, 0, 360);
+		}
+		return degree;
+	}
+
+	/**
+	 * Replies the specified angle translated between -180 and 180.
+	 * 
+	 * @param degree
+	 *            is an angle
+	 * @return normalized angle
+	 */
+	public static float clampDegreeMinus180To180(float degree) {
+		if ((!Float.isNaN(degree)) && (!Float.isInfinite(degree))) {
+			return clampTrigo(degree, -180, 180);
+		}
+		return degree;
+	}
+
+	/** Clamp the given value to fit between the min and max values
+	 * according to a trigonometric-like heuristic.
+	 * If the given value is not between the minimum and maximum
+	 * values and the value is positive, the value is modulo the perimeter
+	 * of the counterclockwise circle.
+	 * If the given value is not between the minimum and maximum
+	 * values and the value is negative, the value is modulo the perimeter
+	 * of the clockwise circle.
+	 * The perimeter is the distance between <var>min</var> and <var>max</var>.
+	 * 
+	 * @param value
+	 * @param min
+	 * @param max
+	 * @return the clamped value
+	 */
+	public static float clampTrigo(float value, float min, float max) {
+		if (Float.isNaN(max)) { // NaN is lower than all the number according to float.compareTo()
+			return Float.NaN;
+		}
+		if (Float.isNaN(min)) {
+			// Clamp max only
+			if (value>max) return max - Float.MAX_VALUE + value;
+		}
+		else {
+			assert(min<=max);
+			if (min==max) return min; // special case: empty interval
+			if (value<min || value >max) {
+				float perimeter = max - min;
+				float nvalue = value - min;
+				float rest = nvalue % perimeter;
+				return (value<0) ? max+rest : rest+min;
+			}
+		}
+		return value;
+	}
+
+	/**
+	 * Compute the signed angle between two vectors.
+	 * 
+	 * @param x1
+	 * @param y1
+	 * @param x2
+	 * @param y2
+	 * @return the angle between <code>-PI</code> and <code>PI</code>.
+	 */
+	public static float signedAngle(float x1, float y1, float x2, float y2) {
+		float length1 = (float)Math.sqrt(x1 * x1 + y1 * y1);
+		float length2 = (float)Math.sqrt(x2 * x2 + y2 * y2);
+
+		if ((length1 == 0f) || (length2 == 0f))
+			return Float.NaN;
+
+		float cx1 = x1;
+		float cy1 = y1;
+		float cx2 = x2;
+		float cy2 = y2;
+
+		// A and B are normalized
+		if (length1 != 1) {
+			cx1 /= length1;
+			cy1 /= length1;
+		}
+
+		if (length2 != 1) {
+			cx2 /= length2;
+			cy2 /= length2;
+		}
+
+		/*
+		 * // First method // Angle // A . B = |A|.|B|.cos(theta) = cos(theta) float dot = x1 * x2 + y1 * y2; float angle = Math.acos(dot);
+		 * 
+		 * // On which side of A, B is located? if ((dot > -1) && (dot < 1)) { dot = MathUtil.determinant(x2, y2, x1, y1); if (dot < 0) angle = -angle; }
+		 */
+
+		// Second method
+		// A . B = |A|.|B|.cos(theta) = cos(theta)
+		float cos = cx1 * cx2 + cy1 * cy2;
+		// A x B = |A|.|B|.sin(theta).N = sin(theta) (where N is the unit vector perpendicular to plane AB)
+		float sin = cx1 * cy2 - cy1 * cx2;
+
+		float angle = (float)Math.atan2(sin, cos);
+
+		return angle;
+	}
+
+	/** Return the trigonometric angle of a vector.
+	 *  The vector is from the first point to the
+	 *  second point.
+	 *  <p>
+	 *  The trigonometric angle is the signed angle between
+	 *  the vectors (1;0) and (p2.x-p1.x;p2.y-p1.y).
+	 *
+	 * @param p1 first point.
+	 * @param p2 second point.
+	 * @return the trigonometric angle in radians in [-PI;PI].
+	 */
+	public static final float angleOfVector( Point2D p1, Point2D p2 ) {
+		return signedAngle(
+				1, 0, 
+				p2.getX() - p1.getX(),
+				p2.getY() - p1.getY());
+	}
+
+	/** Return the trigonometric angle of a vector.
+	 *  The vector is from the first point to the
+	 *  second point.
+	 *  <p>
+	 *  The trigonometric angle is the signed angle between
+	 *  the vectors (1;0) and (x2-x1;y2-y1).
+	 *
+	 * @param x1 is the coordinate of the vector origin point.
+	 * @param y1 is the coordinate of the vector origin point.
+	 * @param x2 is the coordinate of the vector target point.
+	 * @param y2 is the coordinate of the vector target point.
+	 * @return the trigonometric angle in radians in [-PI;PI].
+	 */
+	public static final float angleOfVector(float x1, float y1, float x2, float y2) {
+		return signedAngle(
+				1, 0, 
+				x2-x1, y2-y1);
+	}
+
+	/** Return the trigonometric angle of a vector.
+	 *  The vector is from the first point to the
+	 *  second point.
+	 *  <p>
+	 *  The trigonometric angle is the signed angle between
+	 *  the vectors (1;0) and (x;y).
+	 *
+	 * @param x is the coordinate of the vector.
+	 * @param y is the coordinate of the vector.
+	 * @return the trigonometric angle in radians in [-PI;PI].
+	 */
+	public static final float angleOfVector(float x, float y) {
+		return signedAngle(
+				1, 0, 
+				x, y);
+	}
+
+	/** Replies if a point is closed to a segment.
+	 *
+	 * @param x1 horizontal location of the first-segment begining.
+	 * @param y1 vertical location of the first-segment ending.
+	 * @param x2 horizontal location of the second-segment begining.
+	 * @param y2 vertical location of the second-segment ending.
+	 * @param x horizontal location of the point.
+	 * @param y vertical location of the point.
+	 * @param hitDistance is the maximal hitting distance.
+	 * @return <code>true</code> if the point and the
+	 *         line have closed locations.
+	 */
+	public static boolean isPointClosedToSegment( float x1, float y1, 
+			float x2, float y2, 
+			float x, float y, float hitDistance ) {
+		return ( distancePointToSegment(x, y, x1, y1, x2, y2) < hitDistance ) ;
+	}
+
+	/** Replies if a point is closed to a line.
+	 *
+	 * @param x1 horizontal location of the first-line begining.
+	 * @param y1 vertical location of the first-line ending.
+	 * @param x2 horizontal location of the second-line begining.
+	 * @param y2 vertical location of the second-line ending.
+	 * @param x horizontal location of the point.
+	 * @param y vertical location of the point.
+	 * @param hitDistance is the maximal hitting distance.
+	 * @return <code>true</code> if the point and the
+	 *         line have closed locations.
+	 */
+	public static boolean isPointClosedToLine( float x1, float y1, 
+			float x2, float y2, 
+			float x, float y, float hitDistance ) {
+		return ( distancePointToLine(x, y, x1, y1, x2, y2) < hitDistance ) ;
+	}
+
+	/** Replies the metrics from inches.
+	 *
+	 * @param i the inch value
+	 * @return a value in centimeters
+	 */
+	public static float inchToMetric( float i ) {
+		return i / 0.3937f ;
+	}
+
+	/** Replies the inches from metrics.
+	 *
+	 * @param m the metric value
+	 * @return a value in inches
+	 */
+	public static float metricToInch( float m ) {
+		return m * 0.3937f ;
+	}
+
+	/** Replies the <var>value</var> clamped in
+	 * the specified interval assuming the
+	 * it is a angle in radians.
+	 *
+	 * @param value is the value to clamp.
+	 * @param min is the minimal allowed value.
+	 * @param max is the maximal allowed value.
+	 * @return the clamped value.
+	 */
+	public static float clampAngle( float value, float min, float max ) {
+		assert(min<=max);
+		float v = value;
+		while (v>max) {
+			v -= 2*Math.PI;
+		}
+		if (v<min) return min;
+		return v;
+	}
+
+	/** Replies the <var>value</var> clamped to
+	 * the nearest bounds.
+	 * If |<var>value</var>-<var>minBounds</var>| &gt;
+	 * |<var>value</var>-<var>maxBounds</var>| then the
+	 * returned value is <var>maxBounds</var>; otherwise
+	 * it is <var>minBounds</var>.
+	 *
+	 * @param value is the value to clamp.
+	 * @param minBounds is the minimal allowed value.
+	 * @param maxBounds is the maximal allowed value.
+	 * @return <var>minBounds</var> or <var>maxBounds</var>.
+	 */
+	public static float clampToNearestBounds( float value, float minBounds, float maxBounds ) {
+		assert(minBounds<=maxBounds);
+		float center = (minBounds+maxBounds) / 2f;
+		if (value<=center) return minBounds;
+		return maxBounds;
+	}
+
+	/**
+	 * Replies one position factor for the intersection point between two lines.
+	 * <p>
+	 * Let line equations for L1 and L2:<br>
+	 * <code>L1: P1 + factor1 * (P2-P1)</code><br>
+	 * <code>L2: P3 + factor2 * (P4-P3)</code><br>
+	 * If lines are intersecting, then<br>
+	 * <code>P1 + factor1 * (P2-P1) = P3 + factor2 * (P4-P3)</code>
+	 * <p>
+	 * This function computes and replies <code>factor1</code>.
+	 * 
+	 * @param x1
+	 *            is the X coordinate of the first point of the first segment.
+	 * @param y1
+	 *            is the Y coordinate of the first point of the first segment.
+	 * @param x2
+	 *            is the X coordinate of the second point of the first segment.
+	 * @param y2
+	 *            is the Y coordinate of the second point of the first segment.
+	 * @param x3
+	 *            is the X coordinate of the first point of the second segment.
+	 * @param y3
+	 *            is the Y coordinate of the first point of the second segment.
+	 * @param x4
+	 *            is the X coordinate of the second point of the second segment.
+	 * @param y4
+	 *            is the Y coordinate of the second point of the second segment.
+	 * @return <code>factor1</code> or {@link Float#NaN} if no intersection.
+	 * @since 3.0
+	 */
+	public static float getSegmentSegmentIntersectionFactor(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
+		float X1 = x2 - x1;
+		float Y1 = y2 - y1;
+		float X2 = x4 - x3;
+		float Y2 = y4 - y3;
+
+		// determinant is zero when parallel = det(L1,L2)
+		float det = determinant(X1, Y1, X2, Y2);
+		if (det == 0.)
+			return Float.NaN;
+
+		// Given line equations:
+		// Pa = P1 + ua (P2-P1), and
+		// Pb = P3 + ub (P4-P3)
+		// and
+		// V = (P1-P3)
+		// then
+		// ua = det(L2,V) / det(L1,L2)
+		// ub = det(L1,V) / det(L1,L2)
+		float u = determinant(X1, Y1, x1 - x3, y1 - y3) / det;
+		if (u < 0. || u > 1.)
+			return Float.NaN;
+		u = determinant(X2, Y2, x1 - x3, y1 - y3) / det;
+		return (u < 0. || u > 1.) ? Float.NaN : u;
+	}
+
+	/** Compute the determinant of two vectors.
+	 * <p>
+	 * <pre><code>det(X1,X2) = |X1|.|X2|.sin(a)</code></pre>
+	 * where <code>X1</code> and <code>X2</code> are two vectors
+	 * and <code>a</code> is the angle between <code>X1</code>
+	 * and <code>X2</code>. 
+	 * 
+	 * @param x1
+	 * @param y1
+	 * @param x2
+	 * @param y2
+	 * @return the determinant
+	 */
+	public static float determinant(float x1, float y1, float x2, float y2) {
+		return x1*y2 - x2*y1;
+	}
+
+	/**
+	 * Replies the intersection point between two segments.
+	 * 
+	 * @param x1
+	 *            is the X coordinate of the first point of the first segment.
+	 * @param y1
+	 *            is the Y coordinate of the first point of the first segment.
+	 * @param x2
+	 *            is the X coordinate of the second point of the first segment.
+	 * @param y2
+	 *            is the Y coordinate of the second point of the first segment.
+	 * @param x3
+	 *            is the X coordinate of the first point of the second segment.
+	 * @param y3
+	 *            is the Y coordinate of the first point of the second segment.
+	 * @param x4
+	 *            is the X coordinate of the second point of the second segment.
+	 * @param y4
+	 *            is the Y coordinate of the second point of the second segment.
+	 * @return the intersection point or <code>null</code> if none.
+	 */
+	public static Point2D getSegmentSegmentIntersectionPoint(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
+		float m = getSegmentSegmentIntersectionFactor(x1, y1, x2, y2, x3, y3, x4, y4);
+		if (Float.isNaN(m))
+			return null;
+		return new Point2f(x1 + m * (x2 - x1), y1 + m * (y2 - y1));
+	}
+
+	/** Compute the interpolate point between the two points.
+	 * 
+	 * @param p1
+	 * @param p2
+	 * @param factor is between 0 and 1; 0 for p1, and 1 for p2.
+	 * @return the interpolate point.
+	 */
+	public static Point2D interpolate(Point2D p1, Point2D p2, float factor) {
+		return interpolate(p1.getX(), p1.getY(), p2.getX(), p2.getY(), factor);
+	}
+
+	/** Compute the interpolate point between the two points.
+	 * 
+	 * @param p1x
+	 * @param p1y
+	 * @param p2x
+	 * @param p2y
+	 * @param factor is between 0 and 1; 0 for p1, and 1 for p2.
+	 * @return the interpolate point.
+	 */
+	public static Point2D interpolate(float p1x, float p1y, float p2x, float p2y, float factor) {
+		float f = (factor<0f) ? 0f : factor;
+		if (f>1f) f = 1f;
+		float vx = p2x - p1x;
+		float vy = p2y - p1y;
+		return new Point2f(
+				p1x + factor * vx,
+				p1y + factor * vy);
+	}
+
+	/** Compute the dot product of two vectors.
+	 * 
+	 * @param x1
+	 * @param y1
+	 * @param x2
+	 * @param y2
+	 * @return the dot product.
+	 */
+	public static float dotProduct(float x1, float y1, float x2, float y2) {
+		return x1*x2 + y1*y2;
+	}
+
+	/**
+	 * Replies the relative counterclockwise (CCW) of a segment against a point. Returns an indicator of where the specified point {@code (px,py)} lies with respect to the line segment from {@code (x1,y1)} to {@code (x2,y2)}. The return value can be either 1, -1, or 0 and indicates in which direction the specified line must pivot around its first end point, {@code (x1,y1)}, in order to point at the specified point {@code (px,py)}.
+	 * In other words, given three point P1, P2, and P, is the segments (P1-P2-P) a counterclockwise turn?
+	 * <p>
+	 * A return value of 1 indicates that the line segment must turn in the direction that takes the positive X axis towards the negative Y axis. In the default coordinate system used by Java 2D, this direction is counterclockwise.
+	 * <p>
+	 * A return value of -1 indicates that the line segment must turn in the direction that takes the positive X axis towards the positive Y axis. In the default coordinate system, this direction is clockwise.
+	 * <p>
+	 * A return value of 0 indicates that the point lies exactly on the line segment. Note that an indicator value of 0 is rare and not useful for determining colinearity because of floating point rounding issues.
+	 * <p>
+	 * If the point is colinear with the line segment, but not between the end points, then the value will be -1 if the point lies "beyond {@code (x1,y1)}" or 1 if the point lies "beyond {@code (x2,y2)}".
+	 * 
+	 * @param x1
+	 *            the X coordinate of the start point of the specified line segment
+	 * @param y1
+	 *            the Y coordinate of the start point of the specified line segment
+	 * @param x2
+	 *            the X coordinate of the end point of the specified line segment
+	 * @param y2
+	 *            the Y coordinate of the end point of the specified line segment
+	 * @param px
+	 *            the X coordinate of the specified point to be compared with the specified line segment
+	 * @param py
+	 *            the Y coordinate of the specified point to be compared with the specified line segment
+	 * @param approximateZero
+	 *            indicates if zero may be approximated or not.
+	 * @return an integer that indicates the position of the third specified coordinates with respect to the line segment formed by the first two specified coordinates.
+	 * @see #relativeDistancePointToLine(float, float, float, float, float, float)
+	 */
+	public static int ccw(float x1, float y1, float x2, float y2, float px, float py, boolean approximateZero) {
+		float cx2 = x2 - x1;
+		float cy2 = y2 - y1;
+		float cpx = px - x1;
+		float cpy = py - y1;
+		float ccw = cpx * cy2 - cpy * cx2;
+		if ((approximateZero && isEpsilonZero(ccw)) || (!approximateZero && ccw == 0.)) {
+			// The point is colinear, classify based on which side of
+			// the segment the point falls on. We can calculate a
+			// relative value using the projection of px,py onto the
+			// segment - a negative value indicates the point projects
+			// outside of the segment in the direction of the particular
+			// endpoint used as the origin for the projection.
+			ccw = cpx * cx2 + cpy * cy2;
+			if (ccw > 0.) {
+				// Reverse the projection to be relative to the original x2,y2
+				// x2 and y2 are simply negated.
+				// px and py need to have (x2 - x1) or (y2 - y1) subtracted
+				// from them (based on the original values)
+				// Since we really want to get a positive answer when the
+				// point is "beyond (x2,y2)", then we want to calculate
+				// the inverse anyway - thus we leave x2 & y2 negated.
+				cpx -= cx2;
+				cpy -= cy2;
+				ccw = cpx * cx2 + cpy * cy2;
+				if (ccw < 0f) {
+					ccw = 0f;
+				}
+			}
+		}
+		return (ccw < 0f) ? -1 : ((ccw > 0f) ? 1 : 0);
+	}
+
+	/**
+	 * Replies on which side of a line the given point is located.
+	 * <p>
+	 * A return value of 1 indicates that the line segment must turn in the direction
+	 * that takes the positive X axis towards the negative Y axis. In the default
+	 * coordinate system used by Java 2D, this direction is counterclockwise.
+	 * <p>
+	 * A return value of -1 indicates that the line segment must turn in the direction that takes the positive X axis towards the positive Y axis. In the default coordinate system, this direction is clockwise.
+	 * <p>
+	 * A return value of 0 indicates that the point lies exactly on the line segment. Note that an indicator value of 0 is rare and not useful for determining colinearity because of floating point rounding issues.
+	 * <p>
+	 * This function uses the equal-to-zero test with the error {@link #EPSILON}.
+	 * 
+	 * @param x1
+	 *            the X coordinate of the start point of the specified line segment
+	 * @param y1
+	 *            the Y coordinate of the start point of the specified line segment
+	 * @param x2
+	 *            the X coordinate of the end point of the specified line segment
+	 * @param y2
+	 *            the Y coordinate of the end point of the specified line segment
+	 * @param px
+	 *            the X coordinate of the specified point to be compared with the specified line segment
+	 * @param py
+	 *            the Y coordinate of the specified point to be compared with the specified line segment
+	 * @param approximateZero
+	 *            indicates if zero may be approximated or not.
+	 * @return an integer that indicates the position of the third specified coordinates with respect to the line segment formed by the first two specified coordinates.
+	 * @see #relativeDistancePointToLine(float, float, float, float, float, float)
+	 * @see #isEpsilonZero(float)
+	 */
+	public static int sidePointLine(float x1, float y1, float x2, float y2, float px, float py, boolean approximateZero) {
+		float cx2 = x2 - x1;
+		float cy2 = y2 - y1;
+		float cpx = px - x1;
+		float cpy = py - y1;
+		float side = cpx * cy2 - cpy * cx2;
+		if (approximateZero && side!=0f && isEpsilonZero(side)) {
+			side = 0f;
+		}
+		return (side < 0f) ? -1 : ((side > 0f) ? 1 : 0);
+	}
+
+	/**
+	 * Replies the relative distance from the given point to the given line.
+	 * The replied distance may be negative, depending on which side of 
+	 * the line the point is.
+	 * 
+	 * @param x1
+	 *            the X coordinate of the start point of the specified line segment
+	 * @param y1
+	 *            the Y coordinate of the start point of the specified line segment
+	 * @param x2
+	 *            the X coordinate of the end point of the specified line segment
+	 * @param y2
+	 *            the Y coordinate of the end point of the specified line segment
+	 * @param px
+	 *            the X coordinate of the specified point to be compared with the specified line segment
+	 * @param py
+	 *            the Y coordinate of the specified point to be compared with the specified line segment
+	 * @return the positive or negative distance from the point to the line
+	 * @see #ccw(float, float, float, float, float, float, boolean)
+	 * @see #sidePointLine(float, float, float, float, float, float, boolean)
+	 * @see #distancePointToLine(float, float, float, float, float, float)
+	 */
+	public static float relativeDistancePointToLine(float x1, float y1, float x2, float y2, float px, float py) {
+		float r_denomenator = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
+		if (r_denomenator==0.) return distancePointToPoint(px, py, x1, y1);
+		float s = ((y1-py)*(x2-x1)-(x1-px)*(y2-y1) ) / r_denomenator;
+		return (float)(s * Math.sqrt(r_denomenator));
+	}
+
+	/** Compute the intersection of two lines specified
+	 * by the specified points and vectors.
+	 * 
+	 * @param p1 is a point of the first line.
+	 * @param v1 is the direction of the first line.
+	 * @param p2 is a point of the second line.
+	 * @param v2 is the direction of the second line.
+	 * @return the intersection point or <code>null</code> if there is no intersection.
+	 */
+	public static Point2D computeLineIntersection(Point2D p1, Vector2D v1, Point2D p2, Vector2D v2) {
+		float x1 = p1.getX();
+		float y1 = p1.getY();
+		float x2 = x1 + v1.getX();
+		float y2 = y1 + v1.getY();
+		float x3 = p2.getX();
+		float y3 = p2.getY();
+		float x4 = x3 + v2.getX();
+		float y4 = y3 + v2.getY();
+		float denom = (y4-y3)*(x2-x1) - (x4-x3)*(y2-y1);
+		if (denom==0.) return null;
+		float ua = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3));
+		float ub = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3));
+		if (ua==ub) return null;
+		ua = ua / denom;
+		return new Point2f(
+				x1 + ua * (x2 - x1),
+				y1 + ua * (y2 - y1));
+	}
+
+	/**
+	 * Replies the projection a point on a segment.
+	 * 
+	 * @param px
+	 *            is the coordiante of the point to project
+	 * @param py
+	 *            is the coordiante of the point to project
+	 * @param s1x
+	 *            is the x-coordinate of the first line point.
+	 * @param s1y
+	 *            is the y-coordinate of the first line point.
+	 * @param s2x
+	 *            is the x-coordinate of the second line point.
+	 * @param s2y
+	 *            is the y-coordinate of the second line point.
+	 * @return the projection of the specified point on the line. If 
+	 * equal to {@code 0}, the projection is equal to the first segment point. 
+	 * If equal to {@code 1}, the projection is equal to the second segment point. 
+	 * If inside {@code ]0;1[}, the projection is between the two segment points. 
+	 * If inside {@code ]-inf;0[}, the projection is outside on the side of the 
+	 * first segment point. If inside {@code ]1;+inf[}, the projection is 
+	 * outside on the side of the second segment point.
+	 */
+	public static float projectsPointOnLine(float px, float py, float s1x, float s1y, float s2x, float s2y) {
+		float r_numerator = (px-s1x)*(s2x-s1x) + (py-s1y)*(s2y-s1y);
+		float r_denomenator = (s2x-s1x)*(s2x-s1x) + (s2y-s1y)*(s2y-s1y);
+		return r_numerator / r_denomenator;
+	}
+
+	/**
+	 * Replies the position factory for the intersection point between two lines.
+	 * <p>
+	 * Let line equations for L1 and L2:<br>
+	 * <code>L1: P1 + factor1 * (P2-P1)</code><br>
+	 * <code>L2: P3 + factor2 * (P4-P3)</code><br>
+	 * If lines are intersecting, then<br>
+	 * <code>P1 + factor1 * (P2-P1) = P3 + factor2 * (P4-P3)</code>
+	 * <p>
+	 * This function computes and replies <code>factor1</code>.
+	 * 
+	 * @param x1
+	 *            is the X coordinate of the first point of the first line.
+	 * @param y1
+	 *            is the Y coordinate of the first point of the first line.
+	 * @param x2
+	 *            is the X coordinate of the second point of the first line.
+	 * @param y2
+	 *            is the Y coordinate of the second point of the first line.
+	 * @param x3
+	 *            is the X coordinate of the first point of the second line.
+	 * @param y3
+	 *            is the Y coordinate of the first point of the second line.
+	 * @param x4
+	 *            is the X coordinate of the second point of the second line.
+	 * @param y4
+	 *            is the Y coordinate of the second point of the second line.
+	 * @return <code>factor1</code> or {@link Float#NaN} if no intersection.
+	 */
+	public static float getLineLineIntersectionFactor(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
+		float X1 = x2 - x1;
+		float Y1 = y2 - y1;
+		float X2 = x4 - x3;
+		float Y2 = y4 - y3;
+
+		// determinant is zero when parallel = det(L1,L2)
+		float det = MathUtil.determinant(X1, Y1, X2, Y2);
+		if (det == 0.)
+			return Float.NaN;
+
+		// Given line equations:
+		// Pa = P1 + ua (P2-P1), and
+		// Pb = P3 + ub (P4-P3)
+		// and
+		// V = (P1-P3)
+		// then
+		// ua = det(L2,V) / det(L1,L2)
+		// ub = det(L1,V) / det(L1,L2)
+		return MathUtil.determinant(X2, Y2, x1 - x3, y1 - y3) / det;
+	}
+
+	/**
+	 * Replies if the given point is inside the given ellipse.
+	 * 
+	 * @param px is the point to test.
+	 * @param py is the point to test.
+	 * @param ellx is the min corner of the ellipse.
+	 * @param elly is the min corner of the ellipse.
+	 * @param ellw is the width of the ellipse.
+	 * @param ellh is the height of the ellipse.
+	 * @return <code>true</code> if the point is inside the ellipse;
+	 * <code>false</code> if not.
+	 */
+	public static boolean isPointInEllipse(float px, float py, float ellx, float elly, float ellw, float ellh) {
+		// Copied from AWT Ellipse2D
+
+		// Normalize the coordinates compared to the ellipse
+		// having a center at 0,0 and a radius of 0.5.
+		if (ellw <= 0f || ellh <= 0f) {
+			return false;
+		}
+		float normx = (px - ellx) / ellw - 0.5f;
+		float normy = (py - elly) / ellh - 0.5f;
+		return (normx * normx + normy * normy) < 0.25f;
+	}
+
+	/**
+	 * Replies if the given point is inside the given ellipse.
+	 * 
+	 * @param px is the point to test.
+	 * @param py is the point to test.
+	 * @param cx is the center of the circle.
+	 * @param cy is the center of the circle.
+	 * @param radius is the radius of the circle.
+	 * @return <code>true</code> if the point is inside the circle;
+	 * <code>false</code> if not.
+	 */
+	public static boolean isPointInCircle(float px, float py, float cx, float cy, float radius) {
+		return distanceSquaredPointToPoint(
+				px, py,
+				cx, cy) <= (radius * radius);
+	}
+
+	/** Replies the closest point from the given point in the solid ellipse.
+	 * A solid ellipse is an ellipse with a border and an interior area.
+	 * 
+	 * @param px is the coordinate of the point.
+	 * @param py is the coordinate of the point.
+	 * @param ex is the coordinate of the min corner of the ellipse
+	 * @param ey is the coordinate of the min corner of the ellipse
+	 * @param ew is the width of the ellipse
+	 * @param eh is the height of the ellipse
+	 * @return the closest point in the ellipse
+	 * @see #getClosestPointToSolidEllipse(float, float, float, float, float, float, boolean)
+	 * @see #getClosestPointToShallowEllipse(float, float, float, float, float, float)
+	 */
+	public static Point2D getClosestPointToSolidEllipse(float px, float py, float ex, float ey, float ew, float eh) {
+		return getClosestPointToSolidEllipse(px, py, ex, ey, ew, eh, false);
+	}
+
+	/** Replies the closest point from the given point in the solid ellipse.
+	 * A solid ellipse is an ellipse with a border and an interior area.
+	 * 
+	 * @param px is the coordinate of the point.
+	 * @param py is the coordinate of the point.
+	 * @param ex is the coordinate of the min corner of the ellipse
+	 * @param ey is the coordinate of the min corner of the ellipse
+	 * @param ew is the width of the ellipse
+	 * @param eh is the height of the ellipse
+	 * @param returnNullWhenInside indicates if a <code>null</code> value
+	 * may be returned when the point is inside the ellipse, if
+	 * <code>true</code>; or a point all the time if <code>false</code>.
+	 * @return the closest point in the ellipse
+	 * @see #getClosestPointToShallowEllipse(float, float, float, float, float, float)
+	 */
+	public static Point2D getClosestPointToSolidEllipse(float px, float py, float ex, float ey, float ew, float eh, boolean returnNullWhenInside) {
+		float x, y;
+		if (ew<=0f || eh<=0f) {
+			x = ex;
+			y = ey;
+		}
+		else {
+			// Normalize the coordinates compared to the ellipse
+			// having a center at 0,0 and a radius of 0.5.
+			float normx = (px - ex) / ew - 0.5f;
+			float normy = (py - ey) / eh - 0.5f;
+			if ((normx * normx + normy * normy) < 0.25f) {
+				// The point is inside the ellipse
+				if (returnNullWhenInside) return null;
+				x = px;
+				y = py;
+			}
+			else {
+				// Compute the intersection between the ellipse
+				// centered on (0;0) and the line (0;0)-(x0;y0)
+				float a = ew / 2f;
+				float b = eh / 2f;
+				float x0 = px - (ex + a);
+				float y0 = py - (ey + b);
+
+				float denom = a*a*y0*y0 + b*b*x0*x0;
+				assert(denom>0f); // because the "inside"-test should discard this case.
+
+				denom = (float)Math.sqrt(denom);
+				float factor = (a * b) / denom;
+				x = factor * x0;
+				y = factor * y0;
+
+				// Translate the point to the original coordinate system
+				x += (ex + a);
+				y += (ey + b);
+			}
+		}
+		return new Point2f(x, y);
+	}
+
+	/** Replies the closest point from the given point in the shallow ellipse.
+	 * A shallow ellipse is an ellipse with a border and not an interior area.
+	 * 
+	 * @param px is the coordinate of the point.
+	 * @param py is the coordinate of the point.
+	 * @param ex is the coordinate of the min corner of the ellipse
+	 * @param ey is the coordinate of the min corner of the ellipse
+	 * @param ew is the width of the ellipse
+	 * @param eh is the height of the ellipse
+	 * @return the closest point in the ellipse, or <code>null</code> if 
+	 * the given point is exactly at the center of the ellipse.
+	 */
+	public static Point2D getClosestPointToShallowEllipse(float px, float py, float ex, float ey, float ew, float eh) {
+		float x, y;
+		if (ew<=0f || eh<=0f) {
+			x = ex;
+			y = ey;
+		}
+		else {
+			// Compute the intersection between the ellipse
+			// centered on (0;0) and the line (0;0)-(x0;y0)
+			float a = ew / 2f;
+			float b = eh / 2f;
+			float x0 = px - (ex + a);
+			float y0 = py - (ey + b);
+
+			float denom = a*a*y0*y0 + b*b*x0*x0;
+			if (denom==0f) {
+				// The point is at the center of the ellipse.
+				// Replies allways the same point.
+				return null;
+			}
+
+			denom = (float)Math.sqrt(denom);
+			float factor = (a * b) / denom;
+			x = factor * x0;
+			y = factor * y0;
+
+			// Translate the point to the original coordinate system
+			x += (ex + a);
+			y += (ey + b);
+		}
+		return new Point2f(x, y);
+	}
+
+	/**
+	 * Replies if two lines are parallel.
+	 * <p>
+	 * The given two lines are described respectivaly by two points, i.e. {@code (x1,y1)} and {@code (x2,y2)} for the first line, and {@code (x3,y3)} and {@code (x4,y4)} for the second line.
+	 * <p>
+	 * If you are interested to test if the two lines are colinear, see {@link #isCollinearLines(float, float, float, float, float, float, float, float)}.
+	 * 
+	 * @param x1
+	 *            is the X coordinate of the first point of the first line.
+	 * @param y1
+	 *            is the Y coordinate of the first point of the first line.
+	 * @param x2
+	 *            is the X coordinate of the second point of the first line.
+	 * @param y2
+	 *            is the Y coordinate of the second point of the first line.
+	 * @param x3
+	 *            is the X coordinate of the first point of the second line.
+	 * @param y3
+	 *            is the Y coordinate of the first point of the second line.
+	 * @param x4
+	 *            is the X coordinate of the second point of the second line.
+	 * @param y4
+	 *            is the Y coordinate of the second point of the second line.
+	 * @return <code>true</code> if the two given lines are parallel.
+	 * @see #isCollinearLines(float, float, float, float, float, float, float, float)
+	 * @since 3.0
+	 */
+	public static boolean isParallelLines(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
+		return isCollinearVectors(x2 - x1, y2 - y1, x4 - x3, y4 - y3);
+	}
+
+	/**
+	 * Replies if two lines are parallel.
+	 * <p>
+	 * The given two lines are described respectivaly by two points, i.e. {@code (x1,y1,z1)} and {@code (x2,y2,z2)} for the first line, and {@code (x3,y3,z3)} and {@code (x4,y4,z4)} for the second line.
+	 * <p>
+	 * Line through (x<sub>0</sub>,y<sub>0</sub>,z<sub>0</sub>) in direction (a<sub>0</sub>,b<sub>0</sub>,c<sub>0</sub>) and line through (x<sub>1</sub>,yx<sub>1</sub>,zx<sub>1</sub>) in direction (ax<sub>1</sub>,bx<sub>1</sub>,cx<sub>1</sub>): <center><img src="doc-files/parallellines3d.gif" alt="Parallel lines"></center>
+	 * <p>
+	 * Two lines specified by point and direction are coplanar if and only if the determinant in the numerator is zero. In this case they are concurrent (if the denominator is nonzero) or parallel (if the denominator is zero).
+	 * 
+	 * @param x1
+	 *            is the X coordinate of the first point of the first line.
+	 * @param y1
+	 *            is the Y coordinate of the first point of the first line.
+	 * @param z1
+	 *            is the Z coordinate of the first point of the first line.
+	 * @param x2
+	 *            is the X coordinate of the second point of the first line.
+	 * @param y2
+	 *            is the Y coordinate of the second point of the first line.
+	 * @param z2
+	 *            is the Z coordinate of the second point of the first line.
+	 * @param x3
+	 *            is the X coordinate of the first point of the second line.
+	 * @param y3
+	 *            is the Y coordinate of the first point of the second line.
+	 * @param z3
+	 *            is the Z coordinate of the first point of the second line.
+	 * @param x4
+	 *            is the X coordinate of the second point of the second line.
+	 * @param y4
+	 *            is the Y coordinate of the second point of the second line.
+	 * @param z4
+	 *            is the Z coordinate of the second point of the second line.
+	 * @return <code>true</code> if the two given lines are parallel.
+	 * @since 3.0
+	 */
+	public static boolean isParallelLines(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4) {
+		return isCollinearVectors(x2 - x1, y2 - y1, z2 - z1, x4 - x3, y4 - y3, z4 - z3);
+	}
+
+	/**
+	 * Replies if three points are colinear, ie. one the same line.
+	 * <p>
+	 * Trival approach is: points are collinear iff |AB| + |AC| = |BC|, where A B C are the three points.
+	 * <p>
+	 * This function uses the equal-to-zero test with the error {@link #EPSILON}.
+	 * 
+	 * @param x1
+	 *            is the X coordinate of the first point
+	 * @param y1
+	 *            is the Y coordinate of the first point
+	 * @param x2
+	 *            is the X coordinate of the second point
+	 * @param y2
+	 *            is the Y coordinate of the second point
+	 * @param x3
+	 *            is the X coordinate of the third point
+	 * @param y3
+	 *            is the Y coordinate of the third point
+	 * @return <code>true</code> if the three given points are colinear.
+	 * @since 3.0
+	 * @see #isEpsilonZero(float)
+	 */
+	public static boolean isCollinearPoints(float x1, float y1, float x2, float y2, float x3, float y3) {
+		// Test if three points are colinears
+		// iff det( [ x1 x2 x3 ]
+		// [ y1 y2 y3 ]
+		// [ 1 1 1 ] ) = 0
+		// Do not invoked MathUtil.determinant() to limit computations.
+		return isEpsilonZero(x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2));
+	}
+
+	/**
+	 * Replies if three points are colinear, ie. one the same line.
+	 * <p>
+	 * Trival approach is: points are collinear iff |AB| + |AC| = |BC|, where A B C are the three points.
+	 * <p>
+	 * This function uses the equal-to-zero test with the error {@link #EPSILON}.
+	 * 
+	 * @param x1
+	 *            is the X coordinate of the first point
+	 * @param y1
+	 *            is the Y coordinate of the first point
+	 * @param z1
+	 *            is the Z coordinate of the first point
+	 * @param x2
+	 *            is the X coordinate of the second point
+	 * @param y2
+	 *            is the Y coordinate of the second point
+	 * @param z2
+	 *            is the Z coordinate of the second point
+	 * @param x3
+	 *            is the X coordinate of the third point
+	 * @param y3
+	 *            is the Y coordinate of the third point
+	 * @param z3
+	 *            is the Z coordinate of the third point
+	 * @return <code>true</code> if the three given points are colinear.
+	 * @since 3.0
+	 * @see #isEpsilonZero(float)
+	 */
+	public static boolean isCollinearPoints(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3) {
+		float dx1 = x2 - x1;
+		float dy1 = y2 - y1;
+		float dz1 = z2 - z1;
+		float dx2 = x3 - x1;
+		float dy2 = y3 - y1;
+		float dz2 = z3 - z1;
+
+		float cx = dy1 * dz2 - dy2 * dz1;
+		float cy = dx2 * dz1 - dx1 * dz2;
+		float cz = dx1 * dy2 - dx2 * dy1;
+
+		float sum = cx * cx + cy * cy + cz * cz;
+
+		return isEpsilonZero(sum);
+	}
+
+	/**
+	 * Replies if two vectors are colinear.
+	 * <p>
+	 * This function uses the equal-to-zero test with the error {@link #EPSILON}.
+	 * 
+	 * @param x1
+	 *            is the X coordinate of the first vector
+	 * @param y1
+	 *            is the Y coordinate of the first vector
+	 * @param x2
+	 *            is the X coordinate of the second vector
+	 * @param y2
+	 *            is the Y coordinate of the second vector
+	 * @return <code>true</code> if the two given vectors are colinear.
+	 * @since 3.0
+	 * @see #isEpsilonZero(float)
+	 */
+	public static boolean isCollinearVectors(float x1, float y1, float x2, float y2) {
+		// Test if three points are colinears
+		// iff det( [ x1 x2 x3 ]
+		// [ y1 y2 y3 ]
+		// [ 1 1 1 ] ) = 0
+		// Do not invoked MathUtil.determinant() to limit computation consumption.
+		return isEpsilonZero(x1 * y2 - x2 * y1);
+	}
+
+	/**
+	 * Replies if two vectors are colinear.
+	 * <p>
+	 * This function uses the equal-to-zero test with the error {@link #EPSILON}.
+	 * 
+	 * @param x1
+	 *            is the X coordinate of the first vector
+	 * @param y1
+	 *            is the Y coordinate of the first vector
+	 * @param z1
+	 *            is the Z coordinate of the first vector
+	 * @param x2
+	 *            is the X coordinate of the second vector
+	 * @param y2
+	 *            is the Y coordinate of the second vector
+	 * @param z2
+	 *            is the Z coordinate of the second vector
+	 * @return <code>true</code> if the two given vectors are colinear.
+	 * @since 3.0
+	 * @see #isEpsilonZero(float)
+	 */
+	public static boolean isCollinearVectors(float x1, float y1, float z1, float x2, float y2, float z2) {
+		// Cross product
+		float cx = y1 * z2 - z1 * y2;
+		float cy = z1 * x2 - x1 * z2;
+		float cz = x1 * y2 - y1 * x2;
+
+		float sum = cx * cx + cy * cy + cz * cz;
+
+		return isEpsilonZero(sum);
+	}
+
+	/**
+	 * Replies if two lines are colinear.
+	 * <p>
+	 * The given two lines are described respectivaly by two points, i.e. {@code (x1,y1)} and {@code (x2,y2)} for the first line, and {@code (x3,y3)} and {@code (x4,y4)} for the second line.
+	 * <p>
+	 * If you are interested to test if the two lines are parallel, see {@link #isParallelLines(float, float, float, float, float, float, float, float)}.
+	 * 
+	 * @param x1
+	 *            is the X coordinate of the first point of the first line.
+	 * @param y1
+	 *            is the Y coordinate of the first point of the first line.
+	 * @param x2
+	 *            is the X coordinate of the second point of the first line.
+	 * @param y2
+	 *            is the Y coordinate of the second point of the first line.
+	 * @param x3
+	 *            is the X coordinate of the first point of the second line.
+	 * @param y3
+	 *            is the Y coordinate of the first point of the second line.
+	 * @param x4
+	 *            is the X coordinate of the second point of the second line.
+	 * @param y4
+	 *            is the Y coordinate of the second point of the second line.
+	 * @return <code>true</code> if the two given lines are collinear.
+	 * @see #isParallelLines(float, float, float, float, float, float, float, float)
+	 * @see #isCollinearPoints(float, float, float, float, float, float)
+	 */
+	public static boolean isCollinearLines(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
+		return (isParallelLines(x1, y1, x2, y2, x3, y3, x4, y4) && isCollinearPoints(x1, y1, x2, y2, x3, y3));
+	}
+
+	/**
+	 * Replies if two lines are colinear.
+	 * <p>
+	 * The given two lines are described respectivaly by two points, i.e. {@code (x1,y1,z1)} and {@code (x2,y2,z2)} for the first line, and {@code (x3,y3,z3)} and {@code (x4,y4,z4)} for the second line.
+	 * <p>
+	 * If you are interested to test if the two lines are parallel, see {@link #isParallelLines(float, float, float, float, float, float, float, float, float, float, float, float)}.
+	 * 
+	 * @param x1
+	 *            is the X coordinate of the first point of the first line.
+	 * @param y1
+	 *            is the Y coordinate of the first point of the first line.
+	 * @param z1
+	 *            is the Z coordinate of the first point of the first line.
+	 * @param x2
+	 *            is the X coordinate of the second point of the first line.
+	 * @param y2
+	 *            is the Y coordinate of the second point of the first line.
+	 * @param z2
+	 *            is the Z coordinate of the second point of the first line.
+	 * @param x3
+	 *            is the X coordinate of the first point of the second line.
+	 * @param y3
+	 *            is the Y coordinate of the first point of the second line.
+	 * @param z3
+	 *            is the Z coordinate of the first point of the second line.
+	 * @param x4
+	 *            is the X coordinate of the second point of the second line.
+	 * @param y4
+	 *            is the Y coordinate of the second point of the second line.
+	 * @param z4
+	 *            is the Z coordinate of the second point of the second line.
+	 * @return <code>true</code> if the two given lines are collinear.
+	 * @see #isParallelLines(float, float, float, float, float, float, float, float, float, float, float, float)
+	 * @see #isCollinearPoints(float, float, float, float, float, float, float, float, float)
+	 */
+	public static boolean isCollinearLines(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4) {
+		return (isParallelLines(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) && isCollinearPoints(x1, y1, z1, x2, y2, z2, x3, y3, z3));
+	}
+
+	/** Compute the zone where the point is against the given rectangle
+	 * according to the <a href="http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm";>Cohen-Sutherland algorithm</a>.
+	 * 
+	 * @param px is the coordinates of the points.
+	 * @param py is the coordinates of the points.
+	 * @param rxmin is the min of the coordinates of the rectangle.
+	 * @param rymin is the min of the coordinates of the rectangle.
+	 * @param rxmax is the max of the coordinates of the rectangle.
+	 * @param rymax is the max of the coordinates of the rectangle.
+	 * @return the zone
+	 */
+	public static int getCohenSutherlandCode(int px, int py, int rxmin, int rymin, int rxmax, int rymax) {
+		assert(rxmin<=rxmax);
+		assert(rymin<=rymax);
+		// initialised as being inside of clip window
+		int code = COHEN_SUTHERLAND_INSIDE;
+		if (px<rxmin) {
+			// to the left of clip window
+			code |= COHEN_SUTHERLAND_LEFT;
+		}
+		if (px>rxmax) {
+			// to the right of clip window
+			code |= COHEN_SUTHERLAND_RIGHT;
+		}
+		if (py<rymin) {
+			// to the bottom of clip window
+			code |= COHEN_SUTHERLAND_BOTTOM;
+		}
+		if (py>rymax) {
+			// to the top of clip window
+			code |= COHEN_SUTHERLAND_TOP;
+		}
+		return code;
+	}
+
+	/** Compute the zone where the point is against the given rectangle
+	 * according to the <a href="http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm";>Cohen-Sutherland algorithm</a>.
+	 * 
+	 * @param px is the coordinates of the points.
+	 * @param py is the coordinates of the points.
+	 * @param rxmin is the min of the coordinates of the rectangle.
+	 * @param rymin is the min of the coordinates of the rectangle.
+	 * @param rxmax is the max of the coordinates of the rectangle.
+	 * @param rymax is the max of the coordinates of the rectangle.
+	 * @return the zone
+	 */
+	public static int getCohenSutherlandCode(float px, float py, float rxmin, float rymin, float rxmax, float rymax) {
+		assert(rxmin<=rxmax);
+		assert(rymin<=rymax);
+		// initialised as being inside of clip window
+		int code = COHEN_SUTHERLAND_INSIDE;
+		if (px<rxmin) {
+			// to the left of clip window
+			code |= COHEN_SUTHERLAND_LEFT;
+		}
+		if (px>rxmax) {
+			// to the right of clip window
+			code |= COHEN_SUTHERLAND_RIGHT;
+		}
+		if (py<rymin) {
+			// to the bottom of clip window
+			code |= COHEN_SUTHERLAND_BOTTOM;
+		}
+		if (py>rymax) {
+			// to the top of clip window
+			code |= COHEN_SUTHERLAND_TOP;
+		}
+		return code;
+	}
+
+	/** Clip the given segment against the clipping rectangle
+	 * according to the <a href="http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm";>Cohen-Sutherland algorithm</a>.
+	 * 
+	 * @param p1 is the first point of the segment.
+	 * @param p2 is the first point of the segment.
+	 * @param rxmin is the min of the coordinates of the rectangle.
+	 * @param rymin is the min of the coordinates of the rectangle.
+	 * @param rxmax is the max of the coordinates of the rectangle.
+	 * @param rymax is the max of the coordinates of the rectangle.
+	 * @return <code>true</code> if the segment has an intersection with the
+	 * rectangle and the segment was clipped; <code>false</code> if the segment
+	 * does not intersect the rectangle.
+	 */
+	public static boolean clipSegmentToRectangle(Point2D p1, Point2D p2, float rxmin, float rymin, float rxmax, float rymax) {
+		float x0 = p1.getX();
+		float y0 = p1.getY();
+		float x1 = p2.getX();
+		float y1 = p2.getY();
+		int code1 = getCohenSutherlandCode(x0, y0, rxmin, rymin, rxmax, rymax);
+		int code2 = getCohenSutherlandCode(x1, y1, rxmin, rymin, rxmax, rymax);
+		boolean accept = false;
+		boolean cont = true;
+		float x, y;
+		x = y = 0;
+		
+		while (cont) {
+			if ((code1 | code2)==0) {
+				// Bitwise OR is 0. Trivially accept and get out of loop
+				accept = true;
+				cont = false;
+			}
+			else if ((code1 & code2)!=0) {
+				// Bitwise AND is not 0. Trivially reject and get out of loop
+				cont = false;
+			}
+			else {
+				// failed both tests, so calculate the line segment to clip
+				// from an outside point to an intersection with clip edge
+
+				// At least one endpoint is outside the clip rectangle; pick it.
+				int code3 = code1!=0 ? code1 : code2;
+
+				// Now find the intersection point;
+				// use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
+				if ((code3 & COHEN_SUTHERLAND_TOP)!=0) {
+					// point is above the clip rectangle
+					x = x0 + (x1 - x0) * (rymax - y0) / (y1 - y0);
+					y = rymax;
+				}
+				else if ((code3 & COHEN_SUTHERLAND_BOTTOM)!=0) {
+					// point is below the clip rectangle
+					x = x0 + (x1 - x0) * (rymin - y0) / (y1 - y0);
+					y = rymin;
+				}
+				else if ((code3 & COHEN_SUTHERLAND_RIGHT)!=0) { 
+					// point is to the right of clip rectangle
+					y = y0 + (y1 - y0) * (rxmax - x0) / (x1 - x0);
+					x = rxmax;
+				}
+				else if ((code3 & COHEN_SUTHERLAND_LEFT)!=0) {
+					// point is to the left of clip rectangle
+					y = y0 + (y1 - y0) * (rxmin - x0) / (x1 - x0);
+					x = rxmin;
+				}
+				else {
+					code3 = 0;
+				}
+
+				if (code3!=0) {
+					// Now we move outside point to intersection point to clip
+					// and get ready for next pass.
+					if (code3 == code1) {
+						x0 = x;
+						y0 = y;
+						code1 = getCohenSutherlandCode(x0, y0, rxmin, rymin, rxmax, rymax);
+					}
+					else {
+						x1 = x;
+						y1 = y;
+						code2 = getCohenSutherlandCode(x1, y1, rxmin, rymin, rxmax, rymax);
+					}
+				}
+			}
+		}
+		if (accept) {
+			p1.set(x0, y0);
+			p2.set(x1, y1);
+		}
+		return accept;
+	}
+
+	/** Clip the given segment against the clipping rectangle
+	 * according to the <a href="http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm";>Cohen-Sutherland algorithm</a>.
+	 * 
+	 * @param p1 is the first point of the segment.
+	 * @param p2 is the first point of the segment.
+	 * @param rxmin is the min of the coordinates of the rectangle.
+	 * @param rymin is the min of the coordinates of the rectangle.
+	 * @param rxmax is the max of the coordinates of the rectangle.
+	 * @param rymax is the max of the coordinates of the rectangle.
+	 * @return <code>true</code> if the segment has an intersection with the
+	 * rectangle and the segment was clipped; <code>false</code> if the segment
+	 * does not intersect the rectangle.
+	 */
+	public static boolean clipSegmentToRectangle(Point2D p1, Point2D p2, int rxmin, int rymin, int rxmax, int rymax) {
+		int x0 = p1.x();
+		int y0 = p1.y();
+		int x1 = p2.x();
+		int y1 = p2.y();
+		int code1 = getCohenSutherlandCode(x0, y0, rxmin, rymin, rxmax, rymax);
+		int code2 = getCohenSutherlandCode(x1, y1, rxmin, rymin, rxmax, rymax);
+		boolean accept = false;
+		boolean cont = true;
+		int x, y;
+		x = y = 0;
+
+		while (cont) {
+			if ((code1 | code2)==0) {
+				// Bitwise OR is 0. Trivially accept and get out of loop
+				accept = true;
+				cont = false;
+			}
+			else if ((code1 & code2)!=0) {
+				// Bitwise AND is not 0. Trivially reject and get out of loop
+				cont = false;
+			}
+			else {
+				// failed both tests, so calculate the line segment to clip
+				// from an outside point to an intersection with clip edge
+
+				// At least one endpoint is outside the clip rectangle; pick it.
+				int code3 = code1!=0 ? code1 : code2;
+
+				// Now find the intersection point;
+				// use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
+				if ((code3 & COHEN_SUTHERLAND_TOP)!=0) {
+					// point is above the clip rectangle
+					x = x0 + (x1 - x0) * (rymax - y0) / (y1 - y0);
+					y = rymax;
+				}
+				else if ((code3 & COHEN_SUTHERLAND_BOTTOM)!=0) {
+					// point is below the clip rectangle
+					x = x0 + (x1 - x0) * (rymin - y0) / (y1 - y0);
+					y = rymin;
+				}
+				else if ((code3 & COHEN_SUTHERLAND_RIGHT)!=0) { 
+					// point is to the right of clip rectangle
+					y = y0 + (y1 - y0) * (rxmax - x0) / (x1 - x0);
+					x = rxmax;
+				}
+				else if ((code3 & COHEN_SUTHERLAND_LEFT)!=0) {
+					// point is to the left of clip rectangle
+					y = y0 + (y1 - y0) * (rxmin - x0) / (x1 - x0);
+					x = rxmin;
+				}
+				else {
+					code3 = 0;
+				}
+
+				if (code3!=0) {
+					// Now we move outside point to intersection point to clip
+					// and get ready for next pass.
+					if (code3 == code1) {
+						x0 = x;
+						y0 = y;
+						code1 = getCohenSutherlandCode(x0, y0, rxmin, rymin, rxmax, rymax);
+					}
+					else {
+						x1 = x;
+						y1 = y;
+						code2 = getCohenSutherlandCode(x1, y1, rxmin, rymin, rxmax, rymax);
+					}
+				}
+			}
+		}
+		if (accept) {
+			p1.set(x0, y0);
+			p2.set(x1, y1);
+		}
+		return accept;
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/AbstractRectangularShape2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/AbstractRectangularShape2f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/AbstractRectangularShape2f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,332 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import org.arakhne.afc.math.generic.Point2D;
+
+
+
+/** Abstract implementation of 2D rectangular shapes.
+ * 
+ * @param <T> is the type of the shape implemented by the instance of this class.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractRectangularShape2f<T extends AbstractRectangularShape2f<T>>
+extends AbstractShape2f<T> {
+
+	private static final long serialVersionUID = -2330319571109966087L;
+
+	/** Lowest x-coordinate covered by this rectangular shape. */
+	protected float minx = 0f;
+	/** Lowest y-coordinate covered by this rectangular shape. */
+	protected float miny = 0f;
+	/** Highest x-coordinate covered by this rectangular shape. */
+	protected float maxx = 0f;
+	/** Highest y-coordinate covered by this rectangular shape. */
+	protected float maxy = 0f;
+	
+	/**
+	 */
+	public AbstractRectangularShape2f() {
+		//
+	}
+	
+	/**
+	 * @param min is the min corner of the rectangle.
+	 * @param max is the max corner of the rectangle.
+	 */
+	public AbstractRectangularShape2f(Point2f min, Point2f max) {
+		setFromCorners(min.getX(), min.getY(), max.getX(), max.getY());
+	}
+	
+	/**
+	 * @param x
+	 * @param y
+	 * @param width
+	 * @param height
+	 */
+	public AbstractRectangularShape2f(float x, float y, float width, float height) {
+		setFromCorners(x, y, x+width, y+height);
+	}
+	
+	@Override
+	public void clear() {
+		this.minx = this.miny = this.maxx = this.maxy = 0f;
+	}
+	
+	/** Change the frame of the rectangle.
+	 * 
+	 * @param x
+	 * @param y
+	 * @param width
+	 * @param height
+	 */
+	public void set(float x, float y, float width, float height) {
+		setFromCorners(x, y, x+width, y+height);
+	}
+	
+	/** Change the frame of te rectangle.
+	 * 
+	 * @param min is the min corner of the rectangle.
+	 * @param max is the max corner of the rectangle.
+	 */
+	public void set(Point2f min, Point2f max) {
+		setFromCorners(min.getX(), min.getY(), max.getX(), max.getY());
+	}
+	
+	/** Change the frame of te rectangle.
+	 * 
+	 * @param r
+	 */
+	public void set(AbstractRectangularShape2f<?> r) {
+		setFromCorners(r.getMinX(), r.getMinY(), r.getMaxX(), r.getMaxY());
+	}
+
+	/** Change the width of the rectangle, not the min corner.
+	 * 
+	 * @param width
+	 */
+	public void setWidth(float width) {
+		this.maxx = this.minx + Math.max(0f, width);
+	}
+
+	/** Change the height of the rectangle, not the min corner.
+	 * 
+	 * @param height
+	 */
+	public void setHeight(float height) {
+		this.maxy = this.miny + Math.max(0f, height);
+	}
+
+	/** Change the frame of the rectangle.
+	 * 
+	 * @param p1 is the coordinate of the first corner.
+	 * @param p2 is the coordinate of the second corner.
+	 */
+	public void setFromCorners(Point2D p1, Point2D p2) {
+		setFromCorners(p1.getX(), p1.getY(), p2.getX(), p2.getY());
+	}
+
+	/** Change the frame of the rectangle.
+	 * 
+	 * @param x1 is the coordinate of the first corner.
+	 * @param y1 is the coordinate of the first corner.
+	 * @param x2 is the coordinate of the second corner.
+	 * @param y2 is the coordinate of the second corner.
+	 */
+	public void setFromCorners(float x1, float y1, float x2, float y2) {
+		if (x1<x2) {
+			this.minx = x1;
+			this.maxx = x2;
+		}
+		else {
+			this.minx = x2;
+			this.maxx = x1;
+		}
+		if (y1<y2) {
+			this.miny = y1;
+			this.maxy = y2;
+		}
+		else {
+			this.miny = y2;
+			this.maxy = y1;
+		}
+	}
+	
+	/**
+     * Sets the framing rectangle of this <code>Shape</code>
+     * based on the specified center point coordinates and corner point
+     * coordinates.  The framing rectangle is used by the subclasses of
+     * <code>RectangularShape</code> to define their geometry.
+     *
+     * @param centerX the X coordinate of the specified center point
+     * @param centerY the Y coordinate of the specified center point
+     * @param cornerX the X coordinate of the specified corner point
+     * @param cornerY the Y coordinate of the specified corner point
+     */
+	public void setFromCenter(float centerX, float centerY, float cornerX, float cornerY) {
+		float dx = centerX - cornerX;
+		float dy = centerY - cornerY;
+		setFromCorners(cornerX, cornerY, centerX + dx, centerY + dy);
+	}
+	
+	/** Replies the min X.
+	 * 
+	 * @return the min x.
+	 */
+	public float getMinX() {
+		return this.minx;
+	}
+
+	/** Set the min X.
+	 * 
+	 * @param x the min x.
+	 */
+	public void setMinX(float x) {
+		float o = this.maxx;
+		if (o<x) {
+			this.minx = o;
+			this.maxx = x;
+		}
+		else {
+			this.minx = x;
+		}
+	}
+
+	/** Replies the center x.
+	 * 
+	 * @return the center x.
+	 */
+	public float getCenterX() {
+		return (this.minx + this.maxx) / 2f;
+	}
+
+	/** Replies the max x.
+	 * 
+	 * @return the max x.
+	 */
+	public float getMaxX() {
+		return this.maxx;
+	}
+
+	/** Set the max X.
+	 * 
+	 * @param x the max x.
+	 */
+	public void setMaxX(float x) {
+		float o = this.minx;
+		if (o>x) {
+			this.maxx = o;
+			this.minx = x;
+		}
+		else {
+			this.maxx = x;
+		}
+	}
+
+	/** Replies the min y.
+	 * 
+	 * @return the min y.
+	 */
+	public float getMinY() {
+		return this.miny;
+	}
+
+	/** Set the min Y.
+	 * 
+	 * @param y the min y.
+	 */
+	public void setMinY(float y) {
+		float o = this.maxy;
+		if (o<y) {
+			this.miny = o;
+			this.maxy = y;
+		}
+		else {
+			this.miny = y;
+		}
+	}
+
+	/** Replies the center y.
+	 * 
+	 * @return the center y.
+	 */
+	public float getCenterY() {
+		return (this.miny + this.maxy) / 2f;
+	}
+
+	/** Replies the max y.
+	 * 
+	 * @return the max y.
+	 */
+	public float getMaxY() {
+		return this.maxy;
+	}
+	
+	/** Set the max Y.
+	 * 
+	 * @param y the max y.
+	 */
+	public void setMaxY(float y) {
+		float o = this.miny;
+		if (o>y) {
+			this.maxy = o;
+			this.miny = y;
+		}
+		else {
+			this.maxy = y;
+		}
+	}
+
+	/** Replies the width.
+	 * 
+	 * @return the width.
+	 */
+	public float getWidth() {
+		return this.maxx - this.minx;
+	}
+
+	/** Replies the height.
+	 * 
+	 * @return the height.
+	 */
+	public float getHeight() {
+		return this.maxy - this.miny;
+	}
+	
+	@Override
+	public void translate(float dx, float dy) {
+		this.minx += dx;
+		this.miny += dy;
+		this.maxx += dx;
+		this.maxy += dy;
+	}
+
+	/** Replies if this rectangular shape is empty.
+	 * The rectangular shape is empty when the
+	 * two corners are at the same location.
+	 * 
+	 * @return <code>true</code> if the rectangular shape is empty;
+	 * otherwise <code>false</code>.
+	 */
+	@Override
+	public boolean isEmpty() {
+		return this.minx==this.maxx && this.miny==this.maxy; 
+	}
+	
+	/** Inflate this rectangle with the given amounts.
+	 * 
+	 * @param left
+	 * @param top
+	 * @param right
+	 * @param bottom
+	 */
+	public void inflate(float left, float top, float right, float bottom) {
+		this.minx -= left;
+		this.miny -= top;
+		this.maxx += right;
+		this.maxy += bottom;
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/AbstractShape2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/AbstractShape2f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/AbstractShape2f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,111 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+
+/** Abstract implementation of shapes.
+ * 
+ * @param <T> is the type of the shape implemented by the instance of this class.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractShape2f<T extends Shape2f> implements Shape2f {
+
+	private static final long serialVersionUID = -2724377801599470453L;
+
+	/**
+	 */
+	public AbstractShape2f() {
+		//
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public T clone()  {
+		try {
+			return (T)super.clone();
+		}
+		catch (CloneNotSupportedException e) {
+			throw new Error(e);
+		}
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Shape2f createTransformedShape(Transform2D transform) {
+		return new Path2f(getPathIterator(transform));
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public final PathIterator2f getPathIterator() {
+		return getPathIterator(null);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distance(Point2D p) {
+		return (float)Math.sqrt(distanceSquared(p));
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public final boolean contains(Point2D p) {
+		return contains(p.getX(), p.getY());
+	}
+
+	
+	/** {@inheritDoc}
+	 */
+    @Override
+    public abstract boolean equals(Object obj);
+    
+    /** Compute the bit representation of the floating-point value.
+     * 
+     * @param d
+     * @return the bit representation.
+     */
+    protected static int floatToIntBits(float d) {
+		// Check for +0 or -0
+		if (d == 0f) {
+			return 0;
+		}
+		return Float.floatToIntBits(d);
+	}
+    
+	/** {@inheritDoc}
+	 */
+    @Override
+    public abstract int hashCode();
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Circle2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Circle2f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Circle2f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,651 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import java.util.NoSuchElementException;
+
+import org.arakhne.afc.math.MathUtil;
+import org.arakhne.afc.math.generic.PathWindingRule;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+
+/** 2D circle with floating-point points.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Circle2f extends AbstractShape2f<Circle2f> {
+
+	private static final long serialVersionUID = -5535463117356287850L;
+
+	/**
+	 * ArcIterator.btan(Math.PI/2)
+	 */
+	static final float CTRL_VAL = 0.5522847498307933f;
+
+	/**
+	 * ctrlpts contains the control points for a set of 4 cubic
+	 * bezier curves that approximate a circle of radius 0.5
+	 * centered at 0.5, 0.5
+	 */
+	static final float PCV = 0.5f + CTRL_VAL * 0.5f;
+	/**
+	 * ctrlpts contains the control points for a set of 4 cubic
+	 * bezier curves that approximate a circle of radius 0.5
+	 * centered at 0.5, 0.5
+	 */
+	static final float NCV = 0.5f - CTRL_VAL * 0.5f;
+	/**
+	 * ctrlpts contains the control points for a set of 4 cubic
+	 * bezier curves that approximate a circle of radius 0.5
+	 * centered at 0.5, 0.5
+	 */
+	static float CTRL_PTS[][] = {
+		{  1.0f,  PCV,  PCV,  1.0f,  0.5f,  1.0f },
+		{  NCV,  1.0f,  0.0f,  PCV,  0.0f,  0.5f },
+		{  0.0f,  NCV,  NCV,  0.0f,  0.5f,  0.0f },
+		{  PCV,  0.0f,  1.0f,  NCV,  1.0f,  0.5f }
+	};
+
+	/** Replies if a rectangle is inside in the circle.
+	 * 
+	 * @param cx is the center of the circle.
+	 * @param cy is the center of the circle.
+	 * @param radius is the radius of the circle.
+	 * @param rx is the lowest corner of the rectangle.
+	 * @param ry is the lowest corner of the rectangle.
+	 * @param rwidth is the width of the rectangle.
+	 * @param rheight is the height of the rectangle.
+	 * @return <code>true</code> if the given rectangle is inside the circle;
+	 * otherwise <code>false</code>.
+	 */
+	public static boolean containsCircleRectangle(float cx, float cy, float radius, float rx, float ry, float rwidth, float rheight) {
+		float rcx = (rx + rwidth/2f);
+		float rcy = (ry + rheight/2f);
+		float farX;
+		if (cx<=rcx) farX = rx + rwidth;
+		else farX = rx;
+		float farY;
+		if (cy<=rcy) farY = ry + rheight;
+		else farY = ry;
+		return MathUtil.isPointInCircle(farX, farY, cx, cy, radius);
+	}
+
+	/** Replies if two circles are intersecting.
+	 * 
+	 * @param x1 is the center of the first circle
+	 * @param y1 is the center of the first circle
+	 * @param radius1 is the radius of the first circle
+	 * @param x2 is the center of the second circle
+	 * @param y2 is the center of the second circle
+	 * @param radius2 is the radius of the second circle
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsCircleCircle(float x1, float y1, float radius1, float x2, float y2, float radius2) {
+		float r = radius1+radius2;
+		return MathUtil.distanceSquaredPointToPoint(x1, y1, x2, y2) < (r*r);
+	}
+
+	/** Replies if a circle and a rectangle are intersecting.
+	 * 
+	 * @param x1 is the center of the circle
+	 * @param y1 is the center of the circle
+	 * @param radius is the radius of the circle
+	 * @param x2 is the first corner of the rectangle.
+	 * @param y2 is the first corner of the rectangle.
+	 * @param x3 is the second corner of the rectangle.
+	 * @param y3 is the second corner of the rectangle.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsCircleRectangle(float x1, float y1, float radius, float x2, float y2, float x3, float y3) {
+		float dx;
+		if (x1<x2) {
+			dx = x2 - x1;
+		}
+		else if (x1>x3) {
+			dx = x1 - x3;
+		}
+		else {
+			dx = 0f;
+		}
+		float dy;
+		if (y1<y2) {
+			dy = y2 - y1;
+		}
+		else if (y1>y3) {
+			dy = y1 - y3;
+		}
+		else {
+			dy = 0f;
+		}
+		return (dx*dx+dy*dy) < (radius*radius);
+	}
+
+	/** Replies if a circle and a line are intersecting.
+	 * 
+	 * @param x1 is the center of the circle
+	 * @param y1 is the center of the circle
+	 * @param radius is the radius of the circle
+	 * @param x2 is the first point of the line.
+	 * @param y2 is the first point of the line.
+	 * @param x3 is the second point of the line.
+	 * @param y3 is the second point of the line.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsCircleLine(float x1, float y1, float radius, float x2, float y2, float x3, float y3) {
+		float d = MathUtil.distanceSquaredPointToLine(x1, y1, x2, y2, x3, y3);
+		return d<(radius*radius);
+	}
+
+	/** Replies if a circle and a segment are intersecting.
+	 * 
+	 * @param x1 is the center of the circle
+	 * @param y1 is the center of the circle
+	 * @param radius is the radius of the circle
+	 * @param x2 is the first point of the segment.
+	 * @param y2 is the first point of the segment.
+	 * @param x3 is the second point of the segment.
+	 * @param y3 is the second point of the segment.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsCircleSegment(float x1, float y1, float radius, float x2, float y2, float x3, float y3) {
+		float d = MathUtil.distanceSquaredPointToSegment(x1, y1, x2, y2, x3, y3);
+		return d<(radius*radius);
+	}
+
+	/** X-coordinate of the circle center. */
+	protected float cx = 0f;
+	/** Y-coordinate of the circle center. */
+	protected float cy = 0f;
+	/** Radius of the circle center (must be always positive). */
+	protected float radius = 0f;
+
+	/**
+	 */
+	public Circle2f() {
+		//
+	}
+
+	/**
+	 * @param center
+	 * @param radius
+	 */
+	public Circle2f(Point2D center, float radius) {
+		set(center, radius);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param radius
+	 */
+	public Circle2f(float x, float y, float radius) {
+		set(x, y, radius);
+	}
+	
+	@Override
+	public void clear() {
+		this.cx = this.cy = 0f;
+		this.radius = 0f;
+	}
+	
+	/** Replies if the circle is empty.
+	 * The circle is empty when the radius is nul.
+	 * 
+	 * @return <code>true</code> if the radius is nul;
+	 * otherwise <code>false</code>.
+	 */
+	@Override
+	public boolean isEmpty() {
+		return this.radius<=+0f;
+	}
+
+	/** Change the frame of the circle.
+	 * 
+	 * @param x
+	 * @param y
+	 * @param radius
+	 */
+	public void set(float x, float y, float radius) {
+		this.cx = x;
+		this.cy = y;
+		this.radius = Math.abs(radius);
+	}
+
+	/** Change the frame of te circle.
+	 * 
+	 * @param center
+	 * @param radius
+	 */
+	public void set(Point2D center, float radius) {
+		this.cx = center.getX();
+		this.cy = center.getY();
+		this.radius = Math.abs(radius);
+	}
+
+	/** Replies the center X.
+	 * 
+	 * @return the center x.
+	 */
+	public float getX() {
+		return this.cx;
+	}
+
+	/** Replies the center y.
+	 * 
+	 * @return the center y.
+	 */
+	public float getY() {
+		return this.cy;
+	}
+
+	/** Replies the center.
+	 * 
+	 * @return a copy of the center.
+	 */
+	public Point2f getCenter() {
+		return new Point2f(this.cx, this.cy);
+	}
+
+	/** Replies the center.
+	 * 
+	 * @param center
+	 */
+	public void setCenter(Point2D center) {
+		this.cx = center.getX();
+		this.cy = center.getY();
+	}
+
+	/** Replies the center.
+	 * 
+	 * @param x
+	 * @param y
+	 */
+	public void setCenter(float x, float y) {
+		this.cx = x;
+		this.cy = y;
+	}
+
+	/** Replies the radius.
+	 * 
+	 * @return the radius.
+	 */
+	public float getRadius() {
+		return this.radius;
+	}
+
+	/** Set the radius.
+	 * 
+	 * @param radius is the radius.
+	 */
+	public void setRadius(float radius) {
+		this.radius = Math.abs(radius);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Rectangle2f toBoundingBox() {
+		Rectangle2f r = new Rectangle2f();
+		r.setFromCorners(
+				this.cx-this.radius,
+				this.cy-this.radius,
+				this.cx+this.radius,
+				this.cy+this.radius);
+		return r;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distance(Point2D p) {
+		float d = MathUtil.distancePointToPoint(getX(), getY(), p.getX(), p.getY()) - getRadius();
+		return Math.max(0f, d);
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceSquared(Point2D p) {
+		float d = distance(p);
+		return d * d;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceL1(Point2D p) {
+		Point2D r = getClosestPointTo(p);
+		return r.distanceL1(p);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceLinf(Point2D p) {
+		Point2D r = getClosestPointTo(p);
+		return r.distanceLinf(p);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean contains(float x, float y) {
+		return MathUtil.isPointInCircle(x, y, getX(), getY(), getRadius());
+	}
+	
+	@Override
+	public boolean contains(Rectangle2f r) {
+		return containsCircleRectangle(getX(), getY(), getRadius(),
+				r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight());
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Point2f getClosestPointTo(Point2D p) {
+		Vector2f v = new Vector2f(p);
+		v.sub(this.cx, this.cy);
+		float l = v.lengthSquared();
+		if (l<=(this.radius*this.radius)) {
+			if (p instanceof Point2f) return (Point2f)p;
+			return new Point2f(p);
+		}
+		float s = this.radius/(float)Math.sqrt(l);
+		v.scale(s);
+		return new Point2f(this.cx + v.getX(), this.cy + v.getY());
+	}
+
+	@Override
+	public void translate(float dx, float dy) {
+		this.cx += dx;
+		this.cy += dy;
+	}
+	
+	@Override
+	public PathIterator2f getPathIterator(Transform2D transform) {
+		if (transform==null)
+			return new CopyPathIterator(getX(), getY(), getRadius());
+		return new TransformPathIterator(getX(), getY(), getRadius(), transform);
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (obj == this) {
+			return true;
+		}
+		if (obj instanceof Circle2f) {
+			Circle2f rr2d = (Circle2f) obj;
+			return ((getX() == rr2d.getX()) &&
+					(getY() == rr2d.getY()) &&
+					(getRadius() == rr2d.getRadius()));
+		}
+		return false;
+	}
+
+	@Override
+	public int hashCode() {
+		long bits = 1L;
+		bits = 31L * bits + floatToIntBits(getX());
+		bits = 31L * bits + floatToIntBits(getY());
+		bits = 31L * bits + floatToIntBits(getRadius());
+		return (int) (bits ^ (bits >> 32));
+	}
+
+	@Override
+	public boolean intersects(Rectangle2f s) {
+		return intersectsCircleRectangle(
+				getX(), getY(), getRadius(),
+				s.getMinX(), s.getMinY(),
+				s.getMaxX(), s.getMaxY());
+	}
+
+	@Override
+	public boolean intersects(Ellipse2f s) {
+		return Ellipse2f.intersectsEllipseEllipse(
+				getX()-getRadius(), getY()-getRadius(),
+				getX()+getRadius(), getY()+getRadius(),
+				s.getMinX(), s.getMinY(),
+				s.getMaxX(), s.getMaxY());
+	}
+
+	@Override
+	public boolean intersects(Circle2f s) {
+		return intersectsCircleCircle(
+				getX(), getY(), getRadius(),
+				s.getX(), s.getY(), s.getRadius());
+	}
+
+	@Override
+	public boolean intersects(Segment2f s) {
+		return intersectsCircleSegment(
+				getX(), getY(), getRadius(),
+				s.getX1(), s.getY1(),
+				s.getX2(), s.getY2());
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder b = new StringBuilder();
+		b.append("["); //$NON-NLS-1$
+		b.append(getX());
+		b.append(";"); //$NON-NLS-1$
+		b.append(getY());
+		b.append(";"); //$NON-NLS-1$
+		b.append(getRadius());
+		b.append("]"); //$NON-NLS-1$
+		return b.toString();
+	}
+
+	/** Iterator on the path elements of the circle.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class CopyPathIterator implements PathIterator2f {
+		
+		private final float x;
+		private final float y;
+		private final float r;
+		private int index = 0;
+		private float movex, movey;
+		private float lastx, lasty;
+		
+		/**
+		 * @param x
+		 * @param y
+		 * @param r
+		 */
+		public CopyPathIterator(float x, float y, float r) {
+			this.r = Math.max(0f, r);
+			this.x = x - this.r;
+			this.y = y - this.r;
+			if (this.r<=0f) {
+				this.index = 6;
+			}
+		}
+	
+		@Override
+		public boolean hasNext() {
+			return this.index<=5;
+		}
+	
+		@Override
+		public PathElement2f next() {
+			if (this.index>5) throw new NoSuchElementException();
+			int idx = this.index;
+			++this.index;
+			if (idx==0) {
+				float dr = 2f * this.r;
+				float ctrls[] = CTRL_PTS[3];
+				this.movex = (this.x + ctrls[4] * dr);
+				this.movey = (this.y + ctrls[5] * dr);
+				this.lastx = this.movex;
+				this.lasty = this.movey;
+				return new PathElement2f.MovePathElement2f(
+						this.lastx, this.lasty);
+			}
+			else if (idx<5) {
+				float dr = 2f * this.r;
+				float ctrls[] = CTRL_PTS[idx - 1];
+				float ppx = this.lastx;
+				float ppy = this.lasty;
+				this.lastx = (this.x + ctrls[4] * dr);
+				this.lasty = (this.y + ctrls[5] * dr);
+				return new PathElement2f.CurvePathElement2f(
+						ppx, ppy,
+						(this.x + ctrls[0] * dr),
+						(this.y + ctrls[1] * dr),
+						(this.x + ctrls[2] * dr),
+						(this.y + ctrls[3] * dr),
+						this.lastx, this.lasty);
+			}
+			float ppx = this.lastx;
+			float ppy = this.lasty;
+			this.lastx = this.movex;
+			this.lasty = this.movey;
+			return new PathElement2f.ClosePathElement2f(
+					ppx, ppy,
+					this.lastx, this.lasty);
+		}
+	
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+	
+		@Override
+		public PathWindingRule getWindingRule() {
+			return PathWindingRule.NON_ZERO;
+		}
+		
+	}
+	
+	/** Iterator on the path elements of the circle.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class TransformPathIterator implements PathIterator2f {
+			
+		private final Point2D p1 = new Point2f();
+		private final Point2D p2 = new Point2f();
+		private final Point2D ptmp1 = new Point2f();
+		private final Point2D ptmp2 = new Point2f();
+		private final Transform2D transform;
+		private final float x;
+		private final float y;
+		private final float r;
+		private int index = 0;
+		private float movex, movey;
+		
+		/**
+		 * @param x
+		 * @param y
+		 * @param r
+		 * @param transform
+		 */
+		public TransformPathIterator(float x, float y, float r, Transform2D transform) {
+			assert(transform!=null);
+			this.transform = transform;
+			this.r = Math.max(0f, r);
+			this.x = x - this.r;
+			this.y = y - this.r;
+			if (this.r<=0f) {
+				this.index = 6;
+			}
+		}
+	
+		@Override
+		public boolean hasNext() {
+			return this.index<=5;
+		}
+	
+		@Override
+		public PathElement2f next() {
+			if (this.index>5) throw new NoSuchElementException();
+			int idx = this.index;
+			++this.index;
+			if (idx==0) {
+				float dr = 2f * this.r;
+				float ctrls[] = CTRL_PTS[3];
+				this.movex = (this.x + ctrls[4] * dr);
+				this.movey = (this.y + ctrls[5] * dr);
+				this.p2.set(this.movex, this.movey);
+				this.transform.transform(this.p2);
+				return new PathElement2f.MovePathElement2f(
+						this.p2.getX(), this.p2.getY());
+			}
+			else if (idx<5) {
+				float dr = 2f * this.r;
+				float ctrls[] = CTRL_PTS[idx - 1];
+				this.p1.set(this.p2);
+				this.p2.set(
+						(this.x + ctrls[4] * dr),
+						(this.y + ctrls[5] * dr));
+				this.transform.transform(this.p2);
+				this.ptmp1.set(
+						(this.x + ctrls[0] * dr),
+						(this.y + ctrls[1] * dr));
+				this.transform.transform(this.ptmp1);
+				this.ptmp2.set(
+						(this.x + ctrls[2] * dr),
+						(this.y + ctrls[3] * dr));
+				this.transform.transform(this.ptmp2);
+				return new PathElement2f.CurvePathElement2f(
+						this.p1.getX(), this.p1.getY(),
+						this.ptmp1.getX(), this.ptmp1.getY(),
+						this.ptmp2.getX(), this.ptmp2.getY(),
+						this.p2.getX(), this.p2.getY());
+			}
+			this.p1.set(this.p2);
+			this.p2.set(this.movex, this.movey);
+			this.transform.transform(this.p2);
+			return new PathElement2f.ClosePathElement2f(
+					this.p1.getX(), this.p1.getY(),
+					this.p2.getX(), this.p2.getY());
+		}
+	
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+	
+		@Override
+		public PathWindingRule getWindingRule() {
+			return PathWindingRule.NON_ZERO;
+		}
+		
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Ellipse2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Ellipse2f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Ellipse2f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,699 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import java.util.NoSuchElementException;
+
+import org.arakhne.afc.math.MathUtil;
+import org.arakhne.afc.math.generic.PathWindingRule;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+/** 2D ellipse with floating-point points.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Ellipse2f extends AbstractRectangularShape2f<Ellipse2f> {
+
+	private static final long serialVersionUID = -2745313055404516167L;
+
+	// ArcIterator.btan(Math.PI/2)
+	private static final float CTRL_VAL = 0.5522847498307933f;
+
+	/**
+	 * ctrlpts contains the control points for a set of 4 cubic
+	 * bezier curves that approximate a circle of radius 0.5
+	 * centered at 0.5, 0.5
+	 */
+	static final float PCV = 0.5f + CTRL_VAL * 0.5f;
+	/**
+	 * ctrlpts contains the control points for a set of 4 cubic
+	 * bezier curves that approximate a circle of radius 0.5
+	 * centered at 0.5, 0.5
+	 */
+	static final float NCV = 0.5f - CTRL_VAL * 0.5f;
+	/**
+	 * ctrlpts contains the control points for a set of 4 cubic
+	 * bezier curves that approximate a circle of radius 0.5
+	 * centered at 0.5, 0.5
+	 */
+	static final float CTRL_PTS[][] = {
+		{  1.0f,  PCV,  PCV,  1.0f,  0.5f,  1.0f },
+		{  NCV,  1.0f,  0.0f,  PCV,  0.0f,  0.5f },
+		{  0.0f,  NCV,  NCV,  0.0f,  0.5f,  0.0f },
+		{  PCV,  0.0f,  1.0f,  NCV,  1.0f,  0.5f }
+	};
+
+	/** Replies if a rectangle is inside in the ellipse.
+	 * 
+	 * @param ex is the lowest corner of the ellipse.
+	 * @param ey is the lowest corner of the ellipse.
+	 * @param ewidth is the width of the ellipse.
+	 * @param eheight is the height of the ellipse.
+	 * @param rx is the lowest corner of the rectangle.
+	 * @param ry is the lowest corner of the rectangle.
+	 * @param rwidth is the width of the rectangle.
+	 * @param rheight is the height of the rectangle.
+	 * @return <code>true</code> if the given rectangle is inside the ellipse;
+	 * otherwise <code>false</code>.
+	 */
+	public static boolean containsEllipseRectangle(float ex, float ey, float ewidth, float eheight, float rx, float ry, float rwidth, float rheight) {
+		float ecx = (ex + ewidth/2f);
+		float ecy = (ey + eheight/2f);
+		float rcx = (rx + rwidth/2f);
+		float rcy = (ry + rheight/2f);
+		float farX;
+		if (ecx<=rcx) farX = rx + rwidth;
+		else farX = rx;
+		float farY;
+		if (ecy<=rcy) farY = ry + rheight;
+		else farY = ry;
+		return MathUtil.isPointInEllipse(farX, farY, ex, ey, ewidth, eheight);
+	}
+
+	/** Replies if two ellipses are intersecting.
+	 * 
+	 * @param x1 is the first corner of the first ellipse.
+	 * @param y1 is the first corner of the first ellipse.
+	 * @param x2 is the second corner of the first ellipse.
+	 * @param y2 is the second corner of the first ellipse.
+	 * @param x3 is the first corner of the second ellipse.
+	 * @param y3 is the first corner of the second ellipse.
+	 * @param x4 is the second corner of the second ellipse.
+	 * @param y4 is the second corner of the second ellipse.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsEllipseEllipse(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
+		float ell2w = Math.abs(x4 - x3);
+		float ell2h = Math.abs(y4 - y3);
+		float ellw = Math.abs(x2 - x1);
+		float ellh = Math.abs(y2 - y1);
+
+		if (ell2w <= 0f || ell2h <= 0f) return false;
+		if (ellw <= 0f || ellh <= 0f) return false;
+
+		// Normalize the second ellipse coordinates compared to the ellipse
+		// having a center at 0,0 and a radius of 0.5.
+		float normx0 = (x3 - x1) / ellw - 0.5f;
+		float normx1 = normx0 + ell2w / ellw;
+		float normy0 = (y3 - y1) / ellh - 0.5f;
+		float normy1 = normy0 + ell2h / ellh;
+
+		// find nearest x (left edge, right edge, 0.0)
+		// find nearest y (top edge, bottom edge, 0.0)
+		// if nearest x,y is inside circle of radius 0.5, then intersects
+		float nearx, neary;
+		if (normx0 > 0f) {
+			// center to left of X extents
+			nearx = normx0;
+		} else if (normx1 < 0f) {
+			// center to right of X extents
+			nearx = normx1;
+		} else {
+			nearx = 0f;
+		}
+		if (normy0 > 0f) {
+			// center above Y extents
+			neary = normy0;
+		} else if (normy1 < 0f) {
+			// center below Y extents
+			neary = normy1;
+		} else {
+			neary = 0f;
+		}
+		return (nearx * nearx + neary * neary) < 0.25f;
+	}
+
+	/** Replies if an ellipse and a line are intersecting.
+	 * 
+	 * @param x1 is the first corner of the ellipse.
+	 * @param y1 is the first corner of the ellipse.
+	 * @param x2 is the second corner of the ellipse.
+	 * @param y2 is the second corner of the ellipse.
+	 * @param x3 is the first point of the line.
+	 * @param y3 is the first point of the line.
+	 * @param x4 is the second point of the line.
+	 * @param y4 is the second point of the line.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsEllipseLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
+		float a = (x2 - x1) / 2f;
+		float b = (y2 - y1) / 2f;
+		float h = x1 + a; // h is center of ellipse
+		float k = y1 + b; // k is center of ellipse
+
+		float aas = a*a;
+		float bbs = b*b;
+
+		float aa, bb, cc;
+
+		if (x3!=x4) {
+			float m = (y4-y3)/(x4-x3);
+			float c = y3 - m*x3;
+			//
+			aa = bbs + aas*m*m;
+			bb = 2*aas*c*m - 2*aas*k*m - 2*h*bbs;
+			cc = bbs*h*h + aas*c*c - 2*aas*k*c + aas*k*k - aas*bbs;
+		}
+		else {
+			//
+			// vertical line case
+			//
+			aa = aas;
+			bb = -2f*k*aas;
+			cc = -aas*bbs + bbs*(x3-h)*(x3-h);
+		}
+
+		float d = bb*bb-4*aa*cc;
+		return (d > 0f);
+	}
+
+	/** Replies if an ellipse and a segment are intersecting.
+	 * 
+	 * @param x1 is the first corner of the ellipse.
+	 * @param y1 is the first corner of the ellipse.
+	 * @param x2 is the second corner of the ellipse.
+	 * @param y2 is the second corner of the ellipse.
+	 * @param x3 is the first point of the segment.
+	 * @param y3 is the first point of the segment.
+	 * @param x4 is the second point of the segment.
+	 * @param y4 is the second point of the segment.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsEllipseSegment(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
+		if (x1==x2 || y1==y2) return false;
+		
+		float a = (x2 - x1) / 2f;
+		float b = (y2 - y1) / 2f;
+		float h = x1 + a; // h is center of ellipse
+		float k = y1 + b; // k is center of ellipse
+
+		float aas = a*a;
+		float bbs = b*b;
+
+		float aa, bb, cc;
+		
+		float m = Float.NaN;
+
+		if (x3!=x4) {
+			m = (y4-y3)/(x4-x3);
+			float c = y3 - m*x3;
+			//
+			aa = bbs + aas*m*m;
+			bb = 2*aas*c*m - 2*aas*k*m - 2*h*bbs;
+			cc = bbs*h*h + aas*c*c - 2*aas*k*c + aas*k*k - aas*bbs;
+		}
+		else {
+			//
+			// vertical line case
+			//
+			aa = aas;
+			bb = -2f*k*aas;
+			cc = -aas*bbs + bbs*(x3-h)*(x3-h);
+		}
+
+		float d = bb*bb-4*aa*cc;
+		if (d > 0f) {
+			// Intersection exists between the ellipse and the line.
+			float rootd = (float)Math.sqrt(d);
+			float aa2 = aa*2f;
+
+			float root1 = (-bb + rootd) / aa2;
+			float root2 = (-bb - rootd) / aa2;
+			
+			// Intersection points are (xi1;yi1) and (xi2;yi2)
+			float xi1, xi2, yi1, yi2;
+			
+			if (Float.isNaN(m)) {
+				// Vertical line
+				xi1 = xi2 = x1;
+				yi1 = root1;
+				yi2 = root2;
+			}
+			else {
+				xi1 = root1;
+				xi2 = root2;
+				yi1 = y1 + m * (xi1 - x1);
+				yi2 = y1 + m * (xi2 - x1);
+			}
+			
+			// Compute the relative position of the roots again the segment coordinates
+			// Reuse aa and bb as temp vars.
+			// Reuse d as the length of the segment.
+			// Reuse a and b as the length of the two roots.
+			aa = x4 - x3;
+			bb = y4 - y3;
+			d = aa*aa + bb*bb;
+			aa = xi1 - x3;
+			bb = yi1 - y3;
+			a = aa*aa + bb*bb;
+			aa = xi2 - x3;
+			bb = yi2 - y3;
+			b = aa*aa + bb*bb;
+			
+			if (b<a) {
+				// Ensure root1 and root2 are ordered
+				aa = a;
+				a = b;
+				b = aa;
+			}
+			
+			// Test intersection of 1D segments [a;b] and [0;d]
+			// No intersection when: ( b<=0f ) || ( a>=d )
+			return ( b>0f ) && ( a<d );
+		}
+		return false;
+	}
+
+	/** Replies if two ellipses are intersecting.
+	 * 
+	 * @param x1 is the first corner of the first ellipse.
+	 * @param y1 is the first corner of the first ellipse.
+	 * @param x2 is the second corner of the first ellipse.
+	 * @param y2 is the second corner of the first ellipse.
+	 * @param x3 is the first corner of the second rectangle.
+	 * @param y3 is the first corner of the second rectangle.
+	 * @param x4 is the second corner of the second rectangle.
+	 * @param y4 is the second corner of the second rectangle.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsEllipseRectangle(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
+		// From AWT Ellipse2D
+
+		float rectw = Math.abs(x4 - x3);
+		float recth = Math.abs(y4 - y3);
+		float ellw = Math.abs(x2 - x1);
+		float ellh = Math.abs(y2 - y1);
+
+		if (rectw <= 0f || recth <= 0f) return false;
+		if (ellw <= 0f || ellh <= 0f) return false;
+
+		// Normalize the rectangular coordinates compared to the ellipse
+		// having a center at 0,0 and a radius of 0.5.
+		float normx0 = (x3 - x1) / ellw - 0.5f;
+		float normx1 = normx0 + rectw / ellw;
+		float normy0 = (y3 - y1) / ellh - 0.5f;
+		float normy1 = normy0 + recth / ellh;
+		// find nearest x (left edge, right edge, 0.0)
+		// find nearest y (top edge, bottom edge, 0.0)
+		// if nearest x,y is inside circle of radius 0.5, then intersects
+		float nearx, neary;
+		if (normx0 > 0f) {
+			// center to left of X extents
+			nearx = normx0;
+		} else if (normx1 < 0f) {
+			// center to right of X extents
+			nearx = normx1;
+		} else {
+			nearx = 0f;
+		}
+		if (normy0 > 0f) {
+			// center above Y extents
+			neary = normy0;
+		} else if (normy1 < 0f) {
+			// center below Y extents
+			neary = normy1;
+		} else {
+			neary = 0f;
+		}
+		return (nearx * nearx + neary * neary) < 0.25f;
+	}
+
+	/**
+	 */
+	public Ellipse2f() {
+		//
+	}
+
+	/**
+	 * @param min is the min corner of the ellipse.
+	 * @param max is the max corner of the ellipse.
+	 */
+	public Ellipse2f(Point2f min, Point2f max) {
+		super(min, max);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param width
+	 * @param height
+	 */
+	public Ellipse2f(float x, float y, float width, float height) {
+		super(x, y, width, height);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Rectangle2f toBoundingBox() {
+		return new Rectangle2f(getMinX(), getMinY(), getMaxX(), getMaxY());
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceSquared(Point2D p) {
+		Point2D r = getClosestPointTo(p);
+		return r.distanceSquared(p);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceL1(Point2D p) {
+		Point2D r = getClosestPointTo(p);
+		return r.distanceL1(p);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceLinf(Point2D p) {
+		Point2D r = getClosestPointTo(p);
+		return r.distanceLinf(p);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean contains(float x, float y) {
+		return MathUtil.isPointInEllipse(
+				x, y,
+				getMinX(), getMinY(), getWidth(), getHeight());
+	}
+	
+	@Override
+	public boolean contains(Rectangle2f r) {
+		return containsEllipseRectangle(
+				getMinX(), getMinY(), getWidth(), getHeight(),
+				r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight());
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Point2D getClosestPointTo(Point2D p) {
+		return MathUtil.getClosestPointToSolidEllipse(
+				p.getX(), p.getY(),
+				getMinX(), getMinY(),
+				getWidth(), getHeight());
+	}
+	
+	@Override
+	public PathIterator2f getPathIterator(Transform2D transform) {
+		if (transform==null) {
+			return new CopyPathIterator(
+					getMinX(), getMinY(),
+					getMaxX(), getMaxY());
+		}
+		return new TransformPathIterator(
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY(),
+				transform);
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (obj == this) {
+			return true;
+		}
+		if (obj instanceof Ellipse2f) {
+			Ellipse2f rr2d = (Ellipse2f) obj;
+			return ((getMinX() == rr2d.getMinX()) &&
+					(getMinY() == rr2d.getMinY()) &&
+					(getWidth() == rr2d.getWidth()) &&
+					(getHeight() == rr2d.getHeight()));
+		}
+		return false;
+	}
+
+	@Override
+	public int hashCode() {
+		long bits = 1L;
+		bits = 31L * bits + floatToIntBits(getMinX());
+		bits = 31L * bits + floatToIntBits(getMinY());
+		bits = 31L * bits + floatToIntBits(getMaxX());
+		bits = 31L * bits + floatToIntBits(getMaxY());
+		return (int) (bits ^ (bits >> 32));
+	}
+
+	@Override
+	public boolean intersects(Rectangle2f s) {
+		return intersectsEllipseRectangle(
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY(),
+				s.getMinX(), s.getMinY(),
+				s.getMaxX(), s.getMaxY());
+	}
+
+	@Override
+	public boolean intersects(Ellipse2f s) {
+		return intersectsEllipseRectangle(
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY(),
+				s.getMinX(), s.getMinY(),
+				s.getMaxX(), s.getMaxY());
+	}
+
+	@Override
+	public boolean intersects(Circle2f s) {
+		return intersectsEllipseEllipse(
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY(),
+				s.getX()-s.getRadius(), s.getY()-s.getRadius(),
+				s.getX()+s.getRadius(), s.getY()+s.getRadius());
+	}
+
+	@Override
+	public boolean intersects(Segment2f s) {
+		return intersectsEllipseSegment(
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY(),
+				s.getX1(), s.getY1(),
+				s.getX2(), s.getY2());
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder b = new StringBuilder();
+		b.append("["); //$NON-NLS-1$
+		b.append(getMinX());
+		b.append(";"); //$NON-NLS-1$
+		b.append(getMinY());
+		b.append(";"); //$NON-NLS-1$
+		b.append(getMaxX());
+		b.append(";"); //$NON-NLS-1$
+		b.append(getMaxY());
+		b.append("]"); //$NON-NLS-1$
+		return b.toString();
+	}
+
+	/**
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public static class CopyPathIterator implements PathIterator2f {
+		
+		private final float x1;
+		private final float y1;
+		private final float w;
+		private final float h;
+		private int index;
+		private float lastX, lastY;
+		
+		/**
+		 * @param x1
+		 * @param y1
+		 * @param x2
+		 * @param y2
+		 */
+		public CopyPathIterator(float x1, float y1, float x2, float y2) {
+			this.x1 = x1;
+			this.y1 = y1;
+			this.w = x2 - x1;
+			this.h = y2 - y1;
+			if (this.w==0f && this.h==0f) {
+				this.index = 6;
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.index<=5;
+		}
+
+		@Override
+		public PathElement2f next() {
+			if (this.index>5) throw new NoSuchElementException();
+			int idx = this.index;
+			++this.index;
+			
+			if (idx==0) {
+				float ctrls[] = CTRL_PTS[3];
+				this.lastX = this.x1 + ctrls[4] * this.w;
+				this.lastY = this.y1 + ctrls[5] * this.h;
+				return new PathElement2f.MovePathElement2f(
+						this.lastX,  this.lastY);
+			}
+			else if (idx<5) {
+				float ctrls[] = CTRL_PTS[idx - 1];
+				float ix = this.lastX;
+				float iy = this.lastY;
+				this.lastX = (this.x1 + ctrls[4] * this.w);
+				this.lastY = (this.y1 + ctrls[5] * this.h);
+				return new PathElement2f.CurvePathElement2f(
+						ix,  iy,
+						(this.x1 + ctrls[0] * this.w),
+						(this.y1 + ctrls[1] * this.h),
+						(this.x1 + ctrls[2] * this.w),
+						(this.y1 + ctrls[3] * this.h),
+						this.lastX,
+						this.lastY);
+			}
+
+			return new PathElement2f.ClosePathElement2f(
+					this.lastX, this.lastY,
+					this.lastX, this.lastY);
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return PathWindingRule.NON_ZERO;
+		}
+		
+	}
+
+	/**
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public static class TransformPathIterator implements PathIterator2f {
+		
+		private final Point2D lastPoint = new Point2f();
+		private final Point2D ptmp1 = new Point2f();
+		private final Point2D ptmp2 = new Point2f();
+		private final Transform2D transform;
+		private final float x1;
+		private final float y1;
+		private final float w;
+		private final float h;
+		private int index;
+		
+		/**
+		 * @param x1
+		 * @param y1
+		 * @param x2
+		 * @param y2
+		 * @param transform
+		 */
+		public TransformPathIterator(float x1, float y1, float x2, float y2, Transform2D transform) {
+			this.transform = transform;
+			this.x1 = x1;
+			this.y1 = y1;
+			this.w = x2 - x1;
+			this.h = y2 - y1;
+			if (this.w==0f && this.h==0f) {
+				this.index = 6;
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.index<=5;
+		}
+
+		@Override
+		public PathElement2f next() {
+			if (this.index>5) throw new NoSuchElementException();
+			int idx = this.index;
+			++this.index;
+			
+			if (idx==0) {
+				float ctrls[] = CTRL_PTS[3];
+				this.lastPoint.set(
+						this.x1 + ctrls[4] * this.w,
+						this.y1 + ctrls[5] * this.h);
+				this.transform.transform(this.lastPoint);
+				return new PathElement2f.MovePathElement2f(
+						this.lastPoint.getX(), this.lastPoint.getY());
+			}
+			else if (idx<5) {
+				float ctrls[] = CTRL_PTS[idx - 1];
+				float ix = this.lastPoint.getX();
+				float iy = this.lastPoint.getY();
+				this.lastPoint.set(
+						(this.x1 + ctrls[4] * this.w),
+						(this.y1 + ctrls[5] * this.h));
+				this.transform.transform(this.lastPoint);
+				this.ptmp1.set(
+						(this.x1 + ctrls[0] * this.w),
+						(this.y1 + ctrls[1] * this.h));
+				this.transform.transform(this.ptmp1);
+				this.ptmp2.set(
+						(this.x1 + ctrls[2] * this.w),
+						(this.y1 + ctrls[3] * this.h));
+				this.transform.transform(this.ptmp2);
+				return new PathElement2f.CurvePathElement2f(
+						ix,  iy,
+						this.ptmp1.getX(), this.ptmp1.getY(),
+						this.ptmp2.getX(), this.ptmp2.getY(),
+						this.lastPoint.getX(), this.lastPoint.getY());
+			}
+
+			float ix = this.lastPoint.getX();
+			float iy = this.lastPoint.getY();
+			return new PathElement2f.ClosePathElement2f(
+					ix, iy,
+					ix, iy);
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return PathWindingRule.NON_ZERO;
+		}
+		
+	}
+	
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Path2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Path2f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Path2f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,2866 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import java.lang.ref.SoftReference;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.MathUtil;
+import org.arakhne.afc.math.generic.PathElementType;
+import org.arakhne.afc.math.generic.PathWindingRule;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+/** A generic path.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Path2f extends AbstractShape2f<Path2f> {
+
+	private static final long serialVersionUID = -873231223923726975L;
+
+	/** Multiple of cubic & quad curve size.
+	 */
+	static final int GROW_SIZE = 24;
+
+	/**
+	 * Tests if the specified coordinates are inside the closed
+	 * boundary of the specified {@link PathIterator2f}.
+	 * <p>
+	 * This method provides a basic facility for implementors of
+	 * the {@link Shape2f} interface to implement support for the
+	 * {@link Shape2f#contains(float, float)} method.
+	 *
+	 * @param pi the specified {@code PathIterator2f}
+	 * @param x the specified X coordinate
+	 * @param y the specified Y coordinate
+	 * @return {@code true} if the specified coordinates are inside the
+	 *         specified {@code PathIterator2f}; {@code false} otherwise
+	 */
+	public static boolean contains(PathIterator2f pi, float x, float y) {
+		// Copied from the AWT API
+		int mask = (pi.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 1);
+		int cross = computeCrossingsFromPoint(pi, x, y);
+		return ((cross & mask) != 0);
+	}
+
+	/**
+	 * Tests if the specified rectangle is inside the closed
+	 * boundary of the specified {@link PathIterator2f}.
+	 * <p>
+	 * This method provides a basic facility for implementors of
+	 * the {@link Shape2f} interface to implement support for the
+	 * {@link Shape2f#contains(Rectangle2f)} method.
+	 *
+	 * @param pi the specified {@code PathIterator2f}
+	 * @param rx the lowest corner of the rectangle.
+	 * @param ry the lowest corner of the rectangle.
+	 * @param rwidth is the width of the rectangle.
+	 * @param rheight is the width of the rectangle.
+	 * @return {@code true} if the specified rectangle is inside the
+	 *         specified {@code PathIterator2f}; {@code false} otherwise.
+	 */
+	public static boolean contains(PathIterator2f pi, float rx, float ry, float rwidth, float rheight) {
+		// Copied from AWT API
+        if (rwidth <= 0 || rheight <= 0) {
+            return false;
+        }
+        int mask = (pi.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 2);
+        int crossings = computeCrossingsFromRect(pi, rx, ry, rx+rwidth, ry+rheight);
+        return (crossings != MathConstants.SHAPE_INTERSECTS &&
+                (crossings & mask) != 0);
+	}
+
+	/**
+	 * Tests if the interior of the specified {@link PathIterator2f}
+	 * intersects the interior of a specified set of rectangular
+	 * coordinates.
+	 * <p>
+	 * This method provides a basic facility for implementors of
+	 * the {@link Shape2f} interface to implement support for the
+	 * {@code intersects()} method.
+	 * <p>
+	 * This method object may conservatively return true in
+	 * cases where the specified rectangular area intersects a
+	 * segment of the path, but that segment does not represent a
+	 * boundary between the interior and exterior of the path.
+	 * Such a case may occur if some set of segments of the
+	 * path are retraced in the reverse direction such that the
+	 * two sets of segments cancel each other out without any
+	 * interior area between them.
+	 * To determine whether segments represent true boundaries of
+	 * the interior of the path would require extensive calculations
+	 * involving all of the segments of the path and the winding
+	 * rule and are thus beyond the scope of this implementation.
+	 *
+	 * @param pi the specified {@code PathIterator}
+	 * @param x the specified X coordinate
+	 * @param y the specified Y coordinate
+	 * @param w the width of the specified rectangular coordinates
+	 * @param h the height of the specified rectangular coordinates
+	 * @return {@code true} if the specified {@code PathIterator} and
+	 *         the interior of the specified set of rectangular
+	 *         coordinates intersect each other; {@code false} otherwise.
+	 */
+	public static boolean intersects(PathIterator2f pi, float x, float y, float w, float h) {
+		if (w <= 0f || h <= 0f) {
+			return false;
+		}
+		int mask = (pi.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 2);
+		int crossings = computeCrossingsFromRect(pi, x, y, x+w, y+h);
+		return (crossings == MathConstants.SHAPE_INTERSECTS ||
+				(crossings & mask) != 0);
+	}
+
+	/**
+	 * Calculates the number of times the given path
+	 * crosses the ray extending to the right from (px,py).
+	 * If the point lies on a part of the path,
+	 * then no crossings are counted for that intersection.
+	 * +1 is added for each crossing where the Y coordinate is increasing
+	 * -1 is added for each crossing where the Y coordinate is decreasing
+	 * The return value is the sum of all crossings for every segment in
+	 * the path.
+	 * The path must start with a MOVE_TO, otherwise an exception is
+	 * thrown.
+	 * 
+	 * @param pi is the description of the path.
+	 * @param px is the reference point to test.
+	 * @param py is the reference point to test.
+	 * @return the crossing
+	 */
+	public static int computeCrossingsFromPoint(PathIterator2f pi, float px, float py) {
+		// Copied from the AWT API
+		if (!pi.hasNext()) return 0;
+		PathElement2f element;
+
+		element = pi.next();
+		if (element.type != PathElementType.MOVE_TO) {
+			throw new IllegalArgumentException("missing initial moveto in path definition"); //$NON-NLS-1$
+		}
+
+		float movx = element.toX;
+		float movy = element.toY;
+		float curx = movx;
+		float cury = movy;
+		float endx, endy;
+		int crossings = 0;
+		while (pi.hasNext()) {
+			element = pi.next();
+			switch (element.type) {
+			case MOVE_TO:
+				movx = curx = element.toX;
+				movy = cury = element.toY;
+				break;
+			case LINE_TO:
+				endx = element.toX;
+				endy = element.toY;
+				crossings += Segment2f.computeCrossingsFromPoint(
+						px, py,
+						curx, cury,
+						endx, endy);
+				curx = endx;
+				cury = endy;
+				break;
+			case QUAD_TO:
+				endx = element.toX;
+				endy = element.toY;
+				crossings += computeQuadCurveCrossingsFromPoint(
+						px, py,
+						curx, cury,
+						element.ctrlX1, element.ctrlY1,
+						endx, endy, 0);
+				curx = endx;
+				cury = endy;
+				break;
+			case CURVE_TO:
+				endx = element.toX;
+				endy = element.toY;
+				crossings += computeCubicCurveCrossingsFromPoint(
+						px, py,
+						curx, cury,
+						element.ctrlX1, element.ctrlY1,
+						element.ctrlX2, element.ctrlY2,
+						endx, endy, 0);
+				curx = endx;
+				cury = endy;
+				break;
+			case CLOSE:
+				if (cury != movy || curx != movx) {
+					crossings += Segment2f.computeCrossingsFromPoint(
+							px, py,
+							curx, cury,
+							movx, movy);
+				}
+				curx = movx;
+				cury = movy;
+				break;
+			default:
+			}
+		}
+
+		if (cury != movy) {
+			crossings += Segment2f.computeCrossingsFromPoint(
+					px, py,
+					curx, cury,
+					movx, movy);
+		}
+		return crossings;
+	}
+
+	/**
+	 * Calculates the number of times the given path
+	 * crosses the given ellipse extending to the right.
+	 * 
+	 * @param pi is the description of the path.
+	 * @param ex is the first point of the ellipse.
+	 * @param ey is the first point of the ellipse.
+	 * @param ew is the width of the ellipse.
+	 * @param eh is the height of the ellipse.
+	 * @return the crossing or {@link MathConstants#SHAPE_INTERSECTS}
+	 */
+	public static int computeCrossingsFromEllipse(PathIterator2f pi, float ex, float ey, float ew, float eh) {
+		return computeCrossingsFromEllipse(0, pi, ex, ey, ew, eh, true);
+	}
+	
+	/**
+	 * Calculates the number of times the given path
+	 * crosses the given ellipse extending to the right.
+	 * 
+	 * @param crossings is the initial value for crossing.
+	 * @param pi is the description of the path.
+	 * @param ex is the first point of the ellipse.
+	 * @param ey is the first point of the ellipse.
+	 * @param ew is the width of the ellipse.
+	 * @param eh is the height of the ellipse.
+	 * @param closeable indicates if the shape is automatically closed or not.
+	 * @return the crossing or {@link MathConstants#SHAPE_INTERSECTS}
+	 */
+	static int computeCrossingsFromEllipse(int crossings, PathIterator2f pi, float ex, float ey, float ew, float eh, boolean closeable) {	
+		// Copied from the AWT API
+		if (!pi.hasNext()) return 0;
+		PathElement2f element;
+
+		element = pi.next();
+		if (element.type != PathElementType.MOVE_TO) {
+			throw new IllegalArgumentException("missing initial moveto in path definition"); //$NON-NLS-1$
+		}
+
+		float movx = element.toX;
+		float movy = element.toY;
+		float curx = movx;
+		float cury = movy;
+		float endx, endy;
+		int numCrosses = crossings;
+		while (numCrosses!=MathConstants.SHAPE_INTERSECTS && pi.hasNext()) {
+			element = pi.next();
+			switch (element.type) {
+			case MOVE_TO:
+				movx = curx = element.toX;
+				movy = cury = element.toY;
+				break;
+			case LINE_TO:
+				endx = element.toX;
+				endy = element.toY;
+				numCrosses = Segment2f.computeCrossingsFromEllipse(
+						numCrosses,
+						ex, ey, ew, eh,
+						curx, cury,
+						endx, endy);
+				curx = endx;
+				cury = endy;
+				break;
+			case QUAD_TO:
+			{
+				endx = element.toX;
+				endy = element.toY;
+				Path2f localPath = new Path2f();
+				localPath.moveTo(element.fromX, element.fromY);
+				localPath.quadTo(
+						element.ctrlX1, element.ctrlY1,
+						endx, endy);
+				numCrosses = computeCrossingsFromEllipse(
+						numCrosses,
+						localPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						ex, ey, ew, eh,
+						false);
+				curx = endx;
+				cury = endy;
+				break;
+			}
+			case CURVE_TO:
+				endx = element.toX;
+				endy = element.toY;
+				Path2f localPath = new Path2f();
+				localPath.moveTo(element.fromX, element.fromY);
+				localPath.curveTo(
+						element.ctrlX1, element.ctrlY1,
+						element.ctrlX2, element.ctrlY2,
+						endx, endy);
+				numCrosses = computeCrossingsFromEllipse(
+						numCrosses,
+						localPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						ex, ey, ew, eh,
+						false);
+				curx = endx;
+				cury = endy;
+				break;
+			case CLOSE:
+				if (cury != movy || curx != movx) {
+					numCrosses = Segment2f.computeCrossingsFromEllipse(
+							numCrosses,
+							ex, ey, ew, eh,
+							curx, cury,
+							movx, movy);
+				}
+				curx = movx;
+				cury = movy;
+				break;
+			default:
+			}
+		}
+
+		if (numCrosses!=MathConstants.SHAPE_INTERSECTS && closeable && cury != movy) {
+			numCrosses = Segment2f.computeCrossingsFromEllipse(
+					numCrosses,
+					ex, ey, ew, eh,
+					curx, cury,
+					movx, movy);
+		}
+		
+		return numCrosses;
+	}
+
+	/**
+	 * Calculates the number of times the given path
+	 * crosses the given ellipse extending to the right.
+	 * 
+	 * @param pi is the description of the path.
+	 * @param cx is the center of the circle.
+	 * @param cy is the center of the circle.
+	 * @param radius is the radius of the circle.
+	 * @return the crossing or {@link MathConstants#SHAPE_INTERSECTS}.
+	 */
+	public static int computeCrossingsFromCircle(PathIterator2f pi, float cx, float cy, float radius) {
+		return computeCrossingsFromCircle(0, pi, cx, cy, radius, true);
+	}
+	
+	/**
+	 * Calculates the number of times the given path
+	 * crosses the given circle extending to the right.
+	 * 
+	 * @param crossings is the initial value for crossing.
+	 * @param pi is the description of the path.
+	 * @param cx is the center of the circle.
+	 * @param cy is the center of the circle.
+	 * @param radius is the radius of the circle.
+	 * @param closeable indicates if the shape is automatically closed or not.
+	 * @return the crossing
+	 */
+	static int computeCrossingsFromCircle(int crossings, PathIterator2f pi, float cx, float cy, float radius, boolean closeable) {	
+		// Copied from the AWT API
+		if (!pi.hasNext()) return 0;
+		PathElement2f element;
+
+		element = pi.next();
+		if (element.type != PathElementType.MOVE_TO) {
+			throw new IllegalArgumentException("missing initial moveto in path definition"); //$NON-NLS-1$
+		}
+
+		float movx = element.toX;
+		float movy = element.toY;
+		float curx = movx;
+		float cury = movy;
+		float endx, endy;
+		int numCrosses = crossings;
+		while (numCrosses!=MathConstants.SHAPE_INTERSECTS && pi.hasNext()) {
+			element = pi.next();
+			switch (element.type) {
+			case MOVE_TO:
+				movx = curx = element.toX;
+				movy = cury = element.toY;
+				break;
+			case LINE_TO:
+				endx = element.toX;
+				endy = element.toY;
+				numCrosses = Segment2f.computeCrossingsFromCircle(
+						numCrosses,
+						cx, cy, radius,
+						curx, cury,
+						endx, endy);
+				curx = endx;
+				cury = endy;
+				break;
+			case QUAD_TO:
+			{
+				endx = element.toX;
+				endy = element.toY;
+				Path2f localPath = new Path2f();
+				localPath.moveTo(element.fromX, element.fromY);
+				localPath.quadTo(
+						element.ctrlX1, element.ctrlY1,
+						endx, endy);
+				numCrosses = computeCrossingsFromCircle(
+						numCrosses,
+						localPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						cx, cy, radius,
+						false);
+				curx = endx;
+				cury = endy;
+				break;
+			}
+			case CURVE_TO:
+				endx = element.toX;
+				endy = element.toY;
+				Path2f localPath = new Path2f();
+				localPath.moveTo(element.fromX, element.fromY);
+				localPath.curveTo(
+						element.ctrlX1, element.ctrlY1,
+						element.ctrlX2, element.ctrlY2,
+						endx, endy);
+				numCrosses = computeCrossingsFromCircle(
+						numCrosses,
+						localPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						cx, cy, radius,
+						false);
+				curx = endx;
+				cury = endy;
+				break;
+			case CLOSE:
+				if (cury != movy || curx != movx) {
+					numCrosses = Segment2f.computeCrossingsFromCircle(
+							numCrosses,
+							cx, cy, radius,
+							curx, cury,
+							movx, movy);
+				}
+				curx = movx;
+				cury = movy;
+				break;
+			default:
+			}
+		}
+
+		if (numCrosses!=MathConstants.SHAPE_INTERSECTS && closeable && cury != movy) {
+			numCrosses = Segment2f.computeCrossingsFromCircle(
+					numCrosses,
+					cx, cy, radius,
+					curx, cury,
+					movx, movy);
+		}
+		
+		return numCrosses;
+	}
+
+	/**
+	 * Calculates the number of times the given path
+	 * crosses the given segment extending to the right.
+	 * 
+	 * @param pi is the description of the path.
+	 * @param x1 is the first point of the segment.
+	 * @param y1 is the first point of the segment.
+	 * @param x2 is the first point of the segment.
+	 * @param y2 is the first point of the segment.
+	 * @return the crossing or {@link MathConstants#SHAPE_INTERSECTS}.
+	 */
+	public static int computeCrossingsFromSegment(PathIterator2f pi, float x1, float y1, float x2, float y2) {
+		return computeCrossingsFromSegment(0, pi, x1, y1, x2, y2, true);
+	}
+	
+	/**
+	 * Calculates the number of times the given path
+	 * crosses the given circle extending to the right.
+	 * 
+	 * @param crossings is the initial value for crossing.
+	 * @param pi is the description of the path.
+	 * @param x1 is the first point of the segment.
+	 * @param y1 is the first point of the segment.
+	 * @param x2 is the first point of the segment.
+	 * @param y2 is the first point of the segment.
+	 * @param closeable indicates if the shape is automatically closed or not.
+	 * @return the crossing
+	 */
+	static int computeCrossingsFromSegment(int crossings, PathIterator2f pi, float x1, float y1, float x2, float y2, boolean closeable) {	
+		// Copied from the AWT API
+		if (!pi.hasNext()) return 0;
+		PathElement2f element;
+
+		element = pi.next();
+		if (element.type != PathElementType.MOVE_TO) {
+			throw new IllegalArgumentException("missing initial moveto in path definition"); //$NON-NLS-1$
+		}
+
+		float movx = element.toX;
+		float movy = element.toY;
+		float curx = movx;
+		float cury = movy;
+		float endx, endy;
+		int numCrosses = crossings;
+		while (numCrosses!=MathConstants.SHAPE_INTERSECTS && pi.hasNext()) {
+			element = pi.next();
+			switch (element.type) {
+			case MOVE_TO:
+				movx = curx = element.toX;
+				movy = cury = element.toY;
+				break;
+			case LINE_TO:
+				endx = element.toX;
+				endy = element.toY;
+				numCrosses = Segment2f.computeCrossingsFromSegment(
+						numCrosses,
+						x1, y1, x2, y2,
+						curx, cury,
+						endx, endy);
+				curx = endx;
+				cury = endy;
+				break;
+			case QUAD_TO:
+			{
+				endx = element.toX;
+				endy = element.toY;
+				Path2f localPath = new Path2f();
+				localPath.moveTo(element.fromX, element.fromY);
+				localPath.quadTo(
+						element.ctrlX1, element.ctrlY1,
+						endx, endy);
+				numCrosses = computeCrossingsFromSegment(
+						numCrosses,
+						localPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						x1, y1, x2, y2,
+						false);
+				curx = endx;
+				cury = endy;
+				break;
+			}
+			case CURVE_TO:
+				endx = element.toX;
+				endy = element.toY;
+				Path2f localPath = new Path2f();
+				localPath.moveTo(element.fromX, element.fromY);
+				localPath.curveTo(
+						element.ctrlX1, element.ctrlY1,
+						element.ctrlX2, element.ctrlY2,
+						endx, endy);
+				numCrosses = computeCrossingsFromSegment(
+						numCrosses,
+						localPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						x1, y1, x2, y2,
+						false);
+				curx = endx;
+				cury = endy;
+				break;
+			case CLOSE:
+				if (cury != movy || curx != movx) {
+					numCrosses = Segment2f.computeCrossingsFromSegment(
+							numCrosses,
+							x1, y1, x2, y2,
+							curx, cury,
+							movx, movy);
+				}
+				curx = movx;
+				cury = movy;
+				break;
+			default:
+			}
+		}
+
+		if (numCrosses!=MathConstants.SHAPE_INTERSECTS && closeable && cury != movy) {
+			numCrosses = Segment2f.computeCrossingsFromSegment(
+					numCrosses,
+					x1, y1, x2, y2,
+					curx, cury,
+					movx, movy);
+		}
+		
+		return numCrosses;
+	}
+
+	/**
+	 * Calculates the number of times the quad from (x0,y0) to (x1,y1)
+	 * crosses the ray extending to the right from (px,py).
+	 * If the point lies on a part of the curve,
+	 * then no crossings are counted for that intersection.
+	 * the level parameter should be 0 at the top-level call and will count
+	 * up for each recursion level to prevent infinite recursion
+	 * +1 is added for each crossing where the Y coordinate is increasing
+	 * -1 is added for each crossing where the Y coordinate is decreasing
+	 * 
+	 * @param px is the reference point to test.
+	 * @param py is the reference point to test.
+	 * @param x0 is the first point of the quad curve.
+	 * @param y0 is the first point of the quad curve.
+	 * @param xc is the control point of the quad curve.
+	 * @param yc is the control point of the quad curve.
+	 * @param x1 is the last point of the quad curve.
+	 * @param y1 is the last point of the quad curve.
+	 * @param level may be zero.
+	 * @return the crossing.
+	 */
+	static int computeQuadCurveCrossingsFromPoint(float px, float py,
+			float x0, float y0,
+			float xc, float yc,
+			float x1, float y1,
+			int level) {
+		// Copied from AWT API
+		if (py <  y0 && py <  yc && py <  y1) return 0;
+		if (py >= y0 && py >= yc && py >= y1) return 0;
+		// Note y0 could equal y1...
+		if (px >= x0 && px >= xc && px >= x1) return 0;
+		if (px <  x0 && px <  xc && px <  x1) {
+			if (py >= y0) {
+				if (py < y1) return 1;
+			} else {
+				// py < y0
+				if (py >= y1) return -1;
+			}
+			// py outside of y01 range, and/or y0==y1
+			return 0;
+		}
+		// float precision only has 52 bits of mantissa
+		if (level > 52) return Segment2f.computeCrossingsFromPoint(px, py, x0, y0, x1, y1);
+		float x0c = (x0 + xc) / 2;
+		float y0c = (y0 + yc) / 2;
+		float xc1 = (xc + x1) / 2;
+		float yc1 = (yc + y1) / 2;
+		float nxc = (x0c + xc1) / 2;
+		float nyc = (y0c + yc1) / 2;
+		if (Double.isNaN(nxc) || Double.isNaN(nyc)) {
+			// [xy]c are NaN if any of [xy]0c or [xy]c1 are NaN
+			// [xy]0c or [xy]c1 are NaN if any of [xy][0c1] are NaN
+			// These values are also NaN if opposing infinities are added
+			return 0;
+		}
+		return (computeQuadCurveCrossingsFromPoint(px, py,
+				x0, y0, x0c, y0c, nxc, nyc,
+				level+1) +
+				computeQuadCurveCrossingsFromPoint(px, py,
+						nxc, nyc, xc1, yc1, x1, y1,
+						level+1));
+	}
+
+	/**
+	 * Calculates the number of times the cubic from (x0,y0) to (x1,y1)
+	 * crosses the ray extending to the right from (px,py).
+	 * If the point lies on a part of the curve,
+	 * then no crossings are counted for that intersection.
+	 * the level parameter should be 0 at the top-level call and will count
+	 * up for each recursion level to prevent infinite recursion
+	 * +1 is added for each crossing where the Y coordinate is increasing
+	 * 
+	 * @param px is the reference point to test.
+	 * @param py is the reference point to test.
+	 * @param x0 is the first point of the quad curve.
+	 * @param y0 is the first point of the quad curve.
+	 * @param xc0 is the first control point of the cubic curve.
+	 * @param yc0 is the first control point of the cubic curve.
+	 * @param xc1 is the second control point of the cubic curve.
+	 * @param yc1 is the second control point of the cubic curve.
+	 * @param x1 is the last point of the quad curve.
+	 * @param y1 is the last point of the quad curve.
+	 * @param level may be zero.
+	 * @return the crossing.
+	 * -1 is added for each crossing where the Y coordinate is decreasing
+	 */
+	static int computeCubicCurveCrossingsFromPoint(
+			float px, float py,
+			float x0, float y0,
+			float xc0, float yc0,
+			float xc1, float yc1,
+			float x1, float y1,
+			int level) {
+		// Copied from AWT API
+		if (py <  y0 && py <  yc0 && py <  yc1 && py <  y1) return 0;
+		if (py >= y0 && py >= yc0 && py >= yc1 && py >= y1) return 0;
+		// Note y0 could equal yc0...
+		if (px >= x0 && px >= xc0 && px >= xc1 && px >= x1) return 0;
+		if (px <  x0 && px <  xc0 && px <  xc1 && px <  x1) {
+			if (py >= y0) {
+				if (py < y1) return 1;
+			} else {
+				// py < y0
+				if (py >= y1) return -1;
+			}
+			// py outside of y01 range, and/or y0==yc0
+			return 0;
+		}
+		// float precision only has 52 bits of mantissa
+		if (level > 52) return Segment2f.computeCrossingsFromPoint(px, py, x0, y0, x1, y1);
+		float xmid = (xc0 + xc1) / 2;
+		float ymid = (yc0 + yc1) / 2;
+		float nxc0 = (x0 + xc0) / 2;
+		float nyc0 = (y0 + yc0) / 2;
+		float nxc1 = (xc1 + x1) / 2;
+		float nyc1 = (yc1 + y1) / 2;
+		float xc0m = (nxc0 + xmid) / 2;
+		float yc0m = (nyc0 + ymid) / 2;
+		float xmc1 = (xmid + nxc1) / 2;
+		float ymc1 = (ymid + nyc1) / 2;
+		xmid = (xc0m + xmc1) / 2;
+		ymid = (yc0m + ymc1) / 2;
+		if (Double.isNaN(xmid) || Double.isNaN(ymid)) {
+			// [xy]mid are NaN if any of [xy]c0m or [xy]mc1 are NaN
+			// [xy]c0m or [xy]mc1 are NaN if any of [xy][c][01] are NaN
+			// These values are also NaN if opposing infinities are added
+			return 0;
+		}
+		return (computeCubicCurveCrossingsFromPoint(px, py,
+				x0, y0, nxc0, nyc0,
+				xc0m, yc0m, xmid, ymid, level+1) +
+				computeCubicCurveCrossingsFromPoint(px, py,
+						xmid, ymid, xmc1, ymc1,
+						nxc1, nyc1, x1, y1, level+1));
+	}
+
+	/**
+	 * Accumulate the number of times the path crosses the shadow
+	 * extending to the right of the rectangle.  See the comment
+	 * for the RECT_INTERSECTS constant for more complete details.
+	 * The return value is the sum of all crossings for both the
+	 * top and bottom of the shadow for every segment in the path,
+	 * or the special value RECT_INTERSECTS if the path ever enters
+	 * the interior of the rectangle.
+	 * The path must start with a SEG_MOVETO, otherwise an exception is
+	 * thrown.
+	 * The caller must check r[xy]{min,max} for NaN values.
+	 * 
+	 * @param pi is the iterator on the path elements.
+	 * @param rxmin is the first corner of the rectangle.
+	 * @param rymin is the first corner of the rectangle.
+	 * @param rxmax is the second corner of the rectangle.
+	 * @param rymax is the second corner of the rectangle.
+	 * @return the crossings.
+	 */
+	public static int computeCrossingsFromRect(PathIterator2f pi,
+			float rxmin, float rymin,
+			float rxmax, float rymax) {
+		// Copied from AWT API
+		if (rxmax <= rxmin || rymax <= rymin) return 0;
+		if (!pi.hasNext()) return 0;
+
+		PathElement2f pathElement = pi.next();
+
+		if (pathElement.type != PathElementType.MOVE_TO) {
+			throw new IllegalArgumentException("missing initial moveto in path definition"); //$NON-NLS-1$
+		}
+
+		float curx, cury, movx, movy, endx, endy;
+		curx = movx = pathElement.toX;
+		cury = movy = pathElement.toY;
+		int crossings = 0;
+
+		while (crossings != MathConstants.SHAPE_INTERSECTS
+				&& pi.hasNext()) {
+			pathElement = pi.next();
+			switch (pathElement.type) {
+			case MOVE_TO:
+				// Count should always be a multiple of 2 here.
+				// assert((crossings & 1) != 0);
+				movx = curx = pathElement.toX;
+				movy = cury = pathElement.toY;
+				break;
+			case LINE_TO:
+				endx = pathElement.toX;
+				endy = pathElement.toY;
+				crossings = Segment2f.computeCrossingsFromRect(crossings,
+						rxmin, rymin,
+						rxmax, rymax,
+						curx, cury,
+						endx, endy);
+				curx = endx;
+				cury = endy;
+				break;
+			case QUAD_TO:
+				endx = pathElement.toX;
+				endy = pathElement.toY;
+				crossings = computeQuadCurveCrossingsFromRect(crossings,
+						rxmin, rymin,
+						rxmax, rymax,
+						curx, cury,
+						pathElement.ctrlX1, pathElement.ctrlY1,
+						endx, endy, 0);
+				curx = endx;
+				cury = endy;
+				break;
+			case CURVE_TO:
+				endx = pathElement.toX;
+				endy = pathElement.toY;
+				crossings = computeCubicCurveCrossingsFromRect(crossings,
+						rxmin, rymin,
+						rxmax, rymax,
+						curx, cury,
+						pathElement.ctrlX1, pathElement.ctrlY1,
+						pathElement.ctrlX2, pathElement.ctrlY2,
+						endx, endy, 0);
+				curx = endx;
+				cury = endy;
+				break;
+			case CLOSE:
+				if (curx != movx || cury != movy) {
+					crossings = Segment2f.computeCrossingsFromRect(crossings,
+							rxmin, rymin,
+							rxmax, rymax,
+							curx, cury,
+							movx, movy);
+				}
+				curx = movx;
+				cury = movy;
+				// Count should always be a multiple of 2 here.
+				// assert((crossings & 1) != 0);
+				break;
+			default:
+			}
+		}
+
+		if (crossings != MathConstants.SHAPE_INTERSECTS && (curx != movx || cury != movy)) {
+			crossings = Segment2f.computeCrossingsFromRect(crossings,
+					rxmin, rymin,
+					rxmax, rymax,
+					curx, cury,
+					movx, movy);
+		}
+
+		// Count should always be a multiple of 2 here.
+		// assert((crossings & 1) != 0);
+		return crossings;
+	}
+
+	/**
+	 * Accumulate the number of times the quad crosses the shadow
+	 * extending to the right of the rectangle.  See the comment
+	 * for the RECT_INTERSECTS constant for more complete details.
+	 * 
+	 * @param crossings is the initial value for the crossings.
+	 * @param rxmin is the first corner of the rectangle.
+	 * @param rymin is the first corner of the rectangle.
+	 * @param rxmax is the second corner of the rectangle.
+	 * @param rymax is the second corner of the rectangle.
+	 * @param x0 is the first point of the curve.
+	 * @param y0 is the first point of the curve.
+	 * @param xc is the control point of the curve.
+	 * @param yc is the control point of the curve.
+	 * @param x1 is the last point of the curve.
+	 * @param y1 is the last point of the curve.
+	 * @param level is the recursion level. It may be equal to {@code 0} for the intiial call.
+	 * @return the crossings.
+	 */
+	static int computeQuadCurveCrossingsFromRect(
+			int crossings,
+			float rxmin, float rymin,
+			float rxmax, float rymax,
+			float x0, float y0,
+			float xc, float yc,
+			float x1, float y1,
+			int level) {
+		// Copied from AWT API
+		float ctrlx = xc;
+		float ctrly = yc;
+		int numCrosses = crossings;
+
+		if (y0 >= rymax && ctrly >= rymax && y1 >= rymax) return numCrosses;
+		if (y0 <= rymin && ctrly <= rymin && y1 <= rymin) return numCrosses;
+		if (x0 <= rxmin && ctrlx <= rxmin && x1 <= rxmin) return numCrosses;
+		if (x0 >= rxmax && ctrlx >= rxmax && x1 >= rxmax) {
+			// Quad is entirely to the right of the rect
+			// and the vertical range of the 3 Y coordinates of the quad
+			// overlaps the vertical range of the rect by a non-empty amount
+			// We now judge the crossings solely based on the line segment
+			// connecting the endpoints of the quad.
+			// Note that we may have 0, 1, or 2 crossings as the control
+			// point may be causing the Y range intersection while the
+			// two endpoints are entirely above or below.
+			if (y0 < y1) {
+				// y-increasing line segment...
+				if (y0 <= rymin && y1 >  rymin) ++numCrosses;
+				if (y0 <  rymax && y1 >= rymax) ++numCrosses;
+			}
+			else if (y1 < y0) {
+				// y-decreasing line segment...
+				if (y1 <= rymin && y0 >  rymin) --numCrosses;
+				if (y1 <  rymax && y0 >= rymax) --numCrosses;
+			}
+			return numCrosses;
+		}
+
+		// The intersection of ranges is more complicated
+		// First do trivial INTERSECTS rejection of the cases
+		// where one of the endpoints is inside the rectangle.
+		if ((x0 < rxmax && x0 > rxmin && y0 < rymax && y0 > rymin) ||
+				(x1 < rxmax && x1 > rxmin && y1 < rymax && y1 > rymin)) {
+			return MathConstants.SHAPE_INTERSECTS;
+		}
+		// Otherwise, subdivide and look for one of the cases above.
+		// float precision only has 52 bits of mantissa
+		if (level > 52) {
+			return Segment2f.computeCrossingsFromRect(numCrosses,
+					rxmin, rymin, rxmax, rymax,
+					x0, y0, x1, y1);
+		}
+		float x0c = (x0 + ctrlx) / 2;
+		float y0c = (y0 + ctrly) / 2;
+		float xc1 = (ctrlx + x1) / 2;
+		float yc1 = (ctrly + y1) / 2;
+		ctrlx = (x0c + xc1) / 2;
+		ctrly = (y0c + yc1) / 2;
+		if (Double.isNaN(ctrlx) || Double.isNaN(ctrly)) {
+			// [xy]c are NaN if any of [xy]0c or [xy]c1 are NaN
+			// [xy]0c or [xy]c1 are NaN if any of [xy][0c1] are NaN
+			// These values are also NaN if opposing infinities are added
+			return 0;
+		}
+		numCrosses = computeQuadCurveCrossingsFromRect(numCrosses,
+				rxmin, rymin, rxmax, rymax,
+				x0, y0, x0c, y0c, ctrlx, ctrly,
+				level+1);
+		if (numCrosses != MathConstants.SHAPE_INTERSECTS) {
+			numCrosses = computeQuadCurveCrossingsFromRect(numCrosses,
+					rxmin, rymin, rxmax, rymax,
+					ctrlx, ctrly, xc1, yc1, x1, y1,
+					level+1);
+		}
+		return numCrosses;
+	}
+
+	/**
+	 * Accumulate the number of times the cubic crosses the shadow
+	 * extending to the right of the rectangle.  See the comment
+	 * for the RECT_INTERSECTS constant for more complete details.
+	 * 
+	 * @param crossings is the initial value for the crossings.
+	 * @param rxmin is the first corner of the rectangle.
+	 * @param rymin is the first corner of the rectangle.
+	 * @param rxmax is the second corner of the rectangle.
+	 * @param rymax is the second corner of the rectangle.
+	 * @param x0 is the first point of the curve.
+	 * @param y0 is the first point of the curve.
+	 * @param xc0 is the first control point of the curve.
+	 * @param yc0 is the first control point of the curve.
+	 * @param xc1 is the second control point of the curve.
+	 * @param yc1 is the second control point of the curve.
+	 * @param x1 is the last point of the curve.
+	 * @param y1 is the last point of the curve.
+	 * @param level is the recursion level. It may be equal to {@code 0} for the intiial call.
+	 * @return the crossings.
+	 */
+	static int computeCubicCurveCrossingsFromRect(
+			int crossings,
+			float rxmin, float rymin,
+			float rxmax, float rymax,
+			float x0,  float y0,
+			float xc0, float yc0,
+			float xc1, float yc1,
+			float x1,  float y1,
+			int level) {
+		// Copied from AWT API
+		float ctrlx0 = xc0;
+		float ctrly0 = yc0;
+		float ctrlx1 = xc1;
+		float ctrly1 = yc1;
+		int numCrosses = crossings;
+
+		if (y0 >= rymax && ctrly0 >= rymax && ctrly1 >= rymax && y1 >= rymax) {
+			return numCrosses;
+		}
+		if (y0 <= rymin && ctrly0 <= rymin && ctrly1 <= rymin && y1 <= rymin) {
+			return numCrosses;
+		}
+		if (x0 <= rxmin && ctrlx0 <= rxmin && ctrlx1 <= rxmin && x1 <= rxmin) {
+			return numCrosses;
+		}
+		if (x0 >= rxmax && ctrlx0 >= rxmax && ctrlx1 >= rxmax && x1 >= rxmax) {
+			// Cubic is entirely to the right of the rect
+			// and the vertical range of the 4 Y coordinates of the cubic
+			// overlaps the vertical range of the rect by a non-empty amount
+			// We now judge the crossings solely based on the line segment
+			// connecting the endpoints of the cubic.
+			// Note that we may have 0, 1, or 2 crossings as the control
+			// points may be causing the Y range intersection while the
+			// two endpoints are entirely above or below.
+			if (y0 < y1) {
+				// y-increasing line segment...
+				if (y0 <= rymin && y1 >  rymin) ++numCrosses;
+				if (y0 <  rymax && y1 >= rymax) ++numCrosses;
+			}
+			else if (y1 < y0) {
+				// y-decreasing line segment...
+				if (y1 <= rymin && y0 >  rymin) --numCrosses;
+				if (y1 <  rymax && y0 >= rymax) --numCrosses;
+			}
+			return numCrosses;
+		}
+		// The intersection of ranges is more complicated
+		// First do trivial INTERSECTS rejection of the cases
+		// where one of the endpoints is inside the rectangle.
+		if ((x0 > rxmin && x0 < rxmax && y0 > rymin && y0 < rymax) ||
+				(x1 > rxmin && x1 < rxmax && y1 > rymin && y1 < rymax)) {
+			return MathConstants.SHAPE_INTERSECTS;
+		}
+		// Otherwise, subdivide and look for one of the cases above.
+		// float precision only has 52 bits of mantissa
+		if (level > 52) {
+			return Segment2f.computeCrossingsFromRect(numCrosses,
+					rxmin, rymin, rxmax, rymax,
+					x0, y0, x1, y1);
+		}
+		float xmid = (ctrlx0 + ctrlx1) / 2;
+		float ymid = (ctrly0 + ctrly1) / 2;
+		ctrlx0 = (x0 + ctrlx0) / 2;
+		ctrly0 = (y0 + ctrly0) / 2;
+		ctrlx1 = (ctrlx1 + x1) / 2;
+		ctrly1 = (ctrly1 + y1) / 2;
+		float xc0m = (ctrlx0 + xmid) / 2;
+		float yc0m = (ctrly0 + ymid) / 2;
+		float xmc1 = (xmid + ctrlx1) / 2;
+		float ymc1 = (ymid + ctrly1) / 2;
+		xmid = (xc0m + xmc1) / 2;
+		ymid = (yc0m + ymc1) / 2;
+		if (Double.isNaN(xmid) || Double.isNaN(ymid)) {
+			// [xy]mid are NaN if any of [xy]c0m or [xy]mc1 are NaN
+			// [xy]c0m or [xy]mc1 are NaN if any of [xy][c][01] are NaN
+			// These values are also NaN if opposing infinities are added
+			return 0;
+		}
+		numCrosses = computeCubicCurveCrossingsFromRect(numCrosses,
+				rxmin, rymin, rxmax, rymax,
+				x0, y0, ctrlx0, ctrly0,
+				xc0m, yc0m, xmid, ymid, level+1);
+		if (numCrosses != MathConstants.SHAPE_INTERSECTS) {
+			numCrosses = computeCubicCurveCrossingsFromRect(numCrosses,
+					rxmin, rymin, rxmax, rymax,
+					xmid, ymid, xmc1, ymc1,
+					ctrlx1, ctrly1, x1, y1, level+1);
+		}
+		return crossings;
+	}
+
+	/** Array of types.
+	 */
+	PathElementType[] types;
+
+	/** Array of coords.
+	 */
+	float[] coords;
+
+	/** Number of types in the array.
+	 */
+	int numTypes = 0;
+
+	/** Number of coords in the array.
+	 */
+	int numCoords = 0;
+
+	/** Winding rule for the path.
+	 */
+	PathWindingRule windingRule;
+	
+	/** Indicates if the path is empty.
+	 * The path is empty when there is no point inside, or
+	 * all the points are at the same coordinate, or
+	 * when the path does not represents a drawable path
+	 * (a path with a line or a curve).
+	 */
+	private Boolean isEmpty = Boolean.TRUE;
+	
+	/** Buffer for the bounds of the path.
+	 */
+	private SoftReference<Rectangle2f> bounds = null;
+
+	/**
+	 */
+	public Path2f() {
+		this(PathWindingRule.NON_ZERO);
+	}
+
+	/**
+	 * @param iterator
+	 */
+	public Path2f(Iterator<PathElement2f> iterator) {
+		this(PathWindingRule.NON_ZERO, iterator);
+	}
+
+	/**
+	 * @param windingRule
+	 */
+	public Path2f(PathWindingRule windingRule) {
+		assert(windingRule!=null);
+		this.types = new PathElementType[GROW_SIZE];
+		this.coords = new float[GROW_SIZE];
+		this.windingRule = windingRule;
+	}
+
+	/**
+	 * @param windingRule
+	 * @param iterator
+	 */
+	public Path2f(PathWindingRule windingRule, Iterator<PathElement2f> iterator) {
+		assert(windingRule!=null);
+		this.types = new PathElementType[GROW_SIZE];
+		this.coords = new float[GROW_SIZE];
+		this.windingRule = windingRule;
+		add(iterator);
+	}
+	
+	@Override
+	public void clear() {
+		this.types = new PathElementType[GROW_SIZE];
+		this.coords = new float[GROW_SIZE];
+		this.windingRule = PathWindingRule.NON_ZERO;
+		this.numCoords = 0;
+		this.numTypes = 0;
+		this.isEmpty = true;
+		this.bounds = null;
+	}
+	
+	@Override
+	public String toString() {
+		StringBuilder b = new StringBuilder();
+		b.append("["); //$NON-NLS-1$
+		if (this.numCoords>0) {
+			b.append(this.coords[0]);
+			for(int i=1; i<this.numCoords; ++i) {
+				b.append(", "); //$NON-NLS-1$
+				b.append(this.coords[i]);
+			}
+		}
+		b.append("]"); //$NON-NLS-1$
+		return b.toString();
+	}
+
+	@Override
+	public Path2f clone() {
+		Path2f clone = super.clone();
+		clone.coords = this.coords.clone();
+		clone.types = this.types.clone();
+		return clone;
+	}
+
+	/** Replies the winding rule for the path.
+	 * 
+	 * @return the winding rule for the path.
+	 */
+	public PathWindingRule getWindingRule() {
+		return this.windingRule;
+	}
+
+	/** Set the winding rule for the path.
+	 * 
+	 * @param r is the winding rule for the path.
+	 */
+	public void setWindingRule(PathWindingRule r) {
+		assert(r!=null);
+		this.windingRule = r;
+	}
+
+	/** Add the elements replied by the iterator into this path.
+	 * 
+	 * @param iterator
+	 */
+	public void add(Iterator<PathElement2f> iterator) {
+		PathElement2f element;
+		while (iterator.hasNext()) {
+			element = iterator.next();
+			switch(element.type) {
+			case MOVE_TO:
+				moveTo(element.toX, element.toY);
+				break;
+			case LINE_TO:
+				lineTo(element.toX, element.toY);
+				break;
+			case QUAD_TO:
+				quadTo(element.ctrlX1, element.ctrlY1, element.toX, element.toY);
+				break;
+			case CURVE_TO:
+				curveTo(element.ctrlX1, element.ctrlY1, element.ctrlX2, element.ctrlY2, element.toX, element.toY);
+				break;
+			case CLOSE:
+				closePath();
+				break;
+			default:
+			}
+		}
+	}
+
+	private void ensureSlots(boolean needMove, int n) {
+		if (needMove && this.numTypes==0) {
+			throw new IllegalStateException("missing initial moveto in path definition"); //$NON-NLS-1$
+		}
+		if (this.types.length==this.numTypes) {
+			this.types = Arrays.copyOf(this.types, this.types.length+GROW_SIZE);
+		}
+		while ((this.numCoords+n)>=this.coords.length) {
+			this.coords = Arrays.copyOf(this.coords, this.coords.length+GROW_SIZE);
+		}
+	}
+
+	/**
+	 * Adds a point to the path by moving to the specified
+	 * coordinates specified in float precision.
+	 *
+	 * @param x the specified X coordinate
+	 * @param y the specified Y coordinate
+	 */
+	public void moveTo(float x, float y) {
+		if (this.numTypes>0 && this.types[this.numTypes-1]==PathElementType.MOVE_TO) {
+			this.coords[this.numCoords-2] = x;
+			this.coords[this.numCoords-1] = y;
+		}
+		else {
+			ensureSlots(false, 2);
+			this.types[this.numTypes++] = PathElementType.MOVE_TO;
+			this.coords[this.numCoords++] = x;
+			this.coords[this.numCoords++] = y;
+		}
+		this.bounds = null;
+	}
+
+	/**
+	 * Adds a point to the path by drawing a straight line from the
+	 * current coordinates to the new specified coordinates
+	 * specified in float precision.
+	 *
+	 * @param x the specified X coordinate
+	 * @param y the specified Y coordinate
+	 */
+	public void lineTo(float x, float y) {
+		ensureSlots(true, 2);
+		this.types[this.numTypes++] = PathElementType.LINE_TO;
+		this.coords[this.numCoords++] = x;
+		this.coords[this.numCoords++] = y;
+		this.isEmpty = null;
+		this.bounds = null;
+	}
+
+	/**
+	 * Adds a curved segment, defined by two new points, to the path by
+	 * drawing a Quadratic curve that intersects both the current
+	 * coordinates and the specified coordinates {@code (x2,y2)},
+	 * using the specified point {@code (x1,y1)} as a quadratic
+	 * parametric control point.
+	 * All coordinates are specified in float precision.
+	 *
+	 * @param x1 the X coordinate of the quadratic control point
+	 * @param y1 the Y coordinate of the quadratic control point
+	 * @param x2 the X coordinate of the final end point
+	 * @param y2 the Y coordinate of the final end point
+	 */
+	public void quadTo(float x1, float y1, float x2, float y2) {
+		ensureSlots(true, 4);
+		this.types[this.numTypes++] = PathElementType.QUAD_TO;
+		this.coords[this.numCoords++] = x1;
+		this.coords[this.numCoords++] = y1;
+		this.coords[this.numCoords++] = x2;
+		this.coords[this.numCoords++] = y2;
+		this.isEmpty = null;
+		this.bounds = null;
+	}
+
+	/**
+	 * Adds a curved segment, defined by three new points, to the path by
+	 * drawing a B&eacute;zier curve that intersects both the current
+	 * coordinates and the specified coordinates {@code (x3,y3)},
+	 * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as
+	 * B&eacute;zier control points.
+	 * All coordinates are specified in float precision.
+	 *
+	 * @param x1 the X coordinate of the first B&eacute;zier control point
+	 * @param y1 the Y coordinate of the first B&eacute;zier control point
+	 * @param x2 the X coordinate of the second B&eacute;zier control point
+	 * @param y2 the Y coordinate of the second B&eacute;zier control point
+	 * @param x3 the X coordinate of the final end point
+	 * @param y3 the Y coordinate of the final end point
+	 */
+	public void curveTo(float x1, float y1,
+			float x2, float y2,
+			float x3, float y3) {
+		ensureSlots(true, 6);
+		this.types[this.numTypes++] = PathElementType.CURVE_TO;
+		this.coords[this.numCoords++] = x1;
+		this.coords[this.numCoords++] = y1;
+		this.coords[this.numCoords++] = x2;
+		this.coords[this.numCoords++] = y2;
+		this.coords[this.numCoords++] = x3;
+		this.coords[this.numCoords++] = y3;
+		this.isEmpty = null;
+		this.bounds = null;
+	}
+
+	/**
+	 * Closes the current subpath by drawing a straight line back to
+	 * the coordinates of the last {@code moveTo}.  If the path is already
+	 * closed or if the previous coordinates are for a {@code moveTo}
+	 * then this method has no effect.
+	 */
+	public void closePath() {
+		if (this.numTypes<=0 ||
+			(this.types[this.numTypes-1]!=PathElementType.CLOSE
+			&&this.types[this.numTypes-1]!=PathElementType.MOVE_TO)) {
+			ensureSlots(true, 0);
+			this.types[this.numTypes++] = PathElementType.CLOSE;
+		}
+	}
+
+	/** Replies an iterator on the path elements.
+	 * <p>
+	 * Only {@link PathElementType#MOVE_TO},
+	 * {@link PathElementType#LINE_TO}, and 
+	 * {@link PathElementType#CLOSE} types are returned by the iterator.
+	 * <p>
+	 * The amount of subdivision of the curved segments is controlled by the 
+	 * flatness parameter, which specifies the maximum distance that any point 
+	 * on the unflattened transformed curve can deviate from the returned
+	 * flattened path segments. Note that a limit on the accuracy of the
+	 * flattened path might be silently imposed, causing very small flattening
+	 * parameters to be treated as larger values. This limit, if there is one,
+	 * is defined by the particular implementation that is used.
+	 * <p>
+	 * The iterator for this class is not multi-threaded safe.
+	 * 
+	 * @param flatness is the maximum distance that the line segments used to approximate
+	 * the curved segments are allowed to deviate from any point on the original curve.
+	 * @return an iterator on the path elements.
+	 */
+	public PathIterator2f getPathIterator(float flatness) {
+		return new FlatteningPathIterator(getWindingRule(), getPathIterator(null), flatness, 10);
+	}
+
+	/** Replies an iterator on the path elements.
+	 * <p>
+	 * Only {@link PathElementType#MOVE_TO},
+	 * {@link PathElementType#LINE_TO}, and 
+	 * {@link PathElementType#CLOSE} types are returned by the iterator.
+	 * <p>
+	 * The amount of subdivision of the curved segments is controlled by the 
+	 * flatness parameter, which specifies the maximum distance that any point 
+	 * on the unflattened transformed curve can deviate from the returned
+	 * flattened path segments. Note that a limit on the accuracy of the
+	 * flattened path might be silently imposed, causing very small flattening
+	 * parameters to be treated as larger values. This limit, if there is one,
+	 * is defined by the particular implementation that is used.
+	 * <p>
+	 * The iterator for this class is not multi-threaded safe.
+	 *
+	 * @param transform is an optional affine Transform2D to be applied to the
+	 * coordinates as they are returned in the iteration, or <code>null</code> if 
+	 * untransformed coordinates are desired.
+	 * @param flatness is the maximum distance that the line segments used to approximate
+	 * the curved segments are allowed to deviate from any point on the original curve.
+	 * @return an iterator on the path elements.
+	 */
+	public PathIterator2f getPathIterator(Transform2D transform, float flatness) {
+		return new FlatteningPathIterator(getWindingRule(), getPathIterator(transform), flatness, 10);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public PathIterator2f getPathIterator(Transform2D transform) {
+		if (transform == null) {
+			return new CopyPathIterator();
+		}
+		return new TransformPathIterator(transform);
+	}
+
+	/** Transform the current path.
+	 * This function changes the current path.
+	 * 
+	 * @param transform is the affine transformation to apply.
+	 * @see #createTransformedShape(Transform2D)
+	 */
+	public void transform(Transform2D transform) {
+		if (transform!=null) {
+			Point2D p = new Point2f();
+			for(int i=0; i<this.numCoords;) {
+				p.set(this.coords[i], this.coords[i+1]);
+				transform.transform(p);
+				this.coords[i++] = p.getX();
+				this.coords[i++] = p.getY();
+			}
+			this.bounds = null;
+		}
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void translate(float dx, float dy) {
+		for(int i=0; i<this.numCoords;) {
+			this.coords[i++] += dx;
+			this.coords[i++] += dy;
+		}
+		Rectangle2f bb = this.bounds==null ? null : this.bounds.get();
+		if (bb!=null) bb.translate(dx, dy);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Shape2f createTransformedShape(Transform2D transform) {
+		Path2f newPath = new Path2f(getWindingRule());
+		PathIterator2f pi = getPathIterator();
+		Point2f p = new Point2f();
+		Point2f t1 = new Point2f();
+		Point2f t2 = new Point2f();
+		PathElement2f e;
+		while (pi.hasNext()) {
+			e = pi.next();
+			switch(e.type) {
+			case MOVE_TO:
+				p.set(e.toX, e.toY);
+				transform.transform(p);
+				newPath.moveTo(p.getX(), p.getY());
+				break;
+			case LINE_TO:
+				p.set(e.toX, e.toY);
+				transform.transform(p);
+				newPath.lineTo(p.getX(), p.getY());
+				break;
+			case QUAD_TO:
+				t1.set(e.ctrlX1, e.ctrlY1);
+				transform.transform(t1);
+				p.set(e.toX, e.toY);
+				transform.transform(p);
+				newPath.quadTo(t1.getX(), t1.getY(), p.getX(), p.getY());
+				break;
+			case CURVE_TO:
+				t1.set(e.ctrlX1, e.ctrlY1);
+				transform.transform(t1);
+				t2.set(e.ctrlX2, e.ctrlY2);
+				transform.transform(t2);
+				p.set(e.toX, e.toY);
+				transform.transform(p);
+				newPath.curveTo(t1.getX(), t1.getY(), t2.getX(), t2.getY(), p.getX(), p.getY());
+				break;
+			case CLOSE:
+				newPath.closePath();
+				break;
+			default:
+			}
+		}
+		return newPath;
+	}
+
+	@Override
+	public float distanceSquared(Point2D p) {
+		Point2D c = getClosestPointTo(p);
+		return c.distanceSquared(p);
+	}
+
+	@Override
+	public float distanceL1(Point2D p) {
+		Point2D c = getClosestPointTo(p);
+		return c.distanceL1(p);
+	}
+
+	@Override
+	public float distanceLinf(Point2D p) {
+		Point2D c = getClosestPointTo(p);
+		return c.distanceLinf(p);
+	}
+
+	@Override
+	public boolean contains(float x, float y) {
+		return contains(getPathIterator(), x, y);
+	}
+
+	@Override
+	public boolean contains(Rectangle2f r) {
+		return contains(getPathIterator(),
+				r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight());
+	}
+
+	@Override
+	public boolean intersects(Rectangle2f s) {
+		// Copied from AWT API
+		if (s.isEmpty()) return false;
+		int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
+		int crossings = computeCrossingsFromRect(
+				getPathIterator(),
+				s.getMinX(), s.getMinY(), s.getMaxX(), s.getMaxY());
+		return (crossings == MathConstants.SHAPE_INTERSECTS ||
+				(crossings & mask) != 0);
+	}
+
+	@Override
+	public boolean intersects(Ellipse2f s) {
+		int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
+		int crossings = computeCrossingsFromEllipse(
+				getPathIterator(),
+				s.getMinX(), s.getMinY(), s.getWidth(), s.getHeight());
+		return (crossings == MathConstants.SHAPE_INTERSECTS ||
+				(crossings & mask) != 0);
+	}
+
+	@Override
+	public boolean intersects(Circle2f s) {
+		int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
+		int crossings = computeCrossingsFromCircle(
+				getPathIterator(),
+				s.getX(), s.getY(), s.getRadius());
+		return (crossings == MathConstants.SHAPE_INTERSECTS ||
+				(crossings & mask) != 0);
+	}
+
+	@Override
+	public boolean intersects(Segment2f s) {
+		int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
+		int crossings = computeCrossingsFromSegment(
+				getPathIterator(),
+				s.getX1(), s.getY1(), s.getX2(), s.getY2());
+		return (crossings == MathConstants.SHAPE_INTERSECTS ||
+				(crossings & mask) != 0);
+	}
+
+	@Override
+	public Rectangle2f toBoundingBox() {
+		Rectangle2f bb = this.bounds==null ? null : this.bounds.get();
+		if (bb==null) {
+			float xmin = Float.POSITIVE_INFINITY;
+			float ymin = Float.POSITIVE_INFINITY;
+			float xmax = Float.NEGATIVE_INFINITY;
+			float ymax = Float.NEGATIVE_INFINITY;
+			for(int i=0; i<this.numCoords; i+= 2) {
+				if (this.coords[i]<xmin) xmin = this.coords[i];
+				if (this.coords[i+1]<ymin) ymin = this.coords[i+1];
+				if (this.coords[i]>xmax) xmax = this.coords[i];
+				if (this.coords[i+1]>ymax) ymax = this.coords[i+1];
+			}
+			bb = new Rectangle2f();
+			bb.setFromCorners(xmin, ymin, xmax, ymax);
+			this.bounds = new SoftReference<Rectangle2f>(bb);
+		}
+		return bb;
+	}
+
+	@Override
+	public Point2D getClosestPointTo(Point2D p) {
+		Point2D closest = null;
+		float bestDist = Float.POSITIVE_INFINITY;
+		Point2D candidate;
+		PathIterator2f pi = getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
+		PathElement2f pe;
+
+		int mask = (pi.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 1);
+		int crossings = 0;
+		boolean isClosed = false;
+		float moveX, moveY, currentX, currentY;
+		moveX = moveY = currentX = currentY = Float.NaN;
+		
+		while (pi.hasNext()) {
+			pe = pi.next();
+
+			candidate = null;
+
+			currentX = pe.toX;
+			currentY = pe.toY;
+
+			switch(pe.type) {
+			case MOVE_TO:
+				moveX = pe.toX;
+				moveY = pe.toY;
+				isClosed = false;
+				candidate = new Point2f(pe.toX, pe.toY);
+				break;
+			case LINE_TO:
+			{
+				isClosed = false;
+				float factor =  MathUtil.projectsPointOnLine(
+						p.getX(), p.getY(),
+						pe.fromX, pe.fromY, pe.toX, pe.toY);
+				factor = MathUtil.clamp(factor, 0f, 1f);
+				Vector2f v = new Vector2f(pe.toX, pe.toY);
+				v.sub(pe.fromX, pe.fromY);
+				v.scale(factor);
+				candidate = new Point2f(
+						pe.fromX + v.getX(),
+						pe.fromY + v.getY());
+				crossings += Segment2f.computeCrossingsFromPoint(
+						p.getX(), p.getY(),
+						pe.fromX, pe.fromY, pe.toX, pe.toY);
+				break;
+			}
+			case CLOSE:
+				isClosed = true;
+				if (!pe.isEmpty()) {
+					float factor =  MathUtil.projectsPointOnLine(
+							p.getX(), p.getY(),
+							pe.fromX, pe.fromY, pe.toX, pe.toY);
+					factor = MathUtil.clamp(factor, 0f, 1f);
+					Vector2f v = new Vector2f(pe.toX, pe.toY);
+					v.sub(pe.fromX, pe.fromY);
+					v.scale(factor);
+					candidate = new Point2f(
+							pe.fromX + v.getX(),
+							pe.fromY + v.getY());
+					crossings += Segment2f.computeCrossingsFromPoint(
+							p.getX(), p.getY(),
+							pe.fromX, pe.fromY, pe.toX, pe.toY);
+				}
+				break;
+			case QUAD_TO:
+			case CURVE_TO:
+			default:
+				throw new IllegalStateException();
+			}
+
+			if (candidate!=null) {
+				float d = p.distanceSquared(candidate);
+				if (d<bestDist) {
+					bestDist = d;
+					closest = candidate;
+				}
+			}
+		}
+		
+		if (!isClosed) {
+			crossings += Segment2f.computeCrossingsFromPoint(
+					p.getX(), p.getY(),
+					currentX, currentY,
+					moveX, moveY);
+		}
+		
+		if ((crossings & mask) != 0) return p;
+		return closest;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (obj instanceof Path2f) {
+			Path2f p = (Path2f)obj;
+			return (this.numCoords==p.numCoords
+					&&this.numTypes==p.numTypes
+					&&Arrays.equals(this.coords, p.coords)
+					&&Arrays.equals(this.types, p.types)
+					&&this.windingRule==p.windingRule);
+		}
+		return false;
+	}
+	
+	@Override
+	public int hashCode() {
+		long bits = 1L;
+		bits = 31L * bits + this.numCoords;
+		bits = 31L * bits + this.numTypes;
+		bits = 31L * bits + Arrays.hashCode(this.coords);
+		bits = 31L * bits + Arrays.hashCode(this.types);
+		bits = 31L * bits + this.windingRule.ordinal();
+		return (int) (bits ^ (bits >> 32));
+	}
+
+	/** Replies the coordinates of this path in an array of
+	 * single precision floating-point numbers.
+	 * 
+	 * @return the coordinates.
+	 */
+	public final float[] toFloatArray() {
+		return toFloatArray(null);
+	}
+
+	/** Replies the coordinates of this path in an array of
+	 * single precision floating-point numbers.
+	 * 
+	 * @param transform is the transformation to apply to all the coordinates.
+	 * @return the coordinates.
+	 */
+	public float[] toFloatArray(Transform2D transform) {
+		if (transform==null) {
+			return Arrays.copyOf(this.coords, this.numCoords);
+		}
+		Point2f p = new Point2f();
+		float[] clone = new float[this.numCoords];
+		for(int i=0; i<clone.length;) {
+			p.x = this.coords[i];
+			p.y = this.coords[i+1];
+			transform.transform(p);
+			clone[i++] = p.x;
+			clone[i++] = p.y;
+		}
+		return clone;
+	}
+
+	/** Replies the coordinates of this path in an array of
+	 * double precision floating-point numbers.
+	 * 
+	 * @return the coordinates.
+	 */
+	public final double[] toDoubleArray() {
+		return toDoubleArray(null);
+	}
+
+	/** Replies the coordinates of this path in an array of
+	 * double precision floating-point numbers.
+	 * 
+	 * @param transform is the transformation to apply to all the coordinates.
+	 * @return the coordinates.
+	 */
+	public double[] toDoubleArray(Transform2D transform) {
+		double[] clone = new double[this.numCoords];
+		if (transform==null) {
+			for(int i=0; i<this.numCoords; ++i) {
+				clone[i] = this.coords[i];
+			}
+		}
+		else {
+			Point2f p = new Point2f();
+			for(int i=0; i<clone.length;) {
+				p.x = this.coords[i];
+				p.y = this.coords[i+1];
+				transform.transform(p);
+				clone[i++] = p.x;
+				clone[i++] = p.y;
+			}
+		}
+		return clone;
+	}
+	
+	/** Replies the points of this path in an array.
+	 * 
+	 * @return the points.
+	 */
+	public final Point2D[] toPointArray() {
+		return toPointArray(null);
+	}
+
+	/** Replies the points of this path in an array.
+	 * 
+	 * @param transform is the transformation to apply to all the points.
+	 * @return the points.
+	 */
+	public Point2D[] toPointArray(Transform2D transform) {
+		Point2D[] clone = new Point2D[this.numCoords/2];
+		if (transform==null) {
+			for(int i=0, j=0; j<this.numCoords; ++i) {
+				clone[i] = new Point2f(
+						this.coords[j++],
+						this.coords[j++]);
+			}
+		}
+		else {
+			for(int i=0, j=0; j<clone.length; ++i) {
+				clone[i] = new Point2f(
+						this.coords[j++],
+						this.coords[j++]);
+				transform.transform(clone[i]);
+			}
+		}
+		return clone;
+	}
+
+	/** Replies the collection that is contains all the points of the path.
+	 * 
+	 * @return the point collection.
+	 */
+	public final Collection<Point2D> toCollection() {
+		return new PointCollection();
+	}
+
+	/** Replies the coordinate at the given index.
+	 * The index is in [0;{@link #size()}*2).
+	 *
+	 * @param index
+	 * @return the coordinate at the given index.
+	 */
+	public float getCoordAt(int index) {
+		return this.coords[index];
+	}
+	
+	/** Replies the point at the given index.
+	 * The index is in [0;{@link #size()}).
+	 *
+	 * @param index
+	 * @return the point at the given index.
+	 */
+	public Point2f getPointAt(int index) {
+		return new Point2f(
+				this.coords[index*2],
+				this.coords[index*2+1]);
+	}
+
+	/** Replies the number of points in the path.
+	 *
+	 * @return the number of points in the path.
+	 */
+	public int size() {
+		return this.numCoords/2;
+	}
+	
+	/** Replies if this path is empty.
+	 * The path is empty when there is no point inside, or
+	 * all the points are at the same coordinate, or
+	 * when the path does not represents a drawable path
+	 * (a path with a line or a curve).
+	 * 
+	 * @return <code>true</code> if the path does not contain
+	 * a coordinate; otherwise <code>false</code>.
+	 */
+	@Override
+	public boolean isEmpty() {
+		if (this.isEmpty==null) {
+			this.isEmpty = Boolean.TRUE;
+			PathIterator2f pi = getPathIterator();
+			PathElement2f pe;
+			while (this.isEmpty()==Boolean.TRUE && pi.hasNext()) {
+				pe = pi.next();
+				if (pe.isDrawable()) { 
+					this.isEmpty = Boolean.FALSE;
+				}
+			}
+		}
+		return this.isEmpty;
+	}
+	
+	/** Replies if the given points exists in the coordinates of this path.
+	 * 
+	 * @param p
+	 * @return <code>true</code> if the point is a control point of the path.
+	 */
+	boolean containsPoint(Point2D p) {
+		float x, y;
+		for(int i=0; i<this.numCoords;) {
+			x = this.coords[i++];
+			y = this.coords[i++];
+			if (x==p.getX() && y==p.getY()) {
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	
+	/** Remove the point with the given coordinates.
+	 * 
+	 * @param x
+	 * @param y
+	 * @return <code>true</code> if the point was removed; <code>false</code> otherwise.
+	 */
+	boolean remove(float x, float y) {
+		for(int i=0, j=0; i<this.numCoords && j<this.numTypes;) {
+			switch(this.types[j]) {
+			case MOVE_TO:
+			case LINE_TO:
+				if (x==this.coords[i] && y==this.coords[i+1]) {
+					this.numCoords -= 2;
+					--this.numTypes;
+					System.arraycopy(this.coords, i+2, this.coords, i, this.numCoords);
+					System.arraycopy(this.types, j+1, this.types, j, this.numTypes);
+					return true;
+				}
+				i += 2;
+				++j;
+				break;
+			case CURVE_TO:
+				if ((x==this.coords[i] && y==this.coords[i+1])
+					||(x==this.coords[i+2] && y==this.coords[i+3])
+					||(x==this.coords[i+4] && y==this.coords[i+5])) {
+					this.numCoords -= 6;
+					--this.numTypes;
+					System.arraycopy(this.coords, i+6, this.coords, i, this.numCoords);
+					System.arraycopy(this.types, j+1, this.types, j, this.numTypes);
+					return true;
+				}
+				i += 6;
+				++j;
+				break;
+			case QUAD_TO:
+				if ((x==this.coords[i] && y==this.coords[i+1])
+					||(x==this.coords[i+2] && y==this.coords[i+3])) {
+					this.numCoords -= 4;
+					--this.numTypes;
+					System.arraycopy(this.coords, i+4, this.coords, i, this.numCoords);
+					System.arraycopy(this.types, j+1, this.types, j, this.numTypes);
+					return true;
+				}
+				i += 4;
+				++j;
+				break;
+			case CLOSE:
+				++j;
+				break;
+			default:
+				break;
+			}
+		}
+		return false;
+	}
+	
+	/** Remove the last action.
+	 */
+	public void removeLast() {
+		if (this.numTypes>0) {
+			switch(this.types[this.numTypes-1]) {
+			case CLOSE:
+				// no coord to remove
+				break;
+			case MOVE_TO:
+			case LINE_TO:
+				this.numCoords -= 2;
+				break;
+			case CURVE_TO:
+				this.numCoords -= 6;
+				break;
+			case QUAD_TO:
+				this.numCoords -= 4;
+				break;
+			default:
+				throw new IllegalStateException();
+			}
+			--this.numTypes;
+			this.isEmpty = null;
+			this.bounds = null;
+		}
+	}
+
+	/** Change the coordinates of the last inserted point.
+	 * 
+	 * @param x
+	 * @param y
+	 */
+	public void setLastPoint(float x, float y) {
+		if (this.numCoords>=2) {
+			this.coords[this.numCoords-2] = x;
+			this.coords[this.numCoords-1] = y;
+			this.bounds = null;
+		}
+	}
+
+	/** A path iterator that does not transform the coordinates.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private class CopyPathIterator implements PathIterator2f {
+
+		private final Point2D p1 = new Point2f();
+		private final Point2D p2 = new Point2f();
+		private int iType = 0;
+		private int iCoord = 0;
+		private float movex, movey;
+
+		/**
+		 */
+		public CopyPathIterator() {
+			//
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.iType<Path2f.this.numTypes;
+		}
+
+		@Override
+		public PathElement2f next() {
+			int type = this.iType;
+			if (this.iType>=Path2f.this.numTypes) {
+				throw new NoSuchElementException();
+			}
+			PathElement2f element = null;
+			switch(Path2f.this.types[type]) {
+			case MOVE_TO:
+				if (this.iCoord+2>Path2f.this.numCoords) {
+					throw new NoSuchElementException();
+				}
+				this.movex = Path2f.this.coords[this.iCoord++];
+				this.movey = Path2f.this.coords[this.iCoord++];
+				this.p2.set(this.movex, this.movey);
+				element = new PathElement2f.MovePathElement2f(
+						this.p2.getX(), this.p2.getY());
+				break;
+			case LINE_TO:
+				if (this.iCoord+2>Path2f.this.numCoords) {
+					throw new NoSuchElementException();
+				}
+				this.p1.set(this.p2);
+				this.p2.set(
+						Path2f.this.coords[this.iCoord++],
+						Path2f.this.coords[this.iCoord++]);
+				element = new PathElement2f.LinePathElement2f(
+						this.p1.getX(), this.p1.getY(),
+						this.p2.getX(), this.p2.getY());
+				break;
+			case QUAD_TO:
+			{
+				if (this.iCoord+4>Path2f.this.numCoords) {
+					throw new NoSuchElementException();
+				}
+				this.p1.set(this.p2);
+				float ctrlx = Path2f.this.coords[this.iCoord++];
+				float ctrly = Path2f.this.coords[this.iCoord++];
+				this.p2.set(
+						Path2f.this.coords[this.iCoord++],
+						Path2f.this.coords[this.iCoord++]);
+				element = new PathElement2f.QuadPathElement2f(
+						this.p1.getX(), this.p1.getY(),
+						ctrlx, ctrly,
+						this.p2.getX(), this.p2.getY());
+			}
+			break;
+			case CURVE_TO:
+			{
+				if (this.iCoord+6>Path2f.this.numCoords) {
+					throw new NoSuchElementException();
+				}
+				this.p1.set(this.p2);
+				float ctrlx1 = Path2f.this.coords[this.iCoord++];
+				float ctrly1 = Path2f.this.coords[this.iCoord++];
+				float ctrlx2 = Path2f.this.coords[this.iCoord++];
+				float ctrly2 = Path2f.this.coords[this.iCoord++];
+				this.p2.set(
+						Path2f.this.coords[this.iCoord++],
+						Path2f.this.coords[this.iCoord++]);
+				element = new PathElement2f.CurvePathElement2f(
+						this.p1.getX(), this.p1.getY(),
+						ctrlx1, ctrly1,
+						ctrlx2, ctrly2,
+						this.p2.getX(), this.p2.getY());
+			}
+			break;
+			case CLOSE:
+				this.p1.set(this.p2);
+				this.p2.set(this.movex, this.movey);
+				element = new PathElement2f.ClosePathElement2f(
+						this.p1.getX(), this.p1.getY(),
+						this.p2.getX(), this.p2.getY());
+				break;
+			default:
+			}
+			if (element==null)
+				throw new NoSuchElementException();
+			
+			++this.iType;
+			
+			return element;
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return Path2f.this.getWindingRule();
+		}
+
+	} // class CopyPathIterator
+
+	/** A path iterator that transforms the coordinates.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private class TransformPathIterator implements PathIterator2f {
+
+		private final Transform2D transform;
+		private final Point2D p1 = new Point2f();
+		private final Point2D p2 = new Point2f();
+		private final Point2D ptmp1 = new Point2f();
+		private final Point2D ptmp2 = new Point2f();
+		private int iType = 0;
+		private int iCoord = 0;
+		private float movex, movey;
+
+		/**
+		 * @param transform
+		 */
+		public TransformPathIterator(Transform2D transform) {
+			assert(transform!=null);
+			this.transform = transform;
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.iType<Path2f.this.numTypes;
+		}
+
+		@Override
+		public PathElement2f next() {
+			if (this.iType>=Path2f.this.numTypes) {
+				throw new NoSuchElementException();
+			}
+			PathElement2f element = null;
+			switch(Path2f.this.types[this.iType++]) {
+			case MOVE_TO:
+				this.movex = Path2f.this.coords[this.iCoord++];
+				this.movey = Path2f.this.coords[this.iCoord++];
+				this.p2.set(this.movex, this.movey);
+				this.transform.transform(this.p2);
+				element = new PathElement2f.MovePathElement2f(
+						this.p2.getX(), this.p2.getY());
+				break;
+			case LINE_TO:
+				this.p1.set(this.p2);
+				this.p2.set(
+						Path2f.this.coords[this.iCoord++],
+						Path2f.this.coords[this.iCoord++]);
+				this.transform.transform(this.p2);
+				element = new PathElement2f.LinePathElement2f(
+						this.p1.getX(), this.p1.getY(),
+						this.p2.getX(), this.p2.getY());
+				break;
+			case QUAD_TO:
+			{
+				this.p1.set(this.p2);
+				this.ptmp1.set(
+						Path2f.this.coords[this.iCoord++],
+						Path2f.this.coords[this.iCoord++]);
+				this.transform.transform(this.ptmp1);
+				this.p2.set(
+						Path2f.this.coords[this.iCoord++],
+						Path2f.this.coords[this.iCoord++]);
+				this.transform.transform(this.p2);
+				element = new PathElement2f.QuadPathElement2f(
+						this.p1.getX(), this.p1.getY(),
+						this.ptmp1.getX(), this.ptmp1.getY(),
+						this.p2.getX(), this.p2.getY());
+			}
+			break;
+			case CURVE_TO:
+			{
+				this.p1.set(this.p2);
+				this.ptmp1.set(
+						Path2f.this.coords[this.iCoord++],
+						Path2f.this.coords[this.iCoord++]);
+				this.transform.transform(this.ptmp1);
+				this.ptmp2.set(
+						Path2f.this.coords[this.iCoord++],
+						Path2f.this.coords[this.iCoord++]);
+				this.transform.transform(this.ptmp2);
+				this.p2.set(
+						Path2f.this.coords[this.iCoord++],
+						Path2f.this.coords[this.iCoord++]);
+				this.transform.transform(this.p2);
+				element = new PathElement2f.CurvePathElement2f(
+						this.p1.getX(), this.p1.getY(),
+						this.ptmp1.getX(), this.ptmp1.getY(),
+						this.ptmp2.getX(), this.ptmp2.getY(),
+						this.p2.getX(), this.p2.getY());
+			}
+			break;
+			case CLOSE:
+				this.p1.set(this.p2);
+				this.p2.set(this.movex, this.movey);
+				this.transform.transform(this.p2);
+				element = new PathElement2f.ClosePathElement2f(
+						this.p1.getX(), this.p1.getY(),
+						this.p2.getX(), this.p2.getY());
+				break;
+			default:
+			}
+			if (element==null)
+				throw new NoSuchElementException();
+			return element;
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return Path2f.this.getWindingRule();
+		}
+
+	}  // class TransformPathIterator
+
+	/** A path iterator that is flattening the path.
+	 * This iterator was copied from AWT FlatteningPathIterator.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class FlatteningPathIterator implements PathIterator2f {
+
+		/** Winding rule of the path.
+		 */
+		private final PathWindingRule windingRule;
+
+		/** The source iterator.
+		 */
+		private final Iterator<PathElement2f> pathIterator;
+
+		/**
+		 * Square of the flatness parameter for testing against squared lengths.
+		 */
+		private final float squaredFlatness;
+
+		/**
+		 * Maximum number of recursion levels.
+		 */
+		private final int limit; 
+
+		/** The recursion level at which each curve being held in storage was generated.
+		 */
+		private int levels[];
+
+		/** The cache of interpolated coords.
+		 * Note that this must be long enough
+		 * to store a full cubic segment and
+		 * a relative cubic segment to avoid
+		 * aliasing when copying the coords
+		 * of a curve to the end of the array.
+		 * This is also serendipitously equal
+		 * to the size of a full quad segment
+		 * and 2 relative quad segments.
+		 */
+		private float hold[] = new float[14];
+
+		/** The index of the last curve segment being held for interpolation.
+		 */
+		private int holdEnd;
+
+		/**
+		 * The index of the curve segment that was last interpolated.  This
+		 * is the curve segment ready to be returned in the next call to
+		 * next().
+		 */
+		private int holdIndex;
+
+		/** The ending x of the last segment.
+		 */
+		private float currentX;
+
+		/** The ending y of the last segment.
+		 */
+		private float currentY;
+
+		/** The x of the last move segment.
+		 */
+		private float moveX;
+
+		/** The y of the last move segment.
+		 */
+		private float moveY;
+
+		/** The index of the entry in the
+		 * levels array of the curve segment
+		 * at the holdIndex
+		 */
+		private int levelIndex;
+
+		/** True when iteration is done.
+		 */
+		private boolean done;
+
+		/** The type of the path element.
+		 */
+		private PathElementType holdType;
+
+		/** The x of the last move segment replied by next.
+		 */
+		private float lastNextX;
+
+		/** The y of the last move segment replied by next.
+		 */
+		private float lastNextY;
+
+		/**
+		 * @param windingRule is the winding rule of the path.
+		 * @param pathIterator is the path iterator that may be used to initialize the path.
+		 * @param flatness the maximum allowable distance between the
+		 * control points and the flattened curve
+		 * @param limit the maximum number of recursive subdivisions
+		 * allowed for any curved segment
+		 */
+		public FlatteningPathIterator(PathWindingRule windingRule, Iterator<PathElement2f> pathIterator, float flatness, int limit) {
+			assert(windingRule!=null);
+			assert(flatness>=0f);
+			assert(limit>=0);
+			this.windingRule = windingRule;
+			this.pathIterator = pathIterator;
+			this.squaredFlatness = flatness * flatness;
+			this.limit = limit;
+			this.levels = new int[limit + 1];
+			searchNext();
+		}
+
+		/**
+		 * Ensures that the hold array can hold up to (want) more values.
+		 * It is currently holding (hold.length - holdIndex) values.
+		 */
+		private void ensureHoldCapacity(int want) {
+			if (this.holdIndex - want < 0) {
+				int have = this.hold.length - this.holdIndex;
+				int newsize = this.hold.length + GROW_SIZE;
+				float newhold[] = new float[newsize];
+				System.arraycopy(this.hold, this.holdIndex,
+						newhold, this.holdIndex + GROW_SIZE,
+						have);
+				this.hold = newhold;
+				this.holdIndex += GROW_SIZE;
+				this.holdEnd += GROW_SIZE;
+			}
+		}
+
+		/**
+		 * Returns the square of the flatness, or maximum distance of a
+		 * control point from the line connecting the end points, of the
+		 * quadratic curve specified by the control points stored in the
+		 * indicated array at the indicated index.
+		 * @param coords an array containing coordinate values
+		 * @param offset the index into <code>coords</code> from which to
+		 *          to start getting the values from the array
+		 * @return the flatness of the quadratic curve that is defined by the
+		 *          values in the specified array at the specified index.
+		 */
+		private static float getQuadSquaredFlatness(float coords[], int offset) {
+			return MathUtil.distanceSquaredPointToLine(
+					coords[offset + 2], coords[offset + 3],
+					coords[offset + 0], coords[offset + 1],
+					coords[offset + 4], coords[offset + 5]);
+		}
+
+		/**
+		 * Subdivides the quadratic curve specified by the coordinates
+		 * stored in the <code>src</code> array at indices
+		 * <code>srcoff</code> through <code>srcoff</code>&nbsp;+&nbsp;5
+		 * and stores the resulting two subdivided curves into the two
+		 * result arrays at the corresponding indices.
+		 * Either or both of the <code>left</code> and <code>right</code>
+		 * arrays can be <code>null</code> or a reference to the same array
+		 * and offset as the <code>src</code> array.
+		 * Note that the last point in the first subdivided curve is the
+		 * same as the first point in the second subdivided curve.  Thus,
+		 * it is possible to pass the same array for <code>left</code> and
+		 * <code>right</code> and to use offsets such that
+		 * <code>rightoff</code> equals <code>leftoff</code> + 4 in order
+		 * to avoid allocating extra storage for this common point.
+		 * @param src the array holding the coordinates for the source curve
+		 * @param srcoff the offset into the array of the beginning of the
+		 * the 6 source coordinates
+		 * @param left the array for storing the coordinates for the first
+		 * half of the subdivided curve
+		 * @param leftoff the offset into the array of the beginning of the
+		 * the 6 left coordinates
+		 * @param right the array for storing the coordinates for the second
+		 * half of the subdivided curve
+		 * @param rightoff the offset into the array of the beginning of the
+		 * the 6 right coordinates
+		 */
+		private static void subdivideQuad(float src[], int srcoff,
+				float left[], int leftoff,
+				float right[], int rightoff) {
+			float x1 = src[srcoff + 0];
+			float y1 = src[srcoff + 1];
+			float ctrlx = src[srcoff + 2];
+			float ctrly = src[srcoff + 3];
+			float x2 = src[srcoff + 4];
+			float y2 = src[srcoff + 5];
+			if (left != null) {
+				left[leftoff + 0] = x1;
+				left[leftoff + 1] = y1;
+			}
+			if (right != null) {
+				right[rightoff + 4] = x2;
+				right[rightoff + 5] = y2;
+			}
+			x1 = (x1 + ctrlx) / 2f;
+			y1 = (y1 + ctrly) / 2f;
+			x2 = (x2 + ctrlx) / 2f;
+			y2 = (y2 + ctrly) / 2f;
+			ctrlx = (x1 + x2) / 2f;
+			ctrly = (y1 + y2) / 2f;
+			if (left != null) {
+				left[leftoff + 2] = x1;
+				left[leftoff + 3] = y1;
+				left[leftoff + 4] = ctrlx;
+				left[leftoff + 5] = ctrly;
+			}
+			if (right != null) {
+				right[rightoff + 0] = ctrlx;
+				right[rightoff + 1] = ctrly;
+				right[rightoff + 2] = x2;
+				right[rightoff + 3] = y2;
+			}
+		}
+
+		/**
+		 * Returns the square of the flatness of the cubic curve specified
+		 * by the control points stored in the indicated array at the
+		 * indicated index. The flatness is the maximum distance
+		 * of a control point from the line connecting the end points.
+		 * @param coords an array containing coordinates
+		 * @param offset the index of <code>coords</code> from which to begin
+		 *          getting the end points and control points of the curve
+		 * @return the square of the flatness of the <code>CubicCurve2D</code>
+		 *          specified by the coordinates in <code>coords</code> at
+		 *          the specified offset.
+		 */
+		private static float getCurveSquaredFlatness(float coords[], int offset) {
+			return Math.max(
+					MathUtil.distanceSquaredPointToSegment(
+							coords[offset + 0],
+							coords[offset + 1],
+							coords[offset + 6],
+							coords[offset + 7],
+							coords[offset + 2],
+							coords[offset + 3]),
+							MathUtil.distanceSquaredPointToSegment(
+									coords[offset + 0],
+									coords[offset + 1],
+									coords[offset + 6],
+									coords[offset + 7],
+									coords[offset + 4], coords[offset + 5]));
+		}
+
+		/**
+		 * Subdivides the cubic curve specified by the coordinates
+		 * stored in the <code>src</code> array at indices <code>srcoff</code>
+		 * through (<code>srcoff</code>&nbsp;+&nbsp;7) and stores the
+		 * resulting two subdivided curves into the two result arrays at the
+		 * corresponding indices.
+		 * Either or both of the <code>left</code> and <code>right</code>
+		 * arrays may be <code>null</code> or a reference to the same array
+		 * as the <code>src</code> array.
+		 * Note that the last point in the first subdivided curve is the
+		 * same as the first point in the second subdivided curve. Thus,
+		 * it is possible to pass the same array for <code>left</code>
+		 * and <code>right</code> and to use offsets, such as <code>rightoff</code>
+		 * equals (<code>leftoff</code> + 6), in order
+		 * to avoid allocating extra storage for this common point.
+		 * @param src the array holding the coordinates for the source curve
+		 * @param srcoff the offset into the array of the beginning of the
+		 * the 6 source coordinates
+		 * @param left the array for storing the coordinates for the first
+		 * half of the subdivided curve
+		 * @param leftoff the offset into the array of the beginning of the
+		 * the 6 left coordinates
+		 * @param right the array for storing the coordinates for the second
+		 * half of the subdivided curve
+		 * @param rightoff the offset into the array of the beginning of the
+		 * the 6 right coordinates
+		 */
+		private static void subdivideCurve(
+				float src[], int srcoff,
+				float left[], int leftoff,
+				float right[], int rightoff) {
+			float x1 = src[srcoff + 0];
+			float y1 = src[srcoff + 1];
+			float ctrlx1 = src[srcoff + 2];
+			float ctrly1 = src[srcoff + 3];
+			float ctrlx2 = src[srcoff + 4];
+			float ctrly2 = src[srcoff + 5];
+			float x2 = src[srcoff + 6];
+			float y2 = src[srcoff + 7];
+			if (left != null) {
+				left[leftoff + 0] = x1;
+				left[leftoff + 1] = y1;
+			}
+			if (right != null) {
+				right[rightoff + 6] = x2;
+				right[rightoff + 7] = y2;
+			}
+			x1 = (x1 + ctrlx1) / 2f;
+			y1 = (y1 + ctrly1) / 2f;
+			x2 = (x2 + ctrlx2) / 2f;
+			y2 = (y2 + ctrly2) / 2f;
+			float centerx = (ctrlx1 + ctrlx2) / 2f;
+			float centery = (ctrly1 + ctrly2) / 2f;
+			ctrlx1 = (x1 + centerx) / 2f;
+			ctrly1 = (y1 + centery) / 2f;
+			ctrlx2 = (x2 + centerx) / 2f;
+			ctrly2 = (y2 + centery) / 2f;
+			centerx = (ctrlx1 + ctrlx2) / 2f;
+			centery = (ctrly1 + ctrly2) / 2f;
+			if (left != null) {
+				left[leftoff + 2] = x1;
+				left[leftoff + 3] = y1;
+				left[leftoff + 4] = ctrlx1;
+				left[leftoff + 5] = ctrly1;
+				left[leftoff + 6] = centerx;
+				left[leftoff + 7] = centery;
+			}
+			if (right != null) {
+				right[rightoff + 0] = centerx;
+				right[rightoff + 1] = centery;
+				right[rightoff + 2] = ctrlx2;
+				right[rightoff + 3] = ctrly2;
+				right[rightoff + 4] = x2;
+				right[rightoff + 5] = y2;
+			}
+		}
+
+		private void searchNext() {
+			int level;
+
+			if (this.holdIndex >= this.holdEnd) {
+				if (!this.pathIterator.hasNext()) {
+					this.done = true;
+					return;
+				}
+				PathElement2f pathElement = this.pathIterator.next();
+				this.holdType = pathElement.type;
+				pathElement.toArray(this.hold);
+				this.levelIndex = 0;
+				this.levels[0] = 0;
+			}
+
+			switch (this.holdType) {
+			case MOVE_TO:
+			case LINE_TO:
+				this.currentX = this.hold[0];
+				this.currentY = this.hold[1];
+				if (this.holdType == PathElementType.MOVE_TO) {
+					this.moveX = this.currentX;
+					this.moveY = this.currentY;
+				}
+				this.holdIndex = 0;
+				this.holdEnd = 0;
+				break;
+			case CLOSE:
+				this.currentX = this.moveX;
+				this.currentY = this.moveY;
+				this.holdIndex = 0;
+				this.holdEnd = 0;
+				break;
+			case QUAD_TO:
+				if (this.holdIndex >= this.holdEnd) {
+					// Move the coordinates to the end of the array.
+					this.holdIndex = this.hold.length - 6;
+					this.holdEnd = this.hold.length - 2;
+					this.hold[this.holdIndex + 0] = this.currentX;
+					this.hold[this.holdIndex + 1] = this.currentY;
+					this.hold[this.holdIndex + 2] = this.hold[0];
+					this.hold[this.holdIndex + 3] = this.hold[1];
+					this.hold[this.holdIndex + 4] = this.currentX = this.hold[2];
+					this.hold[this.holdIndex + 5] = this.currentY = this.hold[3];
+				}
+
+				level = this.levels[this.levelIndex];
+				while (level < this.limit) {
+					if (getQuadSquaredFlatness(this.hold, this.holdIndex) < this.squaredFlatness) {
+						break;
+					}
+
+					ensureHoldCapacity(4);
+					subdivideQuad(
+							this.hold, this.holdIndex,
+							this.hold, this.holdIndex - 4,
+							this.hold, this.holdIndex);
+					this.holdIndex -= 4;
+
+					// Now that we have subdivided, we have constructed
+					// two curves of one depth lower than the original
+					// curve.  One of those curves is in the place of
+					// the former curve and one of them is in the next
+					// set of held coordinate slots.  We now set both
+					// curves level values to the next higher level.
+					level++;
+					this.levels[this.levelIndex] = level;
+					this.levelIndex++;
+					this.levels[this.levelIndex] = level;
+				}
+
+				// This curve segment is flat enough, or it is too deep
+				// in recursion levels to try to flatten any more.  The
+				// two coordinates at holdIndex+4 and holdIndex+5 now
+				// contain the endpoint of the curve which can be the
+				// endpoint of an approximating line segment.
+				this.holdIndex += 4;
+				this.levelIndex--;
+				break;
+			case CURVE_TO:
+				if (this.holdIndex >= this.holdEnd) {
+					// Move the coordinates to the end of the array.
+					this.holdIndex = this.hold.length - 8;
+					this.holdEnd = this.hold.length - 2;
+					this.hold[this.holdIndex + 0] = this.currentX;
+					this.hold[this.holdIndex + 1] = this.currentY;
+					this.hold[this.holdIndex + 2] = this.hold[0];
+					this.hold[this.holdIndex + 3] = this.hold[1];
+					this.hold[this.holdIndex + 4] = this.hold[2];
+					this.hold[this.holdIndex + 5] = this.hold[3];
+					this.hold[this.holdIndex + 6] = this.currentX = this.hold[4];
+					this.hold[this.holdIndex + 7] = this.currentY = this.hold[5];
+				}
+
+				level = this.levels[this.levelIndex];
+				while (level < this.limit) {
+					if (getCurveSquaredFlatness(this.hold,this. holdIndex) < this.squaredFlatness) {
+						break;
+					}
+
+					ensureHoldCapacity(6);
+					subdivideCurve(
+							this.hold, this.holdIndex,
+							this.hold, this.holdIndex - 6,
+							this.hold, this.holdIndex);
+					this.holdIndex -= 6;
+
+					// Now that we have subdivided, we have constructed
+					// two curves of one depth lower than the original
+					// curve.  One of those curves is in the place of
+					// the former curve and one of them is in the next
+					// set of held coordinate slots.  We now set both
+					// curves level values to the next higher level.
+					level++;
+					this.levels[this.levelIndex] = level;
+					this.levelIndex++;
+					this.levels[this.levelIndex] = level;
+				}
+
+				// This curve segment is flat enough, or it is too deep
+				// in recursion levels to try to flatten any more.  The
+				// two coordinates at holdIndex+6 and holdIndex+7 now
+				// contain the endpoint of the curve which can be the
+				// endpoint of an approximating line segment.
+				this.holdIndex += 6;
+				this.levelIndex--;
+				break;
+			default:
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			return !this.done;
+		}
+
+		@Override
+		public PathElement2f next() {
+			if (this.done) {
+				throw new NoSuchElementException("flattening iterator out of bounds"); //$NON-NLS-1$
+			}
+
+			PathElement2f element;
+			PathElementType type = this.holdType;
+			if (type!=PathElementType.CLOSE) {
+				float x = this.hold[this.holdIndex + 0];
+				float y = this.hold[this.holdIndex + 1];
+				if (type == PathElementType.MOVE_TO) {
+					element = new PathElement2f.MovePathElement2f(x, y);
+				}
+				else {
+					element = new PathElement2f.LinePathElement2f(
+							this.lastNextX, this.lastNextY,
+							x, y);
+				}
+				this.lastNextX = x;
+				this.lastNextY = y;
+			}
+			else {
+				element = new PathElement2f.ClosePathElement2f(
+						this.lastNextX, this.lastNextY,
+						this.moveX, this.moveY);
+				this.lastNextX = this.moveX;
+				this.lastNextY = this.moveY;
+			}
+
+			searchNext();
+
+			return element;
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return this.windingRule;
+		}
+
+	} // class FlatteningPathIterator
+
+	/** An collection of the points of the path.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private class PointCollection implements Collection<Point2D> {
+
+		/**
+		 */
+		public PointCollection() {
+			//
+		}
+
+		@Override
+		public int size() {
+			return Path2f.this.size();
+		}
+
+		@Override
+		public boolean isEmpty() {
+			return Path2f.this.size()<=0;
+		}
+
+		@Override
+		public boolean contains(Object o) {
+			if (o instanceof Point2D) {
+				return Path2f.this.containsPoint((Point2D)o);
+			}
+			return false;
+		}
+
+		@Override
+		public Iterator<Point2D> iterator() {
+			return new PointIterator();
+		}
+
+		@Override
+		public Object[] toArray() {
+			return Path2f.this.toPointArray();
+		}
+
+		@SuppressWarnings("unchecked")
+		@Override
+		public <T> T[] toArray(T[] a) {
+			Iterator<Point2D> iterator = new PointIterator();
+			for(int i=0; i<a.length && iterator.hasNext(); ++i) {
+				a[i] = (T)iterator.next();
+			}
+			return a;
+		}
+
+		@Override
+		public boolean add(Point2D e) {
+			if (e!=null) {
+				if (Path2f.this.size()==0) {
+					Path2f.this.moveTo(e.getX(), e.getY());
+				}
+				else {
+					Path2f.this.lineTo(e.getX(), e.getY());
+				}
+				return true;
+			}
+			return false;
+		}
+
+		@Override
+		public boolean remove(Object o) {
+			if (o instanceof Point2D) {
+				Point2D p = (Point2D)o;
+				return Path2f.this.remove(p.getX(), p.getY());
+			}
+			return false;
+		}
+
+		@Override
+		public boolean containsAll(Collection<?> c) {
+			for(Object obj : c) {
+				if ((!(obj instanceof Point2D))
+					||(!Path2f.this.containsPoint((Point2D)obj))) {
+					return false;
+				}
+			}
+			return true;
+		}
+
+		@Override
+		public boolean addAll(Collection<? extends Point2D> c) {
+			boolean changed = false;
+			for(Point2D pts : c) {
+				if (add(pts)) {
+					changed = true;
+				}
+			}
+			return changed;
+		}
+
+		@Override
+		public boolean removeAll(Collection<?> c) {
+			boolean changed = false;
+			for(Object obj : c) {
+				if (obj instanceof Point2D) {
+					Point2D pts = (Point2D)obj;
+					if (Path2f.this.remove(pts.getX(), pts.getY())) {
+						changed = true;
+					}
+				}
+			}
+			return changed;
+		}
+
+		@Override
+		public boolean retainAll(Collection<?> c) {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public void clear() {
+			Path2f.this.clear();
+		}
+		
+	} // class PointCollection
+	
+	/** Iterator on the points of the path.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private class PointIterator implements Iterator<Point2D> {
+
+		private int index = 0;
+		private Point2D lastReplied = null;
+		
+		/**
+		 */
+		public PointIterator() {
+			//
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.index<Path2f.this.size();
+		}
+
+		@Override
+		public Point2D next() {
+			try {
+				this.lastReplied = Path2f.this.getPointAt(this.index++);
+				return this.lastReplied;
+			}
+			catch(Throwable _) {
+				throw new NoSuchElementException();
+			}
+		}
+
+		@Override
+		public void remove() {
+			Point2D p = this.lastReplied;
+			this.lastReplied = null;
+			if (p==null)
+				throw new NoSuchElementException();
+			Path2f.this.remove(p.getX(), p.getY());
+		}
+		
+	}
+	
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/PathElement2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/PathElement2f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/PathElement2f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,443 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import java.io.Serializable;
+
+import org.arakhne.afc.math.generic.PathElementType;
+
+/** An element of the path.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class PathElement2f implements Serializable {
+	
+	private static final long serialVersionUID = 8963281073370254033L;
+	
+	/** Create an instance of path element.
+	 * 
+	 * @param type is the type of the new element.
+	 * @param lastX is the coordinate of the last point.
+	 * @param lastY is the coordinate of the last point.
+	 * @param coords are the coordinates.
+	 * @return the instance of path element.
+	 */
+	public static PathElement2f newInstance(PathElementType type, float lastX, float lastY, float[] coords) {
+		switch(type) {
+		case MOVE_TO:
+			return new MovePathElement2f(coords[0], coords[1]);
+		case LINE_TO:
+			return new LinePathElement2f(lastX, lastY, coords[0], coords[1]);
+		case QUAD_TO:
+			return new QuadPathElement2f(lastX, lastY, coords[0], coords[1], coords[2], coords[3]);
+		case CURVE_TO:
+			return new CurvePathElement2f(lastX, lastY, coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
+		case CLOSE:
+			return new ClosePathElement2f(lastX, lastY, coords[0], coords[1]);
+		default:
+		}
+		throw new IllegalArgumentException();
+	}
+	
+	/** Type of the path element.
+	 */
+	public final PathElementType type;
+	
+	/** Source point.
+	 */
+	public final float fromX;
+	
+	/** Source point.
+	 */
+	public final float fromY;
+
+	/** Target point.
+	 */
+	public final float toX;
+	
+	/** Target point.
+	 */
+	public final float toY;
+
+	/** First control point.
+	 */
+	public final float ctrlX1;
+	
+	/** First control point.
+	 */
+	public final float ctrlY1;
+
+	/** Second control point.
+	 */
+	public final float ctrlX2;
+	
+	/** Second control point.
+	 */
+	public final float ctrlY2;
+
+	/**
+	 * @param type is the type of the element.
+	 * @param fromx is the source point.
+	 * @param fromy is the source point.
+	 * @param ctrlx1 is the first control point.
+	 * @param ctrly1 is the first control point.
+	 * @param ctrlx2 is the first control point.
+	 * @param ctrly2 is the first control point.
+	 * @param tox is the target point.
+	 * @param toy is the target point.
+	 */
+	public PathElement2f(PathElementType type, float fromx, float fromy, float ctrlx1, float ctrly1, float ctrlx2, float ctrly2, float tox, float toy) {
+		assert(type!=null);
+		this.type = type;
+		this.fromX = fromx;
+		this.fromY = fromy;
+		this.ctrlX1 = ctrlx1;
+		this.ctrlY1 = ctrly1;
+		this.ctrlX2 = ctrlx2;
+		this.ctrlY2 = ctrly2;
+		this.toX = tox;
+		this.toY = toy;
+	}
+
+	/** Replies if the element is empty, ie. the points are the same.
+	 * 
+	 * @return <code>true</code> if the points are
+	 * the same; otherwise <code>false</code>.
+	 */
+	public abstract boolean isEmpty();
+	
+	/** Replies if the element is not empty and its drawable.
+	 * Only the path elements that may produce pixels on the screen
+	 * must reply <code>true</code> in this function.
+	 * 
+	 * @return <code>true</code> if the path element
+	 * is drawable; otherwise <code>false</code>.
+	 */
+	public abstract boolean isDrawable();
+
+	/** Copy the coords into the given array, except the source point.
+	 * 
+	 * @param array
+	 */
+	public abstract void toArray(float[] array);
+
+	/** Copy the coords into an array, except the source point.
+	 * 
+	 * @return the array of the points, except the source point.
+	 */
+	public abstract float[] toArray();
+
+	/** An element of the path that represents a <code>MOVE_TO</code>.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public static class MovePathElement2f extends PathElement2f {
+		
+		private static final long serialVersionUID = -5596181248741970433L;
+
+		/**
+		 * @param x
+		 * @param y
+		 */
+		public MovePathElement2f(float x, float y) {
+			super(PathElementType.MOVE_TO,
+					Float.NaN, Float.NaN,
+					Float.NaN, Float.NaN,
+					Float.NaN, Float.NaN,
+					x, y);
+		}
+
+		@Override
+		public boolean isEmpty() {
+			return (this.fromX==this.toX) && (this.fromY==this.toY);
+		}
+
+		@Override
+		public boolean isDrawable() {
+			return false;
+		}
+		
+		@Override
+		public void toArray(float[] array) {
+			array[0] = this.toX;
+			array[1] = this.toY;
+		}
+		
+		@Override
+		public float[] toArray() {
+			return new float[] {this.toX, this.toY};
+		}
+
+		@Override
+		public String toString() {
+			return "MOVE("+ //$NON-NLS-1$
+					this.toX+"x"+ //$NON-NLS-1$
+					this.toY+")"; //$NON-NLS-1$
+		}
+
+	}
+	
+	/** An element of the path that represents a <code>LINE_TO</code>.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public static class LinePathElement2f extends PathElement2f {
+		
+		private static final long serialVersionUID = -5878571187312098882L;
+
+		/**
+		 * @param fromx
+		 * @param fromy
+		 * @param tox
+		 * @param toy
+		 */
+		public LinePathElement2f(float fromx, float fromy, float tox, float toy) {
+			super(PathElementType.LINE_TO,
+					fromx, fromy,
+					Float.NaN, Float.NaN,
+					Float.NaN, Float.NaN,
+					tox, toy);
+		}
+		
+		@Override
+		public boolean isEmpty() {
+			return (this.fromX==this.toX) && (this.fromY==this.toY);
+		}
+
+		@Override
+		public boolean isDrawable() {
+			return !isEmpty();
+		}
+
+		@Override
+		public void toArray(float[] array) {
+			array[0] = this.toX;
+			array[1] = this.toY;
+		}
+		
+		@Override
+		public float[] toArray() {
+			return new float[] {this.toX, this.toY};
+		}
+
+		@Override
+		public String toString() {
+			return "LINE("+ //$NON-NLS-1$
+					this.toX+"x"+ //$NON-NLS-1$
+					this.toY+")"; //$NON-NLS-1$
+		}
+
+	}
+	
+	/** An element of the path that represents a <code>QUAD_TO</code>.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public static class QuadPathElement2f extends PathElement2f {
+		
+		private static final long serialVersionUID = 5641358330446739160L;
+
+		/**
+		 * @param fromx
+		 * @param fromy
+		 * @param ctrlx
+		 * @param ctrly
+		 * @param tox
+		 * @param toy
+		 */
+		public QuadPathElement2f(float fromx, float fromy, float ctrlx, float ctrly, float tox, float toy) {
+			super(PathElementType.QUAD_TO,
+					fromx, fromy,
+					ctrlx, ctrly,
+					Float.NaN, Float.NaN,
+					tox, toy);
+		}
+		
+		@Override
+		public boolean isEmpty() {
+			return (this.fromX==this.toX) && (this.fromY==this.toY) &&
+					(this.ctrlX1==this.toX) && (this.ctrlY1==this.toY);
+		}
+
+		@Override
+		public boolean isDrawable() {
+			return !isEmpty();
+		}
+
+		@Override
+		public void toArray(float[] array) {
+			array[0] = this.ctrlX1;
+			array[1] = this.ctrlY1;
+			array[2] = this.toX;
+			array[3] = this.toY;
+		}
+		
+		@Override
+		public float[] toArray() {
+			return new float[] {this.ctrlX1, this.ctrlY1, this.toX, this.toY};
+		}
+		
+		@Override
+		public String toString() {
+			return "QUAD("+ //$NON-NLS-1$
+					this.ctrlX1+"x"+ //$NON-NLS-1$
+					this.ctrlY1+"|"+ //$NON-NLS-1$
+					this.toX+"x"+ //$NON-NLS-1$
+					this.toY+")"; //$NON-NLS-1$
+		}
+
+	}
+
+	/** An element of the path that represents a <code>CURVE_TO</code>.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public static class CurvePathElement2f extends PathElement2f {
+		
+		private static final long serialVersionUID = -1449309552719221756L;
+
+		/**
+		 * @param fromx
+		 * @param fromy
+		 * @param ctrlx1
+		 * @param ctrly1
+		 * @param ctrlx2
+		 * @param ctrly2
+		 * @param tox
+		 * @param toy
+		 */
+		public CurvePathElement2f(float fromx, float fromy, float ctrlx1, float ctrly1, float ctrlx2, float ctrly2, float tox, float toy) {
+			super(PathElementType.CURVE_TO,
+					fromx, fromy,
+					ctrlx1, ctrly1,
+					ctrlx2, ctrly2,
+					tox, toy);
+		}
+		
+		@Override
+		public boolean isEmpty() {
+			return (this.fromX==this.toX) && (this.fromY==this.toY) &&
+					(this.ctrlX1==this.toX) && (this.ctrlY1==this.toY) &&
+					(this.ctrlX2==this.toX) && (this.ctrlY2==this.toY);
+		}
+
+		@Override
+		public boolean isDrawable() {
+			return !isEmpty();
+		}
+
+		@Override
+		public void toArray(float[] array) {
+			array[0] = this.ctrlX1;
+			array[1] = this.ctrlY1;
+			array[2] = this.ctrlX2;
+			array[3] = this.ctrlY2;
+			array[4] = this.toX;
+			array[5] = this.toY;
+		}
+		
+		@Override
+		public float[] toArray() {
+			return new float[] {this.ctrlX1, this.ctrlY1, this.ctrlX2, this.ctrlY2, this.toX, this.toY};
+		}
+
+		@Override
+		public String toString() {
+			return "CURVE("+ //$NON-NLS-1$
+					this.ctrlX1+"x"+ //$NON-NLS-1$
+					this.ctrlY1+"|"+ //$NON-NLS-1$
+					this.ctrlX2+"x"+ //$NON-NLS-1$
+					this.ctrlY2+"|"+ //$NON-NLS-1$
+					this.toX+"x"+ //$NON-NLS-1$
+					this.toY+")"; //$NON-NLS-1$
+		}
+
+	}
+
+	/** An element of the path that represents a <code>CLOSE</code>.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public static class ClosePathElement2f extends PathElement2f {
+		
+		private static final long serialVersionUID = 4643537091880303796L;
+
+		/**
+		 * @param fromx
+		 * @param fromy
+		 * @param tox
+		 * @param toy
+		 */
+		public ClosePathElement2f(float fromx, float fromy, float tox, float toy) {
+			super(PathElementType.CLOSE,
+					fromx, fromy,
+					Float.NaN, Float.NaN,
+					Float.NaN, Float.NaN,
+					tox, toy);
+		}
+		
+		@Override
+		public boolean isEmpty() {
+			return (this.fromX==this.toX) && (this.fromY==this.toY);
+		}
+		
+		@Override
+		public boolean isDrawable() {
+			return false;
+		}
+
+		@Override
+		public void toArray(float[] array) {
+			//
+		}
+		
+		@Override
+		public float[] toArray() {
+			return new float[0];
+		}
+		
+		@Override
+		public String toString() {
+			return "CLOSE"; //$NON-NLS-1$
+		}
+
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/PathElementType.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/PathElementType.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/PathElementType.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,54 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+
+/** Type of a path element.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public enum PathElementType {
+
+	/** Move to the next point.
+	 */
+	MOVE_TO,
+
+	/** Line to the next point.
+	 */
+	LINE_TO,
+
+	/** Quadratic curve to the next point.
+	 */
+	QUAD_TO,
+
+	/** Cubic curve to the next point.
+	 */
+	CURVE_TO,
+
+	/** Close the path.
+	 */
+	CLOSE;
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/PathIterator2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/PathIterator2f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/PathIterator2f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,44 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import java.util.Iterator;
+
+import org.arakhne.afc.math.generic.PathWindingRule;
+
+
+/** This interface describes an interator on path elements.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface PathIterator2f extends Iterator<PathElement2f> {
+
+	/** Replies the winding rule for the path.
+	 * 
+	 * @return the winding rule for the path.
+	 */
+	public PathWindingRule getWindingRule();
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/PathWindingRule.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/PathWindingRule.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/PathWindingRule.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,56 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+
+/** The winding rule to determine the interior of a path.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public enum PathWindingRule {
+
+	/** he winding rule constant for specifying an even-odd rule
+     * for determining the interior of a path.
+     * The even-odd rule specifies that a point lies inside the
+     * path if a ray drawn in any direction from that point to
+     * infinity is crossed by path segments an odd number of times.
+     * <p>
+     * <center><img src="./doc-files/fillrule-evenodd.png" /></center>
+	 */
+	EVEN_ODD,
+
+	/** The winding rule constant for specifying a non-zero rule
+     * for determining the interior of a path.
+     * The non-zero rule specifies that a point lies inside the
+     * path if a ray drawn in any direction from that point to
+     * infinity is crossed by path segments a different number
+     * of times in the counter-clockwise direction than the
+     * clockwise direction.
+     * <p>
+     * <center><img src="./doc-files/fillrule-nonzero.png" /></center>
+	 */
+	NON_ZERO;
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Point2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Point2f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Point2f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,208 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Tuple2D;
+import org.arakhne.afc.math.generic.Vector2D;
+
+/** 2D Point with 2 floating-point numbers.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Point2f extends Tuple2f<Point2D> implements Point2D {
+
+	private static final long serialVersionUID = 8963319137253544821L;
+
+	/**
+	 */
+	public Point2f() {
+		//
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Point2f(Tuple2D<?> tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Point2f(int[] tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Point2f(float[] tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Point2f(int x, int y) {
+		super(x,y);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Point2f(float x, float y) {
+		super(x,y);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Point2f(double x, double y) {
+		super((float)x,(float)y);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Point2f(long x, long y) {
+		super(x,y);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Point2f clone() {
+		return (Point2f)super.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float distanceSquared(Point2D p1) {
+	      float dx, dy;
+	      dx = this.x-p1.getX();  
+	      dy = this.y-p1.getY();
+	      return (dx*dx+dy*dy);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float distance(Point2D p1) {
+	      float  dx, dy;
+	      dx = this.x-p1.getX();  
+	      dy = this.y-p1.getY();
+	      return (float)Math.sqrt(dx*dx+dy*dy);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float distanceL1(Point2D p1) {
+	      return (Math.abs(this.x-p1.getX()) + Math.abs(this.y-p1.getY()));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float distanceLinf(Point2D p1) {
+	      return Math.max( Math.abs(this.x-p1.getX()), Math.abs(this.y-p1.getY()));
+	}
+
+	@Override
+	public void add(Point2D t1, Vector2D t2) {
+		this.x = t1.getX() + t2.getX();
+		this.y = t1.getY() + t2.getY();
+	}
+
+	@Override
+	public void add(Vector2D t1, Point2D t2) {
+		this.x = t1.getX() + t2.getX();
+		this.y = t1.getY() + t2.getY();
+	}
+
+	@Override
+	public void add(Vector2D t1) {
+		this.x += t1.getX();
+		this.y += t1.getY();
+	}
+
+	@Override
+	public void scaleAdd(int s, Vector2D t1, Point2D t2) {
+		this.x = s * t1.getX() + t2.getX();
+		this.y = s * t1.getY() + t2.getY();
+	}
+
+	@Override
+	public void scaleAdd(float s, Vector2D t1, Point2D t2) {
+		this.x = s * t1.getX() + t2.getX();
+		this.y = s * t1.getY() + t2.getY();
+	}
+
+	@Override
+	public void scaleAdd(int s, Point2D t1, Vector2D t2) {
+		this.x = s * t1.getX() + t2.getX();
+		this.y = s * t1.getY() + t2.getY();
+	}
+
+	@Override
+	public void scaleAdd(float s, Point2D t1, Vector2D t2) {
+		this.x = s * t1.getX() + t2.getX();
+		this.y = s * t1.getY() + t2.getY();
+	}
+
+	@Override
+	public void scaleAdd(int s, Vector2D t1) {
+		this.x = s * this.x + t1.getX();
+		this.y = s * this.y + t1.getY();
+	}
+
+	@Override
+	public void scaleAdd(float s, Vector2D t1) {
+		this.x = s * this.x + t1.getX();
+		this.y = s * this.y + t1.getY();
+	}
+
+	@Override
+	public void sub(Point2D t1, Vector2D t2) {
+		this.x = t1.getX() - t2.getX();
+		this.y = t1.getY() - t2.getY();
+	}
+
+	@Override
+	public void sub(Vector2D t1) {
+		this.x -= t1.getX();
+		this.y -= t1.getY();
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Rectangle2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Rectangle2f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Rectangle2f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,900 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import java.util.NoSuchElementException;
+
+import org.arakhne.afc.math.MathUtil;
+import org.arakhne.afc.math.generic.PathWindingRule;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Vector2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+
+/** 2D rectangle with floating-point points.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Rectangle2f extends AbstractRectangularShape2f<Rectangle2f> {
+
+	private static final long serialVersionUID = 8716296371653330467L;
+
+	/** Replies if two rectangles are intersecting.
+	 * 
+	 * @param x1 is the first corner of the first rectangle.
+	 * @param y1 is the first corner of the first rectangle.
+	 * @param x2 is the second corner of the first rectangle.
+	 * @param y2 is the second corner of the first rectangle.
+	 * @param x3 is the first corner of the second rectangle.
+	 * @param y3 is the first corner of the second rectangle.
+	 * @param x4 is the second corner of the second rectangle.
+	 * @param y4 is the second corner of the second rectangle.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsRectangleRectangle(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
+		assert(x1<=x2);
+		assert(y1<=y2);
+		assert(x3<=x4);
+		assert(y3<=y4);
+		return x2 > x3
+				&&
+				x1 < x4
+				&&
+				y2 > y3
+				&&
+				y1 < y4;
+	}
+
+	/** Replies if two rectangles are intersecting.
+	 * 
+	 * @param x1 is the first corner of the rectangle.
+	 * @param y1 is the first corner of the rectangle.
+	 * @param x2 is the second corner of the rectangle.
+	 * @param y2 is the second corner of the rectangle.
+	 * @param x3 is the first point of the line.
+	 * @param y3 is the first point of the line.
+	 * @param x4 is the second point of the line.
+	 * @param y4 is the second point of the line.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsRectangleLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
+		int a, b;
+		a = MathUtil.ccw(x3, y3, x4, y4, x1, y1, false);
+		b = MathUtil.ccw(x3, y3, x4, y4, x2, y1, false);
+		if (a!=b && b!=0) return true;
+		b = MathUtil.ccw(x3, y3, x4, y4, x2, y2, false);
+		if (a!=b && b!=0) return true;
+		b = MathUtil.ccw(x3, y3, x4, y4, x1, y2, false);
+		return (a!=b && b!=0);
+	}
+
+	private static final int CS_INSIDE = 0; // 0000
+	private static final int CS_LEFT = 1; // 0001
+	private static final int CS_RIGHT = 2; // 0010
+	private static final int CS_BOTTOM = 4; // 0100
+	private static final int CS_TOP = 8; // 1000
+
+	private static int getCohenSutherlandRegion(float x1, float y1, float x2, float y2, float px, float py) {
+		int region = CS_INSIDE;
+		if (px<x1) {
+			region |= CS_LEFT;
+		}
+		else if (px>x2) {
+			region |= CS_RIGHT;
+		}
+		if (py<y1) {
+			region |= CS_BOTTOM;
+		}
+		else if (py>y2) {
+			region |= CS_TOP;
+		}
+		return region;
+	}
+
+	/** Replies if two rectangles are intersecting.
+	 * 
+	 * @param x1 is the first corner of the rectangle.
+	 * @param y1 is the first corner of the rectangle.
+	 * @param x2 is the second corner of the rectangle.
+	 * @param y2 is the second corner of the rectangle.
+	 * @param x3 is the first point of the segment.
+	 * @param y3 is the first point of the segment.
+	 * @param x4 is the second point of the segment.
+	 * @param y4 is the second point of the segment.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsRectangleSegment(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
+		float px1 = x3;
+		float py1 = y3;
+		float px2 = x4;
+		float py2 = y4;
+
+		// Cohen–Sutherland algorithm
+		int r1 = getCohenSutherlandRegion(x1, y1, x2, y2, px1, py1);
+		int r2 = getCohenSutherlandRegion(x1, y1, x2, y2, px2, py2);
+		boolean accept = false;
+
+		while (true) {
+			if ((r1 | r2)==0) {
+				// Bitwise OR is 0. Trivially accept and get out of loop
+				accept =  true;
+				break;//to speed up the algorithm
+			}
+			if ((r1 & r2)!=0) {
+				// Bitwise AND is not 0. Trivially reject and get out of loop
+				break;
+			}
+
+			// failed both tests, so calculate the line segment to clip
+			// from an outside point to an intersection with clip edge
+			float x, y;
+
+			// At least one endpoint is outside the clip rectangle; pick it.
+			int outcodeOut = r1!=0 ? r1 : r2;
+
+			// Now find the intersection point;
+			// use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
+			if ((outcodeOut & CS_TOP)!=0) { // point is above the clip rectangle
+				x = px1 + (px2 - px1) * (y2 - py1) / (py2 - py1);
+				y = y2;
+			}
+			else if ((outcodeOut & CS_BOTTOM)!=0) { // point is below the clip rectangle
+				x = px1 + (px2 - px1) * (y1- py1) / (py2 - py1);
+				y = y1;
+			}
+			else if ((outcodeOut & CS_RIGHT)!=0) {  // point is to the right of clip rectangle
+				y = py1 + (py2 - py1) * (x2 - px1) / (px2 - px1);
+				x = x2;
+			}
+			else {
+				//else if ((outcodeOut & CS_LEFT)!=0) {   // point is to the left of clip rectangle
+				y = py1 + (py2 - py1) * (x1 - px1) / (px2 - px1);
+				x = x1;
+			}
+
+			//NOTE:*****************************************************************************************
+
+			/* if you follow this algorithm exactly(at least for c#), then you will fall into an infinite loop 
+            in case a line crosses more than two segments. to avoid that problem, leave out the last else
+            if(outcodeOut & LEFT) and just make it else*/
+
+			//**********************************************************************************************
+
+			// Now we move outside point to intersection point to clip
+			// and get ready for next pass.
+			if (outcodeOut == r1) {
+				px1 = x;
+				py1 = y;
+				r1 = getCohenSutherlandRegion(x1, y1, x2, y2, px1, py1);
+			}
+			else {
+				px2 = x;
+				py2 = y;
+				r2 = getCohenSutherlandRegion(x1, y1, x2, y2, px2, py2);
+			}
+		}
+
+		return accept;
+	}
+
+	/** Compute the union of r1 and r2.
+	 * 
+	 * @param dest is the union.
+	 * @param r1
+	 * @param r2
+	 */
+	public static void union(Rectangle2f dest, Rectangle2f r1, Rectangle2f r2) {
+		dest.setFromCorners(
+				Math.min(r1.getMinX(), r2.getMinX()),
+				Math.min(r1.getMinY(), r2.getMinY()),
+				Math.max(r1.getMaxX(), r2.getMaxX()),
+				Math.max(r1.getMaxY(), r2.getMaxY()));
+	}
+
+	/** Replies if a rectangle is inside in the rectangle.
+	 * 
+	 * @param rx1 is the lowest corner of the enclosing-candidate rectangle.
+	 * @param ry1 is the lowest corner of the enclosing-candidate rectangle.
+	 * @param rwidth1 is the width of the enclosing-candidate rectangle.
+	 * @param rheight1 is the height of the enclosing-candidate rectangle.
+	 * @param rx2 is the lowest corner of the inner-candidate rectangle.
+	 * @param ry2 is the lowest corner of the inner-candidate rectangle.
+	 * @param rwidth2 is the width of the inner-candidate rectangle.
+	 * @param rheight2 is the height of the inner-candidate rectangle.
+	 * @return <code>true</code> if the given rectangle is inside the ellipse;
+	 * otherwise <code>false</code>.
+	 */
+	public static boolean containsRectangleRectangle(float rx1, float ry1, float rwidth1, float rheight1, float rx2, float ry2, float rwidth2, float rheight2) {
+		if (rwidth1<=0f || rwidth2<=0f || rheight1<=0 || rheight2<=0f) {
+            return false;
+        }
+        return (rx2 >= rx1 &&
+                ry2 >= ry1 &&
+                (rx2 + rwidth2) <= rx1 + rwidth1 &&
+                (ry2 + rheight2) <= ry1 + rheight1);
+	}
+
+	/** Compute the intersection of r1 and r2.
+	 * 
+	 * @param dest is the intersection.
+	 * @param r1
+	 * @param r2
+	 */
+	public static void intersection(Rectangle2f dest, Rectangle2f r1, Rectangle2f r2) {
+		float x1 = Math.max(r1.getMinX(), r2.getMinX());
+		float y1 = Math.max(r1.getMinY(), r2.getMinY());
+		float x2 = Math.min(r1.getMaxX(), r2.getMaxX());
+		float y2 = Math.min(r1.getMaxY(), r2.getMaxY());
+		if (x1<=x2 && y1<=y2) {
+			dest.setFromCorners(x1, y1, x2, y2);
+		}
+		else {
+			dest.set(0, 0, 0, 0);
+		}
+	}
+
+	/**
+	 */
+	public Rectangle2f() {
+		//
+	}
+
+	/**
+	 * @param min is the min corner of the rectangle.
+	 * @param max is the max corner of the rectangle.
+	 */
+	public Rectangle2f(Point2f min, Point2f max) {
+		super(min, max);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param width
+	 * @param height
+	 */
+	public Rectangle2f(float x, float y, float width, float height) {
+		super(x, y, width, height);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Rectangle2f toBoundingBox() {
+		return this;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceSquared(Point2D p) {
+		float dx;
+		if (p.getX()<getMinX()) {
+			dx = getMinX() - p.getX();
+		}
+		else if (p.getX()>getMaxX()) {
+			dx = p.getX() - getMaxX();
+		}
+		else {
+			dx = 0f;
+		}
+		float dy;
+		if (p.getY()<getMinY()) {
+			dy = getMinY() - p.getY();
+		}
+		else if (p.getY()>getMaxY()) {
+			dy = p.getY() - getMaxY();
+		}
+		else {
+			dy = 0f;
+		}
+		return dx*dx+dy*dy;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceL1(Point2D p) {
+		float dx;
+		if (p.getX()<getMinX()) {
+			dx = getMinX() - p.getX();
+		}
+		else if (p.getX()>getMaxX()) {
+			dx = p.getX() - getMaxX();
+		}
+		else {
+			dx = 0f;
+		}
+		float dy;
+		if (p.getY()<getMinY()) {
+			dy = getMinY() - p.getY();
+		}
+		else if (p.getY()>getMaxY()) {
+			dy = p.getY() - getMaxY();
+		}
+		else {
+			dy = 0f;
+		}
+		return dx + dy;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceLinf(Point2D p) {
+		float dx;
+		if (p.getX()<getMinX()) {
+			dx = getMinX() - p.getX();
+		}
+		else if (p.getX()>getMaxX()) {
+			dx = p.getX() - getMaxX();
+		}
+		else {
+			dx = 0f;
+		}
+		float dy;
+		if (p.getY()<getMinY()) {
+			dy = getMinY() - p.getY();
+		}
+		else if (p.getY()>getMaxY()) {
+			dy = p.getY() - getMaxY();
+		}
+		else {
+			dy = 0f;
+		}
+		return Math.max(dx, dy);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean contains(float x, float y) {
+		return (x>=getMinX() && x<=getMaxX())
+				&&
+				(y>=getMinY() && y<=getMaxY());
+	}
+	
+	@Override
+	public boolean contains(Rectangle2f r) {
+		return containsRectangleRectangle(
+				getMinX(), getMinY(), getWidth(), getHeight(),
+				r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight());
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Point2D getClosestPointTo(Point2D p) {
+		float x;
+		int same = 0;
+		if (p.getX()<getMinX()) {
+			x = getMinX();
+		}
+		else if (p.getX()>getMaxX()) {
+			x = getMaxX();
+		}
+		else {
+			x = p.getX();
+			++same;
+		}
+		float y;
+		if (p.getY()<getMinY()) {
+			y = getMinY();
+		}
+		else if (p.getY()>getMaxY()) {
+			y = getMaxY();
+		}
+		else {
+			y = p.getY();
+			++same;
+		}
+		if (same==2) return p;
+		return new Point2f(x,y);
+	}
+
+	/** Add the given coordinate in the rectangle.
+	 * <p>
+	 * The corners of the rectangles are moved to
+	 * enclosed the given coordinate.
+	 * 
+	 * @param p
+	 */
+	public void add(Point2D p) {
+		add(p.getX(), p.getY());
+	}
+
+	/** Add the given coordinate in the rectangle.
+	 * <p>
+	 * The corners of the rectangles are moved to
+	 * enclosed the given coordinate.
+	 * 
+	 * @param x
+	 * @param y
+	 */
+	public void add(float x, float y) {
+		if (x<getMinX()) {
+			setMinX(x);
+		}
+		else if (x>getMaxX()) {
+			setMaxX(x);
+		}
+		if (y<getMinY()) {
+			setMinY(y);
+		}
+		else if (y>getMaxY()) {
+			setMaxY(y);
+		}
+	}
+
+	/** Compute and replies the union of this rectangle and the given rectangle.
+	 * This function does not change this rectangle.
+	 * <p>
+	 * It is equivalent to (where <code>ur</code> is the union):
+	 * <pre><code>
+	 * Rectangle2f ur = new Rectangle2f();
+	 * Rectangle2f.union(ur, this, r);
+	 * </code></pre>
+	 * 
+	 * @param r
+	 * @return the union of this rectangle and the given rectangle.
+	 * @see #union(Rectangle2f, Rectangle2f, Rectangle2f)
+	 * @see #setUnion(Rectangle2f)
+	 */
+	public Rectangle2f createUnion(Rectangle2f r) {
+		Rectangle2f rr = new Rectangle2f();
+		union(rr, this, r);
+		return rr;
+	}
+
+	/** Compute and replies the intersection of this rectangle and the given rectangle.
+	 * This function does not change this rectangle.
+	 * <p>
+	 * It is equivalent to (where <code>ir</code> is the intersection):
+	 * <pre><code>
+	 * Rectangle2f ir = new Rectangle2f();
+	 * Rectangle2f.intersection(ir, this, r);
+	 * </code></pre>
+	 * 
+	 * @param r
+	 * @return the union of this rectangle and the given rectangle.
+	 * @see #intersection(Rectangle2f, Rectangle2f, Rectangle2f)
+	 * @see #setIntersection(Rectangle2f)
+	 */
+	public Rectangle2f createIntersection(Rectangle2f r) {
+		Rectangle2f rr = new Rectangle2f();
+		intersection(rr, this, r);
+		return rr;
+	}
+
+	/** Compute the union of this rectangle and the given rectangle and
+	 * change this rectangle with the result of the union.
+	 * <p>
+	 * It is equivalent to:
+	 * <pre><code>
+	 * Rectangle2f.union(this, this, r);
+	 * </code></pre>
+	 * 
+	 * @param r
+	 * @see #union(Rectangle2f, Rectangle2f, Rectangle2f)
+	 * @see #createUnion(Rectangle2f)
+	 */
+	public void setUnion(Rectangle2f r) {
+		union(this, this, r);
+	}
+
+	/** Compute the intersection of this rectangle and the given rectangle.
+	 * This function does not change this rectangle.
+	 * <p>
+	 * It is equivalent to:
+	 * <pre><code>
+	 * Rectangle2f.intersection(this, this, r);
+	 * </code></pre>
+	 * 
+	 * @param r
+	 * @see #intersection(Rectangle2f, Rectangle2f, Rectangle2f)
+	 * @see #createIntersection(Rectangle2f)
+	 */
+	public void setIntersection(Rectangle2f r) {
+		intersection(this, this, r);
+	}
+
+	@Override
+	public PathIterator2f getPathIterator(Transform2D transform) {
+		if (transform==null) {
+			return new CopyPathIterator(
+					getMinX(), getMinY(),
+					getMaxX(), getMaxY());
+		}
+		return new TransformPathIterator(
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY(),
+				transform);
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if (obj == this) {
+			return true;
+		}
+		if (obj instanceof Rectangle2f) {
+			Rectangle2f rr2d = (Rectangle2f) obj;
+			return ((getMinX() == rr2d.getMinX()) &&
+					(getMinY() == rr2d.getMinY()) &&
+					(getWidth() == rr2d.getWidth()) &&
+					(getHeight() == rr2d.getHeight()));
+		}
+		return false;
+	}
+
+	@Override
+	public int hashCode() {
+		long bits = 1L;
+		bits = 31L * bits + floatToIntBits(getMinX());
+		bits = 31L * bits + floatToIntBits(getMinY());
+		bits = 31L * bits + floatToIntBits(getMaxX());
+		bits = 31L * bits + floatToIntBits(getMaxY());
+		return (int) (bits ^ (bits >> 32));
+	}
+
+	@Override
+	public boolean intersects(Rectangle2f s) {
+		return intersectsRectangleRectangle(
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY(),
+				s.getMinX(), s.getMinY(),
+				s.getMaxX(), s.getMaxY());
+	}
+
+	@Override
+	public boolean intersects(Ellipse2f s) {
+		return Ellipse2f.intersectsEllipseRectangle(
+				s.getMinX(), s.getMinY(),
+				s.getMaxX(), s.getMaxY(),
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY());
+	}
+
+	@Override
+	public boolean intersects(Circle2f s) {
+		return Circle2f.intersectsCircleRectangle(
+				s.getX(), s.getY(),
+				s.getRadius(),
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY());
+	}
+
+	@Override
+	public boolean intersects(Segment2f s) {
+		return intersectsRectangleSegment(
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY(),
+				s.getX1(), s.getY1(),
+				s.getX2(), s.getY2());
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder b = new StringBuilder();
+		b.append("["); //$NON-NLS-1$
+		b.append(getMinX());
+		b.append(";"); //$NON-NLS-1$
+		b.append(getMinY());
+		b.append(";"); //$NON-NLS-1$
+		b.append(getMaxX());
+		b.append(";"); //$NON-NLS-1$
+		b.append(getMaxY());
+		b.append("]"); //$NON-NLS-1$
+		return b.toString();
+	}
+	
+	/** Move this rectangle to avoid collision 
+	 * with the reference rectangle.
+	 * 
+	 * @param reference is the rectangle to avoid collision with.
+	 * @return the displacement vector.
+	 */
+	public Vector2D avoidCollisionWith(Rectangle2f reference) {
+		float dx1 = reference.getMaxX() - getMinX();
+		float dx2 = getMaxX() - reference.getMinX();
+		float dy1 = reference.getMaxY() - getMinY();
+		float dy2 = getMaxY() - reference.getMinY();
+
+		float absdx1 = Math.abs(dx1);
+		float absdx2 = Math.abs(dx2);
+		float absdy1 = Math.abs(dy1);
+		float absdy2 = Math.abs(dy2);
+
+		float dx = 0;
+		float dy = 0;
+
+		if (dx1>=0 && absdx1<=absdx2 && absdx1<=absdy1 && absdx1<=absdy2) {
+			// Move according to dx1
+			dx = dx1; 
+		}
+		else if (dx2>=0 && absdx2<=absdx1 && absdx2<=absdy1 && absdx2<=absdy2) {
+			// Move according to dx2
+			dx = - dx2;
+		}
+		else if (dy1>=0 && absdy1<=absdx1 && absdy1<=absdx2 && absdy1<=absdy2) {
+			// Move according to dy1
+			dy = dy1; 
+		}
+		else {
+			// Move according to dy2
+			dy = - dy2;
+		}
+
+		set(
+				getMinX()+dx,
+				getMinY()+dy,
+				getWidth(),
+				getHeight());
+		
+		return new Vector2f(dx, dy);
+	}
+
+	/** Move this rectangle to avoid collision 
+	 * with the reference rectangle.
+	 * 
+	 * @param reference is the rectangle to avoid collision with.
+	 * @param displacementDirection is the direction of the allowed displacement.
+	 * @return the displacement vector.
+	 */
+	public Vector2D avoidCollisionWith(Rectangle2f reference, Vector2D displacementDirection) {
+		if (displacementDirection==null || displacementDirection.lengthSquared()==0f)
+			return avoidCollisionWith(reference);
+		
+		float dx1 = reference.getMaxX() - getMinX();
+		float dx2 = reference.getMinX() - getMaxX();
+		float dy1 = reference.getMaxY() - getMinY();
+		float dy2 = reference.getMinY() - getMaxY();
+
+		float absdx1 = Math.abs(dx1);
+		float absdx2 = Math.abs(dx2);
+		float absdy1 = Math.abs(dy1);
+		float absdy2 = Math.abs(dy2);
+		
+		float dx, dy;
+		
+		if (displacementDirection.getX()<0) {
+			dx = -Math.min(absdx1, absdx2);
+		}
+		else {
+			dx = Math.min(absdx1, absdx2);
+		}
+
+		if (displacementDirection.getY()<0) {
+			dy = -Math.min(absdy1, absdy2);
+		}
+		else {
+			dy = Math.min(absdy1, absdy2);
+		}
+
+		set(
+				getMinX()+dx,
+				getMinY()+dy,
+				getWidth(),
+				getHeight());
+
+		displacementDirection.set(dx, dy);
+		return displacementDirection;
+	}
+
+	/** Iterator on the path elements of the rectangle.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class CopyPathIterator implements PathIterator2f {
+		
+		private final float x1;
+		private final float y1;
+		private final float x2;
+		private final float y2;
+		private int index = 0;
+		
+		/**
+		 * @param x1
+		 * @param y1
+		 * @param x2
+		 * @param y2
+		 */
+		public CopyPathIterator(float x1, float y1, float x2, float y2) {
+			this.x1 = Math.min(x1, x2);
+			this.y1 = Math.min(y1, y2);
+			this.x2 = Math.max(x1, x2);
+			this.y2 = Math.max(y1, y2);
+			if (Math.abs(this.x1-this.x2)<=0f || Math.abs(this.y1-this.y2)<=0f) {
+				this.index = 6;
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.index<=5;
+		}
+
+		@Override
+		public PathElement2f next() {
+			int idx = this.index;
+			++this.index;
+			switch(idx) {
+			case 0:
+				return new PathElement2f.MovePathElement2f(
+						this.x1, this.y1);
+			case 1:
+				return new PathElement2f.LinePathElement2f(
+						this.x1, this.y1,
+						this.x2, this.y1);
+			case 2:
+				return new PathElement2f.LinePathElement2f(
+						this.x2, this.y1,
+						this.x2, this.y2);
+			case 3:
+				return new PathElement2f.LinePathElement2f(
+						this.x2, this.y2,
+						this.x1, this.y2);
+			case 4:
+				return new PathElement2f.LinePathElement2f(
+						this.x1, this.y2,
+						this.x1, this.y1);
+			case 5:
+				return new PathElement2f.ClosePathElement2f(
+						this.x1, this.y1,
+						this.x1, this.y1);
+			default:
+				throw new NoSuchElementException();
+			}
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return PathWindingRule.NON_ZERO;
+		}
+		
+	}
+
+	/** Iterator on the path elements of the rectangle.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class TransformPathIterator implements PathIterator2f {
+		
+		private final Transform2D transform;
+		private final float x1;
+		private final float y1;
+		private final float x2;
+		private final float y2;
+		private int index = 0;
+		
+		private final Point2D p1 = new Point2f();
+		private final Point2D p2 = new Point2f();
+		
+		/**
+		 * @param x1
+		 * @param y1
+		 * @param x2
+		 * @param y2
+		 * @param transform
+		 */
+		public TransformPathIterator(float x1, float y1, float x2, float y2, Transform2D transform) {
+			this.transform = transform;
+			this.x1 = Math.min(x1, x2);
+			this.y1 = Math.min(y1, y2);
+			this.x2 = Math.max(x1, x2);
+			this.y2 = Math.max(y1, y2);
+			if (Math.abs(this.x1-this.x2)<=0f || Math.abs(this.y1-this.y2)<=0f) {
+				this.index = 6;
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.index<=5;
+		}
+
+		@Override
+		public PathElement2f next() {
+			int idx = this.index;
+			++this.index;
+			switch(idx) {
+			case 0:
+				this.p2.set(this.x1, this.y1);
+				if (this.transform!=null) {
+					this.transform.transform(this.p2);
+				}
+				return new PathElement2f.MovePathElement2f(
+						this.p2.getX(), this.p2.getY());
+			case 1:
+				this.p1.set(this.p2);
+				this.p2.set(this.x2, this.y1);
+				if (this.transform!=null) {
+					this.transform.transform(this.p2);
+				}
+				return new PathElement2f.LinePathElement2f(
+						this.p1.getX(), this.p1.getY(),
+						this.p2.getX(), this.p2.getY());
+			case 2:
+				this.p1.set(this.p2);
+				this.p2.set(this.x2, this.y2);
+				if (this.transform!=null) {
+					this.transform.transform(this.p2);
+				}
+				return new PathElement2f.LinePathElement2f(
+						this.p1.getX(), this.p1.getY(),
+						this.p2.getX(), this.p2.getY());
+			case 3:
+				this.p1.set(this.p2);
+				this.p2.set(this.x1, this.y2);
+				if (this.transform!=null) {
+					this.transform.transform(this.p2);
+				}
+				return new PathElement2f.LinePathElement2f(
+						this.p1.getX(), this.p1.getY(),
+						this.p2.getX(), this.p2.getY());
+			case 4:
+				this.p1.set(this.p2);
+				this.p2.set(this.x1, this.y1);
+				if (this.transform!=null) {
+					this.transform.transform(this.p2);
+				}
+				return new PathElement2f.LinePathElement2f(
+						this.p1.getX(), this.p1.getY(),
+						this.p2.getX(), this.p2.getY());
+			case 5:
+				return new PathElement2f.ClosePathElement2f(
+						this.p2.getX(), this.p2.getY(),
+						this.p2.getX(), this.p2.getY());
+			default:
+				throw new NoSuchElementException();
+			}
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return PathWindingRule.NON_ZERO;
+		}
+		
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/RoundRectangle2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/RoundRectangle2f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/RoundRectangle2f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,699 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import java.util.NoSuchElementException;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.MathUtil;
+import org.arakhne.afc.math.generic.PathElementType;
+import org.arakhne.afc.math.generic.PathWindingRule;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+/** 2D round rectangle with floating-point points.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class RoundRectangle2f extends AbstractRectangularShape2f<RoundRectangle2f> {
+
+	private static final long serialVersionUID = 4681356809053380781L;
+
+	/** Replies if a rectangle is inside in the round rectangle.
+	 * 
+	 * @param rx1 is the lowest corner of the round rectangle.
+	 * @param ry1 is the lowest corner of the round rectangle.
+	 * @param rwidth1 is the width of the round rectangle.
+	 * @param rheight1 is the height of the round rectangle.
+	 * @param awidth is the width of the arc of the round rectangle.
+	 * @param aheight is the height of the arc of the round rectangle.
+	 * @param rx2 is the lowest corner of the inner-candidate rectangle.
+	 * @param ry2 is the lowest corner of the inner-candidate rectangle.
+	 * @param rwidth2 is the width of the inner-candidate rectangle.
+	 * @param rheight2 is the height of the inner-candidate rectangle.
+	 * @return <code>true</code> if the given rectangle is inside the ellipse;
+	 * otherwise <code>false</code>.
+	 */
+	public static boolean containsRoundRectangleRectangle(float rx1, float ry1, float rwidth1, float rheight1, float awidth, float aheight, float rx2, float ry2, float rwidth2, float rheight2) {
+		float rcx1 = (rx1 + rwidth1/2f);
+		float rcy1 = (ry1 + rheight1/2f);
+		float rcx2 = (rx2 + rwidth2/2f);
+		float rcy2 = (ry2 + rheight2/2f);
+		float farX;
+		if (rcx1<=rcx2) farX = rx2 + rwidth2;
+		else farX = rx2;
+		float farY;
+		if (rcy1<=rcy2) farY = ry2 + rheight2;
+		else farY = ry2;
+		return containsRoundRectanglePoint(rx1, ry1, rwidth1, rheight1, awidth, aheight, farX, farY);
+	}
+
+	/** Replies if a point is inside in the round rectangle.
+	 * 
+	 * @param rx is the lowest corner of the round rectangle.
+	 * @param ry is the lowest corner of the round rectangle.
+	 * @param rwidth is the width of the round rectangle.
+	 * @param rheight is the height of the round rectangle.
+	 * @param awidth is the width of the arc of the round rectangle.
+	 * @param aheight is the height of the arc of the round rectangle.
+	 * @param px is the point.
+	 * @param py is the point.
+	 * @return <code>true</code> if the given rectangle is inside the ellipse;
+	 * otherwise <code>false</code>.
+	 */
+	public static boolean containsRoundRectanglePoint(float rx, float ry, float rwidth, float rheight, float awidth, float aheight, float px, float py) {
+		if (rwidth<=0f && rheight<=0f) {
+			return rx==px && ry==py;
+		}
+		float rx0 = rx;
+		float ry0 = ry;
+		float rrx1 = rx0 + rwidth;
+		float rry1 = ry0 + rheight;
+		// Check for trivial rejection - point is outside bounding rectangle
+		if (px < rx0 || py < ry0 || px >= rrx1 || py >= rry1) {
+			return false;
+		}
+		float aw = Math.min(rwidth, Math.abs(awidth)) / 2f;
+		float ah = Math.min(rheight, Math.abs(aheight)) / 2f;
+		// Check which corner point is in and do circular containment
+		// test - otherwise simple acceptance
+		if (px >= (rx0 += aw) && px < (rx0 = rrx1 - aw)) {
+			return true;
+		}
+		if (py >= (ry0 += ah) && py < (ry0 = rry1 - ah)) {
+			return true;
+		}
+		float xx = (px - rx0) / aw;
+		float yy = (py - ry0) / ah;
+		return (xx * xx + yy * yy <= 1f);
+	}
+
+	private static final float ANGLE = MathConstants.PI / 4f;
+	private static final float A = 1f - (float)Math.cos(ANGLE);
+	private static final float B = (float)Math.tan(ANGLE);
+	private static final float C = (float)Math.sqrt(1f + B * B) - 1f + A;
+	private static final float CV = 4f / 3f * A * B / C;
+	private static final float ACV = (1f - CV) / 2f;
+	
+	/** For each array:
+	 * 4 values for each point {v0, v1, v2, v3}:
+	 * point = (x + v0 * w + v1 * arcWidth,
+	 * y + v2 * h + v3 * arcHeight)
+	 */
+	static float CTRL_PTS[][] = {
+		{  0f,  0f,  0f,  .5f },
+		{  0f,  0f,  1f, -.5f },
+		{  0f,  0f,  1f, -ACV,
+			0f,  ACV,  1f,  0f,
+			0f,  .5f,  1f,  0f },
+			{  1f, -.5f,  1f,  0f },
+			{  1f, -ACV,  1f,  0f,
+				1f,  0f,  1f, -ACV,
+				1f,  0f,  1f, -.5f },
+				{  1f,  0f,  0f,  .5f },
+				{  1f,  0f,  0f,  ACV,
+					1f, -ACV,  0f,  0f,
+					1f, -.5f,  0f,  0f },
+					{  0f,  .5f,  0f,  0f },
+					{  0f,  ACV,  0f,  0f,
+						0f,  0f,  0f,  ACV,
+						0f,  0f,  0f,  .5f },
+						{},
+	};
+	
+	/** Types of path elements for the round rectangle.
+	 */
+	static PathElementType TYPES[] = {
+		PathElementType.MOVE_TO,
+		PathElementType.LINE_TO, PathElementType.CURVE_TO,
+		PathElementType.LINE_TO, PathElementType.CURVE_TO,
+		PathElementType.LINE_TO, PathElementType.CURVE_TO,
+		PathElementType.LINE_TO, PathElementType.CURVE_TO,
+		PathElementType.CLOSE,
+	};
+
+	
+	/** Width of the arcs at the corner of the box. */
+	protected float arcWidth;
+	/** Height of the arcs at the corner of the box. */
+	protected float arcHeight;
+
+	/**
+	 */
+	public RoundRectangle2f() {
+		this.arcHeight = this.arcWidth = 0f;
+	}
+
+	/**
+	 * @param min is the min corner of the rectangle.
+	 * @param max is the max corner of the rectangle.
+	 * @param arcWidth
+	 * @param arcHeight
+	 */
+	public RoundRectangle2f(Point2f min, Point2f max, float arcWidth, float arcHeight) {
+		super(min, max);
+		this.arcWidth = arcWidth;
+		this.arcHeight = arcHeight;
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param width
+	 * @param height
+	 * @param arcWidth
+	 * @param arcHeight
+	 */
+	public RoundRectangle2f(float x, float y, float width, float height, float arcWidth, float arcHeight) {
+		super(x, y, width, height);
+		this.arcWidth = arcWidth;
+		this.arcHeight = arcHeight;
+	}
+	
+	@Override
+	public void clear() {
+		this.arcHeight = this.arcWidth = 0f;
+		super.clear();
+	}
+
+	/**
+	 * Gets the width of the arc that rounds off the corners.
+	 * @return the width of the arc that rounds off the corners
+	 * of this <code>RoundRectangle2f</code>.
+	 */
+	public float getArcWidth() {
+		return this.arcWidth;
+	}
+
+	/**
+	 * Gets the height of the arc that rounds off the corners.
+	 * @return the height of the arc that rounds off the corners
+	 * of this <code>RoundRectangle2f</code>.
+	 */
+	public float getArcHeight() {
+		return this.arcHeight;
+	}
+
+	/**
+	 * Set the width of the arc that rounds off the corners.
+	 * @param a is the width of the arc that rounds off the corners
+	 * of this <code>RoundRectangle2f</code>.
+	 */
+	public void setArcWidth(float a) {
+		this.arcWidth = a;
+	}
+
+	/**
+	 * Set the height of the arc that rounds off the corners.
+	 * @param a is the height of the arc that rounds off the corners
+	 * of this <code>RoundRectangle2f</code>.
+	 */
+	public void setArcHeight(float a) {
+		this.arcHeight = a;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean contains(float x, float y) {
+		return containsRoundRectanglePoint(
+				getMinX(), getMinY(), getWidth(), getHeight(), getArcWidth(), getArcHeight(),
+				x, y);
+	}
+	
+	@Override
+	public boolean contains(Rectangle2f r) {
+		return containsRoundRectangleRectangle(
+				getMinX(), getMinY(), getWidth(), getHeight(), getArcWidth(), getArcHeight(),
+				r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight());
+	}
+
+	@Override
+	public float distanceSquared(Point2D p) {
+		Point2D n = getClosestPointTo(p);
+		return n.distanceSquared(p);
+	}
+
+	@Override
+	public float distanceL1(Point2D p) {
+		Point2D n = getClosestPointTo(p);
+		return n.distanceL1(p);
+	}
+
+	@Override
+	public float distanceLinf(Point2D p) {
+		Point2D n = getClosestPointTo(p);
+		return n.distanceLinf(p);
+	}
+
+	@Override
+	public Point2D getClosestPointTo(Point2D p) {
+		float px = p.getX();
+		float py = p.getY();
+		float rx1 = getMinX();
+		float ry1 = getMinY();
+		float rx2 = getMaxX();
+		float ry2 = getMaxY();
+
+		float aw = getArcWidth();
+		float ah = getArcHeight();
+
+		int same = 0;
+		float x, y;
+
+		if (px<rx1+aw) {
+			if (py<ry1+ah) {
+				return MathUtil.getClosestPointToSolidEllipse(
+						px, py,
+						rx1, ry1,
+						aw, ah);
+			}
+			if (py>ry2-ah) {
+				return MathUtil.getClosestPointToSolidEllipse(
+						px, py,
+						rx1, ry2-ah,
+						aw, ah);
+			}
+		}
+		else if (px>rx2-aw) {
+			if (py<ry1+ah) {
+				return MathUtil.getClosestPointToSolidEllipse(
+						px, py,
+						rx2-aw, ry1,
+						aw, ah);
+			}
+			if (py>ry2-ah) {
+				return MathUtil.getClosestPointToSolidEllipse(
+						px, py,
+						rx2-aw, ry2-ah,
+						aw, ah);
+			}
+		}
+
+
+		if (px<rx1) {
+			x = rx1;
+		}
+		else if (px>rx2) {
+			x = rx2;
+		}
+		else {
+			x = px;
+			++same;
+		}
+
+		if (py<ry1) {
+			y = ry1;
+		}
+		else if (py>ry2) {
+			y = ry2;
+		}
+		else {
+			y = py;
+			++same;
+		}
+
+		if (same==2) return p;
+		return new Point2f(x,y);
+	}
+
+	@Override
+	public PathIterator2f getPathIterator(Transform2D transform) {
+		if (transform==null) {
+			return new CopyPathIterator(
+					getMinX(), getMinY(),
+					getWidth(), getHeight(),
+					getArcWidth(), getArcHeight());
+		}
+		return new TransformPathIterator(
+				getMinX(), getMinY(),
+				getWidth(), getHeight(),
+				getArcWidth(), getArcHeight(),
+				transform);
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if (obj == this) {
+			return true;
+		}
+		if (obj instanceof RoundRectangle2f) {
+			RoundRectangle2f rr2d = (RoundRectangle2f) obj;
+			return ((getMinX() == rr2d.getMinX()) &&
+					(getMinY() == rr2d.getMinY()) &&
+					(getWidth() == rr2d.getWidth()) &&
+					(getHeight() == rr2d.getHeight()) &&
+					(getArcWidth() == rr2d.getArcWidth()) &&
+					(getArcHeight() == rr2d.getArcHeight()));
+		}
+		return false;
+	}
+
+	@Override
+	public int hashCode() {
+		long bits = 1L;
+		bits = 31L * bits + floatToIntBits(getMinX());
+		bits = 31L * bits + floatToIntBits(getMinY());
+		bits = 31L * bits + floatToIntBits(getMaxX());
+		bits = 31L * bits + floatToIntBits(getMaxY());
+		bits = 31L * bits + floatToIntBits(getArcWidth());
+		bits = 31L * bits + floatToIntBits(getArcHeight());
+		return (int) (bits ^ (bits >> 32));
+	}
+
+	@Override
+	public Rectangle2f toBoundingBox() {
+		return new Rectangle2f(getMinX(), getMinY(), getWidth(), getHeight());
+	}
+
+	@Override
+	public boolean intersects(Rectangle2f s) {
+		return Rectangle2f.intersectsRectangleRectangle(
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY(),
+				s.getMinX(), s.getMinY(),
+				s.getMaxX(), s.getMaxY());
+	}
+
+	@Override
+	public boolean intersects(Ellipse2f s) {
+		return Ellipse2f.intersectsEllipseRectangle(
+				s.getMinX(), s.getMinY(),
+				s.getMaxX(), s.getMaxY(),
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY());
+	}
+
+	@Override
+	public boolean intersects(Circle2f s) {
+		return Circle2f.intersectsCircleRectangle(
+				s.getX(), s.getY(),
+				s.getRadius(),
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY());
+	}
+
+	@Override
+	public boolean intersects(Segment2f s) {
+		return Rectangle2f.intersectsRectangleSegment(
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY(),
+				s.getX1(), s.getY1(),
+				s.getX2(), s.getY2());
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder b = new StringBuilder();
+		b.append("["); //$NON-NLS-1$
+		b.append(getMinX());
+		b.append(";"); //$NON-NLS-1$
+		b.append(getMinY());
+		b.append(";"); //$NON-NLS-1$
+		b.append(getMaxX());
+		b.append(";"); //$NON-NLS-1$
+		b.append(getMaxY());
+		b.append("|"); //$NON-NLS-1$
+		b.append(getArcWidth());
+		b.append("x"); //$NON-NLS-1$
+		b.append(getArcHeight());
+		b.append("]"); //$NON-NLS-1$
+		return b.toString();
+	}
+
+	/** Iterator on the path elements of the rectangle.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class CopyPathIterator implements PathIterator2f {
+		
+		private final float x;
+		private final float y;
+		private final float w;
+		private final float h;
+		private final float aw;
+		private final float ah;
+		private int index = 0;
+		
+		private float moveX, moveY, lastX,  lastY;
+		
+		/**
+		 * @param x
+		 * @param y
+		 * @param w
+		 * @param h
+		 * @param aw
+		 * @param ah
+		 */
+		public CopyPathIterator(float x, float y, float w, float h, float aw, float ah) {
+			this.x = x;
+			this.y = y;
+			this.w = Math.max(0f, w);
+			this.h = Math.max(0f, h);
+			this.aw = Math.min(Math.abs(aw), w);
+			this.ah = Math.min(Math.abs(ah), h);
+			if (this.w<=0f || this.h<=0f) {
+				this.index = TYPES.length;
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.index<TYPES.length;
+		}
+		
+		@Override
+		public PathElement2f next() {
+			if (this.index>=TYPES.length) throw new NoSuchElementException();
+			int idx = this.index;
+
+			PathElement2f element = null;
+			PathElementType type = TYPES[idx];
+			float ctrls[] = CTRL_PTS[idx];
+			float ix, iy;
+			float ctrlx1, ctrly1, ctrlx2, ctrly2;
+			
+			switch(type) {
+			case MOVE_TO:
+				this.moveX = this.lastX = this.x + ctrls[0] * this.w + ctrls[1] * this.aw;
+				this.moveY = this.lastY = this.y + ctrls[2] * this.h + ctrls[3] * this.ah;
+		        element = new PathElement2f.MovePathElement2f(
+		        		this.lastX, this.lastY);
+		        break;
+			case LINE_TO:
+				ix = this.lastX;
+				iy = this.lastY;
+				this.lastX = this.x + ctrls[0] * this.w + ctrls[1] * this.aw;
+				this.lastY = this.y + ctrls[2] * this.h + ctrls[3] * this.ah;
+		        element = new PathElement2f.LinePathElement2f(
+		        		ix, iy,
+		        		this.lastX, this.lastY);
+		        break;
+			case CURVE_TO:
+				ix = this.lastX;
+				iy = this.lastY;
+				ctrlx1 = this.x + ctrls[0] * this.w + ctrls[1] * this.aw;
+				ctrly1 = this.y + ctrls[2] * this.h + ctrls[3] * this.ah;
+				ctrlx2 = this.x + ctrls[4] * this.w + ctrls[5] * this.aw;
+				ctrly2 = this.y + ctrls[6] * this.h + ctrls[7] * this.ah;
+				this.lastX = this.x + ctrls[8] * this.w + ctrls[9] * this.aw;
+				this.lastY = this.y + ctrls[10] * this.h + ctrls[11] * this.ah;
+		        element = new PathElement2f.CurvePathElement2f(
+		        		ix, iy,
+		        		ctrlx1, ctrly1,
+		        		ctrlx2, ctrly2,
+		        		this.lastX, this.lastY);
+		        break;
+			case CLOSE:
+				ix = this.lastX;
+				iy = this.lastY;
+				this.lastX = this.moveX;
+				this.lastY = this.moveY;
+		        element = new PathElement2f.ClosePathElement2f(
+		        		ix, iy,
+		        		this.lastX, this.lastY);
+				break;
+			case QUAD_TO:
+			default:
+				throw new NoSuchElementException();
+			}
+			
+			assert(element!=null);
+			
+			++this.index;
+			
+			return element;
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return PathWindingRule.NON_ZERO;
+		}
+		
+	}
+
+	/** Iterator on the path elements of the rectangle.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class TransformPathIterator implements PathIterator2f {
+		
+		private final Transform2D transform;
+		private final float x;
+		private final float y;
+		private final float w;
+		private final float h;
+		private final float aw;
+		private final float ah;
+		private int index = 0;
+		
+		private float moveX, moveY;
+		private final Point2D last = new Point2f();
+		private final Point2D ctrl1 = new Point2f();
+		private final Point2D ctrl2 = new Point2f();
+		
+		/**
+		 * @param x
+		 * @param y
+		 * @param w
+		 * @param h
+		 * @param aw
+		 * @param ah
+		 * @param transform
+		 */
+		public TransformPathIterator(float x, float y, float w, float h, float aw, float ah, Transform2D transform) {
+			this.transform = transform;
+			this.x = x;
+			this.y = y;
+			this.w = Math.max(0f, w);
+			this.h = Math.max(0f, h);
+			this.aw = Math.min(Math.abs(aw), w);
+			this.ah = Math.min(Math.abs(ah), h);
+			if (this.w<=0f || this.h<=0f) {
+				this.index = TYPES.length;
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.index<TYPES.length;
+		}
+		
+		@Override
+		public PathElement2f next() {
+			if (this.index>=TYPES.length) throw new NoSuchElementException();
+			int idx = this.index;
+
+			PathElement2f element = null;
+			PathElementType type = TYPES[idx];
+			float ctrls[] = CTRL_PTS[idx];
+			float ix, iy;
+			
+			switch(type) {
+			case MOVE_TO:
+				this.moveX = this.x + ctrls[0] * this.w + ctrls[1] * this.aw;
+				this.moveY = this.y + ctrls[2] * this.h + ctrls[3] * this.ah;
+				this.last.set(this.moveX, this.moveY);
+				this.transform.transform(this.last);
+		        element = new PathElement2f.MovePathElement2f(
+		        		this.last.getX(), this.last.getY());
+		        break;
+			case LINE_TO:
+				ix = this.last.getX();
+				iy = this.last.getY();
+				this.last.set(
+						this.x + ctrls[0] * this.w + ctrls[1] * this.aw,
+						this.y + ctrls[2] * this.h + ctrls[3] * this.ah);
+				this.transform.transform(this.last);
+		        element = new PathElement2f.LinePathElement2f(
+		        		ix, iy,
+		        		this.last.getX(), this.last.getY());
+		        break;
+			case CURVE_TO:
+				ix = this.last.getX();
+				iy = this.last.getY();
+				this.ctrl1.set(
+					this.x + ctrls[0] * this.w + ctrls[1] * this.aw,
+					this.y + ctrls[2] * this.h + ctrls[3] * this.ah);
+				this.transform.transform(this.ctrl1);
+				this.ctrl2.set(
+						this.x + ctrls[4] * this.w + ctrls[5] * this.aw,
+						this.y + ctrls[6] * this.h + ctrls[7] * this.ah);
+				this.transform.transform(this.ctrl2);
+				this.last.set(
+						this.x + ctrls[8] * this.w + ctrls[9] * this.aw,
+						this.y + ctrls[10] * this.h + ctrls[11] * this.ah);
+				this.transform.transform(this.last);
+		        element = new PathElement2f.CurvePathElement2f(
+		        		ix, iy,
+		        		this.ctrl1.getX(), this.ctrl1.getY(),
+		        		this.ctrl2.getX(), this.ctrl2.getY(),
+		        		this.last.getX(), this.last.getY());
+		        break;
+			case CLOSE:
+				ix = this.last.getX();
+				iy = this.last.getY();
+				this.last.set(this.moveX, this.moveY);
+				this.transform.transform(this.last);
+		        element = new PathElement2f.ClosePathElement2f(
+		        		ix, iy,
+		        		this.last.getX(), this.last.getY());
+				break;
+			case QUAD_TO:
+			default:
+				throw new NoSuchElementException();
+			}
+			
+			assert(element!=null);
+			
+			++this.index;
+			
+			return element;
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return PathWindingRule.NON_ZERO;
+		}
+		
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Segment2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Segment2f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Segment2f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,804 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import java.util.NoSuchElementException;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.MathUtil;
+import org.arakhne.afc.math.generic.PathWindingRule;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+
+/** 2D line segment with floating-point coordinates.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Segment2f extends AbstractShape2f<Segment2f> {
+
+	private static final long serialVersionUID = -82425036308183925L;
+
+	/**
+	 * Calculates the number of times the line from (x0,y0) to (x1,y1)
+	 * crosses the ray extending to the right from (px,py).
+	 * If the point lies on the line, then no crossings are recorded.
+	 * +1 is returned for a crossing where the Y coordinate is increasing
+	 * -1 is returned for a crossing where the Y coordinate is decreasing
+	 * 
+	 * @param px is the reference point to test.
+	 * @param py is the reference point to test.
+	 * @param x0 is the first point of the line.
+	 * @param y0 is the first point of the line.
+	 * @param x1 is the second point of the line.
+	 * @param y1 is the secondpoint of the line.
+	 * @return the crossing.
+	 */
+	public static int computeCrossingsFromPoint(
+			float px, float py,
+			float x0, float y0,
+			float x1, float y1) {
+		// Copied from AWT API
+		if (py <  y0 && py <  y1) return 0;
+		if (py >= y0 && py >= y1) return 0;
+		// assert(y0 != y1);
+		if (px >= x0 && px >= x1) return 0;
+		if (px <  x0 && px <  x1) return (y0 < y1) ? 1 : -1;
+		float xintercept = x0 + (py - y0) * (x1 - x0) / (y1 - y0);
+		if (px >= xintercept) return 0;
+		return (y0 < y1) ? 1 : -1;
+	}
+
+	/**
+	 * Calculates the number of times the line from (x0,y0) to (x1,y1)
+	 * crosses the segment (sx0,sy0) to (sx1,sy1) extending to the right.
+	 * 
+	 * @param crossings is the initial value for the number of crossings.
+	 * @param sx1 is the first point of the segment to extend.
+	 * @param sy1 is the first point of the segment to extend.
+	 * @param sx2 is the second point of the segment to extend.
+	 * @param sy2 is the second point of the segment to extend.
+	 * @param x0 is the first point of the line.
+	 * @param y0 is the first point of the line.
+	 * @param x1 is the second point of the line.
+	 * @param y1 is the secondpoint of the line.
+	 * @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}
+	 */
+	public static int computeCrossingsFromSegment(
+			int crossings,
+			float sx1, float sy1,
+			float sx2, float sy2,
+			float x0, float y0,
+			float x1, float y1) {
+		int numCrosses = crossings;
+
+		float xmin = Math.min(sx1, sx2);
+		float xmax = Math.max(sx1, sx2);
+		float ymin = Math.min(sy1, sy2);
+		float ymax = Math.max(sy1, sy2);
+
+		if (y0<=ymin && y1<=ymin) return numCrosses;
+		if (y0>=ymax && y1>=ymax) return numCrosses;
+		if (x0<=xmin && x1<=xmin) return numCrosses;
+		if (x0>=xmax && x1>=xmax) {
+			// The line is entirely at the right of the shadow
+			if (y0<y1) {
+				if (y0<=ymin) ++numCrosses;
+				if (y1>=ymax) ++numCrosses;
+			}
+			else {
+				if (y1<=ymin) --numCrosses;
+				if (y0>=ymax) --numCrosses;
+			}
+		}
+		else if (intersectsSegmentSegment(x0, y0, x1, y1, sx1, sy1, sx2, sy2)) {
+			return MathConstants.SHAPE_INTERSECTS;
+		}
+		else {
+			int side1, side2;
+			if (sy1<=sy2) {
+				side1 = MathUtil.sidePointLine(sx1, sy1, sx2, sy2, x0, y0, false);
+				side2 = MathUtil.sidePointLine(sx1, sy1, sx2, sy2, x1, y1, false);
+			}
+			else {
+				side1 = MathUtil.sidePointLine(sx2, sy2, sx1, sy1, x0, y0, false);
+				side2 = MathUtil.sidePointLine(sx2, sy2, sx1, sy1, x1, y1, false);
+			}
+			if (side1>0 || side2>0) {
+				int n1, n2;
+				n1 = computeCrossingsFromPoint(sx1, sy1, x0, y0, x1, y1);
+				n2 = computeCrossingsFromPoint(sx2, sy2, x0, y0, x1, y1);
+				numCrosses += n1 + n2;
+			}
+		}
+
+		return numCrosses;
+	}
+
+	/**
+	 * Calculates the number of times the line from (x0,y0) to (x1,y1)
+	 * crosses the ellipse (ex0,ey0) to (ex1,ey1) extending to the right.
+	 * 
+	 * @param crossings is the initial value for the number of crossings.
+	 * @param ex is the first corner of the ellipse to extend.
+	 * @param ey is the first corner of the ellipse to extend.
+	 * @param ew is the width of the ellipse to extend.
+	 * @param eh is the height of the ellipse  to extend.
+	 * @param x0 is the first point of the line.
+	 * @param y0 is the first point of the line.
+	 * @param x1 is the second point of the line.
+	 * @param y1 is the secondpoint of the line.
+	 * @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}.
+	 */
+	public static int computeCrossingsFromEllipse(
+			int crossings,
+			float ex, float ey,
+			float ew, float eh,
+			float x0, float y0,
+			float x1, float y1) {
+		int numCrosses = crossings;
+
+		float xmin = ex;
+		float ymin = ey;
+		float xmax = ex + Math.max(0, ew);
+		float ymax = ey + Math.max(0, eh);
+
+		if (y0<=ymin && y1<=ymin) return numCrosses;
+		if (y0>=ymax && y1>=ymax) return numCrosses;
+		if (x0<=xmin && x1<=xmin) return numCrosses;
+
+		if (x0>=xmax && x1>=xmax) {
+			// The line is entirely at the right of the shadow
+			if (y0<y1) {
+				if (y0<=ymin) ++numCrosses;
+				if (y1>=ymax) ++numCrosses;
+			}
+			else {
+				if (y1<=ymin) --numCrosses;
+				if (y0>=ymax) --numCrosses;
+			}
+		}
+		else if (Ellipse2f.intersectsEllipseSegment(
+				xmin, ymin, xmax, ymax,
+				x0, y0, x1, y1)) {
+			return MathConstants.SHAPE_INTERSECTS;
+		}
+		else {
+			float xcenter = (xmin+xmax)/2f;
+			numCrosses += computeCrossingsFromPoint(xcenter, ymin, x0, y0, x1, y1);
+			numCrosses += computeCrossingsFromPoint(xcenter, ymax, x0, y0, x1, y1);
+		}
+
+		return numCrosses;
+	}
+
+	/**
+	 * Calculates the number of times the line from (x0,y0) to (x1,y1)
+	 * crosses the ellipse (ex0,ey0) to (ex1,ey1) extending to the right.
+	 * 
+	 * @param crossings is the initial value for the number of crossings.
+	 * @param cx is the center of the circle to extend.
+	 * @param cy is the center of the circle to extend.
+	 * @param radius is the radius of the circle to extend.
+	 * @param x0 is the first point of the line.
+	 * @param y0 is the first point of the line.
+	 * @param x1 is the second point of the line.
+	 * @param y1 is the secondpoint of the line.
+	 * @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}.
+	 */
+	public static int computeCrossingsFromCircle(
+			int crossings,
+			float cx, float cy,
+			float radius,
+			float x0, float y0,
+			float x1, float y1) {
+		int numCrosses = crossings;
+
+		float xmin = cx - Math.abs(radius);
+		float ymin = cy - Math.abs(radius);
+		float ymax = cy + Math.abs(radius);
+
+		if (y0<=ymin && y1<=ymin) return numCrosses;
+		if (y0>=ymax && y1>=ymax) return numCrosses;
+		if (x0<=xmin && x1<=xmin) return numCrosses;
+
+		if (x0>=cx+radius && x1>=cx+radius) {
+			// The line is entirely at the right of the shadow
+			if (y0<y1) {
+				if (y0<=ymin) ++numCrosses;
+				if (y1>=ymax) ++numCrosses;
+			}
+			else {
+				if (y1<=ymin) --numCrosses;
+				if (y0>=ymax) --numCrosses;
+			}
+		}
+		else if (Circle2f.intersectsCircleSegment(
+				cx, cy, radius,
+				x0, y0, x1, y1)) {
+			return MathConstants.SHAPE_INTERSECTS;
+		}
+		else {
+			numCrosses += computeCrossingsFromPoint(cx, ymin, x0, y0, x1, y1);
+			numCrosses += computeCrossingsFromPoint(cx, ymax, x0, y0, x1, y1);
+		}
+
+		return numCrosses;
+	}
+
+	/**
+	 * Accumulate the number of times the line crosses the shadow
+	 * extending to the right of the rectangle.  See the comment
+	 * for the {@link MathUtil#SHAPE_INTERSECTS} constant for more complete details.
+	 * 
+	 * @param crossings is the initial value for the number of crossings.
+	 * @param rxmin is the first corner of the rectangle.
+	 * @param rymin is the first corner of the rectangle.
+	 * @param rxmax is the second corner of the rectangle.
+	 * @param rymax is the second corner of the rectangle.
+	 * @param x0 is the first point of the line.
+	 * @param y0 is the first point of the line.
+	 * @param x1 is the second point of the line.
+	 * @param y1 is the secondpoint of the line.
+	 * @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}.
+	 */
+	public static int computeCrossingsFromRect(
+			int crossings,
+			float rxmin, float rymin,
+			float rxmax, float rymax,
+			float x0, float y0,
+			float x1, float y1) {
+		int numCrosses = crossings;
+
+		if (y0 >= rymax && y1 >= rymax) return numCrosses;
+		if (y0 <= rymin && y1 <= rymin) return numCrosses;
+		if (x0 <= rxmin && x1 <= rxmin) return numCrosses;
+		if (x0 >= rxmax && x1 >= rxmax) {
+			// Line is entirely to the right of the rect
+			// and the vertical ranges of the two overlap by a non-empty amount
+			// Thus, this line segment is partially in the "right-shadow"
+			// Path may have done a complete crossing
+			// Or path may have entered or exited the right-shadow
+			if (y0 < y1) {
+				// y-increasing line segment...
+				// We know that y0 < rymax and y1 > rymin
+				if (y0 <= rymin) ++numCrosses;
+				if (y1 >= rymax) ++numCrosses;
+			}
+			else if (y1 < y0) {
+				// y-decreasing line segment...
+				// We know that y1 < rymax and y0 > rymin
+				if (y1 <= rymin) --numCrosses;
+				if (y0 >= rymax) --numCrosses;
+			}
+			return numCrosses;
+		}
+		// Remaining case:
+		// Both x and y ranges overlap by a non-empty amount
+		// First do trivial INTERSECTS rejection of the cases
+		// where one of the endpoints is inside the rectangle.
+		if ((x0 > rxmin && x0 < rxmax && y0 > rymin && y0 < rymax) ||
+				(x1 > rxmin && x1 < rxmax && y1 > rymin && y1 < rymax)) {
+			return MathConstants.SHAPE_INTERSECTS;
+		}
+		// Otherwise calculate the y intercepts and see where
+		// they fall with respect to the rectangle
+		float xi0 = x0;
+		if (y0 < rymin) {
+			xi0 += ((rymin - y0) * (x1 - x0) / (y1 - y0));
+		}
+		else if (y0 > rymax) {
+			xi0 += ((rymax - y0) * (x1 - x0) / (y1 - y0));
+		}
+		float xi1 = x1;
+		if (y1 < rymin) {
+			xi1 += ((rymin - y1) * (x0 - x1) / (y0 - y1));
+		}
+		else if (y1 > rymax) {
+			xi1 += ((rymax - y1) * (x0 - x1) / (y0 - y1));
+		}
+		if (xi0 <= rxmin && xi1 <= rxmin) return numCrosses;
+		if (xi0 >= rxmax && xi1 >= rxmax) {
+			if (y0 < y1) {
+				// y-increasing line segment...
+				// We know that y0 < rymax and y1 > rymin
+				if (y0 <= rymin) ++numCrosses;
+				if (y1 >= rymax) ++numCrosses;
+			}
+			else if (y1 < y0) {
+				// y-decreasing line segment...
+				// We know that y1 < rymax and y0 > rymin
+				if (y1 <= rymin) --numCrosses;
+				if (y0 >= rymax) --numCrosses;
+			}
+			return numCrosses;
+		}
+		return MathConstants.SHAPE_INTERSECTS;
+	}
+
+	/** Replies if two lines are intersecting.
+	 * 
+	 * @param x1 is the first point of the first line.
+	 * @param y1 is the first point of the first line.
+	 * @param x2 is the second point of the first line.
+	 * @param y2 is the second point of the first line.
+	 * @param x3 is the first point of the second line.
+	 * @param y3 is the first point of the second line.
+	 * @param x4 is the second point of the second line.
+	 * @param y4 is the second point of the second line.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsLineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
+		if (MathUtil.isParallelLines(x1, y1, x2, y2, x3, y3, x4, y4)) {
+			return MathUtil.isCollinearPoints(x1, y1, x2, y2, x3, y3);
+		}
+		return true;
+	}
+
+	/** Replies if a segment and a line are intersecting.
+	 * 
+	 * @param x1 is the first point of the first segment.
+	 * @param y1 is the first point of the first segment.
+	 * @param x2 is the second point of the first segment.
+	 * @param y2 is the second point of the first segment.
+	 * @param x3 is the first point of the second line.
+	 * @param y3 is the first point of the second line.
+	 * @param x4 is the second point of the second line.
+	 * @param y4 is the second point of the second line.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsSegmentLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
+		return (MathUtil.sidePointLine(x3, y3, x4, y4, x1, y1, true) *
+				MathUtil.sidePointLine(x3, y3, x4, y4, x2, y2, true)) <= 0;
+	}
+
+	/** Replies if two segments are intersecting.
+	 * 
+	 * @param x1 is the first point of the first segment.
+	 * @param y1 is the first point of the first segment.
+	 * @param x2 is the second point of the first segment.
+	 * @param y2 is the second point of the first segment.
+	 * @param x3 is the first point of the second segment.
+	 * @param y3 is the first point of the second segment.
+	 * @param x4 is the second point of the second segment.
+	 * @param y4 is the second point of the second segment.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsSegmentSegment(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
+		return (MathUtil.ccw(x1, y1, x2, y2, x3, y3, false) *
+				MathUtil.ccw(x1, y1, x2, y2, x4, y4, false) <= 0)
+				&&
+				(MathUtil.ccw(x3, y3, x4, y4, x1, y1, false) *
+						MathUtil.ccw(x3, y3, x4, y4, x2, y2, false) <= 0);
+	}
+
+	/** X-coordinate of the first point. */
+	protected float ax = 0f;
+	/** Y-coordinate of the first point. */
+	protected float ay = 0f;
+	/** X-coordinate of the second point. */
+	protected float bx = 0f;
+	/** Y-coordinate of the second point. */
+	protected float by = 0f;
+
+	/**
+	 */
+	public Segment2f() {
+		//
+	}
+
+	/**
+	 * @param a
+	 * @param b
+	 */
+	public Segment2f(Point2D a, Point2D b) {
+		set(a, b);
+	}
+
+	/**
+	 * @param x1
+	 * @param y1
+	 * @param x2
+	 * @param y2
+	 */
+	public Segment2f(float x1, float y1, float x2, float y2) {
+		set(x1, y1, x2, y2);
+	}
+
+	@Override
+	public void clear() {
+		this.ax = this.ay = this.bx = this.by = 0f;
+	}
+
+	/**
+	 * Replies if this segment is empty.
+	 * The segment is empty when the two
+	 * points are equal.
+	 * 
+	 * @return <code>true</code> if the two points are
+	 * equal.
+	 */
+	@Override
+	public boolean isEmpty() {
+		return this.ax==this.bx && this.ay==this.by;
+	}
+
+	/** Change the line.
+	 * 
+	 * @param x1
+	 * @param y1
+	 * @param x2
+	 * @param y2
+	 */
+	public void set(float x1, float y1, float x2, float y2) {
+		this.ax = x1;
+		this.ay = y1;
+		this.bx = x2;
+		this.by = y2;
+	}
+
+	/** Change the line.
+	 * 
+	 * @param a
+	 * @param b
+	 */
+	public void set(Point2D a, Point2D b) {
+		this.ax = a.getX();
+		this.ay = a.getY();
+		this.bx = b.getX();
+		this.by = b.getY();
+	}
+
+	/** Replies the X of the first point.
+	 * 
+	 * @return the x of the first point.
+	 */
+	public float getX1() {
+		return this.ax;
+	}
+
+	/** Replies the Y of the first point.
+	 * 
+	 * @return the y of the first point.
+	 */
+	public float getY1() {
+		return this.ay;
+	}
+
+	/** Replies the X of the second point.
+	 * 
+	 * @return the x of the second point.
+	 */
+	public float getX2() {
+		return this.bx;
+	}
+
+	/** Replies the Y of the second point.
+	 * 
+	 * @return the y of the second point.
+	 */
+	public float getY2() {
+		return this.by;
+	}
+
+	/** Replies the first point.
+	 * 
+	 * @return the first point.
+	 */
+	public Point2D getP1() {
+		return new Point2f(this.ax, this.ay);
+	}
+
+	/** Replies the second point.
+	 * 
+	 * @return the second point.
+	 */
+	public Point2D getP2() {
+		return new Point2f(this.bx, this.by);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Rectangle2f toBoundingBox() {
+		Rectangle2f r = new Rectangle2f();
+		r.setFromCorners(
+				this.ax,
+				this.ay,
+				this.bx,
+				this.by);
+		return r;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceSquared(Point2D p) {
+		return MathUtil.distanceSquaredPointToSegment(p.getX(), p.getY(),
+				this.ax, this.ay,
+				this.bx, this.by);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceL1(Point2D p) {
+		float ratio = MathUtil.projectsPointOnLine(p.getX(), p.getY(), this.ax, this.ay, this.bx, this.by);
+		ratio = MathUtil.clamp(ratio, 0f, 1f);
+		Vector2f v = new Vector2f(this.bx, this.by);
+		v.sub(this.ax, this.ay);
+		v.scale(ratio);
+		return Math.abs(this.ax + v.getX() - p.getX())
+				+ Math.abs(this.ay + v.getY() - p.getY());
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceLinf(Point2D p) {
+		float ratio = MathUtil.projectsPointOnLine(p.getX(), p.getY(), this.ax, this.ay, this.bx, this.by);
+		ratio = MathUtil.clamp(ratio, 0f, 1f);
+		Vector2f v = new Vector2f(this.bx, this.by);
+		v.sub(this.ax, this.ay);
+		v.scale(ratio);
+		return Math.max(
+				Math.abs(this.ax + v.getX() - p.getX()),
+				Math.abs(this.ay + v.getY() - p.getY()));
+	}
+
+	/** {@inheritDoc}
+	 * <p>
+	 * This function uses the equal-to-zero test with the error {@link MathConstants#EPSILON}.
+	 * 
+	 * @see MathUtil#isEpsilonZero(float)
+	 */
+	@Override
+	public boolean contains(float x, float y) {
+		return MathUtil.isEpsilonZero(MathUtil.distanceSquaredPointToSegment(x, y,
+				this.ax, this.ay,
+				this.bx, this.by));
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean contains(Rectangle2f r) {
+		return contains(r.getMinX(), r.getMinY())
+				&& contains(r.getMaxX(), r.getMaxY());
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Point2f getClosestPointTo(Point2D p) {
+		float ratio = MathUtil.projectsPointOnLine(p.getX(), p.getY(),
+				this.ax, this.ay,
+				this.bx, this.by);
+		if (ratio<=0f) return new Point2f(this.ax, this.ay);
+		if (ratio>=1f) return new Point2f(this.bx, this.by);
+		return new Point2f(
+				this.ax + (this.bx - this.ax) * ratio,
+				this.ay + (this.by - this.ay) * ratio); 
+	}
+
+	@Override
+	public void translate(float dx, float dy) {
+		this.ax += dx;
+		this.ay += dy;
+		this.bx += dx;
+		this.by += dy;
+	}
+
+	@Override
+	public PathIterator2f getPathIterator(Transform2D transform) {
+		return new SegmentPathIterator(
+				getX1(), getY1(), getX2(), getY2(),
+				transform);
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (obj == this) {
+			return true;
+		}
+		if (obj instanceof Segment2f) {
+			Segment2f rr2d = (Segment2f) obj;
+			return ((getX1() == rr2d.getX1()) &&
+					(getY1() == rr2d.getY1()) &&
+					(getX2() == rr2d.getX2()) &&
+					(getY2() == rr2d.getY2()));
+		}
+		return false;
+	}
+
+	@Override
+	public int hashCode() {
+		long bits = 1L;
+		bits = 31L * bits + floatToIntBits(getX1());
+		bits = 31L * bits + floatToIntBits(getY1());
+		bits = 31L * bits + floatToIntBits(getX2());
+		bits = 31L * bits + floatToIntBits(getY2());
+		return (int) (bits ^ (bits >> 32));
+	}
+
+	/** Transform the current segment.
+	 * This function changes the current segment.
+	 * 
+	 * @param transform is the affine transformation to apply.
+	 * @see #createTransformedShape(Transform2D)
+	 */
+	public void transform(Transform2D transform) {
+		Point2f p = new Point2f(this.ax,  this.ay);
+		transform.transform(p);
+		this.ax = p.getX();
+		this.ay = p.getY();
+		p.set(this.bx, this.by);
+		transform.transform(p);
+		this.bx = p.getX();
+		this.by = p.getY();
+	}
+
+	@Override
+	public Shape2f createTransformedShape(Transform2D transform) {
+		Point2D p1 = transform.transform(this.ax, this.ay);
+		Point2D p2 = transform.transform(this.bx, this.by);
+		return new Segment2f(p1, p2);
+	}
+
+	@Override
+	public boolean intersects(Rectangle2f s) {
+		return Rectangle2f.intersectsRectangleSegment(
+				s.getMinX(), s.getMinY(),
+				s.getMaxX(), s.getMaxY(),
+				getX1(), getY1(),
+				getX2(), getY2());
+	}
+
+	@Override
+	public boolean intersects(Ellipse2f s) {
+		return Ellipse2f.intersectsEllipseSegment(
+				s.getMinX(), s.getMinY(),
+				s.getMaxX(), s.getMaxY(),
+				getX1(), getY1(),
+				getX2(), getY2());
+	}
+
+	@Override
+	public boolean intersects(Circle2f s) {
+		return Circle2f.intersectsCircleSegment(
+				s.getX(), s.getY(),
+				s.getRadius(),
+				getX1(), getY1(),
+				getX2(), getY2());
+	}
+
+	@Override
+	public boolean intersects(Segment2f s) {
+		return intersectsSegmentSegment(
+				getX1(), getY1(),
+				getX2(), getY2(),
+				s.getX1(), s.getY1(),
+				s.getX2(), s.getY2());
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder b = new StringBuilder();
+		b.append("["); //$NON-NLS-1$
+		b.append(getX1());
+		b.append(";"); //$NON-NLS-1$
+		b.append(getY1());
+		b.append("|"); //$NON-NLS-1$
+		b.append(getX2());
+		b.append(";"); //$NON-NLS-1$
+		b.append(getY2());
+		b.append("]"); //$NON-NLS-1$
+		return b.toString();
+	}
+
+	/** Iterator on the path elements of the segment.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class SegmentPathIterator implements PathIterator2f {
+
+		private final Point2D p1 = new Point2f();
+		private final Point2D p2 = new Point2f();
+		private final Transform2D transform;
+		private final float x1;
+		private final float y1;
+		private final float x2;
+		private final float y2;
+		private int index = 0;
+
+		/**
+		 * @param x1
+		 * @param y1
+		 * @param x2
+		 * @param y2
+		 * @param transform
+		 */
+		public SegmentPathIterator(float x1, float y1, float x2, float y2, Transform2D transform) {
+			this.transform = transform;
+			this.x1 = x1;
+			this.y1 = y1;
+			this.x2 = x2;
+			this.y2 = y2;
+			if (this.x1==this.x2 && this.y1==this.y2) {
+				this.index = 2;
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.index<=1;
+		}
+
+		@Override
+		public PathElement2f next() {
+			if (this.index>1) throw new NoSuchElementException();
+			int idx = this.index;
+			++this.index;
+			switch(idx) {
+			case 0:
+				this.p2.set(this.x1, this.y1);
+				if (this.transform!=null) {
+					this.transform.transform(this.p2);
+				}
+				return new PathElement2f.MovePathElement2f(
+						this.p2.getX(), this.p2.getY());
+			case 1:
+				this.p1.set(this.p2);
+				this.p2.set(this.x2, this.y2);
+				if (this.transform!=null) {
+					this.transform.transform(this.p2);
+				}
+				return new PathElement2f.LinePathElement2f(
+						this.p1.getX(), this.p1.getY(),
+						this.p2.getX(), this.p2.getY());
+			default:
+				throw new NoSuchElementException();
+			}
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return PathWindingRule.NON_ZERO;
+		}
+
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Shape2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Shape2f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Shape2f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,155 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Shape2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+/** 2D shape with floating-point points.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Shape2f extends Shape2D {
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Shape2f clone();
+
+	/** Replies the bounds of the shape.
+	 * 
+	 * @return the bounds of the shape.
+	 */
+	public Rectangle2f toBoundingBox();
+	
+	/** Replies the minimal distance from this shape to the given point.
+	 * 
+	 * @param p
+	 * @return the minimal distance between this shape and the point.
+	 */
+	public float distance(Point2D p);
+
+	/** Replies the squared value of the minimal distance from this shape to the given point.
+	 * 
+	 * @param p
+	 * @return squared value of the minimal distance between this shape and the point.
+	 */
+	public float distanceSquared(Point2D p);
+
+	/**
+	 * Computes the L-1 (Manhattan) distance between this shape and
+	 * point p1.  The L-1 distance is equal to abs(x1-x2) + abs(y1-y2).
+	 * @param p the point
+	 * @return the distance.
+	 */
+	public float distanceL1(Point2D p);
+
+	/**
+	 * Computes the L-infinite distance between this shape and
+	 * point p1.  The L-infinite distance is equal to 
+	 * MAX[abs(x1-x2), abs(y1-y2)]. 
+	 * @param p the point
+	 * @return the distance.
+	 */
+	public float distanceLinf(Point2D p);
+
+	/** Translate the shape.
+	 * 
+	 * @param dx
+	 * @param dy
+	 */
+	public void translate(float dx, float dy); 
+	
+	/** Replies if the given point is inside this shape.
+	 * 
+	 * @param x
+	 * @param y
+	 * @return <code>true</code> if the given point is inside this
+	 * shape, otherwise <code>false</code>.
+	 */
+	public boolean contains(float x, float y);
+	
+	/** Replies if the given rectangle is inside this shape.
+	 * 
+	 * @param r
+	 * @return <code>true</code> if the given rectangle is inside this
+	 * shape, otherwise <code>false</code>.
+	 */
+	public boolean contains(Rectangle2f r);
+
+	/** Replies the elements of the paths.
+	 * 
+	 * @param transform is the transformation to apply to the path.
+	 * @return the elements of the path.
+	 */
+	public PathIterator2f getPathIterator(Transform2D transform);
+
+	/** Replies the elements of the paths.
+	 * 
+	 * @return the elements of the path.
+	 */
+	public PathIterator2f getPathIterator();
+
+	/** Apply the transformation to the shape and reply the result.
+	 * This function does not change the current shape.
+	 * 
+	 * @param transform is the transformation to apply to the shape.
+	 * @return the result of the transformation.
+	 */
+	public Shape2f createTransformedShape(Transform2D transform);
+	
+	/** Replies if this shape is intersecting the given rectangle.
+	 * 
+	 * @param s
+	 * @return <code>true</code> if this shape is intersecting the given shape;
+	 * <code>false</code> if there is no intersection.
+	 */
+	public boolean intersects(Rectangle2f s);
+
+	/** Replies if this shape is intersecting the given ellipse.
+	 * 
+	 * @param s
+	 * @return <code>true</code> if this shape is intersecting the given shape;
+	 * <code>false</code> if there is no intersection.
+	 */
+	public boolean intersects(Ellipse2f s);
+
+	/** Replies if this shape is intersecting the given circle.
+	 * 
+	 * @param s
+	 * @return <code>true</code> if this shape is intersecting the given shape;
+	 * <code>false</code> if there is no intersection.
+	 */
+	public boolean intersects(Circle2f s);
+
+	/** Replies if this shape is intersecting the given line.
+	 * 
+	 * @param s
+	 * @return <code>true</code> if this shape is intersecting the given shape;
+	 * <code>false</code> if there is no intersection.
+	 */
+	public boolean intersects(Segment2f s);
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Tuple2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Tuple2f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Tuple2f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,608 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import org.arakhne.afc.math.generic.Tuple2D;
+
+/** 2D tuple with 2 floating-point numbers.
+ * 
+ * @param <T> is the implementation type of the tuple.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Tuple2f<T extends Tuple2D<? super T>> implements Tuple2D<T> {
+
+	private static final long serialVersionUID = 6447733811545555778L;
+	
+	/** x coordinate.
+	 */
+	protected float x;
+
+	/** y coordinate.
+	 */
+	protected float y;
+
+	/**
+	 */
+	public Tuple2f() {
+		this.x = this.y = 0;
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Tuple2f(Tuple2D<?> tuple) {
+		this.x = tuple.getX();
+		this.y = tuple.getY();
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Tuple2f(int[] tuple) {
+		this.x = tuple[0];
+		this.y = tuple[1];
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Tuple2f(float[] tuple) {
+		this.x = tuple[0];
+		this.y = tuple[1];
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Tuple2f(int x, int y) {
+		this.x = x;
+		this.y = y;
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Tuple2f(float x, float y) {
+		this.x = x;
+		this.y = y;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public T clone() {
+		try {
+			return (T)super.clone();
+		}
+		catch(CloneNotSupportedException e) {
+			throw new Error(e);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void absolute() {
+		this.x = Math.abs(this.x);
+		this.y = Math.abs(this.y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void absolute(T t) {
+		t.set(Math.abs(this.x), Math.abs(this.y));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void add(int x, int y) {
+		this.x += x;
+		this.y += y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void add(float x, float y) {
+		this.x += x;
+		this.y += y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addX(int x) {
+		this.x += x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addX(float x) {
+		this.x += x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addY(int y) {
+		this.y += y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addY(float y) {
+		this.y += y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clamp(int min, int max) {
+		if (this.x < min) this.x = min;
+		else if (this.x > max) this.x = max;
+		if (this.y < min) this.y = min;
+		else if (this.y > max) this.y = max;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clamp(float min, float max) {
+		clamp(min, max);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMin(int min) {
+		if (this.x < min) this.x = min;
+		if (this.y < min) this.y = min;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMin(float min) {
+		clampMin(min);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMax(int max) {
+		if (this.x > max) this.x = max;
+		if (this.y > max) this.y = max;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMax(float max) {
+		clampMax(max);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clamp(int min, int max, T t) {
+		if (this.x < min) t.setX(min);
+		else if (this.x > max) t.setX(max);
+		if (this.y < min) t.setY(min);
+		else if (this.y > max) t.setY(max);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clamp(float min, float max, T t) {
+		clamp(min, max, t);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMin(int min, T t) {
+		if (this.x < min) t.setX(min);
+		if (this.y < min) t.setY(min);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMin(float min, T t) {
+		clampMin(min, t);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMax(int max, T t) {
+		if (this.x > max) t.setX(max);
+		if (this.y > max) t.setY(max);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMax(float max, T t) {
+		clampMax(max, t);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void get(T t) {
+		t.set(this.x, this.y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void get(int[] t) {
+		t[0] = (int)this.x;
+		t[1] = (int)this.y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void get(float[] t) {
+		t[0] = this.x;
+		t[1] = this.y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void negate(T t1) {
+		this.x = -t1.getX();
+		this.y = -t1.getY();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void negate() {
+		this.x = -this.x;
+		this.y = -this.y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(int s, T t1) {
+		this.x = s * t1.getX();
+		this.y = s * t1.getY();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(float s, T t1) {
+		this.x = (s * t1.getX());
+		this.y = (s * t1.getY());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(int s) {
+		this.x = s * this.x;
+		this.y = s * this.y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(float s) {
+		this.x = (s * this.x);
+		this.y = (s * this.y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(Tuple2D<?> t1) {
+		this.x = t1.getX();
+		this.y = t1.getY();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(int x, int y) {
+		this.x = x;
+		this.y = y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(float x, float y) {
+		this.x = x;
+		this.y = y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(int[] t) {
+		this.x = t[0];
+		this.y = t[1];
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(float[] t) {
+		this.x = t[0];
+		this.y = t[1];
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float getX() {
+		return this.x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int x() {
+		return (int)this.x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setX(int x) {
+		this.x = x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setX(float x) {
+		this.x = x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float getY() {
+		return this.y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int y() {
+		return (int)this.y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setY(int y) {
+		this.y = y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setY(float y) {
+		this.y = y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void sub(int x, int y) {
+		this.x -= x;
+		this.y -= y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subX(int x) {
+		this.x -= x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subY(int y) {
+		this.y -= y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void sub(float x, float y) {
+		this.x -= x;
+		this.y -= y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subX(float x) {
+		this.x -= x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subY(float y) {
+		this.y -= y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void interpolate(T t1, T t2, float alpha) {
+		this.x = ((1f-alpha)*t1.getX() + alpha*t2.getX());
+		this.y = ((1f-alpha)*t1.getY() + alpha*t2.getY());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void interpolate(T t1, float alpha) {
+		this.x = ((1f-alpha)*this.x + alpha*t1.getX());
+		this.y = ((1f-alpha)*this.y + alpha*t1.getY());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(Tuple2D<?> t1) {
+		try {
+			return(this.x == t1.getX() && this.y == t1.getY());
+		}
+		catch (NullPointerException e2) {
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public boolean equals(Object t1) {
+		try {
+			T t2 = (T) t1;
+			return(this.x == t2.getX() && this.y == t2.getY());
+		}
+		catch(AssertionError e) {
+			throw e;
+		}
+		catch (Throwable e2) {
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean epsilonEquals(T t1, float epsilon) {
+		float diff;
+
+		diff = this.x - t1.getX();
+		if(Float.isNaN(diff)) return false;
+		if((diff<0?-diff:diff) > epsilon) return false;
+
+		diff = this.y - t1.getY();
+		if(Float.isNaN(diff)) return false;
+		if((diff<0?-diff:diff) > epsilon) return false;
+
+		return true;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int hashCode() {
+		int bits = 1;
+		bits = 31 * bits + Float.floatToIntBits(this.x);
+		bits = 31 * bits + Float.floatToIntBits(this.y);
+		return bits ^ (bits >> 32);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		return "(" //$NON-NLS-1$
+				+this.x
+				+";" //$NON-NLS-1$
+				+this.y
+				+")"; //$NON-NLS-1$
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Tuple2fComparator.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Tuple2fComparator.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Tuple2fComparator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,54 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import java.util.Comparator;
+
+/**
+ * Comparator of Tuple2f.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Tuple2fComparator implements Comparator<Tuple2f<?>> {
+	
+	/**
+	 */
+	public Tuple2fComparator() {
+		//
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int compare(Tuple2f<?> o1, Tuple2f<?> o2) {
+		if (o1==o2) return 0;
+		if (o1==null) return Integer.MIN_VALUE;
+		if (o2==null) return Integer.MAX_VALUE;
+		int cmp = Float.compare(o1.getX(), o2.getX());
+		if (cmp!=0) return cmp;
+		return Float.compare(o1.getY(), o2.getY());
+	}
+		
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/UnmodifiablePoint2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/UnmodifiablePoint2f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/UnmodifiablePoint2f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,104 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import org.arakhne.afc.math.generic.Tuple2D;
+
+/** This class implements a Point2f that cannot be modified by
+ * the setters. 
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class UnmodifiablePoint2f extends Point2f {
+
+	private static final long serialVersionUID = -8670105082548919880L;
+
+	/**
+	 */
+	public UnmodifiablePoint2f() {
+		super();
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public UnmodifiablePoint2f(float x, float y) {
+		super(x, y);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public UnmodifiablePoint2f clone() {
+		return (UnmodifiablePoint2f)super.clone();
+	}
+
+	@Override
+	public void set(float x, float y) {
+		//
+	}
+
+	@Override
+	public void set(float[] t) {
+		//
+	}
+	
+	@Override
+	public void set(int x, int y) {
+		//
+	}
+	
+	@Override
+	public void set(int[] t) {
+		//
+	}
+	
+	@Override
+	public void set(Tuple2D<?> t1) {
+		//
+	}
+	
+	@Override
+	public void setX(float x) {
+		//
+	}
+	
+	@Override
+	public void setX(int x) {
+		//
+	}
+	
+	@Override
+	public void setY(float y) {
+		//
+	}
+	
+	@Override
+	public void setY(int y) {
+		//
+	}
+	
+}

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Vector2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Vector2f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object2d/Vector2f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,306 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import org.arakhne.afc.math.MathUtil;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Tuple2D;
+import org.arakhne.afc.math.generic.Vector2D;
+import org.arakhne.afc.math.matrix.Matrix2f;
+
+/** 2D Vector with 2 floating-point values.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Vector2f extends Tuple2f<Vector2D> implements Vector2D {
+
+	private static final long serialVersionUID = -2062941509400877679L;
+
+	/**
+	 */
+	public Vector2f() {
+		//
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Vector2f(Tuple2D<?> tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Vector2f(int[] tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Vector2f(float[] tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Vector2f(int x, int y) {
+		super(x,y);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Vector2f(float x, float y) {
+		super(x,y);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Vector2f(double x, double y) {
+		super((float)x,(float)y);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Vector2f(long x, long y) {
+		super(x,y);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Vector2f clone() {
+		return (Vector2f)super.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float angle(Vector2D v1) {
+		double vDot = dot(v1) / ( length()*v1.length() );
+		if( vDot < -1.) vDot = -1.;
+		if( vDot >  1.) vDot =  1.;
+		return((float) (Math.acos( vDot )));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float dot(Vector2D v1) {
+	      return (this.x*v1.getX() + this.y*v1.getY());
+	}
+	
+	/**
+	 * Multiply this vector, transposed, by the given matrix and replies the resulting vector.
+	 * 
+	 * @param m
+	 * @return transpose(this * m)
+	 */
+	public final Vector2f mul(Matrix2f m) {
+		Vector2f r = new Vector2f();
+		r.x = this.getX() * m.m00 + this.getY() * m.m01;
+		r.y = this.getX() * m.m10 + this.getY() * m.m11;
+		return r;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void perpendicularize() {
+		// Based on the cross product in 3D of (vx,vy,0)x(0,0,1), right-handed
+		//set(getY(), -getX());
+		// Based on the cross product in 3D of (vx,vy,0)x(0,0,1), left-handed
+		set(-getY(), getX());
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float length() {
+        return (float) Math.sqrt(this.x*this.x + this.y*this.y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float lengthSquared() {
+        return (this.x*this.x + this.y*this.y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void normalize(Vector2D v1) {
+        float norm = 1f / v1.length();
+        this.x = (int)(v1.getX()*norm);
+        this.y = (int)(v1.getY()*norm);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void normalize() {
+        float norm;
+        norm = (float)(1./Math.sqrt(this.x*this.x + this.y*this.y));
+        this.x *= norm;
+        this.y *= norm;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float signedAngle(Vector2D v) {
+		return MathUtil.signedAngle(getX(), getY(), v.getX(), v.getY());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void turnVector(float angle) {
+		float sin = (float)Math.sin(angle);
+		float cos = (float)Math.cos(angle);
+		float x =  cos * getX() + sin * getY(); 
+		float y = -sin * getX() + cos * getY();
+		set(x,y);
+	}
+	
+	/** Replies the orientation vector, which is corresponding
+	 * to the given angle on a trigonometric circle.
+	 * 
+	 * @param angle is the angle in radians to translate.
+	 * @return the orientation vector which is corresponding to the given angle.
+	 */
+	public static Vector2f toOrientationVector(float angle) {
+		return new Vector2f(
+				(float)Math.cos(angle),
+				(float)Math.sin(angle));
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float getOrientationAngle() {
+		float angle = (float)Math.acos(getX());
+		if (getY()<0f) angle = -angle;
+		return MathUtil.clampRadian(angle);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void add(Vector2D t1, Vector2D t2) {
+		this.x = t1.getX() + t2.getX();
+		this.y = t1.getY() + t2.getY();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void add(Vector2D t1) {
+		this.x += t1.getX();
+		this.y += t1.getY();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scaleAdd(int s, Vector2D t1, Vector2D t2) {
+		this.x = s * t1.getX() + t2.getX();
+		this.y = s * t1.getY() + t2.getY();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scaleAdd(float s, Vector2D t1, Vector2D t2) {
+		this.x = s * t1.getX() + t2.getX();
+		this.y = s * t1.getY() + t2.getY();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scaleAdd(int s, Vector2D t1) {
+		this.x = s * this.x + t1.getX();
+		this.y = s * this.y + t1.getY();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scaleAdd(float s, Vector2D t1) {
+		this.x = s * this.x + t1.getX();
+		this.y = s * this.y + t1.getY();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void sub(Vector2D t1, Vector2D t2) {
+		this.x = t1.getX() - t2.getX();
+		this.y = t1.getY() - t2.getY();
+	}
+
+	@Override
+	public void sub(Point2D t1, Point2D t2) {
+		this.x = t1.getX() - t2.getX();
+		this.y = t1.getY() - t2.getY();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void sub(Vector2D t1) {
+		this.x -= t1.getX();
+		this.y -= t1.getY();
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Point3f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Point3f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Point3f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,267 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object3d;
+
+import org.arakhne.afc.math.generic.Point3D;
+import org.arakhne.afc.math.generic.Tuple3D;
+import org.arakhne.afc.math.generic.Vector3D;
+
+/** 3D Point with 3 floating-point numbers.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Point3f extends Tuple3f<Point3D> implements Point3D {
+
+	private static final long serialVersionUID = -4821663886493835147L;
+
+	/**
+	 */
+	public Point3f() {
+		//
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Point3f(Tuple3D<?> tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Point3f(int[] tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Point3f(float[] tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Point3f(int x, int y, int z) {
+		super(x,y,z);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Point3f(float x, float y, float z) {
+		super(x,y,z);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Point3f(double x, double y, double z) {
+		super((float)x,(float)y,(float)z);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Point3f(long x, long y, long z) {
+		super(x,y,z);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Point3f clone() {
+		return (Point3f)super.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int distanceSquared(Point3D p1) {
+	      float dx, dy, dz;
+	      dx = this.x-p1.getX();  
+	      dy = this.y-p1.getY();
+	      dz = this.z-p1.getZ();
+	      return (int)(dx*dx+dy*dy+dz*dz);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float getDistanceSquared(Point3D p1) {
+	      float dx, dy, dz;
+	      dx = this.x-p1.getX();  
+	      dy = this.y-p1.getY();
+	      dz = this.z-p1.getZ();
+	      return dx*dx+dy*dy+dz*dz;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int distance(Point3D p1) {
+	      float  dx, dy, dz;
+	      dx = this.x-p1.getX();  
+	      dy = this.y-p1.getY();
+	      dz = this.z-p1.getZ();
+	      return (int)Math.sqrt(dx*dx+dy*dy+dz*dz);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float getDistance(Point3D p1) {
+	      float  dx, dy, dz;
+	      dx = this.x-p1.getX();  
+	      dy = this.y-p1.getY();
+	      dz = this.z-p1.getZ();
+	      return (float)Math.sqrt(dx*dx+dy*dy+dz*dz);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int distanceL1(Point3D p1) {
+	      return (int)(Math.abs(this.x-p1.getX()) + Math.abs(this.y-p1.getY()) + Math.abs(this.z-p1.getZ()));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float getDistanceL1(Point3D p1) {
+	      return Math.abs(this.x-p1.getX()) + Math.abs(this.y-p1.getY() + Math.abs(this.z-p1.getZ()));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int distanceLinf(Point3D p1) {
+	      return (int)Math.max(Math.max( Math.abs(this.x-p1.getX()), Math.abs(this.y-p1.getY())),
+	    		  Math.abs(this.z-p1.getZ()));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float getDistanceLinf(Point3D p1) {
+	      return Math.max( Math.max( Math.abs(this.x-p1.getX()), Math.abs(this.y-p1.getY())),
+	    		  Math.abs(this.z-p1.getZ()));
+	}
+
+	@Override
+	public void add(Point3D t1, Vector3D t2) {
+		this.x = t1.getX() + t2.getX();
+		this.y = t1.getY() + t2.getY();
+		this.z = t1.getZ() + t2.getZ();
+	}
+
+	@Override
+	public void add(Vector3D t1, Point3D t2) {
+		this.x = t1.getX() + t2.getX();
+		this.y = t1.getY() + t2.getY();
+		this.z = t1.getZ() + t2.getZ();
+	}
+
+	@Override
+	public void add(Vector3D t1) {
+		this.x += t1.getX();
+		this.y += t1.getY();
+		this.z += t1.getZ();
+	}
+
+	@Override
+	public void scaleAdd(int s, Vector3D t1, Point3D t2) {
+		this.x = s * t1.getX() + t2.getX();
+		this.y = s * t1.getY() + t2.getY();
+		this.z = s * t1.getZ() + t2.getZ();
+	}
+
+	@Override
+	public void scaleAdd(float s, Vector3D t1, Point3D t2) {
+		this.x = s * t1.getX() + t2.getX();
+		this.y = s * t1.getY() + t2.getY();
+		this.z = s * t1.getZ() + t2.getZ();
+	}
+
+	@Override
+	public void scaleAdd(int s, Point3D t1, Vector3D t2) {
+		this.x = s * t1.getX() + t2.getX();
+		this.y = s * t1.getY() + t2.getY();
+		this.z = s * t1.getZ() + t2.getZ();
+	}
+
+	@Override
+	public void scaleAdd(float s, Point3D t1, Vector3D t2) {
+		this.x = s * t1.getX() + t2.getX();
+		this.y = s * t1.getY() + t2.getY();
+		this.z = s * t1.getZ() + t2.getZ();
+	}
+
+	@Override
+	public void scaleAdd(int s, Vector3D t1) {
+		this.x = s * this.x + t1.getX();
+		this.y = s * this.y + t1.getY();
+		this.z = s * this.z + t1.getZ();
+	}
+
+	@Override
+	public void scaleAdd(float s, Vector3D t1) {
+		this.x = s * this.x + t1.getX();
+		this.y = s * this.y + t1.getY();
+		this.z = s * this.z + t1.getZ();
+	}
+
+	@Override
+	public void sub(Point3D t1, Vector3D t2) {
+		this.x = t1.getX() - t2.getX();
+		this.y = t1.getY() - t2.getY();
+		this.z = t1.getZ() - t2.getZ();
+	}
+
+	@Override
+	public void sub(Vector3D t1) {
+		this.x -= t1.getX();
+		this.y -= t1.getY();
+		this.z -= t1.getZ();
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Quaternion.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Quaternion.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Quaternion.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,696 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object3d;
+
+import java.io.Serializable;
+
+import org.arakhne.afc.math.generic.Vector3D;
+import org.arakhne.afc.math.matrix.Matrix3f;
+import org.arakhne.afc.math.matrix.Matrix4f;
+
+/** A 4 element unit quaternion represented by single precision floating 
+ * point x,y,z,w coordinates.  The quaternion is always normalized.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Quaternion implements Cloneable, Serializable {
+
+	private static final long serialVersionUID = 4494919776986180960L;
+	
+	private final static double EPS = 0.000001;
+	private final static double EPS2 = 1.0e-30;
+
+	/** x coordinate.
+	 */
+	protected float x;
+
+	/** y coordinate.
+	 */
+	protected float y;
+
+	/** z coordinate.
+	 */
+	protected float z;
+
+	/** w coordinate.
+	 */
+	protected float w;
+
+	/**
+	 */
+	public Quaternion() {
+		this.x = this.y = this.z = this.w = 0;
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 * @param w
+	 */
+	public Quaternion(float x, float y, float z, float w) {
+		float mag = (float)(1.0/Math.sqrt( x*x + y*y + z*z + w*w ));
+		this.x = x*mag;
+		this.y = y*mag;
+		this.z = z*mag;
+		this.w = w*mag;
+	}
+
+	/**
+	 * @param axis
+	 * @param angle
+	 */
+	public Quaternion(Vector3D axis, float angle) {
+		setAxisAngle(axis, angle);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Quaternion clone() {
+		try {
+			return (Quaternion)super.clone();
+		}
+		catch(CloneNotSupportedException e) {
+			throw new Error(e);
+		}
+	}
+
+	/** Replies the X coordinate.
+	 * 
+	 * @return x
+	 */
+	public float getX() {
+		return this.x;
+	}
+
+	/** Set the X coordinate.
+	 * 
+	 * @param x
+	 */
+	public void setX(float x) {
+		this.x = x;
+	}
+
+	/** Replies the Y coordinate.
+	 * 
+	 * @return y
+	 */
+	public float getY() {
+		return this.y;
+	}
+
+	/** Set the Y coordinate.
+	 * 
+	 * @param y
+	 */
+	public void setY(float y) {
+		this.y = y;
+	}
+
+	/** Replies the Z coordinate.
+	 * 
+	 * @return z
+	 */
+	public float getZ() {
+		return this.z;
+	}
+
+	/** Set the Z coordinate.
+	 * 
+	 * @param z
+	 */
+	public void setZ(float z) {
+		this.z = z;
+	}
+
+	/** Replies the W coordinate.
+	 * 
+	 * @return w
+	 */
+	public float getW() {
+		return this.w;
+	}
+
+	/** Set the W coordinate.
+	 * 
+	 * @param w
+	 */
+	public void setW(float w) {
+		this.w = w;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(Object t1) {
+		try {
+			Quaternion t2 = (Quaternion) t1;
+			return(this.x == t2.getX() && this.y == t2.getY() && this.z == t2.getZ() && this.w == t2.getW());
+		}
+		catch(AssertionError e) {
+			throw e;
+		}
+		catch (Throwable e2) {
+			return false;
+		}
+	}
+
+	/**
+	 * Returns true if the L-infinite distance between this tuple
+	 * and tuple t1 is less than or equal to the epsilon parameter, 
+	 * otherwise returns false.  The L-infinite
+	 * distance is equal to MAX[abs(x1-x2), abs(y1-y2)]. 
+	 * @param t1  the tuple to be compared to this tuple
+	 * @param epsilon  the threshold value  
+	 * @return  true or false
+	 */
+	public boolean epsilonEquals(Quaternion t1, float epsilon) {
+		float diff;
+
+		diff = this.x - t1.getX();
+		if(Float.isNaN(diff)) return false;
+		if((diff<0?-diff:diff) > epsilon) return false;
+
+		diff = this.y - t1.getY();
+		if(Float.isNaN(diff)) return false;
+		if((diff<0?-diff:diff) > epsilon) return false;
+
+		diff = this.z - t1.getZ();
+		if(Float.isNaN(diff)) return false;
+		if((diff<0?-diff:diff) > epsilon) return false;
+
+		diff = this.w - t1.getW();
+		if(Float.isNaN(diff)) return false;
+		if((diff<0?-diff:diff) > epsilon) return false;
+
+		return true;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int hashCode() {
+		int bits = 1;
+		bits = 31 * bits + Float.floatToIntBits(this.x);
+		bits = 31 * bits + Float.floatToIntBits(this.y);
+		bits = 31 * bits + Float.floatToIntBits(this.z);
+		bits = 31 * bits + Float.floatToIntBits(this.w);
+		return bits ^ (bits >> 32);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		return "(" //$NON-NLS-1$
+				+this.x
+				+";" //$NON-NLS-1$
+				+this.y
+				+";" //$NON-NLS-1$
+				+this.z
+				+";" //$NON-NLS-1$
+				+this.w
+				+")"; //$NON-NLS-1$
+	}
+
+	/**
+	 * Sets the value of this quaternion to the conjugate of quaternion q1.
+	 * @param q1 the source vector
+	 */
+	public final void conjugate(Quaternion q1) {
+		this.x = -q1.x;
+		this.y = -q1.y;
+		this.z = -q1.z;
+		this.w = q1.w;
+	}
+
+	/**
+	 * Sets the value of this quaternion to the conjugate of itself.
+	 */
+	public final void conjugate() {
+		this.x = -this.x;
+		this.y = -this.y;
+		this.z = -this.z;
+	}
+
+	/**
+	 * Sets the value of this quaternion to the quaternion product of
+	 * quaternions q1 and q2 (this = q1 * q2).  
+	 * Note that this is safe for aliasing (e.g. this can be q1 or q2).
+	 * @param q1 the first quaternion
+	 * @param q2 the second quaternion
+	 */
+	public final void mul(Quaternion q1, Quaternion q2) {
+		if (this != q1 && this != q2) {
+			this.w = q1.w*q2.w - q1.x*q2.x - q1.y*q2.y - q1.z*q2.z;
+			this.x = q1.w*q2.x + q2.w*q1.x + q1.y*q2.z - q1.z*q2.y;
+			this.y = q1.w*q2.y + q2.w*q1.y - q1.x*q2.z + q1.z*q2.x;
+			this.z = q1.w*q2.z + q2.w*q1.z + q1.x*q2.y - q1.y*q2.x;
+		}
+		else {
+			float	x, y, w;
+
+			w = q1.w*q2.w - q1.x*q2.x - q1.y*q2.y - q1.z*q2.z;
+			x = q1.w*q2.x + q2.w*q1.x + q1.y*q2.z - q1.z*q2.y;
+			y = q1.w*q2.y + q2.w*q1.y - q1.x*q2.z + q1.z*q2.x;
+			this.z = q1.w*q2.z + q2.w*q1.z + q1.x*q2.y - q1.y*q2.x;
+			this.w = w;
+			this.x = x;
+			this.y = y;
+		}
+	}
+
+
+	/**
+	 * Sets the value of this quaternion to the quaternion product of
+	 * itself and q1 (this = this * q1).  
+	 * @param q1 the other quaternion
+	 */
+	public final void mul(Quaternion q1) {
+		float     x, y, w; 
+
+		w = this.w*q1.w - this.x*q1.x - this.y*q1.y - this.z*q1.z;
+		x = this.w*q1.x + q1.w*this.x + this.y*q1.z - this.z*q1.y;
+		y = this.w*q1.y + q1.w*this.y - this.x*q1.z + this.z*q1.x;
+		this.z = this.w*q1.z + q1.w*this.z + this.x*q1.y - this.y*q1.x;
+		this.w = w;
+		this.x = x;
+		this.y = y;
+	} 
+
+	/** 
+	 * Multiplies quaternion q1 by the inverse of quaternion q2 and places
+	 * the value into this quaternion.  The value of both argument quaternions 
+	 * is preservered (this = q1 * q2^-1).
+	 * @param q1 the first quaternion
+	 * @param q2 the second quaternion
+	 */ 
+	public final void mulInverse(Quaternion q1, Quaternion q2) 
+	{   
+		Quaternion  tempQuat = q2.clone();  
+
+		tempQuat.inverse(); 
+		this.mul(q1, tempQuat); 
+	}
+
+
+
+	/**
+	 * Multiplies this quaternion by the inverse of quaternion q1 and places
+	 * the value into this quaternion.  The value of the argument quaternion
+	 * is preserved (this = this * q^-1).
+	 * @param q1 the other quaternion
+	 */
+	public final void mulInverse(Quaternion q1) {  
+		Quaternion  tempQuat = q1.clone();
+
+		tempQuat.inverse();
+		this.mul(tempQuat);
+	}
+
+	/**
+	 * Sets the value of this quaternion to quaternion inverse of quaternion q1.
+	 * @param q1 the quaternion to be inverted
+	 */
+	public final void inverse(Quaternion q1) {
+		float norm;
+
+		norm = 1f/(q1.w*q1.w + q1.x*q1.x + q1.y*q1.y + q1.z*q1.z);
+		this.w =  norm*q1.w;
+		this.x = -norm*q1.x;
+		this.y = -norm*q1.y;
+		this.z = -norm*q1.z;
+	}
+
+
+	/**
+	 * Sets the value of this quaternion to the quaternion inverse of itself.
+	 */
+	public final void inverse() {
+		float norm;  
+
+		norm = 1f/(this.w*this.w + this.x*this.x + this.y*this.y + this.z*this.z);
+		this.w *=  norm;
+		this.x *= -norm;
+		this.y *= -norm;
+		this.z *= -norm;
+	}
+
+	/**
+	 * Sets the value of this quaternion to the normalized value
+	 * of quaternion q1.
+	 * @param q1 the quaternion to be normalized.
+	 */
+	public final void normalize(Quaternion q1) {
+		float norm;
+
+		norm = (q1.x*q1.x + q1.y*q1.y + q1.z*q1.z + q1.w*q1.w);
+
+		if (norm > 0f) {
+			norm = 1f/(float)Math.sqrt(norm);
+			this.x = norm*q1.x;
+			this.y = norm*q1.y;
+			this.z = norm*q1.z;
+			this.w = norm*q1.w;
+		} else {
+			this.x = 0f;
+			this.y = 0f;
+			this.z = 0f;
+			this.w = 0f;
+		}
+	}
+
+
+	/**
+	 * Normalizes the value of this quaternion in place.
+	 */
+	public final void normalize() {
+		float norm;
+
+		norm = (this.x*this.x + this.y*this.y + this.z*this.z + this.w*this.w);
+
+		if (norm > 0f) {
+			norm = 1f / (float)Math.sqrt(norm);
+			this.x *= norm;
+			this.y *= norm;
+			this.z *= norm;
+			this.w *= norm;
+		} else {
+			this.x = 0f;
+			this.y = 0f;
+			this.z = 0f;
+			this.w = 0f;
+		}
+	}
+
+	/**
+	 * Sets the value of this quaternion to the rotational component of
+	 * the passed matrix.
+	 * @param m1 the Matrix4f
+	 */
+	public final void setFromMatrix(Matrix4f m1) {
+		float ww = 0.25f*(m1.m00 + m1.m11 + m1.m22 + m1.m33);
+
+		if (ww >= 0) {
+			if (ww >= EPS2) {
+				this.w = (float) Math.sqrt(ww);
+				ww =  0.25f/this.w;
+				this.x = (m1.m21 - m1.m12)*ww;
+				this.y = (m1.m02 - m1.m20)*ww;
+				this.z = (m1.m10 - m1.m01)*ww;
+				return;
+			} 
+		}
+		else {
+			this.w = 0;
+			this.x = 0;
+			this.y = 0;
+			this.z = 1;
+			return;
+		}
+
+		this.w = 0;
+		ww = -0.5f*(m1.m11 + m1.m22);
+
+		if (ww >= 0) {
+			if (ww >= EPS2) {
+				this.x = (float) Math.sqrt(ww);
+				ww = 1.0f/(2.0f*this.x);
+				this.y = m1.m10*ww;
+				this.z = m1.m20*ww;
+				return;
+			}
+		} else {
+			this.x = 0;
+			this.y = 0;
+			this.z = 1;
+			return;
+		}
+
+		this.x = 0;
+		ww = 0.5f*(1.0f - m1.m22);
+
+		if (ww >= EPS2) {
+			this.y = (float) Math.sqrt(ww);
+			this.z = m1.m21/(2.0f*this.y);
+			return;
+		}
+
+		this.y = 0;
+		this.z = 1;
+	}
+
+	/**
+	 * Sets the value of this quaternion to the rotational component of
+	 * the passed matrix.
+	 * @param m1 the Matrix3f
+	 */
+	public final void setFromMatrix(Matrix3f m1) {
+		float ww = 0.25f*(m1.m00 + m1.m11 + m1.m22 + 1.0f);
+
+		if (ww >= 0) {
+			if (ww >= EPS2) {
+				this.w = (float) Math.sqrt(ww);
+				ww = 0.25f/this.w;
+				this.x = (m1.m21 - m1.m12)*ww;
+				this.y = (m1.m02 - m1.m20)*ww;
+				this.z = (m1.m10 - m1.m01)*ww;
+				return;
+			}
+		} else {
+			this.w = 0;
+			this.x = 0;
+			this.y = 0;
+			this.z = 1;
+			return;
+		}
+
+		this.w = 0;
+		ww = -0.5f*(m1.m11 + m1.m22);
+		if (ww >= 0) {
+			if (ww >= EPS2) {
+				this.x = (float) Math.sqrt(ww);
+				ww = 0.5f/this.x;
+				this.y = m1.m10*ww;
+				this.z = m1.m20*ww;
+				return;
+			}
+		} else {
+			this.x = 0;
+			this.y = 0;
+			this.z = 1;
+			return;
+		}
+
+		this.x = 0;
+		ww =  0.5f*(1.0f - m1.m22);
+		if (ww >= EPS2) {
+			this.y = (float) Math.sqrt(ww);
+			this.z = m1.m21/(2.0f*this.y);
+			return;
+		}
+
+		this.y = 0;
+		this.z = 1;
+	}
+
+	/** Set the quaternion coordinates.
+	 * 
+	 * @param x
+	 * @param y
+	 * @param z
+	 * @param w
+	 */
+	public void set(float x, float y, float z, float w) {
+		float mag = (float)(1.0/Math.sqrt( x*x + y*y + z*z + w*w ));
+		this.x = x*mag;
+		this.y = y*mag;
+		this.z = z*mag;
+		this.w = w*mag;
+	}
+
+	/**
+	 * Sets the value of this quaternion to the equivalent rotation
+	 * of the Axis-Angle arguments.
+	 * @param axis is the axis of rotation.
+	 * @param angle is the rotation around the axis.
+	 */
+	public final void setAxisAngle(Vector3D axis, float angle) {
+		setAxisAngle(axis.getX(),  axis.getY(), axis.getZ(), angle);
+	}
+
+	/**
+	 * Sets the value of this quaternion to the equivalent rotation
+	 * of the Axis-Angle arguments.
+	 * @param x is the x coordinate of the rotation axis
+	 * @param y is the y coordinate of the rotation axis
+	 * @param z is the z coordinate of the rotation axis
+	 * @param angle is the rotation around the axis.
+	 */
+	public final void setAxisAngle(float x, float y, float z, float angle) {
+		float mag,amag;
+		// Quat = cos(theta/2) + sin(theta/2)(roation_axis) 
+		amag = (float)Math.sqrt(x*x + y*y + z*z);
+		if (amag < EPS ) {
+			this.w = 0.0f;
+			this.x = 0.0f;
+			this.y = 0.0f;
+			this.z = 0.0f;
+		}
+		else {  
+			amag = 1.0f/amag; 
+			mag = (float)Math.sin(angle/2.0);
+			this.w = (float)Math.cos(angle/2.0);
+			this.x = x*amag*mag;
+			this.y = y*amag*mag;
+			this.z = z*amag*mag;
+		}
+	}
+
+	/** Replies the rotation axis represented by this quaternion.
+	 * 
+	 * @return the rotation axis
+	 * @see #setAxisAngle(Vector3D, float)
+	 * @see #setAxisAngle(float, float, float, float)
+	 * @see #getAngle()
+	 */
+	public final Vector3f getAxis() {
+		float mag = this.x*this.x + this.y*this.y + this.z*this.z;  
+
+		if ( mag > EPS ) {
+			mag = (float)Math.sqrt(mag);
+			float invMag = 1f/mag;
+
+			return new Vector3f(
+					this.x*invMag,
+					this.y*invMag,
+					this.z*invMag);
+		}
+		return new Vector3f(0f, 0f, 1f);
+	}
+
+	/** Replies the rotation angle represented by this quaternion.
+	 * 
+	 * @return the rotation axis
+	 * @see #setAxisAngle(Vector3D, float)
+	 * @see #setAxisAngle(float, float, float, float)
+	 * @see #getAxis()
+	 */
+	public final float getAngle() {
+		float mag = this.x*this.x + this.y*this.y + this.z*this.z;  
+
+		if ( mag > EPS ) {
+			mag = (float)Math.sqrt(mag);
+			return (2.f*(float)Math.atan2(mag, this.w)); 
+		}
+		return 0f;
+	}
+
+	/**
+	 *  Performs a great circle interpolation between this quaternion
+	 *  and the quaternion parameter and places the result into this
+	 *  quaternion.
+	 *  @param q1  the other quaternion
+	 *  @param alpha  the alpha interpolation parameter
+	 */
+	public final void interpolate(Quaternion q1, float alpha) {
+		// From "Advanced Animation and Rendering Techniques"
+		// by Watt and Watt pg. 364, function as implemented appeared to be 
+		// incorrect.  Fails to choose the same quaternion for the double
+		// covering. Resulting in change of direction for rotations.
+		// Fixed function to negate the first quaternion in the case that the
+		// dot product of q1 and this is negative. Second case was not needed. 
+
+		double dot,s1,s2,om,sinom;
+
+		dot = this.x*q1.x + this.y*q1.y + this.z*q1.z + this.w*q1.w;
+
+		if ( dot < 0 ) {
+			// negate quaternion
+			q1.x = -q1.x;  q1.y = -q1.y;  q1.z = -q1.z;  q1.w = -q1.w;
+			dot = -dot;
+		}
+
+		if ( (1.0 - dot) > EPS ) {
+			om = Math.acos(dot);
+			sinom = Math.sin(om);
+			s1 = Math.sin((1.0-alpha)*om)/sinom;
+			s2 = Math.sin( alpha*om)/sinom;
+		} else{
+			s1 = 1.0 - alpha;
+			s2 = alpha;
+		}
+
+		this.w = (float)(s1*this.w + s2*q1.w);
+		this.x = (float)(s1*this.x + s2*q1.x);
+		this.y = (float)(s1*this.y + s2*q1.y);
+		this.z = (float)(s1*this.z + s2*q1.z);
+	}
+
+
+
+	/** 
+	 *  Performs a great circle interpolation between quaternion q1
+	 *  and quaternion q2 and places the result into this quaternion. 
+	 *  @param q1  the first quaternion
+	 *  @param q2  the second quaternion
+	 *  @param alpha  the alpha interpolation parameter 
+	 */   
+	public final void interpolate(Quaternion q1, Quaternion q2, float alpha) { 
+		// From "Advanced Animation and Rendering Techniques"
+		// by Watt and Watt pg. 364, function as implemented appeared to be 
+		// incorrect.  Fails to choose the same quaternion for the double
+		// covering. Resulting in change of direction for rotations.
+		// Fixed function to negate the first quaternion in the case that the
+		// dot product of q1 and this is negative. Second case was not needed. 
+
+		double dot,s1,s2,om,sinom;
+
+		dot = q2.x*q1.x + q2.y*q1.y + q2.z*q1.z + q2.w*q1.w;
+
+		if ( dot < 0 ) {
+			// negate quaternion
+			q1.x = -q1.x;  q1.y = -q1.y;  q1.z = -q1.z;  q1.w = -q1.w;
+			dot = -dot;
+		}
+
+		if ( (1.0 - dot) > EPS ) {
+			om = Math.acos(dot);
+			sinom = Math.sin(om);
+			s1 = Math.sin((1.0-alpha)*om)/sinom;
+			s2 = Math.sin( alpha*om)/sinom;
+		} else{
+			s1 = 1.0 - alpha;
+			s2 = alpha;
+		}
+		this.w = (float)(s1*q1.w + s2*q2.w);
+		this.x = (float)(s1*q1.x + s2*q2.x);
+		this.y = (float)(s1*q1.y + s2*q2.y);
+		this.z = (float)(s1*q1.z + s2*q2.z);
+	}
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Tuple3f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Tuple3f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Tuple3f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,710 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object3d;
+
+import org.arakhne.afc.math.generic.Tuple3D;
+
+/** 3D tuple with 3 floating-point numbers.
+ * 
+ * @param <T> is the implementation type of the tuple.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Tuple3f<T extends Tuple3D<? super T>> implements Tuple3D<T> {
+
+	private static final long serialVersionUID = -2153633162767463917L;
+
+	/** x coordinate.
+	 */
+	protected float x;
+
+	/** y coordinate.
+	 */
+	protected float y;
+
+	/** z coordinate.
+	 */
+	protected float z;
+
+	/**
+	 */
+	public Tuple3f() {
+		this.x = this.y = this.z = 0;
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Tuple3f(Tuple3D<?> tuple) {
+		this.x = tuple.getX();
+		this.y = tuple.getY();
+		this.z = tuple.getZ();
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Tuple3f(int[] tuple) {
+		this.x = tuple[0];
+		this.y = tuple[1];
+		this.z = tuple[2];
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Tuple3f(float[] tuple) {
+		this.x = tuple[0];
+		this.y = tuple[1];
+		this.z = tuple[2];
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Tuple3f(int x, int y, int z) {
+		this.x = x;
+		this.y = y;
+		this.z = z;
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Tuple3f(float x, float y, float z) {
+		this.x = x;
+		this.y = y;
+		this.z = z;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public T clone() {
+		try {
+			return (T)super.clone();
+		}
+		catch(CloneNotSupportedException e) {
+			throw new Error(e);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void absolute() {
+		this.x = Math.abs(this.x);
+		this.y = Math.abs(this.y);
+		this.z = Math.abs(this.z);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void absolute(T t) {
+		t.set(Math.abs(this.x), Math.abs(this.y), Math.abs(this.z));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void add(int x, int y, int z) {
+		this.x += x;
+		this.y += y;
+		this.z += z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void add(float x, float y, float z) {
+		this.x += x;
+		this.y += y;
+		this.z += z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addX(int x) {
+		this.x += x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addX(float x) {
+		this.x += x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addY(int y) {
+		this.y += y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addY(float y) {
+		this.y += y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addZ(int z) {
+		this.z += z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addZ(float z) {
+		this.z += z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clamp(int min, int max) {
+		if (this.x < min) this.x = min;
+		else if (this.x > max) this.x = max;
+		if (this.y < min) this.y = min;
+		else if (this.y > max) this.y = max;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clamp(float min, float max) {
+		clamp(min, max);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMin(int min) {
+		if (this.x < min) this.x = min;
+		if (this.y < min) this.y = min;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMin(float min) {
+		clampMin(min);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMax(int max) {
+		if (this.x > max) this.x = max;
+		if (this.y > max) this.y = max;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMax(float max) {
+		clampMax(max);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clamp(int min, int max, T t) {
+		if (this.x < min) t.setX(min);
+		else if (this.x > max) t.setX(max);
+		if (this.y < min) t.setY(min);
+		else if (this.y > max) t.setY(max);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clamp(float min, float max, T t) {
+		clamp(min, max, t);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMin(int min, T t) {
+		if (this.x < min) t.setX(min);
+		if (this.y < min) t.setY(min);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMin(float min, T t) {
+		clampMin(min, t);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMax(int max, T t) {
+		if (this.x > max) t.setX(max);
+		if (this.y > max) t.setY(max);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMax(float max, T t) {
+		clampMax(max, t);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void get(T t) {
+		t.set(this.x, this.y, this.z);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void get(int[] t) {
+		t[0] = (int)this.x;
+		t[1] = (int)this.y;
+		t[2] = (int)this.z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void get(float[] t) {
+		t[0] = this.x;
+		t[1] = this.y;
+		t[2] = this.z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void negate(T t1) {
+		this.x = -t1.getX();
+		this.y = -t1.getY();
+		this.z = -t1.getZ();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void negate() {
+		this.x = -this.x;
+		this.y = -this.y;
+		this.z = -this.z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(int s, T t1) {
+		this.x = s * t1.getX();
+		this.y = s * t1.getY();
+		this.z = s * t1.getZ();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(float s, T t1) {
+		this.x = (s * t1.getX());
+		this.y = (s * t1.getY());
+		this.z = (s * t1.getZ());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(int s) {
+		this.x = s * this.x;
+		this.y = s * this.y;
+		this.z = s * this.z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(float s) {
+		this.x = (s * this.x);
+		this.y = (s * this.y);
+		this.z = (s * this.z);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(Tuple3D<?> t1) {
+		this.x = t1.getX();
+		this.y = t1.getY();
+		this.z = t1.getZ();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(int x, int y, int z) {
+		this.x = x;
+		this.y = y;
+		this.z = z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(float x, float y, float z) {
+		this.x = x;
+		this.y = y;
+		this.z = z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(int[] t) {
+		this.x = t[0];
+		this.y = t[1];
+		this.z = t[2];
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(float[] t) {
+		this.x = t[0];
+		this.y = t[1];
+		this.z = t[2];
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float getX() {
+		return this.x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int x() {
+		return (int)this.x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setX(int x) {
+		this.x = x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setX(float x) {
+		this.x = x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float getY() {
+		return this.y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int y() {
+		return (int)this.y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setY(int y) {
+		this.y = y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setY(float y) {
+		this.y = y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float getZ() {
+		return this.z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int z() {
+		return (int)this.z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setZ(int z) {
+		this.z = z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setZ(float z) {
+		this.z = z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void sub(int x, int y, int z) {
+		this.x -= x;
+		this.y -= y;
+		this.z -= z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subX(int x) {
+		this.x -= x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subY(int y) {
+		this.y -= y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subZ(int z) {
+		this.z -= z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void sub(float x, float y, float z) {
+		this.x -= x;
+		this.y -= y;
+		this.z -= z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subX(float x) {
+		this.x -= x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subY(float y) {
+		this.y -= y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subZ(float z) {
+		this.z -= z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void interpolate(T t1, T t2, float alpha) {
+		this.x = ((1f-alpha)*t1.getX() + alpha*t2.getX());
+		this.y = ((1f-alpha)*t1.getY() + alpha*t2.getY());
+		this.z = ((1f-alpha)*t1.getZ() + alpha*t2.getZ());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void interpolate(T t1, float alpha) {
+		this.x = ((1f-alpha)*this.x + alpha*t1.getX());
+		this.y = ((1f-alpha)*this.y + alpha*t1.getY());
+		this.z = ((1f-alpha)*this.z + alpha*t1.getZ());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(Tuple3D<?> t1) {
+		try {
+			return(this.x == t1.getX() && this.y == t1.getY() && this.z == t1.getZ());
+		}
+		catch (NullPointerException e2) {
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public boolean equals(Object t1) {
+		try {
+			T t2 = (T) t1;
+			return(this.x == t2.getX() && this.y == t2.getY() && this.z == t2.getZ());
+		}
+		catch(AssertionError e) {
+			throw e;
+		}
+		catch (Throwable e2) {
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean epsilonEquals(T t1, float epsilon) {
+		float diff;
+
+		diff = this.x - t1.getX();
+		if(Float.isNaN(diff)) return false;
+		if((diff<0?-diff:diff) > epsilon) return false;
+
+		diff = this.y - t1.getY();
+		if(Float.isNaN(diff)) return false;
+		if((diff<0?-diff:diff) > epsilon) return false;
+
+		diff = this.z - t1.getZ();
+		if(Float.isNaN(diff)) return false;
+		if((diff<0?-diff:diff) > epsilon) return false;
+
+		return true;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int hashCode() {
+		int bits = 1;
+		bits = 31 * bits + Float.floatToIntBits(this.x);
+		bits = 31 * bits + Float.floatToIntBits(this.y);
+		bits = 31 * bits + Float.floatToIntBits(this.z);
+		return bits ^ (bits >> 32);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		return "(" //$NON-NLS-1$
+				+this.x
+				+";" //$NON-NLS-1$
+				+this.y
+				+";" //$NON-NLS-1$
+				+this.z
+				+")"; //$NON-NLS-1$
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Tuple3fComparator.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Tuple3fComparator.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Tuple3fComparator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,56 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object3d;
+
+import java.util.Comparator;
+
+/**
+ * Comparator of Tuple2f.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Tuple3fComparator implements Comparator<Tuple3f<?>> {
+	
+	/**
+	 */
+	public Tuple3fComparator() {
+		//
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int compare(Tuple3f<?> o1, Tuple3f<?> o2) {
+		if (o1==o2) return 0;
+		if (o1==null) return Integer.MIN_VALUE;
+		if (o2==null) return Integer.MAX_VALUE;
+		int cmp = Float.compare(o1.getX(), o2.getX());
+		if (cmp!=0) return cmp;
+		cmp = Float.compare(o1.getY(), o2.getY());
+		if (cmp!=0) return cmp;
+		return Float.compare(o1.getZ(), o2.getZ());
+	}
+		
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Vector3f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Vector3f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/continous/object3d/Vector3f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,321 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object3d;
+
+import org.arakhne.afc.math.generic.Point3D;
+import org.arakhne.afc.math.generic.Tuple3D;
+import org.arakhne.afc.math.generic.Vector3D;
+import org.arakhne.afc.math.matrix.Matrix3f;
+import org.arakhne.afc.math.matrix.Transform3D;
+
+/** 3D Vector with 3 floating-point values.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Vector3f extends Tuple3f<Vector3D> implements Vector3D {
+
+	private static final long serialVersionUID = -1222875298451525734L;
+
+	/**
+	 */
+	public Vector3f() {
+		//
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Vector3f(Tuple3D<?> tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Vector3f(int[] tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Vector3f(float[] tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Vector3f(int x, int y, int z) {
+		super(x,y,z);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Vector3f(float x, float y, float z) {
+		super(x,y,z);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Vector3f(double x, double y, double z) {
+		super((float)x,(float)y,(float)z);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Vector3f(long x, long y, long z) {
+		super(x,y,z);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Vector3f clone() {
+		return (Vector3f)super.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float angle(Vector3D v1) {
+		double vDot = dot(v1) / ( length()*v1.length() );
+		if( vDot < -1.) vDot = -1.;
+		if( vDot >  1.) vDot =  1.;
+		return((float) (Math.acos( vDot )));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float dot(Vector3D v1) {
+		return (this.x*v1.getX() + this.y*v1.getY() + this.z*v1.getZ());
+	}
+
+	/**
+	 * Multiply this vector, transposed, by the given matrix and replies the resulting vector.
+	 * 
+	 * @param m
+	 * @return transpose(this * m)
+	 */
+	public final Vector3f mul(Matrix3f m) {
+		Vector3f r = new Vector3f();
+		r.x = this.getX() * m.m00 + this.getY() * m.m01 + this.getZ() * m.m02;
+		r.y = this.getX() * m.m10 + this.getY() * m.m11 + this.getZ() * m.m12;
+		r.z = this.getX() * m.m20 + this.getY() * m.m21 + this.getZ() * m.m22;
+		return r;
+	}
+
+	@Override
+	public Vector3D cross(Vector3D v1) {
+		return crossLeftHand(v1);
+	}
+
+	@Override
+	public void cross(Vector3D v1, Vector3D v2) {
+		crossLeftHand(v1, v2);
+	}
+
+	@Override
+	public Vector3D crossLeftHand(Vector3D v1) {
+		float x = v1.getY()*getZ() - v1.getZ()*getY();
+		float y = v1.getZ()*getX() - v1.getX()*getZ();
+		float z = v1.getX()*getY() - v1.getY()*getX();
+		return new Vector3f(x,y,z);
+	}
+
+	@Override
+	public void crossLeftHand(Vector3D v1, Vector3D v2) {
+		float x = v2.getY()*v1.getZ() - v2.getZ()*v1.getY();
+		float y = v2.getZ()*v1.getX() - v2.getX()*v1.getZ();
+		float z = v2.getX()*v1.getY() - v2.getY()*v1.getX();
+		set(x,y,z);
+	}
+
+	@Override
+	public Vector3D crossRightHand(Vector3D v1) {
+		float x = getY()*v1.getZ() - getZ()*v1.getY();
+		float y = getZ()*v1.getX() - getX()*v1.getZ();
+		float z = getX()*v1.getY() - getY()*v1.getX();
+		return new Vector3f(x,y,z);
+	}
+
+	@Override
+	public void crossRightHand(Vector3D v1, Vector3D v2) {
+		float x = v1.getY()*v2.getZ() - v1.getZ()*v2.getY();
+		float y = v1.getZ()*v2.getX() - v1.getX()*v2.getZ();
+		float z = v1.getX()*v2.getY() - v1.getY()*v2.getX();
+		set(x,y,z);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float length() {
+        return (float) Math.sqrt(this.x*this.x + this.y*this.y + this.z*this.z);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float lengthSquared() {
+        return (this.x*this.x + this.y*this.y + this.z*this.z);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void normalize(Vector3D v1) {
+        float norm = 1f / v1.length();
+        this.x = (int)(v1.getX()*norm);
+        this.y = (int)(v1.getY()*norm);
+        this.z = (int)(v1.getZ()*norm);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void normalize() {
+        float norm;
+        norm = (float)(1./Math.sqrt(this.x*this.x + this.y*this.y + this.z*this.z));
+        this.x *= norm;
+        this.y *= norm;
+        this.z *= norm;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void turnVector(Vector3D axis, float angle) {
+		Transform3D mat = new Transform3D();
+		mat.setRotation(new Quaternion(axis, angle));
+		mat.transform(this);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void add(Vector3D t1, Vector3D t2) {
+		this.x = t1.getX() + t2.getX();
+		this.y = t1.getY() + t2.getY();
+		this.z = t1.getZ() + t2.getZ();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void add(Vector3D t1) {
+		this.x += t1.getX();
+		this.y += t1.getY();
+		this.z += t1.getZ();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scaleAdd(int s, Vector3D t1, Vector3D t2) {
+		this.x = s * t1.getX() + t2.getX();
+		this.y = s * t1.getY() + t2.getY();
+		this.z = s * t1.getZ() + t2.getZ();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scaleAdd(float s, Vector3D t1, Vector3D t2) {
+		this.x = s * t1.getX() + t2.getX();
+		this.y = s * t1.getY() + t2.getY();
+		this.z = s * t1.getZ() + t2.getZ();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scaleAdd(int s, Vector3D t1) {
+		this.x = s * this.x + t1.getX();
+		this.y = s * this.y + t1.getY();
+		this.z = s * this.z + t1.getZ();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scaleAdd(float s, Vector3D t1) {
+		this.x = s * this.x + t1.getX();
+		this.y = s * this.y + t1.getY();
+		this.z = s * this.z + t1.getZ();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void sub(Vector3D t1, Vector3D t2) {
+		this.x = t1.getX() - t2.getX();
+		this.y = t1.getY() - t2.getY();
+		this.z = t1.getZ() - t2.getZ();
+	}
+
+	@Override
+	public void sub(Point3D t1, Point3D t2) {
+		this.x = t1.getX() - t2.getX();
+		this.y = t1.getY() - t2.getY();
+		this.z = t1.getZ() - t2.getZ();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void sub(Vector3D t1) {
+		this.x -= t1.getX();
+		this.y -= t1.getY();
+		this.z -= t1.getZ();
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/AbstractRectangularShape2i.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/AbstractRectangularShape2i.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/AbstractRectangularShape2i.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,297 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import org.arakhne.afc.math.generic.Point2D;
+
+
+
+/** 2D rectangular shape with integer points.
+ * 
+ * @param <T> is the type of the shape implemented by the instance of this class.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractRectangularShape2i<T extends Shape2i> extends AbstractShape2i<T> {
+
+	private static final long serialVersionUID = -2716430612894131964L;
+	
+	/** Lowest x-coordinate covered by the rectangular shape. */
+	protected int minx = 0;
+	/** Lowest y-coordinate covered by the rectangular shape. */
+	protected int miny = 0;
+	/** Highest x-coordinate covered by the rectangular shape. */
+	protected int maxx = 0;
+	/** Highest x-coordinate covered by the rectangular shape. */
+	protected int maxy = 0;
+	
+	/**
+	 */
+	public AbstractRectangularShape2i() {
+		//
+	}
+	
+	@Override
+	public void clear() {
+		this.minx = this.miny = this.maxx = this.maxy = 0;
+	}
+	
+	/** Replies if this rectangle is empty or not.
+	 * A rectangle is empty when the two corners
+	 * of the rectangle are at the same location.
+	 * 
+	 * @return <code>true</code> if the two corners are
+	 * at the same location; <code>false</code> otherwise.
+	 */
+	@Override
+	public boolean isEmpty() {
+		return this.minx==this.maxx && this.miny==this.maxy; 
+	}
+
+	/** Set the coordinates of this shape from the bounds of the given shape.
+	 * 
+	 * @param shape
+	 */
+	public void set(AbstractRectangularShape2i<?> shape) {
+		setFromCorners(shape.getMinX(), shape.getMinY(), shape.getMaxX(), shape.getMaxY());
+	}
+	
+	/** Change the frame of the rectangle.
+	 * 
+	 * @param x
+	 * @param y
+	 * @param width
+	 * @param height
+	 */
+	public void set(int x, int y, int width, int height) {
+		setFromCorners(x, y, x+width, y+height);
+	}
+	
+	/** Change the frame of te rectangle.
+	 * 
+	 * @param min is the min corner of the rectangle.
+	 * @param max is the max corner of the rectangle.
+	 */
+	public void set(Point2D min, Point2D max) {
+		setFromCorners(min.x(), min.y(), max.x(), max.y());
+	}
+
+	/** Change the frame of the rectangle.
+	 * 
+	 * @param x1 is the coordinate of the first corner.
+	 * @param y1 is the coordinate of the first corner.
+	 * @param x2 is the coordinate of the second corner.
+	 * @param y2 is the coordinate of the second corner.
+	 */
+	public void setFromCorners(int x1, int y1, int x2, int y2) {
+		if (x1<x2) {
+			this.minx = x1;
+			this.maxx = x2;
+		}
+		else {
+			this.minx = x2;
+			this.maxx = x1;
+		}
+		if (y1<y2) {
+			this.miny = y1;
+			this.maxy = y2;
+		}
+		else {
+			this.miny = y2;
+			this.maxy = y1;
+		}
+	}
+	
+	/** Change the frame of the rectangle.
+	 * 
+	 * @param c1 is the first corner.
+	 * @param c2 is the first corner.
+	 */
+	public void setFromCorners(Point2D c2, Point2D c1) {
+		setFromCorners(c1.x(),  c1.y(),  c2.x(), c2.y());
+	}
+	
+	/**
+     * Sets the framing rectangle of this <code>Shape</code>
+     * based on the specified center point coordinates and corner point
+     * coordinates.  The framing rectangle is used by the subclasses of
+     * <code>RectangularShape</code> to define their geometry.
+     *
+     * @param centerX the X coordinate of the specified center point
+     * @param centerY the Y coordinate of the specified center point
+     * @param cornerX the X coordinate of the specified corner point
+     * @param cornerY the Y coordinate of the specified corner point
+     */
+	public void setFromCenter(int centerX, int centerY, int cornerX, int cornerY) {
+		int dx = centerX - cornerX;
+		int dy = centerY - cornerY;
+		setFromCorners(cornerX, cornerY, centerX + dx, centerY + dy);
+	}
+	
+	/** Replies the min X.
+	 * 
+	 * @return the min x.
+	 */
+	public int getMinX() {
+		return this.minx;
+	}
+
+	/** Replies the max x.
+	 * 
+	 * @return the max x.
+	 */
+	public int getMaxX() {
+		return this.maxx;
+	}
+
+	/** Replies the min y.
+	 * 
+	 * @return the min y.
+	 */
+	public int getMinY() {
+		return this.miny;
+	}
+
+	/** Replies the max y.
+	 * 
+	 * @return the max y.
+	 */
+	public int getMaxY() {
+		return this.maxy;
+	}
+	
+	/** Replies the width.
+	 * 
+	 * @return the width.
+	 */
+	public int getWidth() {
+		return this.maxx - this.minx;
+	}
+
+	/** Replies the height.
+	 * 
+	 * @return the height.
+	 */
+	public int getHeight() {
+		return this.maxy - this.miny;
+	}
+	
+	/** Set the min X.
+	 * 
+	 * @param x the min x.
+	 */
+	public void setMinX(int x) {
+		int o = this.maxx;
+		if (o<x) {
+			this.minx = o;
+			this.maxx = x;
+		}
+		else {
+			this.minx = x;
+		}
+	}
+	
+	/** Set the min Y.
+	 * 
+	 * @param y the min y.
+	 */
+	public void setMinY(int y) {
+		int o = this.maxy;
+		if (o<y) {
+			this.miny = o;
+			this.maxy = y;
+		}
+		else {
+			this.miny = y;
+		}
+	}
+	
+	/** Set the max X.
+	 * 
+	 * @param x the max x.
+	 */
+	public void setMaxX(int x) {
+		int o = this.minx;
+		if (o>x) {
+			this.maxx = o;
+			this.minx = x;
+		}
+		else {
+			this.maxx = x;
+		}
+	}
+
+	/** Set the max Y.
+	 * 
+	 * @param y the max y.
+	 */
+	public void setMaxY(int y) {
+		int o = this.miny;
+		if (o>y) {
+			this.maxy = o;
+			this.miny = y;
+		}
+		else {
+			this.maxy = y;
+		}
+	}
+	
+	/** Change the width of the rectangle, not the min corner.
+	 * 
+	 * @param width
+	 */
+	public void setWidth(int width) {
+		this.maxx = this.minx + Math.max(0, width);
+	}
+
+	/** Change the height of the rectangle, not the min corner.
+	 * 
+	 * @param height
+	 */
+	public void setHeight(int height) {
+		this.maxy = this.miny + Math.max(0, height);
+	}
+	
+	@Override
+	public void translate(int dx, int dy) {
+		this.minx += dx;
+		this.miny += dy;
+		this.maxx += dx;
+		this.maxy += dy;
+	}
+	
+	/** Inflate this rectangle with the given amounts.
+	 * 
+	 * @param left
+	 * @param top
+	 * @param right
+	 * @param bottom
+	 */
+	public void inflate(int left, int top, int right, int bottom) {
+		this.minx -= left;
+		this.miny -= top;
+		this.maxx += right;
+		this.maxy += bottom;
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/AbstractShape2i.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/AbstractShape2i.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/AbstractShape2i.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,85 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+/** 2D shape with integer  points.
+ * 
+ * @param <T> is the type of the shape implemented by the instance of this class.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractShape2i<T extends Shape2i> implements Shape2i {
+
+	private static final long serialVersionUID = -3663448743772835647L;
+
+	/**
+	 */
+	public AbstractShape2i() {
+		//
+	}
+
+	/** {@inheritDoc}
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public T clone()  {
+		try {
+			return (T)super.clone();
+		}
+		catch (CloneNotSupportedException e) {
+			throw new Error(e);
+		}
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Shape2i createTransformedShape(Transform2D transform) {
+		return new Path2i(getPathIterator(transform));
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public final boolean contains(Point2D p) {
+		return contains(p.x(), p.y());
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public final float distance(Point2D p) {
+		 return (float)Math.sqrt(distanceSquared(p));
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public final PathIterator2i getPathIterator() {
+		return getPathIterator(null);
+	}
+	
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Circle2i.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Circle2i.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Circle2i.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,966 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.arakhne.afc.math.generic.PathWindingRule;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+
+/** 2D circle with integer coordinates.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Circle2i extends AbstractShape2i<Circle2i> {
+
+	private static final long serialVersionUID = -2396327310912728347L;
+
+	/**
+	 * ArcIterator.btan(Math.PI/2)
+	 */
+	static final float CTRL_VAL = 0.5522847498307933f;
+
+	/**
+	 * ctrlpts contains the control points for a set of 4 cubic
+	 * bezier curves that approximate a circle of radius 0.5
+	 * centered at 0.5, 0.5
+	 */
+	static final float PCV = 0.5f + CTRL_VAL * 0.5f;
+	/**
+	 * ctrlpts contains the control points for a set of 4 cubic
+	 * bezier curves that approximate a circle of radius 0.5
+	 * centered at 0.5, 0.5
+	 */
+	static final float NCV = 0.5f - CTRL_VAL * 0.5f;
+	/**
+	 * ctrlpts contains the control points for a set of 4 cubic
+	 * bezier curves that approximate a circle of radius 0.5
+	 * centered at 0.5, 0.5
+	 */
+	static float CTRL_PTS[][] = {
+		{  1.0f,  PCV,  PCV,  1.0f,  0.5f,  1.0f },
+		{  NCV,  1.0f,  0.0f,  PCV,  0.0f,  0.5f },
+		{  0.0f,  NCV,  NCV,  0.0f,  0.5f,  0.0f },
+		{  PCV,  0.0f,  1.0f,  NCV,  1.0f,  0.5f }
+	};
+
+	/** Replies if the given point is inside the circle.
+	 * 
+	 * @param cx is the x-coordinate of the circle center
+	 * @param cy is the y-coordinate of the circle center
+	 * @param cr is the radius of the circle center
+	 * @param x is the x-coordinate of the point
+	 * @param y is the y-coordinate of the point
+	 * @return <code>true</code> if the point is inside the circle.
+	 */
+	public static boolean contains(int cx, int cy, int cr, int x, int y) {
+		int vx = x - cx;
+		int vy = y - cy;
+
+		if (vx>=-cr && vx<=cr
+				&& vy>=-cr && vy<=cr) {
+			int octant;
+			boolean xpos = (vx>=0);
+			boolean ypos = (vy>=0);
+			if (xpos) {
+				if (ypos) {
+					octant = 0;
+				}
+				else {
+					octant = 2;
+				}
+			}
+			else {
+				if (ypos) {
+					octant = 6;
+				}
+				else {
+					octant = 4;
+				}
+			}
+
+			int px, py, ccw, cpx, cpy;
+			boolean allNull = true;
+			Point2i p;
+			CirclePerimeterIterator iterator = new CirclePerimeterIterator(
+					cx, cy, cr, octant, octant+2, false);
+
+			while (iterator.hasNext()) {
+				p = iterator.next();
+				
+				// Trivial case
+				if (p.x()==x && p.y()==y) return true;
+				
+				px = cy - p.y();
+				py = p.x() - cx;
+				cpx = x - p.x();
+				cpy = y - p.y();
+				ccw = cpx * py - cpy * px;
+
+				if (ccw>0) return false;
+				if (ccw<0) allNull = false;
+			}
+
+			return !allNull;
+		}
+
+		return false;
+	}
+
+	/** Replies if the given point is inside the quadrant of the given circle.
+	 * 
+	 * @param cx is the x-coordinate of the circle center
+	 * @param cy is the y-coordinate of the circle center
+	 * @param cr is the radius of the circle center
+	 * @param quadrant is the quadrant: <table>
+	 * <thead>
+	 * <th><td>quadrant</td><td>x</td><td>y</td></th>
+	 * </thead>
+	 * <tbody>
+	 * <tr><td>0</td><td>&ge;cx</td><td>&ge;cy</td></tr>
+	 * <tr><td>1</td><td>&ge;cx</td><td>&lt;cy</td></tr>
+	 * <tr><td>2</td><td>&lt;cx</td><td>&ge;cy</td></tr>
+	 * <tr><td>3</td><td>&lt;cx</td><td>&lt;cy</td></tr>
+	 * </tbody>
+	 * </table>
+	 * @param x is the x-coordinate of the point
+	 * @param y is the y-coordinate of the point
+	 * @return <code>true</code> if the point is inside the circle.
+	 */
+	public static boolean contains(int cx, int cy, int cr, int quadrant, int x, int y) {
+		int vx = x - cx;
+		int vy = y - cy;
+
+		if (vx>=-cr && vx<=cr
+				&& vy>=-cr && vy<=cr) {
+			int octant;
+			boolean xpos = (vx>=0);
+			boolean ypos = (vy>=0);
+			if (xpos) {
+				if (ypos) {
+					octant = 0;
+				}
+				else {
+					octant = 2;
+				}
+			}
+			else {
+				if (ypos) {
+					octant = 6;
+				}
+				else {
+					octant = 4;
+				}
+			}
+			
+			if (quadrant*2!=octant) return false;
+
+			int px, py, ccw, cpx, cpy;
+			Point2i p;
+			CirclePerimeterIterator iterator = new CirclePerimeterIterator(
+					cx, cy, cr, octant, octant+2, false);
+
+			while (iterator.hasNext()) {
+				p = iterator.next();
+				px = cy - p.y();
+				py = p.x() - cx;
+				cpx = x - p.x();
+				cpy = y - p.y();
+				ccw = cpx * py - cpy * px;
+
+				if (ccw>0) return false;
+			}
+
+			return true;
+		}
+
+		return false;
+	}
+
+	/** Replies the closest point in a circle to a point.
+	 * 
+	 * @param cx is the center of the circle
+	 * @param cy is the center of the circle
+	 * @param cr is the radius of the circle
+	 * @param x is the point
+	 * @param y is the point
+	 * @return the closest point in the circle to the point.
+	 */
+	public static Point2i computeClosestPointTo(int cx, int cy, int cr, int x, int y) {
+		int vx = x - cx;
+		int vy = y - cy;
+
+		int octant;
+		boolean xpos = (vx>=0);
+		boolean ypos = (vy>=0);
+		if (xpos) {
+			if (ypos) {
+				octant = 0;
+			}
+			else {
+				octant = 2;
+			}
+		}
+		else {
+			if (ypos) {
+				octant = 6;
+			}
+			else {
+				octant = 4;
+			}
+		}
+
+		int d, px, py, cpx, cpy, ccw;
+		Point2i p;
+		CirclePerimeterIterator iterator = new CirclePerimeterIterator(
+				cx, cy, cr, octant, octant+2, false);
+
+		boolean isInside = true;
+		int minDist = Integer.MAX_VALUE;
+		Point2i close = new Point2i();
+		
+		while (iterator.hasNext()) {
+			p = iterator.next();
+			px = cy - p.y();
+			py = p.x() - cx;
+			cpx = x - p.x();
+			cpy = y - p.y();
+			ccw = cpx * py - cpy * px;
+			if (ccw>=0) {
+				isInside = false;
+				d = cpx*cpx + cpy*cpy;
+				if (d<minDist) {
+					minDist = d;
+					close.set(p);
+				}
+			}
+		}
+
+		// inside the circle
+		if (isInside) close.set(x,y);
+		return close;
+	}
+
+	/** Replies if two circles are intersecting.
+	 * 
+	 * @param x1 is the center of the first circle
+	 * @param y1 is the center of the first circle
+	 * @param radius1 is the radius of the first circle
+	 * @param x2 is the center of the second circle
+	 * @param y2 is the center of the second circle
+	 * @param radius2 is the radius of the second circle
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsCircleCircle(int x1, int y1, int radius1, int x2, int y2, int radius2) {
+		Point2i c = computeClosestPointTo(x1, y1, radius1, x2, y2);
+		return contains(x2, y2, radius2, c.x(), c.y());
+	}
+
+	/** Replies if a circle and a rectangle are intersecting.
+	 * 
+	 * @param x1 is the center of the circle
+	 * @param y1 is the center of the circle
+	 * @param radius is the radius of the circle
+	 * @param x2 is the first corner of the rectangle.
+	 * @param y2 is the first corner of the rectangle.
+	 * @param x3 is the second corner of the rectangle.
+	 * @param y3 is the second corner of the rectangle.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsCircleRectangle(int x1, int y1, int radius, int x2, int y2, int x3, int y3) {
+		Point2i c = Rectangle2i.computeClosestPoint(x2, y2, x3, y3, x1, y1);
+		return contains(x1, y1, radius, c.x(), c.y());
+	}
+
+	/** Replies if a circle and a segment are intersecting.
+	 * 
+	 * @param x1 is the center of the circle
+	 * @param y1 is the center of the circle
+	 * @param radius is the radius of the circle
+	 * @param x2 is the first point of the segment.
+	 * @param y2 is the first point of the segment.
+	 * @param x3 is the second point of the segment.
+	 * @param y3 is the second point of the segment.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsCircleSegment(int x1, int y1, int radius, int x2, int y2, int x3, int y3) {
+		Point2i p = Segment2i.computeClosestPointTo(x2, y2, x3, y3, x1, y1);
+		return contains(x1, y1, radius, p.x(), p.y());
+	}
+
+	/** X-coordinate of the center of the circle. */
+	protected int cx = 0;
+	/** Y-coordinate of the center of the circle. */
+	protected int cy = 0;
+	/** Radius of the circle. */
+	protected int radius = 0;
+
+	/**
+	 */
+	public Circle2i() {
+		//
+	}
+
+	/**
+	 * @param center
+	 * @param radius
+	 */
+	public Circle2i(Point2D center, int radius) {
+		set(center, radius);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param radius
+	 */
+	public Circle2i(int x, int y, int radius) {
+		set(x, y, radius);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void clear() {
+		this.cx = this.cy = 0;
+		this.radius = 0;
+	}
+
+	/** Replies if this circle is empty.
+	 * The circle is empty when the radius is nul.
+	 * 
+	 * @return <code>true</code> if the radius is nul;
+	 * otherwise <code>false</code>.
+	 */
+	@Override
+	public boolean isEmpty() {
+		return this.radius<=0;
+	}
+
+	/** Change the frame of the circle.
+	 * 
+	 * @param x
+	 * @param y
+	 * @param radius
+	 */
+	public void set(int x, int y, int radius) {
+		this.cx = x;
+		this.cy = y;
+		this.radius = Math.abs(radius);
+	}
+
+	/** Change the frame of te circle.
+	 * 
+	 * @param center
+	 * @param radius
+	 */
+	public void set(Point2D center, int radius) {
+		this.cx = center.x();
+		this.cy = center.y();
+		this.radius = Math.abs(radius);
+	}
+
+	/** Replies the center X.
+	 * 
+	 * @return the center x.
+	 */
+	public int getX() {
+		return this.cx;
+	}
+
+	/** Replies the center y.
+	 * 
+	 * @return the center y.
+	 */
+	public int getY() {
+		return this.cy;
+	}
+
+	/** Replies the center.
+	 * 
+	 * @return a copy of the center.
+	 */
+	public Point2i getCenter() {
+		return new Point2i(this.cx, this.cy);
+	}
+
+	/** Replies the radius.
+	 * 
+	 * @return the radius.
+	 */
+	public int getRadius() {
+		return this.radius;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Rectangle2i toBoundingBox() {
+		Rectangle2i r = new Rectangle2i();
+		r.setFromCorners(
+				this.cx-this.radius,
+				this.cy-this.radius,
+				this.cx+this.radius,
+				this.cy+this.radius);
+		return r;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceSquared(Point2D p) {
+		Point2i c = getClosestPointTo(p);
+		return c.distanceSquared(p);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceL1(Point2D p) {
+		Point2i c = getClosestPointTo(p);
+		return c.distanceL1(p);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceLinf(Point2D p) {
+		Point2i c = getClosestPointTo(p);
+		return c.distanceLinf(p);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Point2i getClosestPointTo(Point2D p) {
+		return computeClosestPointTo(this.cx, this.cy, this.radius, p.x(), p.y());
+	}
+
+	@Override
+	public boolean intersects(Rectangle2i s) {
+		return intersectsCircleRectangle(
+				getX(), getY(), getRadius(),
+				s.getMinX(), s.getMinY(),
+				s.getMaxX(), s.getMaxY());
+	}
+
+	@Override
+	public boolean intersects(Circle2i s) {
+		return intersectsCircleCircle(
+				getX(), getY(), getRadius(),
+				s.getX(), s.getY(), s.getRadius());
+	}
+
+	@Override
+	public boolean intersects(Segment2i s) {
+		return intersectsCircleSegment(
+				getX(), getY(), getRadius(),
+				s.getX1(), s.getY1(),
+				s.getX2(), s.getY2());
+	}
+
+	@Override
+	public Shape2i createTransformedShape(Transform2D transform) {
+		if (transform==null || transform.isIdentity()) return clone();
+		return new Path2i(getPathIterator(transform));
+	}
+
+	@Override
+	public void translate(int dx, int dy) {
+		this.cx += dx;
+		this.cy += dy;
+	}
+
+	@Override
+	public boolean contains(int x, int y) {
+		return contains(this.cx, this.cy, this.radius, x, y);
+	}
+
+	private static void m(int[] quadrants, int k, int x, int y) {
+		if (x>0) {
+			if (y>0) {
+				quadrants[0] |= k;
+			}
+			else {
+				quadrants[1] |= k;
+			}
+		}
+		else {
+			if (y>0) {
+				quadrants[3] |= k;
+			}
+			else {
+				quadrants[2] |= k;
+			}
+		}
+	}
+
+	@Override
+	public boolean contains(Rectangle2i r) {
+		int vx1 = r.getMinX() - this.cx;
+		int vy1 = r.getMinY() - this.cy;
+		int vx2 = r.getMaxX() - this.cx;
+		int vy2 = r.getMaxY() - this.cy;
+
+		if (vx1>=-this.radius && vx1<=this.radius && vy1>=-this.radius && vy1<=this.radius &&
+				vx2>=-this.radius && vx2<=this.radius && vy2>=-this.radius && vy2<=this.radius) {
+			int[] quadrants = new int[4];
+			int[] x = new int[] {vx1, vx2, vx2, vx1};
+			int[] y = new int[] {vy1, vy1, vy2, vy2};
+			for(int i=0; i<4; ++i) {
+				m(quadrants, (1<<i), x[i], y[i]);
+			}
+
+			for(int i=0; i<quadrants.length; ++i) {
+				if (quadrants[i]!=0) {
+					CirclePerimeterIterator iterator = new CirclePerimeterIterator(
+							this.cx, this.cy, this.radius, i*2, i*2+2, false);
+					int px, py, ccw, cpx, cpy;
+					Point2i p;
+
+					while (iterator.hasNext()) {
+						p = iterator.next();
+						px = this.cy - p.y();
+						py = p.x() - this.cx;
+
+						for(int j=0; j<4; ++j) {
+							if ((quadrants[i] & (1<<j))!=0) {
+								cpx = x[j] - p.x();
+								cpy = y[j] - p.y();
+								ccw = cpx * py - cpy * px;				
+								if (ccw>0) return false;
+							}
+						}
+					}
+				}
+			}
+
+			return true;
+		}
+
+		return false;
+	}
+
+	/** Replies the points of the circle perimeters starting by the first octant.
+	 * 
+	 * @return the points on the perimeters.
+	 */
+	@Override
+	public Iterator<Point2i> getPointIterator() {
+		return new CirclePerimeterIterator(this.cx, this.cy, this.radius, 0, 8, true);
+	}
+
+	/** Replies the points of the circle perimeters starting by the first octant.
+	 * 
+	 * @param firstOctantIndex is the index of the first octant (see figure) to treat.
+	 * @param nbOctants is the number of octants to traverse (greater than zero).
+	 * @return the points on the perimeters.
+	 */
+	public Iterator<Point2i> getPointIterator(int firstOctantIndex, int nbOctants) {
+		return getPointIterator(this.cx, this.cy,  this.radius, firstOctantIndex, nbOctants);
+	}
+
+	/** Replies the points of the circle perimeters starting by the first octant.
+	 * 
+	 * @param cx is the center of the radius.
+	 * @param cy is the center of the radius.
+	 * @param radius is the radius of the radius.
+	 * @param firstOctantIndex is the index of the first octant (see figure) to treat.
+	 * @param nbOctants is the number of octants to traverse (greater than zero).
+	 * @return the points on the perimeters.
+	 */
+	public static Iterator<Point2i> getPointIterator(int cx, int cy,  int radius, int firstOctantIndex, int nbOctants) {
+		int startOctant, maxOctant;
+		if (firstOctantIndex<=0)
+			startOctant = 0;
+		else if (firstOctantIndex>=8)
+			startOctant = 7;
+		else
+			startOctant = firstOctantIndex;
+		maxOctant = startOctant + nbOctants;
+		if (maxOctant>8) maxOctant = 8;
+		return new CirclePerimeterIterator(
+				cx, cy, radius,
+				startOctant, maxOctant, true);
+	}
+
+	@Override
+	public PathIterator2i getPathIterator(Transform2D transform) {
+		if (transform==null || transform.isIdentity())
+			return new CopyPathIterator(this.cx, this.cy, this.radius);
+		return new TransformPathIterator(this.cx, this.cy, this.radius, transform);
+	}
+
+	/** Iterates on points on the perimeter of a circle.
+	 * <p>
+	 * The rastrerization is based on a Bresenham algorithm.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class CirclePerimeterIterator implements Iterator<Point2i> {
+
+		private final int cx;
+		private final int cy;
+		private final int cr;
+
+		private final boolean skip;
+		private final int maxOctant;
+
+		private int currentOctant;
+		private int x, y, d;
+		
+		private Point2i next = null;
+		
+		private final Set<Point2i> junctionPoint = new TreeSet<Point2i>(new Tuple2iComparator());
+
+		/**
+		 * @param x
+		 * @param y
+		 * @param r
+		 * @param initialOctant
+		 * @param maxOctant
+		 * @param skip
+		 */
+		public CirclePerimeterIterator(int x, int y, int r, int initialOctant, int maxOctant, boolean skip) {
+			assert(r>=0);
+			this.cx = x;
+			this.cy = y;
+			this.cr = r;
+			this.skip = skip;
+			this.maxOctant = maxOctant;
+			this.currentOctant = initialOctant;
+			reset();
+			searchNext();
+		}
+
+		private void reset() {
+			this.x = 0;
+			this.y = this.cr;
+			this.d = 3 - 2 * this.cr;
+			if (this.skip && (this.currentOctant==3 || this.currentOctant==4 || this.currentOctant==6 || this.currentOctant==7)) {
+				// skip the first point because already replied in previous octant
+				if (this.d<=0) {
+					this.d += 4 * this.x + 6;
+				}
+				else {
+					this.d += 4 * (this.x - this.y) + 10;
+					--this.y;
+				}
+				++this.x;
+			}
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public boolean hasNext() {
+			return this.next!=null;
+		}
+		
+		private void searchNext() {
+			if (this.currentOctant>=this.maxOctant) {
+				this.next = null;
+			}
+			else {
+				this.next = new Point2i();
+				while (true) {
+					switch(this.currentOctant) {
+					case 0:
+						this.next.set(this.cx + this.x, this.cy + this.y);
+						break;
+					case 1:
+						this.next.set(this.cx + this.y, this.cy + this.x);
+						break;
+					case 2:
+						this.next.set(this.cx + this.x, this.cy - this.y);
+						break;
+					case 3:
+						this.next.set(this.cx + this.y, this.cy - this.x);
+						break;
+					case 4:
+						this.next.set(this.cx - this.x, this.cy - this.y);
+						break;
+					case 5:
+						this.next.set(this.cx - this.y, this.cy - this.x);
+						break;
+					case 6:
+						this.next.set(this.cx - this.x, this.cy + this.y);
+						break;
+					case 7:
+						this.next.set(this.cx - this.y, this.cy + this.x);
+						break;
+					default:
+						throw new NoSuchElementException();
+					}
+		
+					if (this.d<=0) {
+						this.d += 4 * this.x + 6;
+					}
+					else {
+						this.d += 4 * (this.x - this.y) + 10;
+						--this.y;
+					}
+					++this.x;
+	
+					if (this.x>this.y) {
+						// The octant is finished.
+						// Save the junction.
+						boolean cont = this.junctionPoint.contains(this.next);
+						if (!cont) this.junctionPoint.add(new Point2i(this.next));
+						// Goto next.
+						++this.currentOctant;
+						reset();
+						if (this.currentOctant>=this.maxOctant) {
+							if (cont) this.next = null;
+							cont = false;
+						}
+						if (!cont) return;
+					}
+					else {
+						return;
+					}
+				}
+			}
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public Point2i next() {
+			Point2i pixel = this.next;
+			if (pixel==null) throw new NoSuchElementException();
+			searchNext();
+			return pixel;
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+	} // class CirclePerimeterIterator
+
+	/** Iterator on the path elements of the circle.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class CopyPathIterator implements PathIterator2i {
+
+		private final int x;
+		private final int y;
+		private final int r;
+		private int index = 0;
+		private int movex, movey;
+		private int lastx, lasty;
+
+		/**
+		 * @param x
+		 * @param y
+		 * @param r
+		 */
+		public CopyPathIterator(int x, int y, int r) {
+			this.r = Math.max(0, r);
+			this.x = x - this.r;
+			this.y = y - this.r;
+			if (this.r<=0f) {
+				this.index = 6;
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.index<=5;
+		}
+
+		@Override
+		public PathElement2i next() {
+			if (this.index>5) throw new NoSuchElementException();
+			int idx = this.index;
+			++this.index;
+			if (idx==0) {
+				int dr = 2 * this.r;
+				float ctrls[] = CTRL_PTS[3];
+				this.movex = (int)(this.x + ctrls[4] * dr);
+				this.movey = (int)(this.y + ctrls[5] * dr);
+				this.lastx = this.movex;
+				this.lasty = this.movey;
+				return new PathElement2i.MovePathElement2i(
+						this.lastx, this.lasty);
+			}
+			else if (idx<5) {
+				int dr = 2 * this.r;
+				float ctrls[] = CTRL_PTS[idx - 1];
+				int ppx = this.lastx;
+				int ppy = this.lasty;
+				this.lastx = (int)(this.x + ctrls[4] * dr);
+				this.lasty = (int)(this.y + ctrls[5] * dr);
+				return new PathElement2i.CurvePathElement2i(
+						ppx, ppy,
+						(int)(this.x + ctrls[0] * dr),
+						(int)(this.y + ctrls[1] * dr),
+						(int)(this.x + ctrls[2] * dr),
+						(int)(this.y + ctrls[3] * dr),
+						this.lastx, this.lasty);
+			}
+			int ppx = this.lastx;
+			int ppy = this.lasty;
+			this.lastx = this.movex;
+			this.lasty = this.movey;
+			return new PathElement2i.ClosePathElement2i(
+					ppx, ppy,
+					this.lastx, this.lasty);
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return PathWindingRule.NON_ZERO;
+		}
+
+	}
+
+	/** Iterator on the path elements of the circle.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class TransformPathIterator implements PathIterator2i {
+
+		private final Point2D p1 = new Point2i();
+		private final Point2D p2 = new Point2i();
+		private final Point2D ptmp1 = new Point2i();
+		private final Point2D ptmp2 = new Point2i();
+		private final Transform2D transform;
+		private final int x;
+		private final int y;
+		private final int r;
+		private int index = 0;
+		private int movex, movey;
+
+		/**
+		 * @param x
+		 * @param y
+		 * @param r
+		 * @param transform
+		 */
+		public TransformPathIterator(int x, int y, int r, Transform2D transform) {
+			assert(transform!=null);
+			this.transform = transform;
+			this.r = Math.max(0, r);
+			this.x = x - this.r;
+			this.y = y - this.r;
+			if (this.r<=0f) {
+				this.index = 6;
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.index<=5;
+		}
+
+		@Override
+		public PathElement2i next() {
+			if (this.index>5) throw new NoSuchElementException();
+			int idx = this.index;
+			++this.index;
+			if (idx==0) {
+				int dr = 2 * this.r;
+				float ctrls[] = CTRL_PTS[3];
+				this.movex = (int)(this.x + ctrls[4] * dr);
+				this.movey = (int)(this.y + ctrls[5] * dr);
+				this.p2.set(this.movex, this.movey);
+				this.transform.transform(this.p2);
+				return new PathElement2i.MovePathElement2i(
+						this.p2.x(), this.p2.y());
+			}
+			else if (idx<5) {
+				int dr = 2 * this.r;
+				float ctrls[] = CTRL_PTS[idx - 1];
+				this.p1.set(this.p2);
+				this.p2.set(
+						(this.x + ctrls[4] * dr),
+						(this.y + ctrls[5] * dr));
+				this.transform.transform(this.p2);
+				this.ptmp1.set(
+						(this.x + ctrls[0] * dr),
+						(this.y + ctrls[1] * dr));
+				this.transform.transform(this.ptmp1);
+				this.ptmp2.set(
+						(this.x + ctrls[2] * dr),
+						(this.y + ctrls[3] * dr));
+				this.transform.transform(this.ptmp2);
+				return new PathElement2i.CurvePathElement2i(
+						this.p1.x(), this.p1.y(),
+						this.ptmp1.x(), this.ptmp1.y(),
+						this.ptmp2.x(), this.ptmp2.y(),
+						this.p2.x(), this.p2.y());
+			}
+			this.p1.set(this.p2);
+			this.p2.set(this.movex, this.movey);
+			this.transform.transform(this.p2);
+			return new PathElement2i.ClosePathElement2i(
+					this.p1.x(), this.p1.y(),
+					this.p2.x(), this.p2.y());
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return PathWindingRule.NON_ZERO;
+		}
+
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Path2i.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Path2i.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Path2i.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,2568 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import java.lang.ref.SoftReference;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.MathUtil;
+import org.arakhne.afc.math.generic.PathElementType;
+import org.arakhne.afc.math.generic.PathWindingRule;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+/** A generic path with integer coordinates.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Path2i extends AbstractShape2i<Path2i> {
+
+	private static final long serialVersionUID = -4229773257722403127L;
+
+	/** Multiple of cubic & quad curve size.
+	 */
+	static final int GROW_SIZE = 24;
+
+	/**
+	 * Calculates the number of times the given path
+	 * crosses the given segment extending to the right.
+	 * 
+	 * @param pi is the description of the path.
+	 * @param x1 is the first point of the segment.
+	 * @param y1 is the first point of the segment.
+	 * @param x2 is the first point of the segment.
+	 * @param y2 is the first point of the segment.
+	 * @return the crossing or {@link MathConstants#SHAPE_INTERSECTS}.
+	 */
+	public static int computeCrossingsFromSegment(PathIterator2i pi, int x1, int y1, int x2, int y2) {
+		return computeCrossingsFromSegment(0, pi, x1, y1, x2, y2, true);
+	}
+	
+	/**
+	 * Calculates the number of times the given path
+	 * crosses the given circle extending to the right.
+	 * 
+	 * @param crossings is the initial value for crossing.
+	 * @param pi is the description of the path.
+	 * @param x1 is the first point of the segment.
+	 * @param y1 is the first point of the segment.
+	 * @param x2 is the first point of the segment.
+	 * @param y2 is the first point of the segment.
+	 * @param closeable indicates if the shape is automatically closed or not.
+	 * @return the crossing
+	 */
+	static int computeCrossingsFromSegment(int crossings, PathIterator2i pi, int x1, int y1, int x2, int y2, boolean closeable) {	
+		// Copied from the AWT API
+		if (!pi.hasNext()) return 0;
+		PathElement2i element;
+
+		element = pi.next();
+		if (element.type != PathElementType.MOVE_TO) {
+			throw new IllegalArgumentException("missing initial moveto in path definition"); //$NON-NLS-1$
+		}
+
+		int movx = element.toX;
+		int movy = element.toY;
+		int curx = movx;
+		int cury = movy;
+		int endx, endy;
+		int numCrosses = crossings;
+		while (numCrosses!=MathConstants.SHAPE_INTERSECTS && pi.hasNext()) {
+			element = pi.next();
+			switch (element.type) {
+			case MOVE_TO:
+				movx = curx = element.toX;
+				movy = cury = element.toY;
+				break;
+			case LINE_TO:
+				endx = element.toX;
+				endy = element.toY;
+				numCrosses = Segment2i.computeCrossingsFromSegment(
+						numCrosses,
+						x1, y1, x2, y2,
+						curx, cury,
+						endx, endy);
+				curx = endx;
+				cury = endy;
+				break;
+			case QUAD_TO:
+			{
+				endx = element.toX;
+				endy = element.toY;
+				Path2i localPath = new Path2i();
+				localPath.moveTo(element.fromX, element.fromY);
+				localPath.quadTo(
+						element.ctrlX1, element.ctrlY1,
+						endx, endy);
+				numCrosses = computeCrossingsFromSegment(
+						numCrosses,
+						localPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						x1, y1, x2, y2,
+						false);
+				curx = endx;
+				cury = endy;
+				break;
+			}
+			case CURVE_TO:
+				endx = element.toX;
+				endy = element.toY;
+				Path2i localPath = new Path2i();
+				localPath.moveTo(element.fromX, element.fromY);
+				localPath.curveTo(
+						element.ctrlX1, element.ctrlY1,
+						element.ctrlX2, element.ctrlY2,
+						endx, endy);
+				numCrosses = computeCrossingsFromSegment(
+						numCrosses,
+						localPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						x1, y1, x2, y2,
+						false);
+				curx = endx;
+				cury = endy;
+				break;
+			case CLOSE:
+				if (cury != movy || curx != movx) {
+					numCrosses = Segment2i.computeCrossingsFromSegment(
+							numCrosses,
+							x1, y1, x2, y2,
+							curx, cury,
+							movx, movy);
+				}
+				curx = movx;
+				cury = movy;
+				break;
+			default:
+			}
+		}
+
+		if (numCrosses!=MathConstants.SHAPE_INTERSECTS && closeable && cury != movy) {
+			numCrosses = Segment2i.computeCrossingsFromSegment(
+					numCrosses,
+					x1, y1, x2, y2,
+					curx, cury,
+					movx, movy);
+		}
+		
+		return numCrosses;
+	}
+
+	/**
+	 * Calculates the number of times the given path
+	 * crosses the given ellipse extending to the right.
+	 * 
+	 * @param pi is the description of the path.
+	 * @param cx is the center of the circle.
+	 * @param cy is the center of the circle.
+	 * @param radius is the radius of the circle.
+	 * @return the crossing or {@link MathConstants#SHAPE_INTERSECTS}.
+	 */
+	public static int computeCrossingsFromCircle(PathIterator2i pi, int cx, int cy, int radius) {
+		return computeCrossingsFromCircle(0, pi, cx, cy, radius, true);
+	}
+	
+	/**
+	 * Calculates the number of times the given path
+	 * crosses the given circle extending to the right.
+	 * 
+	 * @param crossings is the initial value for crossing.
+	 * @param pi is the description of the path.
+	 * @param cx is the center of the circle.
+	 * @param cy is the center of the circle.
+	 * @param radius is the radius of the circle.
+	 * @param closeable indicates if the shape is automatically closed or not.
+	 * @return the crossing
+	 */
+	static int computeCrossingsFromCircle(int crossings, PathIterator2i pi, int cx, int cy, int radius, boolean closeable) {	
+		// Copied from the AWT API
+		if (!pi.hasNext()) return 0;
+		PathElement2i element;
+
+		element = pi.next();
+		if (element.type != PathElementType.MOVE_TO) {
+			throw new IllegalArgumentException("missing initial moveto in path definition"); //$NON-NLS-1$
+		}
+
+		int movx = element.toX;
+		int movy = element.toY;
+		int curx = movx;
+		int cury = movy;
+		int endx, endy;
+		int numCrosses = crossings;
+		while (numCrosses!=MathConstants.SHAPE_INTERSECTS && pi.hasNext()) {
+			element = pi.next();
+			switch (element.type) {
+			case MOVE_TO:
+				movx = curx = element.toX;
+				movy = cury = element.toY;
+				break;
+			case LINE_TO:
+				endx = element.toX;
+				endy = element.toY;
+				numCrosses = Segment2i.computeCrossingsFromCircle(
+						numCrosses,
+						cx, cy, radius,
+						curx, cury,
+						endx, endy);
+				curx = endx;
+				cury = endy;
+				break;
+			case QUAD_TO:
+			{
+				endx = element.toX;
+				endy = element.toY;
+				Path2i localPath = new Path2i();
+				localPath.moveTo(element.fromX, element.fromY);
+				localPath.quadTo(
+						element.ctrlX1, element.ctrlY1,
+						endx, endy);
+				numCrosses = computeCrossingsFromCircle(
+						numCrosses,
+						localPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						cx, cy, radius,
+						false);
+				curx = endx;
+				cury = endy;
+				break;
+			}
+			case CURVE_TO:
+				endx = element.toX;
+				endy = element.toY;
+				Path2i localPath = new Path2i();
+				localPath.moveTo(element.fromX, element.fromY);
+				localPath.curveTo(
+						element.ctrlX1, element.ctrlY1,
+						element.ctrlX2, element.ctrlY2,
+						endx, endy);
+				numCrosses = computeCrossingsFromCircle(
+						numCrosses,
+						localPath.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						cx, cy, radius,
+						false);
+				curx = endx;
+				cury = endy;
+				break;
+			case CLOSE:
+				if (cury != movy || curx != movx) {
+					numCrosses = Segment2i.computeCrossingsFromCircle(
+							numCrosses,
+							cx, cy, radius,
+							curx, cury,
+							movx, movy);
+				}
+				curx = movx;
+				cury = movy;
+				break;
+			default:
+			}
+		}
+
+		if (numCrosses!=MathConstants.SHAPE_INTERSECTS && closeable && cury != movy) {
+			numCrosses = Segment2i.computeCrossingsFromCircle(
+					numCrosses,
+					cx, cy, radius,
+					curx, cury,
+					movx, movy);
+		}
+		
+		return numCrosses;
+	}
+
+	/**
+	 * Calculates the number of times the given path
+	 * crosses the ray extending to the right from (px,py).
+	 * If the point lies on a part of the path,
+	 * then no crossings are counted for that intersection.
+	 * +1 is added for each crossing where the Y coordinate is increasing
+	 * -1 is added for each crossing where the Y coordinate is decreasing
+	 * The return value is the sum of all crossings for every segment in
+	 * the path.
+	 * The path must start with a MOVE_TO, otherwise an exception is
+	 * thrown.
+	 * 
+	 * @param pi is the description of the path.
+	 * @param px is the reference point to test.
+	 * @param py is the reference point to test.
+	 * @return the crossing
+	 */
+	public static int computeCrossingsFromPoint(PathIterator2i pi, int px, int py) {
+		return computeCrossingsFromPoint(pi, px, py, true);
+	}
+	
+	/**
+	 * Calculates the number of times the given path
+	 * crosses the ray extending to the right from (px,py).
+	 * If the point lies on a part of the path,
+	 * then no crossings are counted for that intersection.
+	 * +1 is added for each crossing where the Y coordinate is increasing
+	 * -1 is added for each crossing where the Y coordinate is decreasing
+	 * The return value is the sum of all crossings for every segment in
+	 * the path.
+	 * The path must start with a MOVE_TO, otherwise an exception is
+	 * thrown.
+	 * 
+	 * @param pi is the description of the path.
+	 * @param px is the reference point to test.
+	 * @param py is the reference point to test.
+	 * @param autoClose indicates if the shape is automatically assumed as closed.
+	 * @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}
+	 */
+	static int computeCrossingsFromPoint(PathIterator2i pi, int px, int py, boolean autoClose) {
+		// Copied and adapted from the AWT API
+		if (!pi.hasNext()) return 0;
+		PathElement2i element;
+
+		element = pi.next();
+		if (element.type != PathElementType.MOVE_TO) {
+			throw new IllegalArgumentException("missing initial moveto in path definition"); //$NON-NLS-1$
+		}
+
+		int movx = element.toX;
+		int movy = element.toY;
+		int curx = movx;
+		int cury = movy;
+		int endx, endy;
+		int crossings = 0;
+		
+		while (pi.hasNext()) {
+			element = pi.next();
+			switch (element.type) {
+			case MOVE_TO:
+				movx = curx = element.toX;
+				movy = cury = element.toY;
+				break;
+			case LINE_TO:
+				endx = element.toX;
+				endy = element.toY;
+				crossings = Segment2i.computeCrossingsFromPoint(
+						crossings,
+						px, py,
+						curx, cury,
+						endx, endy);
+				if (crossings==MathConstants.SHAPE_INTERSECTS) {
+					return crossings;
+				}
+				curx = endx;
+				cury = endy;
+				break;
+			case QUAD_TO:
+				endx = element.toX;
+				endy = element.toY;
+				Path2i curve = new Path2i();
+				curve.moveTo(element.fromX, element.fromY);
+				curve.quadTo(element.ctrlX1, element.ctrlY1, endx, endy);
+				int numCrosses = computeCrossingsFromPoint(
+						curve.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						px, py, false);
+				if (numCrosses==MathConstants.SHAPE_INTERSECTS) {
+					return numCrosses;
+				}
+				crossings += numCrosses;
+				curx = endx;
+				cury = endy;
+				break;
+			case CURVE_TO:
+				endx = element.toX;
+				endy = element.toY;
+				curve = new Path2i();
+				curve.moveTo(element.fromX, element.fromY);
+				curve.curveTo(
+						element.ctrlX1, element.ctrlY1,
+						element.ctrlX2, element.ctrlY2,
+						endx, endy);
+				numCrosses = computeCrossingsFromPoint(
+						curve.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						px, py, false);
+				if (numCrosses==MathConstants.SHAPE_INTERSECTS) {
+					return numCrosses;
+				}
+				crossings += numCrosses;
+				curx = endx;
+				cury = endy;
+				break;
+			case CLOSE:
+				if (cury != movy || curx != movx) {
+					crossings = Segment2i.computeCrossingsFromPoint(
+							crossings,
+							px, py,
+							curx, cury,
+							movx, movy);
+					if (crossings==MathConstants.SHAPE_INTERSECTS) {
+						return crossings;
+					}
+				}
+				curx = movx;
+				cury = movy;
+				break;
+			default:
+			}
+		}
+
+		if (autoClose && cury != movy && curx != movx) {
+			crossings = Segment2i.computeCrossingsFromPoint(
+					crossings,
+					px, py,
+					curx, cury,
+					movx, movy);
+		}
+		
+		return crossings;
+	}
+	
+	/**
+	 * Tests if the specified coordinates are inside the closed
+	 * boundary of the specified {@link PathIterator2i}.
+	 * <p>
+	 * This method provides a basic facility for implementors of
+	 * the {@link Shape2i} interface to implement support for the
+	 * {@link Shape2i#contains(int, int)} method.
+	 *
+	 * @param pi the specified {@code PathIterator2f}
+	 * @param x the specified X coordinate
+	 * @param y the specified Y coordinate
+	 * @return {@code true} if the specified coordinates are inside the
+	 *         specified {@code PathIterator2f}; {@code false} otherwise
+	 */
+	public static boolean contains(PathIterator2i pi, int x, int y) {
+		// Copied from the AWT API
+		int mask = (pi.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 1);
+		int cross = computeCrossingsFromPoint(pi, x, y);
+		return ((cross & mask) != 0);
+	}
+	
+	/**
+	 * Accumulate the number of times the path crosses the shadow
+	 * extending to the right of the rectangle.  See the comment
+	 * for the SHAPE_INTERSECTS constant for more complete details.
+	 * The return value is the sum of all crossings for both the
+	 * top and bottom of the shadow for every segment in the path,
+	 * or the special value SHAPE_INTERSECTS if the path ever enters
+	 * the interior of the rectangle.
+	 * The path must start with a SEG_MOVETO, otherwise an exception is
+	 * thrown.
+	 * The caller must check r[xy]{min,max} for NaN values.
+	 * 
+	 * @param pi is the iterator on the path elements.
+	 * @param rxmin is the first corner of the rectangle.
+	 * @param rymin is the first corner of the rectangle.
+	 * @param rxmax is the second corner of the rectangle.
+	 * @param rymax is the second corner of the rectangle.
+	 * @return the crossings.
+	 */
+	public static int computeCrossingsFromRect(PathIterator2i pi,
+			int rxmin, int rymin,
+			int rxmax, int rymax) {
+		return __computeCrossingsFromRect(pi, rxmin, rymin, rxmax, rymax, true, true);
+	}
+	
+	private static int crossingHelper1(
+			int crossings,
+			int rxmin, int rymin,
+			int rxmax, int rymax,
+			int curx, int cury,
+			int movx, int movy,
+			boolean intersectingBehavior) {
+		int crosses = Segment2i.computeCrossingsFromRect(crossings,
+					rxmin, rymin,
+					rxmax, rymax,
+					curx, cury,
+					movx, movy);
+		if (!intersectingBehavior && crosses==MathConstants.SHAPE_INTERSECTS) {
+			int x1 = rxmin+1;
+			int x2 = rxmax-1;
+			int y1 = rymin+1;
+			int y2 = rymax-1;
+			crosses = Segment2i.computeCrossingsFromRect(crossings,
+					x1, y1,
+					x2, y2,
+					curx, cury,
+					movx, movy);
+		}
+		return crosses;
+	}
+	
+	/**
+	 * Accumulate the number of times the path crosses the shadow
+	 * extending to the right of the rectangle.  See the comment
+	 * for the SHAPE_INTERSECTS constant for more complete details.
+	 * The return value is the sum of all crossings for both the
+	 * top and bottom of the shadow for every segment in the path,
+	 * or the special value SHAPE_INTERSECTS if the path ever enters
+	 * the interior of the rectangle.
+	 * The path must start with a SEG_MOVETO, otherwise an exception is
+	 * thrown.
+	 * The caller must check r[xy]{min,max} for NaN values.
+	 * 
+	 * @param pi is the iterator on the path elements.
+	 * @param rxmin is the first corner of the rectangle.
+	 * @param rymin is the first corner of the rectangle.
+	 * @param rxmax is the second corner of the rectangle.
+	 * @param rymax is the second corner of the rectangle.
+	 * @param autoClose indicates if the line from the last point to the last move
+	 * point must be include in the crossing computation.
+	 * @param intersectingBehavior indicates the function is called to determine if the rectangle
+	 * is inside the shape or not. This function determines
+	 * {@link MathConstants#SHAPE_INTERSECTS} in a different way if the
+	 * function is used for containing or intersecting tests.
+	 * @return the crossings count or {@link MathConstants#SHAPE_INTERSECTS}.
+	 */
+	static int __computeCrossingsFromRect(PathIterator2i pi,
+			int rxmin, int rymin,
+			int rxmax, int rymax,
+			boolean autoClose,
+			boolean intersectingBehavior) {
+		// Copied from AWT API
+		if (rxmax <= rxmin || rymax <= rymin) return 0;
+		if (!pi.hasNext()) return 0;
+
+		PathElement2i pathElement = pi.next();
+
+		if (pathElement.type != PathElementType.MOVE_TO) {
+			throw new IllegalArgumentException("missing initial moveto in path definition"); //$NON-NLS-1$
+		}
+
+		int curx, cury, movx, movy, endx, endy;
+		curx = movx = pathElement.toX;
+		cury = movy = pathElement.toY;
+		int crossings = 0;
+
+		while (crossings != MathConstants.SHAPE_INTERSECTS
+				&& pi.hasNext()) {
+			pathElement = pi.next();
+			switch (pathElement.type) {
+			case MOVE_TO:
+				// Count should always be a multiple of 2 here.
+				// assert((crossings & 1) != 0);
+				movx = curx = pathElement.toX;
+				movy = cury = pathElement.toY;
+				break;
+			case LINE_TO:
+				endx = pathElement.toX;
+				endy = pathElement.toY;
+				crossings = crossingHelper1(crossings,
+						rxmin, rymin, rxmax, rymax,
+						curx, cury, endx, endy,
+						intersectingBehavior);
+				if (crossings==MathConstants.SHAPE_INTERSECTS) {
+					return MathConstants.SHAPE_INTERSECTS;
+				}
+				curx = endx;
+				cury = endy;
+				break;
+			case QUAD_TO:
+				endx = pathElement.toX;
+				endy = pathElement.toY;
+				Path2i curve = new Path2i();
+				curve.moveTo(pathElement.fromX, pathElement.fromY);
+				curve.quadTo(pathElement.ctrlX1, pathElement.ctrlY1, endx, endy);
+				int numCrosses = __computeCrossingsFromRect(
+						curve.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						rxmin, rymin, rxmax, rymax,
+						false, intersectingBehavior);
+				if (numCrosses==MathConstants.SHAPE_INTERSECTS) {
+					return MathConstants.SHAPE_INTERSECTS;
+				}
+				crossings += numCrosses;
+				curx = endx;
+				cury = endy;
+				break;
+			case CURVE_TO:
+				endx = pathElement.toX;
+				endy = pathElement.toY;
+				curve = new Path2i();
+				curve.moveTo(pathElement.fromX, pathElement.fromY);
+				curve.curveTo(pathElement.ctrlX1, pathElement.ctrlY1, pathElement.ctrlX2, pathElement.ctrlY2, endx, endy);
+				numCrosses = __computeCrossingsFromRect(
+						curve.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO),
+						rxmin, rymin, rxmax, rymax,
+						false, intersectingBehavior);
+				if (numCrosses==MathConstants.SHAPE_INTERSECTS) {
+					return MathConstants.SHAPE_INTERSECTS;
+				}
+				crossings += numCrosses;
+				curx = endx;
+				cury = endy;
+				break;
+			case CLOSE:
+				if (curx != movx || cury != movy) {
+					crossings = crossingHelper1(crossings,
+							rxmin, rymin, rxmax, rymax,
+							curx, cury, movx, movy,
+							intersectingBehavior);
+					if (crossings==MathConstants.SHAPE_INTERSECTS) {
+						return crossings;
+					}
+				}
+				curx = movx;
+				cury = movy;
+				// Count should always be a multiple of 2 here.
+				// assert((crossings & 1) != 0);
+				break;
+			default:
+			}
+		}
+
+		if (autoClose && crossings != MathConstants.SHAPE_INTERSECTS && (curx != movx || cury != movy)) {
+			crossings = crossingHelper1(crossings,
+					rxmin, rymin, rxmax, rymax,
+					curx, cury, movx, movy,
+					intersectingBehavior);
+		}
+
+		// Count should always be a multiple of 2 here.
+		// assert((crossings & 1) != 0);
+		return crossings;
+	}
+	
+	/**
+	 * Tests if the specified rectangle is inside the closed
+	 * boundary of the specified {@link PathIterator2i}.
+	 * <p>
+	 * This method provides a basic facility for implementors of
+	 * the {@link Shape2i} interface to implement support for the
+	 * {@link Shape2i#contains(Rectangle2i)} method.
+	 *
+	 * @param pi the specified {@code PathIterator2f}
+	 * @param rx the lowest corner of the rectangle.
+	 * @param ry the lowest corner of the rectangle.
+	 * @param rwidth is the width of the rectangle.
+	 * @param rheight is the width of the rectangle.
+	 * @return {@code true} if the specified rectangle is inside the
+	 *         specified {@code PathIterator2f}; {@code false} otherwise.
+	 */
+	public static boolean contains(PathIterator2i pi, int rx, int ry, int rwidth, int rheight) {
+		// Copied and adapted from AWT API
+        if (rwidth <= 0 || rheight <= 0) {
+            return false;
+        }
+        int mask = (pi.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 2);
+        int crossings = __computeCrossingsFromRect(pi, rx, ry, rx+rwidth, ry+rheight, true, false);
+        return (crossings != MathConstants.SHAPE_INTERSECTS &&
+                (crossings & mask) != 0);
+	}
+	
+	/**
+	 * Tests if the interior of the specified {@link PathIterator2i}
+	 * intersects the interior of a specified set of rectangular
+	 * coordinates.
+	 * <p>
+	 * This method provides a basic facility for implementors of
+	 * the {@link Shape2i} interface to implement support for the
+	 * {@code intersects()} method.
+	 * <p>
+	 * This method object may conservatively return true in
+	 * cases where the specified rectangular area intersects a
+	 * segment of the path, but that segment does not represent a
+	 * boundary between the interior and exterior of the path.
+	 * Such a case may occur if some set of segments of the
+	 * path are retraced in the reverse direction such that the
+	 * two sets of segments cancel each other out without any
+	 * interior area between them.
+	 * To determine whether segments represent true boundaries of
+	 * the interior of the path would require extensive calculations
+	 * involving all of the segments of the path and the winding
+	 * rule and are thus beyond the scope of this implementation.
+	 *
+	 * @param pi the specified {@code PathIterator}
+	 * @param x the specified X coordinate
+	 * @param y the specified Y coordinate
+	 * @param w the width of the specified rectangular coordinates
+	 * @param h the height of the specified rectangular coordinates
+	 * @return {@code true} if the specified {@code PathIterator} and
+	 *         the interior of the specified set of rectangular
+	 *         coordinates intersect each other; {@code false} otherwise.
+	 */
+	public static boolean intersects(PathIterator2i pi, int x, int y, int w, int h) {
+		if (w <= 0f || h <= 0f) {
+			return false;
+		}
+		int mask = (pi.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 2);
+		int crossings = __computeCrossingsFromRect(pi, x, y, x+w, y+h, true, true);
+		return (crossings == MathConstants.SHAPE_INTERSECTS ||
+				(crossings & mask) != 0);
+	}
+	
+	/** Array of types.
+	 */
+	PathElementType[] types;
+
+	/** Array of coords.
+	 */
+	int[] coords;
+
+	/** Number of types in the array.
+	 */
+	int numTypes = 0;
+
+	/** Number of coords in the array.
+	 */
+	int numCoords = 0;
+
+	/** Winding rule for the path.
+	 */
+	PathWindingRule windingRule;
+
+	/** Indicates if the path is empty.
+	 * The path is empty when there is no point inside, or
+	 * all the points are at the same coordinate, or
+	 * when the path does not represents a drawable path
+	 * (a path with a line or a curve).
+	 */
+	private Boolean isEmpty = Boolean.TRUE;
+
+	/** Buffer for the bounds of the path.
+	 */
+	private SoftReference<Rectangle2i> bounds = null;
+
+	/**
+	 */
+	public Path2i() {
+		this(PathWindingRule.NON_ZERO);
+	}
+
+	/**
+	 * @param iterator
+	 */
+	public Path2i(Iterator<PathElement2i> iterator) {
+		this(PathWindingRule.NON_ZERO, iterator);
+	}
+
+	/**
+	 * @param windingRule
+	 */
+	public Path2i(PathWindingRule windingRule) {
+		assert(windingRule!=null);
+		this.types = new PathElementType[GROW_SIZE];
+		this.coords = new int[GROW_SIZE];
+		this.windingRule = windingRule;
+	}
+
+	/**
+	 * @param windingRule
+	 * @param iterator
+	 */
+	public Path2i(PathWindingRule windingRule, Iterator<PathElement2i> iterator) {
+		assert(windingRule!=null);
+		this.types = new PathElementType[GROW_SIZE];
+		this.coords = new int[GROW_SIZE];
+		this.windingRule = windingRule;
+		add(iterator);
+	}
+
+	@Override
+	public void clear() {
+		this.types = new PathElementType[GROW_SIZE];
+		this.coords = new int[GROW_SIZE];
+		this.windingRule = PathWindingRule.NON_ZERO;
+		this.numCoords = 0;
+		this.numTypes = 0;
+		this.isEmpty = true;
+		this.bounds = null;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder b = new StringBuilder();
+		b.append("["); //$NON-NLS-1$
+		if (this.numCoords>0) {
+			b.append(this.coords[0]);
+			for(int i=1; i<this.numCoords; ++i) {
+				b.append(", "); //$NON-NLS-1$
+				b.append(this.coords[i]);
+			}
+		}
+		b.append("]"); //$NON-NLS-1$
+		return b.toString();
+	}
+
+	@Override
+	public Path2i clone() {
+		Path2i clone = super.clone();
+		clone.coords = this.coords.clone();
+		clone.types = this.types.clone();
+		return clone;
+	}
+
+	/** Replies the winding rule for the path.
+	 * 
+	 * @return the winding rule for the path.
+	 */
+	public PathWindingRule getWindingRule() {
+		return this.windingRule;
+	}
+
+	/** Set the winding rule for the path.
+	 * 
+	 * @param r is the winding rule for the path.
+	 */
+	public void setWindingRule(PathWindingRule r) {
+		assert(r!=null);
+		this.windingRule = r;
+	}
+
+	/** Add the elements replied by the iterator into this path.
+	 * 
+	 * @param iterator
+	 */
+	public void add(Iterator<PathElement2i> iterator) {
+		PathElement2i element;
+		while (iterator.hasNext()) {
+			element = iterator.next();
+			switch(element.type) {
+			case MOVE_TO:
+				moveTo(element.toX, element.toY);
+				break;
+			case LINE_TO:
+				lineTo(element.toX, element.toY);
+				break;
+			case QUAD_TO:
+				quadTo(element.ctrlX1, element.ctrlY1, element.toX, element.toY);
+				break;
+			case CURVE_TO:
+				curveTo(element.ctrlX1, element.ctrlY1, element.ctrlX2, element.ctrlY2, element.toX, element.toY);
+				break;
+			case CLOSE:
+				closePath();
+				break;
+			default:
+			}
+		}
+	}
+
+	private void ensureSlots(boolean needMove, int n) {
+		if (needMove && this.numTypes==0) {
+			throw new IllegalStateException("missing initial moveto in path definition"); //$NON-NLS-1$
+		}
+		if (this.types.length==this.numTypes) {
+			this.types = Arrays.copyOf(this.types, this.types.length+GROW_SIZE);
+		}
+		while ((this.numCoords+n)>=this.coords.length) {
+			this.coords = Arrays.copyOf(this.coords, this.coords.length+GROW_SIZE);
+		}
+	}
+
+	/**
+	 * Adds a point to the path by moving to the specified
+	 * coordinates specified in float precision.
+	 *
+	 * @param x the specified X coordinate
+	 * @param y the specified Y coordinate
+	 */
+	public void moveTo(int x, int y) {
+		if (this.numTypes>0 && this.types[this.numTypes-1]==PathElementType.MOVE_TO) {
+			this.coords[this.numCoords-2] = x;
+			this.coords[this.numCoords-1] = y;
+		}
+		else {
+			ensureSlots(false, 2);
+			this.types[this.numTypes++] = PathElementType.MOVE_TO;
+			this.coords[this.numCoords++] = x;
+			this.coords[this.numCoords++] = y;
+		}
+		this.bounds = null;
+	}
+
+	/**
+	 * Adds a point to the path by drawing a straight line from the
+	 * current coordinates to the new specified coordinates
+	 * specified in float precision.
+	 *
+	 * @param x the specified X coordinate
+	 * @param y the specified Y coordinate
+	 */
+	public void lineTo(int x, int y) {
+		ensureSlots(true, 2);
+		this.types[this.numTypes++] = PathElementType.LINE_TO;
+		this.coords[this.numCoords++] = x;
+		this.coords[this.numCoords++] = y;
+		this.isEmpty = null;
+		this.bounds = null;
+	}
+
+	/**
+	 * Adds a curved segment, defined by two new points, to the path by
+	 * drawing a Quadratic curve that intersects both the current
+	 * coordinates and the specified coordinates {@code (x2,y2)},
+	 * using the specified point {@code (x1,y1)} as a quadratic
+	 * parametric control point.
+	 * All coordinates are specified in float precision.
+	 *
+	 * @param x1 the X coordinate of the quadratic control point
+	 * @param y1 the Y coordinate of the quadratic control point
+	 * @param x2 the X coordinate of the final end point
+	 * @param y2 the Y coordinate of the final end point
+	 */
+	public void quadTo(int x1, int y1, int x2, int y2) {
+		ensureSlots(true, 4);
+		this.types[this.numTypes++] = PathElementType.QUAD_TO;
+		this.coords[this.numCoords++] = x1;
+		this.coords[this.numCoords++] = y1;
+		this.coords[this.numCoords++] = x2;
+		this.coords[this.numCoords++] = y2;
+		this.isEmpty = null;
+		this.bounds = null;
+	}
+
+	/**
+	 * Adds a curved segment, defined by three new points, to the path by
+	 * drawing a B&eacute;zier curve that intersects both the current
+	 * coordinates and the specified coordinates {@code (x3,y3)},
+	 * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as
+	 * B&eacute;zier control points.
+	 * All coordinates are specified in float precision.
+	 *
+	 * @param x1 the X coordinate of the first B&eacute;zier control point
+	 * @param y1 the Y coordinate of the first B&eacute;zier control point
+	 * @param x2 the X coordinate of the second B&eacute;zier control point
+	 * @param y2 the Y coordinate of the second B&eacute;zier control point
+	 * @param x3 the X coordinate of the final end point
+	 * @param y3 the Y coordinate of the final end point
+	 */
+	public void curveTo(int x1, int y1,
+			int x2, int y2,
+			int x3, int y3) {
+		ensureSlots(true, 6);
+		this.types[this.numTypes++] = PathElementType.CURVE_TO;
+		this.coords[this.numCoords++] = x1;
+		this.coords[this.numCoords++] = y1;
+		this.coords[this.numCoords++] = x2;
+		this.coords[this.numCoords++] = y2;
+		this.coords[this.numCoords++] = x3;
+		this.coords[this.numCoords++] = y3;
+		this.isEmpty = null;
+		this.bounds = null;
+	}
+
+	/**
+	 * Closes the current subpath by drawing a straight line back to
+	 * the coordinates of the last {@code moveTo}.  If the path is already
+	 * closed or if the previous coordinates are for a {@code moveTo}
+	 * then this method has no effect.
+	 */
+	public void closePath() {
+		if (this.numTypes<=0 ||
+				(this.types[this.numTypes-1]!=PathElementType.CLOSE
+				&&this.types[this.numTypes-1]!=PathElementType.MOVE_TO)) {
+			ensureSlots(true, 0);
+			this.types[this.numTypes++] = PathElementType.CLOSE;
+		}
+	}
+
+	/** Replies an iterator on the path elements.
+	 * <p>
+	 * Only {@link PathElementType#MOVE_TO},
+	 * {@link PathElementType#LINE_TO}, and 
+	 * {@link PathElementType#CLOSE} types are returned by the iterator.
+	 * <p>
+	 * The amount of subdivision of the curved segments is controlled by the 
+	 * flatness parameter, which specifies the maximum distance that any point 
+	 * on the unflattened transformed curve can deviate from the returned
+	 * flattened path segments. Note that a limit on the accuracy of the
+	 * flattened path might be silently imposed, causing very small flattening
+	 * parameters to be treated as larger values. This limit, if there is one,
+	 * is defined by the particular implementation that is used.
+	 * <p>
+	 * The iterator for this class is not multi-threaded safe.
+	 * 
+	 * @param flatness is the maximum distance that the line segments used to approximate
+	 * the curved segments are allowed to deviate from any point on the original curve.
+	 * @return an iterator on the path elements.
+	 */
+	public PathIterator2i getPathIterator(float flatness) {
+		return new FlatteningPathIterator(getWindingRule(), getPathIterator(null), flatness, 10);
+	}
+
+	/** Replies an iterator on the path elements.
+	 * <p>
+	 * Only {@link PathElementType#MOVE_TO},
+	 * {@link PathElementType#LINE_TO}, and 
+	 * {@link PathElementType#CLOSE} types are returned by the iterator.
+	 * <p>
+	 * The amount of subdivision of the curved segments is controlled by the 
+	 * flatness parameter, which specifies the maximum distance that any point 
+	 * on the unflattened transformed curve can deviate from the returned
+	 * flattened path segments. Note that a limit on the accuracy of the
+	 * flattened path might be silently imposed, causing very small flattening
+	 * parameters to be treated as larger values. This limit, if there is one,
+	 * is defined by the particular implementation that is used.
+	 * <p>
+	 * The iterator for this class is not multi-threaded safe.
+	 *
+	 * @param transform is an optional affine Transform2D to be applied to the
+	 * coordinates as they are returned in the iteration, or <code>null</code> if 
+	 * untransformed coordinates are desired.
+	 * @param flatness is the maximum distance that the line segments used to approximate
+	 * the curved segments are allowed to deviate from any point on the original curve.
+	 * @return an iterator on the path elements.
+	 */
+	public PathIterator2i getPathIterator(Transform2D transform, float flatness) {
+		return new FlatteningPathIterator(getWindingRule(), getPathIterator(transform), flatness, 10);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public PathIterator2i getPathIterator(Transform2D transform) {
+		if (transform == null) {
+			return new CopyPathIterator();
+		}
+		return new TransformPathIterator(transform);
+	}
+
+	/** Transform the current path.
+	 * This function changes the current path.
+	 * 
+	 * @param transform is the affine transformation to apply.
+	 * @see #createTransformedShape(Transform2D)
+	 */
+	public void transform(Transform2D transform) {
+		if (transform!=null) {
+			Point2D p = new Point2i();
+			for(int i=0; i<this.numCoords;) {
+				p.set(this.coords[i], this.coords[i+1]);
+				transform.transform(p);
+				this.coords[i++] = p.x();
+				this.coords[i++] = p.y();
+			}
+			this.bounds = null;
+		}
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void translate(int dx, int dy) {
+		for(int i=0; i<this.numCoords;) {
+			this.coords[i++] += dx;
+			this.coords[i++] += dy;
+		}
+		Rectangle2i bb = this.bounds==null ? null : this.bounds.get();
+		if (bb!=null) bb.translate(dx, dy);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Shape2i createTransformedShape(Transform2D transform) {
+		Path2i newPath = new Path2i(getWindingRule());
+		PathIterator2i pi = getPathIterator();
+		Point2i p = new Point2i();
+		Point2i t1 = new Point2i();
+		Point2i t2 = new Point2i();
+		PathElement2i e;
+		while (pi.hasNext()) {
+			e = pi.next();
+			switch(e.type) {
+			case MOVE_TO:
+				p.set(e.toX, e.toY);
+				transform.transform(p);
+				newPath.moveTo(p.x(), p.y());
+				break;
+			case LINE_TO:
+				p.set(e.toX, e.toY);
+				transform.transform(p);
+				newPath.lineTo(p.x(), p.y());
+				break;
+			case QUAD_TO:
+				t1.set(e.ctrlX1, e.ctrlY1);
+				transform.transform(t1);
+				p.set(e.toX, e.toY);
+				transform.transform(p);
+				newPath.quadTo(t1.x(), t1.y(), p.x(), p.y());
+				break;
+			case CURVE_TO:
+				t1.set(e.ctrlX1, e.ctrlY1);
+				transform.transform(t1);
+				t2.set(e.ctrlX2, e.ctrlY2);
+				transform.transform(t2);
+				p.set(e.toX, e.toY);
+				transform.transform(p);
+				newPath.curveTo(t1.x(), t1.y(), t2.x(), t2.y(), p.x(), p.y());
+				break;
+			case CLOSE:
+				newPath.closePath();
+				break;
+			default:
+			}
+		}
+		return newPath;
+	}
+
+	@Override
+	public float distanceSquared(Point2D p) {
+		Point2D c = getClosestPointTo(p);
+		return c.distanceSquared(p);
+	}
+
+	@Override
+	public float distanceL1(Point2D p) {
+		Point2D c = getClosestPointTo(p);
+		return c.distanceL1(p);
+	}
+
+	@Override
+	public float distanceLinf(Point2D p) {
+		Point2D c = getClosestPointTo(p);
+		return c.distanceLinf(p);
+	}
+
+	@Override
+	public boolean contains(int x, int y) {
+		return contains(getPathIterator(), x, y);
+	}
+
+	@Override
+	public boolean contains(Rectangle2i r) {
+		return contains(getPathIterator(),
+				r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight());
+	}
+
+	@Override
+	public boolean intersects(Rectangle2i s) {
+		// Copied from AWT API
+		if (s.isEmpty()) return false;
+		int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
+		int crossings = __computeCrossingsFromRect(
+				getPathIterator(),
+				s.getMinX(), s.getMinY(), s.getMaxX(), s.getMaxY(),
+				true, true);
+		return (crossings == MathConstants.SHAPE_INTERSECTS ||
+				(crossings & mask) != 0);
+	}
+
+	@Override
+	public boolean intersects(Circle2i s) {
+		int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
+		int crossings = computeCrossingsFromCircle(
+				getPathIterator(),
+				s.getX(), s.getY(), s.getRadius());
+		return (crossings == MathConstants.SHAPE_INTERSECTS ||
+				(crossings & mask) != 0);
+	}
+
+	@Override
+	public boolean intersects(Segment2i s) {
+		int mask = (this.windingRule == PathWindingRule.NON_ZERO ? -1 : 2);
+		int crossings = computeCrossingsFromSegment(
+				getPathIterator(),
+				s.getX1(), s.getY1(), s.getX2(), s.getY2());
+		return (crossings == MathConstants.SHAPE_INTERSECTS ||
+				(crossings & mask) != 0);
+	}
+
+	@Override
+	public Rectangle2i toBoundingBox() {
+		Rectangle2i bb = this.bounds==null ? null : this.bounds.get();
+		if (bb==null) {
+			int xmin = Integer.MAX_VALUE;
+			int ymin = Integer.MAX_VALUE;
+			int xmax = Integer.MIN_VALUE;
+			int ymax = Integer.MIN_VALUE;
+			for(int i=0; i<this.numCoords; i+= 2) {
+				if (this.coords[i]<xmin) xmin = this.coords[i];
+				if (this.coords[i+1]<ymin) ymin = this.coords[i+1];
+				if (this.coords[i]>xmax) xmax = this.coords[i];
+				if (this.coords[i+1]>ymax) ymax = this.coords[i+1];
+			}
+			bb = new Rectangle2i();
+			bb.setFromCorners(xmin, ymin, xmax, ymax);
+			this.bounds = new SoftReference<Rectangle2i>(bb);
+		}
+		return bb;
+	}
+
+	@Override
+	public Point2D getClosestPointTo(Point2D p) {
+		Point2D solution = new Point2i();
+		float bestDist = Float.POSITIVE_INFINITY;
+		Point2D candidate;
+		PathIterator2i pi = getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
+		PathElement2i pe;
+
+		Segment2i seg = new Segment2i();
+		int mask = (pi.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 1);
+		int crossings = 0;
+		boolean isClosed = false;
+		int moveX, moveY, currentX, currentY;
+		moveX = moveY = currentX = currentY = 0;
+		
+		while (pi.hasNext()) {
+			pe = pi.next();
+
+			candidate = null;
+
+			currentX = pe.toX;
+			currentY = pe.toY;
+
+			switch(pe.type) {
+			case MOVE_TO:
+				moveX = pe.toX;
+				moveY = pe.toY;
+				isClosed = false;
+				break;
+			case LINE_TO:
+			{
+				seg.set(pe.fromX, pe.fromY, pe.toX, pe.toY);
+				isClosed = false;
+				candidate = seg.getClosestPointTo(p);
+				if (crossings!=MathConstants.SHAPE_INTERSECTS) {
+					crossings = Segment2i.computeCrossingsFromPoint(
+							crossings,
+							p.x(), p.y(),
+							pe.fromX, pe.fromY, pe.toX, pe.toY);
+				}
+				break;
+			}
+			case CLOSE:
+				isClosed = true;
+				if (!pe.isEmpty()) {
+					seg.set(pe.fromX, pe.fromY, pe.toX, pe.toY);
+					candidate = seg.getClosestPointTo(p);
+					if (crossings!=MathConstants.SHAPE_INTERSECTS) {
+						crossings = Segment2i.computeCrossingsFromPoint(
+								crossings,
+								p.x(), p.y(),
+								pe.fromX, pe.fromY, pe.toX, pe.toY);
+					}
+				}
+				break;
+			case QUAD_TO:
+			case CURVE_TO:
+			default:
+				throw new IllegalStateException();
+			}
+
+			if (candidate!=null) {
+				float d = p.distanceSquared(candidate);
+				if (d<=0f) return candidate;
+				if (d<bestDist) {
+					bestDist = d;
+					solution.set(candidate);
+				}
+			}
+		}
+		
+		if (!isClosed && crossings!=MathConstants.SHAPE_INTERSECTS) {
+			crossings = Segment2i.computeCrossingsFromPoint(
+					crossings,
+					p.x(), p.y(),
+					currentX, currentY,
+					moveX, moveY);
+		}
+		
+		if (crossings==MathConstants.SHAPE_INTERSECTS || (crossings & mask) != 0) return p;
+		return solution;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (obj instanceof Path2i) {
+			Path2i p = (Path2i)obj;
+			return (this.numCoords==p.numCoords
+					&&this.numTypes==p.numTypes
+					&&Arrays.equals(this.coords, p.coords)
+					&&Arrays.equals(this.types, p.types)
+					&&this.windingRule==p.windingRule);
+		}
+		return false;
+	}
+
+	@Override
+	public int hashCode() {
+		long bits = 1L;
+		bits = 31L * bits + this.numCoords;
+		bits = 31L * bits + this.numTypes;
+		bits = 31L * bits + Arrays.hashCode(this.coords);
+		bits = 31L * bits + Arrays.hashCode(this.types);
+		bits = 31L * bits + this.windingRule.ordinal();
+		return (int) (bits ^ (bits >> 32));
+	}
+
+	/** Replies the coordinates of this path in an array of
+	 * integers.
+	 * 
+	 * @return the coordinates.
+	 */
+	public final int[] toIntArray() {
+		return toIntArray(null);
+	}
+
+	/** Replies the coordinates of this path in an array of
+	 * integers.
+	 * 
+	 * @param transform is the transformation to apply to all the coordinates.
+	 * @return the coordinates.
+	 */
+	public int[] toIntArray(Transform2D transform) {
+		if (transform==null) {
+			return Arrays.copyOf(this.coords, this.numCoords);
+		}
+		Point2i p = new Point2i();
+		int[] clone = new int[this.numCoords];
+		for(int i=0; i<clone.length;) {
+			p.x = this.coords[i];
+			p.y = this.coords[i+1];
+			transform.transform(p);
+			clone[i++] = p.x;
+			clone[i++] = p.y;
+		}
+		return clone;
+	}
+
+	/** Replies the points of this path in an array.
+	 * 
+	 * @return the points.
+	 */
+	public final Point2D[] toPointArray() {
+		return toPointArray(null);
+	}
+
+	/** Replies the points of this path in an array.
+	 * 
+	 * @param transform is the transformation to apply to all the points.
+	 * @return the points.
+	 */
+	public Point2D[] toPointArray(Transform2D transform) {
+		Point2D[] clone = new Point2D[this.numCoords/2];
+		if (transform==null) {
+			for(int i=0, j=0; j<this.numCoords; ++i) {
+				clone[i] = new Point2i(
+						this.coords[j++],
+						this.coords[j++]);
+			}
+		}
+		else {
+			for(int i=0, j=0; j<clone.length; ++i) {
+				clone[i] = new Point2i(
+						this.coords[j++],
+						this.coords[j++]);
+				transform.transform(clone[i]);
+			}
+		}
+		return clone;
+	}
+
+	/** Replies the collection that is contains all the points of the path.
+	 * 
+	 * @return the point collection.
+	 */
+	public final Collection<Point2D> toCollection() {
+		return new PointCollection();
+	}
+
+	/** Replies the coordinate at the given index.
+	 * The index is in [0;{@link #size()}*2).
+	 *
+	 * @param index
+	 * @return the coordinate at the given index.
+	 */
+	public float getCoordAt(int index) {
+		return this.coords[index];
+	}
+
+	/** Replies the point at the given index.
+	 * The index is in [0;{@link #size()}).
+	 *
+	 * @param index
+	 * @return the point at the given index.
+	 */
+	public Point2i getPointAt(int index) {
+		return new Point2i(
+				this.coords[index*2],
+				this.coords[index*2+1]);
+	}
+
+	/** Replies the number of points in the path.
+	 *
+	 * @return the number of points in the path.
+	 */
+	public int size() {
+		return this.numCoords/2;
+	}
+
+	/** Replies if this path is empty.
+	 * The path is empty when there is no point inside, or
+	 * all the points are at the same coordinate, or
+	 * when the path does not represents a drawable path
+	 * (a path with a line or a curve).
+	 * 
+	 * @return <code>true</code> if the path does not contain
+	 * a coordinate; otherwise <code>false</code>.
+	 */
+	@Override
+	public boolean isEmpty() {
+		if (this.isEmpty==null) {
+			this.isEmpty = Boolean.TRUE;
+			PathIterator2i pi = getPathIterator();
+			PathElement2i pe;
+			while (this.isEmpty()==Boolean.TRUE && pi.hasNext()) {
+				pe = pi.next();
+				if (pe.isDrawable()) { 
+					this.isEmpty = Boolean.FALSE;
+				}
+			}
+		}
+		return this.isEmpty;
+	}
+
+	/** Replies if the given points exists in the coordinates of this path.
+	 * 
+	 * @param p
+	 * @return <code>true</code> if the point is a control point of the path.
+	 */
+	boolean containsPoint(Point2D p) {
+		float x, y;
+		for(int i=0; i<this.numCoords;) {
+			x = this.coords[i++];
+			y = this.coords[i++];
+			if (x==p.getX() && y==p.getY()) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+
+	/** Remove the point with the given coordinates.
+	 * 
+	 * @param x
+	 * @param y
+	 * @return <code>true</code> if the point was removed; <code>false</code> otherwise.
+	 */
+	boolean remove(int x, int y) {
+		for(int i=0, j=0; i<this.numCoords && j<this.numTypes;) {
+			switch(this.types[j]) {
+			case MOVE_TO:
+			case LINE_TO:
+				if (x==this.coords[i] && y==this.coords[i+1]) {
+					this.numCoords -= 2;
+					--this.numTypes;
+					System.arraycopy(this.coords, i+2, this.coords, i, this.numCoords);
+					System.arraycopy(this.types, j+1, this.types, j, this.numTypes);
+					return true;
+				}
+				i += 2;
+				++j;
+				break;
+			case CURVE_TO:
+				if ((x==this.coords[i] && y==this.coords[i+1])
+						||(x==this.coords[i+2] && y==this.coords[i+3])
+						||(x==this.coords[i+4] && y==this.coords[i+5])) {
+					this.numCoords -= 6;
+					--this.numTypes;
+					System.arraycopy(this.coords, i+6, this.coords, i, this.numCoords);
+					System.arraycopy(this.types, j+1, this.types, j, this.numTypes);
+					return true;
+				}
+				i += 6;
+				++j;
+				break;
+			case QUAD_TO:
+				if ((x==this.coords[i] && y==this.coords[i+1])
+						||(x==this.coords[i+2] && y==this.coords[i+3])) {
+					this.numCoords -= 4;
+					--this.numTypes;
+					System.arraycopy(this.coords, i+4, this.coords, i, this.numCoords);
+					System.arraycopy(this.types, j+1, this.types, j, this.numTypes);
+					return true;
+				}
+				i += 4;
+				++j;
+				break;
+			case CLOSE:
+				++j;
+				break;
+			default:
+				break;
+			}
+		}
+		return false;
+	}
+
+	/** Remove the last action.
+	 */
+	public void removeLast() {
+		if (this.numTypes>0) {
+			switch(this.types[this.numTypes-1]) {
+			case CLOSE:
+				// no coord to remove
+				break;
+			case MOVE_TO:
+			case LINE_TO:
+				this.numCoords -= 2;
+				break;
+			case CURVE_TO:
+				this.numCoords -= 6;
+				break;
+			case QUAD_TO:
+				this.numCoords -= 4;
+				break;
+			default:
+				throw new IllegalStateException();
+			}
+			--this.numTypes;
+			this.isEmpty = null;
+			this.bounds = null;
+		}
+	}
+
+	/** Change the coordinates of the last inserted point.
+	 * 
+	 * @param x
+	 * @param y
+	 */
+	public void setLastPoint(int x, int y) {
+		if (this.numCoords>=2) {
+			this.coords[this.numCoords-2] = x;
+			this.coords[this.numCoords-1] = y;
+			this.bounds = null;
+		}
+	}
+
+	/** Replies the points along the path.
+	 * <p>
+	 * This function is equivalent to a
+	 * call to {@link #getPathIterator(float)}
+	 * with the default flatness.
+	 * 
+	 * @return the points
+	 */
+	@Override
+	public Iterator<Point2i> getPointIterator() {
+		PathIterator2i pathIterator = getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
+		return new PixelIterator(pathIterator);
+	}
+
+	/** A path iterator that does not transform the coordinates.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private class CopyPathIterator implements PathIterator2i {
+
+		private final Point2D p1 = new Point2i();
+		private final Point2D p2 = new Point2i();
+		private int iType = 0;
+		private int iCoord = 0;
+		private int movex, movey;
+
+		/**
+		 */
+		public CopyPathIterator() {
+			//
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.iType<Path2i.this.numTypes;
+		}
+
+		@Override
+		public PathElement2i next() {
+			int type = this.iType;
+			if (this.iType>=Path2i.this.numTypes) {
+				throw new NoSuchElementException();
+			}
+			PathElement2i element = null;
+			switch(Path2i.this.types[type]) {
+			case MOVE_TO:
+				if (this.iCoord+2>Path2i.this.numCoords) {
+					throw new NoSuchElementException();
+				}
+				this.movex = Path2i.this.coords[this.iCoord++];
+				this.movey = Path2i.this.coords[this.iCoord++];
+				this.p2.set(this.movex, this.movey);
+				element = new PathElement2i.MovePathElement2i(
+						this.p2.x(), this.p2.y());
+				break;
+			case LINE_TO:
+				if (this.iCoord+2>Path2i.this.numCoords) {
+					throw new NoSuchElementException();
+				}
+				this.p1.set(this.p2);
+				this.p2.set(
+						Path2i.this.coords[this.iCoord++],
+						Path2i.this.coords[this.iCoord++]);
+				element = new PathElement2i.LinePathElement2i(
+						this.p1.x(), this.p1.y(),
+						this.p2.x(), this.p2.y());
+				break;
+			case QUAD_TO:
+			{
+				if (this.iCoord+4>Path2i.this.numCoords) {
+					throw new NoSuchElementException();
+				}
+				this.p1.set(this.p2);
+				int ctrlx = Path2i.this.coords[this.iCoord++];
+				int ctrly = Path2i.this.coords[this.iCoord++];
+				this.p2.set(
+						Path2i.this.coords[this.iCoord++],
+						Path2i.this.coords[this.iCoord++]);
+				element = new PathElement2i.QuadPathElement2i(
+						this.p1.x(), this.p1.y(),
+						ctrlx, ctrly,
+						this.p2.x(), this.p2.y());
+			}
+			break;
+			case CURVE_TO:
+			{
+				if (this.iCoord+6>Path2i.this.numCoords) {
+					throw new NoSuchElementException();
+				}
+				this.p1.set(this.p2);
+				int ctrlx1 = Path2i.this.coords[this.iCoord++];
+				int ctrly1 = Path2i.this.coords[this.iCoord++];
+				int ctrlx2 = Path2i.this.coords[this.iCoord++];
+				int ctrly2 = Path2i.this.coords[this.iCoord++];
+				this.p2.set(
+						Path2i.this.coords[this.iCoord++],
+						Path2i.this.coords[this.iCoord++]);
+				element = new PathElement2i.CurvePathElement2i(
+						this.p1.x(), this.p1.y(),
+						ctrlx1, ctrly1,
+						ctrlx2, ctrly2,
+						this.p2.x(), this.p2.y());
+			}
+			break;
+			case CLOSE:
+				this.p1.set(this.p2);
+				this.p2.set(this.movex, this.movey);
+				element = new PathElement2i.ClosePathElement2i(
+						this.p1.x(), this.p1.y(),
+						this.p2.x(), this.p2.y());
+				break;
+			default:
+			}
+			if (element==null)
+				throw new NoSuchElementException();
+
+			++this.iType;
+
+			return element;
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return Path2i.this.getWindingRule();
+		}
+
+	} // class CopyPathIterator
+
+	/** A path iterator that transforms the coordinates.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private class TransformPathIterator implements PathIterator2i {
+
+		private final Transform2D transform;
+		private final Point2D p1 = new Point2i();
+		private final Point2D p2 = new Point2i();
+		private final Point2D ptmp1 = new Point2i();
+		private final Point2D ptmp2 = new Point2i();
+		private int iType = 0;
+		private int iCoord = 0;
+		private int movex, movey;
+
+		/**
+		 * @param transform
+		 */
+		public TransformPathIterator(Transform2D transform) {
+			assert(transform!=null);
+			this.transform = transform;
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.iType<Path2i.this.numTypes;
+		}
+
+		@Override
+		public PathElement2i next() {
+			if (this.iType>=Path2i.this.numTypes) {
+				throw new NoSuchElementException();
+			}
+			PathElement2i element = null;
+			switch(Path2i.this.types[this.iType++]) {
+			case MOVE_TO:
+				this.movex = Path2i.this.coords[this.iCoord++];
+				this.movey = Path2i.this.coords[this.iCoord++];
+				this.p2.set(this.movex, this.movey);
+				this.transform.transform(this.p2);
+				element = new PathElement2i.MovePathElement2i(
+						this.p2.x(), this.p2.y());
+				break;
+			case LINE_TO:
+				this.p1.set(this.p2);
+				this.p2.set(
+						Path2i.this.coords[this.iCoord++],
+						Path2i.this.coords[this.iCoord++]);
+				this.transform.transform(this.p2);
+				element = new PathElement2i.LinePathElement2i(
+						this.p1.x(), this.p1.y(),
+						this.p2.x(), this.p2.y());
+				break;
+			case QUAD_TO:
+			{
+				this.p1.set(this.p2);
+				this.ptmp1.set(
+						Path2i.this.coords[this.iCoord++],
+						Path2i.this.coords[this.iCoord++]);
+				this.transform.transform(this.ptmp1);
+				this.p2.set(
+						Path2i.this.coords[this.iCoord++],
+						Path2i.this.coords[this.iCoord++]);
+				this.transform.transform(this.p2);
+				element = new PathElement2i.QuadPathElement2i(
+						this.p1.x(), this.p1.y(),
+						this.ptmp1.x(), this.ptmp1.y(),
+						this.p2.x(), this.p2.y());
+			}
+			break;
+			case CURVE_TO:
+			{
+				this.p1.set(this.p2);
+				this.ptmp1.set(
+						Path2i.this.coords[this.iCoord++],
+						Path2i.this.coords[this.iCoord++]);
+				this.transform.transform(this.ptmp1);
+				this.ptmp2.set(
+						Path2i.this.coords[this.iCoord++],
+						Path2i.this.coords[this.iCoord++]);
+				this.transform.transform(this.ptmp2);
+				this.p2.set(
+						Path2i.this.coords[this.iCoord++],
+						Path2i.this.coords[this.iCoord++]);
+				this.transform.transform(this.p2);
+				element = new PathElement2i.CurvePathElement2i(
+						this.p1.x(), this.p1.y(),
+						this.ptmp1.x(), this.ptmp1.y(),
+						this.ptmp2.x(), this.ptmp2.y(),
+						this.p2.x(), this.p2.y());
+			}
+			break;
+			case CLOSE:
+				this.p1.set(this.p2);
+				this.p2.set(this.movex, this.movey);
+				this.transform.transform(this.p2);
+				element = new PathElement2i.ClosePathElement2i(
+						this.p1.x(), this.p1.y(),
+						this.p2.x(), this.p2.y());
+				break;
+			default:
+			}
+			if (element==null)
+				throw new NoSuchElementException();
+			return element;
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return Path2i.this.getWindingRule();
+		}
+
+	}  // class TransformPathIterator
+
+	/** An collection of the points of the path.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private class PointCollection implements Collection<Point2D> {
+
+		/**
+		 */
+		public PointCollection() {
+			//
+		}
+
+		@Override
+		public int size() {
+			return Path2i.this.size();
+		}
+
+		@Override
+		public boolean isEmpty() {
+			return Path2i.this.size()<=0;
+		}
+
+		@Override
+		public boolean contains(Object o) {
+			if (o instanceof Point2D) {
+				return Path2i.this.containsPoint((Point2D)o);
+			}
+			return false;
+		}
+
+		@Override
+		public Iterator<Point2D> iterator() {
+			return new PointIterator();
+		}
+
+		@Override
+		public Object[] toArray() {
+			return Path2i.this.toPointArray();
+		}
+
+		@SuppressWarnings("unchecked")
+		@Override
+		public <T> T[] toArray(T[] a) {
+			Iterator<Point2D> iterator = new PointIterator();
+			for(int i=0; i<a.length && iterator.hasNext(); ++i) {
+				a[i] = (T)iterator.next();
+			}
+			return a;
+		}
+
+		@Override
+		public boolean add(Point2D e) {
+			if (e!=null) {
+				if (Path2i.this.size()==0) {
+					Path2i.this.moveTo(e.x(), e.y());
+				}
+				else {
+					Path2i.this.lineTo(e.x(), e.y());
+				}
+				return true;
+			}
+			return false;
+		}
+
+		@Override
+		public boolean remove(Object o) {
+			if (o instanceof Point2D) {
+				Point2D p = (Point2D)o;
+				return Path2i.this.remove(p.x(), p.y());
+			}
+			return false;
+		}
+
+		@Override
+		public boolean containsAll(Collection<?> c) {
+			for(Object obj : c) {
+				if ((!(obj instanceof Point2D))
+						||(!Path2i.this.containsPoint((Point2D)obj))) {
+					return false;
+				}
+			}
+			return true;
+		}
+
+		@Override
+		public boolean addAll(Collection<? extends Point2D> c) {
+			boolean changed = false;
+			for(Point2D pts : c) {
+				if (add(pts)) {
+					changed = true;
+				}
+			}
+			return changed;
+		}
+
+		@Override
+		public boolean removeAll(Collection<?> c) {
+			boolean changed = false;
+			for(Object obj : c) {
+				if (obj instanceof Point2D) {
+					Point2D pts = (Point2D)obj;
+					if (Path2i.this.remove(pts.x(), pts.y())) {
+						changed = true;
+					}
+				}
+			}
+			return changed;
+		}
+
+		@Override
+		public boolean retainAll(Collection<?> c) {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public void clear() {
+			Path2i.this.clear();
+		}
+
+	} // class PointCollection
+
+	/** Iterator on the points of the path.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private class PointIterator implements Iterator<Point2D> {
+
+		private int index = 0;
+		private Point2D lastReplied = null;
+
+		/**
+		 */
+		public PointIterator() {
+			//
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.index<Path2i.this.size();
+		}
+
+		@Override
+		public Point2D next() {
+			try {
+				this.lastReplied = Path2i.this.getPointAt(this.index++);
+				return this.lastReplied;
+			}
+			catch(Throwable _) {
+				throw new NoSuchElementException();
+			}
+		}
+
+		@Override
+		public void remove() {
+			Point2D p = this.lastReplied;
+			this.lastReplied = null;
+			if (p==null)
+				throw new NoSuchElementException();
+			Path2i.this.remove(p.x(), p.y());
+		}
+
+	}
+
+	/** Iterator on the pixels of the path.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class PixelIterator implements Iterator<Point2i> {
+
+		private final PathIterator2i pathIterator;
+		private Iterator<Point2i> lineIterator = null;
+		private Point2i next = null;
+
+		public PixelIterator(PathIterator2i pi) {
+			this.pathIterator = pi;
+			searchNext();
+		}
+
+		private void searchNext() {
+			Point2i old = this.next;
+			this.next = null;
+			while (this.pathIterator.hasNext() && (this.lineIterator==null || !this.lineIterator.hasNext())) {
+				this.lineIterator = null;
+				PathElement2i elt = this.pathIterator.next();
+				if (elt.isDrawable()) {
+					switch(elt.type) {
+					case LINE_TO:
+						this.lineIterator = new Segment2i(
+								elt.fromX, elt.fromY,
+								elt.toX, elt.toY).getPointIterator();
+						break;
+					case MOVE_TO:
+					case CLOSE:
+					case CURVE_TO:
+					case QUAD_TO:
+					default:
+						throw new IllegalStateException();
+					}
+				}
+			}
+			if (this.lineIterator!=null && this.lineIterator.hasNext()) {
+				this.next = this.lineIterator.next();
+				while (this.next.equals(old)) {
+					this.next = this.lineIterator.next();
+				}
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.next!=null;
+		}
+
+		@Override
+		public Point2i next() {
+			Point2i n = this.next;
+			if (n==null) throw new NoSuchElementException();
+			searchNext();
+			return n;
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+	} // class PixelIterator
+
+	/** A path iterator that is flattening the path.
+	 * This iterator was copied from AWT FlatteningPathIterator.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class FlatteningPathIterator implements PathIterator2i {
+
+		/** Winding rule of the path.
+		 */
+		private final PathWindingRule windingRule;
+
+		/** The source iterator.
+		 */
+		private final Iterator<PathElement2i> pathIterator;
+
+		/**
+		 * Square of the flatness parameter for testing against squared lengths.
+		 */
+		private final float squaredFlatness;
+
+		/**
+		 * Maximum number of recursion levels.
+		 */
+		private final int limit; 
+
+		/** The recursion level at which each curve being held in storage was generated.
+		 */
+		private int levels[];
+
+		/** The cache of interpolated coords.
+		 * Note that this must be long enough
+		 * to store a full cubic segment and
+		 * a relative cubic segment to avoid
+		 * aliasing when copying the coords
+		 * of a curve to the end of the array.
+		 * This is also serendipitously equal
+		 * to the size of a full quad segment
+		 * and 2 relative quad segments.
+		 */
+		private float hold[] = new float[14];
+
+		/** The index of the last curve segment being held for interpolation.
+		 */
+		private int holdEnd;
+
+		/**
+		 * The index of the curve segment that was last interpolated.  This
+		 * is the curve segment ready to be returned in the next call to
+		 * next().
+		 */
+		private int holdIndex;
+
+		/** The ending x of the last segment.
+		 */
+		private float currentX;
+
+		/** The ending y of the last segment.
+		 */
+		private float currentY;
+
+		/** The x of the last move segment.
+		 */
+		private float moveX;
+
+		/** The y of the last move segment.
+		 */
+		private float moveY;
+
+		/** The index of the entry in the
+		 * levels array of the curve segment
+		 * at the holdIndex
+		 */
+		private int levelIndex;
+
+		/** True when iteration is done.
+		 */
+		private boolean done;
+
+		/** The type of the path element.
+		 */
+		private PathElementType holdType;
+
+		/** The x of the last move segment replied by next.
+		 */
+		private int lastNextX;
+
+		/** The y of the last move segment replied by next.
+		 */
+		private int lastNextY;
+
+		/**
+		 * @param windingRule is the winding rule of the path.
+		 * @param pathIterator is the path iterator that may be used to initialize the path.
+		 * @param flatness the maximum allowable distance between the
+		 * control points and the flattened curve
+		 * @param limit the maximum number of recursive subdivisions
+		 * allowed for any curved segment
+		 */
+		public FlatteningPathIterator(PathWindingRule windingRule, Iterator<PathElement2i> pathIterator, float flatness, int limit) {
+			assert(windingRule!=null);
+			assert(flatness>=0f);
+			assert(limit>=0);
+			this.windingRule = windingRule;
+			this.pathIterator = pathIterator;
+			this.squaredFlatness = flatness * flatness;
+			this.limit = limit;
+			this.levels = new int[limit + 1];
+			searchNext(true);
+		}
+
+		/**
+		 * Ensures that the hold array can hold up to (want) more values.
+		 * It is currently holding (hold.length - holdIndex) values.
+		 */
+		private void ensureHoldCapacity(int want) {
+			if (this.holdIndex - want < 0) {
+				int have = this.hold.length - this.holdIndex;
+				int newsize = this.hold.length + GROW_SIZE;
+				float newhold[] = new float[newsize];
+				System.arraycopy(this.hold, this.holdIndex,
+						newhold, this.holdIndex + GROW_SIZE,
+						have);
+				this.hold = newhold;
+				this.holdIndex += GROW_SIZE;
+				this.holdEnd += GROW_SIZE;
+			}
+		}
+
+		/**
+		 * Returns the square of the flatness, or maximum distance of a
+		 * control point from the line connecting the end points, of the
+		 * quadratic curve specified by the control points stored in the
+		 * indicated array at the indicated index.
+		 * @param coords an array containing coordinate values
+		 * @param offset the index into <code>coords</code> from which to
+		 *          to start getting the values from the array
+		 * @return the flatness of the quadratic curve that is defined by the
+		 *          values in the specified array at the specified index.
+		 */
+		private static float getQuadSquaredFlatness(float coords[], int offset) {
+			return MathUtil.distanceSquaredPointToLine(
+					coords[offset + 2], coords[offset + 3],
+					coords[offset + 0], coords[offset + 1],
+					coords[offset + 4], coords[offset + 5]);
+		}
+
+		/**
+		 * Subdivides the quadratic curve specified by the coordinates
+		 * stored in the <code>src</code> array at indices
+		 * <code>srcoff</code> through <code>srcoff</code>&nbsp;+&nbsp;5
+		 * and stores the resulting two subdivided curves into the two
+		 * result arrays at the corresponding indices.
+		 * Either or both of the <code>left</code> and <code>right</code>
+		 * arrays can be <code>null</code> or a reference to the same array
+		 * and offset as the <code>src</code> array.
+		 * Note that the last point in the first subdivided curve is the
+		 * same as the first point in the second subdivided curve.  Thus,
+		 * it is possible to pass the same array for <code>left</code> and
+		 * <code>right</code> and to use offsets such that
+		 * <code>rightoff</code> equals <code>leftoff</code> + 4 in order
+		 * to avoid allocating extra storage for this common point.
+		 * @param src the array holding the coordinates for the source curve
+		 * @param srcoff the offset into the array of the beginning of the
+		 * the 6 source coordinates
+		 * @param left the array for storing the coordinates for the first
+		 * half of the subdivided curve
+		 * @param leftoff the offset into the array of the beginning of the
+		 * the 6 left coordinates
+		 * @param right the array for storing the coordinates for the second
+		 * half of the subdivided curve
+		 * @param rightoff the offset into the array of the beginning of the
+		 * the 6 right coordinates
+		 */
+		private static void subdivideQuad(float src[], int srcoff,
+				float left[], int leftoff,
+				float right[], int rightoff) {
+			float x1 = src[srcoff + 0];
+			float y1 = src[srcoff + 1];
+			float ctrlx = src[srcoff + 2];
+			float ctrly = src[srcoff + 3];
+			float x2 = src[srcoff + 4];
+			float y2 = src[srcoff + 5];
+			if (left != null) {
+				left[leftoff + 0] = x1;
+				left[leftoff + 1] = y1;
+			}
+			if (right != null) {
+				right[rightoff + 4] = x2;
+				right[rightoff + 5] = y2;
+			}
+			x1 = (x1 + ctrlx) / 2f;
+			y1 = (y1 + ctrly) / 2f;
+			x2 = (x2 + ctrlx) / 2f;
+			y2 = (y2 + ctrly) / 2f;
+			ctrlx = (x1 + x2) / 2f;
+			ctrly = (y1 + y2) / 2f;
+			if (left != null) {
+				left[leftoff + 2] = x1;
+				left[leftoff + 3] = y1;
+				left[leftoff + 4] = ctrlx;
+				left[leftoff + 5] = ctrly;
+			}
+			if (right != null) {
+				right[rightoff + 0] = ctrlx;
+				right[rightoff + 1] = ctrly;
+				right[rightoff + 2] = x2;
+				right[rightoff + 3] = y2;
+			}
+		}
+
+		/**
+		 * Returns the square of the flatness of the cubic curve specified
+		 * by the control points stored in the indicated array at the
+		 * indicated index. The flatness is the maximum distance
+		 * of a control point from the line connecting the end points.
+		 * @param coords an array containing coordinates
+		 * @param offset the index of <code>coords</code> from which to begin
+		 *          getting the end points and control points of the curve
+		 * @return the square of the flatness of the <code>CubicCurve2D</code>
+		 *          specified by the coordinates in <code>coords</code> at
+		 *          the specified offset.
+		 */
+		private static float getCurveSquaredFlatness(float coords[], int offset) {
+			return Math.max(
+					MathUtil.distanceSquaredPointToSegment(
+							coords[offset + 0],
+							coords[offset + 1],
+							coords[offset + 6],
+							coords[offset + 7],
+							coords[offset + 2],
+							coords[offset + 3]),
+							MathUtil.distanceSquaredPointToSegment(
+									coords[offset + 0],
+									coords[offset + 1],
+									coords[offset + 6],
+									coords[offset + 7],
+									coords[offset + 4], coords[offset + 5]));
+		}
+
+		/**
+		 * Subdivides the cubic curve specified by the coordinates
+		 * stored in the <code>src</code> array at indices <code>srcoff</code>
+		 * through (<code>srcoff</code>&nbsp;+&nbsp;7) and stores the
+		 * resulting two subdivided curves into the two result arrays at the
+		 * corresponding indices.
+		 * Either or both of the <code>left</code> and <code>right</code>
+		 * arrays may be <code>null</code> or a reference to the same array
+		 * as the <code>src</code> array.
+		 * Note that the last point in the first subdivided curve is the
+		 * same as the first point in the second subdivided curve. Thus,
+		 * it is possible to pass the same array for <code>left</code>
+		 * and <code>right</code> and to use offsets, such as <code>rightoff</code>
+		 * equals (<code>leftoff</code> + 6), in order
+		 * to avoid allocating extra storage for this common point.
+		 * @param src the array holding the coordinates for the source curve
+		 * @param srcoff the offset into the array of the beginning of the
+		 * the 6 source coordinates
+		 * @param left the array for storing the coordinates for the first
+		 * half of the subdivided curve
+		 * @param leftoff the offset into the array of the beginning of the
+		 * the 6 left coordinates
+		 * @param right the array for storing the coordinates for the second
+		 * half of the subdivided curve
+		 * @param rightoff the offset into the array of the beginning of the
+		 * the 6 right coordinates
+		 */
+		private static void subdivideCurve(
+				float src[], int srcoff,
+				float left[], int leftoff,
+				float right[], int rightoff) {
+			float x1 = src[srcoff + 0];
+			float y1 = src[srcoff + 1];
+			float ctrlx1 = src[srcoff + 2];
+			float ctrly1 = src[srcoff + 3];
+			float ctrlx2 = src[srcoff + 4];
+			float ctrly2 = src[srcoff + 5];
+			float x2 = src[srcoff + 6];
+			float y2 = src[srcoff + 7];
+			if (left != null) {
+				left[leftoff + 0] = x1;
+				left[leftoff + 1] = y1;
+			}
+			if (right != null) {
+				right[rightoff + 6] = x2;
+				right[rightoff + 7] = y2;
+			}
+			x1 = (x1 + ctrlx1) / 2f;
+			y1 = (y1 + ctrly1) / 2f;
+			x2 = (x2 + ctrlx2) / 2f;
+			y2 = (y2 + ctrly2) / 2f;
+			float centerx = (ctrlx1 + ctrlx2) / 2f;
+			float centery = (ctrly1 + ctrly2) / 2f;
+			ctrlx1 = (x1 + centerx) / 2f;
+			ctrly1 = (y1 + centery) / 2f;
+			ctrlx2 = (x2 + centerx) / 2f;
+			ctrly2 = (y2 + centery) / 2f;
+			centerx = (ctrlx1 + ctrlx2) / 2f;
+			centery = (ctrly1 + ctrly2) / 2f;
+			if (left != null) {
+				left[leftoff + 2] = x1;
+				left[leftoff + 3] = y1;
+				left[leftoff + 4] = ctrlx1;
+				left[leftoff + 5] = ctrly1;
+				left[leftoff + 6] = centerx;
+				left[leftoff + 7] = centery;
+			}
+			if (right != null) {
+				right[rightoff + 0] = centerx;
+				right[rightoff + 1] = centery;
+				right[rightoff + 2] = ctrlx2;
+				right[rightoff + 3] = ctrly2;
+				right[rightoff + 4] = x2;
+				right[rightoff + 5] = y2;
+			}
+		}
+		
+		private void searchNext(boolean isFirst) {
+			do {
+				flattening();
+			}
+			while (!this.done && !isFirst && isSame());
+		}
+		
+		private boolean isSame() {
+			PathElementType type = this.holdType;
+			int x, y;
+			if (type==PathElementType.CLOSE) {
+				x = Math.round(this.moveX);
+				y = Math.round(this.moveY);
+			}
+			else {
+				x = Math.round(this.hold[this.holdIndex + 0]);
+				y = Math.round(this.hold[this.holdIndex + 1]);
+			}
+			return x==this.lastNextX && y==this.lastNextY;
+		}
+
+		private void flattening() {
+			int level;
+
+			if (this.holdIndex >= this.holdEnd) {
+				if (!this.pathIterator.hasNext()) {
+					this.done = true;
+					return;
+				}
+				PathElement2i pathElement = this.pathIterator.next();
+				this.holdType = pathElement.type;
+				pathElement.toArray(this.hold);
+				this.levelIndex = 0;
+				this.levels[0] = 0;
+			}
+
+			switch (this.holdType) {
+			case MOVE_TO:
+			case LINE_TO:
+				this.currentX = this.hold[0];
+				this.currentY = this.hold[1];
+				if (this.holdType == PathElementType.MOVE_TO) {
+					this.moveX = this.currentX;
+					this.moveY = this.currentY;
+				}
+				this.holdIndex = 0;
+				this.holdEnd = 0;
+				break;
+			case CLOSE:
+				this.currentX = this.moveX;
+				this.currentY = this.moveY;
+				this.holdIndex = 0;
+				this.holdEnd = 0;
+				break;
+			case QUAD_TO:
+				if (this.holdIndex >= this.holdEnd) {
+					// Move the coordinates to the end of the array.
+					this.holdIndex = this.hold.length - 6;
+					this.holdEnd = this.hold.length - 2;
+					this.hold[this.holdIndex + 0] = this.currentX;
+					this.hold[this.holdIndex + 1] = this.currentY;
+					this.hold[this.holdIndex + 2] = this.hold[0];
+					this.hold[this.holdIndex + 3] = this.hold[1];
+					this.hold[this.holdIndex + 4] = this.currentX = this.hold[2];
+					this.hold[this.holdIndex + 5] = this.currentY = this.hold[3];
+				}
+
+				level = this.levels[this.levelIndex];
+				while (level < this.limit) {
+					if (getQuadSquaredFlatness(this.hold, this.holdIndex) < this.squaredFlatness) {
+						break;
+					}
+
+					ensureHoldCapacity(4);
+					subdivideQuad(
+							this.hold, this.holdIndex,
+							this.hold, this.holdIndex - 4,
+							this.hold, this.holdIndex);
+					this.holdIndex -= 4;
+
+					// Now that we have subdivided, we have constructed
+					// two curves of one depth lower than the original
+					// curve.  One of those curves is in the place of
+					// the former curve and one of them is in the next
+					// set of held coordinate slots.  We now set both
+					// curves level values to the next higher level.
+					level++;
+					this.levels[this.levelIndex] = level;
+					this.levelIndex++;
+					this.levels[this.levelIndex] = level;
+				}
+
+				// This curve segment is flat enough, or it is too deep
+				// in recursion levels to try to flatten any more.  The
+				// two coordinates at holdIndex+4 and holdIndex+5 now
+				// contain the endpoint of the curve which can be the
+				// endpoint of an approximating line segment.
+				this.holdIndex += 4;
+				this.levelIndex--;
+				break;
+			case CURVE_TO:
+				if (this.holdIndex >= this.holdEnd) {
+					// Move the coordinates to the end of the array.
+					this.holdIndex = this.hold.length - 8;
+					this.holdEnd = this.hold.length - 2;
+					this.hold[this.holdIndex + 0] = this.currentX;
+					this.hold[this.holdIndex + 1] = this.currentY;
+					this.hold[this.holdIndex + 2] = this.hold[0];
+					this.hold[this.holdIndex + 3] = this.hold[1];
+					this.hold[this.holdIndex + 4] = this.hold[2];
+					this.hold[this.holdIndex + 5] = this.hold[3];
+					this.hold[this.holdIndex + 6] = this.currentX = this.hold[4];
+					this.hold[this.holdIndex + 7] = this.currentY = this.hold[5];
+				}
+
+				level = this.levels[this.levelIndex];
+				while (level < this.limit) {
+					if (getCurveSquaredFlatness(this.hold,this. holdIndex) < this.squaredFlatness) {
+						break;
+					}
+
+					ensureHoldCapacity(6);
+					subdivideCurve(
+							this.hold, this.holdIndex,
+							this.hold, this.holdIndex - 6,
+							this.hold, this.holdIndex);
+					this.holdIndex -= 6;
+
+					// Now that we have subdivided, we have constructed
+					// two curves of one depth lower than the original
+					// curve.  One of those curves is in the place of
+					// the former curve and one of them is in the next
+					// set of held coordinate slots.  We now set both
+					// curves level values to the next higher level.
+					level++;
+					this.levels[this.levelIndex] = level;
+					this.levelIndex++;
+					this.levels[this.levelIndex] = level;
+				}
+
+				// This curve segment is flat enough, or it is too deep
+				// in recursion levels to try to flatten any more.  The
+				// two coordinates at holdIndex+6 and holdIndex+7 now
+				// contain the endpoint of the curve which can be the
+				// endpoint of an approximating line segment.
+				this.holdIndex += 6;
+				this.levelIndex--;
+				break;
+			default:
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			return !this.done;
+		}
+
+		@Override
+		public PathElement2i next() {
+			if (this.done) {
+				throw new NoSuchElementException("flattening iterator out of bounds"); //$NON-NLS-1$
+			}
+
+			PathElement2i element;
+			PathElementType type = this.holdType;
+			if (type!=PathElementType.CLOSE) {
+				int x = Math.round(this.hold[this.holdIndex + 0]);
+				int y = Math.round(this.hold[this.holdIndex + 1]);
+				if (type == PathElementType.MOVE_TO) {
+					element = new PathElement2i.MovePathElement2i(x, y);
+				}
+				else {
+					element = new PathElement2i.LinePathElement2i(
+							this.lastNextX, this.lastNextY,
+							x, y);
+				}
+				this.lastNextX = x;
+				this.lastNextY = y;
+			}
+			else {
+				int x = Math.round(this.moveX);
+				int y = Math.round(this.moveY);
+				element = new PathElement2i.ClosePathElement2i(
+						this.lastNextX, this.lastNextY,
+						x, y);
+				this.lastNextX = x;
+				this.lastNextY = y;
+			}
+
+			searchNext(false);
+
+			return element;
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return this.windingRule;
+		}
+
+	} // class FlatteningPathIterator
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/PathElement2i.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/PathElement2i.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/PathElement2i.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,480 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import java.io.Serializable;
+
+import org.arakhne.afc.math.generic.PathElementType;
+
+/** An element of the path.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class PathElement2i implements Serializable {
+	
+	private static final long serialVersionUID = 7757419973445894032L;
+
+	/** Create an instance of path element.
+	 * 
+	 * @param type is the type of the new element.
+	 * @param lastX is the coordinate of the last point.
+	 * @param lastY is the coordinate of the last point.
+	 * @param coords are the coordinates.
+	 * @return the instance of path element.
+	 */
+	public static PathElement2i newInstance(PathElementType type, int lastX, int lastY, int[] coords) {
+		switch(type) {
+		case MOVE_TO:
+			return new MovePathElement2i(coords[0], coords[1]);
+		case LINE_TO:
+			return new LinePathElement2i(lastX, lastY, coords[0], coords[1]);
+		case QUAD_TO:
+			return new QuadPathElement2i(lastX, lastY, coords[0], coords[1], coords[2], coords[3]);
+		case CURVE_TO:
+			return new CurvePathElement2i(lastX, lastY, coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
+		case CLOSE:
+			return new ClosePathElement2i(lastX, lastY, coords[0], coords[1]);
+		default:
+		}
+		throw new IllegalArgumentException();
+	}
+	
+	/** Type of the path element.
+	 */
+	public final PathElementType type;
+	
+	/** Source point.
+	 */
+	public final int fromX;
+	
+	/** Source point.
+	 */
+	public final int fromY;
+
+	/** Target point.
+	 */
+	public final int toX;
+	
+	/** Target point.
+	 */
+	public final int toY;
+
+	/** First control point.
+	 */
+	public final int ctrlX1;
+	
+	/** First control point.
+	 */
+	public final int ctrlY1;
+
+	/** Second control point.
+	 */
+	public final int ctrlX2;
+	
+	/** Second control point.
+	 */
+	public final int ctrlY2;
+
+	/**
+	 * @param type is the type of the element.
+	 * @param fromx is the source point.
+	 * @param fromy is the source point.
+	 * @param ctrlx1 is the first control point.
+	 * @param ctrly1 is the first control point.
+	 * @param ctrlx2 is the first control point.
+	 * @param ctrly2 is the first control point.
+	 * @param tox is the target point.
+	 * @param toy is the target point.
+	 */
+	public PathElement2i(PathElementType type, int fromx, int fromy, int ctrlx1, int ctrly1, int ctrlx2, int ctrly2, int tox, int toy) {
+		assert(type!=null);
+		this.type = type;
+		this.fromX = fromx;
+		this.fromY = fromy;
+		this.ctrlX1 = ctrlx1;
+		this.ctrlY1 = ctrly1;
+		this.ctrlX2 = ctrlx2;
+		this.ctrlY2 = ctrly2;
+		this.toX = tox;
+		this.toY = toy;
+	}
+
+	/** Replies if the element is empty, ie. the points are the same.
+	 * 
+	 * @return <code>true</code> if the points are
+	 * the same; otherwise <code>false</code>.
+	 */
+	public abstract boolean isEmpty();
+	
+	/** Replies if the element is not empty and its drawable.
+	 * Only the path elements that may produce pixels on the screen
+	 * must reply <code>true</code> in this function.
+	 * 
+	 * @return <code>true</code> if the path element
+	 * is drawable; otherwise <code>false</code>.
+	 */
+	public abstract boolean isDrawable();
+
+	/** Copy the coords into the given array, except the source point.
+	 * 
+	 * @param array
+	 */
+	public abstract void toArray(int[] array);
+
+	/** Copy the coords into the given array, except the source point.
+	 * 
+	 * @param array
+	 */
+	public abstract void toArray(float[] array);
+
+	/** Copy the coords into an array, except the source point.
+	 * 
+	 * @return the array of the points, except the source point.
+	 */
+	public abstract int[] toArray();
+
+	/** An element of the path that represents a <code>MOVE_TO</code>.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public static class MovePathElement2i extends PathElement2i {
+		
+		private static final long serialVersionUID = -8591881826671557331L;
+
+		/**
+		 * @param x
+		 * @param y
+		 */
+		public MovePathElement2i(int x, int y) {
+			super(PathElementType.MOVE_TO,
+					0, 0, 0, 0, 0, 0,
+					x, y);
+		}
+
+		@Override
+		public boolean isEmpty() {
+			return (this.fromX==this.toX) && (this.fromY==this.toY);
+		}
+
+		@Override
+		public boolean isDrawable() {
+			return false;
+		}
+		
+		@Override
+		public void toArray(int[] array) {
+			array[0] = this.toX;
+			array[1] = this.toY;
+		}
+		
+		@Override
+		public void toArray(float[] array) {
+			array[0] = this.toX;
+			array[1] = this.toY;
+		}
+
+		@Override
+		public int[] toArray() {
+			return new int[] {this.toX, this.toY};
+		}
+
+		@Override
+		public String toString() {
+			return "MOVE("+ //$NON-NLS-1$
+					this.toX+"x"+ //$NON-NLS-1$
+					this.toY+")"; //$NON-NLS-1$
+		}
+
+	}
+	
+	/** An element of the path that represents a <code>LINE_TO</code>.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public static class LinePathElement2i extends PathElement2i {
+		
+		private static final long serialVersionUID = 497492389885992535L;
+
+		/**
+		 * @param fromx
+		 * @param fromy
+		 * @param tox
+		 * @param toy
+		 */
+		public LinePathElement2i(int fromx, int fromy, int tox, int toy) {
+			super(PathElementType.LINE_TO,
+					fromx, fromy,
+					0, 0, 0, 0,
+					tox, toy);
+		}
+		
+		@Override
+		public boolean isEmpty() {
+			return (this.fromX==this.toX) && (this.fromY==this.toY);
+		}
+
+		@Override
+		public boolean isDrawable() {
+			return !isEmpty();
+		}
+
+		@Override
+		public void toArray(int[] array) {
+			array[0] = this.toX;
+			array[1] = this.toY;
+		}
+		
+		@Override
+		public void toArray(float[] array) {
+			array[0] = this.toX;
+			array[1] = this.toY;
+		}
+
+		@Override
+		public int[] toArray() {
+			return new int[] {this.toX, this.toY};
+		}
+
+		@Override
+		public String toString() {
+			return "LINE("+ //$NON-NLS-1$
+					this.toX+"x"+ //$NON-NLS-1$
+					this.toY+")"; //$NON-NLS-1$
+		}
+
+	}
+	
+	/** An element of the path that represents a <code>QUAD_TO</code>.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public static class QuadPathElement2i extends PathElement2i {
+		
+		private static final long serialVersionUID = 6341899683730854257L;
+
+		/**
+		 * @param fromx
+		 * @param fromy
+		 * @param ctrlx
+		 * @param ctrly
+		 * @param tox
+		 * @param toy
+		 */
+		public QuadPathElement2i(int fromx, int fromy, int ctrlx, int ctrly, int tox, int toy) {
+			super(PathElementType.QUAD_TO,
+					fromx, fromy,
+					ctrlx, ctrly,
+					0, 0,
+					tox, toy);
+		}
+		
+		@Override
+		public boolean isEmpty() {
+			return (this.fromX==this.toX) && (this.fromY==this.toY) &&
+					(this.ctrlX1==this.toX) && (this.ctrlY1==this.toY);
+		}
+
+		@Override
+		public boolean isDrawable() {
+			return !isEmpty();
+		}
+
+		@Override
+		public void toArray(int[] array) {
+			array[0] = this.ctrlX1;
+			array[1] = this.ctrlY1;
+			array[2] = this.toX;
+			array[3] = this.toY;
+		}
+		
+		@Override
+		public void toArray(float[] array) {
+			array[0] = this.ctrlX1;
+			array[1] = this.ctrlY1;
+			array[2] = this.toX;
+			array[3] = this.toY;
+		}
+
+		@Override
+		public int[] toArray() {
+			return new int[] {this.ctrlX1, this.ctrlY1, this.toX, this.toY};
+		}
+		
+		@Override
+		public String toString() {
+			return "QUAD("+ //$NON-NLS-1$
+					this.ctrlX1+"x"+ //$NON-NLS-1$
+					this.ctrlY1+"|"+ //$NON-NLS-1$
+					this.toX+"x"+ //$NON-NLS-1$
+					this.toY+")"; //$NON-NLS-1$
+		}
+
+	}
+
+	/** An element of the path that represents a <code>CURVE_TO</code>.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public static class CurvePathElement2i extends PathElement2i {
+		
+		private static final long serialVersionUID = 1043302430176113524L;
+
+		/**
+		 * @param fromx
+		 * @param fromy
+		 * @param ctrlx1
+		 * @param ctrly1
+		 * @param ctrlx2
+		 * @param ctrly2
+		 * @param tox
+		 * @param toy
+		 */
+		public CurvePathElement2i(int fromx, int fromy, int ctrlx1, int ctrly1, int ctrlx2, int ctrly2, int tox, int toy) {
+			super(PathElementType.CURVE_TO,
+					fromx, fromy,
+					ctrlx1, ctrly1,
+					ctrlx2, ctrly2,
+					tox, toy);
+		}
+		
+		@Override
+		public boolean isEmpty() {
+			return (this.fromX==this.toX) && (this.fromY==this.toY) &&
+					(this.ctrlX1==this.toX) && (this.ctrlY1==this.toY) &&
+					(this.ctrlX2==this.toX) && (this.ctrlY2==this.toY);
+		}
+
+		@Override
+		public boolean isDrawable() {
+			return !isEmpty();
+		}
+
+		@Override
+		public void toArray(int[] array) {
+			array[0] = this.ctrlX1;
+			array[1] = this.ctrlY1;
+			array[2] = this.ctrlX2;
+			array[3] = this.ctrlY2;
+			array[4] = this.toX;
+			array[5] = this.toY;
+		}
+		
+		@Override
+		public void toArray(float[] array) {
+			array[0] = this.ctrlX1;
+			array[1] = this.ctrlY1;
+			array[2] = this.ctrlX2;
+			array[3] = this.ctrlY2;
+			array[4] = this.toX;
+			array[5] = this.toY;
+		}
+
+		@Override
+		public int[] toArray() {
+			return new int[] {this.ctrlX1, this.ctrlY1, this.ctrlX2, this.ctrlY2, this.toX, this.toY};
+		}
+
+		@Override
+		public String toString() {
+			return "CURVE("+ //$NON-NLS-1$
+					this.ctrlX1+"x"+ //$NON-NLS-1$
+					this.ctrlY1+"|"+ //$NON-NLS-1$
+					this.ctrlX2+"x"+ //$NON-NLS-1$
+					this.ctrlY2+"|"+ //$NON-NLS-1$
+					this.toX+"x"+ //$NON-NLS-1$
+					this.toY+")"; //$NON-NLS-1$
+		}
+
+	}
+
+	/** An element of the path that represents a <code>CLOSE</code>.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public static class ClosePathElement2i extends PathElement2i {
+		
+		private static final long serialVersionUID = 2745123226508569279L;
+
+		/**
+		 * @param fromx
+		 * @param fromy
+		 * @param tox
+		 * @param toy
+		 */
+		public ClosePathElement2i(int fromx, int fromy, int tox, int toy) {
+			super(PathElementType.CLOSE,
+					fromx, fromy,
+					0, 0, 0, 0,
+					tox, toy);
+		}
+		
+		@Override
+		public boolean isEmpty() {
+			return (this.fromX==this.toX) && (this.fromY==this.toY);
+		}
+		
+		@Override
+		public boolean isDrawable() {
+			return false;
+		}
+
+		@Override
+		public void toArray(int[] array) {
+			//
+		}
+		
+		@Override
+		public void toArray(float[] array) {
+			//
+		}
+
+		@Override
+		public int[] toArray() {
+			return new int[0];
+		}
+		
+		@Override
+		public String toString() {
+			return "CLOSE"; //$NON-NLS-1$
+		}
+
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/PathIterator2i.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/PathIterator2i.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/PathIterator2i.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,44 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import java.util.Iterator;
+
+import org.arakhne.afc.math.generic.PathWindingRule;
+
+
+/** This interface describes an interator on path elements.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface PathIterator2i extends Iterator<PathElement2i> {
+
+	/** Replies the winding rule for the path.
+	 * 
+	 * @return the winding rule for the path.
+	 */
+	public PathWindingRule getWindingRule();
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Point2i.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Point2i.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Point2i.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,209 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Tuple2D;
+import org.arakhne.afc.math.generic.Vector2D;
+
+/** 2D Point with 2 integers.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Point2i extends Tuple2i<Point2D> implements Point2D {
+
+	private static final long serialVersionUID = 6087683508168847436L;
+
+	/**
+	 */
+	public Point2i() {
+		//
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Point2i(Tuple2D<?> tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Point2i(int[] tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Point2i(float[] tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Point2i(int x, int y) {
+		super(x,y);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Point2i(float x, float y) {
+		super(x,y);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Point2i(double x, double y) {
+		super((float)x,(float)y);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Point2i(long x, long y) {
+		super(x,y);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Point2i clone() {
+		return (Point2i)super.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float distanceSquared(Point2D p1) {
+	      float dx, dy;
+	      dx = this.x-p1.getX();  
+	      dy = this.y-p1.getY();
+	      return (dx*dx+dy*dy);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float distance(Point2D p1) {
+	      float  dx, dy;
+	      dx = this.x-p1.getX();  
+	      dy = this.y-p1.getY();
+	      return (float)Math.sqrt(dx*dx+dy*dy);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float distanceL1(Point2D p1) {
+	      return (Math.abs(this.x-p1.getX()) + Math.abs(this.y-p1.getY()));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float distanceLinf(Point2D p1) {
+	      return (Math.max( Math.abs(this.x-p1.getX()), Math.abs(this.y-p1.getY())));
+	}
+
+	@Override
+	public void add(Point2D t1, Vector2D t2) {
+		this.x = (int)(t1.getX() + t2.getX());
+		this.y = (int)(t1.getY() + t2.getY());
+	}
+
+	@Override
+	public void add(Vector2D t1, Point2D t2) {
+		this.x = (int)(t1.getX() + t2.getX());
+		this.y = (int)(t1.getY() + t2.getY());
+	}
+
+	@Override
+	public void add(Vector2D t1) {
+		this.x = (int)(this.x + t1.getX());
+		this.y = (int)(this.y + t1.getY());
+	}
+
+	@Override
+	public void scaleAdd(int s, Vector2D t1, Point2D t2) {
+		this.x = (int)(s * t1.getX() + t2.getX());
+		this.y = (int)(s * t1.getY() + t2.getY());
+	}
+
+	@Override
+	public void scaleAdd(float s, Vector2D t1, Point2D t2) {
+		this.x = (int)(s * t1.getX() + t2.getX());
+		this.y = (int)(s * t1.getY() + t2.getY());
+	}
+
+	@Override
+	public void scaleAdd(int s, Point2D t1, Vector2D t2) {
+		this.x = (int)(s * t1.getX() + t2.getX());
+		this.y = (int)(s * t1.getY() + t2.getY());
+	}
+
+	@Override
+	public void scaleAdd(float s, Point2D t1, Vector2D t2) {
+		this.x = (int)(s * t1.getX() + t2.getX());
+		this.y = (int)(s * t1.getY() + t2.getY());
+	}
+
+	@Override
+	public void scaleAdd(int s, Vector2D t1) {
+		this.x = (int)(s * this.x + t1.getX());
+		this.y = (int)(s * this.y + t1.getY());
+	}
+
+	@Override
+	public void scaleAdd(float s, Vector2D t1) {
+		this.x = (int)(s * this.x + t1.getX());
+		this.y = (int)(s * this.y + t1.getY());
+	}
+
+	@Override
+	public void sub(Point2D t1, Vector2D t2) {
+		this.x = (int)(t1.getX() - t1.getX());
+		this.y = (int)(t1.getY() - t1.getY());
+	}
+
+	@Override
+	public void sub(Vector2D t1) {
+		this.x = (int)(this.x - t1.getX());
+		this.y = (int)(this.y - t1.getY());
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Rectangle2i.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Rectangle2i.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Rectangle2i.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,720 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2011 Janus Core Developers
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.arakhne.afc.math.generic.PathWindingRule;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+
+/** 2D rectangle with integer coordinates.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Rectangle2i extends AbstractRectangularShape2i<Rectangle2i> {
+
+	private static final long serialVersionUID = 9061018868216880896L;
+
+	/** Replies if two rectangles are intersecting.
+	 * 
+	 * @param x1 is the first corner of the first rectangle.
+	 * @param y1 is the first corner of the first rectangle.
+	 * @param x2 is the second corner of the first rectangle.
+	 * @param y2 is the second corner of the first rectangle.
+	 * @param x3 is the first corner of the second rectangle.
+	 * @param y3 is the first corner of the second rectangle.
+	 * @param x4 is the second corner of the second rectangle.
+	 * @param y4 is the second corner of the second rectangle.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsRectangleRectangle(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
+		assert(x1<=x2);
+		assert(y1<=y2);
+		assert(x3<=x4);
+		assert(y3<=y4);
+		return x2 > x3
+				&&
+				x1 < x4
+				&&
+				y2 > y3
+				&&
+				y1 < y4;
+	}
+	
+	private static int code(int x, int y, int minx, int miny, int maxx, int maxy) {
+		int code = 0;
+		if (x<minx) code |= 0x8;
+		if (x>maxx) code |= 0x4;
+		if (y<miny) code |= 0x2;
+		if (y>maxy) code |= 0x1;
+		return code;
+	}
+
+	/** Replies if a rectangle is intersecting a segment.
+	 * <p>
+	 * The intersection test is partly based on the Cohen-Sutherland
+	 * classification of the segment.
+	 * This classification permits to detect the base cases;
+	 * and to run a clipping-like algorithm for the intersection
+	 * detection.
+	 * 
+	 * @param x1 is the first corner of the rectangle.
+	 * @param y1 is the first corner of the rectangle.
+	 * @param x2 is the second corner of the rectangle.
+	 * @param y2 is the second corner of the rectangle.
+	 * @param x3 is the first point of the segment.
+	 * @param y3 is the first point of the segment.
+	 * @param x4 is the second point of the segment.
+	 * @param y4 is the second point of the segment.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsRectangleSegment(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
+		int c1 = code(x3, y3, x1, y1, x2, y2);
+		int c2 = code(x4, y4, x1, y1, x2, y2);
+		
+		if (c1==0x0 || c2==0x0) return true;
+		if ((c1&c2)!=0x0) return false;
+		
+		int sx1 = x3;
+		int sy1 = y3;
+		int sx2 = x4;
+		int sy2 = y4;
+
+		Point2i pts = new Point2i();
+		Segment2i.LineIterator iterator = new Segment2i.LineIterator(sx1, sy1, sx2, sy2);
+		
+		while (iterator.hasNext() && c1!=0x0 && c2!=0x0 && (c1&c2)==0) {
+			if ((c1&0x1)!=0) {
+				do {
+					iterator.next(pts);
+					sy1 = pts.y();
+				}
+				while (iterator.hasNext() && sy1!=y2);
+				if (sy1!=y2) return false;
+				sx1 = pts.x();
+			}
+			else if ((c1&0x2)!=0) {
+				do {
+					iterator.next(pts);
+					sy1 = pts.y();
+				}
+				while (iterator.hasNext() && sy1!=y1);
+				if (sy1!=y1) return false;
+				sx1 = pts.x();
+			}
+			else if ((c1&0x4)!=0) {
+				do {
+					iterator.next(pts);
+					sx1 = pts.x();
+				}
+				while (iterator.hasNext() && sx1!=x2);
+				if (sx1!=x2) return false;
+				sy1 = pts.y();
+			}
+			else {
+				do {
+					iterator.next(pts);
+					sx1 = pts.x();
+				}
+				while (iterator.hasNext() && sx1!=x1);
+				if (sx1!=x1) return false;
+				sy1 = pts.y();
+			}
+			c1 = code(sx1, sy1, x1, y1, x2, y2);
+		}
+		
+		return c1==0x0 || c2==0x0;
+	}
+
+	/** Compute the closest point on the rectangle from the given point.
+	 * 
+	 * @param minx is the x-coordinate of the lowest coordinate of the rectangle.
+	 * @param miny is the y-coordinate of the lowest coordinate of the rectangle.
+	 * @param maxx is the x-coordinate of the highest coordinate of the rectangle.
+	 * @param maxy is the y-coordinate of the highest coordinate of the rectangle.
+	 * @param px is the x-coordinate of the point.
+	 * @param py is the y-coordinate of the point.
+	 * @return the closest point.
+	 */
+	public static Point2i computeClosestPoint(int minx, int miny, int maxx, int maxy, int px, int py) {
+		int x;
+		int same = 0;
+		if (px<minx) {
+			x = minx;
+		}
+		else if (px>maxx) {
+			x = maxx;
+		}
+		else {
+			x = px;
+			++same;
+		}
+		int y;
+		if (py<miny) {
+			y = miny;
+		}
+		else if (py>maxy) {
+			y = maxy;
+		}
+		else {
+			y = py;
+			++same;
+		}
+		if (same==2) {
+			return new Point2i(px,py);
+		}
+		return new Point2i(x,y);
+	}
+
+	/**
+	 */
+	public Rectangle2i() {
+		//
+	}
+	
+	/**
+	 * @param min is the min corner of the rectangle.
+	 * @param max is the max corner of the rectangle.
+	 */
+	public Rectangle2i(Point2i min, Point2i max) {
+		setFromCorners(min.x(), min.y(), max.x(), max.y());
+	}
+	
+	/**
+	 * @param x
+	 * @param y
+	 * @param width
+	 * @param height
+	 */
+	public Rectangle2i(int x, int y, int width, int height) {
+		setFromCorners(x, y, x+width, y+height);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Rectangle2i toBoundingBox() {
+		return this;
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceSquared(Point2D p) {
+		int dx;
+		if (p.x()<this.minx) {
+			dx = this.minx - p.x();
+		}
+		else if (p.x()>this.maxx) {
+			dx = p.x() - this.maxx;
+		}
+		else {
+			dx = 0;
+		}
+		int dy;
+		if (p.y()<this.miny) {
+			dy = this.miny - p.y();
+		}
+		else if (p.y()>this.maxy) {
+			dy = p.y() - this.maxy;
+		}
+		else {
+			dy = 0;
+		}
+		return dx*dx+dy*dy;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceL1(Point2D p) {
+		int dx;
+		if (p.x()<this.minx) {
+			dx = this.minx - p.x();
+		}
+		else if (p.x()>this.maxx) {
+			dx = p.x() - this.maxx;
+		}
+		else {
+			dx = 0;
+		}
+		int dy;
+		if (p.y()<this.miny) {
+			dy = this.miny - p.y();
+		}
+		else if (p.y()>this.maxy) {
+			dy = p.y() - this.maxy;
+		}
+		else {
+			dy = 0;
+		}
+		return dx + dy;
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceLinf(Point2D p) {
+		int dx;
+		if (p.x()<this.minx) {
+			dx = this.minx - p.x();
+		}
+		else if (p.x()>this.maxx) {
+			dx = p.x() - this.maxx;
+		}
+		else {
+			dx = 0;
+		}
+		int dy;
+		if (p.y()<this.miny) {
+			dy = this.miny - p.y();
+		}
+		else if (p.y()>this.maxy) {
+			dy = p.y() - this.maxy;
+		}
+		else {
+			dy = 0;
+		}
+		return Math.max(dx, dy);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Point2i getClosestPointTo(Point2D p) {
+		return computeClosestPoint(this.minx, this.miny, this.maxx, this.maxy, p.x(), p.y());
+	}
+	
+	@Override
+	public boolean intersects(Rectangle2i s) {
+		return intersectsRectangleRectangle(
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY(),
+				s.getMinX(), s.getMinY(),
+				s.getMaxX(), s.getMaxY());
+	}
+
+	@Override
+	public boolean intersects(Circle2i s) {
+		return Circle2i.intersectsCircleRectangle(
+				s.getX(), s.getY(),
+				s.getRadius(),
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY());
+	}
+
+	@Override
+	public boolean intersects(Segment2i s) {
+		return intersectsRectangleSegment(
+				this.minx, this.miny, this.maxx, this.maxy,
+				s.getX1(), s.getY1(), s.getX2(), s.getY2());
+	}
+
+	@Override
+	public PathIterator2i getPathIterator(Transform2D transform) {
+		if (transform==null) {
+			return new CopyPathIterator(
+					getMinX(), getMinY(),
+					getMaxX(), getMaxY());
+		}
+		return new TransformPathIterator(
+				getMinX(), getMinY(),
+				getMaxX(), getMaxY(),
+				transform);
+	}
+
+	@Override
+	public boolean contains(int x, int y) {
+		return x>=this.minx && x<=this.maxx && y>=this.miny && y<=this.maxy;
+	}
+
+	@Override
+	public boolean contains(Rectangle2i r) {
+		return r.getMinX()>=getMinX() && r.getMaxX()<=getMaxX()
+				&& r.getMinY()>=getMinY() && r.getMaxY()<=getMaxY();
+	}
+
+	/** Replies the points on the bounds of the rectangle starting from
+	 * the top border.
+	 * 
+	 * @return the points on the bounds of the rectangle.
+	 */
+	@Override
+	public Iterator<Point2i> getPointIterator() {
+		return getPointIterator(Side.TOP);
+	}
+
+	/** Replies the points on the bounds of the rectangle.
+	 *
+	 * @param startingBorder is the first border to reply.
+	 * @return the points on the bounds of the rectangle.
+	 */
+	public Iterator<Point2i> getPointIterator(Side startingBorder) {
+		return new RectangleSideIterator(this.minx, this.miny, this.maxx, this.maxy, startingBorder);
+	}
+
+	/** Iterator on the path elements of the rectangle.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class CopyPathIterator implements PathIterator2i {
+		
+		private final int x1;
+		private final int y1;
+		private final int x2;
+		private final int y2;
+		private int index = 0;
+		
+		/**
+		 * @param x1
+		 * @param y1
+		 * @param x2
+		 * @param y2
+		 */
+		public CopyPathIterator(int x1, int y1, int x2, int y2) {
+			this.x1 = Math.min(x1, x2);
+			this.y1 = Math.min(y1, y2);
+			this.x2 = Math.max(x1, x2);
+			this.y2 = Math.max(y1, y2);
+			if (Math.abs(this.x1-this.x2)<=0 || Math.abs(this.y1-this.y2)<=0) {
+				this.index = 6;
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.index<=5;
+		}
+
+		@Override
+		public PathElement2i next() {
+			int idx = this.index;
+			++this.index;
+			switch(idx) {
+			case 0:
+				return new PathElement2i.MovePathElement2i(
+						this.x1, this.y1);
+			case 1:
+				return new PathElement2i.LinePathElement2i(
+						this.x1, this.y1,
+						this.x2, this.y1);
+			case 2:
+				return new PathElement2i.LinePathElement2i(
+						this.x2, this.y1,
+						this.x2, this.y2);
+			case 3:
+				return new PathElement2i.LinePathElement2i(
+						this.x2, this.y2,
+						this.x1, this.y2);
+			case 4:
+				return new PathElement2i.LinePathElement2i(
+						this.x1, this.y2,
+						this.x1, this.y1);
+			case 5:
+				return new PathElement2i.ClosePathElement2i(
+						this.x1, this.y1,
+						this.x1, this.y1);
+			default:
+				throw new NoSuchElementException();
+			}
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return PathWindingRule.NON_ZERO;
+		}
+		
+	}
+
+	/** Iterator on the path elements of the rectangle.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class TransformPathIterator implements PathIterator2i {
+		
+		private final Transform2D transform;
+		private final int x1;
+		private final int y1;
+		private final int x2;
+		private final int y2;
+		private int index = 0;
+		
+		private final Point2D p1 = new Point2i();
+		private final Point2D p2 = new Point2i();
+		
+		/**
+		 * @param x1
+		 * @param y1
+		 * @param x2
+		 * @param y2
+		 * @param transform
+		 */
+		public TransformPathIterator(int x1, int y1, int x2, int y2, Transform2D transform) {
+			this.transform = transform;
+			this.x1 = Math.min(x1, x2);
+			this.y1 = Math.min(y1, y2);
+			this.x2 = Math.max(x1, x2);
+			this.y2 = Math.max(y1, y2);
+			if (Math.abs(this.x1-this.x2)<=0 || Math.abs(this.y1-this.y2)<=0) {
+				this.index = 6;
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.index<=5;
+		}
+
+		@Override
+		public PathElement2i next() {
+			int idx = this.index;
+			++this.index;
+			switch(idx) {
+			case 0:
+				this.p2.set(this.x1, this.y1);
+				if (this.transform!=null) {
+					this.transform.transform(this.p2);
+				}
+				return new PathElement2i.MovePathElement2i(
+						this.p2.x(), this.p2.y());
+			case 1:
+				this.p1.set(this.p2);
+				this.p2.set(this.x2, this.y1);
+				if (this.transform!=null) {
+					this.transform.transform(this.p2);
+				}
+				return new PathElement2i.LinePathElement2i(
+						this.p1.x(), this.p1.y(),
+						this.p2.x(), this.p2.y());
+			case 2:
+				this.p1.set(this.p2);
+				this.p2.set(this.x2, this.y2);
+				if (this.transform!=null) {
+					this.transform.transform(this.p2);
+				}
+				return new PathElement2i.LinePathElement2i(
+						this.p1.x(), this.p1.y(),
+						this.p2.x(), this.p2.y());
+			case 3:
+				this.p1.set(this.p2);
+				this.p2.set(this.x1, this.y2);
+				if (this.transform!=null) {
+					this.transform.transform(this.p2);
+				}
+				return new PathElement2i.LinePathElement2i(
+						this.p1.x(), this.p1.y(),
+						this.p2.x(), this.p2.y());
+			case 4:
+				this.p1.set(this.p2);
+				this.p2.set(this.x1, this.y1);
+				if (this.transform!=null) {
+					this.transform.transform(this.p2);
+				}
+				return new PathElement2i.LinePathElement2i(
+						this.p1.x(), this.p1.y(),
+						this.p2.x(), this.p2.y());
+			case 5:
+				return new PathElement2i.ClosePathElement2i(
+						this.p2.x(), this.p2.y(),
+						this.p2.x(), this.p2.y());
+			default:
+				throw new NoSuchElementException();
+			}
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return PathWindingRule.NON_ZERO;
+		}
+		
+	}
+
+	/** Sides of a rectangle.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public static enum Side {
+		/** Top.
+		 */
+		TOP,
+		/** Right.
+		 */
+		RIGHT,
+		/** Bottom.
+		 */
+		BOTTOM,
+		/** Left.
+		 */
+		LEFT;
+	}
+
+	/** Iterates on points on the sides of a rectangle.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class RectangleSideIterator implements Iterator<Point2i> {
+
+		private final int x0;
+		private final int y0;
+		private final int x1;
+		private final int y1;
+		private final Side firstSide;
+		
+		private Side currentSide;
+		private int i;
+		
+		/**
+		 * @param x1
+		 * @param y1
+		 * @param x2
+		 * @param y2
+		 * @param firstSide
+		 */
+		public RectangleSideIterator(int x1, int y1, int x2, int y2, Side firstSide) {
+			assert(x1<=x2 && y1<=y2);
+			this.firstSide = firstSide;
+			this.x0 = x1;
+			this.y0 = y1;
+			this.x1 = x2;
+			this.y1 = y2;
+			
+			this.currentSide = (x2>x1 && y2>y1) ? this.firstSide : null;
+			this.i = 0;
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public boolean hasNext() {
+			return this.currentSide!=null;
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public Point2i next() {
+			int x, y;
+			
+			switch(this.currentSide) {
+			case TOP:
+				x = this.x0+this.i;
+				y = this.y0;
+				break;
+			case RIGHT:
+				x = this.x1;
+				y = this.y0+this.i+1;
+				break;
+			case BOTTOM:
+				x = this.x1-this.i-1;
+				y = this.y1;
+				break;
+			case LEFT:
+				x = this.x0;
+				y = this.y1-this.i-1;
+				break;
+			default:
+				throw new NoSuchElementException();
+			}
+			
+			++ this.i;
+			Side newSide = null;
+			
+			switch(this.currentSide) {
+			case TOP:
+				if (x>=this.x1) {
+					newSide = Side.RIGHT;
+					this.i = 0;
+				}
+				break;
+			case RIGHT:
+				if (y>=this.y1) {
+					newSide = Side.BOTTOM;
+					this.i = 0;
+				}
+				break;
+			case BOTTOM:
+				if (x<=this.x0) {
+					newSide = Side.LEFT;
+					this.i = 0;
+				}
+				break;
+			case LEFT:
+				if (y<=this.y0+1) {
+					newSide = Side.TOP;
+					this.i = 0;
+				}
+				break;
+			default:
+				throw new NoSuchElementException();
+			}
+
+			if (newSide!=null) {
+				this.currentSide = (this.firstSide==newSide) ? null : newSide;
+			}
+			
+			return new Point2i(x,y);
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+		
+	} // class RectangleIterator
+	
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Segment2i.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Segment2i.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Segment2i.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,1267 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.MathUtil;
+import org.arakhne.afc.math.generic.PathWindingRule;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+
+/** 2D line segment with integer coordinates.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Segment2i extends AbstractShape2i<Segment2i> {
+
+	/**
+	 * Calculates the number of times the line from (x0,y0) to (x1,y1)
+	 * crosses the ellipse (ex0,ey0) to (ex1,ey1) extending to the right.
+	 * <p>
+	 * When the line (x0;y0) to (x1;y1) is crossing one of the up or
+	 * bottom borders of the shadow of the circle, the crossings
+	 * count is increased or decreased, depending if the line is
+	 * going down or up, respectively.
+	 * In the following figure, the circle is represented.
+	 * The "shadow" is the projection of the circle on the right.
+	 * The red lines represent the up and bottom borders.
+	 * <center>
+	 * <a href="doc-files/crossing_circle.png"><img src="doc-files/crossing_circle.png" width="300"/></a>
+	 * </center>
+	 * 
+	 * @param crossings is the initial value for the number of crossings.
+	 * @param cx is the center of the circle to extend.
+	 * @param cy is the center of the circle to extend.
+	 * @param radius is the radius of the circle to extend.
+	 * @param x0 is the first point of the line.
+	 * @param y0 is the first point of the line.
+	 * @param x1 is the second point of the line.
+	 * @param y1 is the secondpoint of the line.
+	 * @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}.
+	 */
+	public static int computeCrossingsFromCircle(
+			int crossings,
+			int cx, int cy,
+			int radius,
+			int x0, int y0,
+			int x1, int y1) {
+		int numCrosses = crossings;
+
+		int xmin = cx - Math.abs(radius);
+		int xmax = cx + Math.abs(radius);
+		int ymin = cy - Math.abs(radius);
+		int ymax = cy + Math.abs(radius);
+
+		// The line is entirely on the top or on the bottom of the shadow
+		if (y0<ymin && y1<ymin) return numCrosses;
+		if (y0>ymax && y1>ymax) return numCrosses;
+		// The line is entierly on the left of the shadow.
+		if (x0<xmin && x1<xmin) return numCrosses;
+		
+		if (x0>xmax && x1>xmax) {
+			// The line is entirely at the right of the center of the shadow.
+			// We may use the standard "rectangle" crossing computation
+			if (y0<y1) {
+				if (y0<ymin) ++numCrosses;
+				if (y1>ymax) ++numCrosses;
+			}
+			else {
+				if (y1<ymin) --numCrosses;
+				if (y0>ymax) --numCrosses;
+			}
+		}
+		else if (Circle2i.intersectsCircleSegment(
+				cx, cy, radius,
+				x0, y0, x1, y1)) {
+			return MathConstants.SHAPE_INTERSECTS;
+		}
+		else {
+			numCrosses = computeCrossingsFromPoint(numCrosses, cx, ymin, x0, y0, x1, y1, true, false);
+			numCrosses = computeCrossingsFromPoint(numCrosses, cx, ymax, x0, y0, x1, y1, false, true);
+		}
+
+		return numCrosses;
+	}
+
+	/**
+	 * Calculates the number of times the line from (x0,y0) to (x1,y1)
+	 * crosses the segment (sx0,sy0) to (sx1,sy1) extending to the right.
+	 * <p>
+	 * When the line (x0;y0) to (x1;y1) is crossing one of the up or
+	 * bottom borders of the shadow of the segment, the crossings
+	 * count is increased or decreased, depending if the line is
+	 * going down or up, respectively.
+	 * In the following figure, the segment is represented.
+	 * The "shadow" is the projection of the segment on the right.
+	 * The red lines represent the up and bottom borders.
+	 * <center>
+	 * <a href="doc-files/crossing_segment.png"><img src="doc-files/crossing_segment.png" width="300"/></a>
+	 * </center>
+	 * 
+	 * @param crossings is the initial value for the number of crossings.
+	 * @param sx1 is the first point of the segment to extend.
+	 * @param sy1 is the first point of the segment to extend.
+	 * @param sx2 is the second point of the segment to extend.
+	 * @param sy2 is the second point of the segment to extend.
+	 * @param x0 is the first point of the line.
+	 * @param y0 is the first point of the line.
+	 * @param x1 is the second point of the line.
+	 * @param y1 is the secondpoint of the line.
+	 * @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}.
+	 */
+	public static int computeCrossingsFromSegment(
+			int crossings,
+			int sx1, int sy1,
+			int sx2, int sy2,
+			int x0, int y0,
+			int x1, int y1) {
+		/* CAUTION:
+		 * --------
+		 * In the comment of this function, it is assumed that y0<=y1,
+		 * to simplify the explanations.
+		 * The source code is handled y0<=y1 and y0>y1.
+		 */
+		int numCrosses = crossings;
+
+		int xmin = Math.min(sx1, sx2);
+		int xmax = Math.max(sx1, sx2);
+		int ymin = Math.min(sy1, sy2);
+		int ymax = Math.max(sy1, sy2);
+
+		// The line is entirely below or up to the shadow of the segment 
+		if (y0<ymin && y1<ymin) return numCrosses;
+		if (y0>ymax && y1>ymax) return numCrosses;
+		// The line is entirely at te left of the segment
+		if (x0<xmin && x1<xmin) return numCrosses;
+
+		if (x0>xmax && x1>xmax) {
+			// The line is entirely at the right of the shadow
+			if (y0<y1) {
+				if (y0<ymin) ++numCrosses;
+				if (y1>ymax) ++numCrosses;
+			}
+			else {
+				if (y1<ymin) --numCrosses;
+				if (y0>ymax) --numCrosses;
+			}
+		}
+		else if (intersectsSegmentSegment(x0, y0, x1, y1, sx1, sy1, sx2, sy2, true, true, null)) {
+			return MathConstants.SHAPE_INTERSECTS;
+		}
+		else {
+			// The line is intersectly partly the bounding rectangle of the segment.
+			// We must determine on which side of the segment the points of the line are.
+			// If side1 is positive, the first point of the line is on the side of the shadow, relatively to the segment
+			// If it is negative, the first point is on the opposite side of the shadow, relatively to the segment.
+			// If it is nul, the point is on line colinear to the segment.
+			// Same for side2 and the second point of the line.
+			int side1, side2;
+			boolean firstIsTop = (sy1<=sy2);
+			if (firstIsTop) {
+				side1 = MathUtil.sidePointLine(sx1, sy1, sx2, sy2, x0, y0, false);
+				side2 = MathUtil.sidePointLine(sx1, sy1, sx2, sy2, x1, y1, false);
+			}
+			else {
+				side1 = MathUtil.sidePointLine(sx2, sy2, sx1, sy1, x0, y0, false);
+				side2 = MathUtil.sidePointLine(sx2, sy2, sx1, sy1, x1, y1, false);
+			}
+			if (side1>=0 || side2>=0) {
+				// At least one point is on the side of the shadow.
+				// Now we compute the intersection with the up and bottom borders.
+				// Intersection is obtained by computed the crossing value from
+				// the two points of the segment.
+				int n1, n2;
+				n1 = computeCrossingsFromPoint(0, sx1, sy1, x0, y0, x1, y1, firstIsTop, !firstIsTop);
+				n2 = computeCrossingsFromPoint(0, sx2, sy2, x0, y0, x1, y1, !firstIsTop, firstIsTop);
+
+				// The total crossing value must be updated with the border's crossing values.
+				numCrosses += n1 + n2;
+			}
+		}
+
+		return numCrosses;
+	}
+
+	/**
+	 * Accumulate the number of times the line crosses the shadow
+	 * extending to the right of the rectangle.
+	 * <p>
+	 * When the line (x0;y0) to (x1;y1) is intersecting the rectangle,
+	 * the value {@link MathConstants#SHAPE_INTERSECTS} is returned.
+	 * When the line (x0;y0) to (x1;y1) is crossing one of the up or
+	 * bottom borders of the shadow of the rectangle, the crossings
+	 * count is increased or decreased, depending if the line is
+	 * going down or up, respectively.
+	 * In the following figure, the rectangle is represented.
+	 * The "shadow" is the projection of the rectangle on the right.
+	 * The red lines represent the up and bottom borders.
+	 * <center>
+	 * <a href="doc-files/crossing_rect.png"><img src="doc-files/crossing_rect.png" width="300"/></a>
+	 * </center>
+	 * 
+	 * @param crossings is the initial value for the number of crossings.
+	 * @param rxmin is the first corner of the rectangle.
+	 * @param rymin is the first corner of the rectangle.
+	 * @param rxmax is the second corner of the rectangle.
+	 * @param rymax is the second corner of the rectangle.
+	 * @param x0 is the first point of the line.
+	 * @param y0 is the first point of the line.
+	 * @param x1 is the second point of the line.
+	 * @param y1 is the secondpoint of the line.
+	 * @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}.
+	 */
+	public static int computeCrossingsFromRect(
+			int crossings,
+			int rxmin, int rymin,
+			int rxmax, int rymax,
+			int x0, int y0,
+			int x1, int y1) {
+		int numCrosses = crossings;
+		// The line is horizontal, only SHAPE_INTERSECT may be replies
+		if (y0==y1) {
+			if (y0>=rymin && y0<=rymax &&
+				(x0>=rxmin || x1>=rxmin) &&
+				(x0<=rxmax || x1<=rxmax)) {
+				return MathConstants.SHAPE_INTERSECTS;
+			}
+			return crossings;
+		}
+		// The line is entirely at the top or at the bottom of the rectangle
+		if (y0 > rymax && y1 > rymax) return numCrosses;
+		if (y0 < rymin && y1 < rymin) return numCrosses;
+		// The line is entirely on the left of the rectangle
+		if (x0 < rxmin && x1 < rxmin) return numCrosses;
+
+		if (x0 > rxmax && x1 > rxmax) {
+			// Line is entirely to the right of the rect
+			// and the vertical ranges of the two overlap by a non-empty amount
+			// Thus, this line segment is partially in the "right-shadow"
+			// Path may have done a complete crossing
+			// Or path may have entered or exited the right-shadow
+			if (y0 < y1) {
+				// y-increasing line segment...
+				// We know that y0 < rymax and y1 > rymin
+				if (y0 < rymin) ++numCrosses;
+				if (y1 > rymax) ++numCrosses;
+			}
+			else if (y1 < y0) {
+				// y-decreasing line segment...
+				// We know that y1 < rymax and y0 > rymin
+				if (y1 < rymin) --numCrosses;
+				if (y0 > rymax) --numCrosses;
+			}
+		}
+		else {
+			// Remaining case:
+			// Both x and y ranges overlap by a non-empty amount
+			// First do trivial INTERSECTS rejection of the cases
+			// where one of the endpoints is inside the rectangle.
+			if ((x0 > rxmin && x0 < rxmax && y0 > rymin && y0 < rymax) ||
+				(x1 > rxmin && x1 < rxmax && y1 > rymin && y1 < rymax)) {
+				return MathConstants.SHAPE_INTERSECTS;
+			}
+
+			// Otherwise calculate the y intercepts and see where
+			// they fall with respect to the rectangle
+			LineIterator iterator;
+			int ymaxline;
+			if (y0<=y1) {
+				iterator = new LineIterator(x0, y0, x1, y1);
+				ymaxline = y1;
+			}
+			else {
+				iterator = new LineIterator(x1, y1, x0, y0);
+				ymaxline = y0;
+			}
+			Point2i p = new Point2i();
+			Integer xintercept1 = null;
+			Integer xintercept2 = null;
+			boolean cont = true;
+			while (iterator.hasNext() && cont) {
+				iterator.next(p);
+				if (p.y()==rymin && (xintercept1==null || xintercept1>p.x())) {
+					xintercept1 = p.x();
+				}
+				if (p.y()==rymax && (xintercept2==null || xintercept2>p.x())) {
+					xintercept2 = p.x();
+				}
+				cont = (p.y()<=ymaxline);
+			}
+			
+			if (xintercept1!=null && xintercept2!=null) {
+				if (xintercept1<rxmin && xintercept2<rxmin) {
+					// the intersection points are entirely on the left
+				}
+				else if (xintercept1>rxmax && xintercept2>rxmax) {
+					// the intersection points are entirely on the right
+					if (y0 < y1) {
+						// y-increasing line segment...
+						// We know that y0 < rymax and y1 > rymin
+						if (y0 <= rymin) ++numCrosses;
+						if (y1 >= rymax) ++numCrosses;
+					}
+					else if (y1 < y0) {
+						// y-decreasing line segment...
+						// We know that y1 < rymax and y0 > rymin
+						if (y1 <= rymin) --numCrosses;
+						if (y0 >= rymax) --numCrosses;
+					}
+				}
+				else {
+					return MathConstants.SHAPE_INTERSECTS;
+				}
+			}
+			else if (xintercept1!=null) {
+				// Only the top line of the rectangle is intersecting the segment
+				if (xintercept1<rxmin) {
+					// the intersection point is at entirely on the left
+				}
+				else if (xintercept1>rxmax) {
+					if (y0 < y1) {
+						// y-increasing line segment...
+						// We know that y0 < rymax and y1 > rymin
+						if (y0 <= rymin) ++numCrosses;
+					}
+					else if (y1 < y0) {
+						// y-decreasing line segment...
+						// We know that y1 < rymax and y0 > rymin
+						if (y1 <= rymin) --numCrosses;
+					}
+				}
+				else {
+					return MathConstants.SHAPE_INTERSECTS;
+				}
+			}
+			else if (xintercept2!=null) {
+				// Only the bottom line of the rectangle is intersecting the segment
+				if (xintercept2<rxmin) {
+					// the intersection point is at entirely on the left
+				}
+				else if (xintercept2>rxmax) {
+					if (y0 < y1) {
+						// y-increasing line segment...
+						// We know that y0 < rymax and y1 > rymin
+						if (y0 <= rymax) ++numCrosses;
+					}
+					else if (y1 < y0) {
+						// y-decreasing line segment...
+						// We know that y1 < rymax and y0 > rymin
+						if (y1 <= rymax) --numCrosses;
+					}
+				}
+				else {
+					return MathConstants.SHAPE_INTERSECTS;
+				}
+			}
+		}
+		
+		return numCrosses;
+	}
+	
+	/**
+	 * Calculates the number of times the line from (x0,y0) to (x1,y1)
+	 * crosses the up/bottom borders of the ray extending to the right from (px,py).
+	 * +x is returned for a crossing where the Y coordinate is increasing.
+	 * -x is returned for a crossing where the Y coordinate is decreasing.
+	 * x is the number of border crossed by the lines.
+	 * <p>
+	 * The borders of the segment are the two side limits between the cells covered by the segment
+	 * and the adjacents cells (not covered by the segment).
+	 * In the following figure, the point (px;py) is represented.
+	 * The "shadow line" is the projection of (px;py) on the right.
+	 * The red lines represent the up and bottom borders.
+	 * <center>
+	 * <a href="doc-files/crossing_point.png"><img src="doc-files/crossing_point.png" width="300"/></a>
+	 * </center>
+	 * 
+	 * @param crossing is the initial value of the crossing.
+	 * @param px is the reference point to test.
+	 * @param py is the reference point to test.
+	 * @param x0 is the first point of the line.
+	 * @param y0 is the first point of the line.
+	 * @param x1 is the second point of the line.
+	 * @param y1 is the secondpoint of the line.
+	 * @return the crossing, {@link MathConstants#SHAPE_INTERSECTS}
+	 */
+	public static int computeCrossingsFromPoint(
+			int crossing,
+			int px, int py,
+			int x0, int y0,
+			int x1, int y1) {
+		return computeCrossingsFromPoint(crossing, px, py, x0, y0, x1, y1, true, true);
+	}
+
+	/**
+	 * Calculates the number of times the line from (x0,y0) to (x1,y1)
+	 * crosses the up/bottom borders of the ray extending to the right from (px,py).
+	 * +x is returned for a crossing where the Y coordinate is increasing.
+	 * -x is returned for a crossing where the Y coordinate is decreasing.
+	 * x is the number of border crossed by the lines.
+	 * <p>
+	 * The borders of the segment are the two side limits between the cells covered by the segment
+	 * and the adjacents cells (not covered by the segment).
+	 * In the following figure, the point (px;py) is represented.
+	 * The "shadow line" is the projection of (px;py) on the right.
+	 * The red lines represent the up and bottom borders.
+	 * <center>
+	 * <a href="doc-files/crossing_point.png"><img src="doc-files/crossing_point.png" width="300"/></a>
+	 * </center>
+	 * 
+	 * @param crossing is the initial value of the crossing.
+	 * @param px is the reference point to test.
+	 * @param py is the reference point to test.
+	 * @param x0 is the first point of the line.
+	 * @param y0 is the first point of the line.
+	 * @param x1 is the second point of the line.
+	 * @param y1 is the secondpoint of the line.
+	 * @param enableTopBorder indicates if the top border must be enabled in the crossing computation.
+	 * @param enableBottomBorder indicates if the bottom border must be enabled in the crossing computation.
+	 * @return the crossing; or {@link MathConstants#SHAPE_INTERSECTS} if the segment is on the point.
+	 */
+	public static int computeCrossingsFromPoint(
+			int crossing,
+			int px, int py,
+			int x0, int y0,
+			int x1, int y1,
+			boolean enableTopBorder,
+			boolean enableBottomBorder) {
+		// The line is horizontal, impossible to intersect the borders.
+		if (y0==y1) return crossing;
+		// The line does cross the shadow line 
+		if (py<y0 && py<y1) return crossing;
+		if (py>y0 && py>y1) return crossing;
+		// The line is entirely on the left of the point
+		if (px>x0 && px>x1) return crossing;
+
+		// General case: try to detect crossing
+		
+		LineIterator iterator = new LineIterator(x0, y0, x1, y1);
+		
+		Point2i p = new Point2i();
+		while (iterator.hasNext()) {
+			iterator.next(p);
+			if (p.y()==py) {
+				if (p.x()==px)
+					return MathConstants.SHAPE_INTERSECTS;
+				if (p.x()>px) {
+					// Found an intersection
+					int numCrosses = crossing;
+					if (y0<=y1) {
+						if (y0<py && enableTopBorder) ++numCrosses;
+						if (y1>py && enableBottomBorder) ++numCrosses;
+					}
+					else {
+						if (y0>py && enableBottomBorder) --numCrosses;
+						if (y1<py && enableTopBorder) --numCrosses;
+					}
+					return numCrosses;
+				}
+			}
+		}
+		
+		return crossing;
+	}
+	
+	/** Replies if two segments are intersecting.
+	 * This function determines if the segments' lines
+	 * are intersecting because using the pixel-based test.
+	 * This function uses the pixels of the segments that are
+	 * computed according to a Bresenham line algorithm.
+	 * 
+	 * @param x1 is the first point of the first segment.
+	 * @param y1 is the first point of the first segment.
+	 * @param x2 is the second point of the first segment.
+	 * @param y2 is the second point of the first segment.
+	 * @param x3 is the first point of the second segment.
+	 * @param y3 is the first point of the second segment.
+	 * @param x4 is the second point of the second segment.
+	 * @param y4 is the second point of the second segment.
+	 * @return <code>true</code> if the two shapes are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsSegmentSegment(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
+		int side1 = MathUtil.sidePointLine(x1, y1, x2, y2, x3, y3, false);
+		int side2 = MathUtil.sidePointLine(x1, y1, x2, y2, x4, y4, false);
+		if ((side1*side2)<=0) {
+			return intersectsSegmentSegment1(x1, y1, x2, y2, x3, y3, x4, y4, true, true, null)!=0;
+		}
+		return false;
+	}
+	
+	/** Replies if two segments are intersecting pixel per pixel.
+	 * This function does not determine if the segments' lines
+	 * are intersecting because using the pixel-based test.
+	 * This function uses the pixels of the segments that are
+	 * computed according to a Bresenham line algorithm.
+	 * 
+	 * @param x1 is the first point of the first segment.
+	 * @param y1 is the first point of the first segment.
+	 * @param x2 is the second point of the first segment.
+	 * @param y2 is the second point of the first segment.
+	 * @param x3 is the first point of the second segment.
+	 * @param y3 is the first point of the second segment.
+	 * @param x4 is the second point of the second segment.
+	 * @param y4 is the second point of the second segment.
+	 * @param enableThirdPoint indicates if the intersection on the third point is computed.
+	 * @param enableFourthPoint indicates if the intersection on the fourth point is computed.
+	 * @param intersectionPoint are the coordinates of the intersection, if exist.
+	 * @return <code>true</code> if the two segments are intersecting; otherwise
+	 * <code>false</code>
+	 */
+	public static boolean intersectsSegmentSegment(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, boolean enableThirdPoint, boolean enableFourthPoint, Point2D intersectionPoint) {
+		return intersectsSegmentSegment1(x1, y1, x2, y2, x3, y3, x4, y4, enableThirdPoint, enableFourthPoint, intersectionPoint)!=0;
+	}
+	
+	/** Replies if two segments are intersecting pixel per pixel.
+	 * This function does not determine if the segments' lines
+	 * are intersecting because using the pixel-based test.
+	 * This function uses the pixels of the segments that are
+	 * computed according to a Bresenham line algorithm.
+	 * 
+	 * @param x1 is the first point of the first segment.
+	 * @param y1 is the first point of the first segment.
+	 * @param x2 is the second point of the first segment.
+	 * @param y2 is the second point of the first segment.
+	 * @param x3 is the first point of the second segment.
+	 * @param y3 is the first point of the second segment.
+	 * @param x4 is the second point of the second segment.
+	 * @param y4 is the second point of the second segment.
+	 * @param enableThirdPoint indicates if the intersection on the third point is computed.
+	 * @param enableFourthPoint indicates if the intersection on the fourth point is computed.
+	 * @param intersectionPoint are the coordinates of the intersection, if exist.
+	 * @return an integer value; if <code>0</code> the two segments are not intersecting;
+	 * <code>1</code> if the two segments are intersecting and the segment 2 has pixels on both
+	 * sides of the segment 1; <code>2</code> if the segments are intersecting and the segment 2
+	 * is only in one side of the segment 1.
+	 */
+	static int intersectsSegmentSegment1(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, boolean enableThirdPoint, boolean enableFourthPoint, Point2D intersectionPoint) {
+		LineIterator it1;
+		if (x1<x2) {
+			it1 = new LineIterator(x1, y1, x2, y2);
+		}
+		else {
+			it1 = new LineIterator(x2, y2, x1, y1);
+		}
+		LineIterator it2;
+		if (x3<x4) {
+			it2 = new LineIterator(x3, y3, x4, y4);
+		}
+		else {
+			it2 = new LineIterator(x4, y4, x3, y3);
+		}
+
+		if (it1.hasNext() && it2.hasNext()) {
+			Point2i p1 = new Point2i();
+			Point2i p2 = new Point2i();
+
+			boolean isFirstPointOfSecondSegment = true;
+
+			it1.next(p1);
+			it2.next(p2);
+
+			do {
+
+				if (p1.x()<p2.x()) {
+					while (it1.hasNext() && p1.x()<p2.x()) {
+						it1.next(p1);
+					}
+				}
+				else if (p2.x()<p1.x()) {
+					while (it2.hasNext() && p2.x()<p1.x()) {
+						it2.next(p2);
+						isFirstPointOfSecondSegment = false;
+					}
+				}
+
+				int x = p1.x();
+				int min1 = p1.y();
+				int max1 = p1.y();
+				int min2 = isFirstPointOfSecondSegment && !enableThirdPoint ? Integer.MAX_VALUE : p2.y();
+				int max2 = isFirstPointOfSecondSegment && !enableThirdPoint ? Integer.MIN_VALUE : p2.y();
+
+				while (it1.hasNext()) {
+					it1.next(p1);
+					if (p1.x()==x) {
+						if (p1.y()<min1) min1 = p1.y();
+						if (p1.y()>max1) max1 = p1.y();
+					}
+					else {
+						break;
+					}
+				}
+
+				while (it2.hasNext()) {
+					it2.next(p2);
+					isFirstPointOfSecondSegment = false;
+					if (p2.x()==x) {
+						if (p2.y()<min2) min2 = p2.y();
+						if (p2.y()>max2) max2 = p2.y();
+					}
+					else {
+						break;
+					}
+				}
+
+				if (max2>=min1 && max1>=min2) {
+					if (intersectionPoint!=null) {
+						intersectionPoint.set(x, Math.max(min1, min2));
+					}
+					return !isFirstPointOfSecondSegment && (it2.hasNext()) ? 1 : 2;
+				}
+			}
+			while (it1.hasNext() && it2.hasNext());
+
+			if (enableFourthPoint && p1.equals(p2)) {
+				if (intersectionPoint!=null) {
+					intersectionPoint.set(p1);
+				}
+				return !isFirstPointOfSecondSegment && (it2.hasNext()) ? 1 : 2;
+			}
+		}
+
+		return 0;
+	}
+
+	private static final long serialVersionUID = -82425036308183925L;
+
+	/** X-coordinate of the first point. */
+	protected int ax = 0;
+	/** Y-coordinate of the first point. */
+	protected int ay = 0;
+	/** X-coordinate of the second point. */
+	protected int bx = 0;
+	/** Y-coordinate of the second point. */
+	protected int by = 0;
+
+	/**
+	 */
+	public Segment2i() {
+		//
+	}
+
+	/**
+	 * @param a
+	 * @param b
+	 */
+	public Segment2i(Point2D a, Point2D b) {
+		set(a, b);
+	}
+
+	/**
+	 * @param x1
+	 * @param y1
+	 * @param x2
+	 * @param y2
+	 */
+	public Segment2i(int x1, int y1, int x2, int y2) {
+		set(x1, y1, x2, y2);
+	}
+
+	@Override
+	public void clear() {
+		this.ax = this.ay = this.bx = this.by = 0;
+	}
+
+	/**
+	 * Replies if this segment is empty.
+	 * The segment is empty when the two
+	 * points are equal.
+	 * 
+	 * @return <code>true</code> if the two points are
+	 * equal.
+	 */
+	@Override
+	public boolean isEmpty() {
+		return this.ax==this.bx && this.ay==this.by;
+	}
+
+	/** Change the line.
+	 * 
+	 * @param x1
+	 * @param y1
+	 * @param x2
+	 * @param y2
+	 */
+	public void set(int x1, int y1, int x2, int y2) {
+		this.ax = x1;
+		this.ay = y1;
+		this.bx = x2;
+		this.by = y2;
+	}
+
+	/** Change the line.
+	 * 
+	 * @param a
+	 * @param b
+	 */
+	public void set(Point2D a, Point2D b) {
+		this.ax = a.x();
+		this.ay = a.y();
+		this.bx = b.x();
+		this.by = b.y();
+	}
+
+	/** Replies the X of the first point.
+	 * 
+	 * @return the x of the first point.
+	 */
+	public int getX1() {
+		return this.ax;
+	}
+
+	/** Replies the Y of the first point.
+	 * 
+	 * @return the y of the first point.
+	 */
+	public int getY1() {
+		return this.ay;
+	}
+
+	/** Replies the X of the second point.
+	 * 
+	 * @return the x of the second point.
+	 */
+	public int getX2() {
+		return this.bx;
+	}
+
+	/** Replies the Y of the second point.
+	 * 
+	 * @return the y of the second point.
+	 */
+	public int getY2() {
+		return this.by;
+	}
+
+	/** Replies the first point.
+	 * 
+	 * @return the first point.
+	 */
+	public Point2D getP1() {
+		return new Point2i(this.ax, this.ay);
+	}
+
+	/** Replies the second point.
+	 * 
+	 * @return the second point.
+	 */
+	public Point2D getP2() {
+		return new Point2i(this.bx, this.by);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Rectangle2i toBoundingBox() {
+		Rectangle2i r = new Rectangle2i();
+		r.setFromCorners(
+				this.ax,
+				this.ay,
+				this.bx,
+				this.by);
+		return r;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceSquared(Point2D p) {
+		Point2D closestPoint = getClosestPointTo(p);
+		return closestPoint.distanceSquared(p);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceL1(Point2D p) {
+		Point2D closestPoint = getClosestPointTo(p);
+		return closestPoint.distanceL1(p);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float distanceLinf(Point2D p) {
+		Point2D closestPoint = getClosestPointTo(p);
+		return closestPoint.distanceLinf(p);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean contains(int x, int y) {
+		if (x>=this.ax && x<=this.bx && y>=this.ay && y<=this.by) {
+			if (this.ax==this.bx || this.ay==this.by) {
+				return true;
+			}
+
+			int minDist = Integer.MAX_VALUE;
+			int d;
+			int a,b;
+			Point2i p = new Point2i();
+			LineIterator iterator = new LineIterator(this.ax, this.ay, this.bx, this.by);
+			while (iterator.hasNext()) {
+				iterator.next(p);
+				a = Math.abs(x-p.x());
+				b = Math.abs(y-p.y());
+				d = a*a + b*b ;
+				if (d==0) return true;
+				if (d>minDist) {
+					return false;
+				}
+				minDist = d;
+			}
+		}
+		return false;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean contains(Rectangle2i r) {
+		return r.isEmpty() && contains(r.getMinX(), r.getMinY());
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Point2i getClosestPointTo(Point2D p) {
+		return computeClosestPointTo(this.ax, this.ay, this.bx, this.by, p.x(), p.y());
+	}
+
+	/** Replies the closest point in a circle to a point.
+	 * 
+	 * @param ax is the x-coordinate of the first point of the segment
+	 * @param ay is the y-coordinate of the first point of the segment
+	 * @param bx is the x-coordinate of the second point of the segment
+	 * @param by is the y-coordinate of the second point of the segment
+	 * @param px is the x-coordinate of the point
+	 * @param py is the x-coordinate of the point
+	 * @return the closest point in the segment to the point.
+	 */
+	public static Point2i computeClosestPointTo(int ax, int ay, int bx, int by, int px, int py) {
+		// Special case
+		//    0 1 2 3 4 5 6 7 8 9 10
+		// 5) | | | | | | | | | | |X|
+		// 4) | | |O| | | | | |X|X| |
+		// 3) | | | | | | |X|X| | | |
+		// 2) | | | | |X|X| | | | | |
+		// 1) | | |X|X| | | | | | | |
+		// 0) |X|X| | | | | | | | | |
+		//
+		// The closest point to point O is (4;2) even
+		// if the distance is increasing between (2;1)
+		// and (4;2). The algo must take this special
+		// case into account.
+
+		int minDist = Integer.MAX_VALUE;
+		int d;
+		int a,b;
+		boolean oneBestFound = false;
+		Point2i solution = new Point2i(ax, ay);
+		Point2i cp = new Point2i();
+		LineIterator iterator = new LineIterator(ax, ay, bx, by);
+		while (iterator.hasNext()) {
+			iterator.next(cp);
+			a = Math.abs(px-cp.x());
+			b = Math.abs(py-cp.y());
+			d = a*a + b*b ;
+			if (d==0) {
+				// We are sure that the closest point was found
+				return cp;
+			}
+			if (d>minDist) {
+				// here we have found a good candidate, but
+				// but due to the rasterization the optimal solution
+				// may be one pixel after the already found.
+				// See the special case configuration at the beginning
+				// of this function.
+				if (oneBestFound) return solution;
+				oneBestFound = true;
+			}
+			else {
+				minDist = d;
+				solution.set(cp);
+				// here we have found a good candidate, but
+				// but due to the rasterization the optimal solution
+				// may be one pixel after the already found.
+				// See the special case configuration at the beginning
+				// of this function.
+				if (oneBestFound) return solution;
+			}
+		}
+		return solution;
+	}
+
+	@Override
+	public void translate(int dx, int dy) {
+		this.ax += dx;
+		this.ay += dy;
+		this.bx += dx;
+		this.by += dy;
+	}
+
+	@Override
+	public PathIterator2i getPathIterator(Transform2D transform) {
+		return new SegmentPathIterator(
+				this.ax, this.ay, this.bx, this.by,
+				transform);
+	}
+
+	/** Replies an iterator on the points of the segment.
+	 * <p>
+	 * The Bresenham line algorithm is an algorithm which determines which points in 
+	 * an n-dimensional raster should be plotted in order to form a close 
+	 * approximation to a straight line between two given points. It is 
+	 * commonly used to draw lines on a computer screen, as it uses only 
+	 * integer addition, subtraction and bit shifting, all of which are 
+	 * very cheap operations in standard computer architectures. It is one of the 
+	 * earliest algorithms developed in the field of computer graphics. A minor extension 
+	 * to the original algorithm also deals with drawing circles.
+	 * <p>
+	 * While algorithms such as Wu's algorithm are also frequently used in modern 
+	 * computer graphics because they can support antialiasing, the speed and 
+	 * simplicity of Bresenham's line algorithm mean that it is still important. 
+	 * The algorithm is used in hardware such as plotters and in the graphics 
+	 * chips of modern graphics cards. It can also be found in many software 
+	 * graphics libraries. Because the algorithm is very simple, it is often 
+	 * implemented in either the firmware or the hardware of modern graphics cards.
+	 * 
+	 * @return an iterator on the points along the Bresenham line.
+	 */
+	@Override
+	public Iterator<Point2i> getPointIterator() {
+		return new LineIterator(this.ax, this.ay, this.bx, this.by);
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (obj == this) {
+			return true;
+		}
+		if (obj instanceof Segment2i) {
+			Segment2i rr2d = (Segment2i) obj;
+			return ((this.ax == rr2d.getX1()) &&
+					(this.ay == rr2d.getY1()) &&
+					(this.bx == rr2d.getX2()) &&
+					(this.by == rr2d.getY2()));
+		}
+		return false;
+	}
+
+	@Override
+	public int hashCode() {
+		long bits = 1L;
+		bits = 31L * bits + this.ax;
+		bits = 31L * bits + this.ay;
+		bits = 31L * bits + this.bx;
+		bits = 31L * bits + this.by;
+		return (int) (bits ^ (bits >> 32));
+	}
+
+	/** Transform the current segment.
+	 * This function changes the current segment.
+	 * 
+	 * @param transform is the affine transformation to apply.
+	 * @see #createTransformedShape(Transform2D)
+	 */
+	public void transform(Transform2D transform) {
+		Point2i p = new Point2i(this.ax,  this.ay);
+		transform.transform(p);
+		this.ax = p.x();
+		this.ay = p.y();
+		p.set(this.bx, this.by);
+		transform.transform(p);
+		this.bx = p.x();
+		this.by = p.y();
+	}
+
+	@Override
+	public Shape2i createTransformedShape(Transform2D transform) {
+		Point2D p1 = transform.transform(this.ax, this.ay);
+		Point2D p2 = transform.transform(this.bx, this.by);
+		return new Segment2i(p1, p2);
+	}
+
+	@Override
+	public boolean intersects(Rectangle2i s) {
+		return Rectangle2i.intersectsRectangleSegment(
+				s.getMinX(), s.getMinY(),
+				s.getMaxX(), s.getMaxY(),
+				getX1(), getY1(),
+				getX2(), getY2());
+	}
+
+	@Override
+	public boolean intersects(Circle2i s) {
+		return Circle2i.intersectsCircleSegment(
+				s.getX(), s.getY(),
+				s.getRadius(),
+				getX1(), getY1(),
+				getX2(), getY2());
+	}
+
+	@Override
+	public boolean intersects(Segment2i s) {
+		return intersectsSegmentSegment(
+				getX1(), getY1(), getX2(), getY2(),
+				s.getX1(), s.getY1(), s.getX2(), s.getY2());
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder b = new StringBuilder();
+		b.append("["); //$NON-NLS-1$
+		b.append(getX1());
+		b.append(";"); //$NON-NLS-1$
+		b.append(getY1());
+		b.append("|"); //$NON-NLS-1$
+		b.append(getX2());
+		b.append(";"); //$NON-NLS-1$
+		b.append(getY2());
+		b.append("]"); //$NON-NLS-1$
+		return b.toString();
+	}
+
+	/** Iterator on the path elements of the segment.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class SegmentPathIterator implements PathIterator2i {
+
+		private final Point2D p1 = new Point2i();
+		private final Point2D p2 = new Point2i();
+		private final Transform2D transform;
+		private final int x1;
+		private final int y1;
+		private final int x2;
+		private final int y2;
+		private int index = 0;
+
+		/**
+		 * @param x1
+		 * @param y1
+		 * @param x2
+		 * @param y2
+		 * @param transform
+		 */
+		public SegmentPathIterator(int x1, int y1, int x2, int y2, Transform2D transform) {
+			this.transform = transform;
+			this.x1 = x1;
+			this.y1 = y1;
+			this.x2 = x2;
+			this.y2 = y2;
+			if (this.x1==this.x2 && this.y1==this.y2) {
+				this.index = 2;
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			return this.index<=1;
+		}
+
+		@Override
+		public PathElement2i next() {
+			if (this.index>1) throw new NoSuchElementException();
+			int idx = this.index;
+			++this.index;
+			switch(idx) {
+			case 0:
+				this.p2.set(this.x1, this.y1);
+				if (this.transform!=null) {
+					this.transform.transform(this.p2);
+				}
+				return new PathElement2i.MovePathElement2i(
+						this.p2.x(), this.p2.y());
+			case 1:
+				this.p1.set(this.p2);
+				this.p2.set(this.x2, this.y2);
+				if (this.transform!=null) {
+					this.transform.transform(this.p2);
+				}
+				return new PathElement2i.LinePathElement2i(
+						this.p1.x(), this.p1.y(),
+						this.p2.x(), this.p2.y());
+			default:
+				throw new NoSuchElementException();
+			}
+		}
+
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public PathWindingRule getWindingRule() {
+			return PathWindingRule.NON_ZERO;
+		}
+
+	}
+
+	/** The Bresenham line algorithm is an algorithm which determines which points in 
+	 * an n-dimensional raster should be plotted in order to form a close 
+	 * approximation to a straight line between two given points. It is 
+	 * commonly used to draw lines on a computer screen, as it uses only 
+	 * integer addition, subtraction and bit shifting, all of which are 
+	 * very cheap operations in standard computer architectures. It is one of the 
+	 * earliest algorithms developed in the field of computer graphics. A minor extension 
+	 * to the original algorithm also deals with drawing circles.
+	 * <p>
+	 * While algorithms such as Wu's algorithm are also frequently used in modern 
+	 * computer graphics because they can support antialiasing, the speed and 
+	 * simplicity of Bresenham's line algorithm mean that it is still important. 
+	 * The algorithm is used in hardware such as plotters and in the graphics 
+	 * chips of modern graphics cards. It can also be found in many software 
+	 * graphics libraries. Because the algorithm is very simple, it is often 
+	 * implemented in either the firmware or the hardware of modern graphics cards.
+	 * 
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	static class LineIterator implements Iterator<Point2i> {
+
+		private final boolean steep;
+		private final int ystep;
+		private final int xstep;
+		private final int deltax;
+		private final int deltay;
+		private final int x1;
+		private int y, x;
+		private int error;
+
+		/**
+		 * @param x0 is the x-coordinate of the first point of the Bresenham line.
+		 * @param y0 is the y-coordinate of the first point of the Bresenham line.
+		 * @param x1 is the x-coordinate of the last point of the Bresenham line.
+		 * @param y1 is the y-coordinate of the last point of the Bresenham line.
+		 */
+		public LineIterator(int x0, int y0, int x1, int y1) {
+			int _x0 = x0;
+			int _y0 = y0;
+			int _x1 = x1;
+			int _y1 = y1;
+
+			this.steep = Math.abs(_y1 - _y0) > Math.abs(_x1 - _x0);
+
+			int swapv;
+			if (this.steep) {
+				//swap(x0, y0);
+				swapv = _x0;
+				_x0 = _y0;
+				_y0 = swapv;
+				//swap(x1, y1);
+				swapv = _x1;
+				_x1 = _y1;
+				_y1 = swapv;
+			}
+			/*if (_x0 > _x1) {
+				//swap(x0, x1);
+				swapv = _x0;
+				_x0 = _x1;
+				_x1 = swapv;
+				//swap(y0, y1);
+				swapv = _y0;
+				_y0 = _y1;
+				_y1 = swapv;
+			}*/
+
+			this.deltax = Math.abs(_x1 - _x0);
+			this.deltay = Math.abs(_y1 - _y0);
+			this.error = this.deltax / 2;
+			this.y = _y0;
+
+			if (_x0 < _x1) this.xstep = 1;
+			else this.xstep = -1;
+
+			if (_y0 < _y1) this.ystep = 1;
+			else this.ystep = -1;
+
+			this.x1 = _x1;
+			this.x = _x0;
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public boolean hasNext() {
+			return ((this.xstep>0) && (this.x <= this.x1))
+					||((this.xstep<0) && (this.x1 <= this.x));
+		}
+
+		/** Replies the next point in the given parameter.
+		 * 
+		 * @param p
+		 */
+		public void next(Point2i p) {
+			if (this.steep) {
+				p.set(this.y, this.x);
+			}
+			else {
+				p.set(this.x, this.y);
+			}
+
+			this.error = this.error - this.deltay;
+
+			if (this.error < 0) {
+				this.y = this.y + this.ystep;
+				this.error = this.error + this.deltax;
+			}
+
+			this.x += this.xstep;
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public Point2i next() {
+			Point2i p = new Point2i();
+			next(p);
+			return p;
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+	} // class LineIterator
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Shape2i.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Shape2i.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Shape2i.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,158 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import java.util.Iterator;
+
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Shape2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+/** 2D shape with integer  points.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Shape2i extends Shape2D {
+
+	/** Replies the bounding box of this shape.
+	 * 
+	 * @return the bounding box of this shape.
+	 */
+	public abstract Rectangle2i toBoundingBox();
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Shape2i clone();
+	
+	/** Replies the minimal distance from this shape to the given point.
+	 * 
+	 * @param p
+	 * @return the minimal distance between this shape and the point.
+	 */
+	public float distance(Point2D p);
+
+	/** Replies the squared value of the minimal distance from this shape to the given point.
+	 * 
+	 * @param p
+	 * @return squared value of the minimal distance between this shape and the point.
+	 */
+	public float distanceSquared(Point2D p);
+
+	/**
+	 * Computes the L-1 (Manhattan) distance between this shape and
+	 * point p1.  The L-1 distance is equal to abs(x1-x2) + abs(y1-y2).
+	 * @param p the point
+	 * @return the distance.
+	 */
+	public float distanceL1(Point2D p);
+
+	/**
+	 * Computes the L-infinite distance between this shape and
+	 * point p1.  The L-infinite distance is equal to 
+	 * MAX[abs(x1-x2), abs(y1-y2)]. 
+	 * @param p the point
+	 * @return the distance.
+	 */
+	public float distanceLinf(Point2D p);
+
+	/** Replies if this shape is intersecting the given rectangle.
+	 * 
+	 * @param s
+	 * @return <code>true</code> if this shape is intersecting the given shape;
+	 * <code>false</code> if there is no intersection.
+	 */
+	public boolean intersects(Rectangle2i s);
+
+	/** Replies if this shape is intersecting the given circle.
+	 * 
+	 * @param s
+	 * @return <code>true</code> if this shape is intersecting the given shape;
+	 * <code>false</code> if there is no intersection.
+	 */
+	public boolean intersects(Circle2i s);
+
+	/** Replies if this shape is intersecting the given segment.
+	 * 
+	 * @param s
+	 * @return <code>true</code> if this shape is intersecting the given shape;
+	 * <code>false</code> if there is no intersection.
+	 */
+	public boolean intersects(Segment2i s);
+
+	/** Replies the elements of the paths.
+	 * 
+	 * @param transform is the transformation to apply to the path.
+	 * @return the elements of the path.
+	 */
+	public PathIterator2i getPathIterator(Transform2D transform);
+
+	/** Replies the elements of the paths.
+	 * 
+	 * @return the elements of the path.
+	 */
+	public PathIterator2i getPathIterator();
+
+	/** Replies an iterator on the points covered by this shape.
+	 * <p>
+	 * The implementation of the iterator depends on the shape type.
+	 * There is no warranty about the order of the points.
+	 * 
+	 * @return an iterator on the points.
+	 */
+	public Iterator<Point2i> getPointIterator();
+	
+	/** Apply the transformation to the shape and reply the result.
+	 * This function does not change the current shape.
+	 * 
+	 * @param transform is the transformation to apply to the shape.
+	 * @return the result of the transformation.
+	 */
+	public Shape2i createTransformedShape(Transform2D transform);
+	
+	/** Translate the shape.
+	 * 
+	 * @param dx
+	 * @param dy
+	 */
+	public void translate(int dx, int dy); 
+
+	/** Replies if the given point is inside this shape.
+	 * 
+	 * @param x
+	 * @param y
+	 * @return <code>true</code> if the given point is inside this
+	 * shape, otherwise <code>false</code>.
+	 */
+	public boolean contains(int x, int y);
+	
+	/** Replies if the given rectangle is inside this shape.
+	 * 
+	 * @param r
+	 * @return <code>true</code> if the given rectangle is inside this
+	 * shape, otherwise <code>false</code>.
+	 */
+	public boolean contains(Rectangle2i r);
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Tuple2i.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Tuple2i.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Tuple2i.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,617 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import org.arakhne.afc.math.generic.Tuple2D;
+
+/** 2D tuple with 2 integers.
+ * 
+ * @param <T> is the implementation type of the tuple.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Tuple2i<T extends Tuple2D<? super T>> implements Tuple2D<T> {
+
+	private static final long serialVersionUID = -7779997414431055683L;
+	
+	/** x coordinate.
+	 */
+	protected int x;
+
+	/** y coordinate.
+	 */
+	protected int y;
+
+	/**
+	 */
+	public Tuple2i() {
+		this.x = this.y = 0;
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Tuple2i(Tuple2i<?> tuple) {
+		this.x = tuple.x;
+		this.y = tuple.y;
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Tuple2i(Tuple2D<?> tuple) {
+		this.x = (int)tuple.getX();
+		this.y = (int)tuple.getY();
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Tuple2i(int[] tuple) {
+		this.x = tuple[0];
+		this.y = tuple[1];
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Tuple2i(float[] tuple) {
+		this.x = (int)tuple[0];
+		this.y = (int)tuple[1];
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Tuple2i(int x, int y) {
+		this.x = x;
+		this.y = y;
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Tuple2i(float x, float y) {
+		this.x = (int)x;
+		this.y = (int)y;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public T clone() {
+		try {
+			return (T)super.clone();
+		}
+		catch(CloneNotSupportedException e) {
+			throw new Error(e);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void absolute() {
+		this.x = Math.abs(this.x);
+		this.y = Math.abs(this.y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void absolute(T t) {
+		t.set(Math.abs(this.x), Math.abs(this.y));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void add(int x, int y) {
+		this.x += x;
+		this.y += y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void add(float x, float y) {
+		this.x += x;
+		this.y += y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addX(int x) {
+		this.x += x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addX(float x) {
+		this.x += x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addY(int y) {
+		this.y += y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addY(float y) {
+		this.y += y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clamp(int min, int max) {
+		if (this.x < min) this.x = min;
+		else if (this.x > max) this.x = max;
+		if (this.y < min) this.y = min;
+		else if (this.y > max) this.y = max;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clamp(float min, float max) {
+		clamp((int)min, (int)max);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMin(int min) {
+		if (this.x < min) this.x = min;
+		if (this.y < min) this.y = min;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMin(float min) {
+		clampMin((int)min);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMax(int max) {
+		if (this.x > max) this.x = max;
+		if (this.y > max) this.y = max;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMax(float max) {
+		clampMax((int)max);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clamp(int min, int max, T t) {
+		if (this.x < min) t.setX(min);
+		else if (this.x > max) t.setX(max);
+		if (this.y < min) t.setY(min);
+		else if (this.y > max) t.setY(max);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clamp(float min, float max, T t) {
+		clamp((int)min, (int)max, t);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMin(int min, T t) {
+		if (this.x < min) t.setX(min);
+		if (this.y < min) t.setY(min);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMin(float min, T t) {
+		clampMin((int)min, t);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMax(int max, T t) {
+		if (this.x > max) t.setX(max);
+		if (this.y > max) t.setY(max);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMax(float max, T t) {
+		clampMax((int)max, t);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void get(T t) {
+		t.set(this.x, this.y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void get(int[] t) {
+		t[0] = this.x;
+		t[1] = this.y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void get(float[] t) {
+		t[0] = this.x;
+		t[1] = this.y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void negate(T t1) {
+		this.x = -t1.x();
+		this.y = -t1.y();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void negate() {
+		this.x = -this.x;
+		this.y = -this.y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(int s, T t1) {
+		this.x = (int)(s * t1.getX());
+		this.y = (int)(s * t1.getY());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(float s, T t1) {
+		this.x = (int)(s * t1.getX());
+		this.y = (int)(s * t1.getY());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(int s) {
+		this.x = s * this.x;
+		this.y = s * this.y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(float s) {
+		this.x = (int)(s * this.x);
+		this.y = (int)(s * this.y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(Tuple2D<?> t1) {
+		this.x = t1.x();
+		this.y = t1.y();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(int x, int y) {
+		this.x = x;
+		this.y = y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(float x, float y) {
+		this.x = (int)x;
+		this.y = (int)y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(int[] t) {
+		this.x = t[0];
+		this.y = t[1];
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(float[] t) {
+		this.x = (int)t[0];
+		this.y = (int)t[1];
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float getX() {
+		return this.x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int x() {
+		return this.x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setX(int x) {
+		this.x = x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setX(float x) {
+		this.x = (int)x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float getY() {
+		return this.y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int y() {
+		return this.y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setY(int y) {
+		this.y = y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setY(float y) {
+		this.y = (int)y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void sub(int x, int y) {
+		this.x -= x;
+		this.y -= y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subX(int x) {
+		this.x -= x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subY(int y) {
+		this.y -= y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void sub(float x, float y) {
+		this.x -= x;
+		this.y -= y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subX(float x) {
+		this.x -= x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subY(float y) {
+		this.y -= y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void interpolate(T t1, T t2, float alpha) {
+		this.x = (int)((1f-alpha)*t1.getX() + alpha*t2.getX());
+		this.y = (int)((1f-alpha)*t1.getY() + alpha*t2.getY());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void interpolate(T t1, float alpha) {
+		this.x = (int)((1f-alpha)*this.x + alpha*t1.getX());
+		this.y = (int)((1f-alpha)*this.y + alpha*t1.getY());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(Tuple2D<?> t1) {
+		try {
+			return(this.x == t1.x() && this.y == t1.y());
+		}
+		catch (NullPointerException e2) {
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public boolean equals(Object t1) {
+		try {
+			T t2 = (T) t1;
+			return(this.x == t2.x() && this.y == t2.y());
+		}
+		catch(AssertionError e) {
+			throw e;
+		}
+		catch (Throwable e2) {
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean epsilonEquals(T t1, float epsilon) {
+		float diff;
+
+		diff = this.x - t1.getX();
+		if(Float.isNaN(diff)) return false;
+		if((diff<0?-diff:diff) > epsilon) return false;
+
+		diff = this.y - t1.getY();
+		if(Float.isNaN(diff)) return false;
+		if((diff<0?-diff:diff) > epsilon) return false;
+
+		return true;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int hashCode() {
+		int bits = 1;
+		bits = 31 * bits + this.x;
+		bits = 31 * bits + this.y;
+		return bits ^ (bits >> 32);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		return "(" //$NON-NLS-1$
+				+this.x
+				+";" //$NON-NLS-1$
+				+this.y
+				+")"; //$NON-NLS-1$
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Tuple2iComparator.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Tuple2iComparator.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Tuple2iComparator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,55 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import java.util.Comparator;
+
+/**
+ * Comparator of Tuple2i.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Tuple2iComparator implements Comparator<Tuple2i<?>> {
+	
+	/**
+	 */
+	public Tuple2iComparator() {
+		//
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int compare(Tuple2i<?> o1, Tuple2i<?> o2) {
+		if (o1==o2) return 0;
+		if (o1==null) return Integer.MIN_VALUE;
+		if (o2==null) return Integer.MAX_VALUE;
+		int cmp = o1.x() - o2.x();
+		if (cmp!=0) return cmp;
+		return o1.y() - o2.y();
+	}
+		
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Vector2i.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Vector2i.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object2d/Vector2i.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,279 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import org.arakhne.afc.math.MathUtil;
+import org.arakhne.afc.math.continous.object2d.Vector2f;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Tuple2D;
+import org.arakhne.afc.math.generic.Vector2D;
+
+/** 2D Vector with 2 integers.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Vector2i extends Tuple2i<Vector2D> implements Vector2D {
+
+	private static final long serialVersionUID = -4528846627184370639L;
+
+	/**
+	 */
+	public Vector2i() {
+		//
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Vector2i(Tuple2D<?> tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Vector2i(int[] tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Vector2i(float[] tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Vector2i(int x, int y) {
+		super(x,y);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Vector2i(float x, float y) {
+		super(x,y);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Vector2i(double x, double y) {
+		super((float)x,(float)y);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 */
+	public Vector2i(long x, long y) {
+		super(x,y);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Vector2i clone() {
+		return (Vector2i)super.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float angle(Vector2D v1) {
+		double vDot = dot(v1) / ( length()*v1.length() );
+		if( vDot < -1.) vDot = -1.;
+		if( vDot >  1.) vDot =  1.;
+		return((float) (Math.acos( vDot )));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float dot(Vector2D v1) {
+	      return (this.x*v1.getX() + this.y*v1.getY());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float length() {
+        return (float) Math.sqrt(this.x*this.x + this.y*this.y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float lengthSquared() {
+        return (this.x*this.x + this.y*this.y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void normalize(Vector2D v1) {
+        float norm;
+        norm = (float) (1./Math.sqrt(v1.getX()*v1.getX() + v1.getY()*v1.getY()));
+        this.x = (int)(v1.getX()*norm);
+        this.y = (int)(v1.getY()*norm);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void normalize() {
+        float norm;
+        norm = (float)(1./Math.sqrt(this.x*this.x + this.y*this.y));
+        this.x *= norm;
+        this.y *= norm;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float signedAngle(Vector2D v) {
+		assert(v!=null);
+		Vector2f a = new Vector2f(this);
+		if (a.length()==0) return Float.NaN;
+		Vector2f b = new Vector2f(v);
+		if (b.length()==0) return Float.NaN;
+		a.normalize();
+		b.normalize();
+		
+		float cos = a.getX() * b.getX() + a.getY() * b.getY();
+		// A x B = |A|.|B|.sin(theta).N = sin(theta) (where N is the unit vector perpendicular to plane AB)
+		float sin = a.getX()*b.getY() - a.getY()*b.getX();
+		
+		float angle = (float)Math.atan2(sin, cos);
+
+		return angle;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void turnVector(float angle) {
+		float sin = (float)Math.sin(angle);
+		float cos = (float)Math.cos(angle);
+		float x =  cos * getX() + sin * getY(); 
+		float y = -sin * getX() + cos * getY();
+		set(x,y);
+	}
+
+	@Override
+	public void add(Vector2D t1, Vector2D t2) {
+		this.x = (int)(t1.getX() + t2.getX());
+		this.y = (int)(t1.getY() + t2.getY());
+	}
+
+	@Override
+	public void add(Vector2D t1) {
+		this.x = (int)(this.x + t1.getX());
+		this.y = (int)(this.y + t1.getY());
+	}
+
+	@Override
+	public void scaleAdd(int s, Vector2D t1, Vector2D t2) {
+		this.x = (int)(s * t1.getX() + t2.getX());
+		this.y = (int)(s * t1.getY() + t2.getY());
+	}
+
+	@Override
+	public void scaleAdd(float s, Vector2D t1, Vector2D t2) {
+		this.x = (int)(s * t1.getX() + t2.getX());
+		this.y = (int)(s * t1.getY() + t2.getY());
+	}
+
+	@Override
+	public void scaleAdd(int s, Vector2D t1) {
+		this.x = (int)(s * this.x + t1.getX());
+		this.y = (int)(s * this.y + t1.getY());
+	}
+
+	@Override
+	public void scaleAdd(float s, Vector2D t1) {
+		this.x = (int)(s * this.x + t1.getX());
+		this.y = (int)(s * this.y + t1.getY());
+	}
+
+	@Override
+	public void sub(Vector2D t1, Vector2D t2) {
+		this.x = (int)(t1.getX() - t2.getX());
+		this.y = (int)(t1.getY() - t2.getY());
+	}
+
+	@Override
+	public void sub(Point2D t1, Point2D t2) {
+		this.x = (int)(t1.getX() - t2.getX());
+		this.y = (int)(t1.getY() - t2.getY());
+	}
+
+	@Override
+	public void sub(Vector2D t1) {
+		this.x = (int)(this.x - t1.getX());
+		this.y = (int)(this.y - t1.getY());
+	}
+
+	/** Replies the orientation vector, which is corresponding
+	 * to the given angle on a trigonometric circle.
+	 * 
+	 * @param angle is the angle in radians to translate.
+	 * @return the orientation vector which is corresponding to the given angle.
+	 */
+	public static Vector2i toOrientationVector(float angle) {
+		return new Vector2i(
+				(float)Math.cos(angle),
+				(float)Math.sin(angle));
+	}
+	
+	@Override
+	public float getOrientationAngle() {
+		float angle = (float)Math.acos(getX());
+		if (getY()<0f) angle = -angle;
+		return MathUtil.clampRadian(angle);
+	}
+
+	@Override
+	public void perpendicularize() {
+		// Based on the cross product in 3D of (vx,vy,0)x(0,0,1), right-handed
+		//set(y(), -x());
+		// Based on the cross product in 3D of (vx,vy,0)x(0,0,1), left-handed
+		set(-y(), x());
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/Point3i.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/Point3i.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/Point3i.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,255 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object3d;
+
+import org.arakhne.afc.math.MathUtil;
+import org.arakhne.afc.math.generic.Point3D;
+import org.arakhne.afc.math.generic.Tuple3D;
+import org.arakhne.afc.math.generic.Vector3D;
+
+/** 3D Point with 3 integers.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Point3i extends Tuple3i<Point3D> implements Point3D {
+
+	private static final long serialVersionUID = -1506750779625667216L;
+
+	/**
+	 */
+	public Point3i() {
+		//
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Point3i(Tuple3D<?> tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Point3i(int[] tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Point3i(float[] tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Point3i(int x, int y, int z) {
+		super(x,y,z);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Point3i(float x, float y, float z) {
+		super(x,y,z);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Point3i(double x, double y, double z) {
+		super((float)x,(float)y,(float)z);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Point3i(long x, long y, long z) {
+		super(x,y,z);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Point3i clone() {
+		return (Point3i)super.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int distanceSquared(Point3D p1) {
+	      float dx, dy, dz;
+	      dx = this.x-p1.getX();  
+	      dy = this.y-p1.getY();
+	      dz = this.z-p1.getZ();
+	      return (int)(dx*dx+dy*dy+dz*dz);
+	}
+
+	@Override
+	public float getDistanceSquared(Point3D p1) {
+	      float dx, dy, dz;
+	      dx = this.x-p1.getX();  
+	      dy = this.y-p1.getY();
+	      dz = this.z-p1.getZ();
+	      return (dx*dx+dy*dy+dz*dz);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int distance(Point3D p1) {
+	      float  dx, dy, dz;
+	      dx = this.x-p1.getX();  
+	      dy = this.y-p1.getY();
+	      dz = this.z-p1.getZ();
+	      return (int)Math.sqrt(dx*dx+dy*dy+dz*dz);
+	}
+
+	@Override
+	public float getDistance(Point3D p1) {
+	      float  dx, dy, dz;
+	      dx = this.x-p1.getX();  
+	      dy = this.y-p1.getY();
+	      dz = this.z-p1.getZ();
+	      return (float)Math.sqrt(dx*dx+dy*dy+dz*dz);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int distanceL1(Point3D p1) {
+	      return (int)(Math.abs(this.x-p1.getX()) + Math.abs(this.y-p1.getY())  + Math.abs(this.z-p1.getZ()));
+	}
+
+	@Override
+	public float getDistanceL1(Point3D p1) {
+	      return Math.abs(this.x-p1.getX()) + Math.abs(this.y-p1.getY())  + Math.abs(this.z-p1.getZ());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int distanceLinf(Point3D p1) {
+	      return (int)(MathUtil.max( Math.abs(this.x-p1.getX()), Math.abs(this.y-p1.getY()), Math.abs(this.z-p1.getZ())));
+	}
+
+	@Override
+	public float getDistanceLinf(Point3D p1) {
+	      return (MathUtil.max( Math.abs(this.x-p1.getX()), Math.abs(this.y-p1.getY()), Math.abs(this.z-p1.getZ())));
+	}
+
+	@Override
+	public void add(Point3D t1, Vector3D t2) {
+		this.x = (int)(t1.getX() + t2.getX());
+		this.y = (int)(t1.getY() + t2.getY());
+		this.z = (int)(t1.getZ() + t2.getZ());
+	}
+
+	@Override
+	public void add(Vector3D t1, Point3D t2) {
+		this.x = (int)(t1.getX() + t2.getX());
+		this.y = (int)(t1.getY() + t2.getY());
+		this.z = (int)(t1.getZ() + t2.getZ());
+	}
+
+	@Override
+	public void add(Vector3D t1) {
+		this.x = (int)(this.x + t1.getX());
+		this.y = (int)(this.y + t1.getY());
+		this.z = (int)(this.z + t1.getZ());
+	}
+
+	@Override
+	public void scaleAdd(int s, Vector3D t1, Point3D t2) {
+		this.x = (int)(s * t1.getX() + t2.getX());
+		this.y = (int)(s * t1.getY() + t2.getY());
+		this.z = (int)(s * t1.getZ() + t2.getZ());
+	}
+
+	@Override
+	public void scaleAdd(float s, Vector3D t1, Point3D t2) {
+		this.x = (int)(s * t1.getX() + t2.getX());
+		this.y = (int)(s * t1.getY() + t2.getY());
+		this.z = (int)(s * t1.getZ() + t2.getZ());
+	}
+
+	@Override
+	public void scaleAdd(int s, Point3D t1, Vector3D t2) {
+		this.x = (int)(s * t1.getX() + t2.getX());
+		this.y = (int)(s * t1.getY() + t2.getY());
+		this.z = (int)(s * t1.getZ() + t2.getZ());
+	}
+
+	@Override
+	public void scaleAdd(float s, Point3D t1, Vector3D t2) {
+		this.x = (int)(s * t1.getX() + t2.getX());
+		this.y = (int)(s * t1.getY() + t2.getY());
+		this.z = (int)(s * t1.getZ() + t2.getZ());
+	}
+
+	@Override
+	public void scaleAdd(int s, Vector3D t1) {
+		this.x = (int)(s * this.x + t1.getX());
+		this.y = (int)(s * this.y + t1.getY());
+		this.z = (int)(s * this.z + t1.getZ());
+	}
+
+	@Override
+	public void scaleAdd(float s, Vector3D t1) {
+		this.x = (int)(s * this.x + t1.getX());
+		this.y = (int)(s * this.y + t1.getY());
+		this.z = (int)(s * this.z + t1.getZ());
+	}
+
+	@Override
+	public void sub(Point3D t1, Vector3D t2) {
+		this.x = (int)(t1.getX() - t1.getX());
+		this.y = (int)(t1.getY() - t1.getY());
+		this.z = (int)(t1.getZ() - t1.getZ());
+	}
+
+	@Override
+	public void sub(Vector3D t1) {
+		this.x = (int)(this.x - t1.getX());
+		this.y = (int)(this.y - t1.getY());
+		this.z = (int)(this.z - t1.getZ());
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/Tuple3i.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/Tuple3i.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/Tuple3i.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,728 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object3d;
+
+import org.arakhne.afc.math.generic.Tuple3D;
+
+/** 3D tuple with 3 integers.
+ * 
+ * @param <T> is the implementation type of the tuple.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Tuple3i<T extends Tuple3D<? super T>> implements Tuple3D<T> {
+
+	private static final long serialVersionUID = 358537735186816489L;
+
+	/** x coordinate.
+	 */
+	protected int x;
+
+	/** y coordinate.
+	 */
+	protected int y;
+
+	/** z coordinate.
+	 */
+	protected int z;
+
+	/**
+	 */
+	public Tuple3i() {
+		this.x = this.y = this.z = 0;
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Tuple3i(Tuple3i<?> tuple) {
+		this.x = tuple.x;
+		this.y = tuple.y;
+		this.z = tuple.z;
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Tuple3i(Tuple3D<?> tuple) {
+		this.x = (int)tuple.getX();
+		this.y = (int)tuple.getY();
+		this.z = (int)tuple.getZ();
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Tuple3i(int[] tuple) {
+		this.x = tuple[0];
+		this.y = tuple[1];
+		this.z = tuple[2];
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Tuple3i(float[] tuple) {
+		this.x = (int)tuple[0];
+		this.y = (int)tuple[1];
+		this.z = (int)tuple[2];
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Tuple3i(int x, int y, int z) {
+		this.x = x;
+		this.y = y;
+		this.z = z;
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Tuple3i(float x, float y, float z) {
+		this.x = (int)x;
+		this.y = (int)y;
+		this.z = (int)z;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public T clone() {
+		try {
+			return (T)super.clone();
+		}
+		catch(CloneNotSupportedException e) {
+			throw new Error(e);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void absolute() {
+		this.x = Math.abs(this.x);
+		this.y = Math.abs(this.y);
+		this.z = Math.abs(this.z);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void absolute(T t) {
+		t.set(Math.abs(this.x), Math.abs(this.y), Math.abs(this.z));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void add(int x, int y, int z) {
+		this.x += x;
+		this.y += y;
+		this.z += z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void add(float x, float y, float z) {
+		this.x += x;
+		this.y += y;
+		this.z += z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addX(int x) {
+		this.x += x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addX(float x) {
+		this.x += x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addY(int y) {
+		this.y += y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addY(float y) {
+		this.y += y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addZ(int z) {
+		this.z += z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addZ(float z) {
+		this.z += z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clamp(int min, int max) {
+		if (this.x < min) this.x = min;
+		else if (this.x > max) this.x = max;
+		if (this.y < min) this.y = min;
+		else if (this.y > max) this.y = max;
+		if (this.z < min) this.z = min;
+		else if (this.z > max) this.z = max;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clamp(float min, float max) {
+		clamp((int)min, (int)max);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMin(int min) {
+		if (this.x < min) this.x = min;
+		if (this.y < min) this.y = min;
+		if (this.z < min) this.z = min;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMin(float min) {
+		clampMin((int)min);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMax(int max) {
+		if (this.x > max) this.x = max;
+		if (this.y > max) this.y = max;
+		if (this.z > max) this.z = max;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMax(float max) {
+		clampMax((int)max);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clamp(int min, int max, T t) {
+		if (this.x < min) t.setX(min);
+		else if (this.x > max) t.setX(max);
+		if (this.y < min) t.setY(min);
+		else if (this.y > max) t.setY(max);
+		if (this.z < min) t.setZ(min);
+		else if (this.z > max) t.setZ(max);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clamp(float min, float max, T t) {
+		clamp((int)min, (int)max, t);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMin(int min, T t) {
+		if (this.x < min) t.setX(min);
+		if (this.y < min) t.setY(min);
+		if (this.z < min) t.setZ(min);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMin(float min, T t) {
+		clampMin((int)min, t);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMax(int max, T t) {
+		if (this.x > max) t.setX(max);
+		if (this.y > max) t.setY(max);
+		if (this.z > max) t.setZ(max);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clampMax(float max, T t) {
+		clampMax((int)max, t);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void get(T t) {
+		t.set(this.x, this.y, this.z);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void get(int[] t) {
+		t[0] = this.x;
+		t[1] = this.y;
+		t[2] = this.z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void get(float[] t) {
+		t[0] = this.x;
+		t[1] = this.y;
+		t[2] = this.z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void negate(T t1) {
+		this.x = -t1.x();
+		this.y = -t1.y();
+		this.z = -t1.z();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void negate() {
+		this.x = -this.x;
+		this.y = -this.y;
+		this.z = -this.z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(int s, T t1) {
+		this.x = (int)(s * t1.getX());
+		this.y = (int)(s * t1.getY());
+		this.z = (int)(s * t1.getZ());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(float s, T t1) {
+		this.x = (int)(s * t1.getX());
+		this.y = (int)(s * t1.getY());
+		this.z = (int)(s * t1.getZ());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(int s) {
+		this.x = s * this.x;
+		this.y = s * this.y;
+		this.z = s * this.z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(float s) {
+		this.x = (int)(s * this.x);
+		this.y = (int)(s * this.y);
+		this.z = (int)(s * this.z);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(Tuple3D<?> t1) {
+		this.x = t1.x();
+		this.y = t1.y();
+		this.z = t1.z();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(int x, int y, int z) {
+		this.x = x;
+		this.y = y;
+		this.z = z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(float x, float y, float z) {
+		this.x = (int)x;
+		this.y = (int)y;
+		this.z = (int)z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(int[] t) {
+		this.x = t[0];
+		this.y = t[1];
+		this.z = t[2];
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void set(float[] t) {
+		this.x = (int)t[0];
+		this.y = (int)t[1];
+		this.z = (int)t[2];
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float getX() {
+		return this.x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int x() {
+		return this.x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setX(int x) {
+		this.x = x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setX(float x) {
+		this.x = (int)x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float getY() {
+		return this.y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int y() {
+		return this.y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setY(int y) {
+		this.y = y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setY(float y) {
+		this.y = (int)y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float getZ() {
+		return this.z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int z() {
+		return this.z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setZ(int z) {
+		this.z = z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setZ(float z) {
+		this.z = (int)z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void sub(int x, int y, int z) {
+		this.x -= x;
+		this.y -= y;
+		this.z -= z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subX(int x) {
+		this.x -= x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subY(int y) {
+		this.y -= y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subZ(int z) {
+		this.z -= z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void sub(float x, float y, float z) {
+		this.x -= x;
+		this.y -= y;
+		this.z -= z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subX(float x) {
+		this.x -= x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subY(float y) {
+		this.y -= y;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void subZ(float z) {
+		this.z -= z;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void interpolate(T t1, T t2, float alpha) {
+		this.x = (int)((1f-alpha)*t1.getX() + alpha*t2.getX());
+		this.y = (int)((1f-alpha)*t1.getY() + alpha*t2.getY());
+		this.z = (int)((1f-alpha)*t1.getZ() + alpha*t2.getZ());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void interpolate(T t1, float alpha) {
+		this.x = (int)((1f-alpha)*this.x + alpha*t1.getX());
+		this.y = (int)((1f-alpha)*this.y + alpha*t1.getY());
+		this.z = (int)((1f-alpha)*this.z + alpha*t1.getZ());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(Tuple3D<?> t1) {
+		try {
+			return(this.x == t1.x() && this.y == t1.y() && this.z == t1.z());
+		}
+		catch (NullPointerException e2) {
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public boolean equals(Object t1) {
+		try {
+			T t2 = (T) t1;
+			return(this.x == t2.x() && this.y == t2.y() && this.z == t2.z());
+		}
+		catch(AssertionError e) {
+			throw e;
+		}
+		catch (Throwable e2) {
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean epsilonEquals(T t1, float epsilon) {
+		float diff;
+
+		diff = this.x - t1.getX();
+		if(Float.isNaN(diff)) return false;
+		if((diff<0?-diff:diff) > epsilon) return false;
+
+		diff = this.y - t1.getY();
+		if(Float.isNaN(diff)) return false;
+		if((diff<0?-diff:diff) > epsilon) return false;
+		
+		diff = this.z - t1.getZ();
+		if(Float.isNaN(diff)) return false;
+		if((diff<0?-diff:diff) > epsilon) return false;
+
+		return true;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int hashCode() {
+		int bits = 1;
+		bits = 31 * bits + this.x;
+		bits = 31 * bits + this.y;
+		bits = 31 * bits + this.z;
+		return bits ^ (bits >> 32);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		return "(" //$NON-NLS-1$
+				+this.x
+				+";" //$NON-NLS-1$
+				+this.y
+				+";" //$NON-NLS-1$
+				+this.z
+				+")"; //$NON-NLS-1$
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/Tuple3iComparator.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/Tuple3iComparator.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/Tuple3iComparator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,57 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object3d;
+
+import java.util.Comparator;
+
+/**
+ * Comparator of Tuple3i.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Tuple3iComparator implements Comparator<Tuple3i<?>> {
+	
+	/**
+	 */
+	public Tuple3iComparator() {
+		//
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int compare(Tuple3i<?> o1, Tuple3i<?> o2) {
+		if (o1==o2) return 0;
+		if (o1==null) return Integer.MIN_VALUE;
+		if (o2==null) return Integer.MAX_VALUE;
+		int cmp = o1.x() - o2.x();
+		if (cmp!=0) return cmp;
+		cmp = o1.y() - o2.y();
+		if (cmp!=0) return cmp;
+		return o1.z() - o2.z();
+	}
+		
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/Vector3i.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/Vector3i.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/discrete/object3d/Vector3i.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,283 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object3d;
+
+import org.arakhne.afc.math.continous.object3d.Quaternion;
+import org.arakhne.afc.math.continous.object3d.Vector3f;
+import org.arakhne.afc.math.generic.Point3D;
+import org.arakhne.afc.math.generic.Tuple3D;
+import org.arakhne.afc.math.generic.Vector3D;
+import org.arakhne.afc.math.matrix.Transform3D;
+
+/** 3D Vector with 3 integers.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Vector3i extends Tuple3i<Vector3D> implements Vector3D {
+
+	private static final long serialVersionUID = 1997599488590527335L;
+
+	/**
+	 */
+	public Vector3i() {
+		//
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Vector3i(Tuple3D<?> tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Vector3i(int[] tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param tuple is the tuple to copy.
+	 */
+	public Vector3i(float[] tuple) {
+		super(tuple);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Vector3i(int x, int y, int z) {
+		super(x,y,z);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Vector3i(float x, float y, float z) {
+		super(x,y,z);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Vector3i(double x, double y, double z) {
+		super((float)x,(float)y,(float)z);
+	}
+
+	/**
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public Vector3i(long x, long y, long z) {
+		super(x,y,z);
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Vector3i clone() {
+		return (Vector3i)super.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float angle(Vector3D v1) {
+		double vDot = dot(v1) / ( length()*v1.length() );
+		if( vDot < -1.) vDot = -1.;
+		if( vDot >  1.) vDot =  1.;
+		return((float) (Math.acos( vDot )));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float dot(Vector3D v1) {
+	      return (this.x*v1.getX() + this.y*v1.getY() + this.z*v1.getZ());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float length() {
+        return (float) Math.sqrt(this.x*this.x + this.y*this.y + this.z*this.z);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float lengthSquared() {
+        return (this.x*this.x + this.y*this.y + this.z*this.z);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void normalize(Vector3D v1) {
+        float norm;
+        norm = (float) (1./Math.sqrt(v1.getX()*v1.getX() + v1.getY()*v1.getY() + v1.getZ()*v1.getZ()));
+        this.x = (int)(v1.getX()*norm);
+        this.y = (int)(v1.getY()*norm);
+        this.z = (int)(v1.getZ()*norm);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void normalize() {
+        float norm;
+        norm = (float)(1./Math.sqrt(this.x*this.x + this.y*this.y + this.z*this.z));
+        this.x *= norm;
+        this.y *= norm;
+        this.z *= norm;
+	}
+
+	@Override
+	public void add(Vector3D t1, Vector3D t2) {
+		this.x = (int)(t1.getX() + t2.getX());
+		this.y = (int)(t1.getY() + t2.getY());
+		this.z = (int)(t1.getZ() + t2.getZ());
+	}
+
+	@Override
+	public void add(Vector3D t1) {
+		this.x = (int)(this.x + t1.getX());
+		this.y = (int)(this.y + t1.getY());
+		this.z = (int)(this.z + t1.getZ());
+	}
+
+	@Override
+	public void scaleAdd(int s, Vector3D t1, Vector3D t2) {
+		this.x = (int)(s * t1.getX() + t2.getX());
+		this.y = (int)(s * t1.getY() + t2.getY());
+		this.z = (int)(s * t1.getZ() + t2.getZ());
+	}
+
+	@Override
+	public void scaleAdd(float s, Vector3D t1, Vector3D t2) {
+		this.x = (int)(s * t1.getX() + t2.getX());
+		this.y = (int)(s * t1.getY() + t2.getY());
+		this.z = (int)(s * t1.getZ() + t2.getZ());
+	}
+
+	@Override
+	public void scaleAdd(int s, Vector3D t1) {
+		this.x = (int)(s * this.x + t1.getX());
+		this.y = (int)(s * this.y + t1.getY());
+		this.z = (int)(s * this.z + t1.getZ());
+	}
+
+	@Override
+	public void scaleAdd(float s, Vector3D t1) {
+		this.x = (int)(s * this.x + t1.getX());
+		this.y = (int)(s * this.y + t1.getY());
+		this.z = (int)(s * this.z + t1.getZ());
+	}
+
+	@Override
+	public void sub(Vector3D t1, Vector3D t2) {
+		this.x = (int)(t1.getX() - t2.getX());
+		this.y = (int)(t1.getY() - t2.getY());
+		this.z = (int)(t1.getZ() - t2.getZ());
+	}
+
+	@Override
+	public void sub(Point3D t1, Point3D t2) {
+		this.x = (int)(t1.getX() - t2.getX());
+		this.y = (int)(t1.getY() - t2.getY());
+		this.z = (int)(t1.getZ() - t2.getZ());
+	}
+
+	@Override
+	public void sub(Vector3D t1) {
+		this.x = (int)(this.x - t1.getX());
+		this.y = (int)(this.y - t1.getY());
+		this.z = (int)(this.z - t1.getZ());
+	}
+
+	@Override
+	public Vector3D cross(Vector3D v1) {
+		return crossLeftHand(v1);
+	}
+
+	@Override
+	public void cross(Vector3D v1, Vector3D v2) {
+		crossLeftHand(v1, v2);
+	}
+
+	@Override
+	public Vector3D crossLeftHand(Vector3D v1) {
+		float x = v1.getY()*getZ() - v1.getZ()*getY();
+		float y = v1.getZ()*getX() - v1.getX()*getZ();
+		float z = v1.getX()*getY() - v1.getY()*getX();
+		return new Vector3i(x,y,z);
+	}
+
+	@Override
+	public void crossLeftHand(Vector3D v1, Vector3D v2) {
+		float x = v2.getY()*v1.getZ() - v2.getZ()*v1.getY();
+		float y = v2.getZ()*v1.getX() - v2.getX()*v1.getZ();
+		float z = v2.getX()*v1.getY() - v2.getY()*v1.getX();
+		set(x,y,z);
+	}
+	
+	@Override
+	public Vector3D crossRightHand(Vector3D v1) {
+		float x = getY()*v1.getZ() - getZ()*v1.getY();
+		float y = getZ()*v1.getX() - getX()*v1.getZ();
+		float z = getX()*v1.getY() - getY()*v1.getX();
+		return new Vector3f(x,y,z);
+	}
+
+	@Override
+	public void crossRightHand(Vector3D v1, Vector3D v2) {
+		float x = v1.getY()*v2.getZ() - v1.getZ()*v2.getY();
+		float y = v1.getZ()*v2.getX() - v1.getX()*v2.getZ();
+		float z = v1.getX()*v2.getY() - v1.getY()*v2.getX();
+		set(x,y,z);
+	}
+
+	@Override
+	public void turnVector(Vector3D axis, float angle) {
+		Transform3D mat = new Transform3D();
+		mat.setRotation(new Quaternion(axis, angle));
+		mat.transform(this);
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/generic/PathElementType.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/generic/PathElementType.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/generic/PathElementType.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,54 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.generic;
+
+
+/** Type of a path element.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public enum PathElementType {
+
+	/** Move to the next point.
+	 */
+	MOVE_TO,
+
+	/** Line to the next point.
+	 */
+	LINE_TO,
+
+	/** Quadratic curve to the next point.
+	 */
+	QUAD_TO,
+
+	/** Cubic curve to the next point.
+	 */
+	CURVE_TO,
+
+	/** Close the path.
+	 */
+	CLOSE;
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/generic/PathWindingRule.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/generic/PathWindingRule.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/generic/PathWindingRule.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,56 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.generic;
+
+
+/** The winding rule to determine the interior of a path.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public enum PathWindingRule {
+
+	/** he winding rule constant for specifying an even-odd rule
+     * for determining the interior of a path.
+     * The even-odd rule specifies that a point lies inside the
+     * path if a ray drawn in any direction from that point to
+     * infinity is crossed by path segments an odd number of times.
+     * <p>
+     * <center><img src="./doc-files/fillrule-evenodd.png" /></center>
+	 */
+	EVEN_ODD,
+
+	/** The winding rule constant for specifying a non-zero rule
+     * for determining the interior of a path.
+     * The non-zero rule specifies that a point lies inside the
+     * path if a ray drawn in any direction from that point to
+     * infinity is crossed by path segments a different number
+     * of times in the counter-clockwise direction than the
+     * clockwise direction.
+     * <p>
+     * <center><img src="./doc-files/fillrule-nonzero.png" /></center>
+	 */
+	NON_ZERO;
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/generic/Point2D.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/generic/Point2D.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/generic/Point2D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,151 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.generic;
+
+/** 2D Point.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Point2D extends Tuple2D<Point2D> {
+
+	/**
+	 * Computes the square of the distance between this point and point p1.
+	 * @param p1 the other point
+	 * @return the distance.
+	 */
+	public float distanceSquared(Point2D p1);
+
+	/**
+	 * Computes the distance between this point and point p1.
+	 * @param p1 the other point
+	 * @return the distance. 
+	 */    
+	public float distance(Point2D p1);
+
+	/**
+	 * Computes the L-1 (Manhattan) distance between this point and
+	 * point p1.  The L-1 distance is equal to abs(x1-x2) + abs(y1-y2).
+	 * @param p1 the other point
+	 * @return the distance.
+	 */
+	public float distanceL1(Point2D p1);
+
+	/**
+	 * Computes the L-infinite distance between this point and
+	 * point p1.  The L-infinite distance is equal to 
+	 * MAX[abs(x1-x2), abs(y1-y2)]. 
+	 * @param p1 the other point
+	 * @return the distance.
+	 */
+	public float distanceLinf(Point2D p1);
+
+	/**
+	 * Sets the value of this tuple to the sum of tuples t1 and t2.
+	 * @param t1 the first tuple
+	 * @param t2 the second tuple
+	 */
+	public void add(Point2D t1, Vector2D t2);
+
+	/**
+	 * Sets the value of this tuple to the sum of tuples t1 and t2.
+	 * @param t1 the first tuple
+	 * @param t2 the second tuple
+	 */
+	public void add(Vector2D t1, Point2D t2);
+	
+	/**
+	 * Sets the value of this tuple to the sum of itself and t1.
+	 * @param t1 the other tuple
+	 */
+	public void add(Vector2D t1);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of tuple t1 plus tuple t2 (this = s*t1 + t2).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be multipled
+	 * @param t2 the tuple to be added
+	 */
+	public void scaleAdd(int s, Vector2D t1, Point2D t2);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of tuple t1 plus tuple t2 (this = s*t1 + t2).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be multipled
+	 * @param t2 the tuple to be added
+	 */
+	public void scaleAdd(float s, Vector2D t1, Point2D t2);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of tuple t1 plus tuple t2 (this = s*t1 + t2).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be multipled
+	 * @param t2 the tuple to be added
+	 */
+	public void scaleAdd(int s, Point2D t1, Vector2D t2);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of tuple t1 plus tuple t2 (this = s*t1 + t2).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be multipled
+	 * @param t2 the tuple to be added
+	 */
+	public void scaleAdd(float s, Point2D t1, Vector2D t2);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of itself and then adds tuple t1 (this = s*this + t1).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be added
+	 */
+	public void scaleAdd(int s, Vector2D t1);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of itself and then adds tuple t1 (this = s*this + t1).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be added
+	 */
+	public void scaleAdd(float s, Vector2D t1);
+
+	
+	/**
+	 * Sets the value of this tuple to the difference
+	 * of tuples t1 and t2 (this = t1 - t2).
+	 * @param t1 the first tuple
+	 * @param t2 the second tuple
+	 */
+	public void sub(Point2D t1, Vector2D t2);
+
+	/**
+	 * Sets the value of this tuple to the difference
+	 * of itself and t1 (this = this - t1).
+	 * @param t1 the other tuple
+	 */
+	public void sub(Vector2D t1);
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/generic/Point3D.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/generic/Point3D.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/generic/Point3D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,182 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.generic;
+
+/** 3D Point.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Point3D extends Tuple3D<Point3D> {
+
+	/**
+	 * Computes the square of the distance between this point and point p1.
+	 * @param p1 the other point
+	 * @return the distance.
+	 */
+	public int distanceSquared(Point3D p1);
+
+	/**
+	 * Computes the square of the distance between this point and point p1.
+	 * @param p1 the other point
+	 * @return the distance.
+	 */
+	public float getDistanceSquared(Point3D p1);
+
+	/**
+	 * Computes the distance between this point and point p1.
+	 * @param p1 the other point
+	 * @return the distance. 
+	 */    
+	public int distance(Point3D p1);
+
+	/**
+	 * Computes the distance between this point and point p1.
+	 * @param p1 the other point
+	 * @return the distance. 
+	 */    
+	public float getDistance(Point3D p1);
+
+	/**
+	 * Computes the L-1 (Manhattan) distance between this point and
+	 * point p1.  The L-1 distance is equal to abs(x1-x2) + abs(y1-y2).
+	 * @param p1 the other point
+	 * @return the distance.
+	 */
+	public int distanceL1(Point3D p1);
+
+	/**
+	 * Computes the L-1 (Manhattan) distance between this point and
+	 * point p1.  The L-1 distance is equal to abs(x1-x2) + abs(y1-y2).
+	 * @param p1 the other point
+	 * @return the distance.
+	 */
+	public float getDistanceL1(Point3D p1);
+
+	/**
+	 * Computes the L-infinite distance between this point and
+	 * point p1.  The L-infinite distance is equal to 
+	 * MAX[abs(x1-x2), abs(y1-y2)]. 
+	 * @param p1 the other point
+	 * @return the distance.
+	 */
+	public int distanceLinf(Point3D p1);
+
+	/**
+	 * Computes the L-infinite distance between this point and
+	 * point p1.  The L-infinite distance is equal to 
+	 * MAX[abs(x1-x2), abs(y1-y2)]. 
+	 * @param p1 the other point
+	 * @return the distance.
+	 */
+	public float getDistanceLinf(Point3D p1);
+
+	/**
+	 * Sets the value of this tuple to the sum of tuples t1 and t2.
+	 * @param t1 the first tuple
+	 * @param t2 the second tuple
+	 */
+	public void add(Point3D t1, Vector3D t2);
+
+	/**
+	 * Sets the value of this tuple to the sum of tuples t1 and t2.
+	 * @param t1 the first tuple
+	 * @param t2 the second tuple
+	 */
+	public void add(Vector3D t1, Point3D t2);
+	
+	/**
+	 * Sets the value of this tuple to the sum of itself and t1.
+	 * @param t1 the other tuple
+	 */
+	public void add(Vector3D t1);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of tuple t1 plus tuple t2 (this = s*t1 + t2).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be multipled
+	 * @param t2 the tuple to be added
+	 */
+	public void scaleAdd(int s, Vector3D t1, Point3D t2);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of tuple t1 plus tuple t2 (this = s*t1 + t2).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be multipled
+	 * @param t2 the tuple to be added
+	 */
+	public void scaleAdd(float s, Vector3D t1, Point3D t2);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of tuple t1 plus tuple t2 (this = s*t1 + t2).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be multipled
+	 * @param t2 the tuple to be added
+	 */
+	public void scaleAdd(int s, Point3D t1, Vector3D t2);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of tuple t1 plus tuple t2 (this = s*t1 + t2).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be multipled
+	 * @param t2 the tuple to be added
+	 */
+	public void scaleAdd(float s, Point3D t1, Vector3D t2);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of itself and then adds tuple t1 (this = s*this + t1).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be added
+	 */
+	public void scaleAdd(int s, Vector3D t1);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of itself and then adds tuple t1 (this = s*this + t1).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be added
+	 */
+	public void scaleAdd(float s, Vector3D t1);
+
+	
+	/**
+	 * Sets the value of this tuple to the difference
+	 * of tuples t1 and t2 (this = t1 - t2).
+	 * @param t1 the first tuple
+	 * @param t2 the second tuple
+	 */
+	public void sub(Point3D t1, Vector3D t2);
+
+	/**
+	 * Sets the value of this tuple to the difference
+	 * of itself and t1 (this = this - t1).
+	 * @param t1 the other tuple
+	 */
+	public void sub(Vector3D t1);
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/generic/Shape2D.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/generic/Shape2D.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/generic/Shape2D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,72 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.generic;
+
+import java.io.Serializable;
+
+/** 2D shape.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Shape2D
+extends Cloneable, Serializable {
+
+	/** Replies if this shape is empty.
+	 * The semantic associated to the state "empty"
+	 * depends on the implemented shape. See the
+	 * subclasses for details.
+	 * 
+	 * @return <code>true</code> if the shape is empty;
+	 * <code>false</code> otherwise.
+	 */
+	public boolean isEmpty();
+
+	/** Clone this shape.
+	 * 
+	 * @return the clone.
+	 */
+	public Shape2D clone();
+
+	/** Reset this shape to be equivalent to
+	 * an just-created instance of this shape type. 
+	 */
+	public void clear();
+
+	/** Replies if the given point is inside this shape.
+	 * 
+	 * @param p
+	 * @return <code>true</code> if the given shape is intersecting this
+	 * shape, otherwise <code>false</code>.
+	 */
+	public boolean contains(Point2D p);
+	
+	/** Replies the point on the shape that is closest to the given point.
+	 * 
+	 * @param p
+	 * @return the closest point on the shape; or the point itself
+	 * if it is inside the shape.
+	 */
+	public Point2D getClosestPointTo(Point2D p);
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/generic/Tuple2D.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/generic/Tuple2D.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/generic/Tuple2D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,426 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.generic;
+
+import java.io.Serializable;
+
+/** 2D tuple.
+ * 
+ * @param <TT> is the type of data that can be added or substracted to this tuple.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Tuple2D<TT extends Tuple2D<? super TT>>
+extends Cloneable, Serializable {
+
+	/** Clone this point.
+	 * 
+	 * @return the clone.
+	 */
+	public TT clone();
+
+	/**
+	 *  Sets each component of this tuple to its absolute value.
+	 */
+	public void absolute();
+
+	/**
+	 *  Sets each component of the tuple parameter to its absolute
+	 *  value and places the modified values into this tuple.
+	 *  @param t   the source tuple, which will not be modified
+	 */
+	public void absolute(TT t);
+
+	/**
+	 * Sets the value of this tuple to the sum of itself and x and y.
+	 * @param x
+	 * @param y
+	 */
+	public void add(int x, int y);
+
+	/**
+	 * Sets the value of this tuple to the sum of itself and x and y.
+	 * @param x
+	 * @param y
+	 */
+	public void add(float x, float y);
+
+	/**
+	 * Sets the x value of this tuple to the sum of itself and x.
+	 * @param x
+	 */
+	public void addX(int x);
+
+	/**
+	 * Sets the x value of this tuple to the sum of itself and x.
+	 * @param x
+	 */
+	public void addX(float x);
+
+	/**
+	 * Sets the y value of this tuple to the sum of itself and y.
+	 * @param y
+	 */
+	public void addY(int y);
+
+	/**
+	 * Sets the y value of this tuple to the sum of itself and y.
+	 * @param y
+	 */
+	public void addY(float y);
+
+	/**
+	 *  Clamps this tuple to the range [low, high].
+	 *  @param min  the lowest value in this tuple after clamping
+	 *  @param max  the highest value in this tuple after clamping
+	 */
+	public void clamp(int min, int max);
+
+	/**
+	 *  Clamps this tuple to the range [low, high].
+	 *  @param min  the lowest value in this tuple after clamping
+	 *  @param max  the highest value in this tuple after clamping
+	 */
+	public void clamp(float min, float max);
+
+	/**
+	 *  Clamps the minimum value of this tuple to the min parameter.
+	 *  @param min   the lowest value in this tuple after clamping
+	 */
+	public void clampMin(int min);
+
+	/**
+	 *  Clamps the minimum value of this tuple to the min parameter.
+	 *  @param min   the lowest value in this tuple after clamping
+	 */
+	public void clampMin(float min);
+
+	/**
+	 *  Clamps the maximum value of this tuple to the max parameter.
+	 *  @param max   the highest value in the tuple after clamping
+	 */
+	public void clampMax(int max);
+
+	/**
+	 *  Clamps the maximum value of this tuple to the max parameter.
+	 *  @param max   the highest value in the tuple after clamping
+	 */
+	public void clampMax(float max);
+
+	/**
+	 *  Clamps the tuple parameter to the range [low, high] and
+	 *  places the values into this tuple.
+	 *  @param min   the lowest value in the tuple after clamping
+	 *  @param max  the highest value in the tuple after clamping
+	 *  @param t   the source tuple, which will not be modified
+	 */
+	public void clamp(int min, int max, TT t);
+
+	/**
+	 *  Clamps the tuple parameter to the range [low, high] and
+	 *  places the values into this tuple.
+	 *  @param min   the lowest value in the tuple after clamping
+	 *  @param max  the highest value in the tuple after clamping
+	 *  @param t   the source tuple, which will not be modified
+	 */
+	public void clamp(float min, float max, TT t);
+
+	/**
+	 *  Clamps the minimum value of the tuple parameter to the min
+	 *  parameter and places the values into this tuple.
+	 *  @param min   the lowest value in the tuple after clamping
+	 *  @param t   the source tuple, which will not be modified
+	 */
+	public void clampMin(int min, TT t);
+
+	/**
+	 *  Clamps the minimum value of the tuple parameter to the min
+	 *  parameter and places the values into this tuple.
+	 *  @param min   the lowest value in the tuple after clamping
+	 *  @param t   the source tuple, which will not be modified
+	 */
+	public void clampMin(float min, TT t);
+
+	/**
+	 *  Clamps the maximum value of the tuple parameter to the max
+	 *  parameter and places the values into this tuple.
+	 *  @param max   the highest value in the tuple after clamping
+	 *  @param t   the source tuple, which will not be modified
+	 */
+	public void clampMax(int max, TT t);
+
+	/**
+	 *  Clamps the maximum value of the tuple parameter to the max
+	 *  parameter and places the values into this tuple.
+	 *  @param max   the highest value in the tuple after clamping
+	 *  @param t   the source tuple, which will not be modified
+	 */
+	public void clampMax(float max, TT t);
+
+	/**
+	 * Copies the values of this tuple into the tuple t.
+	 * @param t is the target tuple
+	 */
+	public void get(TT t);
+
+	/**
+	 *  Copies the value of the elements of this tuple into the array t.
+	 *  @param t the array that will contain the values of the vector
+	 */
+	public void get(int[] t);
+
+	/**
+	 *  Copies the value of the elements of this tuple into the array t.
+	 *  @param t the array that will contain the values of the vector
+	 */
+	public void get(float[] t);
+
+	/**
+	 * Sets the value of this tuple to the negation of tuple t1.
+	 * @param t1 the source tuple
+	 */
+	public void negate(TT t1);
+
+	/**
+	 * Negates the value of this tuple in place.
+	 */
+	public void negate();
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of tuple t1.
+	 * @param s the scalar value
+	 * @param t1 the source tuple
+	 */
+	public void scale(int s, TT t1);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of tuple t1.
+	 * @param s the scalar value
+	 * @param t1 the source tuple
+	 */
+	public void scale(float s, TT t1);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of the scale factor with this.
+	 * @param s the scalar value
+	 */
+	public void scale(int s);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of the scale factor with this.
+	 * @param s the scalar value
+	 */
+	public void scale(float s);
+
+	/**
+	 * Sets the value of this tuple to the value of tuple t1.
+	 * @param t1 the tuple to be copied
+	 */
+	public void set(Tuple2D<?> t1);
+
+	/**
+	 * Sets the value of this tuple to the specified x and y
+	 * coordinates.
+	 * @param x the x coordinate
+	 * @param y the y coordinate
+	 */
+	public void set(int x, int y);
+
+	/**
+	 * Sets the value of this tuple to the specified x and y
+	 * coordinates.
+	 * @param x the x coordinate
+	 * @param y the y coordinate
+	 */
+	public void set(float x, float y);
+
+	/**
+	 * Sets the value of this tuple from the 2 values specified in 
+	 * the array.
+	 * @param t the array of length 2 containing xy in order
+	 */
+	public void set(int[] t);
+
+	/**
+	 * Sets the value of this tuple from the 2 values specified in 
+	 * the array.
+	 * @param t the array of length 2 containing xy in order
+	 */
+	public void set(float[] t);
+
+	/**
+	 * Get the <i>x</i> coordinate.
+	 * 
+	 * @return the x coordinate.
+	 */
+	public float getX();
+
+	/**
+	 * Get the <i>x</i> coordinate.
+	 * 
+	 * @return the x coordinate.
+	 */
+	public int x();
+
+	/**
+	 * Set the <i>x</i> coordinate.
+	 * 
+	 * @param x  value to <i>x</i> coordinate.
+	 */
+	public void setX(int x);
+
+	/**
+	 * Set the <i>x</i> coordinate.
+	 * 
+	 * @param x  value to <i>x</i> coordinate.
+	 */
+	public void setX(float x);
+
+	/**
+	 * Get the <i>y</i> coordinate.
+	 * 
+	 * @return  the <i>y</i> coordinate.
+	 */
+	public float getY();
+
+	/**
+	 * Get the <i>y</i> coordinate.
+	 * 
+	 * @return  the <i>y</i> coordinate.
+	 */
+	public int y();
+
+	/**
+	 * Set the <i>y</i> coordinate.
+	 * 
+	 * @param y value to <i>y</i> coordinate.
+	 */
+	public void setY(int y);
+
+	/**
+	 * Set the <i>y</i> coordinate.
+	 * 
+	 * @param y value to <i>y</i> coordinate.
+	 */
+	public void setY(float y);
+
+	/**
+	 * Sets the value of this tuple to the difference of itself and x and y.
+	 * @param x
+	 * @param y
+	 */
+	public void sub(int x, int y);
+
+	/**
+	 * Sets the value of this tuple to the difference of itself and x and y.
+	 * @param x
+	 * @param y
+	 */
+	public void sub(float x, float y);
+
+	/**
+	 * Sets the x value of this tuple to the difference of itself and x.
+	 * @param x
+	 */
+	public void subX(int x);
+
+	/**
+	 * Sets the x value of this tuple to the difference of itself and x.
+	 * @param x
+	 */
+	public void subX(float x);
+
+	/**
+	 * Sets the y value of this tuple to the difference of itself and y.
+	 * @param y
+	 */
+	public void subY(int y);
+
+	/**
+	 * Sets the y value of this tuple to the difference of itself and y.
+	 * @param y
+	 */
+	public void subY(float y);
+
+	/** 
+	 *  Linearly interpolates between tuples t1 and t2 and places the 
+	 *  result into this tuple:  this = (1-alpha)*t1 + alpha*t2.
+	 *  @param t1  the first tuple
+	 *  @param t2  the second tuple
+	 *  @param alpha  the alpha interpolation parameter
+	 */
+	public void interpolate(TT t1, TT t2, float alpha);
+
+	/**  
+	 *  Linearly interpolates between this tuple and tuple t1 and 
+	 *  places the result into this tuple:  this = (1-alpha)*this + alpha*t1.
+	 *  @param t1  the first tuple
+	 *  @param alpha  the alpha interpolation parameter  
+	 */   
+	public void interpolate(TT t1, float alpha); 
+
+	/**   
+	 * Returns true if all of the data members of Tuple2f t1 are
+	 * equal to the corresponding data members in this Tuple2f.
+	 * @param t1  the vector with which the comparison is made
+	 * @return  true or false
+	 */  
+	public boolean equals(Tuple2D<?> t1);
+
+	/**   
+	 * Returns true if the Object t1 is of type Tuple2f and all of the
+	 * data members of t1 are equal to the corresponding data members in
+	 * this Tuple2f.
+	 * @param t1  the object with which the comparison is made
+	 * @return  true or false
+	 */  
+	@Override
+	public boolean equals(Object t1);
+
+	/**
+	 * Returns true if the L-infinite distance between this tuple
+	 * and tuple t1 is less than or equal to the epsilon parameter, 
+	 * otherwise returns false.  The L-infinite
+	 * distance is equal to MAX[abs(x1-x2), abs(y1-y2)]. 
+	 * @param t1  the tuple to be compared to this tuple
+	 * @param epsilon  the threshold value  
+	 * @return  true or false
+	 */
+	public boolean epsilonEquals(TT t1, float epsilon);
+
+	/**
+	 * Returns a hash code value based on the data values in this
+	 * object.  Two different Tuple2f objects with identical data values
+	 * (i.e., Tuple2f.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
+	 */  
+	@Override
+	public int hashCode();
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/generic/Tuple3D.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/generic/Tuple3D.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/generic/Tuple3D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,484 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.generic;
+
+import java.io.Serializable;
+
+/** 3D tuple.
+ * 
+ * @param <TT> is the type of data that can be added or substracted to this tuple.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Tuple3D<TT extends Tuple3D<? super TT>>
+extends Cloneable, Serializable {
+
+	/** Clone this point.
+	 * 
+	 * @return the clone.
+	 */
+	public TT clone();
+
+	/**
+	 *  Sets each component of this tuple to its absolute value.
+	 */
+	public void absolute();
+
+	/**
+	 *  Sets each component of the tuple parameter to its absolute
+	 *  value and places the modified values into this tuple.
+	 *  @param t   the source tuple, which will not be modified
+	 */
+	public void absolute(TT t);
+
+	/**
+	 * Sets the value of this tuple to the sum of itself and x and y.
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public void add(int x, int y, int z);
+
+	/**
+	 * Sets the value of this tuple to the sum of itself and x and y.
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public void add(float x, float y, float z);
+
+	/**
+	 * Sets the x value of this tuple to the sum of itself and x.
+	 * @param x
+	 */
+	public void addX(int x);
+
+	/**
+	 * Sets the x value of this tuple to the sum of itself and x.
+	 * @param x
+	 */
+	public void addX(float x);
+
+	/**
+	 * Sets the y value of this tuple to the sum of itself and y.
+	 * @param y
+	 */
+	public void addY(int y);
+
+	/**
+	 * Sets the y value of this tuple to the sum of itself and y.
+	 * @param y
+	 */
+	public void addY(float y);
+
+	/**
+	 * Sets the z value of this tuple to the sum of itself and z.
+	 * @param z
+	 */
+	public void addZ(int z);
+
+	/**
+	 * Sets the z value of this tuple to the sum of itself and z.
+	 * @param z
+	 */
+	public void addZ(float z);
+
+	/**
+	 *  Clamps this tuple to the range [low, high].
+	 *  @param min  the lowest value in this tuple after clamping
+	 *  @param max  the highest value in this tuple after clamping
+	 */
+	public void clamp(int min, int max);
+
+	/**
+	 *  Clamps this tuple to the range [low, high].
+	 *  @param min  the lowest value in this tuple after clamping
+	 *  @param max  the highest value in this tuple after clamping
+	 */
+	public void clamp(float min, float max);
+
+	/**
+	 *  Clamps the minimum value of this tuple to the min parameter.
+	 *  @param min   the lowest value in this tuple after clamping
+	 */
+	public void clampMin(int min);
+
+	/**
+	 *  Clamps the minimum value of this tuple to the min parameter.
+	 *  @param min   the lowest value in this tuple after clamping
+	 */
+	public void clampMin(float min);
+
+	/**
+	 *  Clamps the maximum value of this tuple to the max parameter.
+	 *  @param max   the highest value in the tuple after clamping
+	 */
+	public void clampMax(int max);
+
+	/**
+	 *  Clamps the maximum value of this tuple to the max parameter.
+	 *  @param max   the highest value in the tuple after clamping
+	 */
+	public void clampMax(float max);
+
+	/**
+	 *  Clamps the tuple parameter to the range [low, high] and
+	 *  places the values into this tuple.
+	 *  @param min   the lowest value in the tuple after clamping
+	 *  @param max  the highest value in the tuple after clamping
+	 *  @param t   the source tuple, which will not be modified
+	 */
+	public void clamp(int min, int max, TT t);
+
+	/**
+	 *  Clamps the tuple parameter to the range [low, high] and
+	 *  places the values into this tuple.
+	 *  @param min   the lowest value in the tuple after clamping
+	 *  @param max  the highest value in the tuple after clamping
+	 *  @param t   the source tuple, which will not be modified
+	 */
+	public void clamp(float min, float max, TT t);
+
+	/**
+	 *  Clamps the minimum value of the tuple parameter to the min
+	 *  parameter and places the values into this tuple.
+	 *  @param min   the lowest value in the tuple after clamping
+	 *  @param t   the source tuple, which will not be modified
+	 */
+	public void clampMin(int min, TT t);
+
+	/**
+	 *  Clamps the minimum value of the tuple parameter to the min
+	 *  parameter and places the values into this tuple.
+	 *  @param min   the lowest value in the tuple after clamping
+	 *  @param t   the source tuple, which will not be modified
+	 */
+	public void clampMin(float min, TT t);
+
+	/**
+	 *  Clamps the maximum value of the tuple parameter to the max
+	 *  parameter and places the values into this tuple.
+	 *  @param max   the highest value in the tuple after clamping
+	 *  @param t   the source tuple, which will not be modified
+	 */
+	public void clampMax(int max, TT t);
+
+	/**
+	 *  Clamps the maximum value of the tuple parameter to the max
+	 *  parameter and places the values into this tuple.
+	 *  @param max   the highest value in the tuple after clamping
+	 *  @param t   the source tuple, which will not be modified
+	 */
+	public void clampMax(float max, TT t);
+
+	/**
+	 * Copies the values of this tuple into the tuple t.
+	 * @param t is the target tuple
+	 */
+	public void get(TT t);
+
+	/**
+	 *  Copies the value of the elements of this tuple into the array t.
+	 *  @param t the array that will contain the values of the vector
+	 */
+	public void get(int[] t);
+
+	/**
+	 *  Copies the value of the elements of this tuple into the array t.
+	 *  @param t the array that will contain the values of the vector
+	 */
+	public void get(float[] t);
+
+	/**
+	 * Sets the value of this tuple to the negation of tuple t1.
+	 * @param t1 the source tuple
+	 */
+	public void negate(TT t1);
+
+	/**
+	 * Negates the value of this tuple in place.
+	 */
+	public void negate();
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of tuple t1.
+	 * @param s the scalar value
+	 * @param t1 the source tuple
+	 */
+	public void scale(int s, TT t1);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of tuple t1.
+	 * @param s the scalar value
+	 * @param t1 the source tuple
+	 */
+	public void scale(float s, TT t1);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of the scale factor with this.
+	 * @param s the scalar value
+	 */
+	public void scale(int s);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of the scale factor with this.
+	 * @param s the scalar value
+	 */
+	public void scale(float s);
+
+	/**
+	 * Sets the value of this tuple to the value of tuple t1.
+	 * @param t1 the tuple to be copied
+	 */
+	public void set(Tuple3D<?> t1);
+
+	/**
+	 * Sets the value of this tuple to the specified x and y
+	 * coordinates.
+	 * @param x the x coordinate
+	 * @param y the y coordinate
+	 * @param z the z coordinate
+	 */
+	public void set(int x, int y, int z);
+
+	/**
+	 * Sets the value of this tuple to the specified x and y
+	 * coordinates.
+	 * @param x the x coordinate
+	 * @param y the y coordinate
+	 * @param z the z coordinate
+	 */
+	public void set(float x, float y, float z);
+
+	/**
+	 * Sets the value of this tuple from the 2 values specified in 
+	 * the array.
+	 * @param t the array of length 2 containing xy in order
+	 */
+	public void set(int[] t);
+
+	/**
+	 * Sets the value of this tuple from the 2 values specified in 
+	 * the array.
+	 * @param t the array of length 2 containing xy in order
+	 */
+	public void set(float[] t);
+
+	/**
+	 * Get the <i>x</i> coordinate.
+	 * 
+	 * @return the x coordinate.
+	 */
+	public float getX();
+
+	/**
+	 * Get the <i>x</i> coordinate.
+	 * 
+	 * @return the x coordinate.
+	 */
+	public int x();
+
+	/**
+	 * Set the <i>x</i> coordinate.
+	 * 
+	 * @param x  value to <i>x</i> coordinate.
+	 */
+	public void setX(int x);
+
+	/**
+	 * Set the <i>x</i> coordinate.
+	 * 
+	 * @param x  value to <i>x</i> coordinate.
+	 */
+	public void setX(float x);
+
+	/**
+	 * Get the <i>y</i> coordinate.
+	 * 
+	 * @return  the <i>y</i> coordinate.
+	 */
+	public float getY();
+
+	/**
+	 * Get the <i>y</i> coordinate.
+	 * 
+	 * @return  the <i>y</i> coordinate.
+	 */
+	public int y();
+
+	/**
+	 * Set the <i>y</i> coordinate.
+	 * 
+	 * @param y value to <i>y</i> coordinate.
+	 */
+	public void setY(int y);
+
+	/**
+	 * Set the <i>y</i> coordinate.
+	 * 
+	 * @param y value to <i>y</i> coordinate.
+	 */
+	public void setY(float y);
+
+	/**
+	 * Get the <i>z</i> coordinate.
+	 * 
+	 * @return  the <i>z</i> coordinate.
+	 */
+	public float getZ();
+
+	/**
+	 * Get the <i>z</i> coordinate.
+	 * 
+	 * @return  the <i>z</i> coordinate.
+	 */
+	public int z();
+
+	/**
+	 * Set the <i>z</i> coordinate.
+	 * 
+	 * @param z value to <i>z</i> coordinate.
+	 */
+	public void setZ(int z);
+
+	/**
+	 * Set the <i>z</i> coordinate.
+	 * 
+	 * @param z value to <i>z</i> coordinate.
+	 */
+	public void setZ(float z);
+
+	/**
+	 * Sets the value of this tuple to the difference of itself and x, y and z.
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public void sub(int x, int y, int z);
+
+	/**
+	 * Sets the value of this tuple to the difference of itself and x, y and z.
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public void sub(float x, float y, float z);
+
+	/**
+	 * Sets the x value of this tuple to the difference of itself and x.
+	 * @param x
+	 */
+	public void subX(int x);
+
+	/**
+	 * Sets the x value of this tuple to the difference of itself and x.
+	 * @param x
+	 */
+	public void subX(float x);
+
+	/**
+	 * Sets the y value of this tuple to the difference of itself and y.
+	 * @param y
+	 */
+	public void subY(int y);
+
+	/**
+	 * Sets the y value of this tuple to the difference of itself and y.
+	 * @param y
+	 */
+	public void subY(float y);
+
+	/**
+	 * Sets the z value of this tuple to the difference of itself and z.
+	 * @param z
+	 */
+	public void subZ(int z);
+
+	/**
+	 * Sets the z value of this tuple to the difference of itself and z.
+	 * @param z
+	 */
+	public void subZ(float z);
+
+	/** 
+	 *  Linearly interpolates between tuples t1 and t2 and places the 
+	 *  result into this tuple:  this = (1-alpha)*t1 + alpha*t2.
+	 *  @param t1  the first tuple
+	 *  @param t2  the second tuple
+	 *  @param alpha  the alpha interpolation parameter
+	 */
+	public void interpolate(TT t1, TT t2, float alpha);
+
+	/**  
+	 *  Linearly interpolates between this tuple and tuple t1 and 
+	 *  places the result into this tuple:  this = (1-alpha)*this + alpha*t1.
+	 *  @param t1  the first tuple
+	 *  @param alpha  the alpha interpolation parameter  
+	 */   
+	public void interpolate(TT t1, float alpha); 
+
+	/**   
+	 * Returns true if all of the data members of Tuple2f t1 are
+	 * equal to the corresponding data members in this Tuple2f.
+	 * @param t1  the vector with which the comparison is made
+	 * @return  true or false
+	 */  
+	public boolean equals(Tuple3D<?> t1);
+
+	/**   
+	 * Returns true if the Object t1 is of type Tuple2f and all of the
+	 * data members of t1 are equal to the corresponding data members in
+	 * this Tuple2f.
+	 * @param t1  the object with which the comparison is made
+	 * @return  true or false
+	 */  
+	@Override
+	public boolean equals(Object t1);
+
+	/**
+	 * Returns true if the L-infinite distance between this tuple
+	 * and tuple t1 is less than or equal to the epsilon parameter, 
+	 * otherwise returns false.  The L-infinite
+	 * distance is equal to MAX[abs(x1-x2), abs(y1-y2)]. 
+	 * @param t1  the tuple to be compared to this tuple
+	 * @param epsilon  the threshold value  
+	 * @return  true or false
+	 */
+	public boolean epsilonEquals(TT t1, float epsilon);
+
+	/**
+	 * Returns a hash code value based on the data values in this
+	 * object.  Two different Tuple2f objects with identical data values
+	 * (i.e., Tuple2f.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
+	 */  
+	@Override
+	public int hashCode();
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/generic/Vector2D.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/generic/Vector2D.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/generic/Vector2D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,183 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.generic;
+
+
+/** 2D Vector.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Vector2D extends Tuple2D<Vector2D> {
+
+	/**
+	 * Sets the value of this tuple to the sum of tuples t1 and t2.
+	 * @param t1 the first tuple
+	 * @param t2 the second tuple
+	 */
+	public void add(Vector2D t1, Vector2D t2);
+
+
+	/**
+	 * Sets the value of this tuple to the sum of itself and t1.
+	 * @param t1 the other tuple
+	 */
+	public void add(Vector2D t1);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of tuple t1 plus tuple t2 (this = s*t1 + t2).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be multipled
+	 * @param t2 the tuple to be added
+	 */
+	public void scaleAdd(int s, Vector2D t1, Vector2D t2);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of tuple t1 plus tuple t2 (this = s*t1 + t2).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be multipled
+	 * @param t2 the tuple to be added
+	 */
+	public void scaleAdd(float s, Vector2D t1, Vector2D t2);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of itself and then adds tuple t1 (this = s*this + t1).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be added
+	 */
+	public void scaleAdd(int s, Vector2D t1);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of itself and then adds tuple t1 (this = s*this + t1).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be added
+	 */
+	public void scaleAdd(float s, Vector2D t1);
+
+	
+	/**
+	 * Sets the value of this tuple to the difference
+	 * of tuples t1 and t2 (this = t1 - t2).
+	 * @param t1 the first tuple
+	 * @param t2 the second tuple
+	 */
+	public void sub(Vector2D t1, Vector2D t2);
+
+	/**
+	 * Sets the value of this tuple to the difference
+	 * of tuples t1 and t2 (this = t1 - t2).
+	 * @param t1 the first tuple
+	 * @param t2 the second tuple
+	 */
+	public void sub(Point2D t1, Point2D t2);
+
+	/**
+	 * Sets the value of this tuple to the difference
+	 * of itself and t1 (this = this - t1).
+	 * @param t1 the other tuple
+	 */
+	public void sub(Vector2D t1);
+
+	/**
+	 * Computes the dot product of the this vector and vector v1.
+	 * @param v1 the other vector
+	 * @return the dot product.
+	 */
+	public float dot(Vector2D v1);
+	
+	/** Change the coordinates of this vector to make it a perpendicular
+	 * vector to the original coordinates.
+	 */
+	public void perpendicularize();
+
+	/**  
+	 * Returns the length of this vector.
+	 * @return the length of this vector
+	 */  
+	public float length();
+
+	/**  
+	 * Returns the squared length of this vector.
+	 * @return the squared length of this vector
+	 */  
+	public float lengthSquared();
+
+	/**
+	 * Sets the value of this vector to the normalization of vector v1.
+	 * @param v1 the un-normalized vector
+	 */  
+	public void normalize(Vector2D v1);
+
+	/**
+	 * Normalizes this vector in place.
+	 */  
+	public void normalize();
+
+
+	/**
+	 *   Returns the angle in radians between this vector and the vector
+	 *   parameter; the return value is constrained to the range [0,PI].
+	 *   @param v1    the other vector
+	 *   @return   the angle in radians in the range [0,PI]
+	 */
+	public float angle(Vector2D v1);
+
+	/** Compute a signed angle between this vector and the given vector.
+	 * <p>
+	 * The signed angle between this vector and {@code v}
+	 * is the rotation angle to apply to this vector
+	 * to be colinear to {@code v} and pointing the
+	 * same demi-plane. It means that the angle replied
+	 * by this function is be negative if the rotation
+	 * to apply is clockwise, and positive if
+	 * the rotation is counterclockwise.
+	 * <p>
+	 * The value replied by {@link #angle(Vector2D)}
+	 * is the absolute value of the vlaue replied by this
+	 * function. 
+	 * 
+	 * @param v is the vector to reach.
+	 * @return the rotation angle to turn this vector to reach
+	 * {@code v}.
+	 */
+	public float signedAngle(Vector2D v);
+
+	/** Turn this vector about the given rotation angle.
+	 * 
+	 * @param angle is the rotation angle in radians.
+	 */
+	public void turnVector(float angle);
+	
+	/** Replies the orientation angle on a trigonometric circle
+	 * that is corresponding to the given direction of this vector.
+	 * 
+	 * @return the angle on a trigonometric circle that is corresponding
+	 * to the given orientation vector.
+	 */
+	public float getOrientationAngle();
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/generic/Vector3D.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/generic/Vector3D.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/generic/Vector3D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,215 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.generic;
+
+
+/** 3D Vector.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Vector3D extends Tuple3D<Vector3D> {
+
+	/**
+	 * Sets the value of this tuple to the sum of tuples t1 and t2.
+	 * @param t1 the first tuple
+	 * @param t2 the second tuple
+	 */
+	public void add(Vector3D t1, Vector3D t2);
+
+
+	/**
+	 * Sets the value of this tuple to the sum of itself and t1.
+	 * @param t1 the other tuple
+	 */
+	public void add(Vector3D t1);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of tuple t1 plus tuple t2 (this = s*t1 + t2).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be multipled
+	 * @param t2 the tuple to be added
+	 */
+	public void scaleAdd(int s, Vector3D t1, Vector3D t2);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of tuple t1 plus tuple t2 (this = s*t1 + t2).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be multipled
+	 * @param t2 the tuple to be added
+	 */
+	public void scaleAdd(float s, Vector3D t1, Vector3D t2);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of itself and then adds tuple t1 (this = s*this + t1).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be added
+	 */
+	public void scaleAdd(int s, Vector3D t1);
+
+	/**
+	 * Sets the value of this tuple to the scalar multiplication
+	 * of itself and then adds tuple t1 (this = s*this + t1).
+	 * @param s the scalar value
+	 * @param t1 the tuple to be added
+	 */
+	public void scaleAdd(float s, Vector3D t1);
+
+	
+	/**
+	 * Sets the value of this tuple to the difference
+	 * of tuples t1 and t2 (this = t1 - t2).
+	 * @param t1 the first tuple
+	 * @param t2 the second tuple
+	 */
+	public void sub(Vector3D t1, Vector3D t2);
+
+	/**
+	 * Sets the value of this tuple to the difference
+	 * of tuples t1 and t2 (this = t1 - t2).
+	 * @param t1 the first tuple
+	 * @param t2 the second tuple
+	 */
+	public void sub(Point3D t1, Point3D t2);
+
+	/**
+	 * Sets the value of this tuple to the difference
+	 * of itself and t1 (this = this - t1).
+	 * @param t1 the other tuple
+	 */
+	public void sub(Vector3D t1);
+
+	/**
+	 * Computes the dot product of the this vector and vector v1.
+	 * @param v1 the other vector
+	 * @return the dot product.
+	 */
+	public float dot(Vector3D v1);
+
+	/**
+	 * Computes the cross product of the this vector and vector v1.
+	 * The coordinate system's standard depends on the underlying
+	 * implementation of the API.
+	 * One of {@link #crossLeftHand(Vector3D)} or {@link #crossRightHand(Vector3D)}
+	 * will be invoked by this function.
+	 * 
+	 * @param v1 the other vector
+	 * @return the dot product.
+	 * @see #crossLeftHand(Vector3D)
+	 * @see #crossRightHand(Vector3D)
+	 */
+	public Vector3D cross(Vector3D v1);
+
+	/**
+	 * Computes the cross product of the vectors v1 and v2 and
+	 * put the result in this vector.
+	 * The coordinate system's standard depends on the underlying
+	 * implementation of the API.
+	 * One of {@link #crossLeftHand(Vector3D, Vector3D)} or
+	 * {@link #crossRightHand(Vector3D, Vector3D)}
+	 * will be invoked by this function.
+	 * 
+	 * @param v1
+	 * @param v2
+	 * @see #crossLeftHand(Vector3D, Vector3D)
+	 * @see #crossRightHand(Vector3D, Vector3D)
+	 */
+	public void cross(Vector3D v1, Vector3D v2);
+
+	/**
+	 * Computes the cross product of the this vector and vector v1
+	 * as if the vectors are inside a left-hand coordinate system.
+	 * @param v1 the other vector
+	 * @return the dot product.
+	 */
+	public Vector3D crossLeftHand(Vector3D v1);
+
+	/**
+	 * Computes the cross product of the vectors v1 and v2
+	 * as if the vectors are inside a left-hand coordinate system;
+	 * and put the result in this vector.
+	 * @param v1
+	 * @param v2
+	 */
+	public void crossLeftHand(Vector3D v1, Vector3D v2);
+
+	/**
+	 * Computes the cross product of the this vector and vector v1
+	 * as if the vectors are inside a left-hand coordinate system.
+	 * @param v1 the other vector
+	 * @return the dot product.
+	 */
+	public Vector3D crossRightHand(Vector3D v1);
+
+	/**
+	 * Computes the cross product of the vectors v1 and v2
+	 * as if the vectors are inside a left-hand coordinate system;
+	 * and put the result in this vector.
+	 * @param v1
+	 * @param v2
+	 */
+	public void crossRightHand(Vector3D v1, Vector3D v2);
+
+	/**  
+	 * Returns the length of this vector.
+	 * @return the length of this vector
+	 */  
+	public float length();
+
+	/**  
+	 * Returns the squared length of this vector.
+	 * @return the squared length of this vector
+	 */  
+	public float lengthSquared();
+
+	/**
+	 * Sets the value of this vector to the normalization of vector v1.
+	 * @param v1 the un-normalized vector
+	 */  
+	public void normalize(Vector3D v1);
+
+	/**
+	 * Normalizes this vector in place.
+	 */  
+	public void normalize();
+
+
+	/**
+	 *   Returns the angle in radians between this vector and the vector
+	 *   parameter; the return value is constrained to the range [0,PI].
+	 *   @param v1    the other vector
+	 *   @return   the angle in radians in the range [0,PI]
+	 */
+	public float angle(Vector3D v1);
+
+	/** Turn this vector about the given rotation angle.
+	 * 
+	 * @param axis is the axis of rotation.
+	 * @param angle is the rotation angle in radians.
+	 */
+	public void turnVector(Vector3D axis, float angle);
+	
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/matrix/Matrix2f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/matrix/Matrix2f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/matrix/Matrix2f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,1255 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.matrix;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.MathUtil;
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object2d.Tuple2f;
+import org.arakhne.afc.math.continous.object2d.Vector2f;
+import org.arakhne.afc.math.generic.Vector2D;
+
+/**
+ * Is represented internally as a 2x2 floating point matrix. The mathematical
+ * representation is row major, as in traditional matrix mathematics.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Matrix2f implements Serializable, Cloneable, MathConstants {
+
+	private static final long serialVersionUID = -181335987517755500L;
+
+	/**
+	 * The first matrix element in the first row.
+	 */
+	public float m00;
+
+	/**
+	 * The second matrix element in the first row.
+	 */
+	public float m01;
+
+	/**
+	 * The first matrix element in the second row.
+	 */
+	public float m10;
+
+	/**
+	 * The second matrix element in the second row.
+	 */
+	public float m11;
+
+	/**
+	 * Constructs and initializes a Matrix2d from the specified nine values.
+	 * 
+	 * @param m00
+	 *            the [0][0] element
+	 * @param m01
+	 *            the [0][1] element
+	 * @param m10
+	 *            the [1][0] element
+	 * @param m11
+	 *            the [1][1] element
+	 */
+	public Matrix2f(float m00, float m01, float m10, float m11) {
+		this.m00 = m00;
+		this.m01 = m01;
+
+		this.m10 = m10;
+		this.m11 = m11;
+	}
+
+	/**
+	 * Constructs and initializes a Matrix2d from the specified nine- element
+	 * array.
+	 * 
+	 * @param v
+	 *            the array of length 4 containing in order
+	 */
+	public Matrix2f(float[] v) {
+		this.m00 = v[0];
+		this.m01 = v[1];
+
+		this.m10 = v[2];
+		this.m11 = v[3];
+	}
+
+	/**
+	 * Constructs a new matrix with the same values as the Matrix2d parameter.
+	 * 
+	 * @param m1
+	 *            the source matrix
+	 */
+	public Matrix2f(Matrix2f m1) {
+		this.m00 = m1.m00;
+		this.m01 = m1.m01;
+
+		this.m10 = m1.m10;
+		this.m11 = m1.m11;
+	}
+
+	/**
+	 * Constructs and initializes a Matrix2d to all zeros.
+	 */
+	public Matrix2f() {
+		this.m00 = 0f;
+		this.m01 = 0f;
+
+		this.m10 = 0f;
+		this.m11 = 0f;
+	}
+
+	/**
+	 * Returns a string that contains the values of this Matrix2d.
+	 * 
+	 * @return the String representation
+	 */
+	@Override
+	public String toString() {
+		return this.m00 + ", " //$NON-NLS-1$
+		+ this.m01 + "\n"  //$NON-NLS-1$
+		+ this.m10 + ", "  //$NON-NLS-1$
+		+ this.m11 + "\n";  //$NON-NLS-1$
+	}
+
+	/**
+	 * Sets this Matrix2d to identity.
+	 */
+	public final void setIdentity() {
+		this.m00 = 1f;
+		this.m01 = 0f;
+
+		this.m10 = 0f;
+		this.m11 = 1f;
+	}
+
+	/**
+	 * Sets the specified element of this Matrix2f 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;
+			default:
+				throw new ArrayIndexOutOfBoundsException();
+			}
+			break;
+
+		case 1:
+			switch (column) {
+			case 0:
+				this.m10 = value;
+				break;
+			case 1:
+				this.m11 = value;
+				break;
+			default:
+				throw new ArrayIndexOutOfBoundsException();
+			}
+			break;
+
+		default:
+			throw new ArrayIndexOutOfBoundsException();
+		}
+	}
+
+	/**
+	 * 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 float getElement(int row, int column) {
+		switch (row) {
+		case 0:
+			switch (column) {
+			case 0:
+				return (this.m00);
+			case 1:
+				return (this.m01);
+			default:
+				break;
+			}
+			break;
+		case 1:
+			switch (column) {
+			case 0:
+				return (this.m10);
+			case 1:
+				return (this.m11);
+			default:
+				break;
+			}
+			break;
+
+		default:
+			break;
+		}
+
+		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, Vector2D v) {
+		if (row == 0) {
+			v.set(this.m00, this.m01);
+		}
+		else if (row == 1) {
+			v.set(this.m10, this.m11);
+		}
+		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, float v[]) {
+		if (row == 0) {
+			v[0] = this.m00;
+			v[1] = this.m01;
+		}
+		else if (row == 1) {
+			v[0] = this.m10;
+			v[1] = this.m11;
+		}
+		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, Vector2f v) {
+		if (column == 0) {
+			v.set(this.m00, this.m10);
+		}
+		else if (column == 1) {
+			v.set(this.m01, this.m11);
+		}
+		else {
+			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, float v[]) {
+		if (column == 0) {
+			v[0] = this.m00;
+			v[1] = this.m10;
+		}
+		else if (column == 1) {
+			v[0] = this.m01;
+			v[1] = this.m11;
+		}
+		else {
+			throw new ArrayIndexOutOfBoundsException();
+		}
+	}
+
+	/**
+	 * Sets the specified row of this Matrix2d 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
+	 */
+	public final void setRow(int row, float x, float y) {
+		switch (row) {
+		case 0:
+			this.m00 = x;
+			this.m01 = y;
+			break;
+
+		case 1:
+			this.m10 = x;
+			this.m11 = y;
+			break;
+
+		default:
+			throw new ArrayIndexOutOfBoundsException();
+		}
+	}
+
+	/**
+	 * Sets the specified row of this Matrix2d 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, Vector2f v) {
+		switch (row) {
+		case 0:
+			this.m00 = v.getX();
+			this.m01 = v.getY();
+			break;
+
+		case 1:
+			this.m10 = v.getX();
+			this.m11 = v.getY();
+			break;
+
+		default:
+			throw new ArrayIndexOutOfBoundsException();
+		}
+	}
+
+	/**
+	 * Sets the specified row of this Matrix2d to the two values provided.
+	 * 
+	 * @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];
+			break;
+
+		case 1:
+			this.m10 = v[0];
+			this.m11 = v[1];
+			break;
+
+		default:
+			throw new ArrayIndexOutOfBoundsException();
+		}
+	}
+
+	/**
+	 * Sets the specified column of this Matrix2d to the two values provided.
+	 * 
+	 * @param column
+	 *            the column number to be modified (zero indexed)
+	 * @param x
+	 *            the first row element
+	 * @param y
+	 *            the second row element
+	 */
+	public final void setColumn(int column, float x, float y) {
+		switch (column) {
+		case 0:
+			this.m00 = x;
+			this.m10 = y;
+			break;
+
+		case 1:
+			this.m01 = x;
+			this.m11 = y;
+			break;
+
+		default:
+			throw new ArrayIndexOutOfBoundsException();
+		}
+	}
+
+	/**
+	 * Sets the specified column of this Matrix2d 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, Vector2f v) {
+		switch (column) {
+		case 0:
+			this.m00 = v.getX();
+			this.m10 = v.getY();
+			break;
+
+		case 1:
+			this.m01 = v.getX();
+			this.m11 = v.getY();
+			break;
+
+		default:
+			throw new ArrayIndexOutOfBoundsException();
+		}
+	}
+
+	/**
+	 * Sets the specified column of this Matrix2d to the two 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];
+			break;
+
+		case 1:
+			this.m01 = v[0];
+			this.m11 = v[1];
+			break;
+
+		default:
+			throw new ArrayIndexOutOfBoundsException();
+		}
+	}
+
+	/**
+	 * Adds a scalar to each component of this matrix.
+	 * 
+	 * @param scalar
+	 *            the scalar adder
+	 */
+	public final void add(float scalar) {
+		this.m00 += scalar;
+		this.m01 += scalar;
+
+		this.m10 += scalar;
+		this.m11 += 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, Matrix2f m1) {
+		this.m00 = m1.m00 + scalar;
+		this.m01 = m1.m01 + scalar;
+
+		this.m10 = m1.m10 + scalar;
+		this.m11 = m1.m11 + scalar;
+	}
+
+	/**
+	 * 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(Matrix2f m1, Matrix2f m2) {
+		this.m00 = m1.m00 + m2.m00;
+		this.m01 = m1.m01 + m2.m01;
+
+		this.m10 = m1.m10 + m2.m10;
+		this.m11 = m1.m11 + m2.m11;
+	}
+
+	/**
+	 * Sets the value of this matrix to the sum of itself and matrix m1.
+	 * 
+	 * @param m1
+	 *            the other matrix
+	 */
+	public final void add(Matrix2f m1) {
+		this.m00 += m1.m00;
+		this.m01 += m1.m01;
+
+		this.m10 += m1.m10;
+		this.m11 += m1.m11;
+	}
+
+	/**
+	 * 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(Matrix2f m1, Matrix2f m2) {
+		this.m00 = m1.m00 - m2.m00;
+		this.m01 = m1.m01 - m2.m01;
+
+		this.m10 = m1.m10 - m2.m10;
+		this.m11 = m1.m11 - m2.m11;
+	}
+
+	/**
+	 * 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(Matrix2f m1) {
+		this.m00 -= m1.m00;
+		this.m01 -= m1.m01;
+
+		this.m10 -= m1.m10;
+		this.m11 -= m1.m11;
+	}
+
+	/**
+	 * Sets the value of this matrix to its transpose.
+	 */
+	public final void transpose() {
+		float temp;
+		temp = this.m10;
+		this.m10 = this.m01;
+		this.m01 = 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(Matrix2f m1) {
+		if (this != m1) {
+			this.m00 = m1.m00;
+			this.m01 = m1.m10;
+
+			this.m10 = m1.m01;
+			this.m11 = m1.m11;
+		}
+		else {
+			transpose();
+		}
+	}
+
+	/**
+	 * Sets the value of this matrix to the value of the Matrix2d argument.
+	 * 
+	 * @param m1
+	 *            the source Matrix2d
+	 */
+	public final void set(Matrix2f m1) {
+		this.m00 = m1.m00;
+		this.m01 = m1.m01;
+
+		this.m10 = m1.m10;
+		this.m11 = m1.m11;
+	}
+
+	/**
+	 * Sets the values in this Matrix2d equal to the row-major array parameter
+	 * (ie, the first two elements of the array will be copied into the first
+	 * row of this matrix, etc.).
+	 * 
+	 * @param m
+	 *            the double precision array of length 4
+	 */
+	public final void set(float[] m) {
+		this.m00 = m[0];
+		this.m01 = m[1];
+
+		this.m10 = m[2];
+		this.m11 = m[4];
+	}
+
+	/**
+	 * Set the components of the matrix.
+	 * 
+	 * @param m00
+	 *            the [0][0] element
+	 * @param m01
+	 *            the [0][1] element
+	 * @param m10
+	 *            the [1][0] element
+	 * @param m11
+	 *            the [1][1] element
+	 */
+	public void set(float m00, float m01, float m10, float m11) {
+		this.m00 = m00;
+		this.m01 = m01;
+
+		this.m10 = m10;
+		this.m11 = m11;
+	}
+	
+	/**
+	 * Computes the determinant of this matrix.
+	 * 
+	 * @return the determinant of the matrix
+	 */
+	public final float determinant() {
+		return this.m00*this.m11 - this.m01*this.m10;
+	}
+
+	/**
+	 * Multiplies each element of this matrix by a scalar.
+	 * 
+	 * @param scalar
+	 *            The scalar multiplier.
+	 */
+	public final void mul(float scalar) {
+		this.m00 *= scalar;
+		this.m01 *= scalar;
+
+		this.m10 *= scalar;
+		this.m11 *= 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, Matrix2f m1) {
+		this.m00 = scalar * m1.m00;
+		this.m01 = scalar * m1.m01;
+
+		this.m10 = scalar * m1.m10;
+		this.m11 = scalar * m1.m11;
+	}
+
+	/**
+	 * Sets the value of this matrix to the result of multiplying itself with
+	 * matrix m1.
+	 * 
+	 * @param m1
+	 *            the other matrix
+	 */
+	public final void mul(Matrix2f m1) {
+		float _m00, _m01, _m10, _m11;
+
+		_m00 = this.m00 * m1.m00 + this.m01 * m1.m10;
+		_m01 = this.m00 * m1.m01 + this.m01 * m1.m11;
+
+		_m10 = this.m10 * m1.m00 + this.m11 * m1.m10;
+		_m11 = this.m10 * m1.m01 + this.m11 * m1.m11;
+
+		this.m00 = _m00;
+		this.m01 = _m01;
+		this.m10 = _m10;
+		this.m11 = _m11;
+	}
+
+	/**
+	 * Multiply this matrix by the given vector and replies the resulting vector.
+	 * 
+	 * @param v
+	 * @return this * v
+	 */
+	public final Vector2f mul(Vector2D v) {
+		Vector2f r = new Vector2f();
+		r.set(
+				this.m00 * v.getX() + this.m01 * v.getY(),
+				this.m10 * v.getX() + this.m11 * v.getY());
+		return r;
+	}
+
+	/**
+	 * 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(Matrix2f m1, Matrix2f m2) {
+		if (this != m1 && this != m2) {
+			this.m00 = m1.m00 * m2.m00 + m1.m01 * m2.m10;
+			this.m01 = m1.m00 * m2.m01 + m1.m01 * m2.m11;
+
+			this.m10 = m1.m10 * m2.m00 + m1.m11 * m2.m10;
+			this.m11 = m1.m10 * m2.m01 + m1.m11 * m2.m11;
+		}
+		else {
+			float _m00, _m01, _m10, _m11; // vars for temp result matrix
+
+			_m00 = m1.m00 * m2.m00 + m1.m01 * m2.m10;
+			_m01 = m1.m00 * m2.m01 + m1.m01 * m2.m11;
+
+			_m10 = m1.m10 * m2.m00 + m1.m11 * m2.m10;
+			_m11 = m1.m10 * m2.m01 + m1.m11 * m2.m11;
+
+			this.m00 = _m00;
+			this.m01 = _m01;
+			this.m10 = _m10;
+			this.m11 = _m11;
+		}
+	}
+
+	/**
+	 * 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(Matrix2f m1, Matrix2f m2) {
+		if (this != m1 && this != m2) {
+			this.m00 = m1.m00 * m2.m00 + m1.m10 * m2.m01;
+			this.m01 = m1.m00 * m2.m10 + m1.m10 * m2.m11;
+
+			this.m10 = m1.m01 * m2.m00 + m1.m11 * m2.m01;
+			this.m11 = m1.m01 * m2.m10 + m1.m11 * m2.m11;
+		}
+		else {
+			float _m00, _m01, _m10, _m11; // vars for temp result matrix
+
+			_m00 = m1.m00 * m2.m00 + m1.m10 * m2.m01;
+			_m01 = m1.m00 * m2.m10 + m1.m10 * m2.m11;
+
+			_m10 = m1.m01 * m2.m00 + m1.m11 * m2.m01;
+			_m11 = m1.m01 * m2.m10 + m1.m11 * m2.m11;
+
+			this.m00 = _m00;
+			this.m01 = _m01;
+			this.m10 = _m10;
+			this.m11 = _m11;
+		}
+
+	}
+
+	/**
+	 * 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(Matrix2f m1, Matrix2f m2) {
+		if (this != m1 && this != m2) {
+			this.m00 = m1.m00 * m2.m00 + m1.m01 * m2.m01;
+			this.m01 = m1.m00 * m2.m10 + m1.m01 * m2.m11;
+
+			this.m10 = m1.m10 * m2.m00 + m1.m11 * m2.m01;
+			this.m11 = m1.m10 * m2.m10 + m1.m11 * m2.m11;
+		}
+		else {
+			float _m00, _m01, _m10, _m11; // vars for temp result matrix
+
+			_m00 = m1.m00 * m2.m00 + m1.m01 * m2.m01;
+			_m01 = m1.m00 * m2.m10 + m1.m01 * m2.m11;
+
+			_m10 = m1.m10 * m2.m00 + m1.m11 * m2.m01;
+			_m11 = m1.m10 * m2.m10 + m1.m11 * m2.m11;
+
+			this.m00 = _m00;
+			this.m01 = _m01;
+			this.m10 = _m10;
+			this.m11 = _m11;
+		}
+	}
+
+	/**
+	 * 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(Matrix2f m1, Matrix2f m2) {
+		if (this != m1 && this != m2) {
+			this.m00 = m1.m00 * m2.m00 + m1.m10 * m2.m10;
+			this.m01 = m1.m00 * m2.m01 + m1.m10 * m2.m11;
+
+			this.m10 = m1.m01 * m2.m00 + m1.m11 * m2.m10;
+			this.m11 = m1.m01 * m2.m01 + m1.m11 * m2.m11;
+		}
+		else {
+			float _m00, _m01, _m10, _m11; // vars for temp result matrix
+
+			_m00 = m1.m00 * m2.m00 + m1.m10 * m2.m10;
+			_m01 = m1.m00 * m2.m01 + m1.m10 * m2.m11;
+
+			_m10 = m1.m01 * m2.m00 + m1.m11 * m2.m10;
+			_m11 = m1.m01 * m2.m01 + m1.m11 * m2.m11;
+
+			this.m00 = _m00;
+			this.m01 = _m01;
+			this.m10 = _m10;
+			this.m11 = _m11;
+		}
+	}
+
+	/**
+	 * Perform cross product normalization of this matrix.
+	 */
+	public final void normalizeCP() {
+		double mag = 1.0 / Math.sqrt(this.m00 * this.m00 + this.m10 * this.m10);
+		this.m00 = (float)(this.m00 * mag);
+		this.m10 = (float)(this.m10 * mag);
+
+		mag = 1.0 / Math.sqrt(this.m01 * this.m01 + this.m11 * this.m11);
+		this.m01 = (float)(this.m01 * mag);
+		this.m11 = (float)(this.m11 * mag);
+	}
+
+	/**
+	 * 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(Matrix2f m1) {
+		double mag = 1.0 / Math.sqrt(m1.m00 * m1.m00 + m1.m10 * m1.m10);
+		this.m00 = (float)(m1.m00 * mag);
+		this.m10 = (float)(m1.m10 * mag);
+
+		mag = 1.0 / Math.sqrt(m1.m01 * m1.m01 + m1.m11 * m1.m11);
+		this.m01 = (float)(m1.m01 * mag);
+		this.m11 = (float)(m1.m11 * mag);
+	}
+
+	/**
+	 * Returns true if all of the data members of Matrix2d m1 are equal to the
+	 * corresponding data members in this Matrix2d.
+	 * 
+	 * @param m1
+	 *            the matrix with which the comparison is made
+	 * @return true or false
+	 */
+	public boolean equals(Matrix2f m1) {
+		try {
+			return (this.m00 == m1.m00 && this.m01 == m1.m01
+					&& this.m10 == m1.m10
+					&& this.m11 == m1.m11);
+		}
+		catch (NullPointerException e2) {
+			return false;
+		}
+	}
+
+	/**
+	 * Returns true if the Object t1 is of type Matrix2d and all of the data
+	 * members of t1 are equal to the corresponding data members in this
+	 * Matrix2d.
+	 * 
+	 * @param t1
+	 *            the matrix with which the comparison is made
+	 * @return true or false
+	 */
+	@Override
+	public boolean equals(Object t1) {
+		try {
+			Matrix2f m2 = (Matrix2f) t1;
+			return (this.m00 == m2.m00 && this.m01 == m2.m01
+					&& this.m10 == m2.m10
+					&& this.m11 == m2.m11);
+		}
+		catch (ClassCastException e1) {
+			return false;
+		}
+		catch (NullPointerException e2) {
+			return false;
+		}
+	}
+
+	/**
+	 * Returns a hash code value based on the data values in this object. Two
+	 * different Matrix2d objects with identical data values (i.e.,
+	 * Matrix2d.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
+	 */
+	@Override
+	public int hashCode() {
+		long bits = 1L;
+		bits = 31L * bits + floatToIntBits(this.m00);
+		bits = 31L * bits + floatToIntBits(this.m01);
+		bits = 31L * bits + floatToIntBits(this.m10);
+		bits = 31L * bits + floatToIntBits(this.m11);
+		return (int) (bits ^ (bits >> 32));
+	}
+
+	private static int floatToIntBits(float d) {
+		// Check for +0 or -0
+		if (d == 0f) {
+			return 0;
+		}
+		return Float.floatToIntBits(d);
+	}
+	
+	/**
+	 * Sets this matrix to all zeros.
+	 */
+	public final void setZero() {
+		this.m00 = 0f;
+		this.m01 = 0f;
+
+		this.m10 = 0f;
+		this.m11 = 0f;
+	}
+
+	/**
+	 * Sets this matrix as diagonal.
+	 * 
+	 * @param m00
+	 *            the first element of the diagonal
+	 * @param m11
+	 *            the second element of the diagonal
+	 */
+	public final void setDiagonal(float m00, float m11) {
+		this.m00 = m00;
+		this.m01 = 0f;
+		this.m10 = 0f;
+		this.m11 = m11;
+	}
+
+	/**
+	 * Negates the value of this matrix: this = -this.
+	 */
+	public final void negate() {
+		this.m00 = -this.m00;
+		this.m01 = -this.m01;
+
+		this.m10 = -this.m10;
+		this.m11 = -this.m11;
+	}
+
+	/**
+	 * Sets the value of this matrix equal to the negation of of the Matrix2d
+	 * parameter.
+	 * 
+	 * @param m1
+	 *            the source matrix
+	 */
+	public final void negate(Matrix2f m1) {
+		this.m00 = -m1.m00;
+		this.m01 = -m1.m01;
+
+		this.m10 = -m1.m10;
+		this.m11 = -m1.m11;
+	}
+
+	/**
+	 * 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 java.lang.Cloneable
+	 */
+	@Override
+	public Matrix2f clone() {
+		Matrix2f m1 = null;
+		try {
+			m1 = (Matrix2f) 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;
+	}
+
+	/**
+	 * Get the first matrix element in the first row.
+	 * 
+	 * @return Returns the m00.
+	 */
+	public final float getM00() {
+		return this.m00;
+	}
+
+	/**
+	 * Set the first matrix element in the first row.
+	 * 
+	 * @param m00
+	 *            The m00 to set.
+	 */
+	public final void setM00(float m00) {
+		this.m00 = m00;
+	}
+
+	/**
+	 * Get the second matrix element in the first row.
+	 * 
+	 * @return Returns the m01.
+	 */
+	public final float getM01() {
+		return this.m01;
+	}
+
+	/**
+	 * Set the second matrix element in the first row.
+	 * 
+	 * @param m01
+	 *            The m01 to set.
+	 */
+	public final void setM01(float m01) {
+		this.m01 = m01;
+	}
+
+	/**
+	 * Get first matrix element in the second row.
+	 * 
+	 * @return Returns the m10.
+	 */
+	public final float getM10() {
+		return this.m10;
+	}
+
+	/**
+	 * Set first matrix element in the second row.
+	 * 
+	 * @param m10
+	 *            The m10 to set.
+	 */
+	public final void setM10(float m10) {
+		this.m10 = m10;
+	}
+
+	/**
+	 * Get second matrix element in the second row.
+	 * 
+	 * @return Returns the m11.
+	 */
+	public final float getM11() {
+		return this.m11;
+	}
+
+	/**
+	 * Set the second matrix element in the second row.
+	 * 
+	 * @param m11
+	 *            The m11 to set.
+	 */
+	public final void setM11(float m11) {
+		this.m11 = m11;
+	}
+
+	/** Set this matrix with the covariance matrix's elements for the given
+	 * set of tuples.
+	 * 
+	 * @param tuples
+	 * @return the mean of the tuples.
+	 */
+	public final Vector2f cov(Vector2f... tuples) {
+		return cov(Arrays.asList(tuples));
+	}
+
+	/** Set this matrix with the covariance matrix's elements for the given
+	 * set of tuples.
+	 * 
+	 * @param tuples
+	 * @return the mean of the tuples.
+	 */
+	public final Vector2f cov(Point2f... tuples) {
+		return cov(Arrays.asList(tuples));
+	}
+	
+	/** Set this matrix with the covariance matrix's elements for the given
+	 * set of tuples.
+	 * 
+	 * @param tuples
+	 * @return the mean of the tuples.
+	 */
+	public Vector2f cov(Iterable<? extends Tuple2f<?>> tuples) {
+		setZero();
+
+		// Compute the mean m
+		Vector2f m = new Vector2f();
+		int count = 0;
+		for(Tuple2f<?> p : tuples) {
+			m.add(p.getX(), p.getY());
+			++count;
+		}
+
+		if (count==0) return null;
+
+		m.scale(1f/count);
+
+		// Compute the covariance term [Gottshalk2000]
+		// c_ij = sum(p'_i * p'_j) / n
+		// c_ij = sum((p_i - m_i) * (p_j - m_j)) / n
+		for(Tuple2f<?> p : tuples) {
+			this.m00 += (p.getX() - m.getX()) * (p.getX() - m.getX());
+			this.m01 += (p.getX() - m.getX()) * (p.getY() - m.getY()); // same as m10
+			//cov.m10 += (p.getY() - m.getY()) * (p.getX() - m.getX()); // same as m01
+			this.m11 += (p.getY() - m.getY()) * (p.getY() - m.getY());
+		}
+
+		this.m00 /= count;
+		this.m01 /= count;
+		this.m10 = this.m01;
+		this.m11 /= count;
+
+		return m;
+	}
+
+	/** Replies if the matrix is symmetric.
+	 * 
+	 * @return <code>true</code> if the matrix is symmetric, otherwise
+	 * <code>false</code>
+	 */
+	public boolean isSymmetric() {
+		return	this.m01 == this.m10;
+	}
+	
+	/**
+	 * Compute the eigenvectors of this matrix, assuming it is a symmetric matrix
+	 * according to the Jacobi Cyclic Method.
+	 * 
+	 * @param eigenVectors are the matrix of vectors to fill. Eigen vectors are the
+	 * columns of the matrix.
+	 * @return the eigenvalues which are corresponding to the <var>eigenVectors</var> columns.
+	 * @see #eigenVectorsOfSymmetricMatrix(Matrix2f) 
+	 */
+	public float[] eigenVectorsOfSymmetricMatrix(Matrix2f eigenVectors) {
+		assert(eigenVectors!=null);
+
+		// Copy values up to the diagonal
+		float m11 = getElement(0,0);
+		float m12 = getElement(0,1);
+		float m22 = getElement(1,1);
+		
+		boolean sweepsConsumed = true;
+		int i;
+		float u, u2, u2p1, t, c, s;
+		float ri0, ri1;
+		
+		for(int a=0; a<JACOBI_MAX_SWEEPS; ++a) {
+			
+			// Exit loop if off-diagonal entries are small enough
+			if ((Math.abs(m12) < MathConstants.EPSILON)) {
+				sweepsConsumed = false;
+				break;
+			}
+			
+			// Annihilate (1,2) entry
+			if (m12 != 0.) {
+				u = (m22 - m11) *.5f / m12;
+				u2 = u*u;
+				u2p1 = u2 + 1f;
+				
+				if (u2p1!=u2)
+					t = (float)(Math.signum(u) * (Math.sqrt(u2p1) - Math.abs(u)));
+				else
+					t = .5f / u;
+				
+				c = 1f / (float)Math.sqrt(t*t + 1);
+				s = c * t;
+				
+				m11 -= t * m12;
+				m22 += t * m12;
+				m12 = 0f;
+				
+				for(i=0; i<2; ++i) {
+					ri0 = eigenVectors.getElement(i,0);
+					ri1 = eigenVectors.getElement(i,1);
+					eigenVectors.setElement(i, 0, c * ri0 - s * ri1);
+					eigenVectors.setElement(i, 1, s * ri0 + c * ri1);
+				}
+			}
+		}
+		
+		assert(!sweepsConsumed) : "Sweep count consumed during eigenvector computation"; //$NON-NLS-1$
+		
+		// eigenvalues are on the diagonal
+		return new float[] { m11, m22 };
+	}
+	
+	/** Replies if the matrix is identity.
+	 * <p>
+	 * This function uses the equal-to-zero test with the error {@link MathConstants#EPSILON}.
+	 * 
+	 * @return <code>true</code> if the matrix is identity; <code>false</code> otherwise.
+	 * @see MathUtil#isEpsilonZero(float)
+	 * @see MathUtil#isEpsilonEqual(float, float)
+	 */
+	public boolean isIdentity() {
+		return MathUtil.isEpsilonEqual(this.m00, 1f)
+				&& MathUtil.isEpsilonZero(this.m01)
+				&& MathUtil.isEpsilonZero(this.m10)
+				&& MathUtil.isEpsilonEqual(this.m11, 1f);
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/matrix/Matrix3f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/matrix/Matrix3f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/matrix/Matrix3f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,3497 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.matrix;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.MathUtil;
+import org.arakhne.afc.math.continous.object3d.Point3f;
+import org.arakhne.afc.math.continous.object3d.Tuple3f;
+import org.arakhne.afc.math.continous.object3d.Vector3f;
+import org.arakhne.vmutil.locale.Locale;
+
+/**
+ * Is represented internally as a 3x3 floating point matrix. The mathematical
+ * representation is row major, as in traditional matrix mathematics.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Matrix3f implements Serializable, Cloneable, MathConstants {
+
+	private static final long serialVersionUID = -7386754038391115819L;
+
+	/**
+	 * The first matrix element in the first row.
+	 */
+	public float m00;
+
+	/**
+	 * The second matrix element in the first row.
+	 */
+	public float m01;
+
+	/**
+	 * The third matrix element in the first row.
+	 */
+	public float m02;
+
+	/**
+	 * The first matrix element in the second row.
+	 */
+	public float m10;
+
+	/**
+	 * The second matrix element in the second row.
+	 */
+	public float m11;
+
+	/**
+	 * The third matrix element in the second row.
+	 */
+	public float m12;
+
+	/**
+	 * The first matrix element in the third row.
+	 */
+	public float m20;
+
+	/**
+	 * The second matrix element in the third row.
+	 */
+	public float m21;
+
+	/**
+	 * The third matrix element in the third row.
+	 */
+	public float m22;
+
+	/**
+	 * Constructs and initializes a Matrix3f 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 Matrix3f(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float 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 Matrix3f from the specified nine- element
+	 * array.
+	 * 
+	 * @param v
+	 *            the array of length 9 containing in order
+	 */
+	public Matrix3f(float[] 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 Matrix3f parameter.
+	 * 
+	 * @param m1
+	 *            the source matrix
+	 */
+	public Matrix3f(Matrix3f 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;
+	}
+
+	/**
+	 * Constructs and initializes a Matrix3f to all zeros.
+	 */
+	public Matrix3f() {
+		this.m00 = 0f;
+		this.m01 = 0f;
+		this.m02 = 0f;
+
+		this.m10 = 0f;
+		this.m11 = 0f;
+		this.m12 = 0f;
+
+		this.m20 = 0f;
+		this.m21 = 0f;
+		this.m22 = 0f;
+	}
+
+	/**
+	 * Returns a string that contains the values of this Matrix3f.
+	 * 
+	 * @return the String representation
+	 */
+	@Override
+	public String toString() {
+		return this.m00 + ", " //$NON-NLS-1$
+				+ this.m01 + ", "  //$NON-NLS-1$
+				+ this.m02 + "\n"  //$NON-NLS-1$
+				+ this.m10 + ", "  //$NON-NLS-1$
+				+ this.m11 + ", "  //$NON-NLS-1$
+				+ this.m12 + "\n"  //$NON-NLS-1$
+				+ this.m20 + ", " //$NON-NLS-1$
+				+ this.m21 + ", "  //$NON-NLS-1$
+				+ this.m22 + "\n"; //$NON-NLS-1$
+	}
+
+	/**
+	 * Sets this Matrix3f to identity.
+	 */
+	public final void setIdentity() {
+		this.m00 = 1f;
+		this.m01 = 0f;
+		this.m02 = 0f;
+
+		this.m10 = 0f;
+		this.m11 = 1f;
+		this.m12 = 0f;
+
+		this.m20 = 0f;
+		this.m21 = 0f;
+		this.m22 = 1f;
+	}
+
+	/**
+	 * 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, 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;
+			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();
+		}
+	}
+
+	/**
+	 * 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 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);
+			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 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, Vector3f v) {
+		if (row == 0) {
+			v.set(this.m00, this.m01, this.m02);
+		} else if (row == 1) {
+			v.set(this.m10, this.m11, this.m12);
+		} else if (row == 2) {
+			v.set(this.m20, this.m21, this.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, float v[]) {
+		if (row == 0) {
+			v[0] = this.m00;
+			v[1] = this.m01;
+			v[2] = this.m02;
+		} else if (row == 1) {
+			v[0] = this.m10;
+			v[1] = this.m11;
+			v[2] = this.m12;
+		} else if (row == 2) {
+			v[0] = this.m20;
+			v[1] = this.m21;
+			v[2] = this.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, Vector3f v) {
+		if (column == 0) {
+			v.set(this.m00, this.m10, this.m20);
+		} else if (column == 1) {
+			v.set(this.m01, this.m11, this.m21);
+		} else if (column == 2) {
+			v.set(this.m02, this.m12, this.m22);
+		} else {
+			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, float v[]) {
+		if (column == 0) {
+			v[0] = this.m00;
+			v[1] = this.m10;
+			v[2] = this.m20;
+		} else if (column == 1) {
+			v[0] = this.m01;
+			v[1] = this.m11;
+			v[2] = this.m21;
+		} else if (column == 2) {
+			v[0] = this.m02;
+			v[1] = this.m12;
+			v[2] = this.m22;
+		} else {
+			throw new ArrayIndexOutOfBoundsException();
+		}
+
+	}
+
+	/**
+	 * Sets the specified row of this Matrix3f 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, float x, float y, float 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 Matrix3f 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, Vector3f v) {
+		switch (row) {
+		case 0:
+			this.m00 = v.getX();
+			this.m01 = v.getY();
+			this.m02 = v.getZ();
+			break;
+
+		case 1:
+			this.m10 = v.getX();
+			this.m11 = v.getY();
+			this.m12 = v.getZ();
+			break;
+
+		case 2:
+			this.m20 = v.getX();
+			this.m21 = v.getY();
+			this.m22 = v.getZ();
+			break;
+
+		default:
+			throw new ArrayIndexOutOfBoundsException();
+		}
+	}
+
+	/**
+	 * Sets the specified row of this Matrix3f 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, float 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 column of this Matrix3f 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, float x, float y, float 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 Matrix3f 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, Vector3f v) {
+		switch (column) {
+		case 0:
+			this.m00 = v.getX();
+			this.m10 = v.getY();
+			this.m20 = v.getZ();
+			break;
+
+		case 1:
+			this.m01 = v.getX();
+			this.m11 = v.getY();
+			this.m21 = v.getZ();
+			break;
+
+		case 2:
+			this.m02 = v.getX();
+			this.m12 = v.getY();
+			this.m22 = v.getZ();
+			break;
+
+		default:
+			throw new ArrayIndexOutOfBoundsException();
+		}
+	}
+
+	/**
+	 * Sets the specified column of this Matrix3f 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, float 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();
+		}
+	}
+
+	/**
+	 * Adds a scalar to each component of this matrix.
+	 * 
+	 * @param scalar
+	 *            the scalar adder
+	 */
+	public final void add(float scalar) {
+		this.m00 += scalar;
+		this.m01 += scalar;
+		this.m02 += scalar;
+
+		this.m10 += scalar;
+		this.m11 += scalar;
+		this.m12 += scalar;
+
+		this.m20 += scalar;
+		this.m21 += scalar;
+		this.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(float scalar, Matrix3f 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 matrix sum of matrices m1 and m2.
+	 * 
+	 * @param m1
+	 *            the first matrix
+	 * @param m2
+	 *            the second matrix
+	 */
+	public final void add(Matrix3f m1, Matrix3f 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;
+	}
+
+	/**
+	 * Sets the value of this matrix to the sum of itself and matrix m1.
+	 * 
+	 * @param m1
+	 *            the other matrix
+	 */
+	public final void add(Matrix3f 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(Matrix3f m1, Matrix3f 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;
+	}
+
+	/**
+	 * 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(Matrix3f 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 its transpose.
+	 */
+	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.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(Matrix3f 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();
+	}
+
+	/**
+	 * Sets the value of this matrix to the float value of the Matrix3f
+	 * argument.
+	 * 
+	 * @param m1
+	 *            the Matrix3f to be converted to float
+	 */
+	public final void set(Matrix3f 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 values in this Matrix3f 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 float precision array of length 9
+	 */
+	public final void set(float[] m) {
+		this.m00 = m[0];
+		this.m01 = m[1];
+		this.m02 = m[2];
+
+		this.m10 = m[3];
+		this.m11 = m[4];
+		this.m12 = m[5];
+
+		this.m20 = m[6];
+		this.m21 = m[7];
+		this.m22 = m[8];
+
+	}
+	
+	/**
+	 * Set the components of the matrix.
+	 * 
+	 * @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 void set(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float 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 matrix inverse of the passed matrix
+	 * m1.
+	 * 
+	 * @param m1
+	 *            the matrix to be inverted
+	 */
+	public void invert(Matrix3f m1) {
+		invertGeneral(m1);
+	}
+
+	/**
+	 * Inverts this matrix in place.
+	 */
+	public void invert() {
+		invertGeneral(this);
+	}
+
+	/**
+	 * 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(Matrix3f m1) {
+		float result[] = new float[9];
+		int row_perm[] = new int[3];
+		int i;
+		float[] tmp = new float[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 (!luDecomposition(tmp, row_perm)) {
+			throw new SingularMatrixException(Locale.getString("NOT_INVERTABLE_MATRIX")); //$NON-NLS-1$
+		}
+
+		// Perform back substitution on the identity matrix
+		for (i = 0; i < 9; ++i)
+			result[i] = 0f;
+		result[0] = 1f;
+		result[4] = 1f;
+		result[8] = 1f;
+		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];
+
+	}
+
+	/**
+	 * Given a 3x3 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[3]" 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 float.
+	 * 
+	 * This function is similar to luDecomposition, except that it is tuned
+	 * specifically for 3x3 matrices.
+	 * 
+	 * @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.
+	//
+	private static boolean luDecomposition(float[] matrix0, int[] row_perm) {
+
+		float row_scale[] = new float[3];
+
+		// Determine implicit scaling information by looping over rows
+		{
+			int i, j;
+			int ptr, rs;
+			float big, temp;
+
+			ptr = 0;
+			rs = 0;
+
+			// For each row ...
+			i = 3;
+			while (i-- != 0) {
+				big = 0f;
+
+				// For each column, find the largest element in the row
+				j = 3;
+				while (j-- != 0) {
+					temp = matrix0[ptr++];
+					temp = Math.abs(temp);
+					if (temp > big) {
+						big = temp;
+					}
+				}
+
+				// Is the matrix singular?
+				if (big == 0f) {
+					return false;
+				}
+				row_scale[rs++] = 1f / big;
+			}
+		}
+
+		{
+			int j;
+			int mtx;
+
+			mtx = 0;
+
+			// For all columns, execute Crout's method
+			for (j = 0; j < 3; ++j) {
+				int i, imax, k;
+				int target, p1, p2;
+				float sum, big, temp;
+
+				// Determine elements of upper diagonal matrix U
+				for (i = 0; i < j; ++i) {
+					target = mtx + (3 * i) + j;
+					sum = matrix0[target];
+					k = i;
+					p1 = mtx + (3 * i);
+					p2 = mtx + j;
+					while (k-- != 0) {
+						sum -= matrix0[p1] * matrix0[p2];
+						++p1;
+						p2 += 3;
+					}
+					matrix0[target] = sum;
+				}
+
+				// Search for largest pivot element and calculate
+				// intermediate elements of lower diagonal matrix L.
+				big = 0f;
+				imax = -1;
+				for (i = j; i < 3; ++i) {
+					target = mtx + (3 * i) + j;
+					sum = matrix0[target];
+					k = j;
+					p1 = mtx + (3 * i);
+					p2 = mtx + j;
+					while (k-- != 0) {
+						sum -= matrix0[p1] * matrix0[p2];
+						++p1;
+						p2 += 3;
+					}
+					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 = 3;
+					p1 = mtx + (3 * imax);
+					p2 = mtx + (3 * 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 + (3 * j) + j)] == 0f) {
+					return false;
+				}
+
+				// Divide elements of lower diagonal matrix L by pivot
+				if (j != (3 - 1)) {
+					temp = 1f / (matrix0[(mtx + (3 * j) + j)]);
+					target = mtx + (3 * (j + 1)) + j;
+					i = 2 - j;
+					while (i-- != 0) {
+						matrix0[target] *= temp;
+						target += 3;
+					}
+				}
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * 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.
+	//
+	private static void luBacksubstitution(float[] matrix1, int[] row_perm,
+			float[] 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) {
+				float 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 != 0f) {
+					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];
+
+		}
+	}
+
+	/**
+	 * Computes the determinant of this matrix.
+	 * 
+	 * @return the determinant of the matrix
+	 */
+	public final float determinant() {
+		/* det(A,B,C) = det( [ x1 x2 x3 ]
+		 *                   [ y1 y2 y3 ]
+		 *                   [ z1 z2 z3 ] )
+		 */
+		return
+				this.m00 * (this.m11 * this.m22 - this.m21 * this.m12)
+				+ this.m10 * (this.m21 * this.m02 - this.m01 * this.m22)
+				+ this.m20 * (this.m01 * this.m12 - this.m11 * this.m02);
+	}
+
+	/**
+	 * Multiplies each element of this matrix by a scalar.
+	 * 
+	 * @param scalar
+	 *            The scalar multiplier.
+	 */
+	public final void mul(float scalar) {
+		this.m00 *= scalar;
+		this.m01 *= scalar;
+		this.m02 *= scalar;
+
+		this.m10 *= scalar;
+		this.m11 *= scalar;
+		this.m12 *= scalar;
+
+		this.m20 *= scalar;
+		this.m21 *= scalar;
+		this.m22 *= scalar;
+	}
+
+	/** Multiply this matrix by the given vector.
+	 * 
+	 * @param v
+	 * @return the vector resulting of <code>this * v</code>.
+	 */
+	public Vector3f mul(Vector3f v) {
+		return new Vector3f(
+				this.m00 * v.getX() + this.m01 * v.getY() + this.m02 * v.getZ(),
+				this.m10 * v.getX() + this.m11 * v.getY() + this.m12 * v.getZ(),
+				this.m20 * v.getX() + this.m21 * v.getY() + this.m22 * v.getZ());
+	}
+
+	/** Multiply the transposing of this matrix by the given vector.
+	 * 
+	 * @param v
+	 * @return the vector resulting of <code>transpose(this) * v</code>.
+	 */
+	public Vector3f mulTransposeLeft(Vector3f v) {
+		return new Vector3f(
+				this.m00 * v.getX() + this.m10 * v.getY() + this.m20 * v.getZ(),
+				this.m01 * v.getX() + this.m11 * v.getY() + this.m21 * v.getZ(),
+				this.m02 * v.getX() + this.m12 * v.getY() + this.m22 * v.getZ());
+	}
+
+	/**
+	 * 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, Matrix3f 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(Matrix3f m1) {
+		float _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(Matrix3f m1, Matrix3f 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 {
+			float _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(Matrix3f m1) {
+
+		float[] tmp = new float[9]; // scratch matrix
+		float[] tmp_rot = new float[9]; // scratch matrix
+		float[] tmp_scale = new float[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(Matrix3f m1, Matrix3f m2) {
+
+		float[] tmp = new float[9]; // scratch matrix
+		float[] tmp_rot = new float[9]; // scratch matrix
+		float[] tmp_scale = new float[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(Matrix3f m1, Matrix3f 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 {
+			float _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 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(Matrix3f m1, Matrix3f 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 {
+			float _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;
+		}
+	}
+
+	/**
+	 * 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(Matrix3f m1, Matrix3f 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 {
+			float _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;
+		}
+	}
+
+	/**
+	 * 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(Matrix3f m1) {
+
+		float[] tmp = new float[9]; // scratch matrix
+		float[] tmp_rot = new float[9]; // scratch matrix
+		float[] tmp_scale = new float[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() {
+		float mag = 1f / (float)Math.sqrt(this.m00 * this.m00 + this.m10 * this.m10 + this.m20 * this.m20);
+		this.m00 = this.m00 * mag;
+		this.m10 = this.m10 * mag;
+		this.m20 = this.m20 * mag;
+
+		mag = 1f / (float)Math.sqrt(this.m01 * this.m01 + this.m11 * this.m11 + this.m21 * this.m21);
+		this.m01 = this.m01 * mag;
+		this.m11 = this.m11 * mag;
+		this.m21 = this.m21 * mag;
+
+		this.m02 = this.m10 * this.m21 - this.m11 * this.m20;
+		this.m12 = this.m01 * this.m20 - this.m00 * this.m21;
+		this.m22 = this.m00 * this.m11 - this.m01 * this.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(Matrix3f m1) {
+		float mag = 1f / (float)Math.sqrt(m1.m00 * m1.m00 + m1.m10 * m1.m10 + m1.m20
+				* m1.m20);
+		this.m00 = m1.m00 * mag;
+		this.m10 = m1.m10 * mag;
+		this.m20 = m1.m20 * mag;
+
+		mag = 1f / (float)Math.sqrt(m1.m01 * m1.m01 + m1.m11 * m1.m11 + m1.m21
+				* m1.m21);
+		this.m01 = m1.m01 * mag;
+		this.m11 = m1.m11 * mag;
+		this.m21 = m1.m21 * mag;
+
+		this.m02 = this.m10 * this.m21 - this.m11 * this.m20;
+		this.m12 = this.m01 * this.m20 - this.m00 * this.m21;
+		this.m22 = this.m00 * this.m11 - this.m01 * this.m10;
+	}
+
+	/**
+	 * Returns true if all of the data members of Matrix3f m1 are equal to the
+	 * corresponding data members in this Matrix3f.
+	 * 
+	 * @param m1
+	 *            the matrix with which the comparison is made
+	 * @return true or false
+	 */
+	public boolean equals(Matrix3f 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 Matrix3f and all of the data
+	 * members of t1 are equal to the corresponding data members in this
+	 * Matrix3f.
+	 * 
+	 * @param t1
+	 *            the matrix with which the comparison is made
+	 * @return true or false
+	 */
+	@Override
+	public boolean equals(Object t1) {
+		try {
+			Matrix3f m2 = (Matrix3f) 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;
+		}
+
+	}
+
+	/**
+	 * 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
+	 * @return <code>true</code> if this matrix is equals to the specified matrix at epsilon.
+	 */
+	public boolean epsilonEquals(Matrix3f m1, float epsilon) {
+		float diff;
+
+		diff = this.m00 - m1.m00;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m01 - m1.m01;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m02 - m1.m02;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m10 - m1.m10;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m11 - m1.m11;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m12 - m1.m12;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m20 - m1.m20;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m21 - m1.m21;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m22 - m1.m22;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		return true;
+	}
+
+	/**
+	 * Returns a hash code value based on the data values in this object. Two
+	 * different Matrix3f objects with identical data values (i.e.,
+	 * Matrix3f.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
+	 */
+	@Override
+	public int hashCode() {
+		long bits = 1L;
+		bits = 31L * bits + doubleToLongBits(this.m00);
+		bits = 31L * bits + doubleToLongBits(this.m01);
+		bits = 31L * bits + doubleToLongBits(this.m02);
+		bits = 31L * bits + doubleToLongBits(this.m10);
+		bits = 31L * bits + doubleToLongBits(this.m11);
+		bits = 31L * bits + doubleToLongBits(this.m12);
+		bits = 31L * bits + doubleToLongBits(this.m20);
+		bits = 31L * bits + doubleToLongBits(this.m21);
+		bits = 31L * bits + doubleToLongBits(this.m22);
+		return (int) (bits ^ (bits >> 32));
+	}
+
+	private static long doubleToLongBits(float d) {
+		// Check for +0 or -0
+		if (d == 0f) {
+			return 0L;
+		}
+		return Float.floatToIntBits(d);
+	}
+
+	/**
+	 * Sets this matrix to all zeros.
+	 */
+	public final void setZero() {
+		this.m00 = 0f;
+		this.m01 = 0f;
+		this.m02 = 0f;
+
+		this.m10 = 0f;
+		this.m11 = 0f;
+		this.m12 = 0f;
+
+		this.m20 = 0f;
+		this.m21 = 0f;
+		this.m22 = 0f;
+	}
+
+	/**
+	 * Sets this matrix as diagonal.
+	 * 
+	 * @param m00
+	 *            the first element of the diagonal
+	 * @param m11
+	 *            the second element of the diagonal
+	 * @param m22
+	 *            the third element of the diagonal
+	 */
+	public final void setDiagonal(float m00, float m11, float m22) {
+		this.m00 = m00;
+		this.m01 = 0f;
+		this.m02 = 0f;
+		this.m10 = 0f;
+		this.m11 = m11;
+		this.m12 = 0f;
+		this.m20 = 0f;
+		this.m21 = 0f;
+		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 Matrix3f
+	 * parameter.
+	 * 
+	 * @param m1
+	 *            the source matrix
+	 */
+	public final void negate(Matrix3f 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;
+	}
+
+	private static boolean epsilonEquals(float a, float b) {
+		return Math.abs(a-b) <=  MathConstants.EPSILON;
+	}
+
+	private static void compute_svd(float[] m, float[] outScale, float[] outRot) {
+		int i;
+		float g;
+		float[] u1 = new float[9];
+		float[] v1 = new float[9];
+		float[] t1 = new float[9];
+		float[] t2 = new float[9];
+
+		float[] tmp = t1;
+		float[] single_values = t2;
+
+		float[] rot = new float[9];
+		float[] e = new float[3];
+		float[] scales = new float[3];
+
+		int negCnt = 0;
+		float c1, c2, c3, c4;
+		float s1, s2, s3, s4;
+
+		for (i = 0; i < 9; ++i)
+			rot[i] = m[i];
+
+		// u1
+
+		if (m[3] * m[3] < EPSILON) {
+			u1[0] = 1f;
+			u1[1] = 0f;
+			u1[2] = 0f;
+			u1[3] = 0f;
+			u1[4] = 1f;
+			u1[5] = 0f;
+			u1[6] = 0f;
+			u1[7] = 0f;
+			u1[8] = 1f;
+		} else if (m[0] * m[0] < EPSILON) {
+			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] = 0f;
+			u1[1] = 1f;
+			u1[2] = 0f;
+			u1[3] = -1f;
+			u1[4] = 0f;
+			u1[5] = 0f;
+			u1[6] = 0f;
+			u1[7] = 0f;
+			u1[8] = 1f;
+		} else {
+			g = 1f / (float)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] = 0f;
+			u1[3] = -s1;
+			u1[4] = c1;
+			u1[5] = 0f;
+			u1[6] = 0f;
+			u1[7] = 0f;
+			u1[8] = 1f;
+		}
+
+		// u2
+
+		if (m[6] * m[6] < EPSILON) {
+			//
+		} else if (m[0] * m[0] < EPSILON) {
+			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 = 1f / (float)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] < EPSILON) {
+			v1[0] = 1f;
+			v1[1] = 0f;
+			v1[2] = 0f;
+			v1[3] = 0f;
+			v1[4] = 1f;
+			v1[5] = 0f;
+			v1[6] = 0f;
+			v1[7] = 0f;
+			v1[8] = 1f;
+		} else if (m[1] * m[1] < EPSILON) {
+			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] = 1f;
+			v1[1] = 0f;
+			v1[2] = 0f;
+			v1[3] = 0f;
+			v1[4] = 0f;
+			v1[5] = -1f;
+			v1[6] = 0f;
+			v1[7] = 1f;
+			v1[8] = 0f;
+		} else {
+			g = 1f / (float)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] = 1f;
+			v1[1] = 0f;
+			v1[2] = 0f;
+			v1[3] = 0f;
+			v1[4] = c3;
+			v1[5] = -s3;
+			v1[6] = 0f;
+			v1[7] = s3;
+			v1[8] = c3;
+		}
+
+		// u3
+
+		if (m[7] * m[7] < EPSILON) {
+			//
+		} else if (m[4] * m[4] < EPSILON) {
+			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 = 1f / (float)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] < EPSILON && e[1] * e[1] < EPSILON) {
+			//
+		} 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 matrix.
+		if (epsilonEquals(Math.abs(scales[0]), 1f)
+				&& epsilonEquals(Math.abs(scales[1]), 1f)
+				&& epsilonEquals(Math.abs(scales[2]), 1f)) {
+			// System.out.println("Scale components almost to 1f");
+
+			for (i = 0; i < 3; ++i)
+				if (scales[i] < 0f)
+					++negCnt;
+
+			if ((negCnt == 0) || (negCnt == 2)) {
+				// System.out.println("Optimize!!");
+				outScale[0] = outScale[1] = outScale[2] = 1f;
+				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);
+
+	}
+
+	private static void svdReorder(float[] m, float[] t1, float[] t2,
+			float[] scales, float[] outRot, float[] outScale) {
+
+		int[] out = new int[3];
+		int in0, in1, in2, index, i;
+		float[] mag = new float[3];
+		float[] rot = new float[9];
+
+		// check for rotation information in the scales
+		if (scales[0] < 0f) { // 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] < 0f) { // 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] < 0f) { // 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 (epsilonEquals(Math.abs(scales[0]), Math.abs(scales[1]))
+				&& epsilonEquals(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];
+		}
+	}
+
+	private static int compute_qr(float[] s, float[] e, float[] u, float[] v) {
+
+		int k;
+		boolean converged;
+		float shift, r;
+		float[] cosl = new float[2];
+		float[] cosr = new float[2];
+		float[] sinl = new float[2];
+		float[] sinr = new float[2];
+		float[] m = new float[9];
+
+		float utemp, vtemp;
+		float f, g;
+
+		final int MAX_INTERATIONS = 10;
+		final float CONVERGE_TOL = 4.89E-15f;
+
+		float c_b48 = 1f;
+		//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) * (d_sign(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] = 0f;
+			m[3] = 0f;
+			m[4] = s[1];
+			m[5] = e[1];
+			m[6] = 0f;
+			m[7] = 0f;
+			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);
+	}
+
+	private static float d_sign(float a, float b) {
+		float x;
+		x = (a >= 0 ? a : -a);
+		return (b >= 0 ? x : -x);
+	}
+
+	private static float compute_shift(float f, float g, float h) {
+		float d__1, d__2;
+		float fhmn, fhmx, c, fa, ga, ha, as, at, au;
+		float ssmin;
+
+		fa = Math.abs(f);
+		ga = Math.abs(g);
+		ha = Math.abs(h);
+		fhmn = Math.min(fa, ha);
+		fhmx = Math.max(fa, ha);
+		if (fhmn == 0f) {
+			ssmin = 0f;
+			if (fhmx == 0f) {
+				//
+			} else {
+				d__1 = Math.min(fhmx, ga) / Math.max(fhmx, ga);
+			}
+		} else {
+			if (ga < fhmx) {
+				as = fhmn / fhmx + 1f;
+				at = (fhmx - fhmn) / fhmx;
+				d__1 = ga / fhmx;
+				au = d__1 * d__1;
+				c = 2f / ((float)Math.sqrt(as * as + au) + (float)Math.sqrt(at * at + au));
+				ssmin = fhmn * c;
+			} else {
+				au = fhmx / ga;
+				if (au == 0f) {
+					ssmin = fhmn * fhmx / ga;
+				} else {
+					as = fhmn / fhmx + 1f;
+					at = (fhmx - fhmn) / fhmx;
+					d__1 = as * au;
+					d__2 = at * au;
+					c = 1f / ((float)Math.sqrt(d__1 * d__1 + 1f) + (float)Math.sqrt(d__2
+							* d__2 + 1f));
+					ssmin = fhmn * c * au;
+					ssmin += ssmin;
+				}
+			}
+		}
+
+		return (ssmin);
+	}
+
+	private static int compute_2X2(float f, float g, float h,
+			float[] single_values, float[] snl, float[] csl, float[] snr,
+			float[] csr, int index) {
+
+		float c_b3 = 2f;
+		float c_b4 = 1f;
+
+		float d__1;
+		int pmax;
+		float temp;
+		boolean swap;
+		float a, d, l, m, r, s, t, tsign, fa, ga, ha;
+		float ft, gt, ht, mm;
+		boolean gasmal;
+		float tt, clt, crt, slt, srt;
+		float ssmin, ssmax;
+
+		ssmax = single_values[0];
+		ssmin = single_values[1];
+		clt = 0f;
+		crt = 0f;
+		slt = 0f;
+		srt = 0f;
+		tsign = 0f;
+
+		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 == 0f) {
+
+			single_values[1] = ha;
+			single_values[0] = fa;
+			clt = 1f;
+			crt = 1f;
+			slt = 0f;
+			srt = 0f;
+		} else {
+			gasmal = true;
+
+			if (ga > fa) {
+				pmax = 2;
+				if (fa / ga < EPSILON) {
+
+					gasmal = false;
+					ssmax = ga;
+					if (ha > 1.) {
+						ssmin = fa / (ga / ha);
+					} else {
+						ssmin = fa / ga * ha;
+					}
+					clt = 1f;
+					slt = ht / gt;
+					srt = 1f;
+					crt = ft / gt;
+				}
+			}
+			if (gasmal) {
+
+				d = fa - ha;
+				if (d == fa) {
+
+					l = 1f;
+				} else {
+					l = d / fa;
+				}
+
+				m = gt / ft;
+
+				t = 2f - l;
+
+				mm = m * m;
+				tt = t * t;
+				s = (float)Math.sqrt(tt + mm);
+
+				if (l == 0f) {
+					r = Math.abs(m);
+				} else {
+					r = (float)Math.sqrt(l * l + mm);
+				}
+
+				a = (s + r) * .5f;
+
+				if (ga > fa) {
+					pmax = 2;
+					if (fa / ga < EPSILON) {
+
+						gasmal = false;
+						ssmax = ga;
+						if (ha > 1.) {
+							ssmin = fa / (ga / ha);
+						} else {
+							ssmin = fa / ga * ha;
+						}
+						clt = 1f;
+						slt = ht / gt;
+						srt = 1f;
+						crt = ft / gt;
+					}
+				}
+				if (gasmal) {
+
+					d = fa - ha;
+					if (d == fa) {
+
+						l = 1f;
+					} else {
+						l = d / fa;
+					}
+
+					m = gt / ft;
+
+					t = 2f - l;
+
+					mm = m * m;
+					tt = t * t;
+					s = (float)Math.sqrt(tt + mm);
+
+					if (l == 0f) {
+						r = Math.abs(m);
+					} else {
+						r = (float)Math.sqrt(l * l + mm);
+					}
+
+					a = (s + r) * .5f;
+
+					ssmin = ha / a;
+					ssmax = fa * a;
+					if (mm == 0f) {
+
+						if (l == 0f) {
+							t = d_sign(c_b3, ft) * d_sign(c_b4, gt);
+						} else {
+							t = gt / d_sign(d, ft) + m / t;
+						}
+					} else {
+						t = (m / (s + t) + m / (r + l)) * (a + 1f);
+					}
+					l = (float)Math.sqrt(t * t + 4f);
+					crt = 2f / 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 = d_sign(c_b4, csr[0]) * d_sign(c_b4, csl[0])
+						* d_sign(c_b4, f);
+			}
+			if (pmax == 2) {
+				tsign = d_sign(c_b4, snr[0]) * d_sign(c_b4, csl[0])
+						* d_sign(c_b4, g);
+			}
+			if (pmax == 3) {
+				tsign = d_sign(c_b4, snr[0]) * d_sign(c_b4, snl[0])
+						* d_sign(c_b4, h);
+			}
+			single_values[index] = d_sign(ssmax, tsign);
+			d__1 = tsign * d_sign(c_b4, f) * d_sign(c_b4, h);
+			single_values[index + 1] = d_sign(ssmin, d__1);
+
+		}
+		return 0;
+	}
+
+	private static float compute_rot(float f, float g, float[] sin, float[] cos,
+			int index) {
+		float cs, sn;
+		int i;
+		float scale;
+		int count;
+		float f1, g1;
+		float r;
+		final double safmn2 = 2.002083095183101E-146;
+		final double safmx2 = 4.994797680505588E+145;
+
+		if (g == 0f) {
+			cs = 1f;
+			sn = 0f;
+			r = f;
+		} else if (f == 0f) {
+			cs = 0f;
+			sn = 1f;
+			r = g;
+		} else {
+			f1 = f;
+			g1 = g;
+			scale = Math.max(Math.abs(f1), Math.abs(g1));
+			if (scale >= safmx2) {
+				count = 0;
+				while (scale >= safmx2) {
+					++count;
+					f1 *= safmn2;
+					g1 *= safmn2;
+					scale = Math.max(Math.abs(f1), Math.abs(g1));
+				}
+				r = (float)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 = Math.max(Math.abs(f1), Math.abs(g1));
+				}
+				r = (float)Math.sqrt(f1 * f1 + g1 * g1);
+				cs = f1 / r;
+				sn = g1 / r;
+				for (i = 1; i <= count; ++i) {
+					r *= safmn2;
+				}
+			} else {
+				r = (float)Math.sqrt(f1 * f1 + g1 * g1);
+				cs = f1 / r;
+				sn = g1 / r;
+			}
+			if (Math.abs(f) > Math.abs(g) && cs < 0f) {
+				cs = -cs;
+				sn = -sn;
+				r = -r;
+			}
+		}
+		sin[index] = sn;
+		cos[index] = cs;
+		return r;
+
+	}
+
+	private static void mat_mul(float[] m1, float[] m2, float[] m3) {
+		int i;
+		float[] tmp = new float[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];
+		}
+	}
+
+	private static void transpose_mat(float[] in, float[] 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];
+	}
+
+	/**
+	 * 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 java.lang.Cloneable
+	 */
+	@Override
+	public Matrix3f clone() {
+		Matrix3f m1 = null;
+		try {
+			m1 = (Matrix3f) 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;
+	}
+
+	/**
+	 * Get the first matrix element in the first row.
+	 * 
+	 * @return Returns the m00f
+	 * @since vecmath 1.5
+	 */
+	public final float getM00() {
+		return this.m00;
+	}
+
+	/**
+	 * 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;
+	}
+
+	/**
+	 * Get the second matrix element in the first row.
+	 * 
+	 * @return Returns the m01.
+	 * 
+	 * @since vecmath 1.5
+	 */
+	public final float getM01() {
+		return this.m01;
+	}
+
+	/**
+	 * 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;
+	}
+
+	/**
+	 * Get the third matrix element in the first row.
+	 * 
+	 * @return Returns the m02.
+	 * 
+	 * @since vecmath 1.5
+	 */
+	public final float getM02() {
+		return this.m02;
+	}
+
+	/**
+	 * 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;
+	}
+
+	/**
+	 * Get first matrix element in the second row.
+	 * 
+	 * @return Returns the m10f
+	 * 
+	 * @since vecmath 1.5
+	 */
+	public final float getM10() {
+		return this.m10;
+	}
+
+	/**
+	 * 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;
+	}
+
+	/**
+	 * Get second matrix element in the second row.
+	 * 
+	 * @return Returns the m11.
+	 * 
+	 * @since vecmath 1.5
+	 */
+	public final float getM11() {
+		return this.m11;
+	}
+
+	/**
+	 * 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;
+	}
+
+	/**
+	 * Get the third matrix element in the second row.
+	 * 
+	 * @return Returns the m12.
+	 * 
+	 * @since vecmath 1.5
+	 */
+	public final float getM12() {
+		return this.m12;
+	}
+
+	/**
+	 * 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;
+	}
+
+	/**
+	 * Get the first matrix element in the third row.
+	 * 
+	 * @return Returns the m20
+	 * 
+	 * @since vecmath 1.5
+	 */
+	public final float getM20() {
+		return this.m20;
+	}
+
+	/**
+	 * 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;
+	}
+
+	/**
+	 * Get the second matrix element in the third row.
+	 * 
+	 * @return Returns the m21.
+	 * 
+	 * @since vecmath 1.5
+	 */
+	public final float getM21() {
+		return this.m21;
+	}
+
+	/**
+	 * 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;
+	}
+
+	/**
+	 * Get the third matrix element in the third row .
+	 * 
+	 * @return Returns the m22.
+	 * 
+	 * @since vecmath 1.5
+	 */
+	public final float getM22() {
+		return this.m22;
+	}
+
+	/**
+	 * 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;
+	}
+
+	/**
+	 * 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(float scale) {
+
+		float[] tmp_rot = new float[9]; // scratch matrix
+		float[] tmp_scale = new float[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;
+	}
+	
+	/**
+	 * 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 scale factors will be returned. This matrix is
+	 * not modified.
+	 * 
+	 * @return the scale factor of this matrix
+	 */
+	public final float getScale() {
+
+		float[] tmp_scale = new float[3]; // scratch matrix
+		float[] tmp_rot = new float[9]; // scratch matrix
+		getScaleRotate(tmp_scale, tmp_rot);
+
+		return (MathUtil.max(tmp_scale));
+
+	}
+
+	/**
+	 * perform SVD (if necessary to get rotational component)
+	 */
+	private void getScaleRotate(float scales[], float rots[]) {
+
+		float[] tmp = new float[9]; // scratch matrix
+
+		tmp[0] = this.m00;
+		tmp[1] = this.m01;
+		tmp[2] = this.m02;
+
+		tmp[3] = this.m10;
+		tmp[4] = this.m11;
+		tmp[5] = this.m12;
+
+		tmp[6] = this.m20;
+		tmp[7] = this.m21;
+		tmp[8] = this.m22;
+		compute_svd(tmp, scales, rots);
+
+		return;
+	}
+	
+	/**
+	 * Performs singular value decomposition normalization of this matrix.
+	 */
+	public final void normalize() {
+		float[] tmp_rot = new float[9]; // scratch matrix
+		float[] tmp_scale = new float[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];
+
+	}
+
+	/** Set this matrix with the covariance matrix's elements for the given
+	 * set of tuples.
+	 * 
+	 * @param tuples
+	 * @return the mean of the tuples.
+	 */
+	public final Vector3f cov(Vector3f... tuples) {
+		return cov(Arrays.asList(tuples));
+	}
+
+	/** Set this matrix with the covariance matrix's elements for the given
+	 * set of tuples.
+	 * 
+	 * @param tuples
+	 * @return the mean of the tuples.
+	 */
+	public final Vector3f cov(Point3f... tuples) {
+		return cov(Arrays.asList(tuples));
+	}
+
+	/** Set this matrix with the covariance matrix's elements for the given
+	 * set of tuples.
+	 * 
+	 * @param tuples
+	 * @return the mean of the tuples.
+	 */
+	public Vector3f cov(Iterable<? extends Tuple3f<?>> tuples) {
+		setZero();
+
+		// Compute the mean m
+		Vector3f m = new Vector3f();
+		int count = 0;
+		for(Tuple3f<?> p : tuples) {
+			m.add(p.getX(), p.getY(), p.getZ());
+			++count;
+		}
+
+		if (count==0) return null;
+
+		m.scale(1f/count);
+
+		// Compute the covariance term [Gottshalk2000]
+		// c_ij = sum(p'_i * p'_j) / n
+		// c_ij = sum((p_i - m_i) * (p_j - m_j)) / n
+		for(Tuple3f<?> p : tuples) {
+			this.m00 += (p.getX() - m.getX()) * (p.getX() - m.getX());
+			this.m01 += (p.getX() - m.getX()) * (p.getY() - m.getY()); // same as m10
+			this.m02 += (p.getX() - m.getX()) * (p.getZ() - m.getZ()); // same as m20
+			//cov.m10 += (p.getY() - m.getY()) * (p.getX() - m.getX()); // same as m01
+			this.m11 += (p.getY() - m.getY()) * (p.getY() - m.getY());
+			this.m12 += (p.getY() - m.getY()) * (p.getZ() - m.getZ()); // same as m21
+			//cov.m20 += (p.getZ() - m.getZ()) * (p.getX() - m.getX()); // same as m02
+			//cov.m21 += (p.getZ() - m.getZ()) * (p.getY() - m.getY()); // same as m12
+			this.m22 += (p.getZ() - m.getZ()) * (p.getZ() - m.getZ());
+		}
+
+		this.m00 /= count;
+		this.m01 /= count;
+		this.m02 /= count;
+		this.m10 = this.m01;
+		this.m11 /= count;
+		this.m12 /= count;
+		this.m20 = this.m02;
+		this.m21 = this.m12;
+		this.m22 /= count;
+
+		return m;
+	}
+
+	/** Replies if the matrix is symmetric.
+	 * 
+	 * @return <code>true</code> if the matrix is symmetric, otherwise
+	 * <code>false</code>
+	 */
+	public boolean isSymmetric() {
+		return	this.m01 == this.m10
+				&&	this.m02 == this.m20
+				&&	this.m12 == this.m21;
+	}
+
+	/**
+	 * Compute the eigenvectors of the given symmetric matrix
+	 * according to the Jacobi Cyclic Method.
+	 * <p>
+	 * Given the n x n real symmetric matrix A, the routine 
+	 * Jacobi_Cyclic_Method calculates the eigenvalues and 
+	 * eigenvectors of A by successively sweeping through the 
+	 * matrix A annihilating off-diagonal non-zero elements 
+	 * by a rotation of the row and column in which the 
+	 * non-zero element occurs.  
+	 * <p>
+	 * The Jacobi procedure for finding the eigenvalues and eigenvectors of a
+	 * symmetric matrix A is based on finding a similarity transformation
+	 * which diagonalizes A.  The similarity transformation is given by a
+	 * product of a sequence of orthogonal (rotation) matrices each of which
+	 * annihilates an off-diagonal element and its transpose.  The rotation
+	 * effects only the rows and columns containing the off-diagonal element 
+	 * and its transpose, i.e. if a[i][j] is an off-diagonal element, then
+	 * the orthogonal transformation rotates rows a[i][] and a[j][], and
+	 * equivalently it rotates columns a[][i] and a[][j], so that a[i][j] = 0
+	 * and a[j][i] = 0.
+	 * The cyclic Jacobi method considers the off-diagonal elements in the
+	 * following order: (0,1),(0,2),...,(0,n-1),(1,2),...,(n-2,n-1).  If the
+	 * the magnitude of the off-diagonal element is greater than a treshold,
+	 * then a rotation is performed to annihilate that off-diagnonal element.
+	 * The process described above is called a sweep.  After a sweep has been
+	 * completed, the threshold is lowered and another sweep is performed
+	 * with the new threshold. This process is completed until the final
+	 * sweep is performed with the final threshold.
+	 * The orthogonal transformation which annihilates the matrix element
+	 * a[k][m], k != m, is Q = q[i][j], where q[i][j] = 0 if i != j, i,j != k
+	 * i,j != m and q[i][j] = 1 if i = j, i,j != k, i,j != m, q[k][k] =
+	 * q[m][m] = cos(phi), q[k][m] = -sin(phi), and q[m][k] = sin(phi), where
+	 * the angle phi is determined by requiring a[k][m] -> 0.  This condition
+	 * on the angle phi is equivalent to<br>
+	 * cot(2 phi) = 0.5 * (a[k][k] - a[m][m]) / a[k][m]<br>
+	 * Since tan(2 phi) = 2 tan(phi) / (1.0 - tan(phi)^2),<br>
+	 * tan(phi)^2 + 2cot(2 phi) * tan(phi) - 1 = 0.<br>
+	 * Solving for tan(phi), choosing the solution with smallest magnitude,
+	 * tan(phi) = - cot(2 phi) + sgn(cot(2 phi)) sqrt(cot(2phi)^2 + 1).
+	 * Then cos(phi)^2 = 1 / (1 + tan(phi)^2) and sin(phi)^2 = 1 - cos(phi)^2.
+	 * Finally by taking the sqrts and assigning the sign to the sin the same
+	 * as that of the tan, the orthogonal transformation Q is determined.
+	 * Let A" be the matrix obtained from the matrix A by applying the
+	 * similarity transformation Q, since Q is orthogonal, A" = Q'AQ, where Q'
+	 * is the transpose of Q (which is the same as the inverse of Q).  Then
+	 * a"[i][j] = Q'[i][p] a[p][q] Q[q][j] = Q[p][i] a[p][q] Q[q][j],
+	 * where repeated indices are summed over.
+	 * If i is not equal to either k or m, then Q[i][j] is the Kronecker
+	 * delta.   So if both i and j are not equal to either k or m,
+	 * a"[i][j] = a[i][j].
+	 * If i = k, j = k,<br>
+	 * a"[k][k] = a[k][k]*cos(phi)^2 + a[k][m]*sin(2 phi) + a[m][m]*sin(phi)^2<br>
+	 * If i = k, j = m,<br>
+	 * a"[k][m] = a"[m][k] = 0 = a[k][m]*cos(2 phi) + 0.5 * (a[m][m] - a[k][k])*sin(2 phi)<br>
+	 * If i = k, j != k or m,<br>
+	 * a"[k][j] = a"[j][k] = a[k][j] * cos(phi) + a[m][j] * sin(phi)<br>
+	 * If i = m, j = k, a"[m][k] = 0<br>
+	 * If i = m, j = m,<br>
+	 * a"[m][m] = a[m][m]*cos(phi)^2 - a[k][m]*sin(2 phi) + a[k][k]*sin(phi)^2<br>
+	 * If i= m, j != k or m,<br>
+	 * a"[m][j] = a"[j][m] = a[m][j] * cos(phi) - a[k][j] * sin(phi)
+	 * <p>
+	 * If X is the matrix of normalized eigenvectors stored so that the ith
+	 * column corresponds to the ith eigenvalue, then AX = X Lamda, where
+	 * Lambda is the diagonal matrix with the ith eigenvalue stored at
+	 * Lambda[i][i], i.e. X'AX = Lambda and X is orthogonal, the eigenvectors
+	 * are normalized and orthogonal.  So, X = Q1 Q2 ... Qs, where Qi is
+	 * the ith orthogonal matrix,  i.e. X can be recursively approximated by
+	 * the recursion relation X" = X Q, where Q is the orthogonal matrix and
+	 * the initial estimate for X is the identity matrix.<br>
+	 * If j = k, then x"[i][k] = x[i][k] * cos(phi) + x[i][m] * sin(phi),<br>
+	 * if j = m, then x"[i][m] = x[i][m] * cos(phi) - x[i][k] * sin(phi), and<br>
+	 * if j != k and j != m, then x"[i][j] = x[i][j].
+	 *  
+	 * @param eigenVectors are the matrix of vectors to fill. Eigen vectors are the
+	 * columns of the matrix.
+	 * @return the eigenvalues which are corresponding to the <var>eigenVectors</var> columns.
+	 * @see "Mathematics for 3D Game Programming and Computer Graphics, 2nd edition; pp.437." 
+	 */
+	public float[] eigenVectorsOfSymmetricMatrix(Matrix3f eigenVectors) {
+		// Copy values up to the diagonal
+		float m11 = getElement(0,0);
+		float m12 = getElement(0,1);
+		float m13 = getElement(0,2);
+		float m22 = getElement(1,1);
+		float m23 = getElement(1,2);
+		float m33 = getElement(2,2);
+
+		eigenVectors.setIdentity();
+
+		boolean sweepsConsumed = true;
+		int i;
+		float u, u2, u2p1, t, c, s;
+		float tmp, ri0, ri1, ri2;
+
+		for(int a=0; a<JACOBI_MAX_SWEEPS; ++a) {
+
+			// Exit loop if off-diagonal entries are small enough
+			if ((Math.abs(m12) < MathConstants.EPSILON)
+					&& (Math.abs(m13) < MathConstants.EPSILON)
+					&& (Math.abs(m23) < MathConstants.EPSILON)) {
+				sweepsConsumed = false;
+				break;
+			}
+
+			// Annihilate (1,2) entry
+			if (m12 != 0f) {
+				u = (m22 - m11) *.5f / m12;
+				u2 = u*u;
+				u2p1 = u2 + 1f;
+
+				if (u2p1!=u2)
+					t = (float)(Math.signum(u) * (Math.sqrt(u2p1) - Math.abs(u)));
+				else
+					t = .5f / u;
+
+				c = 1f / (float)Math.sqrt(t*t + 1);
+				s = c * t;
+
+				m11 -= t * m12;
+				m22 += t * m12;
+				m12 = 0f;
+
+				tmp = c * m13 - s * m23;
+				m23 = s * m13 + c * m23;
+				m13 = tmp;
+
+				for(i=0; i<3; ++i) {
+					ri0 = eigenVectors.getElement(i,0);
+					ri1 = eigenVectors.getElement(i,1);
+					eigenVectors.setElement(i, 0, c * ri0 - s * ri1);
+					eigenVectors.setElement(i, 1, s * ri0 + c * ri1);
+				}
+			}
+
+			// Annihilate (1,3) entry
+			if (m13 != 0f) {
+				u = (m33 - m11) *.5f / m13;
+				u2 = u*u;
+				u2p1 = u2 + 1f;
+
+				if (u2p1!=u2)
+					t = (float)(Math.signum(u) * (Math.sqrt(u2p1) - Math.abs(u)));
+				else
+					t = .5f / u;
+
+				c = 1f / (float)Math.sqrt(t*t + 1);
+				s = c * t;
+
+				m11 -= t * m13;
+				m33 += t * m13;
+				m13 = 0f;
+
+				tmp = c * m12 - s * m23;
+				m23 = s * m12 + c * m23;
+				m12 = tmp;
+
+				for(i=0; i<3; ++i) {
+					ri0 = eigenVectors.getElement(i,0);
+					ri2 = eigenVectors.getElement(i,2);
+					eigenVectors.setElement(i, 0, c * ri0 - s * ri2);
+					eigenVectors.setElement(i, 2, s * ri0 + c * ri2);
+				}
+			}
+
+			// Annihilate (2,3) entry
+			if (m23 != 0f) {
+				u = (m33 - m22) *.5f / m23;
+				u2 = u*u;
+				u2p1 = u2 + 1f;
+
+				if (u2p1!=u2)
+					t = (float)(Math.signum(u) * (Math.sqrt(u2p1) - Math.abs(u)));
+				else
+					t = .5f / u;
+
+				c = 1f / (float)Math.sqrt(t*t + 1);
+				s = c * t;
+
+				m22 -= t * m23;
+				m33 += t * m23;
+				m23 = 0f;
+
+				tmp = c * m12 - s * m13;
+				m13 = s * m12 + c * m13;
+				m12 = tmp;
+
+				for(i=0; i<3; ++i) {
+					ri1 = eigenVectors.getElement(i,1);
+					ri2 = eigenVectors.getElement(i,2);
+					eigenVectors.setElement(i, 1, c * ri1 - s * ri2);
+					eigenVectors.setElement(i, 2, s * ri1 + c * ri2);
+				}
+			}
+		}
+
+		assert(!sweepsConsumed) : "Sweep count consumed during eigenvector computation"; //$NON-NLS-1$
+
+		// eigenvalues are on the diagonal
+		return new float[] { m11, m22, m33 };
+	}
+	
+	/** Replies if the matrix is identity.
+	 * <p>
+	 * This function uses the equal-to-zero test with the error {@link MathConstants#EPSILON}.
+	 * 
+	 * @return <code>true</code> if the matrix is identity; <code>false</code> otherwise.
+	 * @see MathUtil#isEpsilonZero(float)
+	 * @see MathUtil#isEpsilonEqual(float, float)
+	 */
+	public boolean isIdentity() {
+		return MathUtil.isEpsilonEqual(this.m00, 1f)
+				&& MathUtil.isEpsilonZero(this.m01)
+				&& MathUtil.isEpsilonZero(this.m02)
+				&& MathUtil.isEpsilonZero(this.m10)
+				&& MathUtil.isEpsilonEqual(this.m11, 1f)
+				&& MathUtil.isEpsilonZero(this.m12)
+				&& MathUtil.isEpsilonZero(this.m20)
+				&& MathUtil.isEpsilonZero(this.m21)
+				&& MathUtil.isEpsilonEqual(this.m22, 1f);
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/matrix/Matrix4f.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/matrix/Matrix4f.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/matrix/Matrix4f.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,1962 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.matrix;
+
+import java.io.Serializable;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.MathUtil;
+
+/**
+ * Is represented internally as a 4x4 floating point matrix. The mathematical
+ * representation is row major, as in traditional matrix mathematics.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Matrix4f implements Serializable, Cloneable, MathConstants {
+
+	private static final long serialVersionUID = 7216873052550769543L;
+
+	/**
+	 * The first matrix element in the first row.
+	 */
+	public float m00;
+
+	/**
+	 * The second matrix element in the first row.
+	 */
+	public float m01;
+
+	/**
+	 * The third matrix element in the first row.
+	 */
+	public float m02;
+
+	/**
+	 * The fourth matrix element in the first row.
+	 */
+	public float m03;
+
+	/**
+	 * The first matrix element in the second row.
+	 */
+	public float m10;
+
+	/**
+	 * The second matrix element in the second row.
+	 */
+	public float m11;
+
+	/**
+	 * The third matrix element in the second row.
+	 */
+	public float m12;
+
+	/**
+	 * The fourth matrix element in the second row.
+	 */
+	public float m13;
+
+	/**
+	 * The first matrix element in the third row.
+	 */
+	public float m20;
+
+	/**
+	 * The second matrix element in the third row.
+	 */
+	public float m21;
+
+	/**
+	 * The third matrix element in the third row.
+	 */
+	public float m22;
+
+	/**
+	 * The fourth matrix element in the third row.
+	 */
+	public float m23;
+
+	/**
+	 * The first matrix element in the fourth row.
+	 */
+	public float m30;
+
+	/**
+	 * The second matrix element in the fourth row.
+	 */
+	public float m31;
+
+	/**
+	 * The third matrix element in the fourth row.
+	 */
+	public float m32;
+
+	/**
+	 * The fourth matrix element in the fourth row.
+	 */
+	public float m33;
+
+	/**
+	 * Constructs and initializes a Matrix4f from the specified nine 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 sixteen- element
+	 * array.
+	 * 
+	 * @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 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 to all zeros.
+	 */
+	public Matrix4f() {
+		this.m00 = 0f;
+		this.m01 = 0f;
+		this.m02 = 0f;
+		this.m03 = 0f;
+
+		this.m10 = 0f;
+		this.m11 = 0f;
+		this.m12 = 0f;
+		this.m13 = 0f;
+
+		this.m20 = 0f;
+		this.m21 = 0f;
+		this.m22 = 0f;
+		this.m23 = 0f;
+
+		this.m30 = 0f;
+		this.m31 = 0f;
+		this.m32 = 0f;
+		this.m33 = 0f;
+	}
+
+	/**
+	 * Returns a string that contains the values of this Matrix4f.
+	 * 
+	 * @return the String representation
+	 */
+	@Override
+	public String toString() {
+		return this.m00 + ", " //$NON-NLS-1$
+		+ this.m01 + ", " //$NON-NLS-1$
+		+ this.m02 + ", " //$NON-NLS-1$
+		+ this.m03 + "\n" //$NON-NLS-1$
+		+ this.m10 + ", " //$NON-NLS-1$
+		+ this.m11 + ", " //$NON-NLS-1$
+		+ this.m12 + ", " //$NON-NLS-1$
+		+ this.m13 + "\n" //$NON-NLS-1$
+		+ this.m20 + ", " //$NON-NLS-1$
+		+ this.m21 + ", " //$NON-NLS-1$
+		+ this.m22 + ", " //$NON-NLS-1$
+		+ this.m23 + "\n" //$NON-NLS-1$
+		+ this.m30 + ", " //$NON-NLS-1$
+		+ this.m31 + ", " //$NON-NLS-1$
+		+ this.m32 + ", " //$NON-NLS-1$
+		+ this.m33 + "\n"; //$NON-NLS-1$
+	}
+
+	/**
+	 * Sets this Matrix4f to identity.
+	 */
+	public final void setIdentity() {
+		this.m00 = 1f;
+		this.m01 = 0f;
+		this.m02 = 0f;
+		this.m03 = 0f;
+
+		this.m10 = 0f;
+		this.m11 = 1f;
+		this.m12 = 0f;
+		this.m13 = 0f;
+
+		this.m20 = 0f;
+		this.m21 = 0f;
+		this.m22 = 1f;
+		this.m23 = 0f;
+
+		this.m30 = 0f;
+		this.m31 = 0f;
+		this.m32 = 0f;
+		this.m33 = 1f;
+	}
+
+	/**
+	 * 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, 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();
+		}
+	}
+
+	/**
+	 * 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 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();
+	}
+
+	/**
+	 * 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] = this.m00;
+			v[1] = this.m01;
+			v[2] = this.m02;
+			v[3] = this.m03;
+		} else if (row == 1) {
+			v[0] = this.m10;
+			v[1] = this.m11;
+			v[2] = this.m12;
+			v[3] = this.m13;
+		} else if (row == 2) {
+			v[0] = this.m20;
+			v[1] = this.m21;
+			v[2] = this.m22;
+			v[3] = this.m23;
+		} else if (row == 3) {
+			v[0] = this.m30;
+			v[1] = this.m31;
+			v[2] = this.m32;
+			v[3] = this.m33;
+		} else {
+			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, float v[]) {
+		if (column == 0) {
+			v[0] = this.m00;
+			v[1] = this.m10;
+			v[2] = this.m20;
+			v[3] = this.m30;
+		} else if (column == 1) {
+			v[0] = this.m01;
+			v[1] = this.m11;
+			v[2] = this.m21;
+			v[3] = this.m31;
+		} else if (column == 2) {
+			v[0] = this.m02;
+			v[1] = this.m12;
+			v[2] = this.m22;
+			v[3] = this.m32;
+		} else if (column == 3) {
+			v[0] = this.m03;
+			v[1] = this.m13;
+			v[2] = this.m23;
+			v[3] = this.m33;
+		} else {
+			throw new ArrayIndexOutOfBoundsException();
+		}
+
+	}
+
+	/**
+	 * Sets the specified row of this Matrix4f to the 4 values provided.
+	 * 
+	 * @param row
+	 *            the row number to be modified (zero indexed)
+	 * @param a
+	 *            the first column element
+	 * @param b
+	 *            the second column element
+	 * @param c
+	 *            the third column element
+	 * @param d
+	 *            the fourth column element
+	 */
+	public final void setRow(int row, float a, float b, float c, float d) {
+		switch (row) {
+		case 0:
+			this.m00 = a;
+			this.m01 = b;
+			this.m02 = c;
+			this.m03 = d;
+			break;
+
+		case 1:
+			this.m10 = a;
+			this.m11 = b;
+			this.m12 = c;
+			this.m13 = d;
+			break;
+
+		case 2:
+			this.m20 = a;
+			this.m21 = b;
+			this.m22 = c;
+			this.m23 = d;
+			break;
+
+		case 3:
+			this.m30 = a;
+			this.m31 = b;
+			this.m32 = c;
+			this.m33 = d;
+			break;
+
+		default:
+			throw new ArrayIndexOutOfBoundsException();
+		}
+	}
+
+	/**
+	 * Sets the specified row of this Matrix4f 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, 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 column of this Matrix4f to the three values provided.
+	 * 
+	 * @param column
+	 *            the column number to be modified (zero indexed)
+	 * @param a
+	 *            the first row element
+	 * @param b
+	 *            the second row element
+	 * @param c
+	 *            the third row element
+	 * @param d
+	 *            the fourth row element
+	 */
+	public final void setColumn(int column, float a, float b, float c, float d) {
+		switch (column) {
+		case 0:
+			this.m00 = a;
+			this.m10 = b;
+			this.m20 = c;
+			this.m30 = d;
+			break;
+
+		case 1:
+			this.m01 = a;
+			this.m11 = b;
+			this.m21 = c;
+			this.m31 = d;
+			break;
+
+		case 2:
+			this.m02 = a;
+			this.m12 = b;
+			this.m22 = c;
+			this.m32 = d;
+			break;
+
+		case 3:
+			this.m03 = a;
+			this.m13 = b;
+			this.m23 = c;
+			this.m33 = d;
+			break;
+
+		default:
+			throw new ArrayIndexOutOfBoundsException();
+		}
+	}
+
+	/**
+	 * Sets the specified column of this Matrix4f 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, 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();
+		}
+	}
+
+	/**
+	 * Adds a scalar to each component of this matrix.
+	 * 
+	 * @param scalar
+	 *            the scalar adder
+	 */
+	public final void add(float scalar) {
+		this.m00 += scalar;
+		this.m01 += scalar;
+		this.m02 += scalar;
+		this.m03 += scalar;
+
+		this.m10 += scalar;
+		this.m11 += scalar;
+		this.m12 += scalar;
+		this.m13 += scalar;
+
+		this.m20 += scalar;
+		this.m21 += scalar;
+		this.m22 += scalar;
+		this.m23 += scalar;
+
+		this.m30 += scalar;
+		this.m31 += scalar;
+		this.m32 += scalar;
+		this.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.m12 + 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 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;
+	}
+
+	/**
+	 * 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 difference of matrices m1 and
+	 * m2.
+	 * 
+	 * @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;
+	}
+
+	/**
+	 * 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(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 its transpose.
+	 */
+	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.m12;
+		this.m12 = this.m21;
+		this.m21 = temp;
+
+		temp = this.m13;
+		this.m13 = this.m31;
+		this.m31 = temp;
+
+		temp = this.m23;
+		this.m23 = this.m32;
+		this.m32 = 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();
+		}
+	}
+
+	/**
+	 * Sets the value of this matrix to the float value of the Matrix3f
+	 * argument.
+	 * 
+	 * @param m1
+	 *            the Matrix4f to be converted to float
+	 */
+	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 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 float precision array of length 16
+	 */
+	public final void set(float[] m) {
+		this.m00 = m[0];
+		this.m01 = m[1];
+		this.m02 = m[2];
+		this.m03 = m[3];
+
+		this.m10 = m[4];
+		this.m11 = m[5];
+		this.m12 = m[6];
+		this.m13 = m[7];
+
+		this.m20 = m[8];
+		this.m21 = m[9];
+		this.m22 = m[10];
+		this.m23 = m[11];
+
+		this.m30 = m[12];
+		this.m31 = m[13];
+		this.m32 = m[14];
+		this.m33 = m[15];
+	}
+	
+	/**
+	 * Set the components of the matrix.
+	 * 
+	 * @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 void set(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;
+	}
+
+	/**
+	 * Computes the determinant of this matrix.
+	 * 
+	 * @return the determinant of the matrix
+	 */
+	public final float determinant() {
+		float det1 = this.m22 * this.m33 - this.m23 * this.m32;
+		float det2 = this.m12 * this.m33 - this.m13 * this.m32;
+		float det3 = this.m12 * this.m23 - this.m13 * this.m22;
+		float det4 = this.m02 * this.m33 - this.m03 * this.m32;
+		float det5 = this.m02 * this.m23 - this.m03 * this.m22;
+		float det6 = this.m02 * this.m13 - this.m03 * this.m12;
+		return
+				  this.m00 * (
+						  this.m11 * det1 +
+						  this.m21 * det2 +
+						  this.m31 * det3
+				  ) +
+				  this.m10 * (
+						  this.m01 * det1 +
+						  this.m21 * det4 +
+						  this.m31 * det5
+				  ) +
+				  this.m20 * (
+						  this.m01 * det2 +
+						  this.m11 * det4 +
+						  this.m31 * det6
+				  ) +
+				  this.m30 * (
+						  this.m01 * det3 +
+						  this.m11 * det5 +
+						  this.m21 * det6
+				  );
+	}
+
+	/**
+	 * Multiplies each element of this matrix by a scalar.
+	 * 
+	 * @param scalar
+	 *            The scalar multiplier.
+	 */
+	public final void mul(float scalar) {
+		this.m00 *= scalar;
+		this.m01 *= scalar;
+		this.m02 *= scalar;
+		this.m03 *= scalar;
+
+		this.m10 *= scalar;
+		this.m11 *= scalar;
+		this.m12 *= scalar;
+		this.m13 *= scalar;
+
+		this.m20 *= scalar;
+		this.m21 *= scalar;
+		this.m22 *= scalar;
+		this.m23 *= scalar;
+
+		this.m30 *= scalar;
+		this.m31 *= scalar;
+		this.m32 *= scalar;
+		this.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 = scalar * m1.m00;
+		this.m01 = scalar * m1.m01;
+		this.m02 = scalar * m1.m02;
+		this.m03 = scalar * m1.m03;
+
+		this.m10 = scalar * m1.m10;
+		this.m11 = scalar * m1.m11;
+		this.m12 = scalar * m1.m12;
+		this.m13 = scalar * m1.m13;
+
+		this.m20 = scalar * m1.m20;
+		this.m21 = scalar * m1.m21;
+		this.m22 = scalar * m1.m22;
+		this.m23 = scalar * m1.m23;
+
+		this.m30 = scalar * m1.m30;
+		this.m31 = scalar * m1.m31;
+		this.m32 = scalar * m1.m32;
+		this.m33 = scalar * m1.m33;
+	}
+
+	/**
+	 * 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;
+
+		_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;
+		}
+	}
+
+	/**
+	 * 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
+	 */
+	@Override
+	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;
+		}
+
+	}
+
+	/**
+	 * 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
+	 * @return <code>true</code> if this matrix is equals to the specified matrix at epsilon.
+	 */
+	public boolean epsilonEquals(Matrix4f m1, float epsilon) {
+		float diff;
+
+		diff = this.m00 - m1.m00;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m01 - m1.m01;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m02 - m1.m02;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m03 - m1.m03;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m10 - m1.m10;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m11 - m1.m11;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m12 - m1.m12;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m13 - m1.m13;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m20 - m1.m20;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m21 - m1.m21;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m22 - m1.m22;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m23 - m1.m23;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m30 - m1.m30;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m31 - m1.m31;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m32 - m1.m32;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		diff = this.m33 - m1.m33;
+		if ((diff < 0 ? -diff : diff) > epsilon)
+			return false;
+
+		return true;
+	}
+
+	/**
+	 * 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
+	 */
+	@Override
+	public int hashCode() {
+		long bits = 1L;
+		bits = 31L * bits + floatToIntBits(this.m00);
+		bits = 31L * bits + floatToIntBits(this.m01);
+		bits = 31L * bits + floatToIntBits(this.m02);
+		bits = 31L * bits + floatToIntBits(this.m03);
+		bits = 31L * bits + floatToIntBits(this.m10);
+		bits = 31L * bits + floatToIntBits(this.m11);
+		bits = 31L * bits + floatToIntBits(this.m12);
+		bits = 31L * bits + floatToIntBits(this.m13);
+		bits = 31L * bits + floatToIntBits(this.m20);
+		bits = 31L * bits + floatToIntBits(this.m21);
+		bits = 31L * bits + floatToIntBits(this.m22);
+		bits = 31L * bits + floatToIntBits(this.m23);
+		bits = 31L * bits + floatToIntBits(this.m30);
+		bits = 31L * bits + floatToIntBits(this.m31);
+		bits = 31L * bits + floatToIntBits(this.m32);
+		bits = 31L * bits + floatToIntBits(this.m33);
+		return (int) (bits ^ (bits >> 32));
+	}
+
+	private static int floatToIntBits(float d) {
+		// Check for +0 or -0
+		if (d == 0f) {
+			return 0;
+		}
+		return Float.floatToIntBits(d);
+	}
+	
+	/**
+	 * Sets this matrix to all zeros.
+	 */
+	public final void setZero() {
+		this.m00 = 0f;
+		this.m01 = 0f;
+		this.m02 = 0f;
+		this.m03 = 0f;
+
+		this.m10 = 0f;
+		this.m11 = 0f;
+		this.m12 = 0f;
+		this.m13 = 0f;
+
+		this.m20 = 0f;
+		this.m21 = 0f;
+		this.m22 = 0f;
+		this.m23 = 0f;
+
+		this.m30 = 0f;
+		this.m31 = 0f;
+		this.m32 = 0f;
+		this.m33 = 0f;
+	}
+
+	/**
+	 * Sets this matrix as diagonal.
+	 * 
+	 * @param m00
+	 *            the first element of the diagonal
+	 * @param m11
+	 *            the second element of the diagonal
+	 * @param m22
+	 *            the third element of the diagonal
+	 * @param m33
+	 *            the fourth element of the diagonal
+	 */
+	public final void setDiagonal(float m00, float m11, float m22, float m33) {
+		this.m00 = m00;
+		this.m01 = 0f;
+		this.m02 = 0f;
+		this.m03 = 0f;
+		this.m10 = 0f;
+		this.m11 = m11;
+		this.m12 = 0f;
+		this.m13 = 0f;
+		this.m20 = 0f;
+		this.m21 = 0f;
+		this.m22 = m22;
+		this.m23 = 0f;
+		this.m30 = 0f;
+		this.m31 = 0f;
+		this.m32 = 0f;
+		this.m33 = m33;
+	}
+
+	/**
+	 * 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.m03 = -this.m03;
+
+		this.m10 = -this.m10;
+		this.m11 = -this.m11;
+		this.m12 = -this.m12;
+		this.m13 = -this.m13;
+
+		this.m20 = -this.m20;
+		this.m21 = -this.m21;
+		this.m22 = -this.m22;
+		this.m23 = -this.m23;
+
+		this.m30 = -this.m30;
+		this.m31 = -this.m31;
+		this.m32 = -this.m32;
+		this.m33 = -this.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;
+	}
+
+	/**
+	 * 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 java.lang.Cloneable
+	 */
+	@Override
+	public Matrix4f clone() {
+		Matrix4f m1 = null;
+		try {
+			m1 = (Matrix4f) 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;
+	}
+
+	/**
+	 * Get the first matrix element in the first row.
+	 * 
+	 * @return Returns the m00f
+	 */
+	public final float getM00() {
+		return this.m00;
+	}
+
+	/**
+	 * Set the first matrix element in the first row.
+	 * 
+	 * @param m00
+	 *            The m00 to set.
+	 */
+	public final void setM00(float m00) {
+		this.m00 = m00;
+	}
+
+	/**
+	 * Get the second matrix element in the first row.
+	 * 
+	 * @return Returns the m01.
+	 */
+	public final float getM01() {
+		return this.m01;
+	}
+
+	/**
+	 * Set the second matrix element in the first row.
+	 * 
+	 * @param m01
+	 *            The m01 to set.
+	 */
+	public final void setM01(float m01) {
+		this.m01 = m01;
+	}
+
+	/**
+	 * Get the third matrix element in the first row.
+	 * 
+	 * @return Returns the m02.
+	 */
+	public final float getM02() {
+		return this.m02;
+	}
+
+	/**
+	 * Set the third matrix element in the first row.
+	 * 
+	 * @param m02
+	 *            The m02 to set.
+	 */
+	public final void setM02(float m02) {
+		this.m02 = m02;
+	}
+
+	/**
+	 * Get the fourth matrix element in the first row.
+	 * 
+	 * @return Returns the m03.
+	 */
+	public final float getM03() {
+		return this.m03;
+	}
+
+	/**
+	 * Set the fourth matrix element in the first row.
+	 * 
+	 * @param m03
+	 *            The m03 to set.
+	 */
+	public final void setM03(float m03) {
+		this.m03 = m03;
+	}
+
+	/**
+	 * Get first matrix element in the second row.
+	 * 
+	 * @return Returns the m10
+	 */
+	public final float getM10() {
+		return this.m10;
+	}
+
+	/**
+	 * Set first matrix element in the second row.
+	 * 
+	 * @param m10
+	 *            The m10 to set.
+	 */
+	public final void setM10(float m10) {
+		this.m10 = m10;
+	}
+
+	/**
+	 * Get second matrix element in the second row.
+	 * 
+	 * @return Returns the m11.
+	 */
+	public final float getM11() {
+		return this.m11;
+	}
+
+	/**
+	 * Set the second matrix element in the second row.
+	 * 
+	 * @param m11
+	 *            The m11 to set.
+	 */
+	public final void setM11(float m11) {
+		this.m11 = m11;
+	}
+
+	/**
+	 * Get the third matrix element in the second row.
+	 * 
+	 * @return Returns the m12.
+	 */
+	public final float getM12() {
+		return this.m12;
+	}
+
+	/**
+	 * Set the third matrix element in the second row.
+	 * 
+	 * @param m12
+	 *            The m12 to set.
+	 */
+	public final void setM12(float m12) {
+		this.m12 = m12;
+	}
+
+	/**
+	 * Get the fourth matrix element in the second row.
+	 * 
+	 * @return Returns the m13.
+	 */
+	public final float getM13() {
+		return this.m13;
+	}
+
+	/**
+	 * Set the fourth matrix element in the second row.
+	 * 
+	 * @param m13
+	 *            The m13 to set.
+	 */
+	public final void setM13(float m13) {
+		this.m13 = m13;
+	}
+
+	/**
+	 * Get the first matrix element in the third row.
+	 * 
+	 * @return Returns the m20
+	 */
+	public final float getM20() {
+		return this.m20;
+	}
+
+	/**
+	 * Set the first matrix element in the third row.
+	 * 
+	 * @param m20
+	 *            The m20 to set.
+	 */
+	public final void setM20(float m20) {
+		this.m20 = m20;
+	}
+
+	/**
+	 * Get the second matrix element in the third row.
+	 * 
+	 * @return Returns the m21.
+	 */
+	public final float getM21() {
+		return this.m21;
+	}
+
+	/**
+	 * Set the second matrix element in the third row.
+	 * 
+	 * @param m21
+	 *            The m21 to set.
+	 */
+	public final void setM21(float m21) {
+		this.m21 = m21;
+	}
+
+	/**
+	 * Get the third matrix element in the third row .
+	 * 
+	 * @return Returns the m22.
+	 */
+	public final float getM22() {
+		return this.m22;
+	}
+
+	/**
+	 * Set the third matrix element in the third row.
+	 * 
+	 * @param m22
+	 *            The m22 to set.
+	 */
+	public final void setM22(float m22) {
+		this.m22 = m22;
+	}
+
+	/**
+	 * Get the fourth matrix element in the third row .
+	 * 
+	 * @return Returns the m23.
+	 */
+	public final float getM23() {
+		return this.m23;
+	}
+
+	/**
+	 * Set the fourth matrix element in the third row.
+	 * 
+	 * @param m23
+	 *            The m23 to set.
+	 */
+	public final void setM23(float m23) {
+		this.m23 = m23;
+	}
+
+	/**
+	 * Get the first matrix element in the fourth row.
+	 * 
+	 * @return Returns the m30
+	 */
+	public final float getM30() {
+		return this.m30;
+	}
+
+	/**
+	 * Set the first matrix element in the fourth row.
+	 * 
+	 * @param m30
+	 *            The m30 to set.
+	 */
+	public final void setM30(float m30) {
+		this.m30 = m30;
+	}
+
+	/**
+	 * Get the second matrix element in the fourth row.
+	 * 
+	 * @return Returns the m31.
+	 */
+	public final float getM31() {
+		return this.m31;
+	}
+
+	/**
+	 * Set the second matrix element in the fourth row.
+	 * 
+	 * @param m31
+	 *            The m31 to set.
+	 */
+	public final void setM31(float m31) {
+		this.m31 = m31;
+	}
+
+	/**
+	 * Get the third matrix element in the fourth row .
+	 * 
+	 * @return Returns the m32.
+	 */
+	public final float getM32() {
+		return this.m32;
+	}
+
+	/**
+	 * Set the third matrix element in the fourth row.
+	 * 
+	 * @param m32
+	 *            The m32 to set.
+	 */
+	public final void setM32(float m32) {
+		this.m32 = m32;
+	}
+
+	/**
+	 * Get the fourth matrix element in the fourth row .
+	 * 
+	 * @return Returns the m33.
+	 */
+	public final float getM33() {
+		return this.m33;
+	}
+
+	/**
+	 * Set the fourth matrix element in the fourth row.
+	 * 
+	 * @param m33
+	 *            The m33 to set.
+	 */
+	public final void setM33(float m33) {
+		this.m33 = m33;
+	}
+
+	/** Replies if the matrix is symmetric.
+	 * 
+	 * @return <code>true</code> if the matrix is symmetric, otherwise
+	 * <code>false</code>
+	 */
+	public boolean isSymmetric() {
+		return	this.m01 == this.m10
+			&&	this.m02 == this.m20
+			&&	this.m03 == this.m03
+			&&	this.m12 == this.m21
+			&&	this.m13 == this.m31
+			&&	this.m23 == this.m32;
+	}
+	
+	/** Replies if the matrix is identity.
+	 * <p>
+	 * This function uses the equal-to-zero test with the error {@link MathConstants#EPSILON}.
+	 * 
+	 * @return <code>true</code> if the matrix is identity; <code>false</code> otherwise.
+	 * @see MathUtil#isEpsilonZero(float)
+	 * @see MathUtil#isEpsilonEqual(float, float)
+	 */
+	public boolean isIdentity() {
+		return MathUtil.isEpsilonEqual(this.m00, 1f)
+				&& MathUtil.isEpsilonZero(this.m01)
+				&& MathUtil.isEpsilonZero(this.m02)
+				&& MathUtil.isEpsilonZero(this.m03)
+				&& MathUtil.isEpsilonZero(this.m10)
+				&& MathUtil.isEpsilonEqual(this.m11, 1f)
+				&& MathUtil.isEpsilonZero(this.m12)
+				&& MathUtil.isEpsilonZero(this.m13)
+				&& MathUtil.isEpsilonZero(this.m20)
+				&& MathUtil.isEpsilonZero(this.m21)
+				&& MathUtil.isEpsilonEqual(this.m22, 1f)
+				&& MathUtil.isEpsilonZero(this.m23)
+				&& MathUtil.isEpsilonZero(this.m30)
+				&& MathUtil.isEpsilonZero(this.m31)
+				&& MathUtil.isEpsilonZero(this.m32)
+				&& MathUtil.isEpsilonEqual(this.m33, 1f);
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/matrix/SingularMatrixException.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/matrix/SingularMatrixException.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/matrix/SingularMatrixException.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,63 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2011 Janus Core Developers
+ * Copyright (C) 2012 Stéphane GALLAND
+ * 
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+package org.arakhne.afc.math.matrix;
+
+
+
+/** Exception for all the singular matrices.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class SingularMatrixException extends RuntimeException {
+
+	private static final long serialVersionUID = 2834240107372614319L;
+
+	/**
+	 */
+	public SingularMatrixException() {
+		//
+	}
+
+	/**
+	 * @param message
+	 */
+	public SingularMatrixException(String message) {
+		super(message);
+	}
+
+	/**
+	 * @param cause
+	 */
+	public SingularMatrixException(Throwable cause) {
+		super(cause);
+	}
+
+	/**
+	 * @param message
+	 * @param cause
+	 */
+	public SingularMatrixException(String message, Throwable cause) {
+		super(message, cause);
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/main/java/org/arakhne/afc/math/matrix/Transform2D.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/matrix/Transform2D.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/matrix/Transform2D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,722 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.matrix;
+
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object2d.Vector2f;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Tuple2D;
+
+
+/** A 2D transformation.
+ * Is represented internally as a 3x3 floating point matrix. The
+ * mathematical representation is row major, as in traditional
+ * matrix mathematics.
+ * <p>
+ * <b>IMPORTANT: </b> The Transform2D does not support scale.
+ * <p>
+ * The transformation matrix is:
+ * <pre><code>
+ * | cos(theta) | -sin(theta) | Tx |
+ * | sin(theta) | cos(theta)  | Ty |
+ * | 0          | 0           | 1  |
+ * </code></pre>
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Transform2D extends Matrix3f {
+
+	private static final long serialVersionUID = -3437760883865605968L;
+
+	/** This is the identifity transformation.
+	 */
+	public static final Transform2D IDENTITY = new Transform2D();
+
+	/**
+	 * Constructs a new Transform2D object and sets it to the identity transformation.
+	 */
+	public Transform2D() {
+		super(1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f);
+	}
+
+	/**
+	 * Constructs a new Transform2D object and initializes it from the
+	 * specified transform.
+	 * 
+	 * @param t
+	 */
+	public Transform2D(Transform2D t) {
+		super(t);
+	}
+
+	/**
+	 * @param m
+	 */
+	public Transform2D(Matrix3f m) {
+		super(m);
+	}
+
+	/**
+	 * Constructs and initializes a Matrix3f 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
+	 */
+	public Transform2D(float m00, float m01, float m02, float m10, float m11, float m12) {
+		super(m00, m01, m02, m10, m11, m12, 0f, 0f, 1f);
+	}
+
+	@Override
+	public Transform2D  clone() {
+		return (Transform2D)super.clone();
+	}
+
+	/** Set the position.
+	 * 
+	 * @param x
+	 * @param y
+	 */
+	public void setTranslation(float x, float y) {
+		this.m02 = x;
+		this.m12 = y;
+	}
+
+	/** Set the position.
+	 * 
+	 * @param t
+	 */
+	public void setTranslation(Tuple2D<?> t) {
+		this.m02 = t.getX();
+		this.m12 = t.getY();
+	}
+
+	/** Translate the position.
+	 * 
+	 * @param dx
+	 * @param dy
+	 */
+	public void translate(float dx, float dy) {
+		this.m02 = this.m00 * dx + this.m01 * dy + this.m02;
+		this.m12 = this.m10 * dx + this.m11 * dy + this.m12;
+	}
+
+	/** Translate the position.
+	 * 
+	 * @param t
+	 */
+	public void translate(Vector2f t) {
+		translate(t.getX(), t.getY());
+	}
+
+	/** Replies the X translation.
+	 * 
+	 * @return the amount
+	 */
+	public float getTranslationX() {
+		return this.m02;
+	}
+
+	/** Replies the Y translation.
+	 * 
+	 * @return the amount
+	 */
+	public float getTranslationY() {
+		return this.m12;
+	}
+
+	/** Replies the translation.
+	 * 
+	 * @return the amount
+	 */
+	public Vector2f getTranslationVector() {
+		return new Vector2f(this.m02, this.m12);
+	}
+
+	/**
+	 * Replies the rotation for the object (theta).
+	 * 
+	 * @return the amount
+	 */
+	public float getRotation() {
+		float cosAngle = (float)Math.acos(this.m00);
+		float sinAngle = (float)Math.asin(this.m10);
+		return (sinAngle<0f) ? -cosAngle : cosAngle;
+	}
+
+	/**
+	 * Set the rotation for the object (theta).
+	 * 
+	 * @param theta
+	 */
+	public void setRotation(float theta) {
+		float cosTheta = (float)Math.cos(theta);
+		float sinTheta = (float)Math.sin(theta);
+		this.m00 = cosTheta;
+		this.m01 = -sinTheta;
+		this.m10 = sinTheta;
+		this.m11 = cosTheta;
+	}
+
+	/**
+	 * Rotate the object (theta).
+	 * 
+	 * @param theta
+	 */
+	public void rotate(float theta) {
+		// Copied from AWT API
+		float sin = (float)Math.sin(theta);
+		if (sin == 1f) {
+			rotate90();
+		}
+		else if (sin == -1f) {
+			rotate270();
+		}
+		else {
+			float cos = (float)Math.cos(theta);
+			if (cos == -1f) {
+				rotate180();
+			}
+			else if (cos != 1f) {
+				float M0, M1;
+				M0 = this.m00;
+				M1 = this.m01;
+				this.m00 =  cos * M0 + sin * M1;
+				this.m01 = -sin * M0 + cos * M1;
+				M0 = this.m10;
+				M1 = this.m11;
+				this.m10 =  cos * M0 + sin * M1;
+				this.m11 = -sin * M0 + cos * M1;
+			}
+		}
+	}
+	
+	private final void rotate90() {
+		// Copied from AWT API
+        float M0 = this.m00;
+        this.m00 = this.m01;
+        this.m01 = -M0;
+        M0 = this.m10;
+        this.m10 = this.m11;
+        this.m11 = -M0;
+    }
+	
+    private final void rotate180() {
+		// Copied from AWT API
+    	this.m00 = -this.m00;
+    	this.m11 = -this.m11;
+    	// If there was a shear, then this rotation has no
+    	// effect on the state.
+    	this.m01 = -this.m01;
+    	this.m10 = -this.m10;
+    }
+    
+    private final void rotate270() {
+		// Copied from AWT API
+        float M0 = this.m00;
+        this.m00 = -this.m01;
+        this.m01 = M0;
+        M0 = this.m10;
+        this.m10 = -this.m11;
+        this.m11 = M0;
+    }
+
+	/** Set the scale.
+	 * <p>
+	 * <pre>
+	 *          [   sx   0    0   ]
+	 *          [   0    sy   0   ]
+	 *          [   0    0    1   ]
+	 * </pre>
+	 * 
+	 * @param sx
+	 * @param sy
+	 */
+	public void setScale(float sx, float sy) {
+		this.m00 = sx;
+		this.m11 = sy;
+	}
+
+	/** Set the scale.
+	 * <p>
+	 * <pre>
+	 *          [   sx   0    0   ]
+	 *          [   0    sy   0   ]
+	 *          [   0    0    1   ]
+	 * </pre>
+	 * 
+	 * @param t
+	 */
+	public void setScale(Tuple2D<?> t) {
+		this.m00 = t.getX();
+		this.m11 = t.getY();
+	}
+
+	/** Concatenates this transform with a scaling transformation.
+	 * This is equivalent to calling concatenate(S), where S is an
+	 * <code>Transform2D</code> represented by the following matrix:
+	 * <pre>
+	 *          [   sx   0    0   ]
+	 *          [   0    sy   0   ]
+	 *          [   0    0    1   ]
+	 * </pre>
+	 * 
+	 * @param sx
+	 * @param sy
+	 */
+	public void scale(float sx, float sy) {
+        this.m00 *= sx;
+        this.m11 *= sy;
+        this.m01 *= sy;
+        this.m10 *= sx;
+	}
+
+	/** Concatenates this transform with a scaling transformation.
+	 * This is equivalent to calling concatenate(S), where S is an
+	 * <code>Transform2D</code> represented by the following matrix:
+	 * <pre>
+	 *          [   sx   0    0   ]
+	 *          [   0    sy   0   ]
+	 *          [   0    0    1   ]
+	 * </pre>
+	 * 
+	 * @param t
+	 */
+	public void scale(Tuple2D<?> t) {
+		scale(t.getX(), t.getY());
+	}
+
+	/** Replies the X scaling.
+	 * <p>
+	 * <pre>
+	 *          [   sx   0    0   ]
+	 *          [   0    sy   0   ]
+	 *          [   0    0    1   ]
+	 * </pre>
+	 * 
+	 * @return the amount
+	 */
+	public float getScaleX() {
+		return this.m00;
+	}
+
+	/** Replies the Y scaling.
+	 * <p>
+	 * <pre>
+	 *          [   sx   0    0   ]
+	 *          [   0    sy   0   ]
+	 *          [   0    0    1   ]
+	 * </pre>
+	 * 
+	 * @return the amount
+	 */
+	public float getScaleY() {
+		return this.m11;
+	}
+
+	/** Replies the scaling vector.
+	 * <p>
+	 * <pre>
+	 *          [   sx   0    0   ]
+	 *          [   0    sy   0   ]
+	 *          [   0    0    1   ]
+	 * </pre>
+	 * 
+	 * @return the amount
+	 */
+	public Vector2f getScaleVector() {
+		return new Vector2f(this.m00, this.m11);
+	}
+
+	/** Set the shearing elements.
+	 * <p>
+	 * <pre>
+	 *          [   0    shx  0   ]
+	 *          [   shy  0    0   ]
+	 *          [   0    0    1   ]
+	 * </pre>
+	 * 
+	 * @param shx
+	 * @param shy
+	 */
+	public void setShear(float shx,  float shy) {
+		this.m01 = shx;
+		this.m10 = shy;
+	}
+
+	/** Set the shearing elements.
+	 * <p>
+	 * <pre>
+	 *          [   0    shx  0   ]
+	 *          [   shy  0    0   ]
+	 *          [   0    0    1   ]
+	 * </pre>
+	 * 
+	 * @param t
+	 */
+	public void setShear(Tuple2D<?> t) {
+		this.m01 = t.getX();
+		this.m10 = t.getY();
+	}
+
+	/** Concatenates this transform with a shearing transformation.
+	 * This is equivalent to calling concatenate(S), where S is an
+	 * <code>Transform2D</code> represented by the following matrix:
+	 * <pre>
+	 *          [   1    shx  0   ]
+	 *          [   shy  1    0   ]
+	 *          [   0    0    1   ]
+	 * </pre>
+	 * 
+	 * @param shx
+	 * @param shy
+	 */
+	public void shear(float shx, float shy) {
+		float M0, M1;
+		M0 = this.m00;
+		M1 = this.m01;
+		
+		this.m00 = M0 + M1 * shy;
+		this.m01 = M0 * shx + M1;
+
+		M0 = this.m10;
+		M1 = this.m11;
+		this.m10 = M0 + M1 * shy;
+		this.m11 = M0 * shx + M1;
+	}
+
+	/** Concatenates this transform with a shearing transformation.
+	 * This is equivalent to calling concatenate(S), where S is an
+	 * <code>Transform2D</code> represented by the following matrix:
+	 * <pre>
+	 *          [   1    shx  0   ]
+	 *          [   shy  1    0   ]
+	 *          [   0    0    1   ]
+	 * </pre>
+	 * 
+	 * @param t
+	 */
+	public void shear(Tuple2D<?> t) {
+		shear(t.getX(), t.getY());
+	}
+
+	/** Replies the X shearing.
+	 * <p>
+	 * <pre>
+	 *          [   0    shx  0   ]
+	 *          [   shy  0    0   ]
+	 *          [   0    0    1   ]
+	 * </pre>
+	 * 
+	 * @return the amount
+	 */
+	public float getShearX() {
+		return this.m01;
+	}
+
+	/** Replies the Y shearing.
+	 * <p>
+	 * <pre>
+	 *          [   0    shx  0   ]
+	 *          [   shy  0    0   ]
+	 *          [   0    0    1   ]
+	 * </pre>
+	 * 
+	 * @return the amount
+	 */
+	public float getShearY() {
+		return this.m10;
+	}
+
+	/** Replies the shearing vector.
+	 * <p>
+	 * <pre>
+	 *          [   0    shx  0   ]
+	 *          [   shy  0    0   ]
+	 *          [   0    0    1   ]
+	 * </pre>
+	 * 
+	 * @return the amount
+	 */
+	public Vector2f getShearVector() {
+		return new Vector2f(this.m01, this.m10);
+	}
+
+	/**
+	 * Sets the value of this matrix to a counter clockwise rotation about the x
+	 * axis, and no translation
+	 * 
+	 * @param angle
+	 *            the angle to rotate about the X axis in radians
+	 */
+	public final void makeRotationMatrix(float angle) {
+		float sinAngle, cosAngle;
+
+		sinAngle = (float)Math.sin(angle);
+		cosAngle = (float)Math.cos(angle);
+
+		this.m00 = cosAngle;
+		this.m01 = -sinAngle;
+		this.m02 = 0f;
+
+		this.m10 = sinAngle;
+		this.m11 = cosAngle;
+		this.m12 = 0f;
+
+		this.m20 = 0f;
+		this.m21 = 0f;
+		this.m22 = 1f;
+	}
+
+	/**
+	 * Sets the value of this matrix to the given translation, without rotation.
+	 * 
+	 * @param dx is the translation along X.
+	 * @param dy is the translation along Y.
+	 */
+	public final void makeTranslationMatrix(float dx, float dy) {
+		this.m00 = 1f;
+		this.m01 = 0f;
+		this.m02 = dx;
+
+		this.m10 = 0f;
+		this.m11 = 1f;
+		this.m12 = dy;
+
+		this.m20 = 0f;
+		this.m21 = 0f;
+		this.m22 = 1f;
+	}
+
+	/**
+	 * Sets the value of this matrix to the given scaling, without rotation.
+	 * 
+	 * @param sx is the scaling along X.
+	 * @param sy is the scaling along Y.
+	 */
+	public final void makeScaleMatrix(float sx, float sy) {
+		this.m00 = sx;
+		this.m01 = 0f;
+		this.m02 = 0f;
+
+		this.m10 = 0f;
+		this.m11 = sy;
+		this.m12 = 0f;
+
+		this.m20 = 0f;
+		this.m21 = 0f;
+		this.m22 = 1f;
+	}
+
+	/**
+	 * Sets the value of this matrix to the given scaling, without rotation.
+	 * 
+	 * @param shx is the shearing along X.
+	 * @param shy is the shearing along Y.
+	 */
+	public final void makeShearMatrix(float shx, float shy) {
+		this.m00 = 1f;
+		this.m01 = shx;
+		this.m02 = 0f;
+
+		this.m10 = shy;
+		this.m11 = 1f;
+		this.m12 = 0f;
+
+		this.m20 = 0f;
+		this.m21 = 0f;
+		this.m22 = 1f;
+	}
+
+	/**
+	 * 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 void transform(Tuple2D<?> t) {
+		float x, y;
+		x = this.m00 * t.getX() + this.m01 * t.getY() + this.m02;
+		y = this.m10 * t.getX() + this.m11 * t.getY() + this.m12;
+		t.set(x, y);
+	}
+
+	/** Multiply this matrix by the tuple <x,y> and return the result (r = this*<x,y>).
+	 * 
+	 * @param x
+	 * @param y
+	 * @return the transformation result
+	 */
+	public Point2D transform(float x, float y) {
+		float rx, ry;
+		rx = this.m00 * x + this.m01 * y + this.m02;
+		ry = this.m10 * x + this.m11 * y + this.m12;
+		return new Point2f(rx, ry);
+	}
+
+	/**
+	 * 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 void transform(Tuple2D<?> t, Tuple2D<?> result) {
+		result.set(
+				this.m00 * t.getX() + this.m01 * t.getY() + this.m02,
+				this.m10 * t.getX() + this.m11 * t.getY() + this.m12);
+	}
+
+	/**
+	 * Returns an <code>Transform2D</code> object representing the
+	 * inverse transformation.
+	 * The inverse transform Tx' of this transform Tx
+	 * maps coordinates transformed by Tx back
+	 * to their original coordinates.
+	 * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)).
+	 * <p>
+	 * If this transform maps all coordinates onto a point or a line
+	 * then it will not have an inverse, since coordinates that do
+	 * not lie on the destination point or line will not have an inverse
+	 * mapping.
+	 * The <code>determinant</code> method can be used to determine if this
+	 * transform has no inverse, in which case an exception will be
+	 * thrown if the <code>createInverse</code> method is called.
+	 * @return a new <code>Transform2D</code> object representing the
+	 * inverse transformation.
+	 * @see #determinant()
+	 * @throws SingularMatrixException if the matrix cannot be inverted.
+	 */
+	public Transform2D createInverse() {
+		float det = this.m00 * this.m11 - this.m01 * this.m10;
+		if (Math.abs(det) <= Double.MIN_VALUE) {
+			throw new SingularMatrixException("Determinant is "+det); //$NON-NLS-1$
+		}
+		return new Transform2D(
+				this.m11 / det, 
+				-this.m01 / det, 
+				(this.m01 * this.m12 - this.m11 * this.m02) / det, 
+				-this.m10 / det, 
+				this.m00 / det, 
+				(this.m10 * this.m02 - this.m00 * this.m12) / det);
+	}
+
+	/**
+	 * Set the components of the transformation.
+	 * 
+	 * @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
+	 */
+	public void set(float m00, float m01, float m02, float m10, float m11, float m12) {
+		set(m00, m01, m02, m10, m11, m12, 0f, 0f, 1f);
+	}
+
+	/**
+	 * Invert this transformation.
+	 * The inverse transform Tx' of this transform Tx
+	 * maps coordinates transformed by Tx back
+	 * to their original coordinates.
+	 * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)).
+	 * <p>
+	 * If this transform maps all coordinates onto a point or a line
+	 * then it will not have an inverse, since coordinates that do
+	 * not lie on the destination point or line will not have an inverse
+	 * mapping.
+	 * The <code>determinant</code> method can be used to determine if this
+	 * transform has no inverse, in which case an exception will be
+	 * thrown if the <code>createInverse</code> method is called.
+	 * @see #determinant()
+	 * @throws SingularMatrixException if the matrix cannot be inverted.
+	 */
+	@Override
+	public void invert() {
+		float det = this.m00 * this.m11 - this.m01 * this.m10;
+		if (Math.abs(det) <= Double.MIN_VALUE) {
+			throw new SingularMatrixException("Determinant is "+det); //$NON-NLS-1$
+		}
+		set(
+				this.m11 / det, 
+				-this.m01 / det, 
+				(this.m01 * this.m12 - this.m11 * this.m02) / det, 
+				-this.m10 / det, 
+				this.m00 / det, 
+				(this.m10 * this.m02 - this.m00 * this.m12) / det);
+	}
+
+	/**
+	 * Invert this transformation.
+	 * The inverse transform Tx' of this transform Tx
+	 * maps coordinates transformed by Tx back
+	 * to their original coordinates.
+	 * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)).
+	 * <p>
+	 * If this transform maps all coordinates onto a point or a line
+	 * then it will not have an inverse, since coordinates that do
+	 * not lie on the destination point or line will not have an inverse
+	 * mapping.
+	 * The <code>determinant</code> method can be used to determine if this
+	 * transform has no inverse, in which case an exception will be
+	 * thrown if the <code>createInverse</code> method is called.
+	 * @param m is the matrix to invert
+	 * @see #determinant()
+	 * @throws SingularMatrixException if the matrix cannot be inverted.
+	 */
+	@Override
+	public void invert(Matrix3f m) {
+		float det = m.m00 * m.m11 - m.m01 * m.m10;
+		if (Math.abs(det) <= Double.MIN_VALUE) {
+			throw new SingularMatrixException("Determinant is "+det); //$NON-NLS-1$
+		}
+		set(
+				m.m11 / det, 
+				-m.m01 / det, 
+				(m.m01 * m.m12 - m.m11 * m.m02) / det, 
+				-m.m10 / det, 
+				m.m00 / det, 
+				(m.m10 * m.m02 - m.m00 * m.m12) / det);
+	}
+
+}

Added: trunk/math/src/main/java/org/arakhne/afc/math/matrix/Transform3D.java
===================================================================
--- trunk/math/src/main/java/org/arakhne/afc/math/matrix/Transform3D.java	                        (rev 0)
+++ trunk/math/src/main/java/org/arakhne/afc/math/matrix/Transform3D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,353 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.matrix;
+
+import org.arakhne.afc.math.continous.object3d.Quaternion;
+import org.arakhne.afc.math.continous.object3d.Vector3f;
+import org.arakhne.afc.math.generic.Tuple3D;
+
+
+/** A 3D transformation.
+ * Is represented internally as a 4x4 floating point matrix. The
+ * mathematical representation is row major, as in traditional
+ * matrix mathematics.
+ * <p>
+ * The transformation matrix is:
+ * <pre><code>
+ * | r11 | r12 | r13 | Tx |
+ * | r21 | r22 | r23 | Ty |
+ * | r31 | r32 | r33 | Tz |
+ * | 0   | 0   | 0   | 1  |
+ * </code></pre>
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Transform3D extends Matrix4f {
+	
+	private static final long serialVersionUID = -8427812783666663224L;
+	
+	/** This is the identifity transformation.
+	 */
+	public static final Transform3D IDENTITY = new Transform3D();
+
+	/**
+	 * Constructs a new Transform3D object and sets it to the identity transformation.
+	 */
+	public Transform3D() {
+		setIdentity();
+	}
+	
+	/**
+	 * Constructs and initializes a Matrix4f from the specified nine 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
+	 */
+	public Transform3D(float m00, float m01, float m02, float m03, float m10, float m11, float m12, float m13, float m20, float m21, float m22, float m23) {
+		super(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, 0f, 0f, 0f, 1f);
+	}
+
+	/**
+	 * Constructs a new Transform3D object and initializes it from the
+	 * specified transform.
+	 * 
+	 * @param t
+	 */
+	public Transform3D(Transform3D t) {
+		super(t);
+	}
+
+	/**
+	 * @param m
+	 */
+	public Transform3D(Matrix4f m) {
+		super(m);
+	}
+
+	@Override
+	public Transform3D  clone() {
+		return (Transform3D)super.clone();
+	}
+
+	/** Set the position.
+	 * 
+	 * @param x
+	 * @param y
+	 * @param z
+	 */
+	public void setTranslation(float x, float y, float z) {
+		this.m03 = x;
+		this.m13 = y;
+		this.m23 = z;
+	}
+	
+	/** Set the position.
+	 * 
+	 * @param t
+	 */
+	public void setTranslation(Tuple3D<?> t) {
+		this.m03 = t.getX();
+		this.m13 = t.getY();
+		this.m23 = t.getZ();
+	}
+
+	/** Translate the position.
+	 * 
+	 * @param dx
+	 * @param dy
+	 * @param dz
+	 */
+	public void translate(float dx, float dy, float dz) {
+		this.m03 += dx;
+		this.m13 += dy;
+		this.m23 += dz;
+	}
+	
+	/** Translate the position.
+	 * 
+	 * @param t
+	 */
+	public void translate(Vector3f t) {
+		this.m03 += t.getX();
+		this.m13 += t.getY();
+		this.m23 += t.getZ();
+	}
+
+	/** Replies the X translation.
+	 * 
+	 * @return the amount
+	 */
+	public float getTranslationX() {
+		return this.m03;
+	}
+
+	/** Replies the Y translation.
+	 * 
+	 * @return the amount
+	 */
+	public float getTranslationY() {
+		return this.m13;
+	}
+
+	/** Replies the Z translation.
+	 * 
+	 * @return the amount
+	 */
+	public float getTranslationZ() {
+		return this.m23;
+	}
+
+	/** Replies the translation.
+	 * 
+	 * @return the amount
+	 */
+	public Vector3f getTranslation() {
+		return new Vector3f(this.m03, this.m13, this.m23);
+	}
+
+	/**
+     * Replies the rotation for the object.
+	 * 
+	 * @return the amount
+     */
+    public Quaternion getRotation() {
+    	Quaternion q = new Quaternion();
+    	q.setFromMatrix(this);
+    	return q;
+    }
+
+    /**
+     * Set the rotation for the object but do not change the translation.
+     * 
+     * @param rotation
+     * @see #makeRotationMatrix(Quaternion)
+     */
+    public void setRotation(Quaternion rotation) {
+		this.m00 = (1.0f - 2.0f*rotation.getY()*rotation.getY() - 2.0f*rotation.getZ()*rotation.getZ());
+        this.m10 = (2.0f*(rotation.getX()*rotation.getY() + rotation.getW()*rotation.getZ()));
+        this.m20 = (2.0f*(rotation.getX()*rotation.getZ() - rotation.getW()*rotation.getY()));
+
+        this.m01 = (2.0f*(rotation.getX()*rotation.getY() - rotation.getW()*rotation.getZ()));
+        this.m11 = (1.0f - 2.0f*rotation.getX()*rotation.getX() - 2.0f*rotation.getZ()*rotation.getZ());
+        this.m21 = (2.0f*(rotation.getY()*rotation.getZ() + rotation.getW()*rotation.getX()));
+
+        this.m02 = (2.0f*(rotation.getX()*rotation.getZ() + rotation.getW()*rotation.getY()));
+        this.m12 = (2.0f*(rotation.getY()*rotation.getZ() - rotation.getW()*rotation.getX()));
+        this.m22 = (1.0f - 2.0f*rotation.getX()*rotation.getX() - 2.0f*rotation.getY()*rotation.getY());
+    }
+
+    /**
+     * Rotate the object.
+     * 
+     * @param rotation
+     */
+    public void rotate(Quaternion rotation) {
+    	Transform3D m = new Transform3D();
+    	m.makeRotationMatrix(rotation);
+    	mul(m);
+    }
+    
+    /**
+	 * Sets the value of this matrix to a rotation matrix, and no translation.
+	 * 
+	 * @param rotation
+	 */
+	public final void makeRotationMatrix(Quaternion rotation) {
+		this.m00 = (1.0f - 2.0f*rotation.getY()*rotation.getY() - 2.0f*rotation.getZ()*rotation.getZ());
+        this.m10 = (2.0f*(rotation.getX()*rotation.getY() + rotation.getW()*rotation.getZ()));
+        this.m20 = (2.0f*(rotation.getX()*rotation.getZ() - rotation.getW()*rotation.getY()));
+
+        this.m01 = (2.0f*(rotation.getX()*rotation.getY() - rotation.getW()*rotation.getZ()));
+        this.m11 = (1.0f - 2.0f*rotation.getX()*rotation.getX() - 2.0f*rotation.getZ()*rotation.getZ());
+        this.m21 = (2.0f*(rotation.getY()*rotation.getZ() + rotation.getW()*rotation.getX()));
+
+        this.m02 = (2.0f*(rotation.getX()*rotation.getZ() + rotation.getW()*rotation.getY()));
+        this.m12 = (2.0f*(rotation.getY()*rotation.getZ() - rotation.getW()*rotation.getX()));
+        this.m22 = (1.0f - 2.0f*rotation.getX()*rotation.getX() - 2.0f*rotation.getY()*rotation.getY());
+
+        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 to the given translation, without rotation.
+	 * 
+	 * @param dx is the position to put in the matrix.
+	 * @param dy is the position to put in the matrix.
+	 * @param dz is the position to put in the matrix.
+	 */
+	public final void makeTranslationMatrix(float dx, float dy, float dz) {
+		this.m00 = 1f;
+		this.m01 = 0f;
+		this.m02 = 0f;
+		this.m03 = dx;
+
+		this.m10 = 0f;
+		this.m11 = 1f;
+		this.m12 = 0f;
+		this.m13 = dy;
+
+		this.m20 = 0f;
+		this.m21 = 0f;
+		this.m22 = 1f;
+		this.m23 = dz;
+
+		this.m30 = 0f;
+		this.m31 = 0f;
+		this.m32 = 0f;
+		this.m33 = 1f;
+	}
+
+	/**
+	 * 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 void transform(Tuple3D<?> t) {
+		float x, y, z;
+		x = this.m00 * t.getX() + this.m01 * t.getY() + this.m02 * t.getZ() + this.m03;
+		y = this.m10 * t.getX() + this.m11 * t.getY() + this.m12 * t.getZ() + this.m13;
+		z = this.m20 * t.getX() + this.m21 * t.getY() + this.m22 * t.getZ() + this.m23;
+		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 void transform(Tuple3D<?> t, Tuple3D<?> result) {
+		result.set(
+				this.m00 * t.getX() + this.m01 * t.getY() + this.m02 * t.getZ() + this.m03,
+				this.m10 * t.getX() + this.m11 * t.getY() + this.m12 * t.getZ() + this.m13,
+				this.m20 * t.getX() + this.m21 * t.getY() + this.m22 * t.getZ() + this.m23);
+	}
+
+	/**
+	 * Set the components of the transformation.
+	 * 
+	 * @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
+	 */
+	public void set(float m00, float m01, float m02, float m03, float m10, float m11, float m12, float m13, float m20, float m21, float m22, float m23) {
+		set(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, 0f, 0f, 0f, 1f);
+	}
+
+}

Added: trunk/math/src/main/resources/org/arakhne/afc/math/continuous/Matrix3f.properties
===================================================================
--- trunk/math/src/main/resources/org/arakhne/afc/math/continuous/Matrix3f.properties	                        (rev 0)
+++ trunk/math/src/main/resources/org/arakhne/afc/math/continuous/Matrix3f.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = The matrix cannot be inverted.

Added: trunk/math/src/main/resources/org/arakhne/afc/math/continuous/Matrix3f_fr.properties
===================================================================
--- trunk/math/src/main/resources/org/arakhne/afc/math/continuous/Matrix3f_fr.properties	                        (rev 0)
+++ trunk/math/src/main/resources/org/arakhne/afc/math/continuous/Matrix3f_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = La matrice ne peut pas \xEAtre invers\xE9e.

Added: trunk/math/src/test/java/org/arakhne/afc/math/AbstractMathTestCase.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/AbstractMathTestCase.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/AbstractMathTestCase.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,263 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math;
+
+import java.math.BigDecimal;
+
+import junit.framework.TestCase;
+
+import org.arakhne.afc.math.continous.object2d.Tuple2f;
+
+/**
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractMathTestCase extends TestCase {
+	
+	/** Precision of the floating point number epsilon-tests.
+	 */
+	protected final static int DEFAULT_DECIMAL_COUNT = 8;
+	
+	private int decimalPrecision = DEFAULT_DECIMAL_COUNT;
+	
+	/** Set the epsilon used ben testing floating-point values.
+	 * 
+	 * @param precision is the count of decimal digits to support
+	 */
+	protected void setDecimalPrecision(int precision) {
+		this.decimalPrecision = Math.max(0, precision);
+	}
+	
+	/** Set the epsilon used ben testing floating-point values to
+	 * its default value.
+	 */
+	protected void setDefaultDecimalPrecision() {
+		this.decimalPrecision = DEFAULT_DECIMAL_COUNT;
+	}
+	
+	/** Test if the actual value is equal to the expected value with
+	 * a distance of epsilon.
+	 * 
+	 * @param expected
+	 * @param actual
+	 */
+	protected void assertEpsilonEquals(float expected, float actual) {
+		assertEpsilonEquals(null, expected, actual);
+	}
+	
+	/** Test if the actual value is not equal to the expected value with
+	 * a distance of epsilon.
+	 * 
+	 * @param expected
+	 * @param actual
+	 */
+	protected void assertNotEpsilonEquals(float expected, float actual) {
+		assertNotEpsilonEquals(null, expected, actual);
+	}
+
+	/** Replies if two values are equals at espilon.
+	 * 
+	 * @param a
+	 * @param b
+	 * @param isNaNEqual indicates if the NaN value is equals to itself.
+	 * @return <code>true</code> or <code>false</code>
+	 */
+	private boolean isEpsilonEquals(float a, float b, boolean isNaNEqual) {
+		if (a==b) return true;
+		boolean nanA = Float.isNaN(a);
+		boolean nanB = Float.isNaN(b);
+		if (nanA || nanB) {
+			if (isNaNEqual) return nanA==nanB;
+			return false;
+		}
+		if (!Float.isInfinite(a) && !Float.isInfinite(a)
+			&& !Float.isNaN(a) && !Float.isNaN(b)) {
+			return isEpsilonEquals(new BigDecimal(a), new BigDecimal(b), this.decimalPrecision/2);
+		}
+		return false;
+	}
+	
+	/** Replies if two values are equals at espilon.
+	 * 
+	 * @param a
+	 * @param b
+	 * @return <code>true</code> or <code>false</code>
+	 */
+	protected boolean isEpsilonEquals(float a, float b) {
+		return isEpsilonEquals(a, b, true);
+	}
+
+	/** Replies if two values are equals at espilon.
+	 * 
+	 * @param a
+	 * @param b
+	 * @return <code>true</code> or <code>false</code>
+	 */
+	protected boolean isEpsilonEquals(BigDecimal a, BigDecimal b) {
+		return isEpsilonEquals(a, b, this.decimalPrecision);
+	}
+
+	/** Replies if two values are equals at espilon.
+	 * 
+	 * @param a
+	 * @param b
+	 * @param precision is the number of decimal digits to test.
+	 * @return <code>true</code> or <code>false</code>
+	 */
+	protected static boolean isEpsilonEquals(BigDecimal a, BigDecimal b, int precision) {
+		BigDecimal ma = a.movePointRight(precision);
+		BigDecimal mb = b.movePointRight(precision);
+		BigDecimal aa = ma.setScale(0,BigDecimal.ROUND_HALF_UP);
+		BigDecimal bb = mb.setScale(0,BigDecimal.ROUND_HALF_UP);
+		if (aa.compareTo(bb)==0) return true;
+		aa = ma.setScale(0,BigDecimal.ROUND_DOWN);
+		bb = mb.setScale(0,BigDecimal.ROUND_DOWN);
+		return (aa.compareTo(bb)==0);
+	}
+	
+	/** Test if the actual value is equal to the expected value with
+	 * a distance of epsilon.
+	 * 
+	 * @param message
+	 * @param expected
+	 * @param actual
+	 */
+	protected void assertEpsilonEquals(String message, float expected, float actual) {
+		if (isEpsilonEquals(expected,actual)) return;
+		fail((message==null ? "" : (message+": "))  //$NON-NLS-1$//$NON-NLS-2$
+				+"not same float value, expected:"+ //$NON-NLS-1$
+				expected
+				+", actual:"+actual); //$NON-NLS-1$
+	}
+
+	/** Test if the actual value is not equal to the expected value with
+	 * a distance of epsilon.
+	 * 
+	 * @param message
+	 * @param expected
+	 * @param actual
+	 */
+	protected void assertNotEpsilonEquals(String message, float expected, float actual) {
+		if (!isEpsilonEquals(expected,actual, false)) return;
+		fail((message==null ? "" : (message+": "))  //$NON-NLS-1$//$NON-NLS-2$
+				+"same float value, unexpected:"+ //$NON-NLS-1$
+				expected
+				+", actual:"+actual); //$NON-NLS-1$
+	}
+
+	/** Test if the actual value is equal to the expected value with
+	 * a distance of epsilon.
+	 * 
+	 * @param expected
+	 * @param actual
+	 */
+	protected void assertEpsilonEquals(Tuple2f<?> expected, Tuple2f<?> actual) {
+		assertEpsilonEquals(null, expected, actual);
+	}
+
+	/** Test if the actual value is not equal to the expected value with
+	 * a distance of epsilon.
+	 * 
+	 * @param expected
+	 * @param actual
+	 */
+	protected void assertNotEpsilonEquals(Tuple2f<?> expected, Tuple2f<?> actual) {
+		assertNotEpsilonEquals(null, expected, actual);
+	}
+
+	/** Test if the actual value is equal to the expected value with
+	 * a distance of epsilon.
+	 * 
+	 * @param message
+	 * @param expected
+	 * @param actual
+	 */
+	protected void assertEpsilonEquals(String message, Tuple2f<?> expected, Tuple2f<?> actual) {
+		if (!isEpsilonEquals(expected.getX(), actual.getX())) {
+			fail((message==null ? "" : (message+": "))  //$NON-NLS-1$//$NON-NLS-2$
+					+"not same x value, expected:"+ //$NON-NLS-1$
+					expected.getX()
+					+", actual:"+actual.getX()); //$NON-NLS-1$
+			return;
+		}
+		if (!isEpsilonEquals(expected.getY(), actual.getY())) {
+			fail((message==null ? "" : (message+": "))  //$NON-NLS-1$//$NON-NLS-2$
+					+"not same y value, expected:"+ //$NON-NLS-1$
+					expected.getY()
+					+", actual:"+actual.getY()); //$NON-NLS-1$
+			return;
+		}
+	}
+
+	/** Test if the actual value is not equal to the expected value with
+	 * a distance of epsilon.
+	 * 
+	 * @param message
+	 * @param expected
+	 * @param actual
+	 */
+	protected void assertNotEpsilonEquals(String message, Tuple2f<?> expected, Tuple2f<?> actual) {
+		if (isEpsilonEquals(expected.getX(), actual.getX(), false)) {
+			fail((message==null ? "" : (message+": "))  //$NON-NLS-1$//$NON-NLS-2$
+					+"same x value, unexpected:"+ //$NON-NLS-1$
+					expected.getX()
+					+", actual:"+actual.getX()); //$NON-NLS-1$
+			return;
+		}
+		if (isEpsilonEquals(expected.getY(), actual.getY(), false)) {
+			fail((message==null ? "" : (message+": "))  //$NON-NLS-1$//$NON-NLS-2$
+					+"not same y value, expected:"+ //$NON-NLS-1$
+					expected.getY()
+					+", actual:"+actual.getY()); //$NON-NLS-1$
+			return;
+		}
+	}
+
+	/** Test if the actual value is not equal to the expected value with
+	 * a distance of epsilon.
+	 * 
+	 * @param expected
+	 * @param actual
+	 */
+	protected void assertNotEquals(int expected, int actual) {
+		assertNotEquals(null, expected, actual);
+	}
+
+	/** Test if the actual value is not equal to the expected value with
+	 * a distance of epsilon.
+	 * 
+	 * @param message
+	 * @param expected
+	 * @param actual
+	 */
+	@SuppressWarnings("static-method")
+	protected void assertNotEquals(String message, int expected, int actual) {
+		if (expected==actual) {
+			fail((message==null ? "" : (message+": "))  //$NON-NLS-1$//$NON-NLS-2$
+					+"same value, expecting not equal to:"+ //$NON-NLS-1$
+					expected);
+		}
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/continous/matrix/Matrix2fTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/continous/matrix/Matrix2fTest.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/continous/matrix/Matrix2fTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,107 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.matrix;
+
+import org.arakhne.afc.math.AbstractMathTestCase;
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object2d.Tuple2f;
+import org.arakhne.afc.math.continous.object2d.Vector2f;
+import org.arakhne.afc.math.matrix.Matrix2f;
+
+/**
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Matrix2fTest extends AbstractMathTestCase {
+	
+	/**
+	 */
+	public void testCovMatrix2Tuple2dArray_theory() {
+		Vector2f v1 = new Vector2f(1, 3);
+		Vector2f v2 = new Vector2f(4, -2);
+		Vector2f m = new Vector2f();
+		m.add(v1,v2);
+		m.scale(.5f);
+		
+		Matrix2f expected = new Matrix2f();
+		expected.m00 = ((v1.getX()-m.getX()) * (v1.getX()-m.getX()) + (v2.getX()-m.getX()) * (v2.getX()-m.getX())) / 2f;
+		expected.m01 = ((v1.getX()-m.getX()) * (v1.getY()-m.getY()) + (v2.getX()-m.getX()) * (v2.getY()-m.getY())) / 2f;
+		expected.m10 = ((v1.getY()-m.getY()) * (v1.getX()-m.getX()) + (v2.getY()-m.getY()) * (v2.getX()-m.getX())) / 2f;
+		expected.m11 = ((v1.getY()-m.getY()) * (v1.getY()-m.getY()) + (v2.getY()-m.getY()) * (v2.getY()-m.getY())) / 2f;
+		
+		Matrix2f mat = new Matrix2f();
+		Tuple2f<?> mean = mat.cov(v1, v2);
+		
+		assertEpsilonEquals(m, mean);
+		for(int i=0; i<2; ++i) {
+			for(int j=0; j<2; ++j) {
+				assertEpsilonEquals("i="+i+"; j="+j, //$NON-NLS-1$ //$NON-NLS-2$
+						expected.getElement(i, j), 
+						mat.getElement(i, j));
+			}
+		}
+	}
+	
+	/**
+	 */
+	public void testCovMatrix2Tuple2dArray_example() {
+		// From "Mathematics for 3D Game Programming and Computer Graphics" pp.220
+		// Adapted to 2D by Stephane Galland
+		//
+		// P1 = [ -1, -2 ]
+		// P2 = [ 1, 0 ]
+		// P3 = [ 2, -1 ]
+		// P4 = [ 2, -1 ]
+		//
+		// average: m = [ 1, -1 ]
+		//
+		// Cov = [ 1.5 ,  .5 ]
+		//       [  .5 ,  .5 ]
+		
+		Point2f p1 = new Point2f(-1, -2);
+		Point2f p2 = new Point2f(1, 0);
+		Point2f p3 = new Point2f(2, -1);
+		Point2f p4 = new Point2f(2, -1);
+		
+		Matrix2f cov = new Matrix2f();
+		Tuple2f<?> mean = cov.cov(p1, p2, p3, p4);
+		
+		Point2f expectedMean = new Point2f(1, -1); 
+		Matrix2f expectedCov = new Matrix2f();
+		expectedCov.m00 = 1.5f;
+		expectedCov.m01 = .5f;
+		expectedCov.m10 = .5f;
+		expectedCov.m11 = .5f;
+		
+		assertEpsilonEquals(expectedMean, mean);
+		for(int i=0; i<2; ++i) {
+			for(int j=0; j<2; ++j) {
+				assertEpsilonEquals("i="+i+"; j="+j, //$NON-NLS-1$ //$NON-NLS-2$
+						expectedCov.getElement(i, j), 
+						cov.getElement(i, j));
+			}
+		}
+	}
+	
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/continous/matrix/Transform2DTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/continous/matrix/Transform2DTest.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/continous/matrix/Transform2DTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,760 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.matrix;
+
+import org.arakhne.afc.math.AbstractMathTestCase;
+import org.arakhne.afc.math.MathUtil;
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object2d.Vector2f;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+/**
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Transform2DTest extends AbstractMathTestCase {
+
+	private Transform2D t;
+
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		this.t = new Transform2D(1f, 2f, 3f, 4f, 5f, 6f);
+	}
+
+	@Override
+	protected void tearDown() throws Exception {
+		this.t = null;
+		super.tearDown();
+	}
+
+	/**
+	 */
+	public void testSetTranslationFloatFloat() {
+		this.t.setTranslation(3.4f, 5.6f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3.4f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(5.6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testSetTranslationTuple2D() {
+		this.t.setTranslation(new Point2f(3.4f, 5.6f));
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3.4f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(5.6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testTranslateFloatFloat() {
+		this.t.translate(3.4f, 5.6f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(17.6f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(47.6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.translate(-3.4f, -5.6f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testTranslateFloatFloat_withRot() {
+		this.t.translate(3.4f, 5.6f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(17.6f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(47.6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.rotate(6.7f);        
+
+		this.t.translate(-3.4f, -5.6f);
+		assertEpsilonEquals(1.724082989468516f, this.t.getM00());
+		assertEpsilonEquals(1.423916375854041f, this.t.getM01());
+		assertEpsilonEquals(3.764186131024417f, this.t.getM02());
+		assertEpsilonEquals(5.681782196024269f, this.t.getM10());
+		assertEpsilonEquals(2.952516058710204f, this.t.getM11());
+		assertEpsilonEquals(11.747850604740339f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testTranslateVector2f() {
+		this.t.translate(new Vector2f(3.4f, 5.6f));
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(17.6f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(47.6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.translate(new Vector2f(-3.4f, -5.6f));
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testTranslateVector2f_withRot() {
+		this.t.translate(new Vector2f(3.4f, 5.6f));
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(17.6f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(47.6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.rotate(6.7f);        
+
+		this.t.translate(new Vector2f(-3.4f, -5.6f));
+		assertEpsilonEquals(1.724082989468516f, this.t.getM00());
+		assertEpsilonEquals(1.423916375854041f, this.t.getM01());
+		assertEpsilonEquals(3.764186131024417f, this.t.getM02());
+		assertEpsilonEquals(5.681782196024269f, this.t.getM10());
+		assertEpsilonEquals(2.952516058710204f, this.t.getM11());
+		assertEpsilonEquals(11.747850604740339f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testGetTranslationX() {
+		assertEpsilonEquals(this.t.getM02(), this.t.getTranslationX());
+	}
+
+	/**
+	 */
+	public void testGetTranslationY() {
+		assertEpsilonEquals(this.t.getM12(), this.t.getTranslationY());
+	}
+
+	/**
+	 */
+	public void testGetTranslationVector() {
+		Vector2f v = this.t.getTranslationVector();
+		assertEpsilonEquals(this.t.getM02(), v.getX());
+		assertEpsilonEquals(this.t.getM12(), v.getY());
+	}
+
+	/**
+	 */
+	public void testGetRotation() {
+		assertEpsilonEquals(0f, this.t.getRotation());
+		this.t.setRotation(6.7f);
+		assertEpsilonEquals(MathUtil.clampRadianMinusPIToPI(6.7f), this.t.getRotation());
+	}
+
+	/**
+	 */
+	public void testSetRotationFloat() {
+		this.t.setRotation(6.7f);
+		assertEpsilonEquals(0.914383148235319f, this.t.getM00());
+		assertEpsilonEquals(-0.404849920616598f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(0.404849920616598f, this.t.getM10());
+		assertEpsilonEquals(0.914383148235319f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testRotateFloat() {
+		this.t.rotate(6.7f);
+		assertEpsilonEquals(1.724082989468516f, this.t.getM00());
+		assertEpsilonEquals(1.423916375854041f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(5.681782196024269f, this.t.getM10());
+		assertEpsilonEquals(2.952516058710204f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.rotate(-6.7f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testRotate_withTrans() {
+		this.t.rotate(6.7f);
+		assertEpsilonEquals(1.724082989468516f, this.t.getM00());
+		assertEpsilonEquals(1.423916375854041f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(5.681782196024269f, this.t.getM10());
+		assertEpsilonEquals(2.952516058710204f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.translate(3.4f, 5.6f);
+
+		this.t.rotate(-6.7f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(16.835813868975581f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(41.852149395259651f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testSetScaleFloatFloat() {
+		this.t.setScale(8.9f, 10.11f);
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(10.11f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testSetScaleTuple2D() {
+		this.t.setScale(new Vector2f(8.9f, 10.11f));
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(10.11f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testScaleFloatFloat() {
+		this.t.scale(8.9f, 10.11f);
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(20.22f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(35.6f, this.t.getM10());
+		assertEpsilonEquals(50.55f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.scale(1f/8.9f, 1f/10.11f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testScaleFloatFloat_withTrans() {
+		this.t.scale(8.9f, 10.11f);
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(20.22f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(35.6f, this.t.getM10());
+		assertEpsilonEquals(50.55f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.translate(3.4f, 5.6f);
+
+		this.t.scale(1f/8.9f, 1f/10.11f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(146.492f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(410.12f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testScaleFloatFloat_withRot() {
+		this.t.scale(8.9f, 10.11f);
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(20.22f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(35.6f, this.t.getM10());
+		assertEpsilonEquals(50.55f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.rotate(6.7f);
+
+		this.t.scale(1f/8.9f, 1f/10.11f);
+		assertEpsilonEquals(1.834165776872130f, this.t.getM00());
+		assertEpsilonEquals(1.472370223919924f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(5.956989164533305f, this.t.getM10());
+		assertEpsilonEquals(3.146331450973738f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testScaleTuple2D() {
+		this.t.scale(new Vector2f(8.9f, 10.11f));
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(20.22f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(35.6f, this.t.getM10());
+		assertEpsilonEquals(50.55f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.scale(new Vector2f(1f/8.9f, 1f/10.11f));
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testScaleTuple2D_withTrans() {
+		this.t.scale(new Vector2f(8.9f, 10.11f));
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(20.22f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(35.6f, this.t.getM10());
+		assertEpsilonEquals(50.55f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.translate(3.4f, 5.6f);
+
+		this.t.scale(new Vector2f(1f/8.9f, 1f/10.11f));
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(146.492f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(410.12f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testScaleTuple2D_withRot() {
+		this.t.scale(new Vector2f(8.9f, 10.11f));
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(20.22f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(35.6f, this.t.getM10());
+		assertEpsilonEquals(50.55f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.rotate(6.7f);
+
+		this.t.scale(new Vector2f(1f/8.9f, 1f/10.11f));
+		assertEpsilonEquals(1.834165776872130f, this.t.getM00());
+		assertEpsilonEquals(1.472370223919924f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(5.956989164533305f, this.t.getM10());
+		assertEpsilonEquals(3.146331450973738f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testGetScaleX() {
+		assertEpsilonEquals(this.t.getM00(), this.t.getScaleX());
+	}
+
+	/**
+	 */
+	public void testGetScaleY() {
+		assertEpsilonEquals(this.t.getM11(), this.t.getScaleY());
+	}
+
+	/**
+	 */
+	public void testGetScaleVector() {
+		Vector2f v = this.t.getScaleVector();
+		assertEpsilonEquals(this.t.getM00(), v.getX());
+		assertEpsilonEquals(this.t.getM11(), v.getY());
+	}
+
+	/**
+	 */
+	public void testSetShearFloatFloat() {
+		this.t.setShear(8.9f, 10.11f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(8.9f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(10.11f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testSetShearTuple2D() {
+		this.t.setShear(new Vector2f(8.9f, 10.11f));
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(8.9f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(10.11f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testShearFloatFloat() {
+		this.t.shear(8.9f, 10.11f);
+		assertEpsilonEquals(21.22f, this.t.getM00());
+		assertEpsilonEquals(10.9f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(54.55f, this.t.getM10());
+		assertEpsilonEquals(40.6f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.shear(-8.9f, -10.11f);
+		assertEpsilonEquals(-88.979f, this.t.getM00());
+		assertEpsilonEquals(-177.958f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(-355.916f, this.t.getM10());
+		assertEpsilonEquals(-444.895f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testShearTuple2D() {
+		this.t.shear(new Vector2f(8.9f, 10.11f));
+		assertEpsilonEquals(21.22f, this.t.getM00());
+		assertEpsilonEquals(10.9f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(54.55f, this.t.getM10());
+		assertEpsilonEquals(40.6f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.shear(new Vector2f(-8.9f, -10.11f));
+		assertEpsilonEquals(-88.979f, this.t.getM00());
+		assertEpsilonEquals(-177.958f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(-355.916f, this.t.getM10());
+		assertEpsilonEquals(-444.895f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testGetShearX() {
+		assertEpsilonEquals(this.t.getM01(), this.t.getShearX());
+	}
+
+	/**
+	 */
+	public void testGetShearY() {
+		assertEpsilonEquals(this.t.getM10(), this.t.getShearY());
+	}
+
+	/**
+	 */
+	public void testGetShearVector() {
+		Vector2f v = this.t.getShearVector();
+		assertEpsilonEquals(this.t.getM01(), v.getX());
+		assertEpsilonEquals(this.t.getM10(), v.getY());
+	}
+
+	/**
+	 */
+	public void testMakeRotationMatrix() {
+		this.t.makeRotationMatrix(6.7f);
+		assertEpsilonEquals(0.914383148235319f, this.t.getM00());
+		assertEpsilonEquals(-0.404849920616598f, this.t.getM01());
+		assertEpsilonEquals(0f, this.t.getM02());
+		assertEpsilonEquals(0.404849920616598f, this.t.getM10());
+		assertEpsilonEquals(0.914383148235319f, this.t.getM11());
+		assertEpsilonEquals(0f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testMakeTranslationMatrix() {
+		this.t.makeTranslationMatrix(3.4f, 5.6f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(0f, this.t.getM01());
+		assertEpsilonEquals(3.4f, this.t.getM02());
+		assertEpsilonEquals(0f, this.t.getM10());
+		assertEpsilonEquals(1f, this.t.getM11());
+		assertEpsilonEquals(5.6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testMakeScaleMatrix() {
+		this.t.makeScaleMatrix(8.9f, 10.11f);
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(0f, this.t.getM01());
+		assertEpsilonEquals(0f, this.t.getM02());
+		assertEpsilonEquals(0f, this.t.getM10());
+		assertEpsilonEquals(10.11f, this.t.getM11());
+		assertEpsilonEquals(0f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testMakeShearMatrix() {
+		this.t.makeShearMatrix(8.9f, 10.11f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(8.9f, this.t.getM01());
+		assertEpsilonEquals(0f, this.t.getM02());
+		assertEpsilonEquals(10.11f, this.t.getM10());
+		assertEpsilonEquals(1f, this.t.getM11());
+		assertEpsilonEquals(0f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testTransformTuple2D() {
+		Point2f p = new Point2f();
+
+		p.set(2.3f, 4.5f);
+		this.t.transform(p);
+		assertEpsilonEquals(14.3f, p.getX());
+		assertEpsilonEquals(37.7f, p.getY());
+
+		p.set(-2.3f, -4.5f);
+		this.t.transform(p);
+		assertEpsilonEquals(-8.3f, p.getX());
+		assertEpsilonEquals(-25.7f, p.getY());
+	}
+
+	/**
+	 */
+	public void testTransformFloatFloat() {
+		Point2D p;
+
+		p = this.t.transform(2.3f, 4.5f);
+		assertEpsilonEquals(14.3f, p.getX());
+		assertEpsilonEquals(37.7f, p.getY());
+
+		p = this.t.transform(-2.3f, -4.5f);
+		assertEpsilonEquals(-8.3f, p.getX());
+		assertEpsilonEquals(-25.7f, p.getY());
+	}
+
+	/**
+	 */
+	public void testTransformTuple2DTuple2D() {
+		Point2f p = new Point2f();
+		Point2f r = new Point2f();
+
+		p.set(2.3f, 4.5f);
+		this.t.transform(p, r);
+		assertEpsilonEquals(2.3f, p.getX());
+		assertEpsilonEquals(4.5f, p.getY());
+		assertEpsilonEquals(14.3f, r.getX());
+		assertEpsilonEquals(37.7f, r.getY());
+
+		p.set(-2.3f, -4.5f);
+		this.t.transform(p,r);
+		assertEpsilonEquals(-2.3f, p.getX());
+		assertEpsilonEquals(-4.5f, p.getY());
+		assertEpsilonEquals(-8.3f, r.getX());
+		assertEpsilonEquals(-25.7f, r.getY());
+	}
+
+	/**
+	 */
+	public void testSet() {
+		this.t.set(1.2f, 3.4f, 5.6f, 7.8f, 9.10f, 11.12f);
+		assertEpsilonEquals(1.2f, this.t.getM00());
+		assertEpsilonEquals(3.4f, this.t.getM01());
+		assertEpsilonEquals(5.6f, this.t.getM02());
+		assertEpsilonEquals(7.8f, this.t.getM10());
+		assertEpsilonEquals(9.10f, this.t.getM11());
+		assertEpsilonEquals(11.12f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testCreateInverse() {
+		Transform2D ti = this.t.createInverse();
+		assertEpsilonEquals(-1.666666666666667f, ti.getM00());
+		assertEpsilonEquals(0.666666666666667f, ti.getM01());
+		assertEpsilonEquals(1f, ti.getM02());
+		assertEpsilonEquals(1.333333333333333f, ti.getM10());
+		assertEpsilonEquals(-0.333333333333333f, ti.getM11());
+		assertEpsilonEquals(-2f, ti.getM12());
+		assertEpsilonEquals(0f, ti.getM20());
+		assertEpsilonEquals(0f, ti.getM21());
+		assertEpsilonEquals(1f, ti.getM22());
+	}
+
+	/**
+	 */
+	public void testInvert() {
+		this.t.invert();
+		assertEpsilonEquals(-1.666666666666667f, this.t.getM00());
+		assertEpsilonEquals(0.666666666666667f, this.t.getM01());
+		assertEpsilonEquals(1f, this.t.getM02());
+		assertEpsilonEquals(1.333333333333333f, this.t.getM10());
+		assertEpsilonEquals(-0.333333333333333f, this.t.getM11());
+		assertEpsilonEquals(-2f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/AbstractRectangularShape2fTestCase.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/AbstractRectangularShape2fTestCase.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/AbstractRectangularShape2fTestCase.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,178 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import org.arakhne.afc.math.continous.object2d.AbstractRectangularShape2f;
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+
+
+/**
+ * @param <T> is the type of the shape to test.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractRectangularShape2fTestCase<T extends AbstractRectangularShape2f<?>> extends AbstractShape2fTestCase<T> {
+	
+	/**
+	 */
+	public void testSetFloatFloatFloatFloat() {
+		this.r.set(2.3f, 3.4f, 4.5f, 5.6f);
+		assertEpsilonEquals(2.3f, this.r.getMinX());
+		assertEpsilonEquals(3.4f, this.r.getMinY());
+		assertEpsilonEquals(4.5f, this.r.getWidth());
+		assertEpsilonEquals(5.6f, this.r.getHeight());
+	}
+	
+	/**
+	 */
+	public void testSetPoint2DPoint2D() {
+		this.r.set(new Point2f(2.3f, 3.4f), new Point2f(4.5f, 5.6f));
+		assertEpsilonEquals(2.3f, this.r.getMinX());
+		assertEpsilonEquals(3.4f, this.r.getMinY());
+		assertEpsilonEquals(4.5f, this.r.getMaxX());
+		assertEpsilonEquals(5.6f, this.r.getMaxY());
+	}
+	
+	/**
+	 */
+	public void testSetRectangularShape2f() {
+		Rectangle2f rr = new Rectangle2f(2.3f, 3.4f, 4.5f, 5.6f);
+		this.r.set(rr);
+		assertEpsilonEquals(2.3f, this.r.getMinX());
+		assertEpsilonEquals(3.4f, this.r.getMinY());
+		assertEpsilonEquals(4.5f, this.r.getWidth());
+		assertEpsilonEquals(5.6f, this.r.getHeight());
+	}
+
+	/**
+	 */
+	public void testSetWidth() {
+		this.r.setWidth(2.3f);
+		assertEpsilonEquals(0f, this.r.getMinX());
+		assertEpsilonEquals(0f, this.r.getMinY());
+		assertEpsilonEquals(2.3f, this.r.getMaxX());
+		assertEpsilonEquals(1f, this.r.getMaxY());
+		assertEpsilonEquals(2.3f, this.r.getWidth());
+	}
+
+	/**
+	 */
+	public void testSetHeight() {
+		this.r.setHeight(2.3f);
+		assertEpsilonEquals(0f, this.r.getMinX());
+		assertEpsilonEquals(0f, this.r.getMinY());
+		assertEpsilonEquals(1f, this.r.getMaxX());
+		assertEpsilonEquals(2.3f, this.r.getMaxY());
+		assertEpsilonEquals(2.3f, this.r.getHeight());
+	}
+
+	/**
+	 */
+	public void testSetFromCornersPoint2DPoint2D() {
+		this.r.setFromCorners(new Point2f(2.3f, 3.4f), new Point2f(4.5f, 5.6f));
+		assertEpsilonEquals(2.3f, this.r.getMinX());
+		assertEpsilonEquals(3.4f, this.r.getMinY());
+		assertEpsilonEquals(4.5f, this.r.getMaxX());
+		assertEpsilonEquals(5.6f, this.r.getMaxY());
+	}
+
+	/**
+	 */
+	public void testSetFromCornersFloatFloatFloatFloat() {
+		this.r.setFromCorners(2.3f, 3.4f, 4.5f, 5.6f);
+		assertEpsilonEquals(2.3f, this.r.getMinX());
+		assertEpsilonEquals(3.4f, this.r.getMinY());
+		assertEpsilonEquals(4.5f, this.r.getMaxX());
+		assertEpsilonEquals(5.6f, this.r.getMaxY());
+	}
+	
+	/**
+	 */
+	public void testSetFromCenterFloatFloatFloatFloat() {
+		this.r.setFromCenter(2.3f, 3.4f, 4.5f, 5.6f);
+		//w = 4.5-2.3 = 2.2
+		//h = 5.6-3.4 = 2.2
+		assertEpsilonEquals(2.3f-2.2f, this.r.getMinX());
+		assertEpsilonEquals(3.4f-2.2f, this.r.getMinY());
+		assertEpsilonEquals(2.3f+2.2f, this.r.getMaxX());
+		assertEpsilonEquals(3.4f+2.2f, this.r.getMaxY());
+	}
+	
+	/**
+	 */
+	public void testSetMinX() {
+		this.r.setMinX(2.3f);
+		assertEpsilonEquals(1f, this.r.getMinX());
+		assertEpsilonEquals(2.3f, this.r.getMaxX());
+		this.r.setMinX(-3.4f);
+		assertEpsilonEquals(-3.4f, this.r.getMinX());
+		assertEpsilonEquals(2.3f, this.r.getMaxX());
+	}
+
+	/**
+	 */
+	public void testSetMaxX() {
+		this.r.setMaxX(2.3f);
+		assertEpsilonEquals(0f, this.r.getMinX());
+		assertEpsilonEquals(2.3f, this.r.getMaxX());
+		this.r.setMaxX(-3.4f);
+		assertEpsilonEquals(-3.4f, this.r.getMinX());
+		assertEpsilonEquals(0f, this.r.getMaxX());
+	}
+
+	/**
+	 */
+	public void testSetMinY() {
+		this.r.setMinY(2.3f);
+		assertEpsilonEquals(1f, this.r.getMinY());
+		assertEpsilonEquals(2.3f, this.r.getMaxY());
+		this.r.setMinY(-3.4f);
+		assertEpsilonEquals(-3.4f, this.r.getMinY());
+		assertEpsilonEquals(2.3f, this.r.getMaxY());
+	}
+
+	/**
+	 */
+	public void testSetMaxY() {
+		this.r.setMaxY(2.3f);
+		assertEpsilonEquals(0f, this.r.getMinY());
+		assertEpsilonEquals(2.3f, this.r.getMaxY());
+		this.r.setMaxY(-3.4f);
+		assertEpsilonEquals(-3.4f, this.r.getMinY());
+		assertEpsilonEquals(0f, this.r.getMaxY());
+	}
+	
+	/**
+	 */
+	@Override
+	public void testTranslateFloatFloat() {
+		this.r.translate(2.3f, 3.4f);
+		assertEpsilonEquals(2.3f, this.r.getMinX());
+		assertEpsilonEquals(3.4f, this.r.getMinY());
+		assertEpsilonEquals(3.3f, this.r.getMaxX());
+		assertEpsilonEquals(4.4f, this.r.getMaxY());
+	}
+	
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/AbstractShape2fTestCase.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/AbstractShape2fTestCase.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/AbstractShape2fTestCase.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,152 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import java.util.Arrays;
+
+import junit.framework.AssertionFailedError;
+
+import org.arakhne.afc.math.AbstractMathTestCase;
+import org.arakhne.afc.math.continous.object2d.PathElement2f;
+import org.arakhne.afc.math.continous.object2d.PathIterator2f;
+import org.arakhne.afc.math.continous.object2d.Shape2f;
+import org.arakhne.afc.math.generic.PathElementType;
+
+/**
+ * @param <T> is the type of the shape to test
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractShape2fTestCase<T extends Shape2f> extends AbstractMathTestCase {
+	
+	/** Is the rectangular shape to test.
+	 */
+	protected T r;
+	
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		this.r = createShape();
+	}
+	
+	/** Create the shape to test.
+	 * 
+	 * @return the shape to test.
+	 */
+	protected abstract T createShape();
+	
+	@Override
+	protected void tearDown() throws Exception {
+		this.r = null;
+		super.tearDown();
+	}
+	
+	/** Assert is the given path iterator has a first element with the
+	 * given information.
+	 * 
+	 * @param pi
+	 * @param type
+	 * @param coords
+	 */
+	public void assertElement(PathIterator2f pi, PathElementType type, float... coords) {
+		if (!pi.hasNext()) {
+			fail("expected path element but the iterator is empty"); //$NON-NLS-1$
+		}
+		PathElement2f pe = pi.next();
+		if (!type.equals(pe.type)) {
+			fail("expected: "+type+"; actual: "+pe.type);  //$NON-NLS-1$//$NON-NLS-2$
+		}
+		float[] c = pe.toArray();
+		if (!isEpsilonEquals(c, coords)) {
+			throw new AssertionFailedError("expected: "+Arrays.toString(coords)+"; actual: "+Arrays.toString(c));  //$NON-NLS-1$//$NON-NLS-2$
+		}
+	}
+	
+	/**
+	 * Replies if two arrays have the same values at epsilon.
+	 * 
+	 * @param a
+	 * @param b
+	 * @return <code>true</code> if the two arrays are equal, otherwise
+	 * <code>false</code>.
+	 */
+	public boolean isEpsilonEquals(float[] a, float[] b) {
+		if (a==b) return true;
+		if (a==null && b!=null) return false;
+		if (a!=null && b==null) return false;
+		assert(a!=null && b!=null);
+		if (a.length!=b.length) return false;
+		for(int i=0; i<a.length; ++i) {
+			if (!isEpsilonEquals(a[i], b[i])) return false;
+		}
+		return true;
+	}
+	
+	/** Assert is the given path iterator has no element.
+	 * 
+	 * @param pi
+	 */
+	public static void assertNoElement(PathIterator2f pi) {
+		if (pi.hasNext()) {
+			fail("expected no path element but the iterator is not empty"); //$NON-NLS-1$
+		}
+	}
+	
+	/**
+	 */
+	public abstract void testDistancePoint2D();
+
+	/**
+	 */
+	public abstract void testDistanceSquaredPoint2D();
+
+	/**
+	 */
+	public abstract void testDistanceL1Point2D();
+
+	/**
+	 */
+	public abstract void testDistanceLinfPoint2D();
+	
+	/**
+	 */
+	public abstract void testTranslateFloatFloat();
+	
+	/**
+	 */
+	public abstract void testToBoundingBox();
+
+	/**
+	 */
+	public abstract void testIsEmpty();
+
+	/**
+	 */
+	public abstract void testClone();
+
+	/**
+	 */
+	public abstract void testClear();
+
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Circle2fTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Circle2fTest.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Circle2fTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,460 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.continous.object2d.Circle2f;
+import org.arakhne.afc.math.continous.object2d.PathIterator2f;
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.math.generic.PathElementType;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+/**
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Circle2fTest extends AbstractShape2fTestCase<Circle2f> {
+	
+	@Override
+	protected Circle2f createShape() {
+		return new Circle2f(0f, 0f, 1f);
+	}
+	
+	@Override
+	public void testIsEmpty() {
+		assertFalse(this.r.isEmpty());
+		this.r.setRadius(1234.56f);
+		assertFalse(this.r.isEmpty());
+		this.r.setRadius(-1234.56f);
+		assertFalse(this.r.isEmpty());
+		this.r.setRadius(0f);
+		assertTrue(this.r.isEmpty());
+		this.r.setRadius(-0f);
+		assertTrue(this.r.isEmpty());
+		this.r.setRadius(+0f);
+		assertTrue(this.r.isEmpty());
+		this.r.setRadius(789.1011f);
+		assertFalse(this.r.isEmpty());
+	}
+	
+	@Override
+	public void testClear() {
+		this.r.clear();
+		assertEquals(0f, this.r.getX());
+		assertEquals(0f, this.r.getY());
+		assertEquals(0f, this.r.getRadius());
+	}
+	
+	/**
+	 */
+	public static void testContainsCircleRectangle() {
+		assertTrue(Circle2f.containsCircleRectangle(0f, 0f, 1f, 0f, 0f, .5f, .5f));
+		assertFalse(Circle2f.containsCircleRectangle(0f, 0f, 1f, 0f, 0f, 1f, 1f));
+		assertFalse(Circle2f.containsCircleRectangle(0f, 0f, 1f, 0f, 0f, .5f, 1f));
+	}
+
+	/**
+	 */
+	public static void testIntersectsCircleCircle() {
+		assertFalse(Circle2f.intersectsCircleCircle(
+				0f, 0f, 1f,
+				10f, 10f, 1f));
+		assertTrue(Circle2f.intersectsCircleCircle(
+				0f, 0f, 1f,
+				0f, 0f, 1f));
+		assertTrue(Circle2f.intersectsCircleCircle(
+				0f, 0f, 1f,
+				0f, .5f, 1f));
+		assertTrue(Circle2f.intersectsCircleCircle(
+				0f, 0f, 1f,
+				.5f, 0f, 1f));
+		assertTrue(Circle2f.intersectsCircleCircle(
+				0f, 0f, 1f,
+				.5f, .5f, 1f));
+		assertFalse(Circle2f.intersectsCircleCircle(
+				0f, 0f, 1f,
+				2f, 0f, 1f));
+	}
+
+	/**
+	 */
+	public static void testIntersectsCircleRectangle() {
+		assertFalse(Circle2f.intersectsCircleRectangle(
+				0f, 0f, 1f,
+				-5f, -5f, -4f, -4f));
+		assertTrue(Circle2f.intersectsCircleRectangle(
+				0f, 0f, 1f,
+				-5f, -5f, 5f, 5f));
+		assertTrue(Circle2f.intersectsCircleRectangle(
+				0f, 0f, 1f,
+				-5f, -5f, .5f, .5f));
+		assertFalse(Circle2f.intersectsCircleRectangle(
+				0f, 0f, 1f,
+				-5f, -5f, .5f, -4f));
+		assertFalse(Circle2f.intersectsCircleRectangle(
+				0f, 0f, 1f,
+				20f, .5f, 21f, 1.5f));
+	}
+
+	/**
+	 */
+	public static void testIntersectsCircleLine() {
+		assertTrue(Circle2f.intersectsCircleLine(
+				0f, 0f, 1f,
+				-5f, -5f, -4f, -4f));
+		assertTrue(Circle2f.intersectsCircleLine(
+				0f, 0f, 1f,
+				-5f, -5f, 5f, 5f));
+		assertTrue(Circle2f.intersectsCircleLine(
+				0f, 0f, 1f,
+				-5f, -5f, .5f, .5f));
+		assertFalse(Circle2f.intersectsCircleLine(
+				0f, 0f, 1f,
+				-5f, -5f, .5f, -4f));
+		assertFalse(Circle2f.intersectsCircleLine(
+				0f, 0f, 1f,
+				20f, .5f, 21f, 1.5f));
+	}
+
+	/**
+	 */
+	public static void testIntersectsCircleSegment() {
+		assertFalse(Circle2f.intersectsCircleSegment(
+				0f, 0f, 1f,
+				-5f, -5f, -4f, -4f));
+		assertTrue(Circle2f.intersectsCircleSegment(
+				0f, 0f, 1f,
+				-5f, -5f, 5f, 5f));
+		assertTrue(Circle2f.intersectsCircleSegment(
+				0f, 0f, 1f,
+				-5f, -5f, .5f, .5f));
+		assertFalse(Circle2f.intersectsCircleSegment(
+				0f, 0f, 1f,
+				-5f, -5f, .5f, -4f));
+		assertFalse(Circle2f.intersectsCircleSegment(
+				0f, 0f, 1f,
+				20f, .5f, 21f, 1.5f));
+		assertTrue(Circle2f.intersectsCircleSegment(
+				1f, 1f, 1f,
+				.5f, -1f, .5f, 4f));
+	}
+
+	@Override
+	public void testDistancePoint2D() {
+		float d;
+		d = this.r.distance(new Point2f(.5f,.5f));
+		assertEpsilonEquals(0f,d);
+
+		d = this.r.distance(new Point2f(-1.2f,-3.4f));
+		//d = sqrt( 1.2*1.2 + 3.4*3.4 ) - 1 = sqrt( 1.44 + 11.56 ) - 1 = sqrt( 13 ) - 1
+		assertEpsilonEquals(2.605551275f,d);
+
+		d = this.r.distance(new Point2f(-1.2f,5.6f));
+		//d = sqrt( 1.2*1.2 + 5.6*5.6 ) - 1 = sqrt( 1.44 + 31.36 ) - 1 = sqrt( 32.8 ) - 1
+		assertEpsilonEquals(4.727128425f,d);
+
+		d = this.r.distance(new Point2f(7.6f,5.6f));
+		//d = sqrt( 7.6*7.6 + 5.6*5.6 ) - 1 = sqrt( 57.76 + 31.36 ) - 1 = sqrt( 89.12 ) - 1
+		assertEpsilonEquals(8.440338977f,d);
+	}
+
+	@Override
+	public void testDistanceSquaredPoint2D() {
+		float sd;
+		//d*d = sd
+		sd = this.r.distanceSquared(new Point2f(.5f,.5f));
+		assertEpsilonEquals(0f,sd);
+
+		sd = this.r.distanceSquared(new Point2f(-1.2f,-3.4f));
+		//d = sqrt( 1.2*1.2 + 3.4*3.4 ) - 1 = sqrt( 1.44 + 11.56 ) - 1 = sqrt( 13 ) - 1
+		assertEpsilonEquals(6.788897449f,sd);
+
+		sd = this.r.distanceSquared(new Point2f(-1.2f,5.6f));
+		//d = sqrt( 1.2*1.2 + 5.6*5.6 ) - 1 = sqrt( 1.44 + 31.36 ) - 1 = sqrt( 32.8 ) - 1
+		assertEpsilonEquals(22.345743149f,sd);
+
+		sd = this.r.distanceSquared(new Point2f(7.6f,5.6f));
+		//d = sqrt( 7.6*7.6 + 5.6*5.6 ) - 1 = sqrt( 57.76 + 31.36 ) - 1 = sqrt( 89.12 ) - 1
+		assertEpsilonEquals(71.239322046f,sd);
+	}
+
+	@Override
+	public void testDistanceL1Point2D() {
+		float d;
+		d = this.r.distanceL1(new Point2f(.5f,.5f));
+		assertEpsilonEquals(0f,d);
+
+		d = this.r.distanceL1(new Point2f(-1.2f,-3.4f));
+		// closest point: (-0.332820118; -0.942990334)
+		// vec(p->closest point) = (0.867179882; 2.457009666)
+		// d = 0.867179882 + 2.457009666 
+		assertEpsilonEquals(3.324189548f,d);
+
+		d = this.r.distanceL1(new Point2f(-1.2f,5.6f));
+		// closest point: (-0.209529089; 0.977802414)
+		// vec(p->closest point) = (0.990470911; -4.622197586)
+		// d = 0.990470911 + 4.62219758
+		assertEpsilonEquals(5.612668491f,d);
+
+		d = this.r.distanceL1(new Point2f(7.6f,5.6f));
+		// length = 9.440338977
+		// closest point: (0.805055837; 0.593199038)
+		// vec(p->closest point) = (-6.794944163; -5.006800962)
+		// d = 6.794944163 + 5.006800962
+		assertEpsilonEquals(11.801745125f,d);
+	}
+
+	@Override
+	public void testDistanceLinfPoint2D() {
+		float d;
+		d = this.r.distanceLinf(new Point2f(.5f,.5f));
+		assertEpsilonEquals(0f,d);
+
+		d = this.r.distanceLinf(new Point2f(-1.2f,-3.4f));
+		// closest point: (-0.332820118; -0.942990334)
+		// vec(p->closest point) = (0.867179882; 2.457009666)
+		// d = max(0.867179882; 2.457009666) 
+		assertEpsilonEquals(2.457009666f,d);
+
+		d = this.r.distanceLinf(new Point2f(-1.2f,5.6f));
+		// closest point: (-0.209529089; 0.977802414)
+		// vec(p->closest point) = (0.990470911; -4.622197586)
+		// d = max(0.990470911; 4.62219758)
+		assertEpsilonEquals(4.62219758f,d);
+
+		d = this.r.distanceLinf(new Point2f(7.6f,5.6f));
+		// length = 9.440338977
+		// closest point: (0.805055837; 0.593199038)
+		// vec(p->closest point) = (-6.794944163; -5.006800962)
+		// d = max(6.794944163; 5.006800962)
+		assertEpsilonEquals(6.794944163f,d);
+	}
+		
+	@Override
+	public void testToBoundingBox() {
+		Rectangle2f b = this.r.toBoundingBox();
+		assertEpsilonEquals(-1f, b.getMinX());
+		assertEpsilonEquals(-1f, b.getMinY());
+		assertEpsilonEquals(1f, b.getMaxX());
+		assertEpsilonEquals(1f, b.getMaxY());
+	}
+	
+	@Override
+	public void testClone() {
+		Circle2f b = this.r.clone();
+
+		assertNotSame(b, this.r);
+		assertEpsilonEquals(this.r.getRadius(), b.getRadius());
+		assertEpsilonEquals(this.r.getCenter(), b.getCenter());
+		
+		b.set(this.r.getX()+1f, this.r.getX()+1f, this.r.getRadius()+1f);
+
+		assertNotEpsilonEquals(this.r.getRadius(), b.getRadius());
+		assertNotEpsilonEquals(this.r.getCenter(), b.getCenter());
+	}
+	
+	
+	/**
+	 */
+	public void testContainsFloatFloat() {
+		assertTrue(this.r.contains(0f, 0f));
+		
+		assertFalse(this.r.contains(-2.3f, -3.4f));
+		assertFalse(this.r.contains(-2.3f, .5f));
+		assertFalse(this.r.contains(-2.3f, 5.6f));
+		
+		assertFalse(this.r.contains(.5f, -3.4f));
+		assertTrue(this.r.contains(.5f, .5f));
+		assertFalse(this.r.contains(.5f, 5.6f));
+
+		assertFalse(this.r.contains(5.6f, -3.4f));
+		assertFalse(this.r.contains(5.6f, .5f));
+		assertFalse(this.r.contains(5.6f, 5.6f));
+	}
+	
+	/**
+	 */
+	public void testContainsPoint2D() {
+		assertTrue(this.r.contains(new Point2f(0f, 0f)));
+		
+		assertFalse(this.r.contains(new Point2f(-2.3f, -3.4f)));
+		assertFalse(this.r.contains(new Point2f(-2.3f, .5f)));
+		assertFalse(this.r.contains(new Point2f(-2.3f, 5.6f)));
+		
+		assertFalse(this.r.contains(new Point2f(.5f, -3.4f)));
+		assertTrue(this.r.contains(new Point2f(.5f, .5f)));
+		assertFalse(this.r.contains(new Point2f(.5f, 5.6f)));
+
+		assertFalse(this.r.contains(new Point2f(5.6f, -3.4f)));
+		assertFalse(this.r.contains(new Point2f(5.6f, .5f)));
+		assertFalse(this.r.contains(new Point2f(5.6f, 5.6f)));
+	}
+
+	/**
+	 */
+	public void testGetClosestPointTo() {
+		Point2D p;
+		
+		p = this.r.getClosestPointTo(new Point2f(0f, 0f));
+		assertEpsilonEquals(0f, p.getX());
+		assertEpsilonEquals(0f, p.getY());
+		
+		p = this.r.getClosestPointTo(new Point2f(.5f, .1f));
+		assertEpsilonEquals(.5f, p.getX());
+		assertEpsilonEquals(.1f, p.getY());
+
+		p = this.r.getClosestPointTo(new Point2f(-1.2f,-3.4f));
+		assertEpsilonEquals(-0.332820118f, p.getX());
+		assertEpsilonEquals(-0.942990334f, p.getY());
+
+		p = this.r.getClosestPointTo(new Point2f(-1.2f,5.6f));
+		assertEpsilonEquals(-0.209529089f, p.getX());
+		assertEpsilonEquals(0.977802414f, p.getY());
+
+		p = this.r.getClosestPointTo(new Point2f(7.6f,5.6f));
+		assertEpsilonEquals(0.805055837f, p.getX());
+		assertEpsilonEquals(0.593199038f, p.getY());
+	}
+	
+	/**
+	 */
+	public void testGetPathIteratorVoid() {
+		float t = .5522848f; // tangent length for a circle
+		PathIterator2f pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 0f);
+		assertElement(pi, PathElementType.CURVE_TO, 1f, t, t, 1f, 0f, 1f);
+		assertElement(pi, PathElementType.CURVE_TO, -t, 1f, -1f, t, -1f, 0f);
+		assertElement(pi, PathElementType.CURVE_TO, -1f, -t, -t, -1f, 0f, -1f);
+		assertElement(pi, PathElementType.CURVE_TO, t, -1f, 1f, -t, 1f, 0f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+	
+	/**
+	 */
+	public void testGetPathIteratorTransform2D() {
+		Transform2D tr;
+		PathIterator2f pi;
+		float t = .5522848f; // tangent length for a circle when the circle is not rotated
+		float h = .707106781f; // hypothenus of 1x1
+		float r = .390524327f;
+		
+		tr = new Transform2D();
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 0f);
+		assertElement(pi, PathElementType.CURVE_TO, 1f, t, t, 1f, 0f, 1f);
+		assertElement(pi, PathElementType.CURVE_TO, -t, 1f, -1f, t, -1f, 0f);
+		assertElement(pi, PathElementType.CURVE_TO, -1f, -t, -t, -1f, 0f, -1f);
+		assertElement(pi, PathElementType.CURVE_TO, t, -1f, 1f, -t, 1f, 0f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		tr = new Transform2D();
+		tr.makeTranslationMatrix(3.4f, 4.5f);
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 4.4f, 4.5f);
+		assertElement(pi, PathElementType.CURVE_TO, 4.4f, 4.5f+t, 3.4f+t, 5.5f, 3.4f, 5.5f);
+		assertElement(pi, PathElementType.CURVE_TO, 3.4f-t, 5.5f, 2.4f, 4.5f+t, 2.4f, 4.5f);
+		assertElement(pi, PathElementType.CURVE_TO, 2.4f, 4.5f-t, 3.4f-t, 3.5f, 3.4f, 3.5f);
+		assertElement(pi, PathElementType.CURVE_TO, 3.4f+t, 3.5f, 4.4f, 4.5f-t, 4.4f, 4.5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		tr = new Transform2D();
+		tr.makeRotationMatrix(MathConstants.QUARTER_PI);
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, h, h);
+		assertElement(pi, PathElementType.CURVE_TO, h-r, h+r, -h+r, h+r, -h, h);
+		assertElement(pi, PathElementType.CURVE_TO, -h-r, h-r, -h-r, -h+r, -h, -h);
+		assertElement(pi, PathElementType.CURVE_TO, -h+r, -h-r, h-r, -h-r, h, -h);
+		assertElement(pi, PathElementType.CURVE_TO, h+r, -h+r, h+r, h-r, h, h);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	@Override
+	public void testTranslateFloatFloat() {
+		this.r.translate(2.3f, 3.4f);
+		assertEpsilonEquals(2.3f, this.r.getX());
+		assertEpsilonEquals(3.4f, this.r.getY());
+		assertEpsilonEquals(1f, this.r.getRadius());
+	}
+
+	/**
+	 */
+	public void testSetFloatFloatFloat() {
+		this.r.set(3.4f, 4.5f, 5.6f);
+		assertEpsilonEquals(3.4f, this.r.getX());
+		assertEpsilonEquals(4.5f, this.r.getY());
+		assertEpsilonEquals(5.6f, this.r.getRadius());
+
+		this.r.set(3.4f, 4.5f, -7.8f);
+		assertEpsilonEquals(3.4f, this.r.getX());
+		assertEpsilonEquals(4.5f, this.r.getY());
+		assertEpsilonEquals(7.8f, this.r.getRadius());
+	}
+
+	/**
+	 */
+	public void testSetPoint2DFloat() {
+		this.r.set(new Point2f(3.4f, 4.5f), 5.6f);
+		assertEpsilonEquals(3.4f, this.r.getX());
+		assertEpsilonEquals(4.5f, this.r.getY());
+		assertEpsilonEquals(5.6f, this.r.getRadius());
+
+		this.r.set(new Point2f(3.4f, 4.5f), -7.8f);
+		assertEpsilonEquals(3.4f, this.r.getX());
+		assertEpsilonEquals(4.5f, this.r.getY());
+		assertEpsilonEquals(7.8f, this.r.getRadius());
+	}
+
+	/**
+	 */
+	public void testSetCenterFloatFloat() {
+		this.r.setCenter(3.4f, 4.5f);
+		assertEpsilonEquals(3.4f, this.r.getX());
+		assertEpsilonEquals(4.5f, this.r.getY());
+	}
+
+	/**
+	 */
+	public void testSetCenterPoint2D() {
+		this.r.setCenter(new Point2f(3.4f, 4.5f));
+		assertEpsilonEquals(3.4f, this.r.getX());
+		assertEpsilonEquals(4.5f, this.r.getY());
+	}
+
+	/**
+	 */
+	public void testSetRadius() {
+		this.r.setRadius(5.6f);
+		assertEpsilonEquals(5.6f, this.r.getRadius());
+		this.r.setRadius(-7.8f);
+		assertEpsilonEquals(7.8f, this.r.getRadius());
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Ellipse2fTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Ellipse2fTest.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Ellipse2fTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,444 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.continous.object2d.Ellipse2f;
+import org.arakhne.afc.math.continous.object2d.PathIterator2f;
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.math.generic.PathElementType;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+/**
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Ellipse2fTest extends AbstractRectangularShape2fTestCase<Ellipse2f> {
+	
+	@Override
+	protected Ellipse2f createShape() {
+		return new Ellipse2f(0f, 0f, 2f, 1f);
+	}
+
+	@Override
+	public void testIsEmpty() {
+		assertFalse(this.r.isEmpty());
+		this.r.setMinX(1f);
+		assertFalse(this.r.isEmpty());
+		this.r.setMinY(1f);
+		assertFalse(this.r.isEmpty());
+		this.r.setMinX(2f);
+		assertTrue(this.r.isEmpty());
+		this.r.setMinX(0f);
+		assertFalse(this.r.isEmpty());
+	}
+
+	@Override
+	public void testClear() {
+		this.r.clear();
+		assertEquals(0f, this.r.getMinX());
+		assertEquals(0f, this.r.getMinY());
+		assertEquals(0f, this.r.getMaxX());
+		assertEquals(0f, this.r.getMaxY());
+	}
+
+	@Override
+	public void testClone() {
+		Ellipse2f b = this.r.clone();
+
+		assertNotSame(b, this.r);
+		assertEpsilonEquals(this.r.getMinX(), b.getMinX());
+		assertEpsilonEquals(this.r.getMinY(), b.getMinY());
+		assertEpsilonEquals(this.r.getMaxX(), b.getMaxX());
+		assertEpsilonEquals(this.r.getMaxY(), b.getMaxY());
+		
+		b.set(this.r.getMinX()+1f, this.r.getMinY()+1f,
+				this.r.getWidth()+1f, this.r.getHeight()+1f);
+
+		assertNotEpsilonEquals(this.r.getMinX(), b.getMinX());
+		assertNotEpsilonEquals(this.r.getMinY(), b.getMinY());
+		assertNotEpsilonEquals(this.r.getMaxX(), b.getMaxX());
+		assertNotEpsilonEquals(this.r.getMaxY(), b.getMaxY());
+	}
+
+	@Override
+	public void testSetMinX() {
+		this.r.setMinX(2.3f);
+		assertEpsilonEquals(2f, this.r.getMinX());
+		assertEpsilonEquals(2.3f, this.r.getMaxX());
+		this.r.setMinX(-3.4f);
+		assertEpsilonEquals(-3.4f, this.r.getMinX());
+		assertEpsilonEquals(2.3f, this.r.getMaxX());
+	}
+
+	@Override
+	public void testTranslateFloatFloat() {
+		this.r.translate(2.3f, 3.4f);
+		assertEpsilonEquals(2.3f, this.r.getMinX());
+		assertEpsilonEquals(3.4f, this.r.getMinY());
+		assertEpsilonEquals(4.3f, this.r.getMaxX());
+		assertEpsilonEquals(4.4f, this.r.getMaxY());
+	}
+	
+	@Override
+	public void testSetHeight() {
+		this.r.setHeight(2.3f);
+		assertEpsilonEquals(0f, this.r.getMinX());
+		assertEpsilonEquals(0f, this.r.getMinY());
+		assertEpsilonEquals(2f, this.r.getMaxX());
+		assertEpsilonEquals(2.3f, this.r.getMaxY());
+		assertEpsilonEquals(2.3f, this.r.getHeight());
+	}
+
+	@Override
+	public void testDistancePoint2D() {
+		float d;
+		d = this.r.distance(new Point2f(.5f,.5f));
+		assertEpsilonEquals(0f,d);
+
+		d = this.r.distance(new Point2f(0f,0f));
+		// ecenter = (1; 0.5)
+		// a = 1
+		// b = .5
+		// a*a = 1
+		// b*b = .25
+		// x0 = x - a = 0 - 1 = -1
+		// y0 = y - b = 0 - 0.5 = -0.5
+		// denom*denom = a*a*y0*y0 + b*b*x0*x0 = 0.5
+		// denom = 0.707106781
+		// f = (a*b)/denom = 0.707106781
+		// x = f * x0 = -0.707106781
+		// y = f * y0 = -0.353553391
+		// px = x + a = -0.707106781 + 1 = 0.292893219
+		// py = y + b = -0.353553391 + 0.5 = 0.146446609
+		// Closest point is (0.292893219;0.146446609)
+		assertEpsilonEquals(0.327464574f,d);
+	}
+
+	@Override
+	public void testDistanceSquaredPoint2D() {
+		float d;
+		d = this.r.distanceSquared(new Point2f(.5f,.5f));
+		assertEpsilonEquals(0f,d);
+
+		d = this.r.distanceSquared(new Point2f(0f,0f));
+		// See testDistancePoint2D for details
+		// Closest point is (0.292893219;0.146446609)
+		assertEpsilonEquals(0.107233047f,d);
+	}
+
+	@Override
+	public void testDistanceL1Point2D() {
+		float d;
+		d = this.r.distanceL1(new Point2f(.5f,.5f));
+		assertEpsilonEquals(0f,d);
+
+		d = this.r.distanceL1(new Point2f(0f,0f));
+		// See testDistancePoint2D for details
+		// Closest point is (0.292893219;0.146446609)
+		assertEpsilonEquals(0.439339828f,d);
+	}
+
+	@Override
+	public void testDistanceLinfPoint2D() {
+		float d;
+		d = this.r.distanceLinf(new Point2f(.5f,.5f));
+		assertEpsilonEquals(0f,d);
+
+		d = this.r.distanceLinf(new Point2f(0f,0f));
+		// See testDistancePoint2D for details
+		// Closest point is (0.292893219;0.146446609)
+		assertEpsilonEquals(0.292893219f,d);
+	}
+		
+	@Override
+	public void testToBoundingBox() {
+		Rectangle2f b = this.r.toBoundingBox();
+		assertEpsilonEquals(0f, b.getMinX());
+		assertEpsilonEquals(0f, b.getMinY());
+		assertEpsilonEquals(2f, b.getMaxX());
+		assertEpsilonEquals(1f, b.getMaxY());
+	}
+	
+	/**
+	 */
+	public void testContainsFloatFloat() {
+		assertFalse(this.r.contains(0f, 0f));
+		assertTrue(this.r.contains(.5f, .5f));
+		
+		assertFalse(this.r.contains(-2.3f, -3.4f));
+		assertFalse(this.r.contains(-2.3f, .5f));
+		assertFalse(this.r.contains(-2.3f, 5.6f));
+		
+		assertFalse(this.r.contains(.5f, -3.4f));
+		assertTrue(this.r.contains(.5f, .5f));
+		assertFalse(this.r.contains(.5f, 5.6f));
+
+		assertFalse(this.r.contains(5.6f, -3.4f));
+		assertFalse(this.r.contains(5.6f, .5f));
+		assertFalse(this.r.contains(5.6f, 5.6f));
+	}
+	
+	/**
+	 */
+	public void testContainsPoint2D() {
+		assertFalse(this.r.contains(new Point2f(0f, 0f)));
+		assertTrue(this.r.contains(new Point2f(.5f, .5f)));
+		
+		assertFalse(this.r.contains(new Point2f(-2.3f, -3.4f)));
+		assertFalse(this.r.contains(new Point2f(-2.3f, .5f)));
+		assertFalse(this.r.contains(new Point2f(-2.3f, 5.6f)));
+		
+		assertFalse(this.r.contains(new Point2f(.5f, -3.4f)));
+		assertTrue(this.r.contains(new Point2f(.5f, .5f)));
+		assertFalse(this.r.contains(new Point2f(.5f, 5.6f)));
+
+		assertFalse(this.r.contains(new Point2f(5.6f, -3.4f)));
+		assertFalse(this.r.contains(new Point2f(5.6f, .5f)));
+		assertFalse(this.r.contains(new Point2f(5.6f, 5.6f)));
+	}
+
+	/**
+	 */
+	public void testGetClosestPointTo() {
+		Point2D p;
+		
+		p = this.r.getClosestPointTo(new Point2f(.5f, .4f));
+		assertEpsilonEquals(.5f, p.getX());
+		assertEpsilonEquals(.4f, p.getY());
+		
+		p = this.r.getClosestPointTo(new Point2f(-2.3f, -3.4f));
+		// ecenter = (1; 0.5)
+		// a = 1
+		// b = .5
+		// a*a = 1
+		// b*b = .25
+		// x0 = x - a = -2.3 - 1 = -3.3
+		// y0 = y - b = -3.4 - 0.5 = -3.9
+		// denom*denom = a*a*y0*y0 + b*b*x0*x0 = 17.9325
+		// denom = 4.234678264
+		// f = (a*b)/denom = 0.118072724
+		// x = f * x0 = -0.389639991
+		// y = f * y0 = -0.460483626
+		// px = x + a = -0.389639991 + 1 = 0.610360009
+		// py = y + b = -0.460483626 + 0.5 = 0.039516374
+		assertEpsilonEquals(0.610360009f, p.getX());
+		assertEpsilonEquals(0.039516374f, p.getY());
+		
+		p = this.r.getClosestPointTo(new Point2f(1f, 5.6f));
+		assertEpsilonEquals(1f, p.getX());
+		assertEpsilonEquals(1f, p.getY());
+	}
+	
+	/**
+	 */
+	public void testGetPathIteratorVoid() {
+		float vt = .27614236f; // vertical tangent length for the ellipse
+		float ht = .5522847f; // horizontal tangent length for the ellipse
+		PathIterator2f pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 2f, .5f);
+		assertElement(pi, PathElementType.CURVE_TO, 2f, .5f+vt, 1f+ht, 1f, 1f, 1f);
+		assertElement(pi, PathElementType.CURVE_TO, 1f-ht, 1f, 0f, .5f+vt, 0f, .5f);
+		assertElement(pi, PathElementType.CURVE_TO, 0f, .5f-vt, 1f-ht, 0f, 1f, 0f);
+		assertElement(pi, PathElementType.CURVE_TO, 1f+ht, 0f, 2f, .5f-vt, 2f, .5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+	
+	/**
+	 */
+	public void testGetPathIteratorTransform2D() {
+		Transform2D tr;
+		PathIterator2f pi;
+		float vt = .27614236f; // vertical tangent length for the ellipse
+		float ht = .5522847f; // horizontal tangent length for the ellipse
+
+		tr = new Transform2D();
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 2f, .5f);
+		assertElement(pi, PathElementType.CURVE_TO, 2f, .5f+vt, 1f+ht, 1f, 1f, 1f);
+		assertElement(pi, PathElementType.CURVE_TO, 1f-ht, 1f, 0f, .5f+vt, 0f, .5f);
+		assertElement(pi, PathElementType.CURVE_TO, 0f, .5f-vt, 1f-ht, 0f, 1f, 0f);
+		assertElement(pi, PathElementType.CURVE_TO, 1f+ht, 0f, 2f, .5f-vt, 2f, .5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		tr = new Transform2D();
+		tr.makeTranslationMatrix(3.4f, 4.5f);
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 5.4f, 5f);
+		assertElement(pi, PathElementType.CURVE_TO, 5.4f, 5f+vt, 4.4f+ht, 5.5f, 4.4f, 5.5f);
+		assertElement(pi, PathElementType.CURVE_TO, 4.4f-ht, 5.5f, 3.4f, 5f+vt, 3.4f, 5f);
+		assertElement(pi, PathElementType.CURVE_TO, 3.4f, 5f-vt, 4.4f-ht, 4.5f, 4.4f, 4.5f);
+		assertElement(pi, PathElementType.CURVE_TO, 4.4f+ht, 4.5f, 5.4f, 5f-vt, 5.4f, 5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		tr = new Transform2D();
+		tr.makeRotationMatrix(MathConstants.QUARTER_PI);
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 1.06066f, 1.76777f);
+		assertElement(pi, PathElementType.CURVE_TO, 0.86540f, 1.96303f, 0.39052f, 1.8047f, 0f, 1.41421f);
+		assertElement(pi, PathElementType.CURVE_TO, -0.39052f, 1.02369f, -0.54882f, 0.54882f, -0.35355f, 0.35355f);
+		assertElement(pi, PathElementType.CURVE_TO, -0.15829f, 0.15829f, 0.31658f, 0.31658f, 0.70711f, 0.70711f);
+		assertElement(pi, PathElementType.CURVE_TO, 1.09763f, 1.09763f, 1.25592f, 1.57250f, 1.06066f, 1.76777f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/**
+	 */
+	public static void testContainsEllipseRectangle() {
+		assertFalse(Ellipse2f.containsEllipseRectangle(0f, 0f, 1f, 1f,
+				0f, 0f, 1f, 1f));
+		assertFalse(Ellipse2f.containsEllipseRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, 1f, 1f));
+		assertFalse(Ellipse2f.containsEllipseRectangle(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, 5f));
+		assertFalse(Ellipse2f.containsEllipseRectangle(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, .6f));
+		assertFalse(Ellipse2f.containsEllipseRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, 5f, 5f));
+		assertFalse(Ellipse2f.containsEllipseRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, -4f, -4f));
+		assertFalse(Ellipse2f.containsEllipseRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, 4f, -4f));
+		assertFalse(Ellipse2f.containsEllipseRectangle(0f, 0f, 1f, 1f,
+				5f, -5f, 6f, 5f));
+		assertFalse(Ellipse2f.containsEllipseRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, .0f, .0f));
+		assertFalse(Ellipse2f.containsEllipseRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, .1f, .1f));
+		assertTrue(Ellipse2f.containsEllipseRectangle(0f, 0f, 1f, 1f,
+				.25f, .25f, .5f, .5f));
+	}
+	
+	/**
+	 */
+	public static void testIntersectsEllipseEllipse() {
+		assertTrue(Ellipse2f.intersectsEllipseEllipse(0f, 0f, 1f, 1f,
+				0f, 0f, 1f, 1f));
+		assertTrue(Ellipse2f.intersectsEllipseEllipse(0f, 0f, 1f, 1f,
+				-5f, -5f, 1f, 1f));
+		assertTrue(Ellipse2f.intersectsEllipseEllipse(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, 5f));
+		assertTrue(Ellipse2f.intersectsEllipseEllipse(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, .6f));
+		assertTrue(Ellipse2f.intersectsEllipseEllipse(0f, 0f, 1f, 1f,
+				-5f, -5f, 5f, 5f));
+		assertFalse(Ellipse2f.intersectsEllipseEllipse(0f, 0f, 1f, 1f,
+				-5f, -5f, -4f, -4f));
+		assertFalse(Ellipse2f.intersectsEllipseEllipse(0f, 0f, 1f, 1f,
+				-5f, -5f, 4f, -4f));
+		assertFalse(Ellipse2f.intersectsEllipseEllipse(0f, 0f, 1f, 1f,
+				5f, -5f, 6f, 5f));
+		assertFalse(Ellipse2f.intersectsEllipseEllipse(0f, 0f, 1f, 1f,
+				-5f, -5f, .0f, .0f));
+		assertFalse(Ellipse2f.intersectsEllipseEllipse(0f, 0f, 1f, 1f,
+				-5f, -5f, .1f, .1f));
+	}
+
+	/**
+	 */
+	public static void testIntersectsEllipseRectangle() {
+		assertTrue(Ellipse2f.intersectsEllipseRectangle(0f, 0f, 1f, 1f,
+				0f, 0f, 1f, 1f));
+		assertTrue(Ellipse2f.intersectsEllipseRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, 1f, 1f));
+		assertTrue(Ellipse2f.intersectsEllipseRectangle(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, 5f));
+		assertTrue(Ellipse2f.intersectsEllipseRectangle(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, .6f));
+		assertTrue(Ellipse2f.intersectsEllipseRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, 5f, 5f));
+		assertFalse(Ellipse2f.intersectsEllipseRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, -4f, -4f));
+		assertFalse(Ellipse2f.intersectsEllipseRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, 4f, -4f));
+		assertFalse(Ellipse2f.intersectsEllipseRectangle(0f, 0f, 1f, 1f,
+				5f, -5f, 6f, 5f));
+		assertFalse(Ellipse2f.intersectsEllipseRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, .0f, .0f));
+		assertFalse(Ellipse2f.intersectsEllipseRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, .1f, .1f));
+	}
+
+	/**
+	 */
+	public static void testIntersectsEllipseLine() {
+		assertTrue(Ellipse2f.intersectsEllipseLine(0f, 0f, 1f, 1f,
+				0f, 0f, 1f, 1f));
+		assertTrue(Ellipse2f.intersectsEllipseLine(0f, 0f, 1f, 1f,
+				-5f, -5f, 1f, 1f));
+		assertTrue(Ellipse2f.intersectsEllipseLine(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, 5f));
+		assertTrue(Ellipse2f.intersectsEllipseLine(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, .6f));
+		assertTrue(Ellipse2f.intersectsEllipseLine(0f, 0f, 1f, 1f,
+				-5f, -5f, 5f, 5f));
+		assertTrue(Ellipse2f.intersectsEllipseLine(0f, 0f, 1f, 1f,
+				-5f, -5f, -4f, -4f));
+		assertFalse(Ellipse2f.intersectsEllipseLine(0f, 0f, 1f, 1f,
+				-5f, -5f, 4f, -4f));
+		assertFalse(Ellipse2f.intersectsEllipseLine(0f, 0f, 1f, 1f,
+				5f, -5f, 6f, 5f));
+		assertTrue(Ellipse2f.intersectsEllipseLine(0f, 0f, 1f, 1f,
+				-5f, -5f, .0f, .0f));
+		assertTrue(Ellipse2f.intersectsEllipseLine(0f, 0f, 1f, 1f,
+				-5f, -5f, .1f, .1f));
+		assertTrue(Ellipse2f.intersectsEllipseLine(0f, 0f, 1f, 1f,
+				-5f, -5f, .4f, .3f));
+	}
+
+	/**
+	 */
+	public static void testIntersectsEllipseSegment() {
+		assertTrue(Ellipse2f.intersectsEllipseSegment(0f, 0f, 1f, 1f,
+				0f, 0f, 1f, 1f));
+		assertTrue(Ellipse2f.intersectsEllipseSegment(0f, 0f, 1f, 1f,
+				-5f, -5f, 1f, 1f));
+		assertTrue(Ellipse2f.intersectsEllipseSegment(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, 5f));
+		assertTrue(Ellipse2f.intersectsEllipseSegment(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, .6f));
+		assertTrue(Ellipse2f.intersectsEllipseSegment(0f, 0f, 1f, 1f,
+				-5f, -5f, 5f, 5f));
+		assertFalse(Ellipse2f.intersectsEllipseSegment(0f, 0f, 1f, 1f,
+				-5f, -5f, -4f, -4f));
+		assertFalse(Ellipse2f.intersectsEllipseSegment(0f, 0f, 1f, 1f,
+				-5f, -5f, 4f, -4f));
+		assertFalse(Ellipse2f.intersectsEllipseSegment(0f, 0f, 1f, 1f,
+				5f, -5f, 6f, 5f));
+		assertFalse(Ellipse2f.intersectsEllipseSegment(0f, 0f, 1f, 1f,
+				-5f, -5f, .0f, .0f));
+		assertFalse(Ellipse2f.intersectsEllipseSegment(0f, 0f, 1f, 1f,
+				-5f, -5f, .1f, .1f));
+		assertTrue(Ellipse2f.intersectsEllipseSegment(0f, 0f, 1f, 1f,
+				-5f, -5f, .4f, .3f));
+	}
+	
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Path2fPointCollectionTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Path2fPointCollectionTest.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Path2fPointCollectionTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,259 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.arakhne.afc.math.AbstractMathTestCase;
+import org.arakhne.afc.math.generic.Point2D;
+
+/**
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Path2fPointCollectionTest extends AbstractMathTestCase {
+	
+	/** Is the rectangular shape to test.
+	 */
+	protected Path2f r;
+	
+	/** Is the collection to test.
+	 */
+	protected Collection<Point2D> c;
+
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		this.r = new Path2f();
+		this.r.moveTo(1f, 1f);
+		this.r.lineTo(2f, 2f);
+		this.r.quadTo(3f, 0f, 4f, 3f);
+		this.r.curveTo(5f, -1f, 6f, 5f, 7f, -5f);
+		this.r.closePath();
+		this.c = this.r.toCollection();
+	}
+	
+	@Override
+	protected void tearDown() throws Exception {
+		this.r = null;
+		this.c = null;
+		super.tearDown();
+	}
+	
+	private void assertCoords(float... coords) {
+		assertEquals(coords.length/2, this.r.size());
+		for(int i=0, j=0; i<this.r.size(); ++i) {
+			Point2D p = this.r.getPointAt(i);
+			assertEpsilonEquals(coords[j++], p.getX());
+			assertEpsilonEquals(coords[j++], p.getY());
+		}
+	}
+	
+    /**
+     */
+    public void testSize() {
+    	assertEquals(7, this.c.size());
+    	this.r.removeLast();
+    	assertEquals(7, this.c.size());
+    	this.r.removeLast();
+    	assertEquals(4, this.c.size());
+    	this.r.clear();
+    	assertEquals(0, this.c.size());
+    }
+
+    /**
+     */
+    public void testIsEmpty() {
+    	assertFalse(this.c.isEmpty());
+    	this.r.removeLast();
+    	assertFalse(this.c.isEmpty());
+    	this.r.removeLast();
+    	assertFalse(this.c.isEmpty());
+    	this.r.clear();
+    	assertTrue(this.c.isEmpty());
+    }
+
+    /**
+     */
+    public void testContainsObject() {
+    	assertFalse(this.c.contains(new Object()));
+    	assertTrue(this.c.contains(new Point2f(2f, 2f)));
+    	assertTrue(this.c.contains(new Point2f(6f, 5f)));
+    	assertFalse(this.c.contains(new Point2f(-1f, 6f)));
+    }
+
+    /**
+     */
+    public void testIterator() {
+    	Point2D p;
+    	Iterator<Point2D> iterator = this.c.iterator();
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertEpsilonEquals(1f, p.getX());
+    	assertEpsilonEquals(1f, p.getY());
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertEpsilonEquals(2f, p.getX());
+    	assertEpsilonEquals(2f, p.getY());
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertEpsilonEquals(3f, p.getX());
+    	assertEpsilonEquals(0f, p.getY());
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertEpsilonEquals(4f, p.getX());
+    	assertEpsilonEquals(3f, p.getY());
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertEpsilonEquals(5f, p.getX());
+    	assertEpsilonEquals(-1f, p.getY());
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertEpsilonEquals(6f, p.getX());
+    	assertEpsilonEquals(5f, p.getY());
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertEpsilonEquals(7f, p.getX());
+    	assertEpsilonEquals(-5f, p.getY());
+    	assertFalse(iterator.hasNext());
+    }
+
+    /**
+     */
+    public void testToArray() {
+    	Object[] tab = this.c.toArray();
+    	assertEquals(7, tab.length);
+    	assertTrue(tab[0] instanceof Point2D);
+    	assertEpsilonEquals(1f, ((Point2D)tab[0]).getX());
+    	assertEpsilonEquals(1f, ((Point2D)tab[0]).getY());
+    	assertTrue(tab[1] instanceof Point2D);
+    	assertEpsilonEquals(2f, ((Point2D)tab[1]).getX());
+    	assertEpsilonEquals(2f, ((Point2D)tab[1]).getY());
+    	assertTrue(tab[2] instanceof Point2D);
+    	assertEpsilonEquals(3f, ((Point2D)tab[2]).getX());
+    	assertEpsilonEquals(0f, ((Point2D)tab[2]).getY());
+    	assertTrue(tab[3] instanceof Point2D);
+    	assertEpsilonEquals(4f, ((Point2D)tab[3]).getX());
+    	assertEpsilonEquals(3f, ((Point2D)tab[3]).getY());
+    	assertTrue(tab[4] instanceof Point2D);
+    	assertEpsilonEquals(5f, ((Point2D)tab[4]).getX());
+    	assertEpsilonEquals(-1f, ((Point2D)tab[4]).getY());
+    	assertTrue(tab[5] instanceof Point2D);
+    	assertEpsilonEquals(6f, ((Point2D)tab[5]).getX());
+    	assertEpsilonEquals(5f, ((Point2D)tab[5]).getY());
+    	assertTrue(tab[6] instanceof Point2D);
+    	assertEpsilonEquals(7f, ((Point2D)tab[6]).getX());
+    	assertEpsilonEquals(-5f, ((Point2D)tab[6]).getY());
+    }
+
+    /**
+     */
+    public void testToArrayArray() {
+    	Point2D[] tab = new Point2D[5];
+    	Point2D[] tab2 = this.c.toArray(tab);
+    	assertSame(tab, tab2);
+    	assertEquals(5, tab.length);
+    	assertEpsilonEquals(1f, tab[0].getX());
+    	assertEpsilonEquals(1f, tab[0].getY());
+    	assertEpsilonEquals(2f, tab[1].getX());
+    	assertEpsilonEquals(2f, tab[1].getY());
+    	assertEpsilonEquals(3f, tab[2].getX());
+    	assertEpsilonEquals(0f, tab[2].getY());
+    	assertEpsilonEquals(4f, tab[3].getX());
+    	assertEpsilonEquals(3f, tab[3].getY());
+    	assertEpsilonEquals(5f, tab[4].getX());
+    	assertEpsilonEquals(-1f, tab[4].getY());
+    }
+
+    /**
+     */
+    public void testAdd() {
+    	assertTrue(this.c.add(new Point2f(123f, 456f)));
+    	assertCoords(1f, 1f, 2f, 2f, 3f, 0f, 4f, 3f, 5f, -1f, 6f, 5f, 7f, -5f, 123f, 456f);
+    	this.r.clear();
+    	assertCoords();
+    	assertTrue(this.c.add(new Point2f(123f, 456f)));
+    	assertCoords(123f, 456f);
+    	assertTrue(this.c.add(new Point2f(789f, 1011f)));
+    	assertCoords(123f, 456f, 789f, 1011f);
+    }
+
+    /**
+     */
+    public void testRemove() {
+    	assertFalse(this.c.remove(new Object()));
+    	assertTrue(this.c.remove(new Point2f(2f, 2f)));
+    	assertCoords(1f, 1f, 3f, 0f, 4f, 3f, 5f, -1f, 6f, 5f, 7f, -5f);
+    	assertTrue(this.c.remove(new Point2f(6f, 5f)));
+    	assertCoords(1f, 1f, 3f, 0f, 4f, 3f);
+    }
+
+
+    /**
+     */
+    public void testContainsAll() {
+    	assertTrue(this.c.containsAll(
+    			Arrays.asList(new Point2f(1f, 1f), new Point2f(6f, 5f))));
+    	assertFalse(this.c.containsAll(
+    			Arrays.asList(new Point2f(1f, 1f), new Point2f(6f, 6f))));
+    }
+
+    /**
+     */
+    public void testAddAll() {
+    	this.c.addAll(
+    			Arrays.asList(new Point2f(123f, 456f), new Point2f(789f, 1011f)));
+    	assertCoords(1f, 1f, 2f, 2f, 3f, 0f, 4f, 3f, 5f, -1f, 6f, 5f, 7f, -5f, 123f, 456f, 789f, 1011f);
+    }
+
+    /**
+     */
+    public void testRemoveAll() {
+    	this.c.removeAll(
+    			Arrays.asList(new Point2f(123f, 456f), new Point2f(2f, 2f)));
+    	assertCoords(1f, 1f, 3f, 0f, 4f, 3f, 5f, -1f, 6f, 5f, 7f, -5f);
+    }
+
+    /**
+     */
+    public void testRetainAll() {
+    	try {
+    		this.c.retainAll(Collections.emptyList());
+    		fail("Expecting an exception"); //$NON-NLS-1$
+    	}
+    	catch(Throwable _) {
+    		// Expecting an exception
+    	}
+    }
+
+    /**
+     */
+    public void testClear() {
+    	this.c.clear();
+    	assertCoords();
+    }
+    
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Path2fTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Path2fTest.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Path2fTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,718 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import org.arakhne.afc.math.generic.PathElementType;
+import org.arakhne.afc.math.generic.PathWindingRule;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+/**
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Path2fTest extends AbstractShape2fTestCase<Path2f> {
+	
+	@Override
+	protected Path2f createShape() {
+		Path2f p = new Path2f();
+		p.moveTo(1f, 1f);
+		p.lineTo(2f, 2f);
+		p.quadTo(3f, 0f, 4f, 3f);
+		p.curveTo(5f, -1f, 6f, 5f, 7f, -5f);
+		p.closePath();
+		return p;
+	}
+	
+	@Override
+	public void testIsEmpty() {
+		assertFalse(this.r.isEmpty());
+
+		this.r.clear();
+		assertTrue(this.r.isEmpty());
+		
+		this.r.moveTo(1f, 2f);
+		assertTrue(this.r.isEmpty());
+		this.r.moveTo(3f, 4f);
+		assertTrue(this.r.isEmpty());
+		this.r.lineTo(5f, 6f);
+		assertFalse(this.r.isEmpty());
+		this.r.closePath();
+		assertFalse(this.r.isEmpty());
+
+		this.r.clear();
+		assertTrue(this.r.isEmpty());
+
+		this.r.moveTo(1f, 2f);
+		assertTrue(this.r.isEmpty());
+		this.r.moveTo(3f, 4f);
+		assertTrue(this.r.isEmpty());
+		this.r.lineTo(3f, 4f);
+		assertTrue(this.r.isEmpty());
+		this.r.closePath();
+		assertTrue(this.r.isEmpty());
+
+		this.r.clear();
+		assertTrue(this.r.isEmpty());
+
+		this.r.moveTo(1f, 2f);
+		assertTrue(this.r.isEmpty());
+		this.r.moveTo(3f, 4f);
+		assertTrue(this.r.isEmpty());
+		this.r.lineTo(3f, 4f);
+		assertTrue(this.r.isEmpty());
+		this.r.lineTo(5f, 6f);
+		assertFalse(this.r.isEmpty());
+	}
+
+	@Override
+	public void testClear() {
+		this.r.clear();
+		assertEquals(0, this.r.size());
+	}
+
+	@Override
+	public void testClone() {
+		Path2f b = this.r.clone();
+
+		assertNotSame(b, this.r);
+		PathElement2f pe1, pe2;
+		PathIterator2f i1 = this.r.getPathIterator();
+		PathIterator2f i2 = b.getPathIterator();
+		while (i1.hasNext()) {
+			assertTrue(i2.hasNext());
+			pe1 = i1.next();
+			pe2 = i2.next();
+			assertEquals(pe1.type, pe2.type);
+			assertEpsilonEquals(pe1.fromX, pe2.fromX);
+			assertEpsilonEquals(pe1.fromY, pe2.fromY);
+			assertEpsilonEquals(pe1.ctrlX1, pe2.ctrlX1);
+			assertEpsilonEquals(pe1.ctrlY1, pe2.ctrlY1);
+			assertEpsilonEquals(pe1.ctrlX2, pe2.ctrlX2);
+			assertEpsilonEquals(pe1.ctrlY2, pe2.ctrlY2);
+			assertEpsilonEquals(pe1.toX, pe2.toX);
+			assertEpsilonEquals(pe1.toY, pe2.toY);
+			
+		}
+		assertFalse(i2.hasNext());
+		
+		b.translate(1f, 1f);
+
+		i1 = this.r.getPathIterator();
+		i2 = b.getPathIterator();
+		while (i1.hasNext()) {
+			assertTrue(i2.hasNext());
+			pe1 = i1.next();
+			pe2 = i2.next();
+			assertEquals(pe1.type, pe2.type);
+			assertNotEpsilonEquals(pe1.fromX, pe2.fromX);
+			assertNotEpsilonEquals(pe1.fromY, pe2.fromY);
+			assertNotEpsilonEquals(pe1.ctrlX1, pe2.ctrlX1);
+			assertNotEpsilonEquals(pe1.ctrlY1, pe2.ctrlY1);
+			assertNotEpsilonEquals(pe1.ctrlX2, pe2.ctrlX2);
+			assertNotEpsilonEquals(pe1.ctrlY2, pe2.ctrlY2);
+			assertNotEpsilonEquals(pe1.toX, pe2.toX);
+			assertNotEpsilonEquals(pe1.toY, pe2.toY);
+			
+		}
+		assertFalse(i2.hasNext());
+	}
+
+	/*private static int toX(float x) {
+		return (int)(x * 100) + 20;
+	}
+	
+	private static int toY(float y) {
+		return (int)(y * 100) + 600;
+	}
+
+	private static int toS(float s) {
+		return (int)(s * 100);
+	}
+
+	public void testFlattening() throws IOException {
+		BufferedImage img = new BufferedImage(800, 1000, BufferedImage.TYPE_INT_ARGB);
+		Graphics2D g = (Graphics2D)img.getGraphics();
+		g.setColor(Color.WHITE);
+		g.fillRect(0, 0, 800, 1000);
+		g.setColor(Color.LIGHT_GRAY);
+		for(float y=-6; y<=4; y+=1) {
+			if (y!=0f) g.drawLine(toX(-10), toY(y), toX(10), toY(y));
+		}
+		for(float x=-1; x<=8; x+=1) {
+			if (x!=0f) g.drawLine(toX(x), toY(-10), toX(x), toY(10));
+		}
+		g.setColor(Color.BLACK);
+		g.drawLine(toX(-10), toY(0), toX(10), toY(0));
+		g.drawLine(toX(0), toY(-10), toX(0), toY(10));
+		
+		g.setColor(Color.ORANGE);
+		Segment2f[] ells = new Segment2f[] {
+				new Segment2f(0f, 0f, 1f, 1f),
+				new Segment2f(4f, 3f, 1f, 1f),
+				new Segment2f(2f, 2f, 1f, 1f),
+				new Segment2f(2f, 1f, 1f, 1f),
+				new Segment2f(3f, 0f, 1f, 1f),
+				new Segment2f(-1f, -1f, 1f, 1f),
+				new Segment2f(4f, -3f, 1f, 1f),
+				new Segment2f(-3f, 4f, 1f, 1f),
+				new Segment2f(6f, -5f, 1f, 1f),
+				new Segment2f(4f, 0f, 1f, 1f),
+				new Segment2f(5f, 0f, 1f, 1f),
+				new Segment2f(.01f, .01f, 1f, 1f),
+		};
+		for(Segment2f ell : ells) {
+			g.drawLine(
+					toX(ell.getX1()), toY(ell.getY1()),
+					toX(ell.getX2()), toY(ell.getY2()));
+		}
+		
+		g.setColor(Color.GREEN);
+		
+		Segment2f[] segs = new Segment2f[] {
+//				new Segment2f(-1f, -5f, 5f, -4f),
+//				new Segment2f(-5f, -1f, -7f, 3f),
+//				new Segment2f(10f, -1f, 11f, -2f),
+//				new Segment2f(1f, 6f, 3f, 5f),
+//				new Segment2f(6f, -1f, 5f, .5f),
+//				new Segment2f(6f, -1f, 5f, 2f),
+//				new Segment2f(6f, .5f, 5f, 2f),
+//				new Segment2f(3f, 0f, .5f, .5f),
+//				new Segment2f(3f, 0f, 0f, 2f),
+//				new Segment2f(.5f, -1f, .5f, 4f),
+//				new Segment2f(3f, 0f, 1f, 3f),
+		};
+		for(Segment2f seg : segs) {
+			g.drawLine(toX(seg.getX1()), toY(seg.getY1()), toX(seg.getX2()), toY(seg.getY2()));
+		}
+		
+		PathIterator2f pi = this.r.getPathIterator(.5f);
+		while (pi.hasNext()) {
+			PathElement2f pe = pi.next();
+			switch(pe.type) {
+			case LINE_TO:
+				g.drawLine(
+						toX(pe.fromX), toY(pe.fromY),
+						toX(pe.toX), toY(pe.toY));
+				break;
+			case QUAD_TO:
+				g.draw( new QuadCurve2D.Float(
+						toX(pe.fromX), toY(pe.fromY),
+						toX(pe.ctrlX1), toY(pe.ctrlY1),
+						toX(pe.toX), toY(pe.toY)));
+				break;
+			case CURVE_TO:
+				g.draw( new CubicCurve2D.Float(
+						toX(pe.fromX), toY(pe.fromY),
+						toX(pe.ctrlX1), toY(pe.ctrlY1),
+						toX(pe.ctrlX2), toY(pe.ctrlY2),
+						toX(pe.toX), toY(pe.toY)));
+				break;
+			case CLOSE:
+				g.drawLine(
+						toX(pe.fromX), toY(pe.fromY),
+						toX(pe.toX), toY(pe.toY));
+				break;
+			default:
+			}
+		}
+
+		g.setColor(Color.RED);
+		pi = this.r.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
+		while (pi.hasNext()) {
+			PathElement2f pe = pi.next();
+			switch(pe.type) {
+			case LINE_TO:
+				g.drawLine(
+						toX(pe.fromX), toY(pe.fromY),
+						toX(pe.toX), toY(pe.toY));
+				break;
+			case CLOSE:
+				g.drawLine(
+						toX(pe.fromX), toY(pe.fromY),
+						toX(pe.toX), toY(pe.toY));
+				break;
+			default:
+			}
+		}
+		g.dispose();
+		ImageIO.write(img, "png", new File("/home/sgalland/mytest.png"));
+	}*/
+
+	/**
+	 */
+	public void testContainsPathIterator2fFloatFloat() {
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 0f, 0f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 4f, 3f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 2f, 2f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), 2f, 2f));
+		assertTrue(Path2f.contains(this.r.getPathIterator(), 2f, 1f));
+		assertTrue(Path2f.contains(this.r.getPathIterator(), 5f, 0f));
+		assertFalse(Path2f.contains(this.r.getPathIterator(), -1f, -1f));
+	}
+
+	/**
+	 */
+	public void testIntersectsPathIterator2fFloatFloatFloatFloat() {
+		assertFalse(Path2f.intersects(this.r.getPathIterator(), 0f, 0f, 1f, 1f));
+		assertFalse(Path2f.intersects(this.r.getPathIterator(), 4f, 3f, 1f, 1f));
+		assertFalse(Path2f.intersects(this.r.getPathIterator(), 2f, 2f, 1f, 1f));
+		assertTrue(Path2f.intersects(this.r.getPathIterator(), 2f, 1f, 1f, 1f));
+		assertTrue(Path2f.intersects(this.r.getPathIterator(), 3f, 0f, 1f, 1f));
+		assertFalse(Path2f.intersects(this.r.getPathIterator(), -1f, -1f, 1f, 1f));
+		assertTrue(Path2f.intersects(this.r.getPathIterator(), 4f, -3f, 1f, 1f));
+		assertFalse(Path2f.intersects(this.r.getPathIterator(), -3f, 4f, 1f, 1f));
+		assertTrue(Path2f.intersects(this.r.getPathIterator(), 6f, -5f, 1f, 1f));
+		assertFalse(Path2f.intersects(this.r.getPathIterator(), 4f, 0f, 1f, 1f));
+		assertFalse(Path2f.intersects(this.r.getPathIterator(), 5f, 0f, 1f, 1f));
+	}
+	
+	/**
+	 */
+	public void testGetClosestPointTo() {
+		Point2D p;
+		
+		p = this.r.getClosestPointTo(new Point2f(0f, 0f));
+		assertEpsilonEquals(1f, p.getX());
+		assertEpsilonEquals(1f, p.getY());
+
+		p = this.r.getClosestPointTo(new Point2f(4f, 0f));
+		assertEpsilonEquals(4f, p.getX());
+		assertEpsilonEquals(0f, p.getY());
+
+		p = this.r.getClosestPointTo(new Point2f(4f, 2f));
+		assertEpsilonEquals(4f, p.getX());
+		assertEpsilonEquals(2f, p.getY());
+	}
+	
+
+	@Override
+	public void testDistancePoint2D() {
+		//
+	}
+
+	@Override
+	public void testDistanceSquaredPoint2D() {
+		//
+	}
+
+	@Override
+	public void testDistanceL1Point2D() {
+		//
+	}
+
+	@Override
+	public void testDistanceLinfPoint2D() {
+		//
+	}
+
+	@Override
+	public void testTranslateFloatFloat() {
+		this.r.translate(3.4f, 4.5f);
+		PathIterator2f pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 4.4f, 5.5f);
+		assertElement(pi, PathElementType.LINE_TO, 5.4f, 6.5f);
+		assertElement(pi, PathElementType.QUAD_TO, 6.4f, 4.5f, 7.4f, 7.5f);
+		assertElement(pi, PathElementType.CURVE_TO, 8.4f, 3.5f, 9.4f, 9.5f, 10.4f, -.5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/**
+	 */
+	public void testSetWindingRule() {
+		assertEquals(PathWindingRule.NON_ZERO, this.r.getWindingRule());
+		for(PathWindingRule rule : PathWindingRule.values()) {
+			this.r.setWindingRule(rule);
+			assertEquals(rule, this.r.getWindingRule());
+		}
+	}
+
+	/**
+	 */
+	public void testAddIterator() {
+		Path2f p2 = new Path2f();
+		p2.moveTo(3.4f, 4.5f);
+		p2.lineTo(5.6f, 6.7f);
+		
+		this.r.add(p2.getPathIterator());
+		
+		PathIterator2f pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 1f);
+		assertElement(pi, PathElementType.LINE_TO, 2f, 2f);
+		assertElement(pi, PathElementType.QUAD_TO, 3f, 0f, 4f, 3f);
+		assertElement(pi, PathElementType.CURVE_TO, 5f, -1f, 6f, 5f, 7f, -5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertElement(pi, PathElementType.MOVE_TO, 3.4f, 4.5f);
+		assertElement(pi, PathElementType.LINE_TO, 5.6f, 6.7f);
+		assertNoElement(pi);
+	}
+
+	/** 
+	 */
+	public void testGetPathIteratorVoid() {
+		PathIterator2f pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 1f);
+		assertElement(pi, PathElementType.LINE_TO, 2f, 2f);
+		assertElement(pi, PathElementType.QUAD_TO, 3f, 0f, 4f, 3f);
+		assertElement(pi, PathElementType.CURVE_TO, 5f, -1f, 6f, 5f, 7f, -5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/** 
+	 */
+	public void testGetPathIteratorTransform2D() {
+		Transform2D tr;
+		PathIterator2f pi;
+		
+		tr = new Transform2D();
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 1f);
+		assertElement(pi, PathElementType.LINE_TO, 2f, 2f);
+		assertElement(pi, PathElementType.QUAD_TO, 3f, 0f, 4f, 3f);
+		assertElement(pi, PathElementType.CURVE_TO, 5f, -1f, 6f, 5f, 7f, -5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		tr = new Transform2D();
+		tr.makeTranslationMatrix(3.4f, 4.5f);
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 4.4f, 5.5f);
+		assertElement(pi, PathElementType.LINE_TO, 5.4f, 6.5f);
+		assertElement(pi, PathElementType.QUAD_TO, 6.4f, 4.5f, 7.4f, 7.5f);
+		assertElement(pi, PathElementType.CURVE_TO, 8.4f, 3.5f, 9.4f, 9.5f, 10.4f, -.5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/** 
+	 */
+	public void testTransformTransform2D() {
+		Transform2D tr = new Transform2D();
+		tr.makeTranslationMatrix(3.4f, 4.5f);
+		Transform2D tr2 = new Transform2D();
+		tr2.makeRotationMatrix(5.6f);
+		
+		Path2f clone = this.r.clone();
+		clone.transform(tr);
+
+		PathIterator2f pi = clone.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 4.4f, 5.5f);
+		assertElement(pi, PathElementType.LINE_TO, 5.4f, 6.5f);
+		assertElement(pi, PathElementType.QUAD_TO, 6.4f, 4.5f, 7.4f, 7.5f);
+		assertElement(pi, PathElementType.CURVE_TO, 8.4f, 3.5f, 9.4f, 9.5f, 10.4f, -.5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		clone = this.r.clone();
+		clone.transform(tr2);
+
+		pi = clone.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1.406832516382571f, 0.144299240637928f);
+		assertElement(pi, PathElementType.LINE_TO, 2.813665032765142f, 0.288598481275856f);
+		assertElement(pi, PathElementType.QUAD_TO, 2.32669763553075f, -1.89379991361696f,
+				4.996063427657964f, -0.198368915958538f);
+		assertElement(pi, PathElementType.CURVE_TO, 3.24656275467893f, -3.93189906787186f,
+				7.8097284604231056f, 0.0902295653173185f,
+				2.27262796021014f, -8.29669585765750f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		Transform2D tr3 = new Transform2D();
+		tr3.mul(tr, tr2);
+		clone = this.r.clone();
+		clone.transform(tr3);
+
+		pi = clone.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 4.80683251638257f, 4.64429924063793f);
+		assertElement(pi, PathElementType.LINE_TO, 6.21366503276514f, 4.78859848127586f);
+		assertElement(pi, PathElementType.QUAD_TO, 5.72669763553075f, 2.60620008638304f,
+				8.39606342765796f, 4.30163108404146f);
+		assertElement(pi, PathElementType.CURVE_TO, 6.646562754678927f, 0.568100932128142f,
+				11.20972846042311f, 4.59022956531732f, 5.67262796021014f, -3.79669585765750f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/** 
+	 */
+	public void testCreateTransformedShape2D() {
+		Transform2D tr = new Transform2D();
+		tr.makeTranslationMatrix(3.4f, 4.5f);
+		Path2f p2 = (Path2f)this.r.createTransformedShape(tr);
+
+		PathIterator2f pi = p2.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 4.4f, 5.5f);
+		assertElement(pi, PathElementType.LINE_TO, 5.4f, 6.5f);
+		assertElement(pi, PathElementType.QUAD_TO, 6.4f, 4.5f, 7.4f, 7.5f);
+		assertElement(pi, PathElementType.CURVE_TO, 8.4f, 3.5f, 9.4f, 9.5f, 10.4f, -.5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/** 
+	 */
+	public void testContainsFloatfloat() {
+		assertFalse(this.r.contains(0f, 0f));
+		assertFalse(this.r.contains(4f, 3f));
+		assertFalse(this.r.contains(2f, 2f));
+		assertFalse(this.r.contains(2f, 2f));
+		assertTrue(this.r.contains(2f, 1f));
+		assertTrue(this.r.contains(5f, 0f));
+		assertFalse(this.r.contains(-1f, -1f));
+	}
+
+	/** 
+	 */
+	public void testContainsRectangle2f() {
+		assertFalse(this.r.contains(new Rectangle2f(0f, 0f, 1f, 1f)));
+		assertFalse(this.r.contains(new Rectangle2f(4f, 3f, 1f, 1f)));
+		assertFalse(this.r.contains(new Rectangle2f(2f, 2f, 1f, 1f)));
+		assertFalse(this.r.contains(new Rectangle2f(2f, 1f, 1f, 1f)));
+		assertTrue(this.r.contains(new Rectangle2f(3f, 0f, 1f, 1f)));
+		assertFalse(this.r.contains(new Rectangle2f(-1f, -1f, 1f, 1f)));
+		assertFalse(this.r.contains(new Rectangle2f(4f, -3f, 1f, 1f)));
+		assertFalse(this.r.contains(new Rectangle2f(-3f, 4f, 1f, 1f)));
+		assertFalse(this.r.contains(new Rectangle2f(6f, -5f, 1f, 1f)));
+		assertFalse(this.r.contains(new Rectangle2f(4f, 0f, 1f, 1f)));
+		assertFalse(this.r.contains(new Rectangle2f(5f, 0f, 1f, 1f)));
+		assertFalse(this.r.contains(new Rectangle2f(.01f, .01f, 1f, 1f)));
+	}
+
+	/** 
+	 */
+	public void testIntersectsRectangle2f() {
+		assertFalse(this.r.intersects(new Rectangle2f(0f, 0f, 1f, 1f)));
+		assertFalse(this.r.intersects(new Rectangle2f(4f, 3f, 1f, 1f)));
+		assertFalse(this.r.intersects(new Rectangle2f(2f, 2f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Rectangle2f(2f, 1f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Rectangle2f(3f, 0f, 1f, 1f)));
+		assertFalse(this.r.intersects(new Rectangle2f(-1f, -1f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Rectangle2f(4f, -3f, 1f, 1f)));
+		assertFalse(this.r.intersects(new Rectangle2f(-3f, 4f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Rectangle2f(6f, -5f, 1f, 1f)));
+		assertFalse(this.r.intersects(new Rectangle2f(4f, 0f, 1f, 1f)));
+		assertFalse(this.r.intersects(new Rectangle2f(5f, 0f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Rectangle2f(.01f, .01f, 1f, 1f)));
+	}
+
+	/** 
+	 */
+	public void testIntersectsEllipse2f() {
+		assertFalse(this.r.intersects(new Ellipse2f(0f, 0f, 1f, 2f)));
+		assertFalse(this.r.intersects(new Ellipse2f(4f, 3f, 1f, 2f)));
+		assertFalse(this.r.intersects(new Ellipse2f(2f, 2f, 1f, 2f)));
+		assertTrue(this.r.intersects(new Ellipse2f(2f, 1f, 1f, 2f)));
+		assertTrue(this.r.intersects(new Ellipse2f(3f, 0f, 1f, 2f)));
+		assertFalse(this.r.intersects(new Ellipse2f(-1f, -1f, 1f, 2f)));
+		assertTrue(this.r.intersects(new Ellipse2f(4f, -3f, 1f, 2f)));
+		assertFalse(this.r.intersects(new Ellipse2f(-3f, 4f, 1f, 2f)));
+		assertTrue(this.r.intersects(new Ellipse2f(6f, -5f, 1f, 2f)));
+		assertTrue(this.r.intersects(new Ellipse2f(4f, 0f, 1f, 2f)));
+		assertFalse(this.r.intersects(new Ellipse2f(6f, 0f, 1f, 2f)));
+	}
+
+	/** 
+	 */
+	public void testIntersectsCircle2f() {
+		assertFalse(this.r.intersects(new Circle2f(0f, 0f, 1f)));
+		assertTrue(this.r.intersects(new Circle2f(4f, 3f, 1f)));
+		assertTrue(this.r.intersects(new Circle2f(2f, 2f, 1f)));
+		assertTrue(this.r.intersects(new Circle2f(2f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Circle2f(3f, 0f, 1f)));
+		assertFalse(this.r.intersects(new Circle2f(-1f, -1f, 1f)));
+		assertTrue(this.r.intersects(new Circle2f(4f, -3f, 1f)));
+		assertFalse(this.r.intersects(new Circle2f(-3f, 4f, 1f)));
+		assertTrue(this.r.intersects(new Circle2f(6f, -5f, 1f)));
+		assertTrue(this.r.intersects(new Circle2f(4f, 0f, 1f)));
+		assertTrue(this.r.intersects(new Circle2f(5f, 0f, 1f)));
+		assertFalse(this.r.intersects(new Circle2f(.01f, .01f, 1f)));
+		assertFalse(this.r.intersects(new Circle2f(6f, 2f, .8f)));
+	}
+
+	/** 
+	 */
+	public void testIntersectsSegment2f() {
+		assertFalse(this.r.intersects(new Segment2f(0f, 0f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Segment2f(4f, 3f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Segment2f(2f, 2f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Segment2f(2f, 1f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Segment2f(3f, 0f, 1f, 1f)));
+		assertFalse(this.r.intersects(new Segment2f(-1f, -1f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Segment2f(4f, -3f, 1f, 1f)));
+		assertFalse(this.r.intersects(new Segment2f(4f, -3f, 1f, 0f)));
+		assertFalse(this.r.intersects(new Segment2f(-3f, 4f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Segment2f(6f, -5f, 1f, 1f)));
+		assertFalse(this.r.intersects(new Segment2f(6f, -5f, 1f, 0f)));
+		assertTrue(this.r.intersects(new Segment2f(4f, 0f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Segment2f(5f, 0f, 1f, 1f)));
+		assertFalse(this.r.intersects(new Segment2f(.01f, .01f, 1f, 1f)));
+	}
+
+	@Override
+	public void testToBoundingBox() {
+		Rectangle2f bb = this.r.toBoundingBox();
+		assertEpsilonEquals(1f, bb.getMinX());
+		assertEpsilonEquals(-5f, bb.getMinY());
+		assertEpsilonEquals(7f, bb.getMaxX());
+		assertEpsilonEquals(5f, bb.getMaxY());
+	}
+	
+	/**
+	 */
+	public void testRemoveLast() {
+		PathIterator2f pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 1f);
+		assertElement(pi, PathElementType.LINE_TO, 2f, 2f);
+		assertElement(pi, PathElementType.QUAD_TO, 3f, 0f, 4f, 3f);
+		assertElement(pi, PathElementType.CURVE_TO, 5f, -1f, 6f, 5f, 7f, -5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+		
+		this.r.removeLast();
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 1f);
+		assertElement(pi, PathElementType.LINE_TO, 2f, 2f);
+		assertElement(pi, PathElementType.QUAD_TO, 3f, 0f, 4f, 3f);
+		assertElement(pi, PathElementType.CURVE_TO, 5f, -1f, 6f, 5f, 7f, -5f);
+		assertNoElement(pi);
+
+		this.r.removeLast();
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 1f);
+		assertElement(pi, PathElementType.LINE_TO, 2f, 2f);
+		assertElement(pi, PathElementType.QUAD_TO, 3f, 0f, 4f, 3f);
+		assertNoElement(pi);
+
+		this.r.removeLast();
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 1f);
+		assertElement(pi, PathElementType.LINE_TO, 2f, 2f);
+		assertNoElement(pi);
+
+		this.r.removeLast();
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 1f);
+		assertNoElement(pi);
+
+		this.r.removeLast();
+
+		pi = this.r.getPathIterator();
+		assertNoElement(pi);
+
+		this.r.removeLast();
+
+		pi = this.r.getPathIterator();
+		assertNoElement(pi);
+	}
+
+	/**
+	 */
+	public void testSetLastPointFloatFloat() {
+		PathIterator2f pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 1f);
+		assertElement(pi, PathElementType.LINE_TO, 2f, 2f);
+		assertElement(pi, PathElementType.QUAD_TO, 3f, 0f, 4f, 3f);
+		assertElement(pi, PathElementType.CURVE_TO, 5f, -1f, 6f, 5f, 7f, -5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+		
+		this.r.setLastPoint(123.456f, 789.1011f);
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 1f);
+		assertElement(pi, PathElementType.LINE_TO, 2f, 2f);
+		assertElement(pi, PathElementType.QUAD_TO, 3f, 0f, 4f, 3f);
+		assertElement(pi, PathElementType.CURVE_TO, 5f, -1f, 6f, 5f, 123.456f, 789.1011f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+	
+	/**
+	 */
+	public void testRemoveFloatFloat() {
+		PathIterator2f pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 1f);
+		assertElement(pi, PathElementType.LINE_TO, 2f, 2f);
+		assertElement(pi, PathElementType.QUAD_TO, 3f, 0f, 4f, 3f);
+		assertElement(pi, PathElementType.CURVE_TO, 5f, -1f, 6f, 5f, 7f, -5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+		
+		this.r.remove(2f, 2f);
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 1f);
+		assertElement(pi, PathElementType.QUAD_TO, 3f, 0f, 4f, 3f);
+		assertElement(pi, PathElementType.CURVE_TO, 5f, -1f, 6f, 5f, 7f, -5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		this.r.remove(4f, 3f);
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 1f);
+		assertElement(pi, PathElementType.CURVE_TO, 5f, -1f, 6f, 5f, 7f, -5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		this.r.remove(6f, 5f);
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 1f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		this.r.remove(6f, 5f);
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 1f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/**
+	 */
+	public void testContainsPointPoint2D() {
+		PathIterator2f pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 1f, 1f);
+		assertElement(pi, PathElementType.LINE_TO, 2f, 2f);
+		assertElement(pi, PathElementType.QUAD_TO, 3f, 0f, 4f, 3f);
+		assertElement(pi, PathElementType.CURVE_TO, 5f, -1f, 6f, 5f, 7f, -5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+		
+		assertTrue(this.r.containsPoint(new Point2f(2f, 2f)));
+		assertFalse(this.r.containsPoint(new Point2f(4f, 4f)));
+		assertTrue(this.r.containsPoint(new Point2f(6f, 5f)));
+		assertFalse(this.r.containsPoint(new Point2f(-1f, 6f)));
+		assertFalse(this.r.containsPoint(new Point2f(1234f, 5678f)));
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Rectangle2fTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Rectangle2fTest.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Rectangle2fTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,582 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.continous.object2d.Circle2f;
+import org.arakhne.afc.math.continous.object2d.Ellipse2f;
+import org.arakhne.afc.math.continous.object2d.PathIterator2f;
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.math.continous.object2d.Segment2f;
+import org.arakhne.afc.math.generic.PathElementType;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+/**
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Rectangle2fTest extends AbstractRectangularShape2fTestCase<Rectangle2f> {
+	
+	@Override
+	protected Rectangle2f createShape() {
+		return new Rectangle2f(0f, 0f, 1f, 1f);
+	}
+
+	@Override
+	public void testIsEmpty() {
+		assertFalse(this.r.isEmpty());
+		this.r.setMinX(1f);
+		assertFalse(this.r.isEmpty());
+		this.r.setMinY(1f);
+		assertTrue(this.r.isEmpty());
+		this.r.setMinX(0f);
+		assertFalse(this.r.isEmpty());
+	}
+
+	@Override
+	public void testClear() {
+		this.r.clear();
+		assertEquals(0f, this.r.getMinX());
+		assertEquals(0f, this.r.getMinY());
+		assertEquals(0f, this.r.getMaxX());
+		assertEquals(0f, this.r.getMaxY());
+	}
+
+	@Override
+	public void testClone() {
+		Rectangle2f b = this.r.clone();
+
+		assertNotSame(b, this.r);
+		assertEpsilonEquals(this.r.getMinX(), b.getMinX());
+		assertEpsilonEquals(this.r.getMinY(), b.getMinY());
+		assertEpsilonEquals(this.r.getMaxX(), b.getMaxX());
+		assertEpsilonEquals(this.r.getMaxY(), b.getMaxY());
+		
+		b.set(this.r.getMinX()+1f, this.r.getMinY()+1f,
+				this.r.getWidth()+1f, this.r.getHeight()+1f);
+
+		assertNotEpsilonEquals(this.r.getMinX(), b.getMinX());
+		assertNotEpsilonEquals(this.r.getMinY(), b.getMinY());
+		assertNotEpsilonEquals(this.r.getMaxX(), b.getMaxX());
+		assertNotEpsilonEquals(this.r.getMaxY(), b.getMaxY());
+	}
+
+	@Override
+	public void testDistancePoint2D() {
+		float d;
+		d = this.r.distance(new Point2f(.5f,.5f));
+		assertEpsilonEquals(0f,d);
+
+		d = this.r.distance(new Point2f(-1.2f,-3.4f));
+		//sqrt( 1.2*1.2 + 3.4*3.4 ) = sqrt( 1.44 + 11.56 ) = sqrt( 13 )
+		assertEpsilonEquals(3.605551275f,d);
+
+		d = this.r.distance(new Point2f(-1.2f,5.6f));
+		//sqrt( 1.2*1.2 + 4.6*4.6 ) = sqrt( 1.44 + 21.16 ) = sqrt( 22.6 )
+		assertEpsilonEquals(4.75394573f,d);
+
+		d = this.r.distance(new Point2f(7.6f,5.6f));
+		//sqrt( 6.6*6 + 4.6*4.6 ) = sqrt( 43.56 + 21.16 ) = sqrt( 64.72 )
+		assertEpsilonEquals(8.044874144f,d);
+	}
+
+	@Override
+	public void testDistanceSquaredPoint2D() {
+		float d;
+		d = this.r.distanceSquared(new Point2f(.5f,.5f));
+		assertEpsilonEquals(0f,d);
+
+		d = this.r.distanceSquared(new Point2f(-1.2f,-3.4f));
+		//sqrt( 1.2*1.2 + 3.4*3.4 ) = sqrt( 1.44 + 11.56 ) = sqrt( 13 )
+		assertEpsilonEquals(13f,d);
+
+		d = this.r.distanceSquared(new Point2f(-1.2f,5.6f));
+		//sqrt( 1.2*1.2 + 4.6*4.6 ) = sqrt( 1.44 + 21.16 ) = sqrt( 22.6 )
+		assertEpsilonEquals(22.6f,d);
+
+		d = this.r.distanceSquared(new Point2f(7.6f,5.6f));
+		//sqrt( 6.6*6 + 4.6*4.6 ) = sqrt( 43.56 + 21.16 ) = sqrt( 64.72 )
+		assertEpsilonEquals(64.72f,d);
+	}
+
+	@Override
+	public void testDistanceL1Point2D() {
+		float d;
+		d = this.r.distanceL1(new Point2f(.5f,.5f));
+		assertEpsilonEquals(0f,d);
+
+		d = this.r.distanceL1(new Point2f(-1.2f,-3.4f));
+		//1.2 + 3.4
+		assertEpsilonEquals(4.6f,d);
+
+		d = this.r.distanceL1(new Point2f(-1.2f,5.6f));
+		//1.2 + 4.6
+		assertEpsilonEquals(5.8f,d);
+
+		d = this.r.distanceL1(new Point2f(7.6f,5.6f));
+		//6.6 + 4.6
+		assertEpsilonEquals(11.2f,d);
+	}
+
+	@Override
+	public void testDistanceLinfPoint2D() {
+		float d;
+		d = this.r.distanceLinf(new Point2f(.5f,.5f));
+		assertEpsilonEquals(0f,d);
+
+		d = this.r.distanceLinf(new Point2f(-1.2f,-3.4f));
+		//max( 1.2, 3.4 )
+		assertEpsilonEquals(3.4f,d);
+
+		d = this.r.distanceLinf(new Point2f(-1.2f,5.6f));
+		//max( 1.2, 4.6 )
+		assertEpsilonEquals(4.6f,d);
+
+		d = this.r.distanceLinf(new Point2f(7.6f,5.6f));
+		//max( 6.6, 4.6 )
+		assertEpsilonEquals(6.6f,d);
+	}
+		
+	/**
+	 */
+	public void testUnion() {
+		// a: 2.3x3.4 - 6.8x9
+		// b: 6.5x5.4 - 10.8x8.6
+		Rectangle2f a = new Rectangle2f(2.3f, 3.4f, 4.5f, 5.6f);
+		Rectangle2f b = new Rectangle2f(6.5f, 5.4f, 4.3f, 3.2f);
+		
+		Rectangle2f.union(this.r, a, b);
+		assertEpsilonEquals(2.3f, this.r.getMinX());
+		assertEpsilonEquals(3.4f, this.r.getMinY());
+		assertEpsilonEquals(10.8f, this.r.getMaxX());
+		assertEpsilonEquals(9f, this.r.getMaxY());
+
+		Rectangle2f.union(this.r, b, a);
+		assertEpsilonEquals(2.3f, this.r.getMinX());
+		assertEpsilonEquals(3.4f, this.r.getMinY());
+		assertEpsilonEquals(10.8f, this.r.getMaxX());
+		assertEpsilonEquals(9f, this.r.getMaxY());
+	}
+
+	/**
+	 */
+	public void testIntersection() {
+		// a: 2.3x3.4 - 6.8x9
+		// b: 6.5x5.4 - 10.8x8.6
+		Rectangle2f a = new Rectangle2f(2.3f, 3.4f, 4.5f, 5.6f);
+		Rectangle2f b = new Rectangle2f(6.5f, 5.4f, 4.3f, 3.2f);
+		
+		Rectangle2f.intersection(this.r, a, b);
+		assertEpsilonEquals(6.5f, this.r.getMinX());
+		assertEpsilonEquals(5.4f, this.r.getMinY());
+		assertEpsilonEquals(6.8f, this.r.getMaxX());
+		assertEpsilonEquals(8.6f, this.r.getMaxY());
+
+		Rectangle2f.intersection(this.r, b, a);
+		assertEpsilonEquals(6.5f, this.r.getMinX());
+		assertEpsilonEquals(5.4f, this.r.getMinY());
+		assertEpsilonEquals(6.8f, this.r.getMaxX());
+		assertEpsilonEquals(8.6f, this.r.getMaxY());
+	}
+
+	/**
+	 */
+	public void testCreateUnion() {
+		Rectangle2f rr;
+		
+		// b: 6.5x5.4 - 10.8x8.6
+		Rectangle2f b = new Rectangle2f(6.5f, 5.4f, 4.3f, 3.2f);
+		
+		rr = this.r.createUnion(b);
+		assertEpsilonEquals(0f, rr.getMinX());
+		assertEpsilonEquals(0f, rr.getMinY());
+		assertEpsilonEquals(10.8f, rr.getMaxX());
+		assertEpsilonEquals(8.6f, rr.getMaxY());
+
+		rr = this.r.createUnion(b);
+		assertEpsilonEquals(0f, rr.getMinX());
+		assertEpsilonEquals(0f, rr.getMinY());
+		assertEpsilonEquals(10.8f, rr.getMaxX());
+		assertEpsilonEquals(8.6f, rr.getMaxY());
+	}
+
+	/**
+	 */
+	public void testCreateIntersection() {
+		Rectangle2f rr;
+		
+		// b: 6.5x5.4 - 10.8x8.6
+		Rectangle2f b = new Rectangle2f(6.5f, 5.4f, 4.3f, 3.2f);
+		
+		rr = this.r.createIntersection(b);
+		assertEpsilonEquals(0f, rr.getMinX());
+		assertEpsilonEquals(0f, rr.getMinY());
+		assertEpsilonEquals(0f, rr.getMaxX());
+		assertEpsilonEquals(0f, rr.getMaxY());
+
+		rr = this.r.createIntersection(b);
+		assertEpsilonEquals(0f, rr.getMinX());
+		assertEpsilonEquals(0f, rr.getMinY());
+		assertEpsilonEquals(0f, rr.getMaxX());
+		assertEpsilonEquals(0f, rr.getMaxY());
+	}
+
+	@Override
+	public void testToBoundingBox() {
+		Rectangle2f b = this.r.toBoundingBox();
+		assertSame(this.r, b);
+	}
+	
+	/**
+	 */
+	public void testContainsFloatFloat() {
+		assertTrue(this.r.contains(0f, 0f));
+		
+		assertFalse(this.r.contains(-2.3f, -3.4f));
+		assertFalse(this.r.contains(-2.3f, .5f));
+		assertFalse(this.r.contains(-2.3f, 5.6f));
+		
+		assertFalse(this.r.contains(.5f, -3.4f));
+		assertTrue(this.r.contains(.5f, .5f));
+		assertFalse(this.r.contains(.5f, 5.6f));
+
+		assertFalse(this.r.contains(5.6f, -3.4f));
+		assertFalse(this.r.contains(5.6f, .5f));
+		assertFalse(this.r.contains(5.6f, 5.6f));
+
+		assertTrue(this.r.contains(.01f, .01f));
+	}
+	
+	/**
+	 */
+	public void testContainsPoint2D() {
+		assertTrue(this.r.contains(new Point2f(0f, 0f)));
+		
+		assertFalse(this.r.contains(new Point2f(-2.3f, -3.4f)));
+		assertFalse(this.r.contains(new Point2f(-2.3f, .5f)));
+		assertFalse(this.r.contains(new Point2f(-2.3f, 5.6f)));
+		
+		assertFalse(this.r.contains(new Point2f(.5f, -3.4f)));
+		assertTrue(this.r.contains(new Point2f(.5f, .5f)));
+		assertFalse(this.r.contains(new Point2f(.5f, 5.6f)));
+
+		assertFalse(this.r.contains(new Point2f(5.6f, -3.4f)));
+		assertFalse(this.r.contains(new Point2f(5.6f, .5f)));
+		assertFalse(this.r.contains(new Point2f(5.6f, 5.6f)));
+
+		assertTrue(this.r.contains(new Point2f(.01f, .01f)));
+	}
+
+	/**
+	 */
+	public void testGetClosestPointTo() {
+		Point2D p;
+		
+		p = this.r.getClosestPointTo(new Point2f(0f, 0f));
+		assertEpsilonEquals(0f, p.getX());
+		assertEpsilonEquals(0f, p.getY());
+		
+		p = this.r.getClosestPointTo(new Point2f(-2.3f, -3.4f));
+		assertEpsilonEquals(0f, p.getX());
+		assertEpsilonEquals(0f, p.getY());
+		p = this.r.getClosestPointTo(new Point2f(-2.3f, .5f));
+		assertEpsilonEquals(0f, p.getX());
+		assertEpsilonEquals(.5f, p.getY());
+		p = this.r.getClosestPointTo(new Point2f(-2.3f, 5.6f));
+		assertEpsilonEquals(0f, p.getX());
+		assertEpsilonEquals(1f, p.getY());
+		
+		p = this.r.getClosestPointTo(new Point2f(.5f, -3.4f));
+		assertEpsilonEquals(.5f, p.getX());
+		assertEpsilonEquals(0f, p.getY());
+		p = this.r.getClosestPointTo(new Point2f(.5f, .5f));
+		assertEpsilonEquals(.5f, p.getX());
+		assertEpsilonEquals(.5f, p.getY());
+		p = this.r.getClosestPointTo(new Point2f(.5f, 5.6f));
+		assertEpsilonEquals(.5f, p.getX());
+		assertEpsilonEquals(1f, p.getY());
+
+		p = this.r.getClosestPointTo(new Point2f(5.6f, -3.4f));
+		assertEpsilonEquals(1f, p.getX());
+		assertEpsilonEquals(0f, p.getY());
+		p = this.r.getClosestPointTo(new Point2f(5.6f, .5f));
+		assertEpsilonEquals(1f, p.getX());
+		assertEpsilonEquals(.5f, p.getY());
+		p = this.r.getClosestPointTo(new Point2f(5.6f, 5.6f));
+		assertEpsilonEquals(1f, p.getX());
+		assertEpsilonEquals(1f, p.getY());
+
+		p = this.r.getClosestPointTo(new Point2f(.01f, .01f));
+		assertEpsilonEquals(.01f, p.getX());
+		assertEpsilonEquals(.01f, p.getY());
+	}
+	
+	/**
+	 */
+	public void testAddPoint2D() {
+		this.r.add(new Point2f(2.3f, 3.4f));
+		assertEpsilonEquals(0f, this.r.getMinX());
+		assertEpsilonEquals(0f, this.r.getMinY());
+		assertEpsilonEquals(2.3f, this.r.getMaxX());
+		assertEpsilonEquals(3.4f, this.r.getMaxY());
+
+		this.r.add(new Point2f(-2.3f, -3.4f));
+		assertEpsilonEquals(-2.3f, this.r.getMinX());
+		assertEpsilonEquals(-3.4f, this.r.getMinY());
+		assertEpsilonEquals(2.3f, this.r.getMaxX());
+		assertEpsilonEquals(3.4f, this.r.getMaxY());
+
+		this.r.add(new Point2f(0f, 0f));
+		assertEpsilonEquals(-2.3f, this.r.getMinX());
+		assertEpsilonEquals(-3.4f, this.r.getMinY());
+		assertEpsilonEquals(2.3f, this.r.getMaxX());
+		assertEpsilonEquals(3.4f, this.r.getMaxY());
+	}
+	
+	/**
+	 */
+	public void testAddFloatFloat() {
+		this.r.add(2.3f, 3.4f);
+		assertEpsilonEquals(0f, this.r.getMinX());
+		assertEpsilonEquals(0f, this.r.getMinY());
+		assertEpsilonEquals(2.3f, this.r.getMaxX());
+		assertEpsilonEquals(3.4f, this.r.getMaxY());
+
+		this.r.add(-2.3f, -3.4f);
+		assertEpsilonEquals(-2.3f, this.r.getMinX());
+		assertEpsilonEquals(-3.4f, this.r.getMinY());
+		assertEpsilonEquals(2.3f, this.r.getMaxX());
+		assertEpsilonEquals(3.4f, this.r.getMaxY());
+
+		this.r.add(0f, 0f);
+		assertEpsilonEquals(-2.3f, this.r.getMinX());
+		assertEpsilonEquals(-3.4f, this.r.getMinY());
+		assertEpsilonEquals(2.3f, this.r.getMaxX());
+		assertEpsilonEquals(3.4f, this.r.getMaxY());
+	}
+	
+	
+	/**
+	 */
+	public void testGetPathIteratorVoid() {
+		PathIterator2f pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0f,0f);
+		assertElement(pi, PathElementType.LINE_TO, 1f,0f);
+		assertElement(pi, PathElementType.LINE_TO, 1f,1f);
+		assertElement(pi, PathElementType.LINE_TO, 0f,1f);
+		assertElement(pi, PathElementType.LINE_TO, 0f,0f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/**
+	 */
+	public void testGetPathIteratorTransform2D() {
+		Transform2D tr;
+		PathIterator2f pi;
+		
+		tr = new Transform2D();
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 0f,0f);
+		assertElement(pi, PathElementType.LINE_TO, 1f,0f);
+		assertElement(pi, PathElementType.LINE_TO, 1f,1f);
+		assertElement(pi, PathElementType.LINE_TO, 0f,1f);
+		assertElement(pi, PathElementType.LINE_TO, 0f,0f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		tr = new Transform2D();
+		tr.makeTranslationMatrix(3.4f, 4.5f);
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 3.4f, 4.5f);
+		assertElement(pi, PathElementType.LINE_TO, 4.4f, 4.5f);
+		assertElement(pi, PathElementType.LINE_TO, 4.4f, 5.5f);
+		assertElement(pi, PathElementType.LINE_TO, 3.4f, 5.5f);
+		assertElement(pi, PathElementType.LINE_TO, 3.4f, 4.5f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		tr = new Transform2D();
+		tr.makeRotationMatrix(MathConstants.QUARTER_PI);
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 0f, 0f);
+		assertElement(pi, PathElementType.LINE_TO, 0.707106781f, 0.707106781f);
+		assertElement(pi, PathElementType.LINE_TO, 0f, 1.414213562f);
+		assertElement(pi, PathElementType.LINE_TO, -0.707106781f, 0.707106781f);
+		assertElement(pi, PathElementType.LINE_TO, 0f, 0f);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/**
+	 */
+	public static void testIntersectsRectangleRectangle() {
+		assertTrue(Rectangle2f.intersectsRectangleRectangle(0f, 0f, 1f, 1f,
+				0f, 0f, 1f, 1f));
+		assertTrue(Rectangle2f.intersectsRectangleRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, 1f, 1f));
+		assertTrue(Rectangle2f.intersectsRectangleRectangle(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, 5f));
+		assertTrue(Rectangle2f.intersectsRectangleRectangle(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, .6f));
+		assertTrue(Rectangle2f.intersectsRectangleRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, 5f, 5f));
+		assertFalse(Rectangle2f.intersectsRectangleRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, -4f, -4f));
+		assertFalse(Rectangle2f.intersectsRectangleRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, 4f, -4f));
+		assertFalse(Rectangle2f.intersectsRectangleRectangle(0f, 0f, 1f, 1f,
+				5f, -5f, 6f, 5f));
+	}
+
+	/**
+	 */
+	public static void testContainsRectangleRectangle() {
+		assertTrue(Rectangle2f.containsRectangleRectangle(0f, 0f, 1f, 1f,
+				0f, 0f, 1f, 1f));
+		assertFalse(Rectangle2f.containsRectangleRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, 1f, 1f));
+		assertFalse(Rectangle2f.containsRectangleRectangle(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, 5f));
+		assertFalse(Rectangle2f.containsRectangleRectangle(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, .6f));
+		assertFalse(Rectangle2f.containsRectangleRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, 5f, 5f));
+		assertFalse(Rectangle2f.containsRectangleRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, -4f, -4f));
+		assertFalse(Rectangle2f.containsRectangleRectangle(0f, 0f, 1f, 1f,
+				-5f, -5f, 4f, -4f));
+		assertFalse(Rectangle2f.containsRectangleRectangle(0f, 0f, 1f, 1f,
+				5f, -5f, 6f, 5f));
+		assertTrue(Rectangle2f.containsRectangleRectangle(0f, 0f, 1f, 1f,
+				.25f, .25f, .5f, .5f));
+	}
+
+	/**
+	 */
+	public static void testIntersectsRectangleLine() {
+		assertTrue(Rectangle2f.intersectsRectangleLine(0f, 0f, 1f, 1f,
+				0f, 0f, 1f, 1f));
+		assertTrue(Rectangle2f.intersectsRectangleLine(0f, 0f, 1f, 1f,
+				-5f, -5f, 1f, 1f));
+		assertTrue(Rectangle2f.intersectsRectangleLine(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, 5f));
+		assertTrue(Rectangle2f.intersectsRectangleLine(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, .6f));
+		assertTrue(Rectangle2f.intersectsRectangleLine(0f, 0f, 1f, 1f,
+				-5f, -5f, 5f, 5f));
+		assertTrue(Rectangle2f.intersectsRectangleLine(0f, 0f, 1f, 1f,
+				-5f, -5f, -4f, -4f));
+		assertFalse(Rectangle2f.intersectsRectangleLine(0f, 0f, 1f, 1f,
+				-5f, -5f, 4f, -4f));
+		assertFalse(Rectangle2f.intersectsRectangleLine(0f, 0f, 1f, 1f,
+				5f, -5f, 6f, 5f));
+		assertFalse(Rectangle2f.intersectsRectangleLine(0f, 0f, 1f, 1f,
+				5f, -5f, 5f, 5f));
+	}
+
+	/**
+	 */
+	public static void testIntersectsRectangleSegment() {
+		assertTrue(Rectangle2f.intersectsRectangleSegment(0f, 0f, 1f, 1f,
+				0f, 0f, 1f, 1f));
+		assertTrue(Rectangle2f.intersectsRectangleSegment(0f, 0f, 1f, 1f,
+				-5f, -5f, 1f, 1f));
+		assertTrue(Rectangle2f.intersectsRectangleSegment(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, 5f));
+		assertTrue(Rectangle2f.intersectsRectangleSegment(0f, 0f, 1f, 1f,
+				.5f, .5f, 5f, .6f));
+		assertTrue(Rectangle2f.intersectsRectangleSegment(0f, 0f, 1f, 1f,
+				-5f, -5f, 5f, 5f));
+		assertFalse(Rectangle2f.intersectsRectangleSegment(0f, 0f, 1f, 1f,
+				-5f, -5f, -4f, -4f));
+		assertFalse(Rectangle2f.intersectsRectangleSegment(0f, 0f, 1f, 1f,
+				-5f, -5f, 4f, -4f));
+		assertFalse(Rectangle2f.intersectsRectangleSegment(0f, 0f, 1f, 1f,
+				5f, -5f, 6f, 5f));
+		assertFalse(Rectangle2f.intersectsRectangleSegment(0f, 0f, 1f, 1f,
+				5f, -5f, 5f, 5f));
+	}
+
+	/**
+	 */
+	public void testIntersectsRectangle2f() {
+		assertTrue(this.r.intersects(new Rectangle2f(0f, 0f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Rectangle2f(-5f, -5f, 6f, 6f)));
+		assertTrue(this.r.intersects(new Rectangle2f(.5f, .5f, 4.5f, 4.5f)));
+		assertTrue(this.r.intersects(new Rectangle2f(-5f, -5f, 10f, 10f)));
+		assertFalse(this.r.intersects(new Rectangle2f(5f, .5f, 5f, .6f)));
+		assertFalse(this.r.intersects(new Rectangle2f(-5f, -5f, 1f, 1f)));
+		assertFalse(this.r.intersects(new Rectangle2f(-5f, -5f, 10f, 1f)));
+		assertFalse(this.r.intersects(new Rectangle2f(5f, -5f, 1f, 10f)));
+		assertTrue(this.r.intersects(new Rectangle2f(-5f, -5f, 5.01f, 5.01f)));
+	}
+
+	/**
+	 */
+	public void testIntersectsSegment2f() {
+		assertTrue(this.r.intersects(new Segment2f(0f, 0f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Segment2f(-5f, -5f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Segment2f(.5f, .5f, 5f, 5f)));
+		assertTrue(this.r.intersects(new Segment2f(.5f, .5f, 5f, .6f)));
+		assertTrue(this.r.intersects(new Segment2f(-5f, -5f, 5f, 5f)));
+		assertFalse(this.r.intersects(new Segment2f(-5f, -5f, -4f, -4f)));
+		assertFalse(this.r.intersects(new Segment2f(-5f, -5f, 4f, -4f)));
+		assertFalse(this.r.intersects(new Segment2f(5f, -5f, 6f, 5f)));
+		assertFalse(this.r.intersects(new Segment2f(5f, -5f, 5f, 5f)));
+		assertTrue(this.r.intersects(new Segment2f(-1f, -1f, 0.01f, 0.01f)));
+	}
+
+	/**
+	 */
+	public void testIntersectsCircle2f() {
+		assertTrue(this.r.intersects(new Circle2f(0f, 0f, 1f)));
+		assertFalse(this.r.intersects(new Circle2f(-5f, -5f, 1f)));
+		assertTrue(this.r.intersects(new Circle2f(.5f, .5f, 5f)));
+		assertFalse(this.r.intersects(new Circle2f(-5f, -5f, 5f)));
+		assertFalse(this.r.intersects(new Circle2f(-5f, -5f, 4f)));
+		assertFalse(this.r.intersects(new Circle2f(5f, -5f, 6f)));
+		assertFalse(this.r.intersects(new Circle2f(5f, -5f, 5f)));
+		assertTrue(this.r.intersects(new Circle2f(-1f, -1f, 1.4284f)));
+	}
+
+	/**
+	 */
+	public void testIntersectsEllipse2f() {
+		assertTrue(this.r.intersects(new Ellipse2f(0f, 0f, 1f, 1f)));
+		assertFalse(this.r.intersects(new Ellipse2f(-5f, -5f, 1f, 1f)));
+		assertFalse(this.r.intersects(new Ellipse2f(.5f, .5f, 5f, 5f)));
+		assertTrue(this.r.intersects(new Ellipse2f(.5f, .5f, 5f, .6f)));
+		assertFalse(this.r.intersects(new Ellipse2f(-5f, -5f, 5f, 5f)));
+		assertFalse(this.r.intersects(new Ellipse2f(-5f, -5f, -4f, -4f)));
+		assertFalse(this.r.intersects(new Ellipse2f(-5f, -5f, 4f, -4f)));
+		assertFalse(this.r.intersects(new Ellipse2f(5f, -5f, 6f, 5f)));
+		assertFalse(this.r.intersects(new Ellipse2f(5f, -5f, 5f, 5f)));
+		assertTrue(this.r.intersects(new Ellipse2f(-1f, -1f, 1.2f, 1.2f)));
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/RoundRectangle2fTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/RoundRectangle2fTest.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/RoundRectangle2fTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,459 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import org.arakhne.afc.math.continous.object2d.Circle2f;
+import org.arakhne.afc.math.continous.object2d.Ellipse2f;
+import org.arakhne.afc.math.continous.object2d.PathIterator2f;
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.math.continous.object2d.RoundRectangle2f;
+import org.arakhne.afc.math.continous.object2d.Segment2f;
+import org.arakhne.afc.math.generic.PathElementType;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+/**
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class RoundRectangle2fTest extends AbstractRectangularShape2fTestCase<RoundRectangle2f> {
+
+	@Override
+	protected RoundRectangle2f createShape() {
+		return new RoundRectangle2f(0f, 0f, 1f, 1f, .2f, .4f);
+	}
+
+	@Override
+	public void testIsEmpty() {
+		assertFalse(this.r.isEmpty());
+		this.r.setMinX(1f);
+		assertFalse(this.r.isEmpty());
+		this.r.setMinY(1f);
+		assertTrue(this.r.isEmpty());
+		this.r.setMinX(0f);
+		assertFalse(this.r.isEmpty());
+	}
+
+	@Override
+	public void testClear() {
+		this.r.clear();
+		assertEquals(0f, this.r.getMinX());
+		assertEquals(0f, this.r.getMinY());
+		assertEquals(0f, this.r.getMaxX());
+		assertEquals(0f, this.r.getMaxY());
+		assertEquals(0f, this.r.getArcWidth());
+		assertEquals(0f, this.r.getArcHeight());
+	}
+
+	@Override
+	public void testClone() {
+		RoundRectangle2f b = this.r.clone();
+
+		assertNotSame(b, this.r);
+		assertEpsilonEquals(this.r.getMinX(), b.getMinX());
+		assertEpsilonEquals(this.r.getMinY(), b.getMinY());
+		assertEpsilonEquals(this.r.getMaxX(), b.getMaxX());
+		assertEpsilonEquals(this.r.getMaxY(), b.getMaxY());
+		assertEpsilonEquals(this.r.getArcWidth(), b.getArcWidth());
+		assertEpsilonEquals(this.r.getArcHeight(), b.getArcHeight());
+		
+		b.set(this.r.getMinX()+1f, this.r.getMinY()+1f,
+				this.r.getWidth()+1f, this.r.getHeight()+1f);
+
+		assertNotEpsilonEquals(this.r.getMinX(), b.getMinX());
+		assertNotEpsilonEquals(this.r.getMinY(), b.getMinY());
+		assertNotEpsilonEquals(this.r.getMaxX(), b.getMaxX());
+		assertNotEpsilonEquals(this.r.getMaxY(), b.getMaxY());
+		assertEpsilonEquals(this.r.getArcWidth(), b.getArcWidth());
+		assertEpsilonEquals(this.r.getArcHeight(), b.getArcHeight());
+	}
+
+	@Override
+	public void testDistancePoint2D() {
+		assertEpsilonEquals(0.065492915f, this.r.distance(new Point2f(0f, 0f)));
+		
+		assertEpsilonEquals(4.18243948f, this.r.distance(new Point2f(-2.3f, -3.4f)));
+		assertEpsilonEquals(2.3f, this.r.distance(new Point2f(-2.3f, .5f)));
+		assertEpsilonEquals(5.208449224f, this.r.distance(new Point2f(-2.3f, 5.6f)));
+		
+		assertEpsilonEquals(3.4f, this.r.distance(new Point2f(.5f, -3.4f)));
+		assertEpsilonEquals(0f, this.r.distance(new Point2f(.5f, .5f)));
+		assertEpsilonEquals(4.6f, this.r.distance(new Point2f(.5f, 5.6f)));
+
+		assertEpsilonEquals(5.802671745f, this.r.distance(new Point2f(5.6f, -3.4f)));
+		assertEpsilonEquals(4.6f, this.r.distance(new Point2f(5.6f, .5f)));
+		assertEpsilonEquals(6.590588951f, this.r.distance(new Point2f(5.6f, 5.6f)));
+
+		assertEpsilonEquals(0.04958237f, this.r.distance(new Point2f(.01f, .01f)));
+	}
+
+	@Override
+	public void testDistanceSquaredPoint2D() {
+		assertEpsilonEquals(0.004289322f, this.r.distanceSquared(new Point2f(0f, 0f)));
+		
+		assertEpsilonEquals(17.492800004f, this.r.distanceSquared(new Point2f(-2.3f, -3.4f)));
+		assertEpsilonEquals(5.29f, this.r.distanceSquared(new Point2f(-2.3f, .5f)));
+		assertEpsilonEquals(27.127943319f, this.r.distanceSquared(new Point2f(-2.3f, 5.6f)));
+		
+		assertEpsilonEquals(11.56f, this.r.distanceSquared(new Point2f(.5f, -3.4f)));
+		assertEpsilonEquals(0f, this.r.distanceSquared(new Point2f(.5f, .5f)));
+		assertEpsilonEquals(21.16f, this.r.distanceSquared(new Point2f(.5f, 5.6f)));
+
+		assertEpsilonEquals(33.67099938f, this.r.distanceSquared(new Point2f(5.6f, -3.4f)));
+		assertEpsilonEquals(21.16f, this.r.distanceSquared(new Point2f(5.6f, .5f)));
+		assertEpsilonEquals(43.435862721f, this.r.distanceSquared(new Point2f(5.6f, 5.6f)));
+
+		assertEpsilonEquals(0.002458411f, this.r.distanceSquared(new Point2f(.01f, .01f)));
+	}
+
+	@Override
+	public void testDistanceL1Point2D() {
+		//|0 - 0.029289322| + |0 - 0.058578644|
+		assertEpsilonEquals(0.087867966f, this.r.distanceL1(new Point2f(0f, 0f)));
+		
+		//|-2.3 - 0.02| + |-3.4 - 0.08|
+		assertEpsilonEquals(5.8f, this.r.distanceL1(new Point2f(-2.3f, -3.4f)));
+		//|-2.3 - 0| + |0.5 - 0.5|
+		assertEpsilonEquals(2.3f, this.r.distanceL1(new Point2f(-2.3f, .5f)));
+		//|-2.3 - 0.029289322| + |5.6 - 0.9414214|
+		assertEpsilonEquals(6.987867922f, this.r.distanceL1(new Point2f(-2.3f, 5.6f)));
+		
+		//|0.5 - 0.5| + |-3.4 - 0|
+		assertEpsilonEquals(3.4f, this.r.distanceL1(new Point2f(.5f, -3.4f)));
+		//|0.5 - 0.5| + |0.5 - 0.5|
+		assertEpsilonEquals(0f, this.r.distanceL1(new Point2f(.5f, .5f)));
+		//|0.5 - 0.5| + |5.6 - 1|
+		assertEpsilonEquals(4.6f, this.r.distanceL1(new Point2f(.5f, 5.6f)));
+
+		//|5.6 - 0.99338573| + |-3.4 - 0.12847054|
+		assertEpsilonEquals(8.13508481f, this.r.distanceL1(new Point2f(5.6f, -3.4f)));
+		//|5.6 - 1| + |0.5 - 0.5|
+		assertEpsilonEquals(4.6f, this.r.distanceL1(new Point2f(5.6f, .5f)));
+		//|5.6 - 0.9890606| + |5.6 - 0.89095545|
+		assertEpsilonEquals(9.31998395f, this.r.distanceL1(new Point2f(5.6f, 5.6f)));
+
+		//|0.01 - 0.031225532| + |0.01 - 0.054809466|
+		assertEpsilonEquals(0.066034998f, this.r.distanceL1(new Point2f(.01f, .01f)));
+	}
+
+	@Override
+	public void testDistanceLinfPoint2D() {
+		//max( abs(0 - 0.029289322) , abs(0 - 0.058578644) )
+		assertEpsilonEquals(0.058579f, this.r.distanceLinf(new Point2f(0f, 0f)));
+		
+		//max( abs(-2.3 - 0.02) , abs(-3.4 - 0.08) )
+		assertEpsilonEquals(3.48f, this.r.distanceLinf(new Point2f(-2.3f, -3.4f)));
+		//max( abs(-2.3 - 0) , abs(0.5 - 0.5) )
+		assertEpsilonEquals(2.3f, this.r.distanceLinf(new Point2f(-2.3f, .5f)));
+		//max( abs(-2.3 - 0.029289322) , abs(5.6 - 0.9414214) )
+		assertEpsilonEquals(4.6586f, this.r.distanceLinf(new Point2f(-2.3f, 5.6f)));
+		
+		//max( abs(0.5 - 0.5) , abs(-3.4 - 0) )
+		assertEpsilonEquals(3.4f, this.r.distanceLinf(new Point2f(.5f, -3.4f)));
+		//max( abs(0.5 - 0.5) , abs(0.5 - 0.5) )
+		assertEpsilonEquals(0f, this.r.distanceLinf(new Point2f(.5f, .5f)));
+		//max( abs(0.5 - 0.5) , abs(5.6 - 1) )
+		assertEpsilonEquals(4.6f, this.r.distanceLinf(new Point2f(.5f, 5.6f)));
+
+		//max( abs(5.6 - 0.99338573) , abs(-3.4 - 0.12847054) )
+		assertEpsilonEquals(4.6066f, this.r.distanceLinf(new Point2f(5.6f, -3.4f)));
+		//max( abs(5.6 - 1) , abs(0.5 - 0.5) )
+		assertEpsilonEquals(4.6f, this.r.distanceLinf(new Point2f(5.6f, .5f)));
+		//max( abs(5.6 - 0.9890606) , abs(5.6 - 0.89095545) )
+		assertEpsilonEquals(4.7090f, this.r.distanceLinf(new Point2f(5.6f, 5.6f)));
+
+		//max( abs(0.01 - 0.031225532) , abs(0.01 - 0.054809466) )
+		assertEpsilonEquals(0.044809f, this.r.distanceLinf(new Point2f(.01f, .01f)));
+	}
+
+	@Override
+	public void testToBoundingBox() {
+		Rectangle2f bb = this.r.toBoundingBox();
+		assertEpsilonEquals(0f, bb.getMinX());
+		assertEpsilonEquals(0f, bb.getMinY());
+		assertEpsilonEquals(1f, bb.getMaxX());
+		assertEpsilonEquals(1f, bb.getMaxY());
+	}
+
+	/**
+	 */
+	public void testContainsFloatFloat() {
+		assertFalse(this.r.contains(0f, 0f));
+
+		assertFalse(this.r.contains(-2.3f, -3.4f));
+		assertFalse(this.r.contains(-2.3f, .5f));
+		assertFalse(this.r.contains(-2.3f, 5.6f));
+
+		assertFalse(this.r.contains(.5f, -3.4f));
+		assertTrue(this.r.contains(.5f, .5f));
+		assertFalse(this.r.contains(.5f, 5.6f));
+
+		assertFalse(this.r.contains(5.6f, -3.4f));
+		assertFalse(this.r.contains(5.6f, .5f));
+		assertFalse(this.r.contains(5.6f, 5.6f));
+
+		assertFalse(this.r.contains(.01f, .01f));
+	}
+
+	/**
+	 */
+	public void testContainsPoint2D() {
+		assertFalse(this.r.contains(new Point2f(0f, 0f)));
+
+		assertFalse(this.r.contains(new Point2f(-2.3f, -3.4f)));
+		assertFalse(this.r.contains(new Point2f(-2.3f, .5f)));
+		assertFalse(this.r.contains(new Point2f(-2.3f, 5.6f)));
+
+		assertFalse(this.r.contains(new Point2f(.5f, -3.4f)));
+		assertTrue(this.r.contains(new Point2f(.5f, .5f)));
+		assertFalse(this.r.contains(new Point2f(.5f, 5.6f)));
+
+		assertFalse(this.r.contains(new Point2f(5.6f, -3.4f)));
+		assertFalse(this.r.contains(new Point2f(5.6f, .5f)));
+		assertFalse(this.r.contains(new Point2f(5.6f, 5.6f)));
+
+		assertFalse(this.r.contains(new Point2f(.01f, .01f)));
+	}
+
+	/**
+	 */
+	public void testGetClosestPointTo() {
+		Point2D p;
+		
+		p = this.r.getClosestPointTo(new Point2f(0f, 0f));
+		// ecenter = (.1; .2)
+		// a = .1
+		// b = .2
+		// a*a = .01
+		// b*b = .04
+		// x0 = x - ecenterx = 0 - .1 = -.1
+		// y0 = y - ecentery = 0 - .2 = -.2
+		// denom*denom = a*a*y0*y0 + b*b*x0*x0 = 8.0000e-04
+		// denom = 0.028284
+		// f = (a*b)/denom = 0.70711
+		// x2 = f * x0 = -0.070711
+		// y2 = f * y0 = -0.14142
+		// px2 = x2 + ecenterx = 0.029289
+		// py2 = y2 + ecentery = 0.058579
+		assertEpsilonEquals(0.029289f, p.getX());
+		assertEpsilonEquals(0.058579f, p.getY());
+		
+		p = this.r.getClosestPointTo(new Point2f(-2.3f, -3.4f));
+		assertEpsilonEquals(.02f, p.getX());
+		assertEpsilonEquals(.08f, p.getY());
+		p = this.r.getClosestPointTo(new Point2f(-2.3f, .5f));
+		assertEpsilonEquals(0f, p.getX());
+		assertEpsilonEquals(.5f, p.getY());
+		p = this.r.getClosestPointTo(new Point2f(-2.3f, 5.6f));
+		assertEpsilonEquals(0.0292893218813453f, p.getX());
+		assertEpsilonEquals(0.941421356237309f, p.getY());
+		
+		p = this.r.getClosestPointTo(new Point2f(.5f, -3.4f));
+		assertEpsilonEquals(.5f, p.getX());
+		assertEpsilonEquals(0f, p.getY());
+		p = this.r.getClosestPointTo(new Point2f(.5f, .5f));
+		assertEpsilonEquals(.5f, p.getX());
+		assertEpsilonEquals(.5f, p.getY());
+		p = this.r.getClosestPointTo(new Point2f(.5f, 5.6f));
+		assertEpsilonEquals(.5f, p.getX());
+		assertEpsilonEquals(1f, p.getY());
+
+		p = this.r.getClosestPointTo(new Point2f(5.6f, -3.4f));
+		assertEpsilonEquals(0.993385675169617f, p.getX());
+		assertEpsilonEquals(0.128470546678592f, p.getY());
+		p = this.r.getClosestPointTo(new Point2f(5.6f, .5f));
+		assertEpsilonEquals(1f, p.getX());
+		assertEpsilonEquals(.5f, p.getY());
+		p = this.r.getClosestPointTo(new Point2f(5.6f, 5.6f));
+		assertEpsilonEquals(0.989060526490781f, p.getX());
+		assertEpsilonEquals(0.890955431309734f, p.getY());
+
+		p = this.r.getClosestPointTo(new Point2f(.01f, .01f));
+		assertEpsilonEquals(0.0312255352089217f, p.getX());
+		assertEpsilonEquals(0.0548094632188347f, p.getY());
+	}
+
+	/**
+	 */
+	public void testGetPathIterator() {
+		float vt = 0.11045696f; // vertical tangent length for the arc
+		float ht = 0.055228f; // horizontal tangent length for the arc
+		PathIterator2f pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0f, .2f);
+		assertElement(pi, PathElementType.LINE_TO, 0f, .8f);
+		assertElement(pi, PathElementType.CURVE_TO, 0f, .8f+vt, .1f-ht, 1f, 0.1f, 1f);
+		assertElement(pi, PathElementType.LINE_TO, .9f, 1f);
+		assertElement(pi, PathElementType.CURVE_TO, .9f+ht, 1f, 1f, .8f+vt, 1f, .8f);
+		assertElement(pi, PathElementType.LINE_TO, 1f, .2f);
+		assertElement(pi, PathElementType.CURVE_TO, 1f, .2f-vt, .9f+ht, 0f, .9f, 0f);
+		assertElement(pi, PathElementType.LINE_TO, .1f, 0f);
+		assertElement(pi, PathElementType.CURVE_TO, .1f-ht, 0f, 0f, .2f-vt, 0f, .2f);
+		assertElement(pi, PathElementType.CLOSE);
+	}
+	
+	/**
+	 */
+	public void testGetPathIteratorTransform2D() {
+		Transform2D tr;
+		PathIterator2f pi;
+		float vt = 0.11045696f; // vertical tangent length for the arc
+		float ht = 0.055228f; // horizontal tangent length for the arc
+		
+		tr = new Transform2D();
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 0f, .2f);
+		assertElement(pi, PathElementType.LINE_TO, 0f, .8f);
+		assertElement(pi, PathElementType.CURVE_TO, 0f, .8f+vt, .1f-ht, 1f, 0.1f, 1f);
+		assertElement(pi, PathElementType.LINE_TO, .9f, 1f);
+		assertElement(pi, PathElementType.CURVE_TO, .9f+ht, 1f, 1f, .8f+vt, 1f, .8f);
+		assertElement(pi, PathElementType.LINE_TO, 1f, .2f);
+		assertElement(pi, PathElementType.CURVE_TO, 1f, .2f-vt, .9f+ht, 0f, .9f, 0f);
+		assertElement(pi, PathElementType.LINE_TO, .1f, 0f);
+		assertElement(pi, PathElementType.CURVE_TO, .1f-ht, 0f, 0f, .2f-vt, 0f, .2f);
+		assertElement(pi, PathElementType.CLOSE);
+
+		tr = new Transform2D();
+		tr.makeTranslationMatrix(3.4f, 4.5f);
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 3.4f, 4.7f);
+		assertElement(pi, PathElementType.LINE_TO, 3.4f, 5.3f);
+		assertElement(pi, PathElementType.CURVE_TO, 3.4f, 5.3f+vt, 3.5f-ht, 5.5f, 3.5f, 5.5f);
+		assertElement(pi, PathElementType.LINE_TO, 4.3f, 5.5f);
+		assertElement(pi, PathElementType.CURVE_TO, 4.3f+ht, 5.5f, 4.4f, 5.3f+vt, 4.4f, 5.3f);
+		assertElement(pi, PathElementType.LINE_TO, 4.4f, 4.7f);
+		assertElement(pi, PathElementType.CURVE_TO, 4.4f, 4.7f-vt, 4.3f+ht, 4.5f, 4.3f, 4.5f);
+		assertElement(pi, PathElementType.LINE_TO, 3.5f, 4.5f);
+		assertElement(pi, PathElementType.CURVE_TO, 3.5f-ht, 4.5f, 3.4f, 4.7f-vt, 3.4f, 4.7f);
+		assertElement(pi, PathElementType.CLOSE);
+	}
+
+	/**
+	 */
+	public void testSetArcWidth() {
+		this.r.setArcWidth(4.5f);
+		assertEpsilonEquals(4.5f, this.r.getArcWidth());
+	}
+
+	/**
+	 */
+	public void testSetArcHeight() {
+		this.r.setArcHeight(4.5f);
+		assertEpsilonEquals(4.5f, this.r.getArcHeight());
+	}
+
+	/**
+	 */
+	public void testIntersectsRectangle2f() {
+		assertTrue(this.r.intersects(new Rectangle2f(0f, 0f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Rectangle2f(-5f, -5f, 6f, 6f)));
+		assertTrue(this.r.intersects(new Rectangle2f(.5f, .5f, 4.5f, 4.5f)));
+		assertTrue(this.r.intersects(new Rectangle2f(-5f, -5f, 10f, 10f)));
+		assertFalse(this.r.intersects(new Rectangle2f(5f, .5f, 5f, .6f)));
+		assertFalse(this.r.intersects(new Rectangle2f(-5f, -5f, 1f, 1f)));
+		assertFalse(this.r.intersects(new Rectangle2f(-5f, -5f, 10f, 1f)));
+		assertFalse(this.r.intersects(new Rectangle2f(5f, -5f, 1f, 10f)));
+		assertTrue(this.r.intersects(new Rectangle2f(-5f, -5f, 5.01f, 5.01f)));
+	}
+
+	/**
+	 */
+	public void testIntersectsSegment2f() {
+		assertTrue(this.r.intersects(new Segment2f(0f, 0f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Segment2f(-5f, -5f, 1f, 1f)));
+		assertTrue(this.r.intersects(new Segment2f(.5f, .5f, 5f, 5f)));
+		assertTrue(this.r.intersects(new Segment2f(.5f, .5f, 5f, .6f)));
+		assertTrue(this.r.intersects(new Segment2f(-5f, -5f, 5f, 5f)));
+		assertFalse(this.r.intersects(new Segment2f(-5f, -5f, -4f, -4f)));
+		assertFalse(this.r.intersects(new Segment2f(-5f, -5f, 4f, -4f)));
+		assertFalse(this.r.intersects(new Segment2f(5f, -5f, 6f, 5f)));
+		assertFalse(this.r.intersects(new Segment2f(5f, -5f, 5f, 5f)));
+		assertTrue(this.r.intersects(new Segment2f(-1f, -1f, 0.01f, 0.01f)));
+	}
+
+	/**
+	 */
+	public void testIntersectsCircle2f() {
+		assertTrue(this.r.intersects(new Circle2f(0f, 0f, 1f)));
+		assertFalse(this.r.intersects(new Circle2f(-5f, -5f, 1f)));
+		assertTrue(this.r.intersects(new Circle2f(.5f, .5f, 5f)));
+		assertFalse(this.r.intersects(new Circle2f(-5f, -5f, 5f)));
+		assertFalse(this.r.intersects(new Circle2f(-5f, -5f, 4f)));
+		assertFalse(this.r.intersects(new Circle2f(5f, -5f, 6f)));
+		assertFalse(this.r.intersects(new Circle2f(5f, -5f, 5f)));
+		assertTrue(this.r.intersects(new Circle2f(-1f, -1f, 1.4284f)));
+	}
+
+	/**
+	 */
+	public void testIntersectsEllipse2f() {
+		assertTrue(this.r.intersects(new Ellipse2f(0f, 0f, 1f, 1f)));
+		assertFalse(this.r.intersects(new Ellipse2f(-5f, -5f, 1f, 1f)));
+		assertFalse(this.r.intersects(new Ellipse2f(.5f, .5f, 5f, 5f)));
+		assertTrue(this.r.intersects(new Ellipse2f(.5f, .5f, 5f, .6f)));
+		assertFalse(this.r.intersects(new Ellipse2f(-5f, -5f, 5f, 5f)));
+		assertFalse(this.r.intersects(new Ellipse2f(-5f, -5f, -4f, -4f)));
+		assertFalse(this.r.intersects(new Ellipse2f(-5f, -5f, 4f, -4f)));
+		assertFalse(this.r.intersects(new Ellipse2f(5f, -5f, 6f, 5f)));
+		assertFalse(this.r.intersects(new Ellipse2f(5f, -5f, 5f, 5f)));
+		assertTrue(this.r.intersects(new Ellipse2f(-1f, -1f, 1.2f, 1.2f)));
+	}
+
+	/**
+	 */
+	public static void testContainsRoundRectangleRectangle() {
+		assertFalse(RoundRectangle2f.containsRoundRectangleRectangle(
+				0f, 0f, 1f, 1f, .2f, .4f,
+				0f, 0f, 1f, 1f));
+		assertFalse(RoundRectangle2f.containsRoundRectangleRectangle(
+				0f, 0f, 1f, 1f, .2f, .4f,
+				-5f, -5f, 6f, 6f));
+		assertFalse(RoundRectangle2f.containsRoundRectangleRectangle(
+				0f, 0f, 1f, 1f, .2f, .4f,
+				.5f, .5f, 4.5f, 4.5f));
+		assertFalse(RoundRectangle2f.containsRoundRectangleRectangle(
+				0f, 0f, 1f, 1f, .2f, .4f,
+				-5f, -5f, 10f, 10f));
+		assertFalse(RoundRectangle2f.containsRoundRectangleRectangle(
+				0f, 0f, 1f, 1f, .2f, .4f,
+				5f, .5f, 5f, .6f));
+		assertFalse(RoundRectangle2f.containsRoundRectangleRectangle(
+				0f, 0f, 1f, 1f, .2f, .4f,
+				-5f, -5f, 1f, 1f));
+		assertFalse(RoundRectangle2f.containsRoundRectangleRectangle(
+				0f, 0f, 1f, 1f, .2f, .4f,
+				-5f, -5f, 10f, 1f));
+		assertFalse(RoundRectangle2f.containsRoundRectangleRectangle(
+				0f, 0f, 1f, 1f, .2f, .4f,
+				5f, -5f, 1f, 10f));
+		assertFalse(RoundRectangle2f.containsRoundRectangleRectangle(
+				0f, 0f, 1f, 1f, .2f, .4f,
+				-5f, -5f, 5.01f, 5.01f));
+		assertTrue(RoundRectangle2f.containsRoundRectangleRectangle(
+				0f, 0f, 1f, 1f, .2f, .4f,
+				.25f, .25f, .5f, .5f));
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Segment2fTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Segment2fTest.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/continous/object2d/Segment2fTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,1208 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.continous.object2d;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.continous.object2d.PathIterator2f;
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.math.continous.object2d.Segment2f;
+import org.arakhne.afc.math.generic.PathElementType;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+/**
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Segment2fTest extends AbstractShape2fTestCase<Segment2f> {
+	
+	@Override
+	protected Segment2f createShape() {
+		return new Segment2f(0f, 0f, 1f, 1f);
+	}
+	
+	@Override
+	public void testIsEmpty() {
+		assertFalse(this.r.isEmpty());
+		this.r.clear();
+		assertTrue(this.r.isEmpty());
+	}
+
+	@Override
+	public void testClear() {
+		this.r.clear();
+		assertEquals(0f, this.r.getX1());
+		assertEquals(0f, this.r.getY1());
+		assertEquals(0f, this.r.getX2());
+		assertEquals(0f, this.r.getY2());
+	}
+
+	@Override
+	public void testClone() {
+		Segment2f b = this.r.clone();
+
+		assertNotSame(b, this.r);
+		assertEpsilonEquals(this.r.getX1(), b.getX1());
+		assertEpsilonEquals(this.r.getY1(), b.getY1());
+		assertEpsilonEquals(this.r.getX2(), b.getX2());
+		assertEpsilonEquals(this.r.getY2(), b.getY2());
+		
+		b.set(this.r.getX1()+1f, this.r.getY1()+1f,
+				this.r.getX2()+1f, this.r.getY2()+1f);
+
+		assertNotEpsilonEquals(this.r.getX1(), b.getX1());
+		assertNotEpsilonEquals(this.r.getY1(), b.getY1());
+		assertNotEpsilonEquals(this.r.getX2(), b.getX2());
+		assertNotEpsilonEquals(this.r.getY2(), b.getY2());
+	}
+
+	/**
+	 */
+	public static void testComputeCrossingsFromPoint() {
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromPoint(
+						0f, 0f,
+						10f, -1f, 10f, 1f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromPoint(
+						0f, 0f,
+						10f, -1f, 10f, -.5f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromPoint(
+						0f, 0f,
+						-10f, -1f, -10f, 1f));
+	}
+
+	/**
+	 */
+	public static void testComputeCrossingsFromRect() {
+		assertEquals(
+				2,
+				Segment2f.computeCrossingsFromRect(
+						0,
+						0f, 0f, 1f, 1f,
+						10f, -5f, 10f, 5f));
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromRect(
+						0,
+						0f, 0f, 1f, 1f,
+						10f, -5f, 10f, .5f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromRect(
+						0,
+						0f, 0f, 1f, 1f,
+						10f, -5f, 10f, -1f));
+	}
+	
+	/**
+	 */
+	public static void testComputeCrossingsFromSegment_0011() {
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 0f, 1f, 1f,
+						10f, -5f, 10f, -4f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 0f, 1f, 1f,
+						10f, 5f, 10f, 4f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 0f, 1f, 1f,
+						-5f, .5f, 0f, .6f));
+		
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 0f, 1f, 1f,
+						10f, -1f, 11f, .6f));
+		assertEquals(
+				2,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 0f, 1f, 1f,
+						10f, -1f, 11f, 2f));
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 0f, 1f, 1f,
+						10f, .5f, 11f, 2f));
+
+		assertEquals(
+				-1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 0f, 1f, 1f,
+						10f, 2f, 11f, .6f));
+		assertEquals(
+				-2,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 0f, 1f, 1f,
+						10f, 2f, 11f, -1f));
+		assertEquals(
+				-1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 0f, 1f, 1f,
+						10f, .6f, 11f, -1f));
+
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 0f, 1f, 1f,
+						0f, .5f, .25f, .5f));
+
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 0f, 1f, 1f,
+						.75f, .5f, 1f, .5f));
+
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 0f, 1f, 1f,
+						5f, -5f, .75f, .5f));
+
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 0f, 1f, 1f,
+						5f, -5f, 0f, 1f));
+
+		assertEquals(
+				2,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 0f, 1f, 1f,
+						5f, -5f, 1f, 1f));
+
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 0f, 1f, 1f,
+						-2f, 1f, 5f, -5f));
+	}
+
+	/**
+	 */
+	public static void testComputeCrossingsFromSegment_1100() {
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 1f, 0f, 0f,
+						10f, -5f, 10f, -4f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 1f, 0f, 0f,
+						10f, 5f, 10f, 4f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 1f, 0f, 0f,
+						-5f, .5f, 0f, .6f));
+		
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 1f, 0f, 0f,
+						10f, -1f, 11f, .6f));
+		assertEquals(
+				2,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 1f, 0f, 0f,
+						10f, -1f, 11f, 2f));
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 1f, 0f, 0f,
+						10f, .5f, 11f, 2f));
+
+		assertEquals(
+				-1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 1f, 0f, 0f,
+						10f, 2f, 11f, .6f));
+		assertEquals(
+				-2,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 1f, 0f, 0f,
+						10f, 2f, 11f, -1f));
+		assertEquals(
+				-1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 1f, 0f, 0f,
+						10f, .6f, 11f, -1f));
+
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 1f, 0f, 0f,
+						0f, .5f, .25f, .5f));
+
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 1f, 0f, 0f,
+						.75f, .5f, 1f, .5f));
+
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 1f, 0f, 0f,
+						5f, -5f, .75f, .5f));
+
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 1f, 0f, 0f,
+						5f, -5f, 0f, 1f));
+
+		assertEquals(
+				2,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 1f, 0f, 0f,
+						5f, -5f, 1f, 1f));
+
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 1f, 0f, 0f,
+						-2f, 1f, 5f, -5f));
+	}
+
+	/**
+	 */
+	public static void testComputeCrossingsFromSegment_0110() {
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 1f, 1f, 0f,
+						10f, -5f, 10f, -4f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 1f, 1f, 0f,
+						10f, 5f, 10f, 4f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 1f, 1f, 0f,
+						-5f, .5f, 0f, .6f));
+		
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 1f, 1f, 0f,
+						10f, -1f, 11f, .6f));
+		assertEquals(
+				2,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 1f, 1f, 0f,
+						10f, -1f, 11f, 2f));
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 1f, 1f, 0f,
+						10f, .5f, 11f, 2f));
+
+		assertEquals(
+				-1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 1f, 1f, 0f,
+						10f, 2f, 11f, .6f));
+		assertEquals(
+				-2,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 1f, 1f, 0f,
+						10f, 2f, 11f, -1f));
+		assertEquals(
+				-1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 1f, 1f, 0f,
+						10f, .6f, 11f, -1f));
+
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 1f, 1f, 0f,
+						0f, .5f, .25f, .5f));
+
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 1f, 1f, 0f,
+						.75f, .5f, 1f, .5f));
+
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 1f, 1f, 0f,
+						5f, -.01f, .75f, .5f));
+
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 1f, 1f, 0f,
+						20f, -5f, 0f, 1f));
+
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						0f, 1f, 1f, 0f,
+						5f, 10f, .25f, .5f));
+	}
+
+	/**
+	 */
+	public static void testComputeCrossingsFromSegment_1001() {
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 0f, 0f, 1f,
+						10f, -5f, 10f, -4f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 0f, 0f, 1f,
+						10f, 5f, 10f, 4f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 0f, 0f, 1f,
+						-5f, .5f, 0f, .6f));
+		
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 0f, 0f, 1f,
+						10f, -1f, 11f, .6f));
+		assertEquals(
+				2,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 0f, 0f, 1f,
+						10f, -1f, 11f, 2f));
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 0f, 0f, 1f,
+						10f, .5f, 11f, 2f));
+
+		assertEquals(
+				-1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 0f, 0f, 1f,
+						10f, 2f, 11f, .6f));
+		assertEquals(
+				-2,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 0f, 0f, 1f,
+						10f, 2f, 11f, -1f));
+		assertEquals(
+				-1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 0f, 0f, 1f,
+						10f, .6f, 11f, -1f));
+
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 0f, 0f, 1f,
+						0f, .5f, .25f, .5f));
+
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 0f, 0f, 1f,
+						.75f, .5f, 1f, .5f));
+
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 0f, 0f, 1f,
+						20f, -5f, .75f, .5f));
+
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 0f, 0f, 1f,
+						20f, -5f, 0f, 1f));
+
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromSegment(
+						0,
+						1f, 0f, 0f, 1f,
+						5f, 10f, .25f, .5f));
+	}
+
+	/**
+	 */
+	public static void testComputeCrossingsFromEllipse_PositiveTest() {
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						-1f, -5f, 5f, -4f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						-5f, -1f, -7f, 3f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						10f, -1f, 11f, -2f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						1f, 6f, 3f, 5f));
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						6f, -1f, 5f, .5f));
+		assertEquals(
+				2,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						6f, -1f, 5f, 2f));
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						6f, .5f, 5f, 2f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						3f, 0f, .5f, .5f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						3f, 0f, 0f, 1f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						3f, 0f, 0f, 1f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						.5f, -1f, .5f, 2f));
+		assertEquals(
+				2,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 1f, 1f,
+						7f, -5f, 1f, 1f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						4f, -3f, 1f, 2f,
+						7f, -5f, 1f, 1f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						4f, -3f, 1f, 2f,
+						7f, -5f, 4f, 0f));
+		assertEquals(
+				2,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						4f, -3f, 1f, 2f,
+						7f, -5f, 4.2f, 0f));
+	}
+
+	/**
+	 */
+	public static void testComputeCrossingsFromEllipse_NegativeTest() {
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						5f, -4f, -1f, -5f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						-7f, 3f, -5f, -1f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						11f, -2f, 10f, -1f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						3f, 5f, 1f, 6f));
+		assertEquals(
+				-1,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						5f, .5f, 6f, -1f));
+		assertEquals(
+				-2,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						5f, 2f, 6f, -1f));
+		assertEquals(
+				-1,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						5f, 2f, 6f, .5f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						.5f, .5f, 3f, 0f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						0f, 1f, 3f, 0f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						0f, 1f, 3f, 0f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 2f, 1f,
+						.5f, 2f, .5f, -1f));
+		assertEquals(
+				-2,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						0f, 0f, 1f, 1f,
+						1f, 1f, 7f, -5f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						4f, -3f, 1f, 2f,
+						1f, 1f, 7f, -5f));
+		assertEquals(
+				-2,
+				Segment2f.computeCrossingsFromEllipse(
+						0,
+						4f, -3f, 1f, 2f,
+						4.2f, 0f, 7f, -5f));
+	}
+
+	/**
+	 */
+	public static void testComputeCrossingsFromCircle_PositiveTest() {
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						-1f, -5f, 5f, -4f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						-5f, -1f, -7f, 3f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						10f, -1f, 11f, -2f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						1f, 6f, 3f, 5f));
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						6f, -1f, 5f, .5f));
+		assertEquals(
+				2,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						6f, -1f, 5f, 2f));
+		assertEquals(
+				1,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						6f, .5f, 5f, 2f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						3f, 0f, .5f, .5f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						3f, 0f, 0f, 2f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						.5f, -1f, .5f, 4f));
+		assertEquals(
+				2,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						3f, 0f, 1f, 3f));
+	}
+
+	/**
+	 */
+	public static void testComputeCrossingsFromCircle_NegativeTest() {
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						5f, -4f, -1f, -5f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						-7f, -3f, -5f, -1f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						11f, -2f, 10f, -1f));
+		assertEquals(
+				0,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						3f, 5f,  1f, 6f));
+		assertEquals(
+				-1,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						5f, .5f, 6f, -1f));
+		assertEquals(
+				-2,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						5f, 2f, 6f, -1f));
+		assertEquals(
+				-1,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						5f, 2f, 6f, .5f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						.5f, .5f, 3f, 0f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						0f, 2f, 3f, 0f));
+		assertEquals(
+				MathConstants.SHAPE_INTERSECTS,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						.5f, 4f, .5f, -1f));
+		assertEquals(
+				-2,
+				Segment2f.computeCrossingsFromCircle(
+						0,
+						1f, 1f, 1f,
+						1f, 3f, 3f, 0f));
+	}
+
+	/**
+	 */
+	public static void testIntersectsLineLine() {
+		assertTrue(Segment2f.intersectsLineLine(
+				0f, 0f, 1f, 1f,
+				0f, 0f, 1f, 1f));
+		assertTrue(Segment2f.intersectsLineLine(
+				0f, 0f, 1f, 1f,
+				0f, 0f, 2f, 2f));
+		assertTrue(Segment2f.intersectsLineLine(
+				0f, 0f, 1f, 1f,
+				0f, 0f, .5f, .5f));
+		assertTrue(Segment2f.intersectsLineLine(
+				0f, 0f, 1f, 1f,
+				-3f, -3f, .5f, .5f));
+		assertTrue(Segment2f.intersectsLineLine(
+				0f, 0f, 1f, 1f,
+				-3f, -3f, 0f, 0f));
+		assertTrue(Segment2f.intersectsLineLine(
+				0f, 0f, 1f, 1f,
+				-3f, -3f, -1f, -1f));
+
+		assertTrue(Segment2f.intersectsLineLine(
+				0f, 0f, 1f, 1f,
+				-3f, 0f, 4f, 0f));
+
+		assertFalse(Segment2f.intersectsLineLine(
+				0f, 0f, 1f, 1f,
+				-3f, 0f, -2f, 1f));
+
+		assertFalse(Segment2f.intersectsLineLine(
+				0f, 0f, 1f, 1f,
+				10f, 0f, 9f, -1f));
+	}
+
+	/**
+	 */
+	public static void testIntersectsSegmentLine() {
+		assertTrue(Segment2f.intersectsSegmentLine(
+				0f, 0f, 1f, 1f,
+				0f, 0f, 1f, 1f));
+		assertTrue(Segment2f.intersectsSegmentLine(
+				0f, 0f, 1f, 1f,
+				0f, 0f, 2f, 2f));
+		assertTrue(Segment2f.intersectsSegmentLine(
+				0f, 0f, 1f, 1f,
+				0f, 0f, .5f, .5f));
+		assertTrue(Segment2f.intersectsSegmentLine(
+				0f, 0f, 1f, 1f,
+				-3f, -3f, .5f, .5f));
+		assertTrue(Segment2f.intersectsSegmentLine(
+				0f, 0f, 1f, 1f,
+				-3f, -3f, 0f, 0f));
+		assertTrue(Segment2f.intersectsSegmentLine(
+				0f, 0f, 1f, 1f,
+				-3f, -3f, -1f, -1f));
+
+		assertTrue(Segment2f.intersectsSegmentLine(
+				0f, 0f, 1f, 1f,
+				-3f, 0f, 4f, 0f));
+
+		assertFalse(Segment2f.intersectsSegmentLine(
+				0f, 0f, 1f, 1f,
+				-3f, 0f, -2f, 1f));
+
+		assertFalse(Segment2f.intersectsSegmentLine(
+				0f, 0f, 1f, 1f,
+				10f, 0f, 9f, -1f));
+	}
+
+	/**
+	 */
+	public static void testIntersectsSegmentSegment() {
+		assertTrue(Segment2f.intersectsSegmentSegment(
+				0f, 0f, 1f, 1f,
+				0f, 0f, 1f, 1f));
+		assertTrue(Segment2f.intersectsSegmentSegment(
+				0f, 0f, 1f, 1f,
+				0f, 0f, 2f, 2f));
+		assertTrue(Segment2f.intersectsSegmentSegment(
+				0f, 0f, 1f, 1f,
+				0f, 0f, .5f, .5f));
+		assertTrue(Segment2f.intersectsSegmentSegment(
+				0f, 0f, 1f, 1f,
+				-3f, -3f, .5f, .5f));
+		assertTrue(Segment2f.intersectsSegmentSegment(
+				0f, 0f, 1f, 1f,
+				-3f, -3f, 0f, 0f));
+		assertFalse(Segment2f.intersectsSegmentSegment(
+				0f, 0f, 1f, 1f,
+				-3f, -3f, -1f, -1f));
+
+		assertTrue(Segment2f.intersectsSegmentSegment(
+				0f, 0f, 1f, 1f,
+				-3f, 0f, 4f, 0f));
+
+		assertFalse(Segment2f.intersectsSegmentSegment(
+				0f, 0f, 1f, 1f,
+				-3f, 0f, -2f, 1f));
+
+		assertFalse(Segment2f.intersectsSegmentSegment(
+				0f, 0f, 1f, 1f,
+				10f, 0f, 9f, -1f));
+	}
+
+	@Override
+	public void testDistancePoint2D() {
+		assertEpsilonEquals(0f, this.r.distance(new Point2f(0f, 0f)));
+		assertEpsilonEquals(0f, this.r.distance(new Point2f(.5f, .5f)));
+		assertEpsilonEquals(0f, this.r.distance(new Point2f(1f, 1f)));
+		
+		assertEpsilonEquals(3.733630941f, this.r.distance(new Point2f(2.3f, 4.5f)));
+
+		assertEpsilonEquals(1.414213562f, this.r.distance(new Point2f(2f, 2f)));
+	}
+
+	@Override
+	public void testDistanceSquaredPoint2D() {
+		assertEpsilonEquals(0f, this.r.distanceSquared(new Point2f(0f, 0f)));
+		assertEpsilonEquals(0f, this.r.distanceSquared(new Point2f(.5f, .5f)));
+		assertEpsilonEquals(0f, this.r.distanceSquared(new Point2f(1f, 1f)));
+		
+		assertEpsilonEquals(13.94f, this.r.distanceSquared(new Point2f(2.3f, 4.5f)));
+
+		assertEpsilonEquals(2f, this.r.distanceSquared(new Point2f(2f, 2f)));
+	}
+
+	@Override
+	public void testDistanceL1Point2D() {
+		assertEpsilonEquals(0f, this.r.distanceL1(new Point2f(0f, 0f)));
+		assertEpsilonEquals(0f, this.r.distanceL1(new Point2f(.5f, .5f)));
+		assertEpsilonEquals(0f, this.r.distanceL1(new Point2f(1f, 1f)));
+		
+		assertEpsilonEquals(4.8f, this.r.distanceL1(new Point2f(2.3f, 4.5f)));
+
+		assertEpsilonEquals(2f, this.r.distanceL1(new Point2f(2f, 2f)));
+	}
+
+	@Override
+	public void testDistanceLinfPoint2D() {
+		assertEpsilonEquals(0f, this.r.distanceLinf(new Point2f(0f, 0f)));
+		assertEpsilonEquals(0f, this.r.distanceLinf(new Point2f(.5f, .5f)));
+		assertEpsilonEquals(0f, this.r.distanceLinf(new Point2f(1f, 1f)));
+		
+		assertEpsilonEquals(3.5f, this.r.distanceLinf(new Point2f(2.3f, 4.5f)));
+
+		assertEpsilonEquals(1f, this.r.distanceLinf(new Point2f(2f, 2f)));
+	}
+
+	@Override
+	public void testTranslateFloatFloat() {
+		this.r.translate(3.4f,  4.5f);
+		assertEpsilonEquals(3.4f, this.r.getX1());
+		assertEpsilonEquals(4.5f, this.r.getY1());
+		assertEpsilonEquals(4.4f, this.r.getX2());
+		assertEpsilonEquals(5.5f, this.r.getY2());
+	}
+
+	/**
+	 */
+	public void testSetFloatFloatFloatFloat() {
+		this.r.set(3.4f,  4.5f, 5.6f, 6.7f);
+		assertEpsilonEquals(3.4f, this.r.getX1());
+		assertEpsilonEquals(4.5f, this.r.getY1());
+		assertEpsilonEquals(5.6f, this.r.getX2());
+		assertEpsilonEquals(6.7f, this.r.getY2());
+	}
+
+	/**
+	 */
+	public void testSetPoint2DPoint2D() {
+		this.r.set(new Point2f(3.4f,  4.5f), new Point2f(5.6f, 6.7f));
+		assertEpsilonEquals(3.4f, this.r.getX1());
+		assertEpsilonEquals(4.5f, this.r.getY1());
+		assertEpsilonEquals(5.6f, this.r.getX2());
+		assertEpsilonEquals(6.7f, this.r.getY2());
+	}
+
+	@Override
+	public void testToBoundingBox() {
+		Rectangle2f bb = this.r.toBoundingBox();
+		assertEpsilonEquals(0f, bb.getMinX());
+		assertEpsilonEquals(0f, bb.getMinY());
+		assertEpsilonEquals(1f, bb.getMaxX());
+		assertEpsilonEquals(1f, bb.getMaxY());
+	}
+
+	/**
+	 */
+	public void testContainsPoint2D() {
+		assertTrue(this.r.contains(new Point2f(0f, 0f)));
+		assertTrue(this.r.contains(new Point2f(.5f, .5f)));
+		assertTrue(this.r.contains(new Point2f(1f, 1f)));
+		
+		assertFalse(this.r.contains(new Point2f(2.3f, 4.5f)));
+
+		assertFalse(this.r.contains(new Point2f(2f, 2f)));
+	}
+
+	/**
+	 */
+	public void testContainsFloatFloat() {
+		assertTrue(this.r.contains(0f, 0f));
+		assertTrue(this.r.contains(.5f, .5f));
+		assertTrue(this.r.contains(1f, 1f));
+		
+		assertFalse(this.r.contains(2.3f, 4.5f));
+
+		assertFalse(this.r.contains(2f, 2f));
+	}
+
+	/**
+	 */
+	public void testGetClosestPointTo() {
+		Point2D p;
+		
+		p = this.r.getClosestPointTo(new Point2f(0f,0f));
+		assertEpsilonEquals(0f, p.getX());
+		assertEpsilonEquals(0f, p.getY());
+
+		p = this.r.getClosestPointTo(new Point2f(.5f,.5f));
+		assertEpsilonEquals(.5f, p.getX());
+		assertEpsilonEquals(.5f, p.getY());
+
+		p = this.r.getClosestPointTo(new Point2f(1f,1f));
+		assertEpsilonEquals(1f, p.getX());
+		assertEpsilonEquals(1f, p.getY());
+
+		p = this.r.getClosestPointTo(new Point2f(2f,2f));
+		assertEpsilonEquals(1f, p.getX());
+		assertEpsilonEquals(1f, p.getY());
+
+		p = this.r.getClosestPointTo(new Point2f(-2f,2f));
+		assertEpsilonEquals(0f, p.getX());
+		assertEpsilonEquals(0f, p.getY());
+
+		p = this.r.getClosestPointTo(new Point2f(0.1f,1.2f));
+		assertEpsilonEquals(0.65f, p.getX());
+		assertEpsilonEquals(0.65f, p.getY());
+
+		p = this.r.getClosestPointTo(new Point2f(10.1f,-.2f));
+		assertEpsilonEquals(1f, p.getX());
+		assertEpsilonEquals(1f, p.getY());
+	}
+
+	/**
+	 */
+	public void testgetPathIteratorVoid() {
+		PathIterator2f pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0f,0f);
+		assertElement(pi, PathElementType.LINE_TO, 1f,1f);
+		assertNoElement(pi);
+	}
+
+	/**
+	 */
+	public void testgetPathIteratorTransform2D() {
+		Transform2D tr;
+		PathIterator2f pi;
+		
+		tr = new Transform2D();
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 0f,0f);
+		assertElement(pi, PathElementType.LINE_TO, 1f,1f);
+		assertNoElement(pi);
+
+		tr = new Transform2D();
+		tr.makeTranslationMatrix(3.4f, 4.5f);
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 3.4f, 4.5f);
+		assertElement(pi, PathElementType.LINE_TO, 4.4f, 5.5f);
+		assertNoElement(pi);
+
+		tr = new Transform2D();
+		tr.makeRotationMatrix(MathConstants.QUARTER_PI);
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 0f, 0f);
+		assertElement(pi, PathElementType.LINE_TO, 0f, 1.414213562f);
+		assertNoElement(pi);
+	}
+
+	/**
+	 */
+    public void testCreateTransformedPathTransform2D() {
+    	Segment2f s;
+    	Transform2D tr;
+    	
+    	tr = new Transform2D();    	
+    	s = (Segment2f)this.r.createTransformedShape(tr);
+		assertEpsilonEquals(0f, s.getX1());
+		assertEpsilonEquals(0f, s.getY1());
+		assertEpsilonEquals(1f, s.getX2());
+		assertEpsilonEquals(1f, s.getY2());
+
+    	tr = new Transform2D();
+    	tr.setTranslation(3.4f, 4.5f);
+    	s = (Segment2f)this.r.createTransformedShape(tr);
+		assertEpsilonEquals(3.4f, s.getX1());
+		assertEpsilonEquals(4.5f, s.getY1());
+		assertEpsilonEquals(4.4f, s.getX2());
+		assertEpsilonEquals(5.5f, s.getY2());
+
+    	tr = new Transform2D();
+    	tr.setRotation(MathConstants.PI);
+    	s = (Segment2f)this.r.createTransformedShape(tr);
+		assertEpsilonEquals(0f, s.getX1());
+		assertEpsilonEquals(0f, s.getY1());
+		assertEpsilonEquals(-1f, s.getX2());
+		assertEpsilonEquals(-1f, s.getY2());
+
+    	tr = new Transform2D();
+    	tr.setRotation(MathConstants.QUARTER_PI);
+    	s = (Segment2f)this.r.createTransformedShape(tr);
+		assertEpsilonEquals(0f, s.getX1());
+		assertEpsilonEquals(0f, s.getY1());
+		assertEpsilonEquals(0f, s.getX2());
+		assertEpsilonEquals(1.414213562f, s.getY2());
+    }
+
+	/**
+	 */
+    public void testTransformTransform2D() {
+    	Segment2f s;
+    	Transform2D tr;
+    	
+    	tr = new Transform2D();
+    	s = this.r.clone();
+    	s.transform(tr);
+		assertEpsilonEquals(0f, s.getX1());
+		assertEpsilonEquals(0f, s.getY1());
+		assertEpsilonEquals(1f, s.getX2());
+		assertEpsilonEquals(1f, s.getY2());
+
+    	tr = new Transform2D();
+    	tr.setTranslation(3.4f, 4.5f);
+    	s = this.r.clone();
+    	s.transform(tr);
+		assertEpsilonEquals(3.4f, s.getX1());
+		assertEpsilonEquals(4.5f, s.getY1());
+		assertEpsilonEquals(4.4f, s.getX2());
+		assertEpsilonEquals(5.5f, s.getY2());
+
+    	tr = new Transform2D();
+    	tr.setRotation(MathConstants.PI);
+    	s = this.r.clone();
+    	s.transform(tr);
+		assertEpsilonEquals(0f, s.getX1());
+		assertEpsilonEquals(0f, s.getY1());
+		assertEpsilonEquals(-1f, s.getX2());
+		assertEpsilonEquals(-1f, s.getY2());
+
+    	tr = new Transform2D();
+    	tr.setRotation(MathConstants.QUARTER_PI);
+    	s = this.r.clone();
+    	s.transform(tr);
+		assertEpsilonEquals(0f, s.getX1());
+		assertEpsilonEquals(0f, s.getX2());
+		assertEpsilonEquals(1.414213562f, s.getY2());
+    }
+
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/AbstractRectangularShape2iTestCase.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/AbstractRectangularShape2iTestCase.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/AbstractRectangularShape2iTestCase.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,176 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import org.arakhne.afc.math.continous.object2d.Point2f;
+
+
+/**
+ * @param <T> is the type of the shape to test.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractRectangularShape2iTestCase<T extends AbstractRectangularShape2i<?>> extends AbstractShape2iTestCase<T> {
+	
+	/**
+	 */
+	public void testSetIntIntIntInt() {
+		this.r.set(2, 3, 4, 5);
+		assertEpsilonEquals(2, this.r.getMinX());
+		assertEpsilonEquals(3, this.r.getMinY());
+		assertEpsilonEquals(4, this.r.getWidth());
+		assertEpsilonEquals(5, this.r.getHeight());
+	}
+	
+	/**
+	 */
+	public void testSetPoint2DPoint2D() {
+		this.r.set(new Point2f(2.3f, 3.4f), new Point2f(4.5f, 5.6f));
+		assertEpsilonEquals(2, this.r.getMinX());
+		assertEpsilonEquals(3, this.r.getMinY());
+		assertEpsilonEquals(4, this.r.getMaxX());
+		assertEpsilonEquals(5, this.r.getMaxY());
+	}
+	
+	/**
+	 */
+	public void testSetRectangularShape2i() {
+		Rectangle2i rr = new Rectangle2i(2, 3, 4, 5);
+		this.r.set(rr);
+		assertEpsilonEquals(2, this.r.getMinX());
+		assertEpsilonEquals(3, this.r.getMinY());
+		assertEpsilonEquals(4, this.r.getWidth());
+		assertEpsilonEquals(5, this.r.getHeight());
+	}
+
+	/**
+	 */
+	public void testSetWidth() {
+		this.r.setWidth(2);
+		assertEpsilonEquals(0, this.r.getMinX());
+		assertEpsilonEquals(0, this.r.getMinY());
+		assertEpsilonEquals(2, this.r.getMaxX());
+		assertEpsilonEquals(1, this.r.getMaxY());
+		assertEpsilonEquals(2, this.r.getWidth());
+	}
+
+	/**
+	 */
+	public void testSetHeight() {
+		this.r.setHeight(2);
+		assertEpsilonEquals(0, this.r.getMinX());
+		assertEpsilonEquals(0, this.r.getMinY());
+		assertEpsilonEquals(1, this.r.getMaxX());
+		assertEpsilonEquals(2, this.r.getMaxY());
+		assertEpsilonEquals(2, this.r.getHeight());
+	}
+
+	/**
+	 */
+	public void testSetFromCornersPoint2DPoint2D() {
+		this.r.setFromCorners(new Point2f(2.3f, 3.4f), new Point2f(4.5f, 5.6f));
+		assertEpsilonEquals(2, this.r.getMinX());
+		assertEpsilonEquals(3, this.r.getMinY());
+		assertEpsilonEquals(4, this.r.getMaxX());
+		assertEpsilonEquals(5, this.r.getMaxY());
+	}
+
+	/**
+	 */
+	public void testSetFromCornersFloatFloatFloatFloat() {
+		this.r.setFromCorners(2, 3, 4, 5);
+		assertEpsilonEquals(2, this.r.getMinX());
+		assertEpsilonEquals(3, this.r.getMinY());
+		assertEpsilonEquals(4, this.r.getMaxX());
+		assertEpsilonEquals(5, this.r.getMaxY());
+	}
+	
+	/**
+	 */
+	public void testSetFromCenterFloatFloatFloatFloat() {
+		this.r.setFromCenter(2, 3, 4, 5);
+		//w = 4-2 = 2
+		//h = 5-3 = 2
+		assertEpsilonEquals(2-2, this.r.getMinX());
+		assertEpsilonEquals(3-2, this.r.getMinY());
+		assertEpsilonEquals(2+2, this.r.getMaxX());
+		assertEpsilonEquals(3+2, this.r.getMaxY());
+	}
+	
+	/**
+	 */
+	public void testSetMinX() {
+		this.r.setMinX(2);
+		assertEpsilonEquals(1, this.r.getMinX());
+		assertEpsilonEquals(2, this.r.getMaxX());
+		this.r.setMinX(-3);
+		assertEpsilonEquals(-3, this.r.getMinX());
+		assertEpsilonEquals(2, this.r.getMaxX());
+	}
+
+	/**
+	 */
+	public void testSetMaxX() {
+		this.r.setMaxX(2);
+		assertEpsilonEquals(0, this.r.getMinX());
+		assertEpsilonEquals(2, this.r.getMaxX());
+		this.r.setMaxX(-3);
+		assertEpsilonEquals(-3, this.r.getMinX());
+		assertEpsilonEquals(0, this.r.getMaxX());
+	}
+
+	/**
+	 */
+	public void testSetMinY() {
+		this.r.setMinY(2);
+		assertEpsilonEquals(1, this.r.getMinY());
+		assertEpsilonEquals(2, this.r.getMaxY());
+		this.r.setMinY(-3);
+		assertEpsilonEquals(-3, this.r.getMinY());
+		assertEpsilonEquals(2, this.r.getMaxY());
+	}
+
+	/**
+	 */
+	public void testSetMaxY() {
+		this.r.setMaxY(2);
+		assertEpsilonEquals(0, this.r.getMinY());
+		assertEpsilonEquals(2, this.r.getMaxY());
+		this.r.setMaxY(-3);
+		assertEpsilonEquals(-3, this.r.getMinY());
+		assertEpsilonEquals(0, this.r.getMaxY());
+	}
+	
+	/**
+	 */
+	@Override
+	public void testTranslateIntInt() {
+		this.r.translate(2, 3);
+		assertEpsilonEquals(2, this.r.getMinX());
+		assertEpsilonEquals(3, this.r.getMinY());
+		assertEpsilonEquals(3, this.r.getMaxX());
+		assertEpsilonEquals(4, this.r.getMaxY());
+	}
+	
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/AbstractShape2iTestCase.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/AbstractShape2iTestCase.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/AbstractShape2iTestCase.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,170 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import java.util.Arrays;
+
+import junit.framework.AssertionFailedError;
+
+import org.arakhne.afc.math.AbstractMathTestCase;
+import org.arakhne.afc.math.generic.PathElementType;
+
+/**
+ * @param <T> is the type of the shape to test
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractShape2iTestCase<T extends Shape2i> extends AbstractMathTestCase {
+	
+	/** Is the rectangular shape to test.
+	 */
+	protected T r;
+	
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		this.r = createShape();
+	}
+	
+	/** Create the shape to test.
+	 * 
+	 * @return the shape to test.
+	 */
+	protected abstract T createShape();
+	
+	@Override
+	protected void tearDown() throws Exception {
+		this.r = null;
+		super.tearDown();
+	}
+	
+	/** Assert is the given path iterator has a first element with the
+	 * given information.
+	 * 
+	 * @param pi
+	 * @param type
+	 * @param coords
+	 */
+	public void assertElement(PathIterator2i pi, PathElementType type, int... coords) {
+		if (!pi.hasNext()) {
+			fail("expected path element but the iterator is empty"); //$NON-NLS-1$
+		}
+		PathElement2i pe = pi.next();
+		if (!type.equals(pe.type)) {
+			fail("expected: "+type+"; actual: "+pe.type);  //$NON-NLS-1$//$NON-NLS-2$
+		}
+		int[] c = pe.toArray();
+		if (!isEquals(c, coords)) {
+			throw new AssertionFailedError("expected: "+Arrays.toString(coords)+"; actual: "+Arrays.toString(c));  //$NON-NLS-1$//$NON-NLS-2$
+		}
+	}
+	
+	/**
+	 * Replies if two arrays have the same values at epsilon.
+	 * 
+	 * @param a
+	 * @param b
+	 * @return <code>true</code> if the two arrays are equal, otherwise
+	 * <code>false</code>.
+	 */
+	public boolean isEpsilonEquals(float[] a, float[] b) {
+		if (a==b) return true;
+		if (a==null && b!=null) return false;
+		if (a!=null && b==null) return false;
+		assert(a!=null && b!=null);
+		if (a.length!=b.length) return false;
+		for(int i=0; i<a.length; ++i) {
+			if (!isEpsilonEquals(a[i], b[i])) return false;
+		}
+		return true;
+	}
+	
+	/**
+	 * Replies if two arrays have the same values at epsilon.
+	 * 
+	 * @param a
+	 * @param b
+	 * @return <code>true</code> if the two arrays are equal, otherwise
+	 * <code>false</code>.
+	 */
+	@SuppressWarnings("static-method")
+	public boolean isEquals(int[] a, int[] b) {
+		if (a==b) return true;
+		if (a==null && b!=null) return false;
+		if (a!=null && b==null) return false;
+		assert(a!=null && b!=null);
+		if (a.length!=b.length) return false;
+		for(int i=0; i<a.length; ++i) {
+			if (a[i]!=b[i]) return false;
+		}
+		return true;
+	}
+
+	/** Assert is the given path iterator has no element.
+	 * 
+	 * @param pi
+	 */
+	public static void assertNoElement(PathIterator2i pi) {
+		if (pi.hasNext()) {
+			fail("expected no path element but the iterator is not empty"); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 */
+	public abstract void testDistancePoint2D();
+
+	/**
+	 */
+	public abstract void testDistanceSquaredPoint2D();
+
+	/**
+	 */
+	public abstract void testDistanceL1Point2D();
+
+	/**
+	 */
+	public abstract void testDistanceLinfPoint2D();
+	
+	/**
+	 */
+	public abstract void testTranslateIntInt();
+	
+	/**
+	 */
+	public abstract void testToBoundingBox();
+
+	/**
+	 */
+	public abstract void testIsEmpty();
+
+	/**
+	 */
+	public abstract void testClone();
+
+	/**
+	 */
+	public abstract void testClear();
+
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/Circle2iTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/Circle2iTest.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/Circle2iTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,394 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2011 Janus Core Developers
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import java.util.Iterator;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.generic.PathElementType;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+/**
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Circle2iTest extends AbstractShape2iTestCase<Circle2i> {
+
+	@Override
+	protected Circle2i createShape() {
+		return new Circle2i(5, 8, 5);
+	}
+	
+	@Override
+	public void testIsEmpty() {
+		assertFalse(this.r.isEmpty());
+		this.r.clear();
+		assertTrue(this.r.isEmpty());
+	}
+
+	@Override
+	public void testClone() {
+		Circle2i b = this.r.clone();
+
+		assertNotSame(b, this.r);
+		assertEquals(this.r.getX(), b.getX());
+		assertEquals(this.r.getY(), b.getY());
+		assertEquals(this.r.getRadius(), b.getRadius());
+		
+		b.set(this.r.getX()+1, this.r.getY()+1,
+				this.r.getRadius()+1);
+
+		assertNotEquals(this.r.getX(), b.getX());
+		assertNotEquals(this.r.getY(), b.getY());
+		assertNotEquals(this.r.getRadius(), b.getRadius());
+	}
+
+	@Override
+	public void testClear() {
+		this.r.clear();
+		assertEquals(0, this.r.getX());
+		assertEquals(0, this.r.getY());
+		assertEquals(0, this.r.getRadius());
+	}
+
+	@Override
+	public void testTranslateIntInt() {
+		this.r.translate(3,  4);
+		assertEquals(8, this.r.getX());
+		assertEquals(12, this.r.getY());
+		assertEquals(5, this.r.getRadius());
+	}
+
+	/**
+	 */
+	@Override
+	public void testToBoundingBox() {
+		Rectangle2i r = this.r.toBoundingBox();
+		assertEquals(0, r.getMinX());
+		assertEquals(3, r.getMinY());
+		assertEquals(10, r.getMaxX());
+		assertEquals(13, r.getMaxY());
+	}
+
+	/**
+	 */
+	public void testGetPointIterator_big() {
+		Iterator<Point2i> iterator = this.r.getPointIterator();
+		Point2i p;
+		
+		int[] coords = new int[]{
+			// octant 1
+			5,13,
+			6,13,
+			7,13,
+			8,12,
+			// octant 2
+			10,8,
+			10,9,
+			10,10,
+			9,11,
+			// octant 3
+			5,3,
+			6,3,
+			7,3,
+			8,4,
+			// octant 4 (the first point is skipped because already returns in octant 2)
+			10,7,
+			10,6,
+			9,5,
+			// octant 5 (the first point is skipped because already returns in octant 4)
+			4,3,
+			3,3,
+			2,4,
+			// octant 6 
+			0,8,
+			0,7,
+			0,6,
+			1,5,
+			// octant 7 (the first point is skipped because already returns in octant 1)
+			4,13,
+			3,13,
+			2,12,
+			// octant 8 (the first point is skipped because already returns in octant 5)
+			0,9,
+			0,10,
+			1,11,
+		};
+		
+		for(int i=0; i<coords.length; i+=2) {
+			int x = coords[i];
+			int y = coords[i+1];
+			assertTrue("("+x+";"+y+")", iterator.hasNext()); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals("(>"+x+"<;"+y+")!=("+p.x()+";"+p.y()+")", x, p.x()); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$//$NON-NLS-5$
+			assertEquals("("+x+";>"+y+"<)!=("+p.x()+";"+p.y()+")", y, p.y()); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$//$NON-NLS-5$
+		}
+		
+		assertFalse(iterator.hasNext());
+	}
+
+	/**
+	 */
+	public static void testGetPointIterator_small() {
+		Circle2i circle = new Circle2i(4, 6, 3);
+		Iterator<Point2i> iterator = circle.getPointIterator();
+		Point2i p;
+		
+		int[] coords = new int[]{
+			// octant 1
+			4,9,
+			5,9,
+			6,8,
+			// octant 2
+			7,6,
+			7,7,
+			// 6,8, skipped because already returned
+			// octant 3
+			4,3,
+			5,3,
+			6,4,
+			// octant 4 (the first point is skipped because already returns in octant 2)
+			7,5,
+			// 6,4, skipped because already returned
+			// octant 5 (the first point is skipped because already returns in octant 4)
+			3,3,
+			2,4,
+			// octant 6 
+			1,6,
+			1,5,
+			// 2,4, skipped because already returned
+			// octant 7 (the first point is skipped because already returns in octant 1)
+			3,9,
+			2,8,
+			// octant 8 (the first point is skipped because already returns in octant 5)
+			1,7,
+			// 2,8, skipped because already returned
+		};
+		
+		for(int i=0; i<coords.length; i+=2) {
+			int x = coords[i];
+			int y = coords[i+1];
+			assertTrue("("+x+";"+y+")", iterator.hasNext()); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals("(>"+x+"<;"+y+")!=("+p.x()+";"+p.y()+")", x, p.x()); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$//$NON-NLS-5$
+			assertEquals("("+x+";>"+y+"<)!=("+p.x()+";"+p.y()+")", y, p.y()); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$//$NON-NLS-5$
+		}
+		
+		assertFalse(iterator.hasNext());
+	}
+
+	/**
+	 */
+	public void testGetClosestPointToPoint2D() {
+		Point2i p;
+		
+		p = this.r.getClosestPointTo(new Point2i(5,8));
+		assertNotNull(p);
+		assertEquals(5, p.x());
+		assertEquals(8, p.y());
+		
+		p = this.r.getClosestPointTo(new Point2i(10,10));
+		assertNotNull(p);
+		assertEquals(10, p.x());
+		assertEquals(10, p.y());
+		
+		p = this.r.getClosestPointTo(new Point2i(4,8));
+		assertNotNull(p);
+		assertEquals(4, p.x());
+		assertEquals(8, p.y());
+		
+		p = this.r.getClosestPointTo(new Point2i(0,0));
+		assertNotNull(p);
+		assertEquals(3, p.x());
+		assertEquals(3, p.y());
+
+		p = this.r.getClosestPointTo(new Point2i(5,14));
+		assertNotNull(p);
+		assertEquals(5, p.x());
+		assertEquals(13, p.y());
+	}
+
+	@Override
+	public void testDistancePoint2D() {
+		assertEpsilonEquals(0f, this.r.distance(new Point2i(5,8)));
+		assertEpsilonEquals(0f, this.r.distance(new Point2i(10,10)));
+		assertEpsilonEquals(0f, this.r.distance(new Point2i(4,8)));
+		assertEpsilonEquals(4.242640687f, this.r.distance(new Point2i(0,0)));
+		assertEpsilonEquals(1f, this.r.distance(new Point2i(5,14)));
+	}
+
+	/**
+	 */
+	@Override
+	public void testDistanceSquaredPoint2D() {
+		assertEpsilonEquals(0f, this.r.distanceSquared(new Point2i(5,8)));
+		assertEpsilonEquals(0f, this.r.distanceSquared(new Point2i(10,10)));
+		assertEpsilonEquals(0f, this.r.distanceSquared(new Point2i(4,8)));
+		assertEpsilonEquals(18f, this.r.distanceSquared(new Point2i(0,0)));
+		assertEpsilonEquals(1f, this.r.distanceSquared(new Point2i(5,14)));
+	}
+
+	/**
+	 */
+	@Override
+	public void testDistanceL1Point2D() {
+		assertEpsilonEquals(0f, this.r.distanceL1(new Point2i(5,8)));
+		assertEpsilonEquals(0f, this.r.distanceL1(new Point2i(10,10)));
+		assertEpsilonEquals(0f, this.r.distanceL1(new Point2i(4,8)));
+		assertEpsilonEquals(6f, this.r.distanceL1(new Point2i(0,0)));
+		assertEpsilonEquals(1f, this.r.distanceL1(new Point2i(5,14)));
+	}
+	
+	/**
+	 */
+	@Override
+	public void testDistanceLinfPoint2D() {
+		assertEpsilonEquals(0f, this.r.distanceLinf(new Point2i(5,8)));
+		assertEpsilonEquals(0f, this.r.distanceLinf(new Point2i(10,10)));
+		assertEpsilonEquals(0f, this.r.distanceLinf(new Point2i(4,8)));
+		assertEpsilonEquals(3f, this.r.distanceLinf(new Point2i(0,0)));
+		assertEpsilonEquals(1f, this.r.distanceLinf(new Point2i(5,14)));
+	}
+	
+	/**
+	 */
+	public void testContainsIntInt() {
+		assertFalse(this.r.contains(0,0));
+		assertFalse(this.r.contains(11,10));
+		assertFalse(this.r.contains(11,50));
+		assertFalse(this.r.contains(9,12));
+		assertTrue(this.r.contains(9,11));
+		assertTrue(this.r.contains(8,12));
+		assertTrue(this.r.contains(3,7));
+		assertFalse(this.r.contains(10,11));
+		assertTrue(this.r.contains(9,10));
+		
+		this.r = new Circle2i(-1,-1,1);
+		assertFalse(this.r.contains(0,0));
+	}
+
+	/**
+	 */
+	public void testContainsRectangle2i() {
+		assertFalse(this.r.contains(new Rectangle2i(0,0,1,1)));
+		assertFalse(this.r.contains(new Rectangle2i(0,0,8,1)));
+		assertFalse(this.r.contains(new Rectangle2i(0,0,8,6)));
+		assertFalse(this.r.contains(new Rectangle2i(0,0,100,100)));
+		assertTrue(this.r.contains(new Rectangle2i(7,10,1,1)));
+		assertFalse(this.r.contains(new Rectangle2i(16,0,100,100)));
+		assertFalse(this.r.contains(new Rectangle2i(9,11,5,5)));
+	}
+
+	/**
+	 */
+	public void testIntersectsRectangle2i() {
+		assertFalse(this.r.intersects(new Rectangle2i(0,0,1,1)));
+		assertFalse(this.r.intersects(new Rectangle2i(0,0,8,1)));
+		assertTrue(this.r.intersects(new Rectangle2i(0,0,8,6)));
+		assertTrue(this.r.intersects(new Rectangle2i(0,0,100,100)));
+		assertTrue(this.r.intersects(new Rectangle2i(7,10,1,1)));
+		assertFalse(this.r.intersects(new Rectangle2i(16,0,100,100)));
+	}
+
+	/**
+	 */
+	public void testIntersectsSegment2i() {
+		assertFalse(this.r.intersects(new Segment2i(0,0,1,1)));
+		assertFalse(this.r.intersects(new Segment2i(0,0,8,1)));
+		assertTrue(this.r.intersects(new Segment2i(0,0,8,6)));
+		assertTrue(this.r.intersects(new Segment2i(0,0,100,100)));
+		assertTrue(this.r.intersects(new Segment2i(7,10,1,1)));
+		assertFalse(this.r.intersects(new Segment2i(16,0,100,100)));
+		assertFalse(this.r.intersects(new Segment2i(8,13,10,11)));
+	}
+
+	/**
+	 */
+	public void testIntersectsCircle2i() {
+		assertFalse(this.r.intersects(new Circle2i(0,0,1)));
+		assertTrue(this.r.intersects(new Circle2i(0,0,8)));
+		assertTrue(this.r.intersects(new Circle2i(0,0,100)));
+		assertTrue(this.r.intersects(new Circle2i(7,10,1)));
+		assertFalse(this.r.intersects(new Circle2i(16,0,5)));
+		assertFalse(this.r.intersects(new Circle2i(5,15,1)));
+	}
+
+	/**
+	 */
+	public void testGetPathIteratorVoid() {
+		PathIterator2i pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 10,8);
+		assertElement(pi, PathElementType.CURVE_TO, 10,10, 7,13, 5,13);
+		assertElement(pi, PathElementType.CURVE_TO, 2,13, 0,10, 0,8);
+		assertElement(pi, PathElementType.CURVE_TO, 0,5, 2,3, 5,3);
+		assertElement(pi, PathElementType.CURVE_TO, 7,3, 10,5, 10,8);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/**
+	 */
+	public void testGetPathIteratorTransform2D() {
+		Transform2D tr;
+		PathIterator2i pi;
+		
+		tr = new Transform2D();
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 10,8);
+		assertElement(pi, PathElementType.CURVE_TO, 10,10, 7,13, 5,13);
+		assertElement(pi, PathElementType.CURVE_TO, 2,13, 0,10, 0,8);
+		assertElement(pi, PathElementType.CURVE_TO, 0,5, 2,3, 5,3);
+		assertElement(pi, PathElementType.CURVE_TO, 7,3, 10,5, 10,8);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		tr = new Transform2D();
+		tr.makeTranslationMatrix(3.4f, 4.5f);
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 13,12);
+		assertElement(pi, PathElementType.CURVE_TO, 13,14, 10,17, 8,17);
+		assertElement(pi, PathElementType.CURVE_TO, 5,17, 3,14, 3,12);
+		assertElement(pi, PathElementType.CURVE_TO, 3,9, 5,7, 8,7);
+		assertElement(pi, PathElementType.CURVE_TO, 10,7, 13,9, 13,12);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		tr = new Transform2D();
+		tr.makeRotationMatrix(MathConstants.QUARTER_PI);
+		
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 1,12);
+		assertElement(pi, PathElementType.CURVE_TO, 0,14, -4,14, -5,12);
+		assertElement(pi, PathElementType.CURVE_TO, -7,10, -7,7, -5,5);
+		assertElement(pi, PathElementType.CURVE_TO, -3,3, 0,3, 1,5);
+		assertElement(pi, PathElementType.CURVE_TO, 2,7, 3,10, 1,12);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/Path2iTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/Path2iTest.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/Path2iTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,949 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import java.util.Iterator;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.generic.PathElementType;
+import org.arakhne.afc.math.generic.PathWindingRule;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+/**
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Path2iTest extends AbstractShape2iTestCase<Path2i> {
+	
+	@Override
+	protected Path2i createShape() {
+		Path2i p = new Path2i();
+		p.moveTo(0, 0);
+		p.lineTo(2, 2);
+		p.quadTo(3, 0, 4, 3);
+		p.curveTo(5, -1, 6, 5, 7, -5);
+		p.closePath();
+		return p;
+	}
+	
+	@Override
+	public void testIsEmpty() {
+		assertFalse(this.r.isEmpty());
+
+		this.r.clear();
+		assertTrue(this.r.isEmpty());
+		
+		this.r.moveTo(1, 2);
+		assertTrue(this.r.isEmpty());
+		this.r.moveTo(3, 4);
+		assertTrue(this.r.isEmpty());
+		this.r.lineTo(5, 6);
+		assertFalse(this.r.isEmpty());
+		this.r.closePath();
+		assertFalse(this.r.isEmpty());
+
+		this.r.clear();
+		assertTrue(this.r.isEmpty());
+
+		this.r.moveTo(1, 2);
+		assertTrue(this.r.isEmpty());
+		this.r.moveTo(3, 4);
+		assertTrue(this.r.isEmpty());
+		this.r.lineTo(3, 4);
+		assertTrue(this.r.isEmpty());
+		this.r.closePath();
+		assertTrue(this.r.isEmpty());
+
+		this.r.clear();
+		assertTrue(this.r.isEmpty());
+
+		this.r.moveTo(1, 2);
+		assertTrue(this.r.isEmpty());
+		this.r.moveTo(3, 4);
+		assertTrue(this.r.isEmpty());
+		this.r.lineTo(3, 4);
+		assertTrue(this.r.isEmpty());
+		this.r.lineTo(5, 6);
+		assertFalse(this.r.isEmpty());
+	}
+
+	@Override
+	public void testClear() {
+		this.r.clear();
+		assertEquals(0, this.r.size());
+	}
+
+	@Override
+	public void testClone() {
+		Path2i b = this.r.clone();
+
+		assertNotSame(b, this.r);
+		PathElement2i pe1, pe2;
+		PathIterator2i i1 = this.r.getPathIterator();
+		PathIterator2i i2 = b.getPathIterator();
+		while (i1.hasNext()) {
+			assertTrue(i2.hasNext());
+			pe1 = i1.next();
+			pe2 = i2.next();
+			assertEquals(pe1.type, pe2.type);
+			assertEquals(pe1.fromX, pe2.fromX);
+			assertEquals(pe1.fromY, pe2.fromY);
+			assertEquals(pe1.ctrlX1, pe2.ctrlX1);
+			assertEquals(pe1.ctrlY1, pe2.ctrlY1);
+			assertEquals(pe1.ctrlX2, pe2.ctrlX2);
+			assertEquals(pe1.ctrlY2, pe2.ctrlY2);
+			assertEquals(pe1.toX, pe2.toX);
+			assertEquals(pe1.toY, pe2.toY);
+			
+		}
+		assertFalse(i2.hasNext());
+		
+		b.translate(1, 1);
+
+		i1 = this.r.getPathIterator();
+		i2 = b.getPathIterator();
+		boolean first = true;
+		while (i1.hasNext()) {
+			assertTrue(i2.hasNext());
+			pe1 = i1.next();
+			pe2 = i2.next();
+			assertEquals(pe1.type, pe2.type);
+			if (pe1.type!=PathElementType.MOVE_TO || !first) {
+				assertNotEquals(pe1.fromX, pe2.fromX);
+				assertNotEquals(pe1.fromY, pe2.fromY);
+			}
+			
+			if (pe1.type==PathElementType.CURVE_TO || pe1.type==PathElementType.QUAD_TO) {
+				assertNotEquals(pe1.ctrlX1, pe2.ctrlX1);
+				assertNotEquals(pe1.ctrlY1, pe2.ctrlY1);
+				if (pe1.type==PathElementType.CURVE_TO) {
+					assertNotEquals(pe1.ctrlX2, pe2.ctrlX2);
+					assertNotEquals(pe1.ctrlY2, pe2.ctrlY2);
+				}
+			}
+			
+			assertNotEquals(pe1.toX, pe2.toX);
+			assertNotEquals(pe1.toY, pe2.toY);
+			first = false;
+		}
+		assertFalse(i2.hasNext());
+	}
+
+	/*private static int toX(float x) {
+		return (int)(x * 100) + 20;
+	}
+	
+	private static int toY(float y) {
+		return (int)(y * 100) + 600;
+	}
+
+	private static int toS(float s) {
+		return (int)(s * 100);
+	}
+
+	public void testFlattening() throws IOException {
+		BufferedImage img = new BufferedImage(800, 1000, BufferedImage.TYPE_INT_ARGB);
+		Graphics2D g = (Graphics2D)img.getGraphics();
+		g.setColor(Color.WHITE);
+		g.fillRect(0, 0, 800, 1000);
+		g.setColor(Color.LIGHT_GRAY);
+		for(float y=-6; y<=4; y+=1) {
+			if (y!=0) g.drawLine(toX(-10), toY(y), toX(10), toY(y));
+		}
+		for(float x=-1; x<=8; x+=1) {
+			if (x!=0) g.drawLine(toX(x), toY(-10), toX(x), toY(10));
+		}
+		g.setColor(Color.BLACK);
+		g.drawLine(toX(-10), toY(0), toX(10), toY(0));
+		g.drawLine(toX(0), toY(-10), toX(0), toY(10));
+		
+		g.setColor(Color.ORANGE);
+		Segment2i[] ells = new Segment2i[] {
+				new Segment2i(0, 0, 1, 1),
+				new Segment2i(4, 3, 1, 1),
+				new Segment2i(2, 2, 1, 1),
+				new Segment2i(2, 1, 1, 1),
+				new Segment2i(3, 0, 1, 1),
+				new Segment2i(-1, -1, 1, 1),
+				new Segment2i(4, -3, 1, 1),
+				new Segment2i(-3, 4, 1, 1),
+				new Segment2i(6, -5, 1, 1),
+				new Segment2i(4, 0, 1, 1),
+				new Segment2i(5, 0, 1, 1),
+				new Segment2i(.01, .01, 1, 1),
+		};
+		for(Segment2i ell : ells) {
+			g.drawLine(
+					toX(ell.getX1()), toY(ell.getY1()),
+					toX(ell.getX2()), toY(ell.getY2()));
+		}
+		
+		g.setColor(Color.GREEN);
+		
+		Segment2i[] segs = new Segment2i[] {
+//				new Segment2i(-1, -5, 5, -4),
+//				new Segment2i(-5, -1, -7, 3),
+//				new Segment2i(10, -1, 11, -2),
+//				new Segment2i(1, 6, 3, 5),
+//				new Segment2i(6, -1, 5, .5),
+//				new Segment2i(6, -1, 5, 2),
+//				new Segment2i(6, .5, 5, 2),
+//				new Segment2i(3, 0, .5, .5),
+//				new Segment2i(3, 0, 0, 2),
+//				new Segment2i(.5, -1, .5, 4),
+//				new Segment2i(3, 0, 1, 3),
+		};
+		for(Segment2i seg : segs) {
+			g.drawLine(toX(seg.getX1()), toY(seg.getY1()), toX(seg.getX2()), toY(seg.getY2()));
+		}
+		
+		PathIterator2i pi = this.r.getPathIterator(.5);
+		while (pi.hasNext()) {
+			PathElement2f pe = pi.next();
+			switch(pe.type) {
+			case LINE_TO:
+				g.drawLine(
+						toX(pe.fromX), toY(pe.fromY),
+						toX(pe.toX), toY(pe.toY));
+				break;
+			case QUAD_TO:
+				g.draw( new QuadCurve2D.Float(
+						toX(pe.fromX), toY(pe.fromY),
+						toX(pe.ctrlX1), toY(pe.ctrlY1),
+						toX(pe.toX), toY(pe.toY)));
+				break;
+			case CURVE_TO:
+				g.draw( new CubicCurve2D.Float(
+						toX(pe.fromX), toY(pe.fromY),
+						toX(pe.ctrlX1), toY(pe.ctrlY1),
+						toX(pe.ctrlX2), toY(pe.ctrlY2),
+						toX(pe.toX), toY(pe.toY)));
+				break;
+			case CLOSE:
+				g.drawLine(
+						toX(pe.fromX), toY(pe.fromY),
+						toX(pe.toX), toY(pe.toY));
+				break;
+			default:
+			}
+		}
+
+		g.setColor(Color.RED);
+		pi = this.r.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
+		while (pi.hasNext()) {
+			PathElement2f pe = pi.next();
+			switch(pe.type) {
+			case LINE_TO:
+				g.drawLine(
+						toX(pe.fromX), toY(pe.fromY),
+						toX(pe.toX), toY(pe.toY));
+				break;
+			case CLOSE:
+				g.drawLine(
+						toX(pe.fromX), toY(pe.fromY),
+						toX(pe.toX), toY(pe.toY));
+				break;
+			default:
+			}
+		}
+		g.dispose();
+		ImageIO.write(img, "png", new File("/home/sgalland/mytest.png"));
+	}*/
+	
+	/**
+	 */
+	public void testGetPointIterator() {
+		Point2i p;
+		Iterator<Point2i> it = this.r.getPointIterator();
+		
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 0, p.x());
+		assertEquals(p.toString(), 0, p.y());
+
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 1, p.x());
+		assertEquals(p.toString(), 1, p.y());
+
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 2, p.x());
+		assertEquals(p.toString(), 2, p.y());
+
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 3, p.x());
+		assertEquals(p.toString(), 1, p.y());
+
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 4, p.x());
+		assertEquals(p.toString(), 2, p.y());
+
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 4, p.x());
+		assertEquals(p.toString(), 3, p.y());
+
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 4, p.x());
+		assertEquals(p.toString(), 2, p.y());
+
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 5, p.x());
+		assertEquals(p.toString(), 2, p.y());
+
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 5, p.x());
+		assertEquals(p.toString(), 1, p.y());
+
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 6, p.x());
+		assertEquals(p.toString(), 1, p.y());
+
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 6, p.x());
+		assertEquals(p.toString(), 0, p.y());
+
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 6, p.x());
+		assertEquals(p.toString(), -1, p.y());
+
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 7, p.x());
+		assertEquals(p.toString(), -1, p.y());
+
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 7, p.x());
+		assertEquals(p.toString(), -2, p.y());
+
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 7, p.x());
+		assertEquals(p.toString(), -3, p.y());
+
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 7, p.x());
+		assertEquals(p.toString(), -4, p.y());
+
+		assertTrue(it.hasNext());
+		p = it.next();
+		assertNotNull(p);
+		assertEquals(p.toString(), 7, p.x());
+		assertEquals(p.toString(), -5, p.y());
+
+		assertFalse(it.hasNext());
+	}
+	
+	/**
+	 */
+	public void testComputeCrossingsFromPoint() {
+		assertEquals(0, Path2i.computeCrossingsFromPoint(this.r.getPathIterator(), -2, 1));
+		assertEquals(0, Path2i.computeCrossingsFromPoint(this.r.getPathIterator(), 0, -3));
+		assertEquals(MathConstants.SHAPE_INTERSECTS,
+				Path2i.computeCrossingsFromPoint(this.r.getPathIterator(), 4, 3));
+		assertEquals(-2, Path2i.computeCrossingsFromPoint(this.r.getPathIterator(), 3, 0));
+	}
+
+	/**
+	 */
+	public void testComputeCrossingsFromRect() {
+		assertEquals(0, Path2i.computeCrossingsFromRect(
+				this.r.getPathIterator(),
+				-2, 1, -1, 2));
+		assertEquals(MathConstants.SHAPE_INTERSECTS, Path2i.computeCrossingsFromRect(
+				this.r.getPathIterator(),
+				0, 1, 3, 6));
+		assertEquals(MathConstants.SHAPE_INTERSECTS, Path2i.computeCrossingsFromRect(
+				this.r.getPathIterator(),
+				3, -1, 8, 0));
+		assertEquals(-2, Path2i.computeCrossingsFromRect(
+				this.r.getPathIterator(),
+				3, -1, 4, 0));
+		assertEquals(-2, Path2i.computeCrossingsFromRect(
+				this.r.getPathIterator(),
+				3, -1, 5, 0));
+		assertEquals(0, Path2i.computeCrossingsFromRect(
+				this.r.getPathIterator(),
+				0, -4, 3, -3));
+		assertEquals(MathConstants.SHAPE_INTERSECTS, Path2i.computeCrossingsFromRect(
+				this.r.getPathIterator(),
+				0, -4, 4, -3));
+		assertEquals(MathConstants.SHAPE_INTERSECTS, Path2i.computeCrossingsFromRect(
+				this.r.getPathIterator(),
+				0, -4, 3, -2));
+	}
+
+	/**
+	 */
+	public void testComputeCrossingsFromSegment() {
+		assertEquals(0, Path2i.computeCrossingsFromSegment(
+				this.r.getPathIterator(),
+				-2, 1, -1, 2));
+		assertEquals(0, Path2i.computeCrossingsFromSegment(
+				this.r.getPathIterator(),
+				0, 1, 3, 6));
+		assertEquals(MathConstants.SHAPE_INTERSECTS, Path2i.computeCrossingsFromSegment(
+				this.r.getPathIterator(),
+				3, -1, 8, 0));
+		assertEquals(-2, Path2i.computeCrossingsFromSegment(
+				this.r.getPathIterator(),
+				3, -1, 4, 0));
+		assertEquals(-2, Path2i.computeCrossingsFromSegment(
+				this.r.getPathIterator(),
+				3, -1, 5, 0));
+		assertEquals(0, Path2i.computeCrossingsFromSegment(
+				this.r.getPathIterator(),
+				0, -4, 3, -3));
+		assertEquals(MathConstants.SHAPE_INTERSECTS, Path2i.computeCrossingsFromSegment(
+				this.r.getPathIterator(),
+				0, -4, 4, -3));
+		assertEquals(MathConstants.SHAPE_INTERSECTS, Path2i.computeCrossingsFromSegment(
+				this.r.getPathIterator(),
+				0, -4, 3, -2));
+	}
+	
+	/**
+	 */
+	public void testComputeCrossingsFromCircle() {
+		assertEquals(0, Path2i.computeCrossingsFromCircle(
+				this.r.getPathIterator(),
+				-2, 1, 1));
+		assertEquals(MathConstants.SHAPE_INTERSECTS, Path2i.computeCrossingsFromCircle(
+				this.r.getPathIterator(),
+				-2, 1, 2));
+		assertEquals(MathConstants.SHAPE_INTERSECTS, Path2i.computeCrossingsFromCircle(
+				this.r.getPathIterator(),
+				0, 1, 3));
+		assertEquals(MathConstants.SHAPE_INTERSECTS, Path2i.computeCrossingsFromCircle(
+				this.r.getPathIterator(),
+				3, -1, 8));
+		assertEquals(MathConstants.SHAPE_INTERSECTS, Path2i.computeCrossingsFromCircle(
+				this.r.getPathIterator(),
+				3, -1, 1));
+		assertEquals(-2, Path2i.computeCrossingsFromCircle(
+				this.r.getPathIterator(),
+				4, -1, 0));
+		assertEquals(0, Path2i.computeCrossingsFromCircle(
+				this.r.getPathIterator(),
+				20, 0, 2));
+	}
+
+	/**
+	 */
+	public void testContainsPathIterator2iIntInt() {
+		assertTrue(Path2i.contains(this.r.getPathIterator(), 0, 0));
+		assertTrue(Path2i.contains(this.r.getPathIterator(), 4, 3));
+		assertTrue(Path2i.contains(this.r.getPathIterator(), 2, 2));
+		assertTrue(Path2i.contains(this.r.getPathIterator(), 2, 1));
+		assertTrue(Path2i.contains(this.r.getPathIterator(), 4, 2));
+		assertTrue(Path2i.contains(this.r.getPathIterator(), 4, 3));
+		assertFalse(Path2i.contains(this.r.getPathIterator(), -1, -1));
+		assertFalse(Path2i.contains(this.r.getPathIterator(), 6, 2));
+		assertTrue(Path2i.contains(this.r.getPathIterator(), 3, -2));
+		assertFalse(Path2i.contains(this.r.getPathIterator(), 2, -2));
+	}
+
+	/**
+	 */
+	public void testIntersectsPathIterator2iIntIntIntInt() {
+		assertTrue(Path2i.intersects(this.r.getPathIterator(), 0, 0, 1, 1));
+		assertTrue(Path2i.intersects(this.r.getPathIterator(), 4, 3, 1, 1));
+		assertTrue(Path2i.intersects(this.r.getPathIterator(), 2, 2, 1, 1));
+		assertTrue(Path2i.intersects(this.r.getPathIterator(), 2, 1, 1, 1));
+		assertTrue(Path2i.intersects(this.r.getPathIterator(), 3, 0, 1, 1));
+		assertTrue(Path2i.intersects(this.r.getPathIterator(), -1, -1, 1, 1));
+		assertTrue(Path2i.intersects(this.r.getPathIterator(), 4, -3, 1, 1));
+		assertFalse(Path2i.intersects(this.r.getPathIterator(), -3, 4, 1, 1));
+		assertTrue(Path2i.intersects(this.r.getPathIterator(), 6, -5, 1, 1));
+		assertTrue(Path2i.intersects(this.r.getPathIterator(), 4, 0, 1, 1));
+		assertTrue(Path2i.intersects(this.r.getPathIterator(), 5, 0, 1, 1));
+		assertFalse(Path2i.intersects(this.r.getPathIterator(), 0, -3, 1, 1));
+		assertFalse(Path2i.intersects(this.r.getPathIterator(), 0, -3, 2, 1));
+	}
+	
+	/**
+	 */
+	public void testGetClosestPointTo() {
+		Point2D p;
+		
+		p = this.r.getClosestPointTo(new Point2i(0, 0));
+		assertEquals(p.toString(), 0, p.x());
+		assertEquals(p.toString(), 0, p.y());
+
+		p = this.r.getClosestPointTo(new Point2i(-1, -4)); // remeber: path is closed
+		assertEquals(p.toString(), 1, p.x());
+		assertEquals(p.toString(), -1, p.y());
+
+		p = this.r.getClosestPointTo(new Point2i(4, 0));
+		assertEquals(p.toString(), 4, p.x());
+		assertEquals(p.toString(), 0, p.y());
+
+		p = this.r.getClosestPointTo(new Point2i(4, 2));
+		assertEquals(p.toString(), 4, p.x());
+		assertEquals(p.toString(), 2, p.y());
+
+		p = this.r.getClosestPointTo(new Point2i(4, -1));
+		assertEquals(p.toString(), 4, p.x());
+		assertEquals(p.toString(), -1, p.y());
+	}
+	
+
+	@Override
+	public void testDistancePoint2D() {
+		assertEpsilonEquals(0f, this.r.distance(new Point2i(0, 0)));
+		assertEpsilonEquals(0f, this.r.distance(new Point2i(1, 0)));
+		assertEpsilonEquals(7.071067812f, this.r.distance(new Point2i(-5, -5)));
+		assertEpsilonEquals(3f, this.r.distance(new Point2i(4, 6)));
+		assertEpsilonEquals(1f, this.r.distance(new Point2i(7, 0)));
+	}
+
+	@Override
+	public void testDistanceSquaredPoint2D() {
+		assertEpsilonEquals(0f, this.r.distanceSquared(new Point2i(0, 0)));
+		assertEpsilonEquals(0f, this.r.distanceSquared(new Point2i(1, 0)));
+		assertEpsilonEquals(50f, this.r.distanceSquared(new Point2i(-5, -5)));
+		assertEpsilonEquals(9f, this.r.distanceSquared(new Point2i(4, 6)));
+		assertEpsilonEquals(1f, this.r.distanceSquared(new Point2i(7, 0)));
+	}
+
+	@Override
+	public void testDistanceL1Point2D() {
+		assertEpsilonEquals(0f, this.r.distanceL1(new Point2i(0, 0)));
+		assertEpsilonEquals(0f, this.r.distanceL1(new Point2i(1, 0)));
+		assertEpsilonEquals(10f, this.r.distanceL1(new Point2i(-5, -5)));
+		assertEpsilonEquals(3f, this.r.distanceL1(new Point2i(4, 6)));
+		assertEpsilonEquals(1f, this.r.distanceL1(new Point2i(7, 0)));
+	}
+
+	@Override
+	public void testDistanceLinfPoint2D() {
+		assertEpsilonEquals(0f, this.r.distanceLinf(new Point2i(0, 0)));
+		assertEpsilonEquals(0f, this.r.distanceLinf(new Point2i(1, 0)));
+		assertEpsilonEquals(5f, this.r.distanceLinf(new Point2i(-5, -5)));
+		assertEpsilonEquals(3f, this.r.distanceLinf(new Point2i(4, 6)));
+		assertEpsilonEquals(1f, this.r.distanceLinf(new Point2i(7, 0)));
+	}
+
+	@Override
+	public void testTranslateIntInt() {
+		this.r.translate(3, 4);
+		PathIterator2i pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 3, 4);
+		assertElement(pi, PathElementType.LINE_TO, 5, 6);
+		assertElement(pi, PathElementType.QUAD_TO, 6, 4, 7, 7);
+		assertElement(pi, PathElementType.CURVE_TO, 8, 3, 9, 9, 10, -1);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/**
+	 */
+	public void testSetWindingRule() {
+		assertEquals(PathWindingRule.NON_ZERO, this.r.getWindingRule());
+		for(PathWindingRule rule : PathWindingRule.values()) {
+			this.r.setWindingRule(rule);
+			assertEquals(rule, this.r.getWindingRule());
+		}
+	}
+
+	/**
+	 */
+	public void testAddIterator() {
+		Path2i p2 = new Path2i();
+		p2.moveTo(3, 4);
+		p2.lineTo(5, 6);
+		
+		this.r.add(p2.getPathIterator());
+		
+		PathIterator2i pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.LINE_TO, 2, 2);
+		assertElement(pi, PathElementType.QUAD_TO, 3, 0, 4, 3);
+		assertElement(pi, PathElementType.CURVE_TO, 5, -1, 6, 5, 7, -5);
+		assertElement(pi, PathElementType.CLOSE);
+		assertElement(pi, PathElementType.MOVE_TO, 3, 4);
+		assertElement(pi, PathElementType.LINE_TO, 5, 6);
+		assertNoElement(pi);
+	}
+
+	/** 
+	 */
+	public void testGetPathIteratorVoid() {
+		PathIterator2i pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.LINE_TO, 2, 2);
+		assertElement(pi, PathElementType.QUAD_TO, 3, 0, 4, 3);
+		assertElement(pi, PathElementType.CURVE_TO, 5, -1, 6, 5, 7, -5);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/** 
+	 */
+	public void testGetPathIteratorTransform2D() {
+		Transform2D tr;
+		PathIterator2i pi;
+		
+		tr = new Transform2D();
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.LINE_TO, 2, 2);
+		assertElement(pi, PathElementType.QUAD_TO, 3, 0, 4, 3);
+		assertElement(pi, PathElementType.CURVE_TO, 5, -1, 6, 5, 7, -5);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		tr = new Transform2D();
+		tr.makeTranslationMatrix(3, 4);
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 3, 4);
+		assertElement(pi, PathElementType.LINE_TO, 5, 6);
+		assertElement(pi, PathElementType.QUAD_TO, 6, 4, 7, 7);
+		assertElement(pi, PathElementType.CURVE_TO, 8, 3, 9, 9, 10, -1);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/** 
+	 */
+	public void testTransformTransform2D() {
+		Transform2D tr = new Transform2D();
+		tr.makeTranslationMatrix(3, 4);
+		Transform2D tr2 = new Transform2D();
+		tr2.makeRotationMatrix(5.6f);
+		
+		Path2i clone = this.r.clone();
+		clone.transform(tr);
+
+		PathIterator2i pi = clone.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 3, 4);
+		assertElement(pi, PathElementType.LINE_TO, 5, 6);
+		assertElement(pi, PathElementType.QUAD_TO, 6, 4, 7, 7);
+		assertElement(pi, PathElementType.CURVE_TO, 8, 3, 9, 9, 10, -1);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		clone = this.r.clone();
+		clone.transform(tr2);
+
+		pi = clone.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.LINE_TO, 2, 0);
+		assertElement(pi, PathElementType.QUAD_TO, 2, -1, 4, -0);
+		assertElement(pi, PathElementType.CURVE_TO, 3, -3, 7, 0, 2, -8);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		Transform2D tr3 = new Transform2D();
+		tr3.mul(tr, tr2);
+		clone = this.r.clone();
+		clone.transform(tr3);
+
+		pi = clone.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 3, 4);
+		assertElement(pi, PathElementType.LINE_TO, 5, 4);
+		assertElement(pi, PathElementType.QUAD_TO, 5, 2, 7, 3);
+		assertElement(pi, PathElementType.CURVE_TO, 6, 0, 10, 4, 5, -4);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/** 
+	 */
+	public void testCreateTransformedShape2D() {
+		Transform2D tr = new Transform2D();
+		tr.makeTranslationMatrix(3, 4);
+		Path2i p2 = (Path2i)this.r.createTransformedShape(tr);
+
+		PathIterator2i pi = p2.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 3, 4);
+		assertElement(pi, PathElementType.LINE_TO, 5, 6);
+		assertElement(pi, PathElementType.QUAD_TO, 6, 4, 7, 7);
+		assertElement(pi, PathElementType.CURVE_TO, 8, 3, 9, 9, 10, -1);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/** 
+	 */
+	public void testContainsIntInt() {
+		assertTrue(this.r.contains(0, 0));
+		assertTrue(this.r.contains(4, 3));
+		assertTrue(this.r.contains(2, 2));
+		assertTrue(this.r.contains(2, 1));
+		assertTrue(this.r.contains(4, 2));
+		assertTrue(this.r.contains(4, 3));
+		assertFalse(this.r.contains(-1, -1));
+		assertFalse(this.r.contains(6, 2));
+		assertTrue(this.r.contains(3, -2));
+		assertFalse(this.r.contains(2, -2));
+	}
+
+	/** 
+	 */
+	public void testContainsRectangle2i() {
+		assertFalse(this.r.contains(new Rectangle2i(0, 0, 1, 1)));
+		assertFalse(this.r.contains(new Rectangle2i(4, 3, 1, 1)));
+		assertFalse(this.r.contains(new Rectangle2i(2, 2, 1, 1)));
+		assertFalse(this.r.contains(new Rectangle2i(2, 1, 1, 1)));
+		assertTrue(this.r.contains(new Rectangle2i(3, 0, 1, 1)));
+		assertTrue(this.r.contains(new Rectangle2i(3, 0, 2, 1)));
+		assertFalse(this.r.contains(new Rectangle2i(-1, -1, 1, 1)));
+		assertTrue(this.r.contains(new Rectangle2i(4, -3, 1, 1)));
+		assertFalse(this.r.contains(new Rectangle2i(-3, 4, 1, 1)));
+		assertFalse(this.r.contains(new Rectangle2i(6, -5, 1, 1)));
+		assertTrue(this.r.contains(new Rectangle2i(4, 0, 1, 1)));
+		assertTrue(this.r.contains(new Rectangle2i(5, 0, 1, 1)));
+		assertFalse(this.r.contains(new Rectangle2i(5, 0, 2, 1)));
+		assertFalse(this.r.contains(new Rectangle2i(6, 0, 1, 1)));
+	}
+
+	/** 
+	 */
+	public void testIntersectsRectangle2i() {
+		assertTrue(this.r.intersects(new Rectangle2i(0, 0, 1, 1)));
+		assertTrue(this.r.intersects(new Rectangle2i(4, 3, 1, 1)));
+		assertTrue(this.r.intersects(new Rectangle2i(2, 2, 1, 1)));
+		assertTrue(this.r.intersects(new Rectangle2i(2, 1, 1, 1)));
+		assertTrue(this.r.intersects(new Rectangle2i(3, 0, 1, 1)));
+		assertTrue(this.r.intersects(new Rectangle2i(-1, -1, 1, 1)));
+		assertTrue(this.r.intersects(new Rectangle2i(4, -3, 1, 1)));
+		assertFalse(this.r.intersects(new Rectangle2i(-3, 4, 1, 1)));
+		assertTrue(this.r.intersects(new Rectangle2i(6, -5, 1, 1)));
+		assertTrue(this.r.intersects(new Rectangle2i(4, 0, 1, 1)));
+		assertTrue(this.r.intersects(new Rectangle2i(5, 0, 1, 1)));
+		assertFalse(this.r.intersects(new Rectangle2i(0, -3, 1, 1)));
+		assertFalse(this.r.intersects(new Rectangle2i(0, -3, 2, 1)));
+	}
+
+	/** 
+	 */
+	public void testIntersectsCircle2i() {
+		assertTrue(this.r.intersects(new Circle2i(0, 0, 1)));
+		assertTrue(this.r.intersects(new Circle2i(4, 3, 1)));
+		assertTrue(this.r.intersects(new Circle2i(2, 2, 1)));
+		assertTrue(this.r.intersects(new Circle2i(2, 1, 1)));
+		assertTrue(this.r.intersects(new Circle2i(3, 0, 1)));
+		assertFalse(this.r.intersects(new Circle2i(-1, -1, 1)));
+		assertTrue(this.r.intersects(new Circle2i(4, -3, 1)));
+		assertFalse(this.r.intersects(new Circle2i(-3, 4, 1)));
+		assertTrue(this.r.intersects(new Circle2i(6, -5, 1)));
+		assertTrue(this.r.intersects(new Circle2i(4, 0, 1)));
+		assertTrue(this.r.intersects(new Circle2i(5, 0, 1)));
+		assertTrue(this.r.intersects(new Circle2i(6, 2, 1)));
+		assertFalse(this.r.intersects(new Circle2i(-5, 0, 3)));
+	}
+
+	/** 
+	 */
+	public void testIntersectsSegment2i() {
+		assertTrue(this.r.intersects(new Segment2i(0, 0, 1, 1)));
+		assertTrue(this.r.intersects(new Segment2i(4, 3, 1, 1)));
+		assertTrue(this.r.intersects(new Segment2i(2, 2, 1, 1)));
+		assertTrue(this.r.intersects(new Segment2i(2, 1, 1, 1)));
+		assertTrue(this.r.intersects(new Segment2i(3, 0, 1, 1)));
+		assertTrue(this.r.intersects(new Segment2i(-1, -1, 1, 1)));
+		assertTrue(this.r.intersects(new Segment2i(4, -3, 1, 1)));
+		assertTrue(this.r.intersects(new Segment2i(-3, 4, 1, 1)));
+		assertTrue(this.r.intersects(new Segment2i(6, -5, 1, 1)));
+		assertTrue(this.r.intersects(new Segment2i(4, 0, 1, 1)));
+		assertTrue(this.r.intersects(new Segment2i(5, 0, 1, 1)));
+		assertFalse(this.r.intersects(new Segment2i(-4, -4, -3, -3)));
+		assertFalse(this.r.intersects(new Segment2i(-1, 0, 2, 3)));
+		assertFalse(this.r.intersects(new Segment2i(7, 1, 18, 14)));
+	}
+
+	@Override
+	public void testToBoundingBox() {
+		Rectangle2i bb = this.r.toBoundingBox();
+		assertEquals(0, bb.getMinX());
+		assertEquals(-5, bb.getMinY());
+		assertEquals(7, bb.getMaxX());
+		assertEquals(5, bb.getMaxY());
+	}
+	
+	/**
+	 */
+	public void testRemoveLast() {
+		PathIterator2i pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.LINE_TO, 2, 2);
+		assertElement(pi, PathElementType.QUAD_TO, 3, 0, 4, 3);
+		assertElement(pi, PathElementType.CURVE_TO, 5, -1, 6, 5, 7, -5);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+		
+		this.r.removeLast();
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.LINE_TO, 2, 2);
+		assertElement(pi, PathElementType.QUAD_TO, 3, 0, 4, 3);
+		assertElement(pi, PathElementType.CURVE_TO, 5, -1, 6, 5, 7, -5);
+		assertNoElement(pi);
+
+		this.r.removeLast();
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.LINE_TO, 2, 2);
+		assertElement(pi, PathElementType.QUAD_TO, 3, 0, 4, 3);
+		assertNoElement(pi);
+
+		this.r.removeLast();
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.LINE_TO, 2, 2);
+		assertNoElement(pi);
+
+		this.r.removeLast();
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertNoElement(pi);
+
+		this.r.removeLast();
+
+		pi = this.r.getPathIterator();
+		assertNoElement(pi);
+
+		this.r.removeLast();
+
+		pi = this.r.getPathIterator();
+		assertNoElement(pi);
+	}
+
+	/**
+	 */
+	public void testSetLastPointIntInt() {
+		PathIterator2i pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.LINE_TO, 2, 2);
+		assertElement(pi, PathElementType.QUAD_TO, 3, 0, 4, 3);
+		assertElement(pi, PathElementType.CURVE_TO, 5, -1, 6, 5, 7, -5);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+		
+		this.r.setLastPoint(123, 789);
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.LINE_TO, 2, 2);
+		assertElement(pi, PathElementType.QUAD_TO, 3, 0, 4, 3);
+		assertElement(pi, PathElementType.CURVE_TO, 5, -1, 6, 5, 123, 789);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+	
+	/**
+	 */
+	public void testRemoveIntInt() {
+		PathIterator2i pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.LINE_TO, 2, 2);
+		assertElement(pi, PathElementType.QUAD_TO, 3, 0, 4, 3);
+		assertElement(pi, PathElementType.CURVE_TO, 5, -1, 6, 5, 7, -5);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+		
+		this.r.remove(2, 2);
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.QUAD_TO, 3, 0, 4, 3);
+		assertElement(pi, PathElementType.CURVE_TO, 5, -1, 6, 5, 7, -5);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		this.r.remove(4, 3);
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.CURVE_TO, 5, -1, 6, 5, 7, -5);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		this.r.remove(6, 5);
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		this.r.remove(6, 5);
+
+		pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/**
+	 */
+	public void testContainsPointPoint2D() {
+		PathIterator2i pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.LINE_TO, 2, 2);
+		assertElement(pi, PathElementType.QUAD_TO, 3, 0, 4, 3);
+		assertElement(pi, PathElementType.CURVE_TO, 5, -1, 6, 5, 7, -5);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+		
+		assertTrue(this.r.containsPoint(new Point2i(2, 2)));
+		assertFalse(this.r.containsPoint(new Point2i(4, 4)));
+		assertTrue(this.r.containsPoint(new Point2i(6, 5)));
+		assertFalse(this.r.containsPoint(new Point2i(-1, 6)));
+		assertFalse(this.r.containsPoint(new Point2i(1234, 5678)));
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/Rectangle2iTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/Rectangle2iTest.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/Rectangle2iTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,491 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2011 Janus Core Developers
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import java.util.Iterator;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.discrete.object2d.Rectangle2i.Side;
+import org.arakhne.afc.math.generic.PathElementType;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+
+/**
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Rectangle2iTest extends AbstractShape2iTestCase<Rectangle2i> {
+
+	@Override
+	protected Rectangle2i createShape() {
+		return new Rectangle2i(5, 8, 10, 5);
+	}
+	
+	@Override
+	public void testIsEmpty() {
+		assertFalse(this.r.isEmpty());
+		this.r.clear();
+		assertTrue(this.r.isEmpty());
+	}
+
+	@Override
+	public void testClone() {
+		Rectangle2i b = this.r.clone();
+
+		assertNotSame(b, this.r);
+		assertEquals(this.r.getMinX(), b.getMinX());
+		assertEquals(this.r.getMinY(), b.getMinY());
+		assertEquals(this.r.getMaxX(), b.getMaxX());
+		assertEquals(this.r.getMaxY(), b.getMaxY());
+		
+		b.set(this.r.getMinX()+1, this.r.getMinY()+1,
+				this.r.getWidth()+1, this.r.getHeight()+1);
+
+		assertNotEquals(this.r.getMinX(), b.getMinX());
+		assertNotEquals(this.r.getMinY(), b.getMinY());
+		assertNotEquals(this.r.getMaxX(), b.getMaxX());
+		assertNotEquals(this.r.getMaxY(), b.getMaxY());
+	}
+
+	@Override
+	public void testClear() {
+		this.r.clear();
+		assertEquals(0, this.r.getMinX());
+		assertEquals(0, this.r.getMinY());
+		assertEquals(0, this.r.getMaxX());
+		assertEquals(0, this.r.getMaxY());
+	}
+
+	@Override
+	public void testTranslateIntInt() {
+		this.r.translate(3,  4);
+		assertEquals(8, this.r.getMinX());
+		assertEquals(12, this.r.getMinY());
+		assertEquals(18, this.r.getMaxX());
+		assertEquals(17, this.r.getMaxY());
+	}
+
+	/**
+	 */
+	@Override
+	public void testToBoundingBox() {
+		Rectangle2i r = this.r.toBoundingBox();
+		assertSame(this.r, r);
+	}
+
+	/**
+	 */
+	public void testGetPointIterator() {
+		Iterator<Point2i> iterator = this.r.getPointIterator();
+		Point2i p;
+		
+		int[] coords;
+		
+		coords = new int[] {5,6,7,8,9,10,11,12,13,14,15};
+		for(int x : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(x, p.x());
+			assertEquals(8, p.y());
+		}
+
+		coords = new int[] {9,10,11,12,13};
+		for(int y : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(15, p.x());
+			assertEquals(y, p.y());
+		}
+
+		coords = new int[] {14,13,12,11,10,9,8,7,6,5};
+		for(int x : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(x, p.x());
+			assertEquals(13, p.y());
+		}
+
+		coords = new int[] {12,11,10,9};
+		for(int y : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(5, p.x());
+			assertEquals(y, p.y());
+		}
+
+		assertFalse(iterator.hasNext());
+	}
+
+	/**
+	 */
+	public void testGetPointIteratorSide_Top() {
+		Iterator<Point2i> iterator = this.r.getPointIterator(Side.TOP);
+		Point2i p;
+		
+		int[] coords;
+		
+		coords = new int[] {5,6,7,8,9,10,11,12,13,14,15};
+		for(int x : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(x, p.x());
+			assertEquals(8, p.y());
+		}
+
+		coords = new int[] {9,10,11,12,13};
+		for(int y : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(15, p.x());
+			assertEquals(y, p.y());
+		}
+
+		coords = new int[] {14,13,12,11,10,9,8,7,6,5};
+		for(int x : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(x, p.x());
+			assertEquals(13, p.y());
+		}
+
+		coords = new int[] {12,11,10,9};
+		for(int y : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(5, p.x());
+			assertEquals(y, p.y());
+		}
+
+		assertFalse(iterator.hasNext());
+	}
+
+	/**
+	 */
+	public void testGetPointIteratorSide_Right() {
+		Iterator<Point2i> iterator = this.r.getPointIterator(Side.RIGHT);
+		Point2i p;
+		
+		int[] coords;
+		
+		coords = new int[] {9,10,11,12,13};
+		for(int y : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(15, p.x());
+			assertEquals(y, p.y());
+		}
+
+		coords = new int[] {14,13,12,11,10,9,8,7,6,5};
+		for(int x : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(x, p.x());
+			assertEquals(13, p.y());
+		}
+
+		coords = new int[] {12,11,10,9};
+		for(int y : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(5, p.x());
+			assertEquals(y, p.y());
+		}
+
+		coords = new int[] {5,6,7,8,9,10,11,12,13,14,15};
+		for(int x : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(x, p.x());
+			assertEquals(8, p.y());
+		}
+
+		assertFalse(iterator.hasNext());
+	}
+
+	/**
+	 */
+	public void testGetPointIteratorSide_Bottom() {
+		Iterator<Point2i> iterator = this.r.getPointIterator(Side.BOTTOM);
+		Point2i p;
+		
+		int[] coords;
+		
+		coords = new int[] {14,13,12,11,10,9,8,7,6,5};
+		for(int x : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(x, p.x());
+			assertEquals(13, p.y());
+		}
+
+		coords = new int[] {12,11,10,9};
+		for(int y : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(5, p.x());
+			assertEquals(y, p.y());
+		}
+
+		coords = new int[] {5,6,7,8,9,10,11,12,13,14,15};
+		for(int x : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(x, p.x());
+			assertEquals(8, p.y());
+		}
+
+		coords = new int[] {9,10,11,12,13};
+		for(int y : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(15, p.x());
+			assertEquals(y, p.y());
+		}
+
+		assertFalse(iterator.hasNext());
+	}
+
+	/**
+	 */
+	public void testGetPointIteratorSide_Left() {
+		Iterator<Point2i> iterator = this.r.getPointIterator(Side.LEFT);
+		Point2i p;
+		
+		int[] coords;
+		
+		coords = new int[] {12,11,10,9};
+		for(int y : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(5, p.x());
+			assertEquals(y, p.y());
+		}
+
+		coords = new int[] {5,6,7,8,9,10,11,12,13,14,15};
+		for(int x : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(x, p.x());
+			assertEquals(8, p.y());
+		}
+
+		coords = new int[] {9,10,11,12,13};
+		for(int y : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(15, p.x());
+			assertEquals(y, p.y());
+		}
+
+		coords = new int[] {14,13,12,11,10,9,8,7,6,5};
+		for(int x : coords) {
+			assertTrue(iterator.hasNext());
+			p = iterator.next();
+			assertNotNull(p);
+			assertEquals(x, p.x());
+			assertEquals(13, p.y());
+		}
+
+		assertFalse(iterator.hasNext());
+	}
+
+	@Override
+	public void testDistancePoint2D() {
+		assertEpsilonEquals(0f, this.r.distance(new Point2i(5,8)));
+		assertEpsilonEquals(0f, this.r.distance(new Point2i(10,10)));
+		assertEpsilonEquals(1f, this.r.distance(new Point2i(4,8)));
+		assertEpsilonEquals(9.433981132f, this.r.distance(new Point2i(0,0)));
+	}
+
+	/**
+	 */
+	@Override
+	public void testDistanceSquaredPoint2D() {
+		assertEpsilonEquals(0f, this.r.distanceSquared(new Point2i(5,8)));
+		assertEpsilonEquals(0f, this.r.distanceSquared(new Point2i(10,10)));
+		assertEpsilonEquals(1f, this.r.distanceSquared(new Point2i(4,8)));
+		assertEpsilonEquals(89f, this.r.distanceSquared(new Point2i(0,0)));
+	}
+
+	/**
+	 */
+	@Override
+	public void testDistanceL1Point2D() {
+		assertEpsilonEquals(0f, this.r.distanceL1(new Point2i(5,8)));
+		assertEpsilonEquals(0f, this.r.distanceL1(new Point2i(10,10)));
+		assertEpsilonEquals(1f, this.r.distanceL1(new Point2i(4,8)));
+		assertEpsilonEquals(13f, this.r.distanceL1(new Point2i(0,0)));
+	}
+	
+	/**
+	 */
+	@Override
+	public void testDistanceLinfPoint2D() {
+		assertEpsilonEquals(0f, this.r.distanceLinf(new Point2i(5,8)));
+		assertEpsilonEquals(0f, this.r.distanceLinf(new Point2i(10,10)));
+		assertEpsilonEquals(1f, this.r.distanceLinf(new Point2i(4,8)));
+		assertEpsilonEquals(8f, this.r.distanceLinf(new Point2i(0,0)));
+	}
+	
+	/**
+	 */
+	public void testGetClosestPointToPoint2D() {
+		Point2i p;
+		
+		p = this.r.getClosestPointTo(new Point2i(5,8));
+		assertNotNull(p);
+		assertEquals(5, p.x());
+		assertEquals(8, p.y());
+		
+		p = this.r.getClosestPointTo(new Point2i(10,10));
+		assertNotNull(p);
+		assertEquals(10, p.x());
+		assertEquals(10, p.y());
+		
+		p = this.r.getClosestPointTo(new Point2i(4,8));
+		assertNotNull(p);
+		assertEquals(5, p.x());
+		assertEquals(8, p.y());
+		
+		p = this.r.getClosestPointTo(new Point2i(0,0));
+		assertNotNull(p);
+		assertEquals(5, p.x());
+		assertEquals(8, p.y());
+	}
+
+	/**
+	 */
+	public void testContainsIntInt() {
+		assertFalse(this.r.contains(0,0));
+		assertTrue(this.r.contains(11,10));
+		assertFalse(this.r.contains(11,50));
+	}
+
+	/**
+	 */
+	public void testContainsRectangle2i() {
+		assertFalse(this.r.contains(new Rectangle2i(0,0,1,1)));
+		assertFalse(this.r.contains(new Rectangle2i(0,0,8,1)));
+		assertFalse(this.r.contains(new Rectangle2i(0,0,8,6)));
+		assertFalse(this.r.contains(new Rectangle2i(0,0,100,100)));
+		assertTrue(this.r.contains(new Rectangle2i(7,10,1,1)));
+		assertFalse(this.r.contains(new Rectangle2i(16,0,100,100)));
+	}
+
+	/**
+	 */
+	public void testIntersectsRectangle2i() {
+		assertFalse(this.r.intersects(new Rectangle2i(0,0,1,1)));
+		assertFalse(this.r.intersects(new Rectangle2i(0,0,8,1)));
+		assertFalse(this.r.intersects(new Rectangle2i(0,0,8,6)));
+		assertTrue(this.r.intersects(new Rectangle2i(0,0,100,100)));
+		assertTrue(this.r.intersects(new Rectangle2i(7,10,1,1)));
+		assertFalse(this.r.intersects(new Rectangle2i(16,0,100,100)));
+	}
+
+	/**
+	 */
+	public void testIntersectsSegment2i() {
+		assertFalse(this.r.intersects(new Segment2i(0,0,1,1)));
+		assertFalse(this.r.intersects(new Segment2i(0,0,8,1)));
+		assertFalse(this.r.intersects(new Segment2i(0,0,8,6)));
+		assertTrue(this.r.intersects(new Segment2i(0,0,100,100)));
+		assertTrue(this.r.intersects(new Segment2i(7,10,1,1)));
+		assertFalse(this.r.intersects(new Segment2i(16,0,100,100)));
+	}
+
+	/**
+	 */
+	public void testGetPathIteratorVoid() {
+		PathIterator2i pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 5,8);
+		assertElement(pi, PathElementType.LINE_TO, 15,8);
+		assertElement(pi, PathElementType.LINE_TO, 15,13);
+		assertElement(pi, PathElementType.LINE_TO, 5,13);
+		assertElement(pi, PathElementType.LINE_TO, 5,8);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+	/**
+	 */
+	public void testGetPathIteratorTransform2D() {
+		Transform2D tr;
+		PathIterator2i pi;
+		
+		tr = new Transform2D();
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 5,8);
+		assertElement(pi, PathElementType.LINE_TO, 15,8);
+		assertElement(pi, PathElementType.LINE_TO, 15,13);
+		assertElement(pi, PathElementType.LINE_TO, 5,13);
+		assertElement(pi, PathElementType.LINE_TO, 5,8);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		tr = new Transform2D();
+		tr.makeTranslationMatrix(3.4f, 4.5f);
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 8,12);
+		assertElement(pi, PathElementType.LINE_TO, 18,12);
+		assertElement(pi, PathElementType.LINE_TO, 18,17);
+		assertElement(pi, PathElementType.LINE_TO, 8,17);
+		assertElement(pi, PathElementType.LINE_TO, 8,12);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+
+		tr = new Transform2D();
+		tr.makeRotationMatrix(MathConstants.QUARTER_PI);
+		
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, -2,9);
+		assertElement(pi, PathElementType.LINE_TO, 4,16);
+		assertElement(pi, PathElementType.LINE_TO, 1,19);
+		assertElement(pi, PathElementType.LINE_TO, -5,12);
+		assertElement(pi, PathElementType.LINE_TO, -2,9);
+		assertElement(pi, PathElementType.CLOSE);
+		assertNoElement(pi);
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/Segment2iTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/Segment2iTest.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/discrete/object2d/Segment2iTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,645 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.discrete.object2d;
+
+import java.util.Iterator;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.generic.PathElementType;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+/**
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Segment2iTest extends AbstractShape2iTestCase<Segment2i> implements MathConstants {
+	
+/*	private static void generateSegmentImages(int... coords) {
+		int minx, maxx, miny, maxy;
+		minx = miny = Integer.MAX_VALUE;
+		maxx = maxy=  Integer.MIN_VALUE;
+		for(int i=0; i<coords.length;) {
+			int x = coords[i++];
+			int y = coords[i++];
+			if (x<minx) minx = x;
+			if (x>maxx) maxx = x;
+			if (y<miny) miny = y;
+			if (y>maxy) maxy = y;
+		}
+		Color[] colors = new Color[] {
+			Color.BLACK,
+			Color.RED,
+			Color.GREEN,
+			Color.BLUE,
+			Color.CYAN,
+			Color.LIGHT_GRAY,
+			Color.MAGENTA,
+			Color.ORANGE,
+			Color.PINK,
+			Color.YELLOW,
+		};
+		int width = (maxx-minx+1) * 10;
+		int height = (maxy-miny+1) * 10;
+		BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+		Graphics2D g = (Graphics2D)img.getGraphics();
+		g.setBackground(Color.WHITE);
+		g.clearRect(0, 0, width, height);
+		
+		for(int i=0, j=0; i<coords.length; ++j) {
+			int x1 = coords[i++];
+			int y1 = coords[i++];
+			int x2 = coords[i++];
+			int y2 = coords[i++];
+			g.setColor(new Color(colors[j].getRed(), colors[j].getGreen(), colors[j].getBlue(), 128));
+			Segment2i segment = new Segment2i(x1, y1, x2, y2);
+			Iterator<Point2i> iterator = segment.getPointIterator();
+			while (iterator.hasNext()) {
+				Point2i p = iterator.next();
+				g.fillRect(p.x()*10, p.y()*10, 10, 10);
+			}
+		}
+		
+		g.dispose();
+		
+		File imgFile = new File("/home/sgalland/gen.png");
+		try {
+			ImageIO.write(img, "png", imgFile);
+		}
+		catch (IOException e) {
+			throw new IOError(e);
+		}
+	}
+*/
+	
+	@Override
+	protected Segment2i createShape() {
+		return new Segment2i(0, 0, 10, 5);
+	}
+	
+	@Override
+	public void testIsEmpty() {
+		assertFalse(this.r.isEmpty());
+		this.r.clear();
+		assertTrue(this.r.isEmpty());
+	}
+
+	@Override
+	public void testClear() {
+		this.r.clear();
+		assertEquals(0, this.r.getX1());
+		assertEquals(0, this.r.getY1());
+		assertEquals(0, this.r.getX2());
+		assertEquals(0, this.r.getY2());
+	}
+
+	@Override
+	public void testClone() {
+		Segment2i b = this.r.clone();
+
+		assertNotSame(b, this.r);
+		assertEpsilonEquals(this.r.getX1(), b.getX1());
+		assertEpsilonEquals(this.r.getY1(), b.getY1());
+		assertEpsilonEquals(this.r.getX2(), b.getX2());
+		assertEpsilonEquals(this.r.getY2(), b.getY2());
+		
+		b.set(this.r.getX1()+1, this.r.getY1()+1,
+				this.r.getX2()+1, this.r.getY2()+1);
+
+		assertNotEquals(this.r.getX1(), b.getX1());
+		assertNotEquals(this.r.getY1(), b.getY1());
+		assertNotEquals(this.r.getX2(), b.getX2());
+		assertNotEquals(this.r.getY2(), b.getY2());
+	}
+
+	@Override
+	public void testDistancePoint2D() {
+		assertEpsilonEquals(0f, this.r.distance(new Point2i(0, 0)));
+		assertEpsilonEquals(1f, this.r.distance(new Point2i(1, 1)));
+		
+		assertEpsilonEquals(2.828427125f, this.r.distance(new Point2i(2, 4)));
+
+		assertEpsilonEquals(1f, this.r.distance(new Point2i(2, 2)));
+
+		assertEpsilonEquals(7.071067812f, this.r.distance(new Point2i(-5, 5)));
+	}
+
+	@Override
+	public void testDistanceSquaredPoint2D() {
+		assertEpsilonEquals(0f, this.r.distanceSquared(new Point2i(0, 0)));
+		assertEpsilonEquals(1f, this.r.distanceSquared(new Point2i(1, 1)));
+		
+		assertEpsilonEquals(8f, this.r.distanceSquared(new Point2i(2, 4)));
+
+		assertEpsilonEquals(1f, this.r.distanceSquared(new Point2i(2, 2)));
+
+		assertEpsilonEquals(50f, this.r.distanceSquared(new Point2i(-5, 5)));
+	}
+
+	@Override
+	public void testDistanceL1Point2D() {
+		assertEpsilonEquals(0f, this.r.distanceL1(new Point2i(0, 0)));
+		assertEpsilonEquals(1f, this.r.distanceL1(new Point2i(1, 1)));
+		
+		assertEpsilonEquals(4f, this.r.distanceL1(new Point2i(2, 4)));
+
+		assertEpsilonEquals(1f, this.r.distanceL1(new Point2i(2, 2)));
+
+		assertEpsilonEquals(10f, this.r.distanceL1(new Point2i(-5, 5)));
+	}
+
+	@Override
+	public void testDistanceLinfPoint2D() {
+		assertEpsilonEquals(0f, this.r.distanceLinf(new Point2i(0, 0)));
+		assertEpsilonEquals(1f, this.r.distanceLinf(new Point2i(1, 1)));
+		
+		assertEpsilonEquals(2f, this.r.distanceLinf(new Point2i(2, 4)));
+
+		assertEpsilonEquals(1f, this.r.distanceLinf(new Point2i(2, 2)));
+
+		assertEpsilonEquals(5f, this.r.distanceLinf(new Point2i(-5, 5)));
+	}
+
+	@Override
+	public void testTranslateIntInt() {
+		this.r.translate(3,  4);
+		assertEquals(3, this.r.getX1());
+		assertEquals(4, this.r.getY1());
+		assertEquals(13, this.r.getX2());
+		assertEquals(9, this.r.getY2());
+	}
+
+	/**
+	 */
+	public void testSetIntIntIntInt() {
+		this.r.set(3,  4, 5, 6);
+		assertEquals(3, this.r.getX1());
+		assertEquals(4, this.r.getY1());
+		assertEquals(5, this.r.getX2());
+		assertEquals(6, this.r.getY2());
+	}
+
+	/**
+	 */
+	public void testSetPoint2DPoint2D() {
+		this.r.set(new Point2i(3.4f,  4.5f), new Point2i(5.6f, 6.7f));
+		assertEpsilonEquals(3, this.r.getX1());
+		assertEpsilonEquals(4, this.r.getY1());
+		assertEpsilonEquals(5, this.r.getX2());
+		assertEpsilonEquals(6, this.r.getY2());
+	}
+
+	@Override
+	public void testToBoundingBox() {
+		Rectangle2i bb = this.r.toBoundingBox();
+		assertEpsilonEquals(0, bb.getMinX());
+		assertEpsilonEquals(0, bb.getMinY());
+		assertEpsilonEquals(10, bb.getMaxX());
+		assertEpsilonEquals(5, bb.getMaxY());
+	}
+
+	/**
+	 */
+	public void testContainsPoint2D() {
+		assertTrue(this.r.contains(new Point2i(0, 0)));
+		assertTrue(this.r.contains(new Point2i(10, 5)));
+		
+		assertFalse(this.r.contains(new Point2i(1, 1)));
+		assertFalse(this.r.contains(new Point2i(2, 4)));
+
+		assertFalse(this.r.contains(new Point2i(2, 2)));
+
+		assertTrue(this.r.contains(new Point2i(1, 0)));
+
+		assertFalse(this.r.contains(new Point2i(5, 3)));
+		assertTrue(this.r.contains(new Point2i(5, 2)));
+	}
+
+	/**
+	 */
+	public void testContainsIntInt() {
+		assertTrue(this.r.contains(0, 0));
+		assertTrue(this.r.contains(10, 5));
+		
+		assertFalse(this.r.contains(1, 1));
+		assertFalse(this.r.contains(2, 4));
+
+		assertFalse(this.r.contains(2, 2));
+
+		assertTrue(this.r.contains(1, 0));
+
+		assertFalse(this.r.contains(5, 3));
+		assertTrue(this.r.contains(5, 2));
+	}
+
+	/**
+	 */
+	public void testGetClosestPointTo() {
+		Point2D p;
+		
+		p = this.r.getClosestPointTo(new Point2i(0,0));
+		assertEquals(0, p.x());
+		assertEquals(0, p.y());
+
+		p = this.r.getClosestPointTo(new Point2i(1,1));
+		assertEquals(2, p.x());
+		assertEquals(1, p.y());
+
+		p = this.r.getClosestPointTo(new Point2i(2,2));
+		assertEquals(2, p.x());
+		assertEquals(1, p.y());
+
+		p = this.r.getClosestPointTo(new Point2i(-2,2));
+		assertEquals(0, p.x());
+		assertEquals(0, p.y());
+
+		p = this.r.getClosestPointTo(new Point2i(0f,1f));
+		assertEquals(0, p.x());
+		assertEquals(0, p.y());
+
+		p = this.r.getClosestPointTo(new Point2i(10f,-1f));
+		assertEquals(7, p.x());
+		assertEquals(3, p.y());
+
+		p = this.r.getClosestPointTo(new Point2i(2,4));
+		assertEquals(4, p.x());
+		assertEquals(2, p.y());
+	}
+
+	/**
+	 */
+	public void testGetPathIteratorVoid() {
+		PathIterator2i pi = this.r.getPathIterator();
+		assertElement(pi, PathElementType.MOVE_TO, 0,0);
+		assertElement(pi, PathElementType.LINE_TO, 10,5);
+		assertNoElement(pi);
+	}
+
+	/**
+	 */
+	public void testGetPathIteratorTransform2D() {
+		Transform2D tr;
+		PathIterator2i pi;
+		
+		tr = new Transform2D();
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 0,0);
+		assertElement(pi, PathElementType.LINE_TO, 10,5);
+		assertNoElement(pi);
+
+		tr = new Transform2D();
+		tr.makeTranslationMatrix(3.4f, 4.5f);
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 3, 4);
+		assertElement(pi, PathElementType.LINE_TO, 13, 9);
+		assertNoElement(pi);
+
+		tr = new Transform2D();
+		tr.makeRotationMatrix(MathConstants.QUARTER_PI);
+		pi = this.r.getPathIterator(tr);
+		assertElement(pi, PathElementType.MOVE_TO, 0, 0);
+		assertElement(pi, PathElementType.LINE_TO, 3, 10);
+		assertNoElement(pi);
+	}
+
+	/**
+	 */
+    public void testCreateTransformedPathTransform2D() {
+    	Segment2i s;
+    	Transform2D tr;
+    	
+    	tr = new Transform2D();    	
+    	s = (Segment2i)this.r.createTransformedShape(tr);
+		assertEquals(0, s.getX1());
+		assertEquals(0, s.getY1());
+		assertEquals(10, s.getX2());
+		assertEquals(5, s.getY2());
+
+    	tr = new Transform2D();
+    	tr.setTranslation(3.4f, 4.5f);
+    	s = (Segment2i)this.r.createTransformedShape(tr);
+		assertEquals(3, s.getX1());
+		assertEquals(4, s.getY1());
+		assertEquals(13, s.getX2());
+		assertEquals(9, s.getY2());
+
+    	tr = new Transform2D();
+    	tr.setRotation(MathConstants.PI);
+    	s = (Segment2i)this.r.createTransformedShape(tr);
+		assertEquals(0, s.getX1());
+		assertEquals(0, s.getY1());
+		assertEquals(-10, s.getX2());
+		assertEquals(-5, s.getY2());
+
+    	tr = new Transform2D();
+    	tr.setRotation(MathConstants.QUARTER_PI);
+    	s = (Segment2i)this.r.createTransformedShape(tr);
+		assertEquals(0, s.getX1());
+		assertEquals(0, s.getY1());
+		assertEquals(3, s.getX2());
+		assertEquals(10, s.getY2());
+    }
+
+	/**
+	 */
+    public void testTransformTransform2D() {
+    	Segment2i s;
+    	Transform2D tr;
+    	
+    	tr = new Transform2D();
+    	s = this.r.clone();
+    	s.transform(tr);
+		assertEquals(0, s.getX1());
+		assertEquals(0, s.getY1());
+		assertEquals(10, s.getX2());
+		assertEquals(5, s.getY2());
+
+    	tr = new Transform2D();
+    	tr.setTranslation(3.4f, 4.5f);
+    	s = this.r.clone();
+    	s.transform(tr);
+    	assertEquals(3, s.getX1());
+    	assertEquals(4, s.getY1());
+    	assertEquals(13, s.getX2());
+    	assertEquals(9, s.getY2());
+
+    	tr = new Transform2D();
+    	tr.setRotation(MathConstants.PI);
+    	s = this.r.clone();
+    	s.transform(tr);
+    	assertEquals(0, s.getX1());
+    	assertEquals(0, s.getY1());
+    	assertEquals(-10, s.getX2());
+    	assertEquals(-5, s.getY2());
+
+    	tr = new Transform2D();
+    	tr.setRotation(MathConstants.QUARTER_PI);
+    	s = this.r.clone();
+    	s.transform(tr);
+    	assertEquals(0, s.getX1());
+    	assertEquals(0, s.getY1());
+    	assertEquals(3, s.getX2());
+    	assertEquals(10, s.getY2());
+    }
+    
+    /**
+     */
+    public void testGetPointIterator() {
+    	Point2i p;
+    	Iterator<Point2i> iterator = this.r.getPointIterator();
+
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertNotNull(p);
+    	assertEquals(0, p.x());
+    	assertEquals(0, p.y());
+
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertNotNull(p);
+    	assertEquals(1, p.x());
+    	assertEquals(0, p.y());
+
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertNotNull(p);
+    	assertEquals(2, p.x());
+    	assertEquals(1, p.y());
+
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertNotNull(p);
+    	assertEquals(3, p.x());
+    	assertEquals(1, p.y());
+
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertNotNull(p);
+    	assertEquals(4, p.x());
+    	assertEquals(2, p.y());
+
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertNotNull(p);
+    	assertEquals(5, p.x());
+    	assertEquals(2, p.y());
+
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertNotNull(p);
+    	assertEquals(6, p.x());
+    	assertEquals(3, p.y());
+
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertNotNull(p);
+    	assertEquals(7, p.x());
+    	assertEquals(3, p.y());
+
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertNotNull(p);
+    	assertEquals(8, p.x());
+    	assertEquals(4, p.y());
+
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertNotNull(p);
+    	assertEquals(9, p.x());
+    	assertEquals(4, p.y());
+
+    	assertTrue(iterator.hasNext());
+    	p = iterator.next();
+    	assertNotNull(p);
+    	assertEquals(10, p.x());
+    	assertEquals(5, p.y());
+
+    	assertFalse(iterator.hasNext());
+    }
+    
+    /**
+     */
+    public static void testIntersectsSegmentSegment() {
+    	assertTrue(Segment2i.intersectsSegmentSegment(0, 0, 10, 5, 0, 0, 10, 5));
+    	assertTrue(Segment2i.intersectsSegmentSegment(0, 0, 10, 5, 0, 0, 5, 2));
+    	assertFalse(Segment2i.intersectsSegmentSegment(0, 0, 10, 5, 0, 1, 5, 3));
+    	assertFalse(Segment2i.intersectsSegmentSegment(0, 0, 10, 5, 0, 2, 5, 4));
+    	assertTrue(Segment2i.intersectsSegmentSegment(0, 0, 10, 5, 5, 0, 4, 3));
+    	assertFalse(Segment2i.intersectsSegmentSegment(0, 0, 10, 5, -1, 5, -1, 0));
+    	assertTrue(Segment2i.intersectsSegmentSegment(5, 3, 7, 5, 6, 2, 6, 5));
+    	assertTrue(Segment2i.intersectsSegmentSegment(5, 3, 7, 5, 9, 4, 6, 6));
+    	assertFalse(Segment2i.intersectsSegmentSegment(5, 3, 7, 5, 9, 4, 6, 7));
+    	assertTrue(Segment2i.intersectsSegmentSegment(5, 3, 7, 5, 6, 4, 6, 8));
+    }
+    
+    /**
+     */
+    public static void testComputeCrossingsFromPoint() {
+    	assertEquals(0, Segment2i.computeCrossingsFromPoint(0, 5, 3, -1, -1, 0, 0));
+    	assertEquals(0, Segment2i.computeCrossingsFromPoint(0, 5, 3, 4, -2, 4, 10));
+    	assertEquals(2, Segment2i.computeCrossingsFromPoint(0, 5, 3, 6, -2, 6, 10));
+    	assertEquals(SHAPE_INTERSECTS, Segment2i.computeCrossingsFromPoint(0, 5, 3, 5, -2, 5, 10));
+    	assertEquals(-2, Segment2i.computeCrossingsFromPoint(0, 5, 3, 6, 10, 6, -2));
+    	assertEquals(SHAPE_INTERSECTS, Segment2i.computeCrossingsFromPoint(0, 5, 3, 5, 10, 5, -2));
+    	assertEquals(2, Segment2i.computeCrossingsFromPoint(0, 5, 3, 10, -5, 127, 345));
+    	assertEquals(-2, Segment2i.computeCrossingsFromPoint(0, 5, 3, 127, 345, 10, -5));
+    	assertEquals(1, Segment2i.computeCrossingsFromPoint(0, 5, 3, 127, 3, 200, 345));
+    	assertEquals(-1, Segment2i.computeCrossingsFromPoint(0, 5, 3, 127, 345, 200, 3));
+    	assertEquals(1, Segment2i.computeCrossingsFromPoint(0, 5, 3, 10, 1, 12, 3));
+    	assertEquals(-1, Segment2i.computeCrossingsFromPoint(0, 5, 3, 12, 3, 10, 1));
+    	assertEquals(1, Segment2i.computeCrossingsFromPoint(0, 5, 3, 10, 3, 12, 5));
+    	assertEquals(-1, Segment2i.computeCrossingsFromPoint(0, 5, 3, 12, 5, 10, 3));
+    	assertEquals(0, Segment2i.computeCrossingsFromPoint(0, 4, -1, 7, -5, 0, 0));
+    }
+
+    /**
+     */
+    public static void testComputeCrossingsFromRect() {
+    	int[] data = new int[] {
+    			-1, -1, 0, 0, 				0,
+    			-1, 0, 10, 0,				0,
+    			10, -2, 12, 4,				1,
+    			12, 4, 50, 10,				1,
+    			10, -2, 50, 10,				2,
+    			10, 3, 12, 4,				0,
+    			12, 4, 50, 5,				0,
+    			12, 3, 50, 5,				0,
+    			12, 5, 50, 5,				0,
+    			12, 4, 50, 10,				1,
+    			12, 3, 50, 10,				1,
+    			12, 5, 50, 10,				1,
+    			0, 5, 3, 7,					0,
+    			6, 2, 6, 4,					SHAPE_INTERSECTS,
+    			6, 4, 6, 8,					SHAPE_INTERSECTS,
+    			7, 4, 7, 8,					SHAPE_INTERSECTS,
+    			5, 4, 5, 8,					SHAPE_INTERSECTS,
+    			4, 4, 6, 6,					SHAPE_INTERSECTS,
+    			6, 6, 8, 4,					SHAPE_INTERSECTS,
+    			5, 4, 7, 4,					SHAPE_INTERSECTS,
+    			0, 4, 7, 4,					SHAPE_INTERSECTS,
+    			6, 6, 12, 8,				0,
+    			6, 2, 12, -3,				0,
+    	};
+    	
+    	String label;
+    	for(int i=0; i<data.length;) {
+    		int x1 = data[i++];
+    		int y1 = data[i++];
+    		int x2 = data[i++];
+    		int y2 = data[i++];
+    		int crossing = data[i++];
+    		    		
+    		label = x1+";"+y1+";"+x2+";"+y2;  //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
+    		assertEquals(label, crossing, Segment2i.computeCrossingsFromRect(0, 5, 3, 7, 5, x1, y1, x2, y2));
+
+    		label = x2+";"+y2+";"+x1+";"+y1;  //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
+    		if (crossing!=MathConstants.SHAPE_INTERSECTS) {
+    			crossing = -crossing;
+    		}
+    		assertEquals(label, crossing, Segment2i.computeCrossingsFromRect(0, 5, 3, 7, 5, x2, y2, x1, y1));
+    	}
+    }
+    /**
+     */
+    public static void testComputeCrossingsFromSegment() {
+    	assertEquals(0, Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ -1, -1, 0, 0));
+    	assertEquals(0, Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ -1, 0, 10, 0));
+    	assertEquals(1, Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 10, -2, 12, 4));
+    	assertEquals(1, Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 12, 4, 50, 10));
+    	assertEquals(2, Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 10, -2, 50, 10));
+    	assertEquals(0, Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 10, 3, 12, 4));
+    	assertEquals(0, Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 12, 4, 50, 5));
+    	assertEquals(0, Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 12, 3, 50, 5));
+    	assertEquals(1,
+    			Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 6, 2, 6, 3));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 6, 2, 6, 5));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 6, 2, 6, 4));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 6, 4, 6, 8));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 6, 3, 6, 8));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 7, 4, 7, 8));
+    	assertEquals(0,
+    			Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 5, 4, 5, 8));
+    	assertEquals(0,
+    			Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 4, 4, 6, 6));
+    	assertEquals(0,
+    			Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 4, 3, 6, 5));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 8, 4, 6, 6));
+    	assertEquals(1,
+    			Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 10, 4, 6, 8));
+    	assertEquals(-1,
+    			Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 6, 8, 10, 4));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromSegment(0, /**/ 5, 3, 7, 5, /**/ 5, 4, 100, 6));
+    }
+
+    /**
+     */
+    public static void testComputeCrossingsFromCircle() {
+    	assertEquals(0, Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ -1, -1, 0, 0));
+    	assertEquals(0, Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ -1, 0, 10, 0));
+    	assertEquals(1, Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 10, -2, 12, 4));
+    	assertEquals(1, Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 12, 4, 50, 10));
+    	assertEquals(2, Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 10, -2, 50, 10));
+    	assertEquals(0, Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 10, 3, 12, 4));
+    	assertEquals(0, Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 12, 4, 50, 5));
+    	assertEquals(0, Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 12, 3, 50, 5));
+    	assertEquals(1,
+    			Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 6, 2, 6, 3));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 6, 2, 6, 5));
+    	assertEquals(1,
+    			Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 7, 2, 7, 4));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 6, 2, 6, 4));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 6, 4, 6, 8));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 6, 3, 6, 8));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 7, 4, 7, 8));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 5, 4, 5, 8));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 4, 4, 6, 6));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 4, 3, 6, 5));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 8, 4, 6, 6));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 10, 4, 6, 8));
+    	assertEquals(MathConstants.SHAPE_INTERSECTS,
+    			Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 6, 8, 10, 4));
+    	assertEquals(1,
+    			Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 7, 8, 10, 12));
+    	assertEquals(-1,
+    			Segment2i.computeCrossingsFromCircle(0, /**/ 4, 6, 3, /**/ 10, 12, 7, 8));
+    }
+
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/matrix/Matrix2fTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/matrix/Matrix2fTest.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/matrix/Matrix2fTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,107 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.matrix;
+
+import org.arakhne.afc.math.AbstractMathTestCase;
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object2d.Tuple2f;
+import org.arakhne.afc.math.continous.object2d.Vector2f;
+import org.arakhne.afc.math.matrix.Matrix2f;
+
+/**
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Matrix2fTest extends AbstractMathTestCase {
+	
+	/**
+	 */
+	public void testCovMatrix2Tuple2dArray_theory() {
+		Vector2f v1 = new Vector2f(1, 3);
+		Vector2f v2 = new Vector2f(4, -2);
+		Vector2f m = new Vector2f();
+		m.add(v1,v2);
+		m.scale(.5f);
+		
+		Matrix2f expected = new Matrix2f();
+		expected.m00 = ((v1.getX()-m.getX()) * (v1.getX()-m.getX()) + (v2.getX()-m.getX()) * (v2.getX()-m.getX())) / 2f;
+		expected.m01 = ((v1.getX()-m.getX()) * (v1.getY()-m.getY()) + (v2.getX()-m.getX()) * (v2.getY()-m.getY())) / 2f;
+		expected.m10 = ((v1.getY()-m.getY()) * (v1.getX()-m.getX()) + (v2.getY()-m.getY()) * (v2.getX()-m.getX())) / 2f;
+		expected.m11 = ((v1.getY()-m.getY()) * (v1.getY()-m.getY()) + (v2.getY()-m.getY()) * (v2.getY()-m.getY())) / 2f;
+		
+		Matrix2f mat = new Matrix2f();
+		Tuple2f<?> mean = mat.cov(v1, v2);
+		
+		assertEpsilonEquals(m, mean);
+		for(int i=0; i<2; ++i) {
+			for(int j=0; j<2; ++j) {
+				assertEpsilonEquals("i="+i+"; j="+j, //$NON-NLS-1$ //$NON-NLS-2$
+						expected.getElement(i, j), 
+						mat.getElement(i, j));
+			}
+		}
+	}
+	
+	/**
+	 */
+	public void testCovMatrix2Tuple2dArray_example() {
+		// From "Mathematics for 3D Game Programming and Computer Graphics" pp.220
+		// Adapted to 2D by Stephane Galland
+		//
+		// P1 = [ -1, -2 ]
+		// P2 = [ 1, 0 ]
+		// P3 = [ 2, -1 ]
+		// P4 = [ 2, -1 ]
+		//
+		// average: m = [ 1, -1 ]
+		//
+		// Cov = [ 1.5 ,  .5 ]
+		//       [  .5 ,  .5 ]
+		
+		Point2f p1 = new Point2f(-1, -2);
+		Point2f p2 = new Point2f(1, 0);
+		Point2f p3 = new Point2f(2, -1);
+		Point2f p4 = new Point2f(2, -1);
+		
+		Matrix2f cov = new Matrix2f();
+		Tuple2f<?> mean = cov.cov(p1, p2, p3, p4);
+		
+		Point2f expectedMean = new Point2f(1, -1); 
+		Matrix2f expectedCov = new Matrix2f();
+		expectedCov.m00 = 1.5f;
+		expectedCov.m01 = .5f;
+		expectedCov.m10 = .5f;
+		expectedCov.m11 = .5f;
+		
+		assertEpsilonEquals(expectedMean, mean);
+		for(int i=0; i<2; ++i) {
+			for(int j=0; j<2; ++j) {
+				assertEpsilonEquals("i="+i+"; j="+j, //$NON-NLS-1$ //$NON-NLS-2$
+						expectedCov.getElement(i, j), 
+						cov.getElement(i, j));
+			}
+		}
+	}
+	
+}
\ No newline at end of file

Added: trunk/math/src/test/java/org/arakhne/afc/math/matrix/Transform2DTest.java
===================================================================
--- trunk/math/src/test/java/org/arakhne/afc/math/matrix/Transform2DTest.java	                        (rev 0)
+++ trunk/math/src/test/java/org/arakhne/afc/math/matrix/Transform2DTest.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,760 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2006-10, Multiagent Team, Laboratoire Systemes et Transports, Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.math.matrix;
+
+import org.arakhne.afc.math.AbstractMathTestCase;
+import org.arakhne.afc.math.MathUtil;
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object2d.Vector2f;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+
+/**
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Transform2DTest extends AbstractMathTestCase {
+
+	private Transform2D t;
+
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		this.t = new Transform2D(1f, 2f, 3f, 4f, 5f, 6f);
+	}
+
+	@Override
+	protected void tearDown() throws Exception {
+		this.t = null;
+		super.tearDown();
+	}
+
+	/**
+	 */
+	public void testSetTranslationFloatFloat() {
+		this.t.setTranslation(3.4f, 5.6f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3.4f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(5.6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testSetTranslationTuple2D() {
+		this.t.setTranslation(new Point2f(3.4f, 5.6f));
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3.4f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(5.6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testTranslateFloatFloat() {
+		this.t.translate(3.4f, 5.6f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(17.6f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(47.6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.translate(-3.4f, -5.6f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testTranslateFloatFloat_withRot() {
+		this.t.translate(3.4f, 5.6f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(17.6f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(47.6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.rotate(6.7f);        
+
+		this.t.translate(-3.4f, -5.6f);
+		assertEpsilonEquals(1.724082989468516f, this.t.getM00());
+		assertEpsilonEquals(1.423916375854041f, this.t.getM01());
+		assertEpsilonEquals(3.764186131024417f, this.t.getM02());
+		assertEpsilonEquals(5.681782196024269f, this.t.getM10());
+		assertEpsilonEquals(2.952516058710204f, this.t.getM11());
+		assertEpsilonEquals(11.747850604740339f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testTranslateVector2f() {
+		this.t.translate(new Vector2f(3.4f, 5.6f));
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(17.6f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(47.6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.translate(new Vector2f(-3.4f, -5.6f));
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testTranslateVector2f_withRot() {
+		this.t.translate(new Vector2f(3.4f, 5.6f));
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(17.6f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(47.6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.rotate(6.7f);        
+
+		this.t.translate(new Vector2f(-3.4f, -5.6f));
+		assertEpsilonEquals(1.724082989468516f, this.t.getM00());
+		assertEpsilonEquals(1.423916375854041f, this.t.getM01());
+		assertEpsilonEquals(3.764186131024417f, this.t.getM02());
+		assertEpsilonEquals(5.681782196024269f, this.t.getM10());
+		assertEpsilonEquals(2.952516058710204f, this.t.getM11());
+		assertEpsilonEquals(11.747850604740339f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testGetTranslationX() {
+		assertEpsilonEquals(this.t.getM02(), this.t.getTranslationX());
+	}
+
+	/**
+	 */
+	public void testGetTranslationY() {
+		assertEpsilonEquals(this.t.getM12(), this.t.getTranslationY());
+	}
+
+	/**
+	 */
+	public void testGetTranslationVector() {
+		Vector2f v = this.t.getTranslationVector();
+		assertEpsilonEquals(this.t.getM02(), v.getX());
+		assertEpsilonEquals(this.t.getM12(), v.getY());
+	}
+
+	/**
+	 */
+	public void testGetRotation() {
+		assertEpsilonEquals(0f, this.t.getRotation());
+		this.t.setRotation(6.7f);
+		assertEpsilonEquals(MathUtil.clampRadianMinusPIToPI(6.7f), this.t.getRotation());
+	}
+
+	/**
+	 */
+	public void testSetRotationFloat() {
+		this.t.setRotation(6.7f);
+		assertEpsilonEquals(0.914383148235319f, this.t.getM00());
+		assertEpsilonEquals(-0.404849920616598f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(0.404849920616598f, this.t.getM10());
+		assertEpsilonEquals(0.914383148235319f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testRotateFloat() {
+		this.t.rotate(6.7f);
+		assertEpsilonEquals(1.724082989468516f, this.t.getM00());
+		assertEpsilonEquals(1.423916375854041f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(5.681782196024269f, this.t.getM10());
+		assertEpsilonEquals(2.952516058710204f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.rotate(-6.7f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testRotate_withTrans() {
+		this.t.rotate(6.7f);
+		assertEpsilonEquals(1.724082989468516f, this.t.getM00());
+		assertEpsilonEquals(1.423916375854041f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(5.681782196024269f, this.t.getM10());
+		assertEpsilonEquals(2.952516058710204f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.translate(3.4f, 5.6f);
+
+		this.t.rotate(-6.7f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(16.835813868975581f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(41.852149395259651f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testSetScaleFloatFloat() {
+		this.t.setScale(8.9f, 10.11f);
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(10.11f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testSetScaleTuple2D() {
+		this.t.setScale(new Vector2f(8.9f, 10.11f));
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(10.11f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testScaleFloatFloat() {
+		this.t.scale(8.9f, 10.11f);
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(20.22f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(35.6f, this.t.getM10());
+		assertEpsilonEquals(50.55f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.scale(1f/8.9f, 1f/10.11f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testScaleFloatFloat_withTrans() {
+		this.t.scale(8.9f, 10.11f);
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(20.22f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(35.6f, this.t.getM10());
+		assertEpsilonEquals(50.55f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.translate(3.4f, 5.6f);
+
+		this.t.scale(1f/8.9f, 1f/10.11f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(146.492f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(410.12f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testScaleFloatFloat_withRot() {
+		this.t.scale(8.9f, 10.11f);
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(20.22f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(35.6f, this.t.getM10());
+		assertEpsilonEquals(50.55f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.rotate(6.7f);
+
+		this.t.scale(1f/8.9f, 1f/10.11f);
+		assertEpsilonEquals(1.834165776872130f, this.t.getM00());
+		assertEpsilonEquals(1.472370223919924f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(5.956989164533305f, this.t.getM10());
+		assertEpsilonEquals(3.146331450973738f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testScaleTuple2D() {
+		this.t.scale(new Vector2f(8.9f, 10.11f));
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(20.22f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(35.6f, this.t.getM10());
+		assertEpsilonEquals(50.55f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.scale(new Vector2f(1f/8.9f, 1f/10.11f));
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testScaleTuple2D_withTrans() {
+		this.t.scale(new Vector2f(8.9f, 10.11f));
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(20.22f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(35.6f, this.t.getM10());
+		assertEpsilonEquals(50.55f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.translate(3.4f, 5.6f);
+
+		this.t.scale(new Vector2f(1f/8.9f, 1f/10.11f));
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(2f, this.t.getM01());
+		assertEpsilonEquals(146.492f, this.t.getM02());
+		assertEpsilonEquals(4f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(410.12f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testScaleTuple2D_withRot() {
+		this.t.scale(new Vector2f(8.9f, 10.11f));
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(20.22f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(35.6f, this.t.getM10());
+		assertEpsilonEquals(50.55f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.rotate(6.7f);
+
+		this.t.scale(new Vector2f(1f/8.9f, 1f/10.11f));
+		assertEpsilonEquals(1.834165776872130f, this.t.getM00());
+		assertEpsilonEquals(1.472370223919924f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(5.956989164533305f, this.t.getM10());
+		assertEpsilonEquals(3.146331450973738f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testGetScaleX() {
+		assertEpsilonEquals(this.t.getM00(), this.t.getScaleX());
+	}
+
+	/**
+	 */
+	public void testGetScaleY() {
+		assertEpsilonEquals(this.t.getM11(), this.t.getScaleY());
+	}
+
+	/**
+	 */
+	public void testGetScaleVector() {
+		Vector2f v = this.t.getScaleVector();
+		assertEpsilonEquals(this.t.getM00(), v.getX());
+		assertEpsilonEquals(this.t.getM11(), v.getY());
+	}
+
+	/**
+	 */
+	public void testSetShearFloatFloat() {
+		this.t.setShear(8.9f, 10.11f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(8.9f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(10.11f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testSetShearTuple2D() {
+		this.t.setShear(new Vector2f(8.9f, 10.11f));
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(8.9f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(10.11f, this.t.getM10());
+		assertEpsilonEquals(5f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testShearFloatFloat() {
+		this.t.shear(8.9f, 10.11f);
+		assertEpsilonEquals(21.22f, this.t.getM00());
+		assertEpsilonEquals(10.9f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(54.55f, this.t.getM10());
+		assertEpsilonEquals(40.6f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.shear(-8.9f, -10.11f);
+		assertEpsilonEquals(-88.979f, this.t.getM00());
+		assertEpsilonEquals(-177.958f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(-355.916f, this.t.getM10());
+		assertEpsilonEquals(-444.895f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testShearTuple2D() {
+		this.t.shear(new Vector2f(8.9f, 10.11f));
+		assertEpsilonEquals(21.22f, this.t.getM00());
+		assertEpsilonEquals(10.9f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(54.55f, this.t.getM10());
+		assertEpsilonEquals(40.6f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+
+		this.t.shear(new Vector2f(-8.9f, -10.11f));
+		assertEpsilonEquals(-88.979f, this.t.getM00());
+		assertEpsilonEquals(-177.958f, this.t.getM01());
+		assertEpsilonEquals(3f, this.t.getM02());
+		assertEpsilonEquals(-355.916f, this.t.getM10());
+		assertEpsilonEquals(-444.895f, this.t.getM11());
+		assertEpsilonEquals(6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testGetShearX() {
+		assertEpsilonEquals(this.t.getM01(), this.t.getShearX());
+	}
+
+	/**
+	 */
+	public void testGetShearY() {
+		assertEpsilonEquals(this.t.getM10(), this.t.getShearY());
+	}
+
+	/**
+	 */
+	public void testGetShearVector() {
+		Vector2f v = this.t.getShearVector();
+		assertEpsilonEquals(this.t.getM01(), v.getX());
+		assertEpsilonEquals(this.t.getM10(), v.getY());
+	}
+
+	/**
+	 */
+	public void testMakeRotationMatrix() {
+		this.t.makeRotationMatrix(6.7f);
+		assertEpsilonEquals(0.914383148235319f, this.t.getM00());
+		assertEpsilonEquals(-0.404849920616598f, this.t.getM01());
+		assertEpsilonEquals(0f, this.t.getM02());
+		assertEpsilonEquals(0.404849920616598f, this.t.getM10());
+		assertEpsilonEquals(0.914383148235319f, this.t.getM11());
+		assertEpsilonEquals(0f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testMakeTranslationMatrix() {
+		this.t.makeTranslationMatrix(3.4f, 5.6f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(0f, this.t.getM01());
+		assertEpsilonEquals(3.4f, this.t.getM02());
+		assertEpsilonEquals(0f, this.t.getM10());
+		assertEpsilonEquals(1f, this.t.getM11());
+		assertEpsilonEquals(5.6f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testMakeScaleMatrix() {
+		this.t.makeScaleMatrix(8.9f, 10.11f);
+		assertEpsilonEquals(8.9f, this.t.getM00());
+		assertEpsilonEquals(0f, this.t.getM01());
+		assertEpsilonEquals(0f, this.t.getM02());
+		assertEpsilonEquals(0f, this.t.getM10());
+		assertEpsilonEquals(10.11f, this.t.getM11());
+		assertEpsilonEquals(0f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testMakeShearMatrix() {
+		this.t.makeShearMatrix(8.9f, 10.11f);
+		assertEpsilonEquals(1f, this.t.getM00());
+		assertEpsilonEquals(8.9f, this.t.getM01());
+		assertEpsilonEquals(0f, this.t.getM02());
+		assertEpsilonEquals(10.11f, this.t.getM10());
+		assertEpsilonEquals(1f, this.t.getM11());
+		assertEpsilonEquals(0f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testTransformTuple2D() {
+		Point2f p = new Point2f();
+
+		p.set(2.3f, 4.5f);
+		this.t.transform(p);
+		assertEpsilonEquals(14.3f, p.getX());
+		assertEpsilonEquals(37.7f, p.getY());
+
+		p.set(-2.3f, -4.5f);
+		this.t.transform(p);
+		assertEpsilonEquals(-8.3f, p.getX());
+		assertEpsilonEquals(-25.7f, p.getY());
+	}
+
+	/**
+	 */
+	public void testTransformFloatFloat() {
+		Point2D p;
+
+		p = this.t.transform(2.3f, 4.5f);
+		assertEpsilonEquals(14.3f, p.getX());
+		assertEpsilonEquals(37.7f, p.getY());
+
+		p = this.t.transform(-2.3f, -4.5f);
+		assertEpsilonEquals(-8.3f, p.getX());
+		assertEpsilonEquals(-25.7f, p.getY());
+	}
+
+	/**
+	 */
+	public void testTransformTuple2DTuple2D() {
+		Point2f p = new Point2f();
+		Point2f r = new Point2f();
+
+		p.set(2.3f, 4.5f);
+		this.t.transform(p, r);
+		assertEpsilonEquals(2.3f, p.getX());
+		assertEpsilonEquals(4.5f, p.getY());
+		assertEpsilonEquals(14.3f, r.getX());
+		assertEpsilonEquals(37.7f, r.getY());
+
+		p.set(-2.3f, -4.5f);
+		this.t.transform(p,r);
+		assertEpsilonEquals(-2.3f, p.getX());
+		assertEpsilonEquals(-4.5f, p.getY());
+		assertEpsilonEquals(-8.3f, r.getX());
+		assertEpsilonEquals(-25.7f, r.getY());
+	}
+
+	/**
+	 */
+	public void testSet() {
+		this.t.set(1.2f, 3.4f, 5.6f, 7.8f, 9.10f, 11.12f);
+		assertEpsilonEquals(1.2f, this.t.getM00());
+		assertEpsilonEquals(3.4f, this.t.getM01());
+		assertEpsilonEquals(5.6f, this.t.getM02());
+		assertEpsilonEquals(7.8f, this.t.getM10());
+		assertEpsilonEquals(9.10f, this.t.getM11());
+		assertEpsilonEquals(11.12f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+	/**
+	 */
+	public void testCreateInverse() {
+		Transform2D ti = this.t.createInverse();
+		assertEpsilonEquals(-1.666666666666667f, ti.getM00());
+		assertEpsilonEquals(0.666666666666667f, ti.getM01());
+		assertEpsilonEquals(1f, ti.getM02());
+		assertEpsilonEquals(1.333333333333333f, ti.getM10());
+		assertEpsilonEquals(-0.333333333333333f, ti.getM11());
+		assertEpsilonEquals(-2f, ti.getM12());
+		assertEpsilonEquals(0f, ti.getM20());
+		assertEpsilonEquals(0f, ti.getM21());
+		assertEpsilonEquals(1f, ti.getM22());
+	}
+
+	/**
+	 */
+	public void testInvert() {
+		this.t.invert();
+		assertEpsilonEquals(-1.666666666666667f, this.t.getM00());
+		assertEpsilonEquals(0.666666666666667f, this.t.getM01());
+		assertEpsilonEquals(1f, this.t.getM02());
+		assertEpsilonEquals(1.333333333333333f, this.t.getM10());
+		assertEpsilonEquals(-0.333333333333333f, this.t.getM11());
+		assertEpsilonEquals(-2f, this.t.getM12());
+		assertEpsilonEquals(0f, this.t.getM20());
+		assertEpsilonEquals(0f, this.t.getM21());
+		assertEpsilonEquals(1f, this.t.getM22());
+	}
+
+}
\ No newline at end of file

Added: trunk/math/src/test/resources/org/arakhne/afc/math/discrete/object2d/Segment2i.ods
===================================================================
(Binary files differ)


Property changes on: trunk/math/src/test/resources/org/arakhne/afc/math/discrete/object2d/Segment2i.ods
___________________________________________________________________
Added: svn:mime-type
   + application/vnd.oasis.opendocument.spreadsheet

Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml	2013-01-14 15:11:56 UTC (rev 387)
+++ trunk/pom.xml	2013-03-20 14:54:50 UTC (rev 388)
@@ -59,11 +59,81 @@
 			</dependency>
 			<dependency>
 				<groupId>org.arakhne.afc</groupId>
+				<artifactId>attrs</artifactId>
+				<version>1.0-SNAPSHOT</version>
+			</dependency>
+			<dependency>
+				<groupId>org.arakhne.afc</groupId>
+				<artifactId>util</artifactId>
+				<version>1.0-SNAPSHOT</version>
+			</dependency>
+			<dependency>
+				<groupId>org.arakhne.afc</groupId>
+				<artifactId>math</artifactId>
+				<version>1.0-SNAPSHOT</version>
+			</dependency>
+			<dependency>
+				<groupId>org.arakhne.afc.ui</groupId>
+				<artifactId>ui-base</artifactId>
+				<version>1.0-SNAPSHOT</version>
+			</dependency>
+			<dependency>
+				<groupId>org.arakhne.afc.ui</groupId>
+				<artifactId>ui-awt</artifactId>
+				<version>1.0-SNAPSHOT</version>
+			</dependency>
+			<dependency>
+				<groupId>org.arakhne.afc.ui</groupId>
+				<artifactId>ui-base</artifactId>
+				<version>1.0-SNAPSHOT</version>
+			</dependency>
+			<dependency>
+				<groupId>org.arakhne.afc.ui</groupId>
+				<artifactId>ui-awt</artifactId>
+				<version>1.0-SNAPSHOT</version>
+			</dependency>
+			<dependency>
+				<groupId>org.arakhne.afc.ui</groupId>
+				<artifactId>ui-swing</artifactId>
+				<version>1.0-SNAPSHOT</version>
+			</dependency>
+			<dependency>
+				<groupId>org.arakhne.afc.ui</groupId>
+				<artifactId>ui-vector</artifactId>
+				<version>1.0-SNAPSHOT</version>
+			</dependency>
+			<dependency>
+				<groupId>org.arakhne.afc.ui</groupId>
+				<artifactId>ui-vector-awt</artifactId>
+				<version>1.0-SNAPSHOT</version>
+			</dependency>
+			<dependency>
+				<groupId>org.arakhne.afc.ui</groupId>
+				<artifactId>ui-vector-android</artifactId>
+				<version>1.0-SNAPSHOT</version>
+			</dependency>
+			<dependency>
+				<groupId>org.arakhne.afc</groupId>
 				<artifactId>maventools</artifactId>
 				<version>3.5-SNAPSHOT</version>
 			</dependency>
 
 			<dependency>
+				<groupId>org.swinglabs</groupId>
+				<artifactId>pdf-renderer</artifactId>
+				<version>1.0.5</version>
+			</dependency>
+			<dependency>
+				<groupId>com.google.android</groupId>
+				<artifactId>android</artifactId>
+				<version>4.1.1.4</version>
+			</dependency>
+			<dependency>
+				<groupId>com.google.android</groupId>
+				<artifactId>support-v4</artifactId>
+				<version>r7</version>
+			</dependency>
+			<dependency>
 				<groupId>org.apache.felix</groupId>
 				<artifactId>org.osgi.core</artifactId>
 				<version>1.4.0</version>
@@ -134,6 +204,10 @@
 		<module>arakhneRefs</module>
 		<module>arakhneVmutils</module>
 		<module>arakhneText</module>
+		<module>util</module>
+		<module>math</module>
+		<module>ui</module>
+		<module>attrs</module>
 	</modules>
 
 	<licenses>


Property changes on: trunk/ui
___________________________________________________________________
Added: svn:ignore
   + .classpath
..metadata
..settings
..project
target
bin


Added: trunk/ui/pom.xml
===================================================================
(Binary files differ)


Property changes on: trunk/ui/pom.xml
___________________________________________________________________
Added: svn:mime-type
   + application/xml


Property changes on: trunk/ui/ui-awt
___________________________________________________________________
Added: svn:ignore
   + .classpath
..metadata
..settings
..project
target
bin


Added: trunk/ui/ui-awt/pom.xml
===================================================================
--- trunk/ui/ui-awt/pom.xml	                        (rev 0)
+++ trunk/ui/ui-awt/pom.xml	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,28 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+		<groupId>org.arakhne.afc</groupId>
+		<artifactId>arakhneUi</artifactId>
+		<version>1.0-SNAPSHOT</version>
+  </parent>
+  
+  <groupId>org.arakhne.afc.ui</groupId>
+  <artifactId>ui-awt</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <name>Arakhne AWT Tools</name>
+  
+  <dependencies>
+  	<dependency>
+  		<groupId>org.arakhne.afc</groupId>
+  		<artifactId>util</artifactId>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.arakhne.afc</groupId>
+  		<artifactId>math</artifactId>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.arakhne.afc.ui</groupId>
+  		<artifactId>ui-base</artifactId>
+  	</dependency>
+  </dependencies>
+</project>

Added: trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/AbstractLODGraphics2D.java
===================================================================
--- trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/AbstractLODGraphics2D.java	                        (rev 0)
+++ trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/AbstractLODGraphics2D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,1524 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.awt;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Composite;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.Image;
+import java.awt.Paint;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.RenderingHints.Key;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Dimension2D;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RoundRectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ImageObserver;
+import java.awt.image.RenderedImage;
+import java.awt.image.renderable.RenderableImage;
+import java.lang.ref.WeakReference;
+import java.net.URL;
+import java.text.AttributedCharacterIterator;
+import java.util.Map;
+
+import org.arakhne.afc.ui.Graphics2DLOD;
+import org.arakhne.afc.ui.StringAnchor;
+import org.arakhne.afc.ui.TextAlignment;
+
+/** This graphic context permits to display
+ *  something with a level of details.
+ *
+ * @param <G> is the type of the graphics pointed by this LODGraphics2D.
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractLODGraphics2D<G extends Graphics2D> extends Graphics2D implements LODGraphics2D {
+
+	/** Is the minimal size of the displayed figures when
+	 * a low level of details was adviced.
+	 */
+	protected static final float LOW_DETAIL_LEVEL = 5;
+	
+	/** Margin in the cartridges.
+	 */
+	protected static final float CARTRIDGE_MARGIN = 3;
+
+	////////////////////////////////////////////////////////////
+	// Attributes
+
+	/** This is the graphics target.
+	 */
+	protected final G target ;
+
+	/** This is the target component.
+	 */
+	protected final WeakReference<Component> targetComponent ;
+
+	/** Indicates if this graphics is for printing.
+	 */
+	private final boolean isForPrinting;
+
+	/** Indicates the current LOD.
+	 */
+	private final Graphics2DLOD lod;
+	
+	/** Cartridges.
+	 */
+	private CartridgeNode cartridges = null;
+
+	/** The URL of the image to draw.
+	 */
+	private URL imageResource = null;
+
+	////////////////////////////////////////////////////////////
+	// Constructor
+
+	/** Construct a new ZoomableGraphics2D.
+	 *
+	 * @param target is the graphics in which draws will be done.
+	 * @param target_component is the target component (used to display an icon).
+	 * @param antialiasing permits to force the anti-aliasing flag for the target graphical context
+	 * @param is_for_printing indicates if this graphics environment is for printing or not.
+	 * @param lod indicates the desired LOD used by this graphical context.
+	 */
+	public AbstractLODGraphics2D(G target, Component target_component, boolean antialiasing, boolean is_for_printing, Graphics2DLOD lod) {
+		this.target = target ;
+		this.isForPrinting = is_for_printing;
+		this.lod = lod;
+
+		// Force the anti-aliasing for this graphical context
+		setAntiAliased(antialiasing);
+
+		this.targetComponent = target_component==null ? null : new WeakReference<Component>(target_component);
+	}
+
+	/** Construct a new ZoomableGraphics2D.
+	 *
+	 * @param target is the graphics in which draws will be done.
+	 * @param target_component is the target component (used to display an icon).
+	 * @param antialiasing permits to force the anti-aliasing flag for the target graphical context
+	 * @param lod indicates the desired LOD used by this graphical context.
+	 */
+	public AbstractLODGraphics2D(G target, Component target_component, boolean antialiasing, Graphics2DLOD lod) {
+		this(target, target_component, antialiasing, false, lod);
+	}
+
+	/** Construct a new ZoomableGraphics2D.
+	 *
+	 * @param target is the graphics in which draws will be done.
+	 * @param target_component is the target component (used to display an icon).
+	 * @param lod indicates the desired LOD used by this graphical context.
+	 */
+	public AbstractLODGraphics2D(G target, Component target_component, Graphics2DLOD lod) {
+		this(target, target_component, false, false, lod);
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setImageURL(URL url) {
+		this.imageResource = url;
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public URL getImageURL() {
+		return this.imageResource;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public StringAnchor getStringAnchor() {
+		return StringAnchor.LEFT_BASELINE;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Graphics2DLOD getLOD() {
+		return this.lod;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean isPrinting() {
+		return this.isForPrinting;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean isAntiAliased() {
+		Object a = this.target.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
+		return ((a!=null)&&
+				((a==RenderingHints.VALUE_ANTIALIAS_ON)||
+						(a.equals(RenderingHints.VALUE_ANTIALIAS_ON))));
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void setAntiAliased(boolean anti_aliased_enable) {
+		this.target.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
+				anti_aliased_enable
+				? RenderingHints.VALUE_ANTIALIAS_ON
+						: RenderingHints.VALUE_ANTIALIAS_OFF);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+    public final boolean hit(Shape shape) {
+    	Rectangle2D bounds = shape.getBounds2D();
+        if (bounds!=null) return hit(bounds);
+        return true;
+    }
+    
+	/** {@inheritDoc}
+	 */
+	@Override
+    public boolean hit(Rectangle2D rect) {
+    	if (rect==null || rect.isEmpty()) return false;
+    	
+    	float w = (float)rect.getWidth();
+    	float h = (float)rect.getHeight();
+    	
+    	if ((w<=0)&&(h<=0)) return false;
+    	
+    	if (!this.target.hitClip(
+    			(int)rect.getX(),
+    			(int)rect.getY(),
+    			((int)w)+2,
+    			((int)h)+2)) {
+    		return false;
+    	}
+    
+    	switch(this.lod) {
+    	case SHADOW:
+    	case LOW_LEVEL_OF_DETAIL:
+    		float mini_length = LOW_DETAIL_LEVEL;
+    		float diag = (float)Math.hypot(w,h);
+        	return (diag>=mini_length);
+    	case HIGH_LEVEL_OF_DETAIL:
+    	case NORMAL_LEVEL_OF_DETAIL:
+    		return true;
+		default:
+	    	return true;
+    	}
+    }
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Dimension2D getTargetComponentSize() {
+		Component c = (this.targetComponent==null) ? null : 
+			this.targetComponent.get();
+		return (c!=null) ? c.getSize() : null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Rectangle2D getViewportRect() {
+		Component c = (this.targetComponent==null) ? null : 
+			this.targetComponent.get();
+		if (c==null) return null;
+		Dimension2D d = c.getSize();
+		return new Rectangle2D.Double(
+				0,
+				0,
+				d.getWidth()-1,
+				d.getHeight()-1);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Dimension2D getViewportSize() {
+		Dimension2D d = getTargetComponentSize();
+		if (d==null) return d;
+		return new DoubleDimension(d.getWidth(), d.getHeight());
+	}
+
+	////////////////////////////////////////////////////////////
+	// Graphics API
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void dispose() {
+		if (this.cartridges!=null) {
+			this.cartridges = null;
+		}
+		this.target.dispose() ;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Graphics create(float x, float y, float width, float height) {
+		Graphics2D g = (Graphics2D)create();
+        if (g == null) return null;
+        g.translate(x, y);
+        g.clip(new Rectangle2D.Double(0, 0, width, height));
+        return g;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final Graphics create(int x, int y, int width, int height) {
+		return create((float)x, (float)y, (float)width, (float)height);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clipRect(float x, float y, float width, float height) {
+		Rectangle2D r = new Rectangle2D.Double(x,y,width,height);
+		this.target.clip(r);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void clipRect(int x, int y, int width, int height) {
+		clipRect((float)x, (float)y, (float)width, (float)height);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setClip(float x, float y, float width, float height) {
+		Rectangle2D r = new Rectangle2D.Double(x,y,width,height);
+		this.target.setClip(r);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void setClip(int x, int y, int width, int height) {
+		setClip((float)x, (float)y, (float)width, (float)height);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void draw3DRect(float x, float y, float width, float height,
+			boolean raised) {
+		Paint p = getPaint();
+        Color c = getColor();
+        Color brighter = c.brighter();
+        Color darker = c.darker();
+
+        setColor(raised ? brighter : darker);
+        //drawLine(x, y, x, y + height);
+        fillRect(x, y, 1, height + 1);
+        //drawLine(x + 1, y, x + width - 1, y);
+        fillRect(x + 1, y, width - 1, 1);
+        setColor(raised ? darker : brighter);
+        //drawLine(x + 1, y + height, x + width, y + height);
+        fillRect(x + 1, y + height, width, 1);
+        //drawLine(x + width, y, x + width, y + height - 1);
+        fillRect(x + width, y, 1, height);
+        setPaint(p);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void draw3DRect(int x, int y, int width, int height, boolean raised) {
+		draw3DRect((float)x, (float)y, (float)width, (float)height, raised);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void fill3DRect(float x, float y, float width, float height,
+			boolean raised) {
+		Paint p = getPaint();
+        Color c = getColor();
+        Color brighter = c.brighter();
+        Color darker = c.darker();
+
+        if (!raised) {
+            setColor(darker);
+        } else if (p != c) {
+            setColor(c);
+        }
+        fillRect(x+1, y+1, width-2, height-2);
+        setColor(raised ? brighter : darker);
+        //drawLine(x, y, x, y + height - 1);
+        fillRect(x, y, 1, height);
+        //drawLine(x + 1, y, x + width - 2, y);
+        fillRect(x + 1, y, width - 2, 1);
+        setColor(raised ? darker : brighter);
+        //drawLine(x + 1, y + height - 1, x + width - 1, y + height - 1);
+        fillRect(x + 1, y + height - 1, width - 1, 1);
+        //drawLine(x + width - 1, y, x + width - 1, y + height - 2);
+        fillRect(x + width - 1, y, 1, height - 1);
+        setPaint(p);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void fill3DRect(int x, int y, int width, int height, boolean raised) {
+		fill3DRect((float)x, (float)y, (float)width, (float)height, raised);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void drawChars(char[] data, int offset, int length, float x,
+			float y) {
+		drawString(new String(data, offset, length), x, y);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void drawChars(char[] data, int offset, int length, int x, int y) {
+		drawChars(data, offset, length, (float)x, (float)y);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void drawBytes(byte[] data, int offset, int length, float x,
+			float y) {
+		drawString(new String(data, offset, length), x, y);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void drawBytes(byte[] data, int offset, int length, int x, int y) {
+		drawBytes(data, offset, length, (float)x, (float)y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean hitClip(float x, float y, float width, float height) {
+		return this.target.hitClip((int)x, (int)y, (int)width, (int)height);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final boolean hitClip(int x, int y, int width, int height) {
+		return hitClip((float)x, (float)y, (float)width, (float)height);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean hit(Rectangle2D rect, Shape s, boolean onStroke) {
+		return this.target.hit(rect.getBounds(), s, onStroke);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final boolean hit(Rectangle rect, Shape s, boolean onStroke) {
+		return hit((Rectangle2D)rect, s, onStroke);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public GraphicsConfiguration getDeviceConfiguration() {
+		return this.target.getDeviceConfiguration();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setComposite(Composite comp) {
+		this.target.setComposite(comp);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setPaint(Paint paint) {
+		this.target.setPaint(paint);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setStroke(Stroke s) {
+		this.target.setStroke(s);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setRenderingHint(Key hintKey, Object hintValue) {
+		this.target.setRenderingHint(hintKey, hintValue);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Object getRenderingHint(Key hintKey) {
+		return this.target.getRenderingHint(hintKey);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setRenderingHints(Map<?, ?> hints) {
+		this.target.setRenderingHints(hints);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addRenderingHints(Map<?, ?> hints) {
+		this.target.addRenderingHints(hints);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public RenderingHints getRenderingHints() {
+		return this.target.getRenderingHints();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void scale(double sx, double sy) {
+		scale((float)sx, (float)sy);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void scale(float sx, float sy) {
+		this.target.scale(sx, sy);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void shear(double shx, double shy) {
+		shear((float)shx, (float)shy);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void shear(float shx, float shy) {
+		this.target.shear(shx, shy);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void transform(AffineTransform Tx) {
+		this.target.transform(Tx);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void rotate(double theta) {
+		rotate((float)theta);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void rotate(float theta) {
+		this.target.rotate(theta);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void rotate(double theta, double x, double y) {
+		rotate((float)theta, (float)x, (float)y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void rotate(float theta, float x, float y) {
+		this.target.rotate(theta, x, y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setTransform(AffineTransform Tx) {
+		this.target.setTransform(Tx);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public AffineTransform getTransform() {
+		return this.target.getTransform();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Paint getPaint() {
+		return this.target.getPaint();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Composite getComposite() {
+		return this.target.getComposite();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setBackground(Color color) {
+		this.target.setBackground(color);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Color getBackground() {
+		return this.target.getBackground();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Stroke getStroke() {
+		return this.target.getStroke();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public FontRenderContext getFontRenderContext() {
+		return this.target.getFontRenderContext();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Color getColor() {
+		return this.target.getColor();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setColor(Color c) {
+		this.target.setColor(c);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setPaintMode() {
+		this.target.setPaintMode();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setXORMode(Color c1) {
+		this.target.setXORMode(c1);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) {
+		drawImage(img, op, (float)x, (float)y);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void drawImage(BufferedImage img, BufferedImageOp op, float x,
+			float y) {
+		this.target.drawImage(img, op, (int)x, (int)y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final boolean drawImage(Image img, int x, int y, ImageObserver observer) {
+		return drawImage(img, (float)x, (float)y, observer);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean drawImage(Image img, float x, float y,
+			ImageObserver observer) {
+		return this.target.drawImage(img, (int)x, (int)y, observer);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final boolean drawImage(Image img, int x, int y, int width, int height,
+			ImageObserver observer) {
+		return drawImage(img, (float)x, (float)y, (float)width, (float)height, observer);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean drawImage(Image img, float x, float y, float width, float height,
+			ImageObserver observer) {
+		return this.target.drawImage(img, (int)x, (int)y, (int)width, (int)height, observer);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final boolean drawImage(Image img, int x, int y, Color bgcolor,
+			ImageObserver observer) {
+		return drawImage(img, (float)x, (float)y, bgcolor, observer);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean drawImage(Image img, float x, float y, Color bgcolor,
+			ImageObserver observer) {
+		return this.target.drawImage(img, (int)x, (int)y, bgcolor, observer);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final boolean drawImage(Image img, int x, int y, int width, int height,
+			Color bgcolor, ImageObserver observer) {
+		return drawImage(img, (float)x, (float)y, (float)width, (float)height, bgcolor, observer);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean drawImage(Image img, float x, float y, float width, float height,
+			Color bgcolor, ImageObserver observer) {
+		return this.target.drawImage(img, (int)x, (int)y, (int)width, (int)height, bgcolor, observer);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
+			int sx1, int sy1, int sx2, int sy2, ImageObserver observer) {
+		return drawImage(img, (float)dx1, (float)dy1, (float)dx2, (float)dy2,
+				sx1, sy1, sx2, sy2, observer);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean drawImage(Image img, float dx1, float dy1, float dx2, float dy2,
+			int sx1, int sy1, int sx2, int sy2, ImageObserver observer) {
+		return this.target.drawImage(img,
+				(int)dx1, (int)dy1, (int)dx2, (int)dy2,
+				sx1, sy1, sx2, sy2, observer);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
+			int sx1, int sy1, int sx2, int sy2, Color bgcolor,
+			ImageObserver observer) {
+		return drawImage(img, (float)dx1, (float)dy1, (float)dx2, (float)dy2,
+				sx1, sy1, sx2, sy2, bgcolor, observer);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean drawImage(Image img, float dx1, float dy1, float dx2, float dy2,
+			int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) {
+		return this.target.drawImage(img,
+				(int)dx1, (int)dy1, (int)dx2, (int)dy2,
+				sx1, sy1, sx2, sy2, bgcolor, observer);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void drawGlyphVector(GlyphVector g, float x, float y) {
+		this.target.drawGlyphVector(g, x, y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void copyArea(float x, float y, float width, float height,
+			float dx, float dy) {
+		this.target.copyArea((int)x, (int)y, (int)width, (int)height, (int)dx, (int)dy);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void copyArea(int x, int y, int width, int height, int dx, int dy) {
+		copyArea((float)x, (float)y, (float)width, (float)height, (float)dx, (float)dy);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void drawLine(float x1, float y1, float x2, float y2) {
+		Line2D line = new Line2D.Double(x1,y1,x2,y2);
+		this.target.draw(line);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void drawLine(int x1, int y1, int x2, int y2) {
+		drawLine((float)x1, (float)y1, (float)x2, (float)y2);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void fillRect(float x, float y, float width, float height) {
+		Rectangle2D r = new Rectangle2D.Double(x,y,width,height);
+		this.target.fill(r);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void fillRect(int x, int y, int width, int height) {
+		fillRect((float)x, (float)y, (float)width, (float)height);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void drawRect(float x, float y, float width, float height) {
+		Rectangle2D r = new Rectangle2D.Double(x,y,width,height);
+		this.target.draw(r);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clearRect(float x, float y, float width, float height) {
+		this.target.clearRect((int)x, (int)y, (int)width, (int)height);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void clearRect(int x, int y, int width, int height) {
+		this.target.clearRect(x, y, width, height);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void drawRoundRect(float x, float y, float width, float height,
+			float arcWidth, float arcHeight) {
+		RoundRectangle2D r = new RoundRectangle2D.Double(
+				x, y, width, height, arcWidth, arcHeight);
+		this.target.draw(r);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void drawRoundRect(int x, int y, int width, int height,
+			int arcWidth, int arcHeight) {
+		drawRoundRect((float)x, (float)y, (float)width, (float)height, (float)arcWidth, (float)arcHeight);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void fillRoundRect(float x, float y, float width, float height,
+			float arcWidth, float arcHeight) {
+		RoundRectangle2D r = new RoundRectangle2D.Double(
+				x, y, width, height, arcWidth, arcHeight);
+		this.target.fill(r);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void fillRoundRect(int x, int y, int width, int height,
+			int arcWidth, int arcHeight) {
+		drawRoundRect((float)x, (float)y, (float)width, (float)height, (float)arcWidth, (float)arcHeight);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void drawOval(float x, float y, float width, float height) {
+		Ellipse2D o = new Ellipse2D.Double(x, y, width, height);
+		this.target.draw(o);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void drawOval(int x, int y, int width, int height) {
+		drawOval((float)x, (float)y, (float)width, (float)height);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void fillOval(float x, float y, float width, float height) {
+		Ellipse2D o = new Ellipse2D.Double(x, y, width, height);
+		this.target.fill(o);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void fillOval(int x, int y, int width, int height) {
+		fillOval((float)x, (float)y, (float)width, (float)height);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void drawArc(float x, float y, float width, float height,
+			float startAngle, float arcAngle) {
+		Arc2D a = new Arc2D.Double(x, y, width, height, startAngle, arcAngle,
+				Arc2D.OPEN);
+		this.target.draw(a);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void drawArc(int x, int y, int width, int height, int startAngle,
+			int arcAngle) {
+		drawArc((float)x, (float)y, (float)width, (float)height, (float)startAngle, (float)arcAngle);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void fillArc(float x, float y, float width, float height,
+			float startAngle, float arcAngle) {
+		Arc2D a = new Arc2D.Double(x, y, width, height, startAngle, arcAngle,
+				Arc2D.OPEN);
+		this.target.fill(a);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void fillArc(int x, int y, int width, int height, int startAngle,
+			int arcAngle) {
+		fillArc((float)x, (float)y, (float)width, (float)height, (float)startAngle, (float)arcAngle);
+	}
+	
+	/** Cast the specified x coordinate for poly-elements.
+	 * 
+	 * @param x
+	 * @return the casted x
+	 */
+	protected abstract float polyX(float x);
+
+	/** Cast the specified y coordinate for poly-elements.
+	 * 
+	 * @param y
+	 * @return the casted y
+	 */
+	protected abstract float polyY(float y);
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void drawPolyline(float[] xPoints, float[] yPoints, int nPoints) {
+		if (nPoints>0) {
+			GeneralPath path = new GeneralPath();
+			path.moveTo(xPoints[0], yPoints[0]);
+			for(int i=1; i<nPoints; ++i) {
+				path.moveTo(polyX(xPoints[i]), polyY(yPoints[i]));
+			}
+			this.target.draw(path);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) {
+		if (nPoints>0) {
+			GeneralPath path = new GeneralPath();
+			path.moveTo(xPoints[0], yPoints[0]);
+			for(int i=1; i<nPoints; ++i) {
+				path.moveTo(polyX(xPoints[i]), polyY(yPoints[i]));
+			}
+			this.target.draw(path);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void drawPolygon(float[] xPoints, float[] yPoints, int nPoints) {
+		if (nPoints>0) {
+			GeneralPath path = new GeneralPath();
+			path.moveTo(xPoints[0], yPoints[0]);
+			for(int i=1; i<nPoints; ++i) {
+				path.moveTo(polyX(xPoints[i]), polyY(yPoints[i]));
+			}
+			path.closePath();
+			this.target.draw(path);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void fillPolygon(float[] xPoints, float[] yPoints, int nPoints) {
+		if (nPoints>0) {
+			GeneralPath path = new GeneralPath();
+			path.moveTo(xPoints[0], yPoints[0]);
+			for(int i=1; i<nPoints; ++i) {
+				path.moveTo(polyX(xPoints[i]), polyY(yPoints[i]));
+			}
+			path.closePath();
+			this.target.fill(path);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) {
+		if (nPoints>0) {
+			GeneralPath path = new GeneralPath();
+			path.moveTo(xPoints[0], yPoints[0]);
+			for(int i=1; i<nPoints; ++i) {
+				path.moveTo(polyX(xPoints[i]), polyY(yPoints[i]));
+			}
+			path.closePath();
+			this.target.draw(path);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) {
+		if (nPoints>0) {
+			GeneralPath path = new GeneralPath();
+			path.moveTo(xPoints[0], yPoints[0]);
+			for(int i=1; i<nPoints; ++i) {
+				path.moveTo(polyX(xPoints[i]), polyY(yPoints[i]));
+			}
+			path.closePath();
+			this.target.fill(path);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void draw(Shape s) {
+		this.target.draw(s);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) {
+		return this.target.drawImage(img, xform, obs);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
+		this.target.drawRenderedImage(img, xform);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
+		this.target.drawRenderableImage(img, xform);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void drawString(String str, int x, int y) {
+		drawString(str, (float)x, (float)y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void drawString(String str, float x, float y) {
+		this.target.drawString(str, x, y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void drawString(AttributedCharacterIterator iterator, int x, int y) {
+		drawString(iterator, (float)x, (float)y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void drawString(AttributedCharacterIterator iterator, float x,
+			float y) {
+		this.target.drawString(iterator, x, y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void fill(Shape s) {
+		this.target.fill(s);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void translate(int x, int y) {
+		translate((float)x, (float)y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void translate(double x, double y) {
+		translate((float)x, (float)y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void translate(float tx, float ty) {
+		this.target.translate(tx, ty);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clip(Shape s) {
+		this.target.clip(s);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Font getFont() {
+		return this.target.getFont();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setFont(Font font) {
+		this.target.setFont(font);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public FontMetrics getFontMetrics(Font f) {
+		return this.target.getFontMetrics(f);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final Rectangle getClipBounds() {
+		return getClip().getBounds();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Shape getClip() {
+		return this.target.getClip();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setClip(Shape clip) {
+		this.target.setClip(clip);
+	}
+
+	////////////////////////////////////////////////////////////
+	// LODGraphics2D API
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void drawPoint(float x, float y) {
+		drawPoint(x, y, 1);
+	}
+	
+	/** Draw a point.
+	 * 
+	 * @param x
+	 * @param y
+	 * @param pointSizeInPixel is the size of the point in pixels.
+	 */
+	protected void drawPoint(float x, float y, float pointSizeInPixel) {
+		float s = pointSizeInPixel/2f;
+		fillRect(x-s, y-s, pointSizeInPixel, pointSizeInPixel);
+	}
+
+	/** Compute the bounds of a cartridge which is containing
+	 * the given text.
+	 * 
+	 * @param lines is the text to put in the cartridge.
+	 * @param x is the position of the upper left corner.
+	 * @param y is the position of the upper left corner.
+	 * @param margin is the margin around the text.
+	 * @param bounds are the output bounds.
+	 */
+	protected final void cartridgeBounds(String[] lines, float x, float y, float margin, Rectangle2D bounds) {
+		float px = x;
+		float py = y;
+		
+		FontMetrics metrics = this.target.getFontMetrics();
+		
+		float width, maxWidth = 0;
+		float lineHeight = metrics.getHeight();
+		float height = lineHeight * lines.length + 2*margin;
+		
+		for(String line : lines) {
+			width = metrics.stringWidth(line);
+			if (width>maxWidth) maxWidth = width;
+		}
+		
+		maxWidth += 2*margin;
+
+		Dimension2D screen = getTargetComponentSize();
+		float d = px + maxWidth - (float)screen.getWidth();
+		if (d>0) px -= d;
+		d = py + height - (float)screen.getHeight();
+		if (d>0) py -= d;
+		if (px<0) px = 0;
+		if (py<0) py = 0;
+		
+		bounds.setRect(px,py,maxWidth,height);
+	}
+
+	/** Draw a string inside a cartridge.
+	 * <p>
+	 * A cartridge is a text surround by a box. The LODGraphics2D object
+	 * ensures that the cartridges do not overlaps and tries to move
+	 * them if it is the case.
+	 * 
+	 * @param lines is the text inside the cartridge.
+	 * @param bounds is the position and dimension of the cartridge.
+	 * @param margin is the margin to add into the cartridge box.
+	 * @param backgroundColor is the color of the background, or <code>null</code> for transparent background.
+	 * @param textColor is the color of the text, or <code>null</code> for the current LODGraphics2D color.
+	 * @param borderColor is the color of the cartridge border, or <code>null</code> for transparent borders.
+	 * @param alignment is the alignment of the text in the cartridge
+	 */
+	protected final void drawCartridge(String[] lines, Rectangle2D bounds, float margin, Color textColor, Color backgroundColor, Color borderColor, TextAlignment alignment) {
+		Color tColor = textColor;
+		
+		if (this.cartridges==null) {
+			this.cartridges = new CartridgeNode(bounds);
+		}
+		else {
+			this.cartridges.add(bounds);
+		}
+				
+		if (tColor==null) tColor = this.target.getColor();
+		
+		if (backgroundColor!=null) {
+			this.target.setColor(backgroundColor);
+			this.target.fill(bounds);
+		}
+
+		if (borderColor!=null) {
+			this.target.setColor(borderColor);
+			this.target.draw(bounds);
+		}
+
+		this.target.setColor(tColor);
+		
+		float ix = (float)bounds.getMinX();
+		float iy = (float)bounds.getMinY();
+		float iix = ix;
+		switch(alignment) {
+		case CENTER_ALIGN:
+			ix = (int)(ix + bounds.getWidth()/2);
+			break;
+		case RIGHT_ALIGN:
+			ix = (int)(ix + bounds.getWidth() - margin);
+			break;
+		default:
+			iix = ix + margin;
+			break;
+		}
+		
+		FontMetrics metrics = this.target.getFontMetrics();
+
+		for(String line : lines) {
+			iy += metrics.getHeight();
+			switch(alignment) {
+			case CENTER_ALIGN:
+				iix = ix - metrics.stringWidth(line)/2;
+				break;
+			case RIGHT_ALIGN:
+				iix = ix - metrics.stringWidth(line);
+				break;
+			default:
+			}
+			this.target.drawString(line, iix, iy);
+		}
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void drawCartridge(String text, float x, float y, Color textColor, Color backgroundColor, Color borderColor, TextAlignment alignment) {
+		String[] lines = text.split("\n"); //$NON-NLS-1$
+		float margin = (backgroundColor!=null || borderColor!=null) ? CARTRIDGE_MARGIN : 0;
+		Rectangle2D rectangle = new Rectangle2D.Double();
+		cartridgeBounds(lines, x, y, margin, rectangle);
+		drawCartridge(lines, rectangle, margin, textColor, backgroundColor, borderColor, alignment);
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void drawCartridge(String text, float x, float y) {
+		drawCartridge(text, x, y, null, null, null, TextAlignment.LEFT_ALIGN);
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void drawCartridge(String text, float x, float y, Color backgroundColor, Color borderColor) {
+		drawCartridge(text, x, y, null, backgroundColor, borderColor, TextAlignment.LEFT_ALIGN);
+	}
+		
+	/** R-Tree node for cartridge storage
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class CartridgeNode {
+		
+		private final Rectangle2D data;
+		private CartridgeNode[] children = new CartridgeNode[8];
+		
+		public CartridgeNode(Rectangle2D data) {
+			this.data = data;
+		}
+
+		public void add(Rectangle2D nData) {
+			assert(nData!=null);
+			if (nData==this.data)
+				return; // special case of the rectangle added many times.
+			int childIdx = getChildIndex(nData);
+			if (childIdx==-1) {
+				AwtUtil.avoidCollision(nData, this.data);
+				childIdx = getChildIndex(nData);
+			}
+			if (this.children[childIdx]==null) {
+				this.children[childIdx] = new CartridgeNode(nData);
+			}
+			else {
+				this.children[childIdx].add(nData);
+			}
+		}
+		
+		private int getChildIndex(Rectangle2D newData) {
+			if (newData.getMaxX() < this.data.getMinX()) {
+				if (newData.getMaxY() <= this.data.getMinY()) return 0;
+				if (newData.getMinY() >= this.data.getMaxY()) return 1;
+				return 2;
+			}
+			if (newData.getMinX() > this.data.getMaxX()) {
+				if (newData.getMaxY() <= this.data.getMinY()) return 3;
+				if (newData.getMinY() >= this.data.getMaxY()) return 4;
+				return 5;
+			}
+			if (newData.getMaxY() <= this.data.getMinY()) return 6;
+			if (newData.getMinY() >= this.data.getMaxY()) return 7;
+			return -1;
+		}
+		
+	}
+
+}

Added: trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/AwtUtil.java
===================================================================
--- trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/AwtUtil.java	                        (rev 0)
+++ trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/AwtUtil.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,434 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.awt;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.awt.font.FontRenderContext;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Dimension2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.FilteredImageSource;
+import java.awt.image.ImageObserver;
+import java.awt.image.ImageProducer;
+import java.awt.image.RGBImageFilter;
+
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+
+import org.arakhne.afc.math.continous.object2d.Vector2f;
+import org.arakhne.afc.math.generic.Vector2D;
+import org.arakhne.afc.ui.MouseCursor;
+
+/** Swing-oriented utilities. 
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AwtUtil {
+
+	private static FontRenderContext frc =  null;
+
+	/** Replies the AWT identifier of the mouse cursor.
+	 * 
+	 * @param cursor
+	 * @return the AWT identifier of the mouse cursor.
+	 */
+	public static int getAWTCursorId(MouseCursor cursor) {
+		switch(cursor) {
+		case CROSSHAIR:
+			return Cursor.CROSSHAIR_CURSOR;
+		case TEXT:
+			return Cursor.TEXT_CURSOR;
+		case WAIT:
+			return Cursor.WAIT_CURSOR;
+		case SW_RESIZE:
+			return Cursor.SW_RESIZE_CURSOR;
+		case SE_RESIZE:
+			return Cursor.SE_RESIZE_CURSOR;
+		case NW_RESIZE:
+			return Cursor.NW_RESIZE_CURSOR;
+		case NE_RESIZE:
+			return Cursor.NE_RESIZE_CURSOR;
+		case N_RESIZE:
+			return Cursor.N_RESIZE_CURSOR;
+		case S_RESIZE:
+			return Cursor.S_RESIZE_CURSOR;
+		case W_RESIZE:
+			return Cursor.W_RESIZE_CURSOR;
+		case E_RESIZE:
+			return Cursor.E_RESIZE_CURSOR;
+		case HAND:
+			return Cursor.HAND_CURSOR;
+		case MOVE:
+			return Cursor.MOVE_CURSOR;
+		case INVALID:
+			return Cursor.CUSTOM_CURSOR;
+		case DEFAULT:
+		default:
+		}
+		return Cursor.DEFAULT_CURSOR;
+	}
+
+	/** Replies the AWT mouse cursor.
+	 * 
+	 * @param cursor
+	 * @return the AWT mouse cursor.
+	 */
+	public static Cursor getCursor(MouseCursor cursor) {
+		if (cursor==null || cursor==MouseCursor.DEFAULT)
+			return Cursor.getDefaultCursor();
+		if (cursor==MouseCursor.INVALID) {
+			try {
+				return Cursor.getSystemCustomCursor("Invalid.32x32"); //$NON-NLS-1$
+			}
+			catch (Throwable _) {
+				return Cursor.getDefaultCursor();
+			}
+		}
+		return Cursor.getPredefinedCursor(getAWTCursorId(cursor));
+	}
+
+	/** Compute and reply a transparent version of the given color.
+	 * 
+	 * @param c
+	 * @return the transparent color.
+	 */
+	public static Color makeTransparentColor(Color c) {
+		int alpha = c.getAlpha() / 2;
+		return new Color(
+				c.getRed(), c.getGreen(), c.getBlue(),
+				alpha);
+	}
+
+	/** Replies the current font render context.
+	 * 
+	 * @return the current font render context.
+	 */
+	public static FontRenderContext getVectorFontRenderContext() {
+		synchronized(AwtUtil.class) {
+			FontRenderContext c = frc;
+			if (c==null) {
+				c = new FontRenderContext(new AffineTransform(), true, true);
+				frc = c;
+			}
+			return c;
+		}
+	}
+
+	/** Move the first specified rectangle to avoid collision 
+	 * with the reference.
+	 * 
+	 * @param rectangleToMove is the rectangle to move.
+	 * @param reference is the rectangle to avoid collision with.
+	 * @return the displacement vector.
+	 */
+	public static Vector2D avoidCollision(Rectangle2D rectangleToMove, Rectangle2D reference) {
+		double dx1 = reference.getMaxX() - rectangleToMove.getMinX();
+		double dx2 = rectangleToMove.getMaxX() - reference.getMinX();
+		double dy1 = reference.getMaxY() - rectangleToMove.getMinY();
+		double dy2 = rectangleToMove.getMaxY() - reference.getMinY();
+
+		double absdx1 = Math.abs(dx1);
+		double absdx2 = Math.abs(dx2);
+		double absdy1 = Math.abs(dy1);
+		double absdy2 = Math.abs(dy2);
+
+		double dx = 0;
+		double dy = 0;
+
+		if (dx1>=0 && absdx1<=absdx2 && absdx1<=absdy1 && absdx1<=absdy2) {
+			// Move according to dx1
+			dx = dx1; 
+		}
+		else if (dx2>=0 && absdx2<=absdx1 && absdx2<=absdy1 && absdx2<=absdy2) {
+			// Move according to dx2
+			dx = - dx2;
+		}
+		else if (dy1>=0 && absdy1<=absdx1 && absdy1<=absdx2 && absdy1<=absdy2) {
+			// Move according to dy1
+			dy = dy1; 
+		}
+		else {
+			// Move according to dy2
+			dy = - dy2;
+		}
+
+		rectangleToMove.setFrame(
+				rectangleToMove.getMinX()+dx,
+				rectangleToMove.getMinY()+dy,
+				rectangleToMove.getWidth(),
+				rectangleToMove.getHeight());
+
+		return new Vector2f((float)dx, (float)dy);
+	}
+
+	/** Move the first specified rectangle to avoid collision 
+	 * with the reference.
+	 * 
+	 * @param rectangleToMove is the rectangle to move.
+	 * @param reference is the rectangle to avoid collision with.
+	 * @param displacementDirection is the direction of the allowed displacement.
+	 * @return the displacement vector.
+	 */
+	public static Vector2D avoidCollision(Rectangle2D rectangleToMove, Rectangle2D reference, Vector2D displacementDirection) {
+		if (displacementDirection==null || displacementDirection.lengthSquared()==0f)
+			return avoidCollision(rectangleToMove, reference);
+
+		double dx1 = reference.getMaxX() - rectangleToMove.getMinX();
+		double dx2 = reference.getMinX() - rectangleToMove.getMaxX();
+		double dy1 = reference.getMaxY() - rectangleToMove.getMinY();
+		double dy2 = reference.getMinY() - rectangleToMove.getMaxY();
+
+		double absdx1 = Math.abs(dx1);
+		double absdx2 = Math.abs(dx2);
+		double absdy1 = Math.abs(dy1);
+		double absdy2 = Math.abs(dy2);
+
+		double dx, dy;
+
+		if (displacementDirection.getX()<0) {
+			dx = -Math.min(absdx1, absdx2);
+		}
+		else {
+			dx = Math.min(absdx1, absdx2);
+		}
+
+		if (displacementDirection.getY()<0) {
+			dy = -Math.min(absdy1, absdy2);
+		}
+		else {
+			dy = Math.min(absdy1, absdy2);
+		}
+
+		rectangleToMove.setFrame(
+				rectangleToMove.getMinX()+dx,
+				rectangleToMove.getMinY()+dy,
+				rectangleToMove.getWidth(),
+				rectangleToMove.getHeight());
+
+		displacementDirection.set((float)dx, (float)dy);
+		return displacementDirection;
+	}
+
+	/** Replies the specified image filtered with the given transparency amount.
+	 * <p>
+	 * If this function replies <code>null</code>, you might invokes
+	 * {@link #getTransparencyFilteredImage(Image, float)}
+	 * with a not-<code>null</code> component parameter to obtain an
+	 * image.
+	 * 
+	 * @param image is the image to filter.
+	 * @param alpha indicates how the alpha-component of the image is changed.
+	 * The value is in <code>[-1;1]</code>.
+	 * A value of <code>-1</code> means that the alpha-component is set to none.
+	 * A value of <code>1</code> means that the alpha-component is completely
+	 * set. A value of <code>0</code> means that the alpha-component
+	 * remains the same. 
+	 * @return the filtered image or <code>null</code>.
+	 */
+	public static Image getTransparencyFilteredImage(Image image, float alpha) {
+		if (image==null) return null;
+		return TransparencyFilter.createFilteredImage(
+				image,
+				alpha);
+	}
+
+	/** Scale the given image.
+	 * 
+	 * @param image is the image to scale.
+	 * @param width is the new width.
+	 * @param height is the new height.
+	 * @param observer is invoked when the image becomes ready to use.
+	 * @return the scaled image; or <code>null</code> if the image is not ready or
+	 * not computable.
+	 */
+	public static Image computeScaledImage(ImageIcon image, int width, int height, ImageObserver observer) {
+		if (image==null) return null;
+		Image imageToScale = extractIconImage(null, image);
+		if (imageToScale==null) return null;
+		int currentWidth = imageToScale.getWidth(observer);
+		if (currentWidth<0) return null;
+		int currentHeight = imageToScale.getHeight(observer);
+		if (currentHeight<0) return null;
+		Dimension2D d = computeBothScaledSize(
+				currentWidth, currentHeight, width, height);
+		if (d==null) return imageToScale; // Scaling not necessary
+		return imageToScale.getScaledInstance(
+				(int)d.getWidth(),(int)d.getHeight(),Image.SCALE_SMOOTH);
+	}
+
+	/** Extract an image from the specified icon.
+	 * 
+	 * @param component
+	 * @param icon
+	 * @return the image.
+	 */
+	public static Image extractIconImage(Component component, Icon icon) {
+		Image source;
+		if (icon instanceof ImageIcon) {
+			source = ((ImageIcon)icon).getImage();
+		}
+		else if (component!=null) {
+			source = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
+			Graphics2D g = (Graphics2D)source.getGraphics();
+			icon.paintIcon(component, g, 0, 0);
+			g.dispose();
+		}
+		else {
+			source = null;
+		}
+		return source;
+	}
+
+	/** Scale the given image.
+	 * 
+	 * @param image is the image to scale.
+	 * @param width is the new width.
+	 * @param height is the new height.
+	 * @param observer is notified when the image cannot be loaded.
+	 * @return the scaled image; or <code>null</code> if the image cannot be loaded.
+	 */
+	public static Image computeScaledImage(Image image, int width, int height, ImageObserver observer) {
+		if (image==null) return null;
+		int currentWidth = image.getWidth(observer);
+		if (currentWidth<0) return null;
+		int currentHeight = image.getHeight(observer);
+		if (currentHeight<0) return null;
+		Dimension2D d = computeBothScaledSize(
+				currentWidth, currentHeight, 
+				width, height);
+		if (d==null) return image; // scaling is not necessary
+		return image.getScaledInstance(
+				(int)d.getWidth(),(int)d.getHeight(),Image.SCALE_SMOOTH);
+	}
+
+	private static Dimension2D computeBothScaledSize(int currentWidth, int currentHeight, int desiredWidth, int desiredHeight) {
+		int targetWidth = desiredWidth;
+		int targetHeight = desiredHeight;
+
+		if (targetWidth==-1) targetWidth = currentWidth;
+		if (targetHeight==-1) targetHeight = currentHeight;
+
+		if (targetWidth==currentWidth && targetHeight==currentHeight)
+			return null;
+
+		float wscale = (float)targetWidth / (float)currentWidth; 
+		float hscale = (float)targetHeight / (float)currentHeight;
+		if (hscale<wscale) {
+			int nwidth = (int)(currentWidth * hscale);
+			if (nwidth>targetWidth) targetHeight = -1;
+			else targetWidth = -1;					
+		}
+		else {
+			int nheight = (int)(currentHeight * wscale);
+			if (nheight>targetHeight) targetWidth = -1;
+			else targetHeight = -1;					
+		}
+		return new DoubleDimension(targetWidth,targetHeight);
+	}
+
+	/** This class permits to filter the alpha component of an icon.
+	 * 
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public static class TransparencyFilter extends RGBImageFilter {
+
+		private final float alpha;
+
+		/**
+		 * Creates a alpha-filtered image
+		 * 
+		 * @param i is the image to filter
+		 * @param alpha indicates how the alpha-component of the icon is changed.
+		 * The value is in <code>[-1;1]</code>.
+		 * A value of <code>-1</code> means that the alpha-component is set to none.
+		 * A value of <code>1</code> means that the alpha-component is completely
+		 * set. A value of <code>0</code> means that the alpha-component
+		 * remains the same. 
+		 * @return the result of the filtering.
+		 */
+		public static Image createFilteredImage(Image i, float alpha) {
+			TransparencyFilter filter = new TransparencyFilter(alpha);
+			ImageProducer prod = new FilteredImageSource(i.getSource(), filter);
+			Image filteredImage = Toolkit.getDefaultToolkit().createImage(prod);
+			return filteredImage;
+		}
+
+		/**
+		 * Constructs an TransparencyFilter object that filters a color image to a 
+		 * alphaed image. 
+		 *
+		 * @param alpha indicates how the alpha-component of the icon is changed.
+		 * The value is in <code>[-1;1]</code>.
+		 * A value of <code>-1</code> means that the alpha-component is set to none.
+		 * A value of <code>1</code> means that the alpha-component is completely
+		 * set. A value of <code>0</code> means that the alpha-component
+		 * remains the same. 
+		 */
+		public TransparencyFilter(float alpha) {
+			float f = alpha;
+			if (f<-1f) f = -1f;
+			else if (f>1f) f = 1f;
+			assert(f>=-1f && f<=1f);
+			this.alpha = -f;
+
+			// canFilterIndexColorModel indicates whether or not it is acceptable
+			// to apply the color filtering of the filterRGB method to the color
+			// table entries of an IndexColorModel object in lieu of pixel by pixel
+			// filtering.
+			this.canFilterIndexColorModel = true;
+		}
+
+		/**
+		 * Filter the specified color.
+		 */
+		@Override
+		public int filterRGB(int x, int y, int rgb) {
+			int color_a = (rgb >> 24) & 0xFF; 
+			int color_rgb = rgb & 0x00FFFFFF; 
+
+			if (this.alpha<0f) {
+				color_a += color_a * this.alpha;
+			}
+			else {
+				color_a += (256-color_a) * this.alpha;
+			}
+
+			if (color_a<0) color_a = 0;
+			else if (color_a>255) color_a = 255;
+
+			return ((color_a << 24) & 0xFF000000) | color_rgb;
+		}
+
+	} // class AlphaFilter
+
+}

Added: trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/DefaultLODGraphics2D.java
===================================================================
--- trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/DefaultLODGraphics2D.java	                        (rev 0)
+++ trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/DefaultLODGraphics2D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,149 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.awt;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Path2D;
+import java.awt.geom.PathIterator;
+
+import org.arakhne.afc.ui.Graphics2DLOD;
+
+/** This graphic context permits to display
+ *  something with a level of details.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class DefaultLODGraphics2D extends AbstractLODGraphics2D<Graphics2D> {
+
+	////////////////////////////////////////////////////////////
+	// Constructor
+
+	/** Construct a new ZoomableGraphics2D.
+	 *
+	 * @param target is the graphics in which draws will be done.
+	 * @param target_component is the target component (used to display an icon).
+	 * @param antialiasing permits to force the anti-aliasing flag for the target graphical context
+	 * @param is_for_printing indicates if this graphics environment is for printing or not.
+	 * @param lod indicates the desired LOD used by this graphical context.
+	 */
+	public DefaultLODGraphics2D(Graphics2D target, Component target_component, boolean antialiasing, boolean is_for_printing, Graphics2DLOD lod) {
+		super(target, target_component, antialiasing, is_for_printing, lod);
+	}
+
+	/** Construct a new ZoomableGraphics2D.
+	 *
+	 * @param target is the graphics in which draws will be done.
+	 * @param target_component is the target component (used to display an icon).
+	 * @param antialiasing permits to force the anti-aliasing flag for the target graphical context
+	 * @param lod indicates the desired LOD used by this graphical context.
+	 */
+	public DefaultLODGraphics2D(Graphics2D target, Component target_component, boolean antialiasing, Graphics2DLOD lod) {
+		super(target, target_component, antialiasing, lod);
+	}
+
+	/** Construct a new ZoomableGraphics2D.
+	 *
+	 * @param target is the graphics in which draws will be done.
+	 * @param target_component is the target component (used to display an icon).
+	 * @param lod indicates the desired LOD used by this graphical context.
+	 */
+	public DefaultLODGraphics2D(Graphics2D target, Component target_component, Graphics2DLOD lod) {
+		super(target, target_component, lod);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Graphics create() {
+		return new DefaultLODGraphics2D(
+				(Graphics2D)this.target.create(),
+				this.targetComponent.get(),
+				isAntiAliased(),
+				isPrinting(),
+				getLOD());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected float polyX(float x) {
+		return x;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected float polyY(float y) {
+		return y;
+	}
+	
+	private static Path2D toPath(PathIterator path) {
+		GeneralPath gp = new GeneralPath(path.getWindingRule());
+		float[] coords = new float[6];
+		while (!path.isDone()) {
+			switch(path.currentSegment(coords)) {
+			case PathIterator.SEG_MOVETO:
+				gp.moveTo(coords[0], coords[1]);
+				break;
+			case PathIterator.SEG_LINETO:
+				gp.lineTo(coords[0], coords[1]);
+				break;
+			case PathIterator.SEG_QUADTO:
+				gp.quadTo(coords[0], coords[1], coords[2], coords[3]);
+				break;
+			case PathIterator.SEG_CUBICTO:
+				gp.curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
+				break;
+			case PathIterator.SEG_CLOSE:
+				gp.closePath();
+				break;
+			default:
+			}
+			path.next();
+		}
+		return gp;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void draw(PathIterator pi) {
+		this.target.draw(toPath(pi));
+	}
+
+	@Override
+	public void fill(PathIterator pi) {
+		this.target.fill(toPath(pi));
+	}
+
+}

Added: trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/DoubleDimension.java
===================================================================
--- trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/DoubleDimension.java	                        (rev 0)
+++ trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/DoubleDimension.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,86 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.awt;
+
+import java.awt.geom.Dimension2D;
+
+/** This class implements a {@link Dimension2D} with
+ * double precision floating point values. 
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class DoubleDimension extends Dimension2D {
+
+	private double width;
+	private double height;
+
+	/**
+	 */
+	public DoubleDimension() {
+		this.width = this.height = 0.;
+	}
+
+	/**
+	 * @param w
+	 * @param h
+	 */
+	public DoubleDimension(double w, double h) {
+		this.width = w;
+		this.height = h;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public double getWidth() {
+		return this.width;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public double getHeight() {
+		return this.height;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setSize(double width, double height) {
+		this.width = width;
+		this.height = height;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		return "(w="+this.width+";h="+this.height+")";   //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
+	}
+
+}

Added: trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/ExceptionListener.java
===================================================================
--- trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/ExceptionListener.java	                        (rev 0)
+++ trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/ExceptionListener.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,45 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.awt;
+
+import java.util.EventListener;
+
+/** Listener on exceptions. 
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface ExceptionListener extends EventListener {
+
+	/** Invoked when an exception was thrown.
+	 * <p>
+	 * If all of the listeners reply <code>false</code>, the default
+	 * catching behavior is executed: the exception is forwaded to the VM.
+	 * 
+	 * @param exception is the thrown exception
+	 * @return <code>true</code> if the exception is treated in the listener;
+	 * <code>false</code> if the exception is not treated in the listener.
+	 */
+	public boolean exceptionThrown(Throwable exception);
+
+}

Added: trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/FloatDimension.java
===================================================================
--- trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/FloatDimension.java	                        (rev 0)
+++ trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/FloatDimension.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,105 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.awt;
+
+import java.awt.geom.Dimension2D;
+
+import org.arakhne.afc.util.HashCodeUtil;
+
+/** This class implements a {@link Dimension2D} with
+ * singgle precision floating point values. 
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class FloatDimension extends Dimension2D {
+
+	private float width;
+	private float height;
+
+	/**
+	 */
+	public FloatDimension() {
+		this.width = this.height = 0f;
+	}
+
+	/**
+	 * @param w
+	 * @param h
+	 */
+	public FloatDimension(float w, float h) {
+		this.width = w;
+		this.height = h;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public double getWidth() {
+		return this.width;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public double getHeight() {
+		return this.height;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setSize(double width, double height) {
+		this.width = (float)width;
+		this.height = (float)height;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		return "(w="+this.width+";h="+this.height+")";   //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if (obj==this) return true;
+		if (obj instanceof Dimension2D) {
+			Dimension2D d = (Dimension2D)obj;
+			return d.getWidth()==getWidth() && d.getHeight()==getHeight();
+		}
+		return false;
+	}
+	
+	@Override
+	public int hashCode() {
+		int h = HashCodeUtil.hash(this.width);
+		h = HashCodeUtil.hash(h, this.height);
+		return h;
+	}
+
+}

Added: trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/FontComparator.java
===================================================================
--- trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/FontComparator.java	                        (rev 0)
+++ trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/FontComparator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,53 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.awt;
+
+import java.awt.Font;
+import java.util.Comparator;
+
+/** Comparator of fonts.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class FontComparator implements Comparator<Font> {
+
+	/**
+	 */
+	public FontComparator() {
+		//
+	}
+
+	@Override
+	public int compare(Font o1, Font o2) {
+		if (o1==o2) return 0;
+		if (o1==null) return Integer.MIN_VALUE;
+		if (o2==null) return Integer.MAX_VALUE;
+		int cmp = o1.getFontName().compareToIgnoreCase(o2.getFontName());
+		if (cmp!=0) return cmp;
+		return Float.compare(o1.getSize2D(), o2.getSize2D());
+	}
+
+}

Added: trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/LODGraphics2D.java
===================================================================
--- trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/LODGraphics2D.java	                        (rev 0)
+++ trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/LODGraphics2D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,1622 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+
+package org.arakhne.afc.ui.awt;
+
+
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.GraphicsConfiguration;
+import java.awt.Image;
+import java.awt.Paint;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.RenderingHints.Key;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.font.TextAttribute;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Dimension2D;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ImageObserver;
+import java.awt.image.RenderedImage;
+import java.awt.image.renderable.RenderableImage;
+import java.net.URL;
+import java.text.AttributedCharacterIterator;
+import java.util.Map;
+
+import org.arakhne.afc.ui.Graphics2DLOD;
+import org.arakhne.afc.ui.StringAnchor;
+import org.arakhne.afc.ui.TextAlignment;
+
+/** This graphic context permits to display
+ *  something with a level of details.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface LODGraphics2D {
+
+	/** Sets the URL of an image to draw.
+	 * This URL may be used in place of the image as a reference.
+	 * 
+	 * @param url
+	 */
+	public void setImageURL(URL url);
+	
+	/** Replies the URL of an image to draw.
+	 * This URL may be used in place of the image as a reference.
+	 * 
+	 * @return url
+	 */
+	public URL getImageURL();
+
+	/** Replies the anchor for the strings to draw.
+	 * 
+	 * @return the anchor for the strings.
+	 */
+	public StringAnchor getStringAnchor();
+	
+	/** Replies the LOD.
+	 * 
+	 * @return the LOD.
+	 */
+	public Graphics2DLOD getLOD();
+
+	/** Replies if this graphical context is for printing.
+	 * 
+	 * @return <code>true</code> if this graphical context was
+	 * created for printing.
+	 */
+	public boolean isPrinting();
+
+	/** Replies if this graphical context supports anti-aliasing.
+	 * 
+	 * @return <code>true</code> if this graphical context has its
+	 * anti-aliasing flag set, otherwhise <code>false</code>.
+	 */
+	public boolean isAntiAliased();
+
+	/** Set if this graphical context supports anti-aliasing.
+	 * 
+	 * @param anti_aliased_enable is <code>true</code> to allow antio-aliased,
+	 * otherwise <code>false</code>
+	 */
+	public void setAntiAliased(boolean anti_aliased_enable);
+
+	/**
+	 * Return the size of the target component in the screen space.
+	 * 
+	 * @return a dimension or <code>null</code>
+	 * @see #getViewportRect()
+	 * @see #getViewportSize()
+	 */
+	public Dimension2D getTargetComponentSize();
+
+	/**
+	 * Return the rectangle that corresponds to the displayed
+	 * area.
+	 * 
+	 * @return the rectangle expressed in the logic coordinate space.
+	 * @see #getTargetComponentSize()
+	 * @see #getViewportSize()
+	 */
+	public Rectangle2D getViewportRect();
+
+	/**
+	 * Return the size of the viewport in the logical space.
+	 * 
+	 * @return a dimension or <code>null</code>
+	 * @see #getTargetComponentSize()
+	 * @see #getViewportRect()
+	 */
+	public Dimension2D getViewportSize();
+
+	/** Draws a pixel.
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 *
+	 * @param x is the location where to paint the pixel.
+	 * @param y is the location where to paint the pixel.
+	 */
+	public void drawPoint(float x, float y);
+
+    /** Replies if this figure could drawn or not.
+     * <p>
+     * This function assumes that a drawable figure
+     * must have a bounding box with its width and
+     * height greaters or equals to a specified size.
+     * This size depends on the adviced level of details:
+     * {@link Graphics2DLOD#LOW_LEVEL_OF_DETAIL},
+     * {@link Graphics2DLOD#NORMAL_LEVEL_OF_DETAIL}
+     * or {@link Graphics2DLOD#HIGH_LEVEL_OF_DETAIL}.
+     *
+     * @param shape is the shape to draw.
+     * @return <code>true</code> if the figure is drawable, otherwhise
+     * <code>false</code>.
+     */ 
+    public boolean hit(Shape shape);
+
+    /** Replies if this figure could drawn or not.
+     * <p>
+     * This function assumes that a drawable figure
+     * must have a bounding box with its width and
+     * height greaters or equals to a specified size.
+     * This size depends on the adviced level of details:
+     * {@link Graphics2DLOD#LOW_LEVEL_OF_DETAIL},
+     * {@link Graphics2DLOD#NORMAL_LEVEL_OF_DETAIL}
+     * or {@link Graphics2DLOD#HIGH_LEVEL_OF_DETAIL}.
+     *
+     * @param rect is the rect to draw.
+     * @return <code>true</code> if the figure is drawable, otherwhise
+     * <code>false</code>.
+     */ 
+    public boolean hit(Rectangle2D rect);
+
+    ////////////////////////////////////////////////////////////
+	// Graphics API inherited from java.awt.Graphics
+
+	/**
+	 * Creates a new <code>Graphics</code> object that is 
+	 * a copy of this <code>Graphics</code> object.
+	 * @return     a new graphics context that is a copy of 
+	 *                       this graphics context.
+	 */
+	public abstract Graphics create();
+
+	/**
+	 * Creates a new <code>Graphics</code> object based on this 
+	 * <code>Graphics</code> object, but with a new translation and clip area.
+	 * The new <code>Graphics</code> object has its origin 
+	 * translated to the specified point (<i>x</i>,&nbsp;<i>y</i>). 
+	 * Its clip area is determined by the intersection of the original
+	 * clip area with the specified rectangle.  The arguments are all
+	 * interpreted in the coordinate system of the original 
+	 * <code>Graphics</code> object. The new graphics context is 
+	 * identical to the original, except in two respects: 
+	 * <p>
+	 * <ul>
+	 * <li>
+	 * The new graphics context is translated by (<i>x</i>,&nbsp;<i>y</i>).  
+	 * That is to say, the point (<code>0</code>,&nbsp;<code>0</code>) in the 
+	 * new graphics context is the same as (<i>x</i>,&nbsp;<i>y</i>) in 
+	 * the original graphics context. 
+	 * <li>
+	 * The new graphics context has an additional clipping rectangle, in 
+	 * addition to whatever (translated) clipping rectangle it inherited 
+	 * from the original graphics context. The origin of the new clipping 
+	 * rectangle is at (<code>0</code>,&nbsp;<code>0</code>), and its size  
+	 * is specified by the <code>width</code> and <code>height</code>
+	 * arguments.
+	 * </ul>
+	 * <p>
+	 * @param      x   the <i>x</i> coordinate.
+	 * @param      y   the <i>y</i> coordinate.
+	 * @param      width   the width of the clipping rectangle.
+	 * @param      height   the height of the clipping rectangle.
+	 * @return     a new graphics context.
+	 */
+	public Graphics create(float x, float y, float width, float height);
+
+	/**
+	 * Gets this graphics context's current color.
+	 * @return    this graphics context's current color.
+	 */
+	public Color getColor();
+
+	/**
+	 * Sets this graphics context's current color to the specified 
+	 * color. All subsequent graphics operations using this graphics 
+	 * context use this specified color. 
+	 * @param     c   the new rendering color.
+	 */
+	public void setColor(Color c);
+
+	/**
+	 * Sets the paint mode of this graphics context to overwrite the 
+	 * destination with this graphics context's current color. 
+	 * This sets the logical pixel operation function to the paint or
+	 * overwrite mode.  All subsequent rendering operations will
+	 * overwrite the destination with the current color. 
+	 */
+	public void setPaintMode();
+
+	/**
+	 * Sets the paint mode of this graphics context to alternate between 
+	 * this graphics context's current color and the new specified color. 
+	 * This specifies that logical pixel operations are performed in the 
+	 * XOR mode, which alternates pixels between the current color and 
+	 * a specified XOR color. 
+	 * <p>
+	 * When drawing operations are performed, pixels which are the 
+	 * current color are changed to the specified color, and vice versa. 
+	 * <p>
+	 * Pixels that are of colors other than those two colors are changed 
+	 * in an unpredictable but reversible manner; if the same figure is 
+	 * drawn twice, then all pixels are restored to their original values. 
+	 * @param     c1 the XOR alternation color
+	 */
+	public void setXORMode(Color c1);
+
+	/**
+	 * Gets the current font.
+	 * @return    this graphics context's current font.
+	 */
+	public Font getFont();
+
+	/**
+	 * Sets this graphics context's font to the specified font. 
+	 * All subsequent text operations using this graphics context 
+	 * use this font. A null argument is silently ignored.
+	 * @param  font   the font.
+	 */
+	public void setFont(Font font);
+
+	/**
+	 * Gets the font metrics of the current font.
+	 * @return    the font metrics of this graphics 
+	 *                    context's current font.
+	 */
+	public FontMetrics getFontMetrics();
+
+	/**
+	 * Gets the font metrics for the specified font.
+	 * @return    the font metrics for the specified font.
+	 * @param     f the specified font
+	 */
+	public FontMetrics getFontMetrics(Font f);
+
+
+	/** 
+	 * Intersects the current clip with the specified rectangle.
+	 * The resulting clipping area is the intersection of the current
+	 * clipping area and the specified rectangle.  If there is no 
+	 * current clipping area, either because the clip has never been 
+	 * set, or the clip has been cleared using <code>setClip(null)</code>, 
+	 * the specified rectangle becomes the new clip.
+	 * This method sets the user clip, which is independent of the
+	 * clipping associated with device bounds and window visibility.  
+	 * This method can only be used to make the current clip smaller.
+	 * To set the current clip larger, use any of the setClip methods.
+	 * Rendering operations have no effect outside of the clipping area.
+	 * @param x the x coordinate of the rectangle to intersect the clip with
+	 * @param y the y coordinate of the rectangle to intersect the clip with
+	 * @param width the width of the rectangle to intersect the clip with
+	 * @param height the height of the rectangle to intersect the clip with
+	 */
+	public void clipRect(float x, float y, float width, float height);
+
+	/**
+	 * Sets the current clip to the rectangle specified by the given
+	 * coordinates.  This method sets the user clip, which is 
+	 * independent of the clipping associated with device bounds
+	 * and window visibility.  
+	 * Rendering operations have no effect outside of the clipping area.
+	 * @param       x the <i>x</i> coordinate of the new clip rectangle.
+	 * @param       y the <i>y</i> coordinate of the new clip rectangle.
+	 * @param       width the width of the new clip rectangle.
+	 * @param       height the height of the new clip rectangle.
+	 */
+	public void setClip(float x, float y, float width, float height);
+
+	/**
+	 * Gets the current clipping area.
+	 * This method returns the user clip, which is independent of the
+	 * clipping associated with device bounds and window visibility.
+	 * If no clip has previously been set, or if the clip has been 
+	 * cleared using <code>setClip(null)</code>, this method returns 
+	 * <code>null</code>.
+	 * @return      a <code>Shape</code> object representing the 
+	 *              current clipping area, or <code>null</code> if
+	 *              no clip is set.
+	 */
+	public Shape getClip();
+
+	/**
+	 * Sets the current clipping area to an arbitrary clip shape.
+	 * Not all objects that implement the <code>Shape</code> 
+	 * interface can be used to set the clip.  The only 
+	 * <code>Shape</code> objects that are guaranteed to be 
+	 * supported are <code>Shape</code> objects that are
+	 * obtained via the <code>getClip</code> method and via 
+	 * <code>Rectangle</code> objects.  This method sets the
+	 * user clip, which is independent of the clipping associated
+	 * with device bounds and window visibility.
+	 * @param clip the <code>Shape</code> to use to set the clip
+	 */
+	public void setClip(Shape clip);
+
+	/**
+	 * Copies an area of the component by a distance specified by 
+	 * <code>dx</code> and <code>dy</code>. From the point specified
+	 * by <code>x</code> and <code>y</code>, this method
+	 * copies downwards and to the right.  To copy an area of the 
+	 * component to the left or upwards, specify a negative value for 
+	 * <code>dx</code> or <code>dy</code>.
+	 * If a portion of the source rectangle lies outside the bounds 
+	 * of the component, or is obscured by another window or component, 
+	 * <code>copyArea</code> will be unable to copy the associated
+	 * pixels. The area that is omitted can be refreshed by calling 
+	 * the component's <code>paint</code> method.
+	 * @param       x the <i>x</i> coordinate of the source rectangle.
+	 * @param       y the <i>y</i> coordinate of the source rectangle.
+	 * @param       width the width of the source rectangle.
+	 * @param       height the height of the source rectangle.
+	 * @param       dx the horizontal distance to copy the pixels.
+	 * @param       dy the vertical distance to copy the pixels.
+	 */
+	public void copyArea(float x, float y, float width, float height, float dx, float dy);
+
+	/** 
+	 * Draws a line, using the current color, between the points 
+	 * <code>(x1,&nbsp;y1)</code> and <code>(x2,&nbsp;y2)</code> 
+	 * in this graphics context's coordinate system. 
+	 * @param   x1  the first point's <i>x</i> coordinate.
+	 * @param   y1  the first point's <i>y</i> coordinate.
+	 * @param   x2  the second point's <i>x</i> coordinate.
+	 * @param   y2  the second point's <i>y</i> coordinate.
+	 */
+	public void drawLine(float x1, float y1, float x2, float y2);
+
+	/** 
+	 * Fills the specified rectangle. 
+	 * The left and right edges of the rectangle are at 
+	 * <code>x</code> and <code>x&nbsp;+&nbsp;width&nbsp;-&nbsp;1</code>. 
+	 * The top and bottom edges are at 
+	 * <code>y</code> and <code>y&nbsp;+&nbsp;height&nbsp;-&nbsp;1</code>. 
+	 * The resulting rectangle covers an area 
+	 * <code>width</code> pixels wide by 
+	 * <code>height</code> pixels tall.
+	 * The rectangle is filled using the graphics context's current color. 
+	 * @param         x   the <i>x</i> coordinate 
+	 *                         of the rectangle to be filled.
+	 * @param         y   the <i>y</i> coordinate 
+	 *                         of the rectangle to be filled.
+	 * @param         width   the width of the rectangle to be filled.
+	 * @param         height   the height of the rectangle to be filled.
+	 */
+	public void fillRect(float x, float y, float width, float height);
+
+	/** 
+	 * Draws the outline of the specified rectangle. 
+	 * The left and right edges of the rectangle are at 
+	 * <code>x</code> and <code>x&nbsp;+&nbsp;width</code>. 
+	 * The top and bottom edges are at 
+	 * <code>y</code> and <code>y&nbsp;+&nbsp;height</code>. 
+	 * The rectangle is drawn using the graphics context's current color.
+	 * @param         x   the <i>x</i> coordinate 
+	 *                         of the rectangle to be drawn.
+	 * @param         y   the <i>y</i> coordinate 
+	 *                         of the rectangle to be drawn.
+	 * @param         width   the width of the rectangle to be drawn.
+	 * @param         height   the height of the rectangle to be drawn.
+	 */
+	public void drawRect(float x, float y, float width, float height);
+
+	/** 
+	 * Clears the specified rectangle by filling it with the background
+	 * color of the current drawing surface. This operation does not 
+	 * use the current paint mode. 
+	 * <p>
+	 * Beginning with Java&nbsp;1.1, the background color 
+	 * of offscreen images may be system dependent. Applications should 
+	 * use <code>setColor</code> followed by <code>fillRect</code> to 
+	 * ensure that an offscreen image is cleared to a specific color. 
+	 * @param       x the <i>x</i> coordinate of the rectangle to clear.
+	 * @param       y the <i>y</i> coordinate of the rectangle to clear.
+	 * @param       width the width of the rectangle to clear.
+	 * @param       height the height of the rectangle to clear.
+	 */
+	public void clearRect(float x, float y, float width, float height);
+
+	/** 
+	 * Draws an outlined round-cornered rectangle using this graphics 
+	 * context's current color. The left and right edges of the rectangle 
+	 * are at <code>x</code> and <code>x&nbsp;+&nbsp;width</code>, 
+	 * respectively. The top and bottom edges of the rectangle are at 
+	 * <code>y</code> and <code>y&nbsp;+&nbsp;height</code>. 
+	 * @param      x the <i>x</i> coordinate of the rectangle to be drawn.
+	 * @param      y the <i>y</i> coordinate of the rectangle to be drawn.
+	 * @param      width the width of the rectangle to be drawn.
+	 * @param      height the height of the rectangle to be drawn.
+	 * @param      arcWidth the horizontal diameter of the arc 
+	 *                    at the four corners.
+	 * @param      arcHeight the vertical diameter of the arc 
+	 *                    at the four corners.
+	 */
+	public void drawRoundRect(float x, float y, float width, float height, float arcWidth, float arcHeight);
+
+	/** 
+	 * Fills the specified rounded corner rectangle with the current color.
+	 * The left and right edges of the rectangle 
+	 * are at <code>x</code> and <code>x&nbsp;+&nbsp;width&nbsp;-&nbsp;1</code>, 
+	 * respectively. The top and bottom edges of the rectangle are at 
+	 * <code>y</code> and <code>y&nbsp;+&nbsp;height&nbsp;-&nbsp;1</code>. 
+	 * @param       x the <i>x</i> coordinate of the rectangle to be filled.
+	 * @param       y the <i>y</i> coordinate of the rectangle to be filled.
+	 * @param       width the width of the rectangle to be filled.
+	 * @param       height the height of the rectangle to be filled.
+	 * @param       arcWidth the horizontal diameter 
+	 *                     of the arc at the four corners.
+	 * @param       arcHeight the vertical diameter 
+	 *                     of the arc at the four corners.
+	 */
+	public void fillRoundRect(float x, float y, float width, float height, float arcWidth, float arcHeight);
+
+	/**
+	 * Draws a 3-D highlighted outline of the specified rectangle.
+	 * The edges of the rectangle are highlighted so that they
+	 * appear to be beveled and lit from the upper left corner.
+	 * <p>
+	 * The colors used for the highlighting effect are determined 
+	 * based on the current color.
+	 * The resulting rectangle covers an area that is 
+	 * <code>width&nbsp;+&nbsp;1</code> pixels wide
+	 * by <code>height&nbsp;+&nbsp;1</code> pixels tall.
+	 * @param       x the <i>x</i> coordinate of the rectangle to be drawn.
+	 * @param       y the <i>y</i> coordinate of the rectangle to be drawn.
+	 * @param       width the width of the rectangle to be drawn.
+	 * @param       height the height of the rectangle to be drawn.
+	 * @param       raised a boolean that determines whether the rectangle
+	 *                      appears to be raised above the surface 
+	 *                      or sunk into the surface.
+	 */
+	public void draw3DRect(float x, float y, float width, float height, boolean raised);    
+
+	/**
+	 * Paints a 3-D highlighted rectangle filled with the current color.
+	 * The edges of the rectangle will be highlighted so that it appears
+	 * as if the edges were beveled and lit from the upper left corner.
+	 * The colors used for the highlighting effect will be determined from
+	 * the current color.
+	 * @param       x the <i>x</i> coordinate of the rectangle to be filled.
+	 * @param       y the <i>y</i> coordinate of the rectangle to be filled.
+	 * @param       width the width of the rectangle to be filled.
+	 * @param       height the height of the rectangle to be filled.
+	 * @param       raised a boolean value that determines whether the 
+	 *                      rectangle appears to be raised above the surface 
+	 *                      or etched into the surface.
+	 */
+	public void fill3DRect(float x, float y, float width, float height, boolean raised);    
+
+	/** 
+	 * Draws the outline of an oval.
+	 * The result is a circle or ellipse that fits within the 
+	 * rectangle specified by the <code>x</code>, <code>y</code>, 
+	 * <code>width</code>, and <code>height</code> arguments. 
+	 * <p> 
+	 * The oval covers an area that is 
+	 * <code>width&nbsp;+&nbsp;1</code> pixels wide 
+	 * and <code>height&nbsp;+&nbsp;1</code> pixels tall. 
+	 * @param       x the <i>x</i> coordinate of the upper left 
+	 *                     corner of the oval to be drawn.
+	 * @param       y the <i>y</i> coordinate of the upper left 
+	 *                     corner of the oval to be drawn.
+	 * @param       width the width of the oval to be drawn.
+	 * @param       height the height of the oval to be drawn.
+	 */
+	public void drawOval(float x, float y, float width, float height);
+
+	/** 
+	 * Fills an oval bounded by the specified rectangle with the
+	 * current color.
+	 * @param       x the <i>x</i> coordinate of the upper left corner 
+	 *                     of the oval to be filled.
+	 * @param       y the <i>y</i> coordinate of the upper left corner 
+	 *                     of the oval to be filled.
+	 * @param       width the width of the oval to be filled.
+	 * @param       height the height of the oval to be filled.
+	 */
+	public void fillOval(float x, float y, float width, float height);
+
+	/**
+	 * Draws the outline of a circular or elliptical arc 
+	 * covering the specified rectangle.
+	 * <p>
+	 * The resulting arc begins at <code>startAngle</code> and extends  
+	 * for <code>arcAngle</code> degrees, using the current color.
+	 * Angles are interpreted such that 0&nbsp;degrees 
+	 * is at the 3&nbsp;o'clock position. 
+	 * A positive value indicates a counter-clockwise rotation
+	 * while a negative value indicates a clockwise rotation.
+	 * <p>
+	 * The center of the arc is the center of the rectangle whose origin 
+	 * is (<i>x</i>,&nbsp;<i>y</i>) and whose size is specified by the 
+	 * <code>width</code> and <code>height</code> arguments. 
+	 * <p>
+	 * The resulting arc covers an area 
+	 * <code>width&nbsp;+&nbsp;1</code> pixels wide
+	 * by <code>height&nbsp;+&nbsp;1</code> pixels tall.
+	 * <p>
+	 * The angles are specified relative to the non-square extents of
+	 * the bounding rectangle such that 45 degrees always falls on the
+	 * line from the center of the ellipse to the upper right corner of
+	 * the bounding rectangle. As a result, if the bounding rectangle is
+	 * noticeably longer in one axis than the other, the angles to the
+	 * start and end of the arc segment will be skewed farther along the
+	 * longer axis of the bounds.
+	 * @param        x the <i>x</i> coordinate of the 
+	 *                    upper-left corner of the arc to be drawn.
+	 * @param        y the <i>y</i>  coordinate of the 
+	 *                    upper-left corner of the arc to be drawn.
+	 * @param        width the width of the arc to be drawn.
+	 * @param        height the height of the arc to be drawn.
+	 * @param        startAngle the beginning angle.
+	 * @param        arcAngle the angular extent of the arc, 
+	 *                    relative to the start angle.
+	 */
+	public void drawArc(float x, float y, float width, float height, float startAngle, float arcAngle);
+
+	/** 
+	 * Fills a circular or elliptical arc covering the specified rectangle.
+	 * <p>
+	 * The resulting arc begins at <code>startAngle</code> and extends  
+	 * for <code>arcAngle</code> degrees.
+	 * Angles are interpreted such that 0&nbsp;degrees 
+	 * is at the 3&nbsp;o'clock position. 
+	 * A positive value indicates a counter-clockwise rotation
+	 * while a negative value indicates a clockwise rotation.
+	 * <p>
+	 * The center of the arc is the center of the rectangle whose origin 
+	 * is (<i>x</i>,&nbsp;<i>y</i>) and whose size is specified by the 
+	 * <code>width</code> and <code>height</code> arguments. 
+	 * <p>
+	 * The resulting arc covers an area 
+	 * <code>width&nbsp;+&nbsp;1</code> pixels wide
+	 * by <code>height&nbsp;+&nbsp;1</code> pixels tall.
+	 * <p>
+	 * The angles are specified relative to the non-square extents of
+	 * the bounding rectangle such that 45 degrees always falls on the
+	 * line from the center of the ellipse to the upper right corner of
+	 * the bounding rectangle. As a result, if the bounding rectangle is
+	 * noticeably longer in one axis than the other, the angles to the
+	 * start and end of the arc segment will be skewed farther along the
+	 * longer axis of the bounds.
+	 * @param        x the <i>x</i> coordinate of the 
+	 *                    upper-left corner of the arc to be filled.
+	 * @param        y the <i>y</i>  coordinate of the 
+	 *                    upper-left corner of the arc to be filled.
+	 * @param        width the width of the arc to be filled.
+	 * @param        height the height of the arc to be filled.
+	 * @param        startAngle the beginning angle.
+	 * @param        arcAngle the angular extent of the arc, 
+	 *                    relative to the start angle.
+	 */
+	public void fillArc(float x, float y, float width, float height, float startAngle, float arcAngle);
+
+	/** 
+	 * Draws a sequence of connected lines defined by 
+	 * arrays of <i>x</i> and <i>y</i> coordinates. 
+	 * Each pair of (<i>x</i>,&nbsp;<i>y</i>) coordinates defines a point.
+	 * The figure is not closed if the first point 
+	 * differs from the last point.
+	 * @param       xPoints an array of <i>x</i> points
+	 * @param       yPoints an array of <i>y</i> points
+	 * @param       nPoints the total number of points
+	 */
+	public void drawPolyline( float[] xPoints, float[] yPoints, int nPoints);
+
+	/** 
+	 * Draws a closed polygon defined by 
+	 * arrays of <i>x</i> and <i>y</i> coordinates. 
+	 * Each pair of (<i>x</i>,&nbsp;<i>y</i>) coordinates defines a point.
+	 * <p>
+	 * This method draws the polygon defined by <code>nPoint</code> line 
+	 * segments, where the first <code>nPoint&nbsp;-&nbsp;1</code> 
+	 * line segments are line segments from 
+	 * <code>(xPoints[i&nbsp;-&nbsp;1],&nbsp;yPoints[i&nbsp;-&nbsp;1])</code> 
+	 * to <code>(xPoints[i],&nbsp;yPoints[i])</code>, for 
+	 * 1&nbsp;&le;&nbsp;<i>i</i>&nbsp;&le;&nbsp;<code>nPoints</code>.  
+	 * The figure is automatically closed by drawing a line connecting
+	 * the final point to the first point, if those points are different.
+	 * @param        xPoints   a an array of <code>x</code> coordinates.
+	 * @param        yPoints   a an array of <code>y</code> coordinates.
+	 * @param        nPoints   a the total number of points.
+	 */
+	public void drawPolygon( float[] xPoints, float[] yPoints, int nPoints);
+	
+	/** 
+	 * Fills a closed polygon defined by 
+	 * arrays of <i>x</i> and <i>y</i> coordinates. 
+	 * <p>
+	 * This method draws the polygon defined by <code>nPoint</code> line 
+	 * segments, where the first <code>nPoint&nbsp;-&nbsp;1</code> 
+	 * line segments are line segments from 
+	 * <code>(xPoints[i&nbsp;-&nbsp;1],&nbsp;yPoints[i&nbsp;-&nbsp;1])</code> 
+	 * to <code>(xPoints[i],&nbsp;yPoints[i])</code>, for 
+	 * 1&nbsp;&le;&nbsp;<i>i</i>&nbsp;&le;&nbsp;<code>nPoints</code>.  
+	 * The figure is automatically closed by drawing a line connecting
+	 * the final point to the first point, if those points are different.
+	 * <p>
+	 * The area inside the polygon is defined using an 
+	 * even-odd fill rule, also known as the alternating rule.
+	 * @param        xPoints   a an array of <code>x</code> coordinates.
+	 * @param        yPoints   a an array of <code>y</code> coordinates.
+	 * @param        nPoints   a the total number of points.
+	 */
+	public void fillPolygon( float[] xPoints, float[] yPoints, int nPoints);
+
+	/** 
+	 * Draws the text given by the specified character array, using this 
+	 * graphics context's current font and color. The baseline of the 
+	 * first character is at position (<i>x</i>,&nbsp;<i>y</i>) in this 
+	 * graphics context's coordinate system. 
+	 * @param data the array of characters to be drawn
+	 * @param offset the start offset in the data
+	 * @param length the number of characters to be drawn
+	 * @param x the <i>x</i> coordinate of the baseline of the text
+	 * @param y the <i>y</i> coordinate of the baseline of the text
+	 * @throws NullPointerException if <code>data</code> is <code>null</code>.
+	 * @throws IndexOutOfBoundsException if <code>offset</code> or
+	 * <code>length</code>is less than zero, or 
+	 * <code>offset+length</code> is greater than the length of the
+	 * <code>data</code> array.
+	 */
+	public void drawChars(char data[], int offset, int length, float x, float y);
+
+	/** 
+	 * Draws the text given by the specified byte array, using this 
+	 * graphics context's current font and color. The baseline of the 
+	 * first character is at position (<i>x</i>,&nbsp;<i>y</i>) in this 
+	 * graphics context's coordinate system.
+	 * <p>
+	 * Use of this method is not recommended as each byte is interpreted
+	 * as a Unicode code point in the range 0 to 255, and so can only be
+	 * used to draw Latin characters in that range.
+	 * @param data the data to be drawn
+	 * @param offset the start offset in the data
+	 * @param length the number of bytes that are drawn
+	 * @param x the <i>x</i> coordinate of the baseline of the text
+	 * @param y the <i>y</i> coordinate of the baseline of the text
+	 * @throws NullPointerException if <code>data</code> is <code>null</code>.
+	 * @throws IndexOutOfBoundsException if <code>offset</code> or
+	 * <code>length</code>is less than zero, or <code>offset+length</code> 
+	 * is greater than the length of the <code>data</code> array.
+	 */
+	public void drawBytes(byte data[], int offset, int length, float x, float y);
+
+	/** 
+	 * Draws as much of the specified image as is currently available.
+	 * The image is drawn with its top-left corner at 
+	 * (<i>x</i>,&nbsp;<i>y</i>) in this graphics context's coordinate 
+	 * space. Transparent pixels in the image do not affect whatever 
+	 * pixels are already there. 
+	 * <p>
+	 * This method returns immediately in all cases, even if the
+	 * complete image has not yet been loaded, and it has not been dithered 
+	 * and converted for the current output device.
+	 * <p>
+	 * If the image has completely loaded and its pixels are
+	 * no longer being changed, then
+	 * <code>drawImage</code> returns <code>true</code>. 
+	 * Otherwise, <code>drawImage</code> returns <code>false</code>
+	 * and as more of
+	 * the image becomes available 
+	 * or it is time to draw another frame of animation,
+	 * the process that loads the image notifies 
+	 * the specified image observer.
+	 * @param    img the specified image to be drawn. This method does
+	 *               nothing if <code>img</code> is null.
+	 * @param    x   the <i>x</i> coordinate.
+	 * @param    y   the <i>y</i> coordinate.
+	 * @param    observer    object to be notified as more of 
+	 *                          the image is converted.
+	 * @return   <code>false</code> if the image pixels are still changing;
+	 *           <code>true</code> otherwise.
+	 */
+	public boolean drawImage(Image img, float x, float y, ImageObserver observer);
+
+	/**
+	 * Draws as much of the specified image as has already been scaled
+	 * to fit inside the specified rectangle.
+	 * <p>
+	 * The image is drawn inside the specified rectangle of this 
+	 * graphics context's coordinate space, and is scaled if 
+	 * necessary. Transparent pixels do not affect whatever pixels
+	 * are already there. 
+	 * <p>
+	 * This method returns immediately in all cases, even if the
+	 * entire image has not yet been scaled, dithered, and converted
+	 * for the current output device.
+	 * If the current output representation is not yet complete, then
+	 * <code>drawImage</code> returns <code>false</code>. As more of
+	 * the image becomes available, the process that loads the image notifies
+	 * the image observer by calling its <code>imageUpdate</code> method.
+	 * <p>
+	 * A scaled version of an image will not necessarily be
+	 * available immediately just because an unscaled version of the
+	 * image has been constructed for this output device.  Each size of
+	 * the image may be cached separately and generated from the original
+	 * data in a separate image production sequence.
+	 * @param    img    the specified image to be drawn. This method does
+	 *                  nothing if <code>img</code> is null.
+	 * @param    x      the <i>x</i> coordinate.
+	 * @param    y      the <i>y</i> coordinate.
+	 * @param    width  the width of the rectangle.
+	 * @param    height the height of the rectangle.
+	 * @param    observer    object to be notified as more of 
+	 *                          the image is converted.
+	 * @return   <code>false</code> if the image pixels are still changing;
+	 *           <code>true</code> otherwise.
+	 */
+	public boolean drawImage(Image img, float x, float y, float width, float height, ImageObserver observer);
+
+	/** 
+	 * Draws as much of the specified image as is currently available.
+	 * The image is drawn with its top-left corner at 
+	 * (<i>x</i>,&nbsp;<i>y</i>) in this graphics context's coordinate 
+	 * space.  Transparent pixels are drawn in the specified
+	 * background color.
+	 * <p> 
+	 * This operation is equivalent to filling a rectangle of the
+	 * width and height of the specified image with the given color and then
+	 * drawing the image on top of it, but possibly more efficient.
+	 * <p>
+	 * This method returns immediately in all cases, even if the
+	 * complete image has not yet been loaded, and it has not been dithered 
+	 * and converted for the current output device.
+	 * <p>
+	 * If the image has completely loaded and its pixels are
+	 * no longer being changed, then
+	 * <code>drawImage</code> returns <code>true</code>. 
+	 * Otherwise, <code>drawImage</code> returns <code>false</code>
+	 * and as more of
+	 * the image becomes available 
+	 * or it is time to draw another frame of animation,
+	 * the process that loads the image notifies 
+	 * the specified image observer.
+	 * @param    img the specified image to be drawn. This method does
+	 *               nothing if <code>img</code> is null.
+	 * @param    x      the <i>x</i> coordinate.
+	 * @param    y      the <i>y</i> coordinate.
+	 * @param    bgcolor the background color to paint under the
+	 *                         non-opaque portions of the image.
+	 * @param    observer    object to be notified as more of 
+	 *                          the image is converted.
+	 * @return   <code>false</code> if the image pixels are still changing;
+	 *           <code>true</code> otherwise.
+	 */
+	public boolean drawImage(Image img, float x, float y, Color bgcolor, ImageObserver observer);
+
+	/**
+	 * Draws as much of the specified image as has already been scaled
+	 * to fit inside the specified rectangle.
+	 * <p>
+	 * The image is drawn inside the specified rectangle of this 
+	 * graphics context's coordinate space, and is scaled if 
+	 * necessary. Transparent pixels are drawn in the specified
+	 * background color. 
+	 * This operation is equivalent to filling a rectangle of the
+	 * width and height of the specified image with the given color and then
+	 * drawing the image on top of it, but possibly more efficient.
+	 * <p>
+	 * This method returns immediately in all cases, even if the
+	 * entire image has not yet been scaled, dithered, and converted
+	 * for the current output device.
+	 * If the current output representation is not yet complete then
+	 * <code>drawImage</code> returns <code>false</code>. As more of
+	 * the image becomes available, the process that loads the image notifies 
+	 * the specified image observer.
+	 * <p>
+	 * A scaled version of an image will not necessarily be
+	 * available immediately just because an unscaled version of the
+	 * image has been constructed for this output device.  Each size of
+	 * the image may be cached separately and generated from the original
+	 * data in a separate image production sequence.
+	 * @param    img       the specified image to be drawn. This method does
+	 *                     nothing if <code>img</code> is null.
+	 * @param    x         the <i>x</i> coordinate.
+	 * @param    y         the <i>y</i> coordinate.
+	 * @param    width     the width of the rectangle.
+	 * @param    height    the height of the rectangle.
+	 * @param    bgcolor   the background color to paint under the
+	 *                         non-opaque portions of the image.
+	 * @param    observer    object to be notified as more of 
+	 *                          the image is converted.
+	 * @return   <code>false</code> if the image pixels are still changing;
+	 *           <code>true</code> otherwise.
+	 */
+	public boolean drawImage(Image img, float x, float y, float width, float height, Color bgcolor, ImageObserver observer);
+
+	/**
+	 * Draws as much of the specified area of the specified image as is
+	 * currently available, scaling it on the fly to fit inside the
+	 * specified area of the destination drawable surface. Transparent pixels 
+	 * do not affect whatever pixels are already there.
+	 * <p>
+	 * This method returns immediately in all cases, even if the
+	 * image area to be drawn has not yet been scaled, dithered, and converted
+	 * for the current output device.
+	 * If the current output representation is not yet complete then
+	 * <code>drawImage</code> returns <code>false</code>. As more of
+	 * the image becomes available, the process that loads the image notifies 
+	 * the specified image observer.
+	 * <p>
+	 * This method always uses the unscaled version of the image
+	 * to render the scaled rectangle and performs the required
+	 * scaling on the fly. It does not use a cached, scaled version
+	 * of the image for this operation. Scaling of the image from source
+	 * to destination is performed such that the first coordinate
+	 * of the source rectangle is mapped to the first coordinate of
+	 * the destination rectangle, and the second source coordinate is
+	 * mapped to the second destination coordinate. The subimage is
+	 * scaled and flipped as needed to preserve those mappings.
+	 * @param       img the specified image to be drawn. This method does
+	 *                  nothing if <code>img</code> is null.
+	 * @param       dx1 the <i>x</i> coordinate of the first corner of the
+	 *                    destination rectangle.
+	 * @param       dy1 the <i>y</i> coordinate of the first corner of the
+	 *                    destination rectangle.
+	 * @param       dx2 the <i>x</i> coordinate of the second corner of the
+	 *                    destination rectangle.
+	 * @param       dy2 the <i>y</i> coordinate of the second corner of the
+	 *                    destination rectangle.
+	 * @param       sx1 the <i>x</i> coordinate of the first corner of the
+	 *                    source rectangle.
+	 * @param       sy1 the <i>y</i> coordinate of the first corner of the
+	 *                    source rectangle.
+	 * @param       sx2 the <i>x</i> coordinate of the second corner of the
+	 *                    source rectangle.
+	 * @param       sy2 the <i>y</i> coordinate of the second corner of the
+	 *                    source rectangle.
+	 * @param       observer object to be notified as more of the image is
+	 *                    scaled and converted.
+	 * @return   <code>false</code> if the image pixels are still changing;
+	 *           <code>true</code> otherwise.
+	 */
+	public boolean drawImage(Image img, float dx1, float dy1, float dx2, float dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer);
+
+	/**
+	 * Draws as much of the specified area of the specified image as is
+	 * currently available, scaling it on the fly to fit inside the
+	 * specified area of the destination drawable surface. 
+	 * <p>
+	 * Transparent pixels are drawn in the specified background color. 
+	 * This operation is equivalent to filling a rectangle of the
+	 * width and height of the specified image with the given color and then
+	 * drawing the image on top of it, but possibly more efficient.
+	 * <p>
+	 * This method returns immediately in all cases, even if the
+	 * image area to be drawn has not yet been scaled, dithered, and converted
+	 * for the current output device.
+	 * If the current output representation is not yet complete then
+	 * <code>drawImage</code> returns <code>false</code>. As more of
+	 * the image becomes available, the process that loads the image notifies 
+	 * the specified image observer.
+	 * <p>
+	 * This method always uses the unscaled version of the image
+	 * to render the scaled rectangle and performs the required
+	 * scaling on the fly. It does not use a cached, scaled version
+	 * of the image for this operation. Scaling of the image from source
+	 * to destination is performed such that the first coordinate
+	 * of the source rectangle is mapped to the first coordinate of
+	 * the destination rectangle, and the second source coordinate is
+	 * mapped to the second destination coordinate. The subimage is
+	 * scaled and flipped as needed to preserve those mappings.
+	 * @param       img the specified image to be drawn. This method does
+	 *                  nothing if <code>img</code> is null.
+	 * @param       dx1 the <i>x</i> coordinate of the first corner of the
+	 *                    destination rectangle.
+	 * @param       dy1 the <i>y</i> coordinate of the first corner of the
+	 *                    destination rectangle.
+	 * @param       dx2 the <i>x</i> coordinate of the second corner of the
+	 *                    destination rectangle.
+	 * @param       dy2 the <i>y</i> coordinate of the second corner of the
+	 *                    destination rectangle.
+	 * @param       sx1 the <i>x</i> coordinate of the first corner of the
+	 *                    source rectangle.
+	 * @param       sy1 the <i>y</i> coordinate of the first corner of the
+	 *                    source rectangle.
+	 * @param       sx2 the <i>x</i> coordinate of the second corner of the
+	 *                    source rectangle.
+	 * @param       sy2 the <i>y</i> coordinate of the second corner of the
+	 *                    source rectangle.
+	 * @param       bgcolor the background color to paint under the
+	 *                    non-opaque portions of the image.
+	 * @param       observer object to be notified as more of the image is
+	 *                    scaled and converted.
+	 * @return   <code>false</code> if the image pixels are still changing;
+	 *           <code>true</code> otherwise.
+	 */
+	public boolean drawImage(Image img, float dx1, float dy1, float dx2, float dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer);
+
+	/**
+	 * Disposes of this graphics context and releases 
+	 * any system resources that it is using. 
+	 * A <code>Graphics</code> object cannot be used after 
+	 * <code>dispose</code>has been called.
+	 * <p>
+	 * When a Java program runs, a large number of <code>Graphics</code>
+	 * objects can be created within a short time frame.
+	 * Although the finalization process of the garbage collector 
+	 * also disposes of the same system resources, it is preferable 
+	 * to manually free the associated resources by calling this
+	 * method rather than to rely on a finalization process which 
+	 * may not run to completion for a long period of time.
+	 * <p>
+	 * Graphics objects which are provided as arguments to the 
+	 * <code>paint</code> and <code>update</code> methods 
+	 * of components are automatically released by the system when 
+	 * those methods return. For efficiency, programmers should
+	 * call <code>dispose</code> when finished using
+	 * a <code>Graphics</code> object only if it was created 
+	 * directly from a component or another <code>Graphics</code> object.
+	 */
+	public void dispose();
+
+	/**
+	 * Returns true if the specified rectangular area might intersect 
+	 * the current clipping area.
+	 * The coordinates of the specified rectangular area are in the
+	 * user coordinate space and are relative to the coordinate
+	 * system origin of this graphics context.
+	 * This method may use an algorithm that calculates a result quickly
+	 * but which sometimes might return true even if the specified
+	 * rectangular area does not intersect the clipping area.
+	 * The specific algorithm employed may thus trade off accuracy for
+	 * speed, but it will never return false unless it can guarantee
+	 * that the specified rectangular area does not intersect the
+	 * current clipping area.
+	 * The clipping area used by this method can represent the
+	 * intersection of the user clip as specified through the clip
+	 * methods of this graphics context as well as the clipping
+	 * associated with the device or image bounds and window visibility.
+	 *
+	 * @param x the x coordinate of the rectangle to test against the clip
+	 * @param y the y coordinate of the rectangle to test against the clip
+	 * @param width the width of the rectangle to test against the clip
+	 * @param height the height of the rectangle to test against the clip
+	 * @return <code>true</code> if the specified rectangle intersects
+	 *         the bounds of the current clip; <code>false</code>
+	 *         otherwise.
+	 */
+	public boolean hitClip(float x, float y, float width, float height);
+
+	////////////////////////////////////////////////////////////
+	// Graphics API inherited from java.awt.Graphics2D
+
+	/**
+	 * Strokes the outline of a <code>Shape</code> using the settings of the 
+	 * current <code>Graphics2D</code> context.  The rendering attributes
+	 * applied include the <code>Clip</code>, <code>Transform</code>,
+	 * <code>Paint</code>, <code>Composite</code> and 
+	 * <code>Stroke</code> attributes.
+	 * @param s the <code>Shape</code> to be rendered
+	 */
+	public void draw(Shape s);
+
+	/**
+	 * Strokes the outline of a path using the settings of the 
+	 * current <code>Graphics2D</code> context.  The rendering attributes
+	 * applied include the <code>Clip</code>, <code>Transform</code>,
+	 * <code>Paint</code>, <code>Composite</code> and 
+	 * <code>Stroke</code> attributes.
+	 * @param s the path to be rendered
+	 */
+	public void draw(PathIterator s);
+
+	/**
+	 * Renders an image, applying a transform from image space into user space
+	 * before drawing.
+	 * The transformation from user space into device space is done with
+	 * the current <code>Transform</code> in the <code>Graphics2D</code>.
+	 * The specified transformation is applied to the image before the
+	 * transform attribute in the <code>Graphics2D</code> context is applied.
+	 * The rendering attributes applied include the <code>Clip</code>,
+	 * <code>Transform</code>, and <code>Composite</code> attributes. 
+	 * Note that no rendering is done if the specified transform is 
+	 * noninvertible.
+	 * @param img the specified image to be rendered. 
+	 *            This method does nothing if <code>img</code> is null.
+	 * @param xform the transformation from image space into user space
+	 * @param obs the {@link ImageObserver}
+	 * to be notified as more of the <code>Image</code>
+	 * is converted
+	 * @return <code>true</code> if the <code>Image</code> is 
+	 * fully loaded and completely rendered, or if it's null;
+	 * <code>false</code> if the <code>Image</code> is still being loaded.
+	 */
+	public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs);
+
+	/**
+	 * Renders a <code>BufferedImage</code> that is
+	 * filtered with a 
+	 * {@link BufferedImageOp}.
+	 * The rendering attributes applied include the <code>Clip</code>,
+	 * <code>Transform</code>
+	 * and <code>Composite</code> attributes.  This is equivalent to:
+	 * <pre>
+	 * img1 = op.filter(img, null);
+	 * drawImage(img1, new AffineTransform(1f,0f,0f,1f,x,y), null);
+	 * </pre>
+	 * @param op the filter to be applied to the image before rendering
+	 * @param img the specified <code>BufferedImage</code> to be rendered. 
+	 *            This method does nothing if <code>img</code> is null.
+	 * @param x the x coordinate of the location in user space where
+	 * the upper left corner of the image is rendered
+	 * @param y the y coordinate of the location in user space where
+	 * the upper left corner of the image is rendered
+	 */
+	public void drawImage(BufferedImage img, BufferedImageOp op, float x, float y);
+
+	/**
+	 * Renders a {@link RenderedImage},
+	 * applying a transform from image 
+	 * space into user space before drawing.
+	 * The transformation from user space into device space is done with
+	 * the current <code>Transform</code> in the <code>Graphics2D</code>.
+	 * The specified transformation is applied to the image before the
+	 * transform attribute in the <code>Graphics2D</code> context is applied.
+	 * The rendering attributes applied include the <code>Clip</code>,
+	 * <code>Transform</code>, and <code>Composite</code> attributes. Note
+	 * that no rendering is done if the specified transform is
+	 * noninvertible.
+	 * @param img the image to be rendered. This method does
+	 *            nothing if <code>img</code> is null.
+	 * @param xform the transformation from image space into user space
+	 */
+	public void drawRenderedImage(RenderedImage img, AffineTransform xform);
+
+	/**
+	 * Renders a 
+	 * {@link RenderableImage},
+	 * applying a transform from image space into user space before drawing.
+	 * The transformation from user space into device space is done with
+	 * the current <code>Transform</code> in the <code>Graphics2D</code>.
+	 * The specified transformation is applied to the image before the
+	 * transform attribute in the <code>Graphics2D</code> context is applied.
+	 * The rendering attributes applied include the <code>Clip</code>,
+	 * <code>Transform</code>, and <code>Composite</code> attributes. Note
+	 * that no rendering is done if the specified transform is
+	 * noninvertible.
+	 *<p> 
+	 * Rendering hints set on the <code>Graphics2D</code> object might
+	 * be used in rendering the <code>RenderableImage</code>.  
+	 * If explicit control is required over specific hints recognized by a 
+	 * specific <code>RenderableImage</code>, or if knowledge of which hints 
+	 * are used is required, then a <code>RenderedImage</code> should be   
+	 * obtained directly from the <code>RenderableImage</code>
+	 * and rendered using 
+	 *{@link #drawRenderedImage(RenderedImage, AffineTransform) drawRenderedImage}.
+	 * @param img the image to be rendered. This method does
+	 *            nothing if <code>img</code> is null.
+	 * @param xform the transformation from image space into user space
+	 */
+	public void drawRenderableImage(RenderableImage img, AffineTransform xform);
+
+	/**
+	 * Renders the text specified by the specified <code>String</code>, 
+	 * using the current text attribute state in the <code>Graphics2D</code> context. 
+	 * The baseline of the first character is at position 
+	 * (<i>x</i>,&nbsp;<i>y</i>) in the User Space.
+	 * The rendering attributes applied include the <code>Clip</code>,
+	 * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and
+	 * <code>Composite</code> attributes. For characters in script systems 
+	 * such as Hebrew and Arabic, the glyphs can be rendered from right to
+	 * left, in which case the coordinate supplied is the location of the
+	 * leftmost character on the baseline.
+	 * @param str the <code>String</code> to be rendered
+	 * @param x the x coordinate of the location where the
+	 * <code>String</code> should be rendered
+	 * @param y the y coordinate of the location where the
+	 * <code>String</code> should be rendered
+	 * @throws NullPointerException if <code>str</code> is
+	 *         <code>null</code>
+	 */
+	public void drawString(String str, float x, float y);
+
+	/**
+	 * Renders the text of the specified iterator applying its attributes
+	 * in accordance with the specification of the {@link TextAttribute} class.
+	 * <p>
+	 * The baseline of the first character is at position
+	 * (<i>x</i>,&nbsp;<i>y</i>) in User Space.
+	 * For characters in script systems such as Hebrew and Arabic,
+	 * the glyphs can be rendered from right to left, in which case the 
+	 * coordinate supplied is the location of the leftmost character
+	 * on the baseline.
+	 * @param iterator the iterator whose text is to be rendered
+	 * @param x the x coordinate where the iterator's text is to be
+	 * rendered
+	 * @param y the y coordinate where the iterator's text is to be
+	 * rendered
+	 * @throws NullPointerException if <code>iterator</code> is
+	 *         <code>null</code>
+	 */
+	public void drawString(AttributedCharacterIterator iterator, float x, float y);
+
+	/**
+	 * Renders the text of the specified 
+	 * {@link GlyphVector} using
+	 * the <code>Graphics2D</code> context's rendering attributes.
+	 * The rendering attributes applied include the <code>Clip</code>,
+	 * <code>Transform</code>, <code>Paint</code>, and
+	 * <code>Composite</code> attributes.  The <code>GlyphVector</code>
+	 * specifies individual glyphs from a {@link Font}.
+	 * The <code>GlyphVector</code> can also contain the glyph positions.  
+	 * This is the fastest way to render a set of characters to the
+	 * screen.
+	 * @param g the <code>GlyphVector</code> to be rendered
+	 * @param x the x position in User Space where the glyphs should
+	 * be rendered
+	 * @param y the y position in User Space where the glyphs should
+	 * be rendered
+	 * @throws NullPointerException if <code>g</code> is <code>null</code>.
+	 */
+	public void drawGlyphVector(GlyphVector g, float x, float y);
+
+	/**
+	 * Fills the interior of a <code>Shape</code> using the settings of the   
+	 * <code>Graphics2D</code> context. The rendering attributes applied 
+	 * include the <code>Clip</code>, <code>Transform</code>,
+	 * <code>Paint</code>, and <code>Composite</code>.
+	 * @param s the <code>Shape</code> to be filled
+	 */
+	public void fill(Shape s);
+
+	/**
+	 * Fills the interior of a path using the settings of the   
+	 * <code>Graphics2D</code> context. The rendering attributes applied 
+	 * include the <code>Clip</code>, <code>Transform</code>,
+	 * <code>Paint</code>, and <code>Composite</code>.
+	 * @param s the <code>path</code> to be filled
+	 */
+	public void fill(PathIterator s);
+
+	/**
+	 * Checks whether or not the specified <code>Shape</code> intersects 
+	 * the specified {@link Rectangle}, which is in device
+	 * space. If <code>onStroke</code> is false, this method checks
+	 * whether or not the interior of the specified <code>Shape</code>
+	 * intersects the specified <code>Rectangle</code>.  If 
+	 * <code>onStroke</code> is <code>true</code>, this method checks
+	 * whether or not the <code>Stroke</code> of the specified 
+	 * <code>Shape</code> outline intersects the specified
+	 * <code>Rectangle</code>.
+	 * The rendering attributes taken into account include the
+	 * <code>Clip</code>, <code>Transform</code>, and <code>Stroke</code> 
+	 * attributes.
+	 * @param rect the area in device space to check for a hit
+	 * @param s the <code>Shape</code> to check for a hit
+	 * @param onStroke flag used to choose between testing the 
+	 * stroked or the filled shape.  If the flag is <code>true</code>, the 
+	 * <code>Stroke</code> oultine is tested.  If the flag is 
+	 * <code>false</code>, the filled <code>Shape</code> is tested.
+	 * @return <code>true</code> if there is a hit; <code>false</code> 
+	 * otherwise.
+	 */
+	public boolean hit(Rectangle2D rect, Shape s, boolean onStroke);
+
+	/**
+	 * Returns the device configuration associated with this 
+	 * <code>Graphics2D</code>.
+	 * @return the device configuration of this <code>Graphics2D</code>.
+	 */
+	public GraphicsConfiguration getDeviceConfiguration();
+
+	/**
+	 * Sets the <code>Composite</code> for the <code>Graphics2D</code> context.
+	 * The <code>Composite</code> is used in all drawing methods such as 
+	 * <code>drawImage</code>, <code>drawString</code>, <code>draw</code>,
+	 * and <code>fill</code>.  It specifies how new pixels are to be combined 
+	 * with the existing pixels on the graphics device during the rendering 
+	 * process.
+	 * <p>If this <code>Graphics2D</code> context is drawing to a
+	 * <code>Component</code> on the display screen and the
+	 * <code>Composite</code> is a custom object rather than an
+	 * instance of the <code>AlphaComposite</code> class, and if
+	 * there is a security manager, its <code>checkPermission</code>
+	 * method is called with an <code>AWTPermission("readDisplayPixels")</code>
+	 * permission.
+	 * 
+	 * @param comp the <code>Composite</code> object to be used for rendering
+	 * @throws SecurityException
+	 *         if a custom <code>Composite</code> object is being
+	 *         used to render to the screen and a security manager
+	 *         is set and its <code>checkPermission</code> method
+	 *         does not allow the operation.
+	 */
+	public void setComposite(Composite comp);
+
+	/**
+	 * Sets the <code>Paint</code> attribute for the 
+	 * <code>Graphics2D</code> context.  Calling this method
+	 * with a <code>null</code> <code>Paint</code> object does 
+	 * not have any effect on the current <code>Paint</code> attribute
+	 * of this <code>Graphics2D</code>.  
+	 * @param paint the <code>Paint</code> object to be used to generate 
+	 * color during the rendering process, or <code>null</code>
+	 */
+	public void setPaint( Paint paint );
+
+	/**
+	 * Sets the <code>Stroke</code> for the <code>Graphics2D</code> context.
+	 * @param s the <code>Stroke</code> object to be used to stroke a 
+	 * <code>Shape</code> during the rendering process
+	 */
+	public void setStroke(Stroke s);
+
+	/**
+	 * Sets the value of a single preference for the rendering algorithms.
+	 * Hint categories include controls for rendering quality and overall 
+	 * time/quality trade-off in the rendering process.  Refer to the
+	 * <code>RenderingHints</code> class for definitions of some common
+	 * keys and values.
+	 * @param hintKey the key of the hint to be set.
+	 * @param hintValue the value indicating preferences for the specified
+	 * hint category.
+	 */
+	public void setRenderingHint(Key hintKey, Object hintValue);
+
+	/**
+	 * Returns the value of a single preference for the rendering algorithms.
+	 * Hint categories include controls for rendering quality and overall 
+	 * time/quality trade-off in the rendering process.  Refer to the
+	 * <code>RenderingHints</code> class for definitions of some common
+	 * keys and values.
+	 * @param hintKey the key corresponding to the hint to get. 
+	 * @return an object representing the value for the specified hint key.
+	 * Some of the keys and their associated values are defined in the
+	 * <code>RenderingHints</code> class.
+	 */
+	public Object getRenderingHint(Key hintKey);
+
+	/**
+	 * Replaces the values of all preferences for the rendering
+	 * algorithms with the specified <code>hints</code>.
+	 * The existing values for all rendering hints are discarded and
+	 * the new set of known hints and values are initialized from the
+	 * specified {@link Map} object.
+	 * Hint categories include controls for rendering quality and
+	 * overall time/quality trade-off in the rendering process.
+	 * Refer to the <code>RenderingHints</code> class for definitions of
+	 * some common keys and values.
+	 * @param hints the rendering hints to be set
+	 */
+	public void setRenderingHints(Map<?,?> hints);
+
+	/**
+	 * Sets the values of an arbitrary number of preferences for the
+	 * rendering algorithms.
+	 * Only values for the rendering hints that are present in the
+	 * specified <code>Map</code> object are modified.
+	 * All other preferences not present in the specified 
+	 * object are left unmodified.
+	 * Hint categories include controls for rendering quality and
+	 * overall time/quality trade-off in the rendering process.
+	 * Refer to the <code>RenderingHints</code> class for definitions of
+	 * some common keys and values.
+	 * @param hints the rendering hints to be set
+	 */
+	public void addRenderingHints(Map<?,?> hints);
+
+	/**
+	 * Gets the preferences for the rendering algorithms.  Hint categories
+	 * include controls for rendering quality and overall time/quality
+	 * trade-off in the rendering process.
+	 * Returns all of the hint key/value pairs that were ever specified in 
+	 * one operation.  Refer to the
+	 * <code>RenderingHints</code> class for definitions of some common
+	 * keys and values.
+	 * @return a reference to an instance of <code>RenderingHints</code>
+	 * that contains the current preferences.
+	 */
+	public RenderingHints getRenderingHints();
+
+	/**
+	 * Concatenates the current
+	 * <code>Graphics2D</code> <code>Transform</code> 
+	 * with a translation transform. 
+	 * Subsequent rendering is translated by the specified
+	 * distance relative to the previous position.
+	 * This is equivalent to calling transform(T), where T is an
+	 * <code>AffineTransform</code> represented by the following matrix:
+	 * <pre>
+	 *		[   1    0    tx  ]
+	 *		[   0    1    ty  ]
+	 *		[   0    0    1   ]
+	 * </pre>
+	 * @param tx the distance to translate along the x-axis
+	 * @param ty the distance to translate along the y-axis
+	 */
+	public void translate(float tx, float ty);
+
+	/**
+	 * Concatenates the current <code>Graphics2D</code>
+	 * <code>Transform</code> with a rotation transform. 
+	 * Subsequent rendering is rotated by the specified radians relative
+	 * to the previous origin.
+	 * This is equivalent to calling <code>transform(R)</code>, where R is an
+	 * <code>AffineTransform</code> represented by the following matrix:
+	 * <pre>
+	 *		[   cos(theta)    -sin(theta)    0   ]
+	 *		[   sin(theta)     cos(theta)    0   ]
+	 *		[       0              0         1   ]
+	 * </pre>
+	 * Rotating with a positive angle theta rotates points on the positive
+	 * x axis toward the positive y axis.
+	 * @param theta the angle of rotation in radians
+	 */
+	public void rotate(float theta);
+
+	/**
+	 * Concatenates the current <code>Graphics2D</code> 
+	 * <code>Transform</code> with a translated rotation 
+	 * transform.  Subsequent rendering is transformed by a transform
+	 * which is constructed by translating to the specified location, 
+	 * rotating by the specified radians, and translating back by the same
+	 * amount as the original translation.  This is equivalent to the
+	 * following sequence of calls:
+	 * <pre>
+	 *		translate(x, y);
+	 *		rotate(theta);
+	 *		translate(-x, -y);
+	 * </pre>
+	 * Rotating with a positive angle theta rotates points on the positive
+	 * x axis toward the positive y axis.
+	 * @param theta the angle of rotation in radians
+	 * @param x the x coordinate of the origin of the rotation
+	 * @param y the y coordinate of the origin of the rotation
+	 */
+	public void rotate(float theta, float x, float y);
+
+	/**
+	 * Concatenates the current <code>Graphics2D</code>
+	 * <code>Transform</code> with a scaling transformation 
+	 * Subsequent rendering is resized according to the specified scaling
+	 * factors relative to the previous scaling.
+	 * This is equivalent to calling <code>transform(S)</code>, where S is an
+	 * <code>AffineTransform</code> represented by the following matrix:
+	 * <pre>
+	 *		[   sx   0    0   ]
+	 *		[   0    sy   0   ]
+	 *		[   0    0    1   ]
+	 * </pre>
+	 * @param sx the amount by which X coordinates in subsequent
+	 * rendering operations are multiplied relative to previous
+	 * rendering operations.
+	 * @param sy the amount by which Y coordinates in subsequent 
+	 * rendering operations are multiplied relative to previous 
+	 * rendering operations.
+	 */
+	public void scale(float sx, float sy);
+
+	/**
+	 * Concatenates the current <code>Graphics2D</code>
+	 * <code>Transform</code> with a shearing transform. 
+	 * Subsequent renderings are sheared by the specified
+	 * multiplier relative to the previous position.
+	 * This is equivalent to calling <code>transform(SH)</code>, where SH
+	 * is an <code>AffineTransform</code> represented by the following
+	 * matrix:
+	 * <pre>
+	 *		[   1   shx   0   ]
+	 *		[  shy   1    0   ]
+	 *		[   0    0    1   ]
+	 * </pre>
+	 * @param shx the multiplier by which coordinates are shifted in 
+	 * the positive X axis direction as a function of their Y coordinate
+	 * @param shy the multiplier by which coordinates are shifted in
+	 * the positive Y axis direction as a function of their X coordinate
+	 */
+	public void shear(float shx, float shy);
+
+	/**
+	 * Composes an <code>AffineTransform</code> object with the 
+	 * <code>Transform</code> in this <code>Graphics2D</code> according 
+	 * to the rule last-specified-first-applied.  If the current
+	 * <code>Transform</code> is Cx, the result of composition
+	 * with Tx is a new <code>Transform</code> Cx'.  Cx' becomes the
+	 * current <code>Transform</code> for this <code>Graphics2D</code>.
+	 * Transforming a point p by the updated <code>Transform</code> Cx' is
+	 * equivalent to first transforming p by Tx and then transforming
+	 * the result by the original <code>Transform</code> Cx.  In other
+	 * words, Cx'(p) = Cx(Tx(p)).  A copy of the Tx is made, if necessary,
+	 * so further modifications to Tx do not affect rendering.
+	 * @param Tx the <code>AffineTransform</code> object to be composed with 
+	 * the current <code>Transform</code>
+	 */
+	public void transform(AffineTransform Tx);
+
+	/**
+	 * Overwrites the Transform in the <code>Graphics2D</code> context.
+	 * WARNING: This method should <b>never</b> be used to apply a new
+	 * coordinate transform on top of an existing transform because the 
+	 * <code>Graphics2D</code> might already have a transform that is
+	 * needed for other purposes, such as rendering Swing 
+	 * components or applying a scaling transformation to adjust for the
+	 * resolution of a printer.  
+	 * <p>To add a coordinate transform, use the 
+	 * <code>transform</code>, <code>rotate</code>, <code>scale</code>,
+	 * or <code>shear</code> methods.  The <code>setTransform</code> 
+	 * method is intended only for restoring the original 
+	 * <code>Graphics2D</code> transform after rendering, as shown in this
+	 * example:
+	 * <pre><blockquote>
+	 * // Get the current transform
+	 * AffineTransform saveAT = g2.getTransform();
+	 * // Perform transformation
+	 * g2d.transform(...);
+	 * // Render
+	 * g2d.draw(...);
+	 * // Restore original transform
+	 * g2d.setTransform(saveAT);
+	 * </blockquote></pre>
+	 *
+	 * @param Tx the <code>AffineTransform</code> that was retrieved
+	 *           from the <code>getTransform</code> method
+	 */
+	public void setTransform(AffineTransform Tx);
+
+	/**
+	 * Returns a copy of the current <code>Transform</code> in the 
+	 * <code>Graphics2D</code> context.
+	 * @return the current <code>AffineTransform</code> in the 
+	 *             <code>Graphics2D</code> context.
+	 */
+	public AffineTransform getTransform();
+
+	/**
+	 * Returns the current <code>Paint</code> of the 
+	 * <code>Graphics2D</code> context.
+	 * @return the current <code>Graphics2D</code> <code>Paint</code>,
+	 * which defines a color or pattern.
+	 */
+	public Paint getPaint();
+
+	/**
+	 * Returns the current <code>Composite</code> in the
+	 * <code>Graphics2D</code> context.
+	 * @return the current <code>Graphics2D</code> <code>Composite</code>,
+	 *              which defines a compositing style.
+	 */
+	public Composite getComposite();
+
+	/**
+	 * Sets the background color for the <code>Graphics2D</code> context. 
+	 * The background color is used for clearing a region.
+	 * When a <code>Graphics2D</code> is constructed for a
+	 * <code>Component</code>, the background color is
+	 * inherited from the <code>Component</code>. Setting the background color 
+	 * in the <code>Graphics2D</code> context only affects the subsequent      
+	 * <code>clearRect</code> calls and not the background color of the  
+	 * <code>Component</code>.  To change the background
+	 * of the <code>Component</code>, use appropriate methods of 
+	 * the <code>Component</code>.
+	 * @param color the background color that isused in
+	 * subsequent calls to <code>clearRect</code>
+	 */
+	public void setBackground(Color color);
+
+	/**
+	 * Returns the background color used for clearing a region.
+	 * @return the current <code>Graphics2D</code> <code>Color</code>,
+	 * which defines the background color.
+	 */
+	public Color getBackground();
+
+	/**
+	 * Returns the current <code>Stroke</code> in the
+	 * <code>Graphics2D</code> context.
+	 * @return the current <code>Graphics2D</code> <code>Stroke</code>,
+	 *                 which defines the line style.
+	 */
+	public Stroke getStroke();
+
+	/**
+	 * Intersects the current <code>Clip</code> with the interior of the
+	 * specified <code>Shape</code> and sets the <code>Clip</code> to the
+	 * resulting intersection.  The specified <code>Shape</code> is
+	 * transformed with the current <code>Graphics2D</code>
+	 * <code>Transform</code> before being intersected with the current 
+	 * <code>Clip</code>.  This method is used to make the current
+	 * <code>Clip</code> smaller.
+	 * To make the <code>Clip</code> larger, use <code>setClip</code>.
+	 * The <i>user clip</i> modified by this method is independent of the
+	 * clipping associated with device bounds and visibility.  If no clip has 
+	 * previously been set, or if the clip has been cleared using 
+	 * {@link Graphics#setClip(Shape) setClip} with a <code>null</code>
+	 * argument, the specified <code>Shape</code> becomes the new 
+	 * user clip.
+	 * @param s the <code>Shape</code> to be intersected with the current
+	 *          <code>Clip</code>.  If <code>s</code> is <code>null</code>,
+	 *          this method clears the current <code>Clip</code>.
+	 */
+	public void clip(Shape s);
+
+	/**
+	 * Get the rendering context of the <code>Font</code> within this 
+	 * <code>Graphics2D</code> context.
+	 * The {@link FontRenderContext}
+	 * encapsulates application hints such as anti-aliasing and 
+	 * fractional metrics, as well as target device specific information
+	 * such as dots-per-inch.  This information should be provided by the
+	 * application when using objects that perform typographical
+	 * formatting, such as <code>Font</code> and
+	 * <code>TextLayout</code>.  This information should also be provided
+	 * by applications that perform their own layout and need accurate
+	 * measurements of various characteristics of glyphs such as advance
+	 * and line height when various rendering hints have been applied to
+	 * the text rendering.
+	 *
+	 * @return a reference to an instance of FontRenderContext.
+	 */
+
+	public FontRenderContext getFontRenderContext();
+	
+	/** Draw a string inside a cartridge.
+	 * <p>
+	 * A cartridge is a text surround by a box. The LODGraphics2D object
+	 * ensures that the cartridges do not overlaps and tries to move
+	 * them if it is the case.
+	 * 
+	 * @param text is the text inside the cartridge. If {@code \n} characters
+	 * is inside, the text will be drawn on several lines.
+	 * @param x is the position of the upper left corner of the cartridge.
+	 * @param y is the position of the upper left corner of the cartridge.
+	 * @param backgroundColor is the color of the background, or <code>null</code> for transparent background.
+	 * @param textColor is the color of the text, or <code>null</code> for the current LODGraphics2D color.
+	 * @param borderColor is the color of the cartridge border, or <code>null</code> for transparent borders.
+	 * @param alignment is the alignment of the text in the cartridge
+	 */
+	public void drawCartridge(String text, float x, float y, Color textColor, Color backgroundColor, Color borderColor, TextAlignment alignment);
+
+	/** Draw a string inside a cartridge.
+	 * <p>
+	 * A cartridge is a text surround by a box. The LODGraphics2D object
+	 * ensures that the cartridges do not overlaps and tries to move
+	 * them if it is the case.
+	 * 
+	 * @param text is the text inside the cartridge. If {@code \n} characters
+	 * is inside, the text will be drawn on several lines.
+	 * @param x is the position of the upper left corner of the cartridge.
+	 * @param y is the position of the upper left corner of the cartridge.
+	 */
+	public void drawCartridge(String text, float x, float y);
+
+	/** Draw a string inside a cartridge.
+	 * <p>
+	 * A cartridge is a text surround by a box. The LODGraphics2D object
+	 * ensures that the cartridges do not overlaps and tries to move
+	 * them if it is the case.
+	 * 
+	 * @param text is the text inside the cartridge. If {@code \n} characters
+	 * is inside, the text will be drawn on several lines.
+	 * @param x is the position of the upper left corner of the cartridge.
+	 * @param y is the position of the upper left corner of the cartridge.
+	 * @param backgroundColor is the color of the background, or <code>null</code> for transparent background.
+	 * @param borderColor is the color of the cartridge border, or <code>null</code> for transparent borders.
+	 */
+	public void drawCartridge(String text, float x, float y, Color backgroundColor, Color borderColor);
+	
+}
\ No newline at end of file

Added: trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/SupportedShape.java
===================================================================
--- trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/SupportedShape.java	                        (rev 0)
+++ trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/SupportedShape.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,140 @@
+/* $Id$
+ * 
+ * This is a part of NetEditor project from Arakhne.org:
+ * package org.arakhne.neteditor.zoompanel.
+ * 
+ * Copyright (C) 2002  St&eacute;phane GALLAND, Mahdi Hannoun
+ * Copyright (C) 2004-2012  St&eacute;phane GALLAND
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.awt;
+
+import java.awt.Polygon;
+import java.awt.Shape;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Area;
+import java.awt.geom.CubicCurve2D;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Line2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.QuadCurve2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RoundRectangle2D;
+
+/** This enumeration list all the supported Shape types in a zoomable context.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public enum SupportedShape {
+
+	/** {@link Area}
+	 */
+	AREA(Area.class),
+	/** {@link CubicCurve2D}
+	 */
+	CUBIC_CURVE(CubicCurve2D.class),
+	/** {@link Line2D}
+	 */
+	LINE(Line2D.class),
+	/** {@link Path2D}
+	 */
+	PATH(Path2D.class),
+	/** {@link Polygon}
+	 */
+	POLYGON(Polygon.class),
+	/** {@link QuadCurve2D}
+	 */
+	QUAD_CURVE(QuadCurve2D.class),
+	/** {@link Rectangle2D}
+	 */
+	RECTANGLE2D(Rectangle2D.class),
+	/** {@link Arc2D}
+	 */
+	ARC(Arc2D.class),
+	/** {@link RoundRectangle2D}
+	 */
+	ROUND_RECTANGLE(RoundRectangle2D.class),
+	/** {@link Ellipse2D}
+	 */
+	ELLIPSE(Ellipse2D.class),
+	/** {@link Shape}
+	 */
+	VIRTUALIZABLE_SHAPE(VirtualizableShape.class);
+
+	private final Class<? extends Shape> type;
+
+	SupportedShape(Class<? extends Shape> type) {
+		this.type = type;
+	}
+	
+	/** Replies the AWT class that is corresponding to this shape type.
+	 * 
+	 * @return the AWT class that is corresponding to this shape type.
+	 */
+	public Class<? extends Shape> awtType() {
+		return this.type;
+	}
+
+	/**
+	 * Indicates if the given class corresponds to this supported shape.
+	 * 
+	 * @param shapeType is the class of shape to test against with type.
+	 * @return <code>true</code> is the given class is compatible with this
+	 * type of shape, otherwise <code>false</code> 
+	 */
+	public boolean isTypeOf(Class<?> shapeType) {
+		return ((this.type!=null)&&(this.type.isAssignableFrom(shapeType)));
+	}
+
+	/**
+	 * Replies the supported shape type that corresponding to the given class.
+	 * 
+	 * @param type
+	 * @return the supported shape type that corresponding to the given class.
+	 */
+	public static SupportedShape getTypeOf(Class<?> type) {
+		if (type!=null && Shape.class.isAssignableFrom(type)) {
+			SupportedShape[] values = SupportedShape.values();
+			for (SupportedShape stype : values) {
+				if (stype.isTypeOf(type)) return stype;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Replies the supported shape type that corresponding to the given shape.
+	 * 
+	 * @param shape
+	 * @return the supported shape type that corresponding to the given class.
+	 */
+	public static SupportedShape getTypeOf(Object shape) {
+		if (shape instanceof Shape) {
+			SupportedShape[] values = SupportedShape.values();
+			for (SupportedShape type : values) {
+				if (type.awtType().isInstance(shape))
+					return type;
+			}
+		}
+		return null;
+	}
+
+}

Added: trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/UnsupportedShapeException.java
===================================================================
--- trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/UnsupportedShapeException.java	                        (rev 0)
+++ trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/UnsupportedShapeException.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,45 @@
+/* $Id$
+ * 
+ * This is a part of NetEditor project from Arakhne.org:
+ * package org.arakhne.neteditor.zoompanel.
+ * 
+ * Copyright (C) 2002  St&eacute;phane GALLAND, Mahdi Hannoun
+ * Copyright (C) 2004-2012  St&eacute;phane GALLAND
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.awt;
+
+
+/** Exception thrown when a shape is not supported.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class UnsupportedShapeException extends RuntimeException {
+
+	private static final long serialVersionUID = 2572964388468280513L;
+
+	/**
+	 * @param type
+	 */
+	public UnsupportedShapeException(SupportedShape type) {
+		super(type.name());
+	}
+	
+}

Added: trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/VirtualScreenGraphics2D.java
===================================================================
--- trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/VirtualScreenGraphics2D.java	                        (rev 0)
+++ trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/VirtualScreenGraphics2D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,42 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+
+package org.arakhne.afc.ui.awt;
+
+import org.arakhne.afc.ui.ZoomableContext;
+
+
+/** This graphic context permits to display
+ *  something on a screen with virtual
+ *  coordinate system and with a level of details.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface VirtualScreenGraphics2D extends LODGraphics2D, ZoomableContext {
+
+	//
+	
+}

Added: trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/VirtualizableShape.java
===================================================================
--- trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/VirtualizableShape.java	                        (rev 0)
+++ trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/VirtualizableShape.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,74 @@
+/* $Id$
+ * 
+ * This is a part of NetEditor project from Arakhne.org:
+ * package org.arakhne.neteditor.zoompanel.
+ * 
+ * Copyright (C) 2002  St&eacute;phane GALLAND, Mahdi Hannoun
+ * Copyright (C) 2004-2012  St&eacute;phane GALLAND
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.awt;
+
+import java.awt.Shape;
+import java.awt.geom.Rectangle2D;
+
+/** Shape that may be transformed from screen to a logical
+ * coordinate space.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface VirtualizableShape extends Shape {
+	
+	/** Replies a shape that is the same of this shape
+	 * on the screen.
+	 * 
+	 * @param flipX indicates if the X screen axis is inverted.
+	 * @param flipY indicates if the Y screen axis is inverted.
+	 * @param documentScreenRectangle is the area covered by the document in screen space.
+	 * @param translationX is the translation X in the context.
+	 * @param translationY is the translation Y in the context.
+	 * @param zoom is the current zooming factor of the view.
+	 * @return the shape.
+	 */
+	public VirtualizableShape toScreen(
+			boolean flipX, boolean flipY,
+			Rectangle2D documentScreenRectangle,
+			float translationX, float translationY,
+			float zoom);
+	
+	/** Replies a shape that is the same of this shape
+	 * on the virtual area.
+	 * 
+	 * @param flipX indicates if the X screen axis is inverted.
+	 * @param flipY indicates if the Y screen axis is inverted.
+	 * @param documentScreenRectangle is the area covered by the document in screen space.
+	 * @param translationX is the translation X in the context.
+	 * @param translationY is the translation Y in the context.
+	 * @param zoom is the current zooming factor of the view.
+	 * @return the shape.
+	 */
+	public VirtualizableShape fromScreen(
+			boolean flipX, boolean flipY,
+			Rectangle2D documentScreenRectangle,
+			float translationX, float translationY,
+			float zoom);
+
+}

Added: trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/ZoomableContextUtil.java
===================================================================
--- trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/ZoomableContextUtil.java	                        (rev 0)
+++ trunk/ui/ui-awt/src/main/java/org/arakhne/afc/ui/awt/ZoomableContextUtil.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,804 @@
+/* $Id$
+ * 
+ * This is a part of NetEditor project from Arakhne.org:
+ * package org.arakhne.neteditor.zoompanel.
+ * 
+ * Copyright (C) 2002  St&eacute;phane GALLAND, Mahdi Hannoun
+ * Copyright (C) 2004-2012  St&eacute;phane GALLAND
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.awt;
+
+import java.awt.Font;
+import java.awt.Polygon;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Area;
+import java.awt.geom.CubicCurve2D;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Line2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.PathIterator;
+import java.awt.geom.QuadCurve2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RoundRectangle2D;
+
+/** Utilities for ZoomableContext.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class ZoomableContextUtil {
+
+	/** Translates the specified rectangle
+	 *  into the screen space.
+	 *
+	 * @param r is the rectangle in the logical space when input and the
+	 * same rectangle in screen space when output.
+	 * @param canZoom indicates if the zooming should be applied.
+	 * @param flipX indicates if the X screen axis is inverted.
+	 * @param flipY indicates if the Y screen axis is inverted.
+	 * @param documentScreenRectangle is the area covered by the document in screen space.
+	 * @param translationX is the translation X in the context.
+	 * @param translationY is the translation Y in the context.
+	 * @param zoom is the current zooming factor of the view.
+	 */
+	public static void logical2pixel_r(Rectangle2D r, boolean canZoom,
+			boolean flipX, boolean flipY,
+			Rectangle2D documentScreenRectangle,
+			float translationX, float translationY,
+			float zoom) {
+		if (canZoom)
+			logical2pixel_r(r, flipX, flipY, documentScreenRectangle, translationX, translationY, zoom);
+	}
+	
+	private static void logical2pixel_r(Rectangle2D r,
+			boolean flipX, boolean flipY,
+			Rectangle2D documentScreenRectangle,
+			float translationX, float translationY,
+			float zoom) {
+		assert(r!=null);
+		float x = (float)((flipX) ? r.getMaxX() : r.getMinX());
+		float y = (float)((flipY) ? r.getMaxY() : r.getMinY());
+		float px = logical2pixel_x(x, flipX, documentScreenRectangle, translationX, zoom);
+		float py = logical2pixel_y(y, flipY, documentScreenRectangle, translationY, zoom);
+		float pw = logical2pixel_size((float)r.getWidth(), zoom);
+		float ph = logical2pixel_size((float)r.getHeight(), zoom);
+		r.setRect(px, py, pw, ph);
+	}
+
+	/** Translates the specified rectangle
+	 *  into the logical space.
+	 *
+	 * @param r is the rectangle in the screen space when input and the
+	 * same rectangle in logical space when output.
+	 * @param canZoom indicates if the zooming should be applied.
+	 * @param flipX indicates if the X screen axis is inverted.
+	 * @param flipY indicates if the Y screen axis is inverted.
+	 * @param documentScreenRectangle is the area covered by the document in screen space.
+	 * @param translationX is the translation X in the context.
+	 * @param translationY is the translation Y in the context.
+	 * @param zoom is the current zooming factor of the view.
+	 */
+	public static void pixel2logical_r(Rectangle2D r, boolean canZoom,
+			boolean flipX, boolean flipY,
+			Rectangle2D documentScreenRectangle,
+			float translationX, float translationY,
+			float zoom) {
+		if (canZoom)
+			pixel2logical_r(r, flipX, flipY, documentScreenRectangle, translationX, translationY, zoom);
+	}
+	
+	private static void pixel2logical_r(Rectangle2D r,
+			boolean flipX, boolean flipY,
+			Rectangle2D documentScreenRectangle,
+			float translationX, float translationY,
+			float zoom) {
+		assert(r!=null);
+		float x = (float)((flipX) ? r.getMaxX() : r.getMinX());
+		float y = (float)((flipY) ? r.getMaxY() : r.getMinY());
+		float lx = pixel2logical_x(x, flipX, documentScreenRectangle, translationX, zoom);
+		float ly = pixel2logical_y(y, flipY, documentScreenRectangle, translationY, zoom);
+		float lw = pixel2logical_size((float)r.getWidth(), zoom);
+		float lh = pixel2logical_size((float)r.getHeight(), zoom);
+		r.setRect(lx, ly, lw, lh);
+	}
+
+	private static void create(
+			PathIterator pathIterator,
+			boolean flipX, boolean flipY,
+			Rectangle2D documentScreenRectangle,
+			float translationX, float translationY,
+			float zoom,
+			Path2D output) {
+		float[] coords = new float[8];
+		while (!pathIterator.isDone()) {
+			switch(pathIterator.currentSegment(coords)) {
+			case PathIterator.SEG_MOVETO:
+				output.moveTo(
+						logical2pixel_x(coords[0], flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(coords[1], flipY, documentScreenRectangle, translationY, zoom));
+				break;
+			case PathIterator.SEG_LINETO:
+				output.lineTo(
+						logical2pixel_x(coords[0], flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(coords[1], flipY, documentScreenRectangle, translationY, zoom));
+				break;
+			case PathIterator.SEG_CUBICTO:
+				output.curveTo(
+						logical2pixel_x(coords[0], flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(coords[1], flipY, documentScreenRectangle, translationY, zoom),
+						logical2pixel_x(coords[2], flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(coords[3], flipY, documentScreenRectangle, translationY, zoom),
+						logical2pixel_x(coords[4], flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(coords[5], flipY, documentScreenRectangle, translationY, zoom));
+				break;
+			case PathIterator.SEG_QUADTO:
+				output.quadTo(
+						logical2pixel_x(coords[0], flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(coords[1], flipY, documentScreenRectangle, translationY, zoom),
+						logical2pixel_x(coords[2], flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(coords[3], flipY, documentScreenRectangle, translationY, zoom));
+				break;
+			case PathIterator.SEG_CLOSE:
+				output.closePath();
+				break;
+			default:
+				throw new IllegalArgumentException();
+			}
+			pathIterator.next();
+		}
+	}
+
+	private static void create2(
+			Shape s,
+			boolean flipX, boolean flipY,
+			Rectangle2D documentScreenRectangle,
+			float translationX, float translationY,
+			float zoom,
+			Path2D output) {
+		PathIterator it = s.getPathIterator(new AffineTransform());
+		float[] coords = new float[8];
+		while (!it.isDone()) {
+			switch(it.currentSegment(coords)) {
+			case PathIterator.SEG_MOVETO:
+				output.moveTo(
+						pixel2logical_x(coords[0], flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y(coords[1], flipY, documentScreenRectangle, translationY, zoom));
+				break;
+			case PathIterator.SEG_LINETO:
+				output.lineTo(
+						pixel2logical_x(coords[0], flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y(coords[1], flipY, documentScreenRectangle, translationY, zoom));
+				break;
+			case PathIterator.SEG_CUBICTO:
+				output.curveTo(
+						pixel2logical_x(coords[0], flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y(coords[1], flipY, documentScreenRectangle, translationY, zoom),
+						pixel2logical_x(coords[2], flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y(coords[3], flipY, documentScreenRectangle, translationY, zoom),
+						pixel2logical_x(coords[4], flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y(coords[5], flipY, documentScreenRectangle, translationY, zoom));
+				break;
+			case PathIterator.SEG_QUADTO:
+				output.quadTo(
+						pixel2logical_x(coords[0], flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y(coords[1], flipY, documentScreenRectangle, translationY, zoom),
+						pixel2logical_x(coords[2], flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y(coords[3], flipY, documentScreenRectangle, translationY, zoom));
+				break;
+			case PathIterator.SEG_CLOSE:
+				output.closePath();
+				break;
+			default:
+				throw new IllegalArgumentException();
+			}
+			it.next();
+		}
+	}
+
+	/** Translates the specified shape
+	 *  into the screen space.
+	 *
+	 * @param <T> is the type of shape to convert.
+	 * @param s is the shape in the logical space.
+	 * @param canZoom indicates if the zooming should be applied.
+	 * @param flipX indicates if the X screen axis is inverted.
+	 * @param flipY indicates if the Y screen axis is inverted.
+	 * @param documentScreenRectangle is the area covered by the document in screen space.
+	 * @param translationX is the translation X in the context.
+	 * @param translationY is the translation Y in the context.
+	 * @param zoom is the current zooming factor of the view.
+	 * @return a shape into the screen space.
+	 */
+	public static <T extends Shape> T logical2pixel(
+			T s, boolean canZoom,
+			boolean flipX, boolean flipY,
+			Rectangle2D documentScreenRectangle,
+			float translationX, float translationY,
+			float zoom) {
+		if (canZoom)
+			return logical2pixel(s, flipX, flipY, documentScreenRectangle, translationX, translationY, zoom);
+		return s;
+	}
+
+	@SuppressWarnings("unchecked")
+	private static <T extends Shape> T logical2pixel(
+			T s,
+			boolean flipX, boolean flipY,
+			Rectangle2D documentScreenRectangle,
+			float translationX, float translationY,
+			float zoom) {
+		if (s==null) return null;
+		SupportedShape type = SupportedShape.getTypeOf(s.getClass());
+		try {
+			switch(type) {
+			case RECTANGLE2D:
+			{
+				Rectangle2D r = (Rectangle2D)s;
+				float x = (float)((flipX) ? r.getMaxX() : r.getMinX());
+				float y = (float)((flipY) ? r.getMaxY() : r.getMinY());
+				return (T)new Rectangle2D.Double(
+						logical2pixel_x(x, flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(y, flipY, documentScreenRectangle, translationY, zoom),
+						logical2pixel_size((float)r.getWidth(), zoom),
+						logical2pixel_size((float)r.getHeight(), zoom));
+			}
+			case ROUND_RECTANGLE:
+			{
+				RoundRectangle2D r = (RoundRectangle2D)s;
+				float x = (float)((flipX) ? r.getMaxX() : r.getMinX());
+				float y = (float)((flipY) ? r.getMaxY() : r.getMinY());
+				return (T)new RoundRectangle2D.Double(
+						logical2pixel_x(x, flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(y, flipY, documentScreenRectangle, translationY, zoom),
+						logical2pixel_size((float)r.getWidth(), zoom),
+						logical2pixel_size((float)r.getHeight(), zoom),
+						logical2pixel_size((float)r.getArcWidth(), zoom),
+						logical2pixel_size((float)r.getArcHeight(), zoom));
+			}
+			case ELLIPSE:
+			{
+				Ellipse2D e = (Ellipse2D)s;
+				float x = (float)((flipX) ? e.getMaxX() : e.getMinX());
+				float y = (float)((flipY) ? e.getMaxY() : e.getMinY());
+				return (T)new Ellipse2D.Double(
+						logical2pixel_x(x, flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(y, flipY, documentScreenRectangle, translationY, zoom),
+						logical2pixel_size((float)e.getWidth(), zoom),
+						logical2pixel_size((float)e.getHeight(), zoom));
+			}
+			case LINE:
+			{
+				Line2D l = (Line2D)s;
+				return (T)new Line2D.Double(
+						logical2pixel_x((float)l.getX1(), flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y((float)l.getY1(), flipY, documentScreenRectangle, translationY, zoom),
+						logical2pixel_x((float)l.getX2(), flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y((float)l.getY2(), flipY, documentScreenRectangle, translationY, zoom));
+			}
+			case ARC:
+			{
+				Arc2D a = (Arc2D)s;
+				float x = (float)((flipX) ? a.getMaxX() : a.getMinX());
+				float y = (float)((flipY) ? a.getMaxY() : a.getMinY());
+				return (T)new Arc2D.Double(
+						logical2pixel_x(x, flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(y, flipY, documentScreenRectangle, translationY, zoom),
+						logical2pixel_size((float)a.getWidth(), zoom),
+						logical2pixel_size((float)a.getHeight(), zoom),
+						a.getAngleStart(),
+						a.getAngleExtent(),
+						a.getArcType());
+			}
+			case POLYGON:
+			{
+				Polygon p = (Polygon)s ;
+				float x, y;
+				GeneralPath p2 = new GeneralPath() ;
+				for( int i=0; i < p.npoints; ++i ) {
+					x = logical2pixel_x(p.xpoints[i], flipX, documentScreenRectangle, translationX, zoom);
+					y = logical2pixel_y(p.ypoints[i], flipY, documentScreenRectangle, translationY, zoom);
+					if (i==0)
+						p2.moveTo(x, y);
+					else
+						p2.lineTo(x, y);
+				}
+				p2.closePath();
+				return (T)p2 ;
+			}
+			case CUBIC_CURVE:
+			{
+				CubicCurve2D c = (CubicCurve2D)s ;
+				return (T)new CubicCurve2D.Double(
+						logical2pixel_x((float)c.getX1(), flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y((float)c.getY1(), flipY, documentScreenRectangle, translationY, zoom),
+						logical2pixel_x((float)c.getCtrlX1(), flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y((float)c.getCtrlY1(), flipY, documentScreenRectangle, translationY, zoom),
+						logical2pixel_x((float)c.getCtrlX2(), flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y((float)c.getCtrlY2(), flipY, documentScreenRectangle, translationY, zoom),
+						logical2pixel_x((float)c.getX2(), flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y((float)c.getY2(), flipY, documentScreenRectangle, translationY, zoom));
+			}
+			case QUAD_CURVE:
+			{
+				QuadCurve2D c = (QuadCurve2D)s ;
+				return (T)new QuadCurve2D.Double(
+						logical2pixel_x((float)c.getX1(), flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y((float)c.getY1(), flipY, documentScreenRectangle, translationY, zoom),
+						logical2pixel_x((float)c.getCtrlX(), flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y((float)c.getCtrlY(), flipY, documentScreenRectangle, translationY, zoom),
+						logical2pixel_x((float)c.getX2(), flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y((float)c.getY2(), flipY, documentScreenRectangle, translationY, zoom));
+			}
+			case PATH:
+			{
+				Path2D p = (Path2D)s;
+				Path2D p2 = new GeneralPath(p.getWindingRule());
+				create(
+						p.getPathIterator(null), flipX, flipY, documentScreenRectangle,
+						translationX, translationY, zoom, p2);
+				return (T)p2;
+			}
+			case AREA:
+			{
+				Area a = (Area)s ;
+				GeneralPath p2 = new GeneralPath();
+				create(
+						a.getPathIterator(null),
+						flipX, flipY, documentScreenRectangle,
+						translationX, translationY, zoom, p2);
+				return (T)new Area(p2);
+			}
+			case VIRTUALIZABLE_SHAPE:
+			{
+				VirtualizableShape vs = (VirtualizableShape)s;
+				return (T)vs.toScreen(flipX, flipY, documentScreenRectangle, translationX, translationY, zoom);
+			}
+			default:
+				throw new IllegalArgumentException();
+			}
+		}
+		catch(ClassCastException _) {
+			//
+		}
+
+		throw new UnsupportedShapeException(type);
+	}
+
+	/** Translates the specified shape
+	 *  into the logical space.
+	 *
+	 * @param <T> is the type of shape to convert.
+	 * @param s is the shape in the screen space.
+	 * @param canZoom indicates if the zooming should be applied.
+	 * @param flipX indicates if the X screen axis is inverted.
+	 * @param flipY indicates if the Y screen axis is inverted.
+	 * @param documentScreenRectangle is the area covered by the document in screen space.
+	 * @param translationX is the translation X in the context.
+	 * @param translationY is the translation Y in the context.
+	 * @param zoom is the current zooming factor of the view.
+	 * @return a shape into the logical space.
+	 */
+	public static <T extends Shape> T pixel2logical(
+			T s, boolean canZoom,
+			boolean flipX, boolean flipY,
+			Rectangle2D documentScreenRectangle,
+			float translationX, float translationY,
+			float zoom) {
+		if (canZoom)
+			return pixel2logical(s, flipX, flipY, documentScreenRectangle, translationX, translationY, zoom);
+		return s;
+	}
+	
+	@SuppressWarnings("unchecked")
+	private static <T extends Shape> T pixel2logical(
+			T s,
+			boolean flipX, boolean flipY,
+			Rectangle2D documentScreenRectangle,
+			float translationX, float translationY,
+			float zoom) {
+		if (s==null) return null;
+		SupportedShape type = SupportedShape.getTypeOf(s.getClass());
+		try {
+			switch(type) {
+			case RECTANGLE2D:
+			{
+				Rectangle2D r = (Rectangle2D)s;
+				float x = (float)((flipX) ? r.getMaxX() : r.getMinX());
+				float y = (float)((flipY) ? r.getMaxY() : r.getMinY());
+				return (T)new Rectangle2D.Double(
+						pixel2logical_x(x, flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y(y, flipY, documentScreenRectangle, translationY, zoom),
+						pixel2logical_size((float)r.getWidth(), zoom),
+						pixel2logical_size((float)r.getHeight(), zoom));
+			}
+			case ROUND_RECTANGLE:
+			{
+				RoundRectangle2D r = (RoundRectangle2D)s;
+				float x = (float) ((flipX) ? r.getMaxX() : r.getMinX());
+				float y = (float) ((flipY) ? r.getMaxY() : r.getMinY());
+				return (T)new RoundRectangle2D.Double(
+						pixel2logical_x(x, flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y(y, flipY, documentScreenRectangle, translationY, zoom),
+						pixel2logical_size((float)r.getWidth(), zoom),
+						pixel2logical_size((float)r.getHeight(), zoom),
+						pixel2logical_size((float)r.getArcWidth(), zoom),
+						pixel2logical_size((float)r.getArcHeight(), zoom));
+			}
+			case ELLIPSE:
+			{
+				Ellipse2D e = (Ellipse2D)s;
+				float x = (float) ((flipX) ? e.getMaxX() : e.getMinX());
+				float y = (float) ((flipY) ? e.getMaxY() : e.getMinY());
+				return (T)new Ellipse2D.Double(
+						pixel2logical_x(x, flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y(y, flipY, documentScreenRectangle, translationY, zoom),
+						pixel2logical_size((float)e.getWidth(), zoom),
+						pixel2logical_size((float)e.getHeight(), zoom));
+			}
+			case LINE:
+			{
+				Line2D l = (Line2D)s;
+				return (T)new Line2D.Double(
+						pixel2logical_x((float)l.getX1(), flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y((float)l.getY1(), flipY, documentScreenRectangle, translationY, zoom),
+						pixel2logical_x((float)l.getX2(), flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y((float)l.getY2(), flipY, documentScreenRectangle, translationY, zoom));
+			}
+			case ARC:
+			{
+				Arc2D a = (Arc2D)s;
+				float x = (float) ((flipX) ? a.getMaxX() : a.getMinX());
+				float y = (float) ((flipY) ? a.getMaxY() : a.getMinY());
+				return (T)new Arc2D.Double(
+						pixel2logical_x(x, flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y(y, flipY, documentScreenRectangle, translationY, zoom),
+						pixel2logical_size((float)a.getWidth(), zoom),
+						pixel2logical_size((float)a.getHeight(), zoom),
+						a.getAngleStart(),
+						a.getAngleExtent(),
+						a.getArcType());
+			}
+			case POLYGON:
+			{
+				Polygon p = (Polygon)s ;
+				GeneralPath p2 = new GeneralPath() ;
+				float x, y;
+				for( int i=0; i < p.npoints; ++i ) {
+					x = pixel2logical_x(p.xpoints[i], flipX, documentScreenRectangle, translationX, zoom);
+					y = pixel2logical_y(p.ypoints[i], flipY, documentScreenRectangle, translationY, zoom);
+					if (i==0) p2.moveTo(x, y);
+					else p2.lineTo(x, y);
+				}
+				p2.closePath();
+				return (T)p2 ;
+			}
+			case CUBIC_CURVE:
+			{
+				CubicCurve2D c = (CubicCurve2D)s ;
+				return (T)new CubicCurve2D.Double(
+						pixel2logical_x((float)c.getX1(), flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y((float)c.getY1(), flipY, documentScreenRectangle, translationY, zoom),
+						pixel2logical_x((float)c.getCtrlX1(), flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y((float)c.getCtrlY1(), flipY, documentScreenRectangle, translationY, zoom),
+						pixel2logical_x((float)c.getCtrlX2(), flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y((float)c.getCtrlY2(), flipY, documentScreenRectangle, translationY, zoom),
+						pixel2logical_x((float)c.getX2(), flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y((float)c.getY2(), flipY, documentScreenRectangle, translationY, zoom));
+			}
+			case QUAD_CURVE:
+			{
+				QuadCurve2D c = (QuadCurve2D)s ;
+				return (T)new QuadCurve2D.Double(
+						pixel2logical_x((float)c.getX1(), flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y((float)c.getY1(), flipY, documentScreenRectangle, translationY, zoom),
+						pixel2logical_x((float)c.getCtrlX(), flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y((float)c.getCtrlY(), flipY, documentScreenRectangle, translationY, zoom),
+						pixel2logical_x((float)c.getX2(), flipX, documentScreenRectangle, translationX, zoom),
+						pixel2logical_y((float)c.getY2(), flipY, documentScreenRectangle, translationY, zoom));
+			}
+			case PATH:
+			{
+				Path2D p = (Path2D)s;
+				Path2D p2 = new GeneralPath(p.getWindingRule());
+				create2(
+						p, flipX, flipY, documentScreenRectangle,
+						translationX, translationY, zoom, p2);
+				return (T)p2;
+			}
+			case AREA:
+			{
+				Area a = (Area)s ;
+				GeneralPath p2 = new GeneralPath();
+				create2(
+						a, flipX, flipY, documentScreenRectangle,
+						translationX, translationY, zoom, p2);
+				return (T)new Area(p2);
+			}
+			case VIRTUALIZABLE_SHAPE:
+			{
+				VirtualizableShape vs = (VirtualizableShape)s;
+				return (T)vs.fromScreen(flipX, flipY, documentScreenRectangle, translationX, translationY, zoom);
+			}
+			default:
+				throw new IllegalArgumentException();
+			}
+		}
+		catch(ClassCastException _) {
+			//
+		}
+
+		throw new UnsupportedShapeException(type);
+	}
+
+	/** Translates the specified workspace location
+	 *  into the screen location.
+	 *
+	 * @param l is the coordinate along the workspace space X-axis.
+	 * @param canZoom indicates if the zooming should be applied.
+	 * @param flip indicates if the X axis should be inverted.
+	 * @param documentScreenRectangle is the area covered by the document in screen space.
+	 * @param translation is the current translation of the view.
+	 * @param zoom is the current zooming factor of the view.
+	 * @return a location along the screen space X-axis.
+	 */
+	public static float logical2pixel_x(float l, boolean canZoom, boolean flip,
+			Rectangle2D documentScreenRectangle, float translation, float zoom) {
+		if (canZoom)
+			return logical2pixel_x(l, flip, documentScreenRectangle, translation, zoom);
+		return l;
+	}		
+	
+	private static float logical2pixel_x(float l, boolean flip,
+			Rectangle2D documentScreenRectangle, float translation, float zoom) {
+		float p = (l+translation) * zoom;
+		if (flip) {
+			p = (float) ((documentScreenRectangle.getX() + documentScreenRectangle.getWidth()) - (p - documentScreenRectangle.getX()));
+		}
+		return p;
+	}
+
+	/** Translates the specified workspace location
+	 *  into the screen location.
+	 *
+	 * @param l is the coordinate along the workspace space Y-axis.
+	 * @param canZoom indicates if the zooming should be applied.
+	 * @param flip indicates if the Y axis should be inverted.
+	 * @param documentScreenRectangle is the area covered by the document in screen space.
+	 * @param translation is the current translation of the view.
+	 * @param zoom is the current zooming factor of the view.
+	 * @return a location along the screen space Y-axis.
+	 */
+	public static float logical2pixel_y(float l, boolean canZoom, boolean flip,
+			Rectangle2D documentScreenRectangle, float translation, float zoom) {
+		if (canZoom)
+			return logical2pixel_y(l, flip, documentScreenRectangle, translation, zoom);
+		return l;
+	}
+	
+	private static float logical2pixel_y(float l, boolean flip,
+			Rectangle2D documentScreenRectangle, float translation, float zoom) {
+		float p = (l+translation) * zoom;
+		if (flip) {
+			p = (float) ((documentScreenRectangle.getY() + documentScreenRectangle.getHeight()) - (p - documentScreenRectangle.getY()));
+		}
+		return p;
+	}
+
+	/** Translates the specified screen location
+	 *  into the logical location.
+	 *
+	 * @param p is the location along the screen space X-axis.
+	 * @param canZoom indicates if the zooming should be applied.
+	 * @param flip indicates if the X axis should be inverted.
+	 * @param documentScreenRectangle is the area covered by the document in screen space.
+	 * @param translation is the current translation of the view.
+	 * @param zoom is the current zooming factor of the view.
+	 * @return a location along the logical space X-axis.
+	 */
+	public static float pixel2logical_x(float p, boolean canZoom, boolean flip,
+			Rectangle2D documentScreenRectangle, float translation, float zoom) {
+		if (canZoom)
+			return pixel2logical_x(p, flip, documentScreenRectangle, translation, zoom);
+		return p;
+	}
+
+	private static float pixel2logical_x(float p, boolean flip,
+			Rectangle2D documentScreenRectangle, float translation, float zoom) {
+		float fp;
+		if (flip) {
+			fp = (float) ((documentScreenRectangle.getX() + documentScreenRectangle.getWidth()) - (p - documentScreenRectangle.getX()));
+		}
+		else {
+			fp = p;
+		}
+		return (fp / zoom) - translation;
+	}
+
+	/** Translates the specified screen location
+	 *  into the logical location.
+	 *
+	 * @param p is the location along the screen space Y-axis.
+	 * @param canZoom indicates if the zooming should be applied.
+	 * @param flip indicates if the Y axis should be inverted.
+	 * @param documentScreenRectangle is the area covered by the document in screen space.
+	 * @param translation is the current translation of the view.
+	 * @param zoom is the current zooming factor of the view.
+	 * @return a location along the logical space Y-axis.
+	 */
+	public static float pixel2logical_y(float p, boolean canZoom, boolean flip,
+			Rectangle2D documentScreenRectangle, float translation, float zoom) {
+		if (canZoom)
+			return pixel2logical_y(p, flip, documentScreenRectangle, translation, zoom);
+		return p;
+	}
+	
+	private static float pixel2logical_y(float p, boolean flip,
+			Rectangle2D documentScreenRectangle, float translation, float zoom) {
+		float fp;
+		if (flip) {
+			fp = (float) ((documentScreenRectangle.getY() + documentScreenRectangle.getHeight()) - (p - documentScreenRectangle.getY()));
+		}
+		else {
+			fp = p;
+		}
+		return (fp / zoom) - translation;
+	}
+
+	/** Translates the specified workspace length
+	 *  into the screen length.
+	 *
+	 * @param l is the length in the workspace space.
+	 * @param canZoom indicates if the zooming should be applied.
+	 * @param zoom is the current zooming factor of the view.
+	 * @return a length into the screen space.
+	 */
+	public static float logical2pixel_size(float l, boolean canZoom, float zoom) {
+		if (canZoom)
+			return logical2pixel_size(l, zoom);
+		return l;
+	}
+
+	private static float logical2pixel_size(float l, float zoom) {
+		float s = l * zoom;
+		if ((l!=0)&&(s==0)) s = 1f;
+		return s;
+	}
+
+	/** Translates the specified screen length
+	 *  into the logical length.
+	 *
+	 * @param l is the length in the screen space.
+	 * @param canZoom indicates if the zooming should be applied.
+	 * @param zoom is the current zooming factor of the view.
+	 * @return a length into the logical space.
+	 */
+	public static float pixel2logical_size(float l, boolean canZoom, float zoom) {
+		if (canZoom)
+			return pixel2logical_size(l, zoom);
+		return l;
+	}
+
+	private static float pixel2logical_size(float l, float zoom) {
+		return l / zoom;
+	}
+	
+	/** Translate the specified font from a logical length to a screen length.
+	 * 
+	 * @param font is the font with a size in the logical coordinate system.
+	 * @param canZoom indicates if the zooming should be applied.
+	 * @param zoom is the current zooming factor of the view.
+	 * @return the font with a size suitable for the screen coordinate system.
+	 */
+	public static Font logical2pixel(Font font, boolean canZoom, float zoom) {
+		if (canZoom) {
+			float nSize = logical2pixel_size(font.getSize2D(), zoom);
+			return font.deriveFont(nSize);
+		}
+		return font;
+	}
+
+	/** Translate the specified font from a screen length to a logical length.
+	 * 
+	 * @param font is the font with a size in the screen coordinate system.
+	 * @param canZoom indicates if the zooming should be applied.
+	 * @param zoom is the current zooming factor of the view.
+	 * @return the font with a size suitable for the logical coordinate system.
+	 */
+	public static Font pixel2logical(Font font, boolean canZoom, float zoom) {
+		/*Map<TextAttribute,Object> attrs = (Map<TextAttribute,Object>)font.getAttributes();
+		attrs.put(TextAttribute.SIZE, pixel2logical_size(font.getSize2D()));
+		*/
+		if (canZoom) {
+			float nSize = pixel2logical_size(font.getSize2D(), zoom);
+			return font.deriveFont(nSize);
+		}
+		return font;
+	}
+
+	/** Translates the specified path
+	 *  into the screen space.
+	 *
+	 * @param path is the path in the logical space.
+	 * @param canZoom indicates if the zooming should be applied.
+	 * @param flipX indicates if the X screen axis is inverted.
+	 * @param flipY indicates if the Y screen axis is inverted.
+	 * @param documentScreenRectangle is the area covered by the document in screen space.
+	 * @param translationX is the translation X in the context.
+	 * @param translationY is the translation Y in the context.
+	 * @param zoom is the current zooming factor of the view.
+	 * @return a shape into the screen space.
+	 */
+	public static Path2D logical2pixel(
+			PathIterator path, boolean canZoom,
+			boolean flipX, boolean flipY,
+			Rectangle2D documentScreenRectangle,
+			float translationX, float translationY,
+			float zoom) {
+		if (canZoom) {
+			Path2D p2 = new GeneralPath(path.getWindingRule());
+			create(
+					path, flipX, flipY, documentScreenRectangle,
+					translationX, translationY, zoom, p2);
+			return p2;
+		}
+		GeneralPath pathObj = new GeneralPath();
+		float[] coords = new float[8];
+		while (!path.isDone()) {
+			switch(path.currentSegment(coords)) {
+			case PathIterator.SEG_MOVETO:
+				pathObj.moveTo(
+						logical2pixel_x(coords[0], flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(coords[1], flipY, documentScreenRectangle, translationY, zoom));
+				break;
+			case PathIterator.SEG_LINETO:
+				pathObj.lineTo(
+						logical2pixel_x(coords[0], flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(coords[1], flipY, documentScreenRectangle, translationY, zoom));
+				break;
+			case PathIterator.SEG_CUBICTO:
+				pathObj.curveTo(
+						logical2pixel_x(coords[0], flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(coords[1], flipY, documentScreenRectangle, translationY, zoom),
+						logical2pixel_x(coords[2], flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(coords[3], flipY, documentScreenRectangle, translationY, zoom),
+						logical2pixel_x(coords[4], flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(coords[5], flipY, documentScreenRectangle, translationY, zoom));
+				break;
+			case PathIterator.SEG_QUADTO:
+				pathObj.quadTo(
+						logical2pixel_x(coords[0], flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(coords[1], flipY, documentScreenRectangle, translationY, zoom),
+						logical2pixel_x(coords[2], flipX, documentScreenRectangle, translationX, zoom),
+						logical2pixel_y(coords[3], flipY, documentScreenRectangle, translationY, zoom));
+				break;
+			case PathIterator.SEG_CLOSE:
+				pathObj.closePath();
+				break;
+			default:
+				throw new IllegalArgumentException();
+			}
+			path.next();
+		}
+		return pathObj;
+	}
+}
\ No newline at end of file


Property changes on: trunk/ui/ui-base
___________________________________________________________________
Added: svn:ignore
   + .classpath
..metadata
..settings
..project
target
bin


Added: trunk/ui/ui-base/pom.xml
===================================================================
--- trunk/ui/ui-base/pom.xml	                        (rev 0)
+++ trunk/ui/ui-base/pom.xml	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,26 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.arakhne.afc</groupId>
+		<artifactId>arakhneUi</artifactId>
+		<version>1.0-SNAPSHOT</version>
+	</parent>
+
+	<groupId>org.arakhne.afc.ui</groupId>
+	<artifactId>ui-base</artifactId>
+	<version>1.0-SNAPSHOT</version>
+	<name>Arakhne Base UI Tools</name>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.arakhne.afc</groupId>
+			<artifactId>math</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.arakhne.afc</groupId>
+			<artifactId>util</artifactId>
+		</dependency>
+	</dependencies>
+
+</project>

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/Graphics2DLOD.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/Graphics2DLOD.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/Graphics2DLOD.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,51 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui;
+
+/** Express the different level of details supported
+ * by a zoomable panel.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public enum Graphics2DLOD {
+
+	/** The graphical elements must be drawn as they are shadows.
+	 */
+	SHADOW,
+
+	/** The graphical elements must be drawn with a low level of detail.
+	 */
+	LOW_LEVEL_OF_DETAIL,
+	
+	/** The graphical elements must be drawn with a standard level of detail.
+	 */
+	NORMAL_LEVEL_OF_DETAIL,
+	
+	/** The graphical elements must be drawn with a high level of detail.
+	 */
+	HIGH_LEVEL_OF_DETAIL;
+	
+}
\ No newline at end of file

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/MouseCursor.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/MouseCursor.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/MouseCursor.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,104 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui;
+
+/** Mouse cursors. 
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public enum MouseCursor {
+
+	/** Default mouse cursor.
+	 */
+	DEFAULT,
+	
+    /**
+     * The crosshair cursor.
+     */
+    CROSSHAIR,
+
+    /**
+     * The text cursor.
+     */
+    TEXT,
+
+    /**
+     * The wait cursor.
+     */
+    WAIT,
+
+    /**
+     * The south-west-resize cursor.
+     */
+    SW_RESIZE,
+
+    /**
+     * The south-east-resize cursor.
+     */
+    SE_RESIZE,
+
+    /**
+     * The north-west-resize cursor.
+     */
+    NW_RESIZE,
+
+    /**
+     * The north-east-resize cursor.
+     */
+    NE_RESIZE,
+
+    /**
+     * The north-resize cursor.
+     */
+    N_RESIZE,
+
+    /**
+     * The south-resize cursor.
+     */
+    S_RESIZE,
+
+    /**
+     * The west-resize cursor.
+     */
+    W_RESIZE,
+
+    /**
+     * The east-resize cursor.
+     */
+    E_RESIZE,
+
+    /**
+     * The hand cursor.
+     */
+    HAND,
+
+    /**
+     * The move cursor.
+     */
+    MOVE,
+
+    /** Invalid.
+	 */
+	INVALID;
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/StringAnchor.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/StringAnchor.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/StringAnchor.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,43 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+
+package org.arakhne.afc.ui;
+
+/** The anchor for all the strings.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public enum StringAnchor {
+	/** Anchor it at upper left.
+	 */
+	UPPER_LEFT,
+	/** Anchor it at the left and at the baseline.
+	 */
+	LEFT_BASELINE,
+	/** Anchor it at lower left.
+	 */
+	LOWER_LEFT;	
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/TextAlignment.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/TextAlignment.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/TextAlignment.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,45 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+
+package org.arakhne.afc.ui;
+
+/** Text alignment
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public enum TextAlignment {
+	/** Left alignment.
+	 */
+	LEFT_ALIGN,
+
+	/** Center alignment.
+	 */
+	CENTER_ALIGN,
+
+	/** Right alignment.
+	 */
+	RIGHT_ALIGN;
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/ZoomableContext.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/ZoomableContext.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/ZoomableContext.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,122 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui;
+
+/** This interface describes a zooming context and
+ * permits to make some operation in it.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface ZoomableContext {
+
+	/** Translates the specified workspace length
+	 *  into the screen length.
+	 *
+	 * @param l is the length in the workspace space.
+	 * @return a length into the screen space.
+	 */
+	public float logical2pixel_size(float l);
+	
+	/** Translates the specified workspace location
+	 *  into the screen location.
+	 *
+	 * @param l is the coordinate along the workspace space X-axis.
+	 * @return a location along the screen space X-axis.
+	 */
+	public float logical2pixel_x(float l);
+	
+	/** Translates the specified workspace location
+	 *  into the screen location.
+	 *
+	 * @param l is the coordinate along the workspace space Y-axis.
+	 * @return a location along the screen space Y-axis.
+	 */
+	public float logical2pixel_y(float l);
+	
+	/** Translates the specified screen length
+	 *  into the logical length.
+	 *
+	 * @param l is the length in the screen space.
+	 * @return a length into the logical space.
+	 */
+	public float pixel2logical_size(float l);
+	
+	/** Translates the specified screen location
+	 *  into the logical location.
+	 *
+	 * @param l is the location along the screen space X-axis.
+	 * @return a location along the logical space X-axis.
+	 */
+	public float pixel2logical_x(float l);
+	
+	/** Translates the specified screen location
+	 *  into the logical location.
+	 *
+	 * @param l is the location along the screen space Y-axis.
+	 * @return a location along the logical space Y-axis.
+	 */
+	public float pixel2logical_y(float l);
+
+    /** Replies the graphical zoom factor.
+     * 
+     * @return the scale factor.
+     */
+    public float getScalingFactor();    
+
+	/** Replies the maximal scaling factor allowing in the view
+	 * 
+	 * @return the maximal scaling factor.
+	 */
+	public float getMaxScalingFactor();
+
+	/** Replies the minimal scaling factor allowing in the view
+	 * 
+	 * @return the minimal scaling factor.
+	 */
+	public float getMinScalingFactor();
+	
+	/** Replies the sensitivity of the {@code zoomIn()}
+	 * and {@code zoomOut()} functions.
+	 * 
+	 * @return the sensitivity of the zooming functions.
+	 */
+	public float getScalingSensitivity();
+	
+	/** Replies the X coordinate of the point that is drawn
+	 * at the center of the graphical viewport.
+	 * 
+	 * @return the X coordinate of the view center. 
+	 */
+	public float getFocusX();
+
+	/** Replies the Y coordinate of the point that is drawn
+	 * at the center of the graphical viewport.
+	 * 
+	 * @return the Y coordinate of the view center. 
+	 */
+	public float getFocusY();
+
+}
\ No newline at end of file

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionMode.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionMode.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionMode.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,464 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.actionmode ;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
+
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.ui.MouseCursor;
+import org.arakhne.afc.ui.event.KeyEvent;
+import org.arakhne.afc.ui.event.PointerEvent;
+import org.arakhne.afc.ui.selection.Selectable;
+import org.arakhne.vmutil.locale.Locale;
+
+/** This is the abstract superclass of all editor modes.  A Mode is
+ *  responsible for handling most of the events that come to the
+ *  Editor.  A Mode defines a context for interperting those events.
+ *  For example, a mouse drag in BaseMode
+ *  will define a selection rectangle, while a mouse drag in
+ *  EdgeCreationMode will drag out a
+ *  rubberband line.  Placing the logic for most event handing in
+ *  Modes is key to keeping the Editor source code small and
+ *  managable, and also key for allowing addition of new kinds of user
+ *  interactions without modifying Editor and having to
+ *  integrate these modifications with other contributors
+ *  modifications.
+ *
+ * @param <DRAW> is the type of the graphical object supported by this container.
+ * @param <CANVAS> is the type of the drawing canvas.
+ * @param <COLOR> is the type that is representing a color.
+ * @author $Author: galland$
+ * @author $Author: hannoun$
+ * @author $Author: baumgartner$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class ActionMode<DRAW extends Selectable, CANVAS, COLOR> {
+
+	private WeakReference<ActionModeManager<DRAW,CANVAS,COLOR>> modeManager ;
+
+	/** Say if the cursor has changed.
+	 */
+	protected boolean cursorChanged = false;
+	
+	private boolean isPersistent;
+
+	/** Construct a new Mode.
+	 *
+	 * @param modeManager a reference to the ModeManager that
+	 *                    contains this Mode.
+	 */
+	public ActionMode(ActionModeManager<DRAW,CANVAS,COLOR> modeManager) {
+		this.modeManager = new WeakReference<ActionModeManager<DRAW,CANVAS,COLOR>>(modeManager) ;
+	}
+
+	/** Construct a new Mode.
+	 */
+	public ActionMode() {
+		this.modeManager = null ;
+	}
+	
+	/** Replies if this mode stay active after it has
+	 * created an edge.
+	 * 
+	 * @return <code>true</code> if the mode stay active;
+	 * <code>false</code> if it is stopping is execution.
+	 */
+	public boolean isPersistent() {
+		return this.isPersistent;
+	}
+	
+	/** Set if this mode stay active after it has
+	 * created an edge.
+	 * 
+	 * @param persistent is <code>true</code> if the mode stay active;
+	 * <code>false</code> if it is stopping is execution.
+	 */
+	public void setPersistent(boolean persistent) {
+		this.isPersistent = persistent;
+	}
+
+	/** Invoked when the mode is activated, ie put in the mode stack.
+	 */
+	protected void onModeActivated() {
+		//
+	}
+	
+	/** Invoked when the mode is desactivated, ie removed in the mode stack.
+	 */
+	protected void onModeDesactivated() {
+		//
+	}
+
+	/** Repaint the mode container.
+	 * 
+	 * @param bounds is the area to repaint.
+	 */
+	public void repaint(Rectangle2f bounds) {
+		getModeManagerOwner().repaint(bounds);
+	}
+
+	/** Repaint the mode container.
+	 */
+	public void repaint() {
+		getModeManagerOwner().repaint();
+	}
+
+	/** Set the parent ModeManager of this Mode.
+	 * @param modeManager a reference to the ModeManager that
+	 *                    contains this Mode.
+	 */
+	public void setModeManager(ActionModeManager<DRAW,CANVAS,COLOR> modeManager) {
+		this.modeManager = modeManager==null ? null : new WeakReference<ActionModeManager<DRAW,CANVAS,COLOR>>(modeManager);
+		setCursor(getInitialCursor());
+	}
+
+	/** Reply the parent ModeManager of this Mode.
+	 * @return a reference to the ModeManager that
+	 *         contains this Mode.
+	 */
+	public ActionModeManager<DRAW,CANVAS,COLOR> getModeManager() {
+		return this.modeManager==null ? null : this.modeManager.get();
+	}
+
+	/** Reply the root mode container.
+	 * @return a reference to the ModeContainer.
+	 */
+	public ActionModeManagerOwner<DRAW,CANVAS,COLOR> getModeManagerOwner() {
+		ActionModeManager<DRAW,CANVAS,COLOR> manager = getModeManager();
+		if (manager==null) return null;
+		return manager.getModeManagerOwner();
+	}
+
+	/** Request the key focus for the Workspace parent.
+	 *  <p>
+	 *  This method add to the focus manager to give 
+	 *  keyboard events to the container of the mode manager.
+	 */
+	public void requestFocus() {
+		ActionModeManager<DRAW,CANVAS,COLOR> mm = getModeManager();
+		if (mm!=null) mm.getModeManagerOwner().requestFocus();
+	}
+
+	/** Returns the cursor that should be shown when this Mode starts.
+	 *
+	 * @return a reference to a cursor.
+	 */
+	@SuppressWarnings("static-method")
+	public MouseCursor getInitialCursor() {
+		return MouseCursor.DEFAULT;
+	}
+
+	/** Set the mouse cursor to some appropriate for this mode.
+	 *
+	 * @param c a reference to the new mouse cursor.
+	 */
+	public void setCursor(MouseCursor c) {
+		this.cursorChanged = true;
+		ActionModeManager<DRAW,CANVAS,COLOR> mm = getModeManager();
+		if (mm!=null) mm.getModeManagerOwner().setCursor(
+				c==null ? MouseCursor.DEFAULT : c);
+	}   
+
+	/** Set the mouse cursor to the default.
+	 */
+	protected void restoreCursor() {
+		if ( this.cursorChanged ) {
+			setCursor( getInitialCursor() );
+			this.cursorChanged = false;
+		}
+	}
+
+	/** Clean and pop this mode from the
+	 *  {@link ActionModeManager ModeManager} stack.
+	 *
+	 * @see #setExclusive(boolean)
+	 * @see ActionModeManager#endMode()
+	 * @see #cleanMode()
+	 */
+	public void done() {
+		setCursor(MouseCursor.DEFAULT);
+		setExclusive( false );
+		cleanMode();
+		ActionModeManager<DRAW,CANVAS,COLOR> mm = getModeManager();
+		if (mm!=null) mm.endMode();
+	}
+
+	/** Reply is this mode is exclusive.
+	 *
+	 * @return <code>true</code> if this Mode is the exclusive
+	 *         mode of the enclosing {@link ActionModeManager ModeManager},
+	 *         otherwise <code>false</code>.
+	 * @see ActionModeManager#isExclusiveMode(ActionMode)
+	 */
+	public boolean isExclusive() {
+		ActionModeManager<DRAW,CANVAS,COLOR> mm = getModeManager();
+		if (mm==null) return false;
+		return mm.isExclusiveMode( this );
+	}
+
+	/** Set this mode to exclusive state or not.
+	 *
+	 * @param exclu <code>true</code> if this Mode must be the exclusive
+	 *              mode of the enclosing {@link ActionModeManager ModeManager},
+	 *              otherwise <code>false</code>.
+	 * @see ActionModeManager#setExclusiveMode(ActionMode,boolean)
+	 */
+	public void setExclusive( boolean exclu ) {
+		ActionModeManager<DRAW,CANVAS,COLOR> mm = getModeManager();
+		if (mm!=null) {
+			mm.setExclusiveMode( this, exclu );
+		}
+	}   
+
+	/** Ask to this mode to return to a stable state.
+	 *  This method was called each time this Mode
+	 *  need to be in a stable state e.g., initial
+	 *  mode state.
+	 */
+	public void cleanMode() {
+		//
+	}
+
+	/** Some Mode's should never be exited, but by default any Mode can
+	 *  exit. Mode's which return <code>false</code> for canExit()
+	 *  will not be popped from a {@link ActionModeManager}.
+	 *
+	 * @return <code>true</code> if this mode can be stopped and popped
+	 *         from the ModeManager's stack.
+	 */
+	@SuppressWarnings("static-method")
+	public boolean canExit() { 
+		return true; 
+	}
+
+	/** Modes can paint themselves to give to the user some feedback.
+	 *
+	 * @param g the graphic context.
+	 */
+	public void paint(CANVAS g) {
+		//
+	}
+
+	/** Replies the figure under the mouse cursor.
+	 * To know is the mouse cursor is inside the
+	 * shape of the figure pluease invoke
+	 * {@link #isPointerInFigureShape()}.
+	 * 
+	 * @return the figure under the mosue cursor;
+	 * or <code>null</code> if none.
+	 * @see #isPointerInFigureShape()
+	 */
+	public DRAW getPointedFigure() {
+		return getModeManager().getPointedFigure();
+	}
+	
+	/** Replies if the mouse pointer is inside the shape
+	 * of the pointer figure.
+	 * To determine the pointed figure, please invoke
+	 * {@link #getPointedFigure()}.
+	 * 
+	 * @return <code>true</code> if the mouse pointer is
+	 * inside the shape; otherwise <code>false</code>.
+	 * @see #getPointedFigure()
+	 */
+	public boolean isPointerInFigureShape() {
+		return getModeManager().isPointerInFigureShape();
+	}
+	
+	/** Delegate the execution to the specified mode.
+	 * 
+	 * @param delegation is the type of the mode to create and delegate to.
+	 * @return the delegated mode.
+	 */
+	protected <M extends ActionMode<DRAW,CANVAS,COLOR>> M createDelegation(Class<M> delegation) {
+		M mode = null;
+		try {
+			Constructor<M> cons = delegation.getConstructor(ActionModeManager.class);
+			mode = cons.newInstance(getModeManager());
+		}
+		catch (Throwable e) {
+			//
+		}
+		if (mode==null) {
+			try {
+				mode = delegation.newInstance();
+			}
+			catch (Throwable e) {
+				throw new ActionModeException(Locale.getString("NOT_MODE_TRIGGER"), e); //$NON-NLS-1$
+			}
+			mode.setModeManager(getModeManager());
+		}
+		assert(mode!=null);
+		getModeManager().beginMode(mode);
+		return mode;
+	}
+	
+	/** Delegate the execution to the specified mode.
+	 * 
+	 * @param mode is the mode to delegate to.
+	 */
+	protected <M extends ActionMode<DRAW,CANVAS,COLOR>> void createDelegation(M mode) {
+		assert(mode!=null);
+		mode.setModeManager(getModeManager());
+		getModeManager().beginMode(mode);
+		repaint();
+	}
+
+	/** Force the mode manager to consider the specified event.
+	 * 
+	 * @param event
+	 */
+	protected void forcePointerEvent(PointerEvent event) {
+		getModeManager().updatePointerInfo(event, true);
+	}
+	
+	/**
+     * Invoked when a key has been typed.
+     * See the class description for {@link KeyEvent} for a definition of
+     * a key typed event.
+     * <p>
+     * The KEY_TYPED event may be unavailable on several components.
+     * You must read the document of the component on which the action management
+     * is applied to be sure that the KEY_TYPED event is handled.
+     * 
+     * @param e
+     */
+    public void keyTyped(KeyEvent e) {
+    	//
+    }
+
+    /**
+     * Invoked when a key has been pressed.
+     * See the class description for {@link KeyEvent} for a definition of
+     * a key pressed event.
+     * <p>
+     * The KEY_PRESSED event may be unavailable on several components.
+     * You must read the document of the component on which the action management
+     * is applied to be sure that the KEY_PRESSED event is handled.
+     * 
+     * @param e
+     */
+    public void keyPressed(KeyEvent e) {
+    	//
+    }
+
+    /**
+     * Invoked when a key has been released.
+     * See the class description for {@link KeyEvent} for a definition of
+     * a key released event.
+     * <p>
+     * The KEY_RELEASED event may be unavailable on several components.
+     * You must read the document of the component on which the action management
+     * is applied to be sure that the KEY_RELEASED event is handled.
+     * 
+     * @param e
+     */
+    public void keyReleased(KeyEvent e) {
+    	//
+    }
+
+    /**
+     * Invoked when the pointer button has been clicked (pressed
+     * and released) on a component.
+     * <p>
+     * The POINTER_CLICKED event may be unavailable on several components.
+     * You must read the document of the component on which the action management
+     * is applied to be sure that the POINTER_CLICKED event is handled.
+     * 
+     * @param e
+     * @see #pointerLongClicked(PointerEvent)
+     */
+    public void pointerClicked(PointerEvent e) {
+    	//
+    }
+
+    /**
+     * Invoked when the pointer button has been long clicked (pressed
+     * and released) on a component.
+     * <p>
+     * The POINTER_LONG_CLICKED event may be unavailable on several components.
+     * You must read the document of the component on which the action management
+     * is applied to be sure that the POINTER_LONG_CLICKED event is handled.
+     * 
+     * @param e
+     * @see #pointerClicked(PointerEvent)
+     */
+    public void pointerLongClicked(PointerEvent e) {
+    	//
+    }
+
+    /**
+     * Invoked when a pointer button has been pressed on a component.
+     * <p>
+     * The POINTER_PRESSED event may be unavailable on several components.
+     * You must read the document of the component on which the action management
+     * is applied to be sure that the POINTER_PRESSED event is handled.
+     * 
+     * @param e
+     */
+    public void pointerPressed(PointerEvent e) {
+    	//
+    }
+
+    /**
+     * Invoked when a pointer button has been released on a component.
+     * <p>
+     * The POINTER_RELEASED event may be unavailable on several components.
+     * You must read the document of the component on which the action management
+     * is applied to be sure that the POINTER_RELEASED event is handled.
+     * 
+     * @param e
+     */
+    public void pointerReleased(PointerEvent e) {
+    	//
+    }
+
+    /**
+     * Invoked when the mouse is moved with a button down.
+     * <p>
+     * The POINTER_DRAGGED event may be unavailable on several components.
+     * You must read the document of the component on which the action management
+     * is applied to be sure that the POINTER_DRAGGED event is handled.
+     * 
+     * @param e
+     */
+    public void pointerDragged(PointerEvent e) {
+    	//
+    }
+
+    /**
+     * Invoked when the mouse is moved with no button down.
+     * <p>
+     * The POINTER_MOVED event may be unavailable on several components.
+     * You must read the document of the component on which the action management
+     * is applied to be sure that the POINTER_MOVED event is handled.
+     * 
+     * @param e
+     */
+    public void pointerMoved(PointerEvent e) {
+    	//
+    }
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeAdapter.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeAdapter.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeAdapter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,47 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.actionmode ;
+
+/** This interface represents a listener on the mode events.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class ActionModeAdapter implements ActionModeListener {
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void modeActivated(ActionMode<?,?,?> mode) {
+		//
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void modeDesactivated(ActionMode<?,?,?> mode) {
+		//
+	}
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeException.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeException.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeException.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,57 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.actionmode ;
+
+/** Exception in modes.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class ActionModeException extends RuntimeException {
+
+	private static final long serialVersionUID = 4879931058627554098L;
+
+	/**
+	 * @param message is the error message.
+	 */
+	public ActionModeException(String message) {
+		super(message);
+	}
+
+	/**
+	 * @param exception
+	 */
+	public ActionModeException(Throwable exception) {
+		super(exception);
+	}
+
+	/**
+	 * @param message is the error message.
+	 * @param exception
+	 */
+	public ActionModeException(String message, Throwable exception) {
+		super(message, exception);
+	}
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeListener.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeListener.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeListener.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,47 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.actionmode ;
+
+import java.util.EventListener;
+
+/** This interface represents a listener on the mode events.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface ActionModeListener extends EventListener {
+
+	/** Invoked when a mode is activated.
+	 * 
+	 * @param mode
+	 */
+	public void modeActivated(ActionMode<?,?,?> mode);
+	
+	/** Invoked when a mode is desactivated.
+	 * 
+	 * @param mode
+	 */
+	public void modeDesactivated(ActionMode<?,?,?> mode);
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeManager.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeManager.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeManager.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,745 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.actionmode ;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EventListener;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.ui.ZoomableContext;
+import org.arakhne.afc.ui.event.KeyEvent;
+import org.arakhne.afc.ui.event.PointerEvent;
+import org.arakhne.afc.ui.selection.Selectable;
+import org.arakhne.afc.util.ListenerCollection;
+
+/** ModeManager keeps track of all the 
+ *  {@link ActionMode Modes} for a given workspace.
+ *  Events are passed to the Modes for handling.  The submodes are
+ *  prioritized according to their order on a stack, i.e., the last
+ *  Mode added gets the first chance to handle an Event.  
+ *  <p>
+ *  This ModeManager takes into account an exclusive Mode. An exclusive
+ *  mode is a {@link ActionMode Mode} was received all events before the
+ *  stacked modes. If an event was not eated by the exclusive mode,
+ *  the exclusive mode was destroy and the event was ignored.
+ *
+ * @param <DRAW> is the type of the data supported by this container.
+ * @param <CANVAS> is the type of the drawing canvas.
+ * @param <COLOR> is the type that is representing a color.
+ * @author $Author: galland$
+ * @author $Author: hannoun$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class ActionModeManager<DRAW extends Selectable, CANVAS, COLOR> {
+
+	private final WeakReference<ActionModeManagerOwner<DRAW,CANVAS,COLOR>> container;
+	
+	private final LinkedList<ActionMode<? super DRAW,CANVAS,COLOR>> modeStack = new LinkedList<ActionMode<? super DRAW,CANVAS,COLOR>>();
+	
+	private final UUID viewID;
+	
+	private ActionMode<? super DRAW,CANVAS,COLOR> exclusiveMode = null;
+	
+	private boolean isFigureUnderTheMouseComputed = false;
+	private WeakReference<DRAW> figureUnderTheMouse = null;
+	
+	private boolean isForceHitResetWhenRelease = false;
+	
+	private float mouseX = Float.NaN;
+	private float mouseY = Float.NaN;
+	
+	private final ListenerCollection<EventListener> listeners = new ListenerCollection<EventListener>();
+
+	/**  Construct a ModeManager with no modes.
+	 *
+	 * @param viewID is the identifier of the view associated to this manager.
+	 * @param component is a reference to the component that is 
+	 * containing this mode manager.
+	 */
+	public ActionModeManager(UUID viewID, ActionModeManagerOwner<DRAW,CANVAS,COLOR> component) {
+		assert(viewID!=null);
+		this.viewID = viewID;
+		assert(component!=null);
+		this.container = new WeakReference<ActionModeManagerOwner<DRAW,CANVAS,COLOR>>(component); 
+	}
+	
+	/** Replies an unmodifiable list of the modes.
+	 * 
+	 * @return the modes.
+	 */
+	public List<ActionMode<? super DRAW,CANVAS,COLOR>> getModes() {
+		return Collections.unmodifiableList(this.modeStack);
+	}
+	
+	/** Replies the first mode of the given type from the top
+	 * of the mode stack.
+	 *
+	 * @param type is the type of the mode to retreive.
+	 * @return the mode or <code>null</code> if not found.
+	 */
+	public <M extends ActionMode<? super DRAW,CANVAS,COLOR>> M getModeFromTop(Class<M> type) {
+		Iterator<ActionMode<? super DRAW,CANVAS,COLOR>> iterator = this.modeStack.descendingIterator();
+		ActionMode<? super DRAW,CANVAS,COLOR> mode;
+		while (iterator.hasNext()) {
+			mode = iterator.next();
+			if (type.isInstance(mode))
+				return type.cast(mode);
+		}
+		return null;
+	}
+
+	/** Replies the first mode of the given type from the bottom
+	 * of the mode stack.
+	 *
+	 * @param type is the type of the mode to retreive.
+	 * @return the mode or <code>null</code> if not found.
+	 */
+	public <M extends ActionMode<? super DRAW,CANVAS,COLOR>> M getModeFromBottom(Class<M> type) {
+		Iterator<ActionMode<? super DRAW,CANVAS,COLOR>> iterator = this.modeStack.iterator();
+		ActionMode<? super DRAW,CANVAS,COLOR> mode;
+		while (iterator.hasNext()) {
+			mode = iterator.next();
+			if (type.isInstance(mode))
+				return type.cast(mode);
+		}
+		return null;
+	}
+
+	/** Add listener on the mode events.
+	 * 
+	 * @param listener
+	 */
+	public void addModeListener(ActionModeListener listener) {
+		this.listeners.add(ActionModeListener.class, listener);
+	}
+	
+	/** Remove listener on the mode events.
+	 * 
+	 * @param listener
+	 */
+	public void removeModeListener(ActionModeListener listener) {
+		this.listeners.remove(ActionModeListener.class, listener);
+	}
+	
+	/** Add listener on the figure interaction events.
+	 * 
+	 * @param listener
+	 */
+	public void addSelectableInteractionListener(SelectableInteractionListener listener) {
+		this.listeners.add(SelectableInteractionListener.class, listener);
+	}
+	
+	/** Remove listener on the figure interaction events.
+	 * 
+	 * @param listener
+	 */
+	public void removeSelectableInteractionListener(SelectableInteractionListener listener) {
+		this.listeners.remove(SelectableInteractionListener.class, listener);
+	}
+
+	/** Notifies the listeners about the activation of a mode.
+	 * 
+	 * @param mode
+	 */
+	private void fireModeActivated(ActionMode<?,?,?> mode) {
+		for(ActionModeListener listener : this.listeners.getListeners(ActionModeListener.class)) {
+			listener.modeActivated(mode);
+		}
+	}
+
+	/** Notifies the listeners about the desactivation of a mode.
+	 * 
+	 * @param mode
+	 */
+	private void fireModeDesactivated(ActionMode<?,?,?> mode) {
+		for(ActionModeListener listener : this.listeners.getListeners(ActionModeListener.class)) {
+			listener.modeDesactivated(mode);
+		}
+	}
+
+	/** Notifies the listeners about the action performing on a figure.
+	 * 
+	 * @param figure is the figure on which the action was performed.
+	 */
+	public void fireActionPerformed(Collection<? extends DRAW> figure) {
+		SelectableInteractionEvent event = new SelectableInteractionEvent(this,figure,
+				getModeManagerOwner().isEditable());
+		if (figure instanceof SelectableInteractionListener)
+			((SelectableInteractionListener)figure).actionPerformed(event);
+		for(SelectableInteractionListener listener : this.listeners.getListeners(SelectableInteractionListener.class)) {
+			if (figure!=listener) listener.actionPerformed(event);
+		}
+	}
+
+	/** Notifies the listeners about the action performing on a figure.
+	 * 
+	 * @param figure is the figure on which the action was performed.
+	 * @param pointerEvent is the event causing the action performing.
+	 */
+	public void fireActionPerformed(Collection<? extends DRAW> figure, PointerEvent pointerEvent) {
+		SelectableInteractionEvent event = new SelectableInteractionEvent(this, figure, pointerEvent, getMousePosition(),
+				getModeManagerOwner().isEditable());
+		if (figure instanceof SelectableInteractionListener)
+			((SelectableInteractionListener)figure).actionPerformed(event);
+		for(SelectableInteractionListener listener : this.listeners.getListeners(SelectableInteractionListener.class)) {
+			if (figure!=listener) listener.actionPerformed(event);
+		}
+	}
+
+	/** Notifies the listeners about the action performing on a figure.
+	 * 
+	 * @param figure is the figure on which the action was performed.
+	 * @param keyEvent is the event causing the action performing.
+	 */
+	public void fireActionPerformed(Collection<? extends DRAW> figure, KeyEvent keyEvent) {
+		assert(figure!=null);
+		SelectableInteractionEvent event = new SelectableInteractionEvent(this, figure, keyEvent,
+				getModeManagerOwner().isEditable());
+		if (figure instanceof SelectableInteractionListener)
+			((SelectableInteractionListener)figure).actionPerformed(event);
+		for(SelectableInteractionListener listener : this.listeners.getListeners(SelectableInteractionListener.class)) {
+			if (figure!=listener) listener.actionPerformed(event);
+		}
+	}
+
+	/** Notifies the listeners about the popup action performing on a figure.
+	 * 
+	 * @param pointerEvent is the cause of the popup opening.
+	 * @param figure is the figure on which the action was performed. It may be
+	 * <code>null</code> if the popup action was performed on the background.
+	 */
+	public void firePopupPerformed(PointerEvent pointerEvent, DRAW figure) {
+		SelectableInteractionEvent event = new SelectableInteractionEvent(this,
+				Collections.singleton(figure), pointerEvent, getMousePosition(),
+				getModeManagerOwner().isEditable());
+		if (figure instanceof SelectableInteractionListener)
+			((SelectableInteractionListener)figure).popupPerformed(event);
+		for(SelectableInteractionListener listener : this.listeners.getListeners(SelectableInteractionListener.class)) {
+			if (figure!=listener) listener.popupPerformed(event);
+		}
+	}
+
+	/** Notifies the listeners about an error that occurs in one
+	 * of the JFigureEditor components.
+	 * This function is a wrapper to the ModeContainer function.
+	 * 
+	 * @param error
+	 * @since 16.0
+	 */
+	public void fireError(Throwable error) {
+		getModeManagerOwner().fireError(error);
+	}
+
+	/** Notifies the listeners about the a request to delete a figure and not of the associated
+	 * model object.
+	 * 
+	 * @param figure is the figure to delete.
+	 * @param deleteModel indicates if the underlying model object must be also deleted.
+	 * @return <code>true</code> if all the listeners accept the deletion
+	 * of the figure; <code>false</code> if at least one listener refuses
+	 * the deletion of the figure. 
+	 */
+	public boolean fireFigureDeletionPerformed(DRAW figure, boolean deleteModel) {
+		boolean auth = true;
+		for(SelectableInteractionListener listener : this.listeners.getListeners(SelectableInteractionListener.class)) {
+			if (!listener.figureDeletionPerformed(figure, deleteModel)) {
+				auth = false;
+			}
+		}
+		return auth;
+	}
+
+	/** Replies the identifier of the view associated to this mode manager.
+	 * 
+	 * @return the identifier of the view.
+	 */
+	public UUID getViewID() {
+		return this.viewID;
+	}
+	
+	/** Replies the component that is containing this mode manager.
+	 * 
+	 * @return the component.
+	 */
+	public ActionModeManagerOwner<DRAW,CANVAS,COLOR> getModeManagerOwner() {
+		return this.container.get();
+	}
+	
+	/** Paint each mode in the stack: bottom to top.
+	 *
+	 * @param g the graphic context.
+	 */
+	public void paint(CANVAS g) {
+		if (this.exclusiveMode!=null) {
+			this.exclusiveMode.paint(g);
+		}
+		else {
+			Iterator<ActionMode<? super DRAW,CANVAS,COLOR>> iterator = this.modeStack.iterator();
+			while (iterator.hasNext()) {
+				iterator.next().paint(g);
+			}
+		}
+	}
+
+	/** Reply if the given {@link ActionMode Mode} was the exclusive one.
+	 *
+	 * @param mode
+	 * @return <code>true</code> if <var>mode</var> was the exclusive
+	 *         mode, otherwise <code>false</code>.
+	 */
+	public boolean isExclusiveMode( ActionMode<? super DRAW,CANVAS,COLOR> mode ) {
+		return this.exclusiveMode!=null && this.exclusiveMode==mode;
+	}
+
+	/** Reply if this ModeManager have an exclusive Mode.
+	 *
+	 * @return <code>true</code> if this ModeManager have an
+	 *         exclusive mode, otherwise <code>false</code>.
+	 */
+	public boolean isExclusiveMode() {
+		return this.exclusiveMode != null;
+	}
+
+	/** Set or unset exclusive Mode for the given Mode.
+	 *  <p>
+	 *  If <var>exclu</var> was <code>true</code> then
+	 *  <var>mode</var> is the new exclusive mode <b>only if</b>
+	 *  it is already the exclusive mode or there are no
+	 *  exclusive mode.
+	 *  <p>
+	 *  If <var>exclu</var> was <code>false</code> then
+	 *  the current exclusive mode was unset <b>only if</b>
+	 *  <var>mode</var> was the current exclusive mode.
+	 *
+	 * @param mode a reference to the new/old exclusive mode.
+	 * @param isExclusive <code>true</code> if you would like to set
+	 *              <var>mode</var> as an exclusive mode,
+	 *              otherwise <code>false</code>.
+	 */
+	void setExclusiveMode( ActionMode<? super DRAW,CANVAS,COLOR> mode, boolean isExclusive ) {
+		if ( isExclusive ) {
+			if ( this.exclusiveMode != null ) {
+				this.exclusiveMode.cleanMode();
+			}
+			this.exclusiveMode = mode;
+		}
+		else if ( this.exclusiveMode == mode ) {
+			unsetExclusiveMode();
+		}
+	}
+
+	/** Unset exclusive Mode.
+	 */
+	void unsetExclusiveMode() { 
+		this.exclusiveMode = null; 
+	}
+
+
+	/** Set the editor's current mode to the given Mode instance.
+	 *
+	 * @param mode the mode to push to the stack.
+	 * @return <code>true</code> if the mode was started; otherwise <code>false</code>.
+	 */
+	public boolean beginMode(ActionMode<DRAW,CANVAS,COLOR> mode) {
+		if (this.modeStack.contains(mode)) return false;
+		mode.setModeManager(this) ;
+		this.modeStack.addLast(mode) ;
+		mode.onModeActivated();
+		fireModeActivated(mode);
+		return true;
+	}
+
+	/** Remove the current mode, and set the graph editor's mode to
+	 * the most topless mode.
+	 */
+	void endMode() {
+		if (!this.modeStack.isEmpty()) {
+			ActionMode<? super DRAW,CANVAS,COLOR> mode = this.modeStack.removeLast();
+			if ( isExclusiveMode( mode ) ) {
+				unsetExclusiveMode();
+			}
+			mode.onModeDesactivated();
+			fireModeDesactivated(mode);
+			mode.setModeManager(null);
+		}
+	}
+
+	/** This method permits to reset this manager by remove temp modes
+	 *  and clean permanent modes.
+	 */
+	public void resetModes() {
+		unsetExclusiveMode();
+		
+		List<ActionMode<? super DRAW,CANVAS,COLOR>> removedModes = new ArrayList<ActionMode<? super DRAW,CANVAS,COLOR>>();
+
+		boolean stop = false;
+		Iterator<ActionMode<? super DRAW,CANVAS,COLOR>> iterator = this.modeStack.descendingIterator();
+		while (!stop && iterator.hasNext()) {
+			ActionMode<? super DRAW,CANVAS,COLOR> m = iterator.next();
+			if (!m.canExit()) {
+				stop = true;
+			}
+			else {
+				iterator.remove();
+				m.cleanMode();
+				removedModes.add(m);
+			}
+		}
+		
+		for(ActionMode<? super DRAW,CANVAS,COLOR> m : removedModes) {
+			m.onModeDesactivated();
+			fireModeDesactivated(m);
+			m.setModeManager(null);
+		}
+	}
+	
+	/**
+     * Invoked when a key has been typed.
+     * See the class description for {@link KeyEvent} for a definition of
+     * a key typed event.
+     * 
+     * @param e
+     */
+    public void keyTyped(KeyEvent e) {
+		if (this.exclusiveMode!=null) {
+			this.exclusiveMode.keyTyped(e);
+		}
+		else {
+			Iterator<ActionMode<? super DRAW,CANVAS,COLOR>> iterator = this.modeStack.descendingIterator();
+			while (iterator.hasNext()) {
+				ActionMode<? super DRAW,CANVAS,COLOR> m = iterator.next();
+				m.keyTyped(e);
+				if (e.isConsumed()) return;
+			}
+		}
+    }
+
+    /**
+     * Invoked when a key has been pressed.
+     * See the class description for {@link KeyEvent} for a definition of
+     * a key pressed event.
+     * 
+     * @param e
+     */
+    public void keyPressed(KeyEvent e) {
+		if (this.exclusiveMode!=null) {
+			this.exclusiveMode.keyPressed(e);
+		}
+		else {
+			Iterator<ActionMode<? super DRAW,CANVAS,COLOR>> iterator = this.modeStack.descendingIterator();
+			while (iterator.hasNext()) {
+				ActionMode<? super DRAW,CANVAS,COLOR> m = iterator.next();
+				m.keyPressed(e);
+				if (e.isConsumed()) return;
+			}
+		}
+    }
+
+    /**
+     * Invoked when a key has been released.
+     * See the class description for {@link KeyEvent} for a definition of
+     * a key released event.
+     * 
+     * @param e
+     */
+    public void keyReleased(KeyEvent e) {
+		if (this.exclusiveMode!=null) {
+			this.exclusiveMode.keyReleased(e);
+		}
+		else {
+			Iterator<ActionMode<? super DRAW,CANVAS,COLOR>> iterator = this.modeStack.descendingIterator();
+			while (iterator.hasNext()) {
+				ActionMode<? super DRAW,CANVAS,COLOR> m = iterator.next();
+				m.keyReleased(e);
+				if (e.isConsumed()) return;
+			}
+		}
+    }
+
+    /**
+     * Invoked when the pointer button has been clicked (pressed
+     * and released) on a component.
+     * 
+     * @param e
+     * @see #pointerLongClicked(PointerEvent)
+     */
+    public void pointerClicked(PointerEvent e) {
+		updatePointerInfo(e, false);
+		if (this.exclusiveMode!=null) {
+			this.exclusiveMode.pointerClicked(e);
+		}
+		else {
+			Iterator<ActionMode<? super DRAW,CANVAS,COLOR>> iterator = this.modeStack.descendingIterator();
+			while (iterator.hasNext()) {
+				ActionMode<? super DRAW,CANVAS,COLOR> m = iterator.next();
+				m.pointerClicked(e);
+				if (e.isConsumed()) return;
+			}
+		}
+    }
+
+    /**
+     * Invoked when the pointer button has been long clicked (pressed
+     * and released) on a component.
+     * 
+     * @param e
+     * @see #pointerClicked(PointerEvent)
+     */
+    public void pointerLongClicked(PointerEvent e) {
+    	updatePointerInfo(e, false);
+		if (this.exclusiveMode!=null) {
+			this.exclusiveMode.pointerLongClicked(e);
+		}
+		else {
+			Iterator<ActionMode<? super DRAW,CANVAS,COLOR>> iterator = this.modeStack.descendingIterator();
+			while (iterator.hasNext()) {
+				ActionMode<? super DRAW,CANVAS,COLOR> m = iterator.next();
+				m.pointerLongClicked(e);
+				if (e.isConsumed()) return;
+			}
+		}
+    }
+
+    /**
+     * Invoked when a pointer button has been pressed on a component.
+     * 
+     * @param e
+     */
+    public void pointerPressed(PointerEvent e) {
+    	if (this.isForceHitResetWhenRelease) {
+    		this.isFigureUnderTheMouseComputed = false;
+    		this.figureUnderTheMouse = null;
+    	}
+		updatePointerInfo(e, false);
+		if (this.exclusiveMode!=null) {
+			this.exclusiveMode.pointerPressed(e);
+		}
+		else {
+			Iterator<ActionMode<? super DRAW,CANVAS,COLOR>> iterator = this.modeStack.descendingIterator();
+			while (iterator.hasNext()) {
+				ActionMode<? super DRAW,CANVAS,COLOR> m = iterator.next();
+				m.pointerPressed(e);
+				if (e.isConsumed()) return;
+			}
+		}
+    }
+
+    /**
+     * Invoked when a pointer button has been released on a component.
+     * 
+     * @param e
+     */
+    public void pointerReleased(PointerEvent e) {
+		updatePointerInfo(e, false);
+		if (this.exclusiveMode!=null) {
+			this.exclusiveMode.pointerReleased(e);
+		}
+		else {
+			Iterator<ActionMode<? super DRAW,CANVAS,COLOR>> iterator = this.modeStack.descendingIterator();
+			while (iterator.hasNext()) {
+				ActionMode<? super DRAW,CANVAS,COLOR> m = iterator.next();
+				m.pointerReleased(e);
+				if (e.isConsumed()) return;
+			}
+		}
+		// This is convenient for devices that cannot handle the POINTER_MOVE events.
+		if (this.isForceHitResetWhenRelease) {
+			this.isFigureUnderTheMouseComputed = false;
+			this.figureUnderTheMouse = null;
+		}
+    }
+    
+    /** Force or not to reset the hit figure when pointer is released.
+     * <p>
+     * By default, the hit figure is determine when the pointer is
+     * {@link #pointerDragged(PointerEvent) dragging} and when
+     * the pointer is {@link #pointerMoved(PointerEvent)}.
+     * For the devices that cannot handle the Move event (eg. Android OS),
+     * the hit figure must be reset when the pointer is released to ensure
+     * that the mode manager will try to retreive a hit figure when
+     * the pointer will be pressed again.
+     * So, depending on the event manager of the component that is using this
+     * mode manager, we recommend to call this function with
+     * <code>true</code> as parameter when the POINTER_MOVE event cannot be fired.
+     * 
+     * @param resetHit
+     */
+    public void setResetHitFigureWhenPointerReleased(boolean resetHit) {
+    	this.isForceHitResetWhenRelease = resetHit;
+    }
+
+    /** Replies if the hit figure is reset when pointer is released.
+     * 
+     * @return <code>true</code> when the hit figure is reset.
+     */
+    public boolean isResetHitFigureWhenPointerReleased() {
+    	return this.isForceHitResetWhenRelease;
+    }
+
+    /**
+     * Invoked when the mouse is moved with a button down.
+     * 
+     * @param e
+     */
+    public void pointerDragged(PointerEvent e) {
+		updatePointerInfo(e, true);
+		if (this.exclusiveMode!=null) {
+			this.exclusiveMode.pointerDragged(e);
+		}
+		else {
+			Iterator<ActionMode<? super DRAW,CANVAS,COLOR>> iterator = this.modeStack.descendingIterator();
+			while (iterator.hasNext()) {
+				ActionMode<? super DRAW,CANVAS,COLOR> m = iterator.next();
+				m.pointerDragged(e);
+				if (e.isConsumed()) return;
+			}
+		}
+    }
+
+    /**
+     * Invoked when the mouse is moved with no button down.
+     * 
+     * @param e
+     */
+    public void pointerMoved(PointerEvent e) {
+		updatePointerInfo(e, true);
+		if (this.exclusiveMode!=null) {
+			this.exclusiveMode.pointerMoved(e);
+		}
+		else {
+			Iterator<ActionMode<? super DRAW,CANVAS,COLOR>> iterator = this.modeStack.descendingIterator();
+			while (iterator.hasNext()) {
+				ActionMode<? super DRAW,CANVAS,COLOR> m = iterator.next();
+				m.pointerMoved(e);
+				if (e.isConsumed()) return;
+			}
+		}
+    }
+
+	private void updatePointedData() {
+		if (!this.isFigureUnderTheMouseComputed
+			&& !Float.isNaN(this.mouseX) && !Float.isNaN(this.mouseY)) {
+			DRAW fig = null;
+			this.figureUnderTheMouse = null;
+			ActionModeManagerOwner<DRAW,CANVAS,COLOR> container = getModeManagerOwner();
+			if (container!=null) {
+				fig = container.getFigureAt(this.mouseX, this.mouseY);
+				if (fig!=null) {
+					this.figureUnderTheMouse = new WeakReference<DRAW>(fig);
+				}
+			}
+			this.isFigureUnderTheMouseComputed = true;
+		}
+	}
+	
+	/** Replies the figure under the mouse cursor.
+	 * To know is the mouse cursor is inside the
+	 * shape of the figure pluease invoke
+	 * {@link #isPointerInFigureShape()}.
+	 * 
+	 * @return the figure under the mosue cursor;
+	 * or <code>null</code> if none.
+	 * @see #isPointerInFigureShape()
+	 */
+	public DRAW getPointedFigure() {
+		updatePointedData();
+		return this.figureUnderTheMouse==null ? 
+				null : this.figureUnderTheMouse.get();
+	}
+	
+	/** Replies if the mouse pointer is inside the shape
+	 * of the pointer figure.
+	 * To determine the pointed figure, please invoke
+	 * {@link #getPointedFigure()}.
+	 * 
+	 * @return <code>true</code> if the mouse pointer is
+	 * inside the shape; otherwise <code>false</code>.
+	 * @see #getPointedFigure()
+	 */
+	public boolean isPointerInFigureShape() {
+		updatePointedData();
+		return getPointedFigure()!=null;
+	}
+	
+	/** Upfdate the internal data conerning the pointer.
+	 * 
+	 * @param e
+	 * @param invalidateHitFigure is <code>true</code> to force
+	 * to clear any information about a pointed figure.
+	 */
+	void updatePointerInfo(PointerEvent e, boolean invalidateHitFigure) {
+		if (invalidateHitFigure) {
+			this.isFigureUnderTheMouseComputed = false;
+			this.figureUnderTheMouse = null;
+		}
+		ActionModeManagerOwner<DRAW,CANVAS,COLOR> container = getModeManagerOwner();
+		if (container!=null) {
+			ZoomableContext zc = container.getZoomableContext();
+			if (zc!=null) {
+				this.mouseX = zc.pixel2logical_x(e.getX());
+				this.mouseY = zc.pixel2logical_y(e.getY());
+			}
+			else {
+				this.mouseX = e.getX();
+				this.mouseY = e.getY();
+			}
+		}
+	}
+	
+	/** Replies the current position of the mouse in the document.
+	 * 
+	 * @return the position of the mouse in the document in a new point instance; not on
+	 * the screen.
+	 */
+	public Point2D getMousePosition() {
+		return new Point2f(this.mouseX, this.mouseY);
+	}
+
+	/** Replies the current position of the mouse in the document.
+	 * 
+	 * @return the position of the mouse in the document; not on
+	 * the screen.
+	 */
+	public float getMouseX() {
+		return this.mouseX;
+	}
+
+	/** Replies the current position of the mouse in the document.
+	 * 
+	 * @return the position of the mouse in the document; not on
+	 * the screen.
+	 */
+	public float getMouseY() {
+		return this.mouseY;
+	}
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeManagerOwner.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeManagerOwner.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/ActionModeManagerOwner.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,256 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.actionmode ;
+
+import java.util.Collection;
+import java.util.Set;
+
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.ui.MouseCursor;
+import org.arakhne.afc.ui.ZoomableContext;
+import org.arakhne.afc.ui.selection.Selectable;
+import org.arakhne.afc.ui.selection.SelectionManager;
+import org.arakhne.afc.ui.undo.UndoManager;
+import org.arakhne.afc.ui.undo.Undoable;
+
+/** This interface describes a container of modes.
+ *
+ * @param <DRAW> is the type of the data supported by this container.
+ * @param <CANVAS> is the type of the drawing canvas.
+ * @param <COLOR> is the type that is representing a color.
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface ActionModeManagerOwner<DRAW extends Selectable, CANVAS, COLOR> {
+
+	/** Replies if the mode container want to remove the model
+	 * objects when a figure is removed from the view.
+	 * 
+	 * @return <code>true</code> if both the figure and the
+	 * model objects should be removed at the same time,
+	 * <code>false</code> if only the figure should be removed
+	 * (not the model object).
+	 */
+	public boolean isAlwaysRemovingModelObjects();
+	
+	/** Replies the associated UI component.
+	 * 
+	 * @return the associated UI component.
+	 */
+	public Object getUIComponent();
+	
+	/** Request the keyboard focus.
+	 */
+	public void requestFocus();
+	
+	/** Replies the selection manager.
+	 * 
+	 * @return the selection manager.
+	 */
+	public SelectionManager<? super DRAW> getSelectionManager();
+
+	/** Replies the mode manager.
+	 * 
+	 * @return the mode manager.
+	 */
+	public ActionModeManager<DRAW,CANVAS,COLOR> getModeManager();
+	
+	/**
+	 * Replies the manager of undoable actions.
+	 * 
+	 * @return the undo manager.
+	 */
+	public UndoManager getUndoManager();
+
+	/** Change the cursor associated to the mode container.
+	 * 
+	 * @param cursor is the new cursor.
+	 */
+	public void setCursor(MouseCursor cursor);
+	
+	/** Repaint the mode container.
+	 * 
+	 * @param bounds is the area to repaint.
+	 */
+	public void repaint(Rectangle2f bounds);
+
+	/** Repaint the mode container.
+	 */
+	public void repaint();
+	
+	/** Replies the precision of the clicks.
+	 * 
+	 * @return the precision of the clicks.
+	 */
+	public float getClickPrecision();
+
+	/** Replies the zoomable context used by this container, if one.
+	 * 
+	 * @return zoomable context or <code>null</code>.
+	 */
+	public ZoomableContext getZoomableContext();
+
+	/**
+	 * Transfers the currently selected figures
+	 * to the system clipboard, removing the contents
+	 * from the model.  The current selection is reset.  Does nothing
+	 * for <code>null</code> selections.
+	 */
+	public void cut();
+
+	/**
+	 * Transfers the currently selected figures
+	 * to the system clipboard, leaving the contents
+	 * in the text model.  The current selection remains intact.
+	 * Does nothing for <code>null</code> selections.
+	 */
+	public void copy();
+
+	/**
+	 * Transfers the contents of the system clipboard into the
+	 * associated graph.  If the clipboard is empty, does nothing.
+	 */
+	public void paste();
+	
+	/** Replies if this viewer is interactively editable.
+	 * 
+	 * @return <code>true</code> if this viewer is editable; otherwise <code>false</code>.
+	 */
+	public boolean isEditable();
+
+	/** Replies if the selection manager is enabled.
+	 * 
+	 * @return <code>true</code> if the selection manager is enabled.
+	 */
+	public boolean isSelectionEnabled();
+
+	/** Notifies the listeners about an error that occurs in one
+	 * of the JFigureEditor components.
+	 * 
+	 * @param error
+	 */
+	public void fireError(Throwable error);
+	
+	/** Replies the number of figures in the container.
+	 * 
+	 * @return the number of figures.
+	 */
+	public int getFigureCount();
+
+	/** Replies the figures in the container.
+	 * 
+	 * @return the figures.
+	 */
+	public Collection<? extends DRAW> getFigures();
+
+	/** Replies the figure at the specified index in the container.
+	 * 
+	 * @param index
+	 * @return the figure.
+	 */
+	public DRAW getFigureAt(int index);
+
+	/** Replies the figure that has the specified point in
+	 * its shape. If you want to test the point against
+	 * the bounds of the figures, please use
+	 * {@link #getFigureWithBoundsAt(float, float)}.
+	 * 
+	 * @param x
+	 * @param y
+	 * @return the hit figure, or <code>null</code> if none.
+	 * @see #getFigureWithBoundsAt(float, float)
+	 */
+	public DRAW getFigureAt(float x, float y);
+
+	/** Replies the figure that has its bounds with the specified
+	 * point inside. If you want to test the point against
+	 * the shape of the figures, please use
+	 * {@link #getFigureAt(float, float)}.
+	 * 
+	 * @param x
+	 * @param y
+	 * @return the hit figure, or <code>null</code> if none.
+	 */
+	public DRAW getFigureWithBoundsAt(float x, float y);
+
+	/** Replies the figures that are intersecting the specified bounds.
+	 * 
+	 * @param bounds
+	 * @return the hit figures, never <code>null</code>.
+	 * @see #getFiguresIn(Rectangle2f)
+	 */
+	public Set<DRAW> getFiguresOn(Rectangle2f bounds);
+
+	/** Replies the figures that are inside the specified bounds.
+	 * 
+	 * @param bounds
+	 * @return the hit figures, never <code>null</code>.
+	 * @see #getFiguresOn(Rectangle2f)
+	 */
+	public Set<DRAW> getFiguresIn(Rectangle2f bounds);
+
+	/** Replies the background color used for the selection of objects.
+	 * 
+	 * @return the selection color.
+	 */
+	public COLOR getSelectionBackground();
+
+	/** Replies the background color used for the selection of objects.
+	 * 
+	 * @return the selection color.
+	 */
+	public COLOR getSelectionForeground();
+
+	/** Removes the specified figure.
+	 * 
+	 * @param deleteModel is <code>true</code> if the underlying model object
+	 * associated with the figure must be also deleted; <code>false</code> if
+	 * the underlying object must not be deleted.
+	 * @param disconnectFigureAndModel indicates if the figure and the model
+	 * object may be disconnected or not.
+	 * @param figure is the figure to remove.
+	 * @return the undo action that may be used to cancel the deletions.
+	 */
+	public Undoable removeFigure(boolean deleteModel, boolean disconnectFigureAndModel, DRAW figure);
+	
+	/** Removes the specified figure.
+	 * 
+	 * @param deleteModel is <code>true</code> if the underlying model object
+	 * associated with the figure must be also deleted; <code>false</code> if
+	 * the underlying object must not be deleted.
+	 * @param disconnectFigureAndModel indicates if the figure and the model
+	 * object may be disconnected or not.
+	 * @param figures are the figures to remove.
+	 * @return the undo action that may be used to cancel the deletions.
+	 */
+	public Undoable removeFigures(boolean deleteModel, boolean disconnectFigureAndModel, Iterable<? extends DRAW> figures);
+
+	/** Add a figure.
+	 * 
+	 * @param figure is the figure to add.
+	 * @return the undoable edit.
+	 */
+	public Undoable addFigure(DRAW figure);
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/SelectableInteractionAdapter.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/SelectableInteractionAdapter.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/SelectableInteractionAdapter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,58 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.actionmode ;
+
+import org.arakhne.afc.ui.selection.Selectable;
+
+
+/** This interface represents a listener on the figure interaction events.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class SelectableInteractionAdapter implements SelectableInteractionListener {
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void actionPerformed(SelectableInteractionEvent event) {
+		//
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void popupPerformed(SelectableInteractionEvent event) {
+		//
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean figureDeletionPerformed(Selectable figure,
+			boolean deleteModel) {
+		return true;
+	}
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/SelectableInteractionEvent.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/SelectableInteractionEvent.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/SelectableInteractionEvent.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,181 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.actionmode ;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EventObject;
+
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.ui.event.KeyEvent;
+import org.arakhne.afc.ui.event.PointerEvent;
+import org.arakhne.afc.ui.selection.Selectable;
+
+/** Describes the interaction event.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class SelectableInteractionEvent extends EventObject {
+
+	private static final long serialVersionUID = -1039544916556548342L;
+
+	private final Collection<? extends Selectable> figures;
+	private boolean consumed = false;
+	private final PointerEvent pointerEvent;
+	private final Point2D position;
+	private final KeyEvent keyEvent;
+	private final boolean isEditable;
+	
+	/**
+	 * @param manager
+	 * @param figure
+	 * @param isEditable
+	 */
+	public SelectableInteractionEvent(ActionModeManager<?,?,?> manager, Collection<? extends Selectable> figure, boolean isEditable) {
+		super(manager);
+		this.figures = figure==null ? Collections.<Selectable>emptyList() : figure;
+		this.pointerEvent = null;
+		this.position = null;
+		this.keyEvent = null;
+		this.isEditable = isEditable;
+	}
+
+	/**
+	 * @param manager
+	 * @param figure
+	 * @param pointerEvent
+	 * @param position is the click position in the document.
+	 * @param isEditable
+	 */
+	public SelectableInteractionEvent(ActionModeManager<?,?,?> manager, Collection<? extends Selectable> figure, PointerEvent pointerEvent, Point2D position, boolean isEditable) {
+		super(manager);
+		this.figures = figure==null ? Collections.<Selectable>emptyList() : figure;
+		this.pointerEvent = pointerEvent;
+		this.position = position;
+		this.keyEvent = null;
+		this.isEditable = isEditable;
+	}
+
+	/**
+	 * @param manager
+	 * @param figure
+	 * @param keyEvent
+	 * @param isEditable
+	 */
+	public SelectableInteractionEvent(ActionModeManager<?,?,?> manager, Collection<? extends Selectable> figure, KeyEvent keyEvent, boolean isEditable) {
+		super(manager);
+		this.figures = figure==null ? Collections.<Selectable>emptyList() : figure;
+		this.pointerEvent = null;
+		this.position = null;
+		this.keyEvent = keyEvent;
+		this.isEditable = isEditable;
+	}
+
+	/** Replies if the editor on which the interaction occured enables the edition
+	 * of the figures.
+	 * 
+	 * @return <code>true</code> if the viewer is editable; otherwise <code>false</code>.
+	 */
+	public boolean isEditable() {
+		return this.isEditable;
+	}
+	
+	/**
+	 * Replies the figure on which the event occured.
+	 * 
+	 * @return the figures, never <code>null</code>.
+	 * @see #getSelectable()
+	 */
+	public Collection<? extends Selectable> getSelectables() {
+		return Collections.unmodifiableCollection(this.figures);
+	}
+	
+	/**
+	 * Replies one of the figures on which the event occured.
+	 * The method of selection depends on the implementation.
+	 * There is no warranty that the replied figure is at 
+	 * a given position in the collection replied by {@link #getSelectables()}.
+	 * 
+	 * @return a figure, never <code>null</code>.
+	 * @see #getSelectables()
+	 */
+	public Selectable getSelectable() {
+		return this.figures.iterator().next();
+	}
+
+	/** Replies the pointer event which is the cause of 
+	 * this event.
+	 * 
+	 * @return the pointer event or <code>null</code> if
+	 * there is no pointer event causing this interaction event.
+	 */
+	public PointerEvent getPointerEvent() {
+		return this.pointerEvent;
+	}
+
+	/** Replies the key event which is the cause of 
+	 * this event.
+	 * 
+	 * @return the key event or <code>null</code> if
+	 * there is no key event causing this interaction event.
+	 */
+	public KeyEvent getKeyEvent() {
+		return this.keyEvent;
+	}
+	
+	/**
+     * Consumes this event so that it will not be processed
+     * in the default manner by the source which originated it.
+     */
+    public void consume() {
+        this.consumed = true;
+        if (this.pointerEvent!=null)
+        	this.pointerEvent.consume();
+        if (this.keyEvent!=null)
+        	this.keyEvent.consume();
+    }
+
+    /**
+     * Returns whether or not this event has been consumed.
+     * @return <code>true</code> if the event has been marked as consumed;
+     * otherwise <code>false</code>.
+     */
+    public boolean isConsumed() {
+        return this.consumed;
+    }
+    
+    /** Replies the position of the click in the document.
+     * The value replie by this function differs from the
+     * mouse position replied by {@link PointerEvent}.
+     * This function replies a point in the document coordinate space.
+     * {@link PointerEvent} replies a point on the screen.
+     * 
+     * @return position of the click
+     */
+    public Point2D getPointerPosition() {
+    	return this.position;
+    }
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/SelectableInteractionListener.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/SelectableInteractionListener.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/actionmode/SelectableInteractionListener.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,59 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.actionmode ;
+
+import java.util.EventListener;
+
+import org.arakhne.afc.ui.selection.Selectable;
+
+/** This interface represents a listener on the figure interaction events.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface SelectableInteractionListener extends EventListener {
+
+	/** Invoked when the user performs an action on a figure.
+	 * 
+	 * @param event
+	 */
+	public void actionPerformed(SelectableInteractionEvent event);
+	
+	/** Invoked when the user performs a popup action on a figure.
+	 * 
+	 * @param event
+	 */
+	public void popupPerformed(SelectableInteractionEvent event);
+
+	/** Invoked when the user performs a deletion action of a figure and not
+	 * of the associated model object.
+	 * 
+	 * @param figure is the figure to delete.
+	 * @param deleteModel indicates if the underlying model object must be also deleted.
+	 * @return <code>true</code> if the figure can be deleted, <code>false</code> otherwise.
+	 */
+	public boolean figureDeletionPerformed(Selectable figure, boolean deleteModel);
+
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/event/InputEvent.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/event/InputEvent.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/event/InputEvent.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,90 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.event ;
+
+import java.io.Serializable;
+
+
+
+/** Describe the event related to an input device.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface InputEvent extends Serializable {
+
+	/** Replies the identifier of the device.
+	 * 
+	 * @return the identifier of the device.
+	 */
+	public int getDeviceId();
+	
+	/** Replies if this event was consumed.
+	 * 
+	 * @return <code>true</code> if the event was consumed.
+	 */
+	public boolean isConsumed();
+	
+	/** Consume the event.
+	 */
+	public void consume();
+	
+    /**
+     * Returns whether or not the Shift modifier is down on this event.
+     * 
+     * @return <code>true</code> if the Shift key is down.
+     */
+    public boolean isShiftDown();
+
+    /**
+     * Returns whether or not the Control modifier is down on this event.
+     * @return <code>true</code> if the Control key is down.
+     */
+    public boolean isControlDown();
+
+    /**
+     * Returns whether or not the Meta modifier is down on this event.
+     * @return <code>true</code> if the Meta key is down.
+     */
+    public boolean isMetaDown();
+
+    /**
+     * Returns whether or not the Alt modifier is down on this event.
+     * @return <code>true</code> if the Alt key is down.
+     */
+    public boolean isAltDown();
+
+    /**
+     * Returns whether or not the AltGraph modifier is down on this event.
+     * @return <code>true</code> if the AltGr key is down.
+     */
+    public boolean isAltGraphDown();
+    
+    /** Replies if the contextual action is triggered by this event.
+     * A contextual action may be a request to display a popup menu for
+     * example.
+     * @return <code>true</code> if the contextual action may be triggered.
+     */
+    public boolean isContextualActionTriggered();
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/event/KeyEvent.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/event/KeyEvent.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/event/KeyEvent.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,45 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.event ;
+
+
+/** Describe the event related to the keyboard.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface KeyEvent extends InputEvent {
+
+	/** Replies the key code.
+	 * 
+	 * @return the key code.
+	 */
+	public int getKeyCode();
+	
+	/** Replies the key char.
+	 * 
+	 * @return the key char.
+	 */
+	public char getKeyChar();
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/event/PointerEvent.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/event/PointerEvent.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/event/PointerEvent.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,71 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.event ;
+
+
+/** Describe the event related to the pointer (mouse or finger).
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface PointerEvent extends InputEvent {
+
+	/** Replies the timestamp of when this event occurred.
+	 * 
+	 * @return the timestamp of when this event occurred.
+	 */
+	public long when();
+
+	/** Replies the X position of the pointer.
+	 * 
+	 * @return the X position of the pointer.
+	 */
+	public float getX();
+	
+	/** Replies the Y position of the pointer.
+	 * 
+	 * @return the Y position of the pointer.
+	 */
+	public float getY();
+
+	/**
+     * Returns which, if any, of the mouse buttons has changed state.
+     *
+     * @return the number of the button, or {@code 0} for none.
+     */
+    public int getButton();
+    
+    /** Replies the number of clicks.
+     * 
+     * @return the number of clicks.
+     */
+    public int getClickCount();
+    
+	/**
+     * Returns the orientation of the pointer.
+     *
+     * @return the orientation (in radians) of the pointer.
+     */
+    public float getOrientation();
+    
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/Selectable.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/Selectable.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/Selectable.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,45 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.selection ;
+
+/** An object that can be selected and put inside a {@link SelectionManager}.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Selectable {
+
+	/** Set if this fig accept to be selected.
+	 *
+	 * @param select <code>true</code> if this Fig accepts to be selected.
+	 */
+	public void setSelectable(boolean select) ;
+
+	/** Reply if this fig accept to be selected.
+	 *
+	 * @return <code>true</code> if this Fig accepts to be selected.
+	 */
+	public boolean isSelectable() ;
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/SelectionEvent.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/SelectionEvent.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/SelectionEvent.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,109 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.selection ;
+
+import java.util.EventObject;
+
+/** Event in selection manager.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class SelectionEvent extends EventObject {
+
+	private static final long serialVersionUID = 2414246708227737737L;
+	
+	private final Selectable object;
+	private final boolean isRemoved;
+	private final boolean isLastEvent;
+	
+	/**
+	 * @param source
+	 * @param object
+	 * @param removed
+	 * @param isAdjusting indicates if the event to fire is the last inside
+	 * a sequence of events. If <code>true</code> the event to fire must
+	 * be followed by other selection events that are produces by the
+	 * same action on the selection manager. If <code>false</code>, there
+	 * is no following selection event for the same action on the selection
+	 * manager.
+	 */
+	public SelectionEvent(SelectionManager<?> source, Selectable object, boolean removed, boolean isAdjusting) {
+		super(source);
+		this.object = object;
+		this.isRemoved = removed;
+		this.isLastEvent = !isAdjusting;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public SelectionManager<?> getSource() {
+		return (SelectionManager<?>)super.getSource();
+	}
+	
+	/** Replies the selectable object concerned by the selection event.
+	 * 
+	 * @return the figure.
+	 */
+	public Selectable getSelectable() {
+		return this.object;
+	}
+	
+	/** Replies if the selectable object has just been selected.
+	 * 
+	 * @return <code>true</code> if the selectable object state passes from
+	 * unselected to selected.
+	 */
+	public boolean isSelected() {
+		return !this.isRemoved;
+	}
+
+	/** Replies if the selectable object has just been unselected.
+	 * 
+	 * @return <code>true</code> if the selectable object state passes from
+	 * selected to unselected.
+	 */
+	public boolean isUnselected() {
+		return this.isRemoved;
+	}
+	
+	/** Replies if this event is the last event in a
+	 * sequence of events that are fire by a single
+	 * action on the selection manager.
+	 * <p>
+	 * This flag may be used to update some external
+	 * objects (eg, UI) at the end of a sequence
+	 * of selection changes.
+	 * 
+	 * @return <code>true</code> if this event is the
+	 * last of a sequence of events; <code>false</code>
+	 * if this event is a part of a sequence of selection
+	 * events, but not the last one.
+	 */
+	public boolean isLastEvent() {
+		return this.isLastEvent;
+	}
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/SelectionListener.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/SelectionListener.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/SelectionListener.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,40 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.selection ;
+
+import java.util.EventListener;
+
+/** Listener on events in selection manager.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface SelectionListener extends EventListener {
+
+	/** Invoked when the selection has changed in a selection manager.
+	 * 
+	 * @param event
+	 */
+	public void selectionChanged(SelectionEvent event);
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/SelectionManager.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/SelectionManager.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/SelectionManager.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,518 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.selection ;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.arakhne.afc.util.ListenerCollection;
+
+/** Abstracxt implementation of a selection manager.
+ *
+ * @param <OBJ> is the type of the objects inside this manager.
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class SelectionManager<OBJ extends Selectable> implements Set<OBJ> {
+
+	private final ListenerCollection<SelectionListener> listeners = new ListenerCollection<SelectionListener>();
+
+	private final Class<OBJ> elementType;
+	
+	/** Create a new SelectionManager.
+	 * 
+	 * @param elementType is the type of the elements inside this selection manager.
+	 */
+	public SelectionManager(Class<OBJ> elementType) {
+		this.elementType = elementType;
+	}
+	
+	/** Update the system's selection.
+	 */
+	protected void updateSystemSelection() {
+		//
+	}
+
+	/** Reset any internal bufferized value.
+	 */
+	protected void resetInternalBuffers() {
+		//
+	}
+
+	/** Add the given object inside the inner storage data structure.
+	 * 
+	 * @param object
+	 * @return <code>true</code> if the object was added; <code>false</code>
+	 * if not added.
+	 */
+	protected abstract boolean addInStorage(OBJ object);
+
+	/** Remove the given object from the inner storage data structure.
+	 * 
+	 * @param object
+	 * @return <code>true</code> if the object was removed; <code>false</code>
+	 * if not removed.
+	 */
+	protected abstract boolean removeFromStorage(OBJ object);
+
+	/** Clear the storage
+	 * 
+	 * @return the removed objects.
+	 */
+	protected abstract Collection<OBJ> clearStorage();
+
+	/** Replies an iterator on the storage.
+	 * 
+	 * @return the iterator.
+	 */
+	protected abstract Iterator<OBJ> getIteratorOnStorage();
+
+	/** Invoked when the given object was removed from the inner storage.
+	 * 
+	 * @param object
+	 */
+	protected void onRemovedObject(OBJ object) {
+		//
+	}
+
+	/** Invoked when the given object was added into the inner storage.
+	 * 
+	 * @param object
+	 */
+	protected void onAddedObject(OBJ object) {
+		//
+	}
+
+	/** Add selection listener.
+	 * 
+	 * @param listener
+	 */
+	public final void addSelectionListener(SelectionListener listener) {
+		this.listeners.add(SelectionListener.class, listener);
+	}
+
+	/** Remove selection listener.
+	 * 
+	 * @param listener
+	 */
+	public final void removeSelectionListener(SelectionListener listener) {
+		this.listeners.remove(SelectionListener.class, listener);
+	}
+
+	/** Notifies the listeners about the selection of a selectable object.
+	 * 
+	 * @param selectableObject is the selected object.
+	 * @param isAdjusting indicates if the event to fire is the last inside
+	 * a sequence of events. If <code>true</code> the event to fire must
+	 * be followed by other selection events that are produces by the
+	 * same action on the selection manager. If <code>false</code>, there
+	 * is no following selection event for the same action on the selection
+	 * manager.
+	 */
+	protected final void fireSelected(OBJ selectableObject, boolean isAdjusting) {
+		SelectionEvent event = new SelectionEvent(this, selectableObject, false, isAdjusting);
+		for(SelectionListener listener : this.listeners.getListeners(SelectionListener.class)) {
+			listener.selectionChanged(event);
+		}
+	}
+
+	/** Notifies the listeners about the deselection of a selectable object.
+	 * 
+	 * @param selectableObject is the unselected object.
+	 * @param isAdjusting indicates if the event to fire is the last inside
+	 * a sequence of events. If <code>true</code> the event to fire must
+	 * be followed by other selection events that are produces by the
+	 * same action on the selection manager. If <code>false</code>, there
+	 * is no following selection event for the same action on the selection
+	 * manager.
+	 */
+	protected final void fireUnselected(OBJ selectableObject, boolean isAdjusting) {
+		SelectionEvent event = new SelectionEvent(this, selectableObject, true, isAdjusting);
+		for(SelectionListener listener : this.listeners.getListeners(SelectionListener.class)) {
+			listener.selectionChanged(event);
+		}
+	}
+
+	/** Toggle the selection of figures.
+	 *
+	 * @param selectableObject are the figures to toggle.
+	 * @return <code>true</code> if the selection of a selectable object
+	 * has changed; <code>false</code> if no object has changed
+	 * of selection state.
+	 */
+	public final boolean toggle(OBJ... selectableObject) {
+		return toggle(Arrays.asList(selectableObject));
+	}
+	
+	/** Toggle the selection of figures.
+	 *
+	 * @param selectableObjects are the figures to toggle.
+	 * @return <code>true</code> if the selection of a selectable object
+	 * has changed; <code>false</code> if no object has changed
+	 * of selection state.
+	 */
+	public synchronized final boolean toggle(Collection<? extends OBJ> selectableObjects) {
+		boolean changed = false;
+		OBJ selected = null;
+		OBJ unselected = null;
+		for(OBJ f : selectableObjects) {
+			if (removeFromStorage(f)) {
+				if (selected!=null) {
+					fireSelected(selected, true);
+					selected = null;
+				}
+				else if (unselected!=null) {
+					fireUnselected(unselected, true);
+					unselected = null;
+				}
+				onRemovedObject(f);
+				changed = true;
+				resetInternalBuffers();
+				unselected = f;
+			}
+			else if (f.isSelectable() && addInStorage(f)) {
+				if (selected!=null) {
+					fireSelected(selected, true);
+					selected = null;
+				}
+				else if (unselected!=null) {
+					fireUnselected(unselected, true);
+					unselected = null;
+				}
+				onAddedObject(f);
+				changed = true;
+				resetInternalBuffers();
+				selected = f;
+			}
+		}
+		if (selected!=null) {
+			fireSelected(selected, false);
+		}
+		else if (unselected!=null) {
+			fireUnselected(unselected, false);
+		}
+		if (changed) {
+			updateSystemSelection();
+		}
+		return changed;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized final boolean add(OBJ e) {
+		if (e.isSelectable() && addInStorage(e)) {
+			onAddedObject(e);
+			resetInternalBuffers();
+			fireSelected(e, false);
+			updateSystemSelection();
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized final boolean remove(Object o) {
+		if (o!=null && this.elementType.isInstance(o)) {
+			OBJ obj = this.elementType.cast(o);
+			if (removeFromStorage(obj)) {
+				onRemovedObject(obj);
+				resetInternalBuffers();
+				fireUnselected(obj, false);
+				updateSystemSelection();
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized final boolean addAll(Collection<? extends OBJ> c) {
+		boolean changed = false;
+		if (c!=null) {
+			OBJ selected = null;
+			for(OBJ obj : c) {
+				if (obj.isSelectable() && addInStorage(obj)) {
+					if (selected!=null) {
+						fireSelected(selected, true);
+					}
+					onAddedObject(obj);
+					resetInternalBuffers();
+					changed = true;
+					selected = obj;
+				}
+			}
+			if (selected!=null) {
+				fireSelected(selected, false);
+			}
+		}
+		if (changed) {
+			updateSystemSelection();
+		}
+		return changed;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized final boolean retainAll(Collection<?> c) {
+		boolean changed = false;
+		if (c==null || c.isEmpty()) {
+			changed = !isEmpty();
+			clear();
+		}
+		else {
+			OBJ unselected = null;
+			Iterator<OBJ> iterator = getIteratorOnStorage();
+			OBJ selObject;
+			while (iterator.hasNext()) {
+				selObject = iterator.next();
+				if (!c.contains(selObject)) {
+					if (unselected!=null) {
+						fireUnselected(unselected, true);
+					}
+					iterator.remove();
+					onRemovedObject(selObject);
+					changed = true;
+					resetInternalBuffers();
+					unselected = selObject;
+				}
+			}
+			if (unselected!=null) {
+				fireUnselected(unselected, false);
+			}
+		}
+		if (changed) {
+			updateSystemSelection();
+		}
+		return changed;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized final boolean removeAll(Collection<?> c) {
+		boolean changed = false;
+		if (c!=null) {
+			OBJ unselected = null;
+			for(Object o : c) {
+				if (o!=null && this.elementType.isInstance(o)) {
+					OBJ obj = this.elementType.cast(o);
+					if (removeFromStorage(obj)) {
+						if (unselected!=null) {
+							fireUnselected(unselected, true);
+						}
+						onRemovedObject(obj);
+						resetInternalBuffers();
+						changed = true;
+						unselected = obj;
+					}
+				}
+			}
+			if (unselected!=null) {
+				fireUnselected(unselected, false);
+			}
+		}
+		if (changed) {
+			updateSystemSelection();
+		}
+		return changed;
+	}
+
+	/**
+	 * Select the specified selectable object and deselect all the previously
+	 * selected objects.
+	 * 
+	 * @param e is the selectable object to select.
+	 * @return <code>true</code> if the selection has changed;
+	 * otherwise <code>false</code>.
+	 */
+	public final boolean setSelection(OBJ... e) {
+		return setSelection(Arrays.asList(e));
+	}
+
+	/**
+	 * Select the specified selectable object and deselect all the previously
+	 * selected objects.
+	 * 
+	 * @param e is the selectable object to select.
+	 * @return <code>true</code> if the selection has changed;
+	 * otherwise <code>false</code>.
+	 */
+	public synchronized final boolean setSelection(Collection<? extends OBJ> e) {
+		boolean changed = false;
+
+		Set<OBJ> alreadySelected = new TreeSet<OBJ>();
+		OBJ unselected = null;
+		OBJ selected = null;
+
+		Iterator<OBJ> iterator = getIteratorOnStorage();
+		OBJ selObject;
+		while (iterator.hasNext()) {
+			selObject = iterator.next();
+			if (!e.contains(selObject)) {
+				if (unselected!=null) {
+					fireUnselected(unselected, true);
+				}
+				iterator.remove();
+				onRemovedObject(selObject);
+				changed = true;
+				resetInternalBuffers();
+				unselected = selObject;
+			}
+			else {
+				alreadySelected.add(selObject);
+			}
+		}
+
+		for(OBJ fig : e) {
+			if (fig.isSelectable() && 
+					!alreadySelected.contains(fig)
+					&& addInStorage(fig)) {
+				if (selected!=null) {
+					fireSelected(selected, true);
+				}
+				else if (unselected!=null) {
+					fireUnselected(unselected, true);
+					unselected = null;
+				}
+				onAddedObject(fig);
+				changed = true;
+				resetInternalBuffers();
+				selected = fig;
+			}
+		}
+		
+		if (selected!=null) {
+			fireSelected(selected, false);
+		}
+		else if (unselected!=null) {
+			fireUnselected(unselected, false);
+		}
+
+		if (changed) {
+			updateSystemSelection();
+		}
+
+		return changed;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized final Iterator<OBJ> iterator() {
+		return new SelectionIterator(getIteratorOnStorage());
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized final void clear() {
+		Collection<OBJ> elements = clearStorage();
+		if (!elements.isEmpty()) {
+			resetInternalBuffers();
+			updateSystemSelection();
+			Iterator<OBJ> iterator = elements.iterator();
+			OBJ obj;
+			while (iterator.hasNext()) {
+				obj = iterator.next();
+				onRemovedObject(obj);
+				resetInternalBuffers();
+				fireUnselected(obj, iterator.hasNext());
+			}
+		}
+	}
+
+	/** 
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private class SelectionIterator implements Iterator<OBJ> {
+
+		private final Iterator<OBJ> iterator;
+
+		private OBJ lastObject = null;
+
+		public SelectionIterator(Iterator<OBJ> iterator) {
+			this.iterator = iterator;
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public boolean hasNext() {
+			synchronized(SelectionManager.this) {
+				return this.iterator.hasNext();
+			}
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public OBJ next() {
+			synchronized(SelectionManager.this) {
+				this.lastObject = this.iterator.next();
+			}
+			return this.lastObject;
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void remove() {
+			OBJ obj = this.lastObject;
+			this.lastObject = null;
+			if (obj==null) throw new NoSuchElementException();
+			synchronized(SelectionManager.this) {
+				this.iterator.remove();
+			}
+			onRemovedObject(obj);
+			resetInternalBuffers();
+			fireUnselected(obj, false);
+			updateSystemSelection();
+		}
+
+	} // class SelectionIterator
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/TreeSetSelectionManager.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/TreeSetSelectionManager.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/selection/TreeSetSelectionManager.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,121 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.selection ;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.TreeSet;
+
+/** Implementation of a SelectionManager based on a Tree Set.
+ *
+ * @param <OBJ> is the type of the objects inside this manager.
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class TreeSetSelectionManager<OBJ extends Selectable & Comparable<? super OBJ>> extends SelectionManager<OBJ> {
+
+	/** The collection of Selection instances.
+	 */
+	protected Set<OBJ> storage = new TreeSet<OBJ>();
+
+	/** Create a new SelectionManager.
+	 * 
+	 * @param elementType is the type of the elements inside this selection manager.
+	 */
+	public TreeSetSelectionManager(Class<OBJ> elementType) {
+		super(elementType);
+	}
+
+	@Override
+	protected synchronized boolean removeFromStorage(OBJ object) {
+		return this.storage.remove(object);
+	}
+
+	@Override
+	protected synchronized boolean addInStorage(OBJ object) {
+		return this.storage.add(object);
+	}
+	
+	@Override
+	protected synchronized Iterator<OBJ> getIteratorOnStorage() {
+		return this.storage.iterator();
+	}
+
+	@Override
+	protected synchronized Collection<OBJ> clearStorage() {
+		Set<OBJ> old = this.storage;
+		this.storage = new TreeSet<OBJ>();
+		return old;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized final int size() {
+		return this.storage.size();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized final boolean isEmpty() {
+		return this.storage.isEmpty();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized final boolean contains(Object o) {
+		return this.storage.contains(o);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized final Object[] toArray() {
+		return this.storage.toArray();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized final <T> T[] toArray(T[] a) {
+		return this.storage.toArray(a);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized final boolean containsAll(Collection<?> c) {
+		return this.storage.containsAll(c);
+	}
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/AbstractUndoable.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/AbstractUndoable.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/AbstractUndoable.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,72 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.undo ;
+
+
+
+
+/** Abstract implementation of an Undoable.
+ * This class checks if the undoable action is not died
+ * when undo() or redo() is invoked.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractUndoable implements Undoable {
+
+	private static final long serialVersionUID = -1576372782744665297L;
+	
+	private boolean died = false;
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public final void redo() {
+		if (this.died) throw new IllegalArgumentException();
+		doEdit();
+	}
+	
+	/** Invoked to do the action (redo).
+	 */
+	protected abstract void doEdit();
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public final void undo() {
+		if (this.died) throw new IllegalArgumentException();
+		undoEdit();
+	}
+
+	/** Invoked to undo the action (undo).
+	 */
+	protected abstract void undoEdit();
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void die() {
+		this.died = true;
+	}
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/DefaultUndoManager.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/DefaultUndoManager.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/DefaultUndoManager.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,190 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.undo ;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import org.arakhne.afc.util.ListenerCollection;
+import org.arakhne.vmutil.locale.Locale;
+
+/** Manager of undoable actions.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class DefaultUndoManager implements UndoManager {
+
+	private final ListenerCollection<UndoListener> listeners = new ListenerCollection<UndoListener>();
+	private final LinkedList<Undoable> undoes = new LinkedList<Undoable>(); 
+	private final LinkedList<Undoable> redoes = new LinkedList<Undoable>(); 
+	private int limit = 100;
+	
+	/**
+	 */
+	public DefaultUndoManager() {
+		//
+	}
+
+	@Override
+	public synchronized final void setLimit(int l) {
+		if (l!=this.limit) {
+			this.limit = l;
+		}
+	}
+
+	@Override
+	public synchronized final int getLimit() {
+		return this.limit;
+	}
+
+
+	@Override
+	public void addUndoListener(UndoListener l) {
+		this.listeners.add(UndoListener.class,l);
+	}
+
+	@Override
+	public void removeUndoListener(UndoListener l) {
+		this.listeners.remove(UndoListener.class,l);
+	}
+
+	/**
+	 * Notifies listeners.
+	 */
+	protected void fireChange() {
+		for(UndoListener listener : this.listeners.getListeners(UndoListener.class)) {
+			listener.undoListChanged(this);
+		}
+	}
+	
+	@Override
+	public boolean add(Undoable action) {
+		synchronized(this) {
+			if (action==null || this.limit==0) return false;
+			Iterator<Undoable> iterator;
+			Undoable u;
+			
+			// Clear the list of the redoable actions
+			iterator = this.redoes.iterator();
+			while (iterator.hasNext()) {
+				u = iterator.next();
+				u.die();
+				iterator.remove();
+			}
+	
+			// Remove the too old undoable actions
+			if (this.limit>0) {
+				while (this.undoes.size()>=this.limit) {
+					u = this.undoes.removeFirst();
+					u.die();
+				}
+			}
+			
+			// Add the new action.
+			this.undoes.addLast(action);
+		}
+		
+		fireChange();
+
+		return true;
+	}
+
+	@Override
+	public synchronized void undo() {
+		synchronized(this) {
+			if (this.undoes.isEmpty())
+				throw new IllegalStateException();
+			Undoable u = this.undoes.removeLast();
+			u.undo();
+			this.redoes.addFirst(u);
+		}
+		fireChange();
+	}
+
+	@Override
+	public synchronized boolean canUndo() {
+		return !this.undoes.isEmpty();
+	}
+
+	@Override
+	public void redo() {
+		synchronized(this) {
+			if (this.redoes.isEmpty())
+				throw new IllegalStateException();
+			Undoable u = this.redoes.removeFirst();
+			u.redo();
+			this.undoes.addLast(u);
+		}
+		fireChange();
+	}
+
+	@Override
+	public synchronized boolean canRedo() {
+		return !this.redoes.isEmpty();
+	}
+
+	@Override
+	public synchronized void discardAll() {
+		boolean changed = false;
+		synchronized(this) {
+			Iterator<Undoable> iterator;
+			Undoable u;
+			
+			iterator = this.redoes.iterator();
+			while (iterator.hasNext()) {
+				u = iterator.next();
+				u.die();
+				iterator.remove();
+				changed = true;
+			}
+
+			iterator = this.undoes.iterator();
+			while (iterator.hasNext()) {
+				u = iterator.next();
+				u.die();
+				iterator.remove();
+				changed = false;
+			}
+		}
+		
+		if (changed) {
+			fireChange();
+		}
+	}
+	
+	@Override
+	public synchronized String getUndoPresentationName() {
+		if (canUndo())
+			return Locale.getString("UNDO", this.undoes.getLast().getPresentationName()); //$NON-NLS-1$
+		return Locale.getString("NO_UNDO"); //$NON-NLS-1$
+	}
+
+	@Override
+	public synchronized String getRedoPresentationName() {
+		if (canRedo())
+			return Locale.getString("REDO", this.redoes.getFirst().getPresentationName()); //$NON-NLS-1$
+		return Locale.getString("NO_REDO"); //$NON-NLS-1$
+	}
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/UndoListener.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/UndoListener.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/UndoListener.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,41 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.undo ;
+
+import java.util.EventListener;
+
+
+/** Listener on the list of undoable actions.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface UndoListener extends EventListener {
+
+	/** Invoked when the list of the undo actions has changed.
+	 * 
+	 * @param manager
+	 */
+	public void undoListChanged(UndoManager manager);
+    	
+}
\ No newline at end of file

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/UndoManager.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/UndoManager.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/UndoManager.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,124 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.undo ;
+
+
+/** Manager of undoable actions.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface UndoManager {
+
+	/** Add listener on the changes in the undo list.
+	 * 
+	 * @param l
+	 */
+	public void addUndoListener(UndoListener l);
+	
+	/** Remove listener on the changes in the undo list.
+	 * 
+	 * @param l
+	 */
+	public void removeUndoListener(UndoListener l);
+
+	/**
+     * Undoes the appropriate actions.
+     * @see #canUndo()
+     */
+    public void undo();
+
+    /**
+     * Returns <code>true</code> if actions may be undone.
+     * 
+     * @return <code>true</code> if there are actions to be undone.
+     */
+    public boolean canUndo();
+
+    /**
+     * Redoes the appropriate actions.
+     * @see #canRedo()
+     */
+    public void redo();
+
+    /**
+     * Returns <code>true</code> if actions may be redone.
+     * 
+     * @return <code>true</code> if there are actions to be redone.
+     */
+    public boolean canRedo();
+
+    /**
+     * Returns a description of the undoable form of this edit.
+     *
+     * @return a description of the undoable form of this edit
+     */
+    public String getUndoPresentationName();
+    /**
+     * Returns a description of the redoable form of this edit.
+     * 
+     * @return a description of the redoable form of this edit
+     */
+    public String getRedoPresentationName();
+
+    /**
+     * Adds an <code>Undoable</code> to this
+     * <code>UndoManager</code>, if it's possible.  This removes all
+     * edits from the index of the next edit to the end of the edits
+     * list.  If <code>end</code> has been invoked the edit is not added
+     * and <code>false</code> is returned.  If <code>end</code> hasn't
+     * been invoked this returns <code>true</code>.
+     *
+     * @param action is the action to be added
+     * @return true if <code>action</code> can be incorporated into this
+     *              edit
+     */
+    public boolean add(Undoable action);
+
+    /**
+     * Sets the maximum number of action this <code>UndoManager</code>
+     * holds. A value less than 0 indicates the number of edits is not
+     * limited.
+     *
+     * @param l the new limit
+     */
+    public void setLimit(int l);
+
+    /**
+     * Replies the maximum number of action this <code>UndoManager</code>
+     * holds. A value less than 0 indicates the number of edits is not
+     * limited.
+     *
+     * @return the limit.
+     */
+    public int getLimit();
+
+    /**
+     * Empties the undo manager sending each edit a <code>die</code> message
+     * in the process.
+     *
+     * @see Undoable#die()
+     */
+    public void discardAll();
+    	
+}
\ No newline at end of file

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/Undoable.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/Undoable.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/Undoable.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,64 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.undo ;
+
+import java.io.Serializable;
+
+
+
+/** Action that can be undoed.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Undoable extends Serializable {
+
+	/** Redo the action.
+	 */
+	public void redo();
+
+	/** Undo the action.
+	 */
+	public void undo();
+
+	/** Remove any buffered data.
+	 * This function is invoked by the undo manager
+	 * when this action is removed from the manager.
+	 */
+	public void die();
+
+    /**
+     * This default implementation returns "". Used by
+     * <code>getUndoPresentationName</code> and
+     * <code>getRedoPresentationName</code> to
+     * construct the strings they return. Subclasses should override to
+     * return an appropriate description of the operation this edit
+     * represents.
+     *
+     * @return the empty string ""
+     * @see     UndoManager#getUndoPresentationName()
+     * @see     UndoManager#getRedoPresentationName()
+     */
+    public String getPresentationName();
+
+}

Added: trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/UndoableGroup.java
===================================================================
--- trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/UndoableGroup.java	                        (rev 0)
+++ trunk/ui/ui-base/src/main/java/org/arakhne/afc/ui/undo/UndoableGroup.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,120 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.undo ;
+
+import java.util.ArrayList;
+
+/** A group of undoable actions.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class UndoableGroup implements Undoable {
+
+	private static final long serialVersionUID = 515680393060970792L;
+	
+	private final ArrayList<Undoable> undoables = new ArrayList<Undoable>();
+	private final String presentationName;
+	private boolean died = false;
+	private boolean finalized = false;
+	
+	/**
+	 * @param presentationName
+	 */
+	public UndoableGroup(String presentationName) {
+		this.presentationName = presentationName;
+	}
+	
+    /**
+     * Adds an <code>Undoable</code> to this
+     * <code>UndoManager</code>, if it's possible.
+     * You must call {@link #end()} after all the
+     * actions where added.
+     *
+     * @param action is the action to be added
+     */
+    public void add(Undoable action) {
+    	if (action!=null) this.undoables.add(action);
+    }
+    
+    /** Replies if this group of undoable actions is empty or not.
+     * 
+     * @return <code>true</code> if there is no action in the group.
+     */
+    public boolean isEmpty() {
+    	return this.undoables.isEmpty();
+    }
+
+    /** Replies the number of undoable actions in the group.
+     * 
+     * @return the number of undoable actions in the group.
+     */
+    public int size() {
+    	return this.undoables.size();
+    }
+
+    /**
+     * Finalize the additions in the group.
+     */
+    public void end() {
+    	this.undoables.trimToSize();
+    	this.finalized = true;
+    }
+    
+	@Override
+	public void undo() {
+		if (!this.finalized) throw new IllegalStateException("you must invoked end() before calling undo()"); //$NON-NLS-1$
+		if (this.died || this.undoables.isEmpty()) throw new IllegalStateException();
+		Undoable u;
+		for(int i=this.undoables.size()-1; i>=0; --i) {
+			u = this.undoables.get(i);
+			u.undo();
+		}
+	}
+
+	@Override
+	public void redo() {
+		if (!this.finalized) throw new IllegalStateException("you must invoked end() before calling redo()"); //$NON-NLS-1$
+		if (this.died || this.undoables.isEmpty()) throw new IllegalStateException();
+		for(Undoable u : this.undoables) {
+			u.redo();
+		}
+	}
+
+	@Override
+	public void die() {
+		this.died = true;
+		Undoable u;
+		for(int i=this.undoables.size()-1; i>=0; --i) {
+			u = this.undoables.get(i);
+			u.die();
+		}
+		this.undoables.clear();
+	}
+
+	@Override
+	public String getPresentationName() {
+		return this.presentationName;
+	}
+	
+}

Added: trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/ActionMode.properties
===================================================================
--- trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/ActionMode.properties	                        (rev 0)
+++ trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/ActionMode.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,21 @@
+# $Id$
+# 
+# Copyright (C) 2002, 2012 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+NOT_MODE_TRIGGER = The mode is not implementing the ModeTrigger interface.

Added: trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/ActionModeManager.properties
===================================================================
--- trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/ActionModeManager.properties	                        (rev 0)
+++ trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/ActionModeManager.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,21 @@
+# $Id$
+# 
+# Copyright (C) 2002, 2012 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+TWO_EXCLUSIVE_MODES_AT_SAME_TIME = Two exclusive modes cannot be activated at the same time.

Added: trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/ActionModeManager_fr.properties
===================================================================
--- trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/ActionModeManager_fr.properties	                        (rev 0)
+++ trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/ActionModeManager_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,21 @@
+# $Id$
+# 
+# Copyright (C) 2002, 2012 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+TWO_EXCLUSIVE_MODES_AT_SAME_TIME = Deux modes exclusifs ne peuvent pas \xEAtre activ\xE9s en m\xEAme temps.

Added: trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/ActionMode_fr.properties
===================================================================
--- trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/ActionMode_fr.properties	                        (rev 0)
+++ trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/actionmode/ActionMode_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,21 @@
+# $Id$
+# 
+# Copyright (C) 2002, 2012 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+NOT_MODE_TRIGGER = Le mode n''impl\xE9mente pas l''interface ModeTrigger.

Added: trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/undo/DefaultUndoManager.properties
===================================================================
--- trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/undo/DefaultUndoManager.properties	                        (rev 0)
+++ trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/undo/DefaultUndoManager.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,23 @@
+# $Id$
+# 
+# Copyright (C) 2013 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+NO_UNDO = Undo
+NO_REDO = Redo
+UNDO = Undo {0}
+REDO = Redo {0}

Added: trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/undo/DefaultUndoManager_fr.properties
===================================================================
--- trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/undo/DefaultUndoManager_fr.properties	                        (rev 0)
+++ trunk/ui/ui-base/src/main/resources/org/arakhne/afc/ui/undo/DefaultUndoManager_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,23 @@
+# $Id$
+# 
+# Copyright (C) 2013 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+NO_UNDO = Annuler
+NO_REDO = Refaire
+UNDO = Annuler {0}
+REDO = Refaire {0}


Property changes on: trunk/ui/ui-swing
___________________________________________________________________
Added: svn:ignore
   + .classpath
..metadata
..settings
..project
target
bin


Added: trunk/ui/ui-swing/pom.xml
===================================================================
--- trunk/ui/ui-swing/pom.xml	                        (rev 0)
+++ trunk/ui/ui-swing/pom.xml	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,25 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.arakhne.afc</groupId>
+		<artifactId>arakhneUi</artifactId>
+		<version>1.0-SNAPSHOT</version>
+	</parent>
+
+	<groupId>org.arakhne.afc.ui</groupId>
+	<artifactId>ui-swing</artifactId>
+	<version>1.0-SNAPSHOT</version>
+	<name>Arakhne Swing Widgets</name>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.arakhne.afc</groupId>
+			<artifactId>arakhneVmutils</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.arakhne.afc.ui</groupId>
+			<artifactId>ui-awt</artifactId>
+		</dependency>
+	</dependencies>
+</project>

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/FileFilterSwing.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/FileFilterSwing.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/FileFilterSwing.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,93 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2005-10 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.swing;
+
+import java.io.File;
+
+import javax.swing.filechooser.FileFilter;
+
+/** Implementation of swing file filter.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class FileFilterSwing extends FileFilter implements org.arakhne.afc.io.filefilter.FileFilter  {
+
+	private final org.arakhne.afc.io.filefilter.FileFilter fileFilter;
+	
+	/**
+	 * @param filter
+	 */
+	public FileFilterSwing(org.arakhne.afc.io.filefilter.FileFilter filter) {
+		this.fileFilter = filter;
+	}
+	
+	/** Replies the file filter.
+	 * 
+	 * @return the file filter.
+	 */
+	public final org.arakhne.afc.io.filefilter.FileFilter getFileFilter() {
+		return this.fileFilter;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final boolean accept(File dir, String name) {
+		return accept(new File(dir,name));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean accept(File f) {
+		if (f!=null) {
+			if (this.fileFilter!=null)
+				return this.fileFilter.accept(f);
+		}
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String getDescription() {
+		if (this.fileFilter!=null)
+			return this.fileFilter.getDescription();
+		return null;
+	}
+
+	@Override
+	public String[] getExtensions() {
+		if (this.fileFilter!=null)
+			return this.fileFilter.getExtensions();
+		return new String[0];
+	}
+
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/JColorSelector.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/JColorSelector.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/JColorSelector.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,298 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2005-10 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.swing;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Stroke;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.EventListener;
+
+import javax.swing.JButton;
+import javax.swing.JColorChooser;
+import javax.swing.event.EventListenerList;
+
+/**
+ * This button permits to select a color.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class JColorSelector extends JButton {
+
+	private static final long serialVersionUID = 3759677089992951265L;
+	
+	private ColorButtonModel colorModel = null;
+	private Handler handler = null;
+
+	/**
+	 */
+	public JColorSelector() {
+		this(new ColorButtonModel());
+	}
+
+	/**
+	 * @param initialeColor is the initial color inside the associated model.
+	 */
+	public JColorSelector(Color initialeColor) {
+		this(new ColorButtonModel(initialeColor));
+	}
+
+	/**
+	 * @param model is the color model to use.
+	 */
+	public JColorSelector(ColorButtonModel model) {
+		setColorButtonModel(model);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
+	 */
+	@Override
+	protected void paintComponent(Graphics g) {
+		super.paintComponent(g);
+
+		Dimension sizes = getSize();
+
+		if (this.colorModel!=null) {
+			Color c = this.colorModel.getColor();
+			if (c!=null) {
+				g.setColor(c);
+				g.fill3DRect(5, 5, sizes.width-10,sizes.height-10, false);
+				return;
+			}
+		}
+
+		// Draw a grid
+		Graphics2D g2d = (Graphics2D)g;
+		g2d.setColor(Color.BLACK);
+		Stroke oldStroke = g2d.getStroke();
+		BasicStroke bs = new BasicStroke(1f,
+				BasicStroke.CAP_BUTT,
+				BasicStroke.JOIN_MITER,
+				1f,
+				new float[]{1f, 2f},
+				0f);
+		g2d.setStroke(bs);
+		g2d.drawRect(5, 5, sizes.width-10,sizes.height-10);
+		g2d.setStroke(oldStroke);
+	}
+
+	/** Set the color.
+	 * 
+	 * @param color
+	 */
+	public void setSelectedColor(Color color) {
+		if (this.colorModel!=null)
+			this.colorModel.setColor(color);
+	}
+
+	/** Get the color.
+	 * 
+	 * @return the color or <code>null</code>
+	 */
+	public Color getSelectedColor() {
+		if (this.colorModel!=null)
+			return this.colorModel.getColor();
+		return null;
+	}
+
+	/** Replies the color model.
+	 * 
+	 * @return the color model.
+	 */
+	public ColorButtonModel getColorButtonModel() {
+		return this.colorModel;
+	}
+
+	/** Set the color model.
+	 * 
+	 * @param model
+	 */
+	public void setColorButtonModel(ColorButtonModel model) {
+		if (model==null) return;
+		if (this.colorModel!=null && this.handler!=null) {
+			this.colorModel.removeColorButtonModelListener(this.handler);
+			removeActionListener(this.handler);
+			this.handler = null;
+		}
+		this.colorModel = model;
+		this.handler = new Handler();
+		addActionListener(this.handler);
+		this.colorModel.addColorButtonModelListener(this.handler);
+	}
+
+	/**
+	 * Model for color buttons
+	 * 
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public static class ColorButtonModel {
+
+		private EventListenerList listeners = null;
+		private Color color;
+
+		/**
+		 */
+		public ColorButtonModel() {
+			this.color = null;
+		}
+
+		/**
+		 * @param initialColor
+		 */
+		public ColorButtonModel(Color initialColor) {
+			this.color = initialColor;
+		}
+
+		/** Add a listener.
+		 * 
+		 * @param listener
+		 */
+		public void addColorButtonModelListener(ColorButtonModelListener listener) {
+			if (this.listeners==null)
+				this.listeners = new EventListenerList();
+			this.listeners.add(ColorButtonModelListener.class, listener);
+		}
+
+		/** Remove a listener.
+		 * 
+		 * @param listener
+		 */
+		public void removeColorButtonModelListener(ColorButtonModelListener listener) {
+			if (this.listeners==null) return;
+			this.listeners.remove(ColorButtonModelListener.class, listener);
+			if (this.listeners.getListenerCount()==0)
+				this.listeners = null;
+		}
+
+		/** Fire color change event.
+		 * 
+		 * @param oldColor
+		 * @param newColor
+		 */
+		protected void fireColorChanged(Color oldColor, Color newColor) {
+			if (this.listeners!=null) {
+				ColorButtonModelListener[] list = this.listeners.getListeners(ColorButtonModelListener.class);
+				for(ColorButtonModelListener listener : list) {
+					listener.onColorChange(this, oldColor, newColor);
+				}
+			}
+		}
+
+		/** Replies the color.
+		 * 
+		 * @return the color or <code>null</code>
+		 */
+		public Color getColor() {
+			return this.color;
+		}
+
+		/** Set the color.
+		 * 
+		 * @param color is the color or <code>null</code>
+		 */
+		public void setColor(Color color) {
+			if (color==null || this.color==color || color.equals(this.color))
+				return;
+			Color oldColor = this.color;
+			this.color = color;
+			fireColorChanged(oldColor, this.color);
+		}
+
+	} /* class ColorButtonModel */	
+
+	/**
+	 * Listeners on the color model for color buttons
+	 * 
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public interface ColorButtonModelListener extends EventListener {
+
+		/** Invoked when color change.
+		 * 
+		 * @param source is the button that thrown this event.
+		 * @param oldColor is the old value of the color.
+		 * @param newColor is the new value of the color.
+		 */
+		public void onColorChange(ColorButtonModel source, Color oldColor, Color newColor);
+
+	} /* class ColorButtonModelListener */	
+
+	/**
+	 * Model for color buttons
+	 * 
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private class Handler implements ColorButtonModelListener, ActionListener {
+
+		/**
+		 */
+		public Handler() {
+			//
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void onColorChange(ColorButtonModel source, Color oldColor, Color newColor) {
+			JColorSelector.this.repaint();
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			ColorButtonModel btModel = getColorButtonModel();
+			if (btModel!=null) {
+				Color color = btModel.getColor();
+				Color newColor = JColorChooser.showDialog(JColorSelector.this, null, color);
+				if (newColor!=null) {
+					btModel.setColor(newColor);
+				}
+			}
+		}
+
+	} /* class ColorButtonModelListener */	
+
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/JGroupButton.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/JGroupButton.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/JGroupButton.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,101 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2005-10 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.swing;
+
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.util.Collection;
+
+import javax.swing.AbstractButton;
+import javax.swing.JToggleButton;
+
+/** Button which could acts as a wrapper to a set of buttons.
+*
+* @author $Author: galland$
+* @version $FullVersion$
+* @mavengroupid $GroupId$
+* @mavenartifactid $ArtifactId$
+*/
+public class JGroupButton extends AbstractButton implements ItemListener {
+
+	private static final long serialVersionUID = -8179091743419793116L;
+
+	private final Collection<AbstractButton> groupedButtons;
+	
+	private boolean listening = true; 
+	
+	/** 
+	 * @param buttons are the buttons inside the group
+	 */
+	public JGroupButton(Collection<AbstractButton> buttons) {
+		this.groupedButtons = buttons;
+       setModel(new JToggleButton.ToggleButtonModel());
+		for (AbstractButton button : this.groupedButtons) {
+			button.addItemListener(this);
+		}
+		addItemListener(this);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void itemStateChanged(ItemEvent e) {
+		if (!this.listening) return;
+		this.listening = false;
+		if (e.getSource()==this) {
+			onGroupButtonStateChanged(e);
+		}
+		else {
+			onButtonStateChanged(e);
+		}
+		this.listening = true;
+	}
+	
+	private void onGroupButtonStateChanged(ItemEvent e) {
+		boolean selected = e.getStateChange()==ItemEvent.SELECTED;
+		for (AbstractButton button : this.groupedButtons) {
+			button.setSelected(selected);
+		}
+	}
+
+	/**
+	 * Do not set the selected state of the button which has fire this event.
+	 * 
+	 * @param e
+	 */
+	private void onButtonStateChanged(ItemEvent e) {
+		Object source = e.getSource();
+		if (this.groupedButtons.contains(source)) {
+			boolean selected = e.getStateChange()==ItemEvent.SELECTED;
+			for (AbstractButton b : this.groupedButtons) {
+				if (b!=source) {
+					b.setSelected(selected);
+				}
+			}
+			setSelected(selected);
+		}
+	}
+	
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/JPopupTextField.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/JPopupTextField.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/JPopupTextField.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,438 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.swing;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.HierarchyBoundsListener;
+import java.awt.event.HierarchyEvent;
+import java.awt.event.KeyEvent;
+import java.awt.geom.Point2D;
+import java.lang.ref.WeakReference;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.JComponent;
+import javax.swing.JLayeredPane;
+import javax.swing.JPanel;
+import javax.swing.JRootPane;
+import javax.swing.JTextField;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+import javax.swing.text.Document;
+
+/**
+ * This class provides a UI panel that permits to enter
+ * the string in a popup text field.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @since 4.0
+ */
+public class JPopupTextField extends JPanel {
+
+	private static final long serialVersionUID = 374219507363847111L;
+
+	/** Default size of the border.
+	 */
+	protected static final int BORDER_SIZE = 3;
+
+	private final WeakReference<JComponent> component;
+
+	private Point2D preferredLocation = null;
+	private final EventHandler handler = new EventHandler();
+	private final JTextField textField = new JTextField();
+
+	/**
+	 * @param owner is the component which is owning this popup text field.
+	 * @param borderColor is the color of the borders of the pane. 
+	 * @param backColor is the color of the background of the pane. 
+	 */
+	public JPopupTextField(JComponent owner, Color borderColor, Color backColor) {
+		setFocusable(true);
+		this.component = new WeakReference<JComponent>(owner);
+		Font font = this.textField.getFont();
+		int size = font.getSize() - 2;
+		Font smallFont = font.deriveFont(size);
+		this.textField.setFont(smallFont);
+		this.textField.setMinimumSize(new Dimension(2*font.getSize(), font.getSize()));
+		this.textField.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Cancel");  //$NON-NLS-1$
+		this.textField.getActionMap().put("Cancel", new AbstractAction() { //$NON-NLS-1$
+			private static final long serialVersionUID = -2343325366957567597L;
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				hideInputComponent();
+			}
+		});
+
+		setLayout(new BorderLayout());
+		add(this.textField, BorderLayout.CENTER);
+		if (borderColor!=null) 
+			setBorder(BorderFactory.createLineBorder(borderColor));
+		if (backColor!=null)
+			setBackground(backColor);
+	}
+
+	/** Set the preferred location of the popup text field.
+	 * 
+	 * @param x
+	 * @param y 
+	 */
+	public void setPreferredLocation(float x, float y) {
+		Point2D old = this.preferredLocation;
+		if (old==null || old.getX()!=x || old.getY()!=y) {
+			this.preferredLocation = new Point2D.Float(x,y);
+			layoutInputComponent();
+			firePropertyChange("preferredLocation", old, this.preferredLocation); //$NON-NLS-1$
+		}
+	}
+
+	/** Set the preferred location of the popup text field.
+	 * 
+	 * @param p 
+	 */
+	public void setPreferredLocation(Point2D p) {
+		Point2D old = this.preferredLocation;
+		if (old!=p &&
+				(old==null || p==null || !p.equals(old))) {
+			this.preferredLocation = p;
+			layoutInputComponent();
+			firePropertyChange("preferredLocation", old, this.preferredLocation); //$NON-NLS-1$
+		}
+	}
+
+	/** Replies the preferred location of the popuptext field.
+	 * 
+	 * @return the preferred location or <code>null</code> if none.
+	 */
+	public Point2D getPreferredLocation() {
+		return this.preferredLocation;
+	}
+
+	/** Replies the embedded text field.
+	 * 
+	 * @return the embedded text field.
+	 */
+	protected JTextField getTextField() {
+		return this.textField;
+	}
+
+	/** Replies the document used by this puop up text field.
+	 * 
+	 * @return the document.
+	 */
+	public Document getDocument() {
+		return this.textField.getDocument();
+	}
+
+	/** Set the document used by this puop up text field.
+	 * 
+	 * @param doc is the the new document.
+	 */
+	public void setDocument(Document doc) {
+		this.textField.setDocument(doc);
+	}
+
+	/** Add listener on the validation action in the popup text field.
+	 * 
+	 * @param listener
+	 */
+	public void addActionListener(ActionListener listener) {
+		this.listenerList.add(ActionListener.class, listener);
+	}
+
+	/** Remove listener on the validation action in the popup text field.
+	 * 
+	 * @param listener
+	 */
+	public void removeActionListener(ActionListener listener) {
+		this.listenerList.remove(ActionListener.class, listener);
+	}
+
+	/**
+	 * Fire the event that corresponds to the validation in the popup text field.
+	 * 
+	 * @param eventSource
+	 */
+	protected void fireValidationAction(ActionEvent eventSource) {
+		ActionListener[] list = this.listenerList.getListeners(ActionListener.class);
+		for(ActionListener listener : list) {
+			listener.actionPerformed(eventSource);
+		}
+	}
+
+	/** Invoked when the validation action was processed.
+	 * <p>
+	 * By default this function does nothing.
+	 */
+	protected void onValidationAction() {
+		//
+	}
+
+	/** Simulate the "enter" key press.
+	 */
+	public void doValidation() {
+		ActionEvent e = new ActionEvent(this,
+				ActionEvent.ACTION_PERFORMED,
+				null,
+				System.currentTimeMillis(),
+				0);
+		fireValidationAction(e);
+	}
+
+	/** Simulate the "enter" key press.
+	 * 
+	 * @param sourceEvent is the event that has cause the validation.
+	 */
+	public void doValidation(ActionEvent sourceEvent) {
+		assert(sourceEvent!=null);
+		ActionEvent e = sourceEvent;
+		if (e.getSource()!=this) {
+			e = new ActionEvent(this,
+					sourceEvent.getID(),
+					sourceEvent.getActionCommand(),
+					sourceEvent.getWhen(),
+					sourceEvent.getModifiers());
+		}
+		hideInputComponent();
+		onValidationAction();
+		fireValidationAction(e);
+	}
+
+	/** Replies the associated owner component.
+	 * 
+	 * @return the associated owner component.
+	 */
+	public JComponent getOwner() {
+		return this.component.get();
+	}
+
+	/** Resize and move the popup text field to fit the owner bounds.
+	 */
+	protected void layoutInputComponent() {
+		JComponent component = getOwner();
+		if (component!=null && getParent()!=null) {
+			Dimension cdim = getPreferredSize();
+			if (cdim==null) cdim = new Dimension(100, 20);
+			setSize(cdim);
+
+			JRootPane root = component.getRootPane();
+			JLayeredPane lpane = root.getLayeredPane();
+			Rectangle r = component.getVisibleRect();
+			r = SwingUtilities.convertRectangle(component, r, lpane);
+
+			int x = 0;
+			int y = 0;
+			if (this.preferredLocation!=null) {
+				Point p = new Point();
+				p.setLocation(this.preferredLocation);
+				p = SwingUtilities.convertPoint(component, p, lpane);
+				x = p.x;
+				y = p.y;
+			}
+
+			if (x+cdim.width>r.width) x = r.width - cdim.width;
+			if (y+cdim.height>r.height) y = r.height - cdim.height;
+			if (x<0) x = 0;
+			if (y<0) y = 0;
+
+			setLocation(x, y);
+
+			revalidate();
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setVisible(boolean aFlag) {
+		if (aFlag) {
+			showInputComponent();
+		}
+		else {
+			hideInputComponent();
+		}
+	}
+
+	/** Show the popup text field.
+	 * 
+	 * @return <code>true</code> if the component was shown,
+	 * otherwise <code>false</code>.
+	 */
+	protected boolean showInputComponent() {
+		JComponent component = getOwner();
+		if (component!=null && getParent()==null) {
+			JRootPane root = component.getRootPane();
+			JLayeredPane lpane = root.getLayeredPane();
+			lpane.add(this, JLayeredPane.PALETTE_LAYER);
+
+			layoutInputComponent();
+
+			super.setVisible(true);
+			this.textField.requestFocusInWindow();
+
+			this.textField.addActionListener(this.handler);
+			this.textField.addFocusListener(this.handler);
+			this.textField.addHierarchyBoundsListener(this.handler);
+			onPopupFieldOpened();
+			return true;
+		}
+		return false;
+	}
+
+	/** Hide the popup text field.
+	 * 
+	 * @return <code>true</code> if the component was hidden,
+	 * otherwise <code>false</code>.
+	 */
+	protected boolean hideInputComponent() {
+		JComponent component = getOwner();
+		if (component!=null && getParent()!=null) {
+			this.textField.removeFocusListener(this.handler);
+			this.textField.removeActionListener(this.handler);
+
+			super.setVisible(false);
+			JRootPane root = component.getRootPane();
+			if (root!=null) {
+				JLayeredPane lpane = root.getLayeredPane();
+				if (lpane!=null) lpane.remove(this);
+			}
+			component.requestFocusInWindow();
+			onPopupFieldClosed();
+			return true;
+		}
+		return false;
+	}
+	
+	/** Invoked when the popup field is closed.
+	 */
+	protected void onPopupFieldClosed() {
+		//
+	}
+
+	/** Invoked when the popup field is opened.
+	 */
+	protected void onPopupFieldOpened() {
+		//
+	}
+
+	/**
+	 * Replies the text inside the text field.
+	 * 
+	 * @return the text.
+	 */
+	public String getText() {
+		return this.textField.getText();
+	}
+
+	/**
+	 * Set the text inside the field.
+	 * 
+	 * @param text
+	 */
+	public void setText(String text) {
+		this.textField.setText(text);
+	}
+
+	/**
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 * @since 4.0
+	 */
+	private class EventHandler
+	implements FocusListener, ActionListener,
+	HierarchyBoundsListener {
+
+		/**
+		 */
+		public EventHandler() {
+			//
+		}
+
+		//--------------------------------
+		// FocusListener
+		//--------------------------------
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void focusGained(FocusEvent e) {
+			//
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void focusLost(FocusEvent e) {
+			hideInputComponent();
+		}
+
+		//--------------------------------
+		// KeyListener
+		//--------------------------------
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			doValidation(e);
+		}
+
+		//--------------------------------
+		// FocusListener
+		//--------------------------------
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void ancestorMoved(HierarchyEvent e) {
+			//
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void ancestorResized(HierarchyEvent e) {
+			layoutInputComponent();
+		}
+
+	}
+
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/StandardAction.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/StandardAction.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/StandardAction.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,252 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2005-10 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.swing;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.Icon;
+
+/** This is the default implementation of an action which
+ * could be associated to a text, an icon and a tooltip text.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class StandardAction extends AbstractAction {
+	
+	private static final long serialVersionUID = -4672742688176987414L;
+
+	/** used to say that this command have an associated icon.
+	 */
+	public static final boolean HAS_ICON = true;
+	
+	/** used to say that this command have not an associated icon.
+	 */
+	public static final boolean NO_ICON = false;
+	
+
+	/** Construct a new SimpleAction.
+	 *
+	 * @param name name of this command.
+	 * @param icon is the asscoiated icon.
+	 * 
+	 */
+	public StandardAction(String name, Icon icon) {
+		setText(name);
+		setIcon(icon);
+	}
+
+	/** Construct a new SimpleAction.
+	 *
+	 * @param name name of this command.
+	 */
+	public StandardAction(String name) { 
+		setText(name);
+	}
+
+	/** Construct a new SimpleAction.
+	 *
+	 * @param icon is the asscoiated icon.
+	 * 
+	 */
+	public StandardAction(Icon icon) {
+		setIcon(icon);
+	}
+
+	/** Construct a new SimpleAction.
+	 */
+	public StandardAction() {
+		//
+	}
+
+	////////////////////////////////////////////////////////////////
+	// enabling and disabling
+	
+	/** Determine if this SimpleAction should be shown as grayed out in menus and
+	 *  toolbars.
+	 */
+	public void updateEnabled() { 
+		setEnabled(shouldBeEnabled());
+	}
+	
+	/** Return <code>true</code> if this action should be available
+	 *  to the user.
+	 *
+	 * @return <code>true</code> if this command is enabled,
+	 *         otherwise <code>false</code>.
+	 */
+	public boolean shouldBeEnabled() {
+		return isEnabled();
+	}
+	
+	////////////////////////////////////////////////////////////////
+	// accessors
+
+	/** Replies the associated icon.
+	 * 
+	 * @return the associated icon.
+	 */
+	public Icon getIcon() {
+		Object o = getValue(SMALL_ICON);
+		if ((o!=null)&&(o instanceof Icon)) {
+			return (Icon)o;
+		}
+		return null;
+	}
+	
+	/** Set the associated icon.
+	 * 
+	 * @param icon 
+	 */
+	public void setIcon(Icon icon) {
+		Object old = getValue(SMALL_ICON);
+		putValue(SMALL_ICON, icon);
+		if (old!=icon)
+			firePropertyChange(SMALL_ICON,old,icon);
+	}
+	
+	/** Replies the associated tooltip text.
+	 * 
+	 * @return the associated tooltip text
+	 */
+	public String getToolTipText() {
+		Object o = getValue(SHORT_DESCRIPTION);
+		return (o!=null)? o.toString() : null;
+	}
+	
+	/** Set the associated tooltip text.
+	 * 
+	 * @param text
+	 */
+	public void setToolTipText(String text) {
+		Object old = getValue(SHORT_DESCRIPTION);
+		putValue(SHORT_DESCRIPTION, text);
+		firePropertyChange(SHORT_DESCRIPTION,old,text);
+	}
+
+	/** Replies the associated text.
+	 * 
+	 * @return the associated text.
+	 */
+	public String getText() {
+		Object o = getValue(NAME);
+		return (o!=null)? o.toString() : null;
+	}
+	
+	/** Set the associated text.
+	 * 
+	 * @param text
+	 */
+	public void setText(String text) {
+		Object old = getValue(NAME);
+		putValue(NAME, text);
+		firePropertyChange(NAME,old,text);
+	}
+
+	/** Replies the associated context help message.
+	 * 
+	 * @return the associated context help message.
+	 */
+	public String getContextHelpText() {
+		Object o = getValue(LONG_DESCRIPTION);
+		return (o!=null)? o.toString() : null;
+	}
+	
+	/** Set the associated context help message.
+	 * 
+	 * @param text
+	 */
+	public void setContextHelpText(String text) {
+		Object old = getValue(LONG_DESCRIPTION);
+		putValue(LONG_DESCRIPTION, text);
+		firePropertyChange(LONG_DESCRIPTION,old,text);
+	}
+
+	/** Replies the associated mnemonic character.
+	 * 
+	 * @return the associated mnemonic character.
+	 */
+	public int getMnemonic() {
+		Object o = getValue(MNEMONIC_KEY);
+		return ((o!=null)&&(o instanceof Character)) ? (Character)o : '\0';
+	}
+	
+	/** Set the associated mnemonic character.
+	 * 
+	 * @param mnemonic
+	 */
+	public void setMnemonic(int mnemonic) {
+		Object old = getValue(MNEMONIC_KEY);
+		putValue(Action.MNEMONIC_KEY, mnemonic);
+		firePropertyChange(MNEMONIC_KEY,old,MNEMONIC_KEY);
+	}
+
+	/** Replies the associated action command.
+	 * 
+	 * @return the associated action command.
+	 */
+	public String getActionCommand() {
+		Object o = getValue(ACTION_COMMAND_KEY);
+		return (o!=null)? o.toString() : null;
+	}
+	
+	/** Set the associated action command.
+	 * 
+	 * @param command
+	 */
+	public void setActionCommand(String command) {
+		Object old = getValue(ACTION_COMMAND_KEY);
+		putValue(ACTION_COMMAND_KEY, command);
+		firePropertyChange(ACCELERATOR_KEY,old,command);
+	}
+
+    /** Returns the state of the action. <code>true</code> if the
+     * toggle action is selected, <code>false</code> if it's not.
+     * 
+     * @return <code>true</code> if the toggle action is selected, otherwise
+     * <code>false</code>
+     * @since 4.0
+     */
+	public boolean isSelected() {
+		Object o = getValue(SELECTED_KEY);
+		return Boolean.TRUE.equals(o);
+	}
+	
+    /** Sets the state of the action. Note that this method does not
+     * trigger an <code>actionEvent</code>.
+     *
+     * @param isSelected is <code>true</code> if the action is selected,
+     * otherwise <code>false</code>.
+     * @since 4.0
+     */
+	public void setSelected(boolean isSelected) {
+		Object old = getValue(SELECTED_KEY);
+		Object newValue = Boolean.valueOf(isSelected);
+		putValue(SELECTED_KEY, newValue);
+		firePropertyChange(SELECTED_KEY,old,newValue);
+	}
+
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/event/KeyEventSwing.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/event/KeyEventSwing.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/event/KeyEventSwing.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,106 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2005-10 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.swing.event;
+
+import java.util.EventObject;
+
+import org.arakhne.afc.ui.event.KeyEvent;
+
+/** Swing implementation of a key event.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class KeyEventSwing extends EventObject implements KeyEvent {
+
+	private static final long serialVersionUID = 5413599206960834822L;
+	
+	private final java.awt.event.KeyEvent event;
+	
+	/**
+	 * @param event
+	 */
+	public KeyEventSwing(java.awt.event.KeyEvent event) {
+		super(event.getSource());
+		this.event = event;
+	}
+
+	@Override
+	public int getDeviceId() {
+		return System.identityHashCode(this.event.getComponent());
+	}
+
+	@Override
+	public boolean isConsumed() {
+		return this.event.isConsumed();
+	}
+
+	@Override
+	public void consume() {
+		this.event.consume();
+	}
+
+	@Override
+	public boolean isShiftDown() {
+		return this.event.isShiftDown();
+	}
+
+	@Override
+	public boolean isControlDown() {
+		return this.event.isControlDown();
+	}
+
+	@Override
+	public boolean isMetaDown() {
+		return this.event.isMetaDown();
+	}
+
+	@Override
+	public boolean isAltDown() {
+		return this.event.isAltDown();
+	}
+
+	@Override
+	public boolean isAltGraphDown() {
+		return this.event.isAltGraphDown();
+	}
+
+	@Override
+	public boolean isContextualActionTriggered() {
+		return this.event.getKeyCode()==java.awt.event.KeyEvent.VK_CONTEXT_MENU;
+	}
+
+	@Override
+	public int getKeyCode() {
+		return this.event.getKeyCode();
+	}
+
+	@Override
+	public char getKeyChar() {
+		return this.event.getKeyChar();
+	}
+	
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/event/PointerEventSwing.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/event/PointerEventSwing.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/event/PointerEventSwing.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,127 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2005-10 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.swing.event;
+
+import java.awt.event.MouseEvent;
+import java.util.EventObject;
+
+import org.arakhne.afc.ui.event.PointerEvent;
+
+/** Swing implementation of a pointer event.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class PointerEventSwing extends EventObject implements PointerEvent {
+
+	private static final long serialVersionUID = -4265280802869990270L;
+	
+	private final MouseEvent event;
+	
+	/**
+	 * @param event
+	 */
+	public PointerEventSwing(MouseEvent event) {
+		super(event.getSource());
+		this.event = event;
+	}
+	
+	@Override
+	public long when() {
+		return this.event.getWhen();
+	}
+
+	@Override
+	public int getDeviceId() {
+		return System.identityHashCode(this.event.getComponent());
+	}
+
+	@Override
+	public boolean isConsumed() {
+		return this.event.isConsumed();
+	}
+
+	@Override
+	public void consume() {
+		this.event.consume();
+	}
+
+	@Override
+	public boolean isShiftDown() {
+		return this.event.isShiftDown();
+	}
+
+	@Override
+	public boolean isControlDown() {
+		return this.event.isControlDown();
+	}
+
+	@Override
+	public boolean isMetaDown() {
+		return this.event.isMetaDown();
+	}
+
+	@Override
+	public boolean isAltDown() {
+		return this.event.isAltDown();
+	}
+
+	@Override
+	public boolean isAltGraphDown() {
+		return this.event.isAltGraphDown();
+	}
+
+	@Override
+	public boolean isContextualActionTriggered() {
+		return this.event.isPopupTrigger();
+	}
+
+	@Override
+	public float getX() {
+		return this.event.getX();
+	}
+
+	@Override
+	public float getY() {
+		return this.event.getY();
+	}
+
+	@Override
+	public int getButton() {
+		return this.event.getButton();
+	}
+
+	@Override
+	public float getOrientation() {
+		return 0;
+	}
+
+	@Override
+	public int getClickCount() {
+		return this.event.getClickCount();
+	}
+	
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/progress/ProgressBarModel.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/progress/ProgressBarModel.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/progress/ProgressBarModel.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,360 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.swing.progress;
+
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.lang.ref.WeakReference;
+
+import javax.swing.BoundedRangeModel;
+import javax.swing.DefaultBoundedRangeModel;
+import javax.swing.JProgressBar;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import org.arakhne.afc.progress.DefaultProgression;
+import org.arakhne.afc.progress.Progression;
+
+/**
+ * This model permit to manage a task progression and could be
+ * given to a progress bar.
+ * <p>
+ * <pre><code>
+ * JProgressBar pb = new JProgressBar();
+ * ProgressBarModel pbm = new ProgressBarModel(pb);
+ * </code></pre>
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class ProgressBarModel extends DefaultProgression implements BoundedRangeModel {
+
+	/** Create a wrapped for the given task progression model that may
+	 * be used by the Swing progress bars.
+	 * 
+	 * @param bar is the bar to set.
+	 * @param model is the model to wrap.
+	 * @return the Swing-compliant model.
+	 * @since 4.0
+	 */
+	public static BoundedRangeModel setBarModel(JProgressBar bar, Progression model) {
+		BoundedRangeModel jmodel;
+		if (model instanceof ProgressBarModel) {
+			ProgressBarModel pbm = (ProgressBarModel)model;
+			jmodel = pbm;
+			pbm.setProgressBar(bar);
+		}
+		else {
+			if (model instanceof BoundedRangeModel)
+				jmodel = (BoundedRangeModel)model;
+			else
+				jmodel = new ProgressionProgressBarWrapper(model);
+			bar.setModel(jmodel);
+		}
+		return jmodel;
+	}
+	
+	private WeakReference<JProgressBar> progressBar;
+	
+	private final boolean percentInTooltip;
+	
+	private ComponentListener componentListener = null;
+	
+	private boolean savedStringPaintedFlag;
+	
+	/** Create a progress model with the specified <i>determinate</i> state.
+	 *  The progression will be also shown in
+	 * a tooltip.
+	 * 
+	 * @param progressBar is the progress bar that owns this model
+	 * @param value is the current value (between <var>min</var> and <var>max</var>).
+	 * @param min is the minimal value
+	 * @param max is the maximal value
+	 * @param adjusting must be <code>true</code> to indicates that the progress model
+	 * is currently under adjustement (the value is not known or sure), otherwise
+	 * <code>false</code> 
+	 */
+	public ProgressBarModel(JProgressBar progressBar, int value, int min, int max, boolean adjusting) {
+		this(progressBar, value, min, max, adjusting, true);
+	}
+	
+	/** Create a progress model with the specified <i>determinate</i> state.
+	 * The model is not adjusting.
+	 * 
+	 * @param progressBar is the progress bar that owns this model
+	 * @param value is the current value (between <var>min</var> and <var>max</var>).
+	 * @param min is the minimal value
+	 * @param max is the maximal value
+	 * @param adjusting must be <code>true</code> to indicates that the progress model
+	 * is currently under adjustement (the value is not known or sure), otherwise
+	 * <code>false</code> 
+	 * @param percentInTooltip indicates if the prograssion must be shown in tooptip
+	 */
+	public ProgressBarModel(JProgressBar progressBar, int value, int min, int max, boolean adjusting, boolean percentInTooltip) {
+    	super(value, min, max, adjusting);
+    	this.percentInTooltip = percentInTooltip;
+    	this.progressBar = progressBar==null ? null : new WeakReference<JProgressBar>(progressBar);
+    	sharedInit(progressBar);
+	}
+
+	/** Create a progress model with the specified <i>determinate</i> state.
+	 * The model is not adjusting. The progression will be also shown in
+	 * a tooltip.
+	 * 
+	 * @param progressBar is the progress bar that owns this model
+	 * @param value is the current value (between <var>min</var> and <var>max</var>).
+	 * @param min is the minimal value
+	 * @param max is the maximal value
+	 */
+	public ProgressBarModel(JProgressBar progressBar, int value, int min, int max) {
+    	super(value, min, max);
+    	this.percentInTooltip = true;
+    	this.progressBar = progressBar==null ? null : new WeakReference<JProgressBar>(progressBar);
+    	sharedInit(progressBar);
+	}
+
+	/** Create a progress model with the specified <i>indeterminate</i> state.
+	 * The model is not adjusting.
+	 * 
+	 * @param progressBar is the progress bar that owns this model
+	 * @param min is the minimal value
+	 * @param max is the maximal value
+	 * @param percentInTooltip indicates if the prograssion must be shown in tooptip
+	 */
+	public ProgressBarModel(JProgressBar progressBar, int min, int max, boolean percentInTooltip) {
+    	super(min, max);
+    	this.percentInTooltip = percentInTooltip;
+    	this.progressBar = progressBar==null ? null : new WeakReference<JProgressBar>(progressBar);
+    	sharedInit(progressBar);
+	}
+
+	/** Create a progress model with the specified <i>indeterminate</i> state.
+	 * The model is not adjusting. The progression will be also shown in
+	 * a tooltip.
+	 * 
+	 * @param progressBar is the progress bar that owns this model
+	 * @param min is the minimal value
+	 * @param max is the maximal value
+	 */
+	public ProgressBarModel(JProgressBar progressBar, int min, int max) {
+    	this(progressBar, min, max, true);
+	}
+
+	/** Create a progress model in a <i>indeterminate</i> state.
+	 * The model is not adjusting.
+	 * 
+	 * @param progressBar is the progress bar that owns this model
+	 * @param percentInTooltip indicates if the prograssion must be shown in tooptip
+	 */
+	public ProgressBarModel(JProgressBar progressBar, boolean percentInTooltip) {
+		super();
+    	this.percentInTooltip = percentInTooltip;
+    	this.progressBar = progressBar==null ? null : new WeakReference<JProgressBar>(progressBar);
+    	sharedInit(progressBar);
+	}
+
+	/** Create a progress model in a <i>indeterminate</i> state.
+	 * The model is not adjusting. The progression will be also shown
+	 * in a tooltip.
+	 * 
+	 * @param progressBar is the progress bar that owns this model
+	 */
+	public ProgressBarModel(JProgressBar progressBar) {
+		this(progressBar, true);
+	}
+	
+	private void sharedInit(JProgressBar pb) {
+    	if (pb!=null) {
+    		this.savedStringPaintedFlag = pb.isStringPainted();
+    		pb.setModel(this);
+    		this.componentListener = new ComponentListener() {
+    			
+				@Override
+				public void componentHidden(ComponentEvent e) {
+					update();
+				}
+
+				@Override
+				public void componentMoved(ComponentEvent e) {
+					update();
+				}
+
+				@Override
+				public void componentResized(ComponentEvent e) {
+					update();
+				}
+
+				@Override
+				public void componentShown(ComponentEvent e) {
+					update();
+				}
+
+				@SuppressWarnings("synthetic-access")
+				public void update() {
+					JProgressBar pbar = ProgressBarModel.this.progressBar.get();
+					ComponentListener list = ProgressBarModel.this.componentListener;
+					ProgressBarModel.this.componentListener = null;
+					if (pbar!=null) {
+						if (list!=null) pbar.removeComponentListener(list);
+						updateIndeterminateState(isIndeterminate());
+					}
+				}
+        		
+    		};
+    		pb.addComponentListener(this.componentListener);
+    	}
+    	else {
+    		updateIndeterminateState(isIndeterminate());
+    	}
+	}
+	
+	/**
+	 * This function is a workaround for a Swing bug.
+	 * String painting and indeterminate state may not 
+	 * set to <code>true</code> at the same time.
+	 * 
+	 * @param isIndeterminate
+	 */
+	private void updateIndeterminateState(boolean isIndeterminate) {
+		JProgressBar pg = this.progressBar!=null ? this.progressBar.get() : null;
+		if (pg!=null) {
+			if (isIndeterminate) {
+				this.savedStringPaintedFlag = pg.isStringPainted();
+				pg.setStringPainted(false);
+				pg.setIndeterminate(true);
+			}
+			else {
+				pg.setIndeterminate(false);
+				pg.setStringPainted(this.savedStringPaintedFlag);
+			}
+		}
+	}
+
+	@Override
+	public void addChangeListener(ChangeListener listener) {
+		this.listeners.add(ChangeListener.class, listener);
+	}
+
+	@Override
+	public void removeChangeListener(ChangeListener listener) {
+		this.listeners.remove(ChangeListener.class, listener);
+	}
+	
+	private void fireChange() {
+		ChangeEvent event = new ChangeEvent(this);
+		for(ChangeListener listener : this.listeners.getListeners(ChangeListener.class)) {
+			listener.stateChanged(event);
+		}
+	}
+
+	@Override
+	public int getExtent() {
+		return 1;
+	}
+
+	@Override
+	public boolean getValueIsAdjusting() {
+		return isAdjusting();
+	}
+
+	@Override
+	public void setExtent(int newExtent) {
+		//
+	}
+
+	@Override
+	public void setRangeProperties(int value, int extent, int min, int max, boolean adjusting) {
+		setProperties(value, min, max, adjusting);
+	}
+
+	@Override
+	public void setValueIsAdjusting(boolean b) {
+		setAdjusting(b);
+	}
+
+	@Override
+	protected void fireStateChange() {
+		JProgressBar bar = this.progressBar==null ? null : this.progressBar.get();
+		if (bar!=null) {
+	    	updateIndeterminateState(isIndeterminate());
+			if (this.percentInTooltip) {
+				updateToolip(bar);
+			}
+		}
+		super.fireStateChange();
+	}
+
+	@Override
+	protected void fireValueChange() {
+		if (this.percentInTooltip) {
+			JProgressBar bar = this.progressBar==null ? null : this.progressBar.get();
+			updateToolip(bar);
+		}
+		super.fireValueChange();
+		fireChange();
+	}
+	
+	private void updateToolip(JProgressBar bar) {
+		if (bar!=null) {
+			StringBuilder buffer = new StringBuilder();
+			buffer.append((int)getPercent());
+			buffer.append("%"); //$NON-NLS-1$
+			String comment = getComment();
+			if (comment!=null) {
+				buffer.append(" - ");  //$NON-NLS-1$
+				buffer.append(comment);
+			}
+			bar.setString(comment);
+			bar.setToolTipText(buffer.toString());
+		}
+	}
+	
+	/** Attach a progress bar to this model.
+	 * 
+	 * @param bar
+	 */
+	public void setProgressBar(JProgressBar bar) {
+		JProgressBar oldBar = this.progressBar==null ? null : this.progressBar.get();
+		if (oldBar!=bar) {
+			if (oldBar!=null) {
+				DefaultBoundedRangeModel defaultModel = new DefaultBoundedRangeModel();
+				defaultModel.setRangeProperties(
+						getValue(),
+						getExtent(),
+						getMinimum(), 
+						getMaximum(),
+						isAdjusting());
+				oldBar.setModel(defaultModel);
+			}
+	    	this.progressBar = bar==null ? null : new WeakReference<JProgressBar>(bar);
+	    	if (bar!=null) {
+	    		bar.setModel(this);
+	    	}
+	    	updateIndeterminateState(isIndeterminate());
+	    	fireChange();
+		}
+	}
+	
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/progress/ProgressMonitor.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/progress/ProgressMonitor.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/progress/ProgressMonitor.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,138 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.swing.progress;
+
+import java.awt.Component;
+import java.lang.ref.WeakReference;
+
+import javax.swing.JOptionPane;
+
+import org.arakhne.afc.progress.DefaultProgression;
+import org.arakhne.afc.progress.Progression;
+import org.arakhne.afc.progress.ProgressionEvent;
+import org.arakhne.afc.progress.ProgressionListener;
+
+/**
+ * A ProgressMonitor ables to react to {@link ProgressionEvent}.
+ *
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class ProgressMonitor extends javax.swing.ProgressMonitor implements ProgressionListener {
+
+	/** Create a {@link Progression} model and link it to a
+	 * Swing {@link ProgressMonitor}.
+	 * 
+	 * @param parentComponent the parent component for the dialog box
+     * @param message a descriptive message that will be shown
+     *        to the user to indicate what operation is being monitored.
+     *        This does not change as the operation progresses.
+     *        See the message parameters to methods in
+     *        {@link JOptionPane#message}
+     *        for the range of values.
+     * @param note a short note describing the state of the
+     *        operation.  As the operation progresses, you can call
+     *        setNote to change the note displayed.  This is used,
+     *        for example, in operations that iterate through a
+     *        list of files to show the name of the file being processes.
+     *        If note is initially null, there will be no note line
+     *        in the dialog box and setNote will be ineffective
+     * @return the progression model.
+	 */
+	public static Progression createProgression(Component parentComponent,
+            Object message,
+            String note) {
+		Progression p = new DefaultProgression();
+		p.addProgressionListener(new ProgressMonitor(
+				p,
+				parentComponent, message, note, p.getMinimum(), p.getMaximum()));
+		return p;
+	}
+	
+	private final WeakReference<Progression> progressionModel;
+	
+	/**
+	 * @param parentComponent the parent component for the dialog box
+     * @param message a descriptive message that will be shown
+     *        to the user to indicate what operation is being monitored.
+     *        This does not change as the operation progresses.
+     *        See the message parameters to methods in
+     *        {@link JOptionPane#message}
+     *        for the range of values.
+     * @param note a short note describing the state of the
+     *        operation.  As the operation progresses, you can call
+     *        setNote to change the note displayed.  This is used,
+     *        for example, in operations that iterate through a
+     *        list of files to show the name of the file being processes.
+     *        If note is initially null, there will be no note line
+     *        in the dialog box and setNote will be ineffective
+     * @param min the lower bound of the range
+     * @param max the upper bound of the range
+     * @see #createProgression(Component, Object, String)
+	 */
+	public ProgressMonitor(Component parentComponent,
+            Object message,
+            String note,
+            int min,
+            int max) {
+		this(null, parentComponent, message, note, min, max);
+	}
+
+	private ProgressMonitor(Progression progressionModel,
+			Component parentComponent,
+            Object message,
+            String note,
+            int min,
+            int max) {
+		super(parentComponent, message, note, min, max);
+		this.progressionModel = new WeakReference<Progression>(progressionModel);
+	}
+	
+	@Override
+	public void onProgressionValueChanged(ProgressionEvent event) {
+		if (event.isTaskFinished()) {
+			Progression progression = this.progressionModel.get();
+			this.progressionModel.clear();
+			if (progression!=null) {
+				progression.removeProgressionListener(this);
+			}
+			close();
+		}
+		else {
+			setMinimum(event.getMinimum());
+			setMaximum(event.getMaximum());
+			setProgress(event.getValue());
+			setNote(event.getComment());
+		}
+	}
+
+	@Override
+	public void onProgressionStateChanged(ProgressionEvent event) {
+		if (event.isTaskFinished()) {
+			setNote(event.getComment());
+		}
+	}	
+	
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/progress/ProgressionProgressBarWrapper.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/progress/ProgressionProgressBarWrapper.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/progress/ProgressionProgressBarWrapper.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,237 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.swing.progress;
+
+import javax.swing.BoundedRangeModel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import org.arakhne.afc.progress.Progression;
+import org.arakhne.afc.progress.ProgressionEvent;
+import org.arakhne.afc.progress.ProgressionListener;
+
+/**
+ * This model permit to manage a task progression and could be
+ * given to a progress bar.
+ * <p>
+ * <pre><code>
+ * Progression m = ...;
+ * TaskProgressionProgressBarWrapper pbm = new TaskProgressionProgressBarWrapper(m);
+ * JProgressBar pb = new JProgressBar(pbm);
+ * </code></pre>
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+final class ProgressionProgressBarWrapper
+implements BoundedRangeModel {
+
+	private final Progression model;
+	
+	/**
+	 * @param model is the model to wrap.
+	 */
+	public ProgressionProgressBarWrapper(Progression model) {
+		this.model = model;
+	}
+
+	/** Replies the wrapped model.
+	 * 
+	 * @return the wrapped model.
+	 */
+	public Progression getWrappedModel() {
+		return this.model;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addChangeListener(ChangeListener listener) {
+		this.model.addProgressionListener(new Listener(listener));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void removeChangeListener(ChangeListener listener) {
+		this.model.removeProgressionListener(new Listener(listener));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int getExtent() {
+		return 1;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setExtent(int newExtent) {
+		//
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int getMaximum() {
+		return this.model.getMaximum();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int getMinimum() {
+		return this.model.getMinimum();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int getValue() {
+		return this.model.getValue();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean getValueIsAdjusting() {
+		return this.model.isAdjusting();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setMaximum(int newMaximum) {
+		this.model.setMaximum(newMaximum);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setMinimum(int newMinimum) {
+		this.model.setMinimum(newMinimum);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setRangeProperties(
+			int value, int extent, int min, int max,
+			boolean adjusting) {
+		this.model.setProperties(value, min, max, adjusting);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setValue(int newValue) {
+		int v = getValue();
+		int m = getMaximum();
+		if (v<m && newValue==m) {
+			this.model.end();
+		}
+		else {
+			this.model.setValue(newValue);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setValueIsAdjusting(boolean b) {
+		this.model.setAdjusting(b);
+	}
+
+	/**
+	 * @author $Author: sgalland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class Listener implements ProgressionListener {
+
+		private final ChangeListener listener;
+		
+		/**
+		 * @param l
+		 */
+		public Listener(ChangeListener l) {
+			assert(l!=null);
+			this.listener = l;
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void onProgressionStateChanged(ProgressionEvent event) {
+			ChangeEvent e = new ChangeEvent(event.getSource());
+			this.listener.stateChanged(e);
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void onProgressionValueChanged(ProgressionEvent event) {
+			ChangeEvent e = new ChangeEvent(event.getSource());
+			this.listener.stateChanged(e);
+		}
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public boolean equals(Object obj) {
+			if (obj instanceof Listener)
+				return this.listener.equals(((Listener)obj).listener);
+			return this.listener.equals(obj);
+		}
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public int hashCode() {
+			return this.listener.hashCode();
+		}
+		
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/AbstractCallableUndoableEdit.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/AbstractCallableUndoableEdit.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/AbstractCallableUndoableEdit.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,75 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2005-10 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.swing.undo;
+
+import javax.swing.undo.AbstractUndoableEdit;
+import javax.swing.undo.CannotRedoException;
+
+import org.arakhne.afc.ui.undo.Undoable;
+
+/** Abstract implementation of an undoable edit that is also
+ * directly callable to run/redo the action.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractCallableUndoableEdit extends AbstractUndoableEdit implements Undoable {
+
+	private static final long serialVersionUID = -1330907958341724667L;
+
+	/**
+	 */
+	public AbstractCallableUndoableEdit() {
+		//
+	}
+	
+	@Override
+	public final void redo() throws CannotRedoException {
+		super.redo();
+		doEdit();
+	}
+
+	@Override
+	public final void undo() throws CannotRedoException {
+		super.undo();
+		undoEdit();
+	}
+	
+	/** Do the edition.
+	 * This function is automatically invoked by {@link #redo()}.
+	 * In opposite to {@link #redo()}, this function does not
+	 * throw an exception when the edit was never done before.
+	 */
+	public abstract void doEdit();
+
+	/** Undo the edition.
+	 * This function is automatically invoked by {@link #undo()}.
+	 * In opposite to {@link #undo()}, this function does not
+	 * throw an exception when the edit was never done before.
+	 */
+	public abstract void undoEdit();
+
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/UndoManagerSwing.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/UndoManagerSwing.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/UndoManagerSwing.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,196 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.swing.undo;
+
+import javax.swing.undo.CannotRedoException;
+import javax.swing.undo.CannotUndoException;
+import javax.swing.undo.UndoManager;
+import javax.swing.undo.UndoableEdit;
+
+import org.arakhne.afc.ui.undo.UndoListener;
+import org.arakhne.afc.ui.undo.Undoable;
+import org.arakhne.afc.util.ListenerCollection;
+
+
+/** Implementation of a {@link org.arakhne.afc.ui.undo.UndoManager} based on the
+ * standard Swing {@link UndoManager}.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class UndoManagerSwing extends UndoManager implements org.arakhne.afc.ui.undo.UndoManager {
+
+	private static final long serialVersionUID = 4936381853383834466L;
+
+	private final ListenerCollection<UndoListener> listeners = new ListenerCollection<UndoListener>();
+
+	/**
+	 */
+	public UndoManagerSwing() {
+		super();
+	}
+
+	@Override
+	public void addUndoListener(UndoListener l) {
+		this.listeners.add(UndoListener.class, l);
+	}
+
+	@Override
+	public void removeUndoListener(UndoListener l) {
+		this.listeners.remove(UndoListener.class, l);
+	}
+
+	/**
+	 * Notifies listeners.
+	 */
+	protected void fireChange() {
+		for(UndoListener listener : this.listeners.getListeners(UndoListener.class)) {
+			listener.undoListChanged(this);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void redoTo(UndoableEdit edit) throws CannotRedoException {
+		super.redoTo(edit);
+		fireChange();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void undoTo(UndoableEdit edit) throws CannotUndoException {
+		super.undoTo(edit);
+		fireChange();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized void undo() throws CannotUndoException {
+		boolean inProgress = isInProgress();
+		super.undo();
+		if(inProgress) fireChange();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized void redo() throws CannotRedoException {
+		boolean inProgress = isInProgress();
+		super.redo();
+		if(!inProgress) fireChange();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized boolean addEdit(UndoableEdit anEdit) {
+		if (anEdit==null) return false;
+		boolean retVal = super.addEdit(anEdit);
+		if (retVal) fireChange();
+		return retVal;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized void discardAllEdits() {
+		boolean fire = !this.edits.isEmpty();
+		super.discardAllEdits();
+		if (fire) fireChange();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public synchronized void end() {
+		super.end();
+		fireChange();
+	}
+
+	@Override
+	public final boolean add(Undoable action) {
+		if (action==null) return false;
+		if (action instanceof UndoableEdit) {
+			return addEdit((UndoableEdit)action);
+		}
+		return addEdit(new UndoableWrapper(action));
+	}
+
+	@Override
+	public final void discardAll() {
+		discardAllEdits();
+	}
+
+	/**
+	 * @author $Author: galland$
+	 * @version $Name$ $Revision$ $Date$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private static class UndoableWrapper extends AbstractCallableUndoableEdit {
+		
+		private static final long serialVersionUID = -8903679029751177352L;
+		
+		private final Undoable undoable;
+		
+		/**
+		 * @param undoable
+		 */
+		public UndoableWrapper(Undoable undoable) {
+			this.undoable = undoable;
+		}
+		
+		@Override
+		public void doEdit() {
+			this.undoable.redo();
+		}
+
+		@Override
+		public void undoEdit() {
+			this.undoable.undo();
+		}
+		
+		@Override
+		public String getPresentationName() {
+			return this.undoable.getPresentationName();
+		}
+		
+		@Override
+		public void die() {
+			this.undoable.die();
+			super.die();
+		}
+		
+	}
+
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/UndoableAction.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/UndoableAction.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/UndoableAction.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,196 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2005-10 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.swing.undo;
+
+import java.awt.event.ActionEvent;
+import java.lang.reflect.Constructor;
+
+import javax.swing.Icon;
+import javax.swing.undo.UndoManager;
+
+import org.arakhne.afc.ui.swing.StandardAction;
+import org.arakhne.vmutil.ReflectionUtil;
+
+/** Implementation of an action that is creating and 
+ * adding an UndoableEdit into the given undo manager.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class UndoableAction extends StandardAction {
+
+	private static final long serialVersionUID = 7640859658516394571L;
+	
+	private final UndoManager undoManager;
+	private final Class<? extends AbstractCallableUndoableEdit> type;
+	private final Object[] constructorParameters;
+	
+	/**
+	 * @param label
+	 * @param tooltip
+	 * @param icon
+	 * @param manager
+	 * @param type
+	 * @param constructorParameters
+	 */
+	public UndoableAction(
+			String label,
+			String tooltip,
+			Icon icon,
+			UndoManager manager,
+			Class<? extends AbstractCallableUndoableEdit> type,
+			Object... constructorParameters) {
+		this.undoManager = manager;
+		this.type = type;
+		this.constructorParameters = constructorParameters;
+		setText(label);
+		setToolTipText(tooltip);
+		setIcon(icon);
+	}
+	
+	/**
+	 * @param manager
+	 * @param type
+	 * @param constructorParameters
+	 */
+	public UndoableAction(
+			UndoManager manager,
+			Class<? extends AbstractCallableUndoableEdit> type,
+			Object... constructorParameters) {
+		this(null, null, null, manager, type, constructorParameters);
+	}
+
+	/**
+	 * @param label
+	 * @param icon
+	 * @param manager
+	 * @param type
+	 * @param constructorParameters
+	 */
+	public UndoableAction(
+			String label,
+			Icon icon,
+			UndoManager manager,
+			Class<? extends AbstractCallableUndoableEdit> type,
+			Object... constructorParameters) {
+		this(label, null, icon, manager, type, constructorParameters);
+	}
+
+	/**
+	 * @param label
+	 * @param manager
+	 * @param type
+	 * @param constructorParameters
+	 */
+	public UndoableAction(
+			String label,
+			UndoManager manager,
+			Class<? extends AbstractCallableUndoableEdit> type,
+			Object... constructorParameters) {
+		this(label, null, null, manager, type, constructorParameters);
+	}
+
+	/**
+	 * @param icon
+	 * @param manager
+	 * @param type
+	 * @param constructorParameters
+	 */
+	public UndoableAction(
+			Icon icon,
+			UndoManager manager,
+			Class<? extends AbstractCallableUndoableEdit> type,
+			Object... constructorParameters) {
+		this(null, null, icon, manager, type, constructorParameters);
+	}
+
+	@Override
+	public final void actionPerformed(ActionEvent e) {
+		AbstractCallableUndoableEdit undo;
+		try {
+			undo = newEditInstance(this.type, this.constructorParameters);
+		}
+		catch (Exception ex) {
+			throw new RuntimeException(ex);
+		}
+		undo.doEdit();
+		this.undoManager.addEdit(undo);
+	}
+	
+	/** Invoked to create an instance of undoable edit.
+	 * By default, this function invokes the constructor by
+	 * reflection.
+	 * This function may be overridden to provide an other mean to
+	 * create the edit instance.
+	 * 
+	 * @param type is the type of edit to create.
+	 * @param constructorParameters are the parameters to pass to the constructor.
+	 * @return the edit instance, never <code>null</code>.
+	 * @throws Exception
+	 */
+	@SuppressWarnings("static-method")
+	protected AbstractCallableUndoableEdit newEditInstance(Class<? extends AbstractCallableUndoableEdit> type, Object[] constructorParameters) throws Exception {
+		for(Constructor<?> cons : type.getDeclaredConstructors()) {
+			if (matches(cons.getParameterTypes(), constructorParameters)) {
+				boolean b = cons.isAccessible();
+				try {
+					cons.setAccessible(true);
+					return type.cast(cons.newInstance(constructorParameters));
+				}
+				catch(Throwable e) {
+					//
+				}
+				finally {
+					cons.setAccessible(b);
+				}
+			}
+		}
+		throw new InstantiationException();
+	}
+	
+	/** Test if the instances are matching the types.
+	 * 
+	 * @param types
+	 * @param instances
+	 * @return <code>true</code> if the instances are matching the types;
+	 * otherwise <code>false</code>.
+	 */
+	protected static boolean matches(Class<?>[] types, Object[] instances) {
+		if (types==null) return instances==null || instances.length==0;
+		assert(types!=null);
+		if (instances==null) return types.length==0;
+		if (types.length==instances.length) {
+			for(int i=0; i<types.length; ++i) {
+				// Use the reflection util that supports the base types.
+				if (!ReflectionUtil.isInstance(types[i],instances[i]))
+					return false;
+			}
+			return true;
+		}
+		return false;
+	}
+
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/UndoableGroupSwing.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/UndoableGroupSwing.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/undo/UndoableGroupSwing.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,57 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2005-10 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.swing.undo;
+
+import javax.swing.undo.CompoundEdit;
+
+import org.arakhne.afc.ui.undo.Undoable;
+
+/** Abstract implementation of an undoable edit that is also
+ * directly callable to run/redo the action.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class UndoableGroupSwing extends CompoundEdit implements Undoable {
+
+	private static final long serialVersionUID = -5363974010663738749L;
+	
+	private final String label;
+	
+	/**
+	 * @param label
+     */
+    public UndoableGroupSwing(String label) {
+        super();
+        this.label = label;
+    }
+    
+    @Override
+    public String getPresentationName() {
+    	return this.label;
+    }
+
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/InternalZoomablePanel.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/InternalZoomablePanel.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/InternalZoomablePanel.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,954 @@
+/* $Id$
+ * 
+ * This is a part of NetEditor project from Arakhne.org:
+ * package org.arakhne.neteditor.zoompanel.
+ * 
+ * Copyright (C) 2002  St&eacute;phane GALLAND, Mahdi Hannoun
+ * Copyright (C) 2004-2013  St&eacute;phane GALLAND
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.swing.zoompanel;
+
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.Dimension2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.print.PageFormat;
+import java.awt.print.PrinterException;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+
+import javax.swing.JPanel;
+
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.ui.Graphics2DLOD;
+import org.arakhne.afc.ui.awt.DoubleDimension;
+import org.arakhne.afc.ui.awt.FloatDimension;
+import org.arakhne.afc.ui.awt.ZoomableContextUtil;
+
+/** Component for displaying a zoomable graphical area.
+ * <p>
+ * This class is partly based on the
+ * class <code>NetEditor.Workspace</code> from
+ * the Arakhne.org project.
+ * <p>
+ * Three coordinate systems are used:
+ * <ul>
+ * <li>the workspace coordinates: coordinates
+ * specifical to the displayed document (<code>float</code>),</li>
+ * <li>the screen coordinates: the coordinates on the screen
+ * (<code>int</code>) such as the mouse location on the screen,</li>
+ * <li>the fitted coordinates: corresponds to the scaling of
+ * the workspace coordinates to fit the entire document into the
+ * current screen space.</li>
+ * </ul>
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+final class InternalZoomablePanel extends JPanel implements ZoomableViewport {
+    
+	//-------------------------------------------------------------------
+    // Attributes
+    //-------------------------------------------------------------------
+
+	private static final long serialVersionUID = -2740411873564016759L;
+
+	/** Zoomable panel.
+     */
+    private final WeakReference<ZoomablePanel> zoomPanel;
+
+    /** Flip the X coordinates
+     */
+    private final boolean flipX;
+
+    /** Flip the Y coordinates
+     */
+    private final boolean flipY;
+    
+    /** This is the current zoom factor.
+     */
+    private float zoomFactor = 1.f;
+    
+    /** This is the stepping amount eahc time the
+     * user want to zoom out or in.
+     */
+    private float zoomFactorStep = 1.05f;
+
+	/** Minimal scaling factor.
+	 */
+	private float minScaleFactor = 0.001f;
+
+	/** Maximal scaling factor.
+	 */
+	private float maxScaleFactor = 100.f;
+
+	/** This is the scale factor used to fit the document into
+     * the screen space.
+     */
+    private float fitToWindowFactor = 1f;
+    
+    /** Last restangle passed to {@link #refreshFactors(Rectangle2D)}
+     * when the screen window was empty (typically occurs when
+     * the window was never displayed).
+     */
+    private Rectangle2D lastRefreshRequestRectangle = null;
+    
+    /** This is the document point which is displayed
+     * at the center of the viewport.
+     * <p>
+     * This point corresponds to the workspace coordinate space. 
+     */
+    private Point2f focusPoint = null;
+    
+    /** Size of the drawing area.
+     */
+    private Dimension2D drawingArea = null;
+    
+    /** Buffer for the drawing area.
+     */
+    private SoftReference<Rectangle2D> drawRectangleBuffer = null;
+    
+    /** This attribute permits to store the graphical
+     * translation which is required to center the
+     * {@link #focusPoint} at the center of the
+     * graphical viewport. This variable is set
+     * at the begining of {@link #paint(Graphics)}.
+     * Using its value outside a drawing function have
+     * no sens.
+     */
+    private Dimension2D translationToCenter = null;
+    
+    //-------------------------------------------------------------------
+    // Constructors
+    //-------------------------------------------------------------------
+    
+    /** Create a InternalZoomablePanel and add it into the specified
+     * scroll pane.
+     * 
+     * @param zoompane is the zoom panel that owns this panel.
+     * @param flipX indicates if the X coordinates may be flipped, ie.
+     * the X axis may be inverted. 
+     * @param flipY indicates if the Y coordinates may be flipped, ie.
+     * the Y axis may be inverted. 
+     */
+    public InternalZoomablePanel(ZoomablePanel zoompane, boolean flipX, boolean flipY) {
+    	this.flipX = flipX;
+    	this.flipY = flipY;
+    	this.zoomPanel = new WeakReference<ZoomablePanel>(zoompane);
+    }
+    
+    //-------------------------------------------------------------------
+    // Getter/Setter
+    //-------------------------------------------------------------------
+    
+    /** Replies the scaling factor to apply to have the document fitting the view.
+     * 
+     * @return s
+     */
+    public float getFitInWindowFactor() {
+    	return this.fitToWindowFactor;
+    }
+    
+	/** Replies the current zoom factor.
+	 * 
+	 * @return current zoom factor.
+	 */
+	public float getZoomFactor() {
+		return this.zoomFactor;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public final boolean isXAxisInverted() {
+    	return this.flipX;
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public final boolean isYAxisInverted() {
+    	return this.flipY;
+    }
+    
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float getScalingFactor() {
+		return this.zoomFactor * this.fitToWindowFactor;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public Point2D getFocusPointPixel() {
+    	Rectangle2D bounds = getBounds();
+    	return new Point2D.Double(bounds.getCenterX(),bounds.getCenterY());
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public Point2f getFocusPoint() {
+    	if (this.focusPoint==null) return null;
+    	return new Point2f(this.focusPoint);
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public void setFocusPoint(float x, float y) {
+		if (this.focusPoint==null || x!=this.focusPoint.getX()
+			|| y!=this.focusPoint.getY()) {
+			Point2f old = this.focusPoint;
+	    	if (this.focusPoint==null)
+	    		this.focusPoint = new Point2f(x,y);
+	    	else
+	    		this.focusPoint.set(x, y);
+	    	this.drawRectangleBuffer = null;
+	    	firePropertyChange("targetPoint", old, this.focusPoint); //$NON-NLS-1$
+		}
+    }
+
+    /** Remove the focus point.
+     */
+    void clearFocusPoint() {
+    	if (this.focusPoint!=null) {
+			Point2f old = this.focusPoint;
+	    	this.focusPoint = null;
+	    	this.drawRectangleBuffer = null;
+	    	firePropertyChange("targetPoint", old, this.focusPoint); //$NON-NLS-1$
+    	}
+    }
+
+    //-------------------------------------------------------------------
+    // Paint
+    //-------------------------------------------------------------------
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException {
+		if (pageIndex==0) {
+			Graphics2D g2d = (Graphics2D)g;
+			
+			// Shift Graphic to line up with beginning of print-imageable region
+	        g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
+	        
+	        // Be sure that the document feat to the print-imageable region
+			Rectangle2D r = getDocumentRect();
+			float width = logical2pixel_size((float)r.getWidth());
+			float height = logical2pixel_size((float)r.getHeight());
+	        float pageWidth = (float)pageFormat.getImageableWidth();
+	        float pageHeight = (float)pageFormat.getImageableHeight();
+	        float scale = 
+	        	((pageWidth / width)>(pageHeight / height))
+	        	? (pageHeight / height)
+	        	: (pageWidth / width);
+
+	        	
+			g2d.setColor(Color.BLACK);
+			g2d.drawRect(0,0,(int)pageWidth,(int)pageHeight);
+			
+			g2d.scale(scale, scale);
+	        
+	        // Print the document
+			print(g2d, new Rectangle2D.Float(0, 0, width, height));			
+			
+			return PAGE_EXISTS;
+		}
+		return NO_SUCH_PAGE;
+	}
+
+	/** Print the specified part of this panel into
+	 * the specified graphical context.
+	 * <p>
+	 * The given print area is the part of the panel
+	 * to replies inside an image. It is expressed in pixels.
+	 * <p>
+	 * This function assumes that the upper-left corner
+	 * of the document has the coordinates (0,0) and the
+	 * specified printing area is relative to this origin.
+	 * 
+	 * @param g is the graphical context inside which to print.
+	 * @param print_area is the window coordinates to snap.
+	 */
+	public void print(Graphics2D g, Rectangle2D print_area) {
+		Rectangle2D r = getDocumentRect();
+		float doc_x = logical2pixel_x((float)r.getMinX());
+		float doc_y = logical2pixel_y((float)r.getMinY());
+		float width = logical2pixel_size((float)r.getWidth());
+		float height = logical2pixel_size((float)r.getHeight());
+		
+		// Be sure that the printing area is enclosed inside the document bounds.
+		r = new Rectangle2D.Double(0,0,width,height).createIntersection(print_area);
+
+		// Be sure that the new origin (0,0) corresponds
+		// to upper-left corner of the print area
+		g.translate(-doc_x-r.getX(),-doc_y-r.getY());
+		
+		print(g);
+	}
+
+	/**
+     * Invoke this method to print the component. This method will
+     * result in invocations to <code>printComponent</code>,
+     * <code>printBorder</code> and <code>printChildren</code>. It is
+     * not recommended that you override this method, instead override
+     * one of the previously mentioned methods. This method sets the
+     * component's state such that the double buffer will not be used, eg
+     * painting will be done directly on the passed in <code>Graphics</code>.
+     *
+     * @param g the <code>Graphics</code> context in which to paint
+     */
+    @Override
+	public void print(Graphics g) {
+		synchronized(this.getTreeLock()) {
+	    	ZoomablePanel panel = this.zoomPanel.get();
+			
+	    	boolean opaque = isOpaque();
+	    	boolean antialiasing = panel.isAntiAliased();
+	    	Graphics2DLOD lod = panel.getLOD();
+			
+	    	setOpaque(false);
+	    	panel.setAntiAliased(true);
+	    	panel.setLOD(Graphics2DLOD.HIGH_LEVEL_OF_DETAIL);
+			
+	    	super.print(g);
+			
+			setOpaque(opaque);
+	    	panel.setAntiAliased(antialiasing);
+	    	panel.setLOD(lod);
+		}
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+	public final void paint(Graphics g) {
+
+    	if (getIgnoreRepaint()) return;
+    	
+        Graphics2D g2d = (Graphics2D)g;
+
+        // Compute the factors because the last
+    	// calls to the refreshFactors() was made
+    	// when the screen window is empty
+    	if (this.lastRefreshRequestRectangle!=null) {
+    		Rectangle2D r = this.lastRefreshRequestRectangle;
+    		if (refreshFactors(r)) changeWorkspaceSize(r);
+    	}
+    	
+    	Color color = getBackground();
+    	if (color!=null) g2d.setBackground(color);
+    	
+    	super.paint(g);
+
+    	// Be sure that the document coordinate system was computed
+    	float delta_x, delta_y;
+    	if (this.focusPoint!=null) {
+	    	
+	    	// Get the dimensions of the logical space and the screen space
+    		Rectangle2D screen_bounds = getBounds();
+	
+	    	// Compute the translation for centering the logical
+	    	// space inside the screen space
+	    	
+	    	
+	    	
+	    	float delta_prime = (float)screen_bounds.getCenterX();
+	    	delta_x = pixel2logical_size(delta_prime) - this.focusPoint.getX();
+	    	
+	    	delta_prime = (float)screen_bounds.getCenterY();
+	    	delta_y = pixel2logical_size(delta_prime) - this.focusPoint.getY();
+	    	
+	    	if (this.translationToCenter==null) this.translationToCenter = new DoubleDimension();
+	    	this.translationToCenter.setSize(delta_x,delta_y);
+	    	
+    	}
+    	else {
+    		delta_x = delta_y = 0;
+    	}
+
+    	ZoomablePanel panel = this.zoomPanel.get();
+
+    	if (panel!=null) {
+    		
+    		boolean isPrinting =
+    			(panel.hasFlag(ZoomablePanel.FLAG_IS_PRINTING)||
+   				 panel.hasFlag(ZoomablePanel.FLAG_IS_PRINTING_ALL));
+    		
+        	// Create the new graphical context
+    		panel.paintAllComponents(
+        			g2d, this,
+        			this.zoomFactor,this.fitToWindowFactor,
+        			delta_x,
+    				delta_y,
+    				panel.isAntiAliased(),
+    				isPrinting,
+    				panel.getLOD());
+    	}
+    }
+	
+    //-------------------------------------------------------------------
+    // Sizes
+    //-------------------------------------------------------------------
+	
+	/** Refresh the factors for the zoom and the fit of the document
+	 * into the screen space.
+	 * <p>
+	 * This method do not call {@link #changeWorkspaceSize(Rectangle2D)}
+	 * to refresh the scrollbar and the size of the drawing area.
+	 * You must call <code>changeWorkspaceSize(Rectangle2D)</code> just
+	 * after <code>refreshFactors(Rectangle2D)</code>.
+	 * 
+	 * @param document_bounds is the bounds of the document (in document's units)
+	 * @return a status that indicates if this function succeeded or not.
+	 */
+    boolean refreshFactors(Rectangle2D document_bounds) {
+    	Rectangle2D screen_window = getBounds();
+        
+        if ((screen_window==null)||(screen_window.isEmpty())) {
+        	this.lastRefreshRequestRectangle = document_bounds;
+        	return false;
+        }
+    	this.lastRefreshRequestRectangle = null;
+    	float oldFactor = this.zoomFactor;
+    	this.zoomFactor = 1.f;
+		Point2f oldTarget = this.focusPoint;
+		
+		if (this.focusPoint==null) this.focusPoint = new Point2f();
+
+		if ((document_bounds==null)||(document_bounds.isEmpty())) {
+			this.fitToWindowFactor = 1.f;
+    		
+			this.focusPoint.set(
+					(float)screen_window.getCenterX(),
+					(float)screen_window.getCenterY());
+		}
+    	else {    		
+			
+    		this.fitToWindowFactor = (float)(screen_window.getWidth() / document_bounds.getWidth());
+			float fit = (float)(screen_window.getHeight() / document_bounds.getHeight());
+			if (fit<this.fitToWindowFactor) this.fitToWindowFactor = fit;
+			
+			this.focusPoint.set(
+					(float)document_bounds.getCenterX(),
+					(float)document_bounds.getCenterY());
+    	}
+
+		this.drawRectangleBuffer = null;
+		
+		if (oldFactor!=this.zoomFactor) {
+			this.zoomPanel.get().firePropertyChange("zoomFactor", oldFactor, this.zoomFactor); //$NON-NLS-1$
+		}
+		if (!this.focusPoint.equals(oldTarget)) {
+			this.zoomPanel.get().firePropertyChange("targetPoint", oldTarget, this.focusPoint); //$NON-NLS-1$
+		}
+		
+		return true;
+    }
+	
+    /** Change the workspace area if necessary.
+     *  <p>
+     *  This method is called each time the workspace
+     *  could be extended in one of the four cardinal
+     *  directions.
+     *  <p>
+     *  The size of the workspace and/or the origin point
+     *  are updated if the given Rectangle is outside
+     *  the current workspace.
+     *  <p>
+     *  The scroll bar was not refreshed.
+     *
+     * @param requiredArea the area that must be in workspace. If
+     *        <code>null</code>, the required area will be computed
+     *        with {@link #getDocumentRect()}.
+     * @return <code>true</code> if the bounds have changed; otherwise
+     * <code>false</code>.
+     */
+    boolean changeWorkspaceSize(Rectangle2D requiredArea) {
+    	ZoomablePanel zoompanel = this.zoomPanel.get();
+	    if (zoompanel==null) return false;
+	    Rectangle2D r = requiredArea;
+		if (r==null) r = getDocumentRect() ;
+		if (r==null) return false;
+
+	    // ompute the dimensions of the document in the screen space
+		float documentScreenWidth = logical2pixel_size((float)r.getWidth());
+		float documentScreenHeight = logical2pixel_size((float)r.getHeight());
+
+		// Reset the size of the drawing area
+    	if (this.drawingArea==null) this.drawingArea = new DoubleDimension(documentScreenWidth,documentScreenHeight);
+    	else this.drawingArea.setSize(documentScreenWidth,documentScreenHeight) ;
+    	this.drawRectangleBuffer = null;
+		
+		// Set the location of the scroll bars
+		return zoompanel.revalidateWorkspace();
+     }
+    
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public Dimension2D getDrawingAreaSize() {
+    	return this.drawingArea==null ? new DoubleDimension() : this.drawingArea;
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public Rectangle2D getDrawingAreaRect() {
+		Rectangle2D b = (this.drawRectangleBuffer==null) ? null : this.drawRectangleBuffer.get();
+    	if (b!=null) return b;
+    	
+    	Rectangle2D world = getDocumentRect();
+    	Rectangle2D viewport = getBounds();
+    	
+    	if ((world==null)||(world.isEmpty()))
+			return viewport;
+
+    	if (this.focusPoint==null) {
+    		refreshFactors(world);
+    	}
+    	
+    	if (this.focusPoint==null)
+			return viewport;
+
+    	if (this.drawingArea==null) {
+    		float documentScreenWidth = logical2pixel_size((float)world.getWidth());
+    		float documentScreenHeight = logical2pixel_size((float)world.getHeight());
+    		this.drawingArea = new DoubleDimension(documentScreenWidth, documentScreenHeight);
+    	}
+    	
+    	Rectangle2D world_relative = new Rectangle2D.Double(
+    			world.getX() - this.focusPoint.getX(),
+    			world.getY() - this.focusPoint.getY(),
+    			world.getWidth(), world.getHeight());
+    	
+    	float dx = (float)(this.drawingArea.getWidth() / world.getWidth());
+    	float dy = (float)(this.drawingArea.getHeight() / world.getHeight());
+    	
+    	b = new Rectangle2D.Float(
+    			(float)(world_relative.getX()*dx+viewport.getCenterX()),
+    			(float)(world_relative.getY()*dy+viewport.getCenterY()),
+    			(float)this.drawingArea.getWidth(),
+    			(float)this.drawingArea.getHeight());
+    	this.drawRectangleBuffer = new SoftReference<Rectangle2D>(b);
+    	return b;
+    }
+    
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public float logical2pixel_size(float l) {
+		return ZoomableContextUtil.logical2pixel_size(l, true, this.fitToWindowFactor * this.zoomFactor);
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public float logical2pixel_x(float l) {
+    	return ZoomableContextUtil.logical2pixel_x(l,
+    			true, this.flipX,
+    			getDrawingAreaRect(),
+    			(float)((this.translationToCenter==null) ? 0 : this.translationToCenter.getWidth()),
+    			this.fitToWindowFactor * this.zoomFactor);
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public float logical2pixel_y(float l) {
+    	return ZoomableContextUtil.logical2pixel_y(l,
+    			true, this.flipY,
+    			getDrawingAreaRect(),
+    			(float)((this.translationToCenter==null) ? 0 : this.translationToCenter.getHeight()),
+    			this.fitToWindowFactor * this.zoomFactor);
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public float pixel2logical_size(float l) {
+		return ZoomableContextUtil.pixel2logical_size(l, true, this.fitToWindowFactor * this.zoomFactor);
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public float pixel2logical_x(float p) {
+    	return ZoomableContextUtil.pixel2logical_x(p,
+    			true, this.flipX,
+    			getDrawingAreaRect(),
+    			(float)((this.translationToCenter==null) ? 0 : this.translationToCenter.getWidth()),
+    			this.fitToWindowFactor * this.zoomFactor);
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public float pixel2logical_y(float p) {
+    	return ZoomableContextUtil.pixel2logical_y(p,
+    			true, this.flipY,
+    			getDrawingAreaRect(),
+    			(float)((this.translationToCenter==null) ? 0 : this.translationToCenter.getHeight()),
+    			this.fitToWindowFactor * this.zoomFactor);
+    }
+    
+	/**
+	 * Translate the given shape from the logical coordinate space
+	 * to the screen coordinate space.
+	 * 
+	 * @param rLogic
+	 * @return the translation of rLogic
+	 */
+	public <T extends Shape> T logical2pixel(T rLogic) {
+		float deltaX, deltaY;
+		if (this.translationToCenter!=null) {
+			deltaX = (float)this.translationToCenter.getWidth();
+			deltaY = (float)this.translationToCenter.getHeight();
+		}
+		else {
+			deltaX = deltaY = 0f;
+		}
+		return ZoomableContextUtil.logical2pixel(
+				rLogic, true, this.flipX, this.flipY,
+				getDrawingAreaRect(),
+				deltaX, deltaY,
+				getScalingFactor());
+	}
+
+	/**
+	 * Translate the given font from the logical coordinate space
+	 * to the screen coordinate space.
+	 * 
+	 * @param fLogic
+	 * @return the translation of fLogic
+	 */
+	public Font logical2pixel(Font fLogic) {
+		return ZoomableContextUtil.logical2pixel(
+				fLogic, true, getScalingFactor());
+	}
+
+	/**
+	 * Translate the given shape from the screen coordinate space
+	 * to the logical coordinate space.
+	 * 
+	 * @param rScreen
+	 * @return the translation of rScreen
+	 */
+	public <T extends Shape> T pixel2logical(T rScreen) {
+		float deltaX, deltaY;
+		if (this.translationToCenter!=null) {
+			deltaX = (float)this.translationToCenter.getWidth();
+			deltaY = (float)this.translationToCenter.getHeight();
+		}
+		else {
+			deltaX = deltaY = 0f;
+		}
+		return ZoomableContextUtil.pixel2logical(
+				rScreen, true, this.flipX, this.flipY,
+				getDrawingAreaRect(),
+				deltaX, deltaY,
+				getScalingFactor());
+	}
+	
+	/**
+	 * Translate the given font from the screen coordinate space
+	 * to the logical coordinate space.
+	 * 
+	 * @param f_screen
+	 * @return the translation of f_screen
+	 */
+	public Font pixel2logical(Font f_screen) {
+		return ZoomableContextUtil.pixel2logical(
+				f_screen, true,
+				getScalingFactor());
+	}
+
+	/**
+	 * Replies the translation to center the view.
+	 * 
+	 * @return the translation.
+	 */
+	public float getTranslationToCenterX() {
+		return (float)((this.translationToCenter!=null) ? this.translationToCenter.getWidth() : 0f);
+	}
+
+	/**
+	 * Replies the translation to center the view.
+	 * 
+	 * @return the translation.
+	 */
+	public float getTranslationToCenterY() {
+		return (float)((this.translationToCenter!=null) ? this.translationToCenter.getHeight() : 0f);
+	}
+
+	/** Translates the specified screen rectangle
+	 *  into the logical rectangle.
+	 *
+	 * @param r_screen is the rectangle in the screen space.
+	 * @return a rectangle into the logical space.
+	 */
+	public Rectangle2D pixel2logical(Rectangle2D r_screen) {
+		return new Rectangle2D.Double(
+				pixel2logical_x((float)r_screen.getX()),
+				pixel2logical_y((float)r_screen.getY()),
+				pixel2logical_size((float)r_screen.getWidth()),
+				pixel2logical_size((float)r_screen.getHeight()));
+	}
+
+	/** Translates the specified logical rectangle
+	 *  into the screen rectangle.
+	 *
+	 * @param r_logic is the rectangle in the logical space.
+	 * @return a rectangle into the screen space.
+	 */
+	public Rectangle2D logical2pixel(Rectangle2D r_logic) {
+		return new Rectangle2D.Double(
+				logical2pixel_x((float)r_logic.getX()),
+				logical2pixel_y((float)r_logic.getY()),
+				logical2pixel_size((float)r_logic.getWidth()),
+				logical2pixel_size((float)r_logic.getHeight()));
+	}
+
+	//-------------------------------------------------------------------
+    // Document
+    //-------------------------------------------------------------------
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public Rectangle2D getDocumentRect() {
+    	ZoomablePanel zoompanel = this.zoomPanel.get();
+    	if (zoompanel==null) return null;
+    	return zoompanel.getDocumentRect();
+    }
+		
+    //-------------------------------------------------------------------
+    // Zoom
+    //-------------------------------------------------------------------
+    
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public void setScalingFactor(float factor) {
+    	setScalingFactor(factor,true);
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public void setScalingFactor(float factor, boolean enable_repaint) {
+    	if (factor>0 && this.zoomFactor!=factor) {
+    		float old = this.zoomFactor;
+    		this.zoomFactor = factor;
+    		
+    		Rectangle2D screen_window = getBounds();    		
+    		Rectangle2D document_bounds = getDocumentRect();
+    		Point2f oldFocus = this.focusPoint;
+
+    		if (this.focusPoint==null) {
+    			this.focusPoint = new Point2f();
+        		if (document_bounds==null) {
+        			this.focusPoint.set(
+	        				pixel2logical_x((float)screen_window.getCenterX()),
+	        				pixel2logical_y((float)screen_window.getCenterY()));        			
+        		}
+        		else {
+        			this.focusPoint.set(
+	        				(float)document_bounds.getCenterX(),
+	        				(float)document_bounds.getCenterY());
+        		}
+    		}
+
+    		if (enable_repaint) {
+    			FloatDimension doc_bounds = new FloatDimension();
+	    		
+	    		if (document_bounds==null) {
+		    		doc_bounds.setSize(
+		    				screen_window.getWidth(),
+		    				screen_window.getHeight());
+	    		}
+	    		else {
+		    		doc_bounds.setSize(
+		    				logical2pixel_size((float)document_bounds.getWidth()),
+		    				logical2pixel_size((float)document_bounds.getHeight()));
+	    		}
+	
+	    		changeWorkspaceSize(document_bounds);
+	    		if ((doc_bounds.getWidth()<=screen_window.getWidth())&&
+	        	    (doc_bounds.getHeight()<=screen_window.getHeight()))
+	    			repaint();
+    		}
+    		
+    		this.zoomPanel.get().firePropertyChange("zoomFactor", old, this.zoomFactor); //$NON-NLS-1$
+    		if (!this.focusPoint.equals(oldFocus)) {
+    			this.zoomPanel.get().firePropertyChange("targetPoint", old, this.focusPoint); //$NON-NLS-1$
+    		}
+    	}
+    }
+	
+	/** Change the zooming factor to have the specified
+	 * ratio between 1 pixel and 1 unit in the document.
+	 * <p>
+	 * Each unit from the displayed document will
+	 * have the same graphical size as the amount of 
+	 * pixels specified by the <var>ratio</var>.
+	 * 
+	 * @param ratio 
+	 */
+	public void setZoomFactorForPixelRatio(float ratio) {
+		float onePixel = pixel2logical_size(1);
+		float factor = onePixel * ratio;
+		setScalingFactor(factor);
+	}
+    
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public void zoomIn() {
+    	zoomIn(true);
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public void zoomOut() {
+    	zoomOut(true);
+    }
+    
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public void zoomIn(boolean enable_repaint) {
+    	if (this.zoomFactorStep>0) {
+    		setScalingFactor( this.zoomFactor * this.zoomFactorStep, enable_repaint ) ;
+    	}
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public void zoomOut(boolean enable_repaint) {
+    	if (this.zoomFactorStep>0) {
+    		setScalingFactor( this.zoomFactor / this.zoomFactorStep, enable_repaint ) ;
+    	}
+    }
+    
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public void refreshPanelContent() {
+    	setScalingFactor(this.zoomFactor, true);
+    }
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float getMaxScalingFactor() {
+		return this.maxScaleFactor;
+	}
+
+	/** Set the maximal scaling factor allowing in the view
+	 * 
+	 * @param factor is the maximal scaling factor.
+	 */
+	public void setMaxScalingFactor(float factor) {
+		if (factor>0f && factor!=this.maxScaleFactor) {
+			this.maxScaleFactor = factor;
+			if (this.minScaleFactor>this.maxScaleFactor)
+				this.minScaleFactor = this.maxScaleFactor;
+			if (this.zoomFactor>this.maxScaleFactor)
+				this.zoomFactor = this.maxScaleFactor;
+			repaint();
+		}
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public float getMinScalingFactor() {
+		return this.minScaleFactor;
+	}
+
+	/** Set the minimal scaling factor allowing in the view
+	 * 
+	 * @param factor is the minimal scaling factor.
+	 */
+	public void setMinScalingFactor(float factor) {
+		if (factor>0f && factor!=this.minScaleFactor) {
+			this.minScaleFactor = factor;
+			if (this.maxScaleFactor<this.minScaleFactor)
+				this.maxScaleFactor = this.minScaleFactor;
+			if (this.zoomFactor<this.minScaleFactor)
+				this.zoomFactor = this.minScaleFactor;
+			repaint();
+		}
+	}
+
+	@Override
+	public float getScalingSensitivity() {
+		return this.zoomFactorStep;
+	}
+
+	@Override
+	public float getFocusX() {
+		if (this.focusPoint==null) return Float.NaN;
+		return this.focusPoint.getX();
+	}
+
+	@Override
+	public float getFocusY() {
+		if (this.focusPoint==null) return Float.NaN;
+		return this.focusPoint.getY();
+	}
+    
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/ScrollingMethod.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/ScrollingMethod.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/ScrollingMethod.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,92 @@
+/* $Id$
+ * 
+ * This is a part of NetEditor project from Arakhne.org:
+ * package org.arakhne.neteditor.zoompanel.
+ * 
+ * Copyright (C) 2002  St&eacute;phane GALLAND, Mahdi Hannoun
+ * Copyright (C) 2004-2012  St&eacute;phane GALLAND
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.swing.zoompanel;
+
+import java.awt.event.MouseEvent;
+
+import javax.swing.SwingUtilities;
+
+/** This enumeration contains all the methods to start
+ * scrolling.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public enum ScrollingMethod {
+
+	/** Scroll on middle mouse press.
+	 */
+	MIDDLE_BUTTON {
+		@Override
+		void tryScroll(MouseEvent me, ScrollingMethodListener listener) {
+			if (SwingUtilities.isMiddleMouseButton(me)) {
+				listener.startScolling(me, 0);
+			}
+		}
+	},
+	/** Scroll on left mouse press.
+	 */
+	LEFT_BUTTON {
+		@Override
+		void tryScroll(MouseEvent me, ScrollingMethodListener listener) {
+			if (SwingUtilities.isLeftMouseButton(me)) {
+				listener.startScolling(me, 500);
+			}
+		}
+	};
+	
+	/** Start to scroll if possible.
+	 * 
+	 * @param me is the mouse event.
+	 * @param listener is the listener on the scrolling action.
+	 */
+	abstract void tryScroll(MouseEvent me, ScrollingMethodListener listener);
+
+	/** Listener on scrolling start.
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	interface ScrollingMethodListener {
+
+		/** Start to scroll.
+		 * 
+		 * @param event
+		 * @param delay is the delay (ms) to wait before starting to scroll.
+		 */
+		public void startScolling(MouseEvent event, int delay);
+		
+		/** Stop to scroll.
+		 * 
+		 * @param event
+		 */
+		public void stopScolling(MouseEvent event);
+
+	}
+	
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/ZoomableGraphics2D.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/ZoomableGraphics2D.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/ZoomableGraphics2D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,2166 @@
+/* $Id$
+ * 
+ * This is a part of NetEditor project from Arakhne.org:
+ * package org.arakhne.neteditor.zoompanel.
+ * 
+ * Copyright (C) 2002  St&eacute;phane GALLAND, Mahdi Hannoun
+ * Copyright (C) 2004-2013  St&eacute;phane GALLAND
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.swing.zoompanel;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.font.GlyphVector;
+import java.awt.font.TextAttribute;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Dimension2D;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Line2D;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RoundRectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ImageObserver;
+import java.awt.image.RenderedImage;
+import java.awt.image.renderable.RenderableImage;
+import java.text.AttributedCharacterIterator;
+
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.plaf.FontUIResource;
+
+import org.arakhne.afc.ui.Graphics2DLOD;
+import org.arakhne.afc.ui.StringAnchor;
+import org.arakhne.afc.ui.TextAlignment;
+import org.arakhne.afc.ui.ZoomableContext;
+import org.arakhne.afc.ui.awt.AbstractLODGraphics2D;
+import org.arakhne.afc.ui.awt.AwtUtil;
+import org.arakhne.afc.ui.awt.DoubleDimension;
+import org.arakhne.afc.ui.awt.VirtualScreenGraphics2D;
+import org.arakhne.afc.ui.awt.ZoomableContextUtil;
+
+/** This graphic context permits to display
+ *  something with a good zoom look.
+ *  <p>
+ *  This class was strongly inspirated by
+ *  the zoomable graphics context of 
+ *  <a href="http://www.arakhne.org/rubriques.php3?id_rubrique=15";>Arakhn&ecirc; NetEditor</a>.
+ *  with the authorization of the original authors.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class ZoomableGraphics2D extends AbstractLODGraphics2D<Graphics2D> implements VirtualScreenGraphics2D {
+
+	////////////////////////////////////////////////////////////
+	// Constants
+	
+	/** Indicates if by default the {@link ZoomableGraphics2D} enables zooming.
+	 */
+	public static boolean DEFAULT_ZOOM = true;
+
+	/** Indicates if by default the {@link ZoomableGraphics2D} enables image scaling.
+	 */
+	public static boolean DEFAULT_IMAGE_SCALING = true;
+
+	/** Indicates if by default the {@link ZoomableGraphics2D} enables icon scaling.
+	 */
+	public static boolean DEFAULT_ICON_SCALING = false;
+
+	/** Indicates if by default the {@link ZoomableGraphics2D} enables icon centering.
+	 */
+	public static boolean DEFAULT_ICON_CENTERING = true;
+	
+	/** Default scaling factor applied to the application's font
+	 * to obtain the default size for the fonts inside the zoomable panel.
+	 */
+	public static float DEFAULT_FONT_SCALE_FACTOR = .8f;
+
+	////////////////////////////////////////////////////////////
+	// Attributes
+
+	/** Indicates if the X axis be be inverted.
+	 */
+	protected final boolean flipX;
+
+	/** Indicates if the Y axis be be inverted.
+	 */
+	protected final boolean flipY;
+	
+	/** Is the rectangle covered by the document in the screen space.
+	 */
+	protected final Rectangle2D documentScreenRectangle;
+		
+	/** This is the zoom factor.
+	 */
+	protected final float zoomFactor;
+
+	/** Scale factor to fit the document into a screen.
+	 */
+	protected final float fitToWindowFactor;
+
+	/** Translation to center the logical space.
+	 */
+	protected final float deltaX;
+
+	/** Translation to center the logical space.
+	 */
+	protected final float deltaY;
+
+	/** Scaling sensitivity.
+	 */
+	private final float scalingSensitivity;
+
+	/** Min scaling factor.
+	 */
+	private final float minScalingFactor;
+
+	/** Max scaling factor.
+	 */
+	private final float maxScalingFactor;
+
+	/** Focus point.
+	 */
+	private final float focusX;
+
+	/** Focus point.
+	 */
+	private final float focusY;
+
+	/** Does the zoom feature was activated?
+	 */
+	protected boolean canZoom = DEFAULT_ZOOM;
+
+	/** Indicates if drawn images must be scaled or not.
+	 */
+	private boolean scaleImage = DEFAULT_IMAGE_SCALING;
+	
+	/** Indicates if drawn icons must be scaled or not.
+	 */
+	private boolean scaleIcon = DEFAULT_ICON_SCALING;
+
+	/** Indicates if drawn icons must be center on the painting coordinates or not.
+	 */
+	private boolean centerIcon = DEFAULT_ICON_CENTERING;
+
+	private final float defaultFontSize;
+	private final Rectangle2D tmpRectangle = new Rectangle2D.Double();
+
+	////////////////////////////////////////////////////////////
+	// Constructor
+
+	/** Construct a new ZoomableGraphics2D.
+	 *
+	 * @param target is the graphics in which draws will be done.
+	 * @param target_component is the target component (used to display an icon).
+	 * @param zoom is the zoom factor.
+	 * @param fit_to_window_factor is the factor used to scale the map coordinates.
+	 * @param delta_x is the translation length to center the logical model inside the screen space
+	 * @param delta_y is the translation length to center the logical model inside the screen space
+	 * @param antialiasing permits to force the anti-aliasing flag for the target graphical context
+	 * @param is_for_printing indicates if this graphics environment is for printing or not.
+	 * @param lod indicates the desired LOD used by this graphical context.
+	 * @param flipX indicates if the X axis may be inverted or not.
+	 * @param flipY indicates if the Y axis may be inverted or not.
+	 * @param documentScreenRectangle is the rectangle covered by the document in the screen space.
+	 * @param scalingSensitivity is the sensitivity of the zooming functions.
+	 * @param minScalingFactor is the minimal scaling factor.
+	 * @param maxScalingFactor is the maximal scaling factor.
+	 * @param focusX is the X coordinate of the point that is at the center of the view.
+	 * @param focusY is the Y coordinate of the point that is at the center of the view.
+	 */
+	public ZoomableGraphics2D(Graphics2D target, Component target_component, float zoom, float fit_to_window_factor, float delta_x, float delta_y, boolean antialiasing, boolean is_for_printing, Graphics2DLOD lod, boolean flipX, boolean flipY, Rectangle2D documentScreenRectangle,
+			float scalingSensitivity, float minScalingFactor, float maxScalingFactor,
+			float focusX, float focusY) {
+		super(target, target_component, antialiasing, is_for_printing, lod);
+		this.scalingSensitivity = scalingSensitivity;
+		this.minScalingFactor = minScalingFactor;
+		this.maxScalingFactor = maxScalingFactor;
+		this.focusX = focusX;
+		this.focusY = focusY;
+		this.defaultFontSize = DEFAULT_FONT_SCALE_FACTOR * target.getFont().getSize2D();
+		this.flipX = flipX;
+		this.flipY = flipY;
+		this.documentScreenRectangle = documentScreenRectangle;
+		this.zoomFactor = zoom;
+		this.fitToWindowFactor = fit_to_window_factor;
+		this.deltaX = delta_x;
+		this.deltaY = delta_y;
+		if (canZoom()) {
+			this.target.setFont(ZoomableContextUtil.logical2pixel(
+					getOriginalUnscaledFont(),
+					canZoom(),
+					this.fitToWindowFactor * this.zoomFactor));
+		}
+	}
+
+	/** Construct a new ZoomableGraphics2D.
+	 *
+	 * @param target is the graphics in which draws will be done.
+	 * @param target_component is the target component (used to display an icon).
+	 * @param zoom is the zoom factor.
+	 * @param fit_to_window_factor is the factor used to scale the map coordinates.
+	 * @param delta_x is the translation length to center the logical model inside the screen space
+	 * @param delta_y is the translation length to center the logical model inside the screen space
+	 * @param antialiasing permits to force the anti-aliasing flag for the target graphical context
+	 * @param lod indicates the desired LOD used by this graphical context.
+	 * @param flipX indicates if the X axis may be inverted or not.
+	 * @param flipY indicates if the Y axis may be inverted or not.
+	 * @param documentScreenRectangle is the rectangle covered by the document in the screen space.
+	 * @param scalingSensitivity is the sensitivity of the zooming functions.
+	 * @param minScalingFactor is the minimal scaling factor.
+	 * @param maxScalingFactor is the maximal scaling factor.
+	 * @param focusX is the X coordinate of the point that is at the center of the view.
+	 * @param focusY is the Y coordinate of the point that is at the center of the view.
+	 */
+	public ZoomableGraphics2D(Graphics2D target, Component target_component, float zoom, float fit_to_window_factor, float delta_x, float delta_y, boolean antialiasing, Graphics2DLOD lod, boolean flipX, boolean flipY, Rectangle2D documentScreenRectangle,
+			float scalingSensitivity, float minScalingFactor, float maxScalingFactor,
+			float focusX, float focusY) {
+		super(target, target_component, antialiasing, lod);
+		this.scalingSensitivity = scalingSensitivity;
+		this.minScalingFactor = minScalingFactor;
+		this.maxScalingFactor = maxScalingFactor;
+		this.focusX = focusX;
+		this.focusY = focusY;
+		this.defaultFontSize = getFont().getSize2D();
+		this.flipX = flipX;
+		this.flipY = flipY;
+		this.documentScreenRectangle = documentScreenRectangle;
+		this.zoomFactor = zoom;
+		this.fitToWindowFactor = fit_to_window_factor;
+		this.deltaX = delta_x;
+		this.deltaY = delta_y;
+		if (canZoom()) {
+			this.target.setFont(ZoomableContextUtil.logical2pixel(
+					getOriginalUnscaledFont(),
+					canZoom(),
+					this.fitToWindowFactor * this.zoomFactor));
+		}
+	}
+
+	/** Construct a new ZoomableGraphics2D.
+	 *
+	 * @param target is the graphics in which draws will be done.
+	 * @param target_component is the target component (used to display an icon).
+	 * @param zoom is the zoom factor.
+	 * @param fit_to_window_factor is the factor used to scale the map coordinates.
+	 * @param delta_x is the translation length to center the logical model inside the screen space
+	 * @param delta_y is the translation length to center the logical model inside the screen space
+	 * @param lod indicates the desired LOD used by this graphical context.
+	 * @param flipX indicates if the X axis may be inverted or not.
+	 * @param flipY indicates if the Y axis may be inverted or not.
+	 * @param documentScreenRectangle is the rectangle covered by the document in the screen space.
+	 * @param scalingSensitivity is the sensitivity of the zooming functions.
+	 * @param minScalingFactor is the minimal scaling factor.
+	 * @param maxScalingFactor is the maximal scaling factor.
+	 * @param focusX is the X coordinate of the point that is at the center of the view.
+	 * @param focusY is the Y coordinate of the point that is at the center of the view.
+	 */
+	public ZoomableGraphics2D(Graphics2D target, Component target_component, float zoom, float fit_to_window_factor, float delta_x, float delta_y, Graphics2DLOD lod, boolean flipX, boolean flipY, Rectangle2D documentScreenRectangle,
+			float scalingSensitivity, float minScalingFactor, float maxScalingFactor,
+			float focusX, float focusY) {
+		super(target, target_component, lod);
+		this.scalingSensitivity = scalingSensitivity;
+		this.minScalingFactor = minScalingFactor;
+		this.maxScalingFactor = maxScalingFactor;
+		this.focusX = focusX;
+		this.focusY = focusY;
+		this.defaultFontSize = getFont().getSize2D();
+		this.flipX = flipX;
+		this.flipY = flipY;
+		this.documentScreenRectangle = documentScreenRectangle;
+		this.zoomFactor = zoom;
+		this.fitToWindowFactor = fit_to_window_factor;
+		this.deltaX = delta_x;
+		this.deltaY = delta_y;
+		if (canZoom()) {
+			this.target.setFont(ZoomableContextUtil.logical2pixel(
+					getOriginalUnscaledFont(),
+					canZoom(),
+					this.fitToWindowFactor * this.zoomFactor));
+		}
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected float polyX(float x) {
+		return logical2pixel_x(x);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected float polyY(float y) {
+		return logical2pixel_y(y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final StringAnchor getStringAnchor() {
+		return StringAnchor.UPPER_LEFT;
+	}
+	
+    /** Replies if this figure could drawn or not.
+     * <p>
+     * This function assumes that a drawable figure
+     * must have a bounding box with its width and
+     * height greaters or equals to a specified size.
+     * This size depends on the adviced level of details:
+     * {@link Graphics2DLOD#LOW_LEVEL_OF_DETAIL},
+     * {@link Graphics2DLOD#NORMAL_LEVEL_OF_DETAIL}
+     * or {@link Graphics2DLOD#HIGH_LEVEL_OF_DETAIL}.
+     *
+     * @param rect is the rect to draw.
+     * @return <code>true</code> if the figure is drawable, otherwhise
+     * <code>false</code>.
+     */ 
+    @Override
+	public boolean hit(Rectangle2D rect) {
+    	return super.hit(logical2pixel(rect));
+    }
+
+    ////////////////////////////////////////////////////////////
+	// Graphics API
+
+	/** Creates the current graphics context.
+	 */
+	@Override
+	public Graphics create() {
+		ZoomableGraphics2D z = new ZoomableGraphics2D((Graphics2D)this.target.create(),
+				this.targetComponent.get(),this.zoomFactor,
+				this.fitToWindowFactor,this.deltaX,this.deltaY,
+				isAntiAliased(),
+				isPrinting(),
+				getLOD(),
+				this.flipX,
+				this.flipY,
+				this.documentScreenRectangle,
+				this.scalingSensitivity,
+				this.minScalingFactor,
+				this.maxScalingFactor,
+				this.focusX,
+				this.focusY) ;
+		z.setImageOriginalSize(isImageOriginalSize());
+		z.setIconCentered(isIconCentered());
+		z.setIconOriginalSize(isIconOriginalSize());
+		z.setZoom(canZoom());
+		return z;
+	}
+
+	/** Clears the specified rectangle by filling with the 
+	 *  background color.
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 *
+	 * @param x x coordinate of the rectangle
+	 * @param y y coordinate of the rectangle
+	 * @param width the width of the rectangle
+	 * @param height the height of the rectangle
+	 */
+	@Override
+	public void clearRect( float x, float y, float width, float height ) {
+		this.tmpRectangle.setRect(x, y, width, height);
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+		this.target.clearRect(
+				(int)this.tmpRectangle.getMinX(),
+				(int)this.tmpRectangle.getMinY(),
+				(int)this.tmpRectangle.getWidth(),
+				(int)this.tmpRectangle.getHeight());
+	}
+
+	/** Intersects the current clip with the specified rectangle.
+	 *  <p>
+	 *  The given coordinates will <em>not</em> be transformed.
+	 *
+	 * @param x x coordinate of the rectangle
+	 * @param y y coordinate of the rectangle
+	 * @param width the width of the rectangle
+	 * @param height the height of the rectangle	
+	 */
+	@Override
+	public void clipRect( float x, float y, float width, float height ) {
+		this.tmpRectangle.setRect(x, y, width, height);
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+		Rectangle2D c = new Rectangle2D.Double(
+				this.tmpRectangle.getMinX(),
+				this.tmpRectangle.getMinY(),
+				this.tmpRectangle.getWidth(),
+				this.tmpRectangle.getHeight());
+		this.target.clip(c);
+	}
+
+	/** Copies an area of the component by a distance of 
+	 *  specified <var>dx</var> and <var>dy</var>.
+	 *  <p>
+	 *  This method is not supported in a vectorial context.
+	 *
+	 * @param x x coordinate of the area
+	 * @param y y coordinate of the area
+	 * @param width the width of the area
+	 * @param height the height of the area
+	 * @param dx horizontal translation
+	 * @param dy vertical translation
+	 */
+	@Override
+	public void copyArea( float x, float y, float width, float height, float dx, float dy ) {
+		float z = this.fitToWindowFactor * this.zoomFactor;
+		this.tmpRectangle.setRect(x, y, width, height);
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, z);
+		this.target.copyArea(
+				(int)this.tmpRectangle.getMinX(),
+				(int)this.tmpRectangle.getMinY(),
+				(int)this.tmpRectangle.getWidth(),
+				(int)this.tmpRectangle.getHeight(),
+				(int)ZoomableContextUtil.logical2pixel_size(dx, canZoom(), z),
+				(int)ZoomableContextUtil.logical2pixel_size(dy, canZoom(), z)) ;
+	}
+
+	/** Draws the outline of an arc.
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 *
+	 * @param x x coordinate of the covered rectangle
+	 * @param y y coordinate of the covered rectangle
+	 * @param width the width of the covered rectangle
+	 * @param height the height of the covered rectangle
+	 * @param startAngle the starting angle
+	 * @param arcAngle the size of the arc
+	 */
+	@Override
+	public void drawArc( float x, float y, float width, float height, float startAngle, float arcAngle ) {
+		this.tmpRectangle.setRect(x, y, width, height);
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+		Arc2D arc = new Arc2D.Double(
+				this.tmpRectangle.getMinX(),
+				this.tmpRectangle.getMinY(),
+				this.tmpRectangle.getWidth(),
+				this.tmpRectangle.getHeight(),
+				startAngle,
+				arcAngle,
+				Arc2D.OPEN);
+		this.target.draw(arc);
+	}
+
+	/** Draws the specified image.
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 * <p>
+	 * Caution: this function does not support
+	 * the scaling of the image. So the given image
+	 * will be drawn with its size as in the screen-coordinate space.
+	 *
+	 * @param img the image
+	 * @param x horizontal location of the image
+	 * @param y vertical location of the image
+	 * @param bgColor the color of transparent pixels
+	 * @param obs the notified observer
+	 * @return <code>true</code> if theimage wascomplety drawn.
+	 * @see #setImageOriginalSize(boolean)
+	 * @see #isImageOriginalSize()
+	 */
+	@Override
+	public boolean drawImage( Image img, float x, float y, Color bgColor, ImageObserver obs ) {
+		int imgWidth = img.getWidth(obs);
+		if (imgWidth==-1) return false;
+		int imgHeight = img.getHeight(obs);
+		if (imgHeight==-1) return false;
+		
+		float z = this.fitToWindowFactor * this.zoomFactor;
+		Image drawableImage;
+		
+		if (this.scaleImage) {
+			this.tmpRectangle.setRect(x, y, imgWidth, imgHeight);
+		}
+		else {
+			float sx = ZoomableContextUtil.pixel2logical_size(imgWidth, canZoom(), z);
+			float sy = ZoomableContextUtil.pixel2logical_size(imgHeight, canZoom(), z);
+			this.tmpRectangle.setRect(x, y, sx, sy);
+		}
+
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, z);
+		
+		if (this.tmpRectangle.isEmpty()) return true;
+
+		if (this.scaleImage) {
+			int sx = (int)this.tmpRectangle.getWidth();
+			int sy = (int)this.tmpRectangle.getHeight();
+			drawableImage = new BufferedImage(sx, sy, BufferedImage.TYPE_INT_ARGB);
+			Graphics2D g = (Graphics2D)drawableImage.getGraphics();
+			if (!g.drawImage(img, 0, 0, sx, sy, 0, 0, imgWidth, imgHeight, obs)) {
+				g.dispose();
+				return false;
+			}
+			g.dispose();
+		}
+		else {
+			drawableImage = img;
+		}
+
+		int ix = (int)this.tmpRectangle.getMinX();
+		int iy = (int)this.tmpRectangle.getMinX();
+		
+		return this.target.drawImage(drawableImage, ix, iy, bgColor, obs);
+	}
+
+	/** Draws the specified image with its upper-left corner
+	 * at the given position.
+	 *  <p>
+	 *  The width and height of the given image
+	 *  is expressed according to the value
+	 *  replied by {@link #isImageOriginalSize()}.
+	 *  If {@link #isImageOriginalSize()} replies <code>true</code>
+	 *  the image width and height are assumed to be pixels. 
+	 *  If {@link #isImageOriginalSize()} replies <code>false</code>
+	 *  the image width and height are assumed to be meters. 
+	 *
+	 * @param img the image
+	 * @param x horizontal location of the image
+	 * @param y vertical location of the image
+	 * @param obs the notified observer
+	 * @return <code>true</code> if the image was complety drawn.
+	 * @see #setImageOriginalSize(boolean)
+	 * @see #isImageOriginalSize()
+	 * @see #isImageOriginalSize()
+	 */
+	@Override
+	public boolean drawImage( Image img, float x, float y, ImageObserver obs ) {
+		int imgWidth = img.getWidth(obs);
+		if (imgWidth==-1) return false;
+		int imgHeight = img.getHeight(obs);
+		if (imgHeight==-1) return false;
+		
+		float z = this.fitToWindowFactor * this.zoomFactor;
+		Image drawableImage;
+		
+		if (this.scaleImage) {
+			this.tmpRectangle.setRect(x, y, imgWidth, imgHeight);
+		}
+		else {
+			float sx = ZoomableContextUtil.pixel2logical_size(imgWidth, canZoom(), z);
+			float sy = ZoomableContextUtil.pixel2logical_size(imgHeight, canZoom(), z);
+			this.tmpRectangle.setRect(x, y, sx, sy);
+		}
+
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, z);
+		
+		if (this.tmpRectangle.isEmpty()) return true;
+
+		if (this.scaleImage) {
+			int sx = (int)this.tmpRectangle.getWidth();
+			int sy = (int)this.tmpRectangle.getHeight();
+			drawableImage = new BufferedImage(sx, sy, BufferedImage.TYPE_INT_ARGB);
+			Graphics2D g = (Graphics2D)drawableImage.getGraphics();
+			if (!g.drawImage(img, 0, 0, sx, sy, 0, 0, imgWidth, imgHeight, obs)) {
+				g.dispose();
+				return false;
+			}
+			g.dispose();
+		}
+		else {
+			drawableImage = img;
+		}
+
+		int ix = (int)this.tmpRectangle.getMinX();
+		int iy = (int)this.tmpRectangle.getMinY();
+		
+		return this.target.drawImage(drawableImage, ix, iy, obs);
+	}
+
+	/** Draws the specified image. Scales it if necessary.
+	 *
+	 * @param img the image
+	 * @param x horizontal location of the image
+	 * @param y vertical location of the image
+	 * @param width width of the image
+	 * @param height height of the image
+	 * @param bgColor the color of transparent pixels
+	 * @param obs the notified observer
+	 * @return <code>true</code> if theimage wascomplety drawn.
+	 * @see #setImageOriginalSize(boolean)
+	 * @see #isImageOriginalSize()
+	 */
+	@Override
+	public boolean drawImage( Image img, float x, float y, float width, float height, Color bgColor, ImageObserver obs ) {
+		this.tmpRectangle.setRect(x, y, width, height);
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+
+		int w, h;
+		if (this.scaleImage) {
+			w = (int)this.tmpRectangle.getWidth();
+			h = (int)this.tmpRectangle.getHeight();
+		}
+		else {
+			w = (int)width;
+			h = (int)height;
+		}
+
+		return this.target.drawImage( img, 
+				(int)this.tmpRectangle.getMinX(),
+				(int)this.tmpRectangle.getMinY(),
+				w, h,
+				bgColor, obs ) ;
+	}
+
+	/** Draws the specified image. Scales it if necessary.
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 *
+	 * @param img the image
+	 * @param x horizontal location of the image
+	 * @param y vertical location of the image
+	 * @param width width of the image
+	 * @param height height of the image
+	 * @param obs the notified observer
+	 * @return <code>true</code> if theimage wascomplety drawn.
+	 * @see #setImageOriginalSize(boolean)
+	 * @see #isImageOriginalSize()
+	 */
+	@Override
+	public boolean drawImage( Image img, float x, float y, float width, float height, ImageObserver obs ) {
+		this.tmpRectangle.setRect(x, y, width, height);
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+
+		int w, h;
+		if (this.scaleImage) {
+			w = (int)this.tmpRectangle.getWidth();
+			h = (int)this.tmpRectangle.getHeight();
+		}
+		else {
+			w = (int)width;
+			h = (int)height;
+		}
+
+		return this.target.drawImage( img, 
+				(int)this.tmpRectangle.getMinX(),
+				(int)this.tmpRectangle.getMinY(),
+				w, h,
+				obs ) ;
+	}
+
+	/** Draws a part of the specified image into
+	 *  the given rectangle. Scales it if necessary.
+	 *  <p>
+	 *  The given target coordinates will be transformed.
+	 *
+	 * @param img the image
+	 * @param dx1 horizontal location of the target image upper left corner
+	 * @param dy1 vertical location of the target image upper left corner
+	 * @param dx2 horizontal location of the target image lower right corner
+	 * @param dy2 vertical location of the target image lower right corner
+	 * @param sx1 horizontal location of the source image upper left corner
+	 * @param sy1 vertical location of the source image upper left corner
+	 * @param sx2 horizontal location of the source image lower right corner
+	 * @param sy2 vertical location of the source image lower right corner
+	 * @param bgColor the color of transparent pixels
+	 * @param obs the notified observer
+	 * @return <code>true</code> if theimage wascomplety drawn.
+	 * @see #setImageOriginalSize(boolean)
+	 * @see #isImageOriginalSize()
+	 */
+	@Override
+	public boolean drawImage( Image img, 
+			float dx1, float dy1, 
+			float dx2, float dy2,
+			int sx1, int sy1,
+			int sx2, int sy2,
+			Color bgColor,
+			ImageObserver obs ) {
+		this.tmpRectangle.setRect(Math.min(dx1, dx2), Math.min(dy1, dy2), Math.abs(dx2-dx1), Math.abs(dy2-dy1));
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+
+		return this.target.drawImage( img, 
+				(int)((dx1<=dx2) ? this.tmpRectangle.getMinX() : this.tmpRectangle.getMaxX()),
+				(int)((dx1<=dx2) ? this.tmpRectangle.getMinY() : this.tmpRectangle.getMaxY()), 
+				(int)((dx1<=dx2) ? this.tmpRectangle.getMaxX() : this.tmpRectangle.getMinX()),
+				(int)((dx1<=dx2) ? this.tmpRectangle.getMaxY() : this.tmpRectangle.getMinY()), 
+				sx1, sy1,
+				sx2, sy2,
+				bgColor, obs ) ;
+	}
+
+	/** Draws a part of the specified image into
+	 *  the given rectangle. Scales it if necessary.
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 *
+	 * @param img the image
+	 * @param dx1 horizontal location of the target image upper left corner
+	 * @param dy1 vertical location of the target image upper left corner
+	 * @param dx2 horizontal location of the target image lower right corner
+	 * @param dy2 vertical location of the target image lower right corner
+	 * @param sx1 horizontal location of the source image upper left corner
+	 * @param sy1 vertical location of the source image upper left corner
+	 * @param sx2 horizontal location of the source image lower right corner
+	 * @param sy2 vertical location of the source image lower right corner
+	 * @param obs the notified observer
+	 * @return <code>true</code> if theimage wascomplety drawn.
+	 * @see #setImageOriginalSize(boolean)
+	 * @see #isImageOriginalSize()
+	 */
+	@Override
+	public boolean drawImage( Image img, 
+			float dx1, float dy1, 
+			float dx2, float dy2,
+			int sx1, int sy1,
+			int sx2, int sy2,
+			ImageObserver obs ) {
+		this.tmpRectangle.setRect(Math.min(dx1, dx2), Math.min(dy1, dy2), Math.abs(dx2-dx1), Math.abs(dy2-dy1));
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+
+		return this.target.drawImage( img, 
+				(int)((dx1<=dx2) ? this.tmpRectangle.getMinX() : this.tmpRectangle.getMaxX()),
+				(int)((dx1<=dx2) ? this.tmpRectangle.getMinY() : this.tmpRectangle.getMaxY()), 
+				(int)((dx1<=dx2) ? this.tmpRectangle.getMaxX() : this.tmpRectangle.getMinX()),
+				(int)((dx1<=dx2) ? this.tmpRectangle.getMaxY() : this.tmpRectangle.getMinY()), 
+				sx1, sy1,
+				sx2, sy2,
+				obs ) ;
+	}
+
+	/** Draws an icon.
+	 *
+	 * @param icon is the icon to paint
+	 * @param x is the location where to paint the icon.
+	 * @param y is the location where to paint the icon.
+	 * @see #setIconOriginalSize(boolean)
+	 * @see #setIconCentered(boolean)
+	 */
+	public void drawIcon( Icon icon, float x, float y) {
+		float z = this.fitToWindowFactor * this.zoomFactor;
+		Icon drawableIcon;
+		
+		if (this.scaleIcon) {
+			this.tmpRectangle.setRect(x, y, icon.getIconWidth(), icon.getIconHeight());
+		}
+		else {
+			float sx = ZoomableContextUtil.pixel2logical_size(icon.getIconWidth(), canZoom(), z);
+			float sy = ZoomableContextUtil.pixel2logical_size(icon.getIconHeight(), canZoom(), z);
+			this.tmpRectangle.setRect(x, y, sx, sy);
+		}
+
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+		
+		if (this.tmpRectangle.isEmpty()) return;
+
+		if (this.scaleIcon && icon instanceof ImageIcon) {
+			Image img = AwtUtil.computeScaledImage(
+					(ImageIcon)icon, 
+					(int)this.tmpRectangle.getWidth(),
+					(int)this.tmpRectangle.getHeight(),
+					this.targetComponent.get());
+			drawableIcon = new ImageIcon(img);
+		}
+		else {
+			drawableIcon = icon;
+		}
+
+		int ix = (int)this.tmpRectangle.getMinX();
+		int iy = (int)this.tmpRectangle.getMinY();
+		if (this.centerIcon) {
+			ix -= drawableIcon.getIconWidth() / 2;
+			iy -= drawableIcon.getIconHeight() / 2;
+		}
+		
+		drawableIcon.paintIcon(
+				this.targetComponent.get(),
+				this.target, 
+				ix, iy);
+	}
+
+	/** Draws an icon.
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 *  <p>
+	 *  Icon is not scaled. Icon size is assumed to
+	 *  be pixel-based.
+	 *
+	 * @param icon is the icon to paint
+	 * @param x is the location where to paint the icon.
+	 * @param y is the location where to paint the icon.
+	 * @see #setIconOriginalSize(boolean)
+	 * @see #setIconCentered(boolean)
+	 */
+	public final void drawIcon( Icon icon, int x, int y ) {
+		drawIcon(icon, (float)x, (float)y);
+	}
+
+	/** Draws a line, using the current color, between
+	 *  (<var>x1</var>,<var>y1</var>) and 
+	 *  (<var>x2</var>,<var>y2</var>).
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 *
+	 * @param x1 horizontal location of the start point.
+	 * @param y1 vertical location of the start point.
+	 * @param x2 horizontal location of the target point.
+	 * @param y2 vertical location of the target point.
+	 */
+	@Override
+	public void drawLine( float x1, float y1, float x2, float y2 ) {
+		Line2D line = new Line2D.Double(
+				logical2pixel_x( x1 ), logical2pixel_y( y1 ),
+				logical2pixel_x( x2 ), logical2pixel_y( y2 ) ) ;
+		this.target.draw(line);
+	}
+
+	/** Draws an oval, using the current color, inside
+	 *  the specified rectangle.
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 *
+	 * @param x horizontal location of the enclosing rectangle.
+	 * @param y vertical location of the enclosing rectangle.
+	 * @param w width of the enclosing rectangle.
+	 * @param h height of the enclosing rectangle.
+	 */
+	@Override
+	public void drawOval( float x, float y, float w, float h ) {
+		this.tmpRectangle.setRect(x, y, w, h);
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+		Ellipse2D oval = new Ellipse2D.Double(
+				this.tmpRectangle.getMinX(),
+				this.tmpRectangle.getMinY(),
+				this.tmpRectangle.getWidth(),
+				this.tmpRectangle.getHeight()) ;
+		this.target.draw(oval);
+	}
+
+	/** Draws the outline of a rounded-rectangle.
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 *
+	 * @param x horizontal location of the rectangle.
+	 * @param y vertical location of the rectangle.
+	 * @param w width of the rectangle.
+	 * @param h height of the rectangle.
+	 * @param arcWidth width of the corners' angle.
+	 * @param arcHeight height of the corners' angle.
+	 */
+	@Override
+	public void drawRoundRect( float x, float y, float w, float h, float arcWidth, float arcHeight ) {
+		this.tmpRectangle.setRect(x, y, w, h);
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+		RoundRectangle2D r = new RoundRectangle2D.Double(
+				this.tmpRectangle.getMinX(),
+				this.tmpRectangle.getMinY(),
+				this.tmpRectangle.getWidth(),
+				this.tmpRectangle.getHeight(),
+				ZoomableContextUtil.logical2pixel_size(arcWidth, canZoom(), this.fitToWindowFactor*this.zoomFactor),
+				ZoomableContextUtil.logical2pixel_size(arcHeight, canZoom(), this.fitToWindowFactor*this.zoomFactor)) ;
+		this.target.draw(r);
+	}
+
+	/** Fills an arc.
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 *
+	 * @param x x coordinate of the covered rectangle
+	 * @param y y coordinate of the covered rectangle
+	 * @param width the width of the covered rectangle
+	 * @param height the height of the covered rectangle
+	 * @param startAngle the starting angle
+	 * @param arcAngle the size of the arc
+	 */
+	@Override
+	public void fillArc( float x, float y, float width, float height, float startAngle, float arcAngle ) {
+		this.tmpRectangle.setRect(x, y, width, height);
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+		Arc2D a = new Arc2D.Double(
+				this.tmpRectangle.getMinX(),
+				this.tmpRectangle.getMinY(),
+				this.tmpRectangle.getWidth(),
+				this.tmpRectangle.getHeight(),
+				startAngle, arcAngle,
+				Arc2D.OPEN);
+		this.target.fill(a);
+	}
+
+	/** fills an oval, using the current color, inside
+	 *  the specified rectangle.
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 *
+	 * @param x horizontal location of the enclosing rectangle.
+	 * @param y vertical location of the enclosing rectangle.
+	 * @param w width of the enclosing rectangle.
+	 * @param h height of the enclosing rectangle.
+	 */
+	@Override
+	public void fillOval( float x, float y, float w, float h ) {
+		this.tmpRectangle.setRect(x, y, w, h);
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+		Ellipse2D oval = new Ellipse2D.Double(
+				this.tmpRectangle.getMinX(),
+				this.tmpRectangle.getMinY(),
+				this.tmpRectangle.getWidth(),
+				this.tmpRectangle.getHeight());
+		this.target.fill(oval);
+	}
+
+	/** Fills a rectangle.
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 *
+	 * @param x horizontal location of the rectangle.
+	 * @param y vertical location of the rectangle.
+	 * @param w width of the rectangle.
+	 * @param h height of the rectangle.
+	 */
+	@Override
+	public void fillRect( float x, float y, float w, float h ) {
+		this.tmpRectangle.setRect(x, y, w, h);
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+		this.target.fill(this.tmpRectangle);
+	}
+
+	/** Draws a rectangle.
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 *
+	 * @param x horizontal location of the rectangle.
+	 * @param y vertical location of the rectangle.
+	 * @param w width of the rectangle.
+	 * @param h height of the rectangle.
+	 */
+	@Override
+	public final void drawRect( int x, int y, int w, int h ) {
+		drawRect((float)x, (float)y, (float)w, (float)h);
+	}
+
+	/** Draws a rectangle.
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 *
+	 * @param x horizontal location of the rectangle.
+	 * @param y vertical location of the rectangle.
+	 * @param w width of the rectangle.
+	 * @param h height of the rectangle.
+	 */
+	@Override
+	public void drawRect( float x, float y, float w, float h ) {
+		this.tmpRectangle.setRect(x, y, w, h);
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+		this.target.draw(this.tmpRectangle);
+	}
+
+	/** Fills a rounded-rectangle.
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 *
+	 * @param x horizontal location of the rectangle.
+	 * @param y vertical location of the rectangle.
+	 * @param w width of the rectangle.
+	 * @param h height of the rectangle.
+	 * @param arcWidth width of the corners' angle.
+	 * @param arcHeight height of the corners' angle.
+	 */
+	@Override
+	public void fillRoundRect( float x, float y, float w, float h, float arcWidth, float arcHeight ) {
+		this.tmpRectangle.setRect(x, y, w, h);
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+		RoundRectangle2D r = new RoundRectangle2D.Double(
+				this.tmpRectangle.getMinX(),
+				this.tmpRectangle.getMinY(),
+				this.tmpRectangle.getWidth(),
+				this.tmpRectangle.getHeight(),
+				ZoomableContextUtil.logical2pixel_size(arcWidth, canZoom(), this.fitToWindowFactor*this.zoomFactor),
+				ZoomableContextUtil.logical2pixel_size(arcWidth, canZoom(), this.fitToWindowFactor*this.zoomFactor));
+		this.target.fill(r);
+	}
+
+	/** Replies the current clipping area.
+	 *
+	 * @return the current clipping area.
+	 */
+	@Override
+	public Shape getClip() {
+		return pixel2logical( this.target.getClip() ) ;
+	}
+
+	/** Sets the current clipping area.
+	 *
+	 * @param x horizontal location of the rectangle.
+	 * @param y vertical location of the rectangle.
+	 * @param width is the width of the rectangle.
+	 * @param height is the height of the rectangle.
+	 */
+	@Override
+	public void setClip( float x, float y, float width, float height ) {
+		this.tmpRectangle.setRect(x, y, width, height);
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+		this.target.setClip((Shape)this.tmpRectangle.clone());
+	}
+
+	/** Sets the current clipping area.
+	 *
+	 * @param c the new clipping area that was copyed.
+	 */
+	@Override
+	public void setClip( Shape c ) {
+		this.target.setClip( logical2pixel( c ) ) ;
+	}
+
+	/** {@inheritDoc}
+	 * @see #setFont(Font, boolean)
+	 * @see #setPixelFont(Font)
+	 */
+	@Override
+	public final void setFont( Font f ) {
+		setFont(f, true);
+	}
+	
+	/** Set the current font.
+	 * 
+	 * @param f
+	 * @param enableScaling indicates if the scaling must be apply to the given font.
+	 * @since 4.1
+	 */
+	protected void setFont(Font f, boolean enableScaling) {
+		Font sf = f;
+		if (canZoom() && enableScaling) {
+			sf = ZoomableContextUtil.logical2pixel(
+					sf,
+					canZoom(),
+					this.fitToWindowFactor * this.zoomFactor);
+		}
+		this.target.setFont( sf ) ;
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public FontMetrics getFontMetrics(Font font) {
+		return this.target.getFontMetrics( font ) ;
+	}
+
+	/** {@inheritDoc}
+	 * @see #getPixelFont()
+	 */
+	@Override
+	public Font getFont() {
+		Font font = this.target.getFont();
+		if (canZoom()) {
+			font = ZoomableContextUtil.pixel2logical(
+					font,
+					canZoom(),
+					this.fitToWindowFactor * this.zoomFactor);
+			font = new FontUIResource(font);
+		}
+		return font;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public FontMetrics getFontMetrics() {
+		return this.target.getFontMetrics(getFont());
+	}
+
+	////////////////////////////////////////////////////////////
+	// Graphics2D API
+
+	/** Intersects the current clip with the specified rectangle.
+	 *  <p>
+	 *  The given coordinates will <em>not</em> be transformed.
+	 *
+	 * @param area the clipping area used to intersect.
+	 */
+	@Override
+	public void clip( Shape area ) {
+		this.target.clip( logical2pixel( area ) );
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	protected void drawPoint(float x, float y, float pointSizeInPixel) {
+		super.drawPoint(x, y, pixel2logical_size(pointSizeInPixel));
+	}
+
+	/** Strokes the outline of a specified shape.
+	 *
+	 * @param s the shape to draw.
+	 */
+	@Override
+	public void draw( Shape s ) {
+		this.target.draw( logical2pixel( s ) ) ;
+	}
+	
+	@Override
+	public void draw(PathIterator s) {
+		this.target.draw( ZoomableContextUtil.logical2pixel(
+				s, canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle,
+				this.deltaX, this.deltaY, 
+				this.fitToWindowFactor * this.zoomFactor) );
+	}
+
+	/** Renders the text of the specified glyph.
+	 *
+	 * @param glyph the glyph to display.
+	 * @param x horizontal location of the text.
+	 * @param y vertical location of the text.
+	 */
+	@Override
+	public void drawGlyphVector( GlyphVector glyph, float x, float y ) {
+		if (glyph.getNumGlyphs()>0) {
+			float z = this.zoomFactor*this.fitToWindowFactor;
+			Rectangle2D bounds = glyph.getPixelBounds(glyph.getFontRenderContext(), x, y);
+			this.tmpRectangle.setRect(bounds);
+			ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+					canZoom(), this.flipX, this.flipY,
+					this.documentScreenRectangle, this.deltaX,
+					this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+			AffineTransform o = getTransform();
+			this.target.scale(z,z) ;
+			this.target.drawGlyphVector(glyph,
+					(float)(this.tmpRectangle.getMinX() + this.tmpRectangle.getWidth()),
+					(float)(this.tmpRectangle.getMinY() + this.tmpRectangle.getHeight())) ; 
+			this.target.setTransform(o) ;
+		}
+	}
+
+	/** Draws the specified image.
+	 *
+	 * @param img the image to draw.
+	 * @param op the filter to be applied to the image before rendering.
+	 * @param x horizontal location of the image.
+	 * @param y vertical location of the image.
+	 * @see #setImageOriginalSize(boolean)
+	 * @see #isImageOriginalSize()
+	 */
+	@Override
+	public void drawImage( BufferedImage img, BufferedImageOp op, float x, float y ) {
+		float z = this.fitToWindowFactor * this.zoomFactor;
+		BufferedImage drawableImage;
+		
+		if (this.scaleImage) {
+			this.tmpRectangle.setRect(x, y, img.getWidth(), img.getHeight());
+		}
+		else {
+			float sx = ZoomableContextUtil.pixel2logical_size(img.getWidth(), canZoom(), z);
+			float sy = ZoomableContextUtil.pixel2logical_size(img.getHeight(), canZoom(), z);
+			this.tmpRectangle.setRect(x, y, sx, sy);
+		}
+
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, z);
+		
+		if (this.tmpRectangle.isEmpty()) return;
+
+		if (this.scaleImage) {
+			int sx = (int)this.tmpRectangle.getWidth();
+			int sy = (int)this.tmpRectangle.getHeight();
+			drawableImage = new BufferedImage(sx, sy, img.getType());
+			Graphics2D g = (Graphics2D)drawableImage.getGraphics();
+			g.drawImage(img, 0, 0, sx, sy, 0, 0, img.getWidth(), img.getHeight(), null);
+			g.dispose();
+		}
+		else {
+			drawableImage = img;
+		}
+
+		int ix = (int)this.tmpRectangle.getMinX();
+		int iy = (int)this.tmpRectangle.getMinY();
+		
+		this.target.drawImage(drawableImage, op, ix, iy);
+	}
+
+	/** Draws the given image.
+	 *
+	 * @param img the image to draw.
+	 * @param xform the affine transformation to apply.
+	 * @param obs the notified observer.
+	 * @return <code>true</code> if the image was completly rendered.
+	 * @see #setImageOriginalSize(boolean)
+	 * @see #isImageOriginalSize()
+	 */
+	@Override
+	public boolean drawImage( Image img, AffineTransform xform, ImageObserver obs ) {
+		AffineTransform o = getTransform();
+		float z = this.zoomFactor * this.fitToWindowFactor;
+		float translateX = logical2pixel_x((float)xform.getTranslateX());
+		float translateY = logical2pixel_y((float)xform.getTranslateY());
+
+		AffineTransform yform = new AffineTransform(
+				xform.getScaleX(),xform.getShearY(),
+				xform.getShearX(),xform.getScaleY(),
+				xform.getTranslateX()+translateX,xform.getTranslateY()+translateY);
+
+		if ((canZoom())&&(this.scaleImage)) {
+			yform.scale(z, z);
+		}
+
+		if (this.flipX || this.flipY) {
+			float cx = (float)this.documentScreenRectangle.getCenterX();
+			float cy = (float)this.documentScreenRectangle.getCenterY();
+			yform.translate(-cx, -cy);
+			yform.scale(this.flipX ? -1. : 1., this.flipY ? -1. : 1.);
+			yform.translate(cx,cy);
+		}
+		
+		boolean b = this.target.drawImage( img, yform, obs ) ;
+		setTransform(o);
+		return b;
+	}
+
+	/** Renders a RenderableImage.
+	 *
+	 * @param img the image to draw.
+	 * @param xform the affine transformation to apply.
+	 * @see #setImageOriginalSize(boolean)
+	 * @see #isImageOriginalSize()
+	 */
+	@Override
+	public void drawRenderableImage( RenderableImage img, AffineTransform xform ) {
+		AffineTransform o = getTransform();
+		float z = this.zoomFactor * this.fitToWindowFactor;
+		float translateX = logical2pixel_x((float)xform.getTranslateX());
+		float translateY = logical2pixel_y((float)xform.getTranslateY());
+
+		AffineTransform yform = new AffineTransform(
+				xform.getScaleX(),xform.getShearY(),
+				xform.getShearX(),xform.getScaleY(),
+				xform.getTranslateX()+translateX,xform.getTranslateY()+translateY);
+
+		if ((canZoom())&&(this.scaleImage)) {
+			yform.scale(z, z);
+		}
+
+		if (this.flipX || this.flipY) {
+			float cx = (float)this.documentScreenRectangle.getCenterX();
+			float cy = (float)this.documentScreenRectangle.getCenterY();
+			yform.translate(-cx, -cy);
+			yform.scale(this.flipX ? -1. : 1., this.flipY ? -1. : 1.);
+			yform.translate(cx,cy);
+		}
+		
+		this.target.drawRenderableImage( img, yform ) ;
+		setTransform(o);
+	}
+
+	/** Renders a RenderedImage.
+	 *
+	 * @param img the image to draw.
+	 * @param xform the affine transformation to apply.
+	 * @see #setImageOriginalSize(boolean)
+	 * @see #isImageOriginalSize()
+	 */
+	@Override
+	public void drawRenderedImage( RenderedImage img, AffineTransform xform ) {
+		AffineTransform o = getTransform();
+		float z = this.zoomFactor * this.fitToWindowFactor;
+		float translateX = logical2pixel_x((float)xform.getTranslateX());
+		float translateY = logical2pixel_y((float)xform.getTranslateY());
+
+		AffineTransform yform = new AffineTransform(
+				xform.getScaleX(),xform.getShearY(),
+				xform.getShearX(),xform.getScaleY(),
+				xform.getTranslateX()+translateX,xform.getTranslateY()+translateY);
+
+		if ((canZoom())&&(this.scaleImage)) {
+			yform.scale(z, z);
+		}
+
+		if (this.flipX || this.flipY) {
+			float cx = (float)this.documentScreenRectangle.getCenterX();
+			float cy = (float)this.documentScreenRectangle.getCenterY();
+			yform.translate(-cx, -cy);
+			yform.scale(this.flipX ? -1. : 1., this.flipY ? -1. : 1.);
+			yform.translate(cx,cy);
+		}
+		
+		this.target.drawRenderedImage( img, yform ) ;
+		setTransform(o);
+	}
+
+	/** Draws the given string.
+	 *
+	 * @param s the string to draw.
+	 * @param x horizontal location of the string.
+	 * @param y vertical location of the string.
+	 */
+	@Override
+	public void drawString( AttributedCharacterIterator s, float x, float y ) {
+		FontMetrics fm = getFontMetrics();
+		Rectangle2D r = fm.getMaxCharBounds(this);
+		this.tmpRectangle.setRect(x, y, r.getWidth()*(s.getEndIndex()-s.getBeginIndex()+1), fm.getHeight());
+		int baseline = fm.getMaxDescent();
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+		this.target.drawString(s,
+				(float)this.tmpRectangle.getMinX(),
+				(float)(this.tmpRectangle.getMaxY()-baseline)); 
+	}
+	
+	/** Draws the given string.
+	 *
+	 * @param s the string to draw.
+	 * @param x horizontal location of the string.
+	 * @param y vertical location of the string.
+	 */
+	@Override
+	public void drawString( String s, float x, float y ) {
+		FontMetrics fm = getFontMetrics();
+		this.tmpRectangle.setRect(x, y, fm.stringWidth(s), fm.getHeight());
+		fm = getPixelFontMetrics();
+		int baseline = fm.getMaxDescent();
+		ZoomableContextUtil.logical2pixel_r(this.tmpRectangle,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+		this.target.drawString(s,
+				(float)this.tmpRectangle.getMinX(),
+				(float)(this.tmpRectangle.getMaxY()-baseline)); 
+	}
+
+	/** Fills the interior of a shape.
+	 *
+	 * @param s the shape to fill.
+	 */
+	@Override
+	public void fill( Shape s ) {
+		this.target.fill( logical2pixel( s ) ) ;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void fill( PathIterator path ) {
+		this.target.fill( ZoomableContextUtil.logical2pixel(
+				path, canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle,
+				this.deltaX, this.deltaY, 
+				this.fitToWindowFactor * this.zoomFactor) ) ;
+	}
+
+	/** Adds a rotation into the current
+	 *  affine transformation.
+	 *
+	 * @param theta rotation angle
+	 * @param rx horizontal translation
+	 * @param ry vertical translation
+	 */
+	@Override
+	public void rotate( float theta, float rx, float ry ) {
+		this.target.rotate( theta, logical2pixel_x( rx ), logical2pixel_y( ry ) ) ;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void rotate( float theta ) {
+		this.target.rotate( theta, logical2pixel_x( 0 ), logical2pixel_y( 0 ) ) ;
+	}
+
+	/** Replaces the current brush.
+	 * <p>
+	 * This function scales the specified stroke.
+	 * To set the brush stroke without zoom factor,
+	 * see {@link Graphics2D#setStroke(java.awt.Stroke)}.
+	 * 
+	 * @param stroke the new brush.
+	 * @since 1.8
+	 * @see Graphics2D#setStroke(java.awt.Stroke)
+	 */
+	public void setScaledStroke(Stroke stroke) {
+		if (stroke instanceof BasicStroke) {
+			BasicStroke basicStroke = (BasicStroke) stroke;
+			BasicStroke scaledBasicStroke = new BasicStroke(
+					logical2pixel_size(basicStroke.getLineWidth()),
+					basicStroke.getEndCap(), basicStroke.getLineJoin(),
+					basicStroke.getMiterLimit(), basicStroke.getDashArray(),
+					basicStroke.getDashPhase());
+
+			this.target.setStroke(scaledBasicStroke);
+		}
+		else {
+			this.target.setStroke(stroke);
+		}
+	}
+
+	/** Adds a translation into the current
+	 *  affine transformation.
+	 *
+	 * @param tx horizontal translation.
+	 * @param ty vertical translation
+	 */
+	@Override
+	public void translate( float tx, float ty ) {
+		this.target.translate( logical2pixel_size( tx ), logical2pixel_size( ty ) ) ;
+	}
+
+	//------------------------------------------------------
+	// Getter/Setter
+	//------------------------------------------------------
+
+	/** Replies if the drawn images must be scaled or
+	 * displayed with there original size in the screen space.
+	 * <p>
+	 * The not-scaled images will always be drawn at the
+	 * geo-reference points.
+	 * <p>
+	 * Images are not icons. To retreive the similar flag for icons
+	 * please use {@link #isIconOriginalSize()}.
+	 * 
+	 * @return <code>true</code> if the image must be drawn with
+	 * its original size, otherwhise <code>false</code> to
+	 * assume that each pixel of the bitmap is a square-meter area.
+	 * @see #isIconOriginalSize()
+	 * @see #drawImage(BufferedImage,BufferedImageOp,float,float)
+	 * @see #drawImage(Image,AffineTransform,ImageObserver)
+	 * @see #drawImage(Image,float,float,Color,ImageObserver)
+	 * @see #drawImage(Image,float,float,float,float,Color,ImageObserver)
+	 * @see #drawImage(Image,float,float,float,float,ImageObserver)
+	 * @see #drawImage(Image,float,float,float,float,int,int,int,int,Color,ImageObserver)
+	 * @see #drawImage(Image,float,float,float,float,int,int,int,int,ImageObserver)
+	 * @see #drawImage(Image,float,float,ImageObserver)
+	 * @see #drawRenderableImage(RenderableImage,AffineTransform)
+	 * @see #drawRenderedImage(RenderedImage,AffineTransform)
+	 */
+	public boolean isImageOriginalSize() {
+		return !this.scaleImage ;
+	}
+
+	/** Set if the drawn images must be scaled or
+	 * displayed with there original size in the screen space.
+	 * <p>
+	 * The not-scaled images will always be drawn at the
+	 * geo-reference points.
+	 * <p>
+	 * Images are not icons. To set the similar flag for icons
+	 * please use {@link #setIconOriginalSize(boolean)}.
+	 * 
+	 * @param originalSize must be <code>true</code>
+	 * if the image must be drawn with
+	 * its original size, otherwhise <code>false</code> to
+	 * assume that each pixel of the bitmap is a square-meter area.
+	 * @see #setIconOriginalSize(boolean)
+	 * @see #drawImage(BufferedImage,BufferedImageOp,float,float)
+	 * @see #drawImage(Image,AffineTransform,ImageObserver)
+	 * @see #drawImage(Image,float,float,Color,ImageObserver)
+	 * @see #drawImage(Image,float,float,float,float,Color,ImageObserver)
+	 * @see #drawImage(Image,float,float,float,float,ImageObserver)
+	 * @see #drawImage(Image,float,float,float,float,int,int,int,int,Color,ImageObserver)
+	 * @see #drawImage(Image,float,float,float,float,int,int,int,int,ImageObserver)
+	 * @see #drawImage(Image,float,float,ImageObserver)
+	 * @see #drawRenderableImage(RenderableImage,AffineTransform)
+	 * @see #drawRenderedImage(RenderedImage,AffineTransform)
+	 */
+	public void setImageOriginalSize( boolean originalSize ) {
+		this.scaleImage = !originalSize;
+	}
+
+	/** Replies if the drawn icons must be scaled or
+	 * displayed with there original size in the screen space.
+	 * <p>
+	 * The not-scaled icons will always be drawn at the
+	 * geo-reference points.
+	 * <p>
+	 * Icons are not images. To retreive the similar flag for images
+	 * please use {@link #isImageOriginalSize()}.
+	 * 
+	 * @return <code>true</code> if the icon must be drawn with
+	 * its original size, otherwhise <code>false</code> to
+	 * assume that each pixel of the icon is a square-meter area.
+	 * @see #isImageOriginalSize()
+	 * @see #drawIcon(Icon, float, float)
+	 * @see #drawIcon(Icon, int, int)
+	 * @since 4.0
+	 */
+	public boolean isIconOriginalSize() {
+		return !this.scaleIcon ;
+	}
+
+	/** Set if the drawn icons must be scaled or
+	 * displayed with there original size in the screen space.
+	 * <p>
+	 * The not-scaled icon will always be drawn at the
+	 * geo-reference points.
+	 * <p>
+	 * Icons are not images. To set the similar flag for images
+	 * please use {@link #setImageOriginalSize(boolean)}.
+	 * 
+	 * @param originalSize must be <code>true</code>
+	 * if the icon must be drawn with
+	 * its original size, otherwhise <code>false</code> to
+	 * assume that each pixel of the icon is a square-meter area.
+	 * @see #setImageOriginalSize(boolean)
+	 * @see #drawIcon(Icon, float, float)
+	 * @see #drawIcon(Icon, int, int)
+	 * @since 4.0
+	 */
+	public void setIconOriginalSize( boolean originalSize ) {
+		this.scaleIcon = !originalSize;
+	}
+
+	/** Replies if the drawn icons must be centered on
+	 * the given painting coordinates; or not.
+	 * 
+	 * @return <code>true</code> if the icon is
+	 * centered when drawn, otherwhise <code>false</code>.
+	 * @see #drawIcon(Icon, float, float)
+	 * @see #drawIcon(Icon, int, int)
+	 * @since 4.0
+	 */
+	public boolean isIconCentered() {
+		return this.centerIcon ;
+	}
+
+	/** Set if the drawn icons must be centered on
+	 * the given painting coordinates; or not.
+	 * 
+	 * @param isCentered is <code>true</code> if the icon is
+	 * centered when drawn, otherwhise <code>false</code>.
+	 * @see #drawIcon(Icon, float, float)
+	 * @see #drawIcon(Icon, int, int)
+	 * @since 4.0
+	 */
+	public void setIconCentered(boolean isCentered) {
+		this.centerIcon = isCentered;
+	}
+
+	/** Replies if the zoom feature was activated.
+	 * 
+	 * @return <code>true</code> if the zoom feature was activated, otherwise <code>false</code>
+	 */
+	public boolean canZoom() {
+		return this.canZoom ;
+	}
+
+	/** Changes the activation state of the zooming feature.
+	 * 
+	 * @param activate is <code>true</code> if the zoom feature must be activated, otherwise <code>false</code>
+	 */
+	public void setZoom( boolean activate) {
+		if (activate!=this.canZoom) {
+			this.canZoom = activate ;
+			Font f = getOriginalUnscaledFont();
+			if (activate) {
+				f = ZoomableContextUtil.logical2pixel(
+						f,
+						canZoom(),
+						this.fitToWindowFactor * this.zoomFactor);
+			}
+			this.target.setFont(f);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float getScalingFactor() {
+		return this.zoomFactor * this.fitToWindowFactor;
+	}
+
+	/**
+	 * Replies the translation to center the view.
+	 * 
+	 * @return the translation.
+	 */
+	public float getTranslationToCenterX() {
+		return this.deltaX;
+	}
+
+	/**
+	 * Replies the translation to center the view.
+	 * 
+	 * @return the translation.
+	 */
+	public float getTranslationToCenterY() {
+		return this.deltaY;
+	}
+
+	//------------------------------------------------------
+	// Function releated to the change of coordinates
+	//------------------------------------------------------
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float logical2pixel_size(float l) {
+		return ZoomableContextUtil.logical2pixel_size(l, canZoom(), this.fitToWindowFactor * this.zoomFactor);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float logical2pixel_x(float l) {
+		return ZoomableContextUtil.logical2pixel_x(l,
+				canZoom(), this.flipX,
+				this.documentScreenRectangle,
+				this.deltaX, this.fitToWindowFactor * this.zoomFactor);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float logical2pixel_y(float l) {
+		return ZoomableContextUtil.logical2pixel_y(l,
+				canZoom(), this.flipY,
+				this.documentScreenRectangle,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float pixel2logical_size(float l) {
+		return ZoomableContextUtil.pixel2logical_size(l, canZoom(), this.fitToWindowFactor * this.zoomFactor);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float pixel2logical_x(float p) {
+		return ZoomableContextUtil.pixel2logical_x(p,
+				canZoom(), this.flipX, this.documentScreenRectangle,
+				this.deltaX, this.zoomFactor * this.fitToWindowFactor);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public float pixel2logical_y(float p) {
+		return ZoomableContextUtil.pixel2logical_y(p,
+				canZoom(), this.flipY, this.documentScreenRectangle,
+				this.deltaY, this.zoomFactor * this.fitToWindowFactor);
+	}
+
+	/** Translates the specified point
+	 *  into the screen space.
+	 *
+	 * @param s is the point in the logical space.
+	 * @return a point into the screen space.
+	 */
+	public Point2D logical2pixel(Point2D s) {
+		if ( ! canZoom() ) return s ;
+		return new Point2D.Double(
+				logical2pixel_x((float)s.getX()),
+				logical2pixel_y((float)s.getY()));
+	}
+
+	/**
+	 * Translate the given shape from the logical coordinate space
+	 * to the screen coordinate space.
+	 * 
+	 * @param s
+	 * @return the translation of s
+	 */
+	public <T extends Shape> T logical2pixel(T s) {
+		return ZoomableContextUtil.logical2pixel(
+				s, canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle,
+				this.deltaX, this.deltaY, 
+				this.fitToWindowFactor * this.zoomFactor);
+	}
+
+	/**
+	 * Translate the given font from the logical coordinate space
+	 * to the screen coordinate space.
+	 * 
+	 * @param f
+	 * @return the translation of f
+	 */
+	public Font logical2pixel(Font f) {
+		return ZoomableContextUtil.logical2pixel(
+				f, canZoom(),  
+				this.fitToWindowFactor * this.zoomFactor);
+	}
+
+	/** Translates the specified point
+	 *  into the logical space.
+	 *
+	 * @param s is the point in the screen space.
+	 * @return a point into the logical space.
+	 */
+	public Point2D pixel2logical(Point2D s) {
+		if ( ! canZoom() ) return s ;
+		return new Point2D.Double(
+				pixel2logical_x((int)s.getX()),
+				pixel2logical_y((int)s.getY()));
+	}
+
+	/**
+	 * Translate the given shape from the screen coordinate space
+	 * to the logical coordinate space.
+	 * 
+	 * @param s
+	 * @return the translation of s
+	 */
+	public <T extends Shape> T pixel2logical(T s) {
+		return ZoomableContextUtil.pixel2logical(
+				s, canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle,
+				this.deltaX, this.deltaY,
+				this.fitToWindowFactor * this.zoomFactor);
+	}
+
+	/**
+	 * Translate the given font from the screen coordinate space
+	 * to the logical coordinate space.
+	 * 
+	 * @param f
+	 * @return the translation of f
+	 */
+	public Font pixel2logical(Font f) {
+		return ZoomableContextUtil.pixel2logical(
+				f, canZoom(), 
+				this.fitToWindowFactor * this.zoomFactor);
+	}
+
+	/**
+	 * Return the rectangle that corresponds to the displayed
+	 * area.
+	 * 
+	 * @return the rectangle expressed in the logic
+	 * coordinate space; or <code>null</code>
+	 * @see #getTargetComponentSize()
+	 * @see #getViewportSize()
+	 */
+	@Override
+	public Rectangle2D getViewportRect() {
+		Component c = (this.targetComponent==null) ? null : 
+			this.targetComponent.get();
+		if (c==null) return null;
+		Dimension2D d = c.getSize();
+		if (d==null) return null;
+		Rectangle2D r = new Rectangle2D.Double(
+				0, 0, d.getWidth()-1, d.getHeight()-1);
+		ZoomableContextUtil.pixel2logical_r(
+				r,
+				canZoom(), this.flipX, this.flipY,
+				this.documentScreenRectangle, this.deltaX,
+				this.deltaY, this.fitToWindowFactor * this.zoomFactor);
+		return r;
+	}
+
+	/**
+	 * Return the size of the viewport in the logical space.
+	 * 
+	 * @return a dimension or <code>null</code>
+	 * @see #getTargetComponentSize()
+	 * @see #getViewportRect()
+	 */
+	@Override
+	public Dimension2D getViewportSize() {
+		Component c = (this.targetComponent==null) ? null : 
+			this.targetComponent.get();
+		if (c==null) return null;
+		Dimension2D d = c.getSize();
+		if (d==null) return null;
+		float z = this.fitToWindowFactor * this.zoomFactor;
+		return new DoubleDimension(
+				ZoomableContextUtil.pixel2logical_size(
+						(float)d.getWidth(), canZoom(), z),
+				ZoomableContextUtil.pixel2logical_size(
+						(float)d.getHeight(), canZoom(), z));
+	}
+
+	/** Replies the current zooming context.
+	 * 
+	 * @return the current zooming context.
+	 */
+	public ZoomableContext getZoomableContext() {
+		return this;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public void drawCartridge(String text, float x, float y, Color textColor, Color backgroundColor, Color borderColor, TextAlignment alignment) {
+		String[] lines = text.split("\n"); //$NON-NLS-1$
+		float margin = (backgroundColor!=null || borderColor!=null) ? CARTRIDGE_MARGIN : 0;
+		Rectangle2D cartidgeBounds = new Rectangle2D.Double();
+		float px = ZoomableContextUtil.logical2pixel_x(
+				x,
+				canZoom(), this.flipX, this.documentScreenRectangle,
+				this.deltaX,
+				this.fitToWindowFactor * this.zoomFactor);
+		float py = ZoomableContextUtil.logical2pixel_y(
+				y,
+				canZoom(), this.flipY, this.documentScreenRectangle,
+				this.deltaY,
+				this.fitToWindowFactor * this.zoomFactor);
+		// Force font metric to not be scaled
+		Font oldFont = getFont();
+		Font unscaledFont = oldFont.deriveFont(this.defaultFontSize);
+		setFont(unscaledFont, false);
+		// Compute cartridge
+		cartridgeBounds(lines, px, py, margin, cartidgeBounds);
+		
+		drawCartridge(lines, cartidgeBounds, margin, textColor, backgroundColor, borderColor, alignment);
+		setFont(oldFont, false);
+	}
+	
+	/** Replies the unscaled font.
+	 * 
+	 * @return the unscaled font.
+	 */
+	public Font getOriginalUnscaledFont() {
+		Font oldFont = getFont();
+		return oldFont.deriveFont(this.defaultFontSize);
+	}
+
+	/**
+	 * Gets the font metrics of the current font inside
+	 * the <strong>pixel workspace</strong>.
+	 * <p>
+	 * The replied font metrics ignores any transformation
+	 * previously applied to the graphic context such
+	 * as zooming or translation. It means that any dimension
+	 * and size in the FontMetrics are screen pixels.
+	 * 
+	 * @return    the font metrics of this graphics 
+	 *                    context's current font.
+	 * @see #getFontMetrics() for the logical workspace version.
+	 * @since 4.0
+	 */
+	public FontMetrics getPixelFontMetrics() {
+		return this.target.getFontMetrics();
+	}
+
+	/**
+	 * Gets the font metrics of the given font inside
+	 * the <strong>pixel workspace</strong>.
+	 * <p>
+	 * The replied font metrics ignores any transformation
+	 * previously applied to the graphic context such
+	 * as zooming or translation. It means that any dimension
+	 * and size in the FontMetrics are screen pixels.
+	 * 
+	 * @param f
+	 * @return    the font metrics of this graphics 
+	 *                    context's current font.
+	 * @see #getFontMetrics(Font) for the logical workspace version.
+	 * @since 4.0
+	 */
+	public FontMetrics getPixelFontMetrics(Font f) {
+		return this.target.getFontMetrics(f);
+	}
+
+	/**
+	 * Gets the current font in the <strong>pixel workspace</strong>.
+	 * <p>
+	 * The replied font ignores any transformation
+	 * previously applied to the graphic context such
+	 * as zooming or translation. It means that any dimension
+	 * and size in the Font are screen pixels.
+	 * 
+	 * @return    this graphics context's current font.
+	 * @see #getFont() for the logical workspace version.
+	 * @since 4.0
+	 */
+	public Font getPixelFont() {
+		return this.target.getFont();
+	}
+
+	/**
+	 * Sets this graphics context's font to the specified font
+	 * in <strong>pixel workspace</strong>.
+	 * <p>
+	 * The font ignores any transformation
+	 * previously applied to the graphic context such
+	 * as zooming or translation. It means that any dimension
+	 * and size in the Font are screen pixels.
+	 * <p> 
+	 * All subsequent text operations using this graphics context 
+	 * use this font. A null argument is silently ignored.
+	 * 
+	 * @param  font   the font.
+	 * @see #setFont(Font) for the logical workspace version.
+	 * @since 4.0
+	 */
+	public void setPixelFont(Font font) {
+		this.target.setFont(font);
+	}
+
+	/**
+	 * Renders in the <strong>pixel workspace</strong> the text specified by the specified <code>String</code>, 
+	 * using the current text attribute state in the <code>Graphics2D</code> context.
+	 * <p>
+	 * This functions ignores any transformation
+	 * previously applied to the graphic context such
+	 * as zooming or translation. It means that the string 
+	 * will be rendered according to the FontMetrics replied
+	 * by {@link #getPixelFontMetrics()} and at the screen pixel
+	 * {@code (x,y)}.
+	 * <p>
+	 * The baseline of the first character is at position 
+	 * (<i>x</i>,&nbsp;<i>y</i>) in the User Space.
+	 * The rendering attributes applied include the <code>Clip</code>,
+	 * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and
+	 * <code>Composite</code> attributes. For characters in script systems 
+	 * such as Hebrew and Arabic, the glyphs can be rendered from right to
+	 * left, in which case the coordinate supplied is the location of the
+	 * leftmost character on the baseline.
+	 * @param str the <code>String</code> to be rendered
+	 * @param x the x coordinate of the location where the
+	 * <code>String</code> should be rendered
+	 * @param y the y coordinate of the location where the
+	 * <code>String</code> should be rendered
+	 * @throws NullPointerException if <code>str</code> is
+	 *         <code>null</code>
+	 * @see #drawString(String,float,float) for the logical workspace version.
+	 * @since 4.0
+	 */
+	public void drawPixelString(String str, float x, float y) {
+		this.target.drawString(str, x, y);
+	}
+
+	/**
+	 * Renders in the <strong>pixel workspace</strong> the text specified by the specified <code>String</code>, 
+	 * using the current text attribute state in the <code>Graphics2D</code> context.
+	 * <p>
+	 * This functions ignores any transformation
+	 * previously applied to the graphic context such
+	 * as zooming or translation. It means that the string 
+	 * will be rendered according to the FontMetrics replied
+	 * by {@link #getPixelFontMetrics()} and at the screen pixel
+	 * {@code (x,y)}.
+	 * <p>
+	 * The baseline of the first character is at position 
+	 * (<i>x</i>,&nbsp;<i>y</i>) in the User Space.
+	 * The rendering attributes applied include the <code>Clip</code>,
+	 * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and
+	 * <code>Composite</code> attributes. For characters in script systems 
+	 * such as Hebrew and Arabic, the glyphs can be rendered from right to
+	 * left, in which case the coordinate supplied is the location of the
+	 * leftmost character on the baseline.
+	 * @param str the <code>String</code> to be rendered
+	 * @param x the x coordinate of the location where the
+	 * <code>String</code> should be rendered
+	 * @param y the y coordinate of the location where the
+	 * <code>String</code> should be rendered
+	 * @throws NullPointerException if <code>str</code> is
+	 *         <code>null</code>
+	 * @see #drawString(String,int,int) for the logical workspace version.
+	 * @since 4.0
+	 */
+	public void drawPixelString(String str, int x, int y) {
+		this.target.drawString(str, x, y);
+	}
+
+	/**
+	 * Renders in the <strong>pixel workspace</strong> the text of the specified iterator applying its attributes
+	 * in accordance with the specification of the {@link TextAttribute} class.
+	 * <p>
+	 * This functions ignores any transformation
+	 * previously applied to the graphic context such
+	 * as zooming or translation. It means that the string 
+	 * will be rendered according to the FontMetrics replied
+	 * by {@link #getPixelFontMetrics()} and at the screen pixel
+	 * {@code (x,y)}.
+	 * <p>
+	 * The baseline of the first character is at position
+	 * (<i>x</i>,&nbsp;<i>y</i>) in User Space.
+	 * For characters in script systems such as Hebrew and Arabic,
+	 * the glyphs can be rendered from right to left, in which case the 
+	 * coordinate supplied is the location of the leftmost character
+	 * on the baseline.
+	 * @param iterator the iterator whose text is to be rendered
+	 * @param x the x coordinate where the iterator's text is to be
+	 * rendered
+	 * @param y the y coordinate where the iterator's text is to be
+	 * rendered
+	 * @throws NullPointerException if <code>iterator</code> is
+	 *         <code>null</code>
+	 * @see #drawString(AttributedCharacterIterator, float, float)
+	 * @since 4.0
+	 */
+	public void drawPixelString(AttributedCharacterIterator iterator, float x, float y) {
+		this.target.drawString(iterator, x, y);
+	}
+
+	/**
+	 * Renders in the <strong>pixel workspace</strong> the text of the specified iterator applying its attributes
+	 * in accordance with the specification of the {@link TextAttribute} class.
+	 * <p>
+	 * This functions ignores any transformation
+	 * previously applied to the graphic context such
+	 * as zooming or translation. It means that the string 
+	 * will be rendered according to the FontMetrics replied
+	 * by {@link #getPixelFontMetrics()} and at the screen pixel
+	 * {@code (x,y)}.
+	 * <p>
+	 * The baseline of the first character is at position
+	 * (<i>x</i>,&nbsp;<i>y</i>) in User Space.
+	 * For characters in script systems such as Hebrew and Arabic,
+	 * the glyphs can be rendered from right to left, in which case the 
+	 * coordinate supplied is the location of the leftmost character
+	 * on the baseline.
+	 * @param iterator the iterator whose text is to be rendered
+	 * @param x the x coordinate where the iterator's text is to be
+	 * rendered
+	 * @param y the y coordinate where the iterator's text is to be
+	 * rendered
+	 * @throws NullPointerException if <code>iterator</code> is
+	 *         <code>null</code>
+	 * @see #drawString(AttributedCharacterIterator, int, int)
+	 * @since 4.0
+	 */
+	public void drawPixelString(AttributedCharacterIterator iterator, int x, int y) {
+		this.target.drawString(iterator, x, y);
+	}
+
+	/**
+	 * Renders in the <strong>pixel workspace</strong> the text of the specified 
+	 * {@link GlyphVector} using
+	 * the <code>Graphics2D</code> context's rendering attributes.
+	 * <p>
+	 * This functions ignores any transformation
+	 * previously applied to the graphic context such
+	 * as zooming or translation. It means that the string 
+	 * will be rendered according to the FontMetrics replied
+	 * by {@link #getPixelFontMetrics()} and at the screen pixel
+	 * {@code (x,y)}.
+	 * <p>
+	 * The rendering attributes applied include the <code>Clip</code>,
+	 * <code>Transform</code>, <code>Paint</code>, and
+	 * <code>Composite</code> attributes.  The <code>GlyphVector</code>
+	 * specifies individual glyphs from a {@link Font}.
+	 * The <code>GlyphVector</code> can also contain the glyph positions.  
+	 * This is the fastest way to render a set of characters to the
+	 * screen.
+	 * @param g the <code>GlyphVector</code> to be rendered
+	 * @param x the x position in User Space where the glyphs should
+	 * be rendered
+	 * @param y the y position in User Space where the glyphs should
+	 * be rendered
+	 * @throws NullPointerException if <code>g</code> is <code>null</code>.
+	 * @see #drawGlyphVector(GlyphVector, float, float)
+	 * @since 4.0
+	 */
+	public void drawPixelGlyphVector(GlyphVector g, float x, float y) {
+		this.target.drawGlyphVector(g, x, y);
+	}
+
+	@Override
+	public float getMaxScalingFactor() {
+		return this.maxScalingFactor;
+	}
+
+	@Override
+	public float getMinScalingFactor() {
+		return this.minScalingFactor;
+	}
+
+	@Override
+	public float getScalingSensitivity() {
+		return this.scalingSensitivity;
+	}
+
+	@Override
+	public float getFocusX() {
+		return this.focusX;
+	}
+
+	@Override
+	public float getFocusY() {
+		return this.focusY;
+	}
+
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/ZoomablePanel.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/ZoomablePanel.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/ZoomablePanel.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,1492 @@
+/* $Id$
+ * 
+ * This is a part of NetEditor project from Arakhne.org:
+ * package org.arakhne.neteditor.zoompanel.
+ * 
+ * Copyright (C) 2002  St&eacute;phane GALLAND, Mahdi Hannoun
+ * Copyright (C) 2004-2013  St&eacute;phane GALLAND
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.swing.zoompanel;
+
+
+import java.awt.Adjustable;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.event.AdjustmentEvent;
+import java.awt.event.AdjustmentListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.event.MouseWheelEvent;
+import java.awt.event.MouseWheelListener;
+import java.awt.geom.Dimension2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+import java.awt.print.PageFormat;
+import java.awt.print.Printable;
+import java.awt.print.PrinterException;
+import java.util.EventListener;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.swing.BoxLayout;
+import javax.swing.JPanel;
+import javax.swing.JScrollBar;
+import javax.swing.SwingUtilities;
+import javax.swing.border.Border;
+
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.ui.Graphics2DLOD;
+import org.arakhne.afc.ui.ZoomableContext;
+import org.arakhne.afc.ui.swing.zoompanel.ScrollingMethod.ScrollingMethodListener;
+
+/** Component for displaying a zoomable graphical area.
+ * <p>
+ * This class is partly based on the
+ * class <code>NetEditor.Workspace</code> from
+ * the Arakhne.org project.
+ * <p>
+ * Three coordinate systems are used:
+ * <ul>
+ * <li>the workspace coordinates: coordinates
+ * specifical to the displayed document (<code>float</code>),</li>
+ * <li>the screen coordinates: the coordinates on the screen
+ * (<code>int</code>) such as the mouse location on the screen,</li>
+ * <li>the fitted coordinates: corresponds to the scaling of
+ * the workspace coordinates to fit the entire document into the
+ * current screen space.</li>
+ * </ul>
+ * <p>
+ * Don't forget to call {@link #refreshWorkspaceSize(Rectangle2D)} each
+ * time the size of your document changed. The parameter of this function
+ * is the size of the document expressed in the workspace coordinate system.
+ * <p>
+ * <center>
+ * <img src="./doc-files/ZoomablePanel.png"><br>
+ * Example of a <code>ZoomablePanel</code>.
+ * </center>
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class ZoomablePanel extends JPanel implements Printable {
+	
+	private static final long serialVersionUID = -8971440167157593394L;
+
+	/** Delay after which the mouse-wheel refresher display
+	 * the default level of details (in milliseconds).
+	 * <p>
+	 * This rate will be used when anti-aliasing was not turned on.
+	 */
+	protected static final int REFRESH_DELAY = 400;	
+
+	/** Delay after which the mouse-wheel refresher display
+	 * the default level of details (in milliseconds).
+	 * <p>
+	 * This rate will be used when anti-aliasing was turned on.
+	 */
+	protected static final int REFRESH_DELAY_AA = 500;	
+
+	/** Precision in pixels for the zooming action.
+	 * <p>
+	 * If the mouse has a move greater than this precision,
+	 * the mouse location will be used as a new target point
+	 * for the zoom functions.
+	 */
+	public static final int ZOOM_MOUSE_PRECISION = 5;
+		
+	/** This flag was set when this panel is printing.
+	 * 
+	 * @see #print(Graphics)
+	 * @see #print(Graphics, PageFormat, int)
+	 */
+	public static final int FLAG_IS_PRINTING = 0;
+	
+	/** This flag was set when this panel is printing all.
+	 * 
+	 * @see #printAll(Graphics)
+	 */
+	public static final int FLAG_IS_PRINTING_ALL = 1;
+
+	//-------------------------------------------------------------------
+	// Attributes
+	//-------------------------------------------------------------------
+	
+	/** Is a list of flags for this panel.
+	 */
+	private long flags = 0;
+
+	/** Indicates if the user can scroll the view wh the middle
+	 * button of the mouse was pressed.
+	 */
+	private boolean mouseScrollSupport = true;
+
+	/** Indicates if the zoom factor could be interactively change
+	 * with the mouse wheel.
+	 */
+	private boolean wheelSupport = true;
+	
+	/** Indicates if the zooming area must be re-centred each time
+	 * the mouse wheel was moved.
+	 */
+	private boolean dynamicCenteringWhenWheelMoved = false;
+	
+	/** Zoomable panel.
+	 */
+	final InternalZoomablePanel zoomPanel;
+	
+	/** Horizontal scroll bar.
+	 */
+	final JScrollBar hscroll = new JScrollBar(Adjustable.HORIZONTAL);
+	
+	/** Vertical scroll bar.
+	 */
+	final JScrollBar vscroll = new JScrollBar(Adjustable.VERTICAL);
+	
+	/** Indicates if the scroll movements are catched.
+	 */
+	private boolean allowScrollMoves = true;
+	
+	/** Indicates if the anti-aliasing flag is set or not.
+	 */
+	private boolean antialiasing = false;
+	
+	/** Participates to determine if the adviced level of details.
+	 */
+	private Graphics2DLOD lod = Graphics2DLOD.NORMAL_LEVEL_OF_DETAIL;
+	
+	/** Thread to refresh the panel after a mouse wheel movement.
+	 */
+	private MouseWheelRefresher mouseWheelRefresher = null;
+	
+	/** Event handler.
+	 */
+	private EventListener handler = null;
+	
+	/** Scrolling method.
+	 */
+	private ScrollingMethod scrollingMethod = ScrollingMethod.MIDDLE_BUTTON;
+	
+	/** Component at the corner of the panel.
+	 */
+	private Component cornerComponent = null;
+	
+	/** Border around the viewport.
+	 */
+	private Border viewportBorder = null;
+
+	//-------------------------------------------------------------------
+	// Constructor
+	//-------------------------------------------------------------------
+	
+	/** Create a InternalZoomablePanel and add it into the specified
+	 * scroll pane.
+	 * The X and Y axis are not inverted.
+	 */
+	protected ZoomablePanel() {
+		this(false, false);
+	}
+	
+	/** Create a InternalZoomablePanel and add it into the specified
+	 * scroll pane.
+	 * <p>
+	 * This constructor permits to invert the X and Y axis. If not inverted,
+	 * the logical X/Y axis is directed mapped to
+	 * the corresponding AWT/Swing X/Y axis. If inverted, the logical X/Y
+	 * axis is assumed to have the opposite direction that the AWT/Swing
+	 * X/Y axis.
+	 * 
+	 * @param flipX indicates if the X axis may be inverted.
+	 * @param flipY indicates if the Y axis may be inverted.
+	 */
+	protected ZoomablePanel(boolean flipX, boolean flipY) {
+		this.zoomPanel = new InternalZoomablePanel(this, flipX, flipY);
+		
+		this.zoomPanel.setBackground(getBackground());
+		this.zoomPanel.setForeground(getForeground());
+		
+		this.vscroll.setFocusable(false);
+		this.vscroll.setRequestFocusEnabled(false);
+		this.vscroll.addAdjustmentListener(getEventHandler(AdjustmentListener.class));
+		this.vscroll.setVisible(true);
+		
+		this.hscroll.setFocusable(false);
+		this.hscroll.setRequestFocusEnabled(false);
+		this.hscroll.addAdjustmentListener(getEventHandler(AdjustmentListener.class));
+		this.hscroll.setVisible(true);
+		
+		addComponentListener(new ComponentAdapter() {
+			@Override
+			public void componentResized(ComponentEvent e) {
+				super.componentResized(e);
+				revalidateWorkspace();
+			}
+		});
+		
+		setLayout(new BorderLayout());
+		add(BorderLayout.EAST, this.vscroll);
+		
+		final JPanel southpane = new JPanel();
+		southpane.setLayout(new BoxLayout(southpane,BoxLayout.X_AXIS));
+		southpane.add(this.hscroll);
+		final JPanel p = new JPanel();
+		Dimension2D d1 = this.hscroll.getPreferredSize();
+		Dimension2D d2 = this.vscroll.getPreferredSize();
+		p.setMinimumSize(new Dimension((int)d2.getWidth(), (int)d1.getHeight()));
+		p.setMaximumSize(new Dimension((int)d2.getWidth(), (int)d1.getHeight()));
+		p.setPreferredSize(new Dimension((int)d2.getWidth(), (int)d1.getHeight()));
+		southpane.add(p);
+		add(BorderLayout.SOUTH, southpane);
+		
+		add(BorderLayout.CENTER, this.zoomPanel);
+		
+		addMouseWheelListener(getEventHandler(MouseWheelListener.class));
+		addMouseListener(getEventHandler(MouseListener.class));
+		addMouseMotionListener(getEventHandler(MouseMotionListener.class));
+	}
+	
+	//-------------------------------------------------------------------
+	// Getter/Setter
+	//-------------------------------------------------------------------
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void firePropertyChange(String propertyName, Object oldValue,
+			Object newValue) {
+		// To enable access from the internal zoomable panel
+		super.firePropertyChange(propertyName, oldValue, newValue);
+	}
+	
+	/** Replies the scrolling method.
+	 * 
+	 * @return the scrolling method.
+	 * @since 4.1
+	 */
+	public ScrollingMethod getScrollingMethod() {
+		return this.scrollingMethod;
+	}
+	
+	/** Set the scrolling method.
+	 * 
+	 * @param method is the scrolling method.
+	 * @since 4.1
+	 */
+	public void setScrollingMethod(ScrollingMethod method) {
+		this.scrollingMethod = (method==null) ? ScrollingMethod.MIDDLE_BUTTON : method;
+	}
+
+	/** Replies the zoomable context associated to this panel.
+     * 
+     * @return the zoomable context of this panel.
+     */
+    public ZoomableContext getZoomableContext() {
+    	return this.zoomPanel;
+    }
+
+	/** Replies if the scroll moves are allowed.
+	 * 
+	 * @return <code>true</code> if the scrolling actions are allowed, otherwise <code>false</code>
+	 */
+	boolean isScrollMoveAllowed() {
+		return this.allowScrollMoves;
+	}
+	
+	/** Replies the event handler. If no event handler was already
+	 * attached, create the default instance of event handler.
+	 * 
+	 * @param <T> is the type of event handler to reply
+	 * @param clazz is the type of event handler to reply
+	 * @return the event handler of the given type or <code>null</code>
+	 */
+	protected <T extends EventListener> T getEventHandler(Class<T> clazz) {
+		if (this.handler==null)
+			setEventHandler(createEventHandler());
+		assert(this.handler!=null);
+		return clazz.cast(this.handler);
+	}
+	
+	/** Set the event handler associated to this
+	 * event source.
+	 * 
+	 * @param handler
+	 */
+	protected void setEventHandler(EventListener handler) {
+		this.handler = handler;
+	}
+
+	/** Invoked when the event handler must be created.
+	 * 
+	 * @return the new event handler.
+	 */
+	protected EventListener createEventHandler() {
+		return new EventHandler();
+	}
+	
+	/**
+     * Gets the background color of this component.
+     * @return this component's background color; if this component does
+     *          not have a background color,
+     *          the background color of its parent is returned
+     * @see #setBackground
+     */
+	@Override
+	public Color getBackground() {
+		return (this.zoomPanel==null) ? super.getBackground() : this.zoomPanel.getBackground();
+	}	
+	
+    /**
+     * Sets the background color of this component.
+     * <p>
+     * The background color affects each component differently and the
+     * parts of the component that are affected by the background color 
+     * may differ between operating systems.
+     *
+     * @param c the color to become this component's color;
+     *          if this parameter is <code>null</code>, then this
+     *          component will inherit the background color of its parent
+     * @see #getBackground
+     */
+    @Override
+	public void setBackground(Color c) {
+    	if (this.zoomPanel!=null)
+    		this.zoomPanel.setBackground(c);
+    	super.setBackground(c);    	
+    }
+
+    /** Replies if this panel allow the user to scroll with
+	 * the mouse when the middle button was pressed.
+	 * 
+	 * @return <code>true</code> if the mouse scroll actions are allowed,
+	 * otherwise <code>false</code>
+	 */
+	public boolean isMouseScrollEnable() {
+		return this.mouseScrollSupport;
+	}
+	
+	/** Sets if this panel allow the user to scroll with
+	 * the mouse when the middle button was pressed.
+	 * 
+	 * @param enable must be <code>true</code> if the mouse scroll actions are allowed,
+	 * otherwise <code>false</code>
+	 */
+	public void setMouseScrollEnable(boolean enable) {
+		this.mouseScrollSupport = enable;
+	}
+
+	/** Replies if this panel allow the user to change the zoom
+	 * whith the mouse wheel.
+	 * 
+	 * @return <code>true</code> if the mouse wheel actions are allowed,
+	 * otherwise <code>false</code>
+	 */
+	public boolean isMouseWheelAllowed() {
+		return this.wheelSupport;
+	}
+	
+	/** Sets if this panel allow the user to change the zoom
+	 * whith the mouse wheel.
+	 * 
+	 * @param enable must be <code>true</code> if the mouse wheel actions are allowed,
+	 * otherwise <code>false</code>
+	 */
+	public void setMouseWheelEnable(boolean enable) {
+		this.wheelSupport = enable;
+	}
+	
+	/** Replies if this panel recomputes the zooming target point
+	 * each time the mouse wheel was move.
+	 * 
+	 * @return <code>true</code> if the target point is set according to the
+	 * current mouse position when wheel is used, otherwise <code>false</code>
+	 */
+	public boolean isZoomTargetComputedOnWheelMove() {
+		return this.dynamicCenteringWhenWheelMoved;
+	}
+	
+	/** Sets if this panel recomputes the zooming target point
+	 * each time the mouse wheel was move.
+	 * 
+	 * @param enable must be <code>true</code> if the target point is set according to the
+	 * current mouse position when wheel is used, otherwise <code>false</code>
+	 */
+	public void setZoomTargetComputedOnWheelMove(boolean enable) {
+		this.dynamicCenteringWhenWheelMoved = enable;
+	}
+	
+	/** Replies the current horizontal scroll bar.
+	 * 
+	 * @return the current horizontal scroll bar.
+	 */
+	public JScrollBar getHorizontalScrollBar() {
+		return this.hscroll;
+	}
+	
+	/** Replies the current vertical scroll bar.
+	 * 
+	 * @return the current vertical scroll bar.
+	 */
+	public JScrollBar getVerticalScrollBar() {
+		return this.vscroll;
+	}
+	
+	/** Replies the scaling factor that permits to scale the
+	 * document into the screen window.
+	 * 
+	 * @return the scaling factor.
+	 */
+	public float getDocumentScalingFactor() {
+		return this.zoomPanel.getFitInWindowFactor();
+	}
+	
+	/** Replies the viewport component
+	 * 
+	 * @return the viewport component
+	 */
+	public ZoomableViewport getViewport() {
+		return this.zoomPanel;
+	}
+	
+	/** Replies the target point
+	 * 
+	 * @return the target point
+	 */
+	public Point2f getFocusPoint() {
+		return this.zoomPanel.getFocusPoint();
+	}
+
+	/** Set the target point
+	 * 
+	 * @param x
+	 * @param y
+	 */
+	public void setFocusPoint(float x, float y) {
+		this.zoomPanel.setFocusPoint(x,y);
+		revalidateWorkspace();
+		repaint();
+	}
+
+	/** Replies the component at the lower right corner.
+	 * 
+	 * @return the component at the lower right corner.
+	 */
+	public Component getCorner() {
+		return this.cornerComponent;
+	}
+	
+	/** Replies the component at the lower right corner.
+	 * 
+	 * @param corner the component at the lower right corner.
+	 */
+	public void setCorner(Component corner) {
+		if (this.cornerComponent!=corner) {
+			Component old = this.cornerComponent;
+			this.cornerComponent = corner;
+
+			if (old!=null) remove(old);
+			if (this.cornerComponent!=null) add(this.cornerComponent);
+			
+			firePropertyChange("corner", old, this.cornerComponent); //$NON-NLS-1$
+			revalidate();
+	        repaint();
+		}
+	}
+
+	/** Replies the border arround the viewport.
+	 * 
+	 * @return the border arround the viewport.
+	 */
+	public Border getViewportBorder() {
+		return this.viewportBorder;
+	}
+	
+	/** Replies the border arround the viewport.
+	 * 
+	 * @param border the border arround the viewport.
+	 */
+	public void setViewportBorder(Border border) {
+		if (this.viewportBorder!=border) {
+			Border old = this.viewportBorder;
+			this.viewportBorder = border;
+			
+			firePropertyChange("viewportBorder", old, this.viewportBorder); //$NON-NLS-1$
+		}
+	}
+
+	/** Reply the rectangle that enclose the
+	 *  current document in the document coordinate system.
+	 *
+	 * @return a Rectangle that correspond to the
+	 *          size of the document (in workspace coordinates).
+	 */
+	public abstract Rectangle2D getDocumentRect();
+	
+	/** Reply the rectangle that enclose the visible parts of the
+	 *  current document in the document coordinate system.
+	 *
+	 * @return a Rectangle that correspond to the
+	 *          size of the document (in workspace coordinates).
+	 */
+	public abstract Rectangle2D getVisibleDocumentRect();
+
+	/** Replies if this panel is allowing to
+	 * display a low detail panel.
+	 * <p>
+	 * It occurs when the panel was scrolling
+	 * with the mouse.
+	 * 
+	 * @return the level of detail
+	 */
+	public Graphics2DLOD getLOD() {
+		return this.lod;
+	}
+	
+	/** Replies if this panel is allowing to
+	 * display a low detail panel.
+	 * <p>
+	 * It occurs when the panel was scrolling
+	 * with the mouse.
+	 * 
+	 * @param lod
+	 */
+	void setLOD(Graphics2DLOD lod) {
+		this.lod = lod;
+	}
+
+	/** Return an Image of this Panel.
+	 * <p>
+	 * The given print area is the part of the panel
+	 * to replies inside an image. It is expressed in pixels.
+	 * <p>
+	 * This function assumes that the upper-left corner
+	 * of the document has the coordinates (0,0) and the
+	 * specified printing area is relative to this origin.
+	 * 
+	 * @param print_area is the window coordinates to snap.
+	 * @return the resulting image.
+	 */
+	public RenderedImage getImage(Rectangle2D print_area) {
+		Rectangle2D area;
+		// Compute the default image size: wall document
+		if (print_area==null) {
+			Rectangle2D r = getDocumentRect();
+			
+			float width = logical2pixel_size((float)r.getWidth());
+			float height = logical2pixel_size((float)r.getHeight());
+			
+			area = new Rectangle2D.Double(0, 0, width, height);
+		}
+		else {
+			area = print_area;
+		}
+		
+		//Create the image that must receive the content of the panel
+		try {
+			BufferedImage image = new BufferedImage(
+					(int)area.getWidth(),
+					(int)area.getHeight(),
+					BufferedImage.TYPE_INT_ARGB);
+			// Print the image
+			Graphics2D g = (Graphics2D)image.getGraphics();
+			g.setClip(area);
+			
+			boolean is_set = setFlag(FLAG_IS_PRINTING,true);
+			this.zoomPanel.print(g, area);
+			setFlag(FLAG_IS_PRINTING,is_set);
+			
+			return image;
+		}
+		catch(AssertionError e) {
+			throw e;
+		}
+		catch(Throwable e) {
+			Logger.getAnonymousLogger().log(Level.WARNING, e.getLocalizedMessage(), e);
+		}
+		return null;
+	}
+	
+	/** Return an Image of this Panel.
+	 * <p>
+	 * The returned image corresponds to
+	 * all the document area.
+	 * 
+	 * @return the resulting image.
+	 */
+	public RenderedImage getImage() {
+		return getImage(null);
+	}
+
+	//-------------------------------------------------------------------
+	// Flag
+	//-------------------------------------------------------------------
+	
+	/** Set a flag.
+	 * 
+	 * @param flag_number is the number of the flag to set/unset.
+	 * @param is_set must be <code>true</code> to set the flag or
+	 * <code>false</code> to unset.
+	 * @return <code>true</code> if the flag was previously set, 
+	 * <code>false</code> if the flag was previously unset.
+	 */
+	public boolean setFlag(int flag_number, boolean is_set) {
+		boolean prev = ((1<<flag_number)&this.flags)!=0;
+		if (is_set) {
+			this.flags = (1<<flag_number)|this.flags;
+		}
+		else {
+			this.flags = (~(1<<flag_number))&this.flags;
+		}
+		return prev;
+	}
+
+	/** Replies a flag.
+	 * 
+	 * @param flag_number is the number of the flag to set/unset.
+	 * @return <code>true</code> if the flag was set, 
+	 * <code>false</code> otherwhise.
+	 */
+	public boolean hasFlag(int flag_number) {
+		return ((1<<flag_number)&this.flags)!=0;
+	}
+
+	//-------------------------------------------------------------------
+	// Paint Interface
+	//-------------------------------------------------------------------
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException {
+		boolean is_set = setFlag(FLAG_IS_PRINTING,true);
+		int result = this.zoomPanel.print(g,pageFormat,pageIndex);
+		setFlag(FLAG_IS_PRINTING,is_set);
+		return result;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void print(Graphics g) {
+		boolean is_set = setFlag(FLAG_IS_PRINTING,true);
+		super.print(g);
+		setFlag(FLAG_IS_PRINTING,is_set);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void printAll(Graphics g) {
+		boolean is_set = setFlag(FLAG_IS_PRINTING_ALL,true);
+		super.printAll(g);
+		setFlag(FLAG_IS_PRINTING_ALL,is_set);
+	}
+
+	/** Draw the zoomable panel.
+	 * <p>
+	 * This function draws all the components: the panel itself, the mouse graphical indicators...
+	 * By default this function invokes {@link #paintPanel(ZoomableGraphics2D)}.
+	 * 
+	 * @param gzoom is the context inside painting must be done.
+	 */
+	protected void paintAllComponents(ZoomableGraphics2D gzoom) {
+		paintPanel(gzoom);
+	}
+
+	/** Draw the panel with the specified parameters.
+	 * <p>
+	 * This function is invoqued by the internal panel and
+	 * must invoke {@link #paintAllComponents(ZoomableGraphics2D)}.
+	 *
+	 * @param target is the graphics in which draws will be done.
+	 * @param target_component is the target component (used to display an icon).
+	 * @param zoom is the zoom factor.
+	 * @param fit_to_window_factor is the factor used to scale the map coordinates.
+	 * @param delta_x is the translation length to center the logical model inside the screen space
+	 * @param delta_y is the translation length to center the logical model inside the screen space
+	 * @param useAntialiasing permits to force the anti-aliasing flag for the target graphical context
+	 * @param is_for_printing indicates if this graphics environment is for printing or not.
+	 * @param lodLevel indicates the desired LOD used by this graphical context.
+	 */
+	protected void paintAllComponents(Graphics2D target, Component target_component, 
+			float zoom, float fit_to_window_factor, float delta_x, float delta_y, boolean useAntialiasing, boolean is_for_printing, Graphics2DLOD lodLevel) {
+    	// Create the new graphical context
+    	ZoomableGraphics2D gzoom = new ZoomableGraphics2D(
+    			target, target_component,
+    			zoom,fit_to_window_factor,
+    			delta_x,
+				delta_y,
+				useAntialiasing,
+				is_for_printing,
+				lodLevel,
+				this.zoomPanel.isXAxisInverted(),
+				this.zoomPanel.isYAxisInverted(),
+				this.zoomPanel.getDrawingAreaRect(),
+				this.zoomPanel.getScalingSensitivity(),
+				this.zoomPanel.getMinScalingFactor(),
+				this.zoomPanel.getMaxScalingFactor(),
+				this.zoomPanel.getFocusX(),
+				this.zoomPanel.getFocusY());
+    	
+		paintAllComponents(gzoom);
+	}
+	
+	/** Draw the zoomable panel.
+	 * <p>
+	 * This function only draw the panel's content.
+	 * 
+	 * @param gzoom is the context inside painting must be done.
+	 */
+	protected abstract void paintPanel(ZoomableGraphics2D gzoom);
+	
+	/** Refresh the workspace size to be sure that it
+	 * will corresponds to the specified rectangle.
+	 * <p>
+	 * This method calls {@link InternalZoomablePanel#refreshFactors(Rectangle2D)}
+	 * which fits the document into the current window, and the method
+	 * {@link InternalZoomablePanel#changeWorkspaceSize(Rectangle2D)}
+	 * which permits to change the size of the workspace.
+	 * 
+	 * @param bounds
+	 * @return <code>true</code> if the workspace bounds have changed;
+	 * otherwise <code>false</code>.
+	 */
+	public boolean refreshWorkspaceSize(Rectangle2D bounds) {
+		assert(bounds!=null);
+		if (this.zoomPanel.refreshFactors(bounds))
+			return this.zoomPanel.changeWorkspaceSize(bounds);
+		return false;
+	}
+	
+	/** Refresh the workspace size to be sure that it
+	 * will corresponds to the current bounds of the model.
+	 * <p>
+	 * This function does not change the zoom factor and
+	 * the target point.
+	 * 
+	 * @return <code>true</code> if the workspace bounds have changed;
+	 * otherwise <code>false</code>.
+	 * @since 4.0
+	 */
+	public boolean refreshWorkspaceSize() {
+		return this.zoomPanel.changeWorkspaceSize(getDocumentRect());
+	}
+	
+	/** Refresh the workspace size to be sure that it
+	 * will corresponds to the specified rectangle.
+	 * <p>
+	 * This method calls {@link InternalZoomablePanel#refreshFactors(Rectangle2D)}
+	 * which fits the document into the current window, and the method
+	 * {@link InternalZoomablePanel#changeWorkspaceSize(Rectangle2D)}
+	 * which permits to change the size of the workspace.
+	 * 
+	 * @param width
+	 * @param height
+	 * @return <code>true</code> if the workspace bounds have changed;
+	 * otherwise <code>false</code>.
+	 */
+	public boolean refreshWorkspaceSize(float width, float height) {
+		Rectangle2D bounds = new Rectangle2D.Double(0,0,width,height);
+		if (this.zoomPanel.refreshFactors(bounds))
+			return this.zoomPanel.changeWorkspaceSize(bounds);
+		return false;
+	}
+
+	/** 
+	 * Revalidate the components of the workspace.
+	 * <p>
+	 * This method is called by {@link InternalZoomablePanel}
+	 * each time the workspace size was update, ie. when zooming
+	 * or resizing.
+	 * 
+	 * @return <code>true</code> if the workspace bounds have changed;
+	 * <code>false</code> otherwise.
+	 */
+	boolean revalidateWorkspace() {
+		Rectangle2D r_viewport = this.zoomPanel.getBounds();
+		Rectangle2D r_document = this.zoomPanel.getDrawingAreaRect();
+		
+		if ((r_viewport==null)||(r_document==null)) return false;
+		
+		this.allowScrollMoves = false;
+		
+		// Compute the union of the document's rectangle
+		// and the visible rectangle
+		Rectangle2D full_rect = r_viewport.createUnion(r_document);
+		
+		// Set the horizontal scrollbar bounds 
+		this.hscroll.setValues(
+				(int)(r_viewport.getX() - full_rect.getY()), 
+				(int)r_viewport.getWidth(),	// size of the extent (visible part)
+				0,							// minimal value
+				(int)full_rect.getWidth());	// maximal value
+		this.hscroll.setEnabled(
+				(full_rect.getWidth()>r_viewport.getWidth())&&	// document larger than viewport 
+				(r_viewport.getWidth()>0));					// has visible part
+
+		// Set the horizontal scrollbar bounds 
+		this.vscroll.setValues(
+				(int)(r_viewport.getY() - full_rect.getY()), 
+				(int)r_viewport.getHeight(),	// size of the extent (visible part)
+				0,					// minimal value
+				(int)full_rect.getHeight());	// maximal value
+		this.vscroll.setEnabled(
+				(full_rect.getHeight()>r_viewport.getHeight())&&	// document larger than viewport 
+				(r_viewport.getHeight()>0));					// has visible part
+
+		this.allowScrollMoves = true;
+		
+		this.zoomPanel.repaint();
+		
+		return true;
+	}    
+		
+	//-------------------------------------------------------------------
+	// Coordinate Systems
+	//-------------------------------------------------------------------
+	
+	/** Translates the specified workspace length
+	 *  into the screen length.
+	 *
+	 * @param l is the length in the workspace space.
+	 * @return a length into the screen space.
+	 */
+	public float logical2pixel_size(float l) {
+		return this.zoomPanel.logical2pixel_size(l);
+	}
+	
+	/** Translates the specified workspace location
+	 *  into the screen location.
+	 *
+	 * @param l is the coordinate along the workspace space X-axis.
+	 * @return a location along the screen space X-axis.
+	 */
+	public float logical2pixel_x(float l) {
+		return this.zoomPanel.logical2pixel_x(l);
+	}
+	
+	/** Translates the specified workspace location
+	 *  into the screen location.
+	 *
+	 * @param l is the coordinate along the workspace space Y-axis.
+	 * @return a location along the screen space Y-axis.
+	 */
+	public float logical2pixel_y(float l) {
+		return this.zoomPanel.logical2pixel_y(l);
+	}
+	
+	/** Translates the specified screen length
+	 *  into the logical length.
+	 *
+	 * @param l is the length in the screen space.
+	 * @return a length into the logical space.
+	 */
+	public float pixel2logical_size(float l) {
+		return this.zoomPanel.pixel2logical_size(l);
+	}
+	
+	/** Translates the specified screen location
+	 *  into the logical location.
+	 *
+	 * @param l is the location along the screen space X-axis.
+	 * @return a location along the logical space X-axis.
+	 */
+	public float pixel2logical_x(float l) {
+		return this.zoomPanel.pixel2logical_x(l);
+	}
+	
+	/** Translates the specified screen location
+	 *  into the logical location.
+	 *
+	 * @param l is the location along the screen space Y-axis.
+	 * @return a location along the logical space Y-axis.
+	 */
+	public float pixel2logical_y(float l) {
+		return this.zoomPanel.pixel2logical_y(l);
+	}
+	
+	/** Translates the specified screen rectangle
+	 *  into the logical rectangle.
+	 *
+	 * @param r_screen is the rectangle in the screen space.
+	 * @return a rectangle into the logical space.
+	 */
+	public Rectangle2D pixel2logical(Rectangle2D r_screen) {
+		return this.zoomPanel.pixel2logical(r_screen);
+	}
+
+	/** Translates the specified logical rectangle
+	 *  into the screen rectangle.
+	 *
+	 * @param r_logic is the rectangle in the logical space.
+	 * @return a rectangle into the screen space.
+	 */
+	public Rectangle2D logical2pixel(Rectangle2D r_logic) {
+		return this.zoomPanel.logical2pixel(r_logic);
+	}
+
+	/** Translates the specified screen shape
+	 *  into the logical shape.
+	 *
+	 * @param <T> is the type of the shape to convert.
+	 * @param r_screen is the rectangle in the screen space.
+	 * @return a rectangle into the logical space.
+	 */
+	public <T extends Shape> T pixel2logical(T r_screen) {
+		return this.zoomPanel.pixel2logical(r_screen);
+	}
+
+	/** Translates the specified logical shape
+	 *  into the screen shape.
+	 *
+	 * @param <T> is the type of the shape to convert.
+	 * @param r_logic is the rectangle in the logical space.
+	 * @return a rectangle into the screen space.
+	 */
+	public <T extends Shape> T logical2pixel(T r_logic) {
+		return this.zoomPanel.logical2pixel(r_logic);
+	}
+	
+	/** Translates the specified screen font
+	 *  into the logical font.
+	 *
+	 * @param f_screen is the font in the screen space.
+	 * @return a font into the logical space.
+	 */
+	public Font pixel2logical(Font f_screen) {
+		return this.zoomPanel.pixel2logical(f_screen);
+	}
+
+	/** Translates the specified logical font
+	 *  into the screen font.
+	 *
+	 * @param f_logic is the font in the logical space.
+	 * @return a font into the screen space.
+	 */
+	public Font logical2pixel(Font f_logic) {
+		return this.zoomPanel.logical2pixel(f_logic);
+	}
+
+	//-------------------------------------------------------------------
+	// Zoom
+	//-------------------------------------------------------------------
+	
+	/** Force the panel to be drawn in its initial state:
+	 * no zoom, target point at center.
+	 *
+	 * @param forceFit is <code>true</code> ti force the panel to fit
+	 * the document rect inside the entire graphical area
+	 * when at zoom factor 1, otherwise <code>false</code>
+	 * @param ignoreInvisibleLayers indicates if the invisible layers should be
+	 * ignored to fit the document, otherwise <code>false</code>
+	 */
+	public void defaultView(boolean forceFit, boolean ignoreInvisibleLayers) {
+		Rectangle2D r = ignoreInvisibleLayers ? getVisibleDocumentRect() : getDocumentRect();
+		if (forceFit) {
+			this.zoomPanel.refreshFactors(r);
+			revalidateWorkspace();
+		}
+		else {
+			if (r!=null) {
+				this.zoomPanel.setFocusPoint(
+						(float)r.getCenterX(), (float)r.getCenterY());
+			}
+			else {
+				this.zoomPanel.clearFocusPoint();
+			}
+			setZoomFactor(1f);
+		}
+	}	
+
+	/** Force the panel to be drawn in its initial state:
+	 * no zoom, target point at center.
+	 *
+	 * @param forceFit is <code>true</code> ti force the panel to fit
+	 * the document rect inside the entire graphical area
+	 * when at zoom factor 1, otherwise <code>false</code>
+	 */
+	public void defaultView(boolean forceFit) {
+		defaultView(forceFit, false);
+	}	
+
+	/** Force the panel to be drawn in its initial state:
+	 * no zoom, target point at center.
+	 * <p>
+	 * Equivalent to <code>defaultView(false)</code>
+	 */
+	public final void defaultView() {
+		defaultView(false);
+	}	
+
+	/** Set the zoom factor.
+	 * 
+	 * @param factor is the new zoom factor.
+	 */
+	public void setZoomFactor(float factor) {
+		this.zoomPanel.setScalingFactor(factor);
+	}
+	
+	/** Change the zooming factor to have the specified
+	 * ratio between 1 pixel and 1 unit in the document.
+	 * <p>
+	 * Each unit from the displayed document will
+	 * have the same graphical size as the amount of 
+	 * pixels specified by the <var>ratio</var>.
+	 * 
+	 * @param ratio
+	 */
+	public void setZoomFactorForPixelRatio(float ratio) {
+		this.zoomPanel.setZoomFactorForPixelRatio(ratio);
+	}
+		
+	/** Replies the step between to levels of zoom factor when zooming in/out.
+     * 
+     * @return the zoom factor step when zooming in/out.
+     */
+    public float getScalingSensitivity() {
+        return this.zoomPanel.getScalingSensitivity();
+    }
+
+    /** Zoom in.
+	 */
+	public void zoomIn() {
+		this.zoomPanel.zoomIn();
+	}
+	
+	/** Zoom out.
+	 */
+	public void zoomOut() {
+		this.zoomPanel.zoomOut();
+	}
+	
+	/** Replies if this panel must be drawn with anti-aliased algorithm.
+	 * 
+	 * @return <code>true</code> if the anti-aliased flag was set,
+	 * otherwhise <code>false</code>
+	 */
+	public boolean isAntiAliased() {
+		return this.antialiasing;
+	}
+
+	/** Set if this panel must be drawn with anti-aliased algorithm.
+	 * 
+	 * @param antialiasing must be <code>true</code> if the anti-aliased flag must be set,
+	 * otherwhise <code>false</code>
+	 */
+	public void setAntiAliased(boolean antialiasing) {
+		this.antialiasing = antialiasing;
+	}
+	
+	/**
+	 * Force the mouse listener on mouse wheel actions.
+	 * 
+	 * @param r is the new listenr of mouse wheel actions.
+	 */
+	synchronized void setMouseWheelRefresher(MouseWheelRefresher r) {
+		this.mouseWheelRefresher = r;
+	}
+
+	/**
+	 * Remove the mouse listener on mouse wheel actions.
+	 * 
+	 * @param source is the old listenr of mouse wheel actions.
+	 */
+	synchronized void clearMouseWheelRefresher(MouseWheelRefresher source) {
+		if (this.mouseWheelRefresher==source) {
+			this.mouseWheelRefresher = null;
+			this.lod = Graphics2DLOD.NORMAL_LEVEL_OF_DETAIL;
+			repaint();
+		}
+	}
+	
+	/**
+	 * Replies the mouse listener on mouse wheel actions.
+	 * 
+	 * @return the mouse listener on mouse wheel actions.
+	 */
+	synchronized MouseWheelRefresher getMouseWheelRefresher() {
+		return this.mouseWheelRefresher;
+	}
+
+	/** 
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	class MouseWheelRefresher extends Thread {
+
+		private long refreshDate;
+		
+		/**
+		 * @param event_date
+		 */
+		public MouseWheelRefresher(long event_date) {
+			super("Mouse Wheel Refresher"); //$NON-NLS-1$		
+			this.refreshDate = event_date + 
+			(ZoomablePanel.this.isAntiAliased()
+			? REFRESH_DELAY_AA : REFRESH_DELAY);
+		}
+		
+		/**
+		 * @param event_date
+		 */
+		public synchronized void setEventDate(long event_date) {
+			this.refreshDate = event_date + 
+			(ZoomablePanel.this.isAntiAliased()
+			? REFRESH_DELAY_AA : REFRESH_DELAY);
+		}
+
+		/**
+		 * @return the date of the event.
+		 */
+		public synchronized long getEventDate() {
+			return this.refreshDate;
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void run() {
+			while (System.currentTimeMillis()<=getEventDate()) {
+				Thread.yield();
+			}
+			ZoomablePanel.this.clearMouseWheelRefresher(this);
+		}
+
+	}
+	
+	/** This class handles the events for a {@link ZoomablePanel}.
+	 * 
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public class EventHandler extends MouseAdapter implements ScrollingMethodListener, AdjustmentListener {
+
+		/** Indicates if the mouse scrolling feature is under progress.
+		 */
+		private boolean mouseScrollingUnderProgress = false;
+		
+		/** Current location of the mouse on the panel.
+		 */
+		private int mouseX = -1;
+		
+		/** Current location of the mouse on the panel.
+		 */
+		private int mouseY = -1;
+		
+		/** Location of the mouse on the panel during the last zoom.
+		 */
+		private int zoominMouseX = -1;
+		
+		/** Location of the mouse on the panel during the last zoom.
+		 */
+		private int zoominMouseY = -1;
+		
+		private volatile ScrollWaiter scrollWaiter = null;
+		
+		//-------------------------------------------------------------------
+		// Utilities
+		//-------------------------------------------------------------------
+
+		/** Indicates if the mouse scrolling feature is under progress.
+		 * 
+		 * @return <code>true</code> if the scrolling is under progress;
+		 * otherwise <code>false</code>.
+		 */
+		protected boolean isMouseScrollingUnderProgress() {
+			return this.mouseScrollingUnderProgress;
+		}
+		
+		/** {@inheritDoc}
+		 */
+		@Override
+		public void startScolling(MouseEvent event, int delay) {
+			if ((ZoomablePanel.this.hscroll.isEnabled())||(ZoomablePanel.this.vscroll.isEnabled())) {
+				if (delay<=0) {
+					this.mouseX = event.getX();
+					this.mouseY = event.getY();
+					this.mouseScrollingUnderProgress = true;
+					setLOD(Graphics2DLOD.LOW_LEVEL_OF_DETAIL);
+					setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+					event.consume();
+				}
+				else if (this.scrollWaiter==null) {
+					this.scrollWaiter = new ScrollWaiter(event, System.currentTimeMillis()+delay);
+					SwingUtilities.invokeLater(this.scrollWaiter);
+				}
+			}
+		}
+		
+		/** {@inheritDoc}
+		 */
+		@Override
+		public void stopScolling(MouseEvent event) {
+			this.scrollWaiter = null;
+			if (this.mouseScrollingUnderProgress) {
+				this.mouseScrollingUnderProgress = false;
+				setLOD(Graphics2DLOD.NORMAL_LEVEL_OF_DETAIL);
+				setCursor(Cursor.getDefaultCursor());
+				event.consume();
+				repaint();
+			}
+		}
+
+		/** This class handles the events for a {@link ZoomablePanel}.
+		 * 
+		 * @author $Author: galland$
+		 * @version $FullVersion$
+		 * @mavengroupid $GroupId$
+		 * @mavenartifactid $ArtifactId$
+		 */
+		private class ScrollWaiter implements Runnable {
+
+			private final long timeout;
+			private final MouseEvent event;
+			
+			/**
+			 * @param event
+			 * @param timeout
+			 */
+			public ScrollWaiter(MouseEvent event, long timeout) {
+				this.event = event;
+				this.timeout = timeout;
+			}
+			
+			/**
+			 * {@inheritDoc}
+			 */
+			@SuppressWarnings("synthetic-access")
+			@Override
+			public void run() {
+				if (System.currentTimeMillis()>=this.timeout) {
+					EventHandler.this.startScolling(this.event, 0);
+				}
+				else if (EventHandler.this.scrollWaiter==this) {
+					SwingUtilities.invokeLater(this);
+				}
+			}
+			
+		} // class ScrollWaiter
+		
+		//-------------------------------------------------------------------
+		// MouseListener Interface
+		//-------------------------------------------------------------------
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void mousePressed(MouseEvent event) {
+			//
+			// START A SCROLLING FEATURE
+			//
+			if (isMouseScrollEnable()) {
+				getScrollingMethod().tryScroll(event, this);
+			}
+		}
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void mouseReleased(MouseEvent event) {
+			stopScolling(event);
+		}
+			
+		//-------------------------------------------------------------------
+		// MouseMotion Interface
+		//-------------------------------------------------------------------
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void mouseDragged(MouseEvent event) {
+			//
+			// MOVE THE VIEW ACCORDING TO THE SCROLLING FEATURE
+			//
+			if (!this.mouseScrollingUnderProgress && this.scrollWaiter!=null) {
+				this.scrollWaiter = null;
+				startScolling(event, 0);
+			}
+			if (this.mouseScrollingUnderProgress) {
+				float dx = 0;
+				if (ZoomablePanel.this.hscroll.isEnabled()) {
+					dx = pixel2logical_size(event.getX() - this.mouseX);
+				}
+				float dy = 0;
+				if (ZoomablePanel.this.vscroll.isEnabled()) {
+					dy = pixel2logical_size(event.getY() - this.mouseY);
+				}
+				if ((dx!=0)||(dy!=0)) {
+					Point2f target = ZoomablePanel.this.zoomPanel.getFocusPoint();
+					Rectangle2D doc_rect = getDocumentRect();
+					float x = target.getX()-dx;
+					float y = target.getY()-dy;
+
+					if (x<doc_rect.getMinX()) x = target.getX();
+					if (x>doc_rect.getMaxX()) x = target.getX();
+					if (y<doc_rect.getMinY()) y = target.getY();
+					if (y>doc_rect.getMaxY()) y = target.getY();
+
+					if ((x!=target.getX())||(y!=target.getY())) {
+						ZoomablePanel.this.zoomPanel.setFocusPoint(x,y);
+						revalidateWorkspace();
+						event.consume();
+					}
+				}
+			}
+			this.mouseX = event.getX();
+			this.mouseY = event.getY();		
+		}
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void mouseMoved(MouseEvent event) {
+			this.mouseX = event.getX();
+			this.mouseY = event.getY();
+		}
+		
+		//-------------------------------------------------------------------
+		// MouseWheel Interface
+		//-------------------------------------------------------------------
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void mouseWheelMoved(MouseWheelEvent event) {
+			if (isMouseWheelAllowed() && !event.isConsumed()) {
+				boolean zoomIn = (event.getWheelRotation() < 0);
+
+				// Create a thread that permits to refresh the panel
+				// after a given delay to ensure that the
+				// adviced low-level of details was canceled
+				MouseWheelRefresher mv = getMouseWheelRefresher();
+				if (mv==null) {
+					mv = new MouseWheelRefresher(event.getWhen());
+					setMouseWheelRefresher(mv);
+					mv.start();
+				}
+				else {
+					mv.setEventDate(event.getWhen());
+				}
+				
+				setLOD(Graphics2DLOD.LOW_LEVEL_OF_DETAIL);
+				
+				if (zoomIn) {
+					
+					if ((isZoomTargetComputedOnWheelMove())&&
+						((this.zoominMouseX==-1)||
+						(this.zoominMouseY==-1)||
+						(Math.abs(this.zoominMouseX-this.mouseX)>=ZOOM_MOUSE_PRECISION)||
+						(Math.abs(this.zoominMouseX-this.mouseX)>=ZOOM_MOUSE_PRECISION))) {
+						float x = pixel2logical_x(this.mouseX);
+						float y = pixel2logical_y(this.mouseY);
+						this.zoominMouseX = this.mouseX;
+						this.zoominMouseY = this.mouseY;
+						ZoomablePanel.this.zoomPanel.setFocusPoint(x,y);            		
+					}
+					
+					ZoomablePanel.this.zoomPanel.zoomIn();
+				}
+				else 
+					ZoomablePanel.this.zoomPanel.zoomOut();
+			}
+		}
+		
+		//-------------------------------------------------------------------
+		// AdjustmentListener Interface
+		//-------------------------------------------------------------------
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void adjustmentValueChanged(AdjustmentEvent e) {
+			if (!isScrollMoveAllowed()) return ;
+			
+			setLOD( 
+				e.getValueIsAdjusting()
+				? Graphics2DLOD.LOW_LEVEL_OF_DETAIL
+				: Graphics2DLOD.NORMAL_LEVEL_OF_DETAIL);
+			
+			Point2f target_point = ZoomablePanel.this.zoomPanel.getFocusPoint();
+			if (target_point!=null) {
+				
+				Rectangle2D document_rect = getDocumentRect();
+				
+				if (document_rect!=null) {
+					
+					Rectangle2D viewport = ZoomablePanel.this.zoomPanel.getBounds();
+					
+					if (e.getSource()==ZoomablePanel.this.vscroll) {
+						//
+						// Move the target point vertically
+						float target_y = (float)(e.getValue() + viewport.getHeight()/2.);
+						
+						target_y = pixel2logical_size(target_y) + (float)document_rect.getY();
+						
+						ZoomablePanel.this.zoomPanel.setFocusPoint(target_point.getX(),target_y);
+						
+						revalidateWorkspace();
+					}
+					
+					else if (e.getSource()==ZoomablePanel.this.hscroll) {
+						//
+						// Move the target point horizontally
+						float target_x = (float)(e.getValue() + viewport.getWidth()/2.);
+						
+						target_x = pixel2logical_size(target_x) + (float)document_rect.getX();
+						
+						ZoomablePanel.this.zoomPanel.setFocusPoint(target_x,target_point.getY());
+						
+						revalidateWorkspace();
+					}
+					
+					// Repaint the panel
+					ZoomablePanel.this.zoomPanel.repaint();
+				}
+			}
+		}
+
+	}
+	
+}

Added: trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/ZoomableViewport.java
===================================================================
--- trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/ZoomableViewport.java	                        (rev 0)
+++ trunk/ui/ui-swing/src/main/java/org/arakhne/afc/ui/swing/zoompanel/ZoomableViewport.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,139 @@
+/* $Id$
+ * 
+ * This is a part of NetEditor project from Arakhne.org:
+ * package org.arakhne.neteditor.zoompanel.
+ * 
+ * Copyright (C) 2002  St&eacute;phane GALLAND, Mahdi Hannoun
+ * Copyright (C) 2004-2013  St&eacute;phane GALLAND
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.swing.zoompanel;
+
+
+import java.awt.geom.Dimension2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.print.Printable;
+
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.ui.ZoomableContext;
+
+/** This interface describes the viewport that supports
+ * zooming.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @since 4.0
+ */
+public interface ZoomableViewport extends ZoomableContext, Printable {
+    
+    /** Replies if the X axis may be inverted.
+     * 
+     * @return <code>true</code> if the X axis is inverted,
+     * otherwise <code>false</code>.
+     */
+    public boolean isXAxisInverted();
+
+    /** Replies if the Y axis may be inverted.
+     * 
+     * @return <code>true</code> if the Y axis is inverted,
+     * otherwise <code>false</code>.
+     */
+    public boolean isYAxisInverted();
+    
+    /** Replies the target point in the screen coordinate system.
+     * 
+     * @return the target point in the screen coordinate system
+     */
+    public Point2D getFocusPointPixel();
+
+    /** Replies the target point.
+     * 
+     * @return the target point.
+     */
+    public Point2f getFocusPoint();
+
+    /** Sets the target point.
+     * 
+     * @param x
+     * @param y
+     */
+    public void setFocusPoint(float x, float y);
+	
+    /** Replies the drawing area size in screen coordinates.
+     * 
+     * @return the drawing area size in screen coordinates.
+     */
+    public Dimension2D getDrawingAreaSize();
+
+    /** Replies the document rectangle in screen coordinate system.
+     * 
+     * @return the document rectangle in screen coordinate system.
+     */
+    public Rectangle2D getDrawingAreaRect();
+    
+    /** Reply the rectangle that enclose the
+     *  current document.
+     *
+     * @return a Rectangle that correspond to the
+     *          size of the document (in workspace coordinates).
+     */
+    public Rectangle2D getDocumentRect();
+		
+    /** Set the zoom factor.
+     * 
+     * @param factor is the new zoom factor.
+     */
+    public void setScalingFactor(float factor);
+
+    /** Set the zoom factor.
+     * 
+     * @param factor is the new zoom factor.
+     * @param enable_repaint is <code>true</code> to enable the repainting
+     * of the pane, otherwhise it is <code>false</code>.
+     */
+    public void setScalingFactor(float factor, boolean enable_repaint);
+
+    /** Zoom in.
+     */
+    public void zoomIn();
+
+    /** Zoom out.
+     */
+    public void zoomOut();
+    
+    /** Zoom in.
+     * 
+     * @param enable_repaint is <code>true</code> to enable the repainting
+     * of the pane, otherwhise it is <code>false</code>.
+     */
+    public void zoomIn(boolean enable_repaint);
+
+    /** Zoom out.
+     * 
+     * @param enable_repaint is <code>true</code> to enable the repainting
+     * of the pane, otherwhise it is <code>false</code>.
+     */
+    public void zoomOut(boolean enable_repaint);
+    
+    /** Force the efreshing of this panel.
+     */
+    public void refreshPanelContent();
+    
+}


Property changes on: trunk/ui/ui-vector
___________________________________________________________________
Added: svn:ignore
   + .classpath
..metadata
..settings
..project
target
bin


Added: trunk/ui/ui-vector/pom.xml
===================================================================
--- trunk/ui/ui-vector/pom.xml	                        (rev 0)
+++ trunk/ui/ui-vector/pom.xml	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,33 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+		<groupId>org.arakhne.afc</groupId>
+		<artifactId>arakhneUi</artifactId>
+		<version>1.0-SNAPSHOT</version>
+  </parent>
+  
+  <groupId>org.arakhne.afc.ui</groupId>
+  <artifactId>ui-vector</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <name>Arakhne Vector Window Toolkit</name>
+  
+  <dependencies>
+  	<dependency>
+  		<groupId>org.arakhne.afc</groupId>
+  		<artifactId>arakhneVmutils</artifactId>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.arakhne.afc</groupId>
+  		<artifactId>math</artifactId>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.arakhne.afc</groupId>
+  		<artifactId>util</artifactId>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.arakhne.afc.ui</groupId>
+  		<artifactId>ui-base</artifactId>
+  	</dependency>
+  </dependencies>
+  
+</project>

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/AbstractVectorGraphics2D.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/AbstractVectorGraphics2D.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/AbstractVectorGraphics2D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,407 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.vector;
+
+import java.lang.ref.SoftReference;
+import java.net.URL;
+
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.math.continous.object2d.Shape2f;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.ui.Graphics2DLOD;
+import org.arakhne.afc.ui.TextAlignment;
+import org.arakhne.vmutil.Resources;
+
+/** This graphic context permits to display
+ *  something with a level of details.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractVectorGraphics2D implements VectorGraphics2D {
+
+	private boolean isInteriorPainted = true;
+	private boolean isOutlinePainted = true;
+	private String interiorText = null;
+	private Color outlineColor = null;
+	private Color fillColor = null;
+	private Paint paint = null;
+	
+	private static SoftReference<Image> noPictureBuffer = null;
+	private static SoftReference<Image> noTransparentPictureBuffer = null;
+	
+	/**
+	 */
+	public AbstractVectorGraphics2D() {
+		//
+	}
+	
+	private static Image getNoPicture() {
+		Image noPictureImage = noPictureBuffer==null ? null : noPictureBuffer.get();
+		if (noPictureImage==null) {
+			URL url = Resources.getResource(AbstractVectorGraphics2D.class, "no_picture.png"); //$NON-NLS-1$
+			if (url!=null) {
+				try {
+					noPictureImage = VectorToolkit.image(url);
+					noPictureBuffer = new SoftReference<Image>(noPictureImage);
+				}
+				catch(Throwable _) {
+					noPictureImage = null;
+					noPictureBuffer = null;
+				}
+			}
+		}
+		return noPictureImage;
+	}
+	
+	private static Image getTransparentNoPicture() {
+		Image noPictureImage = noTransparentPictureBuffer==null ? null : noTransparentPictureBuffer.get();
+		if (noPictureImage==null) {
+			Image originalImage = getNoPicture();
+			if (originalImage!=null) {
+				noPictureImage = VectorToolkit.image(originalImage, -.5f);
+				noTransparentPictureBuffer = new SoftReference<Image>(noPictureImage);
+			}
+		}
+		return noPictureImage;
+	}
+
+	@Override
+	public final void drawDefaultImage(float dx1, float dy1, float dx2, float dy2) {
+		Image noPictureImage;
+		if (getLOD()==Graphics2DLOD.SHADOW) {
+			noPictureImage = getTransparentNoPicture();
+		}
+		else {
+			noPictureImage = getNoPicture();
+		}
+		
+		if (noPictureImage!=null) {
+			drawImage(
+					null,
+					noPictureImage,
+					dx1, dy1, dx2, dy2,
+					0, 0, noPictureImage.getWidth(null), noPictureImage.getHeight(null),
+					null);
+		}
+		else {
+			setInteriorPainted(true);
+			Rectangle2f bounds = new Rectangle2f();
+			bounds.setFromCorners(dx1, dy1, dx2, dy2);
+			draw(bounds);
+		}
+	}
+	
+	/**
+	 * @param fillColor is the initial filling color.
+	 * @param outlineColor is the initial outline color.
+	 * @param paint is the initial painting object.
+	 * @param isInteriorPainted is the initial interior painting flag.
+	 * @param isOutlinePainted is the initial outline painting flag.
+	 * @param interiorText is the initial text inside a shape.
+	 */
+	public AbstractVectorGraphics2D(Color fillColor, Color outlineColor, Paint paint, boolean isInteriorPainted, boolean isOutlinePainted, String interiorText) {
+		this.fillColor = fillColor;
+		this.outlineColor = outlineColor;
+		this.paint = paint;
+		this.isInteriorPainted = isInteriorPainted;
+		this.isOutlinePainted = isOutlinePainted;
+		this.interiorText = interiorText;
+	}
+
+	@Override
+	public void dispose() {
+		this.isInteriorPainted = true;
+		this.isOutlinePainted = true;
+		this.interiorText = null;
+		this.outlineColor = this.fillColor = null;
+		this.paint = null;
+	}
+
+	@Override
+	public void reset() {
+		this.isInteriorPainted = true;
+		this.isOutlinePainted = true;
+		this.interiorText = null;
+		this.outlineColor = this.fillColor = null;
+		this.paint = null;
+	}
+
+	/** Log the given message.
+	 * 
+	 * @param message is the message to log.
+	 */
+	protected void log(String message) {
+		System.out.println(toString()+": "+message); //$NON-NLS-1$
+	}
+	
+	/** Log the given message.
+	 * 
+	 * @param message is the message to log out.
+	 * @param exception is the exception to log out.
+	 */
+	protected void log(String message, Throwable exception) {
+		String tag = toString();
+		System.out.println(tag+": "+message); //$NON-NLS-1$
+		System.out.println(tag+": "+exception.getLocalizedMessage()); //$NON-NLS-1$
+		exception.printStackTrace();
+	}
+
+	/** Clean the state machine prior to any drawing.
+	 */
+	protected void preDrawing() {
+		//
+	}
+
+	/** Clean the state machine after any drawing.
+	 */
+	protected void postDrawing() {
+		this.interiorText = null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Font getDefaultFont() {
+		return VectorToolkit.font();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Paint setPaint(Paint paint) {
+		Paint o = this.paint;
+		this.paint = paint;
+		return o;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Color setFillColor(Color color) {
+		Color o = this.fillColor;
+		this.fillColor = color;
+		return o;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Color setOutlineColor(Color color) {
+		Color o = this.outlineColor;
+		this.outlineColor = color;
+		return o;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Color getFillColor() {
+		return this.fillColor;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Color getOutlineColor() {
+		return this.outlineColor;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Paint getPaint() {
+		return this.paint;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean isInteriorPainted() {
+		return this.isInteriorPainted;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setInteriorPainted(boolean painted) {
+		this.isInteriorPainted = painted;
+	}
+
+	@Override
+	public Point2D computeTextPosition(String text, Rectangle2f bounds,
+			TextAlignment halign, TextAlignment valign) {
+		FontMetrics fm = getFontMetrics();
+		float width = fm.stringWidth(text);
+		float height = fm.getHeight();
+		float x = 0;
+		float y = 0;
+
+		switch(halign) {
+		case CENTER_ALIGN:
+			x = bounds.getMinX() + (bounds.getWidth() - width) / 2f;
+			break;
+		case LEFT_ALIGN:
+			x = bounds.getMinX();
+			break;
+		case RIGHT_ALIGN:
+			x = bounds.getMinX() + bounds.getWidth() - width;
+			break;
+		default:
+			throw new IllegalArgumentException();
+		}
+
+		switch(getStringAnchor()) {
+		case UPPER_LEFT:
+			switch(valign) {
+			case CENTER_ALIGN:
+				y = bounds.getMinY() + (bounds.getHeight() - height) / 2f;
+				break;
+			case LEFT_ALIGN:
+				y = bounds.getMinY();
+				break;
+			case RIGHT_ALIGN:
+				y = bounds.getMinY() + bounds.getHeight() - height;
+				break;
+			default:
+				throw new IllegalArgumentException();
+			}
+			break;
+		case LEFT_BASELINE:
+			switch(valign) {
+			case CENTER_ALIGN:
+				y = bounds.getMinY() + (bounds.getHeight() - height) / 2f + fm.getMaxAscent();
+				break;
+			case LEFT_ALIGN:
+				y = bounds.getMinY() + fm.getMaxAscent();
+				break;
+			case RIGHT_ALIGN:
+				y = bounds.getMinY() + bounds.getHeight() - fm.getMaxDescent();
+				break;
+			default:
+				throw new IllegalArgumentException();
+			}
+			break;
+		case LOWER_LEFT:
+			switch(valign) {
+			case CENTER_ALIGN:
+				y = bounds.getMinY() + (bounds.getHeight() + height) / 2f;
+				break;
+			case LEFT_ALIGN:
+				y = bounds.getMinY() + fm.getHeight();
+				break;
+			case RIGHT_ALIGN:
+				y = bounds.getMaxY();
+				break;
+			default:
+				throw new IllegalArgumentException();
+			}
+			break;
+		default:
+			throw new IllegalArgumentException();
+		}
+		return new Point2f(x, y);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean isOutlineDrawn() {
+		return this.isOutlinePainted;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setOutlineDrawn(boolean outlined) {
+		this.isOutlinePainted = outlined;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String getInteriorText() {
+		return this.interiorText;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setInteriorText(String interiorText) {
+		this.interiorText = interiorText;
+	}
+	
+	/** This method paint a string into the rectangle of this figure and
+	 * not outside.
+	 *
+	 * @param text the text to draw.
+	 * @param clip is the clipping shape.
+	 * @param x x coordinate where the text must be draw.
+	 * @param y y coordinate where the text must be draw.
+	 */
+	protected void paintClippedString(String text, Shape2f clip, float x, float y ) {
+		Shape2f oldClipArea = getClip();
+		if (clip!=null) {
+			setClip(clip);
+		}
+		drawString( text, x, y );
+		setClip( oldClipArea );
+	}
+
+	/** This method paint a string into the rectangle of this figure and
+	 * not outside. The text is centered on the figure.
+	 *
+	 * @param text the text to draw.
+	 * @param figureBounds are the bounds of the figure that may be used during the drawing.
+	 * @param clip is the shape that should be used for clipping.
+	 */
+	protected void paintClippedString(String text, Rectangle2f figureBounds, Shape2f clip) {
+		Shape2f oldClipArea = getClip();
+		if (clip!=null) {
+			setClip(clip);
+		}
+		else {
+			setClip(figureBounds);
+		}
+		Point2D p = computeTextPosition(text, figureBounds, TextAlignment.CENTER_ALIGN, TextAlignment.CENTER_ALIGN);
+		drawString( text, p.getX(), p.getY() );
+		setClip( oldClipArea );
+	}
+	
+}

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Color.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Color.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Color.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,127 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector;
+
+import java.io.Serializable;
+
+
+
+/** Interface that is representing a color. 
+ * See {@link VectorToolkit} to create an instance.
+ * For color constants, see {@link VectorToolkit}.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Color extends Paint, Serializable {
+	
+    /**
+     * Returns the green component in the range 0-255 in the default sRGB
+     * space.
+     * @return the green component.
+     * @see #getRGB
+     */
+    public int getGreen();
+
+    /**
+     * Returns the red component in the range 0-255 in the default sRGB
+     * space.
+     * @return the red component.
+     * @see #getRGB
+     */
+    public int getRed();
+
+    /**
+     * Returns the blue component in the range 0-255 in the default sRGB
+     * space.
+     * @return the blue component.
+     * @see #getRGB
+     */
+    public int getBlue();
+
+    /**
+     * Returns the alpha component in the range 0-255.
+     * @return the alpha component.
+     * @see #getRGB
+     */
+    public int getAlpha();
+
+    /**
+     * Returns the RGB value representing the color.
+     * (Bits 24-31 are alpha, 16-23 are red, 8-15 are green, 0-7 are
+     * blue).
+     * @return the RGB value of the color in the default sRGB
+     *         <code>ColorModel</code>.
+     * @see java.awt.image.ColorModel#getRGBdefault
+     * @see #getRed
+     * @see #getGreen
+     * @see #getBlue
+     * @since JDK1.0
+     */
+    public int getRGB();
+
+    /**
+     * Creates a new <code>Color</code> that is a brighter version of this
+     * <code>Color</code>.
+     * <p>
+     * This method applies an arbitrary scale factor to each of the three RGB
+     * components of this <code>Color</code> to create a brighter version
+     * of this <code>Color</code>.
+     * The {@code alpha} value is preserved.
+     * Although <code>brighter</code> and
+     * <code>darker</code> are inverse operations, the results of a
+     * series of invocations of these two methods might be inconsistent
+     * because of rounding errors.
+     * @return     a new <code>Color</code> object that is
+     *                 a brighter version of this <code>Color</code>
+     *                 with the same {@code alpha} value.
+     * @see        java.awt.Color#darker
+     */
+    public Color brighterColor();
+
+    /**
+     * Creates a new <code>Color</code> that is a darker version of this
+     * <code>Color</code>.
+     * <p>
+     * This method applies an arbitrary scale factor to each of the three RGB
+     * components of this <code>Color</code> to create a darker version of
+     * this <code>Color</code>.
+     * The {@code alpha} value is preserved.
+     * Although <code>brighter</code> and
+     * <code>darker</code> are inverse operations, the results of a series
+     * of invocations of these two methods might be inconsistent because
+     * of rounding errors.
+     * @return  a new <code>Color</code> object that is
+     *                    a darker version of this <code>Color</code>
+     *                    with the same {@code alpha} value.
+     * @see        java.awt.Color#brighter
+     */
+    public Color darkerColor();
+    
+    /** Make a color transparent.
+	 * 
+	 * @return the transparent color.
+	 */
+	public Color transparentColor();
+
+}

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Composite.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Composite.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Composite.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,41 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector;
+
+
+/** Interface that is representing a composite. 
+ * See {@link VectorToolkit} to create an instance.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Composite {
+	
+	/** Replies the alpha composition.
+	 * 
+	 * @return a value in [0;1]. If {@code 0} the composite is translucent;
+	 * if {@code 1} the composite is opaque.
+	 */
+	public float getAlpha();
+	
+}

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Dimension.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Dimension.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Dimension.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,47 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector;
+
+/** Interface that is representing a generic dimension. 
+ * See {@link VectorToolkit} to create an instance.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Dimension {
+
+    /**
+     * Returns the width of this <code>Dimension</code> in double
+     * precision.
+     * @return the width of this <code>Dimension</code>.
+     */
+    public float width();
+
+    /**
+     * Returns the height of this <code>Dimension</code> in double
+     * precision.
+     * @return the height of this <code>Dimension</code>.
+     */
+    public float height();
+
+}

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Font.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Font.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Font.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,169 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector;
+
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+
+
+/** Interface that is representing a font. 
+ * See {@link VectorToolkit} to create an instance.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Font {
+	
+	/**
+     * Returns the family name of this <code>Font</code>.
+     *
+     * <p>The family name of a font is font specific. Two fonts such as
+     * Helvetica Italic and Helvetica Bold have the same family name,
+     * <i>Helvetica</i>, whereas their font face names are
+     * <i>Helvetica Bold</i> and <i>Helvetica Italic</i>.
+     *
+     * <p>Use <code>getName</code> to get the logical name of the font.
+     * Use <code>getFontName</code> to get the font face name of the font.
+     * 
+     * @return a <code>String</code> that is the family name of this
+     *          <code>Font</code>.
+     */
+    public String getFamily();
+    
+    /**
+     * Returns the font face name of this <code>Font</code>.  For example,
+     * Helvetica Bold could be returned as a font face name.
+     * Use <code>getFamily</code> to get the family name of the font.
+     * Use <code>getName</code> to get the logical name of the font.
+     * 
+     * <p>The family name of a font is font specific. Two fonts such as
+     * Helvetica Italic and Helvetica Bold have the same family name,
+     * <i>Helvetica</i>, whereas their font face names are
+     * <i>Helvetica Bold</i> and <i>Helvetica Italic</i>.
+     * 
+     * @return a <code>String</code> representing the font face name of
+     *          this <code>Font</code>.
+     */
+    public String getFontName();
+    
+    /**
+     * Returns the logical name of this <code>Font</code>.
+     * Use <code>getFamily</code> to get the family name of the font.
+     * Use <code>getFontName</code> to get the font face name of the font.
+     * 
+     * @return a <code>String</code> representing the logical name of
+     *          this <code>Font</code>.
+     */
+    public String getName();
+    
+    /**
+     * Returns the postscript name of this <code>Font</code>.
+     * Use <code>getFamily</code> to get the family name of the font.
+     * Use <code>getFontName</code> to get the font face name of the font.
+     * @return a <code>String</code> representing the postscript name of
+     *          this <code>Font</code>.
+     */
+    public String getPSName();
+    
+    /**
+     * Returns the point size of this <code>Font</code> in
+     * <code>float</code> value.
+     * 
+     * @return the point size of this <code>Font</code> as a
+     * <code>float</code> value.
+     */
+    public float getSize();
+    
+    /**
+     * Indicates whether or not this <code>Font</code> object's style is
+     * PLAIN.
+     * 
+     * @return    <code>true</code> if this <code>Font</code> has a
+     *            PLAIN sytle;
+     *            <code>false</code> otherwise.
+     */
+    public boolean isPlain();
+
+    /**
+     * Indicates whether or not this <code>Font</code> object's style is
+     * BOLD.
+     * 
+     * @return    <code>true</code> if this <code>Font</code> object's
+     *            style is BOLD;
+     *            <code>false</code> otherwise.
+     */
+    public boolean isBold();
+
+    /**
+     * Indicates whether or not this <code>Font</code> object's style is
+     * ITALIC.
+     * 
+     * @return    <code>true</code> if this <code>Font</code> object's
+     *            style is ITALIC;
+     *            <code>false</code> otherwise.
+     */
+    public boolean isItalic();
+    
+    /**
+     * Creates a new <code>Font</code> object by replicating the current
+     * <code>Font</code> object and applying a new size to it.
+     * 
+     * @param size the size for the new <code>Font</code>.
+     * @return a new <code>Font</code> object.
+     */
+    public Font deriveFont(float size);
+
+    /**
+     * Creates a new <code>Font</code> object by replicating this
+     * <code>Font</code> object and applying a new style and size.
+     * 
+     * @param style the style for the new <code>Font</code>
+     * @param size the size for the new <code>Font</code>
+     * @return a new <code>Font</code> object.
+     */
+    public Font deriveFont(FontStyle style, float size);
+    
+    /**
+     * Creates a new <code>Font</code> object by replicating this
+     * <code>Font</code> object and applying a new style and size.
+     * 
+     * @param style the style for the new <code>Font</code>
+     * @return a new <code>Font</code> object.
+     */
+    public Font deriveFont(FontStyle style);
+
+    /** Replies the bounds of the given font when it is drawn with the given font.
+     * 
+     * @param str
+     * @return the bounds
+     */
+    public Rectangle2f getStringBounds(String str);
+
+    /**
+     * Returns the italic angle of this <code>Font</code>.  The italic angle
+     * is the inverse slope of the caret which best matches the posture of this
+     * <code>Font</code>.
+     * @return the angle of the ITALIC style of this <code>Font</code>.
+     */
+    public float getItalicAngle();
+   
+}

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/FontComparator.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/FontComparator.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/FontComparator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,53 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.vector;
+
+import java.util.Comparator;
+
+/** Comparator of fonts.
+ * See {@link VectorToolkit} to create an instance.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class FontComparator implements Comparator<Font> {
+
+	/**
+	 */
+	public FontComparator() {
+		//
+	}
+
+	@Override
+	public int compare(Font o1, Font o2) {
+		if (o1==o2) return 0;
+		if (o1==null) return Integer.MIN_VALUE;
+		if (o2==null) return Integer.MAX_VALUE;
+		int cmp = o1.getFontName().compareToIgnoreCase(o2.getFontName());
+		if (cmp!=0) return cmp;
+		return Float.compare(o1.getSize(), o2.getSize());
+	}
+
+}

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/FontMetrics.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/FontMetrics.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/FontMetrics.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,151 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector;
+
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+
+/** Interface that is representing a font metrics. 
+ * See {@link VectorToolkit} to create an instance.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface FontMetrics {
+
+    /**
+     * Gets the <code>Font</code> described by this
+     * <code>FontMetrics</code> object.
+     * @return    the <code>Font</code> described by this
+     * <code>FontMetrics</code> object.
+     */
+    public Font getFont();
+
+    /**
+     * Determines the <em>standard leading</em> of the
+     * <code>Font</code> described by this <code>FontMetrics</code>
+     * object.  The standard leading, or
+     * interline spacing, is the logical amount of space to be reserved
+     * between the descent of one line of text and the ascent of the next
+     * line. The height metric is calculated to include this extra space.
+     * @return    the standard leading of the <code>Font</code>.
+     * @see   #getHeight()
+     * @see   #getAscent()
+     * @see   #getDescent()
+     */
+    public float getLeading();
+
+    /**
+     * Determines the <em>font ascent</em> of the <code>Font</code>
+     * described by this <code>FontMetrics</code> object. The font ascent
+     * is the distance from the font's baseline to the top of most
+     * alphanumeric characters. Some characters in the <code>Font</code>
+     * might extend above the font ascent line.
+     * @return     the font ascent of the <code>Font</code>.
+     * @see        #getMaxAscent()
+     */
+    public float getAscent();
+
+    /**
+     * Determines the <em>font descent</em> of the <code>Font</code>
+     * described by this
+     * <code>FontMetrics</code> object. The font descent is the distance
+     * from the font's baseline to the bottom of most alphanumeric
+     * characters with descenders. Some characters in the
+     * <code>Font</code> might extend
+     * below the font descent line.
+     * @return     the font descent of the <code>Font</code>.
+     * @see        #getMaxDescent()
+     */
+    public float getDescent();
+
+    /**
+     * Gets the standard height of a line of text in this font.  This
+     * is the distance between the baseline of adjacent lines of text.
+     * It is the sum of the leading + ascent + descent. Due to rounding
+     * this may not be the same as getAscent() + getDescent() + getLeading().
+     * There is no guarantee that lines of text spaced at this distance are
+     * disjoint; such lines may overlap if some characters overshoot
+     * either the standard ascent or the standard descent metric.
+     * @return    the standard height of the font.
+     * @see       #getLeading()
+     * @see       #getAscent()
+     * @see       #getDescent()
+     */
+    public float getHeight();
+
+    /**
+     * Determines the maximum ascent of the <code>Font</code>
+     * described by this <code>FontMetrics</code> object.  No character
+     * extends further above the font's baseline than this height.
+     * @return    the maximum ascent of any character in the
+     * <code>Font</code>.
+     * @see       #getAscent()
+     */
+    public float getMaxAscent();
+
+    /**
+     * Determines the maximum descent of the <code>Font</code>
+     * described by this <code>FontMetrics</code> object.  No character
+     * extends further below the font's baseline than this height.
+     * @return    the maximum descent of any character in the
+     * <code>Font</code>.
+     * @see       #getDescent()
+     */
+    public float getMaxDescent();
+
+    /**
+     * Gets the maximum advance width of any character in this
+     * <code>Font</code>.  The advance is the
+     * distance from the leftmost point to the rightmost point on the
+     * string's baseline.  The advance of a <code>String</code> is
+     * not necessarily the sum of the advances of its characters.
+     * @return    the maximum advance width of any character
+     *            in the <code>Font</code>, or <code>-1</code> if the
+     *            maximum advance width is not known.
+     */
+    public float getMaxAdvance();
+
+    /**
+     * Returns the total advance width for showing the specified
+     * <code>String</code> in this <code>Font</code>.  The advance
+     * is the distance from the leftmost point to the rightmost point
+     * on the string's baseline.
+     * <p>
+     * Note that the advance of a <code>String</code> is
+     * not necessarily the sum of the advances of its characters.
+     * @param str the <code>String</code> to be measured
+     * @return    the advance width of the specified <code>String</code>
+     *                  in the <code>Font</code> described by this
+     *                  <code>FontMetrics</code>.
+     * @throws NullPointerException if str is null.
+     */
+    public float stringWidth(String str);
+
+    /**
+     * Returns the bounds for the character with the maximum bounds.
+     * @return a <code>Rectangle2f</code> that is the
+     * bounding box for the character with the maximum bounds.
+     */
+    public Rectangle2f getMaxCharBounds();
+    
+}

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/FontStyle.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/FontStyle.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/FontStyle.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,54 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector;
+
+
+
+/** Style of the fonts. 
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public enum FontStyle {
+
+	/**
+	 * The plain style constant.
+	 */
+	PLAIN,
+
+	/**
+	 * The bold style constant.
+	 */
+	BOLD,
+
+	/**
+	 * The italicized style constant.
+	 */
+	ITALIC,
+
+	/**
+	 * The bold and italic style constant.
+	 */
+	BOLD_ITALIC;
+
+}

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Image.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Image.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Image.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,90 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector;
+
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+
+
+/** Interface that is representing a color. 
+ * See {@link VectorToolkit} to create an instance.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Image {
+	
+	/**
+     * Determines the width of the image. If the width is not yet known,
+     * this method returns <code>-1</code> and the specified
+     * <code>ImageObserver</code> object is notified later.
+     * @param     observer   an object waiting for the image to be loaded.
+     * @return    the width of this image, or <code>-1</code>
+     *                   if the width is not yet known.
+     */
+    public int getWidth(ImageObserver observer);
+
+    /**
+     * Determines the height of the image. If the height is not yet known,
+     * this method returns <code>-1</code> and the specified
+     * <code>ImageObserver</code> object is notified later.
+     * @param     observer   an object waiting for the image to be loaded.
+     * @return    the height of this image, or <code>-1</code>
+     *                   if the height is not yet known.
+     */
+    public int getHeight(ImageObserver observer);
+    
+    /** Returns the total number of bands of image data.
+     *  @return the number of bands of image data.
+     */
+    public int getNumBands();
+    
+    /**
+     * Returns an integer pixel in the default RGB color model
+     * (TYPE_INT_ARGB) and default sRGB colorspace.
+     *
+     * @param x the X coordinate of the pixel from which to get
+     *          the pixel in the default RGB color model and sRGB
+     *          color space
+     * @param y the Y coordinate of the pixel from which to get
+     *          the pixel in the default RGB color model and sRGB
+     *          color space
+     * @return an integer pixel in the default RGB color model and
+     *          default sRGB colorspace.
+     */
+    public int getRGB(int x, int y);
+    
+    /** Replies the graphics context of the image; or <code>null</code>
+     * if there is no context available.
+     * 
+     * @return the graphics context.
+     */
+    public VectorGraphics2D getVectorGraphics();
+
+    /** Replies the raster of the image for the given area.
+     * 
+     * @param area is the area for which a raster must be returned.
+     * @return the raster.
+     */
+    public Raster getData(Rectangle2f area);
+    
+}

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/ImageObserver.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/ImageObserver.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/ImageObserver.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,48 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector;
+
+
+/** Interface that is an observer on image loading. 
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface ImageObserver {
+	
+	/**
+     * This method is called when information about an image which was
+     * previously requested using an asynchronous interface becomes
+     * available.
+     *
+     * @param     img   the image being observed.
+     * @param     x   the <i>x</i> coordinate.
+     * @param     y   the <i>y</i> coordinate.
+     * @param     width    the width.
+     * @param     height   the height.
+     * @return    <code>false</code> if the infoflags indicate that the
+     *            image is completely loaded; <code>true</code> otherwise.
+     */
+    public boolean imageUpdate(Image img, int x, int y, int width, int height);
+    
+}

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Margins.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Margins.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Margins.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,71 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector;
+
+/** Interface that is representing a generic margins. 
+ * See {@link VectorToolkit} to create an instance.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Margins {
+
+    /**
+     * Returns the size of the top margin.
+     * 
+     * @return the size.
+     */
+    public float top();
+
+    /**
+     * Returns the size of the left margin.
+     * 
+     * @return the size.
+     */
+    public float left();
+
+    /**
+     * Returns the size of the bottom margin.
+     * 
+     * @return the size.
+     */
+    public float bottom();
+
+    /**
+     * Returns the size of the right margin.
+     * 
+     * @return the size.
+     */
+    public float right();
+
+    /**
+     * Set the margins.
+     * 
+     * @param top
+     * @param left
+     * @param right
+     * @param bottom
+     */
+    public void set(float top, float left, float right, float bottom);
+
+}

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Paint.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Paint.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Paint.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,34 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector;
+
+
+/** Interface that is representing a painter. 
+ * See {@link VectorToolkit} to create an instance.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Paint {
+	//
+}

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/PathUtil.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/PathUtil.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/PathUtil.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,472 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.arakhne.afc.math.MathConstants;
+import org.arakhne.afc.math.MathUtil;
+import org.arakhne.afc.math.continous.object2d.Path2f;
+import org.arakhne.afc.math.continous.object2d.PathElement2f;
+import org.arakhne.afc.math.continous.object2d.Point2f;
+import org.arakhne.afc.math.continous.object2d.Vector2f;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.generic.Vector2D;
+import org.arakhne.afc.util.Pair;
+
+/** Vector-oriented utilities for splines. 
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class PathUtil {
+
+	/** The ratio used to compute the length of the tangent vectors
+	 * at the control points of a spline. The ratio is in
+	 * {@code (0;1]} and represents the ratio of the length of
+	 * the vector between two control points.
+	 */
+	public static final float SPLINE_TANGENT_SIZE_FACTOR = .25f ;
+
+	private static void computeBezierCtrlPoints(Point2D pts0, Point2D pts1, Point2D pts2, Point2D ctrl1, Point2D ctrl2) {		
+		float v0x = (pts0.getX() - pts1.getX());
+		float v0y = (pts0.getY() - pts1.getY());
+
+		float v1x = (pts2.getX() - pts1.getX());
+		float v1y = (pts2.getY() - pts1.getY());
+
+		float tangentx = (pts2.getX() - pts0.getX());
+		float tangenty = (pts2.getY() - pts0.getY());
+
+		float length = (float)Math.sqrt(tangentx*tangentx + tangenty*tangenty);
+		if (length==0f) {
+			// Assume left-handed coordinate system, then invert the perpendicular vector to obtain the tangent
+			tangentx = v0y;
+			tangenty = -v0x;
+			length = (float)Math.sqrt(tangentx*tangentx + tangenty*tangenty);
+			if (length==0f) {
+				ctrl1.set(pts0);
+				ctrl2.set(pts0);
+				return ;
+			}
+			tangentx /= length;
+			tangenty /= length;
+		}
+		else {
+			tangentx /= length;
+			tangenty /= length;
+		}
+
+		float length1 = (float)Math.sqrt(v0x*v0x + v0y*v0y) * SPLINE_TANGENT_SIZE_FACTOR;
+		float length2 = (float)Math.sqrt(v1x*v1x + v1y*v1y) * SPLINE_TANGENT_SIZE_FACTOR;
+
+		ctrl1.set(pts1.getX() - tangentx * length1, pts1.getY() - tangenty * length1);
+		ctrl2.set(pts1.getX() + tangentx * length2, pts1.getY() + tangenty * length2);
+	}
+	
+	/** Create a cubic spline which is passing at the specified
+	 * control points.
+	 * 
+	 * @param path is the path to build.
+	 * @param startTangent is set with the tangent to the first point.
+	 * @param endTangent is set with the tangent to the last point.
+	 * @param controlPath is, if not <code>null</code>, set with the path of 
+	 * all the specified control points of the splines and the intermeditate
+	 * control points.
+	 * @param segmentPath is, if not <code>null</code>, set with the path 
+	 * the specified control points of the splines.
+	 * @param controlPoints are the control point at which the spline must pass.
+	 */
+	public static void createCubicSpline(Path2f path, Vector2D startTangent, Vector2D endTangent, Path2f controlPath, Path2f segmentPath, List<? extends Point2D> controlPoints) {
+		assert(path!=null);
+		if (controlPoints.size()>2) {
+			Point2D pts0, pts1, pts2;
+			Point2D ctrl1, ctrl2;
+
+			// Compute the control points
+			pts0 = controlPoints.get(0);
+			pts1 = controlPoints.get(1);
+			Point2D pts00 = pts0;
+
+			Point2D[] ctrls = new Point2D[(controlPoints.size()-1)*2];
+
+			for(int i=2, j=1; i<controlPoints.size(); ++i, j+=2) {
+				pts2 = controlPoints.get(i);
+
+				ctrl1 = new Point2f();
+				ctrl2 = new Point2f();
+				computeBezierCtrlPoints(pts0, pts1, pts2, ctrl1, ctrl2);
+				assert(ctrls[j]==null);
+				ctrls[j] = ctrl1;
+				assert(ctrls[j+1]==null);
+				ctrls[j+1] = ctrl2;
+				
+				pts0 = pts1;
+				pts1 = pts2;
+			}
+
+			{
+				float vx = (ctrls[1].getX() - pts00.getX());
+				float vy = (ctrls[1].getY() - pts00.getY());
+				float length = (float)Math.sqrt(vx*vx + vy*vy);
+				ctrls[0] = new Point2f(
+						pts00.getX() + vx*SPLINE_TANGENT_SIZE_FACTOR,
+						pts00.getY() + vy*SPLINE_TANGENT_SIZE_FACTOR);
+				if (startTangent!=null) startTangent.set(vx/length, vy/length);
+			}
+
+			{
+				pts0 = controlPoints.get(controlPoints.size()-1);
+				pts1 = ctrls[ctrls.length-2];
+				float vx = (pts1.getX() - pts0.getX());
+				float vy = (pts1.getY() - pts0.getY());
+				float length = (float)Math.sqrt(vx*vx + vy*vy);
+				ctrls[ctrls.length-1] = new Point2f(
+						pts0.getX() + vx*SPLINE_TANGENT_SIZE_FACTOR,
+						pts0.getY() + vy*SPLINE_TANGENT_SIZE_FACTOR);
+				if (endTangent!=null) endTangent.set(vx/length, vy/length);
+			}
+
+			// Draw the spline
+			pts0 = controlPoints.get(0);
+			path.moveTo(pts0.getX(), pts0.getY());
+			if (controlPath!=null) {
+				controlPath.moveTo(pts0.getX(), pts0.getY());
+			}
+			if (segmentPath!=null) {
+				segmentPath.moveTo(pts0.getX(), pts0.getY());
+			}
+			
+			for(int i=1, j=0; i<controlPoints.size() && j<ctrls.length; ++i, j+=2) {
+				pts0 = controlPoints.get(i);
+				path.curveTo(
+						ctrls[j].getX(), ctrls[j].getY(),
+						ctrls[j+1].getX(), ctrls[j+1].getY(),
+						pts0.getX(), pts0.getY());
+				if (controlPath!=null) {
+					controlPath.lineTo(ctrls[j].getX(), ctrls[j].getY());
+					controlPath.lineTo(ctrls[j+1].getX(), ctrls[j+1].getY());
+					controlPath.lineTo(pts0.getX(), pts0.getY());
+				}
+				if (segmentPath!=null) {
+					segmentPath.lineTo(pts0.getX(), pts0.getY());
+				}
+			}
+		}
+		else if (controlPoints.size()==2) {
+			Point2D pts0, pts1;
+			pts0 = controlPoints.get(0);
+			pts1 = controlPoints.get(1);
+			path.moveTo(pts0.getX(), pts0.getY());
+			path.lineTo(pts1.getX(), pts1.getY());
+			startTangent.sub(pts1, pts0);
+			startTangent.normalize();
+			endTangent.sub(pts0, pts1);
+			endTangent.normalize();
+		}
+	}
+	
+	private static Vector2D computeTangent(Point2D p1, Point2D p2, Point2D p3) {
+		double x = ((p3.getX() - p1.getX()) * .5) + p1.getX() - p2.getX();
+		double y = ((p3.getY() - p1.getY()) * .5) + p1.getY() - p2.getY();
+		Vector2D v = new Vector2f((float)x, (float)y);
+		v.normalize();
+		v.perpendicularize();
+		return v;
+	}
+	
+	/** Create a quadratic spline which is passing at the specified
+	 * control points.
+	 * 
+	 * @param path is the path to build.
+	 * @param startTangent is set with the tangent to the first point.
+	 * @param endTangent is set with the tangent to the last point.
+	 * @param controlPath is, if not <code>null</code>, set with the path of 
+	 * all the specified control points of the splines and the intermeditate
+	 * control points.
+	 * @param segmentPath is, if not <code>null</code>, set with the path 
+	 * the specified control points of the splines.
+	 * @param controlPoints are the control point at which the spline must pass.
+	 */
+	public static void createQuadraticSpline(Path2f path, Vector2D startTangent, Vector2D endTangent, Path2f controlPath, Path2f segmentPath, List<? extends Point2D> controlPoints) {
+		if (controlPoints.size()>2) {
+			Point2D pts0, pts1, pts2;
+
+			// Compute the control points
+			pts0 = controlPoints.get(0);
+			pts1 = controlPoints.get(1);
+
+			Vector2D[] tangents = new Vector2D[controlPoints.size()-2];
+			Point2D[] ctrls = new Point2D[controlPoints.size()-1];
+
+			for(int i=2, j=0; i<controlPoints.size(); ++i, ++j) {
+				pts2 = controlPoints.get(i);
+				
+				tangents[i-2] = computeTangent(pts0, pts1, pts2);
+				
+				if (i>2) {
+					ctrls[j] = MathUtil.computeLineIntersection(pts0, tangents[i-3], pts1, tangents[i-2]);
+				}
+				
+				pts0 = pts1;
+				pts1 = pts2;
+			}
+			
+			{
+				pts0 = controlPoints.get(0);
+				pts1 = controlPoints.get(1);
+				Vector2D v = new Vector2f();
+				v.sub(pts1, pts0);
+				v.scale(.5f);
+				Point2D c = new Point2f(
+						pts0.getX()+v.getX(),
+						pts0.getY()+v.getY());
+				v.perpendicularize();
+				ctrls[0] = MathUtil.computeLineIntersection(c, v, pts1, tangents[0]);
+
+				startTangent.sub(ctrls[0], pts0);
+				startTangent.normalize();
+			}
+			
+			{
+				pts0 = controlPoints.get(controlPoints.size()-1);
+				pts1 = controlPoints.get(controlPoints.size()-2);
+				Vector2D v = new Vector2f();
+				v.sub(pts1, pts0);
+				v.scale(.5f);
+				Point2D c = new Point2f(
+						pts0.getX()+v.getX(),
+						pts0.getY()+v.getY());
+				v.perpendicularize();
+				ctrls[ctrls.length-1] = MathUtil.computeLineIntersection(c, v, pts1, tangents[tangents.length-1]);
+
+				endTangent.sub(ctrls[ctrls.length-1], pts0);
+				endTangent.normalize();
+			}
+
+			// Draw the spline
+			pts0 = controlPoints.get(0);
+			path.moveTo(pts0.getX(), pts0.getY());
+			if (controlPath!=null) {
+				controlPath.moveTo(pts0.getX(), pts0.getY());
+			}
+			if (segmentPath!=null) {
+				segmentPath.moveTo(pts0.getX(), pts0.getY());
+			}
+			
+			for(int i=1, j=0; i<controlPoints.size() && j<ctrls.length; ++i, ++j) {
+				pts0 = controlPoints.get(i);
+				path.quadTo(ctrls[j].getX(), ctrls[j].getY(), pts0.getX(), pts0.getY());
+				if (controlPath!=null) {
+					controlPath.lineTo(ctrls[j].getX(), ctrls[j].getY());
+					controlPath.lineTo(pts0.getX(), pts0.getY());
+				}
+				if (segmentPath!=null) {
+					segmentPath.lineTo(pts0.getX(), pts0.getY());
+				}
+			}
+		}
+		else if (controlPoints.size()==2) {
+			Point2D pts0, pts1;
+			pts0 = controlPoints.get(0);
+			pts1 = controlPoints.get(1);
+			path.moveTo(pts0.getX(), pts0.getY());
+			path.lineTo(pts1.getX(), pts1.getY());
+			startTangent.sub(pts1, pts0);
+			startTangent.normalize();
+			endTangent.sub(pts0, pts1);
+			endTangent.normalize();
+		}
+	}
+
+	/** Create the polyline that is passing at the specified control points.
+	 * 
+	 * @param path is the path to fill.
+	 * @param startTangent is, if not <code>null</code>, set to the tangent at the first point.
+	 * @param endTangent is, if not <code>null</code>, set to the tangent at the last point.
+	 * @param controlPoints are the control points to pass at.
+	 */
+	public static void createSegments(Path2f path, Vector2D startTangent, Vector2D endTangent, List<? extends Point2D> controlPoints) {
+		Point2D pts = controlPoints.get(0);
+		Point2D ppts = pts;
+		path.moveTo(pts.getX(), pts.getY());
+		for(int i=1; i<controlPoints.size(); ++i) {
+			pts = controlPoints.get(i);
+			if (i==1 && startTangent!=null) {
+				startTangent.sub(pts, ppts);
+				startTangent.normalize();
+			}
+			if (i==controlPoints.size()-1 && endTangent!=null) {
+				endTangent.sub(ppts, pts);
+				endTangent.normalize();
+			}
+			path.lineTo(pts.getX(), pts.getY());
+			ppts = pts;
+		}
+	}
+
+	/** Compute the length of the segments along the specified path.
+	 * The size of the array <var>lengths</var> must be the same as
+	 * the size of the list <var>controlPoints</var>.
+	 * 
+	 * @param path is the path to traverse.
+	 * @param lengths is the array to fill with the lengths.
+	 * @param controlPoints are the control points between the segments.
+	 */
+	public static void computeSegmentLengths(Path2f path, float[] lengths, List<? extends Point2D> controlPoints) {
+		assert(lengths.length==controlPoints.size());
+		Iterator<PathElement2f> pathIterator = path.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
+		int index = 0 ;
+		
+		PathElement2f pathElement;
+
+		// Checks all segments
+		while (pathIterator.hasNext()) {
+			pathElement = pathIterator.next();
+			
+			if ( ( index < 0 ) || ( index >= controlPoints.size() ) ) {
+				index = -1 ;
+			}
+			else if( controlPoints.get(index+1).equals( 
+					new Point2f( pathElement.toX, pathElement.toY ) ) ) {
+				index ++ ;
+				if (index<lengths.length)
+					lengths[index] = 0;
+			}
+			switch( pathElement.type ) {
+			case MOVE_TO:
+				break;
+			case LINE_TO:
+				lengths[index] += MathUtil.distancePointToPoint(
+							pathElement.fromX, pathElement.fromY,
+							pathElement.toX, pathElement.toY);
+				break;
+			case CLOSE:
+				lengths[index] += MathUtil.distancePointToPoint(
+						pathElement.fromX, pathElement.fromY,
+						pathElement.toX, pathElement.toY);
+				break;
+			default:
+				throw new IllegalStateException();
+			}		
+		}
+	}
+	
+	/** Compute the point along the specified path and which
+	 * is located at the specified position.
+	 * 
+	 * @param path is the path.
+	 * @param factor is the position factor, between 0 and 1.
+	 * @return the point; never <code>null</code>
+	 */
+	public static Point2D interpolate(Path2f path, float factor) {
+		return interpolate(path, factor, null);
+	}
+
+	/** Compute the point along the specified path and which
+	 * is located at the specified position.
+	 * 
+	 * @param path is the path.
+	 * @param factor is the position factor, between 0 and 1.
+	 * @param normal will contain the normal at the specified point.
+	 * @return the point; never <code>null</code>
+	 */
+	public static Point2D interpolate(Path2f path, float factor, Vector2D normal) {
+		Iterator<PathElement2f> iterator = path.getPathIterator(MathConstants.SPLINE_APPROXIMATION_RATIO);
+		PathElement2f pathElement;
+
+		Point2D firstPoint = null;
+		Point2D lastPoint = null;
+		
+		float length = 0;
+		List<Pair<Float,Point2D>> points = new ArrayList<Pair<Float,Point2D>>();
+		float d;
+		while (iterator.hasNext()) {
+			pathElement = iterator.next();
+			switch(pathElement.type) {
+			case MOVE_TO:
+				lastPoint = new Point2f(pathElement.toX, pathElement.toY);
+				if (firstPoint==null) firstPoint = lastPoint;
+				points.add(new Pair<Float,Point2D>(length, lastPoint));
+				break;
+			case LINE_TO:
+			case CLOSE:
+				d = MathUtil.distanceSquaredPointToPoint(
+						pathElement.fromX, pathElement.fromY,
+						pathElement.toX, pathElement.toY);
+				length += d;
+				lastPoint = new Point2f(pathElement.toX, pathElement.toY);
+				points.add(new Pair<Float,Point2D>(length, lastPoint));
+				break;
+			default:
+				throw new IllegalStateException();
+			}
+		}
+		
+		float distance = factor * length;
+		
+		Pair<Float,Point2D> pair;
+		int l = 0;
+		int r = points.size()-1;
+		int c;
+		while (l<r) {
+			c = (l+r)/2;
+			pair = points.get(c);
+			if (distance==pair.getA()) {
+				return pair.getB();
+			}
+			if (distance<pair.getA()) {
+				r = c-1;
+			}
+			else {
+				l = c+1;
+			}
+		}
+		
+		if (l<=0) return firstPoint;
+		if (l>=points.size()) return lastPoint;
+		
+		pair = points.get(l-1);
+		distance -= pair.getA();
+		lastPoint = points.get(l).getB();
+		
+		distance /= pair.getB().distanceSquared(lastPoint);
+		
+		if (normal!=null) {
+			normal.set(lastPoint.getX(), lastPoint.getY());
+			normal.sub(pair.getB().getX(), pair.getB().getY());
+			normal.perpendicularize();
+			normal.normalize();
+		}
+		org.arakhne.afc.math.generic.Point2D p = MathUtil.interpolate(
+				pair.getB().getX(),
+				pair.getB().getY(),
+				lastPoint.getX(),
+				lastPoint.getY(),
+				distance);
+		return new Point2f(p.getX(), p.getY());
+	}
+
+}

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Pdf.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Pdf.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Pdf.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,87 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector;
+
+
+
+/** Wrapper on a PDF API able to extract bitmaps from PDF documents.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Pdf {
+	
+	/** Replies the image of the current page.
+	 * 
+	 * @return the image or <code>null</code> if the image cannot
+	 * be obtained.
+	 */
+	public Image getImage();
+	
+	/** Set the dimension of the viewer.
+	 * 
+	 * @param dimension
+	 */
+	public void setViewerSize(Dimension dimension);
+	
+	/** Replies the current page number that is replied by {@link #getImage()}.
+	 * 
+	 * @return the current page number.
+	 */
+	public int getPageNumber();
+	
+	/** Set the current page number that is replied by {@link #getImage()}.
+	 * 
+	 * @param pageno is the current page number.
+	 * @param observer is the observer of the image loading.
+	 * @return <code>true</code> if the page number has changed.
+	 */
+	public boolean setPageNumber(int pageno, ImageObserver observer);
+
+	/** Replies the total number of pages in the PDF document.
+	 * 
+	 * @return the total number of pages.
+	 */
+	public int getPageCount();
+	
+	/** Stop any asynchronous page loading.
+	 */
+	public void stopPageLoading();
+	
+	/** Release any internal resources.
+	 */
+	public void release();
+	
+	/** Replies the width of the PDF page.
+	 * 
+	 * @return the width or Float.NaN.
+	 */
+	public float getPageWidth();
+
+	/** Replies the height of the PDF page.
+	 * 
+	 * @return the height or Float.NaN.
+	 */
+	public float getPageHeight();
+
+}

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Raster.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Raster.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Raster.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,54 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector;
+
+
+/** Interface that is representing an image raster. 
+ * See {@link VectorToolkit} to create an instance.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Raster {
+
+	/** Returns the total number of bands of image data.
+     *  @return the number of bands of image data.
+     */
+    public int getNumBands();
+    
+    /**
+     * Returns the samples in an array of int for the specified pixel.
+     * An ArrayIndexOutOfBoundsException may be thrown
+     * if the coordinates are not in bounds.  However, explicit bounds
+     * checking is not guaranteed.
+     * 
+     * @param x The X coordinate of the pixel location
+     * @param y The Y coordinate of the pixel location
+     * @param samples An optionally preallocated int array
+     * @return the samples for the specified pixel.
+     * @throws ArrayIndexOutOfBoundsException if the coordinates are not
+     * in bounds, or if iArray is too small to hold the output.
+     */
+    public int[] getPixel(int x, int y, int[] samples);
+    
+}

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Stroke.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Stroke.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/Stroke.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,158 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector;
+
+
+
+/** Interface that is representing a stroke. 
+ * See {@link VectorToolkit} to create an instance.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Stroke {
+
+	/** Default line join.
+	 */
+	public LineJoin DEFAULT_LINE_JOIN = LineJoin.MITER;
+	
+	/** Default end cap.
+	 */
+	public EndCap DEFAULT_END_CAP = EndCap.SQUARE;
+	
+	/** Default miter limit.
+	 */
+	public float DEFAULT_MITTER_LIMIT = 10f;
+
+	/**
+	 * Returns the array representing the lengths of the dash segments.
+	 * Alternate entries in the array represent the user space lengths
+	 * of the opaque and transparent segments of the dashes.
+	 * As the pen moves along the outline of the <code>Shape</code>
+	 * to be stroked, the user space
+	 * distance that the pen travels is accumulated.  The distance
+	 * value is used to index into the dash array.
+	 * The pen is opaque when its current cumulative distance maps
+	 * to an even element of the dash array and transparent otherwise.
+	 * 
+	 * @return the dash array.
+	 */
+	public float[] getDashArray();
+
+	/**
+	 * Returns the current dash phase.
+	 * The dash phase is a distance specified in user coordinates that
+	 * represents an offset into the dashing pattern. In other words, the dash
+	 * phase defines the point in the dashing pattern that will correspond to
+	 * the beginning of the stroke.
+	 * 
+	 * @return the dash phase as a <code>float</code> value.
+	 */
+	public float getDashPhase();
+
+	/** Replies the width of the line.
+	 * 
+	 * @return the width of the line.
+	 */
+	public float getLineWidth();
+
+	/** Replies the type of the line join.
+	 * 
+	 * @return the type of the line join.
+	 */
+	public LineJoin getLineJoin();
+
+	/** Replies the type of the end cap for the stroke.
+	 * 
+	 * @return the type of the end cap for the stroke.
+	 */
+	public EndCap getEndCap();
+
+	/**
+	 * Returns the limit of miter joins.
+	 * 
+	 * @return the limit of miter joins.
+	 */
+	public float getMiterLimit();
+
+	/** Define the types of line joins for a stroke. 
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public enum LineJoin {
+
+		/**
+		 * Joins path segments by extending their outside edges until
+		 * they meet.
+		 */
+		MITER,
+
+		/**
+		 * Joins path segments by rounding off the corner at a radius
+		 * of half the line width.
+		 */
+		ROUND,
+
+		/**
+		 * Joins path segments by connecting the outer corners of their
+		 * wide outlines with a straight segment.
+		 */
+		BEVEL;
+
+	}
+
+	/** Define the types of end caps for a stroke. 
+	 *
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	public enum EndCap {
+
+		/**
+		 * Ends unclosed subpaths and dash segments with no added
+		 * decoration.
+		 */
+		BUTT,
+
+		/**
+		 * Ends unclosed subpaths and dash segments with a round
+		 * decoration that has a radius equal to half of the width
+		 * of the pen.
+		 */
+		ROUND,
+
+		/**
+		 * Ends unclosed subpaths and dash segments with a square
+		 * projection that extends beyond the end of the segment
+		 * to a distance equal to half of the line width.
+		 */
+		SQUARE;
+
+	}
+
+}

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/VectorGraphics2D.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/VectorGraphics2D.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/VectorGraphics2D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,539 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+
+package org.arakhne.afc.ui.vector;
+
+import java.net.URL;
+
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.math.continous.object2d.Shape2f;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+import org.arakhne.afc.ui.Graphics2DLOD;
+import org.arakhne.afc.ui.StringAnchor;
+import org.arakhne.afc.ui.TextAlignment;
+
+/** This graphic context permits to display
+ *  something with a vectorial approach.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface VectorGraphics2D {
+	
+	/** Release all the allocated resources.
+	 */
+	public void dispose();
+	
+	/** Reset the graphical context to its default state if possible.
+	 */
+	public void reset();
+
+	/** Replies the LOD.
+	 * 
+	 * @return the LOD.
+	 */
+	public Graphics2DLOD getLOD();
+
+	/** Replies if the objects are filled.
+	 * 
+	 * @return <code>true</code> if the objects are filled;
+	 * otherwise <code>false</code>.
+	 */
+	public boolean isInteriorPainted();
+
+	/** Set if the objects are filled.
+	 * 
+	 * @param painted is <code>true</code> if the objects are filled;
+	 * otherwise <code>false</code>.
+	 */
+	public void setInteriorPainted(boolean painted);
+
+	/** Replies if the objects are outlined.
+	 * 
+	 * @return <code>true</code> if the objects are outlined;
+	 * otherwise <code>false</code>.
+	 */
+	public boolean isOutlineDrawn();
+
+	/** Set if the objects are outlined.
+	 * 
+	 * @param outlined is <code>true</code> if the objects are outlined;
+	 * otherwise <code>false</code>.
+	 */
+	public void setOutlineDrawn(boolean outlined);
+
+	/** Replies if the text to put inside the next dranw shape.
+	 * 
+	 * @return the text to put in the interior
+	 * of the next shape to be drawn.
+	 */
+	public String getInteriorText();
+
+	/** Set if the text to put inside the next dranw shape.
+	 * 
+	 * @param interiorText is the text to put in the interior
+	 * of the next shape to be drawn.
+	 */
+	public void setInteriorText(String interiorText);
+
+	/** Replies the anchor for the strings to draw.
+	 * 
+	 * @return the anchor for the strings.
+	 */
+	public StringAnchor getStringAnchor();
+	
+	/** Draws a pixel.
+	 *  <p>
+	 *  The given coordinates will be transformed.
+	 *
+	 * @param x is the location where to paint the pixel.
+	 * @param y is the location where to paint the pixel.
+	 */
+	public void drawPoint(float x, float y);
+
+	/**
+	 * Gets the current font.
+	 * @return    this graphics context's current font.
+	 */
+	public Font getFont();
+
+	/**
+	 * Gets the default font.
+	 * @return    this graphics default font.
+	 */
+	public Font getDefaultFont();
+
+	/**
+	 * Sets the current font.
+	 * @param font is this graphics context's current font.
+	 */
+	public void setFont(Font font);
+
+	/**
+	 * Gets the font metrics of the current font.
+	 * @return    the font metrics of this graphics 
+	 *                    context's current font.
+	 */
+	public FontMetrics getFontMetrics();
+
+	/**
+	 * Gets the font metrics for the specified font.
+	 * @return    the font metrics for the specified font.
+	 * @param     f the specified font
+	 */
+	public FontMetrics getFontMetrics(Font f);
+
+	/**
+	 * Gets the current clipping area.
+	 * This method returns the user clip, which is independent of the
+	 * clipping associated with device bounds and window visibility.
+	 * If no clip has previously been set, or if the clip has been 
+	 * cleared using <code>setClip(null)</code>, this method returns 
+	 * <code>null</code>.
+	 * @return      a <code>Shape</code> object representing the 
+	 *              current clipping area, or <code>null</code> if
+	 *              no clip is set.
+	 */
+	public Shape2f getClip();
+
+	/**
+	 * Sets the current clipping area to an arbitrary clip shape.
+	 * @param clip
+	 */
+	public void setClip(Shape2f clip);
+
+	/**
+	 * Sets the current clipping area are the union
+	 * of the current clipping and an arbitrary clip shape.
+	 * @param clip
+	 */
+	public void clip(Shape2f clip);
+
+	/**
+	 * Draws a default image inside the given bounds.
+	 * The image depends on the background implementation. 
+	 * 
+	 * @param       dx1 the <i>x</i> coordinate of the first corner of the
+	 *                    destination rectangle.
+	 * @param       dy1 the <i>y</i> coordinate of the first corner of the
+	 *                    destination rectangle.
+	 * @param       dx2 the <i>x</i> coordinate of the second corner of the
+	 *                    destination rectangle.
+	 * @param       dy2 the <i>y</i> coordinate of the second corner of the
+	 *                    destination rectangle.
+	 */
+	public void drawDefaultImage(float dx1, float dy1, float dx2, float dy2);
+
+	/**
+	 * Draws as much of the specified area of the specified image as is
+	 * currently available, scaling it on the fly to fit inside the
+	 * specified area of the destination drawable surface. Transparent pixels 
+	 * do not affect whatever pixels are already there.
+	 * 
+	 * @param       imageURL is the URL of the image that is drawn.
+	 * @param       img the specified image to be drawn. This method does
+	 *                  nothing if <code>img</code> is null.
+	 * @param       dx1 the <i>x</i> coordinate of the first corner of the
+	 *                    destination rectangle.
+	 * @param       dy1 the <i>y</i> coordinate of the first corner of the
+	 *                    destination rectangle.
+	 * @param       dx2 the <i>x</i> coordinate of the second corner of the
+	 *                    destination rectangle.
+	 * @param       dy2 the <i>y</i> coordinate of the second corner of the
+	 *                    destination rectangle.
+	 * @param       sx1 the <i>x</i> coordinate of the first corner of the
+	 *                    source rectangle.
+	 * @param       sy1 the <i>y</i> coordinate of the first corner of the
+	 *                    source rectangle.
+	 * @param       sx2 the <i>x</i> coordinate of the second corner of the
+	 *                    source rectangle.
+	 * @param       sy2 the <i>y</i> coordinate of the second corner of the
+	 *                    source rectangle.
+	 * @return   <code>false</code> if the image pixels are still changing;
+	 *           <code>true</code> otherwise.
+	 */
+	public boolean drawImage(URL imageURL, Image img, float dx1, float dy1, float dx2, float dy2, int sx1, int sy1, int sx2, int sy2);
+
+	/**
+	 * Draws as much of the specified area of the specified image as is
+	 * currently available, scaling it on the fly to fit inside the
+	 * specified area of the destination drawable surface. Transparent pixels 
+	 * do not affect whatever pixels are already there.
+	 * 
+	 * @param       imageURL is the URL of the image that is drawn.
+	 * @param       img the specified image to be drawn. This method does
+	 *                  nothing if <code>img</code> is null.
+	 * @param       dx1 the <i>x</i> coordinate of the first corner of the
+	 *                    destination rectangle.
+	 * @param       dy1 the <i>y</i> coordinate of the first corner of the
+	 *                    destination rectangle.
+	 * @param       dx2 the <i>x</i> coordinate of the second corner of the
+	 *                    destination rectangle.
+	 * @param       dy2 the <i>y</i> coordinate of the second corner of the
+	 *                    destination rectangle.
+	 * @param       sx1 the <i>x</i> coordinate of the first corner of the
+	 *                    source rectangle.
+	 * @param       sy1 the <i>y</i> coordinate of the first corner of the
+	 *                    source rectangle.
+	 * @param       sx2 the <i>x</i> coordinate of the second corner of the
+	 *                    source rectangle.
+	 * @param       sy2 the <i>y</i> coordinate of the second corner of the
+	 *                    source rectangle.
+	 * @param       observer is the observer of the image.
+	 * @return   <code>false</code> if the image pixels are still changing;
+	 *           <code>true</code> otherwise.
+	 */
+	public boolean drawImage(URL imageURL, Image img, float dx1, float dy1, float dx2, float dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer);
+
+	/**
+	 * Strokes the outline of a <code>Shape</code> using the settings of the 
+	 * current <code>VectorGraphics2D</code> context.  The rendering attributes
+	 * applied include the <code>Clip</code>, <code>Transform</code>,
+	 * <code>Paint</code>, <code>Composite</code> and 
+	 * <code>Stroke</code> attributes.
+	 * @param s the <code>Shape</code> to be rendered
+	 */
+	public void draw(Shape2f s);
+
+	/**
+	 * Renders the text specified by the specified <code>String</code>, 
+	 * using the current text attribute state in the <code>VectorGraphics2D</code> context.
+	 * The specified position depends on the text anchor replied by
+	 * {@lni #getStringAnchor()}.
+	 *  
+	 * @param str the <code>String</code> to be rendered
+	 * @param x the x coordinate of the location where the
+	 * <code>String</code> should be rendered
+	 * @param y the y coordinate of the location where the
+	 * <code>String</code> should be rendered
+	 * @throws NullPointerException if <code>str</code> is
+	 *         <code>null</code>
+	 */
+	public void drawString(String str, float x, float y);
+
+	/**
+	 * Renders the text specified by the specified <code>String</code>, 
+	 * using the current text attribute state in the <code>VectorGraphics2D</code> context.
+	 * The specified position depends on the text anchor replied by
+	 * {@lni #getStringAnchor()}.
+	 *  
+	 * @param str the <code>String</code> to be rendered
+	 * @param x the x coordinate of the location where the
+	 * <code>String</code> should be rendered
+	 * @param y the y coordinate of the location where the
+	 * <code>String</code> should be rendered
+	 * @param clip is the shape outside which the text should not be rendered.
+	 * @throws NullPointerException if <code>str</code> is
+	 *         <code>null</code>
+	 */
+	public void drawString(String str, float x, float y, Shape2f clip);
+
+	/**
+	 * Composes an <code>Transform2D</code> object with the 
+	 * <code>Transform</code> in this <code>VectorGraphics2D</code> according 
+	 * to the rule last-specified-first-applied.  If the current
+	 * <code>Transform</code> is Cx, the result of composition
+	 * with Tx is a new <code>Transform</code> Cx'.  Cx' becomes the
+	 * current <code>Transform</code> for this <code>VectorGraphics2D</code>.
+	 * Transforming a point p by the updated <code>Transform</code> Cx' is
+	 * equivalent to first transforming p by Tx and then transforming
+	 * the result by the original <code>Transform</code> Cx.  In other
+	 * words, Cx'(p) = Cx(Tx(p)).  A copy of the Tx is made, if necessary,
+	 * so further modifications to Tx do not affect rendering.
+	 * @param Tx the <code>Transform2D</code> object to be composed with 
+	 * the current <code>Transform</code>
+	 */
+	public void transform(Transform2D Tx);
+
+	/** CConcatenates the current <code>VectorGraphics2D</code>
+     * <code>Transform2D</code> 
+	 * with a translation transformation.
+     * <p>
+     * This is equivalent to calling <code>transform(R)</code>, where R is an
+     * <code>AffineTransform</code> represented by the following matrix:
+     * <pre>
+     *          [   0    0    tx  ]
+     *          [   0    0    ty  ]
+     *          [   0    0    1   ]
+     * </pre>
+     * 
+     * @param tx is the translation along x.
+     * @param ty is the translation along x.
+     */
+	public void translate(float tx, float ty);
+	
+	/** Concatenates the current <code>VectorGraphics2D</code>
+     * <code>Transform2D</code> 
+	 * with a scaling transformation.
+     * <p>
+     * This is equivalent to calling <code>transform(R)</code>, where R is an
+     * <code>AffineTransform</code> represented by the following matrix:
+     * <pre>
+     *          [   sx   0    0   ]
+     *          [   0    sy   0   ]
+     *          [   0    0    1   ]
+     * </pre>
+     * 
+     * @param sx is the scaling along x.
+     * @param sy is the scaling along x.
+     */
+	public void scale(float sx, float sy);
+	
+	/**
+     * Concatenates the current <code>VectorGraphics2D</code>
+     * <code>Transform2D</code> with a rotation transform.
+     * Subsequent rendering is rotated by the specified radians relative
+     * to the previous origin.
+     * <p>
+     * This is equivalent to calling <code>transform(R)</code>, where R is an
+     * <code>AffineTransform</code> represented by the following matrix:
+     * <pre>
+     *          [   cos(theta)    -sin(theta)    0   ]
+     *          [   sin(theta)     cos(theta)    0   ]
+     *          [       0              0         1   ]
+     * </pre>
+     * 
+     * @param theta the angle of rotation in radians
+     */
+    public abstract void rotate(float theta);
+
+    /**
+     * Concatenates the current <code>VectorGraphics2D</code>
+     * <code>Transform2D</code> with a shearing transform.
+     * <p>
+     * This is equivalent to calling <code>transform(SH)</code>, where SH
+     * is an <code>AffineTransform</code> represented by the following
+     * matrix:
+     * <pre>
+     *          [   1   shx   0   ]
+     *          [  shy   1    0   ]
+     *          [   0    0    1   ]
+     * </pre>
+     * 
+     * @param shx the multiplier by which coordinates are shifted in
+     * the positive X axis direction as a function of their Y coordinate
+     * @param shy the multiplier by which coordinates are shifted in
+     * the positive Y axis direction as a function of their X coordinate
+     */
+    public abstract void shear(float shx, float shy);
+    
+	/**
+	 * Overwrites the Transform in the <code>VectorGraphics2D</code> context.
+	 * WARNING: This method should <b>never</b> be used to apply a new
+	 * coordinate transform on top of an existing transform because the 
+	 * <code>VectorGraphics2D</code> might already have a transform that is
+	 * needed for other purposes, such as rendering Swing 
+	 * components or applying a scaling transformation to adjust for the
+	 * resolution of a printer.  
+	 * <p>To add a coordinate transform, use the 
+	 * <code>transform</code>, <code>rotate</code>, <code>scale</code>,
+	 * or <code>shear</code> methods.  The <code>setTransform</code> 
+	 * method is intended only for restoring the original 
+	 * <code>VectorGraphics2D</code> transform after rendering, as shown in this
+	 * example:
+	 * <pre><blockquote>
+	 * // Get the current transform
+	 * Transform2D saveAT = g2.getTransform();
+	 * // Perform transformation
+	 * g2d.transform(...);
+	 * // Render
+	 * g2d.draw(...);
+	 * // Restore original transform
+	 * g2d.setTransform(saveAT);
+	 * </blockquote></pre>
+	 *
+	 * @param Tx the <code>Transform2D</code> that was retrieved
+	 *           from the <code>getTransform</code> method
+	 * @return the previous transformation.
+	 */
+	public Transform2D setTransform(Transform2D Tx);
+
+	/**
+	 * Returns a copy of the current <code>Transform</code> in the 
+	 * <code>VectorGraphics2D</code> context.
+	 * @return the current <code>Transform2D</code> in the 
+	 *             <code>VectorGraphics2D</code> context.
+	 */
+	public Transform2D getTransform();
+
+	/**
+	 * Sets the background color for the <code>VectorGraphics2D</code> context. 
+	 * The background color is used for clearing a region.
+	 * Setting the background color 
+	 * in the <code>VectorGraphics2D</code> context only affects the subsequent      
+	 * <code>clear</code> calls and not the background color of the  
+	 * graphical component.
+	 * @param color the background color that issued in
+	 * subsequent calls to <code>clear</code>
+	 */
+	public void setBackground(Color color);
+
+	/**
+	 * Returns the background color used for clearing a region.
+	 * @return the current <code>VectorGraphics2D</code> <code>Color</code>,
+	 * which defines the background color.
+	 */
+	public Color getBackground();
+
+	/**
+	 * Clear the area covered by the given shape.
+	 * The background color is used to fill the area.
+	 * <p>
+	 * This function is equivalent to:
+	 * <pre><code>
+	 * c = this.getFillColor();
+	 * this.setFillColor(this.getBackgroundColor());
+	 * this.fill(s);
+	 * this.setFillColor(c);
+	 * </code></pre>
+	 * 
+	 * @param s is the area to clear.
+	 */
+	public void clear(Shape2f s);
+
+	/**
+	 * Sets the object that permits to paint the interior of the objects.
+	 * 
+	 * @param paint the paint
+	 * @return the old paint.
+	 */
+	public Paint setPaint(Paint paint);
+	
+	/**
+	 * Sets the outline color.
+	 * 
+	 * @param color is the foreground color.
+	 * @return the old color.
+	 */
+	public Color setOutlineColor(Color color);
+	
+	/**
+	 * Sets the fill color.
+	 * 
+	 * @param color is the filling color.
+	 * @return the old color
+	 */
+	public Color setFillColor(Color color);
+
+	/** Replies the outline color.
+	 * 
+	 * @return the outline color.
+	 */
+	public Color getOutlineColor();
+	
+	/** Replies the fill color.
+	 * 
+	 * @return the fill color.
+	 */
+	public Color getFillColor();
+
+	/**
+	 * Returns the object that permits to paint the interior of the objects.
+	 * 
+	 * @return the paint.
+	 */
+	public Paint getPaint();
+
+	/**
+	 * Sets the composite.
+	 * 
+	 * @param composite the composite
+	 */
+	public void setComposite(Composite composite);
+	
+	/**
+	 * Returns the composite.
+	 * 
+	 * @return the composite.
+	 */
+	public Composite getComposite();
+
+	/**
+	 * Sets the stroke.
+	 * 
+	 * @param stroke the stroke.
+	 */
+	public void setStroke(Stroke stroke);
+	
+	/**
+	 * Returns the stroke.
+	 * 
+	 * @return the stroke.
+	 */
+	public Stroke getStroke();
+
+	/** Compute the position of a text with the specified alignements
+	 * in the specified bounds.
+	 *
+	 * @param text is the text for which the position should be computed.
+	 * @param bounds are the bounds to consider
+	 * @param halign is the horizontal alignment.
+	 * @param valign is the vertical alignment.
+	 * @return the position of the text.
+	 */
+	public Point2D computeTextPosition(String text, Rectangle2f bounds, TextAlignment halign, TextAlignment valign);
+	
+}
\ No newline at end of file

Added: trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/VectorToolkit.java
===================================================================
--- trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/VectorToolkit.java	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/java/org/arakhne/afc/ui/vector/VectorToolkit.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,945 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.arakhne.afc.math.continous.object2d.Shape2f;
+import org.arakhne.afc.math.matrix.Transform2D;
+import org.arakhne.afc.ui.vector.Stroke.EndCap;
+import org.arakhne.afc.ui.vector.Stroke.LineJoin;
+import org.arakhne.vmutil.locale.Locale;
+
+/** Utilities to create the widgets according
+ * to the currently graphical API.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class VectorToolkit {
+	
+	private static VectorToolkit SINGLETON; 
+	
+	/** Replies the current generic windows toolkit.
+	 * 
+	 * @return the current generic windows toolkit.
+	 */
+	public static VectorToolkit getDefault() {
+		return SINGLETON;
+	}
+	
+	/** Register a type as Windows Toolkit.
+	 * 
+	 * @param type
+	 */
+	@SuppressWarnings("unchecked")
+	private static void init() {
+		Throwable error = null;
+		String toolkits = Locale.getString("WINDOW_TOOLKITS"); //$NON-NLS-1$
+		if (toolkits!=null && !"WINDOW_TOOLKITS".equals(toolkits)) { //$NON-NLS-1$
+			String[] classes = toolkits.split("[ \t\n\f]*;[ \t\n\f]*"); //$NON-NLS-1$
+		
+			List<Class<? extends VectorToolkit>> types = new ArrayList<Class<? extends VectorToolkit>>();
+			
+			for(String className : classes) {
+				try {
+					Class<?> type = Class.forName(className);
+					if (VectorToolkit.class.isAssignableFrom(type)) {
+						types.add((Class<? extends VectorToolkit>)type);
+					}
+				}
+				catch(Throwable _) {
+					//
+				}
+			}
+			
+			if (!types.isEmpty()) {
+				for(Class<? extends VectorToolkit> type : types) {
+					try {
+						VectorToolkit tk = type.newInstance();
+						if (tk.isSupported()) {
+							SINGLETON = tk;
+							return;
+						}
+					}
+					catch(Throwable e) {
+						if (error==null) error = e;
+					}
+				}
+			}
+		}
+		
+		throw new Error("Unable to find a generic Window Toolkit for: "+toolkits, error); //$NON-NLS-1$
+	}
+	
+	/** Replies if this toolkit may be used according to the current JVM settings.
+	 * 
+	 * @return <code>true</code> if the OS and the JVM are supported, otherwise <code>false</code>.
+	 */
+	protected abstract boolean isSupported();
+	
+	/** Cast the given object into an UI native object.
+	 * 
+	 * @param type is the desired type.
+	 * @param o is the object to cast.
+	 * @return the casted value.
+	 */
+	public static <T> T nativeUIObject(Class<T> type, Object o) {
+		return SINGLETON.toNativeUIObject(type, o);
+	}
+	
+	/** Cast the given object into an UI native object.
+	 * 
+	 * @param type is the desired type.
+	 * @param o is the object to cast.
+	 * @return the casted value.
+	 */
+	protected abstract <T> T toNativeUIObject(Class<T> type, Object o);
+	
+	//------------------------------
+	// CONTEXT
+	//------------------------------
+	
+	/** Must be invoked to prepare to draw in the given context.
+	 * 
+	 * @param context
+	 */
+	public static void prepareDrawing(VectorGraphics2D context) {
+		SINGLETON.preDrawing(context);
+	}
+
+	/** Must be invoked to finalize the drawing in the given context.
+	 * 
+	 * @param context
+	 */
+	public static void finalizeDrawing(VectorGraphics2D context) {
+		SINGLETON.postDrawing(context);
+	}
+
+	private VectorGraphics2D currentContext = null;
+	
+	/** Must be invoked to prepare to draw in the given context.
+	 * 
+	 * @param context
+	 */
+	protected void preDrawing(VectorGraphics2D context) {
+		this.currentContext = context;
+	}
+
+	/** Must be invoked to finalize the drawing in the given context.
+	 * 
+	 * @param context
+	 */
+	protected void postDrawing(VectorGraphics2D context) {
+		this.currentContext = null;
+	}
+	
+	/** Replies the current drawing context.
+	 * 
+	 * @return the current drawing context.
+	 */
+	public VectorGraphics2D getCurrentDrawingContext() {
+		return this.currentContext;
+	}
+
+	//------------------------------
+	// Internal objects
+	//------------------------------
+	
+	/** Replies the object with the given Id from the current toolkit.
+	 * The objects associated to the ids depends on the VectorToolkit
+	 * implementation.
+	 * 
+	 * @param id is the identifier of the object.
+	 * @param type is the expected type for the object.
+	 * @return the object, or <code>null</code> if there is no
+	 * object with the given id or if the object is not of
+	 * the correct type.
+	 */
+	public static <T> T getObjectWithId(int id, Class<T> type) {
+		return SINGLETON.findObjectWithId(id,type);
+	}
+	
+	/** Replies the object with the given Id from the current toolkit.
+	 * The objects associated to the ids depends on the VectorToolkit
+	 * implementation.
+	 * 
+	 * @param id is the identifier of the object.
+	 * @param type is the expected type for the object.
+	 * @return the object, or <code>null</code> if there is no
+	 * object with the given id or if the object is not of
+	 * the correct type.
+	 */
+	protected abstract <T> T findObjectWithId(int id, Class<T> type);
+
+	//------------------------------
+	// SHAPE
+	//------------------------------
+
+	/** Create a shape from a native object.
+	 * <p>
+	 * The type of the native object depends on the underlying graphic
+	 * API. It is a <code>java.awt.Shape</code> for AWT.
+	 * 
+	 * @param nativeObject is the native object to translate.
+	 * @return the shape.
+	 */
+	public static Shape2f shape(Object nativeObject) {
+		return SINGLETON.createShape(nativeObject);
+	}
+
+	/** Create a shape.
+	 * <p>
+	 * The type of the native object depends on the underlying graphic
+	 * API. It is a <code>java.awt.Shape</code> for AWT.
+	 * 
+	 * @param nativeObject is the native object to translate.
+	 * @return the shape.
+	 */
+	protected abstract Shape2f createShape(Object nativeObject);
+
+	//------------------------------
+	// TRANSFORM2D
+	//------------------------------
+
+	/** Create the generic transform 2D from the low-level affine transform.
+	 * <p>
+	 * The type of the native affine transform depends on the underlying graphic
+	 * API. It is a <code>java.awt.geom.AffineTransform</code> for AWT.
+	 *   
+	 * @param affineTransform
+	 * @return the transform
+	 */
+	public static Transform2D makeTransform(Object affineTransform) {
+		return SINGLETON.createTransform(affineTransform);
+	}
+
+	/** Create the generic transform 2D from the low-level affine transform.
+	 * <p>
+	 * The type of the native affine transform depends on the underlying graphic
+	 * API. It is a <code>java.awt.geom.AffineTransform</code> for AWT.
+	 *   
+	 * @param affineTransform
+	 * @return the transform
+	 */
+	protected abstract Transform2D createTransform(Object affineTransform);
+
+	//------------------------------
+	// DIMENSION
+	//------------------------------
+
+	/** Create a dimension
+	 * 
+	 * @param width
+	 * @param height
+	 * @return the dimension.
+	 */
+	public static Dimension dimension(float width, float height) {
+		return SINGLETON.createDimension(width, height);
+	}
+
+	/** Create a dimension
+	 * 
+	 * @param width
+	 * @param height
+	 * @return the dimension.
+	 */
+	protected abstract Dimension createDimension(float width, float height);
+
+	//------------------------------
+	// MARGINS
+	//------------------------------
+
+	/** Create margins
+	 * 
+	 * @param top
+	 * @param left
+	 * @param right
+	 * @param bottom
+	 * @return the margins.
+	 */
+	public static Margins margins(float top, float left, float right, float bottom) {
+		return SINGLETON.createMargins(top, left, right, bottom);
+	}
+
+	/** Create margins
+	 * 
+	 * @param top
+	 * @param left
+	 * @param right
+	 * @param bottom
+	 * @return the margins.
+	 */
+	protected abstract Margins createMargins(float top, float left, float right, float bottom);
+
+	//------------------------------
+	// PAINT
+	//------------------------------
+
+	/** Create a paint
+	 * <p>
+	 * The type of the native paint depends on the underlying graphic
+	 * API. It is a <code>java.awt.Paint</code> for AWT.
+	 * 
+	 * @param paintObject
+	 * @return the paint
+	 */
+	public static Paint paint(Object paintObject) {
+		return SINGLETON.createPaint(paintObject);
+	}
+
+	/** Create a paint.
+	 * <p>
+	 * The type of the native paint depends on the underlying graphic
+	 * API. It is a <code>java.awt.Composite</code> for AWT.
+	 * 
+	 * @param paintObject
+	 * @return the paint
+	 */
+	protected abstract Paint createPaint(Object paintObject);
+
+	//------------------------------
+	// COMPOSITE
+	//------------------------------
+
+	/** Create a composite
+	 * <p>
+	 * The type of the native composite depends on the underlying graphic
+	 * API. It is a <code>java.awt.Composite</code> for AWT.
+	 * 
+	 * @param compositeObject
+	 * @return the composite
+	 */
+	public static Composite composite(Object compositeObject) {
+		return SINGLETON.createComposite(compositeObject);
+	}
+
+	/** Create a composite
+	 * <p>
+	 * The type of the native composite depends on the underlying graphic
+	 * API. It is a <code>java.awt.Composite</code> for AWT.
+	 * 
+	 * @param compositeObject
+	 * @return the composite
+	 */
+	protected abstract Composite createComposite(Object compositeObject);
+
+	/** Create an alpha composite.
+	 * 
+	 * @param alpha
+	 * @return the composite.
+	 */
+	public static Composite composite(float alpha) {
+		return SINGLETON.createComposite(alpha);
+	}
+
+	/** Create an alpha composite.
+	 * 
+	 * @param alpha
+	 * @return the composite.
+	 */
+	protected abstract Composite createComposite(float alpha);
+
+	//------------------------------
+	// FONT
+	//------------------------------
+
+	/** Create a font.
+	 * 
+	 * @param name is the name of the font.
+	 * @param style is the style of the font.
+	 * @param size is the size of the font.
+	 * @return the font.
+	 */
+	public static Font font(String name, FontStyle style, float size) {
+		return SINGLETON.createFont(name, style, size);
+	}
+
+	/** Create a font.
+	 * 
+	 * @param name is the name of the font.
+	 * @param style is the style of the font.
+	 * @param size is the size of the font.
+	 * @return the font.
+	 */
+	protected abstract Font createFont(String name, FontStyle style, float size);
+
+	/** Create a font from the background font (AWT, Android...)
+	 * <p>
+	 * The type of the native affine transform depends on the underlying graphic
+	 * API. It is a <code>java.awt.Font</code> for AWT.
+	 * 
+	 * @param fontObject
+	 * @return the font.
+	 */
+	public static Font font(Object fontObject) {
+		return SINGLETON.createFont(fontObject);
+	}
+
+	/** Create a font.
+	 * <p>
+	 * The type of the native affine transform depends on the underlying graphic
+	 * API. It is a <code>java.awt.Font</code> for AWT.
+	 * 
+	 * @param fontObject
+	 * @return the font.
+	 */
+	protected abstract Font createFont(Object fontObject);
+
+	/** Replies the default font.
+	 * 
+	 * @return the default font.
+	 */
+	public static Font font() {
+		return SINGLETON.getDefaultFont();
+	}
+
+	/** Replies the default font.
+	 * 
+	 * @return the default font.
+	 */
+	protected abstract Font getDefaultFont();
+
+	/** Create a font metrics.
+	 * <p>
+	 * The type of the native affine transform depends on the underlying graphic
+	 * API. It is a <code>java.awt.FontMetrics</code> for AWT.
+	 * 
+	 * @param metricsObject is the metrics of the font.
+	 * @return the font metrics.
+	 */
+	public static FontMetrics fontMetrics(Object metricsObject) {
+		return SINGLETON.createFontMetrics(metricsObject);
+	}
+
+	/** Create a font metrics.
+	 * <p>
+	 * The type of the native affine transform depends on the underlying graphic
+	 * API. It is a <code>java.awt.FontMetrics</code> for AWT.
+	 * 
+	 * @param metricsObject is the metrics of the font.
+	 * @return the font metrics.
+	 */
+	protected abstract FontMetrics createFontMetrics(Object metricsObject);
+
+	/** Create a font metrics for the given font.
+	 * 
+	 * @param font
+	 * @return the font metrics.
+	 */
+	public static FontMetrics fontMetrics(Font font) {
+		return SINGLETON.createFontMetrics(font);
+	}
+
+	/** Create a font metrics for the given font.
+	 * 
+	 * @param font
+	 * @return the font metrics.
+	 */
+	protected abstract FontMetrics createFontMetrics(Font font);
+
+	//------------------------------
+	// IMAGE
+	//------------------------------
+
+	/** Create an image.
+	 * 
+	 * @param url
+	 * @return the image
+	 */
+	public static Image image(URL url) {
+		return SINGLETON.createImage(url);
+	}
+
+	/** Create an image.
+	 * 
+	 * @param url
+	 * @return the image
+	 */
+	protected abstract Image createImage(URL url);
+
+	/** Create an image.
+	 * 
+	 * @param stream
+	 * @return the image
+	 */
+	public static Image image(InputStream stream) {
+		return SINGLETON.createImage(stream);
+	}
+
+	/** Create an image.
+	 * 
+	 * @param stream
+	 * @return the image
+	 */
+	protected abstract Image createImage(InputStream stream);
+
+	/** Create an image.
+	 * 
+	 * @param width is the width of the new image.
+	 * @param height is the height of the new image.
+	 * @param isAlpha indicates if alpha color should be supported.
+	 * @return the image
+	 */
+	public static Image image(int width, int height, boolean isAlpha) {
+		return SINGLETON.createImage(width, height, isAlpha);
+	}
+
+	/** Create an image.
+	 * 
+	 * @param width is the width of the new image.
+	 * @param height is the height of the new image.
+	 * @param isAlpha indicates if alpha color should be supported.
+	 * @return the image
+	 */
+	protected abstract Image createImage(int width, int height, boolean isAlpha);
+
+	/** Create an image from the background image or icon (AWT, Android...)
+	 * <p>
+	 * The type of the native affine transform depends on the underlying graphic
+	 * API. It is a <code>java.awt.Image</code>, <code>javax.swing.ImageIcon</code>, or 
+	 * <code>javax.swing.Icon</code> for AWT.
+	 * 
+	 * @param imageObject
+	 * @return the font.
+	 */
+	public static Image image(Object imageObject) {
+		return SINGLETON.createImage(imageObject);
+	}
+
+	/** Create an image from the background image or icon (AWT, Android...).
+	 * <p>
+	 * The type of the native affine transform depends on the underlying graphic
+	 * API. It is a <code>java.awt.Image</code> or <code>javax.swing.ImageIcon</code> for AWT.
+	 * 
+	 * @param imageObject
+	 * @return the image.
+	 */
+	protected abstract Image createImage(Object imageObject);
+	
+	/** Create a transparent version of the given image.
+	 * <p>
+	 * <var>transparency</var>indicates how the alpha-component of the image is changed.
+	 * The value is in <code>[-1;1]</code>.
+	 * A value of <code>-1</code> means that the alpha-component is set to none.
+	 * A value of <code>1</code> means that the alpha-component is completely
+	 * set. A value of <code>0</code> means that the alpha-component
+	 * <p>
+	 * This function does not change the given image.
+	 * See {@link #imageMakeTransparent(Image, float)} to
+	 * change the transparency of the source image.
+	 * 
+	 * @param imageObject
+	 * @param transparency is the transparency to apply to the given image
+	 * @return the transparent image.
+	 * @see #imageMakeTransparent(Image, float)
+	 */
+	public static Image image(Image imageObject, float transparency) {
+		return SINGLETON.createTransparentImage(imageObject, transparency);
+	}
+
+	/** Make the given image more transparent.
+	 * <p>
+	 * <var>transparency</var>indicates how the alpha-component of the image is changed.
+	 * The value is in <code>[-1;1]</code>.
+	 * A value of <code>-1</code> means that the alpha-component is set to none.
+	 * A value of <code>1</code> means that the alpha-component is completely
+	 * set. A value of <code>0</code> means that the alpha-component
+	 * <p>
+	 * The given image is changed.
+	 * See {@link #image(Image, float)} to
+	 * avoid to change the transparency of the source image.
+	 * 
+	 * @param imageObject
+	 * @param transparency is the transparency to apply to the given image
+	 * @return the given <var>imageObject</var> if the source image is mutable; or
+	 * a new image that is a transparent version of the source image if this
+	 * source image is not mutable.
+	 * @see #image(Image, float)
+	 */
+	public static Image imageMakeTransparent(Image imageObject, float transparency) {
+		return SINGLETON.makeTransparentImage(imageObject, transparency);
+	}
+
+	/** Create a transparent version of the given image.
+	 * <p>
+	 * <var>transparency</var>indicates how the alpha-component of the image is changed.
+	 * The value is in <code>[-1;1]</code>.
+	 * A value of <code>-1</code> means that the alpha-component is set to none.
+	 * A value of <code>1</code> means that the alpha-component is completely
+	 * set. A value of <code>0</code> means that the alpha-component
+	 * 
+	 * @param imageObject
+	 * @param transparency is the transparency to apply to the given image
+	 * @return the transparent image.
+	 */
+	protected abstract Image createTransparentImage(Image imageObject, float transparency);
+
+	/** Make the given image more transparent.
+	 * <p>
+	 * <var>transparency</var>indicates how the alpha-component of the image is changed.
+	 * The value is in <code>[-1;1]</code>.
+	 * A value of <code>-1</code> means that the alpha-component is set to none.
+	 * A value of <code>1</code> means that the alpha-component is completely
+	 * set. A value of <code>0</code> means that the alpha-component
+	 * 
+	 * @param imageObject
+	 * @param transparency is the transparency to apply to the given image
+	 * @return the given <var>imageObject</var> if the source image is mutable; or
+	 * a new image that is a transparent version of the source image if this
+	 * source image is not mutable.
+	 */
+	protected abstract Image makeTransparentImage(Image imageObject, float transparency);
+
+	/** Write the image.
+	 * 
+	 * @param image is the image to write.
+	 * @param type is the type of encoding to use (png, jpeg...)
+	 * @param stream is the output stream to write inside.
+	 * @throws IOException
+	 */
+	public static void writeImage(Image image, String type, OutputStream stream) throws IOException {
+		SINGLETON.write(image, type, stream);
+	}
+	
+	/** Write the image.
+	 * 
+	 * @param image is the image to write.
+	 * @param type is the type of encoding to use (png, jpeg...)
+	 * @param stream is the output stream to write inside.
+	 * @throws IOException
+	 */
+	protected abstract void write(Image image, String type, OutputStream stream) throws IOException;
+
+	//------------------------------
+	// STROKE
+	//------------------------------
+
+	/** Create a stroke
+	 * 
+	 * @param width
+	 * @return the stroke
+	 */
+	public static Stroke stroke(float width) {
+		return SINGLETON.createStroke(width, Stroke.DEFAULT_LINE_JOIN, Stroke.DEFAULT_END_CAP, Stroke.DEFAULT_MITTER_LIMIT, null, 0);
+	}
+	
+	/** Create a stroke
+	 * 
+	 * @param width
+	 * @param lineJoin
+	 * @param endCap
+	 * @param mitterLimit
+	 * @return the stroke
+	 */
+	public static Stroke stroke(float width, LineJoin lineJoin, EndCap endCap, float mitterLimit) {
+		return SINGLETON.createStroke(width, lineJoin, endCap, mitterLimit, null, 0);
+	}
+
+	/** Create a stroke
+	 * 
+	 * @param width
+	 * @param lineJoin
+	 * @param endCap
+	 * @param mitterLimit
+	 * @param dashes
+	 * @param dashPhase
+	 * @return the stroke
+	 */
+	public static Stroke stroke(float width, LineJoin lineJoin, EndCap endCap, float mitterLimit, float[] dashes, float dashPhase) {
+		return SINGLETON.createStroke(width, lineJoin, endCap, mitterLimit, dashes, dashPhase);
+	}
+
+	/** Create a stroke
+	 * 
+	 * @param width
+	 * @param join
+	 * @param endCap
+	 * @param mitterLimit
+	 * @param dashes
+	 * @param dashPhase
+	 * @return the stroke
+	 */
+	protected abstract Stroke createStroke(float width, LineJoin join, EndCap endCap, float mitterLimit, float[] dashes, float dashPhase);
+
+	/** Create a stroke
+	 * <p>
+	 * The type of the native affine transform depends on the underlying graphic
+	 * API. It is a <code>java.awt.BasicStroke</code> for AWT.
+	 * 
+	 * @param strokeObject
+	 * @return the stroke
+	 */
+	public static Stroke stroke(Object strokeObject) {
+		return SINGLETON.createStroke(strokeObject);
+	}
+
+	/** Create a stroke
+	 * <p>
+	 * The type of the native affine transform depends on the underlying graphic
+	 * API. It is a <code>java.awt.BasicStroke</code> for AWT.
+	 * 
+	 * @param strokeObject
+	 * @return the stroke
+	 */
+	protected abstract Stroke createStroke(Object strokeObject);
+
+	//------------------------------
+	// COLOR
+	//------------------------------
+	
+    /**
+     * The color white.  In the default sRGB space.
+     */
+    public final static Color WHITE;
+
+    /**
+     * The color light gray.  In the default sRGB space.
+     */
+    public final static Color LIGHT_GRAY;
+
+    /**
+     * The color gray.  In the default sRGB space.
+     */
+    public final static Color GRAY;
+
+    /**
+     * The color dark gray.  In the default sRGB space.
+     */
+    public final static Color DARK_GRAY;
+
+    /**
+     * The color black.  In the default sRGB space.
+     */
+    public final static Color BLACK;
+
+    /**
+     * The color red.  In the default sRGB space.
+     */
+    public final static Color RED;
+
+    /**
+     * The color pink.  In the default sRGB space.
+     */
+    public final static Color PINK;
+
+    /**
+     * The color orange.  In the default sRGB space.
+     */
+    public final static Color ORANGE;
+
+    /**
+     * The color yellow.  In the default sRGB space.
+     */
+    public final static Color YELLOW;
+
+    /**
+     * The color green.  In the default sRGB space.
+     */
+    public final static Color GREEN;
+
+    /**
+     * The color magenta.  In the default sRGB space.
+     */
+    public final static Color MAGENTA;
+
+    /**
+     * The color cyan.  In the default sRGB space.
+     */
+    public final static Color CYAN;
+
+    /**
+     * The color blue.  In the default sRGB space.
+     */
+    public final static Color BLUE;
+
+    /** Create a color.
+	 * 
+	 * @param red is the red component in [0;255].
+	 * @param green is the green component in [0;255].
+	 * @param blue is the blue component in [0;255].
+	 * @param alpha is the alpha component in [0;255], 0 is fully transparent, 255 is fully opaque.
+	 * @return the color.
+	 */
+	public static Color color(int red, int green, int blue, int alpha) {
+		return SINGLETON.createColor(red, green, blue, alpha);
+	}
+
+    /** Create a color. Alpha is 255.
+	 * 
+	 * @param red is the red component in [0;255].
+	 * @param green is the green component in [0;255].
+	 * @param blue is the blue component in [0;255].
+	 * @return the color.
+	 */
+	public static Color color(int red, int green, int blue) {
+		return SINGLETON.createColor(red, green, blue, 255);
+	}
+
+	/** Create a color.
+	 * 
+	 * @param red is the red component in [0;1].
+	 * @param green is the green component in [0;1].
+	 * @param blue is the blue component in [0;1].
+	 * @param alpha is the alpha component in [0;1], 0 is fully transparent, 1 is fully opaque.
+	 * @return the color.
+	 */
+	public static Color color(float red, float green, float blue, float alpha) {
+		return SINGLETON.createColor(
+				(int)(red*255f+.5f),
+				(int)(green*255f+.5f),
+				(int)(blue*255f+.5f),
+				(int)(alpha*255f+.5f));
+	}
+
+	/** Create a color. Alpha is 1.
+	 * 
+	 * @param red is the red component in [0;1].
+	 * @param green is the green component in [0;1].
+	 * @param blue is the blue component in [0;1].
+	 * @return the color.
+	 */
+	public static Color color(float red, float green, float blue) {
+		return SINGLETON.createColor(
+				(int)(red*255f+.5f),
+				(int)(green*255f+.5f),
+				(int)(blue*255f+.5f),
+				255);
+	}
+
+	/** Create a color.
+	 * 
+	 * @param red is the red component in [0;255].
+	 * @param green is the green component in [0;255].
+	 * @param blue is the blue component in [0;255].
+	 * @param alpha is the alpha component in [0;255], 0 is fully transparent, 255 is fully opaque.
+	 * @return the color.
+	 */
+	protected abstract Color createColor(int red, int green, int blue, int alpha);
+
+	/** Create an opaque color.
+	 * 
+	 * @param rgb are the red, green and blue components
+	 * @return the color.
+	 */
+	public static Color color(int rgb) {
+		return SINGLETON.createColor(
+				((rgb >> 16) & 0xFF),
+				((rgb >> 8) & 0xFF),
+				(rgb & 0xFF),
+				255);
+	}
+
+	/** Create a color.
+	 * 
+	 * @param rgb are the red, green and blue components
+	 * @param hasAlpa indicates if <var>rgb</var> contains alpha component.
+	 * @return the color.
+	 */
+	public static Color color(int rgb, boolean hasAlpa) {
+		if (hasAlpa) {
+			return SINGLETON.createColor(
+					((rgb >> 16) & 0xFF),
+					((rgb >> 8) & 0xFF),
+					(rgb & 0xFF),
+					((rgb >> 24) & 0xFF));
+		}
+		return SINGLETON.createColor(
+				((rgb >> 16) & 0xFF),
+				((rgb >> 8) & 0xFF),
+				(rgb & 0xFF),
+				255);
+	}
+	
+    /** Create a color from a underground API color.
+	 * <p>
+	 * The type of the native affine transform depends on the underlying graphic
+	 * API. It is a <code>java.awt.Color</code> for AWT.
+	 * 
+	 * @param rawColorObject is the color in the AWT, SWT or Android API. 
+	 * @return the generic color.
+	 */
+	public static Color color(Object rawColorObject) {
+		return SINGLETON.createColor(rawColorObject);
+	}
+
+    /** Create a color from a underground API color.
+	 * <p>
+	 * The type of the native affine transform depends on the underlying graphic
+	 * API. It is a <code>java.awt.Color</code> for AWT.
+	 * 
+	 * @param rawColorObject is the color in the AWT, SWT or Android API. 
+	 * @return the generic color.
+	 */
+	protected abstract Color createColor(Object rawColorObject);
+
+	//------------------------------
+	// STATIC INIT
+	//------------------------------
+	
+	static {
+		init();
+	    WHITE = color(255, 255, 255, 255);
+	    LIGHT_GRAY = color(192, 192, 192, 255);
+	    GRAY = color(128, 128, 128, 255);
+	    DARK_GRAY = color(64, 64, 64, 255);
+	    BLACK = color(0, 0, 0, 255);
+	    RED = color(255, 0, 0, 255);
+	    PINK = color(255, 175, 175, 255);
+	    ORANGE = color(255, 200, 0, 255);
+	    YELLOW = color(255, 255, 0, 255);
+	    GREEN = color(0, 255, 0, 255);
+	    MAGENTA = color(255, 0, 255, 255);
+	    CYAN = color(0, 255, 255, 255);
+	    BLUE = color(0, 0, 255, 255);
+	}
+
+	//------------------------------
+	// PDF
+	//------------------------------
+	
+	/** Read a PDF file and reply its wrapper.
+	 * 
+	 * @param url is the address of the stream to read.
+	 * @return the PDF wrapper.
+	 * @throws IOException
+	 */
+	public static Pdf pdf(URL url) throws IOException {
+		return SINGLETON.wrapPdf(url);
+	}
+
+	/** Read a PDF file and reply its wrapper.
+	 * 
+	 * @param url is the address of the stream to read.
+	 * @return the PDF wrapper.
+	 * @throws IOException
+	 */
+	protected abstract Pdf wrapPdf(URL url) throws IOException;
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector/src/main/resources/org/arakhne/afc/ui/vector/VectorToolkit.properties
===================================================================
--- trunk/ui/ui-vector/src/main/resources/org/arakhne/afc/ui/vector/VectorToolkit.properties	                        (rev 0)
+++ trunk/ui/ui-vector/src/main/resources/org/arakhne/afc/ui/vector/VectorToolkit.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+WINDOW_TOOLKITS = org.arakhne.afc.ui.vector.android.AndroidVectorToolkit;org.arakhne.afc.ui.vector.awt.AwtVectorToolkit

Added: trunk/ui/ui-vector/src/main/resources/org/arakhne/afc/ui/vector/no_picture.png
===================================================================
(Binary files differ)


Property changes on: trunk/ui/ui-vector/src/main/resources/org/arakhne/afc/ui/vector/no_picture.png
___________________________________________________________________
Added: svn:mime-type
   + image/png


Property changes on: trunk/ui/ui-vector-android
___________________________________________________________________
Added: svn:ignore
   + .classpath
..metadata
..settings
..project
target
bin


Added: trunk/ui/ui-vector-android/pom.xml
===================================================================
--- trunk/ui/ui-vector-android/pom.xml	                        (rev 0)
+++ trunk/ui/ui-vector-android/pom.xml	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,27 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.arakhne.afc</groupId>
+		<artifactId>arakhneUi</artifactId>
+		<version>1.0-SNAPSHOT</version>
+	</parent>
+
+	<groupId>org.arakhne.afc.ui</groupId>
+	<artifactId>ui-vector-android</artifactId>
+	<version>1.0-SNAPSHOT</version>
+	<name>Vector-API Android-Implementation</name>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.arakhne.afc.ui</groupId>
+			<artifactId>ui-vector</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.google.android</groupId>
+			<artifactId>android</artifactId>
+			<scope>provided</scope>
+		</dependency>
+	</dependencies>
+
+</project>

Added: trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidColor.java
===================================================================
--- trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidColor.java	                        (rev 0)
+++ trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidColor.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,191 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.android;
+
+import org.arakhne.afc.ui.vector.Color;
+
+import android.graphics.drawable.Drawable;
+
+/** Android implementation of the generic color.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AndroidColor implements Color, NativeWrapper {
+
+	private static final long serialVersionUID = -3729571234595878175L;
+
+	private static final double FACTOR = 0.7;
+
+	private final int original;
+	private final Drawable originalDrawable;
+
+	/**
+	 * @param color
+	 */
+	public AndroidColor(int color) {
+		this.original = color;
+		this.originalDrawable = null;
+	}
+
+	/**
+	 * @param drawable
+	 */
+	public AndroidColor(Drawable drawable) {
+		this.original = -1;
+		this.originalDrawable = drawable;
+	}
+
+	/**
+	 * @param red
+	 * @param green
+	 * @param blue
+	 * @param alpha
+	 */
+	public AndroidColor(int red, int green, int blue, int alpha) {
+		this.original = android.graphics.Color.argb(alpha, red, green, blue);
+		this.originalDrawable = null;
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if (obj instanceof Color) {
+			return getRGB() == ((Color)obj).getRGB();
+		}
+		if (obj instanceof Number) {
+			return getRGB() == ((Number)obj).intValue();
+		}
+		if (obj instanceof Drawable) {
+			return ((Drawable)obj).equals(this.originalDrawable);
+		}
+		return super.equals(obj);
+	}
+	
+	@Override
+	public int hashCode() {
+		if (this.originalDrawable!=null)
+			return this.originalDrawable.hashCode(); 
+		return this.original;
+	}
+	
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		if (this.originalDrawable!=null && type.isAssignableFrom(Drawable.class)) {
+			return type.cast(this.originalDrawable);
+		}
+		return type.cast(this.original);
+	}
+
+	@Override
+	public int getGreen() {
+		if (this.originalDrawable!=null)
+			throw new IllegalStateException("color is a Drawable"); //$NON-NLS-1$
+		return android.graphics.Color.green(this.original);
+	}
+
+	@Override
+	public int getRed() {
+		if (this.originalDrawable!=null)
+			throw new IllegalStateException("color is a Drawable"); //$NON-NLS-1$
+		return android.graphics.Color.red(this.original);
+	}
+
+	@Override
+	public int getBlue() {
+		if (this.originalDrawable!=null)
+			throw new IllegalStateException("color is a Drawable"); //$NON-NLS-1$
+		return android.graphics.Color.blue(this.original);
+	}
+
+	@Override
+	public int getAlpha() {
+		if (this.originalDrawable!=null)
+			throw new IllegalStateException("color is a Drawable"); //$NON-NLS-1$
+		return android.graphics.Color.alpha(this.original);
+	}
+
+	@Override
+	public int getRGB() {
+		if (this.originalDrawable!=null)
+			throw new IllegalStateException("color is a Drawable"); //$NON-NLS-1$
+		return this.original;
+	}
+
+	@Override
+	public Color brighterColor() {
+		if (this.originalDrawable!=null)
+			throw new IllegalStateException("color is a Drawable"); //$NON-NLS-1$
+		int r = getRed();
+		int g = getGreen();
+		int b = getBlue();
+		int alpha = getAlpha();
+
+		/* From 2D group:
+		 * 1. black.brighter() should return grey
+		 * 2. applying brighter to blue will always return blue, brighter
+		 * 3. non pure color (non zero rgb) will eventually return white
+		 */
+		int i = (int)(1.0/(1.0-FACTOR));
+		if ( r == 0 && g == 0 && b == 0) {
+			return new AndroidColor(i, i, i, alpha);
+		}
+		if ( r > 0 && r < i ) r = i;
+		if ( g > 0 && g < i ) g = i;
+		if ( b > 0 && b < i ) b = i;
+
+		return new AndroidColor(
+				Math.min((int)(r/FACTOR), 255),
+				Math.min((int)(g/FACTOR), 255),
+				Math.min((int)(b/FACTOR), 255),
+				alpha);
+	}
+
+	@Override
+	public Color darkerColor() {
+		if (this.originalDrawable!=null)
+			throw new IllegalStateException("color is a Drawable"); //$NON-NLS-1$
+		return new AndroidColor(
+				Math.max((int)(getRed()  *FACTOR), 0),
+				Math.max((int)(getGreen()*FACTOR), 0),
+				Math.max((int)(getBlue() *FACTOR), 0),
+				getAlpha());
+	}
+
+	@Override
+	public Color transparentColor() {
+		if (this.originalDrawable!=null)
+			throw new IllegalStateException("color is a Drawable"); //$NON-NLS-1$
+		int alpha = getAlpha() / 2;
+		return new AndroidColor(
+				getRed(), getGreen(), getBlue(),
+				alpha);
+	}
+
+	@Override
+	public String toString() {
+		if (this.originalDrawable!=null)
+			return this.originalDrawable.toString();
+		return Integer.toHexString(this.original);
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidComposite.java
===================================================================
--- trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidComposite.java	                        (rev 0)
+++ trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidComposite.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,70 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.android;
+
+import org.arakhne.afc.ui.vector.Composite;
+
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+
+/** Android implementation of the generic composite.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AndroidComposite implements Composite, NativeWrapper {
+
+	private final Paint original;
+
+	/**
+	 * @param paint
+	 */
+	public AndroidComposite(Paint paint) {
+		this.original = paint;
+	}
+
+	/**
+	 * @param alpha
+	 */
+	public AndroidComposite(float alpha) {
+		this.original = new Paint();
+		this.original.setStyle(Style.FILL_AND_STROKE);
+		this.original.setAlpha((int)(alpha * 255));
+	}
+
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(this.original);
+	}
+
+	@Override
+	public float getAlpha() {
+		return this.original.getAlpha() / 255;
+	}
+
+	@Override
+	public String toString() {
+		return this.original.toString();
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidDimension.java
===================================================================
--- trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidDimension.java	                        (rev 0)
+++ trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidDimension.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,79 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.android;
+
+import org.arakhne.afc.ui.vector.Dimension;
+import org.arakhne.afc.util.HashCodeUtil;
+
+/** Android implementation of the generic dimension.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AndroidDimension implements Dimension {
+
+	private final float w;
+	private final float h;
+	
+	/**
+	 * @param w
+	 * @param h
+	 */
+	public AndroidDimension(float w, float h) {
+		this.w = w;
+		this.h = h;
+	}
+
+	@Override
+	public float width() {
+		return this.w;
+	}
+
+	@Override
+	public float height() {
+		return this.h;
+	}
+		
+	@Override
+	public String toString() {
+		return this.h+";"+this.w; //$NON-NLS-1$
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if (obj==this) return true;
+		if (obj instanceof Dimension) {
+			Dimension d = (Dimension)obj;
+			return d.width()==width() && d.height()==height();
+		}
+		return super.equals(obj);
+	}
+
+	@Override
+	public int hashCode() {
+		int h = HashCodeUtil.hash(this.w);
+		h = HashCodeUtil.hash(h, this.h);
+		return h;
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidImage.java
===================================================================
--- trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidImage.java	                        (rev 0)
+++ trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidImage.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,124 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.android;
+
+import java.io.InputStream;
+
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.ui.vector.Image;
+import org.arakhne.afc.ui.vector.ImageObserver;
+import org.arakhne.afc.ui.vector.Raster;
+import org.arakhne.afc.ui.vector.VectorGraphics2D;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+
+/** Android implementation of the generic image.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AndroidImage implements Image, NativeWrapper {
+
+	private final Bitmap image;
+
+	/**
+	 * @param img
+	 */
+	public AndroidImage(Bitmap img) {
+		this.image = img;
+	}
+
+	/**
+	 * @param stream
+	 */
+	public AndroidImage(InputStream stream) {
+		this.image = BitmapFactory.decodeStream(stream);
+	}
+	
+	/**
+	 * @param width
+	 * @param height
+	 * @param isAlpha
+	 */
+	public AndroidImage(int width, int height, boolean isAlpha) {
+		this.image = Bitmap.createBitmap(width, height, Config.ARGB_8888);
+		if (isAlpha) {
+			this.image.eraseColor(Color.TRANSPARENT);
+		}
+		else {
+			this.image.eraseColor(Color.WHITE);
+		}
+	}
+	
+	/** Replies the droid bitmap.
+	 * 
+	 * @return the droid bitmap.
+	 */
+	public Bitmap getBitmap() {
+		return this.image;
+	}
+
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(this.image);
+	}
+
+	@Override
+	public int getWidth(ImageObserver observer) {
+		return this.image.getWidth();
+	}
+
+	@Override
+	public int getHeight(ImageObserver observer) {
+		return this.image.getHeight();
+	}
+
+	@Override
+	public int getNumBands() {
+		return this.image.getByteCount();
+	}
+
+	@Override
+	public int getRGB(int x, int y) {
+		return this.image.getPixel(x, y);
+	}
+
+	@Override
+	public VectorGraphics2D getVectorGraphics() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public Raster getData(Rectangle2f area) {
+		return new AndroidRaster(this.image, area);
+	}
+
+	@Override
+	public String toString() {
+		return this.image.toString();
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidMargins.java
===================================================================
--- trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidMargins.java	                        (rev 0)
+++ trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidMargins.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,80 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.android;
+
+import org.arakhne.afc.ui.vector.Margins;
+
+/** AWT implementation of the generic dimension.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AndroidMargins implements Margins {
+
+	private float top;
+	private float left;
+	private float right;
+	private float bottom;
+	
+	/**
+	 * @param t
+	 * @param l
+	 * @param r
+	 * @param b
+	 */
+	public AndroidMargins(float t, float l, float r, float b) {
+		this.top = t;
+		this.left = l;
+		this.right = r;
+		this.bottom = b;
+	}
+
+	@Override
+	public float top() {
+		return this.top;
+	}
+
+	@Override
+	public float left() {
+		return this.left;
+	}
+		
+	@Override
+	public float right() {
+		return this.right;
+	}
+
+	@Override
+	public float bottom() {
+		return this.bottom;
+	}
+
+	@Override
+	public void set(float top, float left, float right, float bottom) {
+		this.top = top;
+		this.left = left;
+		this.right = right;
+		this.bottom = bottom;
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidPaint.java
===================================================================
--- trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidPaint.java	                        (rev 0)
+++ trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidPaint.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,381 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.android;
+
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.ui.vector.Font;
+import org.arakhne.afc.ui.vector.FontMetrics;
+import org.arakhne.afc.ui.vector.FontStyle;
+import org.arakhne.afc.ui.vector.Stroke;
+import org.arakhne.afc.ui.vector.VectorGraphics2D;
+
+import android.graphics.DashPathEffect;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.Paint.Cap;
+import android.graphics.Paint.Join;
+import android.graphics.Paint.Style;
+
+/** Android implementation of the generic stroke, font and font metrics.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AndroidPaint implements Stroke, org.arakhne.afc.ui.vector.Paint, Font, FontMetrics, NativeWrapper {
+
+	private static int toAndroid(FontStyle fs) {
+		switch(fs) {
+		case BOLD:
+			return Typeface.BOLD;
+		case BOLD_ITALIC:
+			return Typeface.BOLD_ITALIC;
+		case ITALIC:
+			return Typeface.ITALIC;
+		case PLAIN:
+		default:
+			return Typeface.NORMAL;
+		}
+	}
+
+	private static final Paint defaultPaint = new Paint();
+
+	/** Replies the default font.
+	 * 
+	 * @return a copy of the the default font.
+	 */
+	public static AndroidPaint getDefaultFont() {
+		return new AndroidPaint(new Paint(defaultPaint));
+	}
+
+	/** Replies the font with the given attribute
+	 * 
+	 * @param name
+	 * @param style
+	 * @param size
+	 * @param context
+	 * @return the font.
+	 */
+	public static AndroidPaint getFont(String name, FontStyle style, float size, VectorGraphics2D context) {
+		Paint pt = new Paint(((AndroidPaint)context.getFont()).getPaint());
+		pt.setTypeface(Typeface.create(name, toAndroid(style)));
+		pt.setTextSize(size);
+		return new AndroidPaint(pt);
+	}
+
+	private final Paint paint;
+
+	/**
+	 * @param paint
+	 */
+	public AndroidPaint(Paint paint) {
+		this.paint = paint;
+	}
+
+	/** Replies the painting context.
+	 * 
+	 * @return the painting context.
+	 */
+	public Paint getPaint() {
+		return this.paint;
+	}
+
+	/**
+	 * @param width
+	 * @param join
+	 * @param endCap
+	 * @param mitterLimit
+	 * @param dashes
+	 * @param dashPhase
+	 */
+	public AndroidPaint(float width, LineJoin join, EndCap endCap,
+			float mitterLimit, float[] dashes, float dashPhase) {
+		this.paint = new Paint();
+		this.paint.setStyle(Style.FILL_AND_STROKE);
+		this.paint.setStrokeWidth(width);
+		this.paint.setStrokeMiter(mitterLimit);
+		switch(join) {
+		case BEVEL:
+			this.paint.setStrokeJoin(Join.BEVEL);
+			break;
+		case MITER:
+			this.paint.setStrokeJoin(Join.MITER);
+			break;
+		case ROUND:
+			this.paint.setStrokeJoin(Join.ROUND);
+			break;
+		default:
+			throw new IllegalArgumentException("join"); //$NON-NLS-1$
+		}
+		switch(endCap) {
+		case BUTT:
+			this.paint.setStrokeCap(Cap.BUTT);
+			break;
+		case ROUND:
+			this.paint.setStrokeCap(Cap.ROUND);
+			break;
+		case SQUARE:
+			this.paint.setStrokeCap(Cap.SQUARE);
+			break;
+		default:
+			throw new IllegalArgumentException("endCap"); //$NON-NLS-1$
+		}
+		if (dashes!=null && dashes.length>=2) {
+			this.paint.setPathEffect(new DashPathEffect(dashes, dashPhase));
+		}
+	}
+
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(this.paint);
+	}
+
+	@Override
+	public float[] getDashArray() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public float getDashPhase() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public float getLineWidth() {
+		return this.paint.getStrokeWidth();
+	}
+
+	@Override
+	public LineJoin getLineJoin() {
+		switch(this.paint.getStrokeJoin()) {
+		case BEVEL:
+			return LineJoin.BEVEL;
+		case MITER:
+			return LineJoin.MITER;
+		case ROUND:
+			return LineJoin.ROUND;
+		default:
+			throw new IllegalStateException();
+		}
+	}
+
+	@Override
+	public EndCap getEndCap() {
+		switch(this.paint.getStrokeCap()) {
+		case BUTT:
+			return EndCap.BUTT;
+		case SQUARE:
+			return EndCap.SQUARE;
+		case ROUND:
+			return EndCap.ROUND;
+		default:
+			throw new IllegalStateException();
+		}
+	}
+
+	@Override
+	public float getMiterLimit() {
+		return this.paint.getStrokeMiter();
+	}
+
+	@Override
+	public Font getFont() {
+		return this;
+	}
+
+	@Override
+	public float getLeading() {
+		android.graphics.Paint.FontMetrics fm = this.paint.getFontMetrics();
+		return fm.leading;
+	}
+
+	@Override
+	public float getAscent() {
+		android.graphics.Paint.FontMetrics fm = this.paint.getFontMetrics();
+		return Math.abs(fm.ascent);
+	}
+
+	@Override
+	public float getDescent() {
+		android.graphics.Paint.FontMetrics fm = this.paint.getFontMetrics();
+		return Math.abs(fm.descent);
+	}
+
+	@Override
+	public float getHeight() {
+		return this.paint.getTextSize();
+	}
+
+	@Override
+	public float getMaxAscent() {
+		android.graphics.Paint.FontMetrics fm = this.paint.getFontMetrics();
+		return Math.abs(fm.top);
+	}
+
+	@Override
+	public float getMaxDescent() {
+		android.graphics.Paint.FontMetrics fm = this.paint.getFontMetrics();
+		return Math.abs(fm.bottom);
+	}
+
+	@Override
+	public float getMaxAdvance() {
+		return this.paint.measureText("m"); //$NON-NLS-1$
+	}
+
+	@Override
+	public float stringWidth(String str) {
+		return this.paint.measureText(str);
+	}
+
+	@Override
+	public Rectangle2f getStringBounds(String str) {
+		Rect r = new Rect();
+		this.paint.getTextBounds(str, 0, str.length(), r);
+		return new Rectangle2f(0, 0, r.width(), r.height());
+	}
+
+	@Override
+	public Rectangle2f getMaxCharBounds() {
+		return new Rectangle2f(0, 0, getMaxAdvance(), getHeight());
+	}
+
+	/** Replies the preferred size of the font.
+	 * 
+	 * @return the size of the font; or {@code -1} if
+	 * the size is unknown.
+	 */
+	@Override
+	public float getSize() {
+		return this.paint.getTextSize();
+	}
+
+	@Override
+	public String getFamily() {
+		return getName();
+	}
+
+	@Override
+	public String getFontName() {
+		return getPSName();
+	}
+
+	@Override
+	public String getName() {
+		Typeface tf = this.paint.getTypeface();
+		tf = Typeface.create(tf, Typeface.NORMAL);
+		if (tf.equals(Typeface.DEFAULT)) {
+			return "Normal"; //$NON-NLS-1$
+		}
+		if (tf.equals(Typeface.MONOSPACE)) {
+			return "Monospace"; //$NON-NLS-1$
+		}
+		if (tf.equals(Typeface.SANS_SERIF)) {
+			return "Sans"; //$NON-NLS-1$
+		}
+		if (tf.equals(Typeface.SERIF)) {
+			return "Serif"; //$NON-NLS-1$
+		}
+		return "Unknown"; //$NON-NLS-1$
+	}
+
+	@Override
+	public String getPSName() {
+		StringBuilder b = new StringBuilder();
+		b.append(getName());
+		b.append("."); //$NON-NLS-1$
+		Typeface tf = this.paint.getTypeface();
+		if (tf==null) tf = Typeface.DEFAULT;
+		switch(tf.getStyle()) {
+		case Typeface.BOLD_ITALIC:
+			b.append("bolditalic"); //$NON-NLS-1$
+			break;
+		case Typeface.BOLD:
+			b.append("bold"); //$NON-NLS-1$
+			break;
+		case Typeface.ITALIC:
+			b.append("italic"); //$NON-NLS-1$
+			break;
+		default:
+		case Typeface.NORMAL:
+			b.append("plain"); //$NON-NLS-1$
+			break;
+		}
+		return b.toString();
+	}
+
+	@Override
+	public boolean isPlain() {
+		Typeface tf = this.paint.getTypeface();
+		if (tf==null) tf = Typeface.DEFAULT;
+		return !tf.isBold() && !tf.isItalic();
+	}
+
+	@Override
+	public boolean isBold() {
+		Typeface tf = this.paint.getTypeface();
+		if (tf==null) tf = Typeface.DEFAULT;
+		return tf.isBold();
+	}
+
+	@Override
+	public boolean isItalic() {
+		Typeface tf = this.paint.getTypeface();
+		if (tf==null) tf = Typeface.DEFAULT;
+		return tf.isItalic();
+	}
+
+	@Override
+	public Font deriveFont(float size) {
+		Paint pt = new Paint(this.paint);
+		pt.setTextSize(size);
+		return new AndroidPaint(pt);
+	}
+
+	@Override
+	public Font deriveFont(FontStyle style, float size) {
+		Paint pt = new Paint(this.paint);
+		pt.setTextSize(size);
+		pt.setTypeface(Typeface.create(pt.getTypeface(), toAndroid(style)));
+		return new AndroidPaint(pt);
+	}
+
+	@Override
+	public Font deriveFont(FontStyle style) {
+		Paint pt = new Paint(this.paint);
+		pt.setTypeface(Typeface.create(pt.getTypeface(), toAndroid(style)));
+		return new AndroidPaint(pt);
+	}
+
+	@Override
+	public float getItalicAngle() {
+		return 0;
+	}
+
+	@Override
+	public String toString() {
+		if (this.paint==null) return null;
+		return "color=0x"+Integer.toHexString(this.paint.getColor()); //$NON-NLS-1$
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidPdf.java
===================================================================
--- trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidPdf.java	                        (rev 0)
+++ trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidPdf.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,93 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.android;
+
+import java.io.IOException;
+import java.net.URL;
+
+import org.arakhne.afc.ui.vector.Dimension;
+import org.arakhne.afc.ui.vector.Image;
+import org.arakhne.afc.ui.vector.ImageObserver;
+import org.arakhne.afc.ui.vector.Pdf;
+
+/** Android implementation of the generic Pdf.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AndroidPdf implements Pdf {
+
+	/**
+	 * @param url
+	 * @throws IOException
+	 */
+	public AndroidPdf(URL url) throws IOException {
+		//
+	}
+
+	@Override
+	public Image getImage() {
+		return null;
+	}
+
+	@Override
+	public void setViewerSize(Dimension dimension) {
+		//
+	}
+
+	@Override
+	public int getPageNumber() {
+		return -1;
+	}
+
+	@Override
+	public boolean setPageNumber(int pageno, ImageObserver observer) {
+		return false;
+	}
+
+	@Override
+	public int getPageCount() {
+		return 0;
+	}
+
+	@Override
+	public void stopPageLoading() {
+		//
+	}
+
+	@Override
+	public void release() {
+		//
+	}
+
+	@Override
+	public float getPageWidth() {
+		return Float.NaN;
+	}
+
+	@Override
+	public float getPageHeight() {
+		return Float.NaN;
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidRaster.java
===================================================================
--- trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidRaster.java	                        (rev 0)
+++ trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidRaster.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,78 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.android;
+
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.ui.vector.Raster;
+
+import android.graphics.Bitmap;
+
+/** Android implementation of the generic raster.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AndroidRaster implements Raster {
+
+	private final Bitmap image;
+	private final int x;
+	private final int y;
+	private final int width;
+	private final int height;
+
+	/**
+	 * @param img
+	 * @param area
+	 */
+	public AndroidRaster(Bitmap img, Rectangle2f area) {
+		this.image = img;
+		this.x = (int)area.getMinX();
+		this.y = (int)area.getMinY();
+		this.width = (int)Math.ceil(area.getWidth());
+		this.height = (int)Math.ceil(area.getHeight());
+	}
+
+	@Override
+	public int getNumBands() {
+		return this.image.getByteCount();
+	}
+
+	@Override
+	public int[] getPixel(int x, int y, int[] samples) {
+		int[] tab = samples;
+		int size = this.width * this.height;
+		if (tab==null || tab.length<size) {
+			tab = new int[size];
+		}
+		this.image.getPixels(
+				tab, 0, 0,
+				this.x, this.y, this.width, this.height);
+		return tab;
+	}
+
+	@Override
+	public String toString() {
+		return this.image.toString();
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidVectorToolkit.java
===================================================================
--- trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidVectorToolkit.java	                        (rev 0)
+++ trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/AndroidVectorToolkit.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,283 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.android;
+
+import java.io.IOError;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+
+import org.arakhne.afc.math.continous.object2d.Shape2f;
+import org.arakhne.afc.math.matrix.Transform2D;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Composite;
+import org.arakhne.afc.ui.vector.Dimension;
+import org.arakhne.afc.ui.vector.Font;
+import org.arakhne.afc.ui.vector.FontMetrics;
+import org.arakhne.afc.ui.vector.FontStyle;
+import org.arakhne.afc.ui.vector.Image;
+import org.arakhne.afc.ui.vector.Margins;
+import org.arakhne.afc.ui.vector.Pdf;
+import org.arakhne.afc.ui.vector.Stroke;
+import org.arakhne.afc.ui.vector.Stroke.EndCap;
+import org.arakhne.afc.ui.vector.Stroke.LineJoin;
+import org.arakhne.afc.ui.vector.VectorGraphics2D;
+import org.arakhne.afc.ui.vector.VectorToolkit;
+import org.arakhne.vmutil.OperatingSystem;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+
+/** Android implementation of the generic Window toolkit.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AndroidVectorToolkit extends VectorToolkit {
+
+	/**
+	 */
+	public AndroidVectorToolkit() {
+		//
+	}
+
+	@Override
+	protected boolean isSupported() {
+		return OperatingSystem.ANDROID.isCurrentOS();
+	}
+
+	@Override
+	protected void postDrawing(VectorGraphics2D context) {
+		// do not call super function to avoid to reset the current context.
+	}
+
+	@Override
+	protected <T> T toNativeUIObject(Class<T> type, Object o) {
+		if (o instanceof NativeWrapper) {
+			return ((NativeWrapper)o).getNativeObject(type);
+		}
+		return type.cast(o);
+	}
+
+	@Override
+	protected Shape2f createShape(Object nativeObject) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	protected Transform2D createTransform(Object affineTransform) {
+		if (affineTransform==null) return null;
+		Matrix matrix = (Matrix)affineTransform;
+		float[] values = new float[9];
+		matrix.getValues(values);
+		return new Transform2D(
+				values[0], values[1], values[2],
+				values[3], values[4], values[5]);
+	}
+
+	@Override
+	protected Dimension createDimension(float width, float height) {
+		return new AndroidDimension(width, height);
+	}
+
+	@Override
+	protected Margins createMargins(float top, float left, float right,
+			float bottom) {
+		return new AndroidMargins(top, left, right, bottom);
+	}
+
+	@Override
+	protected Composite createComposite(Object compositeObject) {
+		if (compositeObject==null) return null;
+		return new AndroidComposite((Paint)compositeObject);
+	}
+
+	@Override
+	protected Composite createComposite(float alpha) {
+		return new AndroidComposite(alpha);
+	}
+
+	@Override
+	protected org.arakhne.afc.ui.vector.Paint createPaint(Object paintObject) {
+		return new AndroidPaint((Paint)paintObject);
+	}
+
+	@Override
+	protected Font createFont(String name, FontStyle style, float size) {
+		return AndroidPaint.getFont(name, style, size, getCurrentDrawingContext());
+	}
+
+	@Override
+	protected Font createFont(Object fontObject) {
+		if (fontObject==null) return null;
+		return new AndroidPaint((android.graphics.Paint)fontObject);
+	}
+
+	@Override
+	protected Font getDefaultFont() {
+		return AndroidPaint.getDefaultFont();
+	}
+
+	@Override
+	protected FontMetrics createFontMetrics(Object metricsObject) {
+		if (metricsObject==null) return null;
+		return new AndroidPaint((android.graphics.Paint)metricsObject);
+	}
+
+	@Override
+	protected FontMetrics createFontMetrics(Font font) {
+		if (font instanceof FontMetrics)
+			return (FontMetrics)font;
+		return new AndroidPaint(toNativeUIObject(Paint.class, font));
+	}
+
+	@Override
+	protected Image createImage(URL url) {
+		if (url==null) return null;
+		try {
+			return createImage(url.openStream());
+		}
+		catch (IOException e) {
+			throw new IOError(e);
+		}
+	}
+
+	@Override
+	protected Image createImage(InputStream stream) {
+		if (stream==null) return null;
+		AndroidImage img = new AndroidImage(stream);
+		if (img.getBitmap()==null) return null;
+		return img;
+	}
+
+	@Override
+	protected Image createImage(int width, int height, boolean isAlpha) {
+		AndroidImage img = new AndroidImage(width, height, isAlpha);
+		if (img.getBitmap()==null) return null;
+		return img;
+	}
+
+	@Override
+	protected Image createImage(Object imageObject) {
+		if (imageObject==null) return null;
+		AndroidImage img = new AndroidImage((Bitmap)imageObject);
+		if (img.getBitmap()==null) return null;
+		return img;		
+	}
+
+	@Override
+	protected Image createTransparentImage(Image imageObject, float transparency) {
+		if (imageObject==null) return null;
+		Bitmap aImg = toNativeUIObject(Bitmap.class, imageObject);
+		Bitmap mutableBitmap = aImg.copy(Bitmap.Config.ARGB_8888, true);
+		Canvas canvas = new Canvas(mutableBitmap);
+		float t = 255f*(transparency+1f)/2f;
+		int colour = ((int)t & 0xFF) << 24;
+		canvas.drawColor(colour, PorterDuff.Mode.DST_OUT);
+		return new AndroidImage(mutableBitmap);
+	}
+
+	@Override
+	protected Image makeTransparentImage(Image imageObject, float transparency) {
+		if (imageObject==null) return null;
+		Bitmap aImg = toNativeUIObject(Bitmap.class, imageObject);
+		Bitmap mutableBitmap = aImg.isMutable() ?
+					aImg : aImg.copy(Bitmap.Config.ARGB_8888, true);
+		Canvas canvas = new Canvas(mutableBitmap);
+		float t = 255f*(transparency+1f)/2f;
+		int colour = ((int)t & 0xFF) << 24;
+		canvas.drawColor(colour, PorterDuff.Mode.DST_OUT);
+		return new AndroidImage(mutableBitmap);
+	}
+
+	@Override
+	protected void write(Image image, String type, OutputStream stream)
+			throws IOException {
+		String lt = type.toLowerCase();
+		Bitmap aImg = toNativeUIObject(Bitmap.class, image);
+		CompressFormat format;
+		if (lt.equals("png")) { //$NON-NLS-1$
+			format = CompressFormat.PNG;
+		}
+		else if (lt.equals("jpeg") || lt.equals("jpg")) { //$NON-NLS-1$ //$NON-NLS-2$
+			format = CompressFormat.JPEG;
+		}
+		else if (lt.equals("webp")) { //$NON-NLS-1$
+			format = CompressFormat.WEBP;
+		}
+		else {
+			throw new IOException("Unsupported image type: "+type); //$NON-NLS-1$
+		}
+		if (!aImg.compress(format, 100, stream)) {
+			throw new IOException("Unable to write the Android image for type: "+type); //$NON-NLS-1$
+		}
+	}
+
+	@Override
+	protected Stroke createStroke(float width, LineJoin join, EndCap endCap,
+			float mitterLimit, float[] dashes, float dashPhase) {
+		return new AndroidPaint(width, join, endCap, mitterLimit, dashes, dashPhase);
+	}
+
+	@Override
+	protected Stroke createStroke(Object strokeObject) {
+		if (strokeObject==null) return null;
+		return new AndroidPaint((Paint)strokeObject);
+	}
+
+	@Override
+	protected Color createColor(int red, int green, int blue, int alpha) {
+		return new AndroidColor(red, green, blue, alpha);
+	}
+
+	@Override
+	protected Color createColor(Object rawColorObject) {
+		if (rawColorObject==null) return null;
+		if (rawColorObject instanceof Drawable) {
+			return new AndroidColor((Drawable)rawColorObject);
+		}
+		return new AndroidColor(((Number)rawColorObject).intValue());
+	}
+
+	@Override
+	protected <T> T findObjectWithId(int id, Class<T> type) {
+		Object o = null;
+		if (id==0) {
+			o = AndroidPaint.getDefaultFont();
+		}
+		if (o!=null && type.isInstance(o)) return type.cast(o);
+		return null;
+	}
+
+	@Override
+	protected Pdf wrapPdf(URL url) throws IOException {
+		return new AndroidPdf(url);
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/DelegatedVectorGraphics2D.java
===================================================================
--- trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/DelegatedVectorGraphics2D.java	                        (rev 0)
+++ trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/DelegatedVectorGraphics2D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,307 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.android;
+
+import java.net.URL;
+
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.math.continous.object2d.Shape2f;
+import org.arakhne.afc.math.generic.Point2D;
+import org.arakhne.afc.math.matrix.Transform2D;
+import org.arakhne.afc.ui.Graphics2DLOD;
+import org.arakhne.afc.ui.StringAnchor;
+import org.arakhne.afc.ui.TextAlignment;
+import org.arakhne.afc.ui.vector.AbstractVectorGraphics2D;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Composite;
+import org.arakhne.afc.ui.vector.Font;
+import org.arakhne.afc.ui.vector.FontMetrics;
+import org.arakhne.afc.ui.vector.Image;
+import org.arakhne.afc.ui.vector.ImageObserver;
+import org.arakhne.afc.ui.vector.Paint;
+import org.arakhne.afc.ui.vector.Stroke;
+import org.arakhne.afc.ui.vector.VectorGraphics2D;
+
+/** Implementation of a graphics context which is
+ * delegating to another graphics context.
+ *
+ * @param <G> is the type of the delegate.
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class DelegatedVectorGraphics2D<G extends VectorGraphics2D> extends AbstractVectorGraphics2D {
+
+	/** Delegate.
+	 */
+	protected final G delegate;
+	
+	/**
+	 * @param context
+	 */
+	public DelegatedVectorGraphics2D(G context) {
+		super(
+				context.getFillColor(),
+				context.getOutlineColor(),
+				context.getPaint(),
+				context.isInteriorPainted(),
+				context.isOutlineDrawn(),
+				context.getInteriorText());
+		this.delegate = context;
+		
+	}
+	
+	@Override
+	public Graphics2DLOD getLOD() {
+		return this.delegate.getLOD();
+	}
+
+	@Override
+	public StringAnchor getStringAnchor() {
+		return this.delegate.getStringAnchor();
+	}
+
+	@Override
+	public Font getFont() {
+		return this.delegate.getFont();
+	}
+
+	@Override
+	public void setFont(Font font) {
+		this.delegate.setFont(font);
+	}
+
+	@Override
+	public FontMetrics getFontMetrics() {
+		return this.delegate.getFontMetrics();
+	}
+
+	@Override
+	public FontMetrics getFontMetrics(Font f) {
+		return this.delegate.getFontMetrics(f);
+	}
+
+	@Override
+	public Shape2f getClip() {
+		return this.delegate.getClip();
+	}
+
+	@Override
+	public void setClip(Shape2f clip) {
+		this.delegate.setClip(clip);
+	}
+
+	@Override
+	public void clip(Shape2f clip) {
+		this.delegate.clip(clip);
+	}
+	
+	@Override
+	public void transform(Transform2D Tx) {
+		this.delegate.transform(Tx);
+	}
+
+	@Override
+	public void translate(float tx, float ty) {
+		this.delegate.translate(tx, ty);
+	}
+
+	@Override
+	public void scale(float sx, float sy) {
+		this.delegate.scale(sx, sy);
+	}
+
+	@Override
+	public void rotate(float theta) {
+		this.delegate.rotate(theta);
+	}
+
+	@Override
+	public void shear(float shx, float shy) {
+		this.delegate.shear(shx, shy);
+	}
+
+	@Override
+	public Transform2D setTransform(Transform2D Tx) {
+		return this.delegate.setTransform(Tx);
+	}
+
+	@Override
+	public Transform2D getTransform() {
+		return this.delegate.getTransform();
+	}
+
+	@Override
+	public void setBackground(Color color) {
+		this.delegate.setBackground(color);
+	}
+
+	@Override
+	public Color getBackground() {
+		return this.delegate.getBackground();
+	}
+
+	@Override
+	public void clear(Shape2f s) {
+		this.delegate.clear(s);
+	}
+
+	@Override
+	public void setComposite(Composite composite) {
+		this.delegate.setComposite(composite);
+	}
+
+	@Override
+	public Composite getComposite() {
+		return this.delegate.getComposite();
+	}
+
+	@Override
+	public void setStroke(Stroke stroke) {
+		this.delegate.setStroke(stroke);
+	}
+
+	@Override
+	public Stroke getStroke() {
+		return this.delegate.getStroke();
+	}
+				
+	@Override
+	public Point2D computeTextPosition(String text, Rectangle2f bounds,
+			TextAlignment halign, TextAlignment valign) {
+		return this.delegate.computeTextPosition(text, bounds, halign, valign);
+	}
+
+	@Override
+	public Font getDefaultFont() {
+		return this.delegate.getDefaultFont();
+	}
+			
+	@Override
+	public void dispose() {
+		this.delegate.dispose();
+		super.dispose();
+	}
+	
+	@Override
+	public void reset() {
+		this.delegate.reset();
+		super.reset();
+	}	
+	
+	@Override
+	public boolean drawImage(URL imageURL, Image img, float dx1, float dy1,
+			float dx2, float dy2, int sx1, int sy1, int sx2, int sy2) {
+		preDrawing();
+		Image i = onImagePainting(getFillColor(), getOutlineColor(), getPaint(),
+				isInteriorPainted(), isOutlineDrawn(), getInteriorText(), img);
+		boolean b = this.delegate.drawImage(imageURL, i, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2);
+		postDrawing();
+		return b;
+	}
+
+	@Override
+	public boolean drawImage(URL imageURL, Image img, float dx1, float dy1,
+			float dx2, float dy2, int sx1, int sy1, int sx2, int sy2,
+			ImageObserver observer) {
+		preDrawing();
+		Image i = onImagePainting(getFillColor(), getOutlineColor(), getPaint(),
+				isInteriorPainted(), isOutlineDrawn(), getInteriorText(), img);
+		boolean b = this.delegate.drawImage(imageURL, i, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer);
+		postDrawing();
+		return b;
+	}
+
+	@Override
+	public void draw(Shape2f s) {
+		preDrawing();
+		onAttributePainting(getFillColor(), getOutlineColor(), getPaint(),
+				isInteriorPainted(), isOutlineDrawn(), getInteriorText());
+		this.delegate.draw(s);
+		postDrawing();
+	}
+
+	@Override
+	public void drawString(String str, float x, float y) {
+		preDrawing();
+		onAttributePainting(getFillColor(), getOutlineColor(), getPaint(),
+				isInteriorPainted(), isOutlineDrawn(), getInteriorText());
+		this.delegate.drawString(str, x, y);
+		postDrawing();
+	}
+
+	@Override
+	public void drawString(String str, float x, float y, Shape2f clip) {
+		preDrawing();
+		onAttributePainting(getFillColor(), getOutlineColor(), getPaint(),
+				isInteriorPainted(), isOutlineDrawn(), getInteriorText());
+		this.delegate.drawString(str, x, y, clip);
+		postDrawing();
+	}
+
+	@Override
+	public void drawPoint(float x, float y) {
+		preDrawing();
+		onAttributePainting(getFillColor(), getOutlineColor(), getPaint(),
+				isInteriorPainted(), isOutlineDrawn(), getInteriorText());
+		this.delegate.drawPoint(x,y);
+		postDrawing();
+	}
+	
+	/** Invoked to set the painting attributes just before drawing a shape.
+	 * 
+	 * @param fillColor is the color used to fill the shapes.
+	 * @param outlineColor is the color used to draw the outline of the shapes.
+	 * @param paint is the painter.
+	 * @param drawInterior indicates if the interior of the shapes are painted.
+	 * @param drawOutline indicates if the outline of the shapes are painted.
+	 * @param interiorText is the text to drawn inside the shapes.
+	 */
+	protected void onAttributePainting(Color fillColor, Color outlineColor, Paint paint, boolean drawInterior, boolean drawOutline, String interiorText) {
+		if (fillColor!=null) this.delegate.setFillColor(fillColor);
+		if (outlineColor!=null) this.delegate.setOutlineColor(outlineColor);
+		if (paint!=null) this.delegate.setPaint(paint);
+		this.delegate.setInteriorPainted(drawInterior);
+		this.delegate.setOutlineDrawn(drawOutline);
+		this.delegate.setInteriorText(interiorText);
+	}
+
+	/** Invoked to set the painting attributes just before drawing an image.
+	 * This function invokes {@link #onAttributePainting(Color, Color, Paint, boolean, boolean, String)}
+	 * to initialize the painting attributes.
+	 * 
+	 * @param fillColor is the color used to fill the shapes.
+	 * @param outlineColor is the color used to draw the outline of the shapes.
+	 * @param paint is the painter.
+	 * @param drawInterior indicates if the interior of the shapes are painted.
+	 * @param drawOutline indicates if the outline of the shapes are painted.
+	 * @param interiorText is the text to drawn inside the shapes.
+	 * @param image is the image to draw.
+	 * @return the image to draw.
+	 */
+	protected Image onImagePainting(Color fillColor, Color outlineColor, Paint paint, boolean drawInterior, boolean drawOutline, String interiorText, Image image) {
+		onAttributePainting(fillColor, outlineColor, paint, drawInterior, drawOutline, interiorText);
+		return image;
+	}
+
+}

Added: trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/NativeWrapper.java
===================================================================
--- trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/NativeWrapper.java	                        (rev 0)
+++ trunk/ui/ui-vector-android/src/main/java/org/arakhne/afc/ui/vector/android/NativeWrapper.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,40 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.android;
+
+
+/** Interface for all the Android wrappers.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+interface NativeWrapper {
+
+	/** Return the native object.
+	 * 
+	 * @param type is the expected type.
+	 * @return the wrapped object.
+	 */
+	public <T> T getNativeObject(Class<T> type);
+	
+}
\ No newline at end of file


Property changes on: trunk/ui/ui-vector-awt
___________________________________________________________________
Added: svn:ignore
   + .classpath
..metadata
..settings
..project
target
bin


Added: trunk/ui/ui-vector-awt/pom.xml
===================================================================
--- trunk/ui/ui-vector-awt/pom.xml	                        (rev 0)
+++ trunk/ui/ui-vector-awt/pom.xml	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,33 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.arakhne.afc</groupId>
+		<artifactId>arakhneUi</artifactId>
+		<version>1.0-SNAPSHOT</version>
+	</parent>
+
+	<groupId>org.arakhne.afc.ui</groupId>
+	<artifactId>ui-vector-awt</artifactId>
+	<version>1.0-SNAPSHOT</version>
+	<name>Vector-API AWT-Implementation</name>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.arakhne.afc</groupId>
+			<artifactId>arakhneVmutils</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.arakhne.afc.ui</groupId>
+			<artifactId>ui-vector</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.arakhne.afc.ui</groupId>
+			<artifactId>ui-awt</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.swinglabs</groupId>
+			<artifactId>pdf-renderer</artifactId>
+		</dependency>
+	</dependencies>
+</project>

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtBufferedImage.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtBufferedImage.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtBufferedImage.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,94 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.ui.Graphics2DLOD;
+import org.arakhne.afc.ui.awt.DefaultLODGraphics2D;
+import org.arakhne.afc.ui.awt.LODGraphics2D;
+import org.arakhne.afc.ui.vector.Image;
+import org.arakhne.afc.ui.vector.ImageObserver;
+import org.arakhne.afc.ui.vector.Raster;
+import org.arakhne.afc.ui.vector.VectorGraphics2D;
+
+/** AWT implementation of the generic Image.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AwtBufferedImage extends BufferedImage implements Image, NativeWrapper {
+
+	/**
+	 * @param width is the width of the image.
+	 * @param height is the height of the image.
+	 * @param isAlpha indicates if the image supports the alpha colors.
+	 */
+	public AwtBufferedImage(int width, int height, boolean isAlpha) {
+		super(width, height, isAlpha ? BufferedImage.TYPE_INT_ARGB
+				: BufferedImage.TYPE_INT_RGB);
+	}
+
+	@Override
+	public int getWidth(ImageObserver observer) {
+		return getWidth(observer==null ? null : new AwtImageObserver(observer));
+	}
+
+	@Override
+	public int getHeight(ImageObserver observer) {
+		return getHeight(observer==null ? null : new AwtImageObserver(observer));
+	}
+
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(this);
+	}
+
+	@Override
+	public int getNumBands() {
+		return getSampleModel().getNumBands();
+	}
+
+	@Override
+	public VectorGraphics2D getVectorGraphics() {
+		Graphics2D g2d = (Graphics2D)getGraphics();
+		LODGraphics2D lg = new DefaultLODGraphics2D(g2d, null, Graphics2DLOD.HIGH_LEVEL_OF_DETAIL);
+		return new DelegatedVectorGraphics2D<LODGraphics2D>(lg);
+	}
+
+	@Override
+	public Raster getData(Rectangle2f area) {
+		java.awt.image.Raster awtRaster = getData(
+				new Rectangle(
+						(int)area.getMinX(),
+						(int)area.getMinY(),
+						(int)area.getWidth(),
+						(int)area.getHeight()));
+		return new AwtRaster(awtRaster);
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtColor.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtColor.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtColor.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,74 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+import org.arakhne.afc.ui.vector.Color;
+
+/** AWT implementation of the generic color.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AwtColor extends java.awt.Color implements Color, NativeWrapper {
+
+	private static final long serialVersionUID = 6487706440529938798L;
+
+	/**
+	 * @param red
+	 * @param green
+	 * @param blue
+	 * @param alpha
+	 */
+	public AwtColor(int red, int green, int blue, int alpha) {
+		super(red, green, blue, alpha);
+	}
+	
+	private AwtColor(java.awt.Color c) {
+		super(c.getRGB(), true);
+	}
+
+	@Override
+	public Color brighterColor() {
+		return new AwtColor(brighter());
+	}
+
+	@Override
+	public Color darkerColor() {
+		return new AwtColor(darker());
+	}
+	
+	@Override
+	public Color transparentColor() {
+		int alpha = getAlpha() / 2;
+		return new AwtColor(
+				getRed(), getGreen(), getBlue(),
+				alpha);
+	}
+	
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(this);
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtComposite.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtComposite.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtComposite.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,81 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+import java.awt.AlphaComposite;
+
+import org.arakhne.afc.ui.vector.Composite;
+
+/** AWT implementation of the generic alpha composite.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AwtComposite implements Composite, NativeWrapper {
+
+	private final java.awt.Composite composite;
+	
+	/**
+	 * @param type
+	 * @param alpha
+	 */
+	public AwtComposite(int type, float alpha) {
+		this(AlphaComposite.getInstance(type, alpha));
+	}
+	
+	/**
+	 * @param composite
+	 */
+	public AwtComposite(java.awt.Composite composite) {
+		this.composite = composite;
+	}
+
+	@Override
+	public String toString() {
+		return this.composite.toString();
+	}
+
+	/**
+	 * Replies the composite.
+	 * 
+	 * @return the composite.
+	 */
+	public java.awt.Composite getComposite() {
+		return this.composite;
+	}
+
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(this.composite);
+	}
+
+	@Override
+	public float getAlpha() {
+		if (this.composite instanceof AlphaComposite) {
+			return ((AlphaComposite)this.composite).getAlpha();
+		}
+		return 1f;
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtDimension.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtDimension.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtDimension.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,69 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+import org.arakhne.afc.ui.awt.FloatDimension;
+import org.arakhne.afc.ui.vector.Dimension;
+
+/** AWT implementation of the generic dimension.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AwtDimension extends FloatDimension implements Dimension, NativeWrapper {
+
+	/**
+	 * @param w
+	 * @param h
+	 */
+	public AwtDimension(float w, float h) {
+		super(w, h);
+	}
+
+	@Override
+	public float width() {
+		return (float)getWidth();
+	}
+
+	@Override
+	public float height() {
+		return (float)getHeight();
+	}
+		
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(this);
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if (obj==this) return true;
+		if (obj instanceof Dimension) {
+			Dimension d = (Dimension)obj;
+			return d.width()==getWidth() && d.height()==getHeight();
+		}
+		return super.equals(obj);
+	}
+	
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtFont.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtFont.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtFont.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,175 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+import java.awt.geom.Rectangle2D;
+
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.ui.awt.AwtUtil;
+import org.arakhne.afc.ui.vector.Font;
+import org.arakhne.afc.ui.vector.FontStyle;
+
+/** AWT implementation of the generic font.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AwtFont implements Font, NativeWrapper {
+
+	private static int toAWT(FontStyle fs) {
+		switch(fs) {
+		case BOLD:
+			return java.awt.Font.BOLD;
+		case BOLD_ITALIC:
+			return java.awt.Font.BOLD | java.awt.Font.ITALIC;
+		case ITALIC:
+			return java.awt.Font.ITALIC;
+		case PLAIN:
+		default:
+			return java.awt.Font.PLAIN;
+		}
+	}
+	
+	private final java.awt.Font font;
+	
+	/**
+	 * @param name
+	 * @param style
+	 * @param size
+	 */
+	public AwtFont(String name, FontStyle style, float size) {
+		this(new java.awt.Font(name, toAWT(style), (int)Math.ceil(size)));
+	}
+	
+	/**
+	 * @param font
+	 */
+	public AwtFont(java.awt.Font font) {
+		this.font = font;
+	}
+
+	/**
+	 * Replies the font.
+	 * 
+	 * @return the font
+	 */
+	public java.awt.Font getFont() {
+		return this.font;
+	}
+	
+	@Override
+	public String toString() {
+		return this.font.toString();
+	}
+
+	@Override
+	public String getFamily() {
+		return this.font.getFamily();
+	}
+
+	@Override
+	public String getFontName() {
+		return this.font.getFontName();
+	}
+
+	@Override
+	public String getName() {
+		return this.font.getName();
+	}
+
+	@Override
+	public float getSize() {
+		return this.font.getSize2D();
+	}
+
+	@Override
+	public boolean isPlain() {
+		return this.font.isPlain();
+	}
+
+	@Override
+	public boolean isBold() {
+		return this.font.isBold();
+	}
+
+	@Override
+	public boolean isItalic() {
+		return this.font.isItalic();
+	}
+
+	@Override
+	public Font deriveFont(float size) {
+		java.awt.Font aFont = this.font.deriveFont(size);
+		return new AwtFont(aFont);
+	}
+	
+	private static int toAwt(FontStyle style) {
+		switch(style) {
+		case BOLD:
+			return java.awt.Font.BOLD;
+		case ITALIC:
+			return java.awt.Font.ITALIC;
+		case BOLD_ITALIC:
+			return java.awt.Font.BOLD|java.awt.Font.ITALIC;
+		default:
+			return java.awt.Font.PLAIN;
+		}
+	}
+
+	@Override
+	public Font deriveFont(FontStyle style, float size) {
+		java.awt.Font aFont = this.font.deriveFont(toAwt(style), size);
+		return new AwtFont(aFont);
+	}
+
+	@Override
+	public Font deriveFont(FontStyle style) {
+		java.awt.Font aFont = this.font.deriveFont(toAwt(style));
+		return new AwtFont(aFont);
+	}
+
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(this.font);
+	}
+
+	@Override
+	public Rectangle2f getStringBounds(String str) {
+		Rectangle2D r = this.font.getStringBounds(str, 0, str.length(), AwtUtil.getVectorFontRenderContext());
+		return new Rectangle2f(
+				(float)r.getMinX(), (float)r.getMinY(),
+				(float)r.getWidth(), (float)r.getHeight());
+	}
+
+	@Override
+	public String getPSName() {
+		return this.font.getPSName();
+	}
+
+	@Override
+	public float getItalicAngle() {
+		return this.font.getItalicAngle();
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtFontMetrics.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtFontMetrics.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtFontMetrics.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,114 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+import java.awt.geom.Rectangle2D;
+
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.ui.vector.Font;
+import org.arakhne.afc.ui.vector.FontMetrics;
+
+/** AWT implementation of the generic font metrics.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AwtFontMetrics implements FontMetrics, NativeWrapper {
+
+	private final java.awt.FontMetrics fontMetrics;
+	
+	/**
+	 * @param fontMetrics
+	 */
+	public AwtFontMetrics(java.awt.FontMetrics fontMetrics) {
+		this.fontMetrics = fontMetrics;
+	}
+
+	@Override
+	public Font getFont() {
+		return new AwtFont(this.fontMetrics.getFont());
+	}
+
+	@Override
+	public float getLeading() {
+		return this.fontMetrics.getLeading();
+	}
+
+	@Override
+	public float getAscent() {
+		return this.fontMetrics.getAscent();
+	}
+
+	@Override
+	public float getDescent() {
+		return this.fontMetrics.getDescent();
+	}
+
+	@Override
+	public float getHeight() {
+		return this.fontMetrics.getHeight();
+	}
+
+	@Override
+	public float getMaxAscent() {
+		return this.fontMetrics.getMaxAscent();
+	}
+
+	@Override
+	public float getMaxDescent() {
+		return this.fontMetrics.getMaxDescent();
+	}
+
+	@Override
+	public float getMaxAdvance() {
+		return this.fontMetrics.getMaxAdvance();
+	}
+
+	@Override
+	public float stringWidth(String str) {
+		return this.fontMetrics.stringWidth(str);
+	}
+	
+	/** Replies the font metrics.
+	 * 
+	 * @return the font metrics.
+	 */
+	public java.awt.FontMetrics getFontMetrics() {
+		return this.fontMetrics;
+	}
+
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(this.fontMetrics);
+	}
+
+	@Override
+	public Rectangle2f getMaxCharBounds() {
+		Rectangle2D r = this.fontMetrics.getMaxCharBounds(null);
+		return new Rectangle2f(
+				(float)r.getMinX(), (float)r.getMinY(),
+				(float)r.getWidth(), (float)r.getHeight());
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtImage.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtImage.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtImage.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,116 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.ui.Graphics2DLOD;
+import org.arakhne.afc.ui.awt.DefaultLODGraphics2D;
+import org.arakhne.afc.ui.awt.LODGraphics2D;
+import org.arakhne.afc.ui.vector.Image;
+import org.arakhne.afc.ui.vector.ImageObserver;
+import org.arakhne.afc.ui.vector.Raster;
+import org.arakhne.afc.ui.vector.VectorGraphics2D;
+
+/** AWT implementation of the generic Image.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AwtImage implements Image, NativeWrapper {
+
+	private final java.awt.Image image;
+	
+	/**
+	 * @param image
+	 */
+	public AwtImage(java.awt.Image image) {
+		this.image = image;
+	}
+
+	@Override
+	public int getWidth(ImageObserver observer) {
+		return this.image.getWidth(observer==null ? null : new AwtImageObserver(observer));
+	}
+
+	@Override
+	public int getHeight(ImageObserver observer) {
+		return this.image.getHeight(observer==null ? null : new AwtImageObserver(observer));
+	}
+	
+	/** Replies the AWT image.
+	 * 
+	 * @return the AWT image.
+	 */
+	public java.awt.Image getImage() {
+		return this.image;
+	}
+
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(this.image);
+	}
+
+	@Override
+	public int getNumBands() {
+		if (this.image instanceof BufferedImage) {
+			return ((BufferedImage)this.image).getSampleModel().getNumBands();
+		}
+		return 4;
+	}
+
+	@Override
+	public int getRGB(int x, int y) {
+		if (this.image instanceof BufferedImage) {
+			return ((BufferedImage)this.image).getRGB(x, y);
+		}
+		return 0x0;
+	}
+
+	@Override
+	public VectorGraphics2D getVectorGraphics() {
+		Graphics2D g2d = (Graphics2D)this.image.getGraphics();
+		LODGraphics2D lg = new DefaultLODGraphics2D(g2d, null, Graphics2DLOD.HIGH_LEVEL_OF_DETAIL);
+		return new DelegatedVectorGraphics2D<LODGraphics2D>(lg);
+	}
+
+	@Override
+	public Raster getData(Rectangle2f area) {
+		if (this.image instanceof BufferedImage) {
+			BufferedImage bimg = (BufferedImage)this.image;
+			java.awt.image.Raster awtRaster = bimg.getData(
+					new Rectangle(
+							(int)area.getMinX(),
+							(int)area.getMinY(),
+							(int)area.getWidth(),
+							(int)area.getHeight()));
+			return new AwtRaster(awtRaster);
+		}
+		throw new UnsupportedOperationException();
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtImageObserver.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtImageObserver.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtImageObserver.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,64 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+import java.awt.Image;
+
+/** AWT implementation of the generic Image.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AwtImageObserver implements java.awt.image.ImageObserver, NativeWrapper {
+
+	private final org.arakhne.afc.ui.vector.ImageObserver obs;
+	
+	/**
+	 * @param obs
+	 */
+	public AwtImageObserver(org.arakhne.afc.ui.vector.ImageObserver obs) {
+		this.obs = obs;
+	}
+
+	@Override
+	public boolean imageUpdate(Image img, int infoflags, int x, int y,
+			int width, int height) {
+		int expectedFlags = ALLBITS | WIDTH | HEIGHT;
+		boolean loaded = (infoflags&expectedFlags) == expectedFlags;
+		if (loaded) {
+			if (img instanceof org.arakhne.afc.ui.vector.Image) {
+				this.obs.imageUpdate((org.arakhne.afc.ui.vector.Image)img, x, y, width, height);
+			}
+			else {
+				this.obs.imageUpdate(new AwtImage(img), x, y, width, height);
+			}
+		}
+		return !loaded;
+	}
+
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(this.obs);
+	}
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtMargins.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtMargins.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtMargins.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,88 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+import java.awt.Insets;
+
+import org.arakhne.afc.ui.vector.Margins;
+
+/** AWT implementation of the generic dimension.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AwtMargins implements Margins, NativeWrapper {
+
+	private float top;
+	private float left;
+	private float right;
+	private float bottom;
+	
+	/**
+	 * @param t
+	 * @param l
+	 * @param r
+	 * @param b
+	 */
+	public AwtMargins(float t, float l, float r, float b) {
+		this.top = t;
+		this.left = l;
+		this.right = r;
+		this.bottom = b;
+	}
+
+	@Override
+	public float top() {
+		return this.top;
+	}
+
+	@Override
+	public float left() {
+		return this.left;
+	}
+		
+	@Override
+	public float right() {
+		return this.right;
+	}
+
+	@Override
+	public float bottom() {
+		return this.bottom;
+	}
+
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(new Insets(
+				(int)this.top, (int)this.left, (int)this.bottom, (int)this.right));
+	}
+
+	@Override
+	public void set(float top, float left, float right, float bottom) {
+		this.top = top;
+		this.left = left;
+		this.right = right;
+		this.bottom = bottom;
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtPaint.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtPaint.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtPaint.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,50 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+import org.arakhne.afc.ui.vector.Paint;
+
+/** Public implementation of a Paint.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AwtPaint implements Paint, NativeWrapper {
+
+	private final java.awt.Paint paint;
+	
+	/**
+	 * @param paint
+	 */
+	public AwtPaint(java.awt.Paint paint) {
+		super();
+		this.paint = paint;
+	}
+
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(this.paint);
+	}
+	
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtPath.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtPath.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtPath.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,489 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+
+import org.arakhne.afc.math.continous.object2d.Path2f;
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.math.matrix.Transform2D;
+import org.arakhne.afc.ui.awt.VirtualizableShape;
+import org.arakhne.afc.ui.awt.ZoomableContextUtil;
+
+/** Public implementation of a Path2D.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AwtPath implements VirtualizableShape, NativeWrapper {
+
+	private final Path2f path;
+	
+	/**
+	 * @param path
+	 */
+	public AwtPath(Path2f path) {
+		super();
+		this.path = path;
+	}
+
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(this);
+	}
+
+	@Override
+	public Rectangle getBounds() {
+		Rectangle2f bb = this.path.toBoundingBox();
+		int ix = (int)bb.getMinX();
+		int iy = (int)bb.getMinY();
+		int mx = (int)Math.ceil(bb.getMaxX());
+		int my = (int)Math.ceil(bb.getMaxY());
+		return new Rectangle(ix, iy, mx-ix, my-iy);
+	}
+
+	@Override
+	public Rectangle2D getBounds2D() {
+		Rectangle2f bb = this.path.toBoundingBox();
+		return new Rectangle2D.Float(
+				bb.getMinX(), bb.getMinY(),
+				bb.getWidth(), bb.getHeight());
+	}
+
+	@Override
+	public boolean contains(double x, double y) {
+		return this.path.contains((float)x, (float)y);
+	}
+
+	@Override
+	public boolean contains(Point2D p) {
+		return this.path.contains((float)p.getX(), (float)p.getY());
+	}
+
+	@Override
+	public boolean intersects(double x, double y, double w, double h) {
+		return Path2f.intersects(this.path.getPathIterator(),
+				(float)x, (float)y, (float)w, (float)h);
+	}
+
+	@Override
+	public boolean intersects(Rectangle2D r) {
+		return Path2f.intersects(this.path.getPathIterator(),
+				(float)r.getMinX(), (float)r.getMinY(),
+				(float)r.getWidth(), (float)r.getHeight());
+	}
+
+	@Override
+	public boolean contains(double x, double y, double w, double h) {
+		return Path2f.contains(this.path.getPathIterator(),
+				(float)x, (float)y, (float)w, (float)h);
+	}
+
+	@Override
+	public boolean contains(Rectangle2D r) {
+		return Path2f.contains(this.path.getPathIterator(),
+				(float)r.getMinX(), (float)r.getMinY(),
+				(float)r.getWidth(), (float)r.getHeight());
+	}
+
+	@Override
+	public PathIterator getPathIterator(AffineTransform at) {
+		if (at==null || at.isIdentity())
+			return new AwtPathIterator(this.path.getPathIterator());
+		Transform2D tr = new Transform2D(
+				(float)at.getScaleX(),
+				(float)at.getShearX(),
+				(float)at.getTranslateX(),
+				(float)at.getShearY(),
+				(float)at.getScaleY(),
+				(float)at.getTranslateY());
+		return new AwtPathIterator(this.path.getPathIterator(tr));
+	}
+
+	@Override
+	public PathIterator getPathIterator(AffineTransform at, double flatness) {
+		if (at==null || at.isIdentity())
+			return new AwtPathIterator(this.path.getPathIterator((float)flatness));
+		Transform2D tr = new Transform2D(
+				(float)at.getScaleX(),
+				(float)at.getShearX(),
+				(float)at.getTranslateX(),
+				(float)at.getShearY(),
+				(float)at.getScaleY(),
+				(float)at.getTranslateY());
+		return new AwtPathIterator(this.path.getPathIterator(tr, (float)flatness));
+	}
+
+	@Override
+	public VirtualizableShape toScreen(boolean flipX, boolean flipY,
+			Rectangle2D documentScreenRectangle, float translationX,
+			float translationY, float zoom) {
+		return new ScreenPath(
+				flipX, flipY, documentScreenRectangle,
+				translationX, translationY, zoom);
+	}
+
+	@Override
+	public VirtualizableShape fromScreen(boolean flipX, boolean flipY,
+			Rectangle2D documentScreenRectangle, float translationX,
+			float translationY, float zoom) {
+		return this;
+	}
+
+	/**
+	 * @author $Author: galland$
+	 * @version $FullVersion$
+	 * @mavengroupid $GroupId$
+	 * @mavenartifactid $ArtifactId$
+	 */
+	private class ScreenPath implements VirtualizableShape, NativeWrapper {
+
+		final boolean flipX;
+		final boolean flipY;
+		final Rectangle2D documentScreenRectangle;
+		final float translationX;
+		final float translationY;
+		final float zoom;
+		
+		/**
+		 * @param flipX indicates if the X screen axis is inverted.
+		 * @param flipY indicates if the Y screen axis is inverted.
+		 * @param documentScreenRectangle is the area covered by the document in screen space.
+		 * @param translationX is the translation X in the context.
+		 * @param translationY is the translation Y in the context.
+		 * @param zoom is the current zooming factor of the view.
+		 */
+		public ScreenPath(
+				boolean flipX, boolean flipY,
+				Rectangle2D documentScreenRectangle,
+				float translationX, float translationY,
+				float zoom) {
+			this.flipX = flipX;
+			this.flipY = flipY;
+			this.documentScreenRectangle = (Rectangle2D)documentScreenRectangle.clone();
+			this.translationX = translationX;
+			this.translationY = translationY;
+			this.zoom = zoom;
+		}
+
+		@Override
+		public Rectangle getBounds() {
+			Rectangle r = (Rectangle)AwtPath.this.getBounds().clone();
+			ZoomableContextUtil.logical2pixel_r(
+					r,
+					true,
+					this.flipX, this.flipY,
+					this.documentScreenRectangle,
+					this.translationX, this.translationY,
+					this.zoom);
+			return r;
+		}
+
+		@Override
+		public Rectangle2D getBounds2D() {
+			Rectangle2D r = (Rectangle2D)AwtPath.this.getBounds2D().clone();
+			ZoomableContextUtil.logical2pixel_r(
+					r,
+					true,
+					this.flipX, this.flipY,
+					this.documentScreenRectangle,
+					this.translationX, this.translationY,
+					this.zoom);
+			return r;
+		}
+
+		@Override
+		public boolean contains(double x, double y) {
+			float sx = ZoomableContextUtil.pixel2logical_x(
+					(float)x, true, this.flipX,
+					this.documentScreenRectangle,
+					this.translationX, this.zoom);
+			float sy = ZoomableContextUtil.pixel2logical_y(
+					(float)y, true, this.flipY,
+					this.documentScreenRectangle,
+					this.translationY, this.zoom);
+			return AwtPath.this.contains(sx, sy);
+		}
+
+		@Override
+		public boolean contains(Point2D p) {
+			return contains(p.getX(), p.getY());
+		}
+
+		@Override
+		public boolean intersects(double x, double y, double w, double h) {
+			Rectangle2D r = new Rectangle2D.Float((float)x, (float)y, (float)w, (float)h);
+			ZoomableContextUtil.pixel2logical_r(
+					r,
+					true,
+					this.flipX, this.flipY,
+					this.documentScreenRectangle,
+					this.translationX, this.translationY,
+					this.zoom);
+			return AwtPath.this.intersects(r);
+		}
+
+		@Override
+		public boolean intersects(Rectangle2D r) {
+			Rectangle2D rr = (Rectangle2D)r.clone();
+			ZoomableContextUtil.pixel2logical_r(
+					rr,
+					true,
+					this.flipX, this.flipY,
+					this.documentScreenRectangle,
+					this.translationX, this.translationY,
+					this.zoom);
+			return AwtPath.this.intersects(rr);
+		}
+
+		@Override
+		public boolean contains(double x, double y, double w, double h) {
+			Rectangle2D r = new Rectangle2D.Float((float)x, (float)y, (float)w, (float)h);
+			ZoomableContextUtil.pixel2logical_r(
+					r,
+					true,
+					this.flipX, this.flipY,
+					this.documentScreenRectangle,
+					this.translationX, this.translationY,
+					this.zoom);
+			return AwtPath.this.contains(r);
+		}
+
+		@Override
+		public boolean contains(Rectangle2D r) {
+			Rectangle2D rr = (Rectangle2D)r.clone();
+			ZoomableContextUtil.pixel2logical_r(
+					rr,
+					true,
+					this.flipX, this.flipY,
+					this.documentScreenRectangle,
+					this.translationX, this.translationY,
+					this.zoom);
+			return AwtPath.this.contains(rr);
+		}
+
+		@Override
+		public PathIterator getPathIterator(AffineTransform at) {
+			return new ScreenPathIterator(at, AwtPath.this.getPathIterator(null));
+		}
+
+		@Override
+		public PathIterator getPathIterator(AffineTransform at, double flatness) {
+			float vFlatness = ZoomableContextUtil.pixel2logical_size((float)flatness, true, this.zoom);
+			return new ScreenPathIterator(at, AwtPath.this.getPathIterator(null,vFlatness));
+		}
+
+		@Override
+		public <T> T getNativeObject(Class<T> type) {
+			return type.cast(this);
+		}
+
+		@Override
+		public VirtualizableShape toScreen(boolean flipX, boolean flipY,
+				Rectangle2D documentScreenRectangle, float translationX,
+				float translationY, float zoom) {
+			return this;
+		}
+
+		@Override
+		public VirtualizableShape fromScreen(boolean flipX, boolean flipY,
+				Rectangle2D documentScreenRectangle, float translationX,
+				float translationY, float zoom) {
+			return AwtPath.this;
+		}
+		
+		/**
+		 * @author $Author: galland$
+		 * @version $FullVersion$
+		 * @mavengroupid $GroupId$
+		 * @mavenartifactid $ArtifactId$
+		 */
+		private class ScreenPathIterator implements PathIterator {
+
+			private final AffineTransform tr;
+			private final PathIterator pi;
+			
+			public ScreenPathIterator(AffineTransform tr, PathIterator pi) {
+				this.pi = pi;
+				this.tr = tr;
+			}
+
+			@Override
+			public int getWindingRule() {
+				return this.pi.getWindingRule();
+			}
+
+			@Override
+			public boolean isDone() {
+				return this.pi.isDone();
+			}
+
+			@Override
+			public void next() {
+				this.pi.next();
+			}
+
+			@Override
+			public int currentSegment(float[] coords) {
+				int t = this.pi.currentSegment(coords);
+				int numPts = 0;
+				switch(t) {
+				case PathIterator.SEG_CUBICTO:
+					++numPts;
+					coords[4] = ZoomableContextUtil.logical2pixel_x(
+							coords[4],
+							true,
+							ScreenPath.this.flipX,
+							ScreenPath.this.documentScreenRectangle,
+							ScreenPath.this.translationX,
+							ScreenPath.this.zoom);
+					coords[5] = ZoomableContextUtil.logical2pixel_y(
+							coords[5],
+							true,
+							ScreenPath.this.flipY,
+							ScreenPath.this.documentScreenRectangle,
+							ScreenPath.this.translationY,
+							ScreenPath.this.zoom);
+					//$FALL-THROUGH$
+				case PathIterator.SEG_QUADTO:
+					++numPts;
+					coords[2] = ZoomableContextUtil.logical2pixel_x(
+							coords[2],
+							true,
+							ScreenPath.this.flipX,
+							ScreenPath.this.documentScreenRectangle,
+							ScreenPath.this.translationX,
+							ScreenPath.this.zoom);
+					coords[3] = ZoomableContextUtil.logical2pixel_y(
+							coords[3],
+							true,
+							ScreenPath.this.flipY,
+							ScreenPath.this.documentScreenRectangle,
+							ScreenPath.this.translationY,
+							ScreenPath.this.zoom);
+					//$FALL-THROUGH$
+				case PathIterator.SEG_MOVETO:
+				case PathIterator.SEG_LINETO:
+					++numPts;
+					coords[0] = ZoomableContextUtil.logical2pixel_x(
+							coords[0],
+							true,
+							ScreenPath.this.flipX,
+							ScreenPath.this.documentScreenRectangle,
+							ScreenPath.this.translationX,
+							ScreenPath.this.zoom);
+					coords[1] = ZoomableContextUtil.logical2pixel_y(
+							coords[1],
+							true,
+							ScreenPath.this.flipY,
+							ScreenPath.this.documentScreenRectangle,
+							ScreenPath.this.translationY,
+							ScreenPath.this.zoom);
+					if (this.tr!=null) {
+						this.tr.transform(coords, 0, coords, 0, numPts);
+					}
+					break;
+				case SEG_CLOSE:
+				default:
+					break;
+				}
+				return t;
+			}
+
+			@Override
+			public int currentSegment(double[] coords) {
+				int t = this.pi.currentSegment(coords);
+				int numPts = 0;
+				switch(t) {
+				case PathIterator.SEG_CUBICTO:
+					++numPts;
+					coords[4] = ZoomableContextUtil.logical2pixel_x(
+							(float)coords[4],
+							true,
+							ScreenPath.this.flipX,
+							ScreenPath.this.documentScreenRectangle,
+							ScreenPath.this.translationX,
+							ScreenPath.this.zoom);
+					coords[5] = ZoomableContextUtil.logical2pixel_y(
+							(float)coords[5],
+							true,
+							ScreenPath.this.flipY,
+							ScreenPath.this.documentScreenRectangle,
+							ScreenPath.this.translationY,
+							ScreenPath.this.zoom);
+					//$FALL-THROUGH$
+				case PathIterator.SEG_QUADTO:
+					++numPts;
+					coords[2] = ZoomableContextUtil.logical2pixel_x(
+							(float)coords[2],
+							true,
+							ScreenPath.this.flipX,
+							ScreenPath.this.documentScreenRectangle,
+							ScreenPath.this.translationX,
+							ScreenPath.this.zoom);
+					coords[3] = ZoomableContextUtil.logical2pixel_y(
+							(float)coords[3],
+							true,
+							ScreenPath.this.flipY,
+							ScreenPath.this.documentScreenRectangle,
+							ScreenPath.this.translationY,
+							ScreenPath.this.zoom);
+					//$FALL-THROUGH$
+				case PathIterator.SEG_MOVETO:
+				case PathIterator.SEG_LINETO:
+					++numPts;
+					coords[0] = ZoomableContextUtil.logical2pixel_x(
+							(float)coords[0],
+							true,
+							ScreenPath.this.flipX,
+							ScreenPath.this.documentScreenRectangle,
+							ScreenPath.this.translationX,
+							ScreenPath.this.zoom);
+					coords[1] = ZoomableContextUtil.logical2pixel_y(
+							(float)coords[1],
+							true,
+							ScreenPath.this.flipY,
+							ScreenPath.this.documentScreenRectangle,
+							ScreenPath.this.translationY,
+							ScreenPath.this.zoom);
+					if (this.tr!=null) {
+						this.tr.transform(coords, 0, coords, 0, numPts);
+					}
+					break;
+				case SEG_CLOSE:
+				default:
+					break;
+				}
+				return t;
+			}
+			
+		}
+		
+	}
+	
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtPathIterator.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtPathIterator.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtPathIterator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,148 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+import java.awt.geom.PathIterator;
+import java.util.NoSuchElementException;
+
+import org.arakhne.afc.math.continous.object2d.PathElement2f;
+import org.arakhne.afc.math.continous.object2d.PathIterator2f;
+import org.arakhne.afc.math.generic.PathWindingRule;
+
+/** AWT implementation of the generic path iterator.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AwtPathIterator implements PathIterator, NativeWrapper {
+
+	private final PathIterator2f pathIterator;
+	
+	private PathElement2f element = null;
+	
+	/**
+	 * @param pathIterator
+	 */
+	public AwtPathIterator(PathIterator2f pathIterator) {
+		this.pathIterator = pathIterator;
+		next();
+	}
+
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(this.pathIterator);
+	}
+
+	@Override
+	public int getWindingRule() {
+		PathWindingRule rule = this.pathIterator.getWindingRule();
+		switch(rule) {
+		case NON_ZERO:
+			return PathIterator.WIND_NON_ZERO;
+		case EVEN_ODD:
+			return PathIterator.WIND_EVEN_ODD;
+		default:
+		}
+		throw new IllegalArgumentException();
+	}
+
+	@Override
+	public boolean isDone() {
+		return this.element==null;
+	}
+
+	@Override
+	public void next() {
+		this.element = null;
+		if (this.pathIterator.hasNext())
+			this.element = this.pathIterator.next();
+	}
+
+	@Override
+	public int currentSegment(float[] coords) {
+		if (this.element==null) throw new NoSuchElementException();
+		switch(this.element.type) {
+		case MOVE_TO:
+			coords[0] = this.element.toX;
+			coords[1] = this.element.toY;
+			return PathIterator.SEG_MOVETO;
+		case LINE_TO:
+			coords[0] = this.element.toX;
+			coords[1] = this.element.toY;
+			return PathIterator.SEG_LINETO;
+		case QUAD_TO:
+			coords[0] = this.element.ctrlX1;
+			coords[1] = this.element.ctrlY1;
+			coords[2] = this.element.toX;
+			coords[3] = this.element.toY;
+			return PathIterator.SEG_QUADTO;
+		case CURVE_TO:
+			coords[0] = this.element.ctrlX1;
+			coords[1] = this.element.ctrlY1;
+			coords[2] = this.element.ctrlX2;
+			coords[3] = this.element.ctrlY2;
+			coords[4] = this.element.toX;
+			coords[5] = this.element.toY;
+			return PathIterator.SEG_CUBICTO;
+		case CLOSE:
+			return PathIterator.SEG_CLOSE;
+		default:
+		}
+		throw new IllegalStateException();
+	}
+
+	@Override
+	public int currentSegment(double[] coords) {
+		if (this.element==null) throw new NoSuchElementException();
+		switch(this.element.type) {
+		case MOVE_TO:
+			coords[0] = this.element.toX;
+			coords[1] = this.element.toY;
+			return PathIterator.SEG_MOVETO;
+		case LINE_TO:
+			coords[0] = this.element.toX;
+			coords[1] = this.element.toY;
+			return PathIterator.SEG_LINETO;
+		case QUAD_TO:
+			coords[0] = this.element.ctrlX1;
+			coords[1] = this.element.ctrlY1;
+			coords[2] = this.element.toX;
+			coords[3] = this.element.toY;
+			return PathIterator.SEG_QUADTO;
+		case CURVE_TO:
+			coords[0] = this.element.ctrlX1;
+			coords[1] = this.element.ctrlY1;
+			coords[2] = this.element.ctrlX2;
+			coords[3] = this.element.ctrlY2;
+			coords[4] = this.element.toX;
+			coords[5] = this.element.toY;
+			return PathIterator.SEG_CUBICTO;
+		case CLOSE:
+			return PathIterator.SEG_CLOSE;
+		default:
+		}
+		throw new IllegalStateException();
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtPdf.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtPdf.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtPdf.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,264 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+import java.awt.Image;
+import java.awt.image.ImageObserver;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.math.matrix.SingularMatrixException;
+import org.arakhne.afc.math.matrix.Transform2D;
+import org.arakhne.afc.ui.vector.Dimension;
+import org.arakhne.afc.ui.vector.Pdf;
+import org.arakhne.afc.ui.vector.VectorToolkit;
+
+import com.sun.pdfview.Flag;
+import com.sun.pdfview.PDFFile;
+import com.sun.pdfview.PDFPage;
+
+/** Public implementation of a Paint.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AwtPdf implements Pdf, ImageObserver {
+
+	private static byte[] readFully(InputStream stream) throws IOException {
+		byte[] content = new byte[0];
+		byte[] buffer = new byte[2048];
+		int p,n;
+
+		n = stream.read(buffer);
+		while (n>0) {
+			p = content.length;
+			content = Arrays.copyOf(content, p + n);
+			System.arraycopy(buffer, 0, content, p, n);
+			n = stream.read(buffer);
+		}
+
+		return content;
+	}
+
+
+	/** Permits to manipulate the entire PDF file.
+	 */
+	private final PDFFile pdfFile;
+
+	/** Current rendered page.
+	 */
+	private PDFPage currentPdfPage = null;
+
+	/** Number of the current page.
+	 */
+	private int currentPdfPageNumber = -1;
+
+	/** a flag indicating whether the current page is done or not. */
+	private final Flag flag = new Flag();
+
+	/** the current clip, in device space */
+	private Rectangle2f clip;
+	/** the clipping region used for the image */
+	private Rectangle2f prevClip;
+	/** the size of the image */
+	private Dimension prevSize;
+	/** the current transform from device space to page space */
+	private Transform2D currentXform;
+
+	/** Size of the viewer. */
+	private Dimension viewerSize = null;
+
+	/** Image that is used to render the PDF page.
+	 */
+	private transient Image image = null;
+	
+	private final List<org.arakhne.afc.ui.vector.ImageObserver> observers = new ArrayList<org.arakhne.afc.ui.vector.ImageObserver>();
+
+	/**
+	 * @param url
+	 * @throws IOException
+	 */
+	public AwtPdf(URL url) throws IOException {
+		super();
+		this.pdfFile = new PDFFile(ByteBuffer.wrap(readFully(url.openStream())));
+	}
+
+	@Override
+	public int getPageNumber() {
+		return this.currentPdfPageNumber;
+	}
+
+	@Override
+	public int getPageCount() {
+		return this.pdfFile.getNumPages();
+	}
+
+	@Override
+	public void stopPageLoading() {
+		if (this.currentPdfPage!=null) {
+			java.awt.geom.Rectangle2D rr = new java.awt.geom.Rectangle2D.Float(
+					this.prevClip.getMinX(),
+					this.prevClip.getMinY(),
+					this.prevClip.getWidth(),
+					this.prevClip.getHeight());
+			this.currentPdfPage.stop(
+					(int)this.prevSize.width(),
+					(int)this.prevSize.height(),
+					rr);
+		}
+	}
+
+	@Override
+	public synchronized boolean setPageNumber(int pageno, org.arakhne.afc.ui.vector.ImageObserver observer) {
+		PDFPage page = this.pdfFile.getPage(pageno);
+		if (page!=null) {
+			stopPageLoading();
+			this.currentPdfPageNumber = pageno;
+			this.currentPdfPage = page;
+			if (observer!=null) this.observers.add(observer);
+			ensureImage();
+			return true;
+		}
+		return false;
+	}
+
+	private synchronized boolean ensureImage() {
+		assert(this.currentPdfPage!=null);
+
+		// start drawing -- clear the flag to indicate we're in progress.
+		this.flag.clear();
+
+		Dimension sz = this.viewerSize;
+		if (sz!=null && sz.width() + sz.height() > 0f) {
+			// calculate the clipping rectangle in page space from the
+			// desired clip in screen space.
+			Rectangle2f useClip = this.clip;
+			if (this.clip != null && this.currentXform != null) {
+				useClip = this.clip.createTransformedShape(
+						this.currentXform).toBoundingBox();
+			}
+
+			java.awt.geom.Rectangle2D awtUseClip = new java.awt.geom.Rectangle2D.Float(
+					useClip.getMinX(),
+					useClip.getMinY(),
+					useClip.getWidth(),
+					useClip.getHeight());
+
+			java.awt.geom.Dimension2D pageSize = this.currentPdfPage.getUnstretchedSize(
+					(int)sz.width(),
+					(int)sz.height(),
+					awtUseClip);
+
+			// get the new image
+			this.image = this.currentPdfPage.getImage(
+					(int)pageSize.getWidth(),
+					(int)pageSize.getHeight(),
+					awtUseClip, this);
+
+			// calculate the transform from screen to page space
+			this.currentXform = VectorToolkit.makeTransform(this.currentPdfPage.getInitialTransform(
+					(int)pageSize.getWidth(),
+					(int)pageSize.getHeight(),
+					awtUseClip));
+			try {
+				this.currentXform.invert();
+			}
+			catch (SingularMatrixException nte) {
+				//
+			}
+
+			this.prevClip = useClip;
+			this.prevSize = VectorToolkit.dimension(
+					(float)pageSize.getWidth(), (float)pageSize.getHeight());
+
+			return true;
+		}
+		return false;
+	}
+
+	@Override
+	public synchronized void setViewerSize(Dimension dimension) {
+		if ((dimension==null && this.viewerSize!=null)||
+			(dimension!=null && !dimension.equals(this.viewerSize))) {
+			stopPageLoading();
+			this.viewerSize = dimension;
+			if (this.currentPdfPage!=null) ensureImage();
+		}
+	}
+
+	@Override
+	public synchronized boolean imageUpdate(Image img, int type, int x, int y,
+			int width, int height) {
+		int flags = 
+				(java.awt.image.ImageObserver.ALLBITS |
+						java.awt.image.ImageObserver.WIDTH |
+						java.awt.image.ImageObserver.HEIGHT);
+		boolean loaded = (type & flags) == flags;
+		if (loaded) {
+			this.image = img;
+			for(org.arakhne.afc.ui.vector.ImageObserver obs : this.observers) {
+				obs.imageUpdate(new AwtImage(this.image), x, y, width, height);
+			}
+			this.observers.clear();
+		}
+		return !loaded;
+	}
+
+	@Override
+	public org.arakhne.afc.ui.vector.Image getImage() {
+		if (this.image==null) {
+			if (this.currentPdfPage!=null) ensureImage();
+			if (this.image==null) return null;
+		}
+		return new AwtImage(this.image);
+	}
+
+	@Override
+	public void release() {
+		stopPageLoading();
+	}
+
+	@Override
+	public float getPageWidth() {
+		if (this.currentPdfPage!=null) {
+			return this.currentPdfPage.getWidth();
+		}
+		return Float.NaN;
+	}
+
+	@Override
+	public float getPageHeight() {
+		if (this.currentPdfPage!=null) {
+			return this.currentPdfPage.getHeight();
+		}
+		return Float.NaN;
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtRaster.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtRaster.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtRaster.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,59 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+import org.arakhne.afc.ui.vector.Raster;
+
+/** AWT implementation of the generic raster.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AwtRaster implements Raster, NativeWrapper {
+
+	private final java.awt.image.Raster raster;
+	
+	/**
+	 * @param raster
+	 */
+	public AwtRaster(java.awt.image.Raster raster) {
+		this.raster = raster;
+	}
+	
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(this.raster);
+	}
+
+	@Override
+	public int getNumBands() {
+		return this.raster.getNumBands();
+	}
+
+	@Override
+	public int[] getPixel(int x, int y, int[] samples) {
+		return this.raster.getPixel(x, y, samples);
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtStroke.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtStroke.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtStroke.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,146 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+import java.awt.BasicStroke;
+
+import org.arakhne.afc.ui.vector.Stroke;
+
+/** AWT implementation of the generic Stroke.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class AwtStroke implements Stroke, NativeWrapper {
+
+	private static int toAwtLineJoin(LineJoin join) {
+		switch(join) {
+		case BEVEL:
+			return BasicStroke.JOIN_BEVEL;
+		case MITER:
+			return BasicStroke.JOIN_MITER;
+		case ROUND:
+			return BasicStroke.JOIN_ROUND;
+		default:
+		}
+		throw new RuntimeException("unsupported line join: "+join); //$NON-NLS-1$
+	}
+	
+	private static int toAwtEndCap(EndCap endCap) {
+		switch(endCap) {
+		case BUTT:
+			return BasicStroke.CAP_BUTT;
+		case ROUND:
+			return BasicStroke.CAP_ROUND;
+		case SQUARE:
+			return BasicStroke.CAP_SQUARE;
+		default:
+		}
+		throw new RuntimeException("unsupported end cap: "+endCap); //$NON-NLS-1$
+	}
+	
+	private static LineJoin fromAwtLineJoin(int join) {
+		if (join==BasicStroke.JOIN_MITER) return LineJoin.MITER;
+		if (join==BasicStroke.JOIN_BEVEL) return LineJoin.BEVEL;
+		if (join==BasicStroke.JOIN_ROUND) return LineJoin.ROUND;
+		throw new RuntimeException("unsupported line join: "+join); //$NON-NLS-1$
+	}
+	
+	private static EndCap fromAwtEndCap(int endCap) {
+		if (endCap==BasicStroke.CAP_SQUARE) return EndCap.SQUARE;
+		if (endCap==BasicStroke.CAP_BUTT) return EndCap.BUTT;
+		if (endCap==BasicStroke.CAP_ROUND) return EndCap.ROUND;
+		throw new RuntimeException("unsupported end cap: "+endCap); //$NON-NLS-1$
+	}
+
+	private final BasicStroke stroke;
+
+	/**
+	 * @param width
+	 * @param lineJoin
+	 * @param endCap
+	 * @param miterLimit
+	 * @param dashes
+	 * @param dashPhase
+	 */
+	public AwtStroke(float width, LineJoin lineJoin, EndCap endCap, float miterLimit, float[] dashes, float dashPhase) {
+		this.stroke = new BasicStroke(width, toAwtLineJoin(lineJoin), toAwtEndCap(endCap), miterLimit, dashes, dashPhase);
+	}
+	
+	/**
+	 * @param stroke
+	 */
+	public AwtStroke(BasicStroke stroke) {
+		this.stroke = stroke;
+	}
+
+	@Override
+	public String toString() {
+		return this.stroke.toString();
+	}
+	
+	@Override
+	public float[] getDashArray() {
+		return this.stroke.getDashArray();
+	}
+
+	@Override
+	public float getDashPhase() {
+		return this.stroke.getDashPhase();
+	}
+
+	@Override
+	public float getLineWidth() {
+		return this.stroke.getLineWidth();
+	}
+
+	@Override
+	public LineJoin getLineJoin() {
+		return fromAwtLineJoin(this.stroke.getLineJoin());
+	}
+
+	@Override
+	public EndCap getEndCap() {
+		return fromAwtEndCap(this.stroke.getEndCap());
+	}
+
+	@Override
+	public float getMiterLimit() {
+		return this.stroke.getMiterLimit();
+	}
+	
+	/** Replies the AWT stroke.
+	 * 
+	 * @return the AWT stroke.
+	 */
+	public java.awt.Stroke getStroke() {
+		return this.stroke;
+	}
+
+	@Override
+	public <T> T getNativeObject(Class<T> type) {
+		return type.cast(this.stroke);
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtVectorToolkit.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtVectorToolkit.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/AwtVectorToolkit.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,430 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+import java.awt.AlphaComposite;
+import java.awt.Polygon;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Area;
+import java.awt.geom.CubicCurve2D;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Line2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.PathIterator;
+import java.awt.geom.QuadCurve2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RoundRectangle2D;
+import java.awt.image.RenderedImage;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+
+import javax.imageio.ImageIO;
+
+import org.arakhne.afc.math.continous.object2d.Circle2f;
+import org.arakhne.afc.math.continous.object2d.Ellipse2f;
+import org.arakhne.afc.math.continous.object2d.Path2f;
+import org.arakhne.afc.math.continous.object2d.PathIterator2f;
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.math.continous.object2d.RoundRectangle2f;
+import org.arakhne.afc.math.continous.object2d.Segment2f;
+import org.arakhne.afc.math.continous.object2d.Shape2f;
+import org.arakhne.afc.math.generic.PathWindingRule;
+import org.arakhne.afc.math.matrix.Transform2D;
+import org.arakhne.afc.ui.awt.AwtUtil;
+import org.arakhne.afc.ui.awt.SupportedShape;
+import org.arakhne.afc.ui.awt.VirtualizableShape;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Composite;
+import org.arakhne.afc.ui.vector.Dimension;
+import org.arakhne.afc.ui.vector.Font;
+import org.arakhne.afc.ui.vector.FontMetrics;
+import org.arakhne.afc.ui.vector.FontStyle;
+import org.arakhne.afc.ui.vector.Image;
+import org.arakhne.afc.ui.vector.Margins;
+import org.arakhne.afc.ui.vector.Paint;
+import org.arakhne.afc.ui.vector.Pdf;
+import org.arakhne.afc.ui.vector.Stroke;
+import org.arakhne.afc.ui.vector.Stroke.EndCap;
+import org.arakhne.afc.ui.vector.Stroke.LineJoin;
+import org.arakhne.afc.ui.vector.VectorToolkit;
+import org.arakhne.vmutil.OperatingSystem;
+
+/** AWT implementation of the generic Window toolkit.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class AwtVectorToolkit extends VectorToolkit {
+
+	/**
+	 */
+	public AwtVectorToolkit() {
+		//
+	}
+
+	@Override
+	protected boolean isSupported() {
+		return !OperatingSystem.ANDROID.isCurrentOS();
+	}
+
+	@Override
+	protected Composite createComposite(float alpha) {
+		return new AwtComposite(AlphaComposite.SRC_IN, alpha);
+	}
+
+	@Override
+	protected Paint createPaint(Object paintObject) {
+		return new AwtPaint((java.awt.Paint)paintObject);
+	}
+
+	@Override
+	protected Font createFont(String name, FontStyle style, float size) {
+		return new AwtFont(name, style, size);
+	}
+
+	@Override
+	protected Image createImage(URL url) {
+		try {
+			java.awt.Image awtImage = ImageIO.read(url);
+			return new AwtImage(awtImage);
+		}
+		catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	@Override
+	protected Image createImage(InputStream stream) {
+		try {
+			java.awt.Image awtImage = ImageIO.read(stream);
+			return new AwtImage(awtImage);
+		}
+		catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	@Override
+	protected Image createImage(int width, int height, boolean isAlpha) {
+		return new AwtBufferedImage(width, height, isAlpha);
+	}
+
+	@Override
+	protected Stroke createStroke(float width, LineJoin lineJoin, EndCap endCap, float mitterLimit, float[] dashes, float dashPhase) {
+		return new AwtStroke(width, lineJoin, endCap, mitterLimit, dashes, dashPhase);
+	}
+
+	@Override
+	protected Color createColor(int red, int green, int blue, int alpha) {
+		return new AwtColor(red, green, blue, alpha);
+	}
+
+	@Override
+	protected Color createColor(Object rawColorObject) {
+		java.awt.Color c = (java.awt.Color)rawColorObject;
+		return new AwtColor(c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
+	}
+
+	@Override
+	protected Dimension createDimension(float width, float height) {
+		return new AwtDimension(width, height);
+	}
+
+	@Override
+	protected Margins createMargins(float top, float left, float right,
+			float bottom) {
+		return new AwtMargins(top, left, right, bottom);
+	}
+
+	@Override
+	protected Composite createComposite(Object compositeObject) {
+		return new AwtComposite((java.awt.Composite)compositeObject);
+	}
+
+	@Override
+	protected Font createFont(Object fontObject) {
+		return new AwtFont((java.awt.Font)fontObject);
+	}
+
+	@Override
+	protected FontMetrics createFontMetrics(Object metricsObject) {
+		return new AwtFontMetrics((java.awt.FontMetrics)metricsObject);
+	}
+
+	@Override
+	protected FontMetrics createFontMetrics(Font font) {
+		AwtFont awtFont = (AwtFont)font;
+		java.awt.FontMetrics fm = new java.awt.FontMetrics(awtFont.getFont()) {
+			private static final long serialVersionUID = 3136667595495307776L;
+		};
+		return new AwtFontMetrics(fm);
+	}
+
+	@Override
+	protected Stroke createStroke(Object strokeObject) {
+		return new AwtStroke((java.awt.BasicStroke)strokeObject);
+	}
+
+	@Override
+	protected Image createImage(Object imageObject) {
+		if (imageObject instanceof java.awt.Image) {
+			return new AwtImage((java.awt.Image)imageObject);
+		}
+		else if (imageObject instanceof javax.swing.ImageIcon) {
+			return new AwtImage(((javax.swing.ImageIcon)imageObject).getImage());
+		}
+		else if (imageObject instanceof javax.swing.Icon) {
+			javax.swing.Icon ic = (javax.swing.Icon)imageObject;
+			AwtBufferedImage img = new AwtBufferedImage(ic.getIconWidth(), ic.getIconHeight(), true);
+			java.awt.Graphics g = img.getGraphics();
+			ic.paintIcon(null, g, 0, 0);
+			g.dispose();
+			return img;
+		}
+		throw new IllegalArgumentException();
+	}
+
+	@Override
+	protected Shape2f createShape(Object shapeObject) {
+		if (shapeObject instanceof Shape2f)
+			return (Shape2f)shapeObject;
+		SupportedShape type = SupportedShape.getTypeOf(shapeObject.getClass());
+		if (type!=null) {
+			switch(type) {
+			case RECTANGLE2D:
+			{ 
+				Rectangle2D r = (Rectangle2D)shapeObject;
+				return new Rectangle2f((float)r.getMinX(), (float)r.getMinY(), (float)r.getWidth(), (float)r.getHeight());
+			}
+			case LINE:
+			{ 
+				Line2D l = (Line2D)shapeObject;
+				return new Segment2f((float)l.getX1(), (float)l.getY1(), (float)l.getX2(), (float)l.getY2());
+			}
+			case ELLIPSE:
+			{
+				Ellipse2D q = (Ellipse2D)shapeObject;
+				return new Ellipse2f(
+						(float)q.getMinX(), (float)q.getMinY(),
+						(float)q.getWidth(), (float)q.getHeight());
+			}
+			case ROUND_RECTANGLE:
+			{
+				RoundRectangle2D q = (RoundRectangle2D)shapeObject;
+				return new RoundRectangle2f(
+						(float)q.getMinX(), (float)q.getMinY(),
+						(float)q.getWidth(), (float)q.getHeight(),
+						(float)q.getArcWidth(), (float)q.getArcHeight());
+			}
+			case PATH:
+			{
+				Path2D p = (Path2D)shapeObject;
+				return toPath(p.getPathIterator(null));
+			}
+			case POLYGON:
+			{
+				Polygon p = (Polygon)shapeObject;
+				Path2f pp = toPath(p.getPathIterator(null));
+				pp.closePath();
+				return pp;
+			}
+			case AREA:
+			{
+				Area p = (Area)shapeObject;
+				Path2f pp = toPath(p.getPathIterator(null));
+				pp.closePath();
+				return pp;
+			}
+			case QUAD_CURVE:
+			{
+				QuadCurve2D q = (QuadCurve2D)shapeObject;
+				Path2f pp = new Path2f();
+				pp.moveTo((float)q.getX1(), (float)q.getY1());
+				pp.quadTo(
+						(float)q.getCtrlX(), (float)q.getCtrlY(),
+						(float)q.getX2(), (float)q.getY2());
+				return pp;
+			}
+			case CUBIC_CURVE:
+			{
+				CubicCurve2D q = (CubicCurve2D)shapeObject;
+				Path2f pp = new Path2f();
+				pp.moveTo((float)q.getX1(), (float)q.getY1());
+				pp.curveTo(
+						(float)q.getCtrlX1(), (float)q.getCtrlY1(),
+						(float)q.getCtrlX2(), (float)q.getCtrlY2(),
+						(float)q.getX2(), (float)q.getY2());
+				return pp;
+			}
+			case ARC:
+			{
+				Arc2D arc = (Arc2D)shapeObject;
+				return toPath(arc.getPathIterator(null));
+			}
+			case VIRTUALIZABLE_SHAPE:
+			{
+				VirtualizableShape vs = (VirtualizableShape)shapeObject;
+				return toPath(vs.getPathIterator(null));
+			}
+			default:
+			}
+		}
+		throw new IllegalArgumentException();
+	}
+	
+	private static Path2f toPath(PathIterator pi) {
+		Path2f pp = new Path2f(getWindingRule(pi));
+		float[] coords = new float[6];
+		while (!pi.isDone()) {
+			switch(pi.currentSegment(coords)) {
+			case PathIterator.SEG_MOVETO:
+				pp.moveTo(coords[0], coords[1]);
+				break;
+			case PathIterator.SEG_LINETO:
+				pp.lineTo(coords[0], coords[1]);
+				break;
+			case PathIterator.SEG_QUADTO:
+				pp.quadTo(coords[0], coords[1], coords[2], coords[3]);
+				break;
+			case PathIterator.SEG_CUBICTO:
+				pp.curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
+				break;
+			case PathIterator.SEG_CLOSE:
+				pp.closePath();
+				break;
+			default:
+			}
+			pi.next();
+		}
+		return pp;
+	}
+	
+	private static PathWindingRule getWindingRule(PathIterator pi) {
+		PathWindingRule rule;
+		switch(pi.getWindingRule()) {
+		case PathIterator.WIND_NON_ZERO:
+			rule = PathWindingRule.NON_ZERO;
+			break;
+		case PathIterator.WIND_EVEN_ODD:
+			rule = PathWindingRule.EVEN_ODD;
+			break;
+		default:
+			rule = PathWindingRule.NON_ZERO;
+			break;
+		}
+		return rule;
+	}
+
+	@Override
+	protected <T> T toNativeUIObject(Class<T> type, Object o) {
+		if (o instanceof NativeWrapper) {
+			return ((NativeWrapper)o).getNativeObject(type);
+		}
+		if (o instanceof PathIterator2f) {
+			return type.cast(new AwtPathIterator((PathIterator2f)o));
+		}
+		if (o instanceof Rectangle2f) {
+			Rectangle2f r = (Rectangle2f)o;
+			return type.cast(new Rectangle2D.Float(
+					r.getMinX(), r.getMinY(),
+					r.getWidth(), r.getHeight()));
+		}
+		if (o instanceof RoundRectangle2f) {
+			RoundRectangle2f r = (RoundRectangle2f)o;
+			return type.cast(new RoundRectangle2D.Float(
+					r.getMinX(), r.getMinY(),
+					r.getWidth(), r.getHeight(),
+					r.getArcWidth(), r.getArcHeight()));
+		}
+		if (o instanceof Circle2f) {
+			Circle2f r = (Circle2f)o;
+			return type.cast(new Ellipse2D.Float(
+					r.getX()-r.getRadius(), r.getY()-r.getRadius(),
+					r.getX()+r.getRadius(), r.getY()+r.getRadius()));
+		}
+		if (o instanceof Ellipse2f) {
+			Ellipse2f r = (Ellipse2f)o;
+			return type.cast(new Ellipse2D.Float(
+					r.getMinX(), r.getMinY(),
+					r.getWidth(), r.getHeight()));
+		}
+		if (o instanceof Segment2f) {
+			Segment2f r = (Segment2f)o;
+			return type.cast(new Line2D.Float(
+					r.getX1(), r.getY1(),
+					r.getX2(), r.getY2()));
+		}
+		if (o instanceof Path2f) {
+			return type.cast(new AwtPath((Path2f)o));
+		}
+		return type.cast(o);
+	}
+
+	@Override
+	protected Font getDefaultFont() {
+		return new AwtFont(java.awt.Font.decode(null));
+	}
+
+	@Override
+	protected Image createTransparentImage(Image imageObject, float transparency) {
+		java.awt.Image aImg = toNativeUIObject(java.awt.Image.class, imageObject);
+		aImg = AwtUtil.getTransparencyFilteredImage(aImg, transparency);
+		return new AwtImage(aImg);
+	}
+
+	@Override
+	protected Image makeTransparentImage(Image imageObject, float transparency) {
+		java.awt.Image aImg = toNativeUIObject(java.awt.Image.class, imageObject);
+		aImg = AwtUtil.getTransparencyFilteredImage(aImg, transparency);
+		return new AwtImage(aImg);
+	}
+
+	@Override
+	protected Transform2D createTransform(Object affineTransform) {
+		AffineTransform tr = (AffineTransform)affineTransform;
+		return new Transform2D(
+				(float)tr.getScaleX(), (float)tr.getShearX(), (float)tr.getTranslateX(),
+				(float)tr.getShearY(), (float)tr.getScaleY(), (float)tr.getTranslateY());
+	}
+
+	@Override
+	protected void write(Image image, String type, OutputStream stream)
+			throws IOException {
+		ImageIO.write(toNativeUIObject(RenderedImage.class, image), type, stream);
+	}
+
+	@Override
+	protected <T> T findObjectWithId(int id, Class<T> type) {
+		return null;
+	}
+
+	@Override
+	protected Pdf wrapPdf(URL url) throws IOException {
+		return new AwtPdf(url);
+	}
+
+}
\ No newline at end of file

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/DelegatedVectorGraphics2D.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/DelegatedVectorGraphics2D.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/DelegatedVectorGraphics2D.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,308 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.ui.vector.awt;
+
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.net.URL;
+
+import org.arakhne.afc.math.continous.object2d.Rectangle2f;
+import org.arakhne.afc.math.continous.object2d.Shape2f;
+import org.arakhne.afc.math.matrix.Transform2D;
+import org.arakhne.afc.ui.Graphics2DLOD;
+import org.arakhne.afc.ui.StringAnchor;
+import org.arakhne.afc.ui.awt.LODGraphics2D;
+import org.arakhne.afc.ui.vector.AbstractVectorGraphics2D;
+import org.arakhne.afc.ui.vector.Color;
+import org.arakhne.afc.ui.vector.Font;
+import org.arakhne.afc.ui.vector.FontMetrics;
+import org.arakhne.afc.ui.vector.Image;
+import org.arakhne.afc.ui.vector.ImageObserver;
+import org.arakhne.afc.ui.vector.Paint;
+import org.arakhne.afc.ui.vector.Stroke;
+import org.arakhne.afc.ui.vector.VectorToolkit;
+
+/** This graphic context permits to display
+ *  some vector data with a level of details.
+ *
+ * @param <G> is the type of the graphics pointed by this VectorGraphics2D.
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$ */
+public class DelegatedVectorGraphics2D<G extends LODGraphics2D> extends AbstractVectorGraphics2D {
+
+	/** Delegate.
+	 */
+	protected final G delegate;
+
+	/**
+	 * @param delegate
+	 */
+	public DelegatedVectorGraphics2D(G delegate) {
+		this.delegate = delegate;
+	}
+	
+	/** Replies the delegated Graphics2D.
+	 * 
+	 * @return the delegated Graphics2D.
+	 */
+	public G getDelegatedGraphics2D() {
+		return this.delegate;
+	}
+
+	private void setPaint() {
+		Color c = getFillColor();
+		if (c!=null) this.delegate.setColor(VectorToolkit.nativeUIObject(java.awt.Color.class, c));
+		Paint p = getPaint();
+		if (p!=null) this.delegate.setPaint(VectorToolkit.nativeUIObject(java.awt.Paint.class, p));
+	}
+
+	private void setOutlineColor() {
+		Color c = getOutlineColor();
+		if (c!=null) this.delegate.setColor(VectorToolkit.nativeUIObject(java.awt.Color.class, c));
+	}
+
+	@Override
+	public Graphics2DLOD getLOD() {
+		return this.delegate.getLOD();
+	}
+
+	@Override
+	public StringAnchor getStringAnchor() {
+		return this.delegate.getStringAnchor();
+	}
+
+	@Override
+	public void drawPoint(float x, float y) {
+		preDrawing();
+		setPaint();
+		this.delegate.fillRect(x-.5f, y-.5f, 1f, 1f);
+		postDrawing();
+	}
+
+	@Override
+	public Font getFont() {
+		return VectorToolkit.font(this.delegate.getFont());
+	}
+
+	@Override
+	public void setFont(Font font) {
+		this.delegate.setFont(VectorToolkit.nativeUIObject(java.awt.Font.class, font));
+	}
+
+	@Override
+	public FontMetrics getFontMetrics() {
+		return VectorToolkit.fontMetrics(this.delegate.getFontMetrics());
+	}
+
+	@Override
+	public FontMetrics getFontMetrics(Font f) {
+		java.awt.Font af = VectorToolkit.nativeUIObject(java.awt.Font.class, f);
+		return VectorToolkit.fontMetrics(this.delegate.getFontMetrics(af));
+	}
+
+	@Override
+	public Shape2f getClip() {
+		return VectorToolkit.shape(this.delegate.getClip());
+	}
+
+	@Override
+	public void setClip(Shape2f clip) {
+		this.delegate.setClip(VectorToolkit.nativeUIObject(java.awt.Shape.class, clip));
+	}
+
+	@Override
+	public void clip(Shape2f clip) {
+		this.delegate.clip(VectorToolkit.nativeUIObject(java.awt.Shape.class, clip));
+	}
+	
+	@Override
+	public boolean drawImage(URL imageURL, Image img, float dx1, float dy1,
+			float dx2, float dy2, int sx1, int sy1, int sx2, int sy2) {
+		preDrawing();
+		boolean drawn = this.delegate.drawImage(
+				VectorToolkit.nativeUIObject(java.awt.Image.class, img),
+				dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null);
+		postDrawing();
+		return drawn;
+	}
+
+	@Override
+	public boolean drawImage(URL imageURL, Image img, float dx1, float dy1,
+			float dx2, float dy2, int sx1, int sy1, int sx2, int sy2,
+			ImageObserver observer) {
+		preDrawing();
+		boolean drawn = this.delegate.drawImage(
+				VectorToolkit.nativeUIObject(java.awt.Image.class, img),
+				dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2,
+				null);
+		postDrawing();
+		return drawn;
+	}
+
+	@Override
+	public void draw(Shape2f s) {
+		preDrawing();
+		Shape shp = null;
+		if (isInteriorPainted()) {
+			setPaint();
+			shp = VectorToolkit.nativeUIObject(Shape.class, s);
+			this.delegate.fill(shp);
+		}
+		setOutlineColor();
+		String text = getInteriorText();
+		if (text!=null && !text.isEmpty()) {
+			paintClippedString(
+					text,
+					s.toBoundingBox(),
+					s);
+		}
+		if (isOutlineDrawn()) {
+			if (shp==null) shp = VectorToolkit.nativeUIObject(Shape.class, s);
+			this.delegate.draw(shp);
+		}
+		postDrawing();
+	}
+
+	@Override
+	public void drawString(String str, float x, float y) {
+		preDrawing();
+		setOutlineColor();
+		this.delegate.drawString(str, x, y);
+		postDrawing();
+	}
+
+	@Override
+	public void drawString(String str, float x, float y, Shape2f clip) {
+		preDrawing();
+		setOutlineColor();
+		Shape old = this.delegate.getClip();
+		this.delegate.setClip(VectorToolkit.nativeUIObject(Shape.class, clip));
+		this.delegate.drawString(str, x, y);
+		this.delegate.setClip(old);
+		postDrawing();
+	}
+
+	@Override
+	public void transform(Transform2D Tx) {
+		AffineTransform tr = new AffineTransform(Tx.m00, Tx.m10, Tx.m01, Tx.m11, Tx.m12, Tx.m12);
+		this.delegate.transform(tr);
+	}
+
+	@Override
+	public Transform2D setTransform(Transform2D Tx) {
+		AffineTransform old = this.delegate.getTransform();
+		AffineTransform tr = new AffineTransform(Tx.m00, Tx.m10, Tx.m01, Tx.m11, Tx.m02, Tx.m12);
+		this.delegate.setTransform(tr);
+		if (old==null) return null;
+		return new Transform2D(
+				(float)tr.getScaleX(),
+				(float)tr.getShearX(),
+				(float)tr.getTranslateX(),
+				(float)tr.getShearY(),
+				(float)tr.getScaleY(),
+				(float)tr.getTranslateY());
+	}
+
+	@Override
+	public Transform2D getTransform() {
+		AffineTransform tr = this.delegate.getTransform();
+		if (tr==null) return null;
+		return new Transform2D(
+				(float)tr.getScaleX(),
+				(float)tr.getShearX(),
+				(float)tr.getTranslateX(),
+				(float)tr.getShearY(),
+				(float)tr.getScaleY(),
+				(float)tr.getTranslateY());
+	}
+
+	@Override
+	public void setBackground(Color color) {
+		this.delegate.setBackground(new java.awt.Color(color.getRGB(), true));
+	}
+
+	@Override
+	public Color getBackground() {
+		java.awt.Color c = this.delegate.getBackground();
+		return VectorToolkit.color(c.getRGB(), true);
+	}
+
+	@Override
+	public void setComposite(org.arakhne.afc.ui.vector.Composite composite) {
+		this.delegate.setComposite(VectorToolkit.nativeUIObject(java.awt.Composite.class, composite));
+	}
+
+	@Override
+	public org.arakhne.afc.ui.vector.Composite getComposite() {
+		return VectorToolkit.composite(this.delegate.getComposite());
+	}
+
+	@Override
+	public void setStroke(Stroke stroke) {
+		this.delegate.setStroke(VectorToolkit.nativeUIObject(java.awt.Stroke.class, stroke));
+	}
+
+	@Override
+	public Stroke getStroke() {
+		return VectorToolkit.stroke(this.delegate.getStroke());
+	}
+
+	@Override
+	public void dispose() {
+		this.delegate.dispose();
+		super.dispose();
+	}
+
+	@Override
+	public void clear(Shape2f s) {
+		Rectangle2f bb = s.toBoundingBox();
+		this.delegate.clearRect(
+				bb.getMinX(),
+				bb.getMinY(),
+				bb.getWidth(),
+				bb.getHeight());
+	}
+
+	@Override
+	public void translate(float tx, float ty) {
+		this.delegate.translate(tx, ty);
+	}
+
+	@Override
+	public void scale(float sx, float sy) {
+		this.delegate.scale(sx, sy);
+	}
+
+	@Override
+	public void rotate(float theta) {
+		this.delegate.rotate(theta);
+	}
+
+	@Override
+	public void shear(float shx, float shy) {
+		this.delegate.shear(shx, shy);
+	}
+
+}

Added: trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/NativeWrapper.java
===================================================================
--- trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/NativeWrapper.java	                        (rev 0)
+++ trunk/ui/ui-vector-awt/src/main/java/org/arakhne/afc/ui/vector/awt/NativeWrapper.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,41 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2005-09 Stephane GALLAND.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.ui.vector.awt;
+
+
+/** Interface for all the AWT wrappers.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+interface NativeWrapper {
+
+	/** Return the native object.
+	 * 
+	 * @param type is the expected type.
+	 * @return the wrapped object.
+	 */
+	public <T> T getNativeObject(Class<T> type);
+	
+}
\ No newline at end of file


Property changes on: trunk/util
___________________________________________________________________
Added: svn:ignore
   + .classpath
..metadata
..settings
..project
target
bin


Added: trunk/util/pom.xml
===================================================================
--- trunk/util/pom.xml	                        (rev 0)
+++ trunk/util/pom.xml	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,19 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+		<artifactId>afc</artifactId>
+		<groupId>org.arakhne.afc</groupId>
+		<version>4.5-SNAPSHOT</version>
+  </parent>
+  <groupId>org.arakhne.afc</groupId>
+  <artifactId>util</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <name>Arakhne Utilities</name>
+  
+  <dependencies>
+      <dependency>
+          <groupId>org.arakhne.afc</groupId>
+          <artifactId>arakhneVmutils</artifactId>
+      </dependency>
+  </dependencies>
+</project>

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/AbstractFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/AbstractFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/AbstractFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,103 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import java.io.File;
+
+import org.arakhne.vmutil.FileSystem;
+
+/** Abstract implementation of a file filter that may be
+ * used in all the standard Java tools.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public abstract class AbstractFileFilter implements FileFilter {
+
+	private final boolean acceptDirectories;
+	private final String description;
+	private final String[] extensions;
+	
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 * @param description is the description of the file filter.
+	 * @param extensions are the supported extensions.
+	 */
+	public AbstractFileFilter(boolean acceptDirectories, String description, String... extensions) {
+		this.acceptDirectories = acceptDirectories;
+		this.extensions = extensions;
+
+		StringBuilder b = new StringBuilder();
+		b.append(description);
+		b.append(" ("); //$NON-NLS-1$
+		for(int i=0; i<this.extensions.length; ++i) {
+			if (i>0) b.append(", "); //$NON-NLS-1$
+			b.append("*"); //$NON-NLS-1$
+			if (!this.extensions[i].startsWith(".")) //$NON-NLS-1$
+				b.append("."); //$NON-NLS-1$
+			b.append(this.extensions[i]);
+		}
+		b.append(")"); //$NON-NLS-1$
+		this.description = b.toString();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final boolean accept(File f) {
+		if (f.isDirectory()) return  this.acceptDirectories;
+		for(String ext : this.extensions) {
+			if (FileSystem.hasExtension(f, ext)) {
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public final String getDescription() {
+		return this.description;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final boolean accept(File dir, String name) {
+		return accept(new File(dir, name));
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public final String[] getExtensions() {
+		return this.extensions;
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/BMPFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/BMPFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/BMPFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,57 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import org.arakhne.vmutil.locale.Locale;
+
+/** File filter for the Microsoft/IBM bitmap files.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class BMPFileFilter extends AbstractFileFilter {
+
+	/** Default extension for the Microsoft/IBM bitmap files.
+	 */
+	public static final String EXTENSION = "bmp"; //$NON-NLS-1$
+	
+	/**
+	 */
+	public BMPFileFilter() {
+		this(true);
+	}
+
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 */
+	public BMPFileFilter(boolean acceptDirectories) {
+		super(
+				acceptDirectories,
+				Locale.getString(BMPFileFilter.class, "FILE_FILTER_NAME"), //$NON-NLS-1$
+				EXTENSION);
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/DOTFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/DOTFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/DOTFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,57 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import org.arakhne.vmutil.locale.Locale;
+
+/** File filter for the Graphviz DOT files.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class DOTFileFilter extends AbstractFileFilter {
+
+	/** Default extension for the Graphviz DOT files.
+	 */
+	public static final String EXTENSION = "dot"; //$NON-NLS-1$
+
+	/**
+	 */
+	public DOTFileFilter() {
+		this(true);
+	}
+
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 */
+	public DOTFileFilter(boolean acceptDirectories) {
+		super(
+				acceptDirectories,
+				Locale.getString(DOTFileFilter.class, "FILE_FILTER_NAME"), //$NON-NLS-1$
+				EXTENSION);
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/EPSFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/EPSFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/EPSFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,57 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import org.arakhne.vmutil.locale.Locale;
+
+/** File filter for Encapsuled Postscript document.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class EPSFileFilter extends AbstractFileFilter {
+
+	/** Default extension for the Encapsuled Postscript document.
+	 */
+	public static final String EXTENSION = "eps"; //$NON-NLS-1$
+
+	/**
+	 */
+	public EPSFileFilter() {
+		this(true);
+	}
+
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 */
+	public EPSFileFilter(boolean acceptDirectories) {
+		super(
+				acceptDirectories,
+				Locale.getString(EPSFileFilter.class, "FILE_FILTER_NAME"), //$NON-NLS-1$
+				EXTENSION);
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/FileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/FileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/FileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,46 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import java.io.FilenameFilter;
+
+/** File filter.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface FileFilter extends java.io.FileFilter, FilenameFilter {
+
+	/** Replies the description associated to this file filter.
+	 * 
+	 * @return a string that is describing the file filtering.
+	 */
+	public String getDescription();
+	
+	/** Replies the extensions supported by this file filter.
+	 * 
+	 * @return the extensions.
+	 */
+	public String[] getExtensions();
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/GIFFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/GIFFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/GIFFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,57 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import org.arakhne.vmutil.locale.Locale;
+
+/** File filter for the GIF pictures.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class GIFFileFilter extends AbstractFileFilter {
+
+	/** Default extension for the GIF pictures.
+	 */
+	public static final String EXTENSION = "gif"; //$NON-NLS-1$
+
+	/**
+	 */
+	public GIFFileFilter() {
+		this(true);
+	}
+
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 */
+	public GIFFileFilter(boolean acceptDirectories) {
+		super(
+				acceptDirectories,
+				Locale.getString(GIFFileFilter.class, "FILE_FILTER_NAME"), //$NON-NLS-1$
+				EXTENSION);
+	}
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/GMLFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/GMLFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/GMLFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,58 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import org.arakhne.vmutil.locale.Locale;
+
+/** File filter for the "GML" files.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @since 16.0
+ */
+public class GMLFileFilter extends AbstractFileFilter {
+
+	/** Default extension for the "GML" files.
+	 */
+	public static final String EXTENSION = "gml"; //$NON-NLS-1$
+
+	/**
+	 */
+	public GMLFileFilter() {
+		this(true);
+	}
+
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 */
+	public GMLFileFilter(boolean acceptDirectories) {
+		super(
+				acceptDirectories,
+				Locale.getString(GMLFileFilter.class, "FILE_FILTER_NAME"), //$NON-NLS-1$
+				EXTENSION);
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/GXLFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/GXLFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/GXLFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,57 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import org.arakhne.vmutil.locale.Locale;
+
+/** File filter for the "Graph Exchange Format" files.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class GXLFileFilter extends AbstractFileFilter {
+
+	/** Default extension for the "Graph Exchange Format" files.
+	 */
+	public static final String EXTENSION = "gxl"; //$NON-NLS-1$
+
+	/**
+	 */
+	public GXLFileFilter() {
+		this(true);
+	}
+
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 */
+	public GXLFileFilter(boolean acceptDirectories) {
+		super(
+				acceptDirectories,
+				Locale.getString(GXLFileFilter.class, "FILE_FILTER_NAME"), //$NON-NLS-1$
+				EXTENSION);
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/GraphMLFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/GraphMLFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/GraphMLFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,62 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import org.arakhne.vmutil.locale.Locale;
+
+/** File filter for the "GraphML" files.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @since 16.0
+ */
+public class GraphMLFileFilter extends AbstractFileFilter {
+
+	/** Default extension for the "GraphML" files.
+	 */
+	public static final String EXTENSION_GRAPHML = "graphml"; //$NON-NLS-1$
+
+	/** Default extension for the "GraphML" files.
+	 */
+	public static final String EXTENSION_GRL = "grl"; //$NON-NLS-1$
+
+	/**
+	 */
+	public GraphMLFileFilter() {
+		this(true);
+	}
+
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 */
+	public GraphMLFileFilter(boolean acceptDirectories) {
+		super(
+				acceptDirectories,
+				Locale.getString(GraphMLFileFilter.class, "FILE_FILTER_NAME"), //$NON-NLS-1$
+				EXTENSION_GRAPHML, EXTENSION_GRL);
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/ImageFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/ImageFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/ImageFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,56 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import org.arakhne.vmutil.locale.Locale;
+
+/** File filter for all bitmap-based images.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class ImageFileFilter extends MultiFileFilter {
+
+	/**
+	 */
+	public ImageFileFilter() {
+		this(true);
+	}
+
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 */
+	public ImageFileFilter(boolean acceptDirectories) {
+		super(
+				acceptDirectories,
+				Locale.getString(ImageFileFilter.class, "FILE_FILTER_NAME"), //$NON-NLS-1$
+				new BMPFileFilter(false),
+				new GIFFileFilter(false),
+				new JPEGFileFilter(false),
+				new PNGFileFilter(false));
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/JPEGFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/JPEGFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/JPEGFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,65 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import org.arakhne.vmutil.locale.Locale;
+
+/** File filter for JPEG pictures.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class JPEGFileFilter extends AbstractFileFilter {
+
+	/** A standard extension for the JPEG pictures.
+	 */
+	public static final String EXTENSION_JPG = "jpg"; //$NON-NLS-1$
+
+	/** A standard extension for the JPEG pictures.
+	 */
+	public static final String EXTENSION_JPEG = "jpeg"; //$NON-NLS-1$
+
+	/** A standard extension for the JPEG pictures.
+	 */
+	public static final String EXTENSION_JPE = "jpe"; //$NON-NLS-1$
+
+	/**
+	 */
+	public JPEGFileFilter() {
+		this(true);
+	}
+
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 */
+	public JPEGFileFilter(boolean acceptDirectories) {
+		super(
+				acceptDirectories,
+				Locale.getString(JPEGFileFilter.class, "FILE_FILTER_NAME"), //$NON-NLS-1$
+				EXTENSION_JPG, EXTENSION_JPEG, EXTENSION_JPE);
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/JavaFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/JavaFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/JavaFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,57 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import org.arakhne.vmutil.locale.Locale;
+
+/** File filter for Java source file document.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class JavaFileFilter extends AbstractFileFilter {
+
+	/** Default extension for the Java source files.
+	 */
+	public static final String EXTENSION = "java"; //$NON-NLS-1$
+
+	/**
+	 */
+	public JavaFileFilter() {
+		this(true);
+	}
+
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 */
+	public JavaFileFilter(boolean acceptDirectories) {
+		super(
+				acceptDirectories,
+				Locale.getString(JavaFileFilter.class, "FILE_FILTER_NAME"), //$NON-NLS-1$
+				EXTENSION);
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/MultiFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/MultiFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/MultiFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,100 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class MultiFileFilter implements FileFilter {
+
+	private final boolean acceptDirectories;
+	private final FileFilter[] filters;
+	private final String description;
+	
+	/**
+	 * @param description
+	 * @param filters
+	 */
+	public MultiFileFilter(String description, FileFilter... filters) {
+		this(true, description, filters);
+	}
+
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 * @param description
+	 * @param filters
+	 */
+	public MultiFileFilter(boolean acceptDirectories, String description, FileFilter... filters) {
+		this.acceptDirectories = acceptDirectories;
+		this.filters = filters;
+		this.description = description;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean accept(File f) {
+		if (f.isDirectory()) return  this.acceptDirectories;
+		for(FileFilter ff : this.filters) {
+			if (ff.accept(f)) return true;
+		}
+		return false;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public String getDescription() {
+		return this.description;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final boolean accept(File dir, String name) {
+		return accept(new File(dir, name));
+	}
+
+	@Override
+	public String[] getExtensions() {
+		List<String> extensions = new ArrayList<String>();
+		for(FileFilter ff : this.filters) {
+			extensions.addAll(Arrays.asList(ff.getExtensions()));
+		}
+		String[] tab = new String[extensions.size()];
+		extensions.toArray(tab);
+		return tab;
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/NGRFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/NGRFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/NGRFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,62 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import org.arakhne.vmutil.locale.Locale;
+
+/** File filter for the NetEditor graphs.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @since 16.0
+ */
+public class NGRFileFilter extends AbstractFileFilter {
+
+	/** NGR extension.
+	 */
+	public static final String EXTENSION_NGR = "ngr"; //$NON-NLS-1$
+	
+	/** NED extension.
+	 */
+	private static final String EXTENSION_NED = "ned"; //$NON-NLS-1$
+
+	/**
+	 */
+	public NGRFileFilter() {
+		this(true);
+	}
+
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 */
+	public NGRFileFilter(boolean acceptDirectories) {
+		super(
+				acceptDirectories,
+				Locale.getString(NGRFileFilter.class, "FILE_FILTER_NAME"), //$NON-NLS-1$
+				EXTENSION_NGR, EXTENSION_NED);
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/PDFFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/PDFFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/PDFFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,57 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import org.arakhne.vmutil.locale.Locale;
+
+/** File filter for PDF document.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class PDFFileFilter extends AbstractFileFilter {
+
+	/** Default extension for the PDF documents.
+	 */
+	public static final String EXTENSION = "pdf"; //$NON-NLS-1$
+
+	/**
+	 */
+	public PDFFileFilter() {
+		this(true);
+	}
+
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 */
+	public PDFFileFilter(boolean acceptDirectories) {
+		super(
+				acceptDirectories,
+				Locale.getString(PDFFileFilter.class, "FILE_FILTER_NAME"), //$NON-NLS-1$
+				EXTENSION);
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/PNGFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/PNGFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/PNGFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,57 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import org.arakhne.vmutil.locale.Locale;
+
+/** File filter for PNG pictures.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class PNGFileFilter extends AbstractFileFilter {
+
+	/** Default extension for the PNG pictures.
+	 */
+	public static final String EXTENSION = "png"; //$NON-NLS-1$
+
+	/**
+	 */
+	public PNGFileFilter() {
+		this(true);
+	}
+
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 */
+	public PNGFileFilter(boolean acceptDirectories) {
+		super(
+				acceptDirectories,
+				Locale.getString(PNGFileFilter.class, "FILE_FILTER_NAME"), //$NON-NLS-1$
+				EXTENSION);
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/SVGFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/SVGFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/SVGFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,57 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import org.arakhne.vmutil.locale.Locale;
+
+/** File filter for SVG files.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class SVGFileFilter extends AbstractFileFilter {
+
+	/** Default extension for the SVG files.
+	 */
+	public static final String EXTENSION = "svg"; //$NON-NLS-1$
+
+	/**
+	 */
+	public SVGFileFilter() {
+		this(true);
+	}
+
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 */
+	public SVGFileFilter(boolean acceptDirectories) {
+		super(
+				acceptDirectories,
+				Locale.getString(SVGFileFilter.class, "FILE_FILTER_NAME"), //$NON-NLS-1$
+				EXTENSION);
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/filefilter/XMLFileFilter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/filefilter/XMLFileFilter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/filefilter/XMLFileFilter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,57 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012-13 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.filefilter ;
+
+import org.arakhne.vmutil.locale.Locale;
+
+/** File filter for XML files.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class XMLFileFilter extends AbstractFileFilter {
+
+	/** Default extension for the XML files.
+	 */
+	public static final String EXTENSION = "xml"; //$NON-NLS-1$
+
+	/**
+	 */
+	public XMLFileFilter() {
+		this(true);
+	}
+
+	/**
+	 * @param acceptDirectories is <code>true</code> to
+	 * permit to this file filter to accept directories;
+	 * <code>false</code> if the directories should not
+	 * match.
+	 */
+	public XMLFileFilter(boolean acceptDirectories) {
+		super(
+				acceptDirectories,
+				Locale.getString(XMLFileFilter.class, "FILE_FILTER_NAME"), //$NON-NLS-1$
+				EXTENSION);
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/stream/ReaderInputStream.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/stream/ReaderInputStream.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/stream/ReaderInputStream.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,92 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.io.stream ;
+
+import java.io.IOError;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+/** An output stream that is writing inside a Writer.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class ReaderInputStream extends InputStream {
+
+	private final Reader reader;
+
+	/**
+	 * @param reader
+	 */
+	public ReaderInputStream(Reader reader) {
+		this.reader = reader;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void close() throws IOException {
+		this.reader.close();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int read() throws IOException {
+		return this.reader.read();
+	}
+	
+	/**
+	 *  {@inheritDoc}
+	 */
+	@Override
+	public boolean markSupported() {
+		return this.reader.markSupported();
+	}
+	
+	@Override
+	public synchronized void mark(int readlimit) {
+		try {
+			this.reader.mark(readlimit);
+		}
+		catch(IOException e) {
+			throw new IOError(e); 
+		}
+	}
+	
+	@Override
+	public synchronized void reset() throws IOException {
+		this.reader.reset();
+	}
+	
+	@Override
+	public long skip(long n) throws IOException {
+		return this.reader.skip(n);
+	}
+	
+}
\ No newline at end of file

Added: trunk/util/src/main/java/org/arakhne/afc/io/stream/UnclosableInputStream.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/stream/UnclosableInputStream.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/stream/UnclosableInputStream.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,68 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.stream ;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/** An input stream that is ignoring all request to {@link #close()}.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class UnclosableInputStream extends FilterInputStream {
+
+	/**
+	 * @param in
+	 * @throws IOException
+	 */
+	public UnclosableInputStream(InputStream in) throws IOException {
+		super(in);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void close() throws IOException {
+		//
+	}
+
+	/** Call directly the function {@link InputStream#read(byte[], int, int)} on
+	 * the filtered stream.
+	 */
+	@Override
+	public int read(byte[] b, int off, int len) throws IOException {
+		return this.in.read(b, off, len);
+	}
+	
+	/** Call directly the function {@link InputStream#read(byte[])} on
+	 * the filtered stream.
+	 */
+	@Override
+	public int read(byte[] b) throws IOException {
+		return this.in.read(b);
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/stream/UnclosableOutputStream.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/stream/UnclosableOutputStream.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/stream/UnclosableOutputStream.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,71 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.io.stream ;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/** An output stream that is ignoring all request to {@link #close()}.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class UnclosableOutputStream extends FilterOutputStream {
+
+	/**
+	 * @param out
+	 * @throws IOException
+	 */
+	public UnclosableOutputStream(OutputStream out) throws IOException {
+		super(out);
+	}
+
+	/**
+	 * Do not close but still flush the filtered stream.
+	 */
+	@Override
+	public void close() throws IOException {
+		try {
+			flush();
+		}
+		catch (IOException _) {
+			//
+		}
+	}
+
+	/** Call directly the function {@link OutputStream#write(byte[], int, int)} on the filtered stream.
+	 */
+	@Override
+	public void write(byte[] b, int off, int len) throws IOException {
+		this.out.write(b, off, len);
+	}
+
+	/** Call directly the function {@link OutputStream#write(byte[])} on the filtered stream.
+	 */
+	@Override
+	public void write(byte[] b) throws IOException {
+		this.out.write(b);
+	}
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/io/stream/WriterOutputStream.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/io/stream/WriterOutputStream.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/io/stream/WriterOutputStream.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,93 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2002 Stephane GALLAND, Madhi HANNOUN, Marc BAUMGARTNER.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+
+package org.arakhne.afc.io.stream ;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+
+/** An output stream that is writing inside a Writer.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class WriterOutputStream extends OutputStream {
+
+	private final Writer writer;
+
+	/**
+	 * @param writer
+	 */
+	public WriterOutputStream(Writer writer) {
+		this.writer = writer;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void write(int b) throws IOException {
+		this.writer.write(b);
+	}
+	
+	/**
+	 * Write a sequence of characters.
+	 * 
+	 * @param text
+	 * @throws IOException
+	 */
+	public void write(String text) throws IOException {
+		this.writer.write(text);
+	}
+
+	/**
+	 * Write a sequence of characters followed by a carriage return.
+	 * 
+	 * @param text
+	 * @throws IOException
+	 */
+	public void writeln(String text) throws IOException {
+		this.writer.write(text);
+		if (text!=null && text.length()>0)
+			this.writer.write("\n"); //$NON-NLS-1$
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void close() throws IOException {
+		this.writer.close();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void flush() throws IOException {
+		this.writer.flush();
+	}
+	
+}
\ No newline at end of file

Added: trunk/util/src/main/java/org/arakhne/afc/progress/DefaultProgression.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/progress/DefaultProgression.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/progress/DefaultProgression.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,431 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.progress;
+
+import org.arakhne.afc.util.ListenerCollection;
+
+/**
+ * An object that permits to indicates the progression of
+ * a task. The progression of the value is always ascendent.
+ *
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class DefaultProgression implements Progression {
+	
+	private int min, max;
+	private float current; // use floating point value to allow finest subtask's progression.
+	private boolean isIndeterminate;
+	private boolean isAdjusting;
+	private String comment;
+	private SubProgressionModel child = null;
+	
+	/** Collection of listeners.
+	 */
+	protected final ListenerCollection<ProgressionListener> listeners = new ListenerCollection<ProgressionListener>();
+	
+	/** Create a progress model with the specified <i>determinate</i> state.
+	 * 
+	 * @param value is the initial value of this model (between <var>min</var> and <var>max</var>).
+	 * @param min is the minimal value
+	 * @param max is the maximal value
+	 * @param adjusting indicates if this model is under adjustement or not.
+	 */
+	public DefaultProgression(int value, int min, int max, boolean adjusting) {
+		if (min>max) {
+			this.min = max;
+			this.max = min;
+		}
+		else {
+			this.min = min;
+			this.max = max;
+		}
+		this.current = (value<this.min) ? this.min : (value>this.max) ? this.max : value;
+		this.isIndeterminate = false;
+		this.isAdjusting = adjusting;
+	}
+	
+	/** Create a progress model with the specified <i>determinate</i> state.
+	 * The model is not adjusting.
+	 * 
+	 * @param value is the initial value of this model (between <var>min</var> and <var>max</var>).
+	 * @param min is the minimal value
+	 * @param max is the maximal value
+	 */
+	public DefaultProgression(int value, int min, int max) {
+		this(value, min, max, false);
+	}
+
+	/** Create a progress model with the specified <i>indeterminate</i> state.
+	 * The model is not adjusting.
+	 * 
+	 * @param min is the minimal value
+	 * @param max is the maximal value
+	 */
+	public DefaultProgression(int min, int max) {
+		if (min>max) {
+			this.min = max;
+			this.max = min;
+		}
+		else {
+			this.min = min;
+			this.max = max;
+		}
+		this.current = this.min;
+		this.isIndeterminate = true;
+		this.isAdjusting = false;
+	}
+
+	/** Create a progress model in a <i>indeterminate</i> state.
+	 * The model is not adjusting.
+	 */
+	public DefaultProgression() {
+		this.min = this.max = 0;
+		this.current = this.min;
+		this.isIndeterminate = true;
+		this.isAdjusting = false;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void end() {
+		int v = getValue();
+		int m = getMaximum();
+		String comment = getComment();
+		if (v<m || comment!=null) {
+			setValue(m, null);
+		}
+		fireStateChange();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addProgressionListener(ProgressionListener listener) {
+		this.listeners.add(ProgressionListener.class, listener);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void removeProgressionListener(ProgressionListener listener) {
+		this.listeners.remove(ProgressionListener.class, listener);
+	}
+	
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean isRootModel() {
+		return true;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Progression getSuperTask() {
+		return null;
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public int getTaskDepth() {
+		return 0;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final Progression getSubTask() {
+		return this.child;
+	}
+	
+	@Override
+	public final void ensureNoSubTask() {
+		if (this.child!=null) {
+			disconnectSubTask(
+					this.child,
+					this.child.getMaxInParent(),
+					false);
+		}
+	}
+
+	/** Notify listeners about state change.
+	 */
+	protected void fireStateChange() {
+		if (this.listeners!=null) {
+			ProgressionEvent event = new ProgressionEvent(this, isRootModel());
+			for(ProgressionListener listener : this.listeners.getListeners(ProgressionListener.class)) {
+				listener.onProgressionStateChanged(event);
+			}
+		}
+	}
+
+	/** Notify listeners about value change.
+	 */
+	protected void fireValueChange() {
+		if (this.listeners!=null) {
+			ProgressionEvent event = new ProgressionEvent(this, isRootModel());
+			for(ProgressionListener listener : this.listeners.getListeners(ProgressionListener.class)) {
+				listener.onProgressionValueChanged(event);
+			}
+		}
+	}
+
+	@Override
+	public int getMaximum() {
+		return this.max;
+	}
+
+	@Override
+	public int getMinimum() {
+		return this.min;
+	}
+
+	@Override
+	public float getPercent() {
+		float extent = this.max - this.min;
+		if (extent==0f) return 0f;
+		return (this.current - this.min) * 100f / extent;
+	}
+
+	@Override
+	public float getProgressionFactor() {
+		float extent = this.max - this.min;
+		if (extent==0f) return 0f;
+		return (this.current - this.min) / extent;
+	}
+
+	@Override
+	public int getValue() {
+		return (int)this.current;
+	}
+
+	/** Replies the floating-point precision value.
+	 * 
+	 * @return the floating-point precision value.
+	 */
+	protected float getFloatValue() {
+		return this.current;
+	}
+
+	@Override
+	public boolean isAdjusting() {
+		return this.isAdjusting;
+	}
+
+	@Override
+	public boolean isIndeterminate() {
+		return this.isIndeterminate;
+	}
+
+	@Override
+	public void setIndeterminate(boolean newValue) {
+		if (this.isIndeterminate!=newValue) {
+			this.isIndeterminate = newValue;
+			fireStateChange();
+		}
+	}
+
+	@Override
+	public void setMaximum(int newMaximum) {
+		setProperties(this.current, this.min, newMaximum, this.isAdjusting, this.comment, false, false, true, null);
+	}
+
+	@Override
+	public void setMinimum(int newMinimum) {
+		setProperties(this.current, newMinimum, this.max, this.isAdjusting, this.comment, false, false, true, null);
+	}
+
+	@Override
+	public void setProperties(int value, int min, int max, boolean adjusting, String comment) {
+		setProperties(value, min, max, adjusting, comment, true, true, true, this.child);
+	}
+
+	@Override
+	public void setProperties(int value, int min, int max, boolean adjusting) {
+		setProperties(value, min, max, adjusting, this.comment, false, true, true, this.child);
+	}
+
+	@Override
+	public void setValue(int newValue) {
+		setProperties(newValue, this.min, this.max, this.isAdjusting, this.comment, false, true, false, null);
+	}
+
+	@Override
+	public void setValue(int newValue, String comment) {
+		setProperties(newValue, this.min, this.max, this.isAdjusting, comment, true, true, false, null);
+	}
+
+	@Override
+	public void setAdjusting(boolean b) {
+		setProperties(this.current, this.min, this.max, b, this.comment, false, false, false, null);
+	}
+
+	private void setProperties(float value, int min, int max, boolean adjusting, 
+			String comment, boolean writeLocalComment,
+			boolean forceDeterminate, boolean forceValue,
+			SubProgressionModel subTask) {
+		if (this.child!=null && this.child!=subTask) {
+			this.child.disconnect();
+		}
+
+		String uptComment = comment;
+		if (uptComment!=null && "".equals(uptComment)) //$NON-NLS-1$
+			uptComment = null;
+
+		int theMin = min;
+		int theMax = max;
+		if (theMin>max) {
+			int tmp = theMin;
+			theMin = theMax;
+			theMax = tmp;
+		}
+		
+		float theValue = value;
+		if (theValue<theMin) theValue = theMin;
+		if (theValue>theMax) theValue = theMax;
+		
+		boolean changed = 
+			((forceValue)&&(theValue != this.current))
+			|| ((!forceValue)&&(theValue > this.current))
+			|| (theMin != this.min)
+			|| (theMax != this.max)
+			|| (adjusting != this.isAdjusting)
+			|| (uptComment!=this.comment && (uptComment==null || !uptComment.equals(this.comment)));
+		
+		if (changed && !adjusting) {
+			this.current = theValue;
+			this.min = theMin;
+			this.max = theMax;
+			this.isAdjusting = adjusting;
+			if (writeLocalComment) this.comment = uptComment;
+			
+			fireValueChange();
+		}
+
+		if (forceDeterminate) setIndeterminate(false);		
+	}
+
+	@Override
+	public final Progression subTask(int extent, int minValue, int maxValue) {
+		return subTask(extent, minValue, maxValue, false);
+	}
+	
+	@Override
+	public Progression subTask(int extent, int minValue, int maxValue, boolean overwriteComment) {
+		if (this.child!=null) {
+			this.child.disconnect();
+		}
+		this.child = new SubProgressionModel(
+				this, 
+				this.current, this.current+Math.abs(extent), 
+				minValue, maxValue, 
+				this.isIndeterminate, this.isAdjusting,
+				overwriteComment);
+		return this.child;
+	}
+
+	@Override
+	public final Progression subTask(int extent) {
+		return subTask(extent, false);
+	}
+
+	@Override
+	public Progression subTask(int extent, boolean overwriteComment) {
+		if (this.child!=null) {
+			this.child.disconnect();
+		}
+		this.child = new SubProgressionModel(
+				this, 
+				this.current, this.current+Math.abs(extent), 
+				this.isIndeterminate, this.isAdjusting,
+				overwriteComment);
+		return this.child;
+	}
+
+	/** Set the parent value and disconnect this subtask from its parent.
+	 * This method is invoked from a subtask.
+	 * 
+	 * @param subTask
+	 * @param value
+	 * @param overwriteComment indicates if the comment of this parent model may
+	 * be overwritten by the child's comment.
+	 */
+	void disconnectSubTask(SubProgressionModel subTask, float value, boolean overwriteComment) {
+		if (this.child==subTask) {
+			if (overwriteComment) {
+				String cmt = subTask.getComment();
+				if (cmt!=null)
+					this.comment = cmt;
+			}
+			this.child = null;
+			setProperties(value, this.min, this.max, this.isAdjusting, this.comment, overwriteComment, true, false, null);
+		}
+	}
+
+	/** Set the value with a float-point precision. 
+	 * This method is invoked from a subtask.
+	 * 
+	 * @param subTask
+	 * @param newValue
+	 * @param comment
+	 */
+	void setValue(SubProgressionModel subTask, float newValue, String comment) {
+		setProperties(newValue, this.min, this.max, this.isAdjusting, 
+				comment==null ? this.comment : comment, true, true, false,
+				subTask);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String getComment() {
+		if (this.child!=null) {
+			String childComment = this.child.getComment();
+			if (childComment!=null) return childComment;
+		}
+		return this.comment;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setComment(String comment) {
+		setProperties(
+				this.current, 
+				this.min, this.max, 
+				this.isAdjusting, comment, 
+				true, true, false, null);
+	}
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/progress/Progression.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/progress/Progression.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/progress/Progression.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,388 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.progress;
+
+/**
+ * An object that permits to indicates the progression of
+ * a task. The progression of the value is always ascendent.
+ *
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface Progression {
+	
+	/** Add a listener on this model.
+	 * 
+	 * @param listener is the listener to add.
+	 */
+	public void addProgressionListener(ProgressionListener listener);
+
+	/** Remove a listener on this model.
+	 * 
+	 * @param listener is the listener to remove.
+	 */
+	public void removeProgressionListener(ProgressionListener listener);
+
+	/** Returns the minimum acceptable value.
+	 *
+	 * @return the value of the minimum property
+	 * @see #setMinimum
+	 */
+	public int getMinimum();
+
+
+	/** Sets the model's minimum to <I>newMinimum</I>.   The 
+	 * other properties may be changed as well, to ensure 
+	 * that:
+	 * <pre>
+	 * minimum &lt;= value &lt;= maximum
+	 * </pre>
+	 *
+	 * @param newMinimum the model's new minimum
+	 * @see #getMinimum
+	 */
+	public void setMinimum(int newMinimum);
+
+
+	/**
+	 * Returns the model's maximum.
+	 *
+	 * @return the value of the maximum property.
+	 * @see #setMaximum
+	 */
+	public int getMaximum();
+
+
+	/**
+	 * Sets the model's maximum to <I>newMaximum</I>. The other 
+	 * properties may be changed as well, to ensure that
+	 * <pre>
+	 * minimum &lt;= value &lt;= maximum
+	 * </pre>
+	 *
+	 * @param newMaximum the model's new maximum
+	 * @see #getMaximum
+	 */
+	public void setMaximum(int newMaximum);
+
+
+	/**
+	 * Returns the model's current value.  Note that the upper
+	 * limit on the model's value is <code>maximum</code> 
+	 * and the lower limit is <code>minimum</code>.
+	 *
+	 * @return  the model's value
+	 * @see     #setValue
+	 */
+	public int getValue();
+
+	/**
+	 * Returns the model's current value in percent of pregression.
+	 *
+	 * @return  the model's value between 0 and 100
+	 * @see     #getValue()
+	 * @see     #getProgressionFactor()
+	 */
+	public float getPercent();
+
+	/**
+	 * Returns the model's current value in percent of pregression.
+	 *
+	 * @return  the model's value between 0 and 1
+	 * @see     #getValue()
+	 * @see     #getPercent()
+	 */
+	public float getProgressionFactor();
+
+	/**
+	 * Sets the model's current value to <code>newValue</code> if <code>newValue</code>
+	 * satisfies the model's constraints. Those constraints are:
+	 * <pre>
+	 * minimum &lt;= value &lt;= maximum
+	 * </pre>
+	 * Otherwise, if <code>newValue</code> is less than <code>minimum</code> 
+	 * it's set to <code>minimum</code>, if its greater than 
+	 * <code>maximum</code> then it's set to <code>maximum</code>.
+	 *
+	 * @param newValue the model's new value
+	 * @see #getValue
+	 */
+	public void setValue(int newValue);
+
+	/**
+	 * Sets the model's current value to <code>newValue</code> if <code>newValue</code>
+	 * satisfies the model's constraints. Those constraints are:
+	 * <pre>
+	 * minimum &lt;= value &lt;= maximum
+	 * </pre>
+	 * Otherwise, if <code>newValue</code> is less than <code>minimum</code> 
+	 * it's set to <code>minimum</code>, if its greater than 
+	 * <code>maximum</code> then it's set to <code>maximum</code>.
+	 *
+	 * @param newValue the model's new value
+	 * @param comment is the comment to display.
+	 * @see #getValue
+	 */
+	public void setValue(int newValue, String comment);
+
+	/**
+	 * This attribute indicates that any upcoming changes to the value
+	 * of the model should be considered a single event. This attribute
+	 * will be set to true at the start of a series of changes to the value,
+	 * and will be set to false when the value has finished changing.  Normally
+	 * this allows a listener to only take action when the final value change in
+	 * committed, instead of having to do updates for all intermediate values.
+	 * 
+	 * @param b true if the upcoming changes to the value property are part of a series
+	 * @see #subTask(int)
+	 */
+	public void setAdjusting(boolean b);
+
+
+	/**
+	 * Returns true if the current changes to the value property are part 
+	 * of a series of changes.
+	 * 
+	 * @return the valueIsAdjustingProperty.  
+	 * @see #setAdjusting(boolean)
+	 */
+	public boolean isAdjusting();
+
+
+	/**
+	 * This method sets all of the model's data with a single method call.
+	 * The method results in a single change event being generated. This is
+	 * convenient when you need to adjust all the model data simultaneously and
+	 * do not want individual change events to occur.
+	 *
+	 * @param value  an int giving the current value 
+	 * @param min    an int giving the minimum value
+	 * @param max    an int giving the maximum value
+	 * @param adjusting a boolean, true if a series of changes are in
+	 *                    progress
+	 * @param comment is the comment associated to the current task, or <code>null</code>
+	 * 
+	 * @see #setValue
+	 * @see #setMinimum
+	 * @see #setMaximum
+	 * @see #setComment(String)
+	 * @see #setAdjusting(boolean)
+	 */
+	public void setProperties(int value, int min, int max, boolean adjusting, String comment);
+
+	/**
+	 * This method sets all of the model's data with a single method call.
+	 * The method results in a single change event being generated. This is
+	 * convenient when you need to adjust all the model data simultaneously and
+	 * do not want individual change events to occur.
+	 *
+	 * @param value  an int giving the current value 
+	 * @param min    an int giving the minimum value
+	 * @param max    an int giving the maximum value
+	 * @param adjusting a boolean, true if a series of changes are in
+	 *                    progress
+	 * 
+	 * @see #setValue
+	 * @see #setMinimum
+	 * @see #setMaximum
+	 * @see #setComment(String)
+	 * @see #setAdjusting(boolean)
+	 */
+	public void setProperties(int value, int min, int max, boolean adjusting);
+
+	/**
+	 * Sets the <code>indeterminate</code> property of the task progression,
+	 * which determines whether the progression is in determinate
+	 * or indeterminate mode.
+	 * For example an indeterminate progress bar continuously displays animation
+	 * indicating that an operation of unknown length is occurring.
+	 *
+	 * @param newValue	<code>true</code> if the progress bar
+	 * 			should change to indeterminate mode;
+	 * 			<code>false</code> if it should revert to normal.
+	 * @see #isIndeterminate
+	 */
+	public void setIndeterminate(boolean newValue);
+
+	/**
+	 * Returns the value of the <code>indeterminate</code> property.
+	 *
+	 * @return the value of the <code>indeterminate</code> property
+	 * @see    #setIndeterminate
+	 */
+	public boolean isIndeterminate();
+	
+	/** Set the comment associated to the currect task.
+	 * <p>
+	 * If the given string is <code>null</code> or empty,
+	 * the current task is assumed to not have a comment.
+	 * 
+	 * @param comment is the comment for the current task.
+	 */
+	public void setComment(String comment);
+
+	/** Replies the comment associated to the currect task.
+	 * 
+	 * @return the comment for the current task or <code>null</code>
+	 * if the current task has not any comment.
+	 */
+	public String getComment();
+
+	/** Enter into a subtask.
+	 * When a sub task is created, it will progress as a sub part of
+	 * the main task.
+	 * <p>
+	 * The minimum value of the subtask in its parent
+	 * will correspond to the
+	 * current value of the main task. The maximum value of the
+	 * sub task in its parent 
+	 * will correspond to the current value of the main task
+	 * plus the given <var>extend</var>.
+	 * <p>
+	 * If the <var>extend</var> plus the current value is exceeding
+	 * the maximum value, it will be bounded to the maximum.
+	 * 
+	 * @param extent is the size of the sub task inside the main task.
+	 * @param min is the minimum value inside the subtask.
+	 * @param max is the maximum value inside the subtask.
+	 * @return the subtask progression
+	 */
+	public Progression subTask(int extent, int min, int max);
+
+	/** Enter into a subtask.
+	 * When a sub task is created, it will progress as a sub part of
+	 * the main task.
+	 * <p>
+	 * The minimum value of the subtask in its parent
+	 * will correspond to the
+	 * current value of the main task. The maximum value of the
+	 * sub task in its parent 
+	 * will correspond to the current value of the main task
+	 * plus the given <var>extend</var>.
+	 * <p>
+	 * If the <var>extend</var> plus the current value is exceeding
+	 * the maximum value, it will be bounded to the maximum.
+	 * <p>
+	 * The minimum and maximum values of the subtask should be manually
+	 * set the the caller.
+	 * 
+	 * @param extent is the size of the sub task inside the main task.
+	 * @return the subtask progression
+	 */
+	public Progression subTask(int extent);
+
+	/** Enter into a subtask.
+	 * When a sub task is created, it will progress as a sub part of
+	 * the main task.
+	 * <p>
+	 * The minimum value of the subtask in its parent
+	 * will correspond to the
+	 * current value of the main task. The maximum value of the
+	 * sub task in its parent 
+	 * will correspond to the current value of the main task
+	 * plus the given <var>extend</var>.
+	 * <p>
+	 * If the <var>extend</var> plus the current value is exceeding
+	 * the maximum value, it will be bounded to the maximum.
+	 * 
+	 * @param extent is the size of the sub task inside the main task.
+	 * @param min is the minimum value inside the subtask.
+	 * @param max is the maximum value inside the subtask.
+	 * @param overwriteComment indicates if the comment of this task model may
+	 * be overwritten by the comment of the subtask when it is disconnected.
+	 * @return the subtask progression
+	 */
+	public Progression subTask(int extent, int min, int max, boolean overwriteComment);
+
+	/** Enter into a subtask.
+	 * When a sub task is created, it will progress as a sub part of
+	 * the main task.
+	 * <p>
+	 * The minimum value of the subtask in its parent
+	 * will correspond to the
+	 * current value of the main task. The maximum value of the
+	 * sub task in its parent 
+	 * will correspond to the current value of the main task
+	 * plus the given <var>extend</var>.
+	 * <p>
+	 * If the <var>extend</var> plus the current value is exceeding
+	 * the maximum value, it will be bounded to the maximum.
+	 * <p>
+	 * The minimum and maximum values of the subtask should be manually
+	 * set the the caller.
+	 * 
+	 * @param extent is the size of the sub task inside the main task.
+	 * @param overwriteComment indicates if the comment of this task model may
+	 * be overwritten by the comment of the subtask when it is disconnected.
+	 * @return the subtask progression
+	 */
+	public Progression subTask(int extent, boolean overwriteComment);
+
+	/** Replies the current subtask of this task model.
+	 * 
+	 * @return the current subtask or <code>null</code> this task was
+	 * not decomposed.
+	 */
+	public Progression getSubTask();
+
+	/**
+	 * Ensure that there is no opened subtask.
+	 * If a subtask is existing, it is ended().
+	 */
+	public void ensureNoSubTask();
+
+	/** Replies the super task of this task model.
+	 * 
+	 * @return the super task or <code>null</code> this task was
+	 * not a decomposition of a super task.
+	 */
+	public Progression getSuperTask();
+
+	/** Force this progression task to end its indicator.
+	 */
+	public void end();
+
+	/** Replies if is task model is a root model.
+	 * <p>
+	 * {@code isRootMode() == (getTaskDepth()==0)}
+	 * 
+	 * @return <code>true</code> if this model is a root model,
+	 * otherwise <code>false</code>
+	 */
+	public boolean isRootModel();
+
+	/** Replies the depth level of this task.
+	 * The root task (ie. a task without parent task)
+	 * has always a depth of {@code 0}. A subtask of the
+	 * root task has a depth of {@code 1}. A subtask
+	 * of this subtask has a depth of {@code 2}, etc.
+	 * <p>
+	 * {@code isRootMode() == (getTaskDepth()==0)}
+	 * 
+	 * @return the depth of the task.
+	 */
+	public int getTaskDepth();
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionAdapter.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionAdapter.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionAdapter.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,51 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2011, Multiagent Team, Laboratoire Systemes et Transports,
+ *                     Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.progress;
+
+/**
+ * Adapter on a task progression.
+ *
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class ProgressionAdapter implements ProgressionListener {
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void onProgressionStateChanged(ProgressionEvent event) {
+		//
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void onProgressionValueChanged(ProgressionEvent event) {
+		//
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionConsoleMonitor.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionConsoleMonitor.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionConsoleMonitor.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,77 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.progress;
+
+import java.util.logging.Logger;
+
+/**
+ * An object that permits to indicates the progression of
+ * a task. This monitor display on the console the progression
+ * of the task.
+ *
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class ProgressionConsoleMonitor implements ProgressionListener {
+	
+	private final DefaultProgression model = new DefaultProgression();
+	
+	/**
+	 */
+	public ProgressionConsoleMonitor() {
+		//
+	}
+	
+	/** Replies the task progression model.
+	 * 
+	 * @return the task progression model.
+	 */
+	public Progression getModel() {
+		return this.model;
+	}
+
+	@Override
+	public void onProgressionStateChanged(ProgressionEvent event) {
+		//
+	}
+
+	@Override
+	public void onProgressionValueChanged(ProgressionEvent event) {
+		if (!event.isIndeterminate()) {
+			StringBuilder txt = new StringBuilder();
+			Progression p = event.getTaskProgression();
+			
+			txt.append('[');
+			txt.append("%] "); //$NON-NLS-1$
+			String comment = p.getComment();
+			if (comment!=null) {
+				txt.append(comment);
+			}
+			
+			Logger.getAnonymousLogger().info(txt.toString());
+		}
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionEvent.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionEvent.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionEvent.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,166 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.progress;
+
+import java.util.EventObject;
+
+/**
+ * Task progression event.
+ *
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class ProgressionEvent extends EventObject {
+
+	private static final long serialVersionUID = 4840275907048148943L;
+
+	private final boolean isRoot;
+	
+	private final int min;
+
+	private final int max;
+
+	private final int value;
+	
+	private final float factor;
+
+	private final String comment;
+
+	private final boolean isIndeterminate;
+
+	/**
+	 * @param source is the model that was thrown the event.
+	 * @param isRoot indicates if this event was fired by a root task progression source.
+	 */
+	ProgressionEvent(Progression source, boolean isRoot) {
+		super(source);
+		this.isRoot = isRoot;
+		this.min = source.getMinimum();
+		this.max = source.getMaximum();
+		this.value = source.getValue();
+		this.factor = source.getProgressionFactor();
+		this.comment = source.getComment();
+		this.isIndeterminate = source.isIndeterminate();
+	}
+
+	/** Replies if this event was fired by an task progression source
+	 * which is a root source.
+	 * 
+	 * @return <code>true</code> if the task progression is a root,
+	 * otherwise <code>false</code>.
+	 */
+	public boolean isRootTaskProgression() {
+		return this.isRoot;
+	}
+
+	/** Replies if the associated task was marked as finished,
+	 * ie the current value is greater or equal to the maximum
+	 * value AND the associated task progression object is a root
+	 * task.
+	 * 
+	 * @return <code>true</code> if the task was finished,
+	 * otherwise <code>false</code>.
+	 */
+	public boolean isTaskFinished() {
+		return this.isRoot && this.value>=this.max;
+	}
+
+	/** Replies the task progression which generate this event.
+	 * 
+	 * @return the model.
+	 */
+	public Progression getTaskProgression() {
+		return (Progression) getSource();
+	}
+
+	/** Returns the minimum acceptable value.
+	 * 
+	 * @return the minimal value.
+	 */
+	public int getMinimum() {
+		return this.min;
+	}
+
+	/**
+	 * Returns the model's maximum.
+	 * 
+	 * @return the maximal value.
+	 */
+	public int getMaximum() {
+		return this.max;
+	}
+
+	/**
+	 * Returns the model's current value.  Note that the upper
+	 * limit on the model's value is <code>maximum</code> 
+	 * and the lower limit is <code>minimum</code>.
+	 * 
+	 * @return the current value.
+	 */
+	public int getValue() {
+		return this.value;
+	}
+
+	/**
+	 * Returns the model's current value in percent of pregression.
+	 *
+	 * @return  the model's value between 0 and 100
+	 * @see     #getValue()
+	 * @see     #getProgressionFactor()
+	 */
+	public float getPercent() {
+		return this.factor * 100f;
+	}
+
+	/**
+	 * Returns the model's current value in percent of pregression.
+	 *
+	 * @return  the model's value between 0 and 1
+	 * @see     #getValue()
+	 * @see     #getPercent()
+	 */
+	public float getProgressionFactor() {
+		return this.factor;
+	}
+
+	/**
+	 * Returns the model's current comment.
+	 * 
+	 * @return the current comment or <code>null</code>.
+	 */
+	public String getComment() {
+		return this.comment;
+	}
+
+	/**
+	 * Returns the value of the <code>indeterminate</code> property.
+	 *
+	 * @return the value of the <code>indeterminate</code> property
+	 */
+	public boolean isIndeterminate() {
+		return this.isIndeterminate;
+	}
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionInputStream.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionInputStream.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionInputStream.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,153 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2011, Multiagent Team, Laboratoire Systemes et Transports,
+ *                     Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.progress;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * InputStream that is able to notify about the reading progression.
+ *
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class ProgressionInputStream extends FilterInputStream {
+
+	private final Progression monitor;
+	private final int totalSize;
+    private int read = 0;
+	
+	/**
+	 * @param in is the input to read
+	 * @param progression is the progression model used to notify on the reading progression.
+	 */
+	public ProgressionInputStream(InputStream in, Progression progression) {
+		super(in);
+		this.monitor = progression;
+		int size;
+        try {
+            size = this.in.available();
+        }
+        catch(IOException _) {
+            size = 0;
+        }
+        this.totalSize = (size>=0) ? size : 0;
+        this.monitor.setProperties(0, 0, this.totalSize, false);
+	}
+	
+	/**
+	 * @param in is the input to read
+	 * @param totalSize is the total number of bytes to read from the stream.
+	 * @param progression is the progression model used to notify on the reading progression.
+	 */
+	public ProgressionInputStream(InputStream in, int totalSize, Progression progression) {
+		super(in);
+		this.monitor = progression;
+        this.totalSize = (totalSize>=0) ? totalSize : 0;
+        this.monitor.setProperties(0, 0, this.totalSize, false);
+	}
+
+	/** Replies the progression model used by this input stream.
+	 * 
+	 * @return the progression model.
+	 */
+	public Progression getProgression() {
+		return this.monitor;
+	}
+	
+	/**
+     * Overrides <code>FilterInputStream.read</code>
+     * to update the progress monitor after the read.
+     */
+	@Override
+    public int read() throws IOException {
+        int c = this.in.read();
+        if (c >= 0) this.monitor.setValue(++this.read);
+        return c;
+    }
+
+
+    /**
+     * Overrides <code>FilterInputStream.read</code>
+     * to update the progress monitor after the read.
+     */
+	@Override
+    public int read(byte b[]) throws IOException {
+        int nr = this.in.read(b);
+        if (nr > 0) this.monitor.setValue(this.read += nr);
+        return nr;
+    }
+
+
+    /**
+     * Overrides <code>FilterInputStream.read</code>
+     * to update the progress monitor after the read.
+     */
+	@Override
+    public int read(byte b[],
+                    int off,
+                    int len) throws IOException {
+        int nr = this.in.read(b, off, len);
+        if (nr > 0) this.monitor.setValue(this.read += nr);
+        return nr;
+    }
+
+
+    /**
+     * Overrides <code>FilterInputStream.skip</code>
+     * to update the progress monitor after the skip.
+     */
+	@Override
+    public long skip(long n) throws IOException {
+        long nr = this.in.skip(n);
+        if (nr > 0) this.monitor.setValue(this.read += nr);
+        return nr;
+    }
+
+
+    /**
+     * Overrides <code>FilterInputStream.close</code>
+     * to close the progress monitor as well as the stream.
+     */
+	@Override
+    public void close() throws IOException {
+        this.in.close();
+        this.monitor.end();
+    }
+
+
+    /**
+     * Overrides <code>FilterInputStream.reset</code>
+     * to reset the progress monitor as well as the stream.
+     */
+	@Override
+    public synchronized void reset() throws IOException {
+        this.in.reset();
+        this.read = this.totalSize - this.in.available();
+        this.monitor.setValue(this.read);
+    }
+	
+}

Added: trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionListener.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionListener.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionListener.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,49 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.progress;
+
+import java.util.EventListener;
+
+/**
+ * Listener on a task progression.
+ *
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface ProgressionListener extends EventListener {
+	
+    /** Invoked when the value, the minimum or the maximum has changed.
+     * 
+     * @param event is the description of the event.
+     */
+    public void onProgressionValueChanged(ProgressionEvent event);
+
+    /** Invoked when the indeterminate/determinate state of the task progression changed.
+     * 
+     * @param event is the description of the event.
+     */
+    public void onProgressionStateChanged(ProgressionEvent event);
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionOuputStream.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionOuputStream.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionOuputStream.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,99 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2011, Multiagent Team, Laboratoire Systemes et Transports,
+ *                     Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.progress;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * OutputStream that is able to notify about the writing progression.
+ *
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class ProgressionOuputStream extends FilterOutputStream {
+
+	private final Progression monitor;
+	private final int totalSize;
+    private int write = 0;
+	
+	/**
+	 * @param out is the output to write
+	 * @param totalSize is the total number of bytes to write in the stream.
+	 * @param progression is the progression model used to notify on the reading progression.
+	 */
+	public ProgressionOuputStream(OutputStream out, int totalSize, Progression progression) {
+		super(out);
+		this.monitor = progression;
+        this.totalSize = (totalSize>=0) ? totalSize : 0;
+        this.monitor.setProperties(0, 0, this.totalSize, false);
+	}
+
+	/** Replies the progression model used by this input stream.
+	 * 
+	 * @return the progression model.
+	 */
+	public Progression getProgression() {
+		return this.monitor;
+	}
+		
+    /**
+     * {@inheritDoc}
+     */
+	@Override
+    public void write(int b) throws IOException {
+        this.out.write(b);
+        this.monitor.setValue(++this.write);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+	@Override
+    public void write(byte b[]) throws IOException {
+        this.out.write(b);
+        this.monitor.setValue(this.write += b.length);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+	@Override
+    public void write(byte b[], int off, int len) throws IOException {
+        this.out.write(b, off, len);
+        this.monitor.setValue(this.write += len);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+	@Override
+    public void close() throws IOException {
+        super.close();
+        this.monitor.end();
+    }
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionUtil.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionUtil.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/progress/ProgressionUtil.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,369 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.progress;
+
+/**
+ * Utilities around task progression.
+ *
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class ProgressionUtil {
+
+	/** Initialize the given task progression if not <code>null</code>.
+	 * 
+	 * @param model is the task progression to initialize.
+	 * @param value is the progression value. 
+	 * @param min is the minimum value.
+	 * @param max is the maximum value.
+	 * @param isAdjusting indicates if the value is still under changes.
+	 * @param isInderterminate indicates if the progression value is inderterminate or not.
+	 */
+	public static void init(Progression model, int value, int min, int max, boolean isAdjusting, boolean isInderterminate) {
+		if (model!=null) {
+			model.setProperties(value, min, max, isAdjusting);
+			model.setIndeterminate(isInderterminate);
+		}
+	}
+
+	/** Initialize the given task progression if not <code>null</code>.
+	 * <p>
+	 * The inderterminate state is not changed.
+	 * 
+	 * @param model is the task progression to initialize.
+	 * @param min is the minimum value.
+	 * @param max is the maximum value.
+	 * @param isAdjusting indicates if the value is still under changes.
+	 */
+	public static void init(Progression model, int min, int max, boolean isAdjusting) {
+		if (model!=null) {
+			model.setProperties(min, min, max, isAdjusting);
+		}
+	}
+
+	/** Initialize the given task progression if not <code>null</code>.
+	 * <p>
+	 * The inderterminate state is not changed.
+	 * The adjusting state is set to <code>false</code>.
+	 * 
+	 * @param model is the task progression to initialize.
+	 * @param min is the minimum value.
+	 * @param max is the maximum value.
+	 */
+	public static void init(Progression model, int min, int max) {
+		if (model!=null) {
+			model.setProperties(min, min, max, false);
+		}
+	}
+
+	/** Initialize the given task progression if not <code>null</code>.
+	 * 
+	 * @param model is the task progression to initialize.
+	 * @param value is the progression value. 
+	 * @param min is the minimum value.
+	 * @param max is the maximum value.
+	 * @param isAdjusting indicates if the value is still under changes.
+	 * @param isInderterminate indicates if the progression value is inderterminate or not
+	 * @param comment is the text associated with the progression.
+	 */
+	public static void init(Progression model, int value, int min, int max, boolean isAdjusting, boolean isInderterminate, String comment) {
+		if (model!=null) {
+			model.setProperties(value, min, max, isAdjusting, comment);
+			model.setIndeterminate(isInderterminate);
+		}
+	}
+
+	/** Initialize the given task progression if not <code>null</code>.
+	 * <p>
+	 * The inderterminate state is not changed.
+	 * 
+	 * @param model is the task progression to initialize.
+	 * @param min is the minimum value.
+	 * @param max is the maximum value.
+	 * @param isAdjusting indicates if the value is still under changes.
+	 * @param comment is the text associated with the progression.
+	 */
+	public static void init(Progression model, int min, int max, boolean isAdjusting, String comment) {
+		if (model!=null) {
+			model.setProperties(min, min, max, isAdjusting, comment);
+		}
+	}
+
+	/** Initialize the given task progression if not <code>null</code>.
+	 * <p>
+	 * The inderterminate state is not changed.
+	 * The adjusting state is set to <code>false</code>.
+	 * 
+	 * @param model is the task progression to initialize.
+	 * @param min is the minimum value.
+	 * @param max is the maximum value.
+	 * @param comment is the text associated with the progression.
+	 */
+	public static void init(Progression model, int min, int max, String comment) {
+		if (model!=null) {
+			model.setProperties(min, min, max, false, comment);
+		}
+	}
+
+	/** Create and replies a task progression model that is for
+	 * a subtask of the task associated to the given progression model.
+	 * <p>
+	 * The subtask progression model is not initialized.
+	 * When the subtask progression model reaches its end,
+	 * the value of the task progression is {@code s + extent}
+	 * where {@code s} is the value of the task progression when
+	 * this function is called.
+	 *  
+	 * @param model is the model to derivate
+	 * @param extent is the size of the subtask progression that is
+	 * covered in the task progression.
+	 * @return the subtask progression model; or <code>null</code>.
+	 */
+	public static Progression sub(Progression model, int extent) {
+		if (model!=null) {
+			return model.subTask(extent);
+		}
+		return null;
+	}
+
+	/** Create and replies a task progression model that is for
+	 * a subtask of the task associated to the given progression model.
+	 * <p>
+	 * The subtask progression model is not initialized.
+	 * When the subtask progression model reaches its end,
+	 * the value of the task progression is {@code s + extent}
+	 * where {@code s} is the value of the task progression when
+	 * this function is called.
+	 *  
+	 * @param model is the model to derivate
+	 * @param extent is the size of the subtask progression that is
+	 * covered in the task progression.
+	 * @param overwriteComment indicates if the comment of this task model may
+	 * be overwritten by the comment of the subtask when it is disconnected.
+	 * @return the subtask progression model; or <code>null</code>.
+	 */
+	public static Progression sub(Progression model, int extent, boolean overwriteComment) {
+		if (model!=null) {
+			return model.subTask(extent, overwriteComment);
+		}
+		return null;
+	}
+	
+	/** Create and replies a task progression model that is for
+	 * a subtask of the task associated to the given progression model.
+	 * <p>
+	 * The subtask progression model is not initialized.
+	 * When the subtask progression model reaches its end,
+	 * the value of the task progression is equal to the maximum.
+	 *  
+	 * @param model is the model to derivate
+	 * @return the subtask progression model; or <code>null</code>.
+	 */
+	public static Progression subToEnd(Progression model) {
+		if (model!=null) {
+			return model.subTask(model.getMaximum() - model.getValue());
+		}
+		return null;
+	}
+
+	/** Create and replies a task progression model that is for
+	 * a subtask of the task associated to the given progression model.
+	 * <p>
+	 * The subtask progression model is not initialized.
+	 * When the subtask progression model reaches its end,
+	 * the value of the task progression is equal to the maximum.
+	 *  
+	 * @param model is the model to derivate
+	 * @param overwriteComment indicates if the comment of this task model may
+	 * be overwritten by the comment of the subtask when it is disconnected.
+	 * @return the subtask progression model; or <code>null</code>.
+	 */
+	public static Progression subToEnd(Progression model, boolean overwriteComment) {
+		if (model!=null) {
+			return model.subTask(model.getMaximum() - model.getValue(), overwriteComment);
+		}
+		return null;
+	}
+
+	/** Increment the value of the given task progression,
+	 * if not <code>null</code>, by the given amount.
+	 * 
+	 * @param model is the progression to change
+	 * @param value is the value to add to the progression value.
+	 */
+	public static void advance(Progression model, int value) {
+		if (model!=null && value>0) {
+			SubProgressionModel sub = (SubProgressionModel)model.getSubTask();
+			float base;
+			if (sub==null) {
+				base = model.getValue();
+			}
+			else {
+				base = sub.getMinInParent();
+				model.ensureNoSubTask();
+			}
+			model.setValue((int)base + value);
+		}
+	}
+
+	/** Increment the value of the given task progression,
+	 * if not <code>null</code>, by {@code 1}.
+	 * 
+	 * @param model is the progression to change
+	 */
+	public static void advance(Progression model) {
+		advance(model, 1);
+	}
+
+	/** Increment the value of the given task progression,
+	 * if not <code>null</code>, by the given amount.
+	 * 
+	 * @param model is the progression to change
+	 * @param value is the value to add to the progression value.
+	 * @param comment is the comment associated to the progression.
+	 */
+	public static void advance(Progression model, int value, String comment) {
+		if (model!=null && value>0) {
+			model.setValue(model.getValue() + value, comment);
+		}
+	}
+
+	/** Increment the value of the given task progression,
+	 * if not <code>null</code>, by {@code 1}.
+	 * 
+	 * @param model is the progression to change
+	 * @param comment is the comment associated to the progression.
+	 */
+	public static void advance(Progression model, String comment) {
+		advance(model, 1, comment);
+	}
+
+	/** Replies the value of the given task progression,
+	 * if not <code>null</code>.
+	 * 
+	 * @param model is the progression to change
+	 * @return the value or {@code 0} if there is no progession model.
+	 */
+	public static int getValue(Progression model) {
+		if (model!=null) {
+			return model.getValue();
+		}
+		return 0;
+	}
+
+	/** Replies the value from the current progression value
+	 * to the maximal progression value of the given task progression,
+	 * if not <code>null</code>.
+	 * 
+	 * @param model is the progression to change
+	 * @return the positive value to end or {@code 0} if there is no progession model.
+	 */
+	public static int getValueToEnd(Progression model) {
+		if (model!=null) {
+			return model.getMaximum() - model.getValue();
+		}
+		return 0;
+	}
+
+	/** Replies the percent of progression for the given task progression,
+	 * if not <code>null</code>.
+	 * 
+	 * @param model is the progression to change
+	 * @return the percent of progress or {@link Float#NaN}
+	 * if there is no progession model.
+	 */
+	public static float getPercent(Progression model) {
+		if (model!=null) {
+			return model.getPercent();
+		}
+		return Float.NaN;
+	}
+
+	/** Set the value of the given task progression,
+	 * if not <code>null</code>.
+	 * The value must be greater than the current progression value.
+	 * 
+	 * @param model is the progression to change
+	 * @param value is the value to add to the progression value.
+	 */
+	public static void setValue(Progression model, int value) {
+		if (model!=null && value>model.getValue()) {
+			model.setValue(value);
+		}
+	}
+
+	/** Set the value of the given task progression,
+	 * if not <code>null</code>.
+	 * The value must be greater than the current progression value.
+	 * 
+	 * @param model is the progression to change
+	 * @param value is the value to add to the progression value.
+	 * @param comment is the comment associated to the progression.
+	 */
+	public static void setValue(Progression model, int value, String comment) {
+		if (model!=null && value>model.getValue()) {
+			model.setValue(value, comment);
+		}
+	}
+
+	/** Force the given task progression, if not <code>null</code>,
+	 * to have its value equals to the maximal value.
+	 * <p>
+	 * The inderterminate and adjusting states are not changed.
+	 * The comment is reset.
+	 * 
+	 * @param model is the task progression to initialize.
+	 */
+	public static void end(Progression model) {
+		if (model!=null) {
+			model.end();
+			model.setComment(null);
+		}
+	}
+
+	/** Ensure that there is no opened subtask.
+	 * If a subtask is existing, it is ended().
+	 * 
+	 * @param model is the task progression to initialize.
+	 */
+	public static void ensureNoSubTask(Progression model) {
+		if (model!=null) {
+			model.ensureNoSubTask();
+		}
+	}
+
+	/** Replies if the given progression indicators has its value
+	 * equal to its min value.
+	 * 
+	 * @param model is the task progression to initialize.
+	 * @return <code>true</code> if the indicator is at its min value or
+	 * if <var>model</var> is <code>null</code>.
+	 */
+	public static boolean isMinValue(Progression model) {
+		if (model!=null) {
+			return model.getValue()<=model.getMinimum();
+		}
+		return true;
+	}
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/progress/SubProgressionModel.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/progress/SubProgressionModel.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/progress/SubProgressionModel.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,189 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
+ *                        Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.progress;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * An object that permits to indicates the progression of
+ * a task.
+ *
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+class SubProgressionModel extends DefaultProgression {
+	
+	private final int level;
+	private final float minValueInParent;
+	private final float maxValueInParent;
+	private final float extentInParent;
+	private WeakReference<DefaultProgression> parent;
+	private final boolean overwriteCommentWhenDisconnect;
+	
+	/** Create a progress model with the specified <i>determinate</i> state.
+	 * 
+	 * @param parent is the parent model
+	 * @param minPValue is the value at which this sub-model is starting in its parent.
+	 * @param maxPValue is the value at which this sub-model is ending in its parent.
+	 * @param minValue is the minimum value of this progression model.
+	 * @param maxValue is the maximum value of this progression model.
+	 * @param isIndeterminate indicates if this model is under progression.
+	 * @param adjusting indicates if this model is adjusting its value.
+	 * @param overwriteComment indicates if the comment of this subtask may overwrite
+	 * the comment of its parent when it is disconnected.  
+	 */
+	SubProgressionModel(
+			DefaultProgression parent,
+			float minPValue, float maxPValue,
+			int minValue, int maxValue,
+			boolean isIndeterminate, boolean adjusting,
+			boolean overwriteComment) {
+		super(minValue, minValue, maxValue, adjusting);
+		assert(parent!=null);
+		this.level = parent.getTaskDepth() + 1;
+		float uptMaxPValue = maxPValue;
+		if (minPValue>uptMaxPValue) {
+			uptMaxPValue = minPValue;
+		}
+		this.minValueInParent = minPValue;
+    	this.maxValueInParent = uptMaxPValue;
+    	this.extentInParent = uptMaxPValue - minPValue;
+    	this.overwriteCommentWhenDisconnect = overwriteComment;
+    	this.parent = new WeakReference<DefaultProgression>(parent);
+		setIndeterminate(isIndeterminate);
+	}
+	
+	/** Create a progress model with the specified <i>determinate</i> state.
+	 * 
+	 * @param parent is the parent model
+	 * @param minPValue is the value at which this sub-model is starting in its parent.
+	 * @param maxPValue is the value at which this sub-model is ending in its parent.
+	 * @param isIndeterminate indicates if this model is under progression.
+	 * @param adjusting indicates if this model is adjusting its value.  
+	 * @param overwriteComment indicates if the comment of this subtask may overwrite
+	 * the comment of its parent when it is disconnected.  
+	 */
+	SubProgressionModel(DefaultProgression parent, float minPValue, float maxPValue,
+			boolean isIndeterminate, boolean adjusting, boolean overwriteComment) {
+		super((int)minPValue, (int)minPValue, (int)maxPValue);
+		assert(parent!=null);
+		this.level = parent.getTaskDepth() + 1;
+		float uptMaxPValue = maxPValue;
+		if (minPValue>uptMaxPValue) {
+			uptMaxPValue = minPValue;
+		}
+		this.minValueInParent = minPValue;
+    	this.maxValueInParent = uptMaxPValue;
+    	this.extentInParent = uptMaxPValue - minPValue;
+    	this.overwriteCommentWhenDisconnect = overwriteComment;
+    	this.parent = new WeakReference<DefaultProgression>(parent);
+    	setAdjusting(adjusting);
+		setIndeterminate(isIndeterminate);
+	}
+
+	/** Set the parent value and disconnect this subtask from its parent.
+	 */
+	void disconnect() {
+		DefaultProgression parentInstance = getParent();
+		if (parentInstance!=null) {
+			parentInstance.disconnectSubTask(this, this.maxValueInParent, this.overwriteCommentWhenDisconnect);
+		}
+		this.parent = null;
+	}
+	
+	/** Returns the minimal value of this task progression in its parent.
+     * 
+     * @return the value at which this model is supposed to start in its parent.
+     */
+    public float getMinInParent() {
+    	return this.minValueInParent;
+    }
+
+	/** Returns the maximal value of this task progression in its parent.
+     * 
+     * @return the value at which this model is supposed to end in its parent.
+     */
+    public float getMaxInParent() {
+    	return this.maxValueInParent;
+    }
+			
+    /** Returns the parent task.
+     * 
+     * @return the parent task.
+     */
+    protected DefaultProgression getParent() {
+    	return this.parent==null ? null : this.parent.get();
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void end() {
+    	super.end();
+		disconnect();
+    }
+    
+	@Override
+	protected void fireValueChange() {
+		// Notify subtask listeners
+		super.fireValueChange();
+		// Notify parent
+		float factor = getProgressionFactor();
+		if (factor<1f) {
+			DefaultProgression parentInstance = getParent();
+			if (parentInstance!=null) {
+				float valueInParent = this.extentInParent * factor + this.minValueInParent;
+				if (valueInParent>this.maxValueInParent)
+					valueInParent = this.maxValueInParent;
+				parentInstance.setValue(this, valueInParent, getComment());
+			}
+		}
+		else {
+			disconnect();
+		}
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public boolean isRootModel() {
+		return false;
+	}    
+    
+	/** {@inheritDoc}
+	 */
+	@Override
+	public Progression getSuperTask() {
+		return getParent();
+	}
+
+	/** {@inheritDoc}
+	 */
+	@Override
+	public int getTaskDepth() {
+		return this.level;
+	}
+}

Added: trunk/util/src/main/java/org/arakhne/afc/sizediterator/EmptyIterator.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/sizediterator/EmptyIterator.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/sizediterator/EmptyIterator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,103 @@
+/* 
+ * $Id$
+ * 
+ * Janus platform is an open-source multiagent platform.
+ * More details on <http://www.janus-project.org>
+ * Copyright (C) 2010-2011 Janus Core Developers
+ * 
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+package org.arakhne.afc.sizediterator;
+
+import java.util.NoSuchElementException;
+
+
+/**
+ * Sized iterator on an empty collection.
+ * <p>
+ * A sized iterator is an Iterator that is able to
+ * reply the size of the iterated collection and
+ * the number of elements that may be encountered
+ * in the next iterations.
+ *  
+ * @param <M> is the type of element.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class EmptyIterator<M>
+implements SizedIterator<M> {
+
+	/** Singleton.
+	 */
+	@SuppressWarnings("rawtypes")
+	private static final EmptyIterator SINGLETON = new EmptyIterator();
+	
+	/** Replies the singleton.
+	 * @param <M> is the type of the element to iterate on.
+	 * @return the singleton.
+	 */
+	@SuppressWarnings("unchecked")
+	public static <M> EmptyIterator<M> singleton() {
+		return SINGLETON;
+	}
+	
+	/**
+	 */
+	private EmptyIterator() {
+		//
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final boolean hasNext() {
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final M next() {
+		throw new NoSuchElementException();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void remove() {
+		throw new NoSuchElementException();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final int rest() {
+		return 0;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final int totalSize() {
+		return 0;
+	}
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/sizediterator/ModifiableCollectionSizedIterator.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/sizediterator/ModifiableCollectionSizedIterator.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/sizediterator/ModifiableCollectionSizedIterator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,123 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.sizediterator;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+
+/**
+ * Sized iterator on a collection that can be changed through the iterator.
+ * <p>
+ * A sized iterator is an Iterator that is able to
+ * reply the size of the iterated collection and
+ * the number of elements that may be encountered
+ * in the next iterations.
+ * <p>
+ * This iterator enables the use of the function {@link #remove()}.
+ * See {@link UnmodifiableCollectionSizedIterator} for an sized iterator
+ * that disables this function.
+ * 
+ * @param <M> is the type of element.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @see UnmodifiableCollectionSizedIterator
+ */
+public class ModifiableCollectionSizedIterator<M>
+implements SizedIterator<M> {
+
+	private final ModifiableCollectionSizedIteratorOwner<M> owner;
+	private int total;
+	private int rest;
+	private final Iterator<M> iterator;
+	private M lastReplied = null;
+	
+	/**
+	 * @param collection
+	 */
+	public ModifiableCollectionSizedIterator(Collection<M> collection) {
+		this(collection, null);
+	}
+
+	/**
+	 * @param collection
+	 * @param owner
+	 */
+	public ModifiableCollectionSizedIterator(Collection<M> collection, ModifiableCollectionSizedIteratorOwner<M> owner) {
+		assert(collection!=null);
+		this.owner = owner;
+		this.total = this.rest = collection.size();
+		this.iterator = collection.iterator();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int rest() {
+		return this.rest;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int totalSize() {
+		return this.total;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean hasNext() {
+		return this.iterator.hasNext();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public M next() {
+		M elt = this.iterator.next();
+		--this.rest;
+		this.lastReplied = elt;
+		return elt;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void remove() {
+		this.iterator.remove();
+		M data = this.lastReplied;
+		this.lastReplied = null;
+		--this.total;
+		--this.rest;
+		if (data!=null && this.owner!=null)
+			this.owner.onRemoveFromIterator(data);
+	}
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/sizediterator/ModifiableCollectionSizedIteratorOwner.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/sizediterator/ModifiableCollectionSizedIteratorOwner.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/sizediterator/ModifiableCollectionSizedIteratorOwner.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,42 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.sizediterator;
+
+/**
+ * Owner of a {@link ModifiableCollectionSizedIterator}
+ *  
+ * @param <M> is the type of element.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface ModifiableCollectionSizedIteratorOwner<M> {
+
+	/** Invoked just before an element is removed from a
+	 * collection through the iterator.
+	 * 
+	 * @param data is the removed data.
+	 */
+	public void onRemoveFromIterator(M data);
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/sizediterator/MultiSizedIterator.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/sizediterator/MultiSizedIterator.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/sizediterator/MultiSizedIterator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,120 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.sizediterator;
+
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+
+/**
+ * A sized iterator that is based on the used of 
+ * a collection of sized iterators.
+ * 
+ * @param <M> is the type of element.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class MultiSizedIterator<M> implements SizedIterator<M> {
+
+	private final LinkedList<SizedIterator<? extends M>> iterators = new LinkedList<SizedIterator<? extends M>>();
+	private int total = 0;
+	private int returned = 0;
+	private SizedIterator<? extends M> iterator = null;
+	private boolean update = false;
+	private M next = null;
+	
+	/**
+	 */
+	public MultiSizedIterator() {
+		//
+	}
+	
+	/** Add an iterator in the list of iterators.
+	 * 
+	 * @param iterator
+	 */
+	public void addIterator(SizedIterator<? extends M> iterator) {
+		if (this.iterators.add(iterator)) {
+			this.total += iterator.totalSize();
+			this.update = true;
+		}
+	}
+	
+	private void searchNext() {
+		this.next = null;
+		while ((this.iterator==null || !this.iterator.hasNext()) && (!this.iterators.isEmpty())) {
+			this.iterator = this.iterators.removeFirst();
+		}
+		if (this.iterator!=null && this.iterator.hasNext()) {
+			this.next = this.iterator.next();
+		}
+		this.update = false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean hasNext() {
+		if (this.next==null && this.update) searchNext();
+		return this.next!=null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public M next() {
+		if (this.next==null && this.update) searchNext();
+		M v = this.next;
+		if (v==null) throw new NoSuchElementException();
+		++this.returned;
+		searchNext();
+		return v;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void remove() {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int totalSize() {
+		return this.total;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int rest() {
+		return this.total - this.returned;
+	}
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/sizediterator/SizedIterator.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/sizediterator/SizedIterator.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/sizediterator/SizedIterator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,52 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.sizediterator;
+
+import java.util.Iterator;
+
+/**
+ * A sized iterator is an Iterator that is able to
+ * reply the size of the iterated collection and
+ * the number of elements that may be encountered
+ * in the next iterations.
+ * 
+ * @param <M> is the type of element.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface SizedIterator<M> extends Iterator<M> {
+
+	/** Replies the count of elements in the iterated collection.
+	 * 
+	 * @return the count of elements in the iterated collection.
+	 */
+	public int totalSize();
+
+	/** Replies the count of elements which are not replied by the iterator.
+	 * 
+	 * @return the count of elements which are not replied by the iterator.
+	 */
+	public int rest();
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/sizediterator/UnmodifiableCollectionSizedIterator.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/sizediterator/UnmodifiableCollectionSizedIterator.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/sizediterator/UnmodifiableCollectionSizedIterator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,105 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.sizediterator;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+
+/**
+ * Sized iterator on a collection that cannot be changed through the iterator.
+ * <p>
+ * A sized iterator is an Iterator that is able to
+ * reply the size of the iterated collection and
+ * the number of elements that may be encountered
+ * in the next iterations.
+ * <p>
+ * This iterator disables the use of the function {@link #remove()}.
+ * See {@link ModifiableCollectionSizedIterator} for an sized iterator
+ * that enables this function.
+ * 
+ * @param <M> is the type of element.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @see ModifiableCollectionSizedIterator
+ */
+public class UnmodifiableCollectionSizedIterator<M>
+implements SizedIterator<M> {
+
+	private final int total;
+	private int rest;
+	private final Iterator<M> iterator;
+	
+	/**
+	 * @param collection
+	 */
+	public UnmodifiableCollectionSizedIterator(Collection<M> collection) {
+		assert(collection!=null);
+		this.total = this.rest = collection.size();
+		this.iterator = collection.iterator();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int rest() {
+		return this.rest;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int totalSize() {
+		return this.total;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean hasNext() {
+		return this.iterator.hasNext();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public M next() {
+		M elt = this.iterator.next();
+		--this.rest;
+		return elt;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void remove() {
+		throw new UnsupportedOperationException();
+	}
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/sizediterator/UnmodifiableMapKeySizedIterator.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/sizediterator/UnmodifiableMapKeySizedIterator.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/sizediterator/UnmodifiableMapKeySizedIterator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,108 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.sizediterator;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+
+/**
+ * Class that map the keys of a Map to an unmodifiable  sized iterator.
+ * <p>
+ * A sized iterator is an Iterator that is able to
+ * reply the size of the iterated collection and
+ * the number of elements that may be encountered
+ * in the next iterations.
+ * <p>
+ * This class provides features closed to {@link Collections#unmodifiableMap(Map)}
+ * but with a sized iterators on the keys of the map.
+ * To obtain similar features on the map values, see {@link UnmodifiableMapValueSizedIterator}.
+ * <p>
+ * This iterator disables the use of the function {@link #remove()}.
+ * 
+ * @param <K> is the type of keys.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @see UnmodifiableMapValueSizedIterator
+ */
+public class UnmodifiableMapKeySizedIterator<K>
+implements SizedIterator<K> {
+
+	private final int total;
+	private int rest;
+	private final Iterator<K> iterator;
+	
+	/**
+	 * @param map
+	 */
+	public UnmodifiableMapKeySizedIterator(Map<K,?> map) {
+		assert(map!=null);
+		this.total = this.rest = map.size();
+		this.iterator = map.keySet().iterator();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int rest() {
+		return this.rest;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int totalSize() {
+		return this.total;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean hasNext() {
+		return this.iterator.hasNext();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public K next() {
+		K elt = this.iterator.next();
+		--this.rest;
+		return elt;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void remove() {
+		throw new UnsupportedOperationException();
+	}
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/sizediterator/UnmodifiableMapValueSizedIterator.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/sizediterator/UnmodifiableMapValueSizedIterator.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/sizediterator/UnmodifiableMapValueSizedIterator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,108 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.sizediterator;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+
+/**
+ * Class that map the values of a Map to an unmodifiable sized iterator.
+ * <p>
+ * A sized iterator is an Iterator that is able to
+ * reply the size of the iterated collection and
+ * the number of elements that may be encountered
+ * in the next iterations.
+ * <p>
+ * This class provides features closed to {@link Collections#unmodifiableMap(Map)}
+ * but with a sized iterators on the values of the map.
+ * To obtain similar features on the map keys, see {@link UnmodifiableMapKeySizedIterator}.
+ * <p>
+ * This iterator disables the use of the function {@link #remove()}.
+ * 
+ * @param <V> is the type of values.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @see UnmodifiableMapKeySizedIterator
+ */
+public class UnmodifiableMapValueSizedIterator<V>
+implements SizedIterator<V> {
+
+	private final int total;
+	private int rest;
+	private final Iterator<V> iterator;
+	
+	/**
+	 * @param map
+	 */
+	public UnmodifiableMapValueSizedIterator(Map<?,V> map) {
+		assert(map!=null);
+		this.total = this.rest = map.size();
+		this.iterator = map.values().iterator();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int rest() {
+		return this.rest;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int totalSize() {
+		return this.total;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean hasNext() {
+		return this.iterator.hasNext();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public V next() {
+		V elt = this.iterator.next();
+		--this.rest;
+		return elt;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void remove() {
+		throw new UnsupportedOperationException();
+	}
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/sizediterator/UnmodifiableSizedIterator.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/sizediterator/UnmodifiableSizedIterator.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/sizediterator/UnmodifiableSizedIterator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,103 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.sizediterator;
+
+import java.util.Collections;
+
+import org.arakhne.afc.util.UnmodifiableIterator;
+
+/**
+ * Class that make unmodifiable a sized iterator.
+ * <p>
+ * A sized iterator is an Iterator that is able to
+ * reply the size of the iterated collection and
+ * the number of elements that may be encountered
+ * in the next iterations.
+ * <p>
+ * This class provides features closed to {@link Collections#unmodifiableCollection(java.util.Collection)}
+ * but on sized iterators.
+ * <p>
+ * This iterator disables the use of the function {@link #remove()}.
+ * This iterator implementation is a iterator; see
+ * {@link UnmodifiableIterator} for an utility class on a standard Iterator.
+ * 
+ * @param <M> is the type of element.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @see UnmodifiableIterator
+ */
+public class UnmodifiableSizedIterator<M>
+implements SizedIterator<M> {
+
+	private final SizedIterator<M> original;
+
+	/**
+	 * @param iterator
+	 */
+	public UnmodifiableSizedIterator(SizedIterator<M> iterator) {
+		assert(iterator!=null);
+		this.original = iterator;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean hasNext() {
+		return this.original.hasNext();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public M next() {
+		return this.original.next();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void remove() {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int rest() {
+		return this.original.rest();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int totalSize() {
+		return this.original.totalSize();
+	}
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/text/TextUtil.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/text/TextUtil.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/text/TextUtil.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,54 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.text ;
+
+
+/** Utilities on Strings.
+ * 
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @since 15.0
+ */
+public class TextUtil {
+
+	
+	
+	/** Enforced version of the equality test on two strings with case ignoring.
+	 * This enforced version supported <code>null</code> values
+	 * given as parameters.
+	 * 
+	 * @param a
+	 * @param b
+	 * @param isNullEmptyEquivalence indicates if the <code>null</code> value
+	 * is assimilated to the empty string.
+	 * @return <code>true</code> if a is equal to b; otherwise <code>false</code>.
+	 */
+	public static boolean equalsIgnoreCase(String a, String b, boolean isNullEmptyEquivalence) {
+		String aa = (a!=null || !isNullEmptyEquivalence) ? a : "";  //$NON-NLS-1$
+		String bb = (b!=null || !isNullEmptyEquivalence) ? b : "";  //$NON-NLS-1$
+		if (aa==null) return bb==null;
+		if (bb==null) return false;
+		return aa.equalsIgnoreCase(bb);
+	}
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/util/HashCodeUtil.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/util/HashCodeUtil.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/util/HashCodeUtil.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,415 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10 Multiagent Team, Laboratoire Systemes et Transports,
+ *                       Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.util;
+
+import java.lang.ref.Reference;
+import java.util.Arrays;
+
+/** Utilities to compute hash codes.
+ * <p>
+ * The utility class {@link Arrays} privides several
+ * functions to compute hash codes from arrays.
+ *
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @see Arrays
+ */
+public class HashCodeUtil {
+
+	private static final int HASH_VALUE = 31;
+	
+	/** Replies an initialized hash code.
+	 * 
+	 * @return an initialized hash code.
+	 */
+	public static int iddleHash() {
+		return 1;
+	}
+	
+	/** Compute a new hash code from the given value.
+	 * If the value is {@link Reference}, the value
+	 * is dereferenced while it is a {@link Reference}.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Object#hashCode()
+	 */
+	public static int hash(int oldHash, Object value) {
+		int hc;
+		if (value instanceof Reference<?>) {
+			Object referenced = ((Reference<?>)value).get();
+			hc = (referenced==null) ? 0 : referenced.hashCode();
+		}
+		else {
+			hc = ((value==null) ? 0 : value.hashCode());
+		}
+		return add(oldHash, hc);
+	}
+
+	/** Compute a new hash code from the given primitive value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Boolean#hashCode()
+	 */
+	public static int hash(int oldHash, boolean value) {
+		return add(oldHash, Boolean.valueOf(value).hashCode());
+	}
+
+	/** Compute a new hash code from the given primitive value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Character#hashCode()
+	 */
+	public static int hash(int oldHash, char value) {
+		return add(oldHash, Character.valueOf(value).hashCode());
+	}
+
+	/** Compute a new hash code from the given primitive value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Byte#hashCode()
+	 */
+	public static int hash(int oldHash, byte value) {
+		return add(oldHash, Byte.valueOf(value).hashCode());
+	}
+
+	/** Compute a new hash code from the given primitive value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Short#hashCode()
+	 */
+	public static int hash(int oldHash, short value) {
+		return add(oldHash, Short.valueOf(value).hashCode());
+	}
+
+	/** Compute a new hash code from the given primitive value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Integer#hashCode()
+	 */
+	public static int hash(int oldHash, int value) {
+		return add(oldHash, Integer.valueOf(value).hashCode());
+	}
+
+	/** Compute a new hash code from the given primitive value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Long#hashCode()
+	 */
+	public static int hash(int oldHash, long value) {
+		return add(oldHash, Long.valueOf(value).hashCode());
+	}
+
+	/** Compute a new hash code from the given primitive value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Float#hashCode()
+	 */
+	public static int hash(int oldHash, float value) {
+		return add(oldHash, Float.valueOf(value).hashCode());
+	}
+
+	/** Compute a new hash code from the given primitive value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Double#hashCode()
+	 */
+	public static int hash(int oldHash, double value) {
+		return add(oldHash, Double.valueOf(value).hashCode());
+	}
+
+	/** Compute a new hash code from the given primitive value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Arrays#hashCode(boolean[])
+	 */
+	public static int hash(int oldHash, boolean[] value) {
+		return add(oldHash, Arrays.hashCode(value));
+	}
+
+	/** Compute a new hash code from the given primitive value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Arrays#hashCode(char[])
+	 */
+	public static int hash(int oldHash, char[] value) {
+		return add(oldHash, Arrays.hashCode(value));
+	}
+
+	/** Compute a new hash code from the given primitive value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Arrays#hashCode(byte[])
+	 */
+	public static int hash(int oldHash, byte[] value) {
+		return add(oldHash, Arrays.hashCode(value));
+	}
+
+	/** Compute a new hash code from the given primitive value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Arrays#hashCode(short[])
+	 */
+	public static int hash(int oldHash, short[] value) {
+	return add(oldHash, Arrays.hashCode(value));
+	}
+
+	/** Compute a new hash code from the given primitive value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Arrays#hashCode(int[])
+	 */
+	public static int hash(int oldHash, int[] value) {
+		return add(oldHash, Arrays.hashCode(value));
+	}
+
+	/** Compute a new hash code from the given primitive value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Arrays#hashCode(long[])
+	 */
+	public static int hash(int oldHash, long[] value) {
+		return add(oldHash, Arrays.hashCode(value));
+	}
+
+	/** Compute a new hash code from the given primitive value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Arrays#hashCode(double[])
+	 */
+	public static int hash(int oldHash, float[] value) {
+		return add(oldHash, Arrays.hashCode(value));
+	}
+
+	/** Compute a new hash code from the given primitive value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param value is the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 * @see Arrays#hashCode(double[])
+	 */
+	public static int hash(int oldHash, double[] value) {
+		return add(oldHash, Arrays.hashCode(value));
+	}
+	
+	/** Compute a new hash code from the old hash code and the hash code of a value.
+	 * 
+	 * @param oldHash is the old hash code.
+	 * @param valueHashCode is the hash code of the value from which the hash code may be updated.
+	 * @return an new hash code.
+	 */
+	public static int add(int oldHash, int valueHashCode) {
+		return oldHash * HASH_VALUE + valueHashCode;
+	}
+
+	/** Compute the hash code from the given parameters.
+	 * <p>
+	 * This function starts from value <code>1</code> and
+	 * applies <code>h = h * 31 + a.hashCode()</code> for
+	 * each non-null attribute.
+	 * 
+	 * @param attributes
+	 * @return the hash code from the given parameters.
+	 */
+	public static int hash(Object... attributes) {
+		int hash = iddleHash();
+		for(Object o : attributes) {
+			hash = hash(hash, o);
+		}
+	    return hash;    	
+	}
+	
+	/** Compute the hash code from the given primitive value.
+	 * 
+	 * @param value
+	 * @return the hash code from the given primitive value.
+	 * @see Byte#hashCode()
+	 */
+	public static int hash(byte value) {
+		return Byte.valueOf(value).hashCode();    	
+	}
+
+	/** Compute the hash code from the given primitive value.
+	 * 
+	 * @param value
+	 * @return the hash code from the given primitive value.
+	 * @see Short#hashCode()
+	 */
+	public static int hash(short value) {
+		return Short.valueOf(value).hashCode();    	
+	}
+
+	/** Compute the hash code from the given primitive value.
+	 * 
+	 * @param value
+	 * @return the hash code from the given primitive value.
+	 * @see Long#hashCode()
+	 */
+	public static int hash(long value) {
+		return Long.valueOf(value).intValue();    	
+	}
+
+	/** Compute the hash code from the given primitive value.
+	 * 
+	 * @param value
+	 * @return the hash code from the given primitive value.
+	 * @see Float#hashCode()
+	 */
+	public static int hash(float value) {
+		return Float.valueOf(value).hashCode();    	
+	}
+
+	/** Compute the hash code from the given primitive value.
+	 * 
+	 * @param value
+	 * @return the hash code from the given primitive value.
+	 * @see Double#hashCode()
+	 */
+	public static int hash(double value) {
+		return Double.valueOf(value).hashCode();    	
+	}
+
+	/** Compute the hash code from the given primitive value.
+	 * 
+	 * @param value
+	 * @return the hash code from the given primitive value.
+	 * @see Character#hashCode()
+	 */
+	public static int hash(char value) {
+		return Character.valueOf(value).hashCode();    	
+	}
+
+	/** Compute the hash code from the given primitive value.
+	 * 
+	 * @param value
+	 * @return the hash code from the given primitive value.
+	 * @see Boolean#hashCode()
+	 */
+	public static int hash(boolean value) {
+		return Boolean.valueOf(value).hashCode();    	
+	}
+	
+	/** Compute the hash code from the given primitive value.
+	 * 
+	 * @param value
+	 * @return the hash code from the given primitive value.
+	 * @see Arrays#hashCode(byte[])
+	 */
+	public static int hash(byte[] value) {
+		return Arrays.hashCode(value);    
+	}
+
+	/** Compute the hash code from the given primitive value.
+	 * 
+	 * @param value
+	 * @return the hash code from the given primitive value.
+	 * @see Arrays#hashCode(short[])
+	 */
+	public static int hash(short[] value) {
+		return Arrays.hashCode(value);    
+	}
+
+	/** Compute the hash code from the given primitive value.
+	 * 
+	 * @param value
+	 * @return the hash code from the given primitive value.
+	 * @see Arrays#hashCode(long[])
+	 */
+	public static int hash(long[] value) {
+		return Arrays.hashCode(value);    	
+	}
+
+	/** Compute the hash code from the given primitive value.
+	 * 
+	 * @param value
+	 * @return the hash code from the given primitive value.
+	 * @see Arrays#hashCode(float[])
+	 */
+	public static int hash(float[] value) {
+		return Arrays.hashCode(value);    	
+	}
+
+	/** Compute the hash code from the given primitive value.
+	 * 
+	 * @param value
+	 * @return the hash code from the given primitive value.
+	 * @see Arrays#hashCode(double[])
+	 */
+	public static int hash(double[] value) {
+		return Arrays.hashCode(value);    	
+	}
+
+	/** Compute the hash code from the given primitive value.
+	 * 
+	 * @param value
+	 * @return the hash code from the given primitive value.
+	 * @see Arrays#hashCode(char[])
+	 */
+	public static int hash(char[] value) {
+		return Arrays.hashCode(value);    	
+	}
+
+	/** Compute the hash code from the given primitive value.
+	 * 
+	 * @param value
+	 * @return the hash code from the given primitive value.
+	 * @see Arrays#hashCode(boolean[])
+	 */
+	public static int hash(boolean[] value) {
+		return Arrays.hashCode(value);    	
+	}
+
+}
\ No newline at end of file

Added: trunk/util/src/main/java/org/arakhne/afc/util/ListUtil.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/util/ListUtil.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/util/ListUtil.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,150 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.util;
+
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Utilities on lists.
+ * 
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @since 0.4
+ */
+public class ListUtil {
+
+	/** Remove the given element from the list using a dichotomic algorithm.
+	 * <p>
+	 * This function ensure that the comparator is invoked as: <code>comparator(data, dataAlreadyInList)</code>.
+	 * 
+	 * @param <E> is the type of the elements in the list.
+	 * @param list is the list to change.
+	 * @param comparator is the comparator of elements.
+	 * @param data is the data to remove.
+	 * @return <code>true</code> if the data was removed, otherwise <code>false</code>
+	 */
+	public static <E> boolean dichotomicRemove(List<E> list, Comparator<? super E> comparator, E data) {
+		assert(list!=null);
+		assert(comparator!=null);
+		assert(data!=null);
+		int f = 0;
+		int l = list.size()-1;
+		int c;
+		E d;
+		int cmpR;
+		while (l>=f) {
+			c = (f+l)/2;
+			d = list.get(c);
+			cmpR = comparator.compare(data, d);
+			if (cmpR==0) {
+				list.remove(c);
+				return true;
+			}
+			else if (cmpR<0) {
+				l = c-1;
+			}
+			else {
+				f = c+1;
+			}
+		}
+		return false;
+	}
+
+	/** Add the given element in the main list using a dichotomic algorithm.
+	 * <p>
+	 * This function ensure that the comparator is invoked as: <code>comparator(data, dataAlreadyInList)</code>.
+	 * <p>
+	 * If the data is al
+	 * 
+	 * @param <E> is the type of the elements in the list.
+	 * @param list is the list to change.
+	 * @param comparator is the comparator of elements.
+	 * @param data is the data to insert.
+	 * @param allowMultipleOccurencesOfSameValue indicates if multiple
+	 * occurrences of the same value are allowed in the list.
+	 * @return <code>true</code> if the data was added, otherwise <code>false</code>
+	 */
+	public static <E> boolean dichotomicAdd(List<E> list, Comparator<? super E> comparator, E data, boolean allowMultipleOccurencesOfSameValue) {
+		assert(list!=null);
+		assert(comparator!=null);
+		assert(data!=null);
+		int f = 0;
+		int l = list.size()-1;
+		int c;
+		E d;
+		int cmpR;
+		while (l>=f) {
+			c = (f+l)/2;
+			d = list.get(c);
+			cmpR = comparator.compare(data, d);
+			if (cmpR==0 && !allowMultipleOccurencesOfSameValue) return false;
+			if (cmpR<0) {
+				l = c-1;
+			}
+			else {
+				f = c+1;
+			}
+		}
+		list.add(f, data);
+		return true;
+	}
+
+	/** Replies if the given element is inside the list, using a dichotomic algorithm.
+	 * <p>
+	 * This function ensure that the comparator is invoked as: <code>comparator(data, dataAlreadyInList)</code>.
+	 * 
+	 * @param <E> is the type of the elements in the list.
+	 * @param list is the list to explore.
+	 * @param comparator is the comparator of elements.
+	 * @param data is the data to search for.
+	 * @return <code>true</code> if the data is inside the list, otherwise <code>false</code>
+	 */
+	public static <E> boolean dichotomicContains(List<E> list, Comparator<? super E> comparator, E data) {
+		assert(list!=null);
+		assert(comparator!=null);
+		assert(data!=null);
+		int f = 0;
+		int l = list.size()-1;
+		int c;
+		E d;
+		int cmpR;
+		while (l>=f) {
+			c = (f+l)/2;
+			d = list.get(c);
+			cmpR = comparator.compare(data, d);
+			if (cmpR==0) {
+				return true;
+			}
+			else if (cmpR<0) {
+				l = c-1;
+			}
+			else {
+				f = c+1;
+			}
+		}
+		return false;
+	}
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/util/ListenerCollection.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/util/ListenerCollection.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/util/ListenerCollection.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,254 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2012 Janus Core Developers
+ * 
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+package org.arakhne.afc.util;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.EventListener;
+
+/**
+ * A collection of listeners.
+ * <p>
+ * This collection is thread-safe.
+ * <p>
+ * This class is inspirated by <code>EventListenerList</code>.
+ * 
+ * @param <L> is the type of listeners.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class ListenerCollection<L extends EventListener> {
+	
+	private final static Object[] NULL = new Object[0];
+	
+	/** Listeners.
+	 */
+	protected transient Object[] listeners = NULL;
+
+	/**
+	 */
+	public ListenerCollection() {
+		//
+	}
+	
+	/** Replies if this collection is empty.
+	 * 
+	 * @return <code>true</code> if this collection does not
+	 * contains any listener, otherwise <code>false</code>
+	 */
+	public boolean isEmpty() {
+		return this.listeners == NULL;
+	}
+	
+	/** Clear this collection.
+	 */
+	public void clear() {
+		this.listeners = NULL;
+	}
+	
+	/**
+	 * Passes back the event listener list as an array
+	 * of ListenerType-listener pairs.  Note that for 
+	 * performance reasons, this implementation passes back 
+	 * the actual data structure in which the listener data
+	 * is stored internally!  
+	 * This method is guaranteed to pass back a non-null
+	 * array, so that no null-checking is required in 
+	 * fire methods.  A zero-length array of Object should
+	 * be returned if there are currently no listeners.
+	 * <p>
+	 * WARNING!!! Absolutely NO modification of
+	 * the data contained in this array should be made -- if
+	 * any such manipulation is necessary, it should be done
+	 * on a copy of the array returned rather than the array 
+	 * itself.
+	 * 
+	 * @return the listeners.
+	 */
+	public Object[] getListenerList() {
+		return this.listeners;
+	}
+
+	/**
+	 * Return an array of all the listeners of the given type.
+	 *  
+	 * @param <T> is the type of expected listeners.
+	 * @param type is the type of expected listeners.
+	 * @return all of the listeners of the specified type. 
+	 * @exception  ClassCastException if the supplied class
+	 *		is not assignable to EventListener
+	 */
+	@SuppressWarnings("unchecked")
+	public <T extends EventListener> T[] getListeners(Class<T> type) {
+		Object[] l = this.listeners; 
+		int n = getListenerCount(l, type); 
+		T[] result = (T[])Array.newInstance(type, n); 
+		int j = 0; 
+		for (int i = l.length-2; i>=0; i-=2) {
+			if (l[i] == type) {
+				result[j++] = type.cast(l[i+1]);
+			}
+		}
+		return result;   
+	}
+
+	/**
+	 * Returns the total number of listeners for this listener list.
+	 * 
+	 * @return the total number of listeners for this listener list.
+	 */
+	public int size() {
+		return this.listeners.length/2;
+	}
+
+	/**
+	 * Returns the total number of listeners of the supplied type 
+	 * for this listener list.
+	 * 
+	 * @param type
+	 * @return the total number of listeners of the supplied type 
+	 * for this listener list.
+	 */
+	public int getListenerCount(Class<?> type) {
+		return getListenerCount(this.listeners, type);
+	}
+
+	private static int getListenerCount(Object[] list, Class<?> t) {
+		int count = 0;
+		for (int i = 0; i < list.length; i+=2) {
+			if (t == (Class<?>)list[i])
+				++count;
+		}
+		return count;
+	}
+
+	/**
+	 * Adds the listener as a listener of the specified type.
+	 * 
+	 * @param <T> the type of the listener to be added
+	 * @param type the type of the listener to be added
+	 * @param listener the listener to be added
+	 */
+	public synchronized <T extends EventListener> void add(Class<T> type, T listener) {
+		assert(listener!=null);
+		if (this.listeners == NULL) {
+			// if this is the first listener added, 
+			// initialize the lists
+			this.listeners = new Object[] { type, listener };
+		}
+		else {
+			// Otherwise copy the array and add the new listener
+			int i = this.listeners.length;
+			Object[] tmp = new Object[i+2];
+			System.arraycopy(this.listeners, 0, tmp, 0, i);
+
+			tmp[i] = type;
+			tmp[i+1] = listener;
+
+			this.listeners = tmp;
+		}
+	}
+
+	/**
+	 * Removes the listener as a listener of the specified type.
+	 * 
+	 * @param <T> the type of the listener to be removed
+	 * @param type the type of the listener to be removed
+	 * @param listener the listener to be removed
+	 */
+	public synchronized <T extends EventListener> void remove(Class<T> type, T listener) {
+		assert(listener!=null);
+		// Is l on the list?
+		int index = -1;
+		for (int i = this.listeners.length-2; i>=0; i-=2) {
+			if ((this.listeners[i]==type) && (this.listeners[i+1].equals(listener))) {
+				index = i;
+				break;
+			}
+		}
+
+		// If so,  remove it
+		if (index != -1) {
+			Object[] tmp = new Object[this.listeners.length-2];
+			// Copy the list up to index
+			System.arraycopy(this.listeners, 0, tmp, 0, index);
+			// Copy from two past the index, up to
+			// the end of tmp (which is two elements
+			// shorter than the old list)
+			if (index < tmp.length)
+				System.arraycopy(this.listeners, index+2, tmp, index, 
+						tmp.length - index);
+			// set the listener array to the new array or null
+			this.listeners = (tmp.length == 0) ? NULL : tmp;
+		}
+	}
+
+	// Serialization support.  
+	private void writeObject(ObjectOutputStream s) throws IOException {
+		Object[] lList = this.listeners;
+		s.defaultWriteObject();
+
+		// Save the non-null event listeners:
+		for (int i = 0; i < lList.length; i+=2) {
+			Class<?> t = (Class<?>)lList[i];
+			EventListener l = (EventListener)lList[i+1];
+			if ((l!=null) && (l instanceof Serializable)) {
+				s.writeObject(t.getName());
+				s.writeObject(l);
+			}
+		}
+
+		s.writeObject(null);
+	}
+
+	@SuppressWarnings("unchecked")
+	private void readObject(ObjectInputStream s) 
+	throws IOException, ClassNotFoundException {
+		this.listeners = NULL;
+		s.defaultReadObject();
+		Object listenerTypeOrNull;
+
+		while (null != (listenerTypeOrNull = s.readObject())) {
+			ClassLoader cl = Thread.currentThread().getContextClassLoader();
+			EventListener l = (EventListener)s.readObject();
+			add((Class<EventListener>)Class.forName((String)listenerTypeOrNull, true, cl), l);
+		}	    
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		Object[] lList = this.listeners;
+		String s = "EventListenerList: "; //$NON-NLS-1$
+		s += lList.length/2 + " listeners: "; //$NON-NLS-1$
+		for (int i = 0 ; i <= lList.length-2 ; i+=2) {
+			s += " type " + ((Class<?>)lList[i]).getName(); //$NON-NLS-1$
+			s += " listener " + lList[i+1]; //$NON-NLS-1$
+		}
+		return s;
+	}
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/util/MultiValue.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/util/MultiValue.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/util/MultiValue.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,184 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (c) 2005-10 Multiagent Team, Laboratoire Systemes et Transports,
+ *                       Universite de Technologie de Belfort-Montbeliard.
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.util;
+
+import org.arakhne.vmutil.locale.Locale;
+
+/** Utilities class that permits to represent a collection of values
+ * and indicating if they are all the same or not.
+ *
+ * @param <T> is the type of the value.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class MultiValue<T> {
+
+	private boolean isSet =  false;
+	private boolean isMultiple = false;
+	private T object;
+	
+	/**
+	 * @param initialValue is the initial value of the output parameter.
+	 */
+	public MultiValue(T initialValue) {
+		this.object = initialValue;
+		this.isSet = true;
+	}
+
+	/**
+	 * Create empty output parameter.
+	 */
+	public MultiValue() {
+		this.object = null;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		return toString(Locale.getString("MULTIPLE_VALUES")); //$NON-NLS-1$
+	}
+
+	/**
+	 * Replies a string representation of the value in this object.
+	 * If this object is set and has multiple different values, the
+	 * parameter <var>multiValueLabel</var> is replied.
+	 * If this object is not set or equals to <code>null</code>,
+	 * the empty string is replied.
+	 * Otherwise the value is replied. 
+	 * 
+	 * @param multiValueLabel is the label to reply if this object is containing many different values.
+	 * @return the string representation of this object.
+	 */
+	public String toString(String multiValueLabel) {
+		if (this.isSet) {
+			 if (this.isMultiple) {
+				 return multiValueLabel;
+			 }
+			 if (this.object!=null) {
+				 return this.object.toString();
+			 }
+		}
+		return ""; //$NON-NLS-1$
+	}
+
+	/** Replies the value.
+	 * 
+	 * @return the value embedded inside this object.
+	 */
+	public T get() {
+		return this.object;
+	}
+	
+	/** Replies the type of the value.
+	 * 
+	 * @return the type or <code>null</code> if there is no value.
+	 */
+	@SuppressWarnings("unchecked")
+	public Class<? extends T> getValueType() {
+		if (this.object==null) return null;
+		return (Class<? extends T>)this.object.getClass();
+	}
+
+	/** Add the given value to the styored value.
+	 * 
+	 * @param newValue
+	 */
+	public void add(T newValue) {
+		if (this.isSet) {
+			if (!this.isMultiple
+				&& newValue!=this.object && (newValue==null || !newValue.equals(this.object))) {
+				this.isMultiple = true;
+				this.object = null;
+			}
+		}
+		else {
+			this.object = newValue;
+			this.isSet = true;
+			this.isMultiple = false;
+		}
+	}
+
+	/** Clear this multi-value.
+	 */
+	public void clear() {
+		this.object = null;
+		this.isSet = false;
+		this.isMultiple = false;
+	}
+
+	/** Replies if the value was set.
+	 * 
+	 * @return <code>true</code> is the value was set, otherwise <code>false</code>
+	 */
+	public boolean isSet() {
+		return this.isSet;
+	}
+
+	/** Replies if this MultiValue contains different values.
+	 * 
+	 * @return <code>true</code> is values are different, otherwise <code>false</code>
+	 */
+	public boolean isMultipleDifferentValues() {
+		return this.isMultiple;
+	}
+
+	/** Set if this MultiValue contains different values.
+	 * 
+	 * @param multiple is <code>true</code> is values are different, otherwise <code>false</code>
+	 */
+	public void setMultipleDifferentValues(boolean multiple) {
+		this.isMultiple = true;
+		this.isSet = true;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(Object o) {
+		if (o instanceof MultiValue<?>) {
+			MultiValue<?> v = (MultiValue<?>)o;
+			if (this.isSet!=v.isSet || this.isMultiple!=v.isMultiple)
+				return false;
+		}
+		return (this.object==o)
+				||
+				(this.object!=null && this.object.equals(o));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int hashCode() {
+		int h = HashCodeUtil.hash(this.isSet);
+		h = HashCodeUtil.hash(h, this.isMultiple);
+		if (this.object!=null) h = HashCodeUtil.hash(h, this.object);
+		return h;
+	}
+
+}
\ No newline at end of file

Added: trunk/util/src/main/java/org/arakhne/afc/util/Pair.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/util/Pair.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/util/Pair.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,101 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.util;
+
+/** This class implements pair of values. 
+ *
+ * @param <A> is the type of the first value.
+ * @param <B> is the type of the second value.
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public class Pair<A,B> {
+
+	private A a;
+	private B b;
+
+	/**
+	 */
+	public Pair() {
+		this.a = null; 
+		this.b = null; 
+	}
+
+	/**
+	 * @param p
+	 */
+	public Pair(Pair<? extends A, ? extends B> p) {
+		this.a = p.getA();
+		this.b = p.getB();
+	}
+
+	/**
+	 * @param a
+	 * @param b
+	 */
+	public Pair(A a, B b) {
+		this.a = a;
+		this.b = b;
+	}
+	
+	/** Replies the first value of the pair.
+	 * 
+	 * @return the first value of the pair.
+	 */
+	public A getA() {
+		return this.a;
+	}
+
+	/** Replies the second value of the pair.
+	 * 
+	 * @return the second value of the pair.
+	 */
+	public B getB() {
+		return this.b;
+	}
+
+	/** Set the first value of the pair.
+	 * 
+	 * @param a is the first value of the pair.
+	 */
+	public void setA(A a) {
+		this.a = a;
+	}
+
+	/** Set the second value of the pair.
+	 * 
+	 * @param b is the second value of the pair.
+	 */
+	public void setB(B b) {
+		this.b = b;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		return "<"+this.a+";"+this.b+">";  //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
+	}
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/util/PropertyOwner.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/util/PropertyOwner.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/util/PropertyOwner.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,49 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2013 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.util;
+
+import java.util.Map;
+
+/** This interface defines the services for an objects
+ * that is owning properties.
+ *
+ * @author $Author: galland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ */
+public interface PropertyOwner {
+
+	/** Replies the properties of this component.
+	 * 
+	 * @return the properties, never <code>null</code>.
+	 * @see #setProperties(Map)
+	 */
+	public Map<String,Object> getProperties();
+
+	/** Set the properties of the model object, except the UUID.
+	 * 
+	 * @param properties are the properties of this model object, except the UUID.
+	 * @see #getProperties()
+	 */
+	public void setProperties(Map<String,Object> properties);
+
+}

Added: trunk/util/src/main/java/org/arakhne/afc/util/UnmodifiableIterator.java
===================================================================
--- trunk/util/src/main/java/org/arakhne/afc/util/UnmodifiableIterator.java	                        (rev 0)
+++ trunk/util/src/main/java/org/arakhne/afc/util/UnmodifiableIterator.java	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,91 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010-2011 Janus Core Developers
+ * Copyright (C) 2012 Stephane GALLAND.
+ * 
+ * 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.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * This program is free software; you can redistribute it and/or modify
+ */
+package org.arakhne.afc.util;
+
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.arakhne.afc.sizediterator.UnmodifiableSizedIterator;
+
+/**
+ * Class that make unmodifiable an iterator.
+ * <p>
+ * This class provides features closed to {@link Collections#unmodifiableCollection(java.util.Collection)}
+ * but on iterators.
+ * <p>
+ * This iterator disables the use of the function {@link #remove()}.
+ * This iterator implementation is not a sized iterator; see
+ * {@link UnmodifiableSizedIterator} instead.
+ * 
+ * @param <M> is the type of element.
+ * @author $Author: sgalland$
+ * @version $FullVersion$
+ * @mavengroupid $GroupId$
+ * @mavenartifactid $ArtifactId$
+ * @see UnmodifiableSizedIterator
+ */
+public class UnmodifiableIterator<M>
+implements Iterator<M> {
+
+	private final Iterator<M> original;
+
+	/**
+	 * @param iterator
+	 */
+	public UnmodifiableIterator(Iterator<M> iterator) {
+		assert(iterator!=null);
+		this.original = iterator;
+	}
+	
+	/**
+	 * @param iterable
+	 */
+	public UnmodifiableIterator(Iterable<M> iterable) {
+		assert(iterable!=null);
+		this.original = iterable.iterator();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean hasNext() {
+		return this.original.hasNext();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public M next() {
+		return this.original.next();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final void remove() {
+		throw new UnsupportedOperationException();
+	}
+
+}

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/BMPFileFilter.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/BMPFileFilter.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/BMPFileFilter.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Microsoft and IBM Bitmap

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/BMPFileFilter_fr.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/BMPFileFilter_fr.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/BMPFileFilter_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Image Microsoft et IBM

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/DOTFileFilter.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/DOTFileFilter.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/DOTFileFilter.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Graphviz Dot Figure

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/DOTFileFilter_fr.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/DOTFileFilter_fr.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/DOTFileFilter_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Figure Dot Graphviz

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/EPSFileFilter.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/EPSFileFilter.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/EPSFileFilter.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Encapsuled PostScript

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/EPSFileFilter_fr.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/EPSFileFilter_fr.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/EPSFileFilter_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Encapsuled PostScript

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GIFFileFilter.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GIFFileFilter.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GIFFileFilter.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Graphics Interchange Format

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GIFFileFilter_fr.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GIFFileFilter_fr.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GIFFileFilter_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Image en format interop\xE9rable

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GMLFileFilter.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GMLFileFilter.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GMLFileFilter.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = GML File

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GMLFileFilter_fr.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GMLFileFilter_fr.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GMLFileFilter_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Fichier GML

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GXLFileFilter.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GXLFileFilter.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GXLFileFilter.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Graph eXchange Language File

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GXLFileFilter_fr.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GXLFileFilter_fr.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GXLFileFilter_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Document Graph eXchange Language

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GraphMLFileFilter.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GraphMLFileFilter.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GraphMLFileFilter.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = GraphML File

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GraphMLFileFilter_fr.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GraphMLFileFilter_fr.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/GraphMLFileFilter_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Fichier GraphML

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/ImageFileFilter.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/ImageFileFilter.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/ImageFileFilter.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2013 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Images

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/ImageFileFilter_fr.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/ImageFileFilter_fr.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/ImageFileFilter_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2013 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Images

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/JPEGFileFilter.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/JPEGFileFilter.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/JPEGFileFilter.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Joint Photographic Experts Group Image

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/JPEGFileFilter_fr.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/JPEGFileFilter_fr.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/JPEGFileFilter_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Image du Groupe d''Experts en Photographie

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/JavaFileFilter.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/JavaFileFilter.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/JavaFileFilter.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Java Source Code

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/JavaFileFilter_fr.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/JavaFileFilter_fr.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/JavaFileFilter_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Code Source Java

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/NGRFileFilter.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/NGRFileFilter.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/NGRFileFilter.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = NetEditor Graph

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/NGRFileFilter_fr.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/NGRFileFilter_fr.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/NGRFileFilter_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Graphe NetEditor

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/PDFFileFilter.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/PDFFileFilter.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/PDFFileFilter.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Portable Document Format

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/PDFFileFilter_fr.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/PDFFileFilter_fr.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/PDFFileFilter_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Document PDF

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/PNGFileFilter.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/PNGFileFilter.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/PNGFileFilter.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Portable Network Graphics

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/PNGFileFilter_fr.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/PNGFileFilter_fr.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/PNGFileFilter_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Image portable

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/SVGFileFilter.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/SVGFileFilter.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/SVGFileFilter.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Scalable Vector Graphics

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/SVGFileFilter_fr.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/SVGFileFilter_fr.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/SVGFileFilter_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Images vectorielles W3C

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/XMLFileFilter.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/XMLFileFilter.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/XMLFileFilter.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = XML File

Added: trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/XMLFileFilter_fr.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/XMLFileFilter_fr.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/io/filefilter/XMLFileFilter_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,20 @@
+# $Id$
+# 
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+FILE_FILTER_NAME = Fichier XML

Added: trunk/util/src/main/resources/org/arakhne/afc/util/MultiValue.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/util/MultiValue.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/util/MultiValue.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,22 @@
+# $Id$
+# 
+# Copyright (c) 2011, Multiagent Team, Laboratoire Systemes et Transports,
+#                     Universite de Technologie de Belfort-Montbeliard.
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+MULTIPLE_VALUES = Multiple values
\ No newline at end of file

Added: trunk/util/src/main/resources/org/arakhne/afc/util/MultiValue_fr.properties
===================================================================
--- trunk/util/src/main/resources/org/arakhne/afc/util/MultiValue_fr.properties	                        (rev 0)
+++ trunk/util/src/main/resources/org/arakhne/afc/util/MultiValue_fr.properties	2013-03-20 14:54:50 UTC (rev 388)
@@ -0,0 +1,22 @@
+# $Id$
+# 
+# Copyright (c) 2011, Multiagent Team, Laboratoire Systemes et Transports,
+#                     Universite de Technologie de Belfort-Montbeliard.
+# Copyright (C) 2012 Stephane GALLAND.
+# 
+# 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.
+# 
+# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# This program is free software; you can redistribute it and/or modify
+
+MULTIPLE_VALUES = Valeurs multiples
\ No newline at end of file


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/