/* ******************************************************************************
* 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.fishbone.structures;
import java.util.List;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.xmind.gef.GEF;
import org.xmind.gef.draw2d.IAnchor;
import org.xmind.gef.draw2d.IReferencedFigure;
import org.xmind.gef.draw2d.IRotatableReferencedFigure;
import org.xmind.gef.draw2d.ReferencedLayoutData;
import org.xmind.gef.draw2d.geometry.Geometry;
import org.xmind.gef.draw2d.geometry.HorizontalFlipper;
import org.xmind.gef.draw2d.geometry.ITransformer;
import org.xmind.gef.draw2d.geometry.PrecisionDimension;
import org.xmind.gef.draw2d.geometry.PrecisionLine;
import org.xmind.gef.draw2d.geometry.PrecisionLine.LineType;
import org.xmind.gef.draw2d.geometry.PrecisionLine.Side;
import org.xmind.gef.draw2d.geometry.PrecisionPoint;
import org.xmind.gef.draw2d.geometry.PrecisionRectangle;
import org.xmind.gef.part.IPart;
import org.xmind.ui.branch.AbstractBranchStructure;
import org.xmind.ui.branch.BoundaryLayoutHelper;
import org.xmind.ui.branch.IInsertion;
import org.xmind.ui.branch.Insertion;
import org.xmind.ui.internal.figures.TopicFigure;
import org.xmind.ui.internal.fishbone.Fishbone;
import org.xmind.ui.mindmap.IBranchPart;
import org.xmind.ui.mindmap.IBranchRangePart;
import org.xmind.ui.mindmap.INodePart;
import org.xmind.ui.mindmap.IPlusMinusPart;
import org.xmind.ui.mindmap.ISummaryPart;
import org.xmind.ui.mindmap.ITopicPart;
import org.xmind.ui.mindmap.MindMapUI;
import org.xmind.ui.tools.ParentSearchKey;
@SuppressWarnings("restriction")
public class SubFishboneStructure extends AbstractBranchStructure {
private static final double sin = Math.sin(Math
.toRadians(Fishbone.RotateAngle));
private static final double cos = Math.cos(Math
.toRadians(Fishbone.RotateAngle));
private ISubDirection direction;
private ITransformer t = new HorizontalFlipper();
public SubFishboneStructure(ISubDirection direction) {
this.direction = direction;
this.t.setEnabled(false);
}
private double getPadding(IBranchPart branch) {
return getCastedData(branch).getPadding();
}
public void fillLayoutData(IBranchPart branch, ReferencedLayoutData data) {
super.fillLayoutData(branch, data);
data.addMargins(new Insets((int) Math.ceil(getPadding(branch))));
}
protected void doFillPlusMinus(IBranchPart branch,
IPlusMinusPart plusMinus, LayoutInfo info) {
Point ref = info.getReference();
SubFishboneData fd = getCastedData(branch);
fd.r1.setOrigin(ref.x, ref.y);
IFigure figure = plusMinus.getFigure();
Dimension size = figure.getPreferredSize();
PrecisionRectangle topicBounds = getNormalTopicBounds(branch, ref);
int orientation = getSourceOrientation(branch);
double halfWidth = size.width * 0.5d;
double halfHeight = size.height * 0.5d;
double centerX = orientation == PositionConstants.WEST ? topicBounds.x
- halfWidth : topicBounds.right() + halfWidth;
PrecisionPoint center = fd.r1.tp(new PrecisionPoint(centerX, ref.y));
Point loc = center.translate(-halfWidth, -halfHeight).toDraw2DPoint();
info.put(figure, new Rectangle(loc, size));
}
private PrecisionRectangle getNormalTopicBounds(IBranchPart branch,
Point ref) {
ITopicPart topic = branch.getTopicPart();
if (topic != null) {
IFigure figure = topic.getFigure();
if (figure instanceof IRotatableReferencedFigure)
return ((IRotatableReferencedFigure) figure)
.getNormalPreferredBounds(ref);
}
return new PrecisionRectangle(ref.x, ref.y, 0, 0);
}
@Override
protected BoundaryLayoutHelper getBoundaryLayoutHelper(IBranchPart branch) {
return super.getBoundaryLayoutHelper(branch);
}
protected void doFillSubBranches(IBranchPart branch,
List<IBranchPart> subBranches, LayoutInfo info) {
Point ref = info.getReference();
FishboneData fd = getCastedData(branch).getFishboneData();
for (IBranchPart subBranch : subBranches) {
IFigure figure = subBranch.getFigure();
Rectangle rect = fd.getChildPrefBounds(subBranch,
new PrecisionPoint(ref));
if (rect != null)
info.put(figure, rect);
}
}
@Override
protected void doFillCalloutBranches(IBranchPart branch,
List<IBranchPart> calloutBranches, LayoutInfo info) {
Point ref = info.getReference();
FishboneData fd = getCastedData(branch).getFishboneData();
for (IBranchPart calloutBranch : calloutBranches) {
IFigure figure = calloutBranch.getFigure();
Rectangle rect = fd.getChildPrefBounds(calloutBranch,
new PrecisionPoint(ref));
if (rect != null)
info.put(figure, rect);
}
}
protected Object createStructureData(IBranchPart branch) {
return new SubFishboneData(branch, direction);
}
protected boolean isValidStructureData(IBranchPart branch, Object data) {
return super.isValidStructureData(branch, data)
&& (data instanceof SubFishboneData);
}
protected SubFishboneData getCastedData(IBranchPart branch) {
return (SubFishboneData) super.getStructureData(branch);
}
public int calcChildDistance(IBranchPart branch, ParentSearchKey key) {
PrecisionLine boneLine = getBoneLine(branch);
PrecisionPoint source = calcSourceLocation(branch, boneLine);
PrecisionLine sourceRay = getBoneRay(source);
List<IBranchPart> subBranches = branch.getSubBranches();
boolean folded = branch.isFolded();
// PrecisionPoint offset = calcChildOffset(branch, key.getFeedback(),
// boneLine, sourceRay);
PrecisionLine childBoneLine = getChildBoneLine(branch,
key.getFeedback());
double offset = calcChildOffset(boneLine, sourceRay, childBoneLine);
if (offset > 0) {
double range;
if (!subBranches.isEmpty() && !folded) {
int lastIndex = direction.isChildrenTraverseReversed() ? 0
: subBranches.size() - 1;
IBranchPart lastChild = subBranches.get(lastIndex);
// PrecisionPoint lastOffset = calcChildOffset(branch, lastChild,
// boneLine, sourceRay);
double lastOffset = calcChildOffset(boneLine, sourceRay,
getChildBoneLine(branch, lastChild));
range = lastOffset + MindMapUI.SEARCH_RANGE;
} else {
range = MindMapUI.SEARCH_RANGE;
}
if (offset < range) {
double distance = calcChildDistance(boneLine, childBoneLine);
if (distance > 0 && distance < MindMapUI.SEARCH_RANGE)
return Math.max(1, (int) distance);
}
}
return super.calcChildDistance(branch, key);
}
private double calcChildDistance(PrecisionLine boneLine,
PrecisionLine childLine) {
PrecisionPoint target = childLine.getOrigin();
Side childSide = boneLine.getSide(target);
if (needsCalcChildDistance(childSide))
return Geometry.getDistance(target, boneLine);
return -1;
}
private double calcChildOffset(PrecisionLine boneLine,
PrecisionLine sourceRay, PrecisionLine childLine) {
PrecisionPoint joint = boneLine.intersect(childLine);
PrecisionDimension offset = joint.getDifference(sourceRay.getOrigin());
double off = offset.getDiagonal();
if (!sourceRay.contains(joint)) {
off = -off;
}
return off;
}
private boolean needsCalcChildDistance(Side childSide) {
if (childSide == Side.Right)
return direction == ISubDirection.NER
|| direction == ISubDirection.SE
|| direction == ISubDirection.NW
|| direction == ISubDirection.SWR;
if (childSide == Side.Left)
return direction == ISubDirection.NE
|| direction == ISubDirection.SER
|| direction == ISubDirection.NWR
|| direction == ISubDirection.SW;
return false;
}
// private PrecisionPoint calcChildOffset(IBranchPart branch,
// IBranchPart child, PrecisionPoint source) {
//
// PrecisionPoint target = calcChildTargetLocation(branch, child, source);
// PrecisionDimension d = target.getDifference(source);
// SubFishboneStructureData fd = getCastedData(branch);
// double w = d.height * fd.r1.cos() / fd.r1.sin();
// double jointOffset = d.width - w;
// if (direction.isRightHeaded())
// jointOffset = -jointOffset;
// double distance = direction.isDownwards() ? d.height : -d.height;
// PrecisionPoint offset = new PrecisionPoint(jointOffset, distance);
// return direction.isRotated() ? offset.transpose() : offset;
// }
private PrecisionLine getBoneLine(IBranchPart branch) {
IAnchor anchor = ((INodePart) branch.getTopicPart())
.getSourceAnchor(branch);
int orientation = getSourceOrientation(branch);
PrecisionPoint p1 = anchor.getLocation(
Geometry.getOppositePosition(orientation), 0);
PrecisionPoint p2 = anchor.getLocation(orientation, 0);
return new PrecisionLine(p1, p2, LineType.Line);
}
private PrecisionLine getChildBoneLine(IBranchPart branch, IBranchPart child) {
IAnchor anchor = ((INodePart) child.getTopicPart())
.getTargetAnchor(branch);
int orientation = getChildTargetOrientation(branch, child);
PrecisionPoint p1 = anchor.getLocation(orientation, 0);
double angle = direction.getSubDirection().getRotateAngle();
PrecisionPoint p2 = p1.getMoved(Math.toRadians(angle), 100);
return new PrecisionLine(p1, p2, LineType.Line);
}
private PrecisionPoint calcSourceLocation(IBranchPart branch,
PrecisionLine boneLine) {
if (!branch.getSubBranches().isEmpty() && !branch.isFolded()
&& direction.isRotated()) {
PrecisionPoint ref = new PrecisionPoint(((IReferencedFigure) branch
.getTopicPart().getFigure()).getReference());
PrecisionPoint p = ref.getMoved(
Math.toRadians(-direction.getRotateAngle()), 100);
return boneLine.intersect(new PrecisionLine(ref, p, LineType.Line));
}
return ((INodePart) branch.getTopicPart()).getSourceAnchor(branch)
.getLocation(getSourceOrientation(branch), 0);
}
private PrecisionLine getBoneRay(PrecisionPoint p) {
double angle = direction.getRotateAngle();
if (direction == ISubDirection.SER || direction == ISubDirection.NWR
|| direction == ISubDirection.NW
|| direction == ISubDirection.SW) {
angle = 180 + angle;
}
return new PrecisionLine(p, p.getMoved(Math.toRadians(angle), 100),
LineType.Ray);
}
public IInsertion calcInsertion(IBranchPart branch, ParentSearchKey key) {
return new Insertion(branch, calcInsIndex(branch, key, true), key
.getFigure().getSize());
}
protected int calcInsIndex(IBranchPart branch, ParentSearchKey key,
boolean withDisabled) {
if (branch.getSubBranches().isEmpty() || branch.isFolded())
return withDisabled ? 0 : -1;
PrecisionLine boneLine = getBoneLine(branch);
PrecisionPoint source = calcSourceLocation(branch, boneLine);
PrecisionLine sourceRay = getBoneRay(source);
double offset = calcChildOffset(boneLine, sourceRay,
getChildBoneLine(branch, key.getFeedback()));
boolean reversed = direction.isChildrenTraverseReversed();
List<IBranchPart> subBranches = branch.getSubBranches();
int num = subBranches.size();
int ret = 0;
for (IBranchPart subBranch : subBranches) {
double subOffset = calcChildOffset(boneLine, sourceRay,
getChildBoneLine(branch, subBranch));
if (reversed) {
if (offset > subOffset)
return ret;
} else {
if (offset < subOffset)
return ret;
}
if (withDisabled || subBranch.getFigure().isEnabled()) {
ret++;
}
}
return withDisabled ? num : -1;
}
// private PrecisionPoint calcSourceLocation(IBranchPart branch) {
//// return new PrecisionPoint(((IReferencedFigure) branch.getTopicPart()
//// .getFigure()).getReference());
// return ((INodePart) branch.getTopicPart()).getSourceAnchor(branch)
// .getLocation(calcSourceOrientation(branch), 0);
// }
//
// private PrecisionPoint calcChildTargetLocation(IBranchPart branch,
// IBranchPart child, PrecisionPoint source) {
// ITopicPart topic = child.getTopicPart();
// if (topic instanceof INodePart) {
// IAnchor anchor = ((INodePart) topic).getTargetAnchor(branch);
// if (anchor != null) {
// return anchor.getLocation(calcChildTargetOrientation(branch,
// child), 0);
// }
// }
// return new PrecisionPoint(getReference(child));
// }
// private Point getReference(IBranchPart branch) {
// ITopicPart topic = branch.getTopicPart();
// if (topic != null)
// return ((IReferencedFigure) topic.getFigure()).getReference();
// return ((IReferencedFigure) branch.getFigure()).getReference();
// }
// public Object calcNavigation(IBranchPart branch, int direction) {
// int nav = this.direction.calcNavigation(direction);
// if (nav == GEF.NAVI_PREV && branch.getBranchIndex() == 0)
// nav = GEF.NAVI_PARENT;
// return nav;
// }
public IPart calcChildNavigation(IBranchPart branch,
IBranchPart sourceChild, String navReqType, boolean sequential) {
if (direction.isRotated()) {
if (GEF.REQ_NAV_UP.equals(navReqType)) {
return getSubTopicPart(branch, sourceChild.getBranchIndex() - 1);
} else if (GEF.REQ_NAV_DOWN.equals(navReqType)) {
return getSubTopicPart(branch, sourceChild.getBranchIndex() + 1);
} else if (!sequential) {
if (direction.isRightHeaded()) {
if (GEF.REQ_NAV_RIGHT.equals(navReqType)) {
return branch.getTopicPart();
}
} else {
if (GEF.REQ_NAV_LEFT.equals(navReqType)) {
return branch.getTopicPart();
}
}
}
} else {
String prevType = direction.isRightHeaded() ? GEF.REQ_NAV_RIGHT
: GEF.REQ_NAV_LEFT;
String nextType = direction.isRightHeaded() ? GEF.REQ_NAV_LEFT
: GEF.REQ_NAV_RIGHT;
if (prevType.equals(navReqType)) {
ITopicPart prev = getSubTopicPart(branch,
sourceChild.getBranchIndex() - 1);
if (prev == null && !sequential)
return branch.getTopicPart();
return prev;
} else if (nextType.equals(navReqType)) {
return getSubTopicPart(branch, sourceChild.getBranchIndex() + 1);
}
}
return super.calcChildNavigation(branch, sourceChild, navReqType,
sequential);
}
public IPart calcNavigation(IBranchPart branch, String navReqType) {
if (isNavChild(branch, navReqType)) {
if (direction.isChildrenTraverseReversed())
return getSubTopicPart(branch,
branch.getSubBranches().size() - 1);
return getSubTopicPart(branch, 0);
}
return super.calcNavigation(branch, navReqType);
}
private boolean isNavChild(IBranchPart branch, String navReqType) {
if (direction.isRotated()) {
if (direction.isDownwards()) {
if (GEF.REQ_NAV_DOWN.equals(navReqType))
return true;
} else {
if (GEF.REQ_NAV_UP.equals(navReqType))
return true;
}
}
if (direction.isRightHeaded())
return GEF.REQ_NAV_LEFT.equals(navReqType);
return GEF.REQ_NAV_RIGHT.equals(navReqType);
}
public int getChildTargetOrientation(IBranchPart branch,
IBranchPart subBranch) {
if (!branch.getSubBranches().contains(subBranch)) {
return direction.isRightHeaded() ? PositionConstants.EAST
: PositionConstants.WEST;
}
return direction.getChildTargetOrientation();
}
public int getSourceOrientation(IBranchPart branch) {
return direction.getSourceOrientation();
}
public int getRangeGrowthDirection(IBranchPart branch,
IBranchRangePart range) {
return getRangeGrowthDirection();
}
private int getRangeGrowthDirection() {
if (direction.isRotated())
return PositionConstants.SOUTH;
return direction.isRightHeaded() ? PositionConstants.EAST
: PositionConstants.WEST;
}
public int getSummaryDirection(IBranchPart branch, ISummaryPart summary) {
if (direction.isRotated())
return direction.isRightHeaded() ? PositionConstants.EAST
: PositionConstants.EAST;
return direction.isDownwards() ? PositionConstants.SOUTH
: PositionConstants.NORTH;
}
public int getQuickMoveOffset(IBranchPart branch, IBranchPart child,
int direction) {
int rangeGrowthDirection = getRangeGrowthDirection();
if (direction == rangeGrowthDirection)
return 1;
if (direction == Geometry.getOppositePosition(rangeGrowthDirection))
return -1;
return super.getQuickMoveOffset(branch, child, direction);
}
@Override
protected Point calcInsertPosition(IBranchPart branch, IBranchPart child,
ParentSearchKey key) {
List<IBranchPart> subBranches = branch.getSubBranches();
if (subBranches.isEmpty())
return calcFirstChildPosition(branch, key);
int index = calcInsIndex(branch, key, true);
if (index == subBranches.size()) {
return calcInventPosition(subBranches.get(index - 1), null, key,
false);
}
return calcInventPosition(subBranches.get(index), null, key, true);
}
@Override
protected Point calcMovePosition(IBranchPart branch, IBranchPart child,
ParentSearchKey key) {
List<IBranchPart> subBranches = branch.getSubBranches();
List<Integer> disables = getDisableBranches(branch);
int index = calcInsIndex(branch, key, true);
int oldIndex = getOldIndex(branch, child);
if (disables != null) {
if (disables.contains(index - 1)) {
index--;
oldIndex = index;
} else if (disables.contains(index)) {
oldIndex = index;
}
}
Dimension inventSize = key.getInvent().getSize();
if (index == oldIndex) {
if (direction.isRotated()) {
IBranchPart sub = subBranches.get(index);
Dimension size = sub.getTopicPart().getFigure().getSize();
int deltaX = (size.width - inventSize.width) / 2;
return getFigureLocation(sub.getFigure()).getTranslated(
direction.isRightHeaded() ? deltaX : -deltaX, 0);
} else {
IBranchPart sub = subBranches.get(index);
Dimension size = sub.getTopicPart().getFigure().getSize();
double w = (size.height * sin - size.width * cos)
/ (sin * sin - cos * cos);
double deltaX = size.width * 0.5d - w * cos + inventSize.width
* 0.5d + inventSize.width * cos * 0.5d;
double deltaY = direction.isDownwards() ? (-size.height + inventSize.width
* sin) * 0.5d
: (size.height - inventSize.width * sin) * 0.5d;
return getFigureLocation(sub.getFigure()).getTranslated(
direction.isRightHeaded() ? -deltaX : deltaX, deltaY);
}
}
return calcInsertPosition(branch, child, key);
}
@Override
protected Point calcFirstChildPosition(IBranchPart branch,
ParentSearchKey key) {
if (direction.isRotated())
return calcFirstNormalPosition(branch, key);
else
return clacFirstRotatedPosition(branch, key);
}
private Point calcFirstNormalPosition(IBranchPart branch,
ParentSearchKey key) {
Dimension size = branch.getTopicPart().getFigure().getSize();
Dimension inventSize = key.getInvent().getSize();
double h = (size.width * sin - size.height * cos)
/ (sin * sin - cos * cos);
double x = h * sin * 0.5d + inventSize.height * cos / sin
+ inventSize.width * 0.5d;
return getFigureLocation(branch.getFigure()).getTranslated(
direction.isRightHeaded() ? -x : x,
direction.isDownwards() ? inventSize.height * 0.5d
: -inventSize.height * 0.5d);
}
private Point clacFirstRotatedPosition(IBranchPart branch,
ParentSearchKey key) {
Dimension size = branch.getTopicPart().getFigure().getSize();
Dimension inventSize = key.getInvent().getSize();
double x = size.width * 0.5d + key.getFigure().getSize().height * sin
* 0.5d + inventSize.width * (1 + cos) * 0.5d;
return getFigureLocation(branch.getFigure()).getTranslated(
direction.isRightHeaded() ? -x : x,
size.height
* 0.5d
+ (direction.isDownwards() ? inventSize.width * sin
* 0.5d : -inventSize.width * sin * 0.5d));
}
@Override
protected Point calcInventPosition(IBranchPart orientation,
IBranchPart assist, ParentSearchKey key, boolean isBeforeOrientation) {
if (direction.isRotated())
return calcNormalPosition(orientation, key, isBeforeOrientation);
else
return calcRotatedPosition(orientation, key, isBeforeOrientation);
}
private Point calcNormalPosition(IBranchPart orientation,
ParentSearchKey key, boolean isBeforeOrientation) {
Dimension subSize = orientation.getTopicPart().getFigure().getSize();
Point loc = getFigureLocation(orientation.getTopicPart().getFigure());
Rectangle bounds = orientation.getFigure().getBounds();
int top = bounds.y;
int bottom = bounds.bottom();
Dimension insSize = key.getFigure().getSize();
Dimension inventSize = key.getInvent().getSize();
double cot = cos / sin;
double x;
double right = loc.x + (subSize.width - inventSize.width) * 0.5d;
double left = loc.x - (subSize.width - inventSize.width) * 0.5d;
if (isBeforeOrientation) {
if (direction.equals(ISubDirection.NWR)) {
x = right
- (loc.y - top - subSize.height * 0.5d + (insSize.height + inventSize.height) * 0.5d)
* cot;
} else if (direction.equals(ISubDirection.SWR)) {
x = right
+ (loc.y - top + subSize.height * 0.5d + (insSize.height - inventSize.height) * 0.5d)
* cot;
} else if (direction.equals(ISubDirection.NER)) {
x = left
+ (loc.y - top - subSize.height * 0.5 + (insSize.height + inventSize.height) * 0.5d)
* cot;
} else {
x = left
- (loc.y - top + subSize.height * 0.5 + (insSize.height - inventSize.height) * 0.5d)
* cot;
}
} else {
if (direction.equals(ISubDirection.NWR)) {
x = right
+ (bottom - loc.y + subSize.height * 0.5d + (insSize.height - inventSize.height) * 0.5d)
* cot;
} else if (direction.equals(ISubDirection.SWR)) {
x = right
- (bottom - loc.y - subSize.height * 0.5d + (insSize.height + inventSize.height) * 0.5d)
* cot;
} else if (direction.equals(ISubDirection.NER)) {
x = left
- (top - loc.y + subSize.height * 0.5 + (insSize.height - inventSize.height) * 0.5d)
* cot;
} else {
x = left
+ (bottom - loc.y - subSize.height * 0.5 + (insSize.height + inventSize.height) * 0.5d)
* cot;
}
}
double y;
if (isBeforeOrientation)
y = top - (insSize.height) * 0.5d;
else
y = bottom + (insSize.height) * 0.5d;
return new Point().getTranslated(x, y);
}
private Point calcRotatedPosition(IBranchPart orientation,
ParentSearchKey key, boolean isBeforeOrientation) {
double baseY = getBoneLine(orientation.getParentBranch()).getOrigin().y;
Dimension inventSize = key.getInvent().getSize();
Dimension insSize = key.getFigure().getSize();
double deltaY = inventSize.width * sin * 0.5d;
double y = baseY + (direction.isDownwards() ? deltaY : -deltaY);
double x;
if (isBeforeOrientation) {
double offset = calcBeforeOffset(orientation);
double deltaX = (insSize.height / sin - inventSize.width * cos - inventSize.width) * 0.5d;
x = offset + (direction.isRightHeaded() ? deltaX : -deltaX);
} else {
Rectangle pBounds = orientation.getParentBranch().getFigure()
.getBounds();
x = direction.isRightHeaded() ? pBounds.x
- (inventSize.width * cos + inventSize.width) * 0.5
: pBounds.right()
+ (inventSize.width * cos + inventSize.width) * 0.5;
}
return new Point().getTranslated(x, y);
}
private double calcBeforeOffset(IBranchPart branch) {
double offset = 0.0d;
PrecisionLine boneLine = getBoneLine(branch.getParentBranch());
double cot = cos / sin;
List<IBranchPart> callouts = branch.getCalloutBranches();
if (callouts == null || callouts.isEmpty()) {
IFigure figure = branch.getTopicPart().getFigure();
Rectangle bounds = figure.getBounds();
double height = ((TopicFigure) figure)
.getNormalPreferredBounds(new Point()).height;
double delta = height * cos * cot;
offset = direction.isRightHeaded() ? bounds.right() + delta
: bounds.x - delta;
} else {
double y = boneLine.getOrigin().y;
Rectangle bounds = branch.getFigure().getBounds();
offset = direction.isRightHeaded() ? bounds.right() : bounds.x;
for (IBranchPart callout : callouts) {
Rectangle calloutBounds = callout.getFigure().getBounds();
if (direction.equals(ISubDirection.NE)) {
Point tl = calloutBounds.getTopLeft();
offset = Math.min(offset, tl.x - (y - tl.y) * cot);
} else if (direction.equals(ISubDirection.SE)) {
Point bl = calloutBounds.getBottomLeft();
offset = Math.min(offset, bl.x - (bl.y - y) * cot);
} else if (direction.equals(ISubDirection.NW)) {
Point tr = calloutBounds.getTopRight();
offset = Math.max(offset, tr.x + (y - tr.y) * cot);
} else {
Point br = calloutBounds.getBottomRight();
offset = Math.max(offset, br.x + (br.y - y) * cot);
}
}
}
return offset;
}
}