/* 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.bridge; import java.util.ArrayList; import java.util.List; import org.gephi.data.attributes.type.TimeInterval; import org.gephi.dynamic.DynamicUtilities; import org.gephi.dynamic.api.DynamicController; import org.gephi.dynamic.api.DynamicModel; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphController; import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.Group; import org.gephi.graph.api.HierarchicalGraph; import org.gephi.graph.api.Node; import org.gephi.graph.api.Model; import org.gephi.graph.api.NodeIterable; import org.gephi.visualization.GraphLimits; import org.gephi.visualization.VizArchitecture; import org.gephi.visualization.VizController; import org.gephi.visualization.apiimpl.ModelImpl; import org.gephi.visualization.apiimpl.VizConfig; import org.gephi.visualization.api.initializer.Modeler; import org.gephi.visualization.api.objects.ModelClass; import org.gephi.visualization.hull.ConvexHull; import org.gephi.visualization.mode.ModeManager; import org.gephi.visualization.opengl.AbstractEngine; import org.gephi.visualization.opengl.compatibility.objects.Arrow2dModel; import org.gephi.visualization.opengl.compatibility.objects.ConvexHullModel; import org.gephi.visualization.opengl.compatibility.objects.Edge2dModel; import org.openide.util.Lookup; /** * * @author Mathieu Bastian */ public class DHNSDataBridge implements DataBridge, VizArchitecture { //Architecture protected AbstractEngine engine; protected GraphController controller; protected HierarchicalGraph graph; private VizConfig vizConfig; protected ModeManager modeManager; protected GraphLimits limits; protected DynamicModel dynamicModel; protected boolean undirected = false; //Version protected int nodeVersion = -1; protected int edgeVersion = -1; protected int graphView = 0; protected GraphModel gm; //Attributes private int cacheMarker = 0; @Override public void initArchitecture() { this.engine = VizController.getInstance().getEngine(); controller = Lookup.getDefault().lookup(GraphController.class); this.vizConfig = VizController.getInstance().getVizConfig(); this.modeManager = VizController.getInstance().getModeManager(); this.limits = VizController.getInstance().getLimits(); } public void updateWorld() { //System.out.println("update world"); cacheMarker++; GraphModel graphModel = controller.getModel(); if (graphModel == null) { engine.worldUpdated(cacheMarker); return; } if (gm != null && gm != graphModel) { reset(); } gm = graphModel; HierarchicalGraph graph; if (graphModel.isDirected()) { undirected = false; graph = graphModel.getHierarchicalDirectedGraphVisible(); } else if (graphModel.isUndirected()) { undirected = true; graph = graphModel.getHierarchicalUndirectedGraphVisible(); } else if (graphModel.isMixed()) { undirected = false; graph = graphModel.getHierarchicalMixedGraphVisible(); } else { undirected = false; graph = graphModel.getHierarchicalDirectedGraphVisible(); } if (dynamicModel == null) { DynamicController dynamicController = Lookup.getDefault().lookup(DynamicController.class); dynamicModel = dynamicController.getModel(); } graphView = graph.getView().getViewId(); ModelClass[] object3dClasses = engine.getModelClasses(); graph.readLock(); ModelClass nodeClass = object3dClasses[AbstractEngine.CLASS_NODE]; if (nodeClass.isEnabled() && (graph.getNodeVersion() > nodeVersion || modeManager.requireModeChange())) { updateNodes(graph); nodeClass.setCacheMarker(cacheMarker); } ModelClass edgeClass = object3dClasses[AbstractEngine.CLASS_EDGE]; if (edgeClass.isEnabled() && (graph.getEdgeVersion() > edgeVersion || modeManager.requireModeChange())) { updateEdges(graph); updateMetaEdges(graph); edgeClass.setCacheMarker(cacheMarker); if (!undirected && vizConfig.isShowArrows()) { object3dClasses[AbstractEngine.CLASS_ARROW].setCacheMarker(cacheMarker); } } ModelClass potatoClass = object3dClasses[AbstractEngine.CLASS_POTATO]; if (potatoClass.isEnabled() && (graph.getNodeVersion() > nodeVersion || modeManager.requireModeChange())) { updatePotatoes(graph); potatoClass.setCacheMarker(cacheMarker); } nodeVersion = graph.getNodeVersion(); edgeVersion = graph.getEdgeVersion(); graph.readUnlock(); engine.worldUpdated(cacheMarker); } private void updateNodes(HierarchicalGraph graph) { Modeler nodeInit = engine.getModelClasses()[AbstractEngine.CLASS_NODE].getCurrentModeler(); NodeIterable nodeIterable; nodeIterable = graph.getNodes(); for (Node node : nodeIterable) { Model obj = node.getNodeData().getModel(); if (obj == null) { //Model is null, ADD obj = nodeInit.initModel(node.getNodeData()); engine.addObject(AbstractEngine.CLASS_NODE, (ModelImpl) obj); } else if (!obj.isValid()) { engine.addObject(AbstractEngine.CLASS_NODE, (ModelImpl) obj); } obj.setCacheMarker(cacheMarker); //Modeaction if (modeManager.getMode().equals(ModeManager.AVAILABLE_MODES.HIGHLIGHT)) { ModelImpl impl = (ModelImpl) obj; // if (!node.isVisible()) { // ColorLayer.layerColor(impl, 0.8f, 0.8f, 0.8f); // } } } } private void updateEdges(HierarchicalGraph graph) { Modeler edgeInit = engine.getModelClasses()[AbstractEngine.CLASS_EDGE].getCurrentModeler(); Modeler arrowInit = engine.getModelClasses()[AbstractEngine.CLASS_ARROW].getCurrentModeler(); EdgeIterable edgeIterable; edgeIterable = graph.getEdges(); float minWeight = Float.POSITIVE_INFINITY; float maxWeight = Float.NEGATIVE_INFINITY; TimeInterval timeInterval = DynamicUtilities.getVisibleInterval(dynamicModel); for (Edge edge : edgeIterable) { if (edge.getSource().getNodeData().getModel() == null || edge.getTarget().getNodeData().getModel() == null) { continue; } float weight = 1f; if (timeInterval == null) { weight = edge.getWeight(); } else { weight = edge.getWeight(timeInterval.getLow(), timeInterval.getHigh()); } minWeight = Math.min(minWeight, weight); maxWeight = Math.max(maxWeight, weight); Edge2dModel obj = (Edge2dModel) edge.getEdgeData().getModel(); if (obj == null) { //Model is null, ADD obj = (Edge2dModel) edgeInit.initModel(edge.getEdgeData()); engine.addObject(AbstractEngine.CLASS_EDGE, obj); if (!undirected && vizConfig.isShowArrows() && !edge.isSelfLoop()) { Arrow2dModel arrowObj = (Arrow2dModel) arrowInit.initModel(edge.getEdgeData()); engine.addObject(AbstractEngine.CLASS_ARROW, arrowObj); arrowObj.setCacheMarker(cacheMarker); arrowObj.setWeight(weight); obj.setArrow(arrowObj); } } else if (!obj.isValid()) { engine.addObject(AbstractEngine.CLASS_EDGE, obj); if (!undirected && vizConfig.isShowArrows() && !edge.isSelfLoop()) { Arrow2dModel arrowObj = obj.getArrow(); engine.addObject(AbstractEngine.CLASS_ARROW, arrowObj); arrowObj.setCacheMarker(cacheMarker); arrowObj.setWeight(weight); } } else { if (!undirected && vizConfig.isShowArrows() && !edge.isSelfLoop() && edge.isDirected()) { Arrow2dModel arrowObj = obj.getArrow(); arrowObj.setCacheMarker(cacheMarker); arrowObj.setWeight(weight); } } obj.setWeight(weight); obj.setCacheMarker(cacheMarker); } limits.setMinWeight(minWeight); limits.setMaxWeight(maxWeight); } public void updateMetaEdges(HierarchicalGraph graph) { Modeler edgeInit = engine.getModelClasses()[AbstractEngine.CLASS_EDGE].getCurrentModeler(); Modeler arrowInit = engine.getModelClasses()[AbstractEngine.CLASS_ARROW].getCurrentModeler(); float minWeight = Float.POSITIVE_INFINITY; float maxWeight = Float.NEGATIVE_INFINITY; TimeInterval timeInterval = DynamicUtilities.getVisibleInterval(dynamicModel); for (Edge edge : graph.getMetaEdges()) { if (edge.getSource().getNodeData().getModel() == null || edge.getTarget().getNodeData().getModel() == null) { continue; } float weight = 1f; if (timeInterval == null) { weight = edge.getWeight(); } else { weight = edge.getWeight(timeInterval.getLow(), timeInterval.getHigh()); } minWeight = Math.min(minWeight, weight); maxWeight = Math.max(maxWeight, weight); Edge2dModel obj = (Edge2dModel) edge.getEdgeData().getModel(); if (obj == null) { //Model is null, ADD obj = (Edge2dModel) edgeInit.initModel(edge.getEdgeData()); engine.addObject(AbstractEngine.CLASS_EDGE, obj); if (!undirected && vizConfig.isShowArrows() && !edge.isSelfLoop()) { Arrow2dModel arrowObj = (Arrow2dModel) arrowInit.initModel(edge.getEdgeData()); engine.addObject(AbstractEngine.CLASS_ARROW, arrowObj); arrowObj.setCacheMarker(cacheMarker); arrowObj.setWeight(weight); obj.setArrow(arrowObj); } } else if (!obj.isValid()) { engine.addObject(AbstractEngine.CLASS_EDGE, obj); if (!undirected && vizConfig.isShowArrows() && !edge.isSelfLoop()) { Arrow2dModel arrowObj = obj.getArrow(); engine.addObject(AbstractEngine.CLASS_ARROW, arrowObj); arrowObj.setCacheMarker(cacheMarker); arrowObj.setWeight(weight); } } else { if (!undirected && vizConfig.isShowArrows() && !edge.isSelfLoop() && edge.isDirected()) { Arrow2dModel arrowObj = obj.getArrow(); arrowObj.setCacheMarker(cacheMarker); arrowObj.setWeight(weight); } } obj.setWeight(weight); obj.setCacheMarker(cacheMarker); } limits.setMinMetaWeight(minWeight); limits.setMaxMetaWeight(maxWeight); } public void updatePotatoes(HierarchicalGraph graph) { ModelClass potatoClass = engine.getModelClasses()[AbstractEngine.CLASS_POTATO]; if (potatoClass.isEnabled()) { Modeler potInit = engine.getModelClasses()[AbstractEngine.CLASS_POTATO].getCurrentModeler(); List<ModelImpl> hulls = new ArrayList<ModelImpl>(); Node[] nodes = graph.getNodes().toArray(); for (Node n : nodes) { Node parent = graph.getParent(n); if (parent != null) { Group group = (Group) parent; Model hullModel = group.getGroupData().getHullModel(); if (hullModel != null && hullModel.isCacheMatching(cacheMarker)) { ConvexHull hull = (ConvexHull) hullModel.getObj(); hull.addNode(n); hull.setModel(hullModel); } else if (hullModel != null) { //Its not the first time the hull exist ConvexHullModel model = (ConvexHullModel) hullModel; model.setScale(1f); hullModel.setCacheMarker(cacheMarker); hulls.add((ModelImpl) hullModel); } else { ConvexHull ch = new ConvexHull(); ch.setMetaNode(parent); ch.addNode(n); ModelImpl obj = potInit.initModel(ch); group.getGroupData().setHullModel(obj); obj.setCacheMarker(cacheMarker); hulls.add(obj); } } } for (ModelImpl im : hulls) { ConvexHull hull = (ConvexHull) im.getObj(); hull.recompute(); engine.addObject(AbstractEngine.CLASS_POTATO, im); } } } public boolean requireUpdate() { if (graph == null) { //Try to get a graph GraphModel graphModel = controller.getModel(); if (graphModel != null) { graph = graphModel.getHierarchicalGraphVisible(); } } //Refresh reader if sight changed Graph g = graph; if (g != null) { if (g.getGraphModel().getVisibleView().getViewId() != graphView) { reset(); } return g.getNodeVersion() > nodeVersion || g.getEdgeVersion() > edgeVersion; } return false; } public void resetGraph() { graph = null; dynamicModel = null; } public void reset() { nodeVersion = -1; edgeVersion = -1; } private void resetClasses() { for (ModelClass objClass : engine.getModelClasses()) { if (objClass.isEnabled()) { engine.resetObjectClass(objClass); } } } }