/* ****************************************************************************** * Copyright (c) 2006-2012 XMind Ltd. and others. * * This file is a part of XMind 3. XMind releases 3 and * above are dual-licensed under the Eclipse Public License (EPL), * which is available at http://www.eclipse.org/legal/epl-v10.html * and the GNU Lesser General Public License (LGPL), * which is available at http://www.gnu.org/licenses/lgpl.html * See http://www.xmind.net/license.html for details. * * Contributors: * XMind Ltd. - initial API and implementation *******************************************************************************/ package org.xmind.ui.internal.branch; import static java.lang.Math.log; import static java.lang.Math.max; import static java.lang.Math.sqrt; import static org.xmind.ui.internal.branch.BaseRadialStructure.CACHE_NUMBER_RIGHT_BRANCHES; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Insets; import org.xmind.ui.branch.BranchStructureData; import org.xmind.ui.mindmap.IBranchPart; import org.xmind.ui.mindmap.ITopicPart; import org.xmind.ui.util.MindMapUtils; public class RadialData extends BranchStructureData { private static final int Threshold = 200; private int defaultSumSpacing = -1; private int defaultMinSumSpacing = -1; private int numRight = -1; private Dimension ovalSize = null; private int sumSpacing = -1; private int rightSumSpacing = -1; private int rightSpacingAddIn = -1; private int leftSumSpacing = -1; private int leftSpacingAddIn = -1; private int[] childrenSpacing = null; private Map<IBranchPart, Integer> weights = null; public RadialData(IBranchPart branch) { super(branch); } public int getDefaultMinSumSpacing() { if (defaultMinSumSpacing < 0) { defaultMinSumSpacing = calculateDefaultMinSumSpacing(); } return defaultMinSumSpacing; } private int calculateDefaultMinSumSpacing() { return getMinorSpacing2() * 8; } public int getDefaultSumSpacing() { if (defaultSumSpacing < 0) { defaultSumSpacing = calculateDefaultSumSpacing(); } return defaultSumSpacing; } private int calculateDefaultSumSpacing() { return getMinorSpacing2() * 13; } public int getNumRight() { Integer num = getCachedNumRight(); if (num != null) return num.intValue(); if (numRight < 0) { numRight = calculateNumRight(); } return numRight; } private int calculateNumRight() { int num = getSubBranches().size(); if (num > 1) { int totalWeight = getTotalWeight(); num = calculateNumRight(getSplitWeight(totalWeight), totalWeight); // if (totalWeight < getThreshold(num)) { // num = max(2, num); // } } return num; } private Integer getCachedNumRight() { return (Integer) MindMapUtils.getCache(getBranch(), CACHE_NUMBER_RIGHT_BRANCHES); } private int getThreshold(int num) { return (int) (Threshold * (log(num) + 1)); } private boolean isWithinThreshold(int index, int size) { return isWithinThreshold(getSubBranches().get(index), size); } private boolean isWithinThreshold(IBranchPart subBranch, int size) { return getWeight(subBranch) < getThreshold(size); } private int calculateNumRight(int halfWeight, int totalWeight) { int index = 0; int lastIndex = -1; int rightWeight = 0; int blockWeight = 0; List<IBranchPart> subBranches = getSubBranches(); int size = subBranches.size(); for (IBranchPart subBranch : subBranches) { blockWeight += getWeight(subBranch); int num = index + 1; if (!isInSameRangeWithLast(subBranches, index + 1)) { int newRightWeight = rightWeight + blockWeight; if (newRightWeight >= halfWeight) { if (lastIndex >= 0 && newRightWeight - halfWeight > halfWeight - rightWeight) { int lastNum = lastIndex + 1; if (index == 1 && lastIndex == 0 // && (isInSameRangeWithLast(subBranches, index) // || (isWithinThreshold(0, size) // && isWithinThreshold(subBranch, size)))) return 2; return lastNum; } if (index == 0 && isWithinThreshold(subBranch, size) && ((size == 2 // || (size > 2 // && !isInSameRangeWithLast( subBranches, 2))) // && isWithinThreshold(1, size))) { return 2; } return num; } rightWeight = newRightWeight; blockWeight = 0; lastIndex = index; } index++; } return index; } private int getTotalWeight() { int weight = 0; for (IBranchPart subbranch : getSubBranches()) { weight += getWeight(subbranch); } return weight; } private int getWeight(IBranchPart branch) { if (weights == null) { weights = new HashMap<IBranchPart, Integer>(); } Integer weight = weights.get(branch); if (weight == null) { weight = branch.getFigure().getPreferredSize().height + getMinorSpacing2() * 2; weights.put(branch, weight); } return weight.intValue(); } private int getSplitWeight(int weight) { return weight - (weight / 2); } public int getNumLeft() { return getSubBranches().size() - getNumRight(); } public Dimension getOvalSize() { if (ovalSize == null) { ovalSize = calculateOvalSize(); } return ovalSize; } private Dimension calculateOvalSize() { int a; ITopicPart topic = getBranch().getTopicPart(); if (topic != null) { a = topic.getFigure().getPreferredSize().width / 2; } else { a = 0; } int b = getSumSpacing() / 2; int width = a + getMajorSpacing2(); int height = (int) (b / sqrt(1 - (a * a * 1.0) / (width * width))); return new Dimension(width, height); } public int getSumSpacing() { if (sumSpacing < 0) { sumSpacing = calculateSumSpacing(); } return sumSpacing; } private int calculateSumSpacing() { return max(max(getRightSumSpacing(), getLeftSumSpacing()), getDefaultSumSpacing()); } public int getRightSumSpacing() { if (rightSumSpacing < 0) { rightSumSpacing = calculateRightSumSpacing(); } return rightSumSpacing; } private int calculateRightSumSpacing() { return calculateHalfSumSpacing(getNumRight(), true, getRightSpacingAddIn()); } private int calculateHalfSumSpacing(int numHalf, boolean firstOrSecond, int addIn) { if (numHalf <= 0) return 0; if (numHalf == 1) return getDefaultMinSumSpacing(); return calculateSumSpacing(firstOrSecond, addIn); } private int calculateSumSpacing(boolean firstOrSecond, int addIn) { int start = firstOrSecond ? 0 : getNumRight(); int num = firstOrSecond ? getNumRight() : getNumLeft(); int sum = 0; for (int i = start; i < start + num - 1; i++) { sum += calculateChildSpacing(i, firstOrSecond, addIn); } return sum; } private int calculateChildSpacing(int index, boolean firstOrSecond, int addIn) { if (index + 1 < getSubBranches().size()) { Insets ins1 = RadialUtils.getRefInsets( getSubBranches().get(index).getFigure(), firstOrSecond); Insets ins2 = RadialUtils.getRefInsets( getSubBranches().get(index + 1).getFigure(), firstOrSecond); return ins1.bottom + ins2.top + getMinorSpacing2() * 2 + addIn; } return addIn; } private int getMajorSpacing2() { return getMajorSpacing() * 3; } private int getMinorSpacing2() { return getMinorSpacing() * 3 / 4 + 8; } public int getLeftSumSpacing() { if (leftSumSpacing < 0) { leftSumSpacing = calculateLeftSumSpacing(); } return leftSumSpacing; } private int calculateLeftSumSpacing() { return calculateHalfSumSpacing(getNumLeft(), false, getLeftSpacingAddIn()); } public int getRightSpacingAddIn() { if (rightSpacingAddIn < 0) { rightSpacingAddIn = calculateRightSpacingAddIn(); } return rightSpacingAddIn; } private int calculateRightSpacingAddIn() { return calculateHalfSpacingAddIn(getNumRight(), true); } private int calculateHalfSpacingAddIn(int numHalf, boolean firstOrSecond) { if (numHalf <= 1) return 0; int minSum = (numHalf <= 2) ? getDefaultMinSumSpacing() : (int) (getDefaultMinSumSpacing() * f(numHalf)); int sum = calculateSumSpacing(firstOrSecond, 0); return max(minSum - sum, 0) / (numHalf - 1); } private double f(int n) { return log(n) / log(20) + 1; } public int getLeftSpacingAddIn() { if (leftSpacingAddIn < 0) { leftSpacingAddIn = calculateLeftSpacingAddIn(); } return leftSpacingAddIn; } private int calculateLeftSpacingAddIn() { return calculateHalfSpacingAddIn(getNumLeft(), false); } public int[] getChildrenSpacings() { if (childrenSpacing == null) { childrenSpacing = calculateChildrenSpacing(); } return childrenSpacing; } private int[] calculateChildrenSpacing() { int[] list = new int[getSubBranches().size()]; boolean firstOrSecond = true; int addIn = getRightSpacingAddIn(); int numFirst = getNumRight(); for (int i = 0; i < list.length; i++) { if (i == numFirst - 1 || i == list.length - 1) { list[i] = 0; } else { if (i == numFirst) { firstOrSecond = false; addIn = getLeftSpacingAddIn(); } list[i] = calculateChildSpacing(i, firstOrSecond, addIn); } } return list; } public int getX(int y, boolean firstOrSecond) { return firstOrSecond ? getAbsX(y) : -getAbsX(y); } private int getAbsX(int y) { Dimension oval = getOvalSize(); return (int) (oval.width * sqrt(1 - (y * y * 1.0) / (oval.height * oval.height))); } // public int getHalfSumSpacing(boolean firstOrSecond) { // return firstOrSecond ? getRightSumSpacing() : getLeftSumSpacing(); // } }