package org.openlca.app.editors.graphical.layout; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.openlca.app.editors.graphical.model.ProcessNode; import org.openlca.app.editors.graphical.model.ProductSystemNode; import org.openlca.app.editors.graphical.search.MutableProcessLinkSearchMap; import org.openlca.core.model.ProcessLink; import org.openlca.core.model.ProductSystem; public class TreeLayout { private Map<Long, Integer> containing = new HashMap<>(); private Map<Integer, Integer> heights = new HashMap<>(); private Map<Integer, Integer> widths = new HashMap<>(); private Map<Point, Long> locations = new HashMap<>(); private MutableProcessLinkSearchMap linkSearch; private void applyLayout(Node node, int addition, int actualDepth) { int x = actualDepth; int y = node.getSize() / 2 + addition; while (locations.get(new Point(x, y)) != null) { y++; addition++; } locations.put(new Point(x, y), node.processId); for (int i = 0; i < node.leftChildren.size(); i++) { Node child = node.leftChildren.get(i); int sizeAddition = 0; for (int j = 0; j < i; j++) sizeAddition += node.leftChildren.get(j).getSize(); applyLayout(child, addition + sizeAddition, actualDepth - 1); } for (int i = 0; i < node.rightChildren.size(); i++) { Node child = node.rightChildren.get(i); int sizeAddition = 0; for (int a = 0; a < node.leftChildren.size(); a++) sizeAddition += node.leftChildren.get(a).getSize(); for (int j = 0; j < i; j++) sizeAddition += node.rightChildren.get(j).getSize(); applyLayout(child, addition + sizeAddition, actualDepth + 1); } } private Node build(ProductSystem productSystem) { Node node = new Node(); node.processId = productSystem.getReferenceProcess().getId(); build(productSystem, new Node[] { node }); return node; } private void build(ProductSystem productSystem, Node[] nodes) { List<Node> children = new ArrayList<>(); for (Node node : nodes) { long processId = node.processId; for (ProcessLink link : linkSearch.getLinks(processId)) { if (link.processId != processId) continue; long providerId = link.providerId; if (containing.get(providerId) != null) continue; Node child = new Node(); child.processId = providerId; node.leftChildren.add(child); containing.put(child.processId, 1); children.add(child); } } if (children.size() > 0) build(productSystem, children.toArray(new Node[children.size()])); children.clear(); for (Node node : nodes) { long processId = node.processId; for (ProcessLink link : linkSearch.getLinks(processId)) { if (link.providerId != processId) continue; long recipientId = link.processId; if (containing.get(recipientId) != null) continue; Node child = new Node(); child.processId = recipientId; node.rightChildren.add(child); containing.put(child.processId, 1); children.add(child); } } if (children.size() > 0) build(productSystem, children.toArray(new Node[children.size()])); } private void prepare(ProductSystemNode productSystemNode) { for (ProcessNode processNode : productSystemNode.getChildren()) { if (processNode.isVisible()) continue; long id = processNode.process.getId(); Dimension size = processNode.getSize(); containing.put(id, 1); processNode.setXyLayoutConstraints(new Rectangle(0, 0, size.width, size.height)); } for (long processId : productSystemNode.getProductSystem().getProcesses()) if (productSystemNode.getProcessNode(processId) == null) containing.put(processId, 1); } public Map<Integer, Integer> getHeights() { return heights; } public Map<Integer, Integer> getWidths() { return widths; } public void layout(ProductSystemNode productSystemNode) { this.linkSearch = productSystemNode.linkSearch; prepare(productSystemNode); List<Node> nodes = new ArrayList<>(); Node mainNode = build(productSystemNode.getProductSystem()); mainNode.sort(); nodes.add(mainNode); for (ProcessNode processNode : productSystemNode.getChildren()) { if (containing.get(processNode.process.getId()) != null) continue; Node node = new Node(); node.processId = processNode.process.getId(); build(productSystemNode.getProductSystem(), new Node[] { node }); node.sort(); nodes.add(node); } int additionalHeight = 0; for (Node node : nodes) { int newAdditionalHeight = 0; locations.clear(); applyLayout(node, 0, node.getLeftDepth()); int minimumX = Integer.MAX_VALUE; int maximumX = Integer.MIN_VALUE; int minimumY = Integer.MAX_VALUE; int maximumY = Integer.MIN_VALUE; for (Point p : locations.keySet()) { if (p.x < minimumX) minimumX = p.x; if (p.x > maximumX) maximumX = p.x; if (p.y < minimumY) minimumY = p.y; if (p.y > maximumY) maximumY = p.y; } Map<Long, ProcessNode> processNodes = new HashMap<>(); for (ProcessNode processNode : productSystemNode.getChildren()) processNodes.put(processNode.process.getId(), processNode); for (int x = minimumX; x <= maximumX; x++) { widths.put(x, 0); for (int y = minimumY; y <= maximumY; y++) { Long processId = locations.get(new Point(x, y)); if (processId == null) continue; ProcessNode pn = processNodes.get(processId); if (pn == null) continue; Dimension size = pn.getSize(); if (size == null) continue; int width = Math.max(widths.get(x), size.width); widths.put(x, width); } } for (int y = minimumY; y <= maximumY; y++) { heights.put(y, 0); for (int x = minimumX; x <= maximumX; x++) { Long processId = locations.get(new Point(x, y)); if (processId == null) continue; ProcessNode pn = processNodes.get(processId); if (pn == null) continue; Dimension size = pn.getSize(); if (size == null) continue; int height = Math.max(heights.get(y), size.height); heights.put(y, height); } } int xPosition = LayoutManager.H_SPACE; for (int x = minimumX; x <= maximumX; x++) { if (x > minimumX && widths.get(x - 1) > 0) xPosition += widths.get(x - 1) + LayoutManager.H_SPACE; int yPosition = LayoutManager.V_SPACE; for (int y = minimumY; y <= maximumY; y++) { Long processId = locations.get(new Point(x, y)); if (y > minimumY && heights.get(y - 1) > 0) yPosition += heights.get(y - 1) + LayoutManager.V_SPACE; if (processId == null) continue; ProcessNode processNode = processNodes.get(processId); if (processNode == null) continue; Dimension size = processNode.getSize(); if (size == null) continue; processNode.setXyLayoutConstraints(new Rectangle(xPosition, yPosition + additionalHeight, size.width, size.height)); newAdditionalHeight = Math.max(newAdditionalHeight, yPosition + additionalHeight + size.height); } } additionalHeight = newAdditionalHeight + LayoutManager.V_SPACE; } containing.clear(); widths.clear(); heights.clear(); locations.clear(); } private class Node { long processId; List<Node> leftChildren = new ArrayList<>(); List<Node> rightChildren = new ArrayList<>(); int getLeftDepth() { if (leftChildren.size() == 0) return 0; int depth = 1; int depthAdd = 0; for (int i = 0; i < leftChildren.size(); i++) depthAdd = Math.max(depthAdd, leftChildren.get(i).getLeftDepth()); depth += depthAdd; return depth; } int getSize() { if (rightChildren.size() == 0 && leftChildren.size() == 0) return 1; int size = 0; for (int i = 0; i < rightChildren.size(); i++) size += rightChildren.get(i).getSize(); for (int i = 0; i < leftChildren.size(); i++) size += leftChildren.get(i).getSize(); return size; } void sort() { List<Node> temp = new ArrayList<>(); temp.addAll(leftChildren); Collections.sort(temp, (o1, o2) -> { return Integer.compare(o2.getSize(), o1.getSize()); }); leftChildren.clear(); int count = 0; int i = 0; while (count < temp.size()) { leftChildren.add(temp.get(i)); count++; if (count < temp.size()) { leftChildren.add(temp.get(temp.size() - i - 1)); count++; } i++; } } } }