diff options
Diffstat (limited to 'modules/scirenderer/src')
117 files changed, 18558 insertions, 0 deletions
diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/Canvas.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/Canvas.java new file mode 100755 index 000000000..167f38f58 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/Canvas.java @@ -0,0 +1,113 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer; + +import org.scilab.forge.scirenderer.buffers.BuffersManager; +import org.scilab.forge.scirenderer.picking.PickingManager; +import org.scilab.forge.scirenderer.renderer.RendererManager; +import org.scilab.forge.scirenderer.texture.TextureManager; + +import java.awt.Dimension; + +/** + * @author Pierre Lando + */ +public interface Canvas { + + /** + * Set this canvas main drawer. + * @param mainDrawer the new canvas main drawer. + */ + void setMainDrawer(Drawer mainDrawer); + + /** + * Return the canvas main drawer. + * @return the canvas main drawer. + */ + Drawer getMainDrawer(); + + /** + * Return the renderer manager. + * @return the renderer manager. + */ + RendererManager getRendererManager(); + + /** + * Return the buffers manager of this canvas. + * @return the buffers manager of this canvas. + */ + BuffersManager getBuffersManager(); + + /** + * Return the picking manager. + * @return the picking manager. + */ + PickingManager getPickingManager(); + + /** + * Texture manger getter. + * @return the texture manager. + */ + TextureManager getTextureManager(); + + /** + * Return the canvas width. + * @return the canvas width. + */ + int getWidth(); + + /** + * Return the canvas height. + * @return the canvas height. + */ + int getHeight(); + + /** + * Return the canvas dimension. + * @return the canvas dimension. + */ + Dimension getDimension(); + + /** + * Anti-aliasing level getter. + * - 0 for 1x + * - 1 for 2x + * - 2 for 4x + * - 3 for 8x + * - 4 for 16x + * @return the anti-aliasing level. + */ + public int getAntiAliasingLevel(); + + /** + * Anti-aliasing level setter. + * - 0 for 1x + * - 1 for 2x + * - 2 for 4x + * - 3 for 8x + * - 4 for 16x + * @param antiAliasingLevel the new level. + */ + public void setAntiAliasingLevel(int antiAliasingLevel); + + /** Ask the canvas to perform asynchronous drawing. */ + void redraw(); + + /** Ask the canvas to perform asynchronous drawing. */ + void redrawAndWait(); + + /** Wait until a drawing has been performed */ + void waitImage(); + + /** Destroy canvas : release Semaphore and associated threads */ + void destroy(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/Drawer.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/Drawer.java new file mode 100755 index 000000000..a1f31ff71 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/Drawer.java @@ -0,0 +1,29 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer; + +/** + * @author Pierre Lando + */ +public interface Drawer { + + /** + * Ask this drawer to draw with the given drawing tools. + * @param drawingTools the givens drawing tools. + */ + void draw(DrawingTools drawingTools); + + /** + * @return true if it is a 2D drawing + */ + boolean is2DView(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/DrawingTools.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/DrawingTools.java new file mode 100755 index 000000000..0223ea754 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/DrawingTools.java @@ -0,0 +1,138 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer; + +import org.scilab.forge.scirenderer.buffers.ElementsBuffer; +import org.scilab.forge.scirenderer.clipping.ClippingManager; +import org.scilab.forge.scirenderer.lightning.LightManager; +import org.scilab.forge.scirenderer.renderer.Renderer; +import org.scilab.forge.scirenderer.shapes.appearance.Appearance; +import org.scilab.forge.scirenderer.shapes.appearance.Color; +import org.scilab.forge.scirenderer.shapes.geometry.Geometry; +import org.scilab.forge.scirenderer.texture.AnchorPosition; +import org.scilab.forge.scirenderer.texture.Texture; +import org.scilab.forge.scirenderer.tranformations.TransformationManager; +import org.scilab.forge.scirenderer.tranformations.Vector3d; + +/** + * @author Pierre Lando + */ +public interface DrawingTools { + + /** + * Return the canvas where draw is done. + * @return the canvas where draw is done. + */ + Canvas getCanvas(); + + /** + * Return the transformation manager. + * @return the transformation manager. + */ + TransformationManager getTransformationManager(); + + /** + * Return the light manager. + * @return the light manager. + */ + LightManager getLightManager(); + + /** + * Return the clipping manager. + * @return the clipping manager. + */ + ClippingManager getClippingManager(); + + /** + * Clear the canvas with the given color. + * @param color the color used to clear the canvas. + */ + void clear(Color color); + + /** + * Clear the canvas with the given color. + * @param color the color used to clear the canvas. + */ + void clear(java.awt.Color color); + + /** + * Clear the depth buffer. + */ + void clearDepthBuffer(); + + /** + * Ask the given renderer to perform a draw. + * @param renderer the given renderer. + */ + void draw(Renderer renderer); + + /** + * Draw the given geometry with default appearance. + * @param geometry the geometry to draw. + * @throws SciRendererException if the draw is not possible. + */ + void draw(Geometry geometry) throws SciRendererException; + + /** + * Draw the given geometry. + * @param geometry the geometry to draw. + * @param appearance the appearance to use. + * @throws SciRendererException if the draw is not possible. + */ + void draw(Geometry geometry, Appearance appearance) throws SciRendererException; + + /** + * Draw the texture on XY plane in current coordinate. + * The texture is drawn on the rectangle [(0,0)-(texture width,texture height)]. + * @param texture the texture to drawn. + * @throws SciRendererException if the texture is not drawable. + */ + void draw(Texture texture) throws SciRendererException; + + /** + * Draw the given texture at all given position. + * @param texture the texture to draw. + * @param anchor the texture anchor position. + * @param positions the positions where the texture will be drawn. + * @throws SciRendererException if the texture is not drawable. + */ + void draw(Texture texture, AnchorPosition anchor, ElementsBuffer positions) throws SciRendererException; + + /** + * Draw the given texture at all given position with the given rotation angle. + * @param texture the texture to draw. + * @param anchor the texture anchor position. + * @param positions the positions where the texture will be drawn. + * @param rotationAngle the rotation angle. + * @throws SciRendererException if the texture is not drawable. + */ + void draw(Texture texture, AnchorPosition anchor, ElementsBuffer positions, int offset, int stride, double rotationAngle) throws SciRendererException; + + /** + * Draw the given texture at given position. + * @param texture the texture to draw. + * @param anchor the texture anchor position. + * @param position the position where the texture will be drawn. + * @throws SciRendererException if the texture is not drawable. + */ + void draw(Texture texture, AnchorPosition anchor, Vector3d position) throws SciRendererException; + + /** + * Draw the given texture at given position with the given rotation angle. + * @param texture the texture to draw. + * @param anchor the texture anchor position. + * @param position the position where the texture will be drawn. + * @param rotationAngle the rotation angle. + * @throws SciRendererException if the texture is not drawable. + */ + void draw(Texture texture, AnchorPosition anchor, Vector3d position, double rotationAngle) throws SciRendererException; +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/PackageInfo.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/PackageInfo.java new file mode 100755 index 000000000..d738fa9ff --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/PackageInfo.java @@ -0,0 +1,30 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer; + +/** + * This utility class give information about SciRenderer library. + * @author Pierre Lando + */ +public final class PackageInfo { + + /** + * The current version of the library. + */ + public static final String VERSION = "1.1.0"; + + /** + * Private constructor : this is an utility class. + */ + private PackageInfo() { + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/SciRendererException.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/SciRendererException.java new file mode 100755 index 000000000..c01e60d33 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/SciRendererException.java @@ -0,0 +1,53 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer; + +/** + * The SciRendererException is the superclass of all Exception from SciRenderer. + * + * @author Pierre Lando + */ +@SuppressWarnings(value = { "serial" }) +public class SciRendererException extends Exception { + + /** + * Constructs a new exception with null as its detail message. + */ + public SciRendererException() { + super(); + } + + /** + * Constructs a new exception with the specified detail message. + * @param message - the detail message. The detail message is saved for later retrieval by the #Throwable.getMessage() method. + */ + public SciRendererException(String message) { + super(message); + } + + /** + * Constructs a new exception with the specified detail message and cause. + * @param message - the detail message (which is saved for later retrieval by the Throwable.getMessage() method). + * @param cause - the cause (which is saved for later retrieval by the Throwable.getCause() method). (A null value is permitted, and indicates that the cause is nonexistent or unknown.) + */ + public SciRendererException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail message of (cause==null ? null : cause.toString()) (which typically contains the class and detail message of cause). + * @param cause - the cause (which is saved for later retrieval by the Throwable.getCause() method). (A null value is permitted, and indicates that the cause is nonexistent or unknown.) + */ + public SciRendererException(Throwable cause) { + super(cause); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/buffers/BuffersManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/buffers/BuffersManager.java new file mode 100755 index 000000000..9808e4335 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/buffers/BuffersManager.java @@ -0,0 +1,45 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.buffers; + +import java.util.Collection; + +/** + * Interface for a buffers manager. + * @author Pierre Lando + */ +public interface BuffersManager { + + /** + * Create an elements buffer. + * @return a new elements buffer. + */ + ElementsBuffer createElementsBuffer(); + + /** + * Create an indices buffer. + * @return a new indices buffer. + */ + IndicesBuffer createIndicesBuffer(); + + /** + * Release all resources used by the given buffer. + * @param buffer the given buffer. + */ + void dispose(DataBuffer buffer); + + /** + * Release all resources used by the given buffers. + * @param buffers the given buffers. + */ + void dispose(Collection <? extends DataBuffer > buffers); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/buffers/DataBuffer.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/buffers/DataBuffer.java new file mode 100755 index 000000000..232605071 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/buffers/DataBuffer.java @@ -0,0 +1,35 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.buffers; + +import java.nio.Buffer; + +/** + * Interface for a data buffer. + * @author Pierre Lando + */ +public interface DataBuffer { + + /** + * Return the data. + * @return the data. + */ + Buffer getData(); + + /** + * Return the number of elements. + * @return the number of elements. + */ + int getSize(); + + void clear(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/buffers/ElementsBuffer.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/buffers/ElementsBuffer.java new file mode 100755 index 000000000..e763fe7bd --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/buffers/ElementsBuffer.java @@ -0,0 +1,52 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.buffers; + +import java.nio.FloatBuffer; + +/** + * @author Pierre Lando + */ +public interface ElementsBuffer extends DataBuffer { + + /** + * Set the data. + * @param data the new data. + * @param elementSize the size of data elements. + */ + void setData(float[] data, int elementSize); + + /** + * Set the data. + * @param data the new data. + * @param elementSize the size of data elements. + */ + void setData(Float[] data, int elementSize); + + /** + * Set the data. + * @param data the new data. + * @param elementsSize the size of data elements. + */ + void setData(FloatBuffer data, int elementsSize); + + @Override + FloatBuffer getData(); + + /** + * Return the number of coordinate for one element. + * @return the number of coordinate for one element. + */ + int getElementsSize(); + + void clear(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/buffers/IndicesBuffer.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/buffers/IndicesBuffer.java new file mode 100755 index 000000000..416cabe9f --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/buffers/IndicesBuffer.java @@ -0,0 +1,46 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.buffers; + +import java.nio.IntBuffer; +import java.util.Collection; + +/** + * @author Pierre Lando + */ +public interface IndicesBuffer extends DataBuffer { + + /** + * Set the data + * @param indices the new data. + */ + void setData(int[] indices); + + + /** + * Set the data + * @param indices the new data. + */ + void setData(Collection<Integer> indices); + + /** + * Set the data. + * @param indexBuffer the new data. + */ + void setData(IntBuffer indexBuffer); + + @Override + IntBuffer getData(); + + + void clear(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/clipping/ClippingManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/clipping/ClippingManager.java new file mode 100755 index 000000000..e8ef4cd0d --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/clipping/ClippingManager.java @@ -0,0 +1,42 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.clipping; + +/** + * Clipping manager interface. + * + * @author Pierre Lando + */ +public interface ClippingManager { + + /** + * Return the number of available clipping plane. + * At least 6 clipping plane are supported. + * @return the number of available clipping plane. + */ + int getClippingPlaneNumber(); + + /** + * Return the i-th clipping plane. + * {@code null} is returned if i is not a valid index. + * @param i the given index. + * @return the i-th clipping plane. + */ + ClippingPlane getClippingPlane(int i); + + /** + * Disable all clipping plane. + */ + void disableClipping(); + + // TODO add an AABB quick call. (with transformation ?) +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/clipping/ClippingPlane.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/clipping/ClippingPlane.java new file mode 100755 index 000000000..9bf3a9b64 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/clipping/ClippingPlane.java @@ -0,0 +1,63 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.clipping; + +import org.scilab.forge.scirenderer.tranformations.Transformation; +import org.scilab.forge.scirenderer.tranformations.Vector4d; + +/** + * @author Pierre Lando + */ +public interface ClippingPlane { + + /** + * Return the status of this clipping plane. + * @return the status of this clipping plane. + */ + boolean isEnable(); + + /** + * Set the status of this clipping plane. + * @param isEnable new status of this clipping plane. + */ + void setEnable(boolean isEnable); + + /** + * Set the clipping plane equation. + * @param v the plane coordinate. + */ + void setEquation(Vector4d v); + + /** + * Return the plane equation. + * @return the plane equation. + */ + Vector4d getEquation(); + + /** + * Set the coordinate transformation for the plane. + * @param transformation the new coordinate transformation for the plane. + */ + void setTransformation(Transformation transformation); + + /** + * Return the coordinate transformation for the plane. + * @return the coordinate transformation for the plane. + */ + Transformation getTransformation(); + + /** + * Return the clipping plane index. + * @return the clipping plane index.s + */ + int getIndex(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/data/AbstractDataProvider.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/data/AbstractDataProvider.java new file mode 100755 index 000000000..34bf7b5c2 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/data/AbstractDataProvider.java @@ -0,0 +1,68 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.data; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Pierre Lando + * @param <DataUserType> The type of data user. + */ +public abstract class AbstractDataProvider<DataUserType extends DataUser> implements DataProvider<DataUserType> { + + private final Set<DataUserType> users = Collections.synchronizedSet(new HashSet<DataUserType>()); + + @Override + public final void removeDataUser(DataUserType dataUser) { + users.remove(dataUser); + } + + @Override + public final void addDataUser(DataUserType dataUser) { + users.add(dataUser); + } + + /** + * Notify all registered data users for a data update. + */ + protected final void fireUpdate() { + for (DataUserType dataUser : users) { + dataUser.dataUpdated(); + } + } + + /** + * Convert given value to a byte. + * double in [0, 1] are mapped to [0x00, 0xFF]. + * @param value the given value. + * @return the byte corresponding to the given value. + */ + protected byte toByte(double value) { + return (byte) (((int) (value * 255)) & 0xFF); + } + + /** + * Convert given value to a byte. + * double in [0, 1] are mapped to [0x00, 0xFF]. + * @param values the given value. + * @return the byte corresponding to the given value. + */ + protected byte[] toByte(float[] values) { + byte bytes[] = new byte[values.length]; + for (int i = 0; i < values.length; i++) { + bytes[i] = toByte(values[i]); + } + return bytes; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/data/DataProvider.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/data/DataProvider.java new file mode 100755 index 000000000..2ebf73cfb --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/data/DataProvider.java @@ -0,0 +1,37 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.data; + +/** + * @author Pierre Lando + * @param <DataUserType> The type of data user. + */ +public interface DataProvider<DataUserType extends DataUser> { + + /** + * Remove a data user. + * @param dataUser the data user to remove. + */ + void removeDataUser(DataUserType dataUser); + + /** + * Add a data user. + * @param texture the data user to add. + */ + void addDataUser(DataUserType texture); + + /** + * Data provider validity getter. + * @return the validity if this data provider. + */ + boolean isValid(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/data/DataUser.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/data/DataUser.java new file mode 100755 index 000000000..7045caafd --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/data/DataUser.java @@ -0,0 +1,23 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.data; + +/** + * @author Pierre Lando + */ +public interface DataUser { + + /** + * Notify for data update. + */ + void dataUpdated(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/G2DCanvas.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/G2DCanvas.java new file mode 100755 index 000000000..a130a1dca --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/G2DCanvas.java @@ -0,0 +1,189 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d; + +import java.awt.Dimension; +import java.awt.Graphics2D; + +import org.scilab.forge.scirenderer.Canvas; +import org.scilab.forge.scirenderer.Drawer; +import org.scilab.forge.scirenderer.DrawingTools; +import org.scilab.forge.scirenderer.implementation.g2d.buffers.G2DBuffersManager; +import org.scilab.forge.scirenderer.implementation.g2d.motor.Motor3D; +import org.scilab.forge.scirenderer.implementation.g2d.renderer.G2DRendererManager; +import org.scilab.forge.scirenderer.implementation.g2d.texture.G2DTextureManager; +import org.scilab.forge.scirenderer.picking.PickingManager; +import org.scilab.forge.scirenderer.picking.PickingTask; + +/** + * G2D implementation of a Canvas. + * + * @author Calixte DENIZET + */ +public final class G2DCanvas implements Canvas { + + private final G2DDrawingTools drawingTools; + private final G2DBuffersManager buffersManager; + private final G2DRendererManager rendererManager; + private final G2DTextureManager textureManager; + + private final Motor3D motor; + private final Dimension dimension; + + private boolean drawEnabled = true; + + /** The anti-aliasing level */ + private int antiAliasingLevel = 0; + + private static final PickingManager PICKINGMANAGER = new PickingManager() { + + @Override + public void addPickingTask(PickingTask pickingTask) { } + }; + + /** + * The current mainDrawer. + */ + private Drawer mainDrawer; + + /** + * Default constructor. + * @param autoDrawable the G2D autoDrawable this canvas depend on. + */ + G2DCanvas(Graphics2D g2d, int width, int height) { + this.dimension = new Dimension(width, height); + this.motor = new Motor3D(this, g2d, dimension); + + buffersManager = new G2DBuffersManager(); + rendererManager = new G2DRendererManager(); + drawingTools = new G2DDrawingTools(this); + motor.setClippingPlanes(drawingTools.getClippingManager().getClippingPlanes()); + textureManager = new G2DTextureManager(this); + } + + public DrawingTools getDrawingTools() { + return drawingTools; + } + + public void setGraphics(Graphics2D g2d, int width, int height) { + this.dimension.width = width; + this.dimension.height = height; + this.motor.setGraphics(g2d); + } + + // Implementation of getter & setter from Canvas. + + @Override + public void setMainDrawer(Drawer mainDrawer) { + this.mainDrawer = mainDrawer; + } + + @Override + public Drawer getMainDrawer() { + return mainDrawer; + } + + @Override + public G2DRendererManager getRendererManager() { + return rendererManager; + } + + @Override + public G2DBuffersManager getBuffersManager() { + return buffersManager; + } + + @Override + public PickingManager getPickingManager() { + return PICKINGMANAGER; + } + + @Override + public G2DTextureManager getTextureManager() { + return textureManager; + } + + @Override + public int getWidth() { + return dimension.width; + } + + @Override + public int getHeight() { + return dimension.height; + } + + @Override + public Dimension getDimension() { + return dimension; + } + + @Override + public int getAntiAliasingLevel() { + return antiAliasingLevel; + } + + @Override + public void setAntiAliasingLevel(int antiAliasingLevel) { + this.antiAliasingLevel = antiAliasingLevel; + } + + @Override + public void redraw() { + draw(); + } + + @Override + public void redrawAndWait() { + redraw(); + } + + @Override + public void waitImage() { + } + + // G2DCanvas specific getter. + + public void disableDraw() { + drawEnabled = false; + } + + public void enableDraw() { + drawEnabled = true; + } + + public void draw() { + if (drawEnabled) { + try { + mainDrawer.draw(drawingTools); + getMotor3D().setAntialiased(antiAliasingLevel != 0); + getMotor3D().draw(); + } catch (Exception e) { + System.out.println(e + "::::" + mainDrawer); + e.printStackTrace(); + } + } + } + + /** + * Return the OpenGl context. + * @return the OpenGl context. + */ + public Motor3D getMotor3D() { + return motor; + } + + @Override + public void destroy() { + getMotor3D().clean(); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/G2DCanvasFactory.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/G2DCanvasFactory.java new file mode 100755 index 000000000..673badec5 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/G2DCanvasFactory.java @@ -0,0 +1,37 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d; + +import java.awt.Graphics2D; + +/** + * @author Pierre Lando + */ +public final class G2DCanvasFactory { + + /** + * Private constructor. + * This is an utility class. + */ + private G2DCanvasFactory() { + + } + + /** + * Create a canvas from an auto drawable object. + * @param autoDrawable the auto drawable object. + * @return a canvas based on the given auto drawable object. + */ + public static G2DCanvas createCanvas(Graphics2D g2d, int width, int height) { + return new G2DCanvas(g2d, width, height); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/G2DDrawingTools.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/G2DDrawingTools.java new file mode 100755 index 000000000..c8594150c --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/G2DDrawingTools.java @@ -0,0 +1,147 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d; + +import org.scilab.forge.scirenderer.DrawingTools; +import org.scilab.forge.scirenderer.SciRendererException; +import org.scilab.forge.scirenderer.buffers.ElementsBuffer; +import org.scilab.forge.scirenderer.lightning.LightManager; +import org.scilab.forge.scirenderer.renderer.Renderer; +import org.scilab.forge.scirenderer.shapes.appearance.Appearance; +import org.scilab.forge.scirenderer.shapes.appearance.Color; +import org.scilab.forge.scirenderer.shapes.geometry.Geometry; +import org.scilab.forge.scirenderer.texture.AnchorPosition; +import org.scilab.forge.scirenderer.texture.Texture; +import org.scilab.forge.scirenderer.tranformations.TransformationManager; +import org.scilab.forge.scirenderer.tranformations.TransformationManagerImpl; +import org.scilab.forge.scirenderer.tranformations.TransformationManagerListener; +import org.scilab.forge.scirenderer.tranformations.Vector3d; + +import org.scilab.forge.scirenderer.implementation.g2d.G2DCanvas; +import org.scilab.forge.scirenderer.implementation.g2d.clipping.G2DClippingManager; +import org.scilab.forge.scirenderer.implementation.g2d.motor.Motor3D; +import org.scilab.forge.scirenderer.implementation.g2d.lighting.G2DLightManager; + +/** + * + * JoGl implementation of the DrawingTools. + * + * @author Calixte DENIZET + */ +public class G2DDrawingTools implements DrawingTools { + + private final TransformationManager transformationManager; + private final G2DLightManager lightManager; + private final G2DClippingManager clippingManager; + private final G2DCanvas g2dCanvas; + + /** + * Default constructor. + * @param canvas the canvas where this drawing tools live. + */ + G2DDrawingTools(G2DCanvas canvas) { + this.transformationManager = new TransformationManagerImpl(canvas); + this.lightManager = new G2DLightManager(this); + this.clippingManager = new G2DClippingManager(this); + this.g2dCanvas = canvas; + + transformationManager.addListener(new TransformationManagerListener() { + @Override + public void transformationChanged(TransformationManager transformationManager) { + if (transformationManager.isUsingSceneCoordinate()) { + g2dCanvas.getMotor3D().setTransformation(transformationManager.getG2DProjection(), transformationManager.getG2DSingleProjection()); + } else { + g2dCanvas.getMotor3D().setTransformation(transformationManager.getG2DWindowProjection(), transformationManager.getG2DSingleProjection()); + } + } + }); + } + + public Motor3D getMotor3D() { + return g2dCanvas.getMotor3D(); + } + + @Override + public G2DCanvas getCanvas() { + return g2dCanvas; + } + + @Override + public TransformationManager getTransformationManager() { + return transformationManager; + } + + @Override + public LightManager getLightManager() { + return lightManager; + } + + @Override + public G2DClippingManager getClippingManager() { + return clippingManager; + } + + @Override + public void clear(Color color) { + g2dCanvas.getMotor3D().reset(color); + } + + @Override + public void clear(java.awt.Color color) { + g2dCanvas.getMotor3D().reset(color); + } + + @Override + public void clearDepthBuffer() { + g2dCanvas.getMotor3D().clearDepth(); + } + + @Override + public void draw(Renderer renderer) { + g2dCanvas.getRendererManager().draw(this, renderer); + } + + @Override + public void draw(Geometry geometry) throws SciRendererException { + g2dCanvas.getMotor3D().draw(this, geometry, Appearance.getDefault()); + } + + @Override + public void draw(Geometry geometry, Appearance appearance) throws SciRendererException { + g2dCanvas.getMotor3D().draw(this, geometry, appearance); + } + + @Override + public void draw(Texture texture) throws SciRendererException { + g2dCanvas.getTextureManager().draw(this, texture); + } + + @Override + public void draw(Texture texture, AnchorPosition anchor, ElementsBuffer positions) { + g2dCanvas.getMotor3D().draw(this, texture, anchor, positions, 0, 1, 0); + } + + @Override + public void draw(Texture texture, AnchorPosition anchor, ElementsBuffer positions, int offset, int stride, double rotationAngle) { + g2dCanvas.getMotor3D().draw(this, texture, anchor, positions, offset, stride, rotationAngle); + } + + @Override + public void draw(Texture texture, AnchorPosition anchor, Vector3d position) { + g2dCanvas.getMotor3D().draw(this, texture, anchor, position, 0); + } + + @Override + public void draw(Texture texture, AnchorPosition anchor, Vector3d position, double rotationAngle) { + g2dCanvas.getMotor3D().draw(this, texture, anchor, position, rotationAngle); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/buffers/G2DBuffersManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/buffers/G2DBuffersManager.java new file mode 100755 index 000000000..00dbec8cf --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/buffers/G2DBuffersManager.java @@ -0,0 +1,111 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.buffers; + +import org.scilab.forge.scirenderer.buffers.BuffersManager; +import org.scilab.forge.scirenderer.buffers.DataBuffer; +import org.scilab.forge.scirenderer.buffers.ElementsBuffer; +import org.scilab.forge.scirenderer.buffers.IndicesBuffer; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Pierre Lando + */ +public final class G2DBuffersManager implements BuffersManager { + + /** + * Set off current buffers. + */ + private final Set<DataBuffer> buffers = new HashSet<DataBuffer>(); + + // todo : est ce utile d'avoir buffers... pourquoi garder une reference la-dessus + // peut etre pr savoir qu'un buffer vient de ce manager et pas d'un autre. + + /** + * Default constructor. + */ + public G2DBuffersManager() { + } + + @Override + public ElementsBuffer createElementsBuffer() { + G2DElementsBuffer newBuffer = new G2DElementsBuffer(); + buffers.add(newBuffer); + return newBuffer; + } + + @Override + public IndicesBuffer createIndicesBuffer() { + G2DIndicesBuffer newBuffer = new G2DIndicesBuffer(); + buffers.add(newBuffer); + return newBuffer; + } + + @Override + public void dispose(DataBuffer buffer) { + DataBuffer localBuffer = getLocalBuffer(buffer); + if (localBuffer != null) { + localBuffer.clear(); + buffers.remove(localBuffer); + } + } + + @Override + public void dispose(Collection <? extends DataBuffer > buffers) { + for (DataBuffer buffer : buffers) { + dispose(buffer); + } + } + + /** + * This method check buffer to be from here. + * @param buffer the given buffer. + * @return the G2D instance of the buffer. + */ + private DataBuffer getLocalBuffer(DataBuffer buffer) { + if (buffers.contains(buffer)) { + return buffer; + } + + return null; + } + + + /** + * This method check buffer to be from here. + * @param buffer the given buffer. + * @return the G2D instance of the buffer. + */ + private IndicesBuffer getLocalIndicesBuffer(IndicesBuffer buffer) { + if (buffer instanceof G2DIndicesBuffer && buffers.contains(buffer)) { + return buffer; + } + + return null; + } + + /** + * This method check buffer to be from here. + * @param buffer the given buffer. + * @return the G2D instance of the buffer. + */ + private ElementsBuffer getLocalElementsBuffer(ElementsBuffer buffer) { + if (buffer instanceof G2DElementsBuffer && buffers.contains(buffer)) { + return buffer; + } + + return null; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/buffers/G2DElementsBuffer.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/buffers/G2DElementsBuffer.java new file mode 100755 index 000000000..d8f0e17f3 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/buffers/G2DElementsBuffer.java @@ -0,0 +1,209 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - ScilabEnterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.buffers; + +import org.scilab.forge.scirenderer.buffers.DataBuffer; +import org.scilab.forge.scirenderer.buffers.ElementsBuffer; + +import java.nio.FloatBuffer; + +/** + * @author Calixte DENIZET + */ +@SuppressWarnings(value = { "serial" }) +public class G2DElementsBuffer implements DataBuffer, ElementsBuffer { + + /** + * The current size of one element. + */ + public static final int ELEMENT_SIZE = 4; + + /** + * The default vertex. + */ + private static final float[] DEFAULT_VERTEX = new float[] {0, 0, 0, 1}; + + /** + * the data this buffer contain. + */ + private FloatBuffer data; + private final Object mutex; + + /** + * Default constructor. + * The constructor is package : only {@link G2DBuffersManager} can instantiate this object. + */ + G2DElementsBuffer() { + mutex = new Object(); + data = null; + } + + @Override + public void setData(float[] newData, int elementSize) { + // Check the given vertex size + if ((elementSize < 1) || (elementSize > ELEMENT_SIZE)) { + throw new BadElementSizeException(elementSize, 1, ELEMENT_SIZE); + } + + int verticesNumber = newData.length / elementSize; + //FloatBuffer buffer = BufferUtil.newFloatBuffer(ELEMENT_SIZE * verticesNumber); + FloatBuffer buffer = FloatBuffer.allocate(ELEMENT_SIZE * verticesNumber); + buffer.rewind(); + + + // Fill buffer with given data. + // Missing coordinate are filled with the 'DEFAULT_VERTEX' ones. + int k = 0; + for (int i = 0; i < verticesNumber; i++) { + for (int j = 0; j < ELEMENT_SIZE; j++) { + if (j < elementSize) { + buffer.put(newData[k++]); + } else { + buffer.put(DEFAULT_VERTEX[j]); + } + } + } + + buffer.rewind(); + setData(buffer); + } + + @Override + public void setData(Float[] newData, int elementSize) { + + // Check the given vertex size + if ((elementSize < 1) || (elementSize > ELEMENT_SIZE)) { + throw new BadElementSizeException(elementSize, 1, ELEMENT_SIZE); + } + + int verticesNumber = newData.length / elementSize; + //FloatBuffer buffer = BufferUtil.newFloatBuffer(ELEMENT_SIZE * verticesNumber); + FloatBuffer buffer = FloatBuffer.allocate(ELEMENT_SIZE * verticesNumber); + buffer.rewind(); + + + // Fill buffer with given data. + // Missing coordinate are filled with the 'DEFAULT_VERTEX' ones. + int k = 0; + for (int i = 0; i < verticesNumber; i++) { + for (int j = 0; j < ELEMENT_SIZE; j++) { + if (j < elementSize) { + buffer.put(newData[k++]); + } else { + buffer.put(DEFAULT_VERTEX[j]); + } + } + } + + buffer.rewind(); + setData(buffer); + } + + @Override + public void setData(FloatBuffer newData, int elementsSize) { + // Check the given vertex size + if ((elementsSize < 1) || (elementsSize > ELEMENT_SIZE)) { + throw new BadElementSizeException(elementsSize, 1, ELEMENT_SIZE); + } + + if (elementsSize == 4) { + // No need to complete buffer. + if (newData != null) { + newData.rewind(); + } + setData(newData); + return; + } + + int verticesNumber = newData.limit() / elementsSize; + //FloatBuffer buffer = BufferUtil.newFloatBuffer(ELEMENT_SIZE * verticesNumber); + FloatBuffer buffer = FloatBuffer.allocate(ELEMENT_SIZE * verticesNumber); + buffer.rewind(); + + // Fill buffer with given data. + // Missing coordinate are filled with the 'DEFAULT_VERTEX' ones. + newData.rewind(); + for (int i = 0; i < verticesNumber; i++) { + for (int j = 0; j < ELEMENT_SIZE; j++) { + if (j < elementsSize) { + buffer.put(newData.get()); + } else { + buffer.put(DEFAULT_VERTEX[j]); + } + } + } + + buffer.rewind(); + setData(buffer); + } + + @Override + public int getSize() { + synchronized (mutex) { + if (data == null) { + return 0; + } else { + return data.limit() / ELEMENT_SIZE; + } + } + } + + @Override + public int getElementsSize() { + return ELEMENT_SIZE; + } + + @Override + public FloatBuffer getData() { + synchronized (mutex) { + if (data != null) { + return data; + } else { + return null; + } + } + } + + /** + * Really set the data. + * @param data the new data. + */ + private void setData(FloatBuffer data) { + synchronized (mutex) { + this.data = data; + } + } + + /** + * A specific runtime exception for bad elements size. + */ + private static class BadElementSizeException extends RuntimeException { + + /** + * Default constructor. + * @param size the size given for elements. + * @param min the minimum possible size. + * @param max the upper bound of possible size (excluded of possible size). + */ + public BadElementSizeException(int size, int min, int max) { + super("Bad vertex elements size : " + size + ". Should be in [" + min + ", " + (max - 1) + "]"); + } + } + + @Override + public void clear() { + if (data != null) { + data.clear(); + } + data = null; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/buffers/G2DIndicesBuffer.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/buffers/G2DIndicesBuffer.java new file mode 100755 index 000000000..9504c2937 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/buffers/G2DIndicesBuffer.java @@ -0,0 +1,89 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.buffers; + +import org.scilab.forge.scirenderer.buffers.DataBuffer; +import org.scilab.forge.scirenderer.buffers.IndicesBuffer; + +import java.nio.IntBuffer; +import java.util.Collection; + +/** + * @author Calixte DENIZET + */ +public class G2DIndicesBuffer implements IndicesBuffer, DataBuffer { + + /** + * the data this buffer contain. + */ + private IntBuffer data; + + /** + * Default constructor. + * The constructor is package : only {@link G2DBuffersManager} can instantiate this object. + */ + G2DIndicesBuffer() { + data = null; + } + + @Override + public void setData(int[] indices) { + //IntBuffer buffer = BufferUtil.newIntBuffer(indices.length); + IntBuffer buffer = IntBuffer.allocate(indices.length); + buffer.rewind(); + buffer.put(indices); + buffer.rewind(); + this.data = buffer; + } + + @Override + public void setData(Collection<Integer> indices) { + IntBuffer buffer = IntBuffer.allocate(indices.size()); + buffer.rewind(); + for (int index : indices) { + buffer.put(index); + } + buffer.rewind(); + this.data = buffer; + } + + @Override + public void setData(IntBuffer indexBuffer) { + //IntBuffer buffer = BufferUtil.newIntBuffer(indexBuffer.limit()); + IntBuffer buffer = IntBuffer.allocate(indexBuffer.limit()); + buffer.rewind(); + indexBuffer.rewind(); + buffer.put(indexBuffer); + buffer.rewind(); + indexBuffer.rewind(); + this.data = buffer; + } + + @Override + public int getSize() { + if (data == null) { + return 0; + } else { + return data.limit(); + } + } + + @Override + public IntBuffer getData() { + return data.asReadOnlyBuffer(); + } + + public void clear() { + data.clear(); + data = null; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/clipping/G2DClippingManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/clipping/G2DClippingManager.java new file mode 100755 index 000000000..2dbaa0b2f --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/clipping/G2DClippingManager.java @@ -0,0 +1,74 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2011-2012 - Scilab Enterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.clipping; + +import java.util.ArrayList; +import java.util.List; + +import org.scilab.forge.scirenderer.clipping.ClippingManager; +import org.scilab.forge.scirenderer.clipping.ClippingPlane; +import org.scilab.forge.scirenderer.implementation.g2d.G2DDrawingTools; + +/** + * @author Calixte DENIZET + */ +public class G2DClippingManager implements ClippingManager { + + /** + * Used drawing tools. + */ + private final G2DDrawingTools drawingTools; + + /** + * Clipping planes array. + */ + private final List<ClippingPlane> clippingPlanes; + + /** + * Default constructor. + * @param drawingTools used drawing tools. + */ + public G2DClippingManager(G2DDrawingTools drawingTools) { + this.drawingTools = drawingTools; + this.clippingPlanes = new ArrayList<ClippingPlane>(6); + } + + public List<ClippingPlane> getClippingPlanes() { + return clippingPlanes; + } + + @Override + public int getClippingPlaneNumber() { + return Integer.MAX_VALUE; + } + + @Override + public ClippingPlane getClippingPlane(int i) { + if (i < 0 || i >= getClippingPlaneNumber()) { + return null; + } else { + if (i >= clippingPlanes.size() || clippingPlanes.get(i) == null) { + clippingPlanes.add(i, new G2DClippingPlane(i, drawingTools)); + } + return clippingPlanes.get(i); + } + } + + @Override + public void disableClipping() { + for (ClippingPlane clippingPlane : clippingPlanes) { + if (clippingPlane != null) { + clippingPlane.setEnable(false); + } + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/clipping/G2DClippingPlane.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/clipping/G2DClippingPlane.java new file mode 100755 index 000000000..134aa2f3a --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/clipping/G2DClippingPlane.java @@ -0,0 +1,109 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.clipping; + +import org.scilab.forge.scirenderer.clipping.ClippingPlane; +import org.scilab.forge.scirenderer.tranformations.Transformation; +import org.scilab.forge.scirenderer.tranformations.TransformationFactory; +import org.scilab.forge.scirenderer.tranformations.Vector4d; +import org.scilab.forge.scirenderer.implementation.g2d.G2DDrawingTools; + +/** + * @author Pierre Lando + */ +public class G2DClippingPlane implements ClippingPlane { + + /** + * GL index of this clipping plane. + */ + private final int index; + + /** + * Clipping plane enabled status. + */ + private boolean isEnable; + + private G2DDrawingTools drawingTools; + + /** + * Clipping plane equation look like: {@code x*a + y*b + z*c + d = 0}. + * Where {@code equation} is {@code [a, b, c, d]}. + */ + private Vector4d equation = new Vector4d(0, 0, 0, 0); + private Vector4d projectedEquation = new Vector4d(0, 0, 0, 0); + private Transformation transformation = TransformationFactory.getIdentity(); + private Transformation projectionTransf; + private boolean updated; + + /** + * Default constructor. + * @param index the id of this clipping plane. + */ + public G2DClippingPlane(int index, G2DDrawingTools drawingTools) { + this.isEnable = false; + this.index = index; + this.drawingTools = drawingTools; + } + + @Override + public boolean isEnable() { + return isEnable; + } + + @Override + public void setEnable(boolean isEnable) { + this.isEnable = isEnable; + } + + @Override + public void setEquation(Vector4d v) { + Transformation t = drawingTools.getTransformationManager().getModelViewStack().peek(); + if (t != null) { + equation = getTransformedEquation(v, t); + } + } + + @Override + public Vector4d getEquation() { + Transformation t = drawingTools.getMotor3D().getCurrentSingleTransformation(); + if (t != null && projectionTransf != t) { + projectionTransf = t; + projectedEquation = getTransformedEquation(equation, t); + } + + return projectedEquation; + } + + @Override + public void setTransformation(Transformation transformation) { } + + @Override + public Transformation getTransformation() { + return transformation; + } + + @Override + public int getIndex() { + return index; + } + + private static final Vector4d getTransformedEquation(Vector4d equation, Transformation transf) { + double[] matrix = transf.getInverseTransformation().getMatrix(); + double[] v = equation.getData(); + double x = matrix[0] * v[0] + matrix[1] * v[1] + matrix[2] * v[2] + matrix[3] * v[3]; + double y = matrix[4] * v[0] + matrix[5] * v[1] + matrix[6] * v[2] + matrix[7] * v[3]; + double z = matrix[8] * v[0] + matrix[9] * v[1] + matrix[10] * v[2] + matrix[11] * v[3]; + double w = matrix[12] * v[0] + matrix[13] * v[1] + matrix[14] * v[2] + matrix[15] * v[3]; + + return new Vector4d(x, y, z, w); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/lighting/G2DLight.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/lighting/G2DLight.java new file mode 100755 index 000000000..b983fff49 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/lighting/G2DLight.java @@ -0,0 +1,144 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2013 - Pedro SOUZA + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.lighting; + + +import org.scilab.forge.scirenderer.lightning.Light; +import org.scilab.forge.scirenderer.shapes.appearance.Color; +import org.scilab.forge.scirenderer.tranformations.Vector3d; + +/** + * @author Pedro SOUZA + */ +public class G2DLight implements Light { + + private int index; + private boolean isEnable; + private Color ambientColor = new Color(0, 0, 0); + private Color diffuseColor = new Color(0, 0, 0); + private Color specularColor = new Color(0, 0, 0); + private Vector3d position = new Vector3d(0, 0, 0); + private Vector3d spotDirection = new Vector3d(0, 0, -1); + private Vector3d direction = new Vector3d(0, 0, 0); + private float spotAngle = 180; + private boolean isDirectional = false; + + + public G2DLight(int index) { + this.index = index; + } + + @Override + public boolean isEnable() { + return isEnable; + } + + @Override + public void setEnable(boolean enable) { + if (enable != isEnable) { + isEnable = enable; + } + } + + @Override + public Color getAmbientColor() { + return ambientColor; + } + + @Override + public void setAmbientColor(Color color) { + if (color != null) { + ambientColor = color; + } + } + + @Override + public Color getDiffuseColor() { + return diffuseColor; + } + + @Override + public void setDiffuseColor(Color color) { + if (color != null) { + diffuseColor = color; + } + } + + @Override + public Color getSpecularColor() { + return specularColor; + } + + @Override + public void setSpecularColor(Color color) { + if (color != null) { + specularColor = color; + } + } + + @Override + public Vector3d getPosition() { + return position; + } + + @Override + public void setPosition(Vector3d position) { + if (position != null) { + isDirectional = false; + this.position = position; + } + } + + public Vector3d getDirection() { + return direction; + } + + public void setDirection(Vector3d direction) { + if (direction != null) { + isDirectional = true; + this.direction = direction.getNormalized(); + } + } + + @Override + public Vector3d getSpotDirection() { + return spotDirection; + } + + @Override + public void setSpotDirection(Vector3d spotDirection) { + if (spotDirection != null) { + this.spotDirection = spotDirection; + } + } + + @Override + public float getSpotAngle() { + return spotAngle; + } + + @Override + public void setSpotAngle(float angle) { + if (angle != spotAngle) { + spotAngle = angle; + } + } + + @Override + public int getIndex() { + return index; + } + + public boolean isPoint() { + return !isDirectional; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/lighting/G2DLightManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/lighting/G2DLightManager.java new file mode 100755 index 000000000..c8315edff --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/lighting/G2DLightManager.java @@ -0,0 +1,140 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2013 - Pedro SOUZA + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.lighting; + +import org.scilab.forge.scirenderer.lightning.Light; +import org.scilab.forge.scirenderer.lightning.LightManager; +import org.scilab.forge.scirenderer.shapes.appearance.Material; +import org.scilab.forge.scirenderer.implementation.g2d.G2DDrawingTools; +import org.scilab.forge.scirenderer.tranformations.Transformation; +import org.scilab.forge.scirenderer.tranformations.Vector3f; + + + +/** + * @author Pedro SOUZA + */ +public class G2DLightManager implements LightManager { + + /** + * The drawing tools. + */ + private final G2DDrawingTools drawingTools; + + /** + * The lights. + */ + private final G2DLight[] lights; + + /** + * The current lightning status. + */ + private boolean isLightningEnable = DEFAULT_LIGHTNING_STATUS; + + private Material material; + + /** + * Default constructor. + * @param drawingTools the drawing tools. + */ + public G2DLightManager(G2DDrawingTools drawingTools) { + this.drawingTools = drawingTools; + lights = new G2DLight[getLightNumber()]; + } + + @Override + public int getLightNumber() { + return 16; + } + + @Override + public Light getLight(int i) { + if (i < 0 || i >= getLightNumber()) { + return null; + } else { + if (lights[i] == null) { + lights[i] = new G2DLight(i); + } + return lights[i]; + } + } + + @Override + public void setLightningEnable(boolean isLightningEnable) { + this.isLightningEnable = isLightningEnable; + } + + @Override + public boolean isLightningEnable() { + return isLightningEnable; + } + + @Override + public void setMaterial(Material material) { + this.material = material; + } + + public Material getMaterial() { + return material; + } + + /** + * Returns the camera position used to perform the lighting. + */ + public Vector3f getCameraPosition() { + double[] m = drawingTools.getTransformationManager().getTransformation().getMatrix(); + //extract the translation of the matrix + return new Vector3f((float)m[12], (float)m[13], (float)m[14]); + } + + /** + * Returns the vertex transformation as a float array. + */ + public float[] getVertexTransform() { + float[] ret = new float[16]; + double[] m = drawingTools.getTransformationManager().getTransformation().getMatrix(); + for (int i = 0; i < 16; ++i) { + ret[i] = (float)m[i]; + } + return ret; + } + + /** + * Returns the normal transformation as a float array. + * The normal transformation is defined as the inverse transpose + * of the vertex transformation. + */ + public float[] getNormalTransform() { + float[] ret = new float[16]; + double[] m = drawingTools.getTransformationManager().getTransformation().getInverseTransformation().getMatrix(); + + //only the top 3x3 matrix is used. + ret[0] = (float)m[0]; + ret[4] = (float)m[1]; + ret[8] = (float)m[2]; + ret[12] = 0.f; + ret[1] = (float)m[4]; + ret[5] = (float)m[5]; + ret[9] = (float)m[6]; + ret[13] = 0.f; + ret[2] = (float)m[8]; + ret[6] = (float)m[9]; + ret[10] = (float)m[10]; + ret[14] = 0.f; + ret[3] = 0.f; + ret[7] = 0.f; + ret[11] = 0.f; + ret[15] = 1.f; + + return ret; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/AbstractDrawable3DObject.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/AbstractDrawable3DObject.java new file mode 100755 index 000000000..47117feb4 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/AbstractDrawable3DObject.java @@ -0,0 +1,426 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixte Denizet + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.motor; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; +import java.util.HashSet; +import java.util.Set; + +import org.scilab.forge.scirenderer.tranformations.Vector3d; + +/** + * @author Calixte DENIZET + */ +public abstract class AbstractDrawable3DObject { + + public static final double PRECISION = 1e-8; + + private static int defaultPrecedence = 0; + + protected final Vector3d[] vertices; + protected final Color[] colors; + protected final Color color; + + protected int precedence; + protected Vector3d v0; + protected Vector3d v1; + protected Vector3d v0v1; + protected double nv0v1; + protected Vector3d normal; + protected BoundingBox bbox; + + /** + * Default constructor + * @param vertices the vertices + */ + public AbstractDrawable3DObject(Vector3d[] vertices, Color[] colors) throws InvalidPolygonException { + if (vertices == null || vertices.length == 0) { + throw new InvalidPolygonException("Invalid 3D Object: no vertices was given"); + } + this.vertices = vertices; + if (colors != null && AbstractDrawable3DObject.isMonochromatic(colors)) { + this.color = colors[0]; + this.colors = null; + } else { + this.colors = colors; + this.color = null; + } + if (isDegenerate()) { + throw new InvalidPolygonException("Invalid 3D Object: two vertices are the same"); + } + if (isNanOrInf()) { + throw new InvalidPolygonException("Invalid 3D Object: contains NaN or Inf coordinates"); + } + setPrecedence(defaultPrecedence++); + } + + final Color getColor(final int i) { + if (colors != null) { + return colors[i]; + } + + return color; + } + + final boolean isMonochromatic() { + return color != null; + } + + /** + * Get the bounding box + * @return the bounding box + */ + final BoundingBox getBBox() { + if (bbox == null) { + bbox = BoundingBox.getBoundingBox(this); + } + + return bbox; + } + + /** + * Test if an array of colors contains only one color or not + * @param colors the colors array + * @return true if the array is monochromatic + */ + public static boolean isMonochromatic(Color[] colors) { + if (colors != null && colors.length > 0) { + Color c = colors[0]; + for (int i = 1; i < colors.length; i++) { + if (!c.equals(colors[i])) { + return false; + } + } + } + + return true; + } + + /** + * Draw this object on a Graphics2D object + * @param g2d the Graphics2d object where to draw + */ + public abstract void draw(Graphics2D g2d); + + /** + * Reset the default precedence + */ + public static void resetDefaultPrecedence() { + defaultPrecedence = 0; + } + + /** + * Set the precedence of this object. + * @param precedence the precedence of this object + */ + public void setPrecedence(int precedence) { + this.precedence = precedence; + } + + /** + * Get the precedence of this object, i.e. its position in the list of the draw objects. + * The first object has a precedence of 0, the second has a precedence of 1, ... + * @param the precedence + */ + public int getPrecedence() { + return precedence; + } + + /** + * Get the normal vector. + * If no normal vector has been set then it is calculated in using the cross product of the first two vectors. + * @return the normal vector. + */ + public Vector3d getProvidedNormal() { + return normal; + } + + /** + * Get the normal vector. + * If no normal vector has been set then it is calculated in using the cross product of the first two vectors. + * @return the normal vector. + */ + public Vector3d getNormal() { + if (v0v1 == null) { + setNormal(); + } + + return v0v1; + } + + /** + * Set the normal vector + */ + protected void setNormal() { + v0 = vertices[1].minus(vertices[0]); + if (vertices.length >= 3) { + v1 = vertices[2].minus(vertices[0]); + v0v1 = Vector3d.product(v0, v1); + nv0v1 = v0v1.getNorm(); + v0v1 = v0v1.times(1 / nv0v1);; + } else { + v0v1 = new Vector3d(0, 0, 0); + } + } + + /** + * Determinates if the object is contained into a plane + * @return true if the object is planar + */ + protected boolean isPlanar() { + Vector3d n = getNormal(); + if (n.isZero() || vertices.length == 3) { + return true; + } + + for (int i = 1; i < vertices.length; i++) { + if (!isNull(n.scalar(vertices[0].minus(vertices[i])))) { + return false; + } + } + + return true; + } + + public int isBehind(Vector3d v, double a) { + double[] mM = minmax3D(this, v); + + if (isPositiveOrNull(mM[0] + a)) { + return 1; + } + if (mM[1] + a < 0) { + return -1; + } + + return 0; + } + + public static boolean isBehind(Vector3d M, Vector3d v, double a) { + return isPositiveOrNull(M.scalar(v) + a); + } + + /** + * Get the projected polyline of this object + * @return a path 2D + */ + protected Path2D getProjectedPolyLine() { + Path2D.Double path = new Path2D.Double(); + path.moveTo(vertices[0].getX(), vertices[0].getY()); + for (int i = 1; i < vertices.length; i++) { + path.lineTo(vertices[i].getX(), vertices[i].getY()); + } + + return path; + } + + /** + * Get the projected contour (i.e. a closed polyline) of this object + * @return a path 2D + */ + protected Path2D getProjectedContour() { + Path2D path = getProjectedPolyLine(); + path.closePath(); + + return path; + } + + /** + * @param d a number + * @return true if d is near zero + */ + protected final static boolean isNull(final double d) { + return Math.abs(d) <= PRECISION; + } + + /** + * @param d a number + * @return true if d is greater than zero + */ + protected final static boolean isPositiveOrNull(final double d) { + return d >= 0 || isNull(d); + } + + /** + * @param d a number + * @return true if d is greater than zero + */ + protected final static boolean isNegativeOrNull(final double d) { + return d <= 0 || isNull(d); + } + + /** + * @param d1 a number + * @param d2 a number + * @return true if d1 is greater than d2 + */ + protected final static boolean isGreaterOrEqual(final double d1, final double d2) { + return isPositiveOrNull(d1 - d2); + } + + /** + * @param d1 a number + * @param d2 a number + * @return true if d1 is lower than d2 + */ + protected final static boolean isLowerOrEqual(final double d1, final double d2) { + return isPositiveOrNull(d2 - d1); + } + + /** + * @param d1 a number + * @param d2 a number + * @return true if d1 is equal to d2 + */ + protected final static boolean isEqual(final double d1, final double d2) { + return isNull(d1 - d2); + } + + /** + * Get min-max of the projections of the vertices of o on v + * @param o an object + * @param v a vector + * @return an array of size 2 containing min-max. + */ + protected static final double[] minmax3D(final AbstractDrawable3DObject o, final Vector3d v) { + double min = v.scalar(o.vertices[0]); + double max = min; + + for (int i = 1; i < o.vertices.length; i++) { + double s = v.scalar(o.vertices[i]); + if (s < min) { + min = s; + } else if (s > max) { + max = s; + } + } + + return new double[] {min, max}; + } + + /** + * Get min-max of the projections of the vertices of o on v + * @param o an object + * @param v a vector + * @return an array of size 2 containing min-max. + */ + protected static final double[] minmax2D(final AbstractDrawable3DObject o, final double x, final double y) { + double min = x * o.vertices[0].getX() + y * o.vertices[0].getY(); + double max = min; + + for (int i = 1; i < o.vertices.length; i++) { + double s = x * o.vertices[i].getX() + y * o.vertices[i].getY(); + if (s < min) { + min = s; + } else if (s > max) { + max = s; + } + } + + return new double[] {min, max}; + } + + protected static final Color getColorsBarycenter(final Color c1, final Color c2, final double w1, final double w2) { + if (c1 != null && c2 != null && !c1.equals(c2)) { + float[] comp1 = c1.getComponents(null); + float[] comp2 = c2.getComponents(null); + + return new Color((float) (comp1[0] * w1 + comp2[0] * w2), + (float) (comp1[1] * w1 + comp2[1] * w2), + (float) (comp1[2] * w1 + comp2[2] * w2), + (float) (comp1[3] * w1 + comp2[3] * w2)); + } + + return c1; + } + + /** + * @return true if there are two vertices which are indentical + */ + protected boolean isDegenerate() { + Set<Vector3d> set = new HashSet<Vector3d>(); + for (Vector3d v : vertices) { + set.add(v); + } + + return set.size() != vertices.length; + } + + protected boolean isNanOrInf() { + for (Vector3d v : vertices) { + if (isNanOrInf(v)) { + return true; + } + } + + return false; + } + + public static final boolean isNanOrInf(final Vector3d v) { + final double x = v.getX(); + if (Double.isNaN(x) || Double.isInfinite(x)) { + return true; + } + final double y = v.getY(); + if (Double.isNaN(y) || Double.isInfinite(y)) { + return true; + } + final double z = v.getZ(); + if (Double.isNaN(z) || Double.isInfinite(z)) { + return true; + } + return false; + } + + /** + * Create a clip rect for 2D view according to clipping planes passed as arguments + * @param v the clipping plane equation + */ + public final static void makeClip(final double[] clip, final double[] v) { + if (v[1] == 0) { + double x = -v[3] / v[0]; + if (Double.isNaN(clip[0])) { + clip[0] = x; + } else if (clip[0] > x) { + clip[1] = clip[0]; + clip[0] = x; + } else { + clip[1] = x; + } + } else { + double y = -v[3] / v[1]; + if (Double.isNaN(clip[2])) { + clip[2] = y; + } else if (clip[2] > y) { + clip[3] = clip[2]; + clip[2] = y; + } else { + clip[3] = y; + } + } + } + + /** + * Get the clipping shape (for 2D view) + * @return the clipping shape + */ + public final static Shape getClip(final double[] clip) { + if (!Double.isNaN(clip[0]) && !Double.isNaN(clip[1]) && !Double.isNaN(clip[2]) && !Double.isNaN(clip[3])) { + return new Rectangle2D.Double(clip[0], clip[2], clip[1] - clip[0], clip[3] - clip[2]); + } + + return null; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/BoundingBox.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/BoundingBox.java new file mode 100755 index 000000000..a101e17e4 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/BoundingBox.java @@ -0,0 +1,176 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixte Denizet + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.motor; + +import org.scilab.forge.scirenderer.tranformations.Vector3d; + +/** + * @author Calixte DENIZET + * + * Bounding box of a 3D object, useful to speed-up intersection detection. + */ +public class BoundingBox { + + private double minX = Double.POSITIVE_INFINITY; + private double maxX = Double.NEGATIVE_INFINITY; + private double minY = Double.POSITIVE_INFINITY; + private double maxY = Double.NEGATIVE_INFINITY; + private double minZ = Double.POSITIVE_INFINITY; + private double maxZ = Double.NEGATIVE_INFINITY; + + /** + * Default constructor + * @param minX the minimal X + * @param maxX the maximal X + * @param minY the minimal Y + * @param maxY the maximal Y + * @param minZ the minimal Z + * @param maxZ the maximal Z + */ + public BoundingBox(double minX, double maxX, double minY, double maxY, double minZ, double maxZ) { + this.minX = minX; + this.maxX = maxX; + this.minY = minY; + this.maxY = maxY; + this.minZ = minZ; + this.maxZ = maxZ; + } + + /** + * Get the relative x-position of this bounding-box and of the box + * @param box a BoundingBox + * @return 1 if box is on the right, -1 if on the left and 0 if nothing. + */ + public int xCompare(BoundingBox box) { + if (box.minX >= maxX) { + return -1; + } + + if (minX >= box.maxX) { + return 1; + } + + return 0; + } + + /** + * Get the relative y-position of this bounding-box and of the box + * @param box a BoundingBox + * @return 1 if box is on the bottom, -1 if on the top and 0 if nothing. + */ + public int yCompare(BoundingBox box) { + if (box.minY >= maxY) { + return -1; + } + + if (minY >= box.maxY) { + return 1; + } + + return 0; + } + + /** + * Get the relative z-position of this bounding-box and of the box + * @param box a BoundingBox + * @return 1 if box is on the front, -1 if behind and 0 if nothing. + */ + public int zCompare(BoundingBox box) { + if (box.minZ >= maxZ) { + return 1; + } + + if (minZ >= box.maxZ) { + return -1; + } + + return 0; + } + + /** + * @param box a BoundingBox + * @return true if this BoundingBox and the box have an intersection + */ + public boolean isIntersecting(BoundingBox box) { + return box.maxX >= minX && maxX >= box.minX + && box.maxY >= minY && maxY >= box.minY + && box.maxZ >= minZ && maxZ >= box.minZ; + } + + /** + * @return true if the bbox are non-intersecting and no z-overlapping + */ + public boolean isNonZOverlapping(BoundingBox box) { + return (box.maxX < minX || maxX < box.minX || box.maxY < minY || maxY < box.minY) || ((box.maxZ < minZ || maxZ < box.minZ) && (maxX == box.minX || box.maxX == minX || box.minY == maxY || minY == box.maxY)); + } + + /** + * @param box a BoundingBox + * @return true if this BoundingBox and the box have a strict intersection + */ + public boolean isStrictlyIntersecting(BoundingBox box) { + return box.maxX > minX && maxX > box.minX + && box.maxY > minY && maxY > box.minY + && box.maxZ > minZ && maxZ > box.minZ; + } + + /** + * Get the bounding box of an object + * @param object a 3D object + * @return the corresponding bounding-box + */ + public static BoundingBox getBoundingBox(AbstractDrawable3DObject object) { + Vector3d[] vertices = object.vertices; + double minX = Double.POSITIVE_INFINITY; + double maxX = Double.NEGATIVE_INFINITY; + double minY = Double.POSITIVE_INFINITY; + double maxY = Double.NEGATIVE_INFINITY; + double minZ = Double.POSITIVE_INFINITY; + double maxZ = Double.NEGATIVE_INFINITY; + + for (int i = 0; i < vertices.length; i++) { + double x = vertices[i].getX(); + double y = vertices[i].getY(); + double z = vertices[i].getZ(); + if (x < minX) { + minX = x; + } + + if (x > maxX) { + maxX = x; + } + + if (y < minY) { + minY = y; + } + + if (y > maxY) { + maxY = y; + } + + if (z < minZ) { + minZ = z; + } + + if (z > maxZ) { + maxZ = z; + } + } + + return new BoundingBox(minX, maxX, minY, maxY, minZ, maxZ); + } + + @Override + public String toString() { + return "[" + minX + ";" + maxX + "]x" + "[" + minY + ";" + maxY + "]x" + "[" + minZ + ";" + maxZ + "]"; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/Clippable.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/Clippable.java new file mode 100755 index 000000000..9e507e874 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/Clippable.java @@ -0,0 +1,32 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2013 - Scilab Enterprises - Calixte Denizet + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.motor; + +import java.util.List; + +import org.scilab.forge.scirenderer.tranformations.Vector4d; + +/** + * @author Calixte DENIZET + * + * Interface to represent a clippable object. + */ +public interface Clippable { + + /** + * Break this ConvexObject against a plane + * @param v plane definition + * @return a list of ConvexObject. + */ + public List<ConvexObject> breakObject(Vector4d v); + +}
\ No newline at end of file diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/ConvexObject.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/ConvexObject.java new file mode 100755 index 000000000..c0c473f68 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/ConvexObject.java @@ -0,0 +1,321 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixte Denizet + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.motor; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.util.ArrayList; +import java.util.List; + +import org.scilab.forge.scirenderer.tranformations.Vector3d; +import org.scilab.forge.scirenderer.tranformations.Vector4d; + +/** + * @author Calixte DENIZET + * + * Class to represent a convex object. + * Collision and relative positions of convexs object are relatively easy to determinate. + * About the method isBehind, it could be interesting to use the algorithm of Chung-Wang. + */ +public abstract class ConvexObject extends AbstractDrawable3DObject implements Clippable { + + private List<ConvexObject> areas; + + /** + * Default constructor + * @param vertices the vertices + * @param colors the colors + */ + public ConvexObject(Vector3d[] vertices, Color[] colors) throws InvalidPolygonException { + super(vertices, colors); + } + + /** + * Abstract method + * Break this ConvexObject against the ConvexObject o + * @param o a ConvexObject + * @return a list of ConvexObject. + */ + public abstract List<ConvexObject> breakObject(ConvexObject o); + + /** + * {@inheritDoc} + */ + public abstract List<ConvexObject> breakObject(Vector4d v); + + public void addArea(ConvexObject co) { + if (areas == null) { + areas = new ArrayList<ConvexObject>(); + } + areas.add(co); + } + + protected void drawAreas(Graphics2D g2d) { + if (areas != null) { + for (ConvexObject co : areas) { + Shape oldClip = g2d.getClip(); + g2d.clip(this.getProjectedContour()); + co.draw(g2d); + g2d.setClip(oldClip); + } + } + } + + /** + * Test the coplanarity of two objects + * @param o a ConvexObject + * @return true if the two objects are coplanar + */ + public boolean areCoplanar(ConvexObject o) { + if (!(this instanceof Segment)) { + double sc = vertices[0].scalar(getNormal()); + if (o instanceof Segment) { + return isEqual(sc, o.vertices[0].scalar(v0v1)) && isEqual(sc, o.vertices[1].scalar(v0v1)); + } + return isEqual(sc, o.vertices[0].scalar(v0v1)) && isEqual(sc, o.vertices[1].scalar(v0v1)) && isEqual(sc, o.vertices[2].scalar(v0v1)); + } + + if (!(o instanceof Segment)) { + return o.areCoplanar(this); + } + + if (o.vertices[0].equals(vertices[0]) || o.vertices[1].equals(vertices[0]) || o.vertices[0].equals(vertices[1]) || o.vertices[1].equals(vertices[1])) { + return true; + } + + getNormal(); + o.getNormal(); + Vector3d v = Vector3d.product(v0, o.v0); + return isNull(v.scalar(vertices[0].minus(o.vertices[0]))); + } + + /** + * Check if o is behind this. + * Take care: the algorithms used are for convex objects (typically tri-tri, seg-seg or tri-seg) + * @return true if o is behind this + */ + public int isBehind(ConvexObject o) { + BoundingBox bbox = getBBox(); + BoundingBox obbox = o.getBBox(); + // Quick test in using bounding boxes + if (bbox.isNonZOverlapping(obbox)) { + return 0; + } + + // Check if the two objects intersect in projection plane or not + if (check2DIntersection(o)) { + // We have a strict intersection or an intersection on an edge + if (areCoplanar(o)) { + return getPrecedence() > o.getPrecedence() ? 1 : -1; + } + + // Quick test with bounding-box along z-axis + int ret = bbox.zCompare(obbox); + if (ret != 0) { + return ret; + } + + // In the most of the cases, one of the two following test are sufficient to determinate + // if one object is behind or on front of the plane containing this or o + ret = check(o, getNormal()); + if (ret != 0) { + return ret; + } + + ret = check(o, o.getNormal()); + if (ret != 0) { + return ret; + } + + // Check against the cross product of one edge of this and one edge of o + int M = vertices.length == 2 ? 1 : vertices.length; + int N = o.vertices.length == 2 ? 1 : o.vertices.length; + for (int j = 0; j < M; j++) { + int l = (j + 1 < vertices.length) ? j + 1 : 0; + Vector3d e = vertices[l].minus(vertices[j]); + for (int k = 0; k < N; k++) { + int m = (k + 1 < o.vertices.length) ? k + 1 : 0; + Vector3d oe = o.vertices[m].minus(o.vertices[k]); + ret = check(o, Vector3d.product(e, oe).getNormalized()); + if (ret != 0) { + return ret; + } + } + } + + // At this point: there is a collision between the two objects + return 2; + } else { + return 0; + } + } + + /** + * Check the intersections of the projection on the xOy-plane of this and o + * The algorithm is the following: for each edge, determinate the normal vector and project all the points + * of this and o on the normal. If the intersection of [this.min,this.max] and [o.min, o.max] is empty, then + * we have a separating line so the two objects are separated. + * @param o the object to test with this + * @return true if there is a collision + */ + public boolean check2DIntersection(final ConvexObject o) { + int ret = check2D(this, o); + if (ret != -1) { + return false; + } + + ret = check2D(o, this); + if (ret != -1) { + return false; + } + + return true; + } + + /** + * Check the intersections of the projection on the xOy-plane of this and o + * The algorithm is the following: for each edge, determinate the normal vector and project all the points + * of this and o on the normal. If the intersection of [this.min,this.max] and [o.min, o.max] is empty, then + * we have a separating line so the two objects are separated. + * @param o the object to test with this + * @return true if there is a collision + */ + public boolean check2DTrueIntersection(final ConvexObject o) { + int ret = check2D2(this, o); + if (ret == 1) { + return true; + } else if (ret == 0) { + return false; + } + + ret = check2D2(o, this); + if (ret == 1) { + return true; + } else if (ret == 0) { + return false; + } + + return true; + } + + /** + * Check 2D intersection of two convex objects + * @param o1 first object + * @param o2 second object + * @return -1 if strict intersection, 1 if intersection on an edge and 0 if no intersection + */ + private static final int check2D(final ConvexObject o1, final ConvexObject o2) { + // When o1 is a Segment (i.e. o1.vertices;length == 2) it is mandatory to check against ortho(v1-v0) and ortho(v0-v1) + int M = o1.vertices.length == 2 ? 1 : o1.vertices.length; + for (int i = 0; i < M; i++) { + int j = (i + 1 < o1.vertices.length) ? i + 1 : 0; + double xN = o1.vertices[i].getY() - o1.vertices[j].getY(); + double yN = o1.vertices[j].getX() - o1.vertices[i].getX(); + double n = Math.hypot(xN, yN); + xN /= n; + yN /= n; + double[] mM = minmax2D(o1, xN, yN); + double min = mM[0]; + double max = mM[1]; + + mM = minmax2D(o2, xN, yN); + double omin = mM[0]; + double omax = mM[1]; + + if (max < omin || omax < min) { + return 0; + } + + if (isEqual(max, omin) || isEqual(omax, min)) { + return 1; + } + } + + return -1; + } + + /** + * Check 2D intersection of two convex objects + * @param o1 first object + * @param o2 second object + * @return -1 if strict intersection, 1 if intersection on an edge and 0 if no intersection + */ + private static final int check2D2(final ConvexObject o1, final ConvexObject o2) { + // When o1 is a Segment (i.e. o1.vertices;length == 2) it is mandatory to check against ortho(v1-v0) and ortho(v0-v1) + int M = o1.vertices.length == 2 ? 1 : o1.vertices.length; + boolean bool = false; + for (int i = 0; i < M; i++) { + int j = (i + 1 < o1.vertices.length) ? i + 1 : 0; + double xN = o1.vertices[i].getY() - o1.vertices[j].getY(); + double yN = o1.vertices[j].getX() - o1.vertices[i].getX(); + double n = Math.hypot(xN, yN); + xN /= n; + yN /= n; + double[] mM = minmax2D(o1, xN, yN); + double min = mM[0]; + double max = mM[1]; + + mM = minmax2D(o2, xN, yN); + double omin = mM[0]; + double omax = mM[1]; + + if (max < omin || omax < min) { + return 0; + } + + if (!bool && (isEqual(max, omin) || isEqual(omax, min))) { + bool = true; + } + } + + if (bool) { + return 1; + } + + return -1; + } + + /** + * Check the intersection this and o against vector v. + * The algorithm is just to project this and o on the vector v and to check if the two projected sets + * can be separated. + * @param v the vector where to project + * @return 1 if o is behind this, 0 if it is undeterminated and -1 if this is behind o. + */ + protected int check(final ConvexObject o, final Vector3d v) { + if (!v.isNearZero()) { + double[] mM = minmax3D(this, v); + double min = mM[0]; + double max = mM[1]; + + mM = minmax3D(o, v); + double omin = mM[0]; + double omax = mM[1]; + double z = v.getZ(); + + if (Math.signum(z) == 0) { + return 0; + } + + if (isLowerOrEqual(max, omin)) { + return (int) Math.signum(z); + } + + if (isLowerOrEqual(omax, min)) { + return (int) - Math.signum(z); + } + } + + return 0; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/DrawTools.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/DrawTools.java new file mode 100755 index 000000000..925751d5c --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/DrawTools.java @@ -0,0 +1,307 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixte Denizet + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.motor; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.LinearGradientPaint; +import java.awt.Paint; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.Area; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; + +import java.awt.geom.Point2D; +import java.awt.MultipleGradientPaint; + +/** + * @author Calixte DENIZET + */ +public final class DrawTools { + + private static final Stroke stroke = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); + private static final Stroke EMPTYSTROKE = new BasicStroke(0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); + private static final Color TRANSLUCENT_BLACK = new Color(0, 0, 0, 0); + + /** + * Fill a triangle in using a Gouraud shading + * Only two gradient are used rather than three. + * @param g2d the Graphics2D where to draw + * @param t the Triangle to fill + */ + public static final void fillGouraud(final Graphics2D g2d, final Triangle t) { + Path2D contour = t.getProjectedContour(); + double[] v0 = new double[] {t.vertices[0].getX(), t.vertices[0].getY()}; + double[] v1 = new double[] {t.vertices[1].getX(), t.vertices[1].getY()}; + double[] v2 = new double[] {t.vertices[2].getX(), t.vertices[2].getY()}; + double[] pv0 = get2DProjection(v0[0], v0[1], v1[0], v1[1], v2[0], v2[1]); + double[] pv1 = get2DProjection(v1[0], v1[1], v0[0], v0[1], v2[0], v2[1]); + double[] pv2 = get2DProjection(v2[0], v2[1], v0[0], v0[1], v1[0], v1[1]); + + Paint oldPaint = g2d.getPaint(); + Area area = new Area(contour); + area.add(new Area(stroke.createStrokedShape(contour))); + + + g2d.setColor(t.getColor(0)); + g2d.fill(area); + + + float[] col = t.getColor(1).getComponents(null); + GradientPaint gp = new GradientPaint((float) v1[0], (float) v1[1], t.getColor(1), (float) pv1[0], (float) pv1[1], new Color(col[0], col[1], col[2], 0.0f)); + g2d.setPaint(gp); + g2d.fill(area); + + + col = t.getColor(2).getComponents(null); + gp = new GradientPaint((float) v2[0], (float) v2[1], t.getColor(2), (float) pv2[0], (float) pv2[1], new Color(col[0], col[1], col[2], 0.0f)); + g2d.setPaint(gp); + g2d.fill(area); + + g2d.setPaint(oldPaint); + } + + /** + * Draw a texture (ie a BufferedImage) in a triangle + * @param g2d the Graphics2D where to draw + * @param image the texture to apply + * @param ximg the x-coordinates of the triangle to use in the texture + * @param yimg the y-coordinates of the triangle to use in the texture + * @param xdest the x-coordinates of the destination triangle + * @param ydest the y-coordinates of the destination triangle + * @param key the rendering hint to use for interpolation + */ + public static final void drawTriangleTexture(final Graphics2D g2d, final BufferedImage image, final double[] ximg, final double[] yimg, final double[] xdest, final double[] ydest, Object key) { + try { + double w = image.getWidth(); + double h = image.getHeight(); + + Path2D.Double path = new Path2D.Double(); + path.moveTo(xdest[0], ydest[0]); + path.lineTo(xdest[1], ydest[1]); + path.lineTo(xdest[2], ydest[2]); + path.closePath(); + Area area = new Area(path); + area.add(new Area(stroke.createStrokedShape(path))); + + boolean is1d = is1d(ximg, yimg); + + // if we have a 1D texture we must slighlty modified the coordinates to use the algorithm below. + if (checkSourceCoordinates(ximg, yimg)) { + // three coordinates are the same in 1D texture + int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); + int index = (int) Math.floor(w * ximg[0]); + Color color; + if (index >= pixels.length) { + color = new Color(pixels[pixels.length - 1]); + } else if (index < 0) { + color = new Color(pixels[0]); + } else { + color = new Color(pixels[index]); + } + //color = Color.PINK; + + g2d.setColor(color); + g2d.fill(area); + return; + } + + AffineTransform translationDest = AffineTransform.getTranslateInstance(xdest[0], ydest[0]); + AffineTransform translationImg = AffineTransform.getTranslateInstance(-w * ximg[0], -h * yimg[0]); + + AffineTransform toDest = new AffineTransform(xdest[1] - xdest[0], ydest[1] - ydest[0], xdest[2] - xdest[0], ydest[2] - ydest[0], 0, 0); + AffineTransform fromImg = new AffineTransform(w * (ximg[1] - ximg[0]), h * (yimg[1] - yimg[0]), w * (ximg[2] - ximg[0]), h * (yimg[2] - yimg[0]), 0, 0).createInverse(); + + AffineTransform transformation = new AffineTransform(); + transformation.concatenate(translationDest); + transformation.concatenate(toDest); + transformation.concatenate(fromImg); + transformation.concatenate(translationImg); + + AffineTransform oldTransform = g2d.getTransform(); + + // For now we don't enter in this + // SVGGraphics2D doesn't handle MultipleGradient :( + if (false && is1d && key == RenderingHints.VALUE_INTERPOLATION_BILINEAR) { + int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); + float[] fractions = new float[pixels.length]; + for (int i = 0; i < fractions.length; i++) { + fractions[i] = (((float) i) / (fractions.length - 1)); + } + + Color[] colors = new Color[pixels.length]; + for (int i = 0; i < colors.length; i++) { + colors[i] = new Color(pixels[i]); + } + LinearGradientPaint gradient = new LinearGradientPaint(new Point2D.Double(0, 0), new Point2D.Double(pixels.length, 0), fractions, colors, MultipleGradientPaint.CycleMethod.NO_CYCLE, MultipleGradientPaint.ColorSpaceType.SRGB, transformation); + Shape oldClip = g2d.getClip(); + g2d.clip(area); + g2d.setPaint(gradient); + g2d.fill(area); + g2d.setClip(oldClip); + } else { + clamp(g2d, ximg, yimg, xdest, ydest, transformation, image); + Object oldKey = g2d.getRenderingHint(RenderingHints.KEY_INTERPOLATION); + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, key); + g2d.setStroke(EMPTYSTROKE); + Shape oldClip = g2d.getClip(); + g2d.clip(area); + g2d.drawImage(image, transformation, null); + g2d.setClip(oldClip); + if (oldKey != null) { + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, oldKey); + } + } + + g2d.setTransform(oldTransform); + } catch (NoninvertibleTransformException e) { + System.err.println(e); + } + } + + /** + * Draw a texture (ie a BufferedImage) in a parallelogram + * @param g2d the Graphics2D where to draw + * @param image the texture to apply + * @param ximg the x-coordinates of the parallelogram to use in the texture + * @param yimg the y-coordinates of the parallelogram to use in the texture + * @param xdest the x-coordinates of the destination parallelogram + * @param ydest the y-coordinates of the destination parallelogram + * @param key the rendering hint to use for interpolation + */ + public static final void drawParallelogramTexture(final Graphics2D g2d, final BufferedImage image, final double[] ximg, final double[] yimg, final double[] xdest, final double[] ydest, Object key) { + try { + Object oldKey = g2d.getRenderingHint(RenderingHints.KEY_INTERPOLATION); + + double w = image.getWidth(); + double h = image.getHeight(); + + AffineTransform translationDest = AffineTransform.getTranslateInstance(xdest[0], ydest[0]); + AffineTransform translationImg = AffineTransform.getTranslateInstance(-w * ximg[0], -h * yimg[0]); + + AffineTransform toDest = new AffineTransform(xdest[1] - xdest[0], ydest[1] - ydest[0], xdest[2] - xdest[0], ydest[2] - ydest[0], 0, 0); + AffineTransform fromImg = new AffineTransform(w * (ximg[1] - ximg[0]), h * (yimg[1] - yimg[0]), w * (ximg[2] - ximg[0]), h * (yimg[2] - yimg[0]), 0, 0).createInverse(); + + AffineTransform transformation = new AffineTransform(); + transformation.concatenate(translationDest); + transformation.concatenate(toDest); + transformation.concatenate(fromImg); + transformation.concatenate(translationImg); + + AffineTransform oldTransform = g2d.getTransform(); + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, key); + g2d.drawImage(image, transformation, null); + g2d.setTransform(oldTransform); + + if (oldKey != null) { + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, oldKey); + } + } catch (NoninvertibleTransformException e) { } + } + + /** + * Check and modify the coordinates if we have a 1D texture (i.e. all the y-coords are zero) + * @param x the x-coordinates + * @param y the y-coordinates + */ + private static final boolean checkSourceCoordinates(final double[] x, final double[] y) { + if (is1d(x, y)) { + if (!AbstractDrawable3DObject.isEqual(x[0], x[1]) && !AbstractDrawable3DObject.isEqual(x[1], x[2]) && !AbstractDrawable3DObject.isEqual(x[2], x[0])) { + y[0] = 1; + } else if (AbstractDrawable3DObject.isEqual(x[0], x[1]) && !AbstractDrawable3DObject.isEqual(x[1], x[2])) { + y[0] = 1; + } else if (AbstractDrawable3DObject.isEqual(x[0], x[2]) && !AbstractDrawable3DObject.isEqual(x[2], x[1])) { + y[2] = 1; + } else if (AbstractDrawable3DObject.isEqual(x[1], x[2]) && !AbstractDrawable3DObject.isEqual(x[2], x[0])) { + y[2] = 1; + } else { + return true; + } + + y[0] = !AbstractDrawable3DObject.isNull(y[0]) ? 1 : 0; + y[1] = !AbstractDrawable3DObject.isNull(y[1]) ? 1 : 0; + y[2] = !AbstractDrawable3DObject.isNull(y[2]) ? 1 : 0; + } + + return false; + } + + /** + * Check if the triangle in texture is degenerate + * @param x x-coordinates + * @param y y-coordinates + * @return true if 1d + */ + private static final boolean is1d(final double[] x, final double[] y) { + return AbstractDrawable3DObject.isNull(y[0]) && AbstractDrawable3DObject.isNull(y[1]) && AbstractDrawable3DObject.isNull(y[2]); + } + + private static final void clamp(Graphics2D g2d, double[] ximg, double[] yimg, double[] xdest, double[] ydest, AffineTransform transformation, BufferedImage image) { + if (ximg[0] < 0 || ximg[1] < 0 || ximg[2] < 0 || ximg[0] > 1 || ximg[1] > 1 || ximg[2] > 1) { + double w = image.getWidth(); + double h = image.getHeight(); + int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); + Path2D.Double path = new Path2D.Double(); + path.moveTo(w * ximg[0], h * yimg[0]); + path.lineTo(w * ximg[1], h * yimg[1]); + path.lineTo(w * ximg[2], h * yimg[2]); + path.closePath(); + Area tri = new Area(path); + Rectangle2D bounds = tri.getBounds2D(); + + if (bounds.getX() < 0) { + Area rect = new Area(new Rectangle2D.Double(bounds.getX(), bounds.getY(), -bounds.getX(), bounds.getHeight())); + tri.intersect(rect); + if (!tri.isEmpty()) { + tri.transform(transformation); + g2d.setColor(new Color(pixels[0])); + g2d.fill(tri); + } + } + + if (bounds.getX() + bounds.getWidth() > w) { + tri = new Area(path); + Area rect = new Area(new Rectangle2D.Double(w, bounds.getY(), bounds.getX() + bounds.getWidth() - w, bounds.getHeight())); + tri.intersect(rect); + if (!tri.isEmpty()) { + tri.transform(transformation); + g2d.setColor(new Color(pixels[pixels.length - 1])); + g2d.fill(tri); + } + } + } + } + + /** + * Get the projection in 2D of point A on line (BC) + * @param A the point to project + * @param B a point of the line + * @param C an other point of the line (different of B) + * @return the projected point + */ + private static final double[] get2DProjection(final double xA, final double yA, final double xB, final double yB, final double xC, final double yC) { + final double xBC = xC - xB; + final double yBC = yC - yB; + final double n = xBC * xBC + yBC * yBC; + final double s = (xBC * (xA - xB) + yBC * (yA - yB)) / n; + + return new double[] {xB + s * xBC, yB + s * yBC}; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/G2DStroke.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/G2DStroke.java new file mode 100755 index 000000000..f2fbc8617 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/G2DStroke.java @@ -0,0 +1,105 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixte Denizet + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.motor; + +import java.awt.BasicStroke; + +import org.scilab.forge.scirenderer.shapes.appearance.Appearance; + +/** + * @author Calixte DENIZET + */ +public class G2DStroke extends BasicStroke { + + private static final int[] array = new int[16]; + private static final G2DStroke BASIC = new G2DStroke(1, null, 0); + private static float[] prevArray; + private static float prevFactor = -1; + private static short prevPattern = -1; + + public G2DStroke(float lineWidth, float[] dash, float phase) { + super(lineWidth, CAP_BUTT, JOIN_MITER, 10.0f, dash, phase); + } + + public static G2DStroke getStroke(Appearance appearance, double dashPhase) { + Appearance usedAppearance; + if (appearance == null) { + usedAppearance = new Appearance(); + } else { + usedAppearance = appearance; + } + + float factor = usedAppearance.getLineWidth(); + if (factor == 0) { + return new G2DStroke(0, null, 0); + } + + short pattern = usedAppearance.getLinePattern(); + if (pattern == -1 ) { + if (factor == 1) { + return BASIC; + } + + return new G2DStroke(factor, null, 0); + } + + if (factor != prevFactor || pattern != prevPattern) { + prevFactor = factor; + prevPattern = pattern; + prevArray = decodePattern(factor, pattern); + } + + return new G2DStroke(factor, prevArray, (float) dashPhase); + } + + private static final float[] decodePattern(final float factor, short pattern) { + // If the pattern is 1111101011111010, from right to left it becomes + // 0101111101011111, the first 0 is put on the right 1011111010111110 and it is converted into + // 1, 1, 5, 1, 1, 1, 5, 1 + int n = 0xFFFF & pattern; + int i = 0; + int t = Integer.numberOfTrailingZeros(n); + n = (n >> t); + int k = Integer.numberOfLeadingZeros(n) - 16; + + // Here we try to have a shorter pattern (EPS and PS don't like too long pattern in setdash function) + // If the pattern is 101101101101, we can factorize the number by 101 (101+101x1000+101x1000000+101x1000000000) + // so we test if the pattern can be divided by 101010...., by 100100100..., ...) + int twopm1 = (1 << 16) - 1; + for (int j = 2; 2 <= 16; j++) { + int q = twopm1 / ((1 << j) - 1); + if (n % q == 0) { + n = n / q; + break; + } + } + + while (n != 0) { + t = Integer.numberOfTrailingZeros(n); + if (t == 0) { + t = Integer.numberOfTrailingZeros(0xFFFF - n); + } + + array[i++] = t; + n = n >> t; + } + array[i] = k; + + float[] ret = new float[i + 1]; + + for (int j = 0; j <= i; j++) { + ret[j] = ((float) array[j]) * factor; + } + + return ret; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/InvalidPolygonException.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/InvalidPolygonException.java new file mode 100755 index 000000000..c6fd1c600 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/InvalidPolygonException.java @@ -0,0 +1,23 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixte Denizet + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.motor; + +/** + * @author Calixte DENIZET + */ +@SuppressWarnings(value = { "serial" }) +public class InvalidPolygonException extends Exception { + + public InvalidPolygonException(String msg) { + super(msg); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/LightHelper.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/LightHelper.java new file mode 100755 index 000000000..905c302a8 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/LightHelper.java @@ -0,0 +1,344 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2013 - Pedro SOUZA + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.motor; + +import java.awt.Color; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + + +import org.scilab.forge.scirenderer.tranformations.Vector3f; +import org.scilab.forge.scirenderer.implementation.g2d.lighting.G2DLight; +import org.scilab.forge.scirenderer.shapes.appearance.Material; + +/** + * @author Pedro SOUZA + */ +public class LightHelper { + + /** + * @param buffer the float buffer. + * @param stride the stride between elements. + * @return an array of Vector3f from the given float buffer. + */ + public static Vector3f[] getVector3f(FloatBuffer buffer, int stride) { + if (buffer == null) { + return null; + } + if (stride < 3) { + return null; + } + + float[] floats; + buffer.rewind(); + if (buffer.hasArray()) { + floats = buffer.array(); + } else { + floats = new float[buffer.limit()]; + buffer.get(floats); + } + + Vector3f[] ret = new Vector3f[floats.length / stride]; + for (int i = 0; i < floats.length; i += stride) { + ret[i] = new Vector3f(floats[i], floats[i + 1], floats[i + 2]); + } + return ret; + } + + /** + * @param buffer the float buffer. + * @param index the indices buffer. + * @param stride the stride between elements. + * @param transf matrix to transform the vector, if null no transformation is applied. + * @return an array of Vector3f from the given float buffer. + */ + public static Vector3f[] getIndexedVector3f(FloatBuffer buffer, IntBuffer index, int stride, float[] transf) { + if (buffer == null || index == null) { + return null; + } + if (stride < 3) { + return null; + } + + float[] floats; + buffer.rewind(); + if (buffer.hasArray()) { + floats = buffer.array(); + } else { + floats = new float[buffer.limit()]; + buffer.get(floats); + } + + int[] idx; + index.rewind(); + if (index.hasArray()) { + idx = index.array(); + } else { + idx = new int[index.limit()]; + index.get(idx); + } + + Vector3f[] ret = new Vector3f[idx.length]; + float x, y, z; + if (transf != null && transf.length == 16) { + for (int i = 0; i < idx.length; ++i) { + ret[i] = transform(floats[stride * idx[i]], floats[stride * idx[i] + 1], floats[stride * idx[i] + 2], transf); + } + } else { + for (int i = 0; i < idx.length; ++i) { + ret[i] = new Vector3f(floats[stride * idx[i]], floats[stride * idx[i] + 1], floats[stride * idx[i] + 2]); + } + } + return ret; + } + + static Vector3f transform(float x, float y, float z, float[] transf) { + float xx = transf[0] * x + transf[4] * y + transf[8] * z + transf[12]; + float yy = transf[1] * x + transf[5] * y + transf[9] * z + transf[13]; + float zz = transf[2] * x + transf[6] * y + transf[10] * z + transf[14]; + return new Vector3f(xx, yy, zz); + } + + static Vector3f transformDirection(float x, float y, float z, float[] transf) { + float xx = transf[0] * x + transf[4] * y + transf[8] * z; + float yy = transf[1] * x + transf[5] * y + transf[9] * z; + float zz = transf[2] * x + transf[6] * y + transf[10] * z; + return new Vector3f(xx, yy, zz); + } + + /** + * Apply the given ambient color to the output. + * @param ambient the ambient color. + * @param output the color vector to apply the ambient color. + * @param additive if true the ambient color is added to output. + * @return the resulting color vector. + */ + public static Color[] applyAmbient(Color ambient, Color[] output, boolean additive) { + for (int i = 0; i < output.length; ++i) { + if (additive) { + output[i] = getColorSum(ambient, output[i]); + } else { + output[i] = ambient; + } + } + return output; + } + + /** + * Apply the given ambient color to the output. + * @param ambient the ambient color. + * @param input the input color. + * @param output the color vector to apply the ambient color. + * @param additive if true the ambient color is added to output. + * @return the resulting color vector. + */ + public static Color[] applyAmbient(Color ambient, Color[] input, Color[] output, boolean additive) { + for (int i = 0; i < output.length; ++i) { + if (additive) { + output[i] = getColorSum(getColorProduct(ambient, input[i]), output[i]); + } else { + output[i] = getColorProduct(ambient, input[i]); + } + } + return output; + } + + /** + * Apply diffuse light to the output colors + * @param light the light position or direction. + * @param directional if true the vector light is considered a direction otherwise a position. + * @param vertices the surface vertices. + * @param normals the surface normals. + * @param colors the surface per-vertex colors. + * @param dffuse the light diffuse color. + * @param output the output color vector. + * @param additive if true the calculated diffuse color is added to the output. + * @return the resulting color vector. + */ + public static Color[] applyDiffuse(Vector3f light, boolean directional, Vector3f[] vertices, Vector3f[] normals, Color[] colors, Color diffuse, Color[] output, boolean additive) { + float ndotl; + for (int i = 0; i < colors.length; ++i) { + + if (directional) { + ndotl = normals[i].scalar(light); + } else { + Vector3f ray = light.minus(vertices[i]).getNormalized(); + ndotl = normals[i].scalar(ray); + } + ndotl = clamp(ndotl); + Color c = getColorProduct(colors[i], diffuse); + if (additive) { + output[i] = getColorSum(getColorProduct(c, ndotl), output[i]); + } else { + output[i] = getColorProduct(c, ndotl); + } + } + return output; + } + + /** + * Apply diffuse light to the output colors + * @param light the light position or direction. + * @param directional if true the vector light is considered a direction otherwise a position. + * @param vertices the surface vertices. + * @param normals the surface normals. + * @param color the surface color. + * @param output the output color vector. + * @param additive if true the calculated diffuse color is added to the output. + * @return the resulting color vector. + */ + public static Color[] applyDiffuse(Vector3f light, boolean directional, Vector3f[] vertices, Vector3f[] normals, Color color, Color[] output, boolean additive) { + float ndotl; + for (int i = 0; i < output.length; ++i) { + + if (directional) { + ndotl = normals[i].scalar(light); + } else { + Vector3f ray = light.minus(vertices[i]).getNormalized(); + ndotl = normals[i].scalar(ray); + } + ndotl = clamp(ndotl); + if (additive) { + output[i] = getColorSum(getColorProduct(color, ndotl), output[i]); + } else { + output[i] = getColorProduct(color, ndotl); + } + } + return output; + } + + public static Color[] applySpecular(Vector3f camera, Vector3f light, float shininess, boolean directional, Vector3f[] vertices, Vector3f[] normals, Color specular, Color[] output, boolean additive) { + + for (int i = 0; i < output.length; ++i) { + + Vector3f view = camera.minus(vertices[i]).getNormalized(); + Vector3f half; + float ndotl; + if (directional) { + half = view.plus(light); + ndotl = normals[i].scalar(light); + } else { + Vector3f ray = light.minus(vertices[i]).getNormalized(); + half = view.plus(ray); + ndotl = normals[i].scalar(ray); + } + half = half.getNormalized(); + + float s = 0.0f; + if (ndotl > 0.0f) { + s = normals[i].scalar(half); + s = clamp(s); + s = (float)Math.pow((double)s, (double)shininess); + } + + if (additive) { + output[i] = getColorSum(getColorProduct(specular, s), output[i]); + } else { + output[i] = getColorProduct(specular, s); + } + } + return output; + } + + /** + * Apply a per-vertex lighting to the given colors + * @param light the light. + * @param mat the material properties. + * @param camera the camera position. + * @param vertices the surface vertices. + * @param normals the surface normals. + * @param colors the surface per-vertex colors. + * @param output the output color vector. + * @param transf the light transformation matrix. If null no transformation is applyed. + * @param additive if true the calculated color is added to the output. + * @return the resulting color vector. + */ + public static Color[] applyLight(G2DLight light, Material mat, Vector3f camera, Vector3f[] vertices, Vector3f[] normals, Color[] colors, Color[] output, float[] transf, boolean additive) { + Color ambient = getColorProduct(mat.getAmbientColor(), light.getAmbientColor()); + Color diffuse = getColorProduct(mat.getDiffuseColor(), light.getDiffuseColor()); + Color specular = getColorProduct(mat.getSpecularColor(), light.getSpecularColor()); + + Color[] finalColor; + if (mat.isColorMaterialEnable()) { + finalColor = applyAmbient(light.getAmbientColor(), colors, output, additive); + } else { + finalColor = applyAmbient(ambient, output, additive); + } + + float[] v; + if (light.isPoint()) { + v = light.getPosition().getDataAsFloatArray(); + } else { + v = light.getDirection().getDataAsFloatArray(); + } + + Vector3f vec; + if (transf != null && transf.length == 16) { + if (light.isPoint()) { + vec = transform(v[0], v[1], v[2], transf); + } else { + vec = transformDirection(v[0], v[1], v[2], transf).getNormalized(); + } + } else { + vec = new Vector3f(v[0], v[1], v[2]); + } + + if (mat.isColorMaterialEnable()) { + finalColor = applyDiffuse(vec, !light.isPoint(), vertices, normals, colors, light.getDiffuseColor(), finalColor, true); + } else { + finalColor = applyDiffuse(vec, !light.isPoint(), vertices, normals, diffuse, finalColor, true); + } + + finalColor = applySpecular(camera, vec, mat.getShininess(), !light.isPoint(), vertices, normals, specular, finalColor, true); + + return finalColor; + } + + /** + * return the product of the given colors + */ + private static Color getColorProduct(Color a, Color b) { + float[] ca = a.getComponents(null); + float[] cb = b.getComponents(null); + return new Color(ca[0] * cb[0], ca[1] * cb[1], ca[2] * cb[2]); + } + + /** + * return the clamped product of the color + */ + private static Color getColorProduct(Color a, float f) { + float[] ca = a.getComponents(null); + return new Color(clamp(ca[0] * f), clamp(ca[1] * f), clamp(ca[2] * f)); + } + + /** + * return the clamped sum of the given colors + */ + private static Color getColorSum(Color a, Color b) { + float[] ca = a.getComponents(null); + float[] cb = b.getComponents(null); + return new Color(clamp(ca[0] + cb[0]), clamp(ca[1] + cb[1]), clamp(ca[2] + cb[2])); + } + + /** + * Clamp the given value to [0, 1] + */ + private static float clamp(float f) { + f = f < 0.0f ? 0.0f : f; + f = f > 1.0f ? 1.0f : f; + return f; + } + + static Vector3f reflect(Vector3f I, Vector3f N) { + return I.minus(N.times(2 * I.scalar(N))); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/Motor3D.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/Motor3D.java new file mode 100755 index 000000000..9b6be63b7 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/Motor3D.java @@ -0,0 +1,617 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012-2013 - Scilab Enterprises - Calixte Denizet + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.motor; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.Arrays; +import java.util.List; + +import org.scilab.forge.scirenderer.DrawingTools; +import org.scilab.forge.scirenderer.buffers.ElementsBuffer; +import org.scilab.forge.scirenderer.clipping.ClippingPlane; +import org.scilab.forge.scirenderer.implementation.g2d.G2DCanvas; +import org.scilab.forge.scirenderer.implementation.g2d.buffers.G2DElementsBuffer; +import org.scilab.forge.scirenderer.implementation.g2d.texture.G2DTextureDrawingTools; +import org.scilab.forge.scirenderer.implementation.g2d.texture.G2DTextureManager; +import org.scilab.forge.scirenderer.shapes.appearance.Appearance; +import org.scilab.forge.scirenderer.shapes.geometry.Geometry; +import org.scilab.forge.scirenderer.shapes.geometry.Geometry.FaceCullingMode; +import org.scilab.forge.scirenderer.texture.AnchorPosition; +import org.scilab.forge.scirenderer.texture.Texture; +import org.scilab.forge.scirenderer.tranformations.Transformation; +import org.scilab.forge.scirenderer.tranformations.Vector3d; +import org.scilab.forge.scirenderer.tranformations.Vector3f; + +import org.scilab.forge.scirenderer.lightning.Light; +import org.scilab.forge.scirenderer.lightning.LightManager; +import org.scilab.forge.scirenderer.implementation.g2d.lighting.G2DLight; +import org.scilab.forge.scirenderer.implementation.g2d.lighting.G2DLightManager; +import org.scilab.forge.scirenderer.shapes.appearance.Material; + +/** + * @author Calixte DENIZET + */ +public class Motor3D { + + private Transformation transf; + private Transformation singleTransf; + private FaceCullingMode mode = FaceCullingMode.BOTH; + private Graphics2D g2d; + private Dimension dim; + private G2DTextureDrawingTools textureDrawingTools; + private G2DCanvas canvas; + + /** + * Default constructor + * @param g2d a Graphics2D object where to draw + * @param dim the graphic dimensions + */ + public Motor3D(G2DCanvas canvas, Graphics2D g2d, Dimension dim) { + this.canvas = canvas; + this.g2d = g2d; + this.dim = dim; + this.textureDrawingTools = new G2DTextureDrawingTools(g2d); + AbstractDrawable3DObject.resetDefaultPrecedence(); + } + + public void setGraphics(Graphics2D g2d) { + this.g2d = g2d; + this.textureDrawingTools.setGraphics(g2d); + } + + public void setAntialiased(boolean aa) { + if (aa) { + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + } else { + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + } + } + + public boolean is2DView() { + return canvas.getMainDrawer().is2DView(); + } + + public void setClippingPlanes(List<ClippingPlane> clippingPlanes) { + Scene.setClippingPlanes(clippingPlanes); + } + + /** + * Set the face culling mode + * @param mode the mode to set + */ + public void setFaceCullingMode(FaceCullingMode mode) { + this.mode = mode; + } + + /** + * Set the current transformation + * @param transf the transformation to set + */ + public void setTransformation(Transformation transf, Transformation single) { + this.transf = transf; + this.singleTransf = single; + } + + public Transformation getCurrentTransformation() { + return transf; + } + + public Transformation getCurrentSingleTransformation() { + return singleTransf; + } + + /** + * Reset this motor + * @param color the filling color + */ + public void reset(Color color) { + transf = null; + mode = FaceCullingMode.BOTH; + g2d.setColor(color); + g2d.fillRect(0, 0, (int) dim.getWidth(), (int) dim.getHeight()); + Scene.clear(); + } + + /** + * Clear the depth buffer + */ + public void clearDepth() { + Scene.clearDepth(); + } + + /** + * Draw the scene in the Graphics2D + */ + public void draw() { + Scene.drawRoot(g2d); + clean(); + } + + public void clean() { + Scene.clearAll(); + G2DTextureManager.clear(); + } + + public void drawTexture(DrawingTools drawingTools, BufferedImage image, Texture texture) { + try { + SpritedRectangle o = new SpritedRectangle(new Vector3d(0, 0, 0), transf, image, texture.getMagnificationFilter()); + add(o); + } catch (InvalidPolygonException e) { } + } + + /** + * Add the geometry to the scene + * @param drawingTools the DrawingTools + * @param geometry the geometry to draw + * @param appearance the appearance to use + */ + public void draw(DrawingTools drawingTools, Geometry geometry, Appearance appearance) { + setFaceCullingMode(geometry.getFaceCullingMode()); + FloatBuffer vertexBuffer = geometry.getVertices().getData(); + + IntBuffer indicesBuffer = null;; + if (geometry.getIndices() != null) { + indicesBuffer = geometry.getIndices().getData(); + } + + IntBuffer wireIndicesBuffer = null; + if (geometry.getWireIndices() != null) { + wireIndicesBuffer = geometry.getWireIndices().getData(); + } + + FloatBuffer colorBuffer = null; + if (geometry.getColors() != null) { + colorBuffer = geometry.getColors().getData(); + } + + FloatBuffer normalBuffer = null; + if (geometry.getNormals() != null) { + normalBuffer = geometry.getNormals().getData(); + } + + Texture texture = appearance.getTexture(); + FloatBuffer textureCoordinatesBuffer = null; + BufferedImage image = null; + if (texture != null && geometry.getTextureCoordinates() != null) { + textureCoordinatesBuffer = geometry.getTextureCoordinates().getData(); + image = ((G2DTextureManager.G2DTexture) texture).getImage(); + } + + G2DLightManager lm = (G2DLightManager)drawingTools.getLightManager(); + lm.setMaterial(appearance.getMaterial()); + + if (geometry.getFillDrawingMode() != Geometry.FillDrawingMode.NONE) { + addTriangles(vertexBuffer, normalBuffer, colorBuffer, appearance.getFillColor(), indicesBuffer, textureCoordinatesBuffer, image, texture, geometry.getFillDrawingMode(), lm); + } + + if (geometry.getLineDrawingMode() != Geometry.LineDrawingMode.NONE) { + if (appearance.getLineColor() == null) { + addSegments(vertexBuffer, colorBuffer, null, wireIndicesBuffer, geometry.getLineDrawingMode(), appearance); + } else { + addSegments(vertexBuffer, null, appearance.getLineColor(), wireIndicesBuffer, geometry.getLineDrawingMode(), appearance); + } + } + } + + public void draw(DrawingTools drawingTools, Texture texture, AnchorPosition anchor, ElementsBuffer positions, int offset, int stride, double rotationAngle) { + FloatBuffer positionsBuffer = positions.getData(); + float[] buffer; + offset = offset < 0 ? 0 : offset; + stride = stride < 1 ? 1 : stride; + + positionsBuffer.rewind(); + if (positionsBuffer.hasArray()) { + buffer = positionsBuffer.array(); + } else { + buffer = new float[positionsBuffer.limit()]; + positionsBuffer.get(buffer); + } + + Vector3d[] verticesArray = getMultiVectors(buffer, transf, false); + for (int i = offset; i < verticesArray.length; i += stride) { + try { + SpritedRectangle o = new SpritedRectangle(verticesArray[i], texture, anchor, textureDrawingTools, rotationAngle); + add(o); + } catch (InvalidPolygonException e) { } + } + } + + public void draw(DrawingTools drawingTools, Texture texture, AnchorPosition anchor, Vector3d position, double rotationAngle) { + try { + add(new SpritedRectangle(transf.project(position), texture, anchor, textureDrawingTools, rotationAngle)); + } catch (InvalidPolygonException e) { } + } + + /** + * Add a Triangle to the scene + * @param tri the triangle to add + */ + private void add(Triangle tri) { + final boolean is2d = is2DView(); + if (is2d) { + Scene.addToRoot(is2d, tri); + } else { + Vector3d normal = tri.getNormal(); + if (normal != null) { + //normal = transf.projectDirection(normal); + if ((mode == FaceCullingMode.CW && normal.getZ() > 0) || (mode == FaceCullingMode.CCW && normal.getZ() < 0) || mode == FaceCullingMode.BOTH) { + Scene.addToRoot(is2d, tri); + } + } else { + Scene.addToRoot(is2d, tri); + } + } + } + + /** + * Add a segment to the scene + * @param s the segment to add + */ + private void add(Segment s) { + Scene.addToRoot(is2DView(), s); + } + + private void add(SpritedRectangle sprite) { + Scene.addToRoot(is2DView(), sprite); + } + + private void add(PolyLine p) { + Scene.addToRoot(is2DView(), p); + } + + /** + * Get arrays from Buffer + * @param vertices a buffer containing vertices + * @param colors a buffer containing the colors + * @param defaultColor the color to use when colors is null + * @param indices a buffer containg the index of the vertices to retrieve + * @return an array of length 2 containing the vertices array and the colors array + */ + private Object[] getArrays(FloatBuffer vertices, FloatBuffer colors, Color defaultColor, FloatBuffer textureCoords, IntBuffer indices) { + float[] buffer; + Vector3d[] verticesArray; + Vector3d[] textureCoordsArray = null; + Color[] colorsArray; + + vertices.rewind(); + if (vertices.hasArray()) { + buffer = vertices.array(); + } else { + buffer = new float[vertices.limit()]; + vertices.get(buffer); + } + verticesArray = getMultiVectors(buffer, transf, false); + + if (colors != null) { + colors.rewind(); + if (colors.hasArray()) { + buffer = colors.array(); + } else { + buffer = new float[colors.limit()]; + colors.get(buffer); + } + colorsArray = getMultiColors(buffer); + } else { + colorsArray = new Color[vertices.limit() / G2DElementsBuffer.ELEMENT_SIZE]; + Arrays.fill(colorsArray, defaultColor); + } + + if (textureCoords != null) { + textureCoords.rewind(); + if (textureCoords.hasArray()) { + buffer = textureCoords.array(); + } else { + buffer = new float[textureCoords.limit()]; + textureCoords.get(buffer); + } + textureCoordsArray = getMultiVectors(buffer); + } + + if (indices != null) { + indices.rewind(); + int[] ind; + if (indices.hasArray()) { + ind = indices.array(); + } else { + ind = new int[indices.limit()]; + indices.get(ind); + } + Vector3d[] va = new Vector3d[ind.length]; + Color[] ca = new Color[ind.length]; + Vector3d[] ta = null; + if (textureCoords != null) { + ta = new Vector3d[ind.length]; + } + + for (int i = 0; i < ind.length; i++) { + va[i] = verticesArray[ind[i]]; + ca[i] = colorsArray[ind[i]]; + if (ta != null) { + ta[i] = textureCoordsArray[ind[i]]; + } + } + verticesArray = va; + colorsArray = ca; + textureCoordsArray = ta; + } + + return new Object[] {verticesArray, colorsArray, textureCoordsArray}; + } + + /** + * Convert the buffer vertices into vertices array and put the segments in the scene + * @param vertices a buffer containing vertices + * @param colors a buffer containing the colors + * @param defaultColor the color to use when colors is null + * @param indices a buffer containg the index of the vertices to retrieve + * @param drawingMode the drawing mode + * @param stroke the Stroke to use to draw a segment + */ + private void addSegments(FloatBuffer vertices, FloatBuffer colors, Color defaultColor, IntBuffer indices, Geometry.LineDrawingMode drawingMode, Appearance appearance) { + Object[] arrays = getArrays(vertices, colors, defaultColor, null, indices); + Vector3d[] verticesArray = (Vector3d[]) arrays[0]; + Color[] colorsArray = (Color[]) arrays[1]; + Vector3d[] v; + Color[] c; + double cumLength = 0; + + if (verticesArray.length <= 1) { + return; + } + + switch (drawingMode) { + case SEGMENTS_STRIP : + if (is2DView()) { + List<PolyLine> list = PolyLine.getPolyLines(verticesArray, colorsArray, G2DStroke.getStroke(appearance, 0), false); + for (PolyLine p : list) { + add(p); + } + } else { + for (int i = 0; i < verticesArray.length - 1; i++) { + v = new Vector3d[] {verticesArray[i], verticesArray[i + 1]}; + c = new Color[] {colorsArray[i], colorsArray[i + 1]}; + try { + add(new Segment(v, c, G2DStroke.getStroke(appearance, cumLength), false)); + cumLength += Segment.getLength(v); + } catch (InvalidPolygonException e) { + cumLength = 0; + } + } + } + break; + case SEGMENTS_LOOP : + if (is2DView()) { + List<PolyLine> list = PolyLine.getPolyLines(verticesArray, colorsArray, G2DStroke.getStroke(appearance, 0), true); + for (PolyLine p : list) { + add(p); + } + } else { + for (int i = 0; i < verticesArray.length - 1; i++) { + v = new Vector3d[] {verticesArray[i], verticesArray[i + 1]}; + c = new Color[] {colorsArray[i], colorsArray[i + 1]}; + try { + add(new Segment(v, c, G2DStroke.getStroke(appearance, cumLength), false)); + cumLength += Segment.getLength(v); + } catch (InvalidPolygonException e) { + cumLength = 0; + } + } + int n = verticesArray.length - 1; + v = new Vector3d[] {verticesArray[n], verticesArray[0]}; + c = new Color[] {colorsArray[n], colorsArray[0]}; + try { + add(new Segment(v, c, G2DStroke.getStroke(appearance, cumLength), false)); + } catch (InvalidPolygonException e) { } + } + break; + case SEGMENTS : + default : + for (int i = 0; i < verticesArray.length - 1; i += 2) { + v = new Vector3d[] {verticesArray[i], verticesArray[i + 1]}; + c = new Color[] {colorsArray[i], colorsArray[i + 1]}; + try { + add(new Segment(v, c, G2DStroke.getStroke(appearance, 0), is2DView())); + } catch (InvalidPolygonException e) { } + } + break; + } + } + + /** + * Convert the buffer vertices into vertices array and put the triangles in the scene + * @param vertices a buffer containing vertices + * @param normals a buffer containing the normals (not used) + * @param colors a buffer containing the colors + * @param defaultColor the color to use when colors is null + * @param indices a buffer containg the index of the vertices to retrieve + * @param drawingMode the drawing mode + */ + private void addTriangles(FloatBuffer vertices, FloatBuffer normals, FloatBuffer colors, Color defaultColor, IntBuffer indices, FloatBuffer textureCoords, final BufferedImage image, Texture texture, Geometry.FillDrawingMode drawingMode, G2DLightManager lightManager) { + Object[] arrays = getArrays(vertices, colors, defaultColor, textureCoords, indices); + Vector3d[] verticesArray = (Vector3d[]) arrays[0]; + Color[] colorsArray = (Color[]) arrays[1]; + Vector3d[] textureCoordsArray = (Vector3d[]) arrays[2]; + Vector3d[] v; + Color[] c; + Texture.Filter filter = Texture.Filter.NEAREST; + + if (texture != null) { + filter = texture.getMagnificationFilter(); + } + + colorsArray = applyLighting(vertices, normals, indices, colorsArray, lightManager); + + switch (drawingMode) { + case TRIANGLE_FAN : + for (int i = 1; i < verticesArray.length - 1; i++) { + v = new Vector3d[] {verticesArray[0], verticesArray[i], verticesArray[i + 1]}; + try { + if (image == null) { + c = new Color[] {colorsArray[0], colorsArray[i], colorsArray[i + 1]}; + add(new Triangle(v, c, null)); + } else { + add(new Triangle(v, new Vector3d[] {textureCoordsArray[0], textureCoordsArray[i], textureCoordsArray[i + 1]}, image, filter)); + } + } catch (InvalidPolygonException e) { } + } + int n = verticesArray.length - 1; + v = new Vector3d[] {verticesArray[0], verticesArray[n], verticesArray[1]}; + try { + if (image == null) { + c = new Color[] {colorsArray[0], colorsArray[n], colorsArray[1]}; + add(new Triangle(v, c, null)); + } else { + add(new Triangle(v, new Vector3d[] {textureCoordsArray[0], textureCoordsArray[n], textureCoordsArray[1]}, image, filter)); + } + } catch (InvalidPolygonException e) { } + break; + case TRIANGLE_STRIP : + for (int i = 0; i < verticesArray.length - 2; i++) { + v = new Vector3d[] {verticesArray[i], verticesArray[i + 1], verticesArray[i + 2]}; + try { + if (image == null) { + c = new Color[] {colorsArray[i], colorsArray[i + 1], colorsArray[i + 2]}; + add(new Triangle(v, c, null)); + } else { + add(new Triangle(v, new Vector3d[] {textureCoordsArray[i], textureCoordsArray[i + 1], textureCoordsArray[i + 2]}, image, filter)); + } + } catch (InvalidPolygonException e) { } + } + break; + case TRIANGLES : + default : + for (int i = 0; i < verticesArray.length - 2; i += 3) { + v = new Vector3d[] {verticesArray[i], verticesArray[i + 1], verticesArray[i + 2]}; + try { + if (image == null) { + c = new Color[] {colorsArray[i], colorsArray[i + 1], colorsArray[i + 2]}; + add(new Triangle(v, c, null)); + } else { + add(new Triangle(v, new Vector3d[] {textureCoordsArray[i], textureCoordsArray[i + 1], textureCoordsArray[i + 2]}, image, filter)); + } + } catch (InvalidPolygonException e) { } + } + break; + } + } + + /** + * Convert an array of float into an array of Vector3d objects + * @param vertices an array of float containing (vertices.length / G2DElementsBuffer.ELEMENT_SIZE) vectors coordinates + * @param t the transformation to use for the projection + * @param dir if true t.projectDirection() is used rather than t.project() + * @return an array of Vector3d containg the vertices + */ + private static final Vector3d[] getMultiVectors(final float[] vertices, final Transformation t, final boolean dir) { + Vector3d[] v = new Vector3d[vertices.length / G2DElementsBuffer.ELEMENT_SIZE]; + if (dir) { + int j = 0; + for (int i = 0; i < v.length; i++) { + v[i] = t.projectDirection(new Vector3d(vertices[j], vertices[j + 1], vertices[j + 2])); + j += G2DElementsBuffer.ELEMENT_SIZE; + } + } else { + int j = 0; + for (int i = 0; i < v.length; i++) { + v[i] = t.project(new Vector3d(vertices[j], vertices[j + 1], vertices[j + 2])); + j += G2DElementsBuffer.ELEMENT_SIZE; + } + } + + return v; + } + + /** + * Convert an array of float into an array of Vector3d objects + * @param vertices an array of float containing (vertices.length / G2DElementsBuffer.ELEMENT_SIZE) vectors coordinates + * @return an array of Vector3d containg the vertices + */ + private static final Vector3d[] getMultiVectors(final float[] vertices) { + Vector3d[] v = new Vector3d[vertices.length / G2DElementsBuffer.ELEMENT_SIZE]; + int j = 0; + for (int i = 0; i < v.length; i++) { + v[i] = new Vector3d(vertices[j], vertices[j + 1], vertices[j + 2]); + j += G2DElementsBuffer.ELEMENT_SIZE; + } + + return v; + } + + /** + * Convert a float array into a Color array + * @param colors a float array + * @return an array of Color + */ + private static final Color[] getMultiColors(final float[] colors) { + Color[] c = new Color[colors.length / G2DElementsBuffer.ELEMENT_SIZE]; + int j = 0; + Color prev = Color.BLACK; + for (int i = 0; i < c.length; i++) { + c[i] = new Color(colors[j], colors[j + 1], colors[j + 2], colors[j + 3]); + if (prev.equals(c[i])) { + c[i] = prev; + } + prev = c[i]; + j += G2DElementsBuffer.ELEMENT_SIZE; + } + + return c; + } + + /** + * Perform per-vertex lighting + */ + private Color[] applyLighting(FloatBuffer vertices, FloatBuffer normals, IntBuffer index, Color[] colors, G2DLightManager lightManager) { + + if (!lightManager.isLightningEnable() || vertices == null || normals == null + || index == null || colors == null) { + return colors; + } + + Material mat = lightManager.getMaterial(); + if (mat == null) { + return colors; + } + + float[] vertexTransf = lightManager.getVertexTransform(); + float[] normalTransf = lightManager.getNormalTransform(); + //for transformed vertices camera is at origin. + Vector3f camera = new Vector3f(0.f, 0.f , 0.f); + Vector3f[] vertexArray = LightHelper.getIndexedVector3f(vertices, index, G2DElementsBuffer.ELEMENT_SIZE, vertexTransf); + Vector3f[] normalArray = LightHelper.getIndexedVector3f(normals, index, G2DElementsBuffer.ELEMENT_SIZE, normalTransf); + + for (int i = 0; i < normalArray.length; ++i) { + normalArray[i] = normalArray[i].getNormalized(); + } + + + Color[] outColors = new Color[colors.length]; + boolean first = true; + for (int i = 0; i < lightManager.getLightNumber(); ++i) { + G2DLight l = (G2DLight)lightManager.getLight(i); + + if (l == null || !l.isEnable()) { + continue; + } + + outColors = LightHelper.applyLight(l, mat, camera, vertexArray, normalArray, colors, outColors, vertexTransf, !first); + first = false; + } + return outColors; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/PolyLine.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/PolyLine.java new file mode 100755 index 000000000..095e65bb6 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/PolyLine.java @@ -0,0 +1,317 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2013 - Scilab Enterprises - Calixte Denizet + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.motor; + +import java.awt.Color; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.Graphics2D; +import java.util.ArrayList; +import java.util.List; + +import org.scilab.forge.scirenderer.tranformations.Vector3d; +import org.scilab.forge.scirenderer.tranformations.Vector4d; + +/** + * Class to represent a polyline object. This is used only in 2D (in 3D, a line could begin + * on front of a face and could end behind it). + * This allows to draw a polyline like a polyline ! and not as a set of segments. + * Notice that a PolyLine is NOT a convex object... but in 2D it does not matter, algorithms + * which use the convexity are not applyed. + * + * @author Calixte DENIZET + */ +public class PolyLine extends ConvexObject { + + private boolean monochromatic; + private G2DStroke stroke; + protected double[] clip = new double[] {Double.NaN, Double.NaN, Double.NaN, Double.NaN}; + + /** + * Default constructor + * @param vertices the polyline vertices + * @param colors the vertices color + * @param stroke the stroke to used + */ + public PolyLine(Vector3d[] vertices, Color[] colors, G2DStroke stroke) throws InvalidPolygonException { + super(vertices, colors); + if (vertices.length <= 1) { + throw new InvalidPolygonException("A polyline cannot have one or zero point"); + } + + this.monochromatic = isMonochromatic(colors); + this.stroke = stroke; + } + + /** + * Get a set of polylines. The Nan of Inf vectors are removed and so the polyline is splitted. + * @param vertices the polyline vertices + * @param colors the vertices color + * @param stroke the stroke to used + * @param loop if true a looping polyline is created + * @return a set of polylines + */ + public static List<PolyLine> getPolyLines(Vector3d[] vertices, Color[] colors, G2DStroke stroke, boolean loop) { + if (loop) { + Vector3d[] v = new Vector3d[vertices.length + 1]; + Color[] c = new Color[vertices.length + 1]; + for (int i = 0; i < vertices.length; i++) { + v[i] = vertices[i]; + c[i] = colors[i]; + } + v[vertices.length] = v[0]; + c[vertices.length] = c[0]; + vertices = v; + colors = c; + } + + int pos = 0; + List<PolyLine> list = new ArrayList<PolyLine>(1); + while ((pos = trimLeft(vertices, pos)) != -1) { + final int second = findNanOrInf(vertices, pos + 1); + final int len = second - pos; + final Vector3d[] newVertices = new Vector3d[len]; + final Color[] newColors = new Color[len]; + for (int i = 0; i < len; i++) { + newVertices[i] = vertices[pos + i]; + newColors[i] = colors[pos + i]; + } + pos = second + 1; + try { + list.add(new PolyLine(newVertices, newColors, stroke)); + } catch (InvalidPolygonException e) { } + } + + return list; + } + + /** + * {@inheritDoc} + */ + public List<ConvexObject> breakObject(Vector4d v) { + final double[] vv = v.getData(); + final List<ConvexObject> list = new ArrayList<ConvexObject>(1); + + // Since PolyLine are only used in 2D it is useless to check when z != 0 + if (vv[2] == 0) { + final Vector3d np = new Vector3d(vv); + ConvexObject.makeClip(clip, vv); + + int pos = 0; + boolean prev = false; + + for (int i = 0; i < vertices.length; i++) { + final boolean b = isBehind(vertices[i], np, vv[3]); + if (b && !prev) { + pos = i; + prev = true; + } else if (!b && prev) { + prev = false; + try { + list.add(cut(pos, i, np, vv[3])); + } catch (InvalidPolygonException e) { } + } + } + + if (prev) { + try { + list.add(cut(pos, vertices.length, np, vv[3])); + } catch (InvalidPolygonException e) { } + } + } else { + list.add(this); + } + + return list; + } + + /** + * {@inheritDoc} + */ + public List<ConvexObject> breakObject(ConvexObject o) { + return null; + } + + /** + * {@inheritDoc} + */ + protected boolean isDegenerate() { + return false; + } + + /** + * {@inheritDoc} + */ + protected boolean isNanOrInf() { + return false; + } + + /** + * {@inheritDoc} + */ + public Vector3d getNormal() { + // Never used + return null; + } + + /** + * {@inheritDoc} + */ + protected boolean isPlanar() { + // Never used + return true; + } + + /** + * Cut a polyline on a clipping plane + * @param first the first vertex position + * @param second the second vertex position + * @param np the normal vector of the clipping plane + * @param C the constant part of the hyperplane + * @return a cutted PolyLine + */ + private PolyLine cut(int first, int second, Vector3d np, double C) throws InvalidPolygonException { + final boolean cutAtBegin = first != 0; + final boolean cutAtEnd = second != vertices.length; + + if (!cutAtBegin && !cutAtEnd) { + return this; + } + + if (cutAtBegin && cutAtEnd) { + final int len = second - first + 2; + + Vector3d[] newVertices = new Vector3d[len]; + Color[] newColors = new Color[len]; + + for (int i = 1; i < len - 1; i++) { + newVertices[i] = vertices[first + i - 1]; + newColors[i] = getColor(first + i - 1); + } + + double c = (C + vertices[first].scalar(np)) / vertices[first].minus(vertices[first - 1]).scalar(np); + newVertices[0] = Vector3d.getBarycenter(vertices[first - 1], vertices[first], c, 1 - c); + newColors[0] = getColorsBarycenter(getColor(first - 1), getColor(first), c, 1 - c); + + c = (C + vertices[second].scalar(np)) / vertices[second].minus(vertices[second - 1]).scalar(np); + newVertices[len - 1] = Vector3d.getBarycenter(vertices[second - 1], vertices[second], c, 1 - c); + newColors[len - 1] = getColorsBarycenter(getColor(second - 1), getColor(second), c, 1 - c); + + return new PolyLine(newVertices, newColors, this.stroke); + } + + if (cutAtBegin) { + final double c = (C + vertices[first].scalar(np)) / vertices[first].minus(vertices[first - 1]).scalar(np); + final int len = second - first + 1; + + Vector3d[] newVertices = new Vector3d[len]; + Color[] newColors = new Color[len]; + + for (int i = 1; i < len; i++) { + newVertices[i] = vertices[first + i - 1]; + newColors[i] = getColor(first + i - 1); + } + + newVertices[0] = Vector3d.getBarycenter(vertices[first - 1], vertices[first], c, 1 - c); + newColors[0] = getColorsBarycenter(getColor(first - 1), getColor(first), c, 1 - c); + + return new PolyLine(newVertices, newColors, this.stroke); + } else { + final double c = (C + vertices[second].scalar(np)) / vertices[second].minus(vertices[second - 1]).scalar(np); + final int len = second - first + 1; + + Vector3d[] newVertices = new Vector3d[len]; + Color[] newColors = new Color[len]; + + for (int i = 0; i < len - 1; i++) { + newVertices[i] = vertices[first + i]; + newColors[i] = getColor(first + i); + } + + newVertices[len - 1] = Vector3d.getBarycenter(vertices[second - 1], vertices[second], c, 1 - c); + newColors[len - 1] = getColorsBarycenter(getColor(second - 1), getColor(second), c, 1 - c); + return new PolyLine(newVertices, newColors, this.stroke); + } + } + + @Override + public void draw(Graphics2D g2d) { + Stroke oldStroke = g2d.getStroke(); + Shape oldClip = g2d.getClip(); + + Shape newClip = ConvexObject.getClip(clip); + if (newClip != null) { + g2d.clip(newClip); + } + + if (monochromatic) { + g2d.setColor(getColor(0)); + g2d.setStroke(stroke); + g2d.draw(getProjectedPolyLine()); + } else { + // on peut surement faire mieux ici + // avec un LinearGradientPaint + Vector3d start = vertices[0]; + Color color = getColor(0); + double cumLen = 0; + float[] dashArray = stroke.getDashArray(); + float lwidth = stroke.getLineWidth(); + for (int i = 1; i < vertices.length; i++) { + Stroke nstroke = new G2DStroke(lwidth, dashArray, (float) cumLen); + g2d.setStroke(nstroke); + g2d.setColor(color); + g2d.draw(new Line2D.Double(start.getX(), start.getY(), vertices[i].getX(), vertices[i].getY())); + cumLen += Math.hypot(start.getX() - vertices[i].getX(), start.getY() - vertices[i].getY()); + } + } + + if (newClip != null) { + g2d.setClip(oldClip); + } + + g2d.setStroke(oldStroke); + } + + /** + * Get first non Nan (or Inf) vector position + * @param v the array to trim + * @param start the starting position + * @return index of the first non Nan vector or -1 if not found + */ + private static int trimLeft(Vector3d[] v, int start) { + for (int i = start; i < v.length; i++) { + if (!isNanOrInf(v[i])) { + return i; + } + } + + return -1; + } + + /** + * Get first Nan (or Inf) vector position + * @param v the array to trim + * @param start the starting position + * @return index of the first Nan vector + */ + private static int findNanOrInf(Vector3d[] v, int start) { + for (int i = start; i < v.length; i++) { + if (isNanOrInf(v[i])) { + return i; + } + } + + return v.length; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/Scene.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/Scene.java new file mode 100755 index 000000000..06d429b81 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/Scene.java @@ -0,0 +1,250 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixte Denizet + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.motor; + +import java.awt.Graphics2D; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Stack; +import java.util.TreeSet; + +import org.scilab.forge.scirenderer.clipping.ClippingPlane; + +final class Scene { + + private static Set<Scene> faces2d = new TreeSet<Scene>(new Comparator<Scene>() { + public int compare(Scene o1, Scene o2) { + if (o1.object.vertices[0].getZ() == o2.object.vertices[0].getZ()) { + return o1.object.getPrecedence() - o2.object.getPrecedence(); + } + + return (int) Math.signum(o2.object.vertices[0].getZ() - o1.object.vertices[0].getZ()); + } + + public boolean equals(Object obj) { + return this == obj; + } + }); + + private static List<Scene> faces = new ArrayList<Scene>(); + private static List<Scene> disabledFaces = new ArrayList<Scene>(); + private static List<ClippingPlane> clippingPlanes; + private static Stack<Scene> stack = new Stack<Scene>();; + + private List<Scene> behind; + private List<Scene> onfront; + private ConvexObject object; + private boolean drawn; + private boolean is2d; + + private Scene(final ConvexObject object) { + this.object = object; + } + + public static final void setClippingPlanes(final List<ClippingPlane> clippingPlanes) { + Scene.clippingPlanes = clippingPlanes; + } + + private static final List<ConvexObject> breakOnClippingPlane(ConvexObject o) { + List<ConvexObject> list = new ArrayList<ConvexObject>(8); + List<ConvexObject> tmp = new ArrayList<ConvexObject>(8); + list.add(o); + if (clippingPlanes != null) { + for (ClippingPlane clip : clippingPlanes) { + if (clip.isEnable()) { + tmp.clear(); + for (ConvexObject co : list) { + List<ConvexObject> l = co.breakObject(clip.getEquation()); + if (l != null) { + tmp.addAll(l); + } else { + list.clear(); + return list; + } + } + list.clear(); + list.addAll(tmp); + if (list.isEmpty()) { + break; + } + } + } + } + return list; + } + + static final void addToRoot(final boolean is2D, final ConvexObject co) { + try { + List<ConvexObject> broken = breakOnClippingPlane(co); + for (ConvexObject object : broken) { + add(is2D, object); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static final void add(final boolean is2D, final ConvexObject object) { + synchronized (faces) { + Scene st = new Scene(object); + if (is2D) { + st.is2d = true; + faces2d.add(st); + } else { + Set<Scene> toRemove = new HashSet<Scene>(); + List<ConvexObject> toAdd = null; + + for (Scene face : faces) { + if (face.object instanceof Triangle && object instanceof Segment) { + if (((Triangle) face.object).addSegment((Segment) object)) { + if (st != null) { + toRemove.add(st); + st = null; + } + } + } else if (object instanceof Triangle && face.object instanceof Segment) { + if (((Triangle) object).addSegment((Segment) face.object)) { + toRemove.add(face); + } + } + + if (st != null) { + int r = face.object.isBehind(st.object); + + if (r == 1) { + // object is behind face.object + addBehind(face, st); + addOnFront(st, face); + } else if (r == -1) { + // face.object is behind object + addBehind(st, face); + addOnFront(face, st); + } else if (r == 2) { + // collision + toAdd = face.object.breakObject(st.object); + if (toAdd != null && !toAdd.isEmpty()) { + toRemove.add(face); + toRemove.add(st); + st = null; + break; + } + } + } + } + + if (st != null) { + faces.add(st); + } + + for (Scene s : toRemove) { + faces.remove(s); + if (s.onfront != null) { + for (Scene ss : s.onfront) { + ss.behind.remove(s); + } + } + } + if (toAdd != null) { + for (ConvexObject co : toAdd) { + add(false, co); + } + } + } + } + } + + + static final void clearDepth() { + disabledFaces.addAll(faces); + disabledFaces.addAll(faces2d); + faces.clear(); + faces2d.clear(); + } + + static final void drawRoot(final Graphics2D g2d) { + synchronized (faces) { + for (Scene face : disabledFaces) { + face.draw(g2d); + } + + for (Scene face : disabledFaces) { + face.drawn = false; + } + + for (Scene face : faces) { + face.draw(g2d); + } + + for (Scene face : faces2d) { + face.draw(g2d); + } + + // code to help debug + /*for (Scene face : faces) { + face.drawn = false; + } + + for (Scene face : faces) { + if (face.object.marked) { + face.object.draw(g2d); + } + }*/ + } + } + + static final void clear() { + disabledFaces.clear(); + faces.clear(); + faces2d.clear(); + stack.clear(); + } + + static final void clearAll() { + clear(); + clippingPlanes.clear(); + } + + private final void draw(final Graphics2D g2d) { + if (stack.contains(this)) { + this.object.addArea(stack.peek().object); + return; + } + if (!drawn) { + stack.push(this); + drawn = true; + if (behind != null && !behind.isEmpty()) { + for (Scene face : behind) { + face.draw(g2d); + } + } + object.draw(g2d); + stack.pop(); + } + } + + private static final void addBehind(Scene face, Scene s) { + if (face.behind == null) { + face.behind = new ArrayList<Scene>(); + } + face.behind.add(s); + } + + private static final void addOnFront(Scene face, Scene s) { + if (face.onfront == null) { + face.onfront = new ArrayList<Scene>(); + } + face.onfront.add(s); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/Segment.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/Segment.java new file mode 100755 index 000000000..4c924a01b --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/Segment.java @@ -0,0 +1,247 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixte Denizet + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.motor; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.Path2D; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.scilab.forge.scirenderer.tranformations.Vector3d; +import org.scilab.forge.scirenderer.tranformations.Vector4d; + +/** + * Segment object (for info, when modify rendering check for PolyLine too). + * + * @author Calixte DENIZET + */ +public class Segment extends ConvexObject implements Comparable<Segment> { + + private int hash = -1; + protected G2DStroke stroke; + protected List<ConvexObject> segmentOn; + protected boolean is2D; + protected double[] clip = new double[] {Double.NaN, Double.NaN, Double.NaN, Double.NaN}; + + public Segment(Vector3d[] vertices, Color[] colors, G2DStroke stroke, boolean is2D) throws InvalidPolygonException { + super(vertices, colors); + if (vertices.length != 2) { + throw new InvalidPolygonException("Invalid segment: must have 2 vertices."); + } + this.stroke = stroke; + this.is2D = is2D; + } + + public Segment(Vector3d[] vertices, Color[] colors) throws InvalidPolygonException { + this(vertices, colors, null, false); + } + + public void setStroke(G2DStroke stroke) { + this.stroke = stroke; + } + + public double getLength() { + return vertices[0].minus(vertices[1]).getNorm(); + } + + public static double getLength(Vector3d[] vertices) { + return vertices[0].minus(vertices[1]).getNorm(); + } + + public void addConvexObject(ConvexObject co) { + if (segmentOn == null) { + segmentOn = new ArrayList<ConvexObject>(2); + } + segmentOn.add(co); + } + + public void removeConvexObject(ConvexObject co) { + if (segmentOn != null) { + segmentOn.remove(co); + } + } + + public void replaceSegment(List<Segment> segs) { + if (segmentOn != null) { + for (ConvexObject co : segmentOn) { + Triangle t = (Triangle) co; + t.replaceSegment(this, segs); + } + } + } + + public boolean isIn2D() { + return isNull(vertices[0].getZ()) && isNull(vertices[1].getZ()); + } + + public boolean isInFront() { + return isEqual(vertices[0].getZ(), -0.5) && isEqual(vertices[1].getZ(), -0.5); + } + + @Override + public int compareTo(Segment o) { + if (equals(o)) { + return 0; + } + + return getPrecedence() - o.getPrecedence(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Segment) { + Segment s = (Segment) o; + return (s.vertices[0].equals(vertices[0]) && s.vertices[1].equals(vertices[1]) && s.getColor(0).equals(getColor(0)) && s.getColor(1).equals(getColor(1))) || (s.vertices[1].equals(vertices[0]) && s.vertices[0].equals(vertices[1]) && s.getColor(1).equals(getColor(0)) && s.getColor(0).equals(getColor(1))); + } + + return false; + } + + @Override + public int isBehind(ConvexObject o) { + if (o instanceof Triangle && ((Triangle) o).isSegmentAcross(this)) { + return 1; + } + + return super.isBehind(o); + } + + @Override + public List<ConvexObject> breakObject(ConvexObject o) { + if (o instanceof Triangle) { + return ((Triangle) o).breakObject(this); + } else if (o instanceof SpritedRectangle) { + return ((SpritedRectangle) o).breakObject(this); + } + + return null; + } + + @Override + public List<ConvexObject> breakObject(Vector4d v) { + double[] vv = v.getData(); + + if (is2D && vv[2] == 0) { + ConvexObject.makeClip(clip, vv); + } + getNormal(); + Vector3d np = new Vector3d(vv); + boolean a = isBehind(vertices[0], np, vv[3]); + boolean b = isBehind(vertices[1], np, vv[3]); + + if (a && b) { + List<ConvexObject> list = new ArrayList<ConvexObject>(1); + list.add(this); + return list; + } + + if (!a && !b) { + return null; + } + + double c = (vv[3] + vertices[1].scalar(np)) / v0.scalar(np); + Vector3d p = Vector3d.getBarycenter(vertices[0], vertices[1], c, 1 - c); + Color color = getColorsBarycenter(getColor(0), getColor(1), c, 1 - c); + Segment s; + + try { + if (a) { + s = new Segment(new Vector3d[] {vertices[0], p}, new Color[] {getColor(0), color}, this.stroke, this.is2D); + } else { + s = new Segment(new Vector3d[] {p, vertices[1]}, new Color[] {color, getColor(1)}, this.stroke, this.is2D); + } + + List<ConvexObject> list = new ArrayList<ConvexObject>(1); + list.add(s); + + return list; + } catch (InvalidPolygonException e) { } + + return null; + } + + public List<Segment> breakObject(Vector3d p, Vector3d u, Vector3d n) { + getNormal(); + double c = vertices[1].minus(p).scalar(n) / v0.scalar(n); + if (c > 0 && !isNull(c) && c < 1 && !isEqual(c, 1)) { + List<Segment> list = new ArrayList<Segment>(2); + Vector3d q = Vector3d.getBarycenter(vertices[0], vertices[1], c, 1 - c); + Color color = getColorsBarycenter(getColor(0), getColor(1), c, 1 - c); + try { + list.add(new Segment(new Vector3d[] {vertices[0], q}, new Color[] {getColor(0), color}, stroke, this.is2D)); + list.add(new Segment(new Vector3d[] {q, vertices[1]}, new Color[] {color, getColor(1)}, stroke, this.is2D)); + + return list; + } catch (InvalidPolygonException e) { } + } else { + List<Segment> list = new ArrayList<Segment>(1); + try { + list.add(new Segment(new Vector3d[] {vertices[0], vertices[1]}, new Color[] {getColor(0), getColor(1)}, stroke, this.is2D)); + + return list; + } catch (InvalidPolygonException e) { } + } + + return null; + } + + @Override + public void draw(Graphics2D g2d) { + if (segmentOn == null || segmentOn.isEmpty()) { + Path2D polyline = getProjectedPolyLine(); + g2d.setColor(getColor(0)); + Stroke oldStroke = g2d.getStroke(); + if (oldStroke != stroke) { + g2d.setStroke(stroke); + } + + Shape oldClip = g2d.getClip(); + Shape newClip = ConvexObject.getClip(clip); + if (newClip != null) { + g2d.clip(newClip); + } + + g2d.draw(polyline); + + if (oldStroke != stroke) { + g2d.setStroke(oldStroke); + } + + if (newClip != null) { + g2d.setClip(oldClip); + } + + drawAreas(g2d); + } + } + + @Override + public int hashCode() { + if (hash == -1) { + if (colors != null) { + hash = Arrays.hashCode(vertices) + 19 * Arrays.hashCode(colors); + } else { + hash = Arrays.hashCode(vertices) + 19 * getColor(0).hashCode(); + } + } + return hash; + } + + @Override + public String toString() { + return "Segment " + vertices[0].toString() + " " + vertices[1].toString() + " Precedence: " + getPrecedence(); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/SpritedRectangle.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/SpritedRectangle.java new file mode 100755 index 000000000..472403a11 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/SpritedRectangle.java @@ -0,0 +1,312 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixte Denizet + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.motor; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.Path2D; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.List; + +import org.scilab.forge.scirenderer.implementation.g2d.texture.G2DTextureDrawingTools; +import org.scilab.forge.scirenderer.texture.Texture; +import org.scilab.forge.scirenderer.texture.AnchorPosition; +import org.scilab.forge.scirenderer.tranformations.Transformation; +import org.scilab.forge.scirenderer.tranformations.Vector3d; +import org.scilab.forge.scirenderer.tranformations.Vector4d; + +/** + * @author Calixte DENIZET + */ +public class SpritedRectangle extends ConvexObject { + + private static final Color[] COLORS = new Color[] {Color.BLACK, Color.BLACK, Color.BLACK}; + + private Texture sprite; + private BufferedImage image; + private G2DTextureDrawingTools drawingTools; + private double rotationAngle; + private Texture.Filter filter; + private Vector3d position; + + public SpritedRectangle(Vector3d vertex, Texture sprite, AnchorPosition anchor, G2DTextureDrawingTools drawingTools, double rotationAngle) throws InvalidPolygonException { + super(getSpriteVertices(vertex, sprite, anchor, rotationAngle), null); + this.sprite = sprite; + this.drawingTools = drawingTools; + this.rotationAngle = rotationAngle; + this.position = vertex; + } + + public SpritedRectangle(Vector3d vertex, Transformation transf, BufferedImage image, Texture.Filter filter) throws InvalidPolygonException { + super(getSpriteVertices(vertex, transf, image), null); + this.image = image; + this.filter = filter; + this.position = vertex; + } + + @Override + public List<ConvexObject> breakObject(ConvexObject o) { + if (o instanceof Triangle) { + return breakObject((Triangle) o); + } else if (o instanceof Segment) { + return breakObject((Segment) o); + } else if (o instanceof SpritedRectangle) { + return breakObject((SpritedRectangle) o); + } + return null; + } + + public List<ConvexObject> breakObject(Triangle o) { + try { + Vector3d[] v1 = new Vector3d[] {vertices[0], vertices[1], vertices[2]}; + Vector3d[] v2 = new Vector3d[] {vertices[0], vertices[2], vertices[3]}; + Triangle t1 = new Triangle(v1, COLORS); + Triangle t2 = new Triangle(v2, COLORS); + t1.setSprite(this); + t2.setSprite(this); + List<ConvexObject> list = new ArrayList<ConvexObject>(); + if (o.isBehind(t1) == 2) { + List<ConvexObject> list2 = o.breakObject(t1); + if (list2 != null) { + list.addAll(list2); + } + } else { + list.add(t1); + } + if (o.isBehind(t2) == 2) { + List<ConvexObject> list2 = o.breakObject(t2); + if (list2 != null) { + list.addAll(list2); + } + } else { + list.add(t2); + } + + return list; + } catch (InvalidPolygonException e) { } + + return null; + } + + public List<ConvexObject> breakObject(Segment o) { + try { + Vector3d[] v = new Vector3d[] {vertices[0], vertices[1], vertices[2]}; + Triangle t1 = new Triangle(v, COLORS); + t1.setSprite(this); + v = new Vector3d[] {vertices[0], vertices[2], vertices[3]}; + Triangle t2 = new Triangle(v, COLORS); + t2.setSprite(this); + List<ConvexObject> list = t1.breakObject(o); + if (list != null) { + list.add(t2); + return list; + } + + list = t2.breakObject(o); + if (list != null) { + list.add(t1); + return list; + } + } catch (InvalidPolygonException e) { } + + return null; + } + + public List<ConvexObject> breakObject(SpritedRectangle o) { + try { + Vector3d[] v1 = new Vector3d[] {vertices[0], vertices[1], vertices[2]}; + Vector3d[] v2 = new Vector3d[] {vertices[0], vertices[2], vertices[3]}; + Triangle t1 = new Triangle(v1, COLORS); + Triangle t2 = new Triangle(v2, COLORS); + t1.setSprite(this); + t2.setSprite(this); + List<ConvexObject> list = o.breakObject(t1); + if (list == null) { + list = o.breakObject(t2); + } else { + List<ConvexObject> list2 = o.breakObject(t2); + if (list2 != null) { + list.addAll(list2); + } + } + + return list; + } catch (InvalidPolygonException e) { } + + return null; + } + + public List<ConvexObject> breakObject(Vector4d v) { + try { + double[] vv = v.getData(); + Vector3d np = new Vector3d(vv); + int ret = isBehind(np, vv[3]); + if (ret == 1) { + List<ConvexObject> list = new ArrayList<ConvexObject>(); + list.add(this); + return list; + } else if (ret == -1) { + return null; + } + + Vector3d[] v1 = new Vector3d[] {vertices[0], vertices[1], vertices[2]}; + Vector3d[] v2 = new Vector3d[] {vertices[0], vertices[2], vertices[3]}; + Triangle t1 = new Triangle(v1, COLORS); + Triangle t2 = new Triangle(v2, COLORS); + t1.setSprite(this); + t2.setSprite(this); + List<ConvexObject> list = t1.breakObject(v); + if (list != null) { + List<ConvexObject> list2 = t2.breakObject(v); + if (list2 != null) { + list.addAll(list2); + } + return list; + } else { + return t2.breakObject(v); + } + } catch (InvalidPolygonException e) { } + + return null; + } + + private static Vector3d[] getSpriteVertices(Vector3d vertex, Transformation transf, BufferedImage image) { + int w = image.getWidth(); + int h = image.getHeight(); + double x = vertex.getX(); + double y = vertex.getY(); + double z = vertex.getZ(); + + return new Vector3d[] {transf.project(new Vector3d(x, y, z)), transf.project(new Vector3d(x + w, y, z)), transf.project(new Vector3d(x + w, y + h, z)), transf.project(new Vector3d(x, y + h, z))}; + } + + private static Vector3d[] getSpriteVertices(Vector3d vertex, Texture sprite, AnchorPosition anchor) { + Dimension d = sprite.getDataProvider().getTextureSize(); + double spriteWidth = d.getWidth(); + double spriteHeight = d.getHeight(); + double deltaX = 0; + double deltaY = 0; + + switch (anchor) { + case LEFT: + deltaX = spriteWidth / 2; + break; + case LOWER_LEFT: + deltaX = spriteWidth / 2; + deltaY = spriteHeight / 2; + break; + case UPPER_LEFT: + deltaX = spriteWidth / 2; + deltaY = -spriteHeight / 2; + break; + case UP: + deltaY = -spriteHeight / 2; + break; + case CENTER: + break; + case DOWN: + deltaY = spriteHeight / 2; + break; + case RIGHT: + deltaX = -spriteWidth / 2; + break; + case LOWER_RIGHT: + deltaX = -spriteWidth / 2; + deltaY = spriteHeight / 2; + break; + case UPPER_RIGHT: + deltaX = -spriteWidth / 2; + deltaY = -spriteHeight / 2; + break; + default: + } + + double x = deltaX; + double y = -deltaY; + double z = vertex.getZ(); + + return new Vector3d[] {new Vector3d(x - spriteWidth / 2, y - spriteHeight / 2, z), + new Vector3d(x - spriteWidth / 2, y + spriteHeight / 2, z), + new Vector3d(x + spriteWidth / 2, y + spriteHeight / 2, z), + new Vector3d(x + spriteWidth / 2, y - spriteHeight / 2, z) + }; + } + + private static Vector3d[] getSpriteVertices(Vector3d vertex, Texture sprite, AnchorPosition anchor, double rotationAngle) { + Vector3d[] points = getSpriteVertices(vertex, sprite, anchor); + + if (rotationAngle != 0) { + final double a = -rotationAngle * Math.PI / 180; + final double ca = Math.cos(a); + final double sa = Math.sin(a); + + for (int i = 0; i < 4; i++) { + points[i] = new Vector3d(vertex.getX() + ca * points[i].getX() - sa * points[i].getY(), vertex.getY() + sa * points[i].getX() + ca * points[i].getY(), vertex.getZ()); + } + } else { + for (int i = 0; i < 4; i++) { + points[i] = new Vector3d(vertex.getX() + points[i].getX(), vertex.getY() + points[i].getY(), vertex.getZ()); + } + } + + return points; + } + + private static Vector3d unrotate(Vector3d v, Vector3d t, double rotationAngle) { + v = v.minus(t); + final double a = rotationAngle * Math.PI / 180; + final double ca = Math.cos(a); + final double sa = Math.sin(a); + + return new Vector3d(ca * v.getX() - sa * v.getY(), sa * v.getX() + ca * v.getY(), v.getZ()); + } + + public Texture getSprite() { + return sprite; + } + + @Override + public void draw(Graphics2D g2d) { + if (sprite != null) { + Path2D contour = getProjectedContour(); + AffineTransform oldTransf = g2d.getTransform(); + Stroke oldStroke = g2d.getStroke(); + + if (rotationAngle != 0) { + g2d.translate(position.getX(), position.getY()); + g2d.rotate(-rotationAngle * Math.PI / 180); + Vector3d v = unrotate(vertices[0], position, rotationAngle); + g2d.translate(v.getX(), v.getY()); + } else { + g2d.translate(vertices[0].getX(), vertices[0].getY()); + } + + drawingTools.accept(sprite); + g2d.setTransform(oldTransf); + g2d.setStroke(oldStroke); + } else { + Object key = filter == Texture.Filter.LINEAR ? RenderingHints.VALUE_INTERPOLATION_BILINEAR : RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR; + DrawTools.drawParallelogramTexture(g2d, image, new double[] {0, 1, 1, 0}, new double[] {0, 0, 1, 1}, + new double[] {vertices[3].getX(), vertices[2].getX(), vertices[1].getX(), vertices[0].getX()}, + new double[] {vertices[3].getY(), vertices[2].getY(), vertices[1].getY(), vertices[0].getY()}, key); + } + } + + public String toString() { + return "SpritedRectangle: " + vertices[0].toString() + " " + vertices[1].toString() + " " + vertices[2].toString() + " " + vertices[3].toString() + "\nPrecedence: " + precedence; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/Triangle.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/Triangle.java new file mode 100755 index 000000000..d79b40737 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/motor/Triangle.java @@ -0,0 +1,593 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixte Denizet + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.motor; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.Area; +import java.awt.geom.Path2D; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.List; + +import org.scilab.forge.scirenderer.texture.Texture; +import org.scilab.forge.scirenderer.tranformations.Vector3d; +import org.scilab.forge.scirenderer.tranformations.Vector4d; + +/** + * @author Calixte DENIZET + */ +public class Triangle extends ConvexObject { + + private static final Color[] COLORS = new Color[] {Color.BLACK, Color.BLACK, Color.BLACK}; + private static final Stroke stroke = new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); + private static final Stroke EMPTYSTROKE = new BasicStroke(0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); + + private SpritedRectangle sprite; + private BufferedImage image; + private Vector3d[] textureCoords; + private Texture.Filter filter; + + protected List<Segment> segments; + + public Triangle(Vector3d[] vertices, Color[] colors, Vector3d normal) throws InvalidPolygonException { + super(vertices, colors); + if (vertices.length != 3) { + throw new InvalidPolygonException("Invalid triangle: must have 3 vertices."); + } + } + + public Triangle(Vector3d[] vertices, Color[] colors) throws InvalidPolygonException { + this(vertices, colors, null); + } + + public Triangle(Vector3d[] vertices, Vector3d[] textureCoords, BufferedImage image, Texture.Filter filter) throws InvalidPolygonException { + this(vertices, COLORS, null); + this.textureCoords = textureCoords; + this.image = image; + this.filter = filter; + } + + @Override + public int isBehind(ConvexObject o) { + if (o instanceof Segment && isSegmentAcross((Segment) o)) { + return -1; + } + + return super.isBehind(o); + } + + public boolean isIn2D() { + return vertices[0].getZ() == 0 && vertices[1].getZ() == 0 && vertices[2].getZ() == 0; + } + + public boolean addSegment(Segment s) { + if (isSegmentInside(s)) { + if (segments == null) { + segments = new ArrayList<Segment>(3); + } + if (segments.contains(s)) { + segments.remove(s); + s.removeConvexObject(this); + } + segments.add(s); + s.addConvexObject(this); + + return true; + } + + return false; + } + + public boolean pointOnVertices(Vector3d p) { + return p.equals(vertices[0]) || p.equals(vertices[1]) || p.equals(vertices[2]); + } + + public void removeSegment(Segment s) { + if (segments != null) { + segments.remove(s); + s.removeConvexObject(this); + } + } + + public void replaceSegment(Segment s, List<Segment> segs) { + segments.remove(s); + //s.removeConvexObject(this); + for (Segment ss : segs) { + if (segments.contains(ss)) { + segments.remove(ss); + ss.removeConvexObject(this); + } + segments.add(ss); + ss.addConvexObject(this); + } + } + + @Override + public List<ConvexObject> breakObject(ConvexObject o) { + if (o instanceof Triangle) { + return breakObject((Triangle) o); + } else if (o instanceof Segment) { + return breakObject((Segment) o); + } else if (o instanceof SpritedRectangle) { + return ((SpritedRectangle) o).breakObject(this); + } + + return null; + } + + public List<ConvexObject> breakObject(Triangle o) { + Vector3d n = Vector3d.product(getNormal(), o.getNormal()); + n = n.times(1 / n.getNorm2()); + Vector3d u = Vector3d.product(o.v0v1, n); + Vector3d v = Vector3d.product(n, this.v0v1); + Vector3d p = Vector3d.getBarycenter(u, v, this.v0v1.scalar(this.vertices[0]), o.v0v1.scalar(o.vertices[0])); + + List<ConvexObject> list1 = breakTriangleOnLine(o, p, this.v0v1); + List<ConvexObject> list2 = breakTriangleOnLine(this, p, o.v0v1); + + list1.addAll(list2); + + return list1; + } + + public List<ConvexObject> breakObject(Segment o) { + double c = this.getSegmentIntersection(o); + if (Double.isNaN(c)) { + return null; + } + + List<ConvexObject> list = new ArrayList<ConvexObject>(5); + Vector3d p = Vector3d.getBarycenter(o.vertices[0], o.vertices[1], c, 1 - c); + Color cc = getColorsBarycenter(o.getColor(0), o.getColor(1), c, 1 - c); + + try { + list.add(new Segment(new Vector3d[] {o.vertices[0], p}, new Color[] {o.getColor(0), cc}, o.stroke, o.is2D)); + list.add(new Segment(new Vector3d[] {p, o.vertices[1]}, new Color[] {cc, o.getColor(1)}, o.stroke, o.is2D)); + } catch (InvalidPolygonException e) { } + + List<ConvexObject> list1 = breakTriangleOnLine(this, p, Vector3d.product(getNormal(), vertices[0].minus(p))); + list.addAll(list1); + + return list; + } + + protected void setSprite(SpritedRectangle sprite) { + this.sprite = sprite; + } + + protected SpritedRectangle getSprite() { + return sprite; + } + + @Override + public void draw(Graphics2D g2d) { + if (sprite != null) { + Shape oldClip = g2d.getClip(); + Path2D contour = getProjectedContour(); + Area area = new Area(contour); + // Trick to paint the triangle and its outline + //area.add(new Area(stroke.createStrokedShape(contour))); + g2d.clip(area);//getProjectedContour()); + sprite.draw(g2d); + g2d.setClip(oldClip); + } else if (image != null) { + Object key = filter == Texture.Filter.LINEAR ? RenderingHints.VALUE_INTERPOLATION_BILINEAR : RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR; + DrawTools.drawTriangleTexture(g2d, image, new double[] {textureCoords[0].getX(), textureCoords[1].getX(), textureCoords[2].getX()}, new double[] {textureCoords[0].getY(), textureCoords[1].getY(), textureCoords[2].getY()}, new double[] {vertices[0].getX(), vertices[1].getX(), vertices[2].getX()}, new double[] {vertices[0].getY(), vertices[1].getY(), vertices[2].getY()}, key); + } else if (isMonochromatic()) { + // AA generates artifacts on the triangles borders whent they slightlyt overlap + // So we disable it just ot fill the triangle + Object prevAA = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + Path2D contour = getProjectedContour(); + //Area area = new Area(contour); + // Trick to paint the triangle and its outline + // TODO: the newly created Area contains in fact two areas + // it should be better to have one area where its border + // is the external outline of the contour... + // (it would reduce eps/ps/pdf/svg file size) + // fill(area) is very very slow... (for a grayplot 500x500, fill(area) is 10 times slower than fill(contour))... + //area.add(new Area(stroke.createStrokedShape(contour))); + if (g2d.getStroke() != EMPTYSTROKE) { + g2d.setStroke(EMPTYSTROKE); + } + g2d.setColor(getColor(0)); + g2d.fill(contour); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, prevAA); + } else { + DrawTools.fillGouraud(g2d, this); + } + + if (segments != null) { + for (Segment s : segments) { + s.removeConvexObject(this); + s.draw(g2d); + } + } + + drawAreas(g2d); + } + + @Override + public List<ConvexObject> breakObject(Vector4d v) { + double[] vv = v.getData(); + Vector3d np = new Vector3d(vv); + int ret = isBehind(np, vv[3]); + if (ret == 1) { + List<ConvexObject> list = new ArrayList<ConvexObject>(1); + list.add(this); + return list; + } else if (ret == -1) { + return null; + } + + Vector3d n = Vector3d.product(getNormal(), np); + n = n.times(1 / n.getNorm2()); + Vector3d u = Vector3d.product(np, n); + Vector3d w = Vector3d.product(n, this.v0v1); + Vector3d p = Vector3d.getBarycenter(u, w, this.v0v1.scalar(this.vertices[0]), -vv[3]); + + List<ConvexObject> list1 = breakTriangleOnLine(this, p, np); + List<ConvexObject> list = new ArrayList<ConvexObject>(3); + for (ConvexObject o : list1) { + if (o.isBehind(np, vv[3]) == 1) { + list.add(o); + } + } + + return list; + } + + protected boolean isPointInside(final Vector3d v) { + return isPointInside(v, true); + } + + protected boolean isPointInside(final Vector3d v, final boolean checkCoplanarity) { + Vector3d v2 = v.minus(vertices[0]); + getNormal(); + if (checkCoplanarity && !isNull(v2.scalar(v0v1))) { + return false; + } + + Vector3d v0v1v2 = Vector3d.product(v0v1, v2); + double a = -v0v1v2.scalar(v1); + if (a < 0 || isNull(a)) { + return false; + } + + double b = v0v1v2.scalar(v0); + if (b < 0 || isNull(b)) { + return false; + } + + return a + b < nv0v1 || isEqual(a + b, nv0v1); + } + + protected boolean isCoplanar(final Segment s) { + double sc = vertices[0].scalar(getNormal()); + return isEqual(sc, s.vertices[0].scalar(v0v1)) && isEqual(sc, s.vertices[1].scalar(v0v1)); + } + + protected boolean isCoplanar(final Triangle t) { + double sc = vertices[0].scalar(getNormal()); + return isEqual(sc, t.vertices[0].scalar(v0v1)) && isEqual(sc, t.vertices[1].scalar(v0v1)) && isEqual(sc, t.vertices[2].scalar(v0v1)); + } + + protected boolean isSegmentAcross(final Segment s) { + if (!isCoplanar(s)) { + // the segment and the triangle are not coplanar + return false; + } + + return check2DTrueIntersection(s); + } + + protected boolean isSegmentInside(final Segment s) { + if (!isCoplanar(s)) { + // the segment and the triangle are not coplanar + return false; + } + + // Since there is a good probability that a segment is triangle border we check that + if ((s.vertices[0].equals(vertices[0]) || s.vertices[0].equals(vertices[1]) || s.vertices[0].equals(vertices[2])) + && (s.vertices[1].equals(vertices[0]) || s.vertices[1].equals(vertices[1]) || s.vertices[1].equals(vertices[2]))) { + return true; + } + + return isPointInside(s.vertices[0], false) || isPointInside(s.vertices[1], false) || check2DIntersection(s); + } + + protected boolean isSegmentIntersects(final Segment s) { + Vector3d v3 = s.vertices[0].minus(vertices[0]); + Vector3d v4 = s.vertices[1].minus(vertices[0]); + double c = v3.scalar(getNormal()); + + if (Math.signum(c) == Math.signum(v4.scalar(v0v1))) { + return false; + } + + Vector3d v2 = s.vertices[0].minus(s.vertices[1]); + Vector3d v1v2 = Vector3d.product(v1, v2); + double a = v3.scalar(v1v2); + double b = Vector3d.det(v3, v2, v0); + double detv0v1v2 = v0.scalar(v1v2); + double sign = Math.signum(detv0v1v2); + + return Math.signum(a) == sign && Math.signum(b) == sign && Math.signum(c) == sign && a + b <= detv0v1v2 && c <= detv0v1v2; + } + + protected double getSegmentIntersection(final Segment s) { + Vector3d v = s.vertices[1].minus(vertices[0]); + double c = v.scalar(getNormal()) / s.v0.scalar(getNormal()); + + if (isNegativeOrNull(c) || isGreaterOrEqual(c, 1)) { + return Double.NaN; + } + + Vector3d p = Vector3d.getBarycenter(s.vertices[0], s.vertices[1], c, 1 - c); + if (isPointInside(p, false)) { + return c; + } + + return Double.NaN; + } + + protected static List<ConvexObject> breakSegmentOnTriangle(final Triangle t, final Segment s) { + double c = t.getSegmentIntersection(s); + if (Double.isNaN(c)) { + return null; + } + + List<ConvexObject> list = new ArrayList<ConvexObject>(2); + Vector3d p = Vector3d.getBarycenter(s.vertices[0], s.vertices[1], c, 1 - c); + Color cc = getColorsBarycenter(s.getColor(0), s.getColor(1), c, 1 - c); + + try { + list.add(new Segment(new Vector3d[] {s.vertices[0], p}, new Color[] {s.getColor(0), cc}, s.stroke, s.is2D)); + list.add(new Segment(new Vector3d[] {p, s.vertices[1]}, new Color[] {cc, s.getColor(1)}, s.stroke, s.is2D)); + } catch (InvalidPolygonException e) { } + + + return list; + } + + /** + * Break a triangle according to its intersection with a line containing p in the plane of the triangle and orthogonal to n + * The triangle and the line are supposed to be coplanar. + * @param t the triangle to break + * @param p a point of the line + * @param n a vector + * @return a list of triangles + */ + protected static List<ConvexObject> breakTriangleOnLine(Triangle t, Vector3d p, Vector3d n) { + t.getNormal(); + // aP0+(1-a)P1 + double a = t.vertices[1].minus(p).scalar(n) / t.v0.scalar(n); + // bP0+(1-b)P2 + double b = t.vertices[2].minus(p).scalar(n) / t.v1.scalar(n); + // cP1+(1-c)P2 + Vector3d v2 = t.vertices[2].minus(t.vertices[1]); + double c = t.vertices[2].minus(p).scalar(n) / v2.scalar(n); + + List<ConvexObject> list = new ArrayList<ConvexObject>(3); + int i = -1; + int j = -1; + int k = -1; + double weight = -1; + if (isNull(a)) { + // We are on P1 + i = 1; + j = 2; + k = 0; + weight = b; + } + + if (isNull(a - 1)) { + // We are on P0 + if (weight != -1) { + list.add(t); + return list; + } else { + i = 0; + j = 1; + k = 2; + weight = c; + } + } + + if (isNull(b)) { + // We are on P2 + if (weight != -1) { + list.add(t); + return list; + } else { + i = 2; + j = 0; + k = 1; + weight = a; + } + } + + if (i != -1) { + if (weight >= 0 && weight <= 1) { + // We break into two triangles + Vector3d vb = Vector3d.getBarycenter(t.vertices[j], t.vertices[k], weight, 1 - weight); + Color cb = getColorsBarycenter(t.getColor(j), t.getColor(k), weight, 1 - weight); + Vector3d[] vertices1 = new Vector3d[] {t.vertices[i], t.vertices[j], vb}; + Vector3d[] vertices2 = new Vector3d[] {t.vertices[i], vb, t.vertices[k]}; + Color[] colors1 = new Color[] {t.getColor(i), t.getColor(j), cb}; + Color[] colors2 = new Color[] {t.getColor(i), cb, t.getColor(k)}; + + Vector3d[] tvertices1 = null; + Vector3d[] tvertices2 = null; + if (t.textureCoords != null) { + Vector3d tvb = Vector3d.getBarycenter(t.textureCoords[j], t.textureCoords[k], weight, 1 - weight); + tvertices1 = new Vector3d[] {t.textureCoords[i], t.textureCoords[j], tvb}; + tvertices2 = new Vector3d[] {t.textureCoords[i], tvb, t.textureCoords[k]}; + } + + try { + Triangle t1 = new Triangle(vertices1, colors1, null); + t1.setSprite(t.getSprite()); + list.add(t1); + Triangle t2 = new Triangle(vertices2, colors2, null); + t2.setSprite(t.getSprite()); + list.add(t2); + if (t.textureCoords != null) { + t1.textureCoords = tvertices1; + t2.textureCoords = tvertices2; + t1.image = t2.image = t.image; + t1.filter = t2.filter = t.filter; + } + } catch (InvalidPolygonException e) { } + + addSegments(list, t, p, Vector3d.product(t.v0v1, n), n); + + return list; + } else { + list.add(t); + return list; + } + } + + Color cu, cv; + Vector3d u, v; + Vector3d tu = null, tv = null; + // t has not been broken, so continue... + if (a < 0 || a > 1) { + i = 2; + j = 0; + k = 1; + u = Vector3d.getBarycenter(t.vertices[1], t.vertices[2], c, 1 - c); + v = Vector3d.getBarycenter(t.vertices[0], t.vertices[2], b, 1 - b); + cu = getColorsBarycenter(t.getColor(1), t.getColor(2), c, 1 - c); + cv = getColorsBarycenter(t.getColor(0), t.getColor(2), b, 1 - b); + if (t.textureCoords != null) { + tu = Vector3d.getBarycenter(t.textureCoords[1], t.textureCoords[2], c, 1 - c); + tv = Vector3d.getBarycenter(t.textureCoords[0], t.textureCoords[2], b, 1 - b); + } + } else if (b < 0 || b > 1) { + i = 1; + j = 2; + k = 0; + u = Vector3d.getBarycenter(t.vertices[0], t.vertices[1], a, 1 - a); + v = Vector3d.getBarycenter(t.vertices[1], t.vertices[2], c, 1 - c); + cu = getColorsBarycenter(t.getColor(0), t.getColor(1), a, 1 - a); + cv = getColorsBarycenter(t.getColor(1), t.getColor(2), c, 1 - c); + if (t.textureCoords != null) { + tu = Vector3d.getBarycenter(t.textureCoords[0], t.textureCoords[1], a, 1 - a); + tv = Vector3d.getBarycenter(t.textureCoords[1], t.textureCoords[2], c, 1 - c); + } + } else { + i = 0; + j = 1; + k = 2; + u = Vector3d.getBarycenter(t.vertices[0], t.vertices[2], b, 1 - b); + v = Vector3d.getBarycenter(t.vertices[0], t.vertices[1], a, 1 - a); + cu = getColorsBarycenter(t.getColor(0), t.getColor(2), b, 1 - b); + cv = getColorsBarycenter(t.getColor(0), t.getColor(1), a, 1 - a); + if (t.textureCoords != null) { + tu = Vector3d.getBarycenter(t.textureCoords[0], t.textureCoords[2], b, 1 - b); + tv = Vector3d.getBarycenter(t.textureCoords[0], t.textureCoords[1], a, 1 - a); + } + } + + Vector3d[] vertices1 = new Vector3d[] {u, t.vertices[i], v}; + Color[] colors1 = new Color[] {cu, t.getColor(i), cv}; + Vector3d[] vertices2 = new Vector3d[] {u, v, t.vertices[j]}; + Color[] colors2 = new Color[] {cu, cv, t.getColor(j)}; + Vector3d[] vertices3 = new Vector3d[] {u, t.vertices[j], t.vertices[k]}; + Color[] colors3 = new Color[] {cu, t.getColor(j), t.getColor(k)}; + + Vector3d[] tvertices1 = null; + Vector3d[] tvertices2 = null; + Vector3d[] tvertices3 = null; + if (t.textureCoords != null) { + tvertices1 = new Vector3d[] {tu, t.textureCoords[i], tv}; + tvertices2 = new Vector3d[] {tu, tv, t.textureCoords[j]}; + tvertices3 = new Vector3d[] {tu, t.textureCoords[j], t.textureCoords[k]}; + } + + try { + Triangle t1 = new Triangle(vertices1, colors1, null); + t1.setSprite(t.getSprite()); + list.add(t1); + Triangle t2 = new Triangle(vertices2, colors2, null); + t2.setSprite(t.getSprite()); + list.add(t2); + Triangle t3 = new Triangle(vertices3, colors3, null); + t3.setSprite(t.getSprite()); + list.add(t3); + if (t.textureCoords != null) { + t1.textureCoords = tvertices1; + t2.textureCoords = tvertices2; + t3.textureCoords = tvertices3; + t1.image = t2.image = t3.image = t.image; + t1.filter = t2.filter = t3.filter = t.filter; + } + } catch (InvalidPolygonException e) { } + + addSegments(list, t, p, Vector3d.product(t.v0v1, n), n); + + return list; + } + + private static final void addSegments(List<ConvexObject> list, Triangle t, Vector3d p, Vector3d u, Vector3d n) { + if (t.segments != null) { + List<Segment> segs = new ArrayList<Segment>(); + for (Segment s : t.segments) { + s.removeConvexObject(t); + List<Segment> l = s.breakObject(p, u, n); + if (l != null && !l.isEmpty()) { + segs.addAll(l); + s.replaceSegment(l); + } + } + t.textureCoords = null; + + for (Segment s : segs) { + for (ConvexObject tri : list) { + ((Triangle) tri).addSegment(s); + } + } + } + } + + /** + * Get the broken triangles in following the intersection of the planes containing t1 and t2. + * The planes containing t1 and t2 are supposed to be secant. + * @param t1 the first triangle + * @param t2 the second triangle + * @return an array of length 2 containing the resulting triangles for t1 and t2. + */ + protected static List<ConvexObject> breakIntersectingTriangles(Triangle t1, Triangle t2) { + Vector3d n = Vector3d.product(t1.getNormal(), t2.getNormal()); + n = n.times(1 / n.getNorm2()); + Vector3d u = Vector3d.product(t2.v0v1, n); + Vector3d v = Vector3d.product(n, t1.v0v1); + Vector3d p = Vector3d.getBarycenter(u, v, t1.v0v1.scalar(t1.vertices[0]), t2.v0v1.scalar(t2.vertices[0])); + + List<ConvexObject> list1 = breakTriangleOnLine(t1, p, t2.v0v1); + List<ConvexObject> list2 = breakTriangleOnLine(t2, p, t1.v0v1); + list1.addAll(list2); + + return list1; + } + + public String toString() { + return "Triangle: " + vertices[0].toString() + " " + vertices[1].toString() + " " + vertices[2].toString() + "\nColor: " + getColor(0) + "\nPrecedence: " + precedence; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/renderer/G2DRenderer.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/renderer/G2DRenderer.java new file mode 100755 index 000000000..e3bda655c --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/renderer/G2DRenderer.java @@ -0,0 +1,58 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixre DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.renderer; + +import org.scilab.forge.scirenderer.Drawer; +import org.scilab.forge.scirenderer.DrawingTools; +import org.scilab.forge.scirenderer.renderer.Renderer; + +/** + * @author Calixte DENIZET + */ +public class G2DRenderer implements Renderer { + + /** + * The current drawer. + */ + private Drawer drawer; + + /** + * Default constructor. + * The constructor is package : only {@link G2DRendererManager} can instantiate this object. + */ + G2DRenderer() { } + + @Override + public void setDrawer(Drawer drawer) { + this.drawer = drawer; + } + + @Override + public Drawer getDrawer() { + return drawer; + } + + @Override + public void reload() { + // todo : on met qque chose ici ou pas ? + } + + /** + * Perform a draw to the given canvas. + * @param drawingTools the given drawing tools. + */ + public void draw(DrawingTools drawingTools) { + if (drawer != null) { + drawer.draw(drawingTools); + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/renderer/G2DRendererManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/renderer/G2DRendererManager.java new file mode 100755 index 000000000..b31ed5c2a --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/renderer/G2DRendererManager.java @@ -0,0 +1,48 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.renderer; + +import org.scilab.forge.scirenderer.DrawingTools; +import org.scilab.forge.scirenderer.renderer.Renderer; +import org.scilab.forge.scirenderer.renderer.RendererManager; + +/** + * @author Calixte DENIZET + */ +public class G2DRendererManager implements RendererManager { + + /** + * Default constructor. + */ + public G2DRendererManager() { + } + + @Override + public Renderer createRenderer() { + G2DRenderer newRenderer = new G2DRenderer(); + return newRenderer; + } + + @Override + public void dispose(Renderer renderer) { } + + /** + * Perform a draw with the given renderer to the given canvas.. + * @param drawingTools the given drawing tools. + * @param renderer the given renderer. + */ + public void draw(DrawingTools drawingTools, Renderer renderer) { + if (renderer instanceof G2DRenderer) { + ((G2DRenderer) renderer).draw(drawingTools); + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/texture/G2DTextureDrawingTools.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/texture/G2DTextureDrawingTools.java new file mode 100755 index 000000000..3ae9a6a84 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/texture/G2DTextureDrawingTools.java @@ -0,0 +1,207 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.texture; + +import org.scilab.forge.scirenderer.implementation.g2d.motor.G2DStroke; +import org.scilab.forge.scirenderer.implementation.g2d.texture.G2DTextureManager; +import org.scilab.forge.scirenderer.shapes.appearance.Appearance; +import org.scilab.forge.scirenderer.shapes.appearance.Color; +import org.scilab.forge.scirenderer.texture.Texture; +import org.scilab.forge.scirenderer.texture.TextureDrawer; +import org.scilab.forge.scirenderer.texture.TextureDrawingTools; +import org.scilab.forge.scirenderer.texture.TextEntity; + +import javax.swing.Icon; +import javax.swing.JLabel; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.font.TextLayout; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; + +/** + * + * Implementation of {@link TextureDrawingTools}. + * This implementation create a {@link TextureBufferedImage} an fill it with sprite drawing. + * + * @author Pierre Lando + */ +public class G2DTextureDrawingTools implements TextureDrawingTools { + + private Graphics2D g2d; + private int width; + private int height; + + /** + * Default constructor. + * @param width the image width. + * @param height the image height. + * @param useSquareTexture true if square texture is needed. + */ + public G2DTextureDrawingTools(Graphics2D g2d) { + this.g2d = g2d; + } + + public void setGraphics(Graphics2D g2d) { + this.g2d = g2d; + } + + public void accept(Texture texture) { + G2DTextureManager.G2DTexture t = (G2DTextureManager.G2DTexture) texture; + TextureDrawer drawer = t.getDrawer(); + Dimension d = drawer.getTextureSize(); + this.width = (int) d.getWidth(); + this.height = (int) d.getHeight(); + accept(drawer, this.width, this.height); + } + + /** + * Ask this image to accept a sprite drawer. + * This image will contain the drawing of the given drawer. + * @param spriteDrawer the given sprite drawer. + */ + public void accept(TextureDrawer textureDrawer, int width, int height) { + // Change center coordinate to (0, 0). + if (textureDrawer.getOriginPosition() == TextureDrawer.OriginPosition.CENTER) { + g2d.translate(width / 2.0, height / 2.0); + } + + boolean aa = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING) == RenderingHints.VALUE_ANTIALIAS_ON; + if (!aa) { + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + } + textureDrawer.draw(this); + + if (!aa) { + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + } + } + + @Override + public void drawPlus(int size, Appearance appearance) { + if (size > 1) { + int r = size / 2; + int[] coords1 = new int[] { -r, 0, r, 0}; + int[] coords2 = new int[] {0, -r, 0, r}; + + drawPolyline(coords1, appearance); + drawPolyline(coords2, appearance); + } else { + fillDisc(0, 0, 1, appearance.getLineColor()); + } + } + + @Override + public void drawPolyline(int[] coordinates, Appearance appearance) { + if (coordinates.length == 2) { + + } + + int nbPoint = coordinates.length / 2; + + int[] xCoordinates = new int[nbPoint]; + int[] yCoordinates = new int[nbPoint]; + + int k = 0; + for (int i = 0; i < coordinates.length; i += 2) { + xCoordinates[k] = coordinates[i]; + yCoordinates[k] = coordinates[i + 1]; + k++; + } + + g2d.setColor(appearance.getLineColor()); + g2d.setStroke(G2DStroke.getStroke(appearance, 0)); + + g2d.drawPolyline(xCoordinates, yCoordinates, nbPoint); + } + + @Override + public void fillPolygon(int[] coordinates, Appearance appearance) { + int nbPoint = coordinates.length / 2; + + int[] xCoordinates = new int[nbPoint]; + int[] yCoordinates = new int[nbPoint]; + + int k = 0; + for (int i = 0; i < coordinates.length; i += 2) { + xCoordinates[k] = coordinates[i]; + yCoordinates[k] = coordinates[i + 1]; + k++; + } + + if (appearance.getFillColor().getAlphaAsFloat() != 0) { + g2d.setColor(appearance.getFillColor()); + g2d.fillPolygon(xCoordinates, yCoordinates, nbPoint); + } + + if (!appearance.getLineColor().equals(appearance.getFillColor())) { + int usedLength = coordinates.length - (coordinates.length % 2); + int[] borderCoordinate = new int[usedLength + 2]; + System.arraycopy(coordinates, 0, borderCoordinate, 0, usedLength); + borderCoordinate[usedLength] = coordinates[0]; + borderCoordinate[usedLength + 1] = coordinates[1]; + + drawPolyline(borderCoordinate, appearance); + } + } + + @Override + public void drawCircle(int x, int y, int diameter, Appearance appearance) { + g2d.setColor(appearance.getLineColor()); + g2d.setStroke(G2DStroke.getStroke(appearance, 0)); + double r = ((double) diameter) / 2; + g2d.draw(new Ellipse2D.Double(x - r, y - r, diameter, diameter)); + } + + @Override + public void fillDisc(int x, int y, int diameter, Color color) { + if (color.getAlphaAsFloat() != 0) { + g2d.setColor(color); + double r = ((double) diameter) / 2; + g2d.fill(new Ellipse2D.Double(x - r, y - r, diameter, diameter)); + } + } + + @Override + public void draw(TextEntity textEntity, int x, int y) { + if (textEntity != null && textEntity.isValid()) { + if (textEntity.isTextAntiAliased()) { + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + } else { + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + } + + if (textEntity.isTextUseFractionalMetrics()) { + g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); + } else { + g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF); + } + g2d.setColor(textEntity.getTextColor()); + TextLayout textLayout = new TextLayout(textEntity.getText(), textEntity.getFont(), g2d.getFontRenderContext()); + Rectangle2D bounds = textLayout.getBounds(); + g2d.setFont(textEntity.getFont()); + g2d.drawString(textEntity.getText(), (float) (x - bounds.getX()), y + textLayout.getAscent()); + } + } + + @Override + public void draw(Icon icon, int x, int y) { + icon.paintIcon(new JLabel(), g2d, x, y); + } + + @Override + public void clear(Color color) { + g2d.setColor(color); + g2d.fillRect(0, 0, width, height); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/texture/G2DTextureManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/texture/G2DTextureManager.java new file mode 100755 index 000000000..2fb92510b --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/g2d/texture/G2DTextureManager.java @@ -0,0 +1,513 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.g2d.texture; + +import org.scilab.forge.scirenderer.SciRendererException; +import org.scilab.forge.scirenderer.implementation.g2d.G2DCanvas; +import org.scilab.forge.scirenderer.implementation.g2d.G2DDrawingTools; +import org.scilab.forge.scirenderer.texture.AbstractTexture; +import org.scilab.forge.scirenderer.texture.Texture; +import org.scilab.forge.scirenderer.texture.TextureDrawer; +import org.scilab.forge.scirenderer.texture.TextureManager; +import org.scilab.forge.scirenderer.texture.TextureDataProvider.ImageType; + +import java.awt.Dimension; +import java.awt.image.BufferedImage; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Calixte DENIZET + */ +public class G2DTextureManager implements TextureManager { + + private static Map<ImageBuffer, ImageBuffer> images = new HashMap<ImageBuffer, ImageBuffer>(); + + public G2DTextureManager(G2DCanvas canvas) { } + + public static void clear() { + images.clear(); + } + + /** + * Draw the given texture. + * @param drawingTools used drawing tools. + * @param texture the texture too drawn. + * @throws org.scilab.forge.scirenderer.SciRendererException if the texture is invalid. + */ + public void draw(G2DDrawingTools drawingTools, Texture texture) throws SciRendererException { + if (texture instanceof G2DTexture) { + ((G2DTexture) texture).draw(drawingTools); + } + } + + @Override + public Texture createTexture() { + return new G2DTexture(); + } + + @Override + public void dispose(Collection<Texture> textures) { + for (Texture texture : textures) { + dispose(texture); + } + } + + @Override + public void dispose(Texture texture) { + if (texture instanceof G2DTexture) { + ((G2DTexture) texture).dispose(); + } + } + + private static final int toColorComp(final float f) { + return (f < 0) ? 0 : ((f > 1) ? 0xFF : ((int) (f * 255.0f))); + } + + /** + * Inner class for {@link Texture} implementation. + */ + public class G2DTexture extends AbstractTexture implements Texture { + + private ImageBuffer image; + private TextureDrawer drawer; + + /** + * Default constructor. + */ + public G2DTexture() { } + + public void dispose() { + images.remove(image); + } + + @Override + public void setDrawer(TextureDrawer drawer) { + this.drawer = drawer; + super.setDrawer(drawer); + } + + public TextureDrawer getDrawer() { + return drawer; + } + + public void draw(G2DDrawingTools drawingTools) { + drawingTools.getMotor3D().drawTexture(drawingTools, getImage(), this); + } + + public BufferedImage getImage() { + if (image == null) { + Dimension textureSize = getDataProvider().getTextureSize(); + ByteBuffer buffer = getDataProvider().getData(); + ImageType type = getDataProvider().getImageType(); + int imageType = BufferedImage.TYPE_INT_RGB; + int[] ibuffer = null; + final ByteOrder order = ByteOrder.nativeOrder(); + + switch (type) { + case RGB: { + imageType = BufferedImage.TYPE_INT_RGB; + ibuffer = new int[buffer.capacity() / 3]; + int k = 0; + for (int i = 0; i < buffer.capacity(); i += 3) { + ibuffer[k++] = ((buffer.get(i) & 0xFF) << 16) | ((buffer.get(i + 1) & 0xFF) << 8) | (buffer.get(i + 2) & 0xFF); + } + break; + } + case RGB_RGBA: { + imageType = BufferedImage.TYPE_INT_RGB; + ibuffer = new int[buffer.capacity() / 4]; + int k = 0; + if (order == ByteOrder.LITTLE_ENDIAN) { + for (int i = 0; i < buffer.capacity(); i += 4) { + ibuffer[k++] = ((buffer.get(i + 3) & 0xFF) << 16) | ((buffer.get(i + 2) & 0xFF) << 8) | (buffer.get(i + 1) & 0xFF); + } + } else { + for (int i = 0; i < buffer.capacity(); i += 4) { + ibuffer[k++] = ((buffer.get(i) & 0xFF) << 16) | ((buffer.get(i + 1) & 0xFF) << 8) | (buffer.get(i + 2) & 0xFF); + } + } + break; + } + case BGR: { + imageType = BufferedImage.TYPE_INT_BGR; + ibuffer = new int[buffer.capacity() / 3]; + int k = 0; + for (int i = 0; i < buffer.capacity(); i += 3) { + ibuffer[k++] = ((buffer.get(i) & 0xFF) << 16) | ((buffer.get(i + 1) & 0xFF) << 8) | (buffer.get(i + 2) & 0xFF); + } + break; + } + case GRAY: { + imageType = BufferedImage.TYPE_INT_RGB; + ibuffer = new int[buffer.capacity()]; + for (int i = 0; i < buffer.capacity(); i++) { + final int c = buffer.get(i) & 0xFF; + ibuffer[i] = (c << 16) | (c << 8) | c; + } + break; + } + case GRAY_16: { + imageType = BufferedImage.TYPE_INT_RGB; + ibuffer = new int[buffer.capacity() / 2]; + int k = 0; + if (order == ByteOrder.LITTLE_ENDIAN) { + for (int i = 0; i < buffer.capacity(); i += 2) { + final int c = (((buffer.get(i + 1) & 0xFF) << 8) | (buffer.get(i) & 0xFF)) / 257; + ibuffer[k++] = (c << 16) | (c << 8) | c; + } + } else { + for (int i = 0; i < buffer.capacity(); i += 2) { + final int c = (((buffer.get(i) & 0xFF) << 8) | (buffer.get(i + 1) & 0xFF)) / 257; + ibuffer[k++] = (c << 16) | (c << 8) | c; + } + } + break; + } + case RGBA: { + imageType = BufferedImage.TYPE_INT_ARGB; + ibuffer = new int[buffer.capacity() / 4]; + if (order == ByteOrder.LITTLE_ENDIAN) { + int k = 0; + for (int i = 0; i < buffer.capacity(); i += 4) { + ibuffer[k++] = ((buffer.get(i) & 0xFF) << 24) | ((buffer.get(i + 3) & 0xFF) << 16) | ((buffer.get(i + 2) & 0xFF) << 8) | (buffer.get(i + 1) & 0xFF); + } + } else { + int k = 0; + for (int i = 0; i < buffer.capacity(); i += 4) { + ibuffer[k++] = ((buffer.get(i + 3) & 0xFF) << 24) | ((buffer.get(i) & 0xFF) << 16) | ((buffer.get(i + 1) & 0xFF) << 8) | (buffer.get(i + 2) & 0xFF); + } + } + break; + } + case RGBA_REV: { + imageType = BufferedImage.TYPE_INT_ARGB; + ibuffer = new int[buffer.capacity() / 4]; + if (order == ByteOrder.BIG_ENDIAN) { + int k = 0; + for (int i = 0; i < buffer.capacity(); i += 4) { + ibuffer[k++] = ((buffer.get(i) & 0xFF) << 24) | ((buffer.get(i + 3) & 0xFF) << 16) | ((buffer.get(i + 2) & 0xFF) << 8) | (buffer.get(i + 1) & 0xFF); + } + } else { + int k = 0; + for (int i = 0; i < buffer.capacity(); i += 4) { + ibuffer[k++] = ((buffer.get(i + 3) & 0xFF) << 24) | ((buffer.get(i) & 0xFF) << 16) | ((buffer.get(i + 1) & 0xFF) << 8) | (buffer.get(i + 2) & 0xFF); + } + } + break; + } + case ABGR: { + imageType = BufferedImage.TYPE_INT_ARGB; + ibuffer = new int[buffer.capacity() / 4]; + int k = 0; + for (int i = 0; i < buffer.capacity(); i += 4) { + ibuffer[k++] = ((buffer.get(i) & 0xFF) << 24) | ((buffer.get(i + 3) & 0xFF) << 16) | ((buffer.get(i + 2) & 0xFF) << 8) | (buffer.get(i + 1) & 0xFF); + } + break; + } + case RGB_332: { + imageType = BufferedImage.TYPE_INT_RGB; + ibuffer = new int[buffer.capacity()]; + for (int i = 0; i < buffer.capacity(); i++) { + final int c = buffer.get(i) & 0xFF; + ibuffer[i] = ((((c >> 5) & 0x7) * 255 / 7) << 16) | ((((c >> 2) & 0x7) * 255 / 7) << 8) | ((c & 0x3) * 255 / 3); + } + break; + } + case RED: { + imageType = BufferedImage.TYPE_INT_RGB; + ibuffer = new int[buffer.capacity()]; + for (int i = 0; i < buffer.capacity(); i++) { + ibuffer[i] = (buffer.get(i) & 0xFF) << 16; + } + break; + } + case GREEN: { + imageType = BufferedImage.TYPE_INT_RGB; + ibuffer = new int[buffer.capacity()]; + for (int i = 0; i < buffer.capacity(); i++) { + ibuffer[i] = (buffer.get(i) & 0xFF) << 8; + } + break; + } + case BLUE: { + imageType = BufferedImage.TYPE_INT_RGB; + ibuffer = new int[buffer.capacity()]; + for (int i = 0; i < buffer.capacity(); i++) { + ibuffer[i] = (buffer.get(i) & 0xFF); + } + break; + } + case INTENSITY: { + imageType = BufferedImage.TYPE_INT_ARGB; + ibuffer = new int[buffer.capacity()]; + for (int i = 0; i < buffer.capacity(); i++) { + final int c = buffer.get(i) & 0xFF; + ibuffer[i] = (c << 24) | (c << 16) | (c << 8) | c; + } + break; + } + case RGBA_4444: { + imageType = BufferedImage.TYPE_INT_ARGB; + ibuffer = new int[buffer.capacity() / 2]; + int k = 0; + if (order == ByteOrder.LITTLE_ENDIAN) { + for (int i = 0; i < buffer.capacity(); i += 2) { + int c = buffer.get(i) & 0xFF; + final int B = ((c >> 4) & 0xF) * 0x11; + final int A = (c & 0xF) * 0x11; + c = buffer.get(i + 1) & 0xFF; + final int R = ((c >> 4) & 0xF) * 0x11; + final int G = (c & 0xF) * 0x11; + ibuffer[k++] = (A << 24) | (R << 16) | (G << 8) | B; + } + } else { + for (int i = 0; i < buffer.capacity(); i += 2) { + int c = buffer.get(i + 1) & 0xFF; + final int B = ((c >> 4) & 0xF) * 0x2F; + final int A = (c & 0xF) * 0x2F; + c = buffer.get(i) & 0xFF; + final int R = ((c >> 4) & 0xF) * 0x2F; + final int G = (c & 0xF) * 0x2F; + ibuffer[k++] = (A << 24) | (R << 16) | (G << 8) | B; + } + } + break; + } + case RGBA_5551: { + imageType = BufferedImage.TYPE_INT_ARGB; + ibuffer = new int[buffer.capacity() / 2]; + int k = 0; + if (order == ByteOrder.LITTLE_ENDIAN) { + for (int i = 0; i < buffer.capacity(); i += 2) { + int c = buffer.get(i) & 0xFF; + final int B = (((c >> 1) & 0x1F) * 0xFF) / 0x1F; + final int A = (c & 0x1) * 0xFF; + int G = (c >> 6) & 0x3; + c = buffer.get(i + 1) & 0xFF; + final int R = (((c >> 3) & 0x1F) * 0xFF) / 0x1F; + G = (((c & 0x7) << 2 | G) * 0xFF) / 0x1F; + ibuffer[k++] = (A << 24) | (R << 16) | (G << 8) | B; + } + } else { + for (int i = 0; i < buffer.capacity(); i += 2) { + int c = buffer.get(i + 1) & 0xFF; + final int B = (((c >> 1) & 0x1F) * 0xFF) / 0x1F; + final int A = (c & 0x1) * 0xFF; + int G = (c >> 6) & 0x3; + c = buffer.get(i) & 0xFF; + final int R = (((c >> 3) & 0x1F) * 0xFF) / 0x1F; + G = (((c & 0x7) << 2 | G) * 0xFF) / 0x1F; + ibuffer[k++] = (A << 24) | (R << 16) | (G << 8) | B; + } + } + break; + } + case RGB_FLOAT: { + imageType = BufferedImage.TYPE_INT_RGB; + FloatBuffer fbuffer = buffer.asFloatBuffer(); + ibuffer = new int[fbuffer.capacity() / 3]; + int k = 0; + for (int i = 0; i < fbuffer.capacity(); i += 3) { + ibuffer[k++] = (toColorComp(fbuffer.get(i)) << 16) | (toColorComp(buffer.get(i + 1)) << 8) | toColorComp(buffer.get(i + 2)); + } + break; + } + case RGBA_FLOAT: { + imageType = BufferedImage.TYPE_INT_ARGB; + buffer.order(order); + FloatBuffer fbuffer = buffer.asFloatBuffer(); + ibuffer = new int[fbuffer.capacity() / 4]; + int k = 0; + for (int i = 0; i < fbuffer.capacity(); i += 4) { + ibuffer[k++] = (toColorComp(fbuffer.get(i + 3)) << 24) | (toColorComp(fbuffer.get(i)) << 16) | (toColorComp(fbuffer.get(i + 1)) << 8) | toColorComp(fbuffer.get(i + 2)); + } + break; + } + case GRAY_FLOAT: { + imageType = BufferedImage.TYPE_INT_RGB; + buffer.order(order); + FloatBuffer fbuffer = buffer.asFloatBuffer(); + ibuffer = new int[fbuffer.capacity()]; + for (int i = 0; i < fbuffer.capacity(); i++) { + final int c = toColorComp(fbuffer.get(i)); + ibuffer[i] = (c << 16) | (c << 8) | c; + } + break; + } + case RED_16: { + imageType = BufferedImage.TYPE_INT_RGB; + ibuffer = new int[buffer.capacity() / 2]; + int k = 0; + if (order == ByteOrder.LITTLE_ENDIAN) { + for (int i = 0; i < buffer.capacity(); i += 2) { + final int c = (((buffer.get(i + 1) & 0xFF) << 8) | (buffer.get(i) & 0xFF)) / 257; + ibuffer[k++] = c << 16; + } + } else { + for (int i = 0; i < buffer.capacity(); i += 2) { + final int c = (((buffer.get(i) & 0xFF) << 8) | (buffer.get(i + 1) & 0xFF)) / 257; + ibuffer[k++] = c << 16; + } + } + break; + } + case GREEN_16: { + imageType = BufferedImage.TYPE_INT_RGB; + ibuffer = new int[buffer.capacity() / 2]; + int k = 0; + if (order == ByteOrder.LITTLE_ENDIAN) { + for (int i = 0; i < buffer.capacity(); i += 2) { + final int c = (((buffer.get(i + 1) & 0xFF) << 8) | (buffer.get(i) & 0xFF)) / 257; + ibuffer[k++] = c << 8; + } + } else { + for (int i = 0; i < buffer.capacity(); i += 2) { + final int c = (((buffer.get(i) & 0xFF) << 8) | (buffer.get(i + 1) & 0xFF)) / 257; + ibuffer[k++] = c << 8; + } + } + break; + } + case BLUE_16: { + imageType = BufferedImage.TYPE_INT_RGB; + ibuffer = new int[buffer.capacity() / 2]; + int k = 0; + if (order == ByteOrder.LITTLE_ENDIAN) { + for (int i = 0; i < buffer.capacity(); i += 2) { + final int c = (((buffer.get(i + 1) & 0xFF) << 8) | (buffer.get(i) & 0xFF)) / 257; + ibuffer[k++] = c; + } + } else { + for (int i = 0; i < buffer.capacity(); i += 2) { + final int c = (((buffer.get(i) & 0xFF) << 8) | (buffer.get(i + 1) & 0xFF)) / 257; + ibuffer[k++] = c; + } + } + break; + } + case RED_FLOAT: { + imageType = BufferedImage.TYPE_INT_RGB; + buffer.order(order); + FloatBuffer fbuffer = buffer.asFloatBuffer(); + ibuffer = new int[fbuffer.capacity()]; + for (int i = 0; i < fbuffer.capacity(); i++) { + ibuffer[i] = toColorComp(fbuffer.get(i)) << 16; + } + break; + } + case GREEN_FLOAT: { + imageType = BufferedImage.TYPE_INT_RGB; + buffer.order(order); + FloatBuffer fbuffer = buffer.asFloatBuffer(); + ibuffer = new int[fbuffer.capacity()]; + for (int i = 0; i < fbuffer.capacity(); i++) { + ibuffer[i] = toColorComp(fbuffer.get(i)) << 8; + } + break; + } + case BLUE_FLOAT: { + imageType = BufferedImage.TYPE_INT_RGB; + buffer.order(order); + FloatBuffer fbuffer = buffer.asFloatBuffer(); + ibuffer = new int[fbuffer.capacity()]; + for (int i = 0; i < fbuffer.capacity(); i++) { + ibuffer[i] = toColorComp(fbuffer.get(i)); + } + break; + } + case RGBA_BYTE: { + imageType = BufferedImage.TYPE_INT_ARGB; + ibuffer = new int[buffer.capacity() / 4]; + int k = 0; + for (int i = 0; i < buffer.capacity(); i += 4) { + ibuffer[k++] = ((buffer.get(i + 3) & 0xFF) << 24) | ((buffer.get(i) & 0xFF) << 16) | ((buffer.get(i + 1) & 0xFF) << 8) | (buffer.get(i + 2) & 0xFF); + } + break; + } + } + + ImageBuffer ib1; + if (getDataProvider().isRowMajorOrder()) { + ib1 = new ImageBuffer(imageType, ibuffer, (int) textureSize.getWidth(), (int) textureSize.getHeight()); + } else { + int[] tibuffer = new int[ibuffer.length]; + final int w = (int) textureSize.getWidth(); + final int h = (int) textureSize.getHeight(); + int k = 0; + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + tibuffer[k++] = ibuffer[i + j * h]; + } + } + ib1 = new ImageBuffer(imageType, tibuffer, w, h); + } + + ImageBuffer ib2 = images.get(ib1); + if (ib2 == null) { + images.put(ib1, ib1); + ib2 = ib1; + } + + image = ib2; + } + + return image.getImage(); + } + } + + private static class ImageBuffer { + + private int[] buffer; + private int hash; + private int width; + private int height; + private BufferedImage image; + private int type; + + ImageBuffer(int type, int[] buffer, int width, int height) { + this.type = type; + this.buffer = buffer; + this.hash = Arrays.hashCode(buffer) * 31 + type; + this.width = width; + this.height = height; + } + + BufferedImage getImage() { + if (image == null) { + image = new BufferedImage(width, height, type); + image.setRGB(0, 0, width, height, buffer, 0, width); + } + + return image; + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public boolean equals(Object o) { + if (o instanceof ImageBuffer) { + ImageBuffer mfb = (ImageBuffer) o; + return type == mfb.type && Arrays.equals(buffer, mfb.buffer); + } + return false; + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/HardwareFailException.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/HardwareFailException.java new file mode 100755 index 000000000..7fb69d99e --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/HardwareFailException.java @@ -0,0 +1,30 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl; + +/** + * + * An exception to throw when hardware is to bad. + * + * @author Pierre Lando + */ +@SuppressWarnings(value = { "serial" }) +public class HardwareFailException extends Exception { + + /** + * Default constructor. + * @param message the message associated with this exception. + */ + public HardwareFailException(String message) { + super(message); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/JoGLCanvas.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/JoGLCanvas.java new file mode 100755 index 000000000..f0484023c --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/JoGLCanvas.java @@ -0,0 +1,440 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl; + +import java.awt.Dimension; +import java.awt.image.BufferedImage; +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.Semaphore; + +import javax.media.opengl.DebugGL2; +import javax.media.opengl.GL2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLException; +import javax.media.opengl.GLOffscreenAutoDrawable; +import javax.media.opengl.GLProfile; +import javax.swing.SwingUtilities; + +import org.scilab.forge.scirenderer.Canvas; +import org.scilab.forge.scirenderer.Drawer; +import org.scilab.forge.scirenderer.implementation.jogl.buffers.JoGLBuffersManager; +import org.scilab.forge.scirenderer.implementation.jogl.picking.JoGLPickingManager; +import org.scilab.forge.scirenderer.implementation.jogl.renderer.JoGLRendererManager; +import org.scilab.forge.scirenderer.implementation.jogl.texture.JoGLTextureManager; +import org.scilab.forge.scirenderer.picking.PickingManager; + +import com.jogamp.opengl.util.awt.AWTGLReadBufferUtil; + +/** + * JoGL implementation of a Canvas. + * + * @author Pierre Lando + */ +public final class JoGLCanvas implements Canvas, GLEventListener { + + private static final double[][][] ANTI_ALIASING_JITTER = { + {{0.0, 0.0}}, + {{0.5, 0.5}, {0.5, -0.5}}, + { + { -0.25, -0.5}, {0.25, 0.5}, {0.75, -0.5}, {0.25, 0.5} + }, + { + {0.125, -0.125}, { -0.875, 0.875}, { -0.375, 0.375}, {0.375, 0.625}, + {0.625, -0.625}, {0.875, 0.125}, { -0.125, -0.875}, { -0.625, -0.375} + }, + { + { -0.25, -0.125}, {0.25, -0.875}, {0.75, -0.625}, { -0.75, -0.875}, + { -0.25, 0.375}, {0.75, -0.125}, {0.25, 0.125}, { -0.25, 0.875}, + {0.25, -0.375}, { -0.75, 0.125}, { -0.75, 0.625}, { -0.25, -0.625}, + {0.75, 0.875}, {0.75, 0.375}, { -0.75, -0.375}, {0.25, 0.625} + } + }; + + private final GLAutoDrawable autoDrawable; + + private final JoGLDrawingTools drawingTools; + private final JoGLParameters parameters; + private final JoGLBuffersManager buffersManager; + private final JoGLRendererManager rendererManager; + private final JoGLPickingManager pickingManager; + private final JoGLTextureManager textureManager; + + private final CanvasAnimator canvasAnimator; + private boolean isOffscreen; + private DebugGL2 debug; + private boolean isValid = true; + private boolean displayFinished; + + + /** The current mainDrawer. */ + private Drawer mainDrawer; + + /** The anti-aliasing level */ + private int antiAliasingLevel = 0; + + /** + * Default constructor. + * @param autoDrawable the JoGL autoDrawable this canvas depend on. + */ + JoGLCanvas(GLAutoDrawable autoDrawable) { + this.autoDrawable = autoDrawable; + parameters = new JoGLParameters(); + buffersManager = new JoGLBuffersManager(); + rendererManager = new JoGLRendererManager(); + drawingTools = new JoGLDrawingTools(this); + pickingManager = new JoGLPickingManager(this); + textureManager = new JoGLTextureManager(this); + + autoDrawable.addGLEventListener(this); + canvasAnimator = new CanvasAnimator(autoDrawable); + canvasAnimator.redraw(); + } + + /** + * Constructor for offscreen rendering + * @param width the width + * @param height the height + */ + JoGLCanvas(int width, int height) { + this(getOffscreenDrawable(width, height)); + isOffscreen = true; + } + + public void setDebugMode(boolean debug) { + if (debug) { + this.debug = new DebugGL2(autoDrawable.getGL().getGL2()); + } else { + this.debug = null; + } + } + + // Implementation of getter & setter from Canvas. + + @Override + public void setMainDrawer(Drawer mainDrawer) { + this.mainDrawer = mainDrawer; + } + + @Override + public Drawer getMainDrawer() { + return mainDrawer; + } + + @Override + public JoGLRendererManager getRendererManager() { + return rendererManager; + } + + @Override + public JoGLBuffersManager getBuffersManager() { + return buffersManager; + } + + @Override + public PickingManager getPickingManager() { + return pickingManager; + } + + @Override + public JoGLTextureManager getTextureManager() { + return textureManager; + } + + @Override + public int getWidth() { + return autoDrawable.getSurfaceWidth(); + } + + @Override + public int getHeight() { + return autoDrawable.getSurfaceHeight(); + } + + @Override + public Dimension getDimension() { + return new Dimension(autoDrawable.getSurfaceWidth(), autoDrawable.getSurfaceHeight()); + } + + @Override + public void redraw() { + canvasAnimator.redraw(); + } + + @Override + public void redrawAndWait() { + if (SwingUtilities.isEventDispatchThread()) { + if (autoDrawable != null) { + autoDrawable.display(); + } + return; + } + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + autoDrawable.display(); + } + }); + } catch (Exception e) { } + } + + @Override + public void waitImage() { + canvasAnimator.waitEndOfDrawing(); + } + + @Override + public int getAntiAliasingLevel() { + return antiAliasingLevel; + } + + @Override + public void setAntiAliasingLevel(int antiAliasingLevel) { + this.antiAliasingLevel = antiAliasingLevel; + } + + // JoGLCanvas specific getter. + + /** + * Return the OpenGl context. + * @return the OpenGl context. + */ + public GL2 getGl() { + if (debug == null) { + return autoDrawable.getGL().getGL2(); + } else { + return debug; + } + } + + /** + * Return the rendering parameters. + * @return the rendering parameters. + */ + public JoGLParameters getJoGLParameters() { + return parameters; + } + + /** + * Get an image from the autoDrawable + * @return an image + */ + public BufferedImage getImage() { + return getImage(true); + } + + /** + * Get an image from the autoDrawable + * @return an image + */ + public BufferedImage getImage(final boolean alpha) { + while (!canvasAnimator.isDrawFinished() || !displayFinished) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + break; + } + } + + final BufferedImage[] image = new BufferedImage[1]; + final GLContext context = autoDrawable.getContext(); + + if (SwingUtilities.isEventDispatchThread()) { + context.makeCurrent(); + AWTGLReadBufferUtil buffer = new AWTGLReadBufferUtil(GLProfile.getDefault(), alpha); + image[0] = buffer.readPixelsToBufferedImage(getGl(), 0, 0, autoDrawable.getSurfaceWidth(), autoDrawable.getSurfaceHeight(), true); + context.release(); + } else { + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + context.makeCurrent(); + AWTGLReadBufferUtil buffer = new AWTGLReadBufferUtil(GLProfile.getDefault(), alpha); + image[0] = buffer.readPixelsToBufferedImage(getGl(), 0, 0, autoDrawable.getSurfaceWidth(), autoDrawable.getSurfaceHeight(), true); + context.release(); + } + }); + } catch (InterruptedException e) { + + } catch (InvocationTargetException e) { + System.err.println(e); + e.printStackTrace(); + } + } + + return image[0]; + } + + /** + * Destroy the GLPbuffer + */ + public void destroy() { + if (isOffscreen) { + ((GLOffscreenAutoDrawable) autoDrawable).destroy(); + } + try { + isValid = false; + canvasAnimator.finalize();//Thread.dumpStack(); + } catch (Throwable e) { + // TODO: handle exception + } + } + + /** + * Creates a GLPbuffer for an offscreen rendering + * @param width the width + * @param height the height + * @return a GLPbuffer + */ + private static GLAutoDrawable getOffscreenDrawable(int width, int height) { + GLDrawableFactory factory = GLDrawableFactory.getDesktopFactory(); + + GLCapabilities capabilities = new GLCapabilities(GLProfile.getDefault()); + capabilities.setPBuffer(true); + + return factory.createOffscreenAutoDrawable(null, capabilities, null, width, height); + } + + // Implementation of function from GLEventListener. + @Override + public void display(GLAutoDrawable glAutoDrawable) { + if (isValid) { + displayFinished = false; + GL2 gl = getGl().getGL2(); + buffersManager.glSynchronize(gl); + rendererManager.glSynchronize(gl); + drawingTools.glSynchronize(gl); + + if (mainDrawer != null) { + gl.glEnable(GL2.GL_DEPTH_TEST); + gl.glDepthFunc(GL2.GL_LEQUAL); // Set to less equal to allow last drawn object to be on the top. + + if ((antiAliasingLevel > 0) && (antiAliasingLevel < 5) && (drawingTools.getGLCapacity().isAccumulationBufferPresent())) { + org.scilab.forge.scirenderer.renderer.Renderer renderer = rendererManager.createRenderer(); + renderer.setDrawer(mainDrawer); + + double[][] jitter = ANTI_ALIASING_JITTER[antiAliasingLevel]; + Dimension dimension = getDimension(); + gl.glClear(GL2.GL_ACCUM_BUFFER_BIT); + for (double[] aJitter : jitter) { + drawingTools.glSynchronize(gl); + + gl.glMatrixMode(GL2.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glTranslated(aJitter[0] / dimension.getWidth(), aJitter[1] / dimension.getHeight(), 0.); + + gl.glClear(GL2.GL_DEPTH_BUFFER_BIT); + rendererManager.draw(drawingTools, renderer); + //mainDrawer.draw(drawingTools); + gl.glReadBuffer(GL2.GL_BACK); + gl.glAccum(GL2.GL_ACCUM, 1f / jitter.length); + } + + rendererManager.dispose(drawingTools, renderer); + + gl.glDrawBuffer(GL2.GL_BACK); + gl.glAccum(GL2.GL_RETURN, 1.0f); + } else { + gl.glMatrixMode(GL2.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glClear(GL2.GL_DEPTH_BUFFER_BIT); + mainDrawer.draw(drawingTools); + } + } + + pickingManager.glConsume(drawingTools); + displayFinished = true; + } + } + + @Override + public void init(GLAutoDrawable glAutoDrawable) { + textureManager.glReload(); + buffersManager.glReload(); + rendererManager.glReload(); + } + + @Override + public void reshape(GLAutoDrawable glAutoDrawable, int x, int y, int width, int height) { + } + + @Override + public void dispose(GLAutoDrawable drawable) { } + + /** + * this class manage asynchronous scene drawing. + */ + private class CanvasAnimator implements Runnable { + + private final Semaphore semaphore = new Semaphore(1); + private final Thread thread; + private final GLAutoDrawable autoDrawable; + private boolean running = true; + + public CanvasAnimator(GLAutoDrawable autoDrawable) { + this.autoDrawable = autoDrawable; + this.thread = new Thread(this); + thread.start(); + //System.err.println("[DEBUG] nb threads = "+Thread.activeCount()); + } + + @Override + public void finalize() throws Throwable { + running = false; + // we increment the semaphore to allow run() to unlock it and to be sure + // to go out. + semaphore.release(); + autoDrawable.destroy(); + super.finalize(); + } + + public synchronized boolean isDrawFinished() { + return semaphore.availablePermits() == 0; + } + + /** Ask the animator to perform a draw later. */ + public synchronized void redraw() { + semaphore.release(); + } + + /** Wait until a drawing has been performed */ + public void waitEndOfDrawing() { + semaphore.drainPermits(); + } + + @Override + public void run() { + while (running) { + try { + semaphore.acquire(); + semaphore.drainPermits(); + if (running) { + autoDrawable.display(); + } + } catch (InterruptedException e) { + if (running) { + Thread.currentThread().interrupt(); + } + break; + } catch (GLException e) { + if (running) { + throw e; + } + break; + } + } + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/JoGLCanvasFactory.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/JoGLCanvasFactory.java new file mode 100755 index 000000000..f82ae9208 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/JoGLCanvasFactory.java @@ -0,0 +1,49 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl; + +import org.scilab.forge.scirenderer.Canvas; + +import javax.media.opengl.GLAutoDrawable; + +/** + * @author Pierre Lando + */ +public final class JoGLCanvasFactory { + + /** + * Private constructor. + * This is an utility class. + */ + private JoGLCanvasFactory() { + + } + + /** + * Create a canvas from an auto drawable object. + * @param autoDrawable the auto drawable object. + * @return a canvas based on the given auto drawable object. + */ + public static Canvas createCanvas(final GLAutoDrawable autoDrawable) { + return new JoGLCanvas(autoDrawable); + } + + /** + * Create a canvas for an offscreen rendering. + * @param width the width + * @param height the height + * @return a canvas. + */ + public static Canvas createCanvas(final int width, final int height) { + return new JoGLCanvas(width, height); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/JoGLCapacity.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/JoGLCapacity.java new file mode 100755 index 000000000..e27be411f --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/JoGLCapacity.java @@ -0,0 +1,104 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl; + +import javax.media.opengl.GL2; + +/** + * This class store current OpenGl context capacity. + * + * @author Pierre Lando + */ +public class JoGLCapacity { + + /** The actual aliased point size range. */ + private final float[] aliasedPointSizeRange = new float[] {0, 0}; + + /** The actual light number. */ + private final int[] lightNumber = new int[] {8}; + + /** The actual light number. */ + private final int[] clippingPlaneNumber = new int[] {6}; + + /** Maximum texture size. */ + private final int[] textureSize = new int[] {64}; + + /** Accumulation buffer presence */ + private boolean accumulationBufferPresent = false; + + /** ABGR extension */ + private boolean hasABGRExtension = false; + + /** + * Default constructor. + * The constructor is package because, only {@link JoGLDrawingTools} use this class. + */ + JoGLCapacity() { + } + + /** + * Reset the OpenGl capacity from the given context. + * @param gl the given OpenGl context. + */ + void glReload(GL2 gl) { + gl.glGetFloatv(GL2.GL_ALIASED_POINT_SIZE_RANGE, aliasedPointSizeRange, 0); + gl.glGetIntegerv(GL2.GL_MAX_CLIP_PLANES, clippingPlaneNumber, 0); + gl.glGetIntegerv(GL2.GL_MAX_LIGHTS, lightNumber, 0); + gl.glGetIntegerv(GL2.GL_MAX_TEXTURE_SIZE, textureSize, 0); + + int[] nbAccumulationBits = {0, 0, 0}; + gl.glGetIntegerv(GL2.GL_ACCUM_RED_BITS, nbAccumulationBits, 0); + gl.glGetIntegerv(GL2.GL_ACCUM_GREEN_BITS, nbAccumulationBits, 1); + gl.glGetIntegerv(GL2.GL_ACCUM_BLUE_BITS, nbAccumulationBits, 2); + if (nbAccumulationBits[0] == 0 || nbAccumulationBits[1] == 0 || nbAccumulationBits[2] == 0) { + // accumulation buffers not there + accumulationBufferPresent = false; + } else { + accumulationBufferPresent = true; + } + + hasABGRExtension = gl.isExtensionAvailable("GL_EXT_abgr"); + } + + /** + * Return the aliased point size range. + * The returned array had two elements. + * @return the aliased point size range. + */ + public float[] getAliasedPointSizeRange() { + return aliasedPointSizeRange.clone(); + } + + /** + * Return the number of available light. + * @return the number of available light. + */ + public int getLightNumber() { + return lightNumber[0]; + } + + public int getClippingPlaneNumber() { + return clippingPlaneNumber[0]; + } + + public int getMaximumTextureSize() { + return textureSize[0]; + } + + public boolean isAccumulationBufferPresent() { + return accumulationBufferPresent; + } + + public boolean isABRExtensionPresent() { + return hasABGRExtension; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/JoGLDrawingTools.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/JoGLDrawingTools.java new file mode 100755 index 000000000..d7b34d3e5 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/JoGLDrawingTools.java @@ -0,0 +1,188 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl; + +import org.scilab.forge.scirenderer.DrawingTools; +import org.scilab.forge.scirenderer.SciRendererException; +import org.scilab.forge.scirenderer.buffers.ElementsBuffer; +import org.scilab.forge.scirenderer.clipping.ClippingManager; +import org.scilab.forge.scirenderer.implementation.jogl.clipping.JoGLClippingManager; +import org.scilab.forge.scirenderer.implementation.jogl.drawer.JoGLShapeDrawer; +import org.scilab.forge.scirenderer.implementation.jogl.lightning.JoGLLightManager; +import org.scilab.forge.scirenderer.lightning.LightManager; +import org.scilab.forge.scirenderer.renderer.Renderer; +import org.scilab.forge.scirenderer.shapes.appearance.Appearance; +import org.scilab.forge.scirenderer.shapes.appearance.Color; +import org.scilab.forge.scirenderer.shapes.geometry.Geometry; +import org.scilab.forge.scirenderer.texture.AnchorPosition; +import org.scilab.forge.scirenderer.texture.Texture; +import org.scilab.forge.scirenderer.tranformations.TransformationManager; +import org.scilab.forge.scirenderer.tranformations.TransformationManagerImpl; +import org.scilab.forge.scirenderer.tranformations.TransformationManagerListener; +import org.scilab.forge.scirenderer.tranformations.Vector3d; + +import javax.media.opengl.GL2; + +/** + * + * JoGl implementation of the DrawingTools. + * + * @author Pierre Lando + */ +public class JoGLDrawingTools implements DrawingTools { + + private final JoGLCapacity capacity = new JoGLCapacity(); + private final TransformationManager transformationManager; + private final JoGLLightManager lightManager; + private final JoGLClippingManager clippingManager; + private final JoGLCanvas canvas; + private GL2 gl; + + /** + * Default constructor. + * @param canvas the canvas where this drawing tools live. + */ + JoGLDrawingTools(JoGLCanvas canvas) { + this.transformationManager = new TransformationManagerImpl(canvas); + this.lightManager = new JoGLLightManager(this); + this.clippingManager = new JoGLClippingManager(this); + + this.canvas = canvas; + + transformationManager.addListener(new TransformationManagerListener() { + @Override + public void transformationChanged(TransformationManager transformationManager) { + gl.glMatrixMode(GL2.GL_MODELVIEW); + if (transformationManager.isUsingSceneCoordinate()) { + gl.glLoadMatrixd(transformationManager.getTransformation().getMatrix(), 0); + } else { + gl.glLoadMatrixd(transformationManager.getWindowTransformation().getMatrix(), 0); + } + } + }); + } + + /** + * Synchronise to the given OpenGl context. + * @param gl the OpenGL context. + */ + void glSynchronize(GL2 gl) { + this.gl = gl; + transformationManager.reset(); + capacity.glReload(gl); + lightManager.reload(); + clippingManager.reload(); + } + + @Override + public JoGLCanvas getCanvas() { + return canvas; + } + + /** + * Return the OpenGl context. + * @return the OpenGl context. + */ + public GL2 getGl() { + return gl; + } + + /** + * Return the OpenGl capacity of this canvas. + * @return the OpenGl capacity of this canvas. + */ + public JoGLCapacity getGLCapacity() { + return capacity; + } + + @Override + public TransformationManager getTransformationManager() { + return transformationManager; + } + + @Override + public LightManager getLightManager() { + return lightManager; + } + + @Override + public ClippingManager getClippingManager() { + return clippingManager; + } + + @Override + public void clear(Color color) { + gl.glClearColor(color.getRedAsFloat(), color.getGreenAsFloat(), color.getBlueAsFloat(), 1.f); + gl.glClear(GL2.GL_COLOR_BUFFER_BIT); + } + + @Override + public void clear(java.awt.Color color) { + float[] colorData = color.getRGBColorComponents(null); + gl.glClearColor(colorData[0], colorData[1], colorData[2], 1f); + gl.glClear(GL2.GL_COLOR_BUFFER_BIT); + } + + @Override + public void clearDepthBuffer() { + gl.glClear(GL2.GL_DEPTH_BUFFER_BIT); + } + + @Override + public void draw(Renderer renderer) { + canvas.getRendererManager().draw(this, renderer); + } + + @Override + public void draw(Geometry geometry) throws SciRendererException { + JoGLShapeDrawer.getDrawer().draw(this, geometry, Appearance.getDefault()); + } + + @Override + public void draw(Geometry geometry, Appearance appearance) throws SciRendererException { + JoGLShapeDrawer.getDrawer().draw(this, geometry, appearance); + } + + @Override + public void draw(Texture texture) throws SciRendererException { + canvas.getTextureManager().draw(this, texture); + } + + @Override + public void draw(Texture texture, AnchorPosition anchor, ElementsBuffer positions) throws SciRendererException { + canvas.getTextureManager().draw(this, texture, anchor, positions, 0, 1, 0); + } + + @Override + public void draw(Texture texture, AnchorPosition anchor, ElementsBuffer positions, int offset, int stride, double rotationAngle) throws SciRendererException { + canvas.getTextureManager().draw(this, texture, anchor, positions, offset, stride, rotationAngle); + } + + @Override + public void draw(Texture texture, AnchorPosition anchor, Vector3d position) throws SciRendererException { + canvas.getTextureManager().draw(this, texture, anchor, position, 0); + } + + @Override + public void draw(Texture texture, AnchorPosition anchor, Vector3d position, double rotationAngle) throws SciRendererException { + canvas.getTextureManager().draw(this, texture, anchor, position, rotationAngle); + } + + /** + * Bind the given texture to the OpenGl context. + * @param texture the given texture. + * @throws SciRendererException is thrown if the texture is invalid. + */ + public void bind(Texture texture) throws SciRendererException { + canvas.getTextureManager().bind(this, texture); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/JoGLParameters.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/JoGLParameters.java new file mode 100755 index 000000000..86ab16b5f --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/JoGLParameters.java @@ -0,0 +1,65 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl; + +import javax.media.opengl.GL; +import javax.media.opengl.glu.GLU; + +/** + * @author Pierre Lando + */ +public class JoGLParameters { + + private static final boolean USE_DISPLAY_LIST = false; + private static final boolean USE_POINT_SPRITE = true; + private static Boolean USE_VBO; + + /** + * Default constructor. + * It's package : only JoGLCanvas need this kind of object. + */ + JoGLParameters() { + } + + /** + * Use display list. + * @return true if use display list. + */ + public boolean useDisplayList() { + return USE_DISPLAY_LIST; + } + + /** + * Use VBO. + * @return true if use VBO. + */ + public boolean useVBO() { + if (USE_VBO == null) { + GL gl = GLU.getCurrentGL(); + USE_VBO = gl.isExtensionAvailable("GL_ARB_vertex_buffer_object") + && gl.isFunctionAvailable("glBindBufferARB") + && gl.isFunctionAvailable("glGenBuffersARB") + && gl.isFunctionAvailable("glBufferDataARB") + && gl.isFunctionAvailable("glDeleteBuffersARB"); + } + + return USE_VBO; + } + + /** + * Use point sprite. + * @return true if use point sprite. + */ + public boolean usePointSprite() { + return USE_POINT_SPRITE; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/buffers/JoGLBuffersManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/buffers/JoGLBuffersManager.java new file mode 100755 index 000000000..25a1c1bee --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/buffers/JoGLBuffersManager.java @@ -0,0 +1,226 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.buffers; + +import org.scilab.forge.scirenderer.buffers.BuffersManager; +import org.scilab.forge.scirenderer.buffers.DataBuffer; +import org.scilab.forge.scirenderer.buffers.ElementsBuffer; +import org.scilab.forge.scirenderer.buffers.IndicesBuffer; + +import javax.media.opengl.GL2; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.Stack; + +/** + * @author Pierre Lando + */ +public final class JoGLBuffersManager implements BuffersManager { + + /** + * Set off current buffers. + */ + private final Set<JoGLDataBuffer> buffers = new HashSet<JoGLDataBuffer>(); + + /** + * Set of dead buffers. + */ + private final Stack<JoGLDataBuffer> deadBuffers = new Stack<JoGLDataBuffer>(); + + /** + * Default constructor. + */ + public JoGLBuffersManager() { + } + + @Override + public synchronized ElementsBuffer createElementsBuffer() { + JoGLElementsBuffer newBuffer = new JoGLElementsBuffer(); + buffers.add(newBuffer); + return newBuffer; + } + + @Override + public synchronized IndicesBuffer createIndicesBuffer() { + JoGLIndicesBuffer newBuffer = new JoGLIndicesBuffer(); + buffers.add(newBuffer); + return newBuffer; + } + + @Override + public synchronized void dispose(DataBuffer buffer) { + JoGLDataBuffer localBuffer = getLocalBuffer(buffer); + if (localBuffer != null) { + buffers.remove(localBuffer); + localBuffer.clear(); + deadBuffers.push(localBuffer); + } + } + + @Override + public synchronized void dispose(Collection <? extends DataBuffer > buffers) { + for (DataBuffer buffer : buffers) { + dispose(buffer); + } + } + + /** + * Called when previous OpenGl context is gone. + */ + public synchronized void glReload() { + for (JoGLDataBuffer buffer : buffers) { + buffer.reload(); + } + } + + /** + * Called before rendering for synchronisation. + * Clean dead buffers. + * @param gl the OpenGl context. + */ + public synchronized void glSynchronize(GL2 gl) { + int[] names = new int[deadBuffers.size()]; + int i = 0; + while (!deadBuffers.isEmpty()) { + int n = deadBuffers.pop().disposeWithoutDelete(gl); + if (n != -1) { + names[i++] = n; + } + } + + if (i != 0) { + gl.glDeleteBuffers(i, names, 0); + } + } + + /** + * Bind the given buffer as vertex buffer. + * @param gl the OpenGl context where the buffer is bind. + * @param buffer the buffer to bind. + * @return the number of element actually bind. + */ + public int bindVertexBuffer(GL2 gl, ElementsBuffer buffer) { + JoGLElementsBuffer localBuffer = getLocalElementsBuffer(buffer); + if (localBuffer != null) { + return localBuffer.bindAsVertexBuffer(gl); + } else { + return 0; + } + } + + /** + * Bind the given buffer as normal buffer. + * @param gl the OpenGl context where the buffer is bind. + * @param buffer the buffer to bind. + * @return the number of element actually bind. + */ + public int bindNormalsBuffer(GL2 gl, ElementsBuffer buffer) { + JoGLElementsBuffer localBuffer = getLocalElementsBuffer(buffer); + if (localBuffer != null) { + return localBuffer.bindAsNormalsBuffer(gl); + } else { + return 0; + } + } + + /** + * Bind the given buffer as texture coordinate buffer. + * @param gl the OpenGl context where the buffer is bind. + * @param buffer the buffer to bind. + * @return the number of element actually bind. + */ + public int bindTextureCoordinatesBuffer(GL2 gl, ElementsBuffer buffer) { + JoGLElementsBuffer localBuffer = getLocalElementsBuffer(buffer); + if (localBuffer != null) { + return localBuffer.bindAsTextureCoordinatesBuffer(gl); + } else { + return 0; + } + } + + /** + * Bind the given buffer as color buffer. + * @param gl the OpenGl context where the buffer is bind. + * @param buffer the buffer to bind. + * @return the number of element actually bind. + */ + public int bindColorsBuffer(GL2 gl, ElementsBuffer buffer) { + JoGLElementsBuffer localBuffer = getLocalElementsBuffer(buffer); + if (localBuffer != null) { + return localBuffer.bindAsColorsBuffer(gl); + } else { + return 0; + } + } + + /** + * Bind the given buffer as indices buffer. + * @param gl the OpenGl context where the buffer is bind. + * @param buffer the buffer to bind. + * @return the number of element actually bind. + */ + public int bindIndicesBuffer(GL2 gl, IndicesBuffer buffer) { + JoGLDataBuffer localBuffer = getLocalBuffer(buffer); + if (localBuffer != null) { + gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, localBuffer.getGlName(gl)); + return buffer.getSize(); + } else { + return 0; + } + } + + /** + * This method check buffer to be from here. + * @param buffer the given buffer. + * @return the JoGL instance of the buffer. + */ + private synchronized JoGLDataBuffer getLocalBuffer(DataBuffer buffer) { + if (buffer instanceof JoGLDataBuffer) { + JoGLDataBuffer localBuffer = (JoGLDataBuffer) buffer; + if (buffers.contains(localBuffer)) { + return localBuffer; + } + } + return null; + } + + /** + * This method check buffer to be from here. + * @param buffer the given buffer. + * @return the JoGL instance of the buffer. + */ + private synchronized JoGLIndicesBuffer getLocalIndicesBuffer(IndicesBuffer buffer) { + if (buffer instanceof JoGLIndicesBuffer) { + JoGLIndicesBuffer localBuffer = (JoGLIndicesBuffer) buffer; + if (buffers.contains(localBuffer)) { + return localBuffer; + } + } + return null; + } + + /** + * This method check buffer to be from here. + * @param buffer the given buffer. + * @return the JoGL instance of the buffer. + */ + private synchronized JoGLElementsBuffer getLocalElementsBuffer(ElementsBuffer buffer) { + if (buffer instanceof JoGLElementsBuffer) { + JoGLElementsBuffer localBuffer = (JoGLElementsBuffer) buffer; + if (buffers.contains(localBuffer)) { + return localBuffer; + } + } + return null; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/buffers/JoGLDataBuffer.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/buffers/JoGLDataBuffer.java new file mode 100755 index 000000000..0912785c7 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/buffers/JoGLDataBuffer.java @@ -0,0 +1,150 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.buffers; + +import org.scilab.forge.scirenderer.buffers.DataBuffer; + +import javax.media.opengl.GL; +import java.nio.Buffer; + +/** + * @author Pierre Lando + */ +public abstract class JoGLDataBuffer implements DataBuffer { + + /** + * True if the data have been uploaded to OpenGl driver. + */ + private boolean dataUploaded; + + /** + * The OpenGl name for this buffer. + * - null : no name + * - an Integer : the name. + */ + private Integer glName; + + /** + * Default constructor. + * The constructor is package : only {@link JoGLBuffersManager} can instantiate this object. + */ + JoGLDataBuffer() { + dataUploaded = false; + glName = null; + } + + /** + * Called to reload a buffer. + */ + public void reload() { + glName = null; + dataUploaded = false; + } + + /** + * Called when it's time to kill this buffer. + * @param gl the OpenGl context. + */ + public void dispose(GL gl) { + if ((glName != null) && gl.glIsBuffer(glName)) { + int[] t = new int[] {glName}; + gl.glDeleteBuffers(1, t, 0); + glName = null; + dataUploaded = false; + } + } + + /** + * Called when it's time to kill this buffer. + * The effective delete is done in JoGLBuffersManager::glSynchronize (to vectorize it). + * @param gl the OpenGl context. + */ + public int disposeWithoutDelete(GL gl) { + if ((glName != null) && gl.glIsBuffer(glName)) { + int n = glName; + glName = null; + dataUploaded = false; + + return n; + } + + return -1; + } + + /** + * Synchronize this buffer. + * @param gl the OpenGl context where synchronization is done. + */ + protected void synchronize(GL gl) { + // Check the glName. + if ((glName == null) || !(gl.glIsBuffer(glName))) { + int[] t = new int[1]; + gl.glGenBuffers(1, t, 0); + glName = t[0]; + dataUploaded = false; + } + + // Upload data to the OpenGl driver. + if (!dataUploaded) { + gl.glBindBuffer(getGLBindDestination(), glName); + gl.glBufferData(getGLBindDestination(), getByteSize(), getByteBuffer(), GL.GL_STATIC_DRAW); + gl.glBindBuffer(getGLBindDestination(), 0); + dataUploaded = true; + } + } + + /** + * Return the data uploaded status. + * @return the data uploaded status. + */ + protected boolean isDataUploaded() { + return dataUploaded; + } + + /** + * Set the data uploaded status. + * @param dataUploaded the new data uploaded status. + */ + protected void setDataUploaded(boolean dataUploaded) { + this.dataUploaded = dataUploaded; + } + + /** + * Return the OpenGl name of this buffer. + * @param gl the OpenGl context. + * @return the OpenGl name of this buffer. + */ + protected Integer getGlName(GL gl) { + synchronize(gl); + return glName; + } + + /** + * Return the data as byte buffer. + * @return the data as byte buffer. + */ + protected abstract Buffer getByteBuffer(); + + /** + * Return the OpenGl bind destination. + * @return the OpenGl bind destination. + */ + protected abstract int getGLBindDestination(); + + /** + * Return the size of this buffer in byte. + * @return the size of this buffer in byte. + */ + public abstract int getByteSize(); + + public abstract void clear(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/buffers/JoGLElementsBuffer.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/buffers/JoGLElementsBuffer.java new file mode 100755 index 000000000..0824b710a --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/buffers/JoGLElementsBuffer.java @@ -0,0 +1,313 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.buffers; + +import org.scilab.forge.scirenderer.buffers.ElementsBuffer; + +import javax.media.opengl.GL2; +import java.nio.Buffer; +import java.nio.FloatBuffer; + +/** + * @author Pierre Lando + */ +@SuppressWarnings(value = { "serial" }) +public class JoGLElementsBuffer extends JoGLDataBuffer implements ElementsBuffer { + + /** + * The current size of one element. + */ + public static final int ELEMENT_SIZE = 4; + + /** + * The default vertex. + */ + private static final float[] DEFAULT_VERTEX = new float[] {0, 0, 0, 1}; + + /** + * the data this buffer contain. + */ + private FloatBuffer data; + private final Object mutex; + + /** + * Default constructor. + * The constructor is package : only {@link JoGLBuffersManager} can instantiate this object. + */ + JoGLElementsBuffer() { + mutex = new Object(); + data = null; + } + + + @Override + public void setData(float[] newData, int elementSize) { + // Check the given vertex size + if ((elementSize < 1) || (elementSize > ELEMENT_SIZE)) { + throw new BadElementSizeException(elementSize, 1, ELEMENT_SIZE); + } + + int verticesNumber = newData.length / elementSize; + //FloatBuffer buffer = BufferUtil.newFloatBuffer(ELEMENT_SIZE * verticesNumber); + FloatBuffer buffer = FloatBuffer.allocate(ELEMENT_SIZE * verticesNumber); + buffer.rewind(); + + + // Fill buffer with given data. + // Missing coordinate are filled with the 'DEFAULT_VERTEX' ones. + int k = 0; + for (int i = 0; i < verticesNumber; i++) { + for (int j = 0; j < ELEMENT_SIZE; j++) { + if (j < elementSize) { + buffer.put(newData[k++]); + } else { + buffer.put(DEFAULT_VERTEX[j]); + } + } + } + + buffer.rewind(); + setData(buffer); + setDataUploaded(false); + } + + @Override + public void setData(Float[] newData, int elementSize) { + + // Check the given vertex size + if ((elementSize < 1) || (elementSize > ELEMENT_SIZE)) { + throw new BadElementSizeException(elementSize, 1, ELEMENT_SIZE); + } + + int verticesNumber = newData.length / elementSize; + //FloatBuffer buffer = BufferUtil.newFloatBuffer(ELEMENT_SIZE * verticesNumber); + FloatBuffer buffer = FloatBuffer.allocate(ELEMENT_SIZE * verticesNumber); + buffer.rewind(); + + + // Fill buffer with given data. + // Missing coordinate are filled with the 'DEFAULT_VERTEX' ones. + int k = 0; + for (int i = 0; i < verticesNumber; i++) { + for (int j = 0; j < ELEMENT_SIZE; j++) { + if (j < elementSize) { + buffer.put(newData[k++]); + } else { + buffer.put(DEFAULT_VERTEX[j]); + } + } + } + + buffer.rewind(); + setData(buffer); + setDataUploaded(false); + } + + @Override + public void setData(FloatBuffer newData, int elementsSize) { + // Check the given vertex size + if ((elementsSize < 1) || (elementsSize > ELEMENT_SIZE)) { + throw new BadElementSizeException(elementsSize, 1, ELEMENT_SIZE); + } + + if (elementsSize == 4) { + // No need to complete buffer. + if (newData != null) { + newData.rewind(); + } + setData(newData); + setDataUploaded(false); + return; + } + + int verticesNumber = newData.limit() / elementsSize; + //FloatBuffer buffer = BufferUtil.newFloatBuffer(ELEMENT_SIZE * verticesNumber); + FloatBuffer buffer = FloatBuffer.allocate(ELEMENT_SIZE * verticesNumber); + buffer.rewind(); + + // Fill buffer with given data. + // Missing coordinate are filled with the 'DEFAULT_VERTEX' ones. + newData.rewind(); + for (int i = 0; i < verticesNumber; i++) { + for (int j = 0; j < ELEMENT_SIZE; j++) { + if (j < elementsSize) { + buffer.put(newData.get()); + } else { + buffer.put(DEFAULT_VERTEX[j]); + } + } + } + + buffer.rewind(); + setData(buffer); + setDataUploaded(false); + } + + @Override + public int getSize() { + synchronized (mutex) { + if (data == null) { + return 0; + } else { + return data.limit() / ELEMENT_SIZE; + } + } + } + + @Override + public FloatBuffer getData() { + synchronized (mutex) { + if (data != null) { + return data; + } else { + return null; + } + } + } + + @Override + public int getElementsSize() { + return ELEMENT_SIZE; + } + + @Override + public int getByteSize() { + synchronized (mutex) { + if (data == null) { + return 0; + } else { + return data.limit() * (Float.SIZE / Byte.SIZE); + } + } + } + + @Override + protected Buffer getByteBuffer() { + synchronized (mutex) { + if (data != null) { + data.rewind(); + } + return data; + } + } + + @Override + protected int getGLBindDestination() { + return GL2.GL_ARRAY_BUFFER; + } + + public int bindAsVertexBuffer(GL2 gl) { + synchronized (mutex) { + if (getSize() != 0) { + gl.glEnableClientState(GL2.GL_VERTEX_ARRAY); + gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, getGlName(gl)); + gl.glVertexPointer(getElementsSize(), GL2.GL_FLOAT, 0, 0); + gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0); + if (data == null) { + return 0; + } else { + return data.limit() * (Float.SIZE / Byte.SIZE); + } + } else { + return 0; + } + } + } + + public int bindAsNormalsBuffer(GL2 gl) { + synchronized (mutex) { + if (getSize() != 0) { + gl.glEnableClientState(GL2.GL_NORMAL_ARRAY); + gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, getGlName(gl)); + gl.glNormalPointer(GL2.GL_FLOAT, getElementsSize() * Float.SIZE / Byte.SIZE, 0); + gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0); + if (data == null) { + return 0; + } else { + return data.limit() * (Float.SIZE / Byte.SIZE); + } + } else { + return 0; + } + } + } + + public int bindAsColorsBuffer(GL2 gl) { + synchronized (mutex) { + if (getSize() != 0) { + gl.glEnableClientState(GL2.GL_COLOR_ARRAY); + gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, getGlName(gl)); + gl.glColorPointer(getElementsSize(), GL2.GL_FLOAT, 0, 0); + gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0); + if (data == null) { + return 0; + } else { + return data.limit() * (Float.SIZE / Byte.SIZE); + } + } else { + return 0; + } + } + } + + public int bindAsTextureCoordinatesBuffer(GL2 gl) { + synchronized (mutex) { + if (getSize() != 0) { + gl.glEnableClientState(GL2.GL_TEXTURE_COORD_ARRAY); + gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, getGlName(gl)); + gl.glTexCoordPointer(getElementsSize(), GL2.GL_FLOAT, 0, 0); + gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0); + if (data == null) { + return 0; + } else { + return data.limit() * (Float.SIZE / Byte.SIZE); + } + } else { + return 0; + } + } + } + + /** + * Really set the data. + * @param data the new data. + */ + private void setData(FloatBuffer data) { + synchronized (mutex) { + this.data = data; + } + } + + /** + * A specific runtime exception for bad elements size. + */ + private static class BadElementSizeException extends RuntimeException { + + /** + * Default constructor. + * @param size the size given for elements. + * @param min the minimum possible size. + * @param max the upper bound of possible size (excluded of possible size). + */ + public BadElementSizeException(int size, int min, int max) { + super("Bad vertex elements size : " + size + ". Should be in [" + min + ", " + (max - 1) + "]"); + } + } + + @Override + public void clear() { + if (data != null) { + data.clear(); + } + data = null; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/buffers/JoGLIndicesBuffer.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/buffers/JoGLIndicesBuffer.java new file mode 100755 index 000000000..a6678c2bf --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/buffers/JoGLIndicesBuffer.java @@ -0,0 +1,116 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.buffers; + +import org.scilab.forge.scirenderer.buffers.IndicesBuffer; + +import javax.media.opengl.GL; +import java.nio.Buffer; +import java.nio.IntBuffer; +import java.util.Collection; + +/** + * @author Pierre Lando + */ +public class JoGLIndicesBuffer extends JoGLDataBuffer implements IndicesBuffer { + + /** + * the data this buffer contain. + */ + private IntBuffer data; + + /** + * Default constructor. + * The constructor is package : only {@link JoGLBuffersManager} can instantiate this object. + */ + JoGLIndicesBuffer() { + data = null; + } + + + @Override + public void setData(int[] indices) { + //IntBuffer buffer = BufferUtil.newIntBuffer(indices.length); + IntBuffer buffer = IntBuffer.allocate(indices.length); + buffer.rewind(); + buffer.put(indices); + buffer.rewind(); + this.data = buffer; + setDataUploaded(false); + } + + @Override + public void setData(Collection<Integer> indices) { + IntBuffer buffer = IntBuffer.allocate(indices.size()); + buffer.rewind(); + for (int index : indices) { + buffer.put(index); + } + buffer.rewind(); + this.data = buffer; + setDataUploaded(false); + } + + @Override + public void setData(IntBuffer indexBuffer) { + //IntBuffer buffer = BufferUtil.newIntBuffer(indexBuffer.limit()); + IntBuffer buffer = IntBuffer.allocate(indexBuffer.limit()); + buffer.rewind(); + indexBuffer.rewind(); + buffer.put(indexBuffer); + buffer.rewind(); + indexBuffer.rewind(); + this.data = buffer; + setDataUploaded(false); + } + + @Override + public int getSize() { + if (data == null) { + return 0; + } else { + return data.limit(); + } + } + + @Override + public IntBuffer getData() { + return data.asReadOnlyBuffer(); + } + + @Override + public int getByteSize() { + if (data == null) { + return 0; + } else { + return data.limit() * (Integer.SIZE / Byte.SIZE); + } + } + + @Override + protected Buffer getByteBuffer() { + if (data != null) { + data.rewind(); + } + return data; + } + + @Override + protected int getGLBindDestination() { + return GL.GL_ELEMENT_ARRAY_BUFFER; + } + + public void clear() { + data.clear(); + data = null; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/clipping/JoGLClippingManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/clipping/JoGLClippingManager.java new file mode 100755 index 000000000..48722c68a --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/clipping/JoGLClippingManager.java @@ -0,0 +1,78 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.clipping; + +import org.scilab.forge.scirenderer.clipping.ClippingManager; +import org.scilab.forge.scirenderer.clipping.ClippingPlane; +import org.scilab.forge.scirenderer.implementation.jogl.JoGLDrawingTools; + +import javax.media.opengl.GL2; + +/** + * @author Pierre Lando + */ +public class JoGLClippingManager implements ClippingManager { + + /** + * Used drawing tools. + */ + private final JoGLDrawingTools drawingTools; + + /** + * Clipping planes array. + */ + private final JoGLClippingPlane[] clippingPlanes; + + /** + * Default constructor. + * @param drawingTools used drawing tools. + */ + public JoGLClippingManager(JoGLDrawingTools drawingTools) { + this.drawingTools = drawingTools; + this.clippingPlanes = new JoGLClippingPlane[getClippingPlaneNumber()]; + } + + @Override + public int getClippingPlaneNumber() { + return drawingTools.getGLCapacity().getClippingPlaneNumber(); + } + + @Override + public JoGLClippingPlane getClippingPlane(int i) { + if (i < 0 || i >= getClippingPlaneNumber()) { + return null; + } else { + if (clippingPlanes[i] == null) { + clippingPlanes[i] = new JoGLClippingPlane(drawingTools.getGl().getGL2(), i); + } + return clippingPlanes[i]; + } + } + + @Override + public void disableClipping() { + for (ClippingPlane clippingPlane : clippingPlanes) { + if (clippingPlane != null) { + clippingPlane.setEnable(false); + } + } + } + + public void reload() { + GL2 gl = drawingTools.getGl().getGL2(); + for (JoGLClippingPlane clippingPlane : clippingPlanes) { + if (clippingPlane != null) { + clippingPlane.reload(gl); + } + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/clipping/JoGLClippingPlane.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/clipping/JoGLClippingPlane.java new file mode 100755 index 000000000..dc4361b65 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/clipping/JoGLClippingPlane.java @@ -0,0 +1,123 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.clipping; + +import org.scilab.forge.scirenderer.clipping.ClippingPlane; +import org.scilab.forge.scirenderer.implementation.jogl.utils.GLShortCuts; +import org.scilab.forge.scirenderer.tranformations.Transformation; +import org.scilab.forge.scirenderer.tranformations.TransformationFactory; +import org.scilab.forge.scirenderer.tranformations.Vector4d; + +import javax.media.opengl.GL2; + +/** + * @author Pierre Lando + */ +public class JoGLClippingPlane implements ClippingPlane { + + /** + * GL index of this clipping plane. + */ + private final int index; + + /** + * Current GL context. + */ + private GL2 gl; + + /** + * Clipping plane enabled status. + */ + private boolean isEnable; + + /** + * Clipping plane equation look like: {@code x*a + y*b + z*c + d = 0}. + * Where {@code equation} is {@code [a, b, c, d]}. + */ + private Vector4d equation = new Vector4d(0, 0, 0, 0); + private Transformation transformation = TransformationFactory.getIdentity(); + + /** + * Default constructor. + * @param gl the OpenGl context. + * @param index the id of this clipping plane. + */ + public JoGLClippingPlane(GL2 gl, int index) { + this.isEnable = false; + this.index = index; + reload(gl); + } + + @Override + public boolean isEnable() { + return isEnable; + } + + @Override + public void setEnable(boolean isEnable) { + this.isEnable = isEnable; + GLShortCuts.setEnable(gl, GL2.GL_CLIP_PLANE0 + getIndex(), isEnable); + } + + @Override + public void setEquation(Vector4d v) { + equation = v; + setGLEquation(); + } + + @Override + public Vector4d getEquation() { + return equation; + } + + @Override + public void setTransformation(Transformation transformation) { + this.transformation = transformation; + setGLEquation(); + } + + @Override + public Transformation getTransformation() { + return transformation; + } + + @Override + public int getIndex() { + return index; + } + + /** + * Set GL context and synchronise it. + * @param gl the current gl context. + */ + void reload(GL2 gl) { + this.gl = gl; + GLShortCuts.setEnable(gl, GL2.GL_CLIP_PLANE0 + getIndex(), isEnable()); + setGLEquation(); + } + + /** + * Set the GL equation. + */ + private void setGLEquation() { + // We use global coordinate. So we need to load identity. + gl.glMatrixMode(GL2.GL_MODELVIEW); + gl.glPushMatrix(); + if (transformation == null) { + gl.glLoadIdentity(); + } else { + gl.glLoadMatrixd(transformation.getMatrix(), 0); + } + gl.glClipPlane(GL2.GL_CLIP_PLANE0 + getIndex(), equation.getData(), 0); + gl.glPopMatrix(); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/drawer/JoGLShapeDrawer.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/drawer/JoGLShapeDrawer.java new file mode 100755 index 000000000..87f3099e8 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/drawer/JoGLShapeDrawer.java @@ -0,0 +1,420 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.drawer; + +import org.scilab.forge.scirenderer.SciRendererException; +import org.scilab.forge.scirenderer.buffers.IndicesBuffer; +import org.scilab.forge.scirenderer.implementation.jogl.JoGLDrawingTools; +import org.scilab.forge.scirenderer.implementation.jogl.buffers.JoGLBuffersManager; +import org.scilab.forge.scirenderer.implementation.jogl.buffers.JoGLElementsBuffer; +import org.scilab.forge.scirenderer.implementation.jogl.utils.GLShortCuts; +import org.scilab.forge.scirenderer.shapes.appearance.Appearance; +import org.scilab.forge.scirenderer.shapes.geometry.Geometry; +import org.scilab.forge.scirenderer.texture.Texture; +import org.scilab.forge.scirenderer.lightning.LightManager; +import org.scilab.forge.scirenderer.shapes.appearance.Material; + +import javax.media.opengl.GL2; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +/** + * Utility class for drawing shapes. + * @author Pierre Lando + */ +public final class JoGLShapeDrawer { + + private static JoGLShapeDrawer drawer; + + /** + * Private constructor : this is an utility class. + */ + private JoGLShapeDrawer() { + } + + /** + * Singleton getter. + * @return the unique {@link JoGLShapeDrawer}. + */ + public static JoGLShapeDrawer getDrawer() { + if (drawer == null) { + drawer = new JoGLShapeDrawer(); + } + return drawer; + } + + /** + * Draw a given geometry with given appearance. + * @param drawingTools the drawing tools. + * @param geometry the geometry. + * @param appearance the appearance. + * @throws org.scilab.forge.scirenderer.SciRendererException if the draw is not possible. + */ + public void draw(JoGLDrawingTools drawingTools, Geometry geometry, Appearance appearance) throws SciRendererException { + GL2 gl = drawingTools.getGl().getGL2(); + gl.glFrontFace(GL2.GL_CCW); + switch (geometry.getFaceCullingMode()) { + case CW: + gl.glEnable(GL2.GL_CULL_FACE); + gl.glCullFace(GL2.GL_FRONT); + break; + case CCW: + gl.glEnable(GL2.GL_CULL_FACE); + gl.glCullFace(GL2.GL_BACK); + break; + case BOTH: + gl.glDisable(GL2.GL_CULL_FACE); + break; + default: + gl.glDisable(GL2.GL_CULL_FACE); + break; + } + + if (drawingTools.getCanvas().getJoGLParameters().useVBO()) { + vboDrawing(drawingTools, geometry, appearance); + } else { + directDrawing(drawingTools, geometry, appearance); + } + + GLShortCuts.useLineAppearance(gl, null); + gl.glDisable(GL2.GL_CULL_FACE); + } + + /** + * Perform geometry drawing using VBO. + * @param drawingTools the drawing tools. + * @param geometry the geometry to draw. + * @param appearance the current appearance. + * @throws org.scilab.forge.scirenderer.SciRendererException if the draw is not possible. + */ + private void vboDrawing(JoGLDrawingTools drawingTools, Geometry geometry, Appearance appearance) throws SciRendererException { + final GL2 gl = drawingTools.getGl().getGL2(); + final JoGLBuffersManager buffersManager = drawingTools.getCanvas().getBuffersManager(); + final Texture texture = appearance.getTexture(); + + int verticesNumber = buffersManager.bindVertexBuffer(gl, geometry.getVertices()); + if (verticesNumber == 0) { + gl.glDisableClientState(GL2.GL_VERTEX_ARRAY); + return; + } + + buffersManager.bindNormalsBuffer(gl, geometry.getNormals()); + + if (texture != null && geometry.getTextureCoordinates() != null) { + synchronized (texture) { + if (texture.isValid()) { + drawingTools.bind(texture); + buffersManager.bindTextureCoordinatesBuffer(gl, geometry.getTextureCoordinates()); + } + } + } else { + buffersManager.bindColorsBuffer(gl, geometry.getColors()); + } + + // We use polygon offset for filled geometry if required. + if (geometry.getPolygonOffsetMode()) { + gl.glEnable(GL2.GL_POLYGON_OFFSET_FILL); + gl.glPolygonOffset(1, 1); + } + + GLShortCuts.useColor(gl, appearance.getFillColor()); + + LightManager lm = drawingTools.getLightManager(); + boolean lighting = lm.isLightningEnable(); + if (lighting) { + lm.setMaterial(appearance.getMaterial()); + gl.glEnable(GL2.GL_NORMALIZE); + } + + IndicesBuffer indices = geometry.getIndices(); + if (geometry.getFillDrawingMode() != Geometry.FillDrawingMode.NONE) { + if (indices != null) { + int indicesSize = buffersManager.bindIndicesBuffer(gl, indices); + if (indicesSize > 0) { + gl.glDrawElements(getGlMode(geometry.getFillDrawingMode()), indicesSize, GL2.GL_UNSIGNED_INT, 0); + gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, 0); + } + } else { + int count = geometry.getVertices().getSize(); + if (count > 0) { + gl.glDrawArrays(getGlMode(geometry.getFillDrawingMode()), 0, count); + } + } + } + + if (geometry.getPolygonOffsetMode()) { + gl.glDisable(GL2.GL_POLYGON_OFFSET_FILL); + } + + gl.glDisableClientState(GL2.GL_COLOR_ARRAY); + gl.glDisableClientState(GL2.GL_NORMAL_ARRAY); + gl.glDisableClientState(GL2.GL_TEXTURE_COORD_ARRAY); + gl.glDisable(GL2.GL_TEXTURE_2D); + //disable lighting to draw lines + lm.setLightningEnable(false); + gl.glDisable(GL2.GL_NORMALIZE); + + if (geometry.getLineDrawingMode() != Geometry.LineDrawingMode.NONE) { + if (appearance.getLineColor() != null || geometry.getColors() != null) { + GLShortCuts.useLineAppearance(gl, appearance); + if (appearance.getLineColor() == null) { + buffersManager.bindColorsBuffer(gl, geometry.getColors()); + } + + if (geometry.getWireIndices() != null) { + int edgesIndicesSize = buffersManager.bindIndicesBuffer(gl, geometry.getWireIndices()); + if (edgesIndicesSize > 0) { + gl.glDrawElements(getGlMode(geometry.getLineDrawingMode()), edgesIndicesSize, GL2.GL_UNSIGNED_INT, 0); + gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, 0); + } + } else { + int count = geometry.getVertices().getSize(); + if (count > 0) { + gl.glDrawArrays(getGlMode(geometry.getLineDrawingMode()), 0, count); + } + } + + gl.glDisableClientState(GL2.GL_COLOR_ARRAY); + } + } + + gl.glDisableClientState(GL2.GL_VERTEX_ARRAY); + lm.setLightningEnable(lighting); + } + + /** + * Perform geometry drawing by direct OpenGl call. + * @param drawingTools the drawing tools. + * @param geometry the geometry to draw. + * @param appearance the used appearance. + * @throws org.scilab.forge.scirenderer.SciRendererException if the draw is not possible. + */ + private void directDrawing( + JoGLDrawingTools drawingTools, Geometry geometry, Appearance appearance + ) throws SciRendererException { + final double sfactor; + final double tfactor; + final float[] fbuffer = new float[4]; + + GL2 gl = drawingTools.getGl().getGL2(); + if (geometry.getVertices() == null) { + return; + } + + FloatBuffer vertexBuffer = geometry.getVertices().getData(); + IndicesBuffer indices = geometry.getIndices(); + + FloatBuffer colorBuffer; + if (geometry.getColors() != null) { + colorBuffer = geometry.getColors().getData(); + } else { + colorBuffer = null; + } + + FloatBuffer normalBuffer; + if (geometry.getNormals() != null) { + normalBuffer = geometry.getNormals().getData(); + } else { + normalBuffer = null; + } + + Texture texture = appearance.getTexture(); + FloatBuffer textureCoordinatesBuffer; + if (texture != null && geometry.getTextureCoordinates() != null) { + synchronized (texture) { + drawingTools.bind(texture); + textureCoordinatesBuffer = geometry.getTextureCoordinates().getData(); + sfactor = texture.getSScaleFactor(); + tfactor = texture.getTScaleFactor(); + } + } else { + textureCoordinatesBuffer = null; + sfactor = 1; + tfactor = 1; + } + + final int elementsSize = JoGLElementsBuffer.ELEMENT_SIZE; + + if (geometry.getPolygonOffsetMode()) { + gl.glEnable(GL2.GL_POLYGON_OFFSET_FILL); + gl.glPolygonOffset(1, 1); + } + + LightManager lm = drawingTools.getLightManager(); + boolean lighting = lm.isLightningEnable(); + if (lighting) { + lm.setMaterial(appearance.getMaterial()); + } + + if (geometry.getFillDrawingMode() != Geometry.FillDrawingMode.NONE) { + GLShortCuts.useColor(gl, appearance.getFillColor()); + gl.glBegin(getGlMode(geometry.getFillDrawingMode())); + if (indices != null) { + IntBuffer indicesBuffer = indices.getData(); + indicesBuffer.rewind(); + for (int i = 0; i < indicesBuffer.limit(); i++) { + int index = indicesBuffer.get(i); + if ((index * elementsSize) < vertexBuffer.limit()) { + + if (colorBuffer != null) { + colorBuffer.position(index * elementsSize); + gl.glColor4fv(colorBuffer); + } + + if (normalBuffer != null) { + normalBuffer.position(index * elementsSize); + gl.glNormal3fv(normalBuffer); + } + + if (textureCoordinatesBuffer != null) { + textureCoordinatesBuffer.position(index * elementsSize); + textureCoordinatesBuffer.get(fbuffer); + + gl.glTexCoord4f((float) (sfactor * fbuffer[0]), (float) (tfactor * fbuffer[1]), fbuffer[2], fbuffer[3]); + } + + vertexBuffer.position(index * elementsSize); + gl.glVertex4fv(vertexBuffer); + + } + } + } else { + vertexBuffer.rewind(); + + if (colorBuffer != null) { + colorBuffer.rewind(); + } + + if (normalBuffer != null) { + normalBuffer.rewind(); + } + + for (int i = 0; i < vertexBuffer.limit(); i += elementsSize) { + if (colorBuffer != null) { + colorBuffer.position(i); + gl.glColor4fv(colorBuffer); + } + + if (normalBuffer != null) { + normalBuffer.position(i); + gl.glNormal3fv(normalBuffer); + } + + if (textureCoordinatesBuffer != null) { + textureCoordinatesBuffer.position(i); + gl.glTexCoord4fv(textureCoordinatesBuffer); + } + + vertexBuffer.position(i); + gl.glVertex4fv(vertexBuffer); + } + } + gl.glEnd(); + } + + if (geometry.getPolygonOffsetMode()) { + gl.glDisable(GL2.GL_POLYGON_OFFSET_FILL); + } + + gl.glDisable(GL2.GL_TEXTURE_2D); + //disable lighting to draw lines + lm.setLightningEnable(false); + + // Draw edges if any. + if (geometry.getLineDrawingMode() != Geometry.LineDrawingMode.NONE) { + GLShortCuts.useLineAppearance(gl, appearance); + if (appearance.getLineColor() != null) { + gl.glBegin(getGlMode(geometry.getLineDrawingMode())); + if (geometry.getWireIndices() != null) { + IntBuffer edgesIndicesBuffer = geometry.getWireIndices().getData(); + edgesIndicesBuffer.rewind(); + while (edgesIndicesBuffer.remaining() != 0) { + int index = edgesIndicesBuffer.get(); + if ((index * elementsSize) < vertexBuffer.limit()) { + vertexBuffer.position(index * elementsSize); + gl.glVertex4fv(vertexBuffer); + } + } + } else { + for (int i = 0; i < vertexBuffer.limit(); i += elementsSize) { + vertexBuffer.position(i); + gl.glVertex4fv(vertexBuffer); + } + } + gl.glEnd(); + } else if (colorBuffer != null) { + gl.glBegin(getGlMode(geometry.getLineDrawingMode())); + if (geometry.getWireIndices() != null) { + IntBuffer edgesIndicesBuffer = geometry.getWireIndices().getData(); + edgesIndicesBuffer.rewind(); + while (edgesIndicesBuffer.remaining() != 0) { + int index = edgesIndicesBuffer.get(); + if ((index * elementsSize) < vertexBuffer.limit()) { + colorBuffer.position(index * elementsSize); + gl.glColor4fv(colorBuffer); + + vertexBuffer.position(index * elementsSize); + gl.glVertex4fv(vertexBuffer); + + } + } + } else { + for (int i = 0; i < vertexBuffer.limit(); i += elementsSize) { + colorBuffer.position(i); + vertexBuffer.position(i); + gl.glColor4fv(colorBuffer); + gl.glVertex4fv(vertexBuffer); + } + } + gl.glEnd(); + } + } + lm.setLightningEnable(lighting); + } + + + /** + * Return the gl drawing mode corresponding to the given {@link Geometry.FillDrawingMode}. + * @param drawingMode the given drawing mode.. + * @return the gl drawing mode corresponding to the given {@link Geometry.FillDrawingMode}. + */ + private int getGlMode(Geometry.FillDrawingMode drawingMode) { + switch (drawingMode) { + case TRIANGLE_FAN: + return GL2.GL_TRIANGLE_FAN; + case TRIANGLE_STRIP: + return GL2.GL_TRIANGLE_STRIP; + case TRIANGLES: + return GL2.GL_TRIANGLES; + default: + return GL2.GL_TRIANGLES; + } + } + + /** + * Return the gl drawing mode corresponding to the given {@link org.scilab.forge.scirenderer.shapes.geometry.Geometry.LineDrawingMode} + * @param drawingMode the given drawing mode. + * @return the gl drawing mode corresponding to the given {@link org.scilab.forge.scirenderer.shapes.geometry.Geometry.LineDrawingMode} + */ + private int getGlMode(Geometry.LineDrawingMode drawingMode) { + switch (drawingMode) { + case SEGMENTS: + return GL2.GL_LINES; + case SEGMENTS_LOOP: + return GL2.GL_LINE_LOOP; + case SEGMENTS_STRIP: + return GL2.GL_LINE_STRIP; + default: + return GL2.GL_LINES; + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/lightning/JoGLLight.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/lightning/JoGLLight.java new file mode 100755 index 000000000..2a99c4105 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/lightning/JoGLLight.java @@ -0,0 +1,188 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.lightning; + +import javax.media.opengl.GL2; + +import org.scilab.forge.scirenderer.implementation.jogl.utils.GLShortCuts; +import org.scilab.forge.scirenderer.lightning.Light; +import org.scilab.forge.scirenderer.shapes.appearance.Color; +import org.scilab.forge.scirenderer.tranformations.Vector3d; + +/** + * @author Pierre Lando + */ +public class JoGLLight implements Light { + private final int index; + private GL2 gl; + private boolean isEnable; + private Color ambientColor = new Color(0, 0, 0); + private Color diffuseColor = new Color(0, 0, 0); + private Color specularColor = new Color(0, 0, 0); + private Vector3d position = new Vector3d(0, 0, 0); + private Vector3d spotDirection = new Vector3d(0, 0, 1); + private Vector3d direction = new Vector3d(0, 0, 0); + private float spotAngle = 180; + private boolean isDirectional = false; + + /** + * Default constructor. + * @param gl the gl context. + * @param index the light index. + */ + public JoGLLight(GL2 gl, int index) { + this.gl = gl; + this.index = index; + } + + /** + * Reload this light. + * @param gl the gl context. + */ + public void reload(GL2 gl) { + this.gl = gl; + + GLShortCuts.setEnable(gl, GL2.GL_LIGHT0 + index, isEnable); + gl.glLightfv(GL2.GL_LIGHT0 + index, GL2.GL_AMBIENT, ambientColor.getRGBComponents(null), 0); + gl.glLightfv(GL2.GL_LIGHT0 + index, GL2.GL_DIFFUSE, diffuseColor.getRGBComponents(null), 0); + gl.glLightfv(GL2.GL_LIGHT0 + index, GL2.GL_SPECULAR, specularColor.getRGBComponents(null), 0); + if (isDirectional) { + float[] pos = position.getDataAsFloatArray(4); + pos[3] = 0.0f; + pos[0] = -pos[0]; + pos[1] = -pos[1]; + pos[2] = -pos[2]; + gl.glLightfv(GL2.GL_LIGHT0 + index, GL2.GL_POSITION, pos, 0); + } else { + float[] pos = position.getDataAsFloatArray(4); + pos[3] = 1.0f; + gl.glLightfv(GL2.GL_LIGHT0 + index, GL2.GL_POSITION, pos, 0); + } + gl.glLightfv(GL2.GL_LIGHT0 + index, GL2.GL_SPOT_DIRECTION, spotDirection.getDataAsFloatArray(4), 0); + } + + @Override + public boolean isEnable() { + return isEnable; + } + + @Override + public void setEnable(boolean enable) { + if (enable != isEnable) { + isEnable = enable; + GLShortCuts.setEnable(gl, GL2.GL_LIGHT0 + index, isEnable); + } + } + + @Override + public Color getAmbientColor() { + return ambientColor; + } + + @Override + public void setAmbientColor(Color color) { + if (color != null) { + ambientColor = color; + gl.glLightfv(GL2.GL_LIGHT0 + index, GL2.GL_AMBIENT, ambientColor.getRGBComponents(null), 0); + } + } + + @Override + public Color getDiffuseColor() { + return diffuseColor; + } + + @Override + public void setDiffuseColor(Color color) { + if (color != null) { + diffuseColor = color; + gl.glLightfv(GL2.GL_LIGHT0 + index, GL2.GL_DIFFUSE, diffuseColor.getRGBComponents(null), 0); + } + } + + @Override + public Color getSpecularColor() { + return specularColor; + } + + @Override + public void setSpecularColor(Color color) { + if (color != null) { + specularColor = color; + gl.glLightfv(GL2.GL_LIGHT0 + index, GL2.GL_SPECULAR, specularColor.getRGBComponents(null), 0); + } + } + + @Override + public Vector3d getPosition() { + return position; + } + + @Override + public void setPosition(Vector3d position) { + if (position != null) { + isDirectional = false; + this.position = position; + float[] pos = position.getDataAsFloatArray(4); + pos[3] = 1.0f; + gl.glLightfv(GL2.GL_LIGHT0 + index, GL2.GL_POSITION, pos, 0); + } + } + + public Vector3d getDirection() { + return direction; + } + + public void setDirection(Vector3d direction) { + if (direction != null) { + isDirectional = true; + this.direction = direction; + float[] dir = direction.getDataAsFloatArray(4); + dir[3] = 0.0f; + dir[0] = -dir[0]; + dir[1] = -dir[1]; + dir[2] = -dir[2]; + gl.glLightfv(GL2.GL_LIGHT0 + index, GL2.GL_POSITION, dir, 0); + } + } + + @Override + public Vector3d getSpotDirection() { + return spotDirection; + } + + @Override + public void setSpotDirection(Vector3d spotDirection) { + if (spotDirection != null) { + this.spotDirection = spotDirection; + gl.glLightfv(GL2.GL_LIGHT0 + index, GL2.GL_SPOT_DIRECTION, spotDirection.getNormalized().getDataAsFloatArray(4), 0); + } + } + + @Override + public float getSpotAngle() { + return spotAngle; + } + + @Override + public void setSpotAngle(float angle) { + if (angle != spotAngle) { + spotAngle = angle; + gl.glLightf(GL2.GL_LIGHT0 + index, GL2.GL_SPOT_CUTOFF, spotAngle); + } + } + + @Override + public int getIndex() { + return index; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/lightning/JoGLLightManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/lightning/JoGLLightManager.java new file mode 100755 index 000000000..4621f50fc --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/lightning/JoGLLightManager.java @@ -0,0 +1,114 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.lightning; + +import org.scilab.forge.scirenderer.implementation.jogl.JoGLDrawingTools; +import org.scilab.forge.scirenderer.implementation.jogl.utils.GLShortCuts; +import org.scilab.forge.scirenderer.lightning.Light; +import org.scilab.forge.scirenderer.lightning.LightManager; +import org.scilab.forge.scirenderer.shapes.appearance.Material; + +import javax.media.opengl.GL2; + +/** + * JoGL implementation of {@link LightManager} + * + * @author Pierre Lando + */ +public class JoGLLightManager implements LightManager { + + /** + * The drawing tools. + */ + private final JoGLDrawingTools drawingTools; + + /** + * The lights. + */ + private final JoGLLight[] lights; + + /** + * The current lightning status. + */ + private boolean isLightningEnable = DEFAULT_LIGHTNING_STATUS; + + /** + * Default constructor. + * @param drawingTools the drawing tools. + */ + public JoGLLightManager(JoGLDrawingTools drawingTools) { + this.drawingTools = drawingTools; + lights = new JoGLLight[getLightNumber()]; + } + + @Override + public int getLightNumber() { + return drawingTools.getGLCapacity().getLightNumber(); + } + + @Override + public Light getLight(int i) { + if (i < 0 || i >= getLightNumber()) { + return null; + } else { + if (lights[i] == null) { + lights[i] = new JoGLLight(drawingTools.getGl(), i); + } + return lights[i]; + } + } + + @Override + public void setLightningEnable(boolean isLightningEnable) { + this.isLightningEnable = isLightningEnable; + GLShortCuts.setEnable(drawingTools.getGl().getGL2(), GL2.GL_LIGHTING, isLightningEnable); + } + + @Override + public boolean isLightningEnable() { + return isLightningEnable; + } + + @Override + public void setMaterial(Material material) { + if (material != null) { + GLShortCuts.setEnable(drawingTools.getGl().getGL2(), GL2.GL_COLOR_MATERIAL, material.isColorMaterialEnable()); + float[] black = new float[] {0.0f, 0.0f, 0.0f, 1.0f}; + drawingTools.getGl().glLightModelfv(GL2.GL_LIGHT_MODEL_AMBIENT, black, 0); + drawingTools.getGl().glLightModeli(GL2.GL_LIGHT_MODEL_LOCAL_VIEWER, GL2.GL_FALSE); + drawingTools.getGl().glLightModeli(GL2.GL_LIGHT_MODEL_TWO_SIDE, GL2.GL_TRUE); + + if (material.isColorMaterialEnable()) { + drawingTools.getGl().glColorMaterial(GL2.GL_FRONT_AND_BACK, GL2.GL_AMBIENT_AND_DIFFUSE); + } else { + drawingTools.getGl().glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_AMBIENT, material.getAmbientColor().getComponents(null), 0); + drawingTools.getGl().glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_DIFFUSE, material.getDiffuseColor().getComponents(null), 0); + } + drawingTools.getGl().glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_SPECULAR, material.getSpecularColor().getComponents(null), 0); + drawingTools.getGl().glMaterialf(GL2.GL_FRONT_AND_BACK, GL2.GL_SHININESS, material.getShininess()); + float[] f = {0.0f, 0.0f, 0.0f, 0.0f}; + drawingTools.getGl().glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_EMISSION, f, 0); + } + } + /** + * Reload light. + */ + public void reload() { + GL2 gl = drawingTools.getGl().getGL2(); + GLShortCuts.setEnable(gl, GL2.GL_LIGHTING, isLightningEnable); + for (JoGLLight light : lights) { + if (light != null && light.isEnable()) { + light.reload(gl); + } + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/picking/GLPickingManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/picking/GLPickingManager.java new file mode 100755 index 000000000..799c93eff --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/picking/GLPickingManager.java @@ -0,0 +1,85 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.picking; + +import javax.media.opengl.GL; + +/** + * @author Pierre Lando + */ +public class GLPickingManager { + + /** + * The last used unique color. + */ + private UniqueColor uniqueColor; + + /** + * Default constructor. + */ + public GLPickingManager() { + } + + + /** + * This class make an unique color from an index. + */ + private class UniqueColor { + private final int redSize; + private final int greenSize; + private final int blueSize; + private final GL gl; + + /** + * Constructor. + * @param gl the used OpenGl context. + */ + public UniqueColor(GL gl) { + this.gl = gl; + int[] colorBits = new int[3]; + gl.glGetIntegerv(GL.GL_RED_BITS, colorBits, 0); + gl.glGetIntegerv(GL.GL_GREEN_BITS, colorBits, 1); + gl.glGetIntegerv(GL.GL_BLUE_BITS, colorBits, 2); + + redSize = 1 << colorBits[0]; + greenSize = 1 << colorBits[1]; + blueSize = 1 << colorBits[2]; + } + + /** + * Set the color by index. + * @param index given index. + */ + public void setColor(int index) { + int i = index; + float b = (i % blueSize) / (blueSize - 1f); + i = i >> blueSize; + float g = (i % greenSize) / (greenSize - 1f); + i = i >> greenSize; + float r = (i % redSize) / (redSize - 1f); + + //gl.glColor3f(r, g, b); + gl.glClearColor(r, g, b, 0); + } + + /** + * Get index from color. + * @param r red component of the color. + * @param g green component of the color. + * @param b blue component of the color. + * @return the index corresponding to the given color. + */ + public int getIndex(float r, float g, float b) { + return (int) (((r * (redSize - 1f) + g) * (greenSize - 1f) + b) * (blueSize - 1f)); + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/picking/JoGLPickingManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/picking/JoGLPickingManager.java new file mode 100755 index 000000000..aed85219c --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/picking/JoGLPickingManager.java @@ -0,0 +1,57 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.picking; + +import org.scilab.forge.scirenderer.implementation.jogl.JoGLCanvas; +import org.scilab.forge.scirenderer.implementation.jogl.JoGLDrawingTools; +import org.scilab.forge.scirenderer.picking.PickingManager; +import org.scilab.forge.scirenderer.picking.PickingTask; +import org.scilab.forge.scirenderer.picking.PickingTools; + +import java.util.Stack; + +/** + * + * JoGL implementation of {@link org.scilab.forge.scirenderer.picking.PickingManager} + * + * @author Pierre Lando + */ +public class JoGLPickingManager implements PickingManager { + + private final Stack<PickingTask> stack = new Stack<PickingTask>(); + private final JoGLCanvas canvas; + + /** + * Default constructor. + * @param canvas the canvas where picking is performed. + */ + public JoGLPickingManager(JoGLCanvas canvas) { + this.canvas = canvas; + } + + @Override + public void addPickingTask(PickingTask pickingTask) { + stack.push(pickingTask); + canvas.redraw(); + } + + /** + * Ask to consume picking task with the given drawing tools. + * @param drawingTools the given drawing tools. + */ + public void glConsume(JoGLDrawingTools drawingTools) { + PickingTools pickingTools = new JoGLPickingTools(drawingTools); + while (!stack.isEmpty()) { + stack.pop().perform(pickingTools); + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/picking/JoGLPickingTools.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/picking/JoGLPickingTools.java new file mode 100755 index 000000000..dbb3dd768 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/picking/JoGLPickingTools.java @@ -0,0 +1,65 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.picking; + +import org.scilab.forge.scirenderer.Canvas; +import org.scilab.forge.scirenderer.implementation.jogl.JoGLDrawingTools; +import org.scilab.forge.scirenderer.picking.PickingTools; +import org.scilab.forge.scirenderer.tranformations.Vector3d; + +import javax.media.opengl.GL2; +import java.awt.Point; +import java.nio.FloatBuffer; + +/** + * + * JoGL implementation of {@link PickingTools} + * + * @author Pierre Lando + */ +public class JoGLPickingTools implements PickingTools { + + private final JoGLDrawingTools drawingTools; + + /** + * Default constructor. + * @param drawingTools the drawing tools to use. + */ + public JoGLPickingTools(JoGLDrawingTools drawingTools) { + this.drawingTools = drawingTools; + } + + @Override + public Vector3d getUnderlyingPoint(Point windowPosition) { + GL2 gl = drawingTools.getGl().getGL2(); + + int x = windowPosition.x; + int y = drawingTools.getCanvas().getHeight() - windowPosition.y - 1; + FloatBuffer buffer = FloatBuffer.allocate(1); + + buffer.rewind(); + gl.glReadPixels(x, y, 1, 1, GL2.GL_DEPTH_COMPONENT, GL2.GL_FLOAT, buffer); + buffer.rewind(); + float vz = 2f * buffer.get() - 1; + + float vx = 2f * x / drawingTools.getCanvas().getWidth() - 1f; + float vy = 2f * y / drawingTools.getCanvas().getHeight() - 1f; + + return new Vector3d(vx, vy, vz); + } + + @Override + public Canvas getCanvas() { + return drawingTools.getCanvas(); + } + +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/renderer/JoGLRenderer.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/renderer/JoGLRenderer.java new file mode 100755 index 000000000..cc43ada64 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/renderer/JoGLRenderer.java @@ -0,0 +1,120 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.renderer; + +import org.scilab.forge.scirenderer.Drawer; +import org.scilab.forge.scirenderer.implementation.jogl.JoGLDrawingTools; +import org.scilab.forge.scirenderer.renderer.Renderer; + +import javax.media.opengl.GL2; + +/** + * @author Pierre Lando + */ +public class JoGLRenderer implements Renderer { + + /** + * The current drawer. + */ + private Drawer drawer; + + /** + * The OpenGl display list name. + */ + private Integer glName; + + /** + * Store the display list up to date status. + */ + private boolean upToDate; + + /** + * Default constructor. + * The constructor is package : only {@link JoGLRendererManager} can instantiate this object. + */ + JoGLRenderer() { + upToDate = false; + drawer = null; + glName = null; + } + + @Override + public void setDrawer(Drawer drawer) { + this.drawer = drawer; + upToDate = false; + } + + @Override + public Drawer getDrawer() { + return drawer; + } + + @Override + public void reload() { + glName = null; + upToDate = false; + } + + /** + * Perform a draw to the given canvas. + * @param drawingTools the given drawing tools. + */ + public void draw(JoGLDrawingTools drawingTools) { + if (drawingTools.getCanvas().getJoGLParameters().useDisplayList()) { + synchronize(drawingTools); + GL2 gl = drawingTools.getGl().getGL2(); + gl.glCallList(glName); + } else { + if (drawer != null) { + drawer.draw(drawingTools); + } + } + } + + /** + * Synchronize the display list. + * @param drawingTools drawing tools. + */ + private void synchronize(JoGLDrawingTools drawingTools) { + GL2 gl = drawingTools.getGl().getGL2(); + + // Check glName. + if ((glName == null) || !(gl.glIsList(glName))) { + glName = gl.glGenLists(1); + upToDate = false; + } + + // Check up to date. + if (!upToDate) { + gl.glNewList(glName, GL2.GL_COMPILE); + + if (drawer != null) { + drawer.draw(drawingTools); + } + + gl.glEndList(); + upToDate = true; + } + } + + /** + * Dispose resources. + * @param gl the current OpenGl context. + */ + void dispose(GL2 gl) { + if ((glName != null) && gl.glIsList(glName)) { + gl.glDeleteLists(glName, 1); + glName = null; + upToDate = false; + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/renderer/JoGLRendererManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/renderer/JoGLRendererManager.java new file mode 100755 index 000000000..ed10de48e --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/renderer/JoGLRendererManager.java @@ -0,0 +1,100 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.renderer; + +import org.scilab.forge.scirenderer.implementation.jogl.JoGLDrawingTools; +import org.scilab.forge.scirenderer.renderer.Renderer; +import org.scilab.forge.scirenderer.renderer.RendererManager; + +import javax.media.opengl.GL2; +import java.util.HashSet; +import java.util.Set; +import java.util.Stack; + +/** + * @author Pierre Lando + */ +public class JoGLRendererManager implements RendererManager { + + /** + * Set of current renderer. + */ + private final Set<JoGLRenderer> rendererSet = new HashSet<JoGLRenderer>(); + + /** + * Set of dead buffers. + */ + private final Stack<JoGLRenderer> deadRendererSet = new Stack<JoGLRenderer>(); + + /** + * Default constructor. + */ + public JoGLRendererManager() { + } + + @Override + public Renderer createRenderer() { + JoGLRenderer newRenderer = new JoGLRenderer(); + rendererSet.add(newRenderer); + return newRenderer; + } + + @Override + public void dispose(Renderer renderer) { + if ((renderer != null) && (renderer instanceof JoGLRenderer)) { + JoGLRenderer localRenderer = (JoGLRenderer) renderer; + rendererSet.remove(localRenderer); + deadRendererSet.push(localRenderer); + } + } + + public void dispose(JoGLDrawingTools drawingTools, Renderer renderer) { + if ((renderer != null) && (renderer instanceof JoGLRenderer)) { + JoGLRenderer localRenderer = (JoGLRenderer) renderer; + localRenderer.dispose(drawingTools.getGl().getGL2()); + rendererSet.remove(localRenderer); + deadRendererSet.push(localRenderer); + } + } + + /** + * Perform a draw with the given renderer to the given canvas.. + * @param drawingTools the given drawing tools. + * @param renderer the given renderer. + */ + public void draw(JoGLDrawingTools drawingTools, Renderer renderer) { + if ((renderer != null) && (renderer instanceof JoGLRenderer)) { + ((JoGLRenderer) renderer).draw(drawingTools); + } + } + + /** + * Ask all {@link JoGLRenderer} to reload. + * This is needed when the OpenGl context has been lost. + */ + public void glReload() { + for (JoGLRenderer renderer : rendererSet) { + renderer.reload(); + } + } + + /** + * Synchronize to OpenGl context. + * Mostly consist to dispose dead renderer resources. + * @param gl the current OpenGl context. + */ + public void glSynchronize(GL2 gl) { + while (!deadRendererSet.isEmpty()) { + deadRendererSet.pop().dispose(gl); + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/texture/JoGLTextureManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/texture/JoGLTextureManager.java new file mode 100755 index 000000000..71876e18e --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/texture/JoGLTextureManager.java @@ -0,0 +1,745 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.texture; + +import com.jogamp.opengl.util.texture.TextureIO; +import com.jogamp.opengl.util.texture.TextureCoords; + +import org.scilab.forge.scirenderer.SciRendererException; +import org.scilab.forge.scirenderer.buffers.ElementsBuffer; +import org.scilab.forge.scirenderer.implementation.jogl.JoGLCanvas; +import org.scilab.forge.scirenderer.implementation.jogl.JoGLDrawingTools; +import org.scilab.forge.scirenderer.texture.AbstractTexture; +import org.scilab.forge.scirenderer.texture.AnchorPosition; +import org.scilab.forge.scirenderer.texture.Texture; +import org.scilab.forge.scirenderer.texture.TextureManager; +import org.scilab.forge.scirenderer.texture.TextureDataProvider; +import org.scilab.forge.scirenderer.tranformations.Transformation; +import org.scilab.forge.scirenderer.tranformations.TransformationManager; +import org.scilab.forge.scirenderer.tranformations.Vector3d; + + +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.GL2ES1; +import javax.media.opengl.GL2GL3; +import javax.media.opengl.GLProfile; +import java.awt.Dimension; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Pierre Lando + */ +public class JoGLTextureManager implements TextureManager { + + private final Set<JoGLTexture> allTextures = Collections.synchronizedSet(new HashSet<JoGLTexture>()); + JoGLCanvas canvas; + + public JoGLTextureManager(JoGLCanvas canvas) { + this.canvas = canvas; + } + + /** + * Texture binder. + * Bind the given texture to the given OpenGl context. + * @param drawingTools drawing tools. + * @param texture given texture. + * @throws org.scilab.forge.scirenderer.SciRendererException if the texture can't be bind. + */ + public void bind(JoGLDrawingTools drawingTools, Texture texture) throws SciRendererException { + if ((texture instanceof JoGLTexture) && (allTextures.contains((JoGLTexture) texture))) { + ((JoGLTexture) texture).bind(drawingTools); + } + } + + /** + * Draw the given texture. + * @param drawingTools used drawing tools. + * @param texture the texture too drawn. + * @throws org.scilab.forge.scirenderer.SciRendererException if the texture is invalid. + */ + public void draw(JoGLDrawingTools drawingTools, Texture texture) throws SciRendererException { + if ((texture instanceof JoGLTexture) && (allTextures.contains((JoGLTexture) texture))) { + final JoGLTexture jt = (JoGLTexture) texture; + if (jt.preDraw(drawingTools)) { + jt.draw(drawingTools); + jt.postDraw(drawingTools); + } + } + } + + public void draw(JoGLDrawingTools drawingTools, Texture texture, AnchorPosition anchor, ElementsBuffer positions, int offset, int stride, double rotationAngle) throws SciRendererException { + if ((texture instanceof JoGLTexture) && (allTextures.contains((JoGLTexture) texture))) { + if (positions != null) { + FloatBuffer data = positions.getData(); + if (data != null) { + float[] position = {0, 0, 0, 1}; + final JoGLTexture jt = (JoGLTexture) texture; + if (jt.preDraw(drawingTools)) { + stride = stride < 1 ? 1 : stride; + offset = offset < 0 ? 0 : offset; + if (stride == 1) { + data.position(4 * offset); + while (data.remaining() >= 4) { + data.get(position); + jt.draw(drawingTools, anchor, new Vector3d(position), rotationAngle); + } + } else { + int mark = 4 * offset; + if (mark < data.capacity()) { + data.position(mark); + while (data.remaining() >= 4) { + data.get(position); + mark += stride * 4; + if (mark < data.capacity()) { + data.position(mark); + } else { + break; + } + jt.draw(drawingTools, anchor, new Vector3d(position), rotationAngle); + } + } + } + jt.postDraw(drawingTools); + } + } + } + } + } + + public void draw(JoGLDrawingTools drawingTools, Texture texture, AnchorPosition anchor, Vector3d position, double rotationAngle) throws SciRendererException { + if ((texture instanceof JoGLTexture) && (allTextures.contains((JoGLTexture) texture))) { + final JoGLTexture jt = (JoGLTexture) texture; + jt.preDraw(drawingTools); + jt.draw(drawingTools, anchor, position, rotationAngle); + jt.postDraw(drawingTools); + } + } + + /** Called when gl context is gone. */ + public void glReload() { + synchronized (allTextures) { + for (JoGLTexture texture : allTextures) { + texture.glReload(); + } + } + } + + @Override + public Texture createTexture() { + JoGLTexture texture = new JoGLTexture(); + allTextures.add(texture); + return texture; + } + + @Override + public void dispose(Collection<Texture> textures) { + for (Texture texture : textures) { + dispose(texture); + } + } + + @Override + public void dispose(Texture texture) { + if ((texture instanceof JoGLTexture) && (allTextures.contains((JoGLTexture) texture))) { + allTextures.remove((JoGLTexture) texture); + ((JoGLTexture) texture).dispose(); + } + } + + /** + * Inner class for {@link Texture} implementation. + */ + public class JoGLTexture extends AbstractTexture implements Texture { + private com.jogamp.opengl.util.texture.Texture[] textures; + private JoGLTextureData[] textureData; + private int wCuts; + private int hCuts; + private double sfactor = 1; + private double tfactor = 1; + private ByteBuffer buffer; + private TextureDataProvider.ImageType previousType; + private JoGLDrawingTools drawingTools; + + /** + * Default constructor. + */ + public JoGLTexture() { + } + + public final boolean isRowMajorOrder() { + return getDataProvider().isRowMajorOrder(); + } + + /** + * Bind the texture in the OpenGl context. + * @param drawingTools current drawing tools. + * @throws SciRendererException if the texture is invalid. + */ + public synchronized void bind(JoGLDrawingTools drawingTools) throws SciRendererException { + if (this.drawingTools == null) { + this.drawingTools = this.drawingTools; + } + + GL2 gl = drawingTools.getGl().getGL2(); + if (isValid()) { + checkData(drawingTools); + if (textures.length == 1) { + gl.glEnable(GL2.GL_TEXTURE_2D); + gl.glEnable(GL2.GL_BLEND); + gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA); + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_REPLACE); + + textures[0].setTexParameteri(gl, GL2.GL_TEXTURE_MAG_FILTER, getAsGLFilter(getMagnificationFilter(), false)); + textures[0].setTexParameteri(gl, GL2.GL_TEXTURE_MIN_FILTER, getAsGLFilter(getMinifyingFilter(), false)); + if (gl.isNPOTTextureAvailable()) { + textures[0].setTexParameteri(gl, GL2.GL_TEXTURE_WRAP_S, getAsGLWrappingMode(getSWrappingMode())); + textures[0].setTexParameteri(gl, GL2.GL_TEXTURE_WRAP_T, getAsGLWrappingMode(getTWrappingMode())); + } else { + textures[0].setTexParameteri(gl, GL2.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP_TO_EDGE); + textures[0].setTexParameteri(gl, GL2.GL_TEXTURE_WRAP_T, GL2.GL_CLAMP_TO_EDGE); + } + textures[0].bind(gl); + + /* sfactor and tfactor are useful to have the correct texture coordinates when the texture + was transformed into a power-of-two texture */ + Dimension textureSize = getDataProvider().getTextureSize(); + sfactor = (double) textureSize.width / (double) textures[0].getWidth(); + tfactor = (double) textureSize.height / (double) textures[0].getHeight(); + } else { + throw new SciRendererException("Texture is too large"); + } + } else { + throw new SciRendererException("Texture have no data."); + } + } + + /** + * Check if the texture data are up to date. + * @param drawingTools the drawing tools. + * @throws SciRendererException if the texture is too big. + */ + private synchronized void checkData(JoGLDrawingTools drawingTools) throws SciRendererException { + if (isValid() && !upToDate) { + GL2 gl = drawingTools.getGl().getGL2(); + + Dimension textureSize = getDataProvider().getTextureSize(); + int maxSize = drawingTools.getGLCapacity().getMaximumTextureSize(); + wCuts = (int) Math.ceil(textureSize.getWidth() / maxSize); + hCuts = (int) Math.ceil(textureSize.getHeight() / maxSize); + ByteBuffer newBuffer = getDataProvider().getData(); + TextureDataProvider.ImageType newType = getDataProvider().getImageType(); + + if (newBuffer != null && textureSize.width != 0 && textureSize.height != 0) { + boolean hasChanged = false; + if (newBuffer != buffer || newType != previousType) { + releaseTextures(gl); + if (newBuffer != null) { + textures = new com.jogamp.opengl.util.texture.Texture[wCuts * hCuts]; + textureData = new JoGLTextureData[wCuts * hCuts]; + } + buffer = newBuffer; + previousType = newType; + hasChanged = true; + } + + if (newBuffer != null || buffer != null) { + if (wCuts == 1 && hCuts == 1) { + if (hasChanged) { + if (isRowMajorOrder()) { + textureData[0] = new JoGLTextureData(gl.getGLProfile(), newType, buffer, textureSize.width, textureSize.height); + } else { + textureData[0] = new JoGLTextureData(gl.getGLProfile(), newType, buffer, textureSize.height, textureSize.width); + } + try { + textures[0] = TextureIO.newTexture(textureData[0]); + } catch (Exception e) { + System.err.println(e); + } + } else { + try { + textures[0].updateSubImage(gl, textureData[0], 0, 0, 0); + } catch (Exception e) { + System.err.println(e); + } + } + } else { + int k = 0; + for (int i = 0; i < wCuts; i++) { + for (int j = 0; j < hCuts; j++) { + if (hasChanged) { + final int x = i * maxSize; + final int y = j * maxSize; + final int width = getSubTextureSize((i == (wCuts - 1)), textureSize.width, maxSize); + final int height = getSubTextureSize((j == (hCuts - 1)), textureSize.height, maxSize); + if (isRowMajorOrder()) { + textureData[k] = new JoGLTextureData(gl.getGLProfile(), newType, buffer, width, height, x, y, textureSize.width, textureSize.height); + } else { + textureData[k] = new JoGLTextureData(gl.getGLProfile(), newType, buffer, height, width, y, x, textureSize.height, textureSize.width); + } + textures[k] = TextureIO.newTexture(textureData[k]); + } else { + textures[k].updateSubImage(gl, textureData[k], 0, 0, 0); + } + k++; + } + } + } + } + } + if (textures != null) { + upToDate = true; + } + } + } + + private void releaseTextures(GL2 gl) { + if (textures != null) { + for (com.jogamp.opengl.util.texture.Texture texture : textures) { + if (texture != null) { + texture.destroy(gl); + } + } + textures = null; + } + + if (textureData != null) { + for (JoGLTextureData td : textureData) { + if (td != null) { + td.destroy(); + } + } + textureData = null; + } + } + + public void dispose() { + if (drawingTools != null) { + releaseTextures(drawingTools.getGl().getGL2()); + } + } + + public boolean preDraw(JoGLDrawingTools drawingTools) throws SciRendererException { + checkData(drawingTools); + + if (textures == null) { + return false; + } + + final GL2 gl = drawingTools.getGl().getGL2(); + + gl.glMatrixMode(GL2.GL_TEXTURE); + gl.glPushMatrix(); + gl.glLoadIdentity(); + + gl.glMatrixMode(GL2.GL_PROJECTION); + gl.glPushMatrix(); + gl.glLoadIdentity(); + + gl.glEnable(GL2.GL_TEXTURE_2D); + gl.glEnable(GL2.GL_BLEND); + + // ONE => SRC_ALPHA (fix for bug 13673) + gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA); + + gl.glEnable(GL2.GL_ALPHA_TEST); + gl.glAlphaFunc(GL2.GL_GREATER, 0.0f); + + gl.glPushAttrib(GL2.GL_ALL_ATTRIB_BITS); + + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_REPLACE); + + for (int k = 0; k < wCuts * hCuts; k++) { + textures[k].enable(gl); + textures[k].setTexParameteri(gl, GL2.GL_TEXTURE_MAG_FILTER, getAsGLFilter(getMagnificationFilter(), false)); + textures[k].setTexParameteri(gl, GL2.GL_TEXTURE_MIN_FILTER, getAsGLFilter(getMinifyingFilter(), false)); + if (gl.isNPOTTextureAvailable()) { + textures[k].setTexParameteri(gl, GL2.GL_TEXTURE_WRAP_S, getAsGLWrappingMode(getSWrappingMode())); + textures[k].setTexParameteri(gl, GL2.GL_TEXTURE_WRAP_T, getAsGLWrappingMode(getTWrappingMode())); + } else { + textures[k].setTexParameteri(gl, GL2.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP_TO_EDGE); + textures[k].setTexParameteri(gl, GL2.GL_TEXTURE_WRAP_T, GL2.GL_CLAMP_TO_EDGE); + } + } + + return true; + } + + public void draw(JoGLDrawingTools drawingTools, AnchorPosition anchor, Vector3d position, double rotationAngle) throws SciRendererException { + TransformationManager transformationManager = drawingTools.getTransformationManager(); + Transformation canvasProjection = transformationManager.getCanvasProjection(); + boolean sceneCoordinate = drawingTools.getTransformationManager().isUsingSceneCoordinate(); + Dimension dimension = drawingTools.getCanvas().getDimension(); + Vector3d projected; + final GL2 gl = drawingTools.getGl().getGL2(); + + if (sceneCoordinate) { + projected = canvasProjection.project(position); + } else { + projected = position; + } + + gl.glMatrixMode(GL2.GL_MODELVIEW); + gl.glPushMatrix(); + gl.glLoadIdentity(); + gl.glOrtho(0, dimension.width, 0, dimension.height, 1, -1); + + if (rotationAngle == 0) { + gl.glTranslated(Math.round(projected.getX() + getAnchorDeltaX(anchor)), Math.round(projected.getY() + getAnchorDeltaY(anchor)), projected.getZ()); + } else { + gl.glTranslated(Math.round(projected.getX()), Math.round(projected.getY()), projected.getZ()); + gl.glRotated(rotationAngle, 0, 0, 1); + gl.glTranslated(Math.round(getAnchorDeltaX(anchor)), Math.round(getAnchorDeltaY(anchor)), 0); + } + + draw(drawingTools); + + gl.glMatrixMode(GL2.GL_MODELVIEW); + gl.glPopMatrix(); + } + + public void postDraw(JoGLDrawingTools drawingTools) { + final GL2 gl = drawingTools.getGl().getGL2(); + + for (int k = 0; k < wCuts * hCuts; k++) { + textures[k].disable(gl); + } + + gl.glPopAttrib(); + gl.glDisable(GL2.GL_ALPHA_TEST); + gl.glDisable(GL2.GL_TEXTURE_2D); + gl.glDisable(GL2.GL_BLEND); + + gl.glMatrixMode(GL2.GL_PROJECTION); + gl.glPopMatrix(); + + gl.glMatrixMode(GL2.GL_TEXTURE); + gl.glPopMatrix(); + } + + /** + * Draw the texture in XY plane. + * @param drawingTools the drawing tools. + * @throws SciRendererException if the texture is invalid. + */ + public void draw(JoGLDrawingTools drawingTools) throws SciRendererException { + final int maxSize = drawingTools.getGLCapacity().getMaximumTextureSize(); + final Dimension textureSize = getDataProvider().getTextureSize(); + final GL2 gl = drawingTools.getGl().getGL2(); + + if (textureSize != null && textures != null) { + if (wCuts == 1 && hCuts == 1) { + final TextureCoords coords; + if (isRowMajorOrder()) { + coords = textures[0].getSubImageTexCoords(0, 0, textureSize.width, textureSize.height); + } else { + coords = textures[0].getSubImageTexCoords(0, 0, textureSize.height, textureSize.width); + } + textures[0].bind(gl); + + gl.glBegin(GL2.GL_QUADS); + gl.glTexCoord2f(coords.left(), coords.bottom()); + if (isRowMajorOrder()) { + gl.glVertex2f(0, 0); + } else { + gl.glVertex2f(textureSize.width, textureSize.height); + } + gl.glTexCoord2f(coords.right(), coords.bottom()); + gl.glVertex2f(textureSize.width, 0); + gl.glTexCoord2f(coords.right(), coords.top()); + if (isRowMajorOrder()) { + gl.glVertex2f(textureSize.width, textureSize.height); + } else { + gl.glVertex2f(0, 0); + } + gl.glTexCoord2f(coords.left(), coords.top()); + gl.glVertex2f(0, textureSize.height); + gl.glEnd(); + } else { + int k = 0; + for (int i = 0; i < wCuts; i++) { + for (int j = 0; j < hCuts; j++) { + final int x = i * maxSize; + final int y = j * maxSize; + final int width = getSubTextureSize((i == (wCuts - 1)), textureSize.width, maxSize); + final int height = getSubTextureSize((j == (hCuts - 1)), textureSize.height, maxSize); + + // if the texture has been transformed in a power-of-two texture we need to have the correct coords. + final TextureCoords coords; + if (isRowMajorOrder()) { + coords = textures[k].getSubImageTexCoords(0, 0, width, height); + } else { + coords = textures[k].getSubImageTexCoords(0, 0, height, width); + } + textures[k].bind(gl); + + gl.glBegin(GL2.GL_QUADS); + gl.glTexCoord2f(coords.left(), coords.top()); + gl.glVertex2f(x, textureSize.height - y); + gl.glTexCoord2f(coords.right(), coords.top()); + if (isRowMajorOrder()) { + gl.glVertex2f(x + width, textureSize.height - y); + } else { + gl.glVertex2f(x, textureSize.height - y - height); + } + gl.glTexCoord2f(coords.right(), coords.bottom()); + gl.glVertex2f(x + width, textureSize.height - y - height); + gl.glTexCoord2f(coords.left(), coords.bottom()); + if (isRowMajorOrder()) { + gl.glVertex2f(x, textureSize.height - y - height); + } else { + gl.glVertex2f(x + width, textureSize.height - y); + } + gl.glEnd(); + + k++; + } + } + } + } + } + + @Override + public double getSScaleFactor() { + return sfactor; + } + + @Override + public double getTScaleFactor() { + return tfactor; + } + + /** + * Compute the sub texture size. + * @param lastPart true if this is the last part. + * @param textureSize the global texture size. + * @param maxSize the maximum sub-texture size. + * @return the sub texture size. + */ + private int getSubTextureSize(boolean lastPart, int textureSize, int maxSize) { + if (lastPart) { + int lastSize = textureSize % maxSize; + if (lastSize == 0) { + return maxSize; + } else { + return lastSize; + } + } else { + return maxSize; + } + } + + private int getAsGLWrappingMode(Texture.Wrap wrappingMode) { + switch (wrappingMode) { + case CLAMP: + return GL2.GL_CLAMP_TO_EDGE; + case REPEAT: + return GL2.GL_REPEAT; + default: + return GL2.GL_REPEAT; + } + } + + private int getAsGLFilter(Texture.Filter filter, boolean mipmap) { + int returnedValue; + if (mipmap) { + switch (filter) { + case LINEAR: + returnedValue = GL2.GL_LINEAR_MIPMAP_LINEAR; + break; + case NEAREST: + returnedValue = GL2.GL_NEAREST_MIPMAP_NEAREST; + break; + default: + returnedValue = GL2.GL_NEAREST; + break; + } + } else { + switch (filter) { + case LINEAR: + returnedValue = GL2.GL_LINEAR; + break; + case NEAREST: + returnedValue = GL2.GL_NEAREST; + break; + default: + returnedValue = GL2.GL_NEAREST; + break; + } + } + return returnedValue; + } + + /** Called when gl context is gone. */ + public void glReload() { + buffer = null; + textures = null; + upToDate = false; + } + + /** + * Return the deltaX to apply to the sprite in regards to the given anchor. + * @param anchor the given anchor. + * @return the deltaX to apply to the sprite in regards to the given anchor. + */ + protected double getAnchorDeltaX(AnchorPosition anchor) { + int spriteWidth = getDataProvider().getTextureSize().width; + switch (anchor) { + case LEFT: + case LOWER_LEFT: + case UPPER_LEFT: + return 0; + case UP: + case CENTER: + case DOWN: + return -spriteWidth / 2f; + case RIGHT: + case LOWER_RIGHT: + case UPPER_RIGHT: + return -spriteWidth; + default: + return 0; + } + } + + /** + * Return the deltaY to apply to the sprite in regards to the given anchor. + * @param anchor the given anchor. + * @return the deltaY to apply to the sprite in regards to the given anchor. + */ + protected double getAnchorDeltaY(AnchorPosition anchor) { + int spriteHeight = getDataProvider().getTextureSize().height; + switch (anchor) { + case UPPER_LEFT: + case UP: + case UPPER_RIGHT: + return -spriteHeight; + case LEFT: + case CENTER: + case RIGHT: + return -spriteHeight / 2f; + case LOWER_LEFT: + case DOWN: + case LOWER_RIGHT: + return 0; + default: + return 0; + } + } + } + + private static class JoGLTextureData extends com.jogamp.opengl.util.texture.TextureData { + + private ByteBuffer data; + private int shift; + + public JoGLTextureData(GLProfile glp, int[] infos, ByteBuffer data, int width, int height, int x, int y, int totalWidth, int totalHeight) { + super(glp, infos[0], width, height, 0, infos[1], infos[2], false, false, true, null, null); + this.data = data; + this.shift = ((x == 0 && y == 0) || data.capacity() == 0) ? 0 : (data.capacity() / (totalWidth * totalHeight) * (x + y * totalWidth)); + this.rowLength = totalWidth; + this.alignment = infos[3]; + } + + public JoGLTextureData(GLProfile glp, TextureDataProvider.ImageType type, ByteBuffer data, int width, int height) { + this(glp, getInfos(type), data, width, height, 0, 0, width, height); + } + + public JoGLTextureData(GLProfile glp, TextureDataProvider.ImageType type, ByteBuffer data, int width, int height, int x, int y, int totalWidth, int totalHeight) { + this(glp, getInfos(type), data, width, height, x, y, totalWidth, totalHeight); + } + + public ByteBuffer getBuffer() { + if (shift == 0) { + return data; + } else { + data.position(shift); + ByteBuffer b = data.slice(); + data.rewind(); + + return b; + } + } + + public void setData(ByteBuffer data) { + this.data = data; + } + + public void destroy() { + super.destroy(); + this.data = null; + } + + public static int[] getInfos(TextureDataProvider.ImageType type) { + switch (type) { + case RGB: + //internalFormat, pixelFormat, pixelType, alignment + return new int[] {GL2.GL_RGB, GL.GL_RGB, GL2.GL_UNSIGNED_BYTE, 1}; + case RGB_RGBA: + return new int[] {GL2.GL_RGB, GL.GL_RGBA, GL2.GL_UNSIGNED_INT_8_8_8_8, 4}; + case BGR: + return new int[] {GL2GL3.GL_RGB, GL2GL3.GL_BGR, GL.GL_UNSIGNED_BYTE, 1}; + case GRAY: + return new int[] {GL.GL_LUMINANCE, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE, 1}; + case GRAY_16: + return new int[] {GL2.GL_LUMINANCE16, GL2.GL_LUMINANCE, GL2.GL_UNSIGNED_SHORT, 2}; + case RGBA: + return new int[] {GL2.GL_RGBA, GL.GL_RGBA, GL2.GL_UNSIGNED_INT_8_8_8_8, 4}; + case RGBA_REV: + return new int[] {GL2.GL_RGBA, GL2.GL_RGBA, GL2.GL_UNSIGNED_INT_8_8_8_8_REV, 4}; + case ABGR: + return new int[] {GL2.GL_RGBA, GL2.GL_ABGR_EXT, GL.GL_UNSIGNED_BYTE, 1}; + case RGB_332: + return new int[] {GL2.GL_R3_G3_B2, GL2.GL_RGB, GL2.GL_UNSIGNED_BYTE_3_3_2, 1}; + case RED: + return new int[] {GL2.GL_RED, GL2.GL_RED, GL2.GL_UNSIGNED_BYTE, 1}; + case GREEN: + return new int[] {GL2.GL_RGB8, GL2.GL_GREEN, GL2.GL_UNSIGNED_BYTE, 1}; + case BLUE: + return new int[] {GL2.GL_RGB8, GL2.GL_BLUE, GL2.GL_UNSIGNED_BYTE, 1}; + case INTENSITY: + return new int[] {GL2.GL_INTENSITY, GL2.GL_LUMINANCE, GL2.GL_UNSIGNED_BYTE, 1}; + case RGBA_4444: + return new int[] {GL2.GL_RGBA4, GL2.GL_RGBA, GL2.GL_UNSIGNED_SHORT_4_4_4_4, 2}; + case RGBA_5551: + return new int[] {GL2.GL_RGB5_A1, GL2.GL_RGBA, GL2.GL_UNSIGNED_SHORT_5_5_5_1, 2}; + case RGB_FLOAT: + return new int[] {GL2ES1.GL_RGB16F, GL2.GL_RGB, GL2.GL_FLOAT, 4}; + case RGBA_FLOAT: + return new int[] {GL2.GL_RGBA16F, GL2.GL_RGBA, GL2.GL_FLOAT, 4}; + case GRAY_FLOAT: + return new int[] {GL2.GL_LUMINANCE16F, GL2.GL_LUMINANCE, GL2.GL_FLOAT, 4}; + case RED_16: + return new int[] {GL2.GL_RGB16, GL2.GL_RED, GL2.GL_UNSIGNED_SHORT, 2}; + case GREEN_16: + return new int[] {GL2.GL_RGB16, GL2.GL_GREEN, GL2.GL_UNSIGNED_SHORT, 2}; + case BLUE_16: + return new int[] {GL2.GL_RGB16, GL2.GL_BLUE, GL2.GL_UNSIGNED_SHORT, 2}; + case RED_FLOAT: + return new int[] {GL2ES1.GL_RGB16F, GL2.GL_RED, GL2.GL_FLOAT, 4}; + case GREEN_FLOAT: + return new int[] {GL2ES1.GL_RGB16F, GL2.GL_GREEN, GL2.GL_FLOAT, 4}; + case BLUE_FLOAT: + return new int[] {GL2ES1.GL_RGB16F, GL2.GL_BLUE, GL2.GL_FLOAT, 4}; + case RGBA_BYTE: + return new int[] {GL2.GL_RGBA, GL2.GL_RGBA, GL2.GL_UNSIGNED_BYTE, 1}; + } + + return new int[] {GL2.GL_RGB, GL.GL_RGB, GL2.GL_UNSIGNED_BYTE, 1}; + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/utils/G2DShortCuts.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/utils/G2DShortCuts.java new file mode 100755 index 000000000..b9baa6d5c --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/utils/G2DShortCuts.java @@ -0,0 +1,57 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.utils; + +import org.scilab.forge.scirenderer.shapes.appearance.Appearance; +import org.scilab.forge.scirenderer.shapes.appearance.Color; + +import java.awt.BasicStroke; +import java.awt.Graphics2D; +import java.awt.Stroke; + +/** + * + * Utility class for common {@link java.awt.Graphics2D} calls. + * + * @author Pierre Lando + */ +public final class G2DShortCuts { + + /** + * Private constructor: this is an utility class. + */ + private G2DShortCuts() { + } + + + + /** + * Use the given color for drawing. + * @param g2d the {@link Graphics2D} where the color will be used. + * @param c the given color. + */ + public static void useColor(Graphics2D g2d, Color c) { + java.awt.Color color = new java.awt.Color(c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha()); + g2d.setColor(color); + } + + /** + * Use the given appearance for drawing lines. + * @param g2d the {@link Graphics2D} where the appearance will be used. + * @param appearance the given appearance. + */ + public static void useLineAppearance(Graphics2D g2d, Appearance appearance) { + // TODO : add line pattern. + Stroke stroke = new BasicStroke(appearance.getLineWidth()); + g2d.setStroke(stroke); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/utils/GLShortCuts.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/utils/GLShortCuts.java new file mode 100755 index 000000000..7928a7e57 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/implementation/jogl/utils/GLShortCuts.java @@ -0,0 +1,110 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.implementation.jogl.utils; + +import org.scilab.forge.scirenderer.shapes.appearance.Appearance; +import org.scilab.forge.scirenderer.shapes.appearance.Color; + +import javax.media.opengl.GL2; + +/** + * + * Utility class for common OpenGl calls. + * + * @author Pierre Lando + */ +public final class GLShortCuts { + + private static final short FULL_LINE_PATTERN = (short) 0xFFFF; + + + /** + * Private constructor. + * This is an utility class. + */ + private GLShortCuts() { + } + + + /** + * Set the OpenGl context line appearance from the given appearance. + * If appearance is null, default value are used.. + * @param gl the OpenGl context. + * @param appearance the appearance to use. + */ + public static void useLineAppearance(GL2 gl, Appearance appearance) { + Appearance usedAppearance; + if (appearance == null) { + usedAppearance = new Appearance(); + } else { + usedAppearance = appearance; + } + + useColor(gl, usedAppearance.getLineColor()); + gl.glLineWidth(getClampedLineWidth(gl, usedAppearance.getLineWidth())); + short pattern = usedAppearance.getLinePattern(); + if (pattern != FULL_LINE_PATTERN) { + gl.glEnable(GL2.GL_LINE_STIPPLE); + gl.glLineStipple((int) usedAppearance.getLineWidth(), pattern); + } else { + gl.glDisable(GL2.GL_LINE_STIPPLE); + } + } + + /** + * Return given lineWidth clamped to lineWidth range. + * @param gl the current gl context. + * @param lineWidth given line width. + * @return given lineWidth clamped to lineWidth range. + */ + private static float getClampedLineWidth(GL2 gl, float lineWidth) { + float[] range = new float[] {1f, 1f}; + if (gl.glIsEnabled(GL2.GL_LINE_SMOOTH)) { + gl.glGetFloatv(GL2.GL_SMOOTH_LINE_WIDTH_RANGE, range, 0); + } else { + gl.glGetFloatv(GL2.GL_ALIASED_LINE_WIDTH_RANGE, range, 0); + } + + if (lineWidth < range[0]) { + return range[0]; + } else if (lineWidth > range[1]) { + return range[1]; + } else { + return lineWidth; + } + } + + /** + * Set the OpenGl context color to the given color. + * @param gl the OpenGl context. + * @param color the color to use. + */ + public static void useColor(GL2 gl, Color color) { + if (color != null) { + gl.glColor3f(color.getRedAsFloat(), color.getGreenAsFloat(), color.getBlueAsFloat()); + } + } + + /** + * Enable or disable GL option. + * @param gl the current gl. + * @param option the option to change. + * @param status the new option status. + */ + public static void setEnable(GL2 gl, int option, boolean status) { + if (status) { + gl.glEnable(option); + } else { + gl.glDisable(option); + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/lightning/Light.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/lightning/Light.java new file mode 100755 index 000000000..d42c7a2db --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/lightning/Light.java @@ -0,0 +1,126 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.lightning; + +import org.scilab.forge.scirenderer.shapes.appearance.Color; +import org.scilab.forge.scirenderer.tranformations.Vector3d; + +/** + * The light interface. + * + * @author Pierre Lando + */ +public interface Light { + + /** + * Return the status of this light. + * @return the status of this light. + */ + boolean isEnable(); + + /** + * Set the status of this light. + * @param enable the new status of this light. + */ + void setEnable(boolean enable); + + /** + * Return the ambient color of this light; + * @return the ambient color of this light; + */ + Color getAmbientColor(); + + /** + * Set the ambient color of this light. + * @param color the new ambient color of this light. + */ + void setAmbientColor(Color color); + + /** + * Return the diffuse color of this light; + * @return the diffuse color of this light; + */ + Color getDiffuseColor(); + + /** + * Set the diffuse color of this light. + * @param color the new diffuse color of this light. + */ + void setDiffuseColor(Color color); + + /** + * Return the specular color of this light; + * @return the specular color of this light; + */ + Color getSpecularColor(); + + /** + * Set the specular color of this light. + * @param color the new specular color of this light. + */ + void setSpecularColor(Color color); + + /** + * Return the light position. + * @return the light position. + */ + Vector3d getPosition(); + + /** + * Set the light position. + * @param position the new position. + */ + void setPosition(Vector3d position); + + /** + * Get the light direction. + * @return the light direction. + */ + Vector3d getDirection(); + + /** + * Set the light direction. + * Used only for directional light. + * @param position the new position. + */ + void setDirection(Vector3d direction); + + /** + * Return the spot direction. + * @return the spot direction. + */ + Vector3d getSpotDirection(); + + /** + * Set the spot direction. + * @param spotDirection the new spot direction. + */ + void setSpotDirection(Vector3d spotDirection); + + /** + * Return the spot angle. + * @return the spot angle. + */ + float getSpotAngle(); + + /** + * Set the spot angle. + * @param angle the new spot angle. + */ + void setSpotAngle(float angle); + + /** + * Return the light index. + * @return the light index. + */ + int getIndex(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/lightning/LightManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/lightning/LightManager.java new file mode 100755 index 000000000..0dd294c2b --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/lightning/LightManager.java @@ -0,0 +1,61 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.lightning; + +import org.scilab.forge.scirenderer.shapes.appearance.Material; + +/** + * Light manager interface. + * + * @author Pierre Lando + */ +public interface LightManager { + + /** + * The default lightning status. + */ + boolean DEFAULT_LIGHTNING_STATUS = false; + + /** + * Return the number of available light. + * @return the number of available light. + */ + int getLightNumber(); + + /** + * Return the i-th light. + * {@code null} is returned if i is not a valid index. + * @param i the given index. + * @return the i-th light. + */ + Light getLight(int i); + + /** + * Set the lightning status. + * Lighting is initially disabled. + * When it is enabled, light sources that are enabled contribute to the lighting calculation. + * @param isLightningEnable the new lightning status. + */ + void setLightningEnable(boolean isLightningEnable); + + /** + * Return the lightning status. + * @return the lightning status. + */ + boolean isLightningEnable(); + + /** + * Set the material properties used for lighting. + * @param material the material. + */ + void setMaterial(Material material); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/picking/PickingManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/picking/PickingManager.java new file mode 100755 index 000000000..2efb6e29b --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/picking/PickingManager.java @@ -0,0 +1,27 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.picking; + +/** + * + * Interface for a picking manager. + * + * @author Pierre Lando + */ +public interface PickingManager { + + /** + * Add a picking task. + * @param pickingTask the new picking task. + */ + void addPickingTask(PickingTask pickingTask); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/picking/PickingTask.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/picking/PickingTask.java new file mode 100755 index 000000000..7dacdfa69 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/picking/PickingTask.java @@ -0,0 +1,26 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.picking; + +/** + * Interface for a picking task. + * + * @author Pierre Lando + */ +public interface PickingTask { + + /** + * Call to perform the task. + * @param pickingTools the picking tools to use. + */ + void perform(PickingTools pickingTools); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/picking/PickingTools.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/picking/PickingTools.java new file mode 100755 index 000000000..ee5892042 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/picking/PickingTools.java @@ -0,0 +1,38 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.picking; + +import java.awt.Point; + +import org.scilab.forge.scirenderer.Canvas; +import org.scilab.forge.scirenderer.tranformations.Vector3d; + +/** + * Interface for picking tools. + * + * @author Pierre Lando + */ +public interface PickingTools { + + /** + * Return the scene coordinate of the pixel under the given windows position. + * @param windowPosition the given window position. + * @return the scene coordinate of the pixel under the given windows position. + */ + Vector3d getUnderlyingPoint(Point windowPosition); + + /** + * Return the canvas. + * @return the canvas. + */ + Canvas getCanvas(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/renderer/Renderer.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/renderer/Renderer.java new file mode 100755 index 000000000..05a9dca33 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/renderer/Renderer.java @@ -0,0 +1,43 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.renderer; + +import org.scilab.forge.scirenderer.Drawer; + +/** + * + * Interface for a renderer. + * + * A renderer is used to improve drawing performance. + * If you have an uge static object to draw at each frame, encapsulate his drawer in a renderer improve drawing performance. + * + * @author Pierre Lando + */ +public interface Renderer { + + /** + * Set the drawer. + * @param drawer the new drawer. + */ + void setDrawer(Drawer drawer); + + /** + * Return the drawer. + * @return the drawer. + */ + Drawer getDrawer(); + + /** + * Ask for reload. + */ + void reload(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/renderer/RendererManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/renderer/RendererManager.java new file mode 100755 index 000000000..fde8ba995 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/renderer/RendererManager.java @@ -0,0 +1,33 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.renderer; + +/** + * + * Interface for renderer manager. + * + * @author Pierre Lando + */ +public interface RendererManager { + + /** + * Create a new renderer. + * @return a new renderer. + */ + Renderer createRenderer(); + + /** + * Release all resources used by the given renderer. + * @param renderer the given renderer. + */ + void dispose(Renderer renderer); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/DefaultRulerModel.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/DefaultRulerModel.java new file mode 100755 index 000000000..18b94c19f --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/DefaultRulerModel.java @@ -0,0 +1,436 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * Copyright (C) 2013 - Scilab Enterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.ruler; + +import org.scilab.forge.scirenderer.ruler.graduations.Graduations; +import org.scilab.forge.scirenderer.ruler.graduations.LinearGraduations; +import org.scilab.forge.scirenderer.ruler.graduations.LogarithmicGraduations; +import org.scilab.forge.scirenderer.shapes.appearance.Color; +import org.scilab.forge.scirenderer.tranformations.Vector3d; + +/** + * Default ruler model. + * + * @author Pierre Lando + */ +public final class DefaultRulerModel implements RulerModel { + + /** + * Value associated with the first point. + */ + private double firstValue = DEFAULT_FIRST_VALUE; + + /** + * Value associated with the second point. + */ + private double secondValue = DEFAULT_SECOND_VALUE; + + /** + * First point position in world coordinate. + */ + private Vector3d firstPoint = DEFAULT_FIRST_POINT; + + /** + * Second point position in world coordinate. + */ + private Vector3d secondPoint = DEFAULT_SECOND_POINT; + + /** + * Ticks direction, in world coordinate. + */ + private Vector3d ticksDirection = DEFAULT_TICKS_DIRECTION; + + /** + * Ticks length, in pixel. + */ + private int ticksLength = DEFAULT_TICK_LENGTH; + + /** + * Sub ticks length, in pixel. + */ + private int subTicksLength = DEFAULT_SUB_TICK_LENGTH; + + /** + * Minimal sub-ticks distance. + */ + private double minimalSubTicksDistance = DEFAULT_MINIMAL_SUB_TICKS_DISTANCE; + + /** + * Sprite distance to segment, in pixel. + + */ + private int spriteDistance = DEFAULT_SPRITE_DISTANCE; + + /** + * The graduations. + */ + private Graduations graduations; + + /** + * Current margin. + * The margin is the minimal allowed distance between to sprite of the same ruler. + */ + private double margin = DEFAULT_MARGIN; + + /** + * Current line visibility. + */ + private boolean lineVisible = DEFAULT_LINE_VISIBLE; + + /** + * Auto-ticks status. + */ + private boolean isAutoTicks = DEFAULT_AUTO_TICKS_STATUS; + + /** + * Logarithmic status. + */ + private boolean isLogarithmic = DEFAULT_LOGARITHMIC_STATUS; + + /** + * Current color. + */ + private Color color = DEFAULT_COLOR; + + /** + * Used graduations when {@link this#isAutoTicks} is false. + */ + private Graduations userGraduation; + + /** + * Line width + */ + private double lineWidth = DEFAULT_LINE_WIDTH; + + private int subticksNumber = -1; + private String format = ""; + private double scale; + private double translate; + + /** + * Default constructor. + */ + public DefaultRulerModel() { + } + + @Override + public double getFirstValue() { + return firstValue; + } + + /** + * First value setter. + * @param firstValue the new first value. + */ + public void setFirstValue(double firstValue) { + graduations = null; + this.firstValue = (Double.isInfinite(firstValue) ? Double.MAX_VALUE : firstValue); + } + + @Override + public double getSecondValue() { + return secondValue; + } + + /** + * Second value setter. + * @param secondValue the new second value. + */ + public void setSecondValue(double secondValue) { + graduations = null; + this.secondValue = (Double.isInfinite(secondValue) ? Double.MAX_VALUE : secondValue); + } + + /** + * Set the first and second value in one call. + * @param firstValue the first value. + * @param secondValue the second value. + */ + public void setValues(double firstValue, double secondValue) { + setFirstValue(firstValue); + setSecondValue(secondValue); + } + + @Override + public Vector3d getFirstPoint() { + return firstPoint; + } + + /** + * First point setter. + * @param firstPoint the new first point. + */ + public void setFirstPoint(Vector3d firstPoint) { + this.firstPoint = firstPoint; + } + + @Override + public Vector3d getSecondPoint() { + return secondPoint; + } + + /** + * Second point setter. + * @param secondPoint the new second point. + */ + public void setSecondPoint(Vector3d secondPoint) { + this.secondPoint = secondPoint; + } + + /** + * Point setter. + * @param firstPoint the new first point. + * @param secondPoint the new second point. + */ + public void setPoints(Vector3d firstPoint, Vector3d secondPoint) { + this.firstPoint = firstPoint; + this.secondPoint = secondPoint; + } + + @Override + public Vector3d getTicksDirection() { + return ticksDirection; + } + + /** + * Ticks direction setter. + * @param ticksDirection the new ticks direction. + */ + public void setTicksDirection(Vector3d ticksDirection) { + this.ticksDirection = ticksDirection; + } + + @Override + public int getTicksLength() { + return ticksLength; + } + + /** + * Ticks length setter. + * @param ticksLength the new tick length in pixels. + */ + public void setTicksLength(int ticksLength) { + this.ticksLength = ticksLength; + } + + @Override + public int getSubTicksLength() { + return subTicksLength; + } + + /** + * Sub-ticks length setter. + * @param subTicksLength the new sub-tick length in pixels. + */ + public void setSubTicksLength(int subTicksLength) { + this.subTicksLength = subTicksLength; + } + + @Override + public Graduations getGraduations() { + if (isAutoTicks) { + if (graduations == null) { + if (isLogarithmic) { + graduations = LogarithmicGraduations.create(getFirstValue(), getSecondValue()); + } else { + graduations = LinearGraduations.create(getFirstValue(), getSecondValue()); + } + } + return graduations; + } else { + return userGraduation; + } + } + + /** + * User graduation setter. + * @param graduations the new user-defined graduations. + */ + public void setUserGraduation(Graduations graduations) { + this.userGraduation = graduations; + } + + @Override + public Vector3d getPosition(double value) { + if ((firstPoint != null) && (secondPoint != null)) { + if (isLogarithmic()) { + double valueLog = Math.log10(value); + double firstValueLog = Math.log10(firstValue); + double secondValueLog = Math.log10(secondValue); + double s = (secondValueLog - firstValueLog); + double k1 = (valueLog - firstValueLog) / s; + double k2 = (secondValueLog - valueLog) / s; + return firstPoint.times(k2).plus(secondPoint.times(k1)); + } else { + double s = (secondValue - firstValue); + return firstPoint.times((secondValue - value) / s).plus(secondPoint.times((value - firstValue) / s)); + } + } else { + return null; + } + } + + @Override + public double getMargin() { + return margin; + } + + /** + * Margin setter. + * The margin is minimal distance accepted between ticks labels. + * @param margin the new margin value. + */ + public void setMargin(double margin) { + this.margin = margin; + } + + @Override + public boolean isLineVisible() { + return lineVisible; + } + + /** + * Line visibility setter. + * @param lineVisible the new line visibility status. + */ + public void setLineVisible(boolean lineVisible) { + this.lineVisible = lineVisible; + } + + @Override + public boolean isAutoTicks() { + return isAutoTicks; + } + + /** + * Auto-ticks parameter setter. + * If it set to {@code false}, the user defined graduation will be used. + * @param isAutoTicks the new auto-ticks status. + */ + public void setAutoTicks(boolean isAutoTicks) { + this.isAutoTicks = isAutoTicks; + } + + @Override + public boolean isLogarithmic() { + return isLogarithmic; + } + + /** + * Logarithmic or linear setter + * @param isLogarithmic the new logarithmic status. + */ + public void setLogarithmic(boolean isLogarithmic) { + if (isLogarithmic != this.isLogarithmic) { + this.isLogarithmic = isLogarithmic; + graduations = null; + } + } + + @Override + public int getSpriteDistance() { + return spriteDistance; + } + + /** + * Sprite distance setter. + * @param spriteDistance the new sprite distance to the main ticks in pixel. + */ + public void setSpriteDistance(int spriteDistance) { + this.spriteDistance = spriteDistance; + } + + @Override + public double getMinimalSubTicksDistance() { + return minimalSubTicksDistance; + } + + /** + * Minimal accepted distance between sub-ticks setter. + * @param minimalSubTicksDistance the new minimal accepted distance between sub-ticks. + */ + public void setMinimalSubTicksDistance(double minimalSubTicksDistance) { + this.minimalSubTicksDistance = minimalSubTicksDistance; + } + + @Override + public Color getColor() { + return color; + } + + /** + * Ruler color setter. + * @param color the new rule color. + */ + public void setColor(Color color) { + this.color = color; + } + + @Override + public double getLineWidth() { + return lineWidth; + } + + /** + * Ruler line width setter + * @param lineWidth the new line width + */ + public void setLineWidth(double lineWidth) { + this.lineWidth = lineWidth; + } + + @Override + public int getSubticksNumber() { + return subticksNumber; + } + + /** + * Set number of subticks. + * @param N the number of subticks or -1 if the computation is automatic. + */ + public void setSubticksNumber(int N) { + this.subticksNumber = N; + } + + @Override + public String getFormat() { + return format; + } + + /** + * Set the format. + * @param format the format as used in String.format. + */ + public void setFormat(String format) { + this.format = format; + } + + @Override + public double getScale() { + return scale; + } + + @Override + public double getTranslate() { + return translate; + } + + /** + * Set the scale-translate factors. + * @param . + */ + public void setSTFactors(Double[] factors) { + this.scale = factors[0]; + this.translate = factors[1]; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/DefaultRulerSpriteFactory.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/DefaultRulerSpriteFactory.java new file mode 100755 index 000000000..ae709107a --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/DefaultRulerSpriteFactory.java @@ -0,0 +1,63 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.ruler; + +import org.scilab.forge.scirenderer.shapes.appearance.Color; +import org.scilab.forge.scirenderer.texture.TextEntity; +import org.scilab.forge.scirenderer.texture.Texture; +import org.scilab.forge.scirenderer.texture.TextureDrawer; +import org.scilab.forge.scirenderer.texture.TextureDrawingTools; +import org.scilab.forge.scirenderer.texture.TextureManager; + +import java.awt.Dimension; +import java.text.DecimalFormat; + +/** + * @author Pierre Lando + */ +public final class DefaultRulerSpriteFactory implements RulerSpriteFactory { + + /** + * Default constructor. + */ + public DefaultRulerSpriteFactory() { + } + + @Override + public Texture create(double value, DecimalFormat adaptedFormat, TextureManager textureManager) { + String text = adaptedFormat.format(value); + final TextEntity textEntity = new TextEntity(text); + textEntity.setTextColor(new Color(0, 0, 0)); + textEntity.setTextAntiAliased(false); + + Texture texture = textureManager.createTexture(); + texture.setDrawer(new TextureDrawer() { + + @Override + public void draw(TextureDrawingTools drawingTools) { + drawingTools.draw(textEntity, 0, 0); + } + + @Override + public Dimension getTextureSize() { + return textEntity.getSize(); + } + + @Override + public OriginPosition getOriginPosition() { + return OriginPosition.UPPER_LEFT; + } + }); + + return texture; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/RulerDrawer.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/RulerDrawer.java new file mode 100755 index 000000000..dd7b88b61 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/RulerDrawer.java @@ -0,0 +1,726 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando + * Copyright (C) 2012 - Scilab Enterprises - Bruno JOFRET + * Copyright (C) 2013 - Scilab Enterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.ruler; + +import java.awt.Dimension; +import java.awt.geom.Rectangle2D; +import java.nio.FloatBuffer; +import java.text.DecimalFormat; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.scilab.forge.scirenderer.DrawingTools; +import org.scilab.forge.scirenderer.SciRendererException; +import org.scilab.forge.scirenderer.buffers.BuffersManager; +import org.scilab.forge.scirenderer.buffers.ElementsBuffer; +import org.scilab.forge.scirenderer.ruler.graduations.Graduations; +import org.scilab.forge.scirenderer.ruler.graduations.UserDefinedFormat; +import org.scilab.forge.scirenderer.shapes.appearance.Appearance; +import org.scilab.forge.scirenderer.shapes.geometry.DefaultGeometry; +import org.scilab.forge.scirenderer.shapes.geometry.Geometry; +import org.scilab.forge.scirenderer.texture.AnchorPosition; +import org.scilab.forge.scirenderer.texture.Texture; +import org.scilab.forge.scirenderer.texture.TextureManager; +import org.scilab.forge.scirenderer.tranformations.Transformation; +import org.scilab.forge.scirenderer.tranformations.Vector3d; + +/** + * @author Pierre Lando + */ +public class RulerDrawer { + + /** + * Sprite map. + */ + private final Map<Double, Texture> spriteMap = new ConcurrentHashMap<Double, Texture>(); + + /** + * The current {@link TextureManager}. + */ + private final TextureManager textureManager; + + /** + * The used {@link RulerSpriteFactory}. + */ + private RulerSpriteFactory spriteFactory; + + private OneShotRulerDrawer oneShotRulerDrawer; + + /** + * Ruler drawer constructor. + * @param textureManager the {@link TextureManager} of the canvas where the ruler will be drawn. + */ + public RulerDrawer(TextureManager textureManager) { + this.textureManager = textureManager; + this.spriteFactory = new DefaultRulerSpriteFactory(); + this.oneShotRulerDrawer = new OneShotRulerDrawer(); + } + + /** + * Ruler drawing method. + * @param drawingTools the {@link DrawingTools} of the canvas where the ruler will be drawn. + * @param model the {@link RulerModel} of the drawn ruler. + * @return the {@link RulerDrawingResult} give information about how the ruler have been drawn. + */ + public RulerDrawingResult draw(DrawingTools drawingTools, RulerModel model) { + return oneShotRulerDrawer.drawWithResults(drawingTools, model); + } + + /** + * Get the sprite factory + * @return the sprite factory + */ + public RulerSpriteFactory getSpriteFactory() { + return this.spriteFactory; + } + + /** + * Draw the ruler + * @param drawingTools the {@link DrawingTools} of the canvas where the ruler will be drawn. + */ + public void draw(DrawingTools drawingTools) { + oneShotRulerDrawer.draw(drawingTools); + } + + /** + * Get the model + * @return the ruler model. + */ + public RulerModel getModel() { + return oneShotRulerDrawer.rulerModel; + } + + /** + * Get the subticks values + * @return the values. + */ + public List<Double> getSubTicksValue() { + return oneShotRulerDrawer.subTicksValue; + } + + /** + * Get the ticks values + * @return the values. + */ + public List<Double> getTicksValue() { + return oneShotRulerDrawer.ticksValue; + } + + /** + * Compute different parameters on a ruler + * @param rulerModel the {@link RulerModel} of the drawn ruler. + * @param canvasProjection the canvas projection. + */ + public RulerDrawingResult computeRuler(RulerModel model, Transformation canvasProjection) { + return oneShotRulerDrawer.computeRuler(model, canvasProjection); + } + + /** + * Set the current {@link RulerSpriteFactory}. + * All existing sprite will be cleared. + * This ruler drawer will use the new {@link RulerSpriteFactory}. + * @param spriteFactory the new {@link RulerSpriteFactory}. + */ + public void setSpriteFactory(RulerSpriteFactory spriteFactory) { + disposeResources(); + this.spriteFactory = spriteFactory; + } + + /** + * Dispose all used resources. + */ + public void disposeResources() { + textureManager.dispose(spriteMap.values()); + spriteMap.clear(); + oneShotRulerDrawer.dispose(); + } + + public double getDistanceRatio() { + return oneShotRulerDrawer.getDistanceRatio(); + } + + /** + * This class actually perform all the rendering of one ruler. + */ + private class OneShotRulerDrawer { + + private Transformation canvasProjection; + private RulerModel rulerModel; + private Vector3d windowSubTicksDelta; + private Vector3d windowTicksDelta; + + /** + * Sprite Dimension map. Used as cached values in order not to charge DataProvider. + */ + private Map<Double, Dimension> spriteDimensionMap = new HashMap<Double, Dimension>(); + + private List<PositionedSprite> spritesList = new LinkedList<PositionedSprite>(); + + /** + * The maximum distance corresponding to the actually displayed sprites. + */ + private double maximalSpritesDistance; + + /** + * Deepest possible {@see Graduations} + */ + private Graduations graduations; + + private List<Double> subTicksValue; + private List<Double> ticksValue; + private int density; + private double distRatio; + + public OneShotRulerDrawer() { } + + public synchronized void dispose() { + spriteDimensionMap.clear(); + spritesList.clear(); + if (subTicksValue != null) { + subTicksValue.clear(); + } + if (ticksValue != null) { + ticksValue.clear(); + } + rulerModel = null; + } + + public double getDistanceRatio() { + return distRatio; + } + + /** + * Compute different parameters on a ruler + * @param drawingTools the {@link DrawingTools} of the canvas where the ruler will be drawn. + * @param rulerModel the {@link RulerModel} of the drawn ruler. + * @param canvasProjection the canvas projection. + */ + public synchronized RulerDrawingResult computeRuler(RulerModel rulerModel, Transformation canvasProjection) { + // Same code as drawWithResults (without drawing) + // Historically, computations were made when drawing and they are made before drawing. + // TODO: remove drawWithResults ?? + this.canvasProjection = canvasProjection; + this.rulerModel = rulerModel; + subTicksValue = new LinkedList<Double>(); + ticksValue = new LinkedList<Double>(); + spritesList = new LinkedList<PositionedSprite>(); + + Vector3d windowTicksDirection = canvasProjection.projectDirection(rulerModel.getTicksDirection()); + windowTicksDirection = windowTicksDirection.setZ(0); + + Vector3d normalizedProjectedTicksDirection = windowTicksDirection.getNormalized(); + windowSubTicksDelta = normalizedProjectedTicksDirection.times(rulerModel.getSubTicksLength()); + windowTicksDelta = normalizedProjectedTicksDirection.times(rulerModel.getTicksLength()); + + DecimalFormat format; + if (rulerModel.isAutoTicks()) { + format = computeAutoGraduation(); + } else { + format = computeUserGraduation(); + } + computeTicksData(); + + double distRatio = computeTicksDistanceRatio(windowTicksDirection.getNorm()); + + return new RulerDrawingResult(format, ticksValue, subTicksValue, density, distRatio, normalizedProjectedTicksDirection); + } + + /** + * Constructor. + * @param drawingTools the {@link DrawingTools} of the canvas where the ruler will be drawn. + * @param rulerModel the {@link RulerModel} of the drawn ruler. + */ + public synchronized RulerDrawingResult drawWithResults(DrawingTools drawingTools, RulerModel rulerModel) { + this.rulerModel = rulerModel; + subTicksValue = new LinkedList<Double>(); + ticksValue = new LinkedList<Double>(); + spritesList = new LinkedList<PositionedSprite>(); + canvasProjection = drawingTools.getTransformationManager().getCanvasProjection(); + + Vector3d windowTicksDirection = canvasProjection.projectDirection(rulerModel.getTicksDirection()); + windowTicksDirection = windowTicksDirection.setZ(0); + + Vector3d normalizedProjectedTicksDirection = windowTicksDirection.getNormalized(); + windowSubTicksDelta = normalizedProjectedTicksDirection.times(rulerModel.getSubTicksLength()); + windowTicksDelta = normalizedProjectedTicksDirection.times(rulerModel.getTicksLength()); + + DecimalFormat format; + if (rulerModel.isAutoTicks()) { + format = computeAutoGraduation(); + } else { + format = computeUserGraduation(); + } + computeTicksData(); + + draw(drawingTools); + + double distRatio = computeTicksDistanceRatio(windowTicksDirection.getNorm()); + + return new RulerDrawingResult(format, ticksValue, subTicksValue, density, distRatio, normalizedProjectedTicksDirection); + } + + /** + * Compute the ratio between windows ticks norm and the sprite distance. + * @param windowTicksNorm the windows tics norm. + * @return the ratio between windows ticks norm and the sprite distance. + */ + private double computeTicksDistanceRatio(double windowTicksNorm) { + if (windowTicksNorm == 0) { + distRatio = 1.0; + } else if (maximalSpritesDistance == 0) { + distRatio = rulerModel.getSpriteDistance() / windowTicksNorm; + } else { + distRatio = maximalSpritesDistance / windowTicksNorm; + } + return distRatio; + } + + /** + * Actually perform the ruler drawing. + * @param drawingTools {@link DrawingTools} used to perform the ruler drawing. + */ + private synchronized void draw(DrawingTools drawingTools) { + if (rulerModel == null) { + return; + } + + BuffersManager bufferManager = drawingTools.getCanvas().getBuffersManager(); + ElementsBuffer vertices = bufferManager.createElementsBuffer(); + fillVertices(vertices, rulerModel, ticksValue, subTicksValue, canvasProjection); + DefaultGeometry geometry = new DefaultGeometry(); + geometry.setFillDrawingMode(Geometry.FillDrawingMode.NONE); + geometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS); + geometry.setVertices(vertices); + + Appearance appearance = new Appearance(); + appearance.setLineColor(rulerModel.getColor()); + appearance.setLineWidth((float) rulerModel.getLineWidth()); + + drawingTools.getTransformationManager().useWindowCoordinate(); + try { + for (PositionedSprite positionedSprite : spritesList) { + drawingTools.draw(positionedSprite.getSprite(), AnchorPosition.CENTER, positionedSprite.getWindowPosition()); + } + drawingTools.draw(geometry, appearance); + } catch (SciRendererException ignored) { + } + drawingTools.getTransformationManager().useSceneCoordinate(); + bufferManager.dispose(vertices); + } + + /** + * Compute the {@link Graduations} used to the ruler drawing in auto-ticks mode.. + * @return the used decimal format. + */ + private DecimalFormat computeAutoGraduation() { + /* The maximum distance corresponding to the actually displayed sprites. */ + double maxSpritesDistance = 0.0; + + Graduations currentGraduations = rulerModel.getGraduations(); + Graduations ticksGraduation = currentGraduations; + DecimalFormat format = currentGraduations.getFormat(); + String f = rulerModel.getFormat(); + if (f != null && !f.isEmpty()) { + format = new UserDefinedFormat(format, f, rulerModel.getScale(), rulerModel.getTranslate()); + } + + boolean canGetMore = true; + List<PositionedSprite> newSpritesList = new LinkedList<PositionedSprite>(); + while (currentGraduations != null) { + /* The maximum distance to any of the sprites' farthest sides at a given iteration. */ + double currentMaximalSpritesDistance = 0; + + newSpritesList.clear(); + List<Double> ticks = currentGraduations.getNewValues(); + for (double value : ticks) { + Texture sprite = computeSprite(value, format); + Vector3d windowPosition = canvasProjection.project(rulerModel.getPosition(value)); + + // X != X means NaN so we are not able to project coordinates + // return basic format + if (windowPosition.getX() != windowPosition.getX()) { + return format; + } + + Dimension textureSize = computeSpriteDimension(value); + + Vector3d delta = projectCenterToEdge(textureSize, windowTicksDelta); + PositionedSprite newSprite = new PositionedSprite(sprite, textureSize, windowPosition.plus(windowTicksDelta.plus(delta))); + newSpritesList.add(newSprite); + Vector3d farDelta = windowTicksDelta.plus(delta.times(2.0)); + currentMaximalSpritesDistance = Math.max(currentMaximalSpritesDistance, farDelta.getNorm()); + } + + if (collide(newSpritesList, rulerModel.getMargin()) || collide(spritesList, newSpritesList, rulerModel.getMargin())) { + currentGraduations = currentGraduations.getAlternative(); + canGetMore = false; + } else { + maxSpritesDistance = Math.max(maxSpritesDistance, currentMaximalSpritesDistance); + spritesList.addAll(newSpritesList); + ticksGraduation = currentGraduations; + if (canGetMore) { + currentGraduations = currentGraduations.getMore(); + } else { + currentGraduations = null; + } + } + } + + this.graduations = ticksGraduation; + this.maximalSpritesDistance = maxSpritesDistance; + + return format; + } + + /** + * Compute the {@link Graduations} used to the ruler drawing in auto-ticks mode.. + * @return the used decimal format. + */ + private DecimalFormat computeUserGraduation() { + /* The maximum distance corresponding to the actually displayed sprites. */ + double maxSpritesDistance = 0.0; + Graduations currentGraduations = rulerModel.getGraduations(); + + List<Double> ticks = currentGraduations.getNewValues(); + DecimalFormat format = currentGraduations.getFormat(); + for (double value : ticks) { + Texture sprite = computeSprite(value, format); + if (sprite != null) { + Vector3d windowPosition = canvasProjection.project(rulerModel.getPosition(value)); + + Vector3d delta = projectCenterToEdge(sprite, windowTicksDelta); + spritesList.add(new PositionedSprite(sprite, windowPosition.plus(windowTicksDelta.plus(delta)))); + + Vector3d farDelta = windowTicksDelta.plus(delta.times(2.0)); + maxSpritesDistance = Math.max(maxSpritesDistance, farDelta.getNorm()); + } + } + + this.graduations = currentGraduations; + this.maximalSpritesDistance = maxSpritesDistance; + return format; + } + + /** + * Compute the ticks, sub-ticks data and the sub-ticks density. + */ + private void computeTicksData() { + if (graduations != null) { + ticksValue = graduations.getAllValues(); + final int N = rulerModel.getSubticksNumber(); + + if (N < 0) { + Graduations subGraduation = graduations.getSubGraduations(); + while ((subGraduation != null) && (computeTicksDistance(subGraduation) < rulerModel.getMinimalSubTicksDistance())) { + subGraduation = subGraduation.getAlternative(); + } + + if (subGraduation != null) { + subTicksValue = subGraduation.getAllValues(); + } else { + subTicksValue = new LinkedList<Double>(); + } + } else { + subTicksValue = graduations.getSubGraduations(N); + } + density = getDensity(); + } else { + subTicksValue = new LinkedList<Double>(); + ticksValue = new LinkedList<Double>(); + density = 0; + } + } + + private int getDensity() { + int N = ticksValue == null ? 0 : ticksValue.size(); + int M = subTicksValue == null ? 0 : subTicksValue.size(); + + if (M <= N || N == 1) { + return 0; + } + + return (M - N) / (N - 1); + } + + /** + * Compute and return the minimal screen distance between two successive ticks of the given {@link Graduations}. + * If the given {@link Graduations} is <code>null</code>, the returned value is {@link Double#MAX_VALUE}. + * @param graduations the given {@link Graduations}. + * @return the minimal screen distance between two successive ticks of the given {@link Graduations}. + */ + private double computeTicksDistance(Graduations graduations) { + double minimalDistance = Double.MAX_VALUE; + if (graduations != null) { + Vector3d previousProjection = null; + for (double currentValue : graduations.getAllValues()) { + Vector3d currentProjection = canvasProjection.project(rulerModel.getPosition(currentValue)); + if (previousProjection != null) { + minimalDistance = Math.min(minimalDistance, currentProjection.minus(previousProjection).getNorm2()); + } + previousProjection = currentProjection; + } + minimalDistance = Math.sqrt(minimalDistance); + } + return minimalDistance; + } + + /** + * Return true if at leas two element of {@see spritesList} collide. + * @param spritesList the list of sprite to be tested. + * @param margin the collision margin. + * @return true if at leas two element of {@see spritesList} collide. + */ + private boolean collide(List<PositionedSprite> spritesList, double margin) { + for (int i = 0; i < spritesList.size(); i++) { + Rectangle2D bounds = spritesList.get(i).getWindowBounds(); + for (int j = i + 1; j < spritesList.size(); j++) { + if (collide(bounds, spritesList.get(j).getWindowBounds(), margin)) { + return true; + } + } + } + return false; + } + + /** + * Return true if the at least one element of <code>newSpritesList</code> collides with one element of <code>spritesList</code>. + * @param spritesList the list of reference sprites. + * @param newSpritesList the list of new sprites. + * @param margin the collision margin. + * @return true if the at least one element of <code>newSpritesList</code> collides with one element of <code>spritesList</code>. + */ + private boolean collide(List<PositionedSprite> spritesList, List<PositionedSprite> newSpritesList, double margin) { + for (PositionedSprite sprite1 : newSpritesList) { + Rectangle2D bounds = sprite1.getWindowBounds(); + for (PositionedSprite sprite2 : spritesList) { + if (collide(bounds, sprite2.getWindowBounds(), margin)) { + return true; + } + } + } + return false; + } + + /** + * Return true if the givens rectangles collide. + * @param rectangle1 first rectangle. + * @param rectangle2 second rectangle. + * @param margin the margin. + * @return true if the givens rectangles collide. + */ + private boolean collide(Rectangle2D rectangle1, Rectangle2D rectangle2, double margin) { + return ((rectangle2.getMinX() - rectangle1.getMaxX()) < margin) + && ((rectangle1.getMinX() - rectangle2.getMaxX()) < margin) + && ((rectangle2.getMinY() - rectangle1.getMaxY()) < margin) + && ((rectangle1.getMinY() - rectangle2.getMaxY()) < margin); + } + + /** + * Compute and return a translation along the given <code>direction</code>. + * This translation is the vector between the <code>sprite</code> center and is projection along the given + * {@see direction} to the sprite edges. + * @param sprite the given {@link Texture} + * @param direction the given direction. + * @return the vector between the sprite center and is projection to the sprite edges. + */ + private Vector3d projectCenterToEdge(Texture sprite, Vector3d direction) { + Vector3d usedDirection; + if ((direction == null) || (direction.isZero())) { + usedDirection = new Vector3d(1, 0, 0); + } else { + usedDirection = direction; + } + + /* +1 is used to have a space between the tick and its label */ + Dimension textureSize = sprite.getDataProvider().getTextureSize(); + double ratioX = textureSize.width / Math.abs(usedDirection.getX()); + double ratioY = textureSize.height / Math.abs(usedDirection.getY()); + double ratio = Math.min(ratioY, ratioX); + + return usedDirection.times((ratio + 1) / 2); + } + + /** + * Compute and return a translation along the given <code>direction</code>. + * This translation is the vector between the <code>sprite</code> center and is projection along the given + * {@see direction} to the sprite edges. + * @param textureSize the size of corresponding texture + * @param direction the given direction. + * @return the vector between the sprite center and is projection to the sprite edges. + */ + private Vector3d projectCenterToEdge(Dimension textureSize, Vector3d direction) { + Vector3d usedDirection; + if ((direction == null) || (direction.isZero())) { + usedDirection = new Vector3d(1, 0, 0); + } else { + usedDirection = direction; + } + + double ratioX = textureSize.width / Math.abs(usedDirection.getX()); + double ratioY = textureSize.height / Math.abs(usedDirection.getY()); + double ratio = Math.min(ratioY, ratioX); + + return usedDirection.times((ratio + 1) / 2); + } + + /** + * Fill a vertices buffer with the needed data to draw a ruler. + * @param verticesBuffer the {@link ElementsBuffer} to fill. + * @param rulerModel the {@link RulerModel} to draw. + * @param ticksValue the list of ticks. + * @param subTicksValue the list of sub-ticks. + * @param canvasProjection the used canvas projection. + */ + private void fillVertices(ElementsBuffer verticesBuffer, RulerModel rulerModel, List<Double> ticksValue, List<Double> subTicksValue, Transformation canvasProjection) { + Vector3d a = rulerModel.getFirstPoint(); + Vector3d b = rulerModel.getSecondPoint(); + + if ((a != null) && (b != null)) { + int bufferSize = 2 * ticksValue.size() + 2 * subTicksValue.size(); + if (rulerModel.isLineVisible()) { + bufferSize += 2; + } + FloatBuffer data = FloatBuffer.allocate(4 * bufferSize); + data.rewind(); + + for (double value : ticksValue) { + Vector3d p = canvasProjection.project(rulerModel.getPosition(value)); + data.put(p.getDataAsFloatArray(4)); + data.put(p.plus(windowTicksDelta).getDataAsFloatArray(4)); + } + + for (double value : subTicksValue) { + Vector3d p = canvasProjection.project(rulerModel.getPosition(value)); + data.put(p.getDataAsFloatArray(4)); + data.put(p.plus(windowSubTicksDelta).getDataAsFloatArray(4)); + } + + if (rulerModel.isLineVisible()) { + data.put(canvasProjection.project(a).getDataAsFloatArray(4)); + data.put(canvasProjection.project(b).getDataAsFloatArray(4)); + } + + data.rewind(); + verticesBuffer.setData(data, 4); + } else { + verticesBuffer.setData(new float[0], 4); + } + } + + /** + * Compute the {@link Texture} for a given value. + * The {@link Texture} is made once using the current {@link RulerSpriteFactory}. + * @param value the given value. + * @param format the format to use. + * @return A {@link Texture} for the label at the given value. + */ + private Texture computeSprite(double value, DecimalFormat format) { + Texture sprite = spriteMap.get(value); + if (sprite == null) { + sprite = spriteFactory.create(value, format, textureManager); + if (sprite != null) { + spriteMap.put(value, sprite); + } + } + return sprite; + } + + private Dimension computeSpriteDimension(double value) { + Dimension spriteDimension = spriteDimensionMap.get(value); + if (spriteDimension == null) { + Texture sprite = spriteMap.get(value); + if (sprite != null) { + spriteDimension = sprite.getDataProvider().getTextureSize(); + spriteDimensionMap.put(value, spriteDimension); + } + } + + return spriteDimension; + } + + /** + * This class is a basic container for a {@link Texture} and an associated window position. + * + * @author Pierre Lando + */ + private class PositionedSprite { + + private final Texture sprite; + private final Vector3d windowPosition; + private final Rectangle2D windowsBounds; + + /** + * Default constructor. + * @param sprite the {@link Texture}. + * @param windowPosition the window position. + */ + public PositionedSprite(Texture sprite, Vector3d windowPosition) { + //long tic = Calendar.getInstance().getTimeInMillis(); + this.windowPosition = windowPosition; + this.sprite = sprite; + + Dimension textureSize = sprite.getDataProvider().getTextureSize(); + windowsBounds = new Rectangle2D.Double( + windowPosition.getX(), + windowPosition.getY(), + textureSize.width, + textureSize.height + ); + + //System.err.println("====[new PositionedSprite] time = "+(Calendar.getInstance().getTimeInMillis() - tic)); + } + + public PositionedSprite(Texture sprite, Dimension textureSize, Vector3d windowPosition) { + //long tic = Calendar.getInstance().getTimeInMillis(); + this.windowPosition = windowPosition; + this.sprite = sprite; + + windowsBounds = new Rectangle2D.Double( + windowPosition.getX(), + windowPosition.getY(), + textureSize.width, + textureSize.height + ); + + //System.err.println("====[new PositionedSprite] time = "+(Calendar.getInstance().getTimeInMillis() - tic)); + } + /** + * Return the {@link Texture} + * @return the {@link Texture} + */ + public Texture getSprite() { + return sprite; + } + + /** + * Return the window position of the {@link Texture}. + * @return the window position of the {@link Texture}. + */ + public Vector3d getWindowPosition() { + return windowPosition; + } + + /** + * Return the window bounds of the {@link Texture} + * @return the window bounds of the {@link Texture} + */ + public Rectangle2D getWindowBounds() { + return windowsBounds; + } + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/RulerDrawingResult.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/RulerDrawingResult.java new file mode 100755 index 000000000..8c0da2b05 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/RulerDrawingResult.java @@ -0,0 +1,136 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.ruler; + +import org.scilab.forge.scirenderer.tranformations.Vector3d; + +import java.text.DecimalFormat; +import java.util.List; + +/** + * Contain information about the ruler drawing result. + * + * @author Pierre Lando + */ +public class RulerDrawingResult { + + /** + * Values of drawn ticks. + */ + private final double[] ticksValues; + + /** + * Values of drawn sub-ticks. + */ + private final double[] subTicksValues; + + /** + * Sub-ticks density. + */ + private final int subTicksDensity; + + /** + * The ratio between the maximum distance to any of the sprites' farthest sides + * along the projected ticks direction and the projected ticks direction norm. + */ + private final double maxDistToTicksDirNorm; + + /** + * The normalized projected ticks direction. + */ + private final Vector3d normalizedTicksDirection; + + /** The used format to draw sprites */ + private final DecimalFormat format; + + /** + * Package constructor. + * Those object can only be created by a {@link RulerDrawer} + * + * @param format + * @param ticksValues values of drawn ticks. + * @param subTicksValues values of drawn sub-ticks. + * @param subTicksDensity sub-ticks density. + * @param maxDistToTicksDirNorm the ratio between the maximum sprite distance and the projected ticks direction norm. + * @param ticksDir the normalized projected ticks direction. + */ + RulerDrawingResult(DecimalFormat format, List<Double> ticksValues, List<Double> subTicksValues, int subTicksDensity, double maxDistToTicksDirNorm, Vector3d ticksDir) { + this.format = format; + this.ticksValues = listToArray(ticksValues); + this.subTicksValues = listToArray(subTicksValues); + this.subTicksDensity = subTicksDensity; + this.maxDistToTicksDirNorm = maxDistToTicksDirNorm; + this.normalizedTicksDirection = new Vector3d(ticksDir); + } + + /** + * Format getter + * @return the format. + */ + public DecimalFormat getFormat() { + return format; + } + + /** + * Convert a list of Double to an array of double. + * @param list the given list. + * @return an array of double with the value of the given list. + */ + private double[] listToArray(List<Double> list) { + double[] array = new double[list.size()]; + int index = 0; + for (double value : list) { + array[index++] = value; + } + return array; + } + + /** + * Drawn ticks values getter. + * @return the drawn ticks values. + */ + public double[] getTicksValues() { + return ticksValues.clone(); + } + + /** + * Drawn sub-ticks values getter. + * @return the drawn sub-ticks values. + */ + public double[] getSubTicksValues() { + return subTicksValues.clone(); + } + + /** + * Drawn sub-ticks density getter. + * @return the drawn sub-ticks density. + */ + public int getSubTicksDensity() { + return subTicksDensity; + } + + /** + * Maximum sprite distance to projected ticks direction norm ratio getter. + * @return the distance to ticks direction norm ratio. + */ + public double getMaxDistToTicksDirNorm() { + return maxDistToTicksDirNorm; + } + + /** + * Normalized projected ticks direction getter. + * @return the normalized projected ticks direction. + */ + public Vector3d getNormalizedTicksDirection() { + return normalizedTicksDirection; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/RulerModel.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/RulerModel.java new file mode 100755 index 000000000..baaa86919 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/RulerModel.java @@ -0,0 +1,225 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando + * Copyright (C) 2013 - Scilab Enterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.ruler; + +import org.scilab.forge.scirenderer.ruler.graduations.Graduations; +import org.scilab.forge.scirenderer.shapes.appearance.Color; +import org.scilab.forge.scirenderer.tranformations.Vector3d; + +/** + * @author Pierre Lando + */ +public interface RulerModel { + + /** + * Default first value. + */ + double DEFAULT_FIRST_VALUE = 0; + + /** + * Default second value. + */ + double DEFAULT_SECOND_VALUE = 1; + + /** + * Default first point position. + */ + Vector3d DEFAULT_FIRST_POINT = new Vector3d(0, 0, 0); + + /** + * Default second point position. + */ + Vector3d DEFAULT_SECOND_POINT = new Vector3d(1, 0, 0); + + /** + * Default margin in pixel. + */ + double DEFAULT_MARGIN = 12.0; + + /** + * Default main line visibility. + */ + boolean DEFAULT_LINE_VISIBLE = true; + + /** + * Default {@link org.scilab.forge.scirenderer.sprite.Sprite} distance to the main line in pixel. + */ + int DEFAULT_SPRITE_DISTANCE = 12; + + /** + * Default sub-tick length in pixel. + */ + int DEFAULT_SUB_TICK_LENGTH = 5; + + /** + * Default tick length in pixel. + */ + int DEFAULT_TICK_LENGTH = 10; + + /** + * Default minimal sub-ticks distance. + */ + double DEFAULT_MINIMAL_SUB_TICKS_DISTANCE = 8; + + /** + * Default auto-ticks status. + */ + boolean DEFAULT_AUTO_TICKS_STATUS = true; + + /** + * Default logarithmic status. + */ + boolean DEFAULT_LOGARITHMIC_STATUS = false; + + /** + * Default ticks direction. + */ + Vector3d DEFAULT_TICKS_DIRECTION = new Vector3d(1, 0, 0); + + /** + * Default color. + */ + Color DEFAULT_COLOR = new Color(0, 0, 0); + + /** + * Default line width. + */ + double DEFAULT_LINE_WIDTH = 1.0; + + /** + * Return used graduation to draw this ruler. + * @return the used graduation to draw this ruler. + */ + Graduations getGraduations(); + + /** + * First value getter. + * @return the first values. + */ + double getFirstValue(); + + /** + * Second value getter. + * @return the second values. + */ + double getSecondValue(); + + /** + * First point getter. + * @return the first point. + */ + Vector3d getFirstPoint(); + + /** + * Second point getter. + * @return the second point. + */ + Vector3d getSecondPoint(); + + /** + * Ticks direction getter. + * @return the ticks direction. + */ + Vector3d getTicksDirection(); + + /** + * Ticks length getter. + * @return the ticks length in pixel. + */ + int getTicksLength(); + + /** + * Sub-ticks length getter. + * @return the sub-ticks length in pixel. + */ + int getSubTicksLength(); + + /** + * Return the position corresponding to the given value. + * @param value the given value. + * @return the position corresponding to the given value. + */ + Vector3d getPosition(double value); + + /** + * Return the accepted margin (in pixel) for label drawing. + * @return the accepted margin (in pixel) for label drawing. + */ + double getMargin(); + + /** + * Line visibility getter. + * @return the line visibility status. + */ + boolean isLineVisible(); + + /** + * Auto-ticking getter. + * @return the auto-ticking status. + */ + boolean isAutoTicks(); + + /** + * Logarithmic state getter. + * @return the logarithmic state + */ + boolean isLogarithmic(); + + /** + * Sprite distance getter. + * @return the sprite distance (in pixel) between the ruler line and the sprites edges. + */ + int getSpriteDistance(); + + /** + * Minimal accepted sub-ticks distance getter. + * @return the minimal accepted sub-ticks distance. + */ + double getMinimalSubTicksDistance(); + + /** + * Color getter. + * @return the color of ruler line and ticks. + */ + Color getColor(); + + /** + * Line width getter. + * @return the line width of ruler line, grid and ticks. + */ + double getLineWidth(); + + /** + * Number of subticks. + * @return the number of subticks or -1 if the computation is automatic. + */ + int getSubticksNumber(); + + /** + * Get the format to print the ticks label + * @return the format + */ + String getFormat(); + + /** + * Get the scale factor to recompute label value + * @return the scale factor + */ + double getScale(); + + /** + * Get the translate factor to recompute label value + * @return the translate factor + */ + double getTranslate(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/RulerSpriteFactory.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/RulerSpriteFactory.java new file mode 100755 index 000000000..44a45596f --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/RulerSpriteFactory.java @@ -0,0 +1,32 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.ruler; + +import org.scilab.forge.scirenderer.texture.Texture; +import org.scilab.forge.scirenderer.texture.TextureManager; + +import java.text.DecimalFormat; + +/** + * @author Pierre Lando + */ +public interface RulerSpriteFactory { + + /** + * Return the texture for the given value. + * @param value the value. + * @param adaptedFormat an adapted number format for the given value. + * @param textureManager {@link TextureManager} to use. + * @return a positioned text entity for the given value. + */ + Texture create(double value, DecimalFormat adaptedFormat, TextureManager textureManager); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/AbstractGraduations.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/AbstractGraduations.java new file mode 100755 index 000000000..4a97ceb13 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/AbstractGraduations.java @@ -0,0 +1,233 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * Copyright (C) 2013-2015 - Scilab Enterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.ruler.graduations; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; + +/** + * @author Pierre Lando + */ +public abstract class AbstractGraduations implements Graduations { + + protected static final double PRECISION = 1e-8; + + /** The left bracket used by {@link #toString()} */ + private static final String LEFT_BRACKET = "["; + + /** The right bracket used by {@link #toString()} */ + private static final String RIGHT_BRACKET = "]"; + + /** True if the lower bound is included in the graduation interval. */ + private final boolean isLowerBoundIncluded; + + /** True if the upper bound is included in the graduation interval. */ + private final boolean isUpperBoundIncluded; + + /** Interval lower bound. */ + private final double lowerBound; + + /** Interval upper bound. */ + private final double upperBound; + + private final Graduations parentGraduations; + private DecimalFormat numberFormat; + protected List<Double> subValues; + + /** + * Constructor from parent graduations. + * This constructor copy information from given {@link Graduations} and set it as is parent. + * @param parentGraduations the parent graduations to copy. + */ + public AbstractGraduations(Graduations parentGraduations) { + this.parentGraduations = parentGraduations; + this.isLowerBoundIncluded = parentGraduations.isLowerBoundIncluded(); + this.isUpperBoundIncluded = parentGraduations.isUpperBoundIncluded(); + this.lowerBound = parentGraduations.getLowerBound(); + this.upperBound = parentGraduations.getUpperBound(); + } + + /** + * Root constructor. + * Graduations made this way don't have a parent. + * @param lowerBound the actual lower bounds. + * @param lowerBoundIncluded the actual lower bounds included status. + * @param upperBound the actual upper bounds. + * @param upperBoundIncluded the actual upper bounds included status. + */ + public AbstractGraduations(double lowerBound, boolean lowerBoundIncluded, double upperBound, boolean upperBoundIncluded) { + this.parentGraduations = null; + this.isLowerBoundIncluded = lowerBoundIncluded; + this.isUpperBoundIncluded = upperBoundIncluded; + this.lowerBound = lowerBound; + this.upperBound = upperBound; + } + + /** + * Root constructor. + * Graduations made this way don't have a parent. + * There bounds are included. + * @param lowerBound the actual lower bounds included status. + * @param upperBound the actual upper bounds included status. + */ + public AbstractGraduations(double lowerBound, double upperBound) { + this.parentGraduations = null; + this.isLowerBoundIncluded = true; + this.isUpperBoundIncluded = true; + this.lowerBound = lowerBound; + this.upperBound = upperBound; + } + + /** + * Child constructor. + * @param parentGraduations the parent graduation. + * @param lowerBound the actual lower bounds. + * @param lowerBoundIncluded the actual lower bounds included status. + * @param upperBound the actual upper bounds. + * @param upperBoundIncluded the actual upper bounds included status. + */ + public AbstractGraduations(Graduations parentGraduations, double lowerBound, boolean lowerBoundIncluded, double upperBound, boolean upperBoundIncluded) { + this.parentGraduations = parentGraduations; + this.isLowerBoundIncluded = lowerBoundIncluded; + this.isUpperBoundIncluded = upperBoundIncluded; + this.lowerBound = lowerBound; + this.upperBound = upperBound; + } + + @Override + public final double getLowerBound() { + return lowerBound; + } + + @Override + public final boolean isLowerBoundIncluded() { + return isLowerBoundIncluded; + } + + @Override + public final double getUpperBound() { + return upperBound; + } + + @Override + public final boolean isUpperBoundIncluded() { + return isUpperBoundIncluded; + } + + @Override + public final Graduations getParentGraduations() { + return parentGraduations; + } + + @Override + public final boolean contain(double value) { + if (value == lowerBound) { + return isLowerBoundIncluded; + } + if (value == upperBound) { + return isUpperBoundIncluded; + } + return (lowerBound < value) && (value < upperBound); + } + + /** + * Equivalent to contain but for interval [0, upper-lower] (to avoid rounding error in computations) + */ + public final boolean containRelative(double value) { + if (value == 0 || Math.abs(value / (upperBound - lowerBound)) <= PRECISION) { + return isLowerBoundIncluded; + } + if (value == upperBound - lowerBound || Math.abs(1 - value / (upperBound - lowerBound)) <= PRECISION) { + return isUpperBoundIncluded; + } + return (0 < value) && (value < upperBound - lowerBound); + } + + @Override + public final DecimalFormat getFormat() { + if (numberFormat == null) { + double maxDisplayedValue = Math.max(Math.abs(lowerBound), Math.abs(upperBound)); + double len = Math.abs(upperBound - lowerBound); + + if (maxDisplayedValue < 1e-3) { + numberFormat = new DecimalFormat("0.##########E00"); + } else if (false && len <= 1e-3) { + // desactivated for now... + // the user should be able to do that itself + numberFormat = new TinyIntervalFormat("0.####E00", "0.##E00"); + } else if (maxDisplayedValue >= 1e6) { + numberFormat = new DecimalFormat("0.##########E00"); + } else if (maxDisplayedValue < 1) { + numberFormat = new DecimalFormat("0.######"); + } else { + numberFormat = new DecimalFormat("#,##0.####"); + } + + DecimalFormatSymbols decimalFormatSymbols = numberFormat.getDecimalFormatSymbols(); + decimalFormatSymbols.setExponentSeparator("e"); + numberFormat.setDecimalFormatSymbols(decimalFormatSymbols); + } + return numberFormat; + } + + @Override + public List<Double> getSubGraduations(final int N) { + if (subValues == null) { + List<Double> ticksValue = getAllValues(); + if (N == 0 || ticksValue.size() == 0) { + subValues = new LinkedList<Double>(); + } else { + Collections.sort(ticksValue); + subValues = new LinkedList<Double>(); + + for (int i = 0; i < ticksValue.size() - 1; i++) { + final double first = ticksValue.get(i); + final double second = ticksValue.get(i + 1); + final double step = (second - first) / (N + 1); + double v = first; + for (int j = 0; j <= N; j++) { + subValues.add(v); + v += step; + } + } + subValues.add(ticksValue.get(ticksValue.size() - 1)); + } + } + + return subValues; + } + + @Override + public String toString() { + String lowerBoundBracket; + String upperBoundBracket; + + if (isLowerBoundIncluded) { + lowerBoundBracket = LEFT_BRACKET; + } else { + lowerBoundBracket = RIGHT_BRACKET; + } + + if (isUpperBoundIncluded) { + upperBoundBracket = RIGHT_BRACKET; + } else { + upperBoundBracket = LEFT_BRACKET; + } + return getClass().getSimpleName() + lowerBoundBracket + + getFormat().format(lowerBound) + ", " + + getFormat().format(upperBound) + upperBoundBracket; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/Graduations.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/Graduations.java new file mode 100755 index 000000000..a8aa09bf9 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/Graduations.java @@ -0,0 +1,108 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * Copyright (C) 2013 - Scilab Enterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.ruler.graduations; + +import java.text.DecimalFormat; +import java.util.List; + +/** + * @author Pierre Lando + */ +public interface Graduations { + + /** + * Return the lower bound. + * @return the lower bound. + */ + double getLowerBound(); + + /** + * Return true if the lower bound is included. + * @return true if the lower bound is included. + */ + boolean isLowerBoundIncluded(); + + /** + * Return the upper bound. + * @return the upper bound. + */ + double getUpperBound(); + + /** + * Return true if the lower bound is included. + * @return true if the lower bound is included. + */ + boolean isUpperBoundIncluded(); + + /** + * Return true if the interval contain the given value. + * @param value the given value. + * @return true if the interval contain the given value. + */ + boolean contain(double value); + + /** + * Return an adapted number format. + * @return an adapted number format. + */ + DecimalFormat getFormat(); + + /** + * Return all values of this graduation. + * @return all values of this graduation. + */ + List<Double> getAllValues(); + + /** + * Return values not present in parents graduations. + * @return values not present in parents graduations. + */ + List<Double> getNewValues(); + + /** + * Return the parent graduation. + * @return the parent graduation. + */ + Graduations getParentGraduations(); + + /** + * Return a child graduation with more values. + * @return a child graduation with more values. + */ + Graduations getMore(); + + /** + * Return a child graduation with more values but less than <code>getMore()</code> + * @return a child graduation with more values but less than <code>getMore()</code> + */ + Graduations getAlternative(); + + /** + * Return a child graduation for sub ticks. + * @return a child graduation for sub ticks. + */ + Graduations getSubGraduations(); + + /** + * Get the list of subgraduations corresponding to N subticks between two main consecutives ticks + * @param N the number of graduations + * @return the corresponding list + */ + List<Double> getSubGraduations(int N); + + /** + * Return the density of sub ticks. + * @return the density of sub ticks. + */ + int getSubDensity(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/LinearGraduations.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/LinearGraduations.java new file mode 100755 index 000000000..418ee172a --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/LinearGraduations.java @@ -0,0 +1,294 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * Copyright (C) 2013 - Scilab Enterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.ruler.graduations; + +import java.util.LinkedList; +import java.util.List; + +/** + * A linear graduation is a graduation with regular spaces mark. + * The mark distance is called "Step" and have for value {@code mantissa x 10^exponent}. + * Where mantissa is 1, 2 or 5. + * Exponent is an integer. + * + * @author Pierre Lando + */ +public final class LinearGraduations extends AbstractGraduations implements Graduations { + + /** + * The step exponent. + */ + protected final int stepExponent; + + /** + * The step mantissa. + */ + protected final int stepMantissa; + + private LinearGraduations moreGraduation; + private LinearGraduations alternativeGraduation; + private Graduations subGraduation; + private Double stepValue; + private List<Double> allValues; + private List<Double> newValues; + + /** + * Private constructor. + * Use creates methods. + */ + private LinearGraduations() { + super(null); + stepExponent = 0; + stepMantissa = 0; + } + + private LinearGraduations(Graduations parentGraduations, int stepExponent, int stepMantissa) { + super(parentGraduations); + this.stepExponent = stepExponent; + this.stepMantissa = stepMantissa; + } + + private LinearGraduations(Graduations parentGraduations, double lowerBound, boolean lowerBoundIncluded, double upperBound, boolean upperBoundIncluded) { + super(parentGraduations, lowerBound, lowerBoundIncluded, upperBound, upperBoundIncluded); + if (lowerBound != upperBound) { + double size = upperBound - lowerBound; + stepExponent = (int) Math.ceil(Math.log10(size)); + stepMantissa = 1; + } else { + stepExponent = 0; + stepMantissa = 0; + newValues = new LinkedList<Double>(); + allValues = new LinkedList<Double>(); + allValues.add(lowerBound); + } + } + + public static LinearGraduations create(double lowerBound, double upperBound) { + return create(lowerBound, true, upperBound, true); + } + + public static LinearGraduations create( + Graduations parentGraduations, + double lowerBound, boolean lowerBoundIncluded, + double upperBound, boolean upperBoundIncluded + ) { + if (lowerBound < upperBound) { + return new LinearGraduations(parentGraduations, lowerBound, lowerBoundIncluded, upperBound, upperBoundIncluded); + } else { + return new LinearGraduations(parentGraduations, upperBound, upperBoundIncluded, lowerBound, lowerBoundIncluded); + } + } + + public static LinearGraduations create(double lowerBound, boolean lowerBoundIncluded, double upperBound, boolean upperBoundIncluded) { + if (lowerBound < upperBound) { + return new LinearGraduations(null, lowerBound, lowerBoundIncluded, upperBound, upperBoundIncluded); + } else { + return new LinearGraduations(null, upperBound, upperBoundIncluded, lowerBound, lowerBoundIncluded); + } + } + + + private static final double mypow10(int e) { + double p = 10; + double r = 1; + final boolean signed = e < 0; + if (signed) { + e = -e; + } + while (e != 0) { + if ((e & 1) != 0) { + r *= p; + } + p *= p; + e >>= 1; + } + + return signed ? 1 / r : r; + } + + private static final long myceil(double x) { + if (x == 0) { + return 0L; + } + + double r = Math.round(x); + if (Math.abs(1 - r / x) <= PRECISION) { + return (long) r; + } + + return (long) Math.ceil(x); + } + + private Double getStepValue() { + if (stepValue == null) { + if (stepMantissa == 1) { + stepValue = mypow10(stepExponent); + } else { + stepValue = stepMantissa * mypow10(stepExponent); + } + } + return stepValue; + } + + private final long getIndex(double x) { + switch (stepMantissa) { + case 1: + return myceil(mypow10(-stepExponent) * x); + case 2: + return myceil(5 * mypow10(-stepExponent - 1) * x); + case 5: + return myceil(mypow10(-stepExponent - 1) * x * 2); + default: + return myceil(mypow10(-stepExponent) * x / stepMantissa); + } + } + + private boolean isNewIndex(final long index) { + /* We are now searching for value look like + * index * (stepMantissa * 10^n) and we don't want (previousStrepMantissa * 10^k) value. + */ + + switch (stepMantissa) { + case 1: + // (5 * index * stepMantissa) % 10 != 0 + return (index % 2) != 0; + case 2: + // (2 * index * stepMantissa) % 10 != 0 + return (index % 5) != 0; + default: + // (5 * index * stepMantissa) % 10 != 0 + return ((index * stepMantissa) % 2) != 0; + } + } + + @Override + public List<Double> getNewValues() { + if (getParentGraduations() == null) { + return getAllValues(); + } + + if (newValues == null) { + newValues = new LinkedList<Double>(); + final double lb = getLowerBound(); + + long currentIndex = getIndex(lb); + double currentValue = getStepValue() * currentIndex; + double value = currentValue - lb; + + if (value == 0 && !containRelative(value)) { + value += getStepValue(); + currentIndex++; + } + + while (containRelative(value) && !Double.isInfinite(lb + value)) { + if (isNewIndex(currentIndex)) { + newValues.add(lb + value); + } + value += getStepValue(); + currentIndex++; + } + } + + return newValues; + } + + @Override + public List<Double> getAllValues() { + if (allValues == null) { + final double lb = getLowerBound(); + allValues = new LinkedList<Double>(); + double currentValue = getStepValue() * getIndex(lb); + double value = currentValue - lb; + + if (value == 0 && !containRelative(value)) { + value += getStepValue(); + } + + while (containRelative(value) && !Double.isInfinite(lb + value)) { + allValues.add(lb + value); + value += getStepValue(); + } + } + return allValues; + } + + @Override + public LinearGraduations getMore() { + if (stepMantissa != 5) { + if (moreGraduation == null) { + if (stepMantissa == 1) { + moreGraduation = new LinearGraduations(this, stepExponent - 1, 2); + } else { + moreGraduation = new LinearGraduations(this, stepExponent, 1); + } + } + return moreGraduation; + } else { + return null; + } + } + + @Override + public LinearGraduations getAlternative() { + if (stepMantissa == 2) { + if (alternativeGraduation == null) { + if (getParentGraduations() == null) { + alternativeGraduation = new LinearGraduations(null, getLowerBound(), true, getLowerBound(), true); + } else { + alternativeGraduation = new LinearGraduations(getParentGraduations(), stepExponent, 5); + } + } + return alternativeGraduation; + } else { + return null; + } + } + + @Override + public Graduations getSubGraduations() { + if (subGraduation == null) { + switch (stepMantissa) { + case 1: + subGraduation = new LinearGraduations(this, stepExponent - 1, 5); + break; + case 2: + subGraduation = new LinearGraduations(this, stepExponent, 1); + break; + case 5: + subGraduation = new LinearGraduations(getParentGraduations(), stepExponent, 1); + break; + default: + subGraduation = null; + break; + } + } + return subGraduation; + } + + @Override + public int getSubDensity() { + if (stepMantissa == 5) { + return 5; + } else { + return 2; + } + } + + @Override + public String toString() { + String s = super.toString(); + s += "; stepMantissa=" + stepMantissa + "; stepExponent=" + stepExponent + "; parent=" + getParentGraduations(); + + return s; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/LogarithmicGraduations.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/LogarithmicGraduations.java new file mode 100755 index 000000000..63e162f16 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/LogarithmicGraduations.java @@ -0,0 +1,348 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * Copyright (C) 2013-2015 - Scilab Enterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.ruler.graduations; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Pierre Lando + */ +public final class LogarithmicGraduations extends AbstractGraduations implements Graduations { + + /** + * Exponent of the step. + * Possible value are : 1, 2 and 3*k with k > 0 + */ + private final int stepExponent; + + private List<Double> allValues; + private Graduations subGraduation; + private Graduations moreGraduation; + private Graduations alternativeGraduation; + + /** + * Private child constructor. + * The interval is copied from parent's one. + * @param parentGraduations the parent graduation. + * @param stepExponent the step exponent. + */ + private LogarithmicGraduations(Graduations parentGraduations, int stepExponent) { + super(parentGraduations); + this.stepExponent = stepExponent; + } + + /** + * Private root graduation constructor. + * This graduation has no parent. + * @param lowerBound actual lower bound. + * @param lowerBoundIncluded true if the lower bound is included in the interval. + * @param upperBound actual upper bound. + * @param upperBoundIncluded true if the upper bound is included in the interval. + */ + private LogarithmicGraduations(double lowerBound, boolean lowerBoundIncluded, double upperBound, boolean upperBoundIncluded) { + super(lowerBound, lowerBoundIncluded, upperBound, upperBoundIncluded); + if (lowerBound != upperBound) { + stepExponent = 1; + } else { + stepExponent = 0; + allValues = new LinkedList<Double>(); + allValues.add(lowerBound); + } + } + + public static LogarithmicGraduations create(double lowerBound, double upperBound) { + return create(lowerBound, true, upperBound, true); + } + + public static LogarithmicGraduations create(double lowerBound, boolean lowerBoundIncluded, double upperBound, boolean upperBoundIncluded) { + if (lowerBound < upperBound) { + return new LogarithmicGraduations(lowerBound, lowerBoundIncluded, upperBound, upperBoundIncluded); + } else { + return new LogarithmicGraduations(upperBound, upperBoundIncluded, lowerBound, lowerBoundIncluded); + } + } + + @Override + public List<Double> getAllValues() { + if (allValues == null) { + allValues = new LinkedList<Double>(); + int currentExponent = (int) Math.ceil(Math.log10(getLowerBound())); + double currentValue = Math.pow(10, currentExponent); + final double step = Math.pow(10, stepExponent); + + if ((currentValue == getLowerBound()) && (!isLowerBoundIncluded())) { + currentValue *= step; + } + + while (contain(currentValue) && !Double.isInfinite(currentValue)) { + allValues.add(currentValue); + currentValue *= step; + } + } + return allValues; + } + + @Override + public List<Double> getNewValues() { + return getAllValues(); + } + + @Override + public Graduations getMore() { + if (moreGraduation == null) { + moreGraduation = new LinLogGraduation(this); + } + return moreGraduation; + } + + @Override + public Graduations getAlternative() { + if (alternativeGraduation == null) { + int nextStep = 3 + stepExponent - stepExponent % 3; + alternativeGraduation = new LogarithmicGraduations(this, nextStep); + } + return alternativeGraduation; + } + + @Override + public Graduations getSubGraduations() { + if (subGraduation == null) { + if (stepExponent > 1) { + subGraduation = new LogarithmicGraduations(this, stepExponent / 3); + } else { + subGraduation = new LinLogGraduation(this).getSubGraduations(); + } + } + return subGraduation; + } + + @Override + public List<Double> getSubGraduations(final int N) { + if (subValues == null) { + List<Double> ticksValue = getAllValues(); + if (N == 0 || ticksValue.size() == 0) { + subValues = new LinkedList<Double>(); + } else { + Collections.sort(ticksValue); + subValues = new LinkedList<Double>(); + + for (int i = 0; i < ticksValue.size() - 1; i++) { + final double first = Math.log10(ticksValue.get(i)); + final double second = Math.log10(ticksValue.get(i + 1)); + final double step = (second - first) / (N + 1); + double v = first; + for (int j = 0; j <= N; j++) { + subValues.add(Math.pow(10, v)); + v += step; + } + } + subValues.add(ticksValue.get(ticksValue.size() - 1)); + } + } + + return subValues; + } + + @Override + public int getSubDensity() { + if (stepExponent >= 3) { + return 3; + } else if (stepExponent == 2) { + return stepExponent; + } else { + return getSubGraduations().getSubDensity(); + } + } + + @Override + public String toString() { + String s = super.toString(); + s += "; stepExponent=" + stepExponent + "; parent=" + getParentGraduations(); + + return s; + } + + /** + * This class manage linear graduation between 10^n and 10^(n+1) + */ + private class LinLogGraduation extends AbstractGraduations implements Graduations { + private Graduations alternativeLLGraduation; + private Graduations moreLLGraduation; + private Graduations subLLGraduation; + + private List<Double> allValues; + private List<Double> newValues; + + private final List<Graduations> graduationsList; + + public LinLogGraduation(LogarithmicGraduations parentGraduations) { + super(parentGraduations); + graduationsList = computeGraduationsList(); + } + + private LinLogGraduation(Graduations parentGraduations, List<Graduations> graduationsList) { + super(parentGraduations); + this.graduationsList = graduationsList; + } + + @Override + public List<Double> getAllValues() { + if (allValues == null) { + allValues = new LinkedList<Double>(); + for (Graduations graduations : graduationsList) { + allValues.addAll(graduations.getAllValues()); + } + + allValues.addAll(getLogarithmicParent().getAllValues()); + } + return allValues; + } + + @Override + public List<Double> getNewValues() { + if (newValues == null) { + newValues = new LinkedList<Double>(); + if (getParentGraduations() instanceof LogarithmicGraduations) { + for (Graduations graduations : graduationsList) { + newValues.addAll(graduations.getAllValues()); + } + } else { + for (Graduations graduations : graduationsList) { + newValues.addAll(graduations.getNewValues()); + } + } + } + return newValues; + } + + @Override + public Graduations getMore() { + if (moreLLGraduation == null) { + List<Graduations> moreList = new LinkedList<Graduations>(); + for (Graduations graduations : graduationsList) { + Graduations more = graduations.getMore(); + if (more != null) { + moreList.add(more); + } + } + if (!moreList.isEmpty()) { + moreLLGraduation = new LinLogGraduation(this, moreList); + } + } + return moreLLGraduation; + } + + @Override + public Graduations getAlternative() { + if (alternativeLLGraduation == null) { + List<Graduations> alternativeList = new LinkedList<Graduations>(); + for (Graduations graduations : graduationsList) { + Graduations alternative = graduations.getAlternative(); + if (alternative != null) { + alternativeList.add(alternative); + } + } + if (!alternativeList.isEmpty()) { + alternativeLLGraduation = new LinLogGraduation(this, alternativeList); + } + } + return alternativeLLGraduation; + } + + @Override + public Graduations getSubGraduations() { + if (subLLGraduation == null) { + List<Graduations> subList = new LinkedList<Graduations>(); + for (Graduations graduations : graduationsList) { + Graduations sub = graduations.getSubGraduations(); + if (sub != null) { + subList.add(sub); + } + } + if (subList.isEmpty()) { + subLLGraduation = getMore().getSubGraduations(); + } else { + subLLGraduation = new LinLogGraduation(this, subList); + } + } + return subLLGraduation; + } + + @Override + public int getSubDensity() { + return 0; + } + + private List<Graduations> computeGraduationsList() { + List<Graduations> list = new LinkedList<Graduations>(); + + /** + * Let a and b a power of 10. + * lowerBound < a < b < upperBound + */ + + double aPower = Math.ceil(Math.log10(getLowerBound())); + double bPower = Math.floor(Math.log10(getUpperBound())); + double a = Math.pow(10, aPower); + double b = Math.pow(10, bPower); + + if (aPower > bPower) { + // Case of 10^n <= a < b <= 10^(n+1) + list.add(LinearGraduations.create( + this, + getLowerBound(), true, + getUpperBound(), true + )); + } else { + if (a != getLowerBound()) { + list.add(LinearGraduations.create( + this, + getLowerBound(), true, + a, false + )); + } + + if (aPower != bPower) { + // Limit iterations on power smaller than 10^310 (Max double ~ 10^308) + for (double i = aPower; i < Math.min(bPower, 310); i++) { + list.add(LinearGraduations.create( + this, + Math.pow(10, i), false, + Math.pow(10, i + 1), false + )); + } + } + + if (b != getUpperBound()) { + list.add(LinearGraduations.create( + this, + b, false, + getUpperBound(), true + )); + } + } + + return list; + } + + private Graduations getLogarithmicParent() { + Graduations currentGraduation = getParentGraduations(); + while (!(currentGraduation instanceof LogarithmicGraduations)) { + currentGraduation = currentGraduation.getParentGraduations(); + } + return currentGraduation; + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/TinyIntervalFormat.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/TinyIntervalFormat.java new file mode 100755 index 000000000..efe7dd6f8 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/TinyIntervalFormat.java @@ -0,0 +1,95 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2013 - Scilab Enterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.ruler.graduations; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.FieldPosition; + +/** + * TinyIntervalFormat allows to format 3.0001 into "3+1e-4" or 2.9999 in "3-1e-4" + * It is useful to represent values in a tiny intervall to avoid to have the same representation + * for different values. + * For a number 3.0001, 3 is called the base and 1e-4 the fractional part. + */ +public class TinyIntervalFormat extends DecimalFormat { + + private static final DecimalFormat simpleFormat = new DecimalFormat("0.######"); + + private DecimalFormat fracFormat; + + /** + * Constructor + * @param basePattern the pattern to represent the base + * @param fracPattern the pattern to represent frac + */ + public TinyIntervalFormat(String basePattern, String fracPattern) { + super(basePattern); + fracFormat = new DecimalFormat(fracPattern); + } + + /** + * {@inheritDoc} + */ + public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) { + double[] parts = getParts(number); + + if (parts[1] == 0) { + return super.format(number, result, fieldPosition); + } + + if (parts[0] != 0) { + if (parts[0] < 10) { + result = simpleFormat.format(parts[0], result, fieldPosition); + } else { + result = super.format(parts[0], result, fieldPosition); + } + + if (parts[1] > 0) { + result.append("+"); + } else if (parts[1] < 0) { + result.append("-"); + } + + return fracFormat.format(Math.abs(parts[1]), result, fieldPosition); + } + + return fracFormat.format(parts[1], result, fieldPosition); + } + + /** + * {@inheritDoc} + */ + public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) { + super.setDecimalFormatSymbols(newSymbols); + fracFormat.setDecimalFormatSymbols(newSymbols); + simpleFormat.setDecimalFormatSymbols(newSymbols); + } + + /** + * Get the base and the frac part of the number x + * @param x the value + * @return the parts + */ + private static final double[] getParts(final double x) { + double p = 1; + double y = x; + double f = y - Math.round(y); + while (Math.abs(f) > 1e-2) { + y *= 10; + p *= 10; + f = y - Math.round(y); + } + + return new double[] {Math.round(y) / p, f / p}; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/UserDefinedFormat.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/UserDefinedFormat.java new file mode 100755 index 000000000..6a63ab853 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/ruler/graduations/UserDefinedFormat.java @@ -0,0 +1,96 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2014 - Scilab Enterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.ruler.graduations; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.FieldPosition; +import java.util.Formatter; +import java.util.IllegalFormatConversionException; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Use a user defined format to format ticks label. + */ +public class UserDefinedFormat extends DecimalFormat { + + private String format; + private double scale; + private double translation; + private DecimalFormat fallback; + + /** + * Constructor + * @param basePattern the pattern to represent the base + * @param fracPattern the pattern to represent frac + */ + public UserDefinedFormat(DecimalFormat fallback, String format, double scale, double translation) { + super(); + this.format = format; + this.scale = scale; + this.translation = translation; + this.fallback = fallback; + } + + public String getFormat() { + return format; + } + + /** + * {@inheritDoc} + */ + public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) { + final double d = scale * (number - translation); + if (format != null && !format.isEmpty()) { + try { + Formatter fmt = new Formatter(Locale.US); + fmt.format(Locale.US, format, d); + return result.append(fmt.toString()); + } catch (IllegalFormatConversionException e) { + try { + Formatter fmt = new Formatter(Locale.US); + fmt.format(Locale.US, format, (long) d); + return result.append(fmt.toString()); + } catch (Exception ee) { } + } catch (ArrayIndexOutOfBoundsException e) { + // Java bug in Formatter format("%.1g", 0.) or something like that is faulty + // So what is following is just a crappy workaround to replace g by f... + if (d == 0) { + Pattern pat = Pattern.compile("([^%]*%[-#+ 0,(]?\\.[01])[gG](.*)"); + Matcher match = pat.matcher(format); + if (match.find() && match.groupCount() == 2) { + StringBuilder buffer = new StringBuilder(format.length()); + buffer.append(format.substring(0, match.start())).append(match.group(1)).append('f').append(match.group(2)); + format = buffer.toString(); + + try { + Formatter fmt = new Formatter(Locale.US); + fmt.format(Locale.US, format, d); + return result.append(fmt.toString()); + } catch (Exception ee) { } + } + } + } catch (Exception e) { } + } + + return fallback.format(number, result, fieldPosition); + } + + /** + * {@inheritDoc} + */ + public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) { + fallback.setDecimalFormatSymbols(newSymbols); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/shapes/appearance/Appearance.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/shapes/appearance/Appearance.java new file mode 100755 index 000000000..7b7a51cfc --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/shapes/appearance/Appearance.java @@ -0,0 +1,190 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.shapes.appearance; + +import org.scilab.forge.scirenderer.texture.Texture; + +/** + * + * Basic class for appearance parameters. + * + * @author Pierre Lando + */ +public final class Appearance { + + /** + * The default line width. + */ + public static final float DEFAULT_LINE_WIDTH = 1.0f; + + /** + * The default line pattern (full line). + * The 16 bits of the pattern represents how lines are drawn. + */ + public static final short DEFAULT_LINE_PATTERN = (short) 0xFFFF; + + /** + * The default line color. + */ + public static final Color DEFAULT_LINE_COLOR = new Color(.8f, .8f, .8f); + + /** + * The default fill color. + */ + public static final Color DEFAULT_FILL_COLOR = new Color(.8f, .8f, .8f); + + /** + * Current {@link Texture} + */ + private Texture texture; + + /** + * The current line width. + */ + private float lineWidth = DEFAULT_LINE_WIDTH; + + /** + * The current line pattern. + * The 16 bits of the pattern represents how lines are drawn. + */ + private short linePattern = DEFAULT_LINE_PATTERN; + + /** + * the current line color. + */ + private Color lineColor = DEFAULT_LINE_COLOR; + + /** + * The current fill color. + */ + private Color fillColor = DEFAULT_FILL_COLOR; + + /** + * The material used for lighting. + */ + private Material material; + + /** + * Default constructor. + */ + public Appearance() { + } + + /** + * Return the default appearance. + * @return the default appearance. + */ + public static Appearance getDefault() { + return new Appearance(); + } + + /** + * Texture getter. + * @return the current texture if any. + */ + public Texture getTexture() { + if ((texture != null) && (texture.isValid())) { + return texture; + } else { + return null; + } + } + + /** + * Texture setter. + * @param texture the new texture. + */ + public void setTexture(Texture texture) { + this.texture = texture; + } + + /** + * Return the line width. + * @return the line width. + */ + public float getLineWidth() { + return lineWidth; + } + + /** + * Set the line width. + * @param lineWidth the new line width. + */ + public void setLineWidth(float lineWidth) { + this.lineWidth = lineWidth; + } + + /** + * Return the line pattern. + * @return the line pattern. + */ + public short getLinePattern() { + return linePattern; + } + + /** + * Set the line pattern. + * @param linePattern the new line pattern. + */ + public void setLinePattern(short linePattern) { + this.linePattern = linePattern; + } + + /** + * Return the line color. + * @return the line color. + */ + public Color getLineColor() { + return lineColor; + } + + /** + * Set the line color. + * @param lineColor the new lne color. + */ + public void setLineColor(Color lineColor) { + this.lineColor = lineColor; + } + + /** + * Return the fill color. + * @return the fill color. + */ + public Color getFillColor() { + return fillColor; + } + + /** + * Set the fill color. + * @param fillColor the new fill color. + */ + public void setFillColor(Color fillColor) { + this.fillColor = fillColor; + } + + /** + * Get the material. + * @return the material. + */ + public Material getMaterial() { + return material; + } + + /** + * Set the material. + * @param the new material. + */ + public void setMaterial(Material m) { + material = m; + } +} + diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/shapes/appearance/Color.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/shapes/appearance/Color.java new file mode 100755 index 000000000..52bae0f9d --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/shapes/appearance/Color.java @@ -0,0 +1,104 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.shapes.appearance; + +/** + * @author Pierre Lando + */ +@SuppressWarnings(value = { "serial" }) +public final class Color extends java.awt.Color { + + private static final float COMPONENT_MAX_VALUE = 255f; + + /** + * The default color. + */ + private static final Color DEFAULT_COLOR = new Color(.2f, .3f, .4f); + + /** + * Default constructor. + * Create a copy of the default color. + */ + public Color() { + this(DEFAULT_COLOR); + } + + /** + * Copy constructor + * @param c the color to copy. + */ + public Color(Color c) { + super(c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha()); + } + + /** + * Creates an sRGB color with the specified red, green, blue, and + * alpha values in the range [0.0; 1.0]. The actual color + * used in rendering depends on finding the best match given the + * color space available for a particular output device. + * + * @param r the red component + * @param g the green component + * @param b the blue component + * @param a the alpha component + */ + public Color(float r, float g, float b, float a) { + super(r, g, b, a); + } + + /** + * Creates an opaque sRGB color with the specified red, green, and blue + * values in the range [0.0; 1.0]. Alpha is defaulted to 1.0. The + * actual color used in rendering depends on finding the best + * match given the color space available for a particular output + * device. + * + * @param r the red component + * @param g the green component + * @param b the blue component + */ + public Color(float r, float g, float b) { + super(r, g, b); + } + + /** + * Return red component value. In the range [0; 1]. + * @return red component value. In the range [0; 1]. + */ + public float getRedAsFloat() { + return ((float) getRed()) / COMPONENT_MAX_VALUE; + } + + /** + * Return green component value. In the range [0; 1]. + * @return green component value. In the range [0; 1]. + */ + public float getGreenAsFloat() { + return ((float) getGreen()) / COMPONENT_MAX_VALUE; + } + + /** + * Return blue component value. In the range [0; 1]. + * @return blue component value. In the range [0; 1]. + */ + public float getBlueAsFloat() { + return ((float) getBlue()) / COMPONENT_MAX_VALUE; + } + + /** + * Return alpha component value. In the range [0; 1]. + * @return alpha component value. In the range [0; 1]. + */ + public float getAlphaAsFloat() { + return ((float) getAlpha()) / COMPONENT_MAX_VALUE; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/shapes/appearance/Material.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/shapes/appearance/Material.java new file mode 100755 index 000000000..28e516885 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/shapes/appearance/Material.java @@ -0,0 +1,100 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2013 - Pedro SOUZA + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.shapes.appearance; + +/** + * Material class used for lighting + * @author Pedro SOUZA + */ +public class Material { + + /** enable use color as material */ + private boolean colorMaterial = true; + /** ambient color */ + private Color ambient; + /** diffuse color*/ + private Color diffuse; + /** specular color */ + private Color specular; + /** shininess level */ + private float shininess; + + /** + * @param the new ambient color;. + */ + public void setAmbientColor(Color color) { + ambient = color; + } + + /** + * @param the new diffuse color. + */ + public void setDiffuseColor(Color color) { + diffuse = color; + } + + /** + * @param the new specular color. + */ + public void setSpecularColor(Color color) { + specular = color; + } + + /** + * @param the new shinines. + */ + public void setShininess(float s) { + shininess = s; + } + + /** + * @return the ambient color. + */ + public Color getAmbientColor() { + return ambient; + } + + /** + * @return the diffuse color. + */ + public Color getDiffuseColor() { + return diffuse; + } + + /** + * @return the specular color. + */ + public Color getSpecularColor() { + return specular; + } + + /** + * @return the shininess. + */ + public float getShininess() { + return shininess; + } + + /** + * @return the color material status. + */ + public boolean isColorMaterialEnable() { + return colorMaterial; + } + + /** + * @param the new color material status. + */ + public void setColorMaterialEnable(boolean status) { + colorMaterial = status; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/shapes/geometry/DefaultGeometry.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/shapes/geometry/DefaultGeometry.java new file mode 100755 index 000000000..8e2865457 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/shapes/geometry/DefaultGeometry.java @@ -0,0 +1,175 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.shapes.geometry; + +import org.scilab.forge.scirenderer.buffers.ElementsBuffer; +import org.scilab.forge.scirenderer.buffers.IndicesBuffer; + +/** + * + * Default implementation of a Geometry. + * + * @author Pierre Lando + */ +public class DefaultGeometry implements Geometry { + + private FaceCullingMode faceCullingMode = DEFAULT_FACE_CULLING_MODE; + private FillDrawingMode fillDrawingMode = DEFAULT_FILL_DRAWING_MODE; + private LineDrawingMode lineDrawingMode = DEFAULT_LINE_DRAWING_MODE; + + /** Specifies whether polygon offset is used or not when rendering geometry. */ + private boolean polygonOffsetMode = DEFAULT_POLYGON_OFFSET_MODE; + + private ElementsBuffer textureCoordinates; + private ElementsBuffer vertices; + private ElementsBuffer normals; + private ElementsBuffer colors; + + private IndicesBuffer wireIndices; + private IndicesBuffer indices; + + /** + * Default constructor. + */ + public DefaultGeometry() { + } + + @Override + public final FaceCullingMode getFaceCullingMode() { + return faceCullingMode; + } + + @Override + public final FillDrawingMode getFillDrawingMode() { + return fillDrawingMode; + } + + @Override + public final LineDrawingMode getLineDrawingMode() { + return lineDrawingMode; + } + + @Override + public final boolean getPolygonOffsetMode() { + return polygonOffsetMode; + } + + @Override + public final ElementsBuffer getVertices() { + return vertices; + } + + @Override + public final IndicesBuffer getIndices() { + return indices; + } + + @Override + public final IndicesBuffer getWireIndices() { + return wireIndices; + } + + @Override + public final ElementsBuffer getColors() { + return colors; + } + + @Override + public final ElementsBuffer getTextureCoordinates() { + return textureCoordinates; + } + + @Override + public final ElementsBuffer getNormals() { + return normals; + } + + /** + * Face culling mode setter. + * @param faceCullingMode the new face culling mode. + */ + public final void setFaceCullingMode(FaceCullingMode faceCullingMode) { + this.faceCullingMode = faceCullingMode; + } + + /** + * Fill drawing mode setter. + * @param fillDrawingMode the new fill drawing mode. + */ + public final void setFillDrawingMode(FillDrawingMode fillDrawingMode) { + this.fillDrawingMode = fillDrawingMode; + } + + /** + * Line drawing mode setter. + * @param lineDrawingMode the new line drawing mode. + */ + public final void setLineDrawingMode(LineDrawingMode lineDrawingMode) { + this.lineDrawingMode = lineDrawingMode; + } + + /** + * Polygon offset mode setter. + * @param polygonOffsetMode the new polygon offset mode. + */ + public final void setPolygonOffsetMode(boolean polygonOffsetMode) { + this.polygonOffsetMode = polygonOffsetMode; + } + + /** + * Texture coordinates setter. + * @param textureCoordinates the new texture coordinate data. + */ + public final void setTextureCoordinates(ElementsBuffer textureCoordinates) { + this.textureCoordinates = textureCoordinates; + } + + /** + * Vertices setter. + * @param vertices the new vertices data. + */ + public final void setVertices(ElementsBuffer vertices) { + this.vertices = vertices; + } + + /** + * Normals setter. + * @param normals the new normals data. + */ + public final void setNormals(ElementsBuffer normals) { + this.normals = normals; + } + + /** + * Colors setter. + * @param colors the new colors data. + */ + public final void setColors(ElementsBuffer colors) { + this.colors = colors; + } + + /** + * Wire indices setter. + * @param wireIndices the new wire indices data. + */ + public final void setWireIndices(IndicesBuffer wireIndices) { + this.wireIndices = wireIndices; + } + + /** + * Indices setter. + * @param indicesBuffer the new indices data. + */ + public final void setIndices(IndicesBuffer indicesBuffer) { + this.indices = indicesBuffer; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/shapes/geometry/Geometry.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/shapes/geometry/Geometry.java new file mode 100755 index 000000000..bb81c7a18 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/shapes/geometry/Geometry.java @@ -0,0 +1,176 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.shapes.geometry; + +import org.scilab.forge.scirenderer.buffers.ElementsBuffer; +import org.scilab.forge.scirenderer.buffers.IndicesBuffer; + +/** + * + * Interface for a geometry. + * + * @author Pierre Lando + */ +public interface Geometry { + + /** + * This enum specify face culling. + */ + enum FaceCullingMode { + /** + * Both faces are rendered (default value) + */ + BOTH, + + /** + * Only counter clockwise faces are visible. + */ + CCW, + + /** + * Only clockwise faces are visible. + */ + CW + } + + /** + * Default face culling mode. + */ + FaceCullingMode DEFAULT_FACE_CULLING_MODE = FaceCullingMode.BOTH; + + /** + * This enum specify how geometry is rendered. + */ + enum FillDrawingMode { + /** + * Treats each triplet of vertices as an independent triangle. + */ + TRIANGLES, + + /** + * Draws a connected group of triangles. + */ + TRIANGLE_STRIP, + + /** + * Draws a connected group of triangles with common first element. + */ + TRIANGLE_FAN, + + /** + * Nothing is filled. + */ + NONE + } + + /** + * Default fill drawing mode. + */ + FillDrawingMode DEFAULT_FILL_DRAWING_MODE = FillDrawingMode.TRIANGLES; + + /** + * Line drawing modes declaration. + */ + enum LineDrawingMode { + /** + * Each pair of vertices define an independent segment. + */ + SEGMENTS, + + /** + * Connected group of segments from the first vertex to the last. + */ + SEGMENTS_STRIP, + + /** + * Connected group of segments from the first vertex to the last, then back to the first. + */ + SEGMENTS_LOOP, + + /** + * No segments. + */ + NONE + } + + /** + * Default wire drawing mode. + */ + LineDrawingMode DEFAULT_LINE_DRAWING_MODE = LineDrawingMode.NONE; + + /** + * Default polygon offset mode. + */ + boolean DEFAULT_POLYGON_OFFSET_MODE = false; + + /** + * Face-culling mode getter. + * @return the face culling mode. + */ + FaceCullingMode getFaceCullingMode(); + + /** + * Fill drawing mode getter. + * @return the fill-drawing mode for this object. + */ + FillDrawingMode getFillDrawingMode(); + + /** + * Line drawing mode getter. + * @return the line drawing mode. + */ + LineDrawingMode getLineDrawingMode(); + + /** + * Polygon offset mode getter. + * @return the polygon offset mode. + */ + boolean getPolygonOffsetMode(); + + /** + * Return the vertices. + * @return the vertices. + */ + ElementsBuffer getVertices(); + + /** + * Return the colors. + * @return the colors. + */ + ElementsBuffer getColors(); + + /** + * Texture coordinates getter. + * @return the texture coordinate. + */ + ElementsBuffer getTextureCoordinates(); + + /** + * Return the normals. + * @return the normals. + */ + ElementsBuffer getNormals(); + + /** + * Return the indices if any. + * If <code>null</code> is returned, indices should be treated as consecutive number. + * @return the indices. + */ + IndicesBuffer getIndices(); + + /** + * Return the wire indices. + * If <code>null</code> is returned, no edges are drawn. + * @return the edges indices. + */ + IndicesBuffer getWireIndices(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/AbstractTexture.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/AbstractTexture.java new file mode 100755 index 000000000..afa5506de --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/AbstractTexture.java @@ -0,0 +1,132 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.texture; + +/** + * @author Pierre Lando + */ +public class AbstractTexture implements Texture { + + /** + * Texture data provider. + */ + protected TextureDataProvider textureDataProvider; + + /** + * True if the data are up to date. + */ + protected boolean upToDate; + + /** + * Current magnification filtering method. + */ + private Filter magnificationFilter = Filter.NEAREST; + + /** + * Current minifying filtering method. + */ + private Filter minifyingFilter = Filter.NEAREST; + + private Wrap sWrappingMode = Wrap.CLAMP; + private Wrap tWrappingMode = Wrap.CLAMP; + + /** + * Default constructor. + */ + public AbstractTexture() { + upToDate = false; + } + + @Override + public synchronized boolean isValid() { + return (textureDataProvider != null) && (textureDataProvider.isValid()); + } + + @Override + public Wrap getSWrappingMode() { + return sWrappingMode; + } + + @Override + public void setSWrappingMode(Wrap sWrappingMode) { + this.sWrappingMode = sWrappingMode; + } + + @Override + public Wrap getTWrappingMode() { + return tWrappingMode; + } + + @Override + public void setTWrappingMode(Wrap tWrappingMode) { + this.tWrappingMode = tWrappingMode; + } + + @Override + public Filter getMinifyingFilter() { + return minifyingFilter; + } + + @Override + public void setMinifyingFilter(Filter minifyingFilter) { + this.minifyingFilter = minifyingFilter; + } + + @Override + public Filter getMagnificationFilter() { + return magnificationFilter; + } + + @Override + public void setMagnificationFilter(Filter magnificationFilter) { + this.magnificationFilter = magnificationFilter; + } + + @Override + public synchronized TextureDataProvider getDataProvider() { + return textureDataProvider; + } + + @Override + public synchronized void setDataProvider(TextureDataProvider provider) { + if (textureDataProvider != null) { + textureDataProvider.removeDataUser(this); + } + + textureDataProvider = provider; + upToDate = false; + + if (textureDataProvider != null) { + textureDataProvider.addDataUser(this); + } + } + + @Override + public void setDrawer(TextureDrawer textureDrawer) { + setDataProvider(new DrawnTextureDataProvider(textureDrawer)); + } + + @Override + public void dataUpdated() { + upToDate = false; + } + + @Override + public double getSScaleFactor() { + return 1; + } + + @Override + public double getTScaleFactor() { + return 1; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/AbstractTextureDataProvider.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/AbstractTextureDataProvider.java new file mode 100755 index 000000000..c37bacc4e --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/AbstractTextureDataProvider.java @@ -0,0 +1,71 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2012 - Scilab Enterprises - Calixte DENIZET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.texture; + +import java.awt.Dimension; +import java.awt.image.BufferedImage; +import java.nio.ByteBuffer; + +import org.scilab.forge.scirenderer.data.AbstractDataProvider; + +/** + * @author Calixte DENIZET + */ +public abstract class AbstractTextureDataProvider extends AbstractDataProvider<Texture> implements TextureDataProvider { + + protected ImageType imageType; + + @Override + public ImageType getImageType() { + return imageType; + } + + @Override + public boolean isRowMajorOrder() { + return true; + } + + @Override + public BufferedImage getImage() { + final ByteBuffer data = getData(); + if (data == null) { + return null; + } + + data.rewind(); + Dimension size = getTextureSize(); + final int width = size.width; + final int height = size.height; + + final int[] ibuffer = new int[data.capacity() / 4]; + final byte[] RGBA = new byte[4]; + for (int i = 0; i < ibuffer.length; i++) { + data.get(RGBA); + ibuffer[i] = ((RGBA[3] & 0xFF) << 24) + ((RGBA[0] & 0xFF) << 16) + ((RGBA[1] & 0xFF) << 8) + (RGBA[2] & 0xFF); + } + data.rewind(); + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + image.setRGB(0, 0, width, height, ibuffer, 0, width); + + return image; + } + + @Override + public BufferedImage getSubImage(int x, int y, int width, int height) { + BufferedImage image = getImage(); + if (image == null) { + return null; + } + + return image.getSubimage(x, y, width, height); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/AnchorPosition.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/AnchorPosition.java new file mode 100755 index 000000000..9e91e778d --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/AnchorPosition.java @@ -0,0 +1,63 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.texture; + +/** + * This is an enumeration of possible sprite anchor position. + * @author Pierre Lando + */ +public enum AnchorPosition { + /** + * Anchor is in the upper left sprite corner. + */ + UPPER_LEFT, + + /** + * Anchor is in the upper right sprite corner. + */ + UPPER_RIGHT, + + /** + * Anchor is in the lower left sprite corner. + */ + LOWER_LEFT, + + /** + * Anchor is in the lower right sprite corner. + */ + LOWER_RIGHT, + + /** + * Anchor is in the sprite center. + */ + CENTER, + + /** + * Anchor is in the center of the right edge of the sprite. + */ + RIGHT, + + /** + * Anchor is in the center of the left edge of the sprite. + */ + LEFT, + + /** + * Anchor is in the center of the down edge of the sprite. + */ + DOWN, + + /** + * Anchor is in the center of the upper edge of the sprite. + */ + UP, +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/BufferedImageTextureDrawingTools.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/BufferedImageTextureDrawingTools.java new file mode 100755 index 000000000..81eb74b99 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/BufferedImageTextureDrawingTools.java @@ -0,0 +1,202 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.texture; + +import org.scilab.forge.scirenderer.implementation.jogl.utils.G2DShortCuts; +import org.scilab.forge.scirenderer.shapes.appearance.Appearance; +import org.scilab.forge.scirenderer.shapes.appearance.Color; + +import javax.swing.Icon; +import javax.swing.JLabel; +import java.awt.AlphaComposite; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; + +/** + * + * Implementation of {@link org.scilab.forge.scirenderer.texture.TextureDrawingTools}. + * This implementation create a {@link TextureBufferedImage} an fill it with texture drawing. + * + * @author Pierre Lando + */ +public class BufferedImageTextureDrawingTools implements TextureDrawingTools { + + /** + * The {@link Graphics2D} used to fill the {@link TextureBufferedImage} + */ + private Graphics2D g2d; + private final TextureBufferedImage image; + private final Dimension textureSize; + private final AffineTransform baseTransform; + + /** + * Default constructor. + * @param textureSize the texture size. + */ + public BufferedImageTextureDrawingTools(Dimension textureSize) { + image = new TextureBufferedImage(textureSize.width, textureSize.height); + this.textureSize = textureSize; + + double deltaW = (image.getWidth() - textureSize.width) / 2.0; + double deltaH = (image.getHeight() - textureSize.height) / 2.0; + baseTransform = AffineTransform.getTranslateInstance(deltaW, deltaH); + } + + /** + * Ask this image to accept a texture drawer. + * This image will contain the drawing of the given drawer. + * @param textureDrawer the given texture drawer. + */ + public void accept(TextureDrawer textureDrawer) { + g2d = image.createGraphics(); + g2d.setComposite(AlphaComposite.Src); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + g2d.transform(baseTransform); + + // Change center coordinate to (0, 0). + if (textureDrawer.getOriginPosition() == TextureDrawer.OriginPosition.CENTER) { + g2d.translate(textureSize.width / 2, textureSize.height / 2); + } + + textureDrawer.draw(this); + + g2d.dispose(); + } + + @Override + public void drawPlus(int size, Appearance appearance) { + int r = size / 2; + int[] coords1 = new int[] { -r, 0, r, 0}; + drawPolyline(coords1, appearance); + if (r != 0) { + int[] coords2 = new int[] {0, -r, 0, r}; + drawPolyline(coords2, appearance); + } + } + + @Override + public void drawPolyline(int[] coordinates, Appearance appearance) { + int nbPoint = coordinates.length / 2; + + int[] xCoordinates = new int[nbPoint]; + int[] yCoordinates = new int[nbPoint]; + + int k = 0; + for (int i = 0; i < coordinates.length; i += 2) { + xCoordinates[k] = coordinates[i]; + yCoordinates[k] = coordinates[i + 1]; + k++; + } + + G2DShortCuts.useLineAppearance(g2d, appearance); + G2DShortCuts.useColor(g2d, appearance.getLineColor()); + g2d.drawPolyline(xCoordinates, yCoordinates, nbPoint); + + } + + @Override + public void fillPolygon(int[] coordinates, Appearance appearance) { + int nbPoint = coordinates.length / 2; + + int[] xCoordinates = new int[nbPoint]; + int[] yCoordinates = new int[nbPoint]; + + int k = 0; + for (int i = 0; i < coordinates.length; i += 2) { + xCoordinates[k] = coordinates[i]; + yCoordinates[k] = coordinates[i + 1]; + k++; + } + + if (appearance.getFillColor().getAlphaAsFloat() != 0) { + G2DShortCuts.useColor(g2d, appearance.getFillColor()); + g2d.fillPolygon(xCoordinates, yCoordinates, nbPoint); + } + + if (!appearance.getLineColor().equals(appearance.getFillColor())) { + int usedLength = coordinates.length - (coordinates.length % 2); + int[] borderCoordinate = new int[usedLength + 2]; + System.arraycopy(coordinates, 0, borderCoordinate, 0, usedLength); + borderCoordinate[usedLength] = coordinates[0]; + borderCoordinate[usedLength + 1] = coordinates[1]; + + drawPolyline(borderCoordinate, appearance); + } + } + + @Override + public void drawCircle(int x, int y, int diameter, Appearance appearance) { + G2DShortCuts.useLineAppearance(g2d, appearance); + G2DShortCuts.useColor(g2d, appearance.getLineColor()); + int r = diameter / 2; + g2d.drawOval(x - r, y - r, diameter, diameter); + } + + @Override + public void fillDisc(int x, int y, int diameter, Color color) { + if (color.getAlphaAsFloat() != 0) { + G2DShortCuts.useColor(g2d, color); + int r = diameter / 2; + g2d.fillOval(x - r, y - r, diameter, diameter); + } + } + + @Override + public void draw(TextEntity textEntity, int x, int y) { + if ((textEntity != null) && (textEntity.isValid())) { + if (textEntity.isTextAntiAliased()) { + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + } else { + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + } + + if (textEntity.isTextUseFractionalMetrics()) { + g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); + } else { + g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF); + } + g2d.setColor(textEntity.getTextColor()); + TextLayout textLayout = new TextLayout(textEntity.getText(), textEntity.getFont(), g2d.getFontRenderContext()); + Rectangle2D bounds = textLayout.getBounds(); + textLayout.draw(g2d, (float) (x + 1 - bounds.getX()), y + textLayout.getAscent()); + } + } + + @Override + public void draw(Icon icon, int x, int y) { + icon.paintIcon(new JLabel(), g2d, x, y); + } + + @Override + public void clear(Color color) { + AffineTransform oldTransform = g2d.getTransform(); + g2d.setTransform(baseTransform); + G2DShortCuts.useColor(g2d, color); + g2d.fillRect(0, 0, textureSize.width, textureSize.height); + g2d.setTransform(oldTransform); + } + + /** + * Return the image. + * @return the image. + */ + public TextureBufferedImage getImage() { + return image; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/DrawnTextureDataProvider.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/DrawnTextureDataProvider.java new file mode 100755 index 000000000..db228e88d --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/DrawnTextureDataProvider.java @@ -0,0 +1,134 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.texture; + +import org.scilab.forge.scirenderer.data.AbstractDataProvider; + +import java.awt.Dimension; +import java.awt.image.BufferedImage; +import java.nio.ByteBuffer; + +/** + * @author Pierre Lando + */ +public class DrawnTextureDataProvider extends AbstractDataProvider<Texture> implements TextureDataProvider { + + /** Texture drawer */ + private TextureDrawer textureDrawer; + protected ImageType imageType; + + /** Current image */ + BufferedImageTextureDrawingTools image; + + public DrawnTextureDataProvider(TextureDrawer textureDrawer) { + this.textureDrawer = textureDrawer; + imageType = ImageType.RGBA_BYTE; + //reDraw(); + } + + @Override + public ImageType getImageType() { + return imageType; + } + + @Override + public boolean isRowMajorOrder() { + return true; + } + + /** Reload the texture and recall the texture drawing tools. */ + public void reDraw() { + if (isValid()) { + image = new BufferedImageTextureDrawingTools(textureDrawer.getTextureSize()); + image.accept(textureDrawer); + } + } + + /** + * Texture drawer setter. + * @param textureDrawer the new texture drawer. + */ + public void setTextureDrawingTools(TextureDrawer textureDrawer) { + this.textureDrawer = textureDrawer; + reDraw(); + } + + @Override + public Dimension getTextureSize() { + if (isValid()) { + return textureDrawer.getTextureSize(); + } else { + return new Dimension(-1, -1); + } + } + + @Override + public ByteBuffer getData() { + if (isValid()) { + if (image == null) { + reDraw(); + } + return image.getImage().getRGBABuffer(); + } else { + return null; + } + } + + @Override + public ByteBuffer getSubData(int x, int y, int width, int height) { + if (isValid()) { + ByteBuffer buffer = getData(); + return buffer; + /* + ByteBuffer tempBuffer = ByteBuffer.allocate(4 * width * height); + byte[] data = new byte[4 * height]; + for (int j = y; j < y + height; j++) { + buffer.position(4 * (x + j * getTextureSize().width)); + buffer.get(data); + tempBuffer.put(data); + } + tempBuffer.rewind(); + buffer.rewind(); + return tempBuffer; + */ + } else { + return null; + } + } + + @Override + public BufferedImage getImage() { + if (isValid()) { + if (image == null) { + reDraw(); + } + return image.getImage(); + } else { + return null; + } + } + + @Override + public BufferedImage getSubImage(int x, int y, int width, int height) { + if (isValid()) { + BufferedImage image = getImage(); + return image.getSubimage(x, y, width, height); + } else { + return null; + } + } + + @Override + public boolean isValid() { + return textureDrawer != null; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextEntity.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextEntity.java new file mode 100755 index 000000000..21df6e29a --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextEntity.java @@ -0,0 +1,203 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.texture; + +import org.scilab.forge.scirenderer.shapes.appearance.Color; + +import java.awt.Dimension; +import java.awt.Font; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.Rectangle2D; + +/** + * @author Pierre Lando + */ +public class TextEntity { + + /** + * The default text color. + */ + public static final Color DEFAULT_TEXT_COLOR = new Color(0.f, 0.f, 0.f); + + /** + * The default text anti-aliased status. + */ + public static final boolean DEFAULT_TEXT_ANTI_ALIASED = true; + + /** + * The default text use fractional matrix status. + */ + public static final boolean DEFAULT_TEXT_USE_FRACTIONAL_METRICS = true; + + /** + * The default font. + */ + private static final Font DEFAULT_FONT = new Font(null); + + /** + * The current text color. + */ + private Color textColor = DEFAULT_TEXT_COLOR; + + /** + * The current text anti-aliased status. + */ + private boolean textAntiAliased = DEFAULT_TEXT_ANTI_ALIASED; + + /** + * The current text use fractional matrix status. + */ + private boolean textUseFractionalMetrics = DEFAULT_TEXT_USE_FRACTIONAL_METRICS; + + /** + * The text content of this object. + */ + private String text; + + /** + * The font used by this text entity. + */ + private Font font; + + private TextLayout layout; + + /** + * Default constructor. + * @param text the text content. + */ + public TextEntity(String text) { + this.text = text; + this.font = DEFAULT_FONT; + } + + /** + * Return the text content. + * @return the text content. + */ + public String getText() { + return text; + } + + /** + * Set the text content. + * @param text the new text content. + */ + public void setText(String text) { + this.text = text; + this.layout = null; + } + + /** + * Return the text font. + * @return the text font. + */ + public Font getFont() { + return font; + } + + /** + * Set the text font. + * @param font the new text font. + */ + public void setFont(Font font) { + this.font = font; + this.layout = null; + } + + /** + * Return the text color. + * @return the text color. + */ + public Color getTextColor() { + return textColor; + } + + /** + * Set the text color. + * @param textColor the new text color. + */ + public void setTextColor(Color textColor) { + this.textColor = textColor; + } + + /** + * Return the text anti-aliased status. + * @return the text anti-aliased status. + */ + public boolean isTextAntiAliased() { + return textAntiAliased; + } + + /** + * Set the text anti-aliased status. + * @param textAntiAliased the new text anti-aliased status. + */ + public void setTextAntiAliased(boolean textAntiAliased) { + this.textAntiAliased = textAntiAliased; + this.layout = null; + } + + /** + * Return the text use fractional metrics status. + * @return the text use fractional metrics status. + */ + public boolean isTextUseFractionalMetrics() { + return textUseFractionalMetrics; + } + + /** + * Set the text use fractional metrics status. + * @param textUseFractionalMetrics the new text use fractional metrics status. + */ + public void setTextUseFractionalMetrics(boolean textUseFractionalMetrics) { + this.textUseFractionalMetrics = textUseFractionalMetrics; + this.layout = null; + } + + /** + * TextEntity validity getter. + * @return true if the text entity is valid. + */ + public boolean isValid() { + return ((getFont() != null) + && (getText() != null) + && (getText().length() > 0) + ); + } + + public TextLayout getLayout() { + if (layout == null) { + FontRenderContext frc = new FontRenderContext(null, isTextAntiAliased(), isTextUseFractionalMetrics()); + layout = new TextLayout(getText(), getFont(), frc); + } + + return layout; + } + + /** + * Return the dimension in pixel of the text entity. + * @return the dimension in pixel of the text entity. + */ + public Dimension getSize() { + if (isValid()) { + TextLayout textLayout = getLayout(); + Dimension dimension = new Dimension(); + Rectangle2D r = textLayout.getBounds(); + /* +1 added to fix rendering of ticks labels, a pixel row/column was missing */ + dimension.setSize(r.getWidth() + 2, textLayout.getAscent() + textLayout.getDescent() + 1); + return dimension; + } else { + return new Dimension(0, 0); + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/Texture.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/Texture.java new file mode 100755 index 000000000..3c6b48181 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/Texture.java @@ -0,0 +1,140 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.texture; + +import org.scilab.forge.scirenderer.data.DataUser; + +/** + * @author Pierre Lando + */ +public interface Texture extends DataUser { + + /** + * Enumeration of wrapping possibility. + */ + public enum Wrap { + /** + * REPEAT: the integer part of the texture coordinate to be ignored. + */ + REPEAT, + + /** + * CLAMP: the texture coordinate is clamped to [0, 1]. + */ + CLAMP + } + + /** + * Enumeration of filtering possibility. + */ + public enum Filter { + /** + * Use the value of the texture element that is nearest (in Manhattan distance) to the center of the pixel being textured. + */ + NEAREST, + + /** + * Use the weighted average of the four texture elements that are closest to the center of the pixel being textured. + */ + LINEAR + } + + /** + * Texture validity getter. + * @return true is this texture is valid and can be used for rendering. + */ + boolean isValid(); + + /** + * Wrapping mode on the first texture coordinate getter. + * For more information on wrapping mode {see Wrap}. + * @return the wrapping mode on the first texture coordinate. + */ + Wrap getSWrappingMode(); + + /** + * Wrapping mode on the first texture coordinate. + * @param wrappingMode the new wrapping mode on the first texture coordinate. + */ + void setSWrappingMode(Wrap wrappingMode); + + /** + * Wrapping mode on the second dimension getter. + * If the texture dimension is 1, this method will return <code>null</code>. + * For more information on wrapping mode {see Wrap}. + * @return the wrapping mode on the second dimension. + */ + Wrap getTWrappingMode(); + + /** + * Wrapping mode on the second texture coordinate. + * @param wrappingMode the new wrapping mode on the second texture coordinate. + */ + void setTWrappingMode(Wrap wrappingMode); + + /** + * Minifying filter getter. + * @return the used minifying filter. + */ + Filter getMinifyingFilter(); + + /** + * Minifying filter mode setter. + * @param minifyingFilter the new minifying filter mode. + */ + void setMinifyingFilter(Filter minifyingFilter); + + /** + * Magnification filter getter. + * @return the used magnification filter. + */ + Filter getMagnificationFilter(); + + /** + * Magnification filter mode setter. + * @param magnificationFilter the new minifying filter mode. + */ + void setMagnificationFilter(Filter magnificationFilter); + + /** + * Texture data provider getter. + * @return the texture data provider. + */ + TextureDataProvider getDataProvider(); + + /** + * Texture data provider setter. + * The texture is set to 'no up to date'. + * @param provider the new texture data provider. + */ + void setDataProvider(TextureDataProvider provider); + + /** + * Set the texture data provider as a drawn texture data provider. + * @param textureDrawer the given texture drawer. + */ + void setDrawer(TextureDrawer textureDrawer); + + /** + * 2D-Texture coordinates must be modified according to the real texture dimension which can differ from + * the textureSize (with certains GC, a texture must have a size which is a power-of-two). + * @return the scale factor for the s-coordinate + */ + double getSScaleFactor(); + + /** + * 2D-Texture coordinates must be modified according to the real texture dimension which can differ from + * the textureSize (with certains GC, a texture must have a size which is a power-of-two). + * @return the scale factor for the t-coordinate + */ + double getTScaleFactor(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextureBufferedImage.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextureBufferedImage.java new file mode 100755 index 000000000..0d886aae1 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextureBufferedImage.java @@ -0,0 +1,88 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.texture; + +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import java.awt.BorderLayout; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * @author Pierre Lando + */ +public class TextureBufferedImage extends BufferedImage { + private static final int A_SHIFT = 24; + private static final int R_SHIFT = 16; + private static final int G_SHIFT = 8; + private static final int B_SHIFT = 0; + private static final int COMPONENT_MASK = 0xFF; + private static final int NB_COMPONENTS = 4; + + /** + * Default constructor. + * @param width texture width. + * @param height texture height. + */ + public TextureBufferedImage(int width, int height) { + super(width, height, TYPE_INT_ARGB); + } + + /** + * Return the buffer data of the image. + * Returned data are stored in 4 bytes (RGBA) per pixel. + * @return the buffer data of the image. + */ + public int[] getRGBAData() { + int[] pixels = ((DataBufferInt) getRaster().getDataBuffer()).getData(); + + for (int i = 0; i < pixels.length; i++) { + pixels[i] = (pixels[i] & 0xFF00FF00) | ((pixels[i] << 16) & 0x00FF0000) | ((pixels[i] >> 16) & 0xFF); + } + + //updateFrame(this); + return pixels; + } + + static JLabel label; + private static void updateFrame(TextureBufferedImage textureBufferedImage) { + if (label == null) { + JFrame frame = new JFrame("Test"); + frame.setLayout(new BorderLayout()); + label = new JLabel(); + frame.add(label, BorderLayout.CENTER); + frame.setSize(500, 500); + frame.setVisible(true); + } + label.setIcon(new ImageIcon(textureBufferedImage)); + } + + + /** + * Buffered data getter. + * @return a byte buffer filled with RGBA data. + */ + public ByteBuffer getRGBABuffer() { + int[] pixels = ((DataBufferInt) getRaster().getDataBuffer()).getData(); + ByteBuffer buffer = ByteBuffer.allocate(pixels.length * 4); + buffer.order(ByteOrder.nativeOrder()); + for (int i = 0; i < pixels.length; i++) { + buffer.putInt((pixels[i] & 0xFF00FF00) | ((pixels[i] << 16) & 0x00FF0000) | ((pixels[i] >> 16) & 0xFF)); + } + buffer.rewind(); + + return buffer; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextureDataProvider.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextureDataProvider.java new file mode 100755 index 000000000..e814d2c49 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextureDataProvider.java @@ -0,0 +1,125 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.texture; + +import org.scilab.forge.scirenderer.data.DataProvider; + +import java.awt.Dimension; +import java.awt.image.BufferedImage; +import java.nio.ByteBuffer; + +/** + * @author Pierre Lando + */ +public interface TextureDataProvider extends DataProvider<Texture> { + + public enum ImageType { + RGB, RGB_RGBA, BGR, GRAY, GRAY_16, RGBA, RGBA_REV, ABGR, RGB_332, RED, GREEN, BLUE, INTENSITY, RGBA_4444, RGBA_5551, RGB_FLOAT, RGBA_FLOAT, GRAY_FLOAT, RED_16, GREEN_16, BLUE_16, RED_FLOAT, GREEN_FLOAT, BLUE_FLOAT, RGBA_BYTE; + + public static ImageType fromInt(int n) { + switch (n) { + case 0: + return RGB; + case 1: + return RGB_RGBA; + case 2: + return BGR; + case 3: + return GRAY; + case 4: + return GRAY_16; + case 5: + return RGBA; + case 6: + return RGBA_REV; + case 7: + return ABGR; + case 8: + return RGB_332; + case 9: + return RED; + case 10: + return GREEN; + case 11: + return BLUE; + case 12: + return INTENSITY; + case 13: + return RGBA_4444; + case 14: + return RGBA_5551; + case 15: + return RGB_FLOAT; + case 16: + return RGBA_FLOAT; + case 17: + return GRAY_FLOAT; + case 18: + return RED_16; + case 19: + return GREEN_16; + case 20: + return BLUE_16; + case 21: + return RED_FLOAT; + case 22: + return GREEN_FLOAT; + case 23: + return BLUE_FLOAT; + case 24: + return RGBA_BYTE; + default: + return GRAY; + } + } + } + + boolean isRowMajorOrder(); + + ImageType getImageType(); + + /** + * Texture size getter. + * @return the size of the texture in pixel. + */ + Dimension getTextureSize(); + + /** + * Data getter. + * @return the data. + */ + ByteBuffer getData(); + + /** + * Sub-data getter. + * @param x rectangle origin x-coordinate. + * @param y rectangle origin y-coordinate. + * @param width rectangle width. + * @param height rectangle height. + * @return the data. + */ + ByteBuffer getSubData(int x, int y, int width, int height); + + /** + * @return the data as a BufferedImage + */ + BufferedImage getImage(); + + /** + * @param x rectangle origin x-coordinate. + * @param y rectangle origin y-coordinate. + * @param width rectangle width. + * @param height rectangle height. + * @return the sub-data as a BufferedImage. + */ + BufferedImage getSubImage(int x, int y, int width, int height); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextureDrawer.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextureDrawer.java new file mode 100755 index 000000000..64db0fd80 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextureDrawer.java @@ -0,0 +1,54 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.texture; + +import java.awt.Dimension; + +/** + * @author Pierre Lando + */ +public interface TextureDrawer { + + /** + * Origin position. + */ + public enum OriginPosition { + /** + * Origin is the sprite center. + */ + CENTER, + + /** + * Origin is the upper left sprite corner. + */ + UPPER_LEFT + } + + /** + * Call the texture drawing. + * @param textureDrawingTools the used drawing tools. + */ + void draw(TextureDrawingTools textureDrawingTools); + + /** + * Texture size getter. + * @return texture size. + */ + Dimension getTextureSize(); + + /** + * Return the origin position. + * Warning: change the returned value during a draw call have no effect. + * @return the origin position. + */ + OriginPosition getOriginPosition(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextureDrawingTools.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextureDrawingTools.java new file mode 100755 index 000000000..a6cea6d26 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextureDrawingTools.java @@ -0,0 +1,89 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.texture; + +import org.scilab.forge.scirenderer.shapes.appearance.Appearance; +import org.scilab.forge.scirenderer.shapes.appearance.Color; + +import javax.swing.Icon; + +/** + * + * Interface for the sprite drawing tools. + * + * + * @author Pierre Lando + */ +public interface TextureDrawingTools { + + /** + * Draw a plus. + * @param size the plus size. + * @param appearance the used appearance. + */ + void drawPlus(int size, Appearance appearance); + + /** + * Draw a polyline. + * @param coordinates polyline's point coordinates. + * @param appearance the used appearance. + */ + void drawPolyline(int[] coordinates, Appearance appearance); + + + /** + * Fill a polygon. + * @param coordinates polygon's point coordinates. + * @param appearance the used appearance. + */ + void fillPolygon(int[] coordinates, Appearance appearance); + + /** + * Draw a circle. + * @param x the x coordinate of the circle center. + * @param y the y coordinate of the circle center. + * @param diameter the circle diameter. + * @param appearance the circle appearance. + */ + void drawCircle(int x, int y, int diameter, Appearance appearance); + + /** + * Fill a disc of given diameter, centered at (x, y) with the given appearance. + * @param x the x coordinate of the disc center. + * @param y the y coordinate of the disc center. + * @param diameter the disc diameter. + * @param color the disc color. + */ + void fillDisc(int x, int y, int diameter, Color color); + + /** + * Draw the given text at the given position with the given appearance. + * @param textEntity the text entity to draw. + * @param x the x text position. + * @param y the y text position. + */ + void draw(TextEntity textEntity, int x, int y); + + /** + * Draw the given {@link javax.swing.Icon} at the given position. + * @param icon the given icon to paint. + * @param x the x text position. + * @param y the y text position. + */ + void draw(Icon icon, int x, int y); + + /** + * Clear the sprite with the given color. + * @param color the new background color. + */ + void clear(Color color); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextureManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextureManager.java new file mode 100755 index 000000000..17aada7bb --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/texture/TextureManager.java @@ -0,0 +1,34 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.texture; + +import java.util.Collection; + +/** + * @author Pierre Lando + */ +public interface TextureManager { + + /** + * Texture creator. + * @return a new {@link Texture} + */ + Texture createTexture(); + + /** + * Dispose the given textures. + * @param textures textures to dispose. + */ + void dispose(Collection<Texture> textures); + + void dispose(Texture texture); +}
\ No newline at end of file diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/DegenerateMatrixException.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/DegenerateMatrixException.java new file mode 100755 index 000000000..291addbe8 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/DegenerateMatrixException.java @@ -0,0 +1,29 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.tranformations; + +import org.scilab.forge.scirenderer.SciRendererException; + +/** + * @author Pierre Lando + */ +@SuppressWarnings(value = { "serial" }) +public class DegenerateMatrixException extends SciRendererException { + + /** + * Default constructor. + * @param message the message associated with this exception. + */ + public DegenerateMatrixException(String message) { + super(message); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/Rotation.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/Rotation.java new file mode 100755 index 000000000..8783894d8 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/Rotation.java @@ -0,0 +1,294 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.tranformations; + +/** + * This class represent a Rotation. + * The data are stored as an unitary quaternion [a; b; c; d]. + * A rotation of an angle alpha along the vector [x; y; z] is stored thus : + * a = cos (alpha / 2) + * b = sin (alpha / 2) * nx + * c = sin (alpha / 2) * ny + * d = sin (alpha / 2) * nz + * + * Where [nx; ny; nz] is [x; y; z] / norm([x; y; z]). + * + * @author Pierre Lando + */ +public class Rotation { + + private static final int MAX_SELF_OPERATION = 16; + private int opCount; + private double a; + private double b; + private double c; + private double d; + private double[] rotationMatrix; + + /** + * Default constructor. + * The created object represent identity rotation. + */ + public Rotation() { + a = 1; + b = 0; + c = 0; + d = 0; + normalize(); + } + + /** + * Copy constructor. + * The created object is a copy of the given rotation r. + * @param r the given rotation. + */ + public Rotation(Rotation r) { + a = r.a; + b = r.b; + c = r.c; + d = r.d; + normalize(); + } + + /** + * The created object represent a rotation of an angle of 'alpha' radians along the vector 'v'. + * @param alpha the rotation angle in radians. + * @param v the vector carrying the rotation. + */ + public Rotation(double alpha, Vector3d v) { + double t = Math.cos(alpha / 2); + Vector3d nv = v.getNormalized().times(Math.sin(alpha / 2)); + + a = t; + b = nv.getX(); + c = nv.getY(); + d = nv.getZ(); + normalize(); + } + + /** + * The created object represent a rotation of an angle of 'alpha' radians along the vector 'v'. + * @param alpha the rotation angle in radians. + * @param v the vector carrying the rotation. + */ + public Rotation(double alpha, Vector3f v) { + this(alpha, v.asDouble()); + } + + /** + * Get a rotation from an angle alpha given in degree and an axis v + * @param alpha angle of rotation in degree + * @param v the axis of the rotation + * @return a Rotation object + * + * For information, the function has been written to fix bug 11399. + * For angle (in radians) close to Pi the Rotation has a 'a' close to 0 + * and that conflicts with small axes where length is closed to a: rounding errors + * led to a "bad" transformation matrix (AxesDrawer::computeBoxTransformation). + */ + public static Rotation getDegreeRotation(double alpha, Vector3d v) { + int a = (int) alpha; + if (alpha == a) { + a = a % 360; + if (a == 0) { + return new Rotation(1, 0, 0, 0); + } + if (a == 180) { + Vector3d nv = v.getNormalized(); + return new Rotation(0, nv.getX(), nv.getY(), nv.getZ()); + } + + return new Rotation(((double) a) / 180.0 * Math.PI, v); + } + + alpha = (alpha / 180.0 - 2 * Math.floor(alpha / 360.0)) * Math.PI; + + return new Rotation(alpha, v); + } + + + private Rotation(double a, double b, double c, double d) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + normalize(); + } + + public Rotation(float[] v) { + if ((v != null) && (v.length == 4)) { + a = v[0]; + b = v[1]; + c = v[2]; + d = v[3]; + } else { // TODO throw invalid data exception + a = 1; + b = 0; + c = 0; + d = 0; + } + normalize(); + } + + public Rotation(double[] v) { + if ((v != null) && (v.length == 4)) { + a = v[0]; + b = v[1]; + c = v[2]; + d = v[3]; + } else { // TODO throw invalid data exception + a = 1; + b = 0; + c = 0; + d = 0; + } + normalize(); + } + + /** + * Return the inverse rotation. + * @return the inverse rotation. + */ + public Rotation getInverse() { + return new Rotation(a, -b, -c, -d); + } + + /** + * Return true if this object represents an identity transformation. + * @return true if this object represents an identity transformation. + */ + public boolean isIdentity() { + return (a == 1); + } + + public double[] getRotationMatrix() { + return rotationMatrix.clone(); + } + public double[] getUnRotateMatrix() { + return new double[] { + rotationMatrix[0], rotationMatrix[4], rotationMatrix[8], rotationMatrix[12], + rotationMatrix[1], rotationMatrix[5], rotationMatrix[9], rotationMatrix[13], + rotationMatrix[2], rotationMatrix[6], rotationMatrix[10], rotationMatrix[14], + rotationMatrix[3], rotationMatrix[7], rotationMatrix[11], rotationMatrix[15] + }; + } + + public Rotation multiply(Rotation q) { + double ar = a * q.a - b * q.b - c * q.c - d * q.d; + double br = a * q.b + b * q.a + c * q.d - d * q.c; + double cr = a * q.c - b * q.d + c * q.a + d * q.b; + double dr = a * q.d + b * q.c - c * q.b + d * q.a; + a = ar; + b = br; + c = cr; + d = dr; + selfCheck(); + return this; + } + + private void selfCheck() { + if (opCount++ > MAX_SELF_OPERATION) { + normalize(); + } else { + computeRotationMatrix(); + } + } + + private void normalize() { + opCount = 0; + double f = 1f / Math.sqrt((a * a) + (b * b) + (c * c) + (d * d)); + a *= f; + b *= f; + c *= f; + d *= f; + computeRotationMatrix(); + } + + + public Rotation times(Rotation q) { + return new Rotation(this).multiply(q); + } + + public Vector3d conjugate(Vector3d v) { + return new Vector3d( + rotationMatrix[0] * v.getX() + rotationMatrix[4] * v.getY() + rotationMatrix[8] * v.getZ(), + rotationMatrix[1] * v.getX() + rotationMatrix[5] * v.getY() + rotationMatrix[9] * v.getZ(), + rotationMatrix[2] * v.getX() + rotationMatrix[6] * v.getY() + rotationMatrix[10] * v.getZ() + ); + } + public Vector3d conjugateInverse(Vector3d v) { + return new Vector3d( + rotationMatrix[0] * v.getX() + rotationMatrix[1] * v.getY() + rotationMatrix[2] * v.getZ(), + rotationMatrix[4] * v.getX() + rotationMatrix[5] * v.getY() + rotationMatrix[6] * v.getZ(), + rotationMatrix[8] * v.getX() + rotationMatrix[9] * v.getY() + rotationMatrix[10] * v.getZ() + ); + } + + private void computeRotationMatrix() { + double a2 = a * a; + double b2 = b * b; + double c2 = c * c; + double d2 = d * d; + this.rotationMatrix = new double[] { + a2 + b2 - c2 - d2, 2 * ((b * c) - (a * d)), 2 * ((a * c) + (b * d)), 0, + 2 * ((a * d) + (b * c)), a2 - b2 + c2 - d2, 2 * ((c * d) - (a * b)), 0, + 2 * ((b * d) - (a * c)), 2 * ((a * b) + (c * d)), a2 - b2 - c2 + d2, 0, + 0, 0, 0, 1 + }; + } + + public String toString() { + return "(" + a + ", " + b + ", " + c + ", " + d + ")"; + } + + public Vector3d getVectorY() { + return new Vector3d(rotationMatrix[1], rotationMatrix[5], rotationMatrix[9]); + } + + public Rotation power(double p) { + // TODO : do a better S.L.E.R.P. + double cos = a; + double sin = Math.sqrt((b * b) + (c * c) + (d * d)); + + if (sin < 0.001) { + return new Rotation(); + } + + double t = (p - 1) * Math.atan2(sin, cos); + double na = cos * Math.cos(t) - sin * Math.sin(t); + + double beta = Math.cos(t) + Math.sin(t) * (cos / sin); + return new Rotation(na, beta * b, beta * c, beta * d); + } + + public double[] getData() { + return new double[] {a, b, c, d}; + } + + public float[] getDataAsFloatArray() { + return new float[] {(float) a, (float) b, (float) c, (float) d}; + } + + @Override + public int hashCode() { + int hashCode = 0; + hashCode += 7 * (new Double(a).hashCode()); + hashCode += 17 * (new Double(b).hashCode()); + hashCode += 29 * (new Double(c).hashCode()); + hashCode += 37 * (new Double(d).hashCode()); + return hashCode; + } + + public boolean equals(Rotation r2) { + return (r2 != null) && (a == r2.a) && (b == r2.b) && (c == r2.c) && (d == r2.d); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/Transformation.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/Transformation.java new file mode 100755 index 000000000..70556042d --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/Transformation.java @@ -0,0 +1,82 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.tranformations; + +/** + * @author Pierre Lando + */ +public interface Transformation { + + /** + * Return this right times the given transformation. + * @param transformation the given transformation. + * @return a new transformation. + */ + Transformation rightTimes(Transformation transformation); + + /** + * Return this left times the given transformation. + * @param transformation the given transformation. + * @return a new transformation. + */ + Transformation leftTimes(Transformation transformation); + + /** + * Return the inverse transformation. + * @return the inverse transformation. + */ + Transformation getInverseTransformation(); + + /** + * Project the given vector. + * W value is set to 1. + * @param vector the given vector. + * @return the given vector projected. + */ + Vector3d project(Vector3d vector); + + /** + * Project the given direction. + * Translation part is not used. + * @param direction the direction to project. + * @return the given direction projected. + */ + Vector3d projectDirection(Vector3d direction); + + /** + * Unproject the given vector. + * W value is set to 1. + * @param vector the given vector. + * @return the given vector un-projected. + */ + Vector3d unproject(Vector3d vector); + + /** + * Return true if is identity, false otherwise. + * @return true if is identity, false otherwise. + */ + boolean isIdentity(); + + /** + * Return this transformation matrix. + * The returned array is a clone of the transformation array. + * @return this transformation matrix. + */ + double[] getMatrix(); + + /** + * Return this transformation inverse matrix. + * The returned array is a clone of the transformation array. + * @return this transformation inverse matrix. + */ + double[] getInverseMatrix(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationFactory.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationFactory.java new file mode 100755 index 000000000..7439ebd73 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationFactory.java @@ -0,0 +1,680 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.tranformations; + +import java.awt.Dimension; + +/** + * @author Pierre Lando + */ +public final class TransformationFactory { + + /** + * The identity transformation. + */ + static final Transformation IDENTITY_TRANSFORMATION = new IdentityTransformation(); + + /** + * Default constructor. + * The constructor is private : this is an utility class. + */ + private TransformationFactory() { + } + + + /** + * Return the identity transformation. + * @return the identity transformation. + */ + public static Transformation getIdentity() { + return IDENTITY_TRANSFORMATION; + } + + /** + * Return a translate transformation. + * @param x translation in x coordinate. + * @param y translation in y coordinate. + * @param z translation in z coordinate. + * @return a translate transformation. + */ + public static Transformation getTranslateTransformation(double x, double y, double z) { + if ((x == 0) && (y == 0) && (z == 0)) { + return IDENTITY_TRANSFORMATION; + } else { + return new TranslateTransformation(x, y, z); + } + } + + private static Transformation getTranslateTransformation(Vector3d t) { + return getTranslateTransformation(t.getX(), t.getY(), t.getZ()); + } + + /** + * Return a scale transformation. + * @param x scale in x. + * @param y scale in y. + * @param z scale in z. + * @return a scale transformation. + * @throws DegenerateMatrixException - A DegenerateMatrixException is thrown if one of the scale factor is zero. + */ + public static Transformation getScaleTransformation(double x, double y, double z) throws DegenerateMatrixException { + if ((x == 1) && (y == 1) && (z == 1)) { + return IDENTITY_TRANSFORMATION; + } else if ((x == 0) || (y == 0) || (z == 0)) { + throw new DegenerateMatrixException("Scale matrix with 0 factor."); + } else { + return new ScaleTransformation(x, y, z); + } + } + + /** + * Return a scale transformation. + * @param s scale vector. + * @return a scale transformation. + * @throws DegenerateMatrixException - A DegenerateMatrixException is thrown if one coordinate of the scale vector is zero. + */ + private static Transformation getScaleTransformation(Vector3d s) throws DegenerateMatrixException { + return getScaleTransformation(s.getX(), s.getY(), s.getZ()); + } + + /** + * Return a scale transformation. + * @param s scale value. + * @return a scale transformation. + * @throws DegenerateMatrixException - A DegenerateMatrixException is thrown if one coordinate of the scale vector is zero. + */ + public static Transformation getScaleTransformation(double s) throws DegenerateMatrixException { + return getScaleTransformation(s, s, s); + } + + /** + * Return a rotation transformation. + * @param angle the rotation angle in degree. + * @param x the x coordinate of the rotation axes. + * @param y the y coordinate of the rotation axes. + * @param z the z coordinate of the rotation axes. + * @return a rotation transformation. + * @throws DegenerateMatrixException - A DegenerateMatrixException is thrown if the rotation axes is zero. + */ + public static Transformation getRotationTransformation(double angle, double x, double y, double z) throws DegenerateMatrixException { + if ((x == 0) && (y == 0) && (z == 0)) { + throw new DegenerateMatrixException("Rotation axes should not be nul."); + } else if (angle == 0) { + return IDENTITY_TRANSFORMATION; + } else { + return new RotationTransformation(Rotation.getDegreeRotation(angle, new Vector3d(x, y, z))); + } + } + + /** + * Return a rotation transformation. + * @param q the quaternion. + * @return a rotation transformation. + */ + public static Transformation getRotationTransformation(Rotation q) { + if (q.isIdentity()) { + return IDENTITY_TRANSFORMATION; + } else { + return new RotationTransformation(q); + } + } + + /** + * Return the 'preferred aspect ratio transformation' + * @param dimension the canvas dimension. + * @param preferredRatio the preferred aspect ratio. + * @return the 'preferred aspect ratio transformation' + * @throws DegenerateMatrixException - A DegenerateMatrixException is thrown if the given value are not reasonable. + */ + public static Transformation getPreferredAspectRatioTransformation(Dimension dimension, double preferredRatio) throws DegenerateMatrixException { + double ratio = dimension.getWidth() / dimension.getHeight(); + if (ratio > preferredRatio) { + return getScaleTransformation(1 / ratio, 1, 1); + } else { + return getScaleTransformation(1, ratio, 1); + } + } + + /** + * Return a perspective transformation. + * @param near the distance from the viewer to the near clipping plane. + * @param far the distance from the viewer to the far clipping plane. + * @param fov the field of view angle in degree. + * @return a perspective transformation. + */ + public static Transformation getPerspectiveTransformation(double near, double far, double fov) { + return new PerspectiveTransformation(near, far, fov); + } + + /** + * Return an orthographic transformation. + * @param left the left plane distance to origin. + * @param right the right plane distance to origin. + * @param bottom the bottom plane distance to origin. + * @param top the top plane distance to origin. + * @param near the near plane distance to origin. + * @param far the far plane distance to origin. + * @return an orthographic transformation. + */ + public static Transformation getOrthographic(double left, double right, double bottom, double top, double near, double far) { + return new OrthographicTransformation(left, right, bottom, top, near, far); + } + + /** + * Return an affine transformation + * aX + b + * @param s the scale parameter. + * @param t the translate parameter. + * @return an affine transformation + * @throws DegenerateMatrixException - A DegenerateMatrixException is thrown if one coordinate of the scale vector is zero. + */ + public static Transformation getAffineTransformation(Vector3d s, Vector3d t) throws DegenerateMatrixException { + return getTranslateTransformation(t).rightTimes(getScaleTransformation(s)); + } + + /** + * Abstract transformation. + * Implement common methods of all transformation. + */ + private abstract static class AbstractTransformation implements Transformation { + + @Override + public Transformation rightTimes(Transformation transformation) { + return getProductTransformation(this, transformation); + } + + @Override + public Transformation leftTimes(Transformation transformation) { + return getProductTransformation(transformation, this); + } + + @Override + public Vector3d project(Vector3d vector) { + double[] matrix = getMatrix(); + double x = matrix[0] * vector.getX() + matrix[4] * vector.getY() + matrix[8] * vector.getZ() + matrix[12]; + double y = matrix[1] * vector.getX() + matrix[5] * vector.getY() + matrix[9] * vector.getZ() + matrix[13]; + double z = matrix[2] * vector.getX() + matrix[6] * vector.getY() + matrix[10] * vector.getZ() + matrix[14]; + double w = matrix[3] * vector.getX() + matrix[7] * vector.getY() + matrix[11] * vector.getZ() + matrix[15]; + return new Vector3d(x / w, y / w, z / w); + } + + @Override + public Vector3d projectDirection(Vector3d direction) { + double[] matrix = getMatrix(); + double x = matrix[0] * direction.getX() + matrix[4] * direction.getY() + matrix[8] * direction.getZ(); + double y = matrix[1] * direction.getX() + matrix[5] * direction.getY() + matrix[9] * direction.getZ(); + double z = matrix[2] * direction.getX() + matrix[6] * direction.getY() + matrix[10] * direction.getZ(); + double w = matrix[3] * direction.getX() + matrix[7] * direction.getY() + matrix[11] * direction.getZ() + matrix[15]; + return new Vector3d(x / w, y / w, z / w); + } + + @Override + public Vector3d unproject(Vector3d vector) { + return getInverseTransformation().project(vector); + } + + @Override + public Transformation getInverseTransformation() { + return new AbstractTransformation() { + @Override + public boolean isIdentity() { + return AbstractTransformation.this.isIdentity(); + } + + @Override + public double[] getMatrix() { + return AbstractTransformation.this.getInverseMatrix(); + } + + @Override + public double[] getInverseMatrix() { + return AbstractTransformation.this.getMatrix(); + } + }; + } + + @Override + public String toString() { + double[] matrix = getMatrix(); + String r = ""; + for (int i = 0; i < 16; i++) { + r += matrix[i]; + if ((i % 4) == 3) { + r += "\n"; + } else { + r += ", "; + } + } + return r; + } + + /** + * Return a product transformation. + * @param t1 the first transformation. + * @param t2 the second transformation. + * @return t1 x t2 + */ + private Transformation getProductTransformation(Transformation t1, Transformation t2) { + if (t1.isIdentity()) { + return t2; + } else if (t2.isIdentity()) { + return t1; + } else { + return new ProductTransformation(t1, t2); + } + } + } + + + /** + * The identity transformation. + */ + private static class IdentityTransformation extends AbstractTransformation { + + /** + * The identity matrix data. + */ + private static final double[] IDENTITY_MATRIX = new double[] { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + }; + + /** + * Default constructor. + */ + IdentityTransformation() { + } + + @Override + public Transformation rightTimes(Transformation transformation) { + return transformation; + } + + @Override + public Transformation leftTimes(Transformation transformation) { + return transformation; + } + + @Override + public Vector3d project(Vector3d vector) { + return vector; + } + + @Override + public Vector3d projectDirection(Vector3d direction) { + return direction; + } + + @Override + public Vector3d unproject(Vector3d vector) { + return vector; + } + + @Override + public boolean isIdentity() { + return true; + } + + @Override + public double[] getMatrix() { + return IDENTITY_MATRIX.clone(); + } + + @Override + public double[] getInverseMatrix() { + return IDENTITY_MATRIX.clone(); + } + } + + /** + * A translate transformation. + */ + private static class TranslateTransformation extends AbstractTransformation { + + /** + * translation in x coordinate. + */ + private final double x; + + /** + * translation in y coordinate. + */ + private final double y; + + /** + * translation in z coordinate. + */ + private final double z; + + /** + * Default constructor. + * + * (x, y, z) != (0, 0, 0). + * + * @param x translation in x coordinate. + * @param y translation in y coordinate. + * @param z translation in z coordinate. + */ + public TranslateTransformation(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public Vector3d project(Vector3d vector) { + return new Vector3d(vector.getX() + x, vector.getY() + y, vector.getZ() + z); + } + + @Override + public Vector3d unproject(Vector3d vector) { + return new Vector3d(vector.getX() - x, vector.getY() - y, vector.getZ() - z); + } + + @Override + public boolean isIdentity() { + return false; + } + + @Override + public double[] getMatrix() { + return new double[] { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + x, y, z, 1 + }; + } + + @Override + public double[] getInverseMatrix() { + return new double[] { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + -x, -y, -z, 1 + }; + } + } + + + /** + * A scale transformation. + */ + private static class ScaleTransformation extends AbstractTransformation { + + /** + * scale in x coordinate. + */ + private final double x; + + /** + * scale in y coordinate. + */ + private final double y; + + /** + * scale in z coordinate. + */ + private final double z; + + + /** + * Default constructor. + * + * x, y and z not 0 or 1. + * + * @param x scale in x coordinate. + * @param y scale in y coordinate. + * @param z scale in z coordinate. + */ + public ScaleTransformation(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public Vector3d project(Vector3d vector) { + return new Vector3d(vector.getX() * x, vector.getY() * y, vector.getZ() * z); + } + + @Override + public Vector3d unproject(Vector3d vector) { + return new Vector3d(vector.getX() / x, vector.getY() / y, vector.getZ() / z); + } + + @Override + public boolean isIdentity() { + return false; + } + + @Override + public double[] getMatrix() { + return new double[] { + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1 + }; + } + + @Override + public double[] getInverseMatrix() { + return new double[] { + 1 / x, 0, 0, 0, + 0, 1 / y, 0, 0, + 0, 0, 1 / z, 0, + 0, 0, 0, 1 + }; + } + } + + /** + * A perspective transformation. + */ + private static class PerspectiveTransformation extends AbstractTransformation { + + private final double[] matrix; + private final double[] inverseMatrix; + + /** + * Default constructor. + * @param near the distance to the near plane. + * @param far the distance to the far plane. + * @param fov the field of view in degree. + */ + PerspectiveTransformation(double near, double far, double fov) { + double fInv = Math.tan(Math.toRadians(fov / 2)); + double f = 1 / fInv; + + matrix = new double[] { + f, 0, 0, 0, + 0, f, 0, 0, + 0, 0, (far + near) / (near - far), -1, + 0, 0, 2 * far * near / (near - far), 0 + }; + + inverseMatrix = new double[] { + fInv, 0, 0, 0, + 0, fInv, 0, 0, + 0, 0, 0, (near - far) / (2 * far * near), + 0, 0, -1, (near + far) / (2 * far * near) + }; + } + + @Override + public boolean isIdentity() { + return false; + } + + @Override + public double[] getMatrix() { + return matrix.clone(); + } + + @Override + public double[] getInverseMatrix() { + return inverseMatrix.clone(); + } + } + + /** + * An orthographic transformation. + */ + private static class OrthographicTransformation extends AbstractTransformation { + + private final double[] matrix; + private final double[] inverseMatrix; + private final boolean isIdentity; + + /** + * Default constructor. + * @param left the left plane distance to origin. + * @param right the right plane distance to origin. + * @param bottom the bottom plane distance to origin. + * @param top the top plane distance to origin. + * @param near the near plane distance to origin. + * @param far the far plane distance to origin. + */ + public OrthographicTransformation(double left, double right, double bottom, double top, double near, double far) { + double tx = (right + left) / (left - right); + double ty = (top + bottom) / (bottom - top); + double tz = (far + near) / (near - far); + + matrix = new double[] { + 2 / (right - left), 0, 0, 0, + 0, 2 / (top - bottom), 0, 0, + 0, 0, 2 / (near - far), 0, + tx, ty, tz, 1 + }; + + inverseMatrix = new double[] { + (right - left) / 2, 0, 0, 0, + 0, (top - bottom) / 2, 0, 0, + 0, 0, (near - far) / 2, 0, + (right + left) / 2, (top + bottom) / 2, -(near + far) / 2, 1 + }; + + isIdentity = ((left == -1) && (right == 1) && (bottom == -1) && (top == 1) && (far == -1) && (near == 1)); + + } + + @Override + public boolean isIdentity() { + return isIdentity; + } + + @Override + public double[] getMatrix() { + return matrix.clone(); + } + + @Override + public double[] getInverseMatrix() { + return inverseMatrix.clone(); + } + } + + /** + * A rotation transformation. + */ + private static class RotationTransformation extends AbstractTransformation { + + private final double[] matrix; + private final double[] inverseMatrix; + + /** + * Default constructor. + * @param q the quaternion. + */ + public RotationTransformation(Rotation q) { + matrix = q.getRotationMatrix(); + inverseMatrix = q.getUnRotateMatrix(); + } + + @Override + public boolean isIdentity() { + return false; + } + + @Override + public double[] getMatrix() { + return matrix.clone(); + } + + @Override + public double[] getInverseMatrix() { + return inverseMatrix.clone(); + } + } + + /** + * A product transformation. + */ + private static class ProductTransformation extends AbstractTransformation { + + private final double[] matrix; + private final double[] inverseMatrix; + private final boolean isIdentity; + + /** + * Default constructor. + * @param t1 first transformation. + * @param t2 first transformation. + */ + public ProductTransformation(Transformation t1, Transformation t2) { + isIdentity = t1.isIdentity() && t2.isIdentity(); + matrix = multiply(t1.getMatrix(), t2.getMatrix()); + + // TODO : inverse 'matrix' instead. + inverseMatrix = multiply(t2.getInverseMatrix(), t1.getInverseMatrix()); + } + + @Override + public boolean isIdentity() { + return isIdentity; + } + + @Override + public double[] getMatrix() { + return matrix.clone(); + } + + @Override + public double[] getInverseMatrix() { + return inverseMatrix.clone(); + } + } + + /** + * Multiply two matrix. + * @param matrix1 first matrix. + * @param matrix2 second matrix. + * @return matrix1 x matrix2 + */ + private static double[] multiply(double[] matrix1, double[] matrix2) { + double[] result = new double[16]; + for (int l = 0; l < 4; l++) { + for (int c = 0; c < 4; c++) { + double r = 0; + for (int i = 0; i < 4; i++) { + r += matrix1[(4 * i) + c] * matrix2[(4 * l) + i]; + } + result[(4 * l) + c] = r; + } + } + return result; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationManager.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationManager.java new file mode 100755 index 000000000..c0c900c3a --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationManager.java @@ -0,0 +1,118 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.tranformations; + +/** + * + * This is an interface for a transformation manager. + * The transformation manager is used to manage transformation applied to vertex data sent for drawing. + * + * Coordinate are in first multiplied by the matrix at the top of ModelView matrix stack. + * And then, the result is multiplied by the matrix at the top of Projection matrix stack. + * + * 'Projection' and 'ModelView' name refers to OpenGl 1.1. + * + * The rendered vertex are in the box [-1, 1]^3. + * + * @author Pierre Lando + */ +public interface TransformationManager { + + /** + * Add a listener. + * @param listener added. + */ + void addListener(TransformationManagerListener listener); + + /** + * Remove a listener. + * @param listener removed. + */ + void removeListener(TransformationManagerListener listener); + + + /** + * Return the model view matrix stack. + * @return the model view matrix stack. + */ + TransformationStack getModelViewStack(); + + /** + * Return the projection matrix stack. + * @return the projection matrix stack. + */ + TransformationStack getProjectionStack(); + + /** + * Return the top scene transformation. + * @return the top scene transformation. + */ + Transformation getTransformation(); + + /** + * Return the inverse of window transformation. + * @return the inverse window transformation. + */ + Transformation getInverseWindowTransformation(); + + /** + * Return the window transformation. + * @return the window transformation. + */ + Transformation getWindowTransformation(); + + /** + * Return the canvas projection matrix. + * @return the canvas projection matrix. + */ + Transformation getCanvasProjection(); + + /** + * Return the canvas projection matrix for Graphics2D. + * @return the canvas projection matrix. + */ + Transformation getG2DProjection(); + + /** + * Return the projection matrix (no modelView). + * @return the projection matrix. + */ + Transformation getG2DSingleProjection(); + + /** + * Return the window projection matrix for Graphics2D. + * @return the window projection matrix. + */ + Transformation getG2DWindowProjection(); + + /** + * Clear all stack. + */ + void reset(); + + + /** + * Switch to window coordinate. + */ + void useWindowCoordinate(); + + /** + * Switch to scene coordinate. + */ + void useSceneCoordinate(); + + /** + * Return true if is using scene coordinate. + * @return true if is using scene coordinate. + */ + boolean isUsingSceneCoordinate(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationManagerImpl.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationManagerImpl.java new file mode 100755 index 000000000..e8bef60f3 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationManagerImpl.java @@ -0,0 +1,226 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.tranformations; + +import org.scilab.forge.scirenderer.Canvas; + +import javax.swing.event.EventListenerList; + +/** + * Default {@link org.scilab.forge.scirenderer.tranformations.TransformationManager} implementation. + * @author Pierre Lando + */ +public class TransformationManagerImpl implements TransformationManager { + + /** + * We use swing EventListenerList for facility. + */ + private final EventListenerList listeners; + + private final Canvas canvas; + private final TransformationStack modelViewStack; + private final TransformationStack projectionStack; + + /** + * The peek transformation value. + * Set to null if it must be recalculate. + */ + private Transformation topTransformation; + private boolean usingSceneCoordinate = true; + + + /** + * Standard constructor. + * @param canvas the canvas. + */ + public TransformationManagerImpl(Canvas canvas) { + this.canvas = canvas; + listeners = new EventListenerList(); + modelViewStack = new TransformationStackImpl(); + projectionStack = new TransformationStackImpl(); + topTransformation = null; + + + final TransformationStackListener listener = new TransformationStackListener() { + @Override + public void changed(TransformationStack transformationStack, TransformationStack.TransformationStackEvent event, Transformation top) { + topTransformation = null; + fireTransformationChanged(); + } + }; + + modelViewStack.addListener(listener); + projectionStack.addListener(listener); + } + + + @Override + public void addListener(TransformationManagerListener listener) { + listeners.add(TransformationManagerListener.class, listener); + } + + @Override + public void removeListener(TransformationManagerListener listener) { + listeners.remove(TransformationManagerListener.class, listener); + } + + @Override + public TransformationStack getModelViewStack() { + return modelViewStack; + } + + @Override + public TransformationStack getProjectionStack() { + return projectionStack; + } + + @Override + public Transformation getTransformation() { + if (topTransformation == null) { + final Transformation modelView = modelViewStack.peek(); + final Transformation projection = projectionStack.peek(); + final Transformation returnedTransformation = projection.rightTimes(modelView); + topTransformation = returnedTransformation; + return returnedTransformation; + } else { + return topTransformation; + } + } + + @Override + public Transformation getWindowTransformation() { + double w = 2.0 / canvas.getWidth(); + double h = 2.0 / canvas.getHeight(); + try { + return TransformationFactory.getAffineTransformation( + new Vector3d(w, h, 1), + new Vector3d(-1, -1, 0) + ); + } catch (DegenerateMatrixException e) { + // Should not occur as long as canvas have not infinite size. + throw new Error("Canvas is to big.", e); + } + } + + @Override + public Transformation getInverseWindowTransformation() { + double w = canvas.getWidth() / 2; + double h = canvas.getHeight() / 2; + try { + return TransformationFactory.getAffineTransformation( + new Vector3d(w, h, 1), + new Vector3d(w, h, 0) + ); + } catch (DegenerateMatrixException e) { + // Should not occur as long as canvas have not infinite size. + throw new Error("Canvas is to big.", e); + } + } + + @Override + public Transformation getCanvasProjection() { + double w = canvas.getWidth() / 2.0; + double h = canvas.getHeight() / 2.0; + try { + Transformation windowTransformation = TransformationFactory.getAffineTransformation( + new Vector3d(w, h, 1), + new Vector3d(w, h, 0) + ); + return windowTransformation.rightTimes(getTransformation()); + } catch (DegenerateMatrixException e) { + // Should not occur as long as canvas have not infinite size. + throw new Error("Canvas is to big.", e); + } + } + + @Override + public Transformation getG2DProjection() { + double w = canvas.getWidth() / 2.0; + double h = canvas.getHeight() / 2.0; + try { + Transformation windowTransformation = TransformationFactory.getAffineTransformation( + new Vector3d(w, -h, 1), + new Vector3d(w, h, 0) + ); + return windowTransformation.rightTimes(getTransformation()); + } catch (DegenerateMatrixException e) { + // Should not occur as long as canvas have not infinite size. + throw new Error("Canvas is to big.", e); + } + } + + @Override + public Transformation getG2DSingleProjection() { + double w = canvas.getWidth() / 2.0; + double h = canvas.getHeight() / 2.0; + try { + Transformation windowTransformation = TransformationFactory.getAffineTransformation( + new Vector3d(w, -h, 1), + new Vector3d(w, h, 0) + ); + return windowTransformation.rightTimes(projectionStack.peek()); + } catch (DegenerateMatrixException e) { + // Should not occur as long as canvas have not infinite size. + throw new Error("Canvas is to big.", e); + } + } + + @Override + public Transformation getG2DWindowProjection() { + double h = canvas.getHeight(); + try { + return TransformationFactory.getAffineTransformation( + new Vector3d(1, -1, 1), + new Vector3d(0, h, 0) + ); + } catch (DegenerateMatrixException e) { + // Should not occur as long as canvas have not infinite size. + throw new Error("Canvas is to big.", e); + } + } + + @Override + public void reset() { + modelViewStack.clear(); + projectionStack.clear(); + } + + @Override + public void useWindowCoordinate() { + if (isUsingSceneCoordinate()) { + usingSceneCoordinate = false; + fireTransformationChanged(); + } + } + + @Override + public void useSceneCoordinate() { + if (!isUsingSceneCoordinate()) { + usingSceneCoordinate = true; + fireTransformationChanged(); + } + } + + @Override + public boolean isUsingSceneCoordinate() { + return usingSceneCoordinate; + } + + /** + * Notify listeners the top transformation have changed. + */ + protected void fireTransformationChanged() { + for (TransformationManagerListener listener : listeners.getListeners(TransformationManagerListener.class)) { + listener.transformationChanged(this); + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationManagerListener.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationManagerListener.java new file mode 100755 index 000000000..97193553f --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationManagerListener.java @@ -0,0 +1,31 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.tranformations; + +import java.util.EventListener; + +/** + * + * The listener of a {@link org.scilab.forge.scirenderer.tranformations.TransformationManager} functions are called when events occurs in the + * listened {@link org.scilab.forge.scirenderer.tranformations.TransformationManager}. + * + * @author Pierre Lando + */ +public interface TransformationManagerListener extends EventListener { + + /** + * Called when the top transformation of the listened TransformationManager have changed. + * @param transformationManager object where event occur. + */ + void transformationChanged(final TransformationManager transformationManager); + +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationStack.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationStack.java new file mode 100755 index 000000000..7805e1254 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationStack.java @@ -0,0 +1,86 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.tranformations; + +/** + * @author Pierre Lando + */ +public interface TransformationStack { + + /** + * Enum of possible transformation stack event. + */ + enum TransformationStackEvent { + /** + * When a matrix have been popped. + */ + POPPED, + + /** + * When a matrix have been pushed. + */ + PUSHED, + + /** + * When the stack have been cleared. + */ + CLEARED + } + + + /** + * Add a listener. + * @param listener added listener. + */ + void addListener(TransformationStackListener listener); + + /** + * Remove a listener. + * @param listener removed listener. + */ + void removeListener(TransformationStackListener listener); + + /** + * Return the top transformation. + * @return the top transformation. + */ + Transformation peek(); + + /** + * Push the given transformation on the stack. + * @param transformation the given transformation. + */ + void push(Transformation transformation); + + /** + * Push the given transformation right time the peek on the stack. + * @param transformation the given transformation. + */ + void pushRightMultiply(Transformation transformation); + + /** + * Push the given transformation left time the peek on the stack. + * @param transformation the given transformation. + */ + void pushLeftMultiply(Transformation transformation); + + /** + * Pop one matrix on the stack. + * @return the popped matrix. + */ + Transformation pop(); + + /** + * Pop all matrix on the stack except identity. + */ + void clear(); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationStackImpl.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationStackImpl.java new file mode 100755 index 000000000..452618e09 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationStackImpl.java @@ -0,0 +1,98 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.tranformations; + +import java.util.Stack; + +import javax.swing.event.EventListenerList; + +/** + * @author Pierre Lando + */ +public class TransformationStackImpl implements TransformationStack { + + /** + * We use swing EventListenerList for facility. + */ + private final EventListenerList listeners; + + private final Stack<Transformation> stack; + + public TransformationStackImpl() { + listeners = new EventListenerList(); + stack = new Stack<Transformation>(); + } + + @Override + public void addListener(TransformationStackListener listener) { + listeners.add(TransformationStackListener.class, listener); + } + + @Override + public void removeListener(TransformationStackListener listener) { + listeners.remove(TransformationStackListener.class, listener); + } + + @Override + public Transformation peek() { + if (stack.isEmpty()) { + return TransformationFactory.getIdentity(); + } else { + return stack.peek(); + } + } + + @Override + public void push(Transformation transformation) { + if (transformation != null) { + stack.push(transformation); + fireChanged(TransformationStackEvent.PUSHED, transformation); + } + } + + @Override + public void pushRightMultiply(Transformation transformation) { + push(peek().rightTimes(transformation)); + } + + @Override + public void pushLeftMultiply(Transformation transformation) { + push(peek().leftTimes(transformation)); + } + + @Override + public Transformation pop() { + Transformation value = stack.pop(); + if (value == null) { + value = TransformationFactory.getIdentity(); + } + fireChanged(TransformationStackEvent.POPPED, value); + return value; + } + + @Override + public void clear() { + stack.clear(); + fireChanged(TransformationStackEvent.CLEARED, peek()); + } + + /** + * Fire a change event. + * @param event the event. + * @param top the new top transformation. + */ + protected void fireChanged(TransformationStackEvent event, Transformation top) { + for (TransformationStackListener listener : listeners.getListeners(TransformationStackListener.class)) { + listener.changed(this, event, top); + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationStackListener.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationStackListener.java new file mode 100755 index 000000000..040b678fa --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/TransformationStackListener.java @@ -0,0 +1,28 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.tranformations; + +import java.util.EventListener; + +/** + * @author Pierre Lando + */ +public interface TransformationStackListener extends EventListener { + + /** + * Called when an event occur in the listened transformation stack. + * @param transformationStack the transformation stack where the event occur. + * @param event the event. + * @param top the new peek transformation of the stack. + */ + void changed(final TransformationStack transformationStack, final TransformationStack.TransformationStackEvent event, final Transformation top); +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/Vector3d.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/Vector3d.java new file mode 100755 index 000000000..5feccb266 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/Vector3d.java @@ -0,0 +1,206 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.tranformations; + +import java.util.Arrays; + +/** + * @author Pierre Lando + */ +public class Vector3d { + + private final double x; + private final double y; + private final double z; + private int hash = -1; + + public Vector3d(Vector3d v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + + public Vector3d(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Vector3d(float[] position) { + this.x = position[0]; + this.y = position[1]; + this.z = position[2]; + } + + public Vector3d(double[] position) { + this.x = position[0]; + this.y = position[1]; + this.z = position[2]; + } + + public Vector3d(Double[] position) { + this.x = position[0]; + this.y = position[1]; + this.z = position[2]; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public double getZ() { + return z; + } + + @Override + public String toString() { + return "[" + x + ", " + y + ", " + z + "]"; + } + + public double[] getData() { + return new double[] {x, y, z}; + } + + public float[] getDataAsFloatArray() { + return new float[] {(float) x, (float) y, (float) z}; + } + + public float[] getDataAsFloatArray(int size) { + if (size == 4) { + return new float[] {(float) x, (float) y, (float) z, 1}; + } else { + return new float[] {(float) x, (float) y, (float) z}; + } + } + + public Vector3d plus(Vector3d v) { + return new Vector3d(x + v.x, y + v.y, z + v.z); + } + + public Vector3d minus(Vector3d v) { + return new Vector3d(x - v.x, y - v.y, z - v.z); + } + + public Vector3d times(double d) { + return new Vector3d(x * d, y * d, z * d); + } + + public Vector3d getNormalized() { + double norm = getNorm(); + if (norm == 0) { + return new Vector3d(0, 0, 0); + } + return this.times(1 / getNorm()); + } + + public double getNorm() { + return Math.sqrt(getNorm2()); + } + + public double getNorm2() { + return scalar(this); + } + + public double scalar(Vector3d v) { + return x * v.x + y * v.y + z * v.z; + } + + /** + * Create a new vector cross-product of the given vectors. + * @param v1 the first given vector. + * @param v2 the second given vector. + * @return a new vector cross-product of the given vectors. + */ + public static Vector3d product(Vector3d v1, Vector3d v2) { + return new Vector3d( + v1.y * v2.z - v1.z * v2.y, + v1.z * v2.x - v1.x * v2.z, + v1.x * v2.y - v1.y * v2.x + ); + } + + public final static double det(final Vector3d v0, final Vector3d v1, final Vector3d v2) { + return v0.x * (v1.y * v2.z - v1.z * v2.y) + v0.y * (v1.z * v2.x - v1.x * v2.z) + v0.z * (v1.x * v2.y - v1.y * v2.x); + } + + public final static Vector3d getBarycenter(Vector3d v0, Vector3d v1, double w0, double w1) { + return new Vector3d(v0.x * w0 + v1.x * w1, v0.y * w0 + v1.y * w1, v0.z * w0 + v1.z * w1); + } + + /** + * Create a new vector, copy of this one, with a new X value. + * @param x the new X value. + * @return a new vector, copy of this one, with a new X value. + */ + public Vector3d setX(double x) { + return new Vector3d(x, y, z); + } + + + /** + * Create a new vector, copy of this one, with a new Y value. + * @param y the new Y value. + * @return a new vector, copy of this one, with a new Y value. + */ + public Vector3d setY(double y) { + return new Vector3d(x, y, z); + } + + + /** + * Create a new vector, copy of this one, with a new Z value. + * @param z the new Z value. + * @return a new vector, copy of this one, with a new Z value. + */ + public Vector3d setZ(double z) { + return new Vector3d(x, y, z); + } + + /** + * Return true if this vector is (0, 0, 0). + * @return true if this vector is (0, 0, 0). + */ + public boolean isZero() { + return (x == 0) && (y == 0) && (z == 0); + } + + /** + * Return true if this vector is (0, 0, 0). + * @return true if this vector is (0, 0, 0). + */ + public boolean isNearZero() { + final double eps = 1e-9; + return Math.abs(x) <= eps && Math.abs(y) <= eps && Math.abs(z) <= eps; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Vector3d) { + Vector3d v = (Vector3d) obj; + final double eps = 1e-9; + return Math.abs(v.x - x) <= eps && Math.abs(v.y - y) <= eps && Math.abs(v.z - z) <= eps; + } + return false; + } + + @Override + public int hashCode() { + if (hash == -1) { + hash = Arrays.hashCode(new double[] {x, y, z}); + } + return hash; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/Vector3f.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/Vector3f.java new file mode 100755 index 000000000..281276870 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/Vector3f.java @@ -0,0 +1,106 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.tranformations; + +/** + * @author Pierre Lando + */ +public class Vector3f { + private final float x; + private final float y; + private final float z; + + public Vector3f(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + public float getX() { + return x; + } + + public float getY() { + return y; + } + + public float getZ() { + return z; + } + + @Override + public String toString() { + return "[" + x + ", " + y + ", " + z + "]"; + } + + public Vector3d asDouble() { + return new Vector3d(x, y, z); + } + + public Vector3f plus(Vector3f v) { + return new Vector3f(x + v.x, y + v.y, z + v.z); + } + + public Vector3f minus(Vector3f v) { + return new Vector3f(x - v.x, y - v.y, z - v.z); + } + + public Vector3f negate() { + return new Vector3f(-x, -y, -z); + } + + public Vector3f times(float d) { + return new Vector3f(x * d, y * d, z * d); + } + + public Vector3f getNormalized() { + float norm = getNorm(); + if (norm == 0) { + return new Vector3f(0.0f, 0.0f, 0.0f); + } + return this.times(1.0f / getNorm()); + } + + public float getNorm() { + return (float)Math.sqrt(getNorm2()); + } + + public float getNorm2() { + return scalar(this); + } + + public float scalar(Vector3f v) { + return x * v.x + y * v.y + z * v.z; + } + + /** + * Create a new vector cross-product of the given vectors. + * @param v1 the first given vector. + * @param v2 the second given vector. + * @return a new vector cross-product of the given vectors. + */ + public static Vector3f product(Vector3f v1, Vector3f v2) { + return new Vector3f( + v1.y * v2.z - v1.z * v2.y, + v1.z * v2.x - v1.x * v2.z, + v1.x * v2.y - v1.y * v2.x + ); + } + + public final static float det(final Vector3f v0, final Vector3f v1, final Vector3f v2) { + return v0.x * (v1.y * v2.z - v1.z * v2.y) + v0.y * (v1.z * v2.x - v1.x * v2.z) + v0.z * (v1.x * v2.y - v1.y * v2.x); + } + + public final static Vector3f getBarycenter(Vector3f v0, Vector3f v1, float w0, float w1) { + return new Vector3f(v0.x * w0 + v1.x * w1, v0.y * w0 + v1.y * w1, v0.z * w0 + v1.z * w1); + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/Vector4d.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/Vector4d.java new file mode 100755 index 000000000..66a9dc60e --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/tranformations/Vector4d.java @@ -0,0 +1,32 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.tranformations; + +/** + * @author Pierre Lando + */ +public class Vector4d { + private final double[] data; + + + public Vector4d(double x, double y, double z, double w) { + data = new double[] {x, y, z, w}; + } + + public double[] getData() { + return data.clone(); + } + + public String toString() { + return "[" + data[0] + ", " + data[1] + ", " + data[2] + ", " + data[3] + "]"; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/utils/shapes/geometry/CubeFactory.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/utils/shapes/geometry/CubeFactory.java new file mode 100755 index 000000000..7c42467b3 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/utils/shapes/geometry/CubeFactory.java @@ -0,0 +1,170 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.utils.shapes.geometry; + +import org.scilab.forge.scirenderer.Canvas; +import org.scilab.forge.scirenderer.buffers.BuffersManager; +import org.scilab.forge.scirenderer.buffers.ElementsBuffer; +import org.scilab.forge.scirenderer.buffers.IndicesBuffer; +import org.scilab.forge.scirenderer.shapes.geometry.DefaultGeometry; +import org.scilab.forge.scirenderer.shapes.geometry.Geometry; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +/** + * An utility class to create cube geometry. + * @author Pierre Lando + */ +public final class CubeFactory { + + /** + * The vertex size. + */ + private static final int VERTEX_SIZE = 4; + + /** + * The constructor is private : this is an utility class. + */ + private CubeFactory() { + } + + /** + * Return a geometry representing a simple cube. + * @param canvas the canvas where the buffers are created. + * @return a geometry representing a simple cube. + */ + public static Geometry createCube(Canvas canvas) { + return createCube(canvas, 1); + } + + /** + * Return a geometry representing a cube. + * Each cube face are made of density^2 square. + * + * @param canvas the canvas where the geometry buffers are created. + * @param density the square density on cube faces. + * @return a geometry representing a cube. + */ + public static Geometry createCube(Canvas canvas, int density) { + return createCube(canvas, density, false); + } + + /** + * Return a geometry representing a cube. + * Each cube face are made of density^2 square. + * + * @param canvas the canvas where the geometry buffers are created. + * @param density the square density on cube faces. + * @param wired true if the wire is visible. + * @return a geometry representing a cube. + */ + public static Geometry createCube(Canvas canvas, int density, boolean wired) { + if (density < 1) { + return null; + } else { + int borderLength = density + 1; + FloatBuffer vertices = FloatBuffer.allocate(6 * borderLength * borderLength * 4); + FloatBuffer normals = FloatBuffer.allocate(6 * borderLength * borderLength * 4); + IntBuffer indices = IntBuffer.allocate(6 * 6 * density * density); + IntBuffer wireIndices = IntBuffer.allocate(8 * 6 * density * density); + + int shift = 0; + for (int axes = 0; axes < 3; axes++) { + for (int orientation : new int[] { -1, 1}) { + float[] normal = getVectorPermutation(axes, 0, 0, orientation); + for (int i = 0; i < borderLength; i++) { + for (int j = 0; j < borderLength; j++) { + float s = (2f * i) / density - 1f; + float t = (2f * j) / density - 1f; + vertices.put(getVectorPermutation(axes, s, t, orientation)); + normals.put(normal); + } + } + for (int i = 0; i < density; i++) { + for (int j = 0; j < density; j++) { + int index = shift + j + i * borderLength; + indices.put(index); + indices.put(index + 1); + indices.put(index + borderLength); + + indices.put(index + 1); + indices.put(index + borderLength + 1); + indices.put(index + borderLength); + + wireIndices.put(index); + wireIndices.put(index + 1); + wireIndices.put(index + 1); + wireIndices.put(index + borderLength + 1); + wireIndices.put(index + borderLength + 1); + wireIndices.put(index + borderLength); + wireIndices.put(index + borderLength); + wireIndices.put(index); + } + } + shift += borderLength * borderLength; + } + } + + vertices.rewind(); + normals.rewind(); + indices.rewind(); + + BuffersManager bm = canvas.getBuffersManager(); + final ElementsBuffer verticesBuffer = bm.createElementsBuffer(); + final ElementsBuffer normalsBuffer = bm.createElementsBuffer(); + final IndicesBuffer indicesBuffer = bm.createIndicesBuffer(); + + verticesBuffer.setData(vertices, VERTEX_SIZE); + normalsBuffer.setData(normals, VERTEX_SIZE); + indicesBuffer.setData(indices); + + DefaultGeometry geometry = new DefaultGeometry(); + geometry.setFaceCullingMode(Geometry.FaceCullingMode.BOTH); + geometry.setFillDrawingMode(Geometry.FillDrawingMode.TRIANGLES); + geometry.setVertices(verticesBuffer); + geometry.setNormals(normalsBuffer); + geometry.setIndices(indicesBuffer); + + if (wired) { + final IndicesBuffer wireIndicesBuffer = bm.createIndicesBuffer(); + wireIndicesBuffer.setData(wireIndices); + geometry.setWireIndices(wireIndicesBuffer); + geometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS); + geometry.setPolygonOffsetMode(true); + } + + return geometry; + } + } + + /** + * Return a permutation of the [s; t; u] vector. + * @param permutation the permutation factor. + * @param s x coordinate of the given vector. + * @param t y coordinate of the given vector. + * @param u z coordinate of the given vector. + * @return a permutation of the [s; t; u] vector. + */ + private static float[] getVectorPermutation(int permutation, float s, float t, float u) { + switch (permutation % 3) { + case 0: + return new float[] {s, t, u, 1}; + case 1: + return new float[] {u, s, t, 1}; + case 2: + return new float[] {t, u, s, 1}; + default: + return null; + } + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/utils/shapes/geometry/SphereFactory.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/utils/shapes/geometry/SphereFactory.java new file mode 100755 index 000000000..55cd25544 --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/utils/shapes/geometry/SphereFactory.java @@ -0,0 +1,146 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2011-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.utils.shapes.geometry; + +import org.scilab.forge.scirenderer.Canvas; +import org.scilab.forge.scirenderer.buffers.ElementsBuffer; +import org.scilab.forge.scirenderer.buffers.IndicesBuffer; +import org.scilab.forge.scirenderer.shapes.geometry.DefaultGeometry; +import org.scilab.forge.scirenderer.shapes.geometry.Geometry; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +/** + * @author Pierre Lando + */ +public final class SphereFactory { + private static SphereFactory singleton; + + private SphereFactory() { + } + + public static SphereFactory getSingleton() { + if (singleton == null) { + singleton = new SphereFactory(); + } + return singleton; + } + + /** + * Create a sphere geometry with classic longitude / latitude division. + * @param canvas the canvas where the geometry will be created. + * @param radius the sphere radius. + * @param givenLatitude number of division in latitude. + * @param givenLongitude number of division in longitude. + * @return a sphere geometry with classic longitude / latitude division. + */ + public Geometry create(Canvas canvas, Float radius, int givenLatitude, int givenLongitude) { + int latitude; + int longitude; + if (givenLatitude < 3) { + latitude = 3; + } else { + latitude = givenLatitude; + } + + if (givenLongitude < 4) { + longitude = 4; + } else { + longitude = givenLongitude; + } + + ElementsBuffer vertexBuffer = canvas.getBuffersManager().createElementsBuffer(); + vertexBuffer.setData(createVertexData(radius, latitude, longitude), 4); + + IndicesBuffer indicesBuffer = canvas.getBuffersManager().createIndicesBuffer(); + indicesBuffer.setData(createIndicesData(latitude, longitude)); + + DefaultGeometry geometry = new DefaultGeometry(); + geometry.setFillDrawingMode(Geometry.FillDrawingMode.TRIANGLES); + geometry.setVertices(vertexBuffer); + geometry.setIndices(indicesBuffer); + + return geometry; + } + + private IntBuffer createIndicesData(int latitudeResolution, int longitudeResolution) { + int indexNumber = 6 * (latitudeResolution - 3) * longitudeResolution + 6 * longitudeResolution; + IntBuffer indexBuffer = IntBuffer.allocate(indexNumber); + indexBuffer.rewind(); + + for (int i = 0; i < longitudeResolution; i++) { + indexBuffer.put(0); + indexBuffer.put(1 + i); + indexBuffer.put(1 + (i + 1) % longitudeResolution); + } + + for (int latitude = 1; latitude < latitudeResolution - 2; latitude++) { + for (int longitude = 0; longitude < longitudeResolution; longitude++) { + int aIndex = 1 + longitude + (latitude - 1) * longitudeResolution; + int bIndex = 1 + ((longitude + 1) % longitudeResolution) + (latitude - 1) * longitudeResolution; + int cIndex = 1 + ((longitude + 1) % longitudeResolution) + latitude * longitudeResolution; + int dIndex = 1 + longitude + latitude * longitudeResolution; + + indexBuffer.put(aIndex); + indexBuffer.put(bIndex); + indexBuffer.put(cIndex); + + indexBuffer.put(aIndex); + indexBuffer.put(cIndex); + indexBuffer.put(dIndex); + } + } + + + int lastVertexIndex = (latitudeResolution - 2) * longitudeResolution + 2 - 1; + for (int i = 0; i < longitudeResolution; i++) { + indexBuffer.put(lastVertexIndex); + indexBuffer.put(lastVertexIndex - longitudeResolution + i); + indexBuffer.put(lastVertexIndex - longitudeResolution + (i + 1) % longitudeResolution); + } + + indexBuffer.rewind(); + return indexBuffer; + } + + private FloatBuffer createVertexData(Float radius, int latitudeResolution, int longitudeResolution) { + int vertexNumber = (latitudeResolution - 2) * longitudeResolution + 2; + FloatBuffer dataBuffer = FloatBuffer.allocate(vertexNumber * 4); + dataBuffer.rewind(); + + dataBuffer.put(new float[] {0, 0, radius, 1}); + + for (int latitude = 1; latitude < latitudeResolution - 1; latitude++) { + double latitudeAngle = latitude * Math.PI / (latitudeResolution - 1); + + float z = (float) (radius * Math.cos(latitudeAngle)); + float discRadius = (float) (radius * Math.sin(latitudeAngle)); + + for (int longitude = 0; longitude < longitudeResolution; longitude++) { + double longitudeAngle = longitude * 2 * Math.PI / longitudeResolution; + float x = (float) (discRadius * Math.cos(longitudeAngle)); + float y = (float) (discRadius * Math.sin(longitudeAngle)); + + dataBuffer.put(x); + dataBuffer.put(y); + dataBuffer.put(z); + dataBuffer.put(1); + } + } + + dataBuffer.put(new float[] {0, 0, -radius, 1}); + + dataBuffer.rewind(); + return dataBuffer; + } +} diff --git a/modules/scirenderer/src/org/scilab/forge/scirenderer/utils/shapes/geometry/TetrahedronFactory.java b/modules/scirenderer/src/org/scilab/forge/scirenderer/utils/shapes/geometry/TetrahedronFactory.java new file mode 100755 index 000000000..83e44c50b --- /dev/null +++ b/modules/scirenderer/src/org/scilab/forge/scirenderer/utils/shapes/geometry/TetrahedronFactory.java @@ -0,0 +1,138 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + */ + +package org.scilab.forge.scirenderer.utils.shapes.geometry; + +import org.scilab.forge.scirenderer.Canvas; +import org.scilab.forge.scirenderer.buffers.ElementsBuffer; +import org.scilab.forge.scirenderer.buffers.IndicesBuffer; +import org.scilab.forge.scirenderer.shapes.geometry.DefaultGeometry; +import org.scilab.forge.scirenderer.shapes.geometry.Geometry; +import org.scilab.forge.scirenderer.tranformations.Vector3d; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +/** + * An utility class to create a tetrahedron. + * @author Pierre Lando + */ +public final class TetrahedronFactory { + private static final int ELEMENTS_SIZE = 4; + + private static final Vector3d A = new Vector3d(+1, +1, +1); // Point #0 + private static final Vector3d B = new Vector3d(+1, -1, -1); // Point #1 + private static final Vector3d C = new Vector3d(-1, +1, -1); // Point #2 + private static final Vector3d D = new Vector3d(-1, -1, +1); // Point #3 + + /** + * Private constructor. + * This is an utility class. + */ + private TetrahedronFactory() { + } + + /** + * @param canvas the canvas where the buffers are created. + * @return a Tetrahedron geometry. + */ + public static DefaultGeometry createTetrahedron(Canvas canvas) { + Vector3d mab = A.plus(B).times(.5); // Point #4 + Vector3d mac = A.plus(C).times(.5); // Point #5 + Vector3d mad = A.plus(D).times(.5); // Point #6 + + Vector3d mbc = B.plus(C).times(.5); // Point #7 + Vector3d mbd = B.plus(D).times(.5); // Point #8 + + Vector3d mcd = C.plus(D).times(.5); // Point #9 + + // Create vertices buffer. + FloatBuffer vertices = FloatBuffer.allocate(10 * ELEMENTS_SIZE); + vertices.rewind(); + vertices.put(A.getDataAsFloatArray(ELEMENTS_SIZE)); + vertices.put(B.getDataAsFloatArray(ELEMENTS_SIZE)); + vertices.put(C.getDataAsFloatArray(ELEMENTS_SIZE)); + vertices.put(D.getDataAsFloatArray(ELEMENTS_SIZE)); + + vertices.put(mab.getDataAsFloatArray(ELEMENTS_SIZE)); + vertices.put(mac.getDataAsFloatArray(ELEMENTS_SIZE)); + vertices.put(mad.getDataAsFloatArray(ELEMENTS_SIZE)); + + vertices.put(mbc.getDataAsFloatArray(ELEMENTS_SIZE)); + vertices.put(mbd.getDataAsFloatArray(ELEMENTS_SIZE)); + + vertices.put(mcd.getDataAsFloatArray(ELEMENTS_SIZE)); + vertices.rewind(); + + // 4 triangles per faces. + // 4 faces. + // 3 indices by triangles. + IntBuffer indices = IntBuffer.allocate(48); + indices.rewind(); + indices.put(new int[] { + 0, 4, 5, + 4, 1, 7, + 5, 7, 2, + 7, 5, 4, + + 0, 5, 6, + 5, 2, 9, + 6, 9, 3, + 9, 6, 5, + + 0, 6, 4, + 6, 3, 8, + 4, 8, 1, + 8, 4, 6, + + 8, 9, 7, + 9, 8, 3, + 7, 1, 8, + 2, 7, 9 + }); + indices.rewind(); + + IntBuffer edgesIndices = IntBuffer.allocate(48); + edgesIndices.rewind(); + edgesIndices.put(new int[] { + 0, 4, 4, 1, + 0, 5, 5, 2, + 0, 6, 6, 3, + 4, 5, 5, 6, 6, 4, + 1, 7, 7, 2, + 2, 9, 9, 3, + 3, 8, 8, 1, + 4, 7, 7, 5, 5, 9, 9, 6, 6, 8, 8, 4, + 7, 9, 9, 8, 8, 7 + }); + + edgesIndices.rewind(); + + canvas.getBuffersManager().createElementsBuffer(); + ElementsBuffer vertexBuffer = canvas.getBuffersManager().createElementsBuffer(); + IndicesBuffer indicesBuffer = canvas.getBuffersManager().createIndicesBuffer(); + IndicesBuffer edgesIndicesBuffer = canvas.getBuffersManager().createIndicesBuffer(); + + vertexBuffer.setData(vertices, ELEMENTS_SIZE); + indicesBuffer.setData(indices); + edgesIndicesBuffer.setData(edgesIndices); + + DefaultGeometry geometry = new DefaultGeometry(); + geometry.setFillDrawingMode(Geometry.FillDrawingMode.TRIANGLES); + geometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS); + geometry.setPolygonOffsetMode(true); + geometry.setWireIndices(edgesIndicesBuffer); + geometry.setIndices(indicesBuffer); + geometry.setVertices(vertexBuffer); + + return geometry; + } +} |