All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.conqat.lib.commons.treemap.StripeTreeMapAlgorithm Maven / Gradle / Ivy

There is a newer version: 2024.7.2
Show newest version
/*
 * Copyright (c) CQSE GmbH
 *
 * 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 org.conqat.lib.commons.treemap;

import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;

/**
 * The strip layout algorithm adapted from Bederson, Shneiderman, Wattenberg: "Ordered and Quantum
 * Treemaps".
 * 

* This is useful as it tries to minimize the aspect ratio of the generated squares while * maintaining the original order. * * @author Benjamin Hummel */ public class StripeTreeMapAlgorithm implements ITreeMapLayoutAlgorithm { /** {@inheritDoc} */ @Override public void layout(ITreeMapNode tree, Rectangle2D target) { tree.setLayoutRectangle(target); layoutChildren(tree); } /** Layouts the children of the given node (if it has any). */ private void layoutChildren(ITreeMapNode 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> l = new ArrayList<>(); List> lastList = new ArrayList<>(); for (ITreeMapNode 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); } } /** * Calculates the average aspect ratio of the given list of nodes if the provided height may be * used. */ private static double calcAvgAspect(List> l, double layoutHeight, double areaScale) { if (l.isEmpty()) { return 1e8; } double area = 0; for (ITreeMapNode node : l) { area += node.getArea(); } double layoutWidth = area * areaScale / layoutHeight; double aspectSum = 0; for (ITreeMapNode 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 double layoutList(List> l, double layoutHeight, double layoutX, double layoutY, double areaScale) { double nodeArea = 0; for (ITreeMapNode node : l) { nodeArea += node.getArea(); } double layoutWidth = nodeArea * areaScale / layoutHeight; for (ITreeMapNode node : l) { double nodeHeight = node.getArea() * areaScale / layoutWidth; node.setLayoutRectangle(new Rectangle2D.Double(layoutX, layoutY, layoutWidth, nodeHeight)); layoutY += nodeHeight; layoutChildren(node); } return layoutWidth; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy