/* Copyright 2008-2010 Gephi Authors : Mathieu Bastian <mathieu.bastian@gephi.org> Website : http://www.gephi.org This file is part of Gephi. Gephi is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Gephi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Gephi. If not, see <http://www.gnu.org/licenses/>. */ package org.gephi.visualization.opengl.compatibility; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.media.opengl.GL; import javax.media.opengl.glu.GLU; import org.gephi.visualization.VizArchitecture; import org.gephi.visualization.VizController; import org.gephi.visualization.apiimpl.Scheduler; import org.gephi.visualization.apiimpl.VizConfig; import org.gephi.visualization.api.objects.CompatibilityModelClass; import org.gephi.visualization.scheduler.SimpleFPSAnimator; import org.gephi.visualization.swing.GraphDrawableImpl; /** * * @author Mathieu Bastian */ public class CompatibilityScheduler implements Scheduler, VizArchitecture { //States AtomicBoolean animating = new AtomicBoolean(); AtomicBoolean cameraMoved = new AtomicBoolean(); AtomicBoolean mouseMoved = new AtomicBoolean(); AtomicBoolean objectsMoved = new AtomicBoolean(); AtomicBoolean startDrag = new AtomicBoolean(); AtomicBoolean drag = new AtomicBoolean(); AtomicBoolean stopDrag = new AtomicBoolean(); AtomicBoolean mouseClick = new AtomicBoolean(); //Architeture private GraphDrawableImpl graphDrawable; private CompatibilityEngine engine; private VizConfig vizConfig; //Current GL private GL gl; private GLU glu; //Animator private SimpleFPSAnimator simpleFPSAnimator; private float fpsLimit = 30f; public void initArchitecture() { this.graphDrawable = VizController.getInstance().getDrawable(); this.engine = (CompatibilityEngine) VizController.getInstance().getEngine(); this.vizConfig = VizController.getInstance().getVizConfig(); initPools(); init(); } private ThreadPoolExecutor pool1; private ThreadPoolExecutor pool2; private List<Runnable> modelSegments; private Semaphore pool1Semaphore = new Semaphore(0); private Semaphore pool2Semaphore = new Semaphore(0); private Runnable selectionSegment; private Runnable startDragSegment; private Runnable dragSegment; private Runnable refreshLimitsSegment; private Runnable mouseClickSegment; private void initPools() { pool1 = new ThreadPoolExecutor(0, 4, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactory() { public Thread newThread(Runnable r) { Thread t = new Thread(r, "VisualizationThreadPool 1"); t.setDaemon(true); return t; } }) { @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); pool1Semaphore.release(); } @Override public void execute(Runnable command) { super.execute(command); } }; pool2 = new ThreadPoolExecutor(0, 4, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactory() { public Thread newThread(Runnable r) { Thread t = new Thread(r, "VisualizationThreadPool 2"); t.setDaemon(true); return t; } }) { @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); pool2Semaphore.release(); } @Override public void execute(Runnable command) { super.execute(command); } }; } public void init() { modelSegments = new ArrayList<Runnable>(); for (final CompatibilityModelClass objClass : engine.lodClasses) { modelSegments.add(new Runnable() { public void run() { if (objClass.isEnabled()) { objClass.lod(engine.getOctree().getObjectIterator(objClass.getClassId())); } } }); } selectionSegment = new Runnable() { public void run() { engine.mouseMove(); } }; refreshLimitsSegment = new Runnable() { public void run() { } }; dragSegment = new Runnable() { public void run() { //Drag if (stopDrag.getAndSet(false)) { engine.stopDrag(); } if (startDrag.getAndSet(false)) { engine.startDrag(); } if (drag.getAndSet(false)) { engine.mouseDrag(); } } }; mouseClickSegment = new Runnable() { public void run() { engine.mouseClick(); } }; } @Override public synchronized void start() { simpleFPSAnimator = new SimpleFPSAnimator(this, graphDrawable, fpsLimit); simpleFPSAnimator.start(); } @Override public synchronized void stop() { if (simpleFPSAnimator == null) { return; } if (simpleFPSAnimator.isAnimating()) { simpleFPSAnimator.shutdown(); } cameraMoved.set(false); mouseMoved.set(false); objectsMoved.set(false); startDrag.set(false); drag.set(false); stopDrag.set(false); mouseClick.set(false); } public boolean isAnimating() { if (simpleFPSAnimator != null && simpleFPSAnimator.isAnimating()) { return true; } return false; } @Override public void display(GL gl, GLU glu) { if (simpleFPSAnimator.isDisplayCall()) { this.gl = gl; this.glu = glu; //Boolean vals boolean execMouseClick = mouseClick.getAndSet(false); boolean execMouseMove = mouseMoved.getAndSet(false); boolean execDrag = drag.get() || startDrag.get() || stopDrag.get(); //Calculate permits int pool1Permit = 0; int pool2Permit = 0; if (execMouseMove) { pool2Permit++; } else if (execDrag) { pool2Permit++; } if (execMouseClick) { pool2Permit++; } if (cameraMoved.getAndSet(false)) { graphDrawable.setCameraPosition(gl, glu); pool1Permit = modelSegments.size(); engine.getOctree().updateVisibleOctant(gl); //Objects iterators in octree are ready //Task MODEL for (int i = 0; i < modelSegments.size(); i++) { Runnable r = modelSegments.get(i); pool1.execute(r); } } //Task SELECTED if (execMouseMove) { engine.updateSelection(gl, glu); pool2.execute(selectionSegment); } else if (execDrag) { pool2.execute(dragSegment); } //Task AFTERSELECTION if (execMouseClick) { pool2.execute(mouseClickSegment); } try { if (pool1Permit > 0) { pool1Semaphore.acquire(pool1Permit); } } catch (Exception e) { e.printStackTrace(); } //Display engine.beforeDisplay(gl, glu); engine.display(gl, glu); engine.afterDisplay(gl, glu); try { if (pool2Permit > 0) { pool2Semaphore.acquire(pool2Permit); } } catch (Exception e) { e.printStackTrace(); } } } public void updateWorld() { if (engine.updateWorld()) { cameraMoved.set(true); mouseMoved.set(true); } } @Override public void updatePosition() { if (objectsMoved.getAndSet(false)) { engine.updateObjectsPosition(); cameraMoved.set(true); } } @Override public void requireUpdateVisible() { cameraMoved.set(true); } @Override public void requireUpdateSelection() { mouseMoved.set(true); } @Override public void requireStartDrag() { startDrag.set(true); } @Override public void requireDrag() { drag.set(true); } @Override public void requireStopDrag() { stopDrag.set(true); } @Override public void requireUpdatePosition() { objectsMoved.set(true); } @Override public void requireMouseClick() { mouseClick.set(true); } public void setFps(float maxFps) { this.fpsLimit = maxFps; if (simpleFPSAnimator != null) { simpleFPSAnimator.setFps(maxFps); } } public float getFps() { return fpsLimit; } }