package org.jzy3d.plot3d.rendering.scene;

import java.util.ArrayList;
import java.util.List;

import org.jzy3d.maths.BoundingBox3d;
import org.jzy3d.maths.TicToc;
import org.jzy3d.plot3d.primitives.AbstractComposite;
import org.jzy3d.plot3d.primitives.AbstractDrawable;
import org.jzy3d.plot3d.primitives.IGLBindedResource;
import org.jzy3d.plot3d.primitives.selectable.Selectable;
import org.jzy3d.plot3d.rendering.compat.GLES2CompatUtils;
import org.jzy3d.plot3d.rendering.legends.AWTLegend;
import org.jzy3d.plot3d.rendering.legends.ILegend;
import org.jzy3d.plot3d.rendering.ordering.AbstractOrderingStrategy;
import org.jzy3d.plot3d.rendering.ordering.DefaultOrderingStrategy;
import org.jzy3d.plot3d.rendering.view.Camera;
import org.jzy3d.plot3d.rendering.view.View;
import org.jzy3d.plot3d.transform.Transform;

import com.jogamp.opengl.GL;
import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
import com.jogamp.opengl.glu.GLU;

/**
 * The scene's {@link Graph} basically stores the scene content and facilitate
 * objects control
 * 
 * The graph may decompose all {@link AbstractComposite} into a list of their
 * {@link AbstractDrawable}s primitives if constructor is called with parameters
 * enabling sorting.
 * 
 * The list of primitives is ordered using either the provided
 * {@link DefaultOrderingStrategy} or an other specified
 * {@link AbstractOrderingStrategy}. Sorting is usefull for handling
 * transparency properly.
 * 
 * The {@link Graph} maintains a reference to its mother {@link Scene} in order
 * to inform the {@link View}s when its content has change and that repainting
 * is required.
 * 
 * The add() method allows adding a {@link AbstractDrawable} to the scene Graph
 * and updates all views' viewpoint in order to target the center of the scene.
 * 
 * @author Martin Pernollet
 */
public class Graph {
    public Graph(Scene scene) {
        this(scene, new DefaultOrderingStrategy(), true);
    }

    public Graph(Scene scene, boolean sort) {
        this(scene, new DefaultOrderingStrategy(), sort);
    }

    public Graph(Scene scene, AbstractOrderingStrategy strategy) {
        this(scene, strategy, true);
    }

    public Graph(Scene scene, AbstractOrderingStrategy strategy, boolean sort) {
        this.scene = scene;
        this.strategy = strategy;
        this.sort = sort;
        this.components = new ArrayList();
        // components = Collections.synchronizedList(new
        // ArrayList());
    }

    public synchronized void dispose() {
        // synchronized(components){
        for (AbstractDrawable c : components)
            if (c != null)
                c.dispose();
        // }
        components.clear();
        scene = null;
    }

    /* */

    /**
     * Add a Drawable to the graph and call all views' so that they update their
     * bounds according to their mode (automatic or manual).
     * 
     * Addition is to the graph is synchronized.
     * 
     * @param drawable
     *            : The drawable that must be added to the scene graph.
     * @param update
     *            : should be true if you wish to have all the views updated
     *            with old bounds including drawable bounds
     */
    public void add(AbstractDrawable drawable, boolean updateViews) {
        synchronized (this) {
            components.add(drawable);
        }

        if (updateViews)
            for (View view : scene.views)
                view.updateBounds();
    }

    public void add(AbstractDrawable drawable) {
        add(drawable, true);
    }

    public void add(List drawables, boolean updateViews) {
        for (AbstractDrawable d : drawables)
            add(d, false);
        if (updateViews)
            for (View view : scene.views)
                view.updateBounds();
    }

    public void add(List drawables) {
        add(drawables, true);
    }

    /**
     * Delete a Drawable from the SceneGraph and update all views' viewpoint in
     * order to target the center of the scene.
     * 
     * @param drawable
     *            The drawable that must be deleted from the scene graph.
     */
    public boolean remove(AbstractDrawable drawable, boolean updateViews) {
        boolean output = false;
        synchronized (this) {
            output = components.remove(drawable);
        }
        BoundingBox3d bbox = getBounds();
        for (View view : scene.views) {
            view.lookToBox(bbox);
            if (updateViews)
                view.shoot();
        }
        return output;
    }

    public boolean remove(AbstractDrawable drawable) {
        return remove(drawable, true);
    }

    public List getAll() {
        return components;
    }

    public synchronized List getAllGLBindedResources() {
        List out = new ArrayList();
        for (AbstractDrawable c : components) {
            if (c instanceof IGLBindedResource) {
                out.add((IGLBindedResource) c);
            }
        }
        return out;
    }

    public void mountAllGLBindedResources(GL gl) {
        final List all = getAllGLBindedResources();
        for (IGLBindedResource r : all)
            if (!r.hasMountedOnce())
                r.mount(gl);
    }

    /* */

    /**
     * Decompose all {@link AbstractComposite} objects, and sort the extracted
     * monotype (i.e. non-{@link AbstractComposite} {@link AbstractDrawable}s)
     * in order to render them according to the default -or defined-
     * {@link AbstractOrderingStrategy}.
     */
    public void draw(GL gl, GLU glu, Camera camera) {
        draw(gl, glu, camera, components, sort);
    }

