/** * Calculates the average aspect ratio of the given list of nodes if the provided height may be * used. */ private <T> double calcAvgAspect(List<ITreeMapNode<T>> l, double layoutHeight, double areaScale) { if (l.isEmpty()) { return 1e8; } double area = 0; for (ITreeMapNode<T> node : l) { area += node.getArea(); } double layoutWidth = area * areaScale / layoutHeight; double aspectSum = 0; for (ITreeMapNode<T> node : l) { double localHeight = node.getArea() * areaScale / layoutWidth; double localAspect = Math.max(layoutWidth / localHeight, localHeight / layoutWidth); aspectSum += localAspect; } return aspectSum / l.size(); }
/** * Layout the given list of nodes in one column. * * @param l the list of nodes. * @param layoutHeight the height of the column. * @param layoutX the x start value. * @param layoutY the y start value. * @param areaScale the scale factor used to convert from node area to layout area. * @return the layout width of the column. */ private <T> double layoutList( List<ITreeMapNode<T>> l, double layoutHeight, double layoutX, double layoutY, double areaScale) { double nodeArea = 0; for (ITreeMapNode<T> node : l) { nodeArea += node.getArea(); } double layoutWidth = nodeArea * areaScale / layoutHeight; for (ITreeMapNode<T> node : l) { double nodeHeight = node.getArea() * areaScale / layoutWidth; node.setLayoutRectangle(new Rectangle2D.Double(layoutX, layoutY, layoutWidth, nodeHeight)); layoutY += nodeHeight; layoutChildren(node); } return layoutWidth; }
/** Layouts the children of the given node (if it has any). */ private <T> void layoutChildren(ITreeMapNode<T> node) { if (node.getChildren().isEmpty()) { return; } Rectangle2D rect = node.getLayoutRectangle(); double scale = rect.getWidth() * rect.getHeight() / node.getArea(); double layoutX = rect.getMinX(); double lastX = 0; List<ITreeMapNode<T>> l = new ArrayList<ITreeMapNode<T>>(); List<ITreeMapNode<T>> lastList = new ArrayList<ITreeMapNode<T>>(); for (ITreeMapNode<T> child : node.getChildren()) { double oldAspect = calcAvgAspect(l, rect.getHeight(), scale); l.add(child); double newAspect = calcAvgAspect(l, rect.getHeight(), scale); if (oldAspect < newAspect) { l.remove(l.size() - 1); lastX = layoutX; lastList.clear(); lastList.addAll(l); layoutX += layoutList(l, rect.getHeight(), layoutX, rect.getMinY(), scale); l.clear(); l.add(child); } } // last list might be too small, so potentially merge with previous one lastList.addAll(l); if (calcAvgAspect(lastList, rect.getHeight(), scale) < calcAvgAspect(l, rect.getHeight(), scale)) { layoutList(lastList, rect.getHeight(), lastX, rect.getMinY(), scale); } else { layoutList(l, rect.getHeight(), layoutX, rect.getMinY(), scale); } }
/** {@inheritDoc} */ @Override public <T> void layout(ITreeMapNode<T> tree, Rectangle2D target) { tree.setLayoutRectangle(target); layoutChildren(tree); }