--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.g2d.multileveldiagram;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+\r
+import org.simantics.g2d.diagram.IDiagram;\r
+import org.simantics.g2d.diagram.impl.Diagram;\r
+import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
+import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;\r
+\r
+/**\r
+ * This class describes a composition of a multi-layer diagram. \r
+ * \r
+ * @author Toni Kalajainen\r
+ */\r
+public class LayerComposition {\r
+\r
+ /** Diagram hint, MorphHandlers read this key from the diagram */\r
+ public static final Key KEY_PHASE = new KeyOf(MorphPhase.class);\r
+ \r
+ /** Transition function */\r
+ TransitionFunction function = TransitionFunction.SIGMOID; \r
+ \r
+ /** Diagramming info */\r
+ ArrayList<Node> nodes = new ArrayList<Node>();\r
+ /** All diagrams */\r
+ ArrayList<IDiagram> diagrams = new ArrayList<IDiagram>();\r
+ /** Zoom levels */\r
+ MorphLevel[] levels = null;\r
+ \r
+ /**\r
+ * Add a new diagram to the composition.\r
+ * The top most diagram has startZoom open-ended, and the bottom diagram\r
+ * has endZoom open-ended.\r
+ * \r
+ * @param diagram\r
+ * @param startZoom startZoom in percents or null if open-ended\r
+ * @param endZoom endZoom in percents or null if open-ended\r
+ */\r
+ public void addLayer(IDiagram diagram, Double startZoom, Double endZoom)\r
+ {\r
+ assert(diagram!=null); \r
+ if (startZoom!=null && endZoom!=null)\r
+ assert(startZoom<endZoom);\r
+ assert(startZoom!=null || endZoom!=null);\r
+ if (startZoom!=null) {\r
+ nodes.add(new Node(diagram, startZoom));\r
+ }\r
+ if (endZoom!=null) {\r
+ nodes.add(new Node(diagram, endZoom));\r
+ }\r
+ levels = null;\r
+ }\r
+ \r
+ public void removeLayer(IDiagram diagram)\r
+ {\r
+ Iterator<Node> i = nodes.iterator();\r
+ while (i.hasNext()) {\r
+ Node n = i.next();\r
+ if (n.diagram == diagram)\r
+ i.remove();\r
+ }\r
+ levels = null;\r
+ }\r
+ \r
+ /**\r
+ * Builds Zoom Levels.\r
+ */\r
+ private void _buildZoomLevels()\r
+ {\r
+ diagrams.clear();\r
+ Collections.sort(nodes);\r
+ int c = nodes.size();\r
+ ArrayList<MorphLevel> levels = new ArrayList<MorphLevel>(c+1);\r
+ MorphLevel prevZL = null;\r
+ for (int i = 0; i<=c; i++ )\r
+ {\r
+ MorphLevel zl = new MorphLevel();\r
+ if (i==0) {\r
+ zl.ud = nodes.get(0).diagram;\r
+ zl.ld = nodes.get(0).diagram;\r
+ zl.ul = -Double.MAX_VALUE;\r
+ zl.ll = nodes.get(0).level;\r
+ } else if (i==c) {\r
+ zl.ud = nodes.get(c-1).diagram;\r
+ zl.ld = nodes.get(c-1).diagram;\r
+ zl.ul = nodes.get(c-1).level;\r
+ zl.ll = Double.MAX_VALUE;\r
+ } else {\r
+ zl.ud = nodes.get(i-1).diagram;\r
+ zl.ld = nodes.get(i ).diagram;\r
+ zl.ul = nodes.get(i-1).level;\r
+ zl.ll = nodes.get(i ).level;\r
+ }\r
+\r
+ if (prevZL!=null && prevZL.ld == zl.ld && prevZL.ud == zl.ud)\r
+ {\r
+ prevZL.ll = Math.max(prevZL.ll, zl.ll);\r
+ prevZL.ul = Math.min(prevZL.ul, zl.ul);\r
+ zl = prevZL;\r
+ } else {\r
+ prevZL = zl;\r
+ levels.add(zl);\r
+ } \r
+ if (i>0) {\r
+ nodes.get(i-1).upper = prevZL;\r
+ nodes.get(i-1).lower = zl;\r
+ }\r
+ \r
+ if (zl.ud!=null && !diagrams.contains(zl.ud))\r
+ diagrams.add(zl.ud);\r
+ if (zl.ld!=null && !diagrams.contains(zl.ld))\r
+ diagrams.add(zl.ld);\r
+ }\r
+ this.levels = levels.toArray(new MorphLevel[levels.size()]);\r
+ }\r
+ \r
+ /**\r
+ * Get ordered list of zoom levels starting from the top most level \r
+ * (which is diagram)\r
+ * @return ordered list of zoom levels\r
+ */\r
+ public MorphLevel[] getMorphLevels()\r
+ {\r
+ if (levels==null) _buildZoomLevels();\r
+ return levels;\r
+ }\r
+ \r
+ public MorphPhase getTransitionInfo(double zoomLevel)\r
+ {\r
+ if (levels==null) _buildZoomLevels();\r
+ MorphLevel zl = null;\r
+ for (int i=0; i<levels.length; i++)\r
+ {\r
+ zl = levels[i];\r
+ if (zl.isInLevel(zoomLevel)) break;\r
+ }\r
+ \r
+ MorphPhase zp = new MorphPhase();\r
+ zp.level = zl;\r
+ if (zl.ld==null || zl.ud==null || zl.ld==zl.ud)\r
+ return zp;\r
+ \r
+ // Interpolate phase\r
+ zp.phase = (zoomLevel - zl.ul) / (zl.ll - zl.ul); \r
+ zp.phase = function.f(zp.phase);\r
+ if (zp.phase<0) zp.phase = 0;\r
+ if (zp.phase>1) zp.phase = 1;\r
+ return zp;\r
+ }\r
+ \r
+ public void setTransitionFunction(TransitionFunction function)\r
+ {\r
+ this.function = function;\r
+ }\r
+\r
+ public TransitionFunction getFunction() {\r
+ return function;\r
+ }\r
+ \r
+ /**\r
+ * Diagrams from (first occurance) top to down order\r
+ * @return\r
+ */\r
+ public List<IDiagram> getDiagrams() {\r
+ if (levels==null) _buildZoomLevels();\r
+ return Collections.unmodifiableList(diagrams);\r
+ }\r
+ \r
+ @Override\r
+ public String toString() {\r
+ return getDiagrams().toString();\r
+ }\r
+ \r
+ /** Information about a single layer */\r
+ public static class LayerInfo {\r
+ public double ul, ll;\r
+ public IDiagram diagram;\r
+ }\r
+\r
+ public List<LayerInfo> buildMorphLayers()\r
+ {\r
+ List<LayerInfo> result = new ArrayList<LayerInfo>();\r
+ \r
+ for (MorphLevel ml : getMorphLevels())\r
+ {\r
+ // Upper and lower layers are the same\r
+ if (ml.ud != null && ml.ud==ml.ld)\r
+ { \r
+ LayerInfo li = new LayerInfo();\r
+ li.diagram = ml.ud;\r
+ li.ll = ml.ll;\r
+ li.ul = ml.ul;\r
+ result.add(li);\r
+ continue;\r
+ }\r
+ // Build morphing layer\r
+ IDiagram ud = ml.ud == null ? Diagram.EMPTY_DIAGRAM : ml.ud;\r
+ IDiagram ld = ml.ld == null ? Diagram.EMPTY_DIAGRAM : ml.ld;\r
+ LayerInfo li = new LayerInfo();\r
+ li.ll = ml.ll;\r
+ li.ul = ml.ul;\r
+ li.diagram = TransitionDiagram.createTransitionDiagram(ud, ld, TransitionDiagram.MORPH_ELEMENT_CLASS);\r
+ result.add(li);\r
+ }\r
+ \r
+ return result;\r
+ }\r
+ \r
+ private static class Node implements Comparable<Node> {\r
+ double level;\r
+ IDiagram diagram;\r
+ MorphLevel upper, lower;\r
+ @Override\r
+ public int compareTo(Node n) {\r
+ double diff = level - n.level;\r
+ if (diff<0) return -1;\r
+ if (diff>0) return 1;\r
+ return 0;\r
+ }\r
+ public Node(IDiagram diagram, double level) {\r
+ this.level = level;\r
+ this.diagram = diagram;\r
+ } \r
+ }\r
+ public static class MorphPhase {\r
+ public MorphLevel level;\r
+ /** Phase, 0==upper .. 1==lower */\r
+ public double phase;\r
+ @Override\r
+ public String toString() {\r
+ return phase + "\t"+level;\r
+ }\r
+ }\r
+ \r
+ public static class MorphLevel {\r
+ /**\r
+ * Upper and lower diagrams (one may be null)\r
+ */\r
+ public IDiagram ud, ld;\r
+ public double ul, ll;\r
+ public IDiagram diagram;\r
+ \r
+ /**\r
+ * Is zoom level locked to a diagram\r
+ * @return\r
+ */\r
+ public boolean isLocked()\r
+ {\r
+ return ud == ld;\r
+ }\r
+ \r
+ public boolean isInTransition()\r
+ {\r
+ return ud != ld;\r
+ }\r
+ \r
+ public boolean isInLevel(double zoomLevel)\r
+ {\r
+ return zoomLevel >=ul && zoomLevel<=ll;\r
+ }\r
+ \r
+ @Override\r
+ public String toString() {\r
+ String un = "[]";\r
+ if (ud!=null) un = ud.toString();\r
+ String ln = "[]";\r
+ if (ld!=null) ln = ld.toString();\r
+ \r
+ return un+" .. "+ln;\r
+ }\r
+ \r
+ } \r
+ \r
+}\r