    protected TicToc t = new TicToc();

    protected synchronized void draw(GL gl, GLU glu, Camera camera, List components, boolean sort) {
        glMatrixMode(gl);
        if (!sort) {
            drawSimple(gl, glu, camera, components);
        } else {
            drawDecomposition(gl, glu, camera);
        }
    }

    public void glMatrixMode(GL gl) {
        if (gl.isGL2()) {
            gl.getGL2().glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
        } else {
            GLES2CompatUtils.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
        }
    }

    public void drawSimple(GL gl, GLU glu, Camera camera, List components) {
        // render all items of the graph
        // synchronized(components){
        for (AbstractDrawable d : components)
            if (d.isDisplayed())
                d.draw(gl, glu, camera);
        // }
    }

    public void drawDecomposition(GL gl, GLU glu, Camera camera) {
        // Render sorted monotypes
        List monotypes = getDecomposition();
        strategy.sort(monotypes, camera);

        for (AbstractDrawable d : monotypes) {
            if (d.isDisplayed())
                d.draw(gl, glu, camera);
        }
    }

    /**
     * Expand all {@link AbstractComposites} instance into a list of atomic
     * {@link AbstractDrawable} types and return all the current Graph
     * primitives decomposition.
     */
    public List getDecomposition() {
        ArrayList monotypes;
        synchronized (components) {
            monotypes = Decomposition.getDecomposition(components);
        }
        return monotypes;
    }

    /** Update all interactive {@link AbstractDrawable} projections */
    public synchronized void project(GL gl, GLU glu, Camera camera) {
        // synchronized(components){
        for (AbstractDrawable d : components) {
            if (d instanceof Selectable)
                ((Selectable) d).project(gl, glu, camera);
        }
        // }
    }

    /* */

    /** Get the {@link @Drawable} ordering strategy. */
    public AbstractOrderingStrategy getStrategy() {
        return strategy;
    }

    /** Set the {@link @Drawable} ordering strategy. */
    public void setStrategy(AbstractOrderingStrategy strategy) {
        this.strategy = strategy;
    }

    /**
     * Delegate transforming iteratively to all Drawable of this graph and
     * stores the given transform for keeping the ability of retrieving it.
     */
    public synchronized void setTransform(Transform transform) {
        this.transform = transform;

        synchronized (components) {
            for (AbstractDrawable c : components) {
                if (c != null)
                    c.setTransform(transform);
            }
        }
    }

    /** Return the transform that was affected to this composite. */
    public Transform getTransform() {
        return transform;
    }

    /**
     * Creates and return a BoundingBox3d that embed all Drawable bounds, among
     * those that have a defined bounding box.
     */
    public synchronized BoundingBox3d getBounds() {
        if (components.size() == 0)
            return new BoundingBox3d(0, 0, 0, 0, 0, 0);
        else {
            BoundingBox3d box = new BoundingBox3d();

            for (AbstractDrawable c : components) {
                if (c != null && c.getBounds() != null) {
                    BoundingBox3d drawableBounds = c.getBounds();
                    if (!drawableBounds.isReset()) {
                        box.add(drawableBounds);
                    }
                }
            }
            return box;
        }
    }

    /* */

    /**
     * Return the list of available {@link AbstractDrawable}'s {@link AWTLegend}
     * .
     */
    public synchronized List getLegends() {
        List list = new ArrayList();

        // synchronized(components){
        for (AbstractDrawable c : components)
            if (c != null)
                if (c.hasLegend() && c.isLegendDisplayed())
                    list.add(c.getLegend());
        // }
        return list;
    }

    /**
     * Return true if the {@link Graph} contains at least one
     * {@link AbstractDrawable} that has {@link AWTLegend} that must be
     * displayed.
     */
    public synchronized int hasLegends() {
        int k = 0;

        // synchronized(components){
        for (AbstractDrawable c : components)
            if (c != null)
                if (c.hasLegend() && c.isLegendDisplayed())
                    k++;
        // }
        return k;
    }

    /* */

    /** Print out information concerning all Drawable of this composite. */
    @Override
    public synchronized String toString() {
        String output = "(Graph) #elements:" + components.size() + ":\n";

        int k = 0;
        synchronized(components){
            for (AbstractDrawable c : components) {
                if (c != null)
                    output += " Graph element [" + (k++) + "]:" + c.toString(1) + "\n";
                else
                    output += " Graph element [" + (k++) + "] (null)\n";
            }
        }
        return output;
    }
    
    

    /* */

    public boolean isSort() {
        return sort;
    }

    /**
     * Set sort to false to desactivate decomposition of drawable. 
     * 
     * This bypass ranking polygons w.r.t. camera. This will produce visual cue
     * if the scene is dynamic (changing the list of polygons or viewpoints).
     * @param sort
     */
    public void setSort(boolean sort) {
        this.sort = sort;
    }

    public Scene getScene() {
        return scene;
    }

    protected List components;
    protected Scene scene;
    protected Transform transform;
    // protected OrderingStrategy strategy;

    protected boolean VERBOSE = false;
    protected AbstractOrderingStrategy strategy;
    protected boolean sort = true;
}

 

Ads help maintain this website.