/* Copyright 2011-2016 Google Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package com.google.security.zynamics.zylib.yfileswrap.gui.zygraph.functions; import com.google.common.base.Preconditions; import com.google.security.zynamics.zylib.gui.SwingInvoker; import com.google.security.zynamics.zylib.yfileswrap.gui.zygraph.AbstractZyGraph; import com.google.security.zynamics.zylib.yfileswrap.gui.zygraph.settings.ILayoutSettings; import y.layout.BufferedLayouter; import y.layout.CanonicMultiStageLayouter; import y.layout.GraphLayout; import y.layout.IntersectionCalculator; import y.layout.LabelLayoutTranslator; import y.layout.LayoutOrientation; import y.layout.LayoutTool; import y.layout.PortCalculator; import y.layout.PortConstraint; import y.layout.PortConstraintKeys; import y.layout.circular.CircularLayouter; import y.util.DataProviders; import y.view.DefaultGraph2DRenderer; import y.view.Graph2D; import y.view.LayoutMorpher; import y.view.NodeRealizerIntersectionCalculator; public class LayoutFunctions { public final static int PREFERRED_ANIMATION_TIME_CONSTANT_FACTOR_MS = 100; /** * Layouts the graph using the last set layouter that was passed to setLayouter. */ public static GraphLayout doLayout(final AbstractZyGraph<?, ?> graph, final CanonicMultiStageLayouter layouter) { Preconditions.checkNotNull(layouter, "Internal Error: Can not layout the graph without initializing the layouter first"); GraphLayout graphLayout = null; final ILayoutSettings layoutSettings = graph.getSettings().getLayoutSettings(); if (layoutSettings.getCurrentLayouter().getLayoutOrientation() == LayoutOrientation.TOP_TO_BOTTOM) { graph.getGraph().addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, DataProviders.createConstantDataProvider(PortConstraint.create(PortConstraint.SOUTH))); graph.getGraph().addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, DataProviders.createConstantDataProvider(PortConstraint.create(PortConstraint.NORTH))); } if (layoutSettings.getCurrentLayouter().getLayoutOrientation() == LayoutOrientation.LEFT_TO_RIGHT) { graph.getGraph().addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, DataProviders.createConstantDataProvider(PortConstraint.create(PortConstraint.EAST))); graph.getGraph().addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, DataProviders.createConstantDataProvider(PortConstraint.create(PortConstraint.WEST))); } layouter.setLabelLayouter(new LabelLayoutTranslator()); layouter.setLabelLayouterEnabled(true); if ((graph.getNodes().size() < layoutSettings.getAnimateLayoutNodeThreshold()) && (graph.getEdges().size() < layoutSettings.getAnimateLayoutEdgeThreshold())) { if (graph.getSettings().getLayoutSettings().getAnimateLayout()) { ((DefaultGraph2DRenderer) graph.getView().getGraph2DRenderer()).setDrawEdgesFirst(true); graphLayout = new BufferedLayouter(layouter).calcLayout(graph.getGraph()); final LayoutMorpher layoutMorpher = new LayoutMorpher(); layoutMorpher.setSmoothViewTransform(true); layoutMorpher.setPreferredDuration(PREFERRED_ANIMATION_TIME_CONSTANT_FACTOR_MS * graph.getSettings().getDisplaySettings().getAnimationSpeed()); final GraphLayout morpherLayout = graphLayout; new SwingInvoker() { @Override protected void operation() { layoutMorpher.execute(graph.getView(), morpherLayout); } }.invokeLater(); recalculatePorts(layouter, graph.getGraph()); } else { graphLayout = new BufferedLayouter(layouter).calcLayout(graph.getGraph()); LayoutTool.applyGraphLayout(graph.getGraph(), graphLayout); recalculatePorts(layouter, graph.getGraph()); } } else { graphLayout = new BufferedLayouter(layouter).calcLayout(graph.getGraph()); LayoutTool.applyGraphLayout(graph.getGraph(), graphLayout); final LayoutMorpher layoutMorpher = new LayoutMorpher(); layoutMorpher.setPreferredDuration(PREFERRED_ANIMATION_TIME_CONSTANT_FACTOR_MS * graph.getSettings().getDisplaySettings().getAnimationSpeed()); layoutMorpher.execute(graph.getView(), graphLayout); } return graphLayout; } public static void recalculatePorts(final CanonicMultiStageLayouter layouter, final Graph2D graph) { // Effect: Ensures that ports are drawn onto node borders, and not onto the node center. (Only // Circular layout!) // Justification: Circular layout uses as the standard port the center of the node, this will be // corrected by // calling the following function. // Exclusion: Port of nodes with non rectangle shapes, have to be additionally recalculated. if (layouter instanceof CircularLayouter) { // Port correction LayoutTool.clipEdgesOnBB(graph); // Recalculate ports (necessary for circular proximity nodes) final PortCalculator pc = new PortCalculator(); final NodeRealizerIntersectionCalculator nrics = new NodeRealizerIntersectionCalculator(graph, true); graph.addDataProvider(IntersectionCalculator.SOURCE_INTERSECTION_CALCULATOR_DPKEY, nrics); final NodeRealizerIntersectionCalculator nrict = new NodeRealizerIntersectionCalculator(graph, false); graph.addDataProvider(IntersectionCalculator.TARGET_INTERSECTION_CALCULATOR_DPKEY, nrict); pc.doLayout(graph); } } }