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

com.sun.javafx.scene.traversal.Hueristic2D Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.javafx.scene.traversal;

import java.util.List;
import java.util.Stack;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import com.sun.javafx.Logging;
import sun.util.logging.PlatformLogger;

import static com.sun.javafx.scene.traversal.Direction.*;


public class Hueristic2D implements Algorithm {

    PlatformLogger focusLogger;

    Hueristic2D() {
        focusLogger = Logging.getFocusLogger();
    }

    public Node traverse(Node node, Direction dir, TraversalEngine engine) {
        Node newNode = null;
        int newNodeIndex = -1;

        cacheTraversal(node, dir, engine);

        if (focusLogger.isLoggable(PlatformLogger.FINER)) {
            focusLogger.finer("old focus owner : "+node+", bounds : "+engine.getBounds(node));
        }

        if (NEXT.equals(dir)) {
            newNode = findNextFocusablePeer(node);
        }
        else if (PREVIOUS.equals(dir)) {
            newNode = (findPreviousFocusablePeer(node));
        }
        else if (UP.equals(dir) || DOWN.equals(dir) || LEFT.equals(dir) || RIGHT.equals(dir) ) {
            /*
            ** if there is a node top of stack then make sure it's traversable
            */
            if (reverseDirection == true && !traversalNodeStack.empty()) {
                if (!traversalNodeStack.peek().isFocusTraversable()) {
                    traversalNodeStack.clear();
                }
                else {
                    newNode = traversalNodeStack.pop();
                }
            }

            Bounds currentB = node.localToScene(node.getLayoutBounds());
            BoundingBox bb = null;

            if (cacheStartTraversalNode != null) {
                Bounds cachedB = cacheStartTraversalNode.localToScene(cacheStartTraversalNode.getLayoutBounds());
                switch (dir) {
                case UP:
                    newNode = getNearestNodeUp(currentB, cachedB, engine, node, newNode);
                    break;
                case DOWN:
                    newNode = getNearestNodeDown(currentB, cachedB, engine, node, newNode);
                    break;
                case LEFT:
                    newNode = getNearestNodeLeft(currentB, cachedB, engine, node, newNode);
                    break;
                case RIGHT:
                    newNode = getNearestNodeRight(currentB, cachedB, engine, node, newNode);
                    break;
                default:
                    break;
                }
            }
        }

        if (focusLogger.isLoggable(PlatformLogger.FINER)) {
            if (newNode != null) {
                focusLogger.finer("new focus owner : "+newNode+", bounds : "+engine.getBounds(newNode));
            }
            else {
                focusLogger.finer("no focus transfer");
            }
        }

        /*
        ** newNode will be null if there are no
        ** possible targets in the direction.
        ** don't cache null, there's no coming back from that!
        */
        if (newNode != null) {
            cacheLastTraversalNode = newNode;
            if (reverseDirection == false) {
                traversalNodeStack.push(node);
            }
        }
        return newNode;
    }

    private Node findNextFocusablePeer(Node node) {
        Node startNode = node;
        Node newNode = null;
        List parentNodes = findPeers(startNode);
        if (parentNodes == null) {
            if (focusLogger.isLoggable(PlatformLogger.FINER)) {
                focusLogger.finer("can't find peers for a node without a parent");
            }
            return null;
        }

        int ourIndex = parentNodes.indexOf(startNode);

        if (ourIndex == -1) {
            if (focusLogger.isLoggable(PlatformLogger.FINER)) {
                focusLogger.finer("index not founds, no focus transfer");
            }
            return null;
        }

        newNode = findNextFocusableInList(parentNodes, ourIndex+1);

        /*
        ** we've reached the end of the peer nodes, and none have been selected,
        ** time to look at our parents peers.....
        */
        while (newNode == null && startNode != null) {
            List peerNodes;
            int parentIndex;

            Parent parent = startNode.getParent();
            if (parent != null) {
                peerNodes = findPeers(parent);
                if (peerNodes != null) {
                    parentIndex = peerNodes.indexOf(parent);
                    newNode = findNextFocusableInList(peerNodes, parentIndex+1);
                }
            }
            startNode = parent;
        }

        if (newNode == null) {
            /*
            ** find the top-most parent which is not at it's end-of-list
            */
            Parent parent = null;
            Parent p1 = node.getParent();
            while (p1 != null) {
                parent = p1;
                p1 = p1.getParent();
            }
            parentNodes = parent.getChildrenUnmodifiable();
            newNode = findNextFocusableInList(parentNodes, 0);
        }

        return newNode;
    }

    private Node findNextParent(Node node) {
        return null;
    }

    private Node findNextFocusableInList(List nodeList, int startIndex) {
        Node newNode = null;

        for (int i = startIndex ; i < nodeList.size() ; i++) {

            Node nextNode = nodeList.get(i);
            if (nextNode.isFocusTraversable() == true && nextNode.isDisabled() == false && nextNode.impl_isTreeVisible() == true) {
                newNode = nextNode;
                break;
            }
            else if (nextNode instanceof javafx.scene.Parent) {
                List nextNodesList = ((Parent)nextNode).getChildrenUnmodifiable();
                if (nextNodesList.size() > 0) {
                    newNode = findNextFocusableInList(nextNodesList, 0);
                    if (newNode != null) {
                        break;
                    }
                }
            }
        }
        return newNode;
    }

    private Node findPreviousFocusablePeer(Node node) {
        Node startNode = node;
        Node newNode = null;
        List parentNodes = findPeers(startNode);

        int ourIndex = parentNodes.indexOf(startNode);

        if (ourIndex == -1) {
            if (focusLogger.isLoggable(PlatformLogger.FINER)) {
                focusLogger.finer("index not founds, no focus transfer");
            }
            return null;
        }

        newNode = findPreviousFocusableInList(parentNodes, ourIndex-1);

        /*
        ** we've reached the end of the peer nodes, and none have been selected,
        ** time to look at our parents peers.....
        */
        while (newNode == null && startNode != null) {
            List peerNodes;
            int parentIndex;

            Parent parent = startNode.getParent();
            if (parent != null) {
                peerNodes = findPeers(parent);
                if (peerNodes != null) {
                    parentIndex = peerNodes.indexOf(parent);
                    newNode = findPreviousFocusableInList(peerNodes, parentIndex-1);
                }
            }
            startNode = parent;
        }

        if (newNode == null) {
            /*
            ** find the top-most parent which is not at it's end-of-list
            */
            Parent parent = null;
            Parent p1 = node.getParent();
            while (p1 != null) {
                parent = p1;
                p1 = p1.getParent();
            }
            parentNodes = parent.getChildrenUnmodifiable();
            newNode = findPreviousFocusableInList(parentNodes, parentNodes.size()-1);
        }

        return newNode;
    }


    private Node findPreviousFocusableInList(List nodeList, int startIndex) {
        Node newNode = null;

        for (int i = startIndex ; i >= 0 ; i--) {
            Node prevNode = nodeList.get(i);
            if (prevNode.isFocusTraversable() == true && prevNode.isDisabled() == false && prevNode.impl_isTreeVisible() == true) {
                newNode = prevNode;
                break;
            }
            else if (prevNode instanceof javafx.scene.Parent) {
                List prevNodesList = ((Parent)prevNode).getChildrenUnmodifiable();
                if (prevNodesList.size() > 0) {
                    newNode = findPreviousFocusableInList(prevNodesList, prevNodesList.size()-1);
                    if (newNode != null) {
                        break;
                    }
                }
            }
        }
        return newNode;
    }

    private List findPeers(Node node) {
        List parentNodes = null;
        Parent parent = node.getParent();
        /*
        ** check that we haven't hit the top-level
        */
        if (parent != null) {
            parentNodes = parent.getChildrenUnmodifiable();
        }
        return parentNodes;
    }

    private static Parent getParent(Node child) {
        return (child.getParent() instanceof Group) ? (child.getParent().getParent()) : (child.getParent());
    }

    private boolean isOnAxis(Direction dir, Bounds cur, Bounds tgt) {

        final double cmin, cmax, tmin, tmax;

        if (dir == UP || dir == DOWN) {
            cmin = cur.getMinX();
            cmax = cur.getMaxX();
            tmin = tgt.getMinX();
            tmax = tgt.getMaxX();
        }
        else { // dir == LEFT || dir == RIGHT
            cmin = cur.getMinY();
            cmax = cur.getMaxY();
            tmin = tgt.getMinY();
            tmax = tgt.getMaxY();
        }

        return tmin <= cmax && tmax >= cmin;
    }

    /**
     * Compute the out-distance to the near edge of the target in the
     * traversal direction. Negative means the near edge is "behind".
     */
    private double outDistance(Direction dir, Bounds cur, Bounds tgt) {

        final double distance;
        if (dir == UP) {
            distance = cur.getMinY() - tgt.getMaxY();
        }
        else if (dir == DOWN) {
            distance = tgt.getMinY() - cur.getMaxY();
        }
        else if (dir == LEFT) {
            distance = cur.getMinX() - tgt.getMaxX();
        }
        else { // dir == RIGHT
            distance = tgt.getMinX() - cur.getMaxX();
        }
        return distance;
    }

    /**
     * Computes the side distance from current center to target center.
     * Always positive. This is only used for on-axis nodes.
     */
    private double centerSideDistance(Direction dir, Bounds cur, Bounds tgt) {
        final double cc; // current center
        final double tc; // target center

        if (dir == UP || dir == DOWN) {
            cc = cur.getMinX() + cur.getWidth() / 2.0f;
            tc = tgt.getMinX() + tgt.getWidth() / 2.0f;
        }
        else { // dir == LEFT || dir == RIGHT
            cc = cur.getMinY() + cur.getHeight() / 2.0f;
            tc = tgt.getMinY() + tgt.getHeight() / 2.0f;
        }
        return Math.abs(tc - cc);
    }

    /**
     * Computes the side distance between the closest corners of the current
     * and target. Always positive. This is only used for off-axis nodes.
     */
    private double cornerSideDistance(Direction dir, Bounds cur, Bounds tgt) {

        final double distance;

        if (dir == UP || dir == DOWN) {
            if (tgt.getMinX() > cur.getMaxX()) {
                // on the right
                distance = tgt.getMinX() - cur.getMaxX();
            }
            else {
                // on the left
                distance = cur.getMinX() - tgt.getMaxX();
            }
        }
        else { // dir == LEFT or dir == RIGHT

            if (tgt.getMinY() > cur.getMaxY()) {
                // below
                distance = tgt.getMinY() - cur.getMaxY();
            }
            else {
                // above
                distance = cur.getMinY() - tgt.getMaxY();
            }
        }
        return distance;
    }

    protected Node cacheStartTraversalNode = null;
    protected Direction cacheStartTraversalDirection = null;
    protected boolean reverseDirection = false;
    protected Node cacheLastTraversalNode = null;
    protected Stack traversalNodeStack = new Stack();

    private void cacheTraversal(Node node, Direction dir, TraversalEngine engine) {
        if (!traversalNodeStack.empty() && node != cacheLastTraversalNode) {
            /*
            ** we didn't get here by arrow key,
            ** dump the cache
            */
            traversalNodeStack.clear();
        }
        /*
        ** Next or Previous cancels the row caching
        */
        if (dir == Direction.NEXT || dir == Direction.PREVIOUS) {
            traversalNodeStack.clear();
            reverseDirection = false;
        }
        else {
            if (cacheStartTraversalNode == null || dir != cacheStartTraversalDirection) {

                if ((dir == UP && cacheStartTraversalDirection == DOWN) ||
                    (dir == DOWN && cacheStartTraversalDirection == UP) ||
                    (dir == LEFT && cacheStartTraversalDirection == RIGHT) ||
                    (dir == RIGHT && cacheStartTraversalDirection == LEFT) && !traversalNodeStack.empty()) {
                    reverseDirection = true;
                }
                else {
                    /*
                    ** if we don't have a row set, or the direction has changed, then
                    ** make the current node the row.
                    ** otherwise we are moving in the same direction as last time, so
                    ** we'll just leave it alone.
                    */
                    cacheStartTraversalNode = node;
                    cacheStartTraversalDirection = dir;
                    reverseDirection = false;
                    traversalNodeStack.clear();
                }
            }
            else {
                /*
                ** we're going this way again!
                */
                reverseDirection = false;
            }
        }
    }

    protected Node getNearestNodeUp(Bounds currentB, Bounds originB, TraversalEngine engine, Node node, Node reversingNode) {

        List nodes = engine.getAllTargetNodes();

        Bounds biasedB = new BoundingBox(originB.getMinX(), currentB.getMinY(), originB.getWidth(), currentB.getHeight());

        Point2D currentMid2D = new Point2D(currentB.getMinX()+(currentB.getWidth()/2), currentB.getMinY());
        Point2D currentTopLeft2D = new Point2D(currentB.getMinX(), currentB.getMinY());
        Point2D currentTopRight2D = new Point2D(currentB.getMaxX(), currentB.getMinY());

        Point2D originTopLeft2D = new Point2D(originB.getMinX(), originB.getMinY());

        TargetNode reversingTargetNode = null;
        TargetNode targetNode = new TargetNode();
        TargetNode nearestNodeCurrentSimple2D = null;
        TargetNode nearestNodeOriginSimple2D = null;
        TargetNode nearestNodeAverageUp = null;
        TargetNode nearestNodeOnOriginX = null;
        TargetNode nearestNodeOnCurrentX = null;
        TargetNode nearestNodeTopLeft = null;
        TargetNode nearestNodeAnythingAnywhereUp = null;

        if (nodes.size() > 0) {
            int nodeIndex;

            if (reversingNode != null) {
                Bounds targetBounds = reversingNode.localToScene(reversingNode.getLayoutBounds());
                reversingTargetNode = new TargetNode();

                reversingTargetNode.node = reversingNode;
                reversingTargetNode.bounds = targetBounds;

                /*
                ** closest biased : simple 2d
                */
                double outdB = outDistance(Direction.UP, biasedB, targetBounds);

                if (isOnAxis(Direction.UP, biasedB, targetBounds)) {
                    reversingTargetNode.biased2DMetric = outdB + centerSideDistance(Direction.UP, biasedB, targetBounds) / 100;
                }
                else {
                    final double cosd = cornerSideDistance(Direction.UP, biasedB, targetBounds);
                    reversingTargetNode.biased2DMetric = 100000 + outdB*outdB + 9*cosd*cosd;
                }

                /*
                ** closest current : simple 2d
                */
                double outdC = outDistance(Direction.UP, currentB, targetBounds);

                if (isOnAxis(Direction.UP, currentB, targetBounds)) {
                    reversingTargetNode.current2DMetric = outdC + centerSideDistance(Direction.UP, currentB, targetBounds) / 100;
                }
                else {
                    final double cosd = cornerSideDistance(Direction.UP, currentB, targetBounds);
                    reversingTargetNode.current2DMetric = 100000 + outdC*outdC + 9*cosd*cosd;
                }


                reversingTargetNode.bottomLeftDistance = currentTopLeft2D.distance(targetBounds.getMinX(), targetBounds.getMaxY());
                reversingTargetNode.midDistance = currentMid2D.distance(targetBounds.getMinX()+(originB.getWidth()/2), targetBounds.getMaxY());
                reversingTargetNode.bottomRightDistance = currentTopRight2D.distance(originB.getMaxX(), targetBounds.getMaxY());

                double currentTopLeftToTargetMidDistance = currentTopLeft2D.distance(targetBounds.getMinX()+(targetBounds.getWidth()/2), targetBounds.getMaxY());
                double currentTopLeftToTargetBottomRightDistance = currentTopLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMaxY());
                double currentTopRightToTargetBottomLeftDistance = currentTopRight2D.distance(targetBounds.getMinX(), targetBounds.getMaxY());
                double currentTopRightToTargetMidDistance = currentTopRight2D.distance(targetBounds.getMinX()+(targetBounds.getWidth()/2), targetBounds.getMaxY());
                double currentTopRightToTargetBottomRightDistance = currentTopRight2D.distance(targetBounds.getMaxX(), targetBounds.getMaxY());
                double currentMidToTargetBottomLeftDistance = currentMid2D.distance(targetBounds.getMinX(), targetBounds.getMaxY());
                double currentMidToTargetMidDistance = currentMid2D.distance(targetBounds.getMinX()+(targetBounds.getWidth()/2), targetBounds.getMaxY());
                double currentMidToTargetBottomRightDistance = currentMid2D.distance(targetBounds.getMaxX(), targetBounds.getMaxY());

                double biasTopLeftToTargetMidDistance = currentTopLeft2D.distance(targetBounds.getMinX()+(originB.getWidth()/2), targetBounds.getMaxY());
                double biasTopLeftToTargetBottomRightDistance = currentTopLeft2D.distance(originB.getMaxX(), targetBounds.getMaxY());
                double biasTopRightToTargetMidDistance = currentTopRight2D.distance(targetBounds.getMinX()+(originB.getWidth()/2), targetBounds.getMaxY());
                double biasMidToTargetBottomRightDistance = currentMid2D.distance(originB.getMaxX(), targetBounds.getMaxY());

                reversingTargetNode.averageDistance =
                    (reversingTargetNode.bottomLeftDistance+biasTopLeftToTargetMidDistance+biasTopLeftToTargetBottomRightDistance+
                     currentTopRightToTargetBottomLeftDistance+reversingTargetNode.bottomRightDistance+biasTopRightToTargetMidDistance+reversingTargetNode.midDistance)/7;

                reversingTargetNode.biasShortestDistance =
                    findMin9(reversingTargetNode.bottomLeftDistance, biasTopLeftToTargetMidDistance, biasTopLeftToTargetBottomRightDistance,
                             currentTopRightToTargetBottomLeftDistance, biasTopRightToTargetMidDistance, reversingTargetNode.bottomRightDistance,
                             currentMidToTargetBottomLeftDistance, reversingTargetNode.midDistance, biasMidToTargetBottomRightDistance);

                reversingTargetNode.shortestDistance =
                    findMin9(reversingTargetNode.bottomLeftDistance, currentTopLeftToTargetMidDistance, currentTopLeftToTargetBottomRightDistance,
                             currentTopRightToTargetBottomLeftDistance, currentTopRightToTargetMidDistance, currentTopRightToTargetBottomRightDistance,
                             currentMidToTargetBottomLeftDistance, currentMidToTargetMidDistance, currentMidToTargetBottomRightDistance);

            }

            for (nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) {

                Bounds targetBounds = nodes.get(nodeIndex).localToScene(nodes.get(nodeIndex).getLayoutBounds());
                /*
                ** check that the target node starts after we start target.minX > origin.minX
                ** and the target node ends after we end target.maxX > origin.maxX
                */
                if ((currentB.getMinY() > targetBounds.getMaxY())) {

                    targetNode.node = nodes.get(nodeIndex);
                    targetNode.bounds = targetBounds;

                    /*
                    ** closest biased : simple 2d
                    */
                    double outdB = outDistance(Direction.UP, biasedB, targetBounds);

                    if (isOnAxis(Direction.UP, biasedB, targetBounds)) {
                        targetNode.biased2DMetric = outdB + centerSideDistance(Direction.UP, biasedB, targetBounds) / 100;
                    }
                    else {
                        final double cosd = cornerSideDistance(Direction.UP, biasedB, targetBounds);
                        targetNode.biased2DMetric = 100000 + outdB*outdB + 9*cosd*cosd;
                    }
                    /*
                    ** closest current : simple 2d
                    */
                    double outdC = outDistance(Direction.UP, currentB, targetBounds);

                    if (isOnAxis(Direction.UP, currentB, targetBounds)) {
                        targetNode.current2DMetric = outdC + centerSideDistance(Direction.UP, currentB, targetBounds) / 100;
                    }
                    else {
                        final double cosd = cornerSideDistance(Direction.UP, currentB, targetBounds);
                        targetNode.current2DMetric = 100000 + outdC*outdC + 9*cosd*cosd;
                    }


                    targetNode.bottomLeftDistance = currentTopLeft2D.distance(targetBounds.getMinX(), targetBounds.getMaxY());
                    targetNode.midDistance = currentMid2D.distance(targetBounds.getMinX()+(originB.getWidth()/2), targetBounds.getMaxY());
                    targetNode.bottomRightDistance = currentTopRight2D.distance(originB.getMaxX(), targetBounds.getMaxY());

                    double currentTopLeftToTargetMidDistance = currentTopLeft2D.distance(targetBounds.getMinX()+(targetBounds.getWidth()/2), targetBounds.getMaxY());
                    double currentTopLeftToTargetBottomRightDistance = currentTopLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMaxY());
                    double currentTopRightToTargetBottomLeftDistance = currentTopRight2D.distance(targetBounds.getMinX(), targetBounds.getMaxY());
                    double currentTopRightToTargetMidDistance = currentTopRight2D.distance(targetBounds.getMinX()+(targetBounds.getWidth()/2), targetBounds.getMaxY());
                    double currentTopRightToTargetBottomRightDistance = currentTopRight2D.distance(targetBounds.getMaxX(), targetBounds.getMaxY());
                    double currentMidToTargetBottomLeftDistance = currentMid2D.distance(targetBounds.getMinX(), targetBounds.getMaxY());
                    double currentMidToTargetMidDistance = currentMid2D.distance(targetBounds.getMinX()+(targetBounds.getWidth()/2), targetBounds.getMaxY());
                    double currentMidToTargetBottomRightDistance = currentMid2D.distance(targetBounds.getMaxX(), targetBounds.getMaxY());

                    double biasTopLeftToTargetMidDistance = currentTopLeft2D.distance(targetBounds.getMinX()+(originB.getWidth()/2), targetBounds.getMaxY());
                    double biasTopLeftToTargetBottomRightDistance = currentTopLeft2D.distance(originB.getMaxX(), targetBounds.getMaxY());
                    double biasTopRightToTargetMidDistance = currentTopRight2D.distance(targetBounds.getMinX()+(originB.getWidth()/2), targetBounds.getMaxY());
                    double biasMidToTargetBottomRightDistance = currentMid2D.distance(originB.getMaxX(), targetBounds.getMaxY());

                    targetNode.averageDistance =
                        (targetNode.bottomLeftDistance+biasTopLeftToTargetMidDistance+biasTopLeftToTargetBottomRightDistance+
                         currentTopRightToTargetBottomLeftDistance+targetNode.bottomRightDistance+biasTopRightToTargetMidDistance+targetNode.midDistance)/7;

                    targetNode.biasShortestDistance =
                        findMin9(targetNode.bottomLeftDistance, biasTopLeftToTargetMidDistance, biasTopLeftToTargetBottomRightDistance,
                                 currentTopRightToTargetBottomLeftDistance, biasTopRightToTargetMidDistance, targetNode.bottomRightDistance,
                                 currentMidToTargetBottomLeftDistance, targetNode.midDistance, biasMidToTargetBottomRightDistance);

                    targetNode.shortestDistance =
                        findMin9(targetNode.bottomLeftDistance, currentTopLeftToTargetMidDistance, currentTopLeftToTargetBottomRightDistance,
                                 currentTopRightToTargetBottomLeftDistance, currentTopRightToTargetMidDistance, currentTopRightToTargetBottomRightDistance,
                                 currentMidToTargetBottomLeftDistance, currentMidToTargetMidDistance, currentMidToTargetBottomRightDistance);

                    /*
                    ** closest biased : simple 2d
                    */
                    if (outdC >= 0.0) {
                        if (nearestNodeOriginSimple2D == null || targetNode.biased2DMetric < nearestNodeOriginSimple2D.biased2DMetric) {

                            if (nearestNodeOriginSimple2D == null) {
                                nearestNodeOriginSimple2D = new TargetNode();
                            }
                            nearestNodeOriginSimple2D.copy(targetNode);
                        }
                    }
                    /*
                    ** closest current : simple 2d
                    */
                    if (outdC >= 0.0) {
                        if (nearestNodeCurrentSimple2D == null || targetNode.current2DMetric < nearestNodeCurrentSimple2D.current2DMetric) {

                            if (nearestNodeCurrentSimple2D == null) {
                                nearestNodeCurrentSimple2D = new TargetNode();
                            }
                            nearestNodeCurrentSimple2D.copy(targetNode);
                        }
                    }
                    /*
                    ** on the Origin X
                    */
                    if ((originB.getMaxX() > targetBounds.getMinX()) && (targetBounds.getMaxX() > originB.getMinX())) {
                        if (nearestNodeOnOriginX == null || nearestNodeOnOriginX.biasShortestDistance > targetNode.biasShortestDistance) {

                            if (nearestNodeOnOriginX == null) {
                                nearestNodeOnOriginX = new TargetNode();
                            }
                            nearestNodeOnOriginX.copy(targetNode);
                        }
                    }
                    /*
                    ** on the Current X
                    */
                    if ((currentB.getMaxX() > targetBounds.getMinX()) && (targetBounds.getMaxX() > currentB.getMinX())) {
                        if (nearestNodeOnCurrentX == null || nearestNodeOnCurrentX.biasShortestDistance > targetNode.biasShortestDistance) {

                            if (nearestNodeOnCurrentX == null) {
                                nearestNodeOnCurrentX = new TargetNode();
                            }
                            nearestNodeOnCurrentX.copy(targetNode);
                        }
                    }
                    /*
                    ** Closest top left / bottom left corners.
                    */
                    if (nearestNodeTopLeft == null || nearestNodeTopLeft.bottomLeftDistance > targetNode.bottomLeftDistance) {
                        if (((originB.getMinY() >= currentB.getMinY()) && (targetBounds.getMinY() >= currentB.getMinY()))  ||
                            ((originB.getMinY() <= currentB.getMinY()) && (targetBounds.getMinY() <= currentB.getMinY()))) {

                            if (nearestNodeTopLeft == null) {
                                nearestNodeTopLeft = new TargetNode();
                            }
                            nearestNodeTopLeft.copy(targetNode);
                        }
                    }

                    if (nearestNodeAverageUp == null || nearestNodeAverageUp.averageDistance > targetNode.averageDistance) {
                        if (((originB.getMinX() >= currentB.getMinX()) && (targetBounds.getMinX() >= currentB.getMinX()))  ||
                            ((originB.getMinX() <= currentB.getMinX()) && (targetBounds.getMinX() <= currentB.getMinX()))) {

                            if (nearestNodeAverageUp == null) {
                                nearestNodeAverageUp = new TargetNode();
                            }
                            nearestNodeAverageUp.copy(targetNode);
                        }
                    }

                    if (nearestNodeAnythingAnywhereUp == null || nearestNodeAnythingAnywhereUp.shortestDistance > targetNode.shortestDistance) {

                        if (nearestNodeAnythingAnywhereUp == null) {
                            nearestNodeAnythingAnywhereUp = new TargetNode();
                        }
                        nearestNodeAnythingAnywhereUp.copy(targetNode);
                    }
                }
            }
        }
        nodes.clear();

        if (reversingTargetNode != null) {
            reversingTargetNode.originTopLeftDistance = originTopLeft2D.distance(reversingTargetNode.bounds.getMinX(), reversingTargetNode.bounds.getMaxY());
        }

        if (nearestNodeOriginSimple2D != null) {
            nearestNodeOriginSimple2D.originTopLeftDistance = originTopLeft2D.distance(nearestNodeOriginSimple2D.bounds.getMinX(), nearestNodeOriginSimple2D.bounds.getMaxY());
        }

        if (nearestNodeCurrentSimple2D != null) {
            nearestNodeCurrentSimple2D.originTopLeftDistance = originTopLeft2D.distance(nearestNodeCurrentSimple2D.bounds.getMinX(), nearestNodeCurrentSimple2D.bounds.getMaxY());
        }

        if (nearestNodeOnOriginX != null) {
            nearestNodeOnOriginX.originTopLeftDistance = originTopLeft2D.distance(nearestNodeOnOriginX.bounds.getMinX(), nearestNodeOnOriginX.bounds.getMaxY());
        }

        if (nearestNodeOnCurrentX != null) {
            nearestNodeOnCurrentX.originTopLeftDistance = originTopLeft2D.distance(nearestNodeOnCurrentX.bounds.getMinX(), nearestNodeOnCurrentX.bounds.getMaxY());
        }

        if (nearestNodeAverageUp != null) {
            nearestNodeAverageUp.originTopLeftDistance = originTopLeft2D.distance(nearestNodeAverageUp.bounds.getMinX(), nearestNodeAverageUp.bounds.getMaxY());
        }

        if (nearestNodeTopLeft != null) {
            nearestNodeTopLeft.originTopLeftDistance = originTopLeft2D.distance(nearestNodeTopLeft.bounds.getMinX(), nearestNodeTopLeft.bounds.getMaxY());
        }

        if (nearestNodeAnythingAnywhereUp != null) {
            nearestNodeAnythingAnywhereUp.originTopLeftDistance = originTopLeft2D.distance(nearestNodeAnythingAnywhereUp.bounds.getMinX(), nearestNodeAnythingAnywhereUp.bounds.getMaxY());
        }

        if (focusLogger.isLoggable(PlatformLogger.FINER)) {
            if (reversingTargetNode != null) {
                focusLogger.finer("reversingTargetNode.node : "+reversingTargetNode.node);
            }
            if (nearestNodeOriginSimple2D != null) {
                focusLogger.finer("nearestNodeOriginSimple2D.node : "+nearestNodeOriginSimple2D.node);
            }
            if (nearestNodeCurrentSimple2D != null) {
                focusLogger.finer("nearestNodeCurrentSimple2D.node : "+nearestNodeCurrentSimple2D.node);
            }
            if (nearestNodeOnOriginX != null) {
                focusLogger.finer("nearestNodeOnOriginX.node : "+nearestNodeOnOriginX.node);
            }
            if (nearestNodeOnCurrentX != null) {
                focusLogger.finer("nearestNodeOnCurrentX.node : "+nearestNodeOnCurrentX.node);
            }
            if (nearestNodeAverageUp != null) {
                focusLogger.finer("nearestNodeAverageUp.node : "+nearestNodeAverageUp.node);
            }
            if (nearestNodeTopLeft != null) {
                focusLogger.finer("nearestNodeTopLeft.node : "+nearestNodeTopLeft.node);
            }
            if (nearestNodeAnythingAnywhereUp != null) {
                focusLogger.finer("nearestNodeAnythingAnywhereUp.node : "+nearestNodeAnythingAnywhereUp.node);
            }
        }

        /*
        ** we've just changed direction, and have a node on stack.
        ** there is a strong preference for this node, just make sure
        ** it's not a bad choice, as sometimes we got here as a last-chance
        */
        if (reversingTargetNode != null) {
            return reversingNode;
        }

        if (nearestNodeOnOriginX != null && nearestNodeOnOriginX.biasShortestDistance < Double.MAX_VALUE) {
            /*
            ** there's a preference, all else being equal, to return nearestNodeOnOriginX
            */
            if (nearestNodeOnCurrentX != null && nearestNodeAverageUp != null &&
                nearestNodeOnOriginX.node == nearestNodeOnCurrentX.node && nearestNodeOnOriginX.node == nearestNodeAverageUp.node) {
                return nearestNodeOnOriginX.node;
            }
            if (nearestNodeOnCurrentX != null && nearestNodeOriginSimple2D != null &&
                nearestNodeOnOriginX.node == nearestNodeOnCurrentX.node && nearestNodeOnOriginX.node == nearestNodeOriginSimple2D.node) {
                return nearestNodeOnOriginX.node;
            }
            if (nearestNodeOnCurrentX != null && nearestNodeTopLeft != null &&
                nearestNodeOnOriginX.node == nearestNodeOnCurrentX.node && nearestNodeOnOriginX.node == nearestNodeTopLeft.node) {
                return nearestNodeOnOriginX.node;
            }
            if (nearestNodeOnCurrentX != null && nearestNodeAnythingAnywhereUp != null &&
                nearestNodeOnOriginX.node == nearestNodeOnCurrentX.node && nearestNodeOnOriginX.node == nearestNodeAnythingAnywhereUp.node) {
                return nearestNodeOnOriginX.node;
            }
            if (nearestNodeAverageUp != null && nearestNodeOnOriginX.node == nearestNodeAverageUp.node) {
                return nearestNodeOnOriginX.node;
            }

            if (nearestNodeOnCurrentX != null && nearestNodeOnCurrentX.biasShortestDistance < Double.MAX_VALUE) {
                if (nearestNodeOnOriginX == null || (nearestNodeOnCurrentX.bottomLeftDistance < nearestNodeOnOriginX.bottomLeftDistance) &&
                    (nearestNodeOnCurrentX.originTopLeftDistance < nearestNodeOnOriginX.originTopLeftDistance) &&
                    (nearestNodeOnCurrentX.bounds.getMinX() - currentTopLeft2D.getX()) < (nearestNodeOnOriginX.bounds.getMinX() - currentTopLeft2D.getX()) )  {

                    return nearestNodeOnCurrentX.node;
                }
                else if (nearestNodeOnOriginX != null) {
                    if (nearestNodeAverageUp == null || nearestNodeOnOriginX.averageDistance < nearestNodeAverageUp.averageDistance) {
                        return nearestNodeOnOriginX.node;
                    }
                }
            }

        }
        else {
            if (nearestNodeOnOriginX == null && nearestNodeOnCurrentX == null && nearestNodeCurrentSimple2D != null) {
                if (nearestNodeAverageUp != null && nearestNodeTopLeft != null && nearestNodeAnythingAnywhereUp != null && (nearestNodeAverageUp.node == nearestNodeTopLeft.node && nearestNodeAverageUp.node == nearestNodeAnythingAnywhereUp.node)) {
                    return nearestNodeAverageUp.node;
                }
                return nearestNodeCurrentSimple2D.node;
            }
            else if (nearestNodeAverageUp != null && nearestNodeTopLeft != null && nearestNodeAnythingAnywhereUp != null &&
                     nearestNodeAverageUp.biasShortestDistance == nearestNodeTopLeft.biasShortestDistance &&
                     nearestNodeAverageUp.biasShortestDistance == nearestNodeAnythingAnywhereUp.biasShortestDistance &&
                     nearestNodeAverageUp.biasShortestDistance < Double.MAX_VALUE) {

                if (nearestNodeOnOriginX != null && nearestNodeOnOriginX.originTopLeftDistance < nearestNodeAverageUp.originTopLeftDistance) {
                    return nearestNodeOnOriginX.node;
                }
                else {
                    return nearestNodeAverageUp.node;
                }
            }
        }

        /*
        ** is the average closer?
        */
        if (nearestNodeAverageUp != null && (nearestNodeOnOriginX == null || (nearestNodeAverageUp.biasShortestDistance < nearestNodeOnOriginX.biasShortestDistance))) {
            /*
            ** but is one in the way
            */
            if (nearestNodeOnOriginX != null && (nearestNodeOnOriginX.bounds.getMaxY() >= nearestNodeAverageUp.bounds.getMaxY())) {
                return nearestNodeOnOriginX.node;
            }
            if (nearestNodeOriginSimple2D != null) {
                if (nearestNodeOriginSimple2D.current2DMetric <= nearestNodeAverageUp.current2DMetric) {
                    return nearestNodeOriginSimple2D.node;
                }
                if (nearestNodeOriginSimple2D.bounds.getMaxY() >= nearestNodeAverageUp.bounds.getMaxY()) {
                    return nearestNodeOriginSimple2D.node;
                }
            }
            return nearestNodeAverageUp.node;
        }

        /*
        ** this is an odd one, in that is isn't the closest on current, or on the
        ** origin, but it looks better for most cases...
        */
        if ((nearestNodeCurrentSimple2D != null && nearestNodeOnCurrentX != null && nearestNodeAverageUp != null && nearestNodeTopLeft != null && nearestNodeAnythingAnywhereUp != null) &&
            (nearestNodeCurrentSimple2D.node == nearestNodeOnCurrentX.node) &&
            (nearestNodeCurrentSimple2D.node ==  nearestNodeAverageUp.node) &&
            (nearestNodeCurrentSimple2D.node == nearestNodeTopLeft.node) &&
            (nearestNodeCurrentSimple2D.node == nearestNodeAnythingAnywhereUp.node)) {
            return nearestNodeCurrentSimple2D.node;
        }

        if (nearestNodeOnOriginX != null && (nearestNodeOnCurrentX == null || (nearestNodeOnOriginX.bottomRightDistance < nearestNodeOnCurrentX.bottomRightDistance))) {
            return nearestNodeOnOriginX.node;
        }
        /*
        ** There isn't a clear winner, just go to the one nearest the current
        ** focus owner, or if invalid then try the other contenders.
        */
        if (nearestNodeOnOriginX != null) {
            return nearestNodeOnOriginX.node;
        }
        else if (nearestNodeOriginSimple2D != null) {
            return nearestNodeOriginSimple2D.node;
        }
        else if (nearestNodeOnCurrentX != null) {
            return nearestNodeOnCurrentX.node;
        }
        else if (nearestNodeAverageUp != null) {
            return nearestNodeAverageUp.node;
        }
        else if (nearestNodeTopLeft != null) {
            return nearestNodeTopLeft.node;
        }
        else if (nearestNodeAnythingAnywhereUp != null) {
            return nearestNodeAnythingAnywhereUp.node;
        }
        return null;
    }


    protected Node getNearestNodeDown(Bounds currentB, Bounds originB, TraversalEngine engine, Node node, Node reversingNode) {

        List nodes = engine.getAllTargetNodes();

        Bounds biasedB = new BoundingBox(originB.getMinX(), currentB.getMinY(), originB.getWidth(), currentB.getHeight());

        Point2D currentMid2D = new Point2D(currentB.getMinX()+(currentB.getWidth()/2), currentB.getMaxY());
        Point2D currentBottomLeft2D = new Point2D(currentB.getMinX(), currentB.getMaxY());
        Point2D currentBottomRight2D = new Point2D(currentB.getMaxX(), currentB.getMaxY());

        Point2D originBottomLeft2D = new Point2D(originB.getMinX(), originB.getMaxY());

        TargetNode reversingTargetNode = null;
        TargetNode targetNode = new TargetNode();
        TargetNode nearestNodeCurrentSimple2D = null;
        TargetNode nearestNodeOriginSimple2D = null;
        TargetNode nearestNodeAverageDown = null;
        TargetNode nearestNodeOnOriginX = null;
        TargetNode nearestNodeOnCurrentX = null;
        TargetNode nearestNodeBottomLeft = null;
        TargetNode nearestNodeAnythingAnywhereDown = null;

        if (nodes.size() > 0) {
            int nodeIndex;

            if (reversingNode != null) {
                Bounds targetBounds = reversingNode.localToScene(reversingNode.getLayoutBounds());
                reversingTargetNode = new TargetNode();

                reversingTargetNode.node = reversingNode;
                reversingTargetNode.bounds = targetBounds;

                /*
                ** closest biased : simple 2d
                */
                double outdB = outDistance(Direction.DOWN, biasedB, targetBounds);

                if (isOnAxis(Direction.DOWN, biasedB, targetBounds)) {
                    reversingTargetNode.biased2DMetric = outdB + centerSideDistance(Direction.DOWN, biasedB, targetBounds) / 100;
                }
                else {
                    final double cosd = cornerSideDistance(Direction.DOWN, biasedB, targetBounds);
                    reversingTargetNode.biased2DMetric = 100000 + outdB*outdB + 9*cosd*cosd;
                }

                /*
                ** closest current : simple 2d
                */
                double outdC = outDistance(Direction.DOWN, currentB, targetBounds);

                if (isOnAxis(Direction.DOWN, currentB, targetBounds)) {
                    reversingTargetNode.current2DMetric = outdC + centerSideDistance(Direction.DOWN, currentB, targetBounds) / 100;
                }
                else {
                    final double cosd = cornerSideDistance(Direction.DOWN, currentB, targetBounds);
                    reversingTargetNode.current2DMetric = 100000 + outdC*outdC + 9*cosd*cosd;
                }

                reversingTargetNode.topLeftDistance = currentBottomLeft2D.distance(targetBounds.getMinX(), targetBounds.getMinY());
                reversingTargetNode.midDistance = currentMid2D.distance(targetBounds.getMinX()+(originB.getWidth()/2), targetBounds.getMinY());
                reversingTargetNode.topRightDistance = currentBottomRight2D.distance(originB.getMaxX(), targetBounds.getMinY());

                double currentBottomLeftToTargetTopRightDistance = currentBottomLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMinY());
                double currentBottomLeftToTargetMidDistance = currentBottomLeft2D.distance(targetBounds.getMinX()+(targetBounds.getWidth()/2), targetBounds.getMinY());
                double currentBottomRightToTargetTopLeftDistance = currentBottomRight2D.distance(targetBounds.getMinX(), targetBounds.getMinY());
                double currentBottomRightToTargetTopRightDistance = currentBottomRight2D.distance(targetBounds.getMaxX(), targetBounds.getMinY());
                double currentBottomRightToTargetMidDistance = currentBottomRight2D.distance(targetBounds.getMinX()+(targetBounds.getWidth()/2), targetBounds.getMinY());
                double currentMidToTargetTopLeftDistance = currentMid2D.distance(targetBounds.getMinX(), targetBounds.getMinY());
                double currentMidToTargetTopRightDistance = currentMid2D.distance(targetBounds.getMaxX(), targetBounds.getMinY());
                double currentMidToTargetMidDistance = currentMid2D.distance(targetBounds.getMinX()+(targetBounds.getWidth()/2), targetBounds.getMinY());

                double biasBottomLeftToTargetTopRightDistance = currentBottomLeft2D.distance(originB.getMaxX(), targetBounds.getMinY());
                double biasBottomLeftToTargetMidDistance = currentBottomLeft2D.distance(targetBounds.getMinX()+(originB.getWidth()/2), targetBounds.getMinY());
                double biasBottomRightToTargetMidDistance = currentBottomRight2D.distance(targetBounds.getMinX()+(originB.getWidth()/2), targetBounds.getMinY());
                double biasMidToTargetTopRightDistance = currentMid2D.distance(originB.getMaxX(), targetBounds.getMinY());

                reversingTargetNode.averageDistance =
                    (reversingTargetNode.topLeftDistance+biasBottomLeftToTargetMidDistance+biasBottomLeftToTargetTopRightDistance+
                     currentBottomRightToTargetTopLeftDistance+reversingTargetNode.topRightDistance+biasBottomRightToTargetMidDistance+reversingTargetNode.midDistance)/7;

                reversingTargetNode.biasShortestDistance =
                    findMin9(reversingTargetNode.topLeftDistance, biasBottomLeftToTargetMidDistance, biasBottomLeftToTargetTopRightDistance,
                             currentBottomRightToTargetTopLeftDistance, biasBottomRightToTargetMidDistance, reversingTargetNode.topRightDistance,
                             currentMidToTargetTopLeftDistance, reversingTargetNode.midDistance, biasMidToTargetTopRightDistance);

                reversingTargetNode.shortestDistance =
                    findMin9(reversingTargetNode.topLeftDistance, currentBottomLeftToTargetMidDistance, currentBottomLeftToTargetTopRightDistance,
                             currentBottomRightToTargetTopLeftDistance, currentBottomRightToTargetMidDistance, currentBottomRightToTargetTopRightDistance,
                             currentMidToTargetTopLeftDistance, currentMidToTargetMidDistance, currentMidToTargetTopRightDistance);

            }

            for (nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) {

                Bounds targetBounds = nodes.get(nodeIndex).localToScene(nodes.get(nodeIndex).getLayoutBounds());
                /*
                ** check that the target node starts after we start target.minX > origin.minX
                ** and the target node ends after we end target.maxX > origin.maxX
                */
                if ((currentB.getMaxY() < targetBounds.getMinY())) {

                    targetNode.node = nodes.get(nodeIndex);
                    targetNode.bounds = targetBounds;

                    /*
                    ** closest biased : simple 2d
                    */
                    double outdB = outDistance(Direction.DOWN, biasedB, targetBounds);

                    if (isOnAxis(Direction.DOWN, biasedB, targetBounds)) {
                        targetNode.biased2DMetric = outdB + centerSideDistance(Direction.DOWN, biasedB, targetBounds) / 100;
                    }
                    else {
                        final double cosd = cornerSideDistance(Direction.DOWN, biasedB, targetBounds);
                        targetNode.biased2DMetric = 100000 + outdB*outdB + 9*cosd*cosd;
                    }
                    /*
                    ** closest current : simple 2d
                    */
                    double outdC = outDistance(Direction.DOWN, currentB, targetBounds);

                    if (isOnAxis(Direction.DOWN, currentB, targetBounds)) {
                        targetNode.current2DMetric = outdC + centerSideDistance(Direction.DOWN, currentB, targetBounds) / 100;
                    }
                    else {
                        final double cosd = cornerSideDistance(Direction.DOWN, currentB, targetBounds);
                        targetNode.current2DMetric = 100000 + outdC*outdC + 9*cosd*cosd;
                    }

                    targetNode.topLeftDistance = currentBottomLeft2D.distance(targetBounds.getMinX(), targetBounds.getMinY());
                    targetNode.midDistance = currentMid2D.distance(targetBounds.getMinX()+(originB.getWidth()/2), targetBounds.getMinY());
                    targetNode.topRightDistance = currentBottomRight2D.distance(originB.getMaxX(), targetBounds.getMinY());

                    double currentBottomLeftToTargetTopRightDistance = currentBottomLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMinY());
                    double currentBottomLeftToTargetMidDistance = currentBottomLeft2D.distance(targetBounds.getMinX()+(targetBounds.getWidth()/2), targetBounds.getMinY());
                    double currentBottomRightToTargetTopLeftDistance = currentBottomRight2D.distance(targetBounds.getMinX(), targetBounds.getMinY());
                    double currentBottomRightToTargetTopRightDistance = currentBottomRight2D.distance(targetBounds.getMaxX(), targetBounds.getMinY());
                    double currentBottomRightToTargetMidDistance = currentBottomRight2D.distance(targetBounds.getMinX()+(targetBounds.getWidth()/2), targetBounds.getMinY());
                    double currentMidToTargetTopLeftDistance = currentMid2D.distance(targetBounds.getMinX(), targetBounds.getMinY());
                    double currentMidToTargetTopRightDistance = currentMid2D.distance(targetBounds.getMaxX(), targetBounds.getMinY());
                    double currentMidToTargetMidDistance = currentMid2D.distance(targetBounds.getMinX()+(targetBounds.getWidth()/2), targetBounds.getMinY());

                    double biasBottomLeftToTargetTopRightDistance = currentBottomLeft2D.distance(originB.getMaxX(), targetBounds.getMinY());
                    double biasBottomLeftToTargetMidDistance = currentBottomLeft2D.distance(targetBounds.getMinX()+(originB.getWidth()/2), targetBounds.getMinY());
                    double biasBottomRightToTargetMidDistance = currentBottomRight2D.distance(targetBounds.getMinX()+(originB.getWidth()/2), targetBounds.getMinY());
                    double biasMidToTargetTopRightDistance = currentMid2D.distance(originB.getMaxX(), targetBounds.getMinY());

                    targetNode.averageDistance =
                        (targetNode.topLeftDistance+biasBottomLeftToTargetMidDistance+biasBottomLeftToTargetTopRightDistance+
                         currentBottomRightToTargetTopLeftDistance+targetNode.topRightDistance+biasBottomRightToTargetMidDistance+targetNode.midDistance)/7;

                    targetNode.biasShortestDistance =
                        findMin9(targetNode.topLeftDistance, biasBottomLeftToTargetMidDistance, biasBottomLeftToTargetTopRightDistance,
                                 currentBottomRightToTargetTopLeftDistance, biasBottomRightToTargetMidDistance, targetNode.topRightDistance,
                                 currentMidToTargetTopLeftDistance, targetNode.midDistance, biasMidToTargetTopRightDistance);

                    targetNode.shortestDistance =
                        findMin9(targetNode.topLeftDistance, currentBottomLeftToTargetMidDistance, currentBottomLeftToTargetTopRightDistance,
                                 currentBottomRightToTargetTopLeftDistance, currentBottomRightToTargetMidDistance, currentBottomRightToTargetTopRightDistance,
                                 currentMidToTargetTopLeftDistance, currentMidToTargetMidDistance, currentMidToTargetTopRightDistance);


                    /*
                    ** closest biased : simple 2d
                    */
                    if (outdB >= 0.0) {
                        if (nearestNodeOriginSimple2D == null || targetNode.biased2DMetric < nearestNodeOriginSimple2D.biased2DMetric) {

                            if (nearestNodeOriginSimple2D == null) {
                                nearestNodeOriginSimple2D = new TargetNode();
                            }
                            nearestNodeOriginSimple2D.copy(targetNode);
                        }
                    }
                    /*
                    ** closest current : simple 2d
                    */
                    if (outdC >= 0.0) {
                        if (nearestNodeCurrentSimple2D == null || targetNode.current2DMetric < nearestNodeCurrentSimple2D.current2DMetric) {

                            if (nearestNodeCurrentSimple2D == null) {
                                nearestNodeCurrentSimple2D = new TargetNode();
                            }
                            nearestNodeCurrentSimple2D.copy(targetNode);
                        }
                    }
                    /*
                    ** on the Origin X
                    */
                    if ((originB.getMaxX() > targetBounds.getMinX()) && (targetBounds.getMaxX() > originB.getMinX())) {
                        if (nearestNodeOnOriginX == null || nearestNodeOnOriginX.biasShortestDistance > targetNode.biasShortestDistance) {

                            if (nearestNodeOnOriginX == null) {
                                nearestNodeOnOriginX = new TargetNode();
                            }
                            nearestNodeOnOriginX.copy(targetNode);
                        }
                    }
                    /*
                    ** on the Current X
                    */
                    if ((currentB.getMaxX() > targetBounds.getMinX()) && (targetBounds.getMaxX() > currentB.getMinX())) {
                        if (nearestNodeOnCurrentX == null || nearestNodeOnCurrentX.biasShortestDistance > targetNode.biasShortestDistance) {

                            if (nearestNodeOnCurrentX == null) {
                                nearestNodeOnCurrentX = new TargetNode();
                            }
                            nearestNodeOnCurrentX.copy(targetNode);
                        }
                    }
                    /*
                    ** Closest bottom left / top left corners.
                    */
                    if (nearestNodeBottomLeft == null || nearestNodeBottomLeft.topLeftDistance > targetNode.topLeftDistance) {
                        if (((originB.getMinX() >= currentB.getMinX()) && (targetBounds.getMinX() >= currentB.getMinX()))  ||
                            ((originB.getMinX() <= currentB.getMinX()) && (targetBounds.getMinX() <= currentB.getMinX()))) {

                            if (nearestNodeBottomLeft == null) {
                                nearestNodeBottomLeft = new TargetNode();
                            }
                            nearestNodeBottomLeft.copy(targetNode);
                        }
                    }

                    if (nearestNodeAverageDown == null || nearestNodeAverageDown.averageDistance > targetNode.averageDistance) {
                        if (((originB.getMinX() >= currentB.getMinX()) && (targetBounds.getMinX() >= currentB.getMinX()))  ||
                            ((originB.getMinX() <= currentB.getMinX()) && (targetBounds.getMinX() <= currentB.getMinX()))) {

                            if (nearestNodeAverageDown == null) {
                                nearestNodeAverageDown = new TargetNode();
                            }
                            nearestNodeAverageDown.copy(targetNode);
                        }
                    }

                    if (nearestNodeAnythingAnywhereDown == null || nearestNodeAnythingAnywhereDown.shortestDistance > targetNode.shortestDistance) {
                        if (nearestNodeAnythingAnywhereDown == null) {
                            nearestNodeAnythingAnywhereDown = new TargetNode();
                        }
                        nearestNodeAnythingAnywhereDown.copy(targetNode);
                    }
                }
            }
        }
        nodes.clear();

        if (reversingTargetNode != null) {
            reversingTargetNode.originBottomLeftDistance = originBottomLeft2D.distance(reversingTargetNode.bounds.getMinX(), reversingTargetNode.bounds.getMinY());
        }
        if (nearestNodeOriginSimple2D != null) {
            nearestNodeOriginSimple2D.originBottomLeftDistance = originBottomLeft2D.distance(nearestNodeOriginSimple2D.bounds.getMinX(), nearestNodeOriginSimple2D.bounds.getMinY());
        }
        if (nearestNodeCurrentSimple2D != null) {
            nearestNodeCurrentSimple2D.originBottomLeftDistance = originBottomLeft2D.distance(nearestNodeCurrentSimple2D.bounds.getMinX(), nearestNodeCurrentSimple2D.bounds.getMinY());
        }
        if (nearestNodeOnOriginX != null) {
            nearestNodeOnOriginX.originBottomLeftDistance = originBottomLeft2D.distance(nearestNodeOnOriginX.bounds.getMinX(), nearestNodeOnOriginX.bounds.getMinY());
        }
        if (nearestNodeOnCurrentX != null) {
            nearestNodeOnCurrentX.originBottomLeftDistance = originBottomLeft2D.distance(nearestNodeOnCurrentX.bounds.getMinX(), nearestNodeOnCurrentX.bounds.getMinY());
        }
        if (nearestNodeAverageDown != null) {
            nearestNodeAverageDown.originBottomLeftDistance = originBottomLeft2D.distance(nearestNodeAverageDown.bounds.getMinX(), nearestNodeAverageDown.bounds.getMinY());
        }
        if (nearestNodeBottomLeft != null) {
            nearestNodeBottomLeft.originBottomLeftDistance = originBottomLeft2D.distance(nearestNodeBottomLeft.bounds.getMinX(), nearestNodeBottomLeft.bounds.getMinY());
        }
        if (nearestNodeAnythingAnywhereDown != null) {
            nearestNodeAnythingAnywhereDown.originBottomLeftDistance = originBottomLeft2D.distance(nearestNodeAnythingAnywhereDown.bounds.getMinX(), nearestNodeAnythingAnywhereDown.bounds.getMinY());
        }

        if (focusLogger.isLoggable(PlatformLogger.FINER)) {
            if (reversingTargetNode != null) {
                focusLogger.finer("reversingTargetNode.node : "+reversingTargetNode.node);
            }
            if (nearestNodeOriginSimple2D != null) {
                focusLogger.finer("nearestNodeOriginSimple2D.node : "+nearestNodeOriginSimple2D.node);
            }
            if (nearestNodeCurrentSimple2D != null) {
                focusLogger.finer("nearestNodeCurrentSimple2D.node : "+nearestNodeCurrentSimple2D.node);
            }
            if (nearestNodeOnOriginX != null) {
                focusLogger.finer("nearestNodeOnOriginX.node : "+nearestNodeOnOriginX.node);
            }
            if (nearestNodeOnCurrentX != null) {
                focusLogger.finer("nearestNodeOnCurrentX.node : "+nearestNodeOnCurrentX.node);
            }
            if (nearestNodeAverageDown != null) {
                focusLogger.finer("nearestNodeAverageDown.node : "+nearestNodeAverageDown.node);
            }
            if (nearestNodeBottomLeft != null) {
                focusLogger.finer("nearestNodeTopLeft.node : "+nearestNodeBottomLeft.node);
            }
            if (nearestNodeAnythingAnywhereDown != null) {
                focusLogger.finer("nearestNodeAnythingAnywhereDown.node : "+nearestNodeAnythingAnywhereDown.node);
            }
        }

        /*
        ** we've just changed direction, and have a node on stack.
        ** there is a strong preference for this node, just make sure
        ** it's not a bad choice, as sometimes we got here as a last-chance
        */
        if (reversingTargetNode != null) {
            return reversingNode;
        }
        if (nearestNodeOnOriginX != null && nearestNodeOnOriginX.biasShortestDistance < Double.MAX_VALUE) {
            /*
            ** there's a preference, all else being equal, to return nearestNodeOnOriginX
            */
            if (nearestNodeOnCurrentX != null && nearestNodeAverageDown != null &&
                nearestNodeOnOriginX.node == nearestNodeOnCurrentX.node && nearestNodeOnOriginX.node == nearestNodeAverageDown.node) {
                return nearestNodeOnOriginX.node;
            }
            if (nearestNodeOnCurrentX != null && nearestNodeOriginSimple2D != null &&
                nearestNodeOnOriginX.node == nearestNodeOnCurrentX.node && nearestNodeOnOriginX.node == nearestNodeOriginSimple2D.node) {
                return nearestNodeOnOriginX.node;
            }
            if (nearestNodeOnCurrentX != null && nearestNodeBottomLeft != null && nearestNodeOnOriginX.node == nearestNodeOnCurrentX.node && nearestNodeOnOriginX.node == nearestNodeBottomLeft.node) {
                return nearestNodeOnOriginX.node;
            }
            if (nearestNodeOnCurrentX != null && nearestNodeAnythingAnywhereDown != null && nearestNodeOnOriginX.node == nearestNodeOnCurrentX.node && nearestNodeOnOriginX.node == nearestNodeAnythingAnywhereDown.node) {
                return nearestNodeOnOriginX.node;
            }
            if (nearestNodeAverageDown != null && nearestNodeOnOriginX.node == nearestNodeAverageDown.node) {
                return nearestNodeOnOriginX.node;
            }

            if (nearestNodeOnCurrentX != null && nearestNodeOnCurrentX.biasShortestDistance < Double.MAX_VALUE) {
                if (nearestNodeOnOriginX == null || (nearestNodeOnCurrentX.topLeftDistance < nearestNodeOnOriginX.topLeftDistance) &&
                    (nearestNodeOnCurrentX.originBottomLeftDistance < nearestNodeOnOriginX.originBottomLeftDistance) &&
                    (nearestNodeOnCurrentX.bounds.getMinX() - currentBottomLeft2D.getX()) < (nearestNodeOnOriginX.bounds.getMinX() - currentBottomLeft2D.getX())  )  {

                    return nearestNodeOnCurrentX.node;
                }
                else  if (nearestNodeOnOriginX != null) {
                    if (nearestNodeAverageDown == null || nearestNodeOnOriginX.averageDistance < nearestNodeAverageDown.averageDistance) {
                        return nearestNodeOnOriginX.node;
                    }
                }
            }
        }
        else {
            if (nearestNodeOnOriginX == null && nearestNodeOnCurrentX == null && nearestNodeCurrentSimple2D != null) {
                if (nearestNodeAverageDown != null && nearestNodeBottomLeft != null && nearestNodeAnythingAnywhereDown != null && (nearestNodeAverageDown.node == nearestNodeBottomLeft.node && nearestNodeAverageDown.node == nearestNodeAnythingAnywhereDown.node)) {
                    return nearestNodeAverageDown.node;
                }
                return nearestNodeCurrentSimple2D.node;
            }
            else if (nearestNodeAverageDown != null && nearestNodeBottomLeft != null && nearestNodeAnythingAnywhereDown != null &&
                     nearestNodeAverageDown.biasShortestDistance == nearestNodeBottomLeft.biasShortestDistance &&
                     nearestNodeAverageDown.biasShortestDistance == nearestNodeAnythingAnywhereDown.biasShortestDistance &&
                     nearestNodeAverageDown.biasShortestDistance < Double.MAX_VALUE) {

                if (nearestNodeOnOriginX != null && nearestNodeOnOriginX.originBottomLeftDistance < nearestNodeAverageDown.originBottomLeftDistance) {
                    return nearestNodeOnOriginX.node;
                }
                else {
                    return nearestNodeAverageDown.node;
                }
            }
        }

        /*
        ** is the average closer?
        */
        if (nearestNodeAverageDown != null && (nearestNodeOnOriginX == null || (nearestNodeAverageDown.biasShortestDistance < nearestNodeOnOriginX.biasShortestDistance))) {
            /*
            ** but is one in the way
            */
            if (nearestNodeOnOriginX != null && (nearestNodeOnOriginX.bounds.getMinY() <= nearestNodeAverageDown.bounds.getMinY())) {
                return nearestNodeOnOriginX.node;
            }
            if (nearestNodeOriginSimple2D != null) {
                if (nearestNodeOriginSimple2D.current2DMetric <= nearestNodeAverageDown.current2DMetric) {
                    return nearestNodeOriginSimple2D.node;
                }
                if (nearestNodeOnOriginX.bounds.getMinY() <= nearestNodeAverageDown.bounds.getMinY()) {
                    return nearestNodeOriginSimple2D.node;
                }
            }
            return nearestNodeAverageDown.node;
        }

        /*
        ** this is an odd one, in that is isn't the closest on current, or on the
        ** origin, but it looks better for most cases...
        */
        if ((nearestNodeCurrentSimple2D != null && nearestNodeOnCurrentX != null && nearestNodeAverageDown != null && nearestNodeBottomLeft != null && nearestNodeAnythingAnywhereDown != null) &&
            (nearestNodeCurrentSimple2D.node == nearestNodeOnCurrentX.node) &&
            (nearestNodeCurrentSimple2D.node ==  nearestNodeAverageDown.node) &&
            (nearestNodeCurrentSimple2D.node == nearestNodeBottomLeft.node) &&
            (nearestNodeCurrentSimple2D.node == nearestNodeAnythingAnywhereDown.node)) {
            return nearestNodeCurrentSimple2D.node;
        }

        if (nearestNodeOnOriginX != null && (nearestNodeOnCurrentX == null || (nearestNodeOnOriginX.topRightDistance < nearestNodeOnCurrentX.topRightDistance))) {
            return nearestNodeOnOriginX.node;
        }
        /*
        ** There isn't a clear winner, just go to the one nearest the current
        ** focus owner, or if invalid then try the other contenders.
        */
        if (nearestNodeOnOriginX != null) {
            return nearestNodeOnOriginX.node;
        }
        else if (nearestNodeOriginSimple2D != null) {
            return nearestNodeOriginSimple2D.node;
        }
        else if (nearestNodeOnCurrentX != null) {
            return nearestNodeOnCurrentX.node;
        }
        else if (nearestNodeAverageDown != null) {
            return nearestNodeAverageDown.node;
        }
        else if (nearestNodeBottomLeft != null) {
            return nearestNodeBottomLeft.node;
        }
        else if (nearestNodeAnythingAnywhereDown != null) {
            return nearestNodeAnythingAnywhereDown.node;
        }
        return null;
    }

    protected Node getNearestNodeLeft(Bounds currentB, Bounds originB, TraversalEngine engine, Node node, Node reversingNode) {

        List nodes = engine.getAllTargetNodes();

        Bounds biasedB = new BoundingBox(currentB.getMinX(), originB.getMinY(), currentB.getWidth(), originB.getHeight());

        Point2D currentMid2D = new Point2D(currentB.getMinX(), currentB.getMinY()+(currentB.getHeight()/2));
        Point2D currentTopLeft2D = new Point2D(currentB.getMinX(), currentB.getMinY());
        Point2D currentBottomLeft2D = new Point2D(currentB.getMinX(), currentB.getMaxY());

        Point2D originTopLeft2D = new Point2D(originB.getMinX(), originB.getMinY());

        TargetNode reversingTargetNode = null;
        TargetNode targetNode = new TargetNode();
        TargetNode nearestNodeCurrentSimple2D = null;
        TargetNode nearestNodeOriginSimple2D = null;
        TargetNode nearestNodeAverageLeft = null;
        TargetNode nearestNodeOnOriginY = null;
        TargetNode nearestNodeOnCurrentY = null;
        TargetNode nearestNodeTopLeft = null;
        TargetNode nearestNodeAnythingAnywhereLeft = null;

        if (nodes.size() > 0) {
            int nodeIndex;

            if (reversingNode != null) {
                Bounds targetBounds = reversingNode.localToScene(reversingNode.getLayoutBounds());
                reversingTargetNode = new TargetNode();

                reversingTargetNode.node = reversingNode;
                reversingTargetNode.bounds = targetBounds;

                /*
                ** closest biased : simple 2d
                */
                double outdB = outDistance(Direction.LEFT, biasedB, targetBounds);

                if (isOnAxis(Direction.LEFT, biasedB, targetBounds)) {
                    reversingTargetNode.biased2DMetric = outdB + centerSideDistance(Direction.LEFT, biasedB, targetBounds) / 100;
                }
                else {
                    final double cosd = cornerSideDistance(Direction.LEFT, biasedB, targetBounds);
                    reversingTargetNode.biased2DMetric = 100000 + outdB*outdB + 9*cosd*cosd;
                }
                /*
                ** closest current : simple 2d
                */
                double outdC = outDistance(Direction.LEFT, currentB, targetBounds);

                if (isOnAxis(Direction.LEFT, currentB, targetBounds)) {
                    reversingTargetNode.current2DMetric = outdC + centerSideDistance(Direction.LEFT, currentB, targetBounds) / 100;
                }
                else {
                    final double cosd = cornerSideDistance(Direction.LEFT, currentB, targetBounds);
                    reversingTargetNode.current2DMetric = 100000 + outdC*outdC + 9*cosd*cosd;
                }

                reversingTargetNode.topRightDistance = currentTopLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMinY());
                reversingTargetNode.midDistance = currentMid2D.distance(targetBounds.getMaxX(), targetBounds.getMinY()+(originB.getHeight()/2));
                reversingTargetNode.bottomRightDistance = currentBottomLeft2D.distance(originB.getMaxX(), targetBounds.getMaxY());

                double currentTopLeftToTargetBottomRightDistance = currentTopLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMaxY());
                double currentTopLeftToTargetMidDistance = currentTopLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMinY()+(targetBounds.getHeight()/2));
                double currentBottomLeftToTargetTopRightDistance = currentBottomLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMinY());
                double currentBottomLeftToTargetBottomRightDistance = currentBottomLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMaxY());
                double currentBottomLeftToTargetMidDistance = currentBottomLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMinY()+(targetBounds.getHeight()/2));
                double currentMidToTargetTopRightDistance = currentMid2D.distance(targetBounds.getMaxX(), targetBounds.getMinY());
                double currentMidToTargetBottomRightDistance = currentMid2D.distance(targetBounds.getMaxX(), targetBounds.getMaxY());
                double currentMidToTargetMidDistance = currentMid2D.distance(targetBounds.getMaxX(), targetBounds.getMinY()+(targetBounds.getHeight()/2));

                double biasTopLeftToTargetBottomRightDistance = currentTopLeft2D.distance(originB.getMaxX(), targetBounds.getMaxY());
                double biasTopLeftToTargetMidDistance = currentTopLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMinY()+(originB.getHeight()/2));
                double biasBottomLeftToTargetMidDistance = currentBottomLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMinY()+(originB.getHeight()/2));
                double biasMidToTargetBottomRightDistance = currentMid2D.distance(originB.getMaxX(), targetBounds.getMaxY());

                reversingTargetNode.averageDistance =
                    (reversingTargetNode.topRightDistance+biasTopLeftToTargetBottomRightDistance+biasTopLeftToTargetMidDistance+
                     currentBottomLeftToTargetTopRightDistance+reversingTargetNode.bottomRightDistance+biasBottomLeftToTargetMidDistance)/7;

                reversingTargetNode.biasShortestDistance = findMin9(reversingTargetNode.topRightDistance, biasTopLeftToTargetBottomRightDistance, biasTopLeftToTargetMidDistance,
                                                currentBottomLeftToTargetTopRightDistance, reversingTargetNode.bottomRightDistance, biasBottomLeftToTargetMidDistance,
                                                currentMidToTargetTopRightDistance, biasMidToTargetBottomRightDistance, reversingTargetNode.midDistance);

                reversingTargetNode.shortestDistance = findMin9(reversingTargetNode.topRightDistance, currentTopLeftToTargetBottomRightDistance, currentTopLeftToTargetMidDistance,
                                            currentBottomLeftToTargetTopRightDistance, currentBottomLeftToTargetBottomRightDistance, currentBottomLeftToTargetMidDistance,
                                            currentMidToTargetTopRightDistance, currentMidToTargetBottomRightDistance, currentMidToTargetMidDistance);
            }


            for (nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) {

                Bounds targetBounds = nodes.get(nodeIndex).localToScene(nodes.get(nodeIndex).getLayoutBounds());
                /*
                ** check that the target node starts after we start target.minX > origin.minX
                ** and the target node ends after we end target.maxX > origin.maxX
                */
                if (currentB.getMinX() >  targetBounds.getMinX()) {

                    targetNode.node = nodes.get(nodeIndex);
                    targetNode.bounds = targetBounds;

                    /*
                    ** closest biased : simple 2d
                    */
                    double outdB = outDistance(Direction.LEFT, biasedB, targetBounds);

                    if (isOnAxis(Direction.LEFT, biasedB, targetBounds)) {
                        targetNode.biased2DMetric = outdB + centerSideDistance(Direction.LEFT, biasedB, targetBounds) / 100;
                    }
                    else {
                        final double cosd = cornerSideDistance(Direction.LEFT, biasedB, targetBounds);
                        targetNode.biased2DMetric = 100000 + outdB*outdB + 9*cosd*cosd;
                    }
                    /*
                    ** closest current : simple 2d
                    */
                    double outdC = outDistance(Direction.LEFT, currentB, targetBounds);

                    if (isOnAxis(Direction.LEFT, currentB, targetBounds)) {
                        targetNode.current2DMetric = outdC + centerSideDistance(Direction.LEFT, currentB, targetBounds) / 100;
                    }
                    else {
                        final double cosd = cornerSideDistance(Direction.LEFT, currentB, targetBounds);
                        targetNode.current2DMetric = 100000 + outdC*outdC + 9*cosd*cosd;
                    }

                    targetNode.topRightDistance = currentTopLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMinY());
                    targetNode.midDistance = currentMid2D.distance(targetBounds.getMaxX(), targetBounds.getMinY()+(originB.getHeight()/2));
                    targetNode.bottomRightDistance = currentBottomLeft2D.distance(originB.getMaxX(), targetBounds.getMaxY());

                    double currentTopLeftToTargetBottomRightDistance = currentTopLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMaxY());
                    double currentTopLeftToTargetMidDistance = currentTopLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMinY()+(targetBounds.getHeight()/2));
                    double currentBottomLeftToTargetTopRightDistance = currentBottomLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMinY());
                    double currentBottomLeftToTargetBottomRightDistance = currentBottomLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMaxY());
                    double currentBottomLeftToTargetMidDistance = currentBottomLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMinY()+(targetBounds.getHeight()/2));
                    double currentMidToTargetTopRightDistance = currentMid2D.distance(targetBounds.getMaxX(), targetBounds.getMinY());
                    double currentMidToTargetBottomRightDistance = currentMid2D.distance(targetBounds.getMaxX(), targetBounds.getMaxY());
                    double currentMidToTargetMidDistance = currentMid2D.distance(targetBounds.getMaxX(), targetBounds.getMinY()+(targetBounds.getHeight()/2));

                    double biasTopLeftToTargetBottomRightDistance = currentTopLeft2D.distance(originB.getMaxX(), targetBounds.getMaxY());
                    double biasTopLeftToTargetMidDistance = currentTopLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMinY()+(originB.getHeight()/2));
                    double biasBottomLeftToTargetMidDistance = currentBottomLeft2D.distance(targetBounds.getMaxX(), targetBounds.getMinY()+(originB.getHeight()/2));
                    double biasMidToTargetBottomRightDistance = currentMid2D.distance(originB.getMaxX(), targetBounds.getMaxY());

                    targetNode.averageDistance =
                        (targetNode.topRightDistance+biasTopLeftToTargetBottomRightDistance+biasTopLeftToTargetMidDistance+
                         currentBottomLeftToTargetTopRightDistance+targetNode.bottomRightDistance+biasBottomLeftToTargetMidDistance)/7;

                    targetNode.biasShortestDistance =
                        findMin9(targetNode.topRightDistance, biasTopLeftToTargetBottomRightDistance, biasTopLeftToTargetMidDistance,
                                 currentBottomLeftToTargetTopRightDistance, targetNode.bottomRightDistance, biasBottomLeftToTargetMidDistance,
                                 currentMidToTargetTopRightDistance, biasMidToTargetBottomRightDistance, targetNode.midDistance);

                    targetNode.shortestDistance =
                        findMin9(targetNode.topRightDistance, currentTopLeftToTargetBottomRightDistance, currentTopLeftToTargetMidDistance,
                                 currentBottomLeftToTargetTopRightDistance, currentBottomLeftToTargetBottomRightDistance, currentBottomLeftToTargetMidDistance,
                                 currentMidToTargetTopRightDistance, currentMidToTargetBottomRightDistance, currentMidToTargetMidDistance);


                    /*
                    ** closest biased : simple 2d
                    */
                    if (outdB >= 0.0) {
                        if (nearestNodeOriginSimple2D == null || targetNode.biased2DMetric < nearestNodeOriginSimple2D.biased2DMetric) {

                            if (nearestNodeOriginSimple2D == null) {
                                nearestNodeOriginSimple2D = new TargetNode();
                            }
                            nearestNodeOriginSimple2D.copy(targetNode);
                        }
                    }
                    /*
                    ** closest current : simple 2d
                    */
                    if (outdC >= 0.0) {
                        if (nearestNodeCurrentSimple2D == null || targetNode.current2DMetric < nearestNodeCurrentSimple2D.current2DMetric) {

                            if (nearestNodeCurrentSimple2D == null) {
                                nearestNodeCurrentSimple2D = new TargetNode();
                            }
                            nearestNodeCurrentSimple2D.copy(targetNode);
                        }
                    }
                    /*
                    ** on the Origin Y
                    */
                    if ((originB.getMaxY() > targetBounds.getMinY()) && (targetBounds.getMaxY() > originB.getMinY())) {
                        if (nearestNodeOnOriginY == null || nearestNodeOnOriginY.topRightDistance > targetNode.topRightDistance) {

                            if (nearestNodeOnOriginY == null) {
                                nearestNodeOnOriginY = new TargetNode();
                            }
                            nearestNodeOnOriginY.copy(targetNode);
                        }
                    }
                    /*
                    ** on the Current Y
                    */
                    if ((currentB.getMaxY() > targetBounds.getMinY()) && (targetBounds.getMaxY() > currentB.getMinY())) {
                        if (nearestNodeOnCurrentY == null || nearestNodeOnCurrentY.topRightDistance > targetNode.topRightDistance) {

                            if (nearestNodeOnCurrentY == null) {
                                nearestNodeOnCurrentY = new TargetNode();
                            }
                            nearestNodeOnCurrentY.copy(targetNode);
                        }
                    }
                    /*
                    ** Closest top left / top right corners.
                    */
                    if (nearestNodeTopLeft == null || nearestNodeTopLeft.topRightDistance > targetNode.topRightDistance) {

                        if (nearestNodeTopLeft == null) {
                            nearestNodeTopLeft = new TargetNode();
                        }
                        nearestNodeTopLeft.copy(targetNode);
                    }

                    if (nearestNodeAverageLeft == null || nearestNodeAverageLeft.averageDistance > targetNode.averageDistance) {

                        if (nearestNodeAverageLeft == null) {
                            nearestNodeAverageLeft = new TargetNode();
                        }
                        nearestNodeAverageLeft.copy(targetNode);
                    }

                    if (nearestNodeAnythingAnywhereLeft == null || nearestNodeAnythingAnywhereLeft.shortestDistance > targetNode.shortestDistance) {

                        if (nearestNodeAnythingAnywhereLeft == null) {
                            nearestNodeAnythingAnywhereLeft = new TargetNode();
                        }
                        nearestNodeAnythingAnywhereLeft.copy(targetNode);
                    }
                }
            }
        }
        nodes.clear();

        if (reversingTargetNode != null) {
            reversingTargetNode.originTopRightDistance = originTopLeft2D.distance(reversingTargetNode.bounds.getMinX(), reversingTargetNode.bounds.getMinY());
        }
        if (nearestNodeOriginSimple2D != null) {
            nearestNodeOriginSimple2D.originTopRightDistance = originTopLeft2D.distance(nearestNodeOriginSimple2D.bounds.getMinX(), nearestNodeOriginSimple2D.bounds.getMinY());
        }
        if (nearestNodeCurrentSimple2D != null) {
            nearestNodeCurrentSimple2D.originTopRightDistance = originTopLeft2D.distance(nearestNodeCurrentSimple2D.bounds.getMinX(), nearestNodeCurrentSimple2D.bounds.getMinY());
        }
        if (nearestNodeOnOriginY != null) {
            nearestNodeOnOriginY.originTopRightDistance = originTopLeft2D.distance(nearestNodeOnOriginY.bounds.getMinX(), nearestNodeOnOriginY.bounds.getMinY());
        }
        if (nearestNodeOnCurrentY != null) {
            nearestNodeOnCurrentY.originTopRightDistance = originTopLeft2D.distance(nearestNodeOnCurrentY.bounds.getMinX(), nearestNodeOnCurrentY.bounds.getMinY());
        }
        if (nearestNodeAverageLeft != null) {
            nearestNodeAverageLeft.originTopRightDistance = originTopLeft2D.distance(nearestNodeAverageLeft.bounds.getMinX(), nearestNodeAverageLeft.bounds.getMinY());
        }
        if (nearestNodeTopLeft != null) {
            nearestNodeTopLeft.originTopRightDistance = originTopLeft2D.distance(nearestNodeTopLeft.bounds.getMinX(), nearestNodeTopLeft.bounds.getMinY());
        }
        if (nearestNodeAnythingAnywhereLeft != null) {
            nearestNodeAnythingAnywhereLeft.originTopRightDistance = originTopLeft2D.distance(nearestNodeAnythingAnywhereLeft.bounds.getMinX(), nearestNodeAnythingAnywhereLeft.bounds.getMinY());
        }

        if (nearestNodeOnCurrentY == null && nearestNodeOnOriginY == null && reversingTargetNode == null) {
            cacheStartTraversalNode = null;
            cacheStartTraversalDirection = null;
            reverseDirection = false;
            traversalNodeStack.clear();
        }

        if (focusLogger.isLoggable(PlatformLogger.FINER)) {
            if (reversingTargetNode != null) {
                focusLogger.finer("reversingTargetNode.node : "+reversingTargetNode.node);
            }
            if (nearestNodeOriginSimple2D != null) {
                focusLogger.finer("nearestNodeOriginSimple2D.node : "+nearestNodeOriginSimple2D.node);
            }
            if (nearestNodeCurrentSimple2D != null) {
                focusLogger.finer("nearestNodeCurrentSimple2D.node : "+nearestNodeCurrentSimple2D.node);
            }
            if (nearestNodeOnOriginY != null) {
                focusLogger.finer("nearestNodeOnOriginY.node : "+nearestNodeOnOriginY.node);
            }
            if (nearestNodeOnCurrentY != null) {
                focusLogger.finer("nearestNodeOnCurrentY.node : "+nearestNodeOnCurrentY.node);
            }
            if (nearestNodeAverageLeft != null) {
                focusLogger.finer("nearestNodeAverageLeft.node : "+nearestNodeAverageLeft.node);
            }
            if (nearestNodeTopLeft != null) {
                focusLogger.finer("nearestNodeTopLeft.node : "+nearestNodeTopLeft.node);
            }
            if (nearestNodeAnythingAnywhereLeft != null) {
                focusLogger.finer("nearestNodeAnythingAnywhereLeft.node : "+nearestNodeAnythingAnywhereLeft.node);
            }
        }

        /*
        ** we've just changed direction, and have a node on stack.
        ** there is a strong preference for this node, just make sure
        ** it's not a bad choice, as sometimes we got here as a last-chance
        */
        if (reversingTargetNode != null) {
            return reversingNode;
        }

        if (nearestNodeOnOriginY != null && nearestNodeOnOriginY.biasShortestDistance < Double.MAX_VALUE) {
            /*
            ** there's a preference, all else being equal, to return nearestNodeOnOriginY
            */
            if (nearestNodeOnOriginY != null && nearestNodeOnCurrentY != null &&
                (nearestNodeOnOriginY.node == nearestNodeOnCurrentY.node)) {
                return nearestNodeOnOriginY.node;
            }
            if (nearestNodeOnOriginY != null && nearestNodeOnCurrentY != null && nearestNodeAverageLeft != null &&
                (nearestNodeOnOriginY.node == nearestNodeOnCurrentY.node) && (nearestNodeOnOriginY.node == nearestNodeAverageLeft.node)) {
                return nearestNodeOnOriginY.node;
            }
            if (nearestNodeOnOriginY != null && nearestNodeOnCurrentY != null && nearestNodeTopLeft != null &&
                (nearestNodeOnOriginY.node == nearestNodeOnCurrentY.node) && (nearestNodeOnOriginY.node == nearestNodeTopLeft.node)) {
                return nearestNodeOnOriginY.node;
            }
            if (nearestNodeOnOriginY != null && nearestNodeOnCurrentY != null && nearestNodeAnythingAnywhereLeft != null &&
                (nearestNodeOnOriginY.node == nearestNodeOnCurrentY.node) && (nearestNodeOnOriginY.node == nearestNodeAnythingAnywhereLeft.node)) {
                return nearestNodeOnOriginY.node;
            }
            if (nearestNodeOnOriginY != null && nearestNodeAverageLeft != null &&
                (nearestNodeOnOriginY.node == nearestNodeAverageLeft.node)) {
                return nearestNodeOnOriginY.node;
            }

            if (nearestNodeOnCurrentY != null && nearestNodeOnCurrentY.biasShortestDistance < Double.MAX_VALUE) {
                if (nearestNodeOnOriginY == null ||
                    (nearestNodeOnCurrentY.bottomRightDistance < nearestNodeOnOriginY.bottomRightDistance) &&
                    (nearestNodeOnCurrentY.originTopRightDistance < nearestNodeOnOriginY.originTopRightDistance) &&
                    (nearestNodeOnCurrentY.bounds.getMinY() - currentTopLeft2D.getY()) < (nearestNodeOnOriginY.bounds.getMinY() - currentTopLeft2D.getY())  )  {

                    return nearestNodeOnCurrentY.node;
                }
                else if (nearestNodeOnOriginY != null) {
                    if (nearestNodeAverageLeft != null || nearestNodeOnOriginY.averageDistance < nearestNodeAverageLeft.averageDistance) {
                        return nearestNodeOnOriginY.node;
                    }
                }
            }
        }
        else {
            if (nearestNodeOnOriginY == null && nearestNodeOnCurrentY == null && nearestNodeCurrentSimple2D != null) {
                if (nearestNodeAverageLeft != null && nearestNodeTopLeft != null && nearestNodeAverageLeft != null &&  nearestNodeAnythingAnywhereLeft != null &&
                    nearestNodeAverageLeft.node == nearestNodeTopLeft.node && nearestNodeAverageLeft.node == nearestNodeAnythingAnywhereLeft.node) {
                    return nearestNodeAverageLeft.node;
                }
                return nearestNodeCurrentSimple2D.node;
            }
            else if (nearestNodeAverageLeft != null && nearestNodeTopLeft != null && nearestNodeAnythingAnywhereLeft != null &&
                     nearestNodeAverageLeft.biasShortestDistance == nearestNodeTopLeft.biasShortestDistance &&
                     nearestNodeAverageLeft.biasShortestDistance == nearestNodeAnythingAnywhereLeft.biasShortestDistance &&
                     nearestNodeAverageLeft.biasShortestDistance < Double.MAX_VALUE) {

                if (nearestNodeOnOriginY != null && nearestNodeOnOriginY.originTopRightDistance < nearestNodeAverageLeft.originTopRightDistance) {
                    return nearestNodeOnOriginY.node;
                }
                else {
                    return nearestNodeAverageLeft.node;
                }
            }
        }

        /*
        ** is the average closer?
        */
        if (nearestNodeAverageLeft != null && (nearestNodeOnOriginY == null || nearestNodeAverageLeft.biasShortestDistance < nearestNodeOnOriginY.biasShortestDistance)) {
            /*
            ** but is one in the way
            */
            if (nearestNodeOnOriginY != null && (nearestNodeOnOriginY.bounds.getMaxX() >= nearestNodeAverageLeft.bounds.getMaxX())) {
                return nearestNodeOnOriginY.node;
            }
            /*
            ** maybe Origin is better than this?
            */
            if (nearestNodeOnOriginY != null && nearestNodeOnCurrentY != null && nearestNodeOnOriginY.biasShortestDistance < Double.MAX_VALUE && (nearestNodeOnOriginY.node == nearestNodeOnCurrentY.node)) {
                return nearestNodeOnOriginY.node;
            }

            if (nearestNodeOnCurrentY != null && nearestNodeOnOriginY != null && nearestNodeOnCurrentY.biasShortestDistance < Double.MAX_VALUE && (nearestNodeOnCurrentY.biasShortestDistance < nearestNodeOnOriginY.biasShortestDistance)) {
                return nearestNodeOnCurrentY.node;
            }

            if (nearestNodeOnOriginY != null &&  nearestNodeAverageLeft != null && nearestNodeOnOriginY.biasShortestDistance < Double.MAX_VALUE && (nearestNodeOnOriginY.originTopRightDistance < nearestNodeAverageLeft.originTopRightDistance)) {
                return nearestNodeOnOriginY.node;
            }
            return nearestNodeAverageLeft.node;
        }


        if (nearestNodeOnOriginY != null && nearestNodeOnCurrentY != null && nearestNodeOnOriginY.bottomRightDistance < nearestNodeOnCurrentY.bottomRightDistance) {
            return nearestNodeOnOriginY.node;
        }

        /*
        ** if any of the remaining match we'll take that
        */
        if (nearestNodeOnCurrentY != null && nearestNodeTopLeft != null && nearestNodeOnCurrentY.biasShortestDistance < Double.MAX_VALUE && (nearestNodeOnCurrentY.node == nearestNodeTopLeft.node)) {
            return nearestNodeOnCurrentY.node;
        }
        /*
        ** There isn't a clear winner, just go to the one nearest the current
        ** focus owner, or if invalid then try the other contenders.
        */
        if (nearestNodeOnOriginY != null) {
            return nearestNodeOnOriginY.node;
        }
        else if (nearestNodeOriginSimple2D != null) {
            return nearestNodeOriginSimple2D.node;
        }
        else if (nearestNodeOnCurrentY != null) {
            return nearestNodeOnCurrentY.node;
        }
        else if (nearestNodeAverageLeft != null) {
            return nearestNodeAverageLeft.node;
        }
        else if (nearestNodeTopLeft != null) {
            return nearestNodeTopLeft.node;
        }
        else if (nearestNodeAnythingAnywhereLeft != null) {
            return nearestNodeAnythingAnywhereLeft.node;
        }
        return null;
    }

    protected Node getNearestNodeRight(Bounds currentB, Bounds originB, TraversalEngine engine, Node node, Node reversingNode) {

        List nodes = engine.getAllTargetNodes();

        Bounds biasedB = new BoundingBox(currentB.getMinX(), originB.getMinY(), currentB.getWidth(), originB.getHeight());

        Point2D currentMid2D = new Point2D(currentB.getMaxX(), currentB.getMinY()+(currentB.getHeight()/2));
        Point2D currentTopRight2D = new Point2D(currentB.getMaxX(), currentB.getMinY());
        Point2D currentBottomRight2D = new Point2D(currentB.getMaxX(), currentB.getMaxY());

        Point2D originTopRight2D = new Point2D(originB.getMaxX(), originB.getMinY());

        TargetNode reversingTargetNode = null;
        TargetNode targetNode = new TargetNode();
        TargetNode nearestNodeCurrentSimple2D = null;
        TargetNode nearestNodeOriginSimple2D = null;
        TargetNode nearestNodeAverageRight = null;
        TargetNode nearestNodeOnOriginY = null;
        TargetNode nearestNodeOnCurrentY = null;
        TargetNode nearestNodeTopRight = null;
        TargetNode nearestNodeAnythingAnywhereRight = null;

        if (nodes.size() > 0) {
            int nodeIndex;

            if (reversingNode != null) {
                Bounds targetBounds = reversingNode.localToScene(reversingNode.getLayoutBounds());
                reversingTargetNode = new TargetNode();

                reversingTargetNode.node = reversingNode;
                reversingTargetNode.bounds = targetBounds;

                /*
                ** closest biased : simple 2d
                */
                double outdB = outDistance(Direction.RIGHT, biasedB, targetBounds);

                if (isOnAxis(Direction.RIGHT, biasedB, targetBounds)) {
                    reversingTargetNode.biased2DMetric = outdB + centerSideDistance(Direction.RIGHT, biasedB, targetBounds) / 100;
                }
                else {
                    final double cosd = cornerSideDistance(Direction.RIGHT, biasedB, targetBounds);
                    reversingTargetNode.biased2DMetric = 100000 + outdB*outdB + 9*cosd*cosd;
                }
                /*
                ** closest current : simple 2d
                */
                double outdC = outDistance(Direction.RIGHT, currentB, targetBounds);

                if (isOnAxis(Direction.RIGHT, currentB, targetBounds)) {
                    reversingTargetNode.current2DMetric = outdC + centerSideDistance(Direction.RIGHT, currentB, targetBounds) / 100;
                }
                else {
                    final double cosd = cornerSideDistance(Direction.RIGHT, currentB, targetBounds);
                    reversingTargetNode.current2DMetric = 100000 + outdC*outdC + 9*cosd*cosd;
                }

                reversingTargetNode.topLeftDistance = currentTopRight2D.distance(targetBounds.getMinX(), targetBounds.getMinY());
                reversingTargetNode.midDistance  = currentMid2D.distance(targetBounds.getMinX(), targetBounds.getMinY()+(originB.getHeight()/2));
                reversingTargetNode.bottomLeftDistance = currentBottomRight2D.distance(originB.getMinX(), targetBounds.getMaxY());

                double currentTopRightToTargetBottomLeftDistance = currentTopRight2D.distance(targetBounds.getMinX(), targetBounds.getMaxY());
                double currentTopRightToTargetMidDistance = currentTopRight2D.distance(targetBounds.getMinX(), targetBounds.getMinY()+(targetBounds.getHeight()/2));
                double currentBottomRightToTargetTopLeftDistance = currentBottomRight2D.distance(targetBounds.getMinX(), targetBounds.getMinY());
                double currentBottomRightToTargetBottomLeftDistance = currentBottomRight2D.distance(targetBounds.getMinX(), targetBounds.getMaxY());
                double currentBottomRightToTargetMidDistance = currentBottomRight2D.distance(targetBounds.getMinX(), targetBounds.getMinY()+(targetBounds.getHeight()/2));
                double currentMidToTargetTopLeftDistance = currentMid2D.distance(targetBounds.getMinX(), targetBounds.getMinY());
                double currentMidToTargetBottomLeftDistance = currentMid2D.distance(targetBounds.getMinX(), targetBounds.getMaxY());
                double currentMidToTargetMidDistance = currentMid2D.distance(targetBounds.getMinX(), targetBounds.getMinY()+(targetBounds.getHeight()/2));

                double biasTopRightToTargetBottomLeftDistance = currentTopRight2D.distance(originB.getMinX(), targetBounds.getMaxY());
                double biasTopRightToTargetMidDistance = currentTopRight2D.distance(targetBounds.getMinX(), targetBounds.getMinY()+(originB.getHeight()/2));
                double biasBottomRightToTargetMidDistance = currentBottomRight2D.distance(targetBounds.getMinX(), targetBounds.getMinY()+(originB.getHeight()/2));
                double biasMidToTargetBottomLeftDistance = currentMid2D.distance(originB.getMinX(), targetBounds.getMaxY());

                reversingTargetNode.averageDistance =
                    (reversingTargetNode.topLeftDistance+biasTopRightToTargetBottomLeftDistance+biasTopRightToTargetMidDistance+
                     currentBottomRightToTargetTopLeftDistance+reversingTargetNode.bottomLeftDistance+biasBottomRightToTargetMidDistance)/7;


                reversingTargetNode.biasShortestDistance =
                    findMin9(reversingTargetNode.topLeftDistance, biasTopRightToTargetBottomLeftDistance, biasTopRightToTargetMidDistance,
                             currentBottomRightToTargetTopLeftDistance, reversingTargetNode.bottomLeftDistance, biasBottomRightToTargetMidDistance,
                             currentMidToTargetTopLeftDistance, biasMidToTargetBottomLeftDistance, reversingTargetNode.midDistance);

                reversingTargetNode.shortestDistance =
                    findMin9(reversingTargetNode.topLeftDistance, currentTopRightToTargetBottomLeftDistance, currentTopRightToTargetMidDistance,
                             currentBottomRightToTargetTopLeftDistance, currentBottomRightToTargetBottomLeftDistance, currentBottomRightToTargetMidDistance,
                             currentMidToTargetTopLeftDistance, currentMidToTargetBottomLeftDistance, currentMidToTargetMidDistance);
            }


            for (nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) {

                Bounds targetBounds = nodes.get(nodeIndex).localToScene(nodes.get(nodeIndex).getLayoutBounds());
                /*
                ** check that the target node starts after we start target.minX > origin.minX
                ** and the target node ends after we end target.maxX > origin.maxX
                */
                if (currentB.getMaxX() <  targetBounds.getMaxX()) {

                    targetNode.node = nodes.get(nodeIndex);
                    targetNode.bounds = targetBounds;

                    /*
                    ** closest biased : simple 2d
                    */
                    double outdB = outDistance(Direction.RIGHT, biasedB, targetBounds);

                    if (isOnAxis(Direction.RIGHT, biasedB, targetBounds)) {
                        targetNode.biased2DMetric = outdB + centerSideDistance(Direction.RIGHT, biasedB, targetBounds) / 100;
                    }
                    else {
                        final double cosd = cornerSideDistance(Direction.RIGHT, biasedB, targetBounds);
                        targetNode.biased2DMetric = 100000 + outdB*outdB + 9*cosd*cosd;
                    }
                    /*
                    ** closest current : simple 2d
                    */
                    double outdC = outDistance(Direction.RIGHT, currentB, targetBounds);

                    if (isOnAxis(Direction.RIGHT, currentB, targetBounds)) {
                        targetNode.current2DMetric = outdC + centerSideDistance(Direction.RIGHT, currentB, targetBounds) / 100;
                    }
                    else {
                        final double cosd = cornerSideDistance(Direction.RIGHT, currentB, targetBounds);
                        targetNode.current2DMetric = 100000 + outdC*outdC + 9*cosd*cosd;
                    }

                    targetNode.topLeftDistance = currentTopRight2D.distance(targetBounds.getMinX(), targetBounds.getMinY());
                    targetNode.midDistance = currentMid2D.distance(targetBounds.getMinX(), targetBounds.getMinY()+(originB.getHeight()/2));
                    targetNode.bottomLeftDistance = currentBottomRight2D.distance(originB.getMinX(), targetBounds.getMaxY());

                    double currentTopRightToTargetBottomLeftDistance = currentTopRight2D.distance(targetBounds.getMinX(), targetBounds.getMaxY());
                    double currentTopRightToTargetMidDistance = currentTopRight2D.distance(targetBounds.getMinX(), targetBounds.getMinY()+(targetBounds.getHeight()/2));
                    double currentBottomRightToTargetTopLeftDistance = currentBottomRight2D.distance(targetBounds.getMinX(), targetBounds.getMinY());
                    double currentBottomRightToTargetBottomLeftDistance = currentBottomRight2D.distance(targetBounds.getMinX(), targetBounds.getMaxY());
                    double currentBottomRightToTargetMidDistance = currentBottomRight2D.distance(targetBounds.getMinX(), targetBounds.getMinY()+(targetBounds.getHeight()/2));
                    double currentMidToTargetTopLeftDistance = currentMid2D.distance(targetBounds.getMinX(), targetBounds.getMinY());
                    double currentMidToTargetBottomLeftDistance = currentMid2D.distance(targetBounds.getMinX(), targetBounds.getMaxY());
                    double currentMidToTargetMidDistance = currentMid2D.distance(targetBounds.getMinX(), targetBounds.getMinY()+(targetBounds.getHeight()/2));

                    double biasTopRightToTargetBottomLeftDistance = currentTopRight2D.distance(originB.getMinX(), targetBounds.getMaxY());
                    double biasTopRightToTargetMidDistance = currentTopRight2D.distance(targetBounds.getMinX(), targetBounds.getMinY()+(originB.getHeight()/2));
                    double biasBottomRightToTargetMidDistance = currentBottomRight2D.distance(targetBounds.getMinX(), targetBounds.getMinY()+(originB.getHeight()/2));
                    double biasMidToTargetBottomLeftDistance = currentMid2D.distance(originB.getMinX(), targetBounds.getMaxY());

                    targetNode.averageDistance =
                        (targetNode.topLeftDistance+biasTopRightToTargetBottomLeftDistance+biasTopRightToTargetMidDistance+
                         currentBottomRightToTargetTopLeftDistance+targetNode.bottomLeftDistance+biasBottomRightToTargetMidDistance)/7;

                    targetNode.biasShortestDistance =
                        findMin9(targetNode.topLeftDistance, biasTopRightToTargetBottomLeftDistance, biasTopRightToTargetMidDistance,
                                 currentBottomRightToTargetTopLeftDistance, targetNode.bottomLeftDistance, biasBottomRightToTargetMidDistance,
                                 currentMidToTargetTopLeftDistance, biasMidToTargetBottomLeftDistance, targetNode.midDistance);

                    targetNode.shortestDistance =
                        findMin9(targetNode.topLeftDistance, currentTopRightToTargetBottomLeftDistance, currentTopRightToTargetMidDistance,
                                 currentBottomRightToTargetTopLeftDistance, currentBottomRightToTargetBottomLeftDistance, currentBottomRightToTargetMidDistance,
                                 currentMidToTargetTopLeftDistance, currentMidToTargetBottomLeftDistance, currentMidToTargetMidDistance);


                    /*
                    ** closest biased : simple 2d
                    */
                    if (outdB >= 0.0) {
                        if (nearestNodeOriginSimple2D == null || targetNode.biased2DMetric < nearestNodeOriginSimple2D.biased2DMetric) {

                            if (nearestNodeOriginSimple2D == null) {
                                nearestNodeOriginSimple2D = new TargetNode();
                            }
                            nearestNodeOriginSimple2D.copy(targetNode);
                        }
                    }
                    /*
                    ** closest current : simple 2d
                    */
                    if (outdC >= 0.0) {
                        if (nearestNodeCurrentSimple2D == null || targetNode.current2DMetric < nearestNodeCurrentSimple2D.current2DMetric) {

                            if (nearestNodeCurrentSimple2D == null) {
                                nearestNodeCurrentSimple2D = new TargetNode();
                            }
                            nearestNodeCurrentSimple2D.copy(targetNode);
                        }
                    }
                    /*
                    ** on the Origin Y
                    */
                    if ((originB.getMaxY() > targetBounds.getMinY()) && (targetBounds.getMaxY() > originB.getMinY())) {
                        if (nearestNodeOnOriginY == null || nearestNodeOnOriginY.topLeftDistance > targetNode.topLeftDistance) {

                            if (nearestNodeOnOriginY == null) {
                                nearestNodeOnOriginY = new TargetNode();
                            }
                            nearestNodeOnOriginY.copy(targetNode);
                        }
                    }
                    /*
                    ** on the Current Y
                    */
                    if ((currentB.getMaxY() > targetBounds.getMinY()) && (targetBounds.getMaxY() > currentB.getMinY())) {
                        if (nearestNodeOnCurrentY == null || nearestNodeOnCurrentY.topLeftDistance > targetNode.topRightDistance) {

                            if (nearestNodeOnCurrentY == null) {
                                nearestNodeOnCurrentY = new TargetNode();
                            }
                            nearestNodeOnCurrentY.copy(targetNode);
                        }
                    }
                    /*
                    ** Closest top right / top left corners.
                    */
                    if (nearestNodeTopRight == null || nearestNodeTopRight.topLeftDistance > targetNode.topLeftDistance) {
                        if (nearestNodeTopRight == null) {
                            nearestNodeTopRight = new TargetNode();
                        }
                        nearestNodeTopRight.copy(targetNode);
                    }

                    if (nearestNodeAverageRight == null || nearestNodeAverageRight.averageDistance > targetNode.averageDistance) {
                        if (nearestNodeAverageRight == null) {
                            nearestNodeAverageRight = new TargetNode();
                        }
                        nearestNodeAverageRight.copy(targetNode);
                    }

                    if (nearestNodeAnythingAnywhereRight == null || nearestNodeAnythingAnywhereRight.shortestDistance > targetNode.shortestDistance) {

                        if (nearestNodeAnythingAnywhereRight == null) {
                            nearestNodeAnythingAnywhereRight = new TargetNode();
                        }
                        nearestNodeAnythingAnywhereRight.copy(targetNode);
                    }
                }
            }
        }
        nodes.clear();

        if (reversingTargetNode != null) {
            reversingTargetNode.originTopLeftDistance = originTopRight2D.distance(reversingTargetNode.bounds.getMinX(), reversingTargetNode.bounds.getMinY());
        }
        if (nearestNodeOriginSimple2D != null) {
            nearestNodeOriginSimple2D.originTopLeftDistance = originTopRight2D.distance(nearestNodeOriginSimple2D.bounds.getMinX(), nearestNodeOriginSimple2D.bounds.getMinY());
        }
        if (nearestNodeCurrentSimple2D != null) {
            nearestNodeCurrentSimple2D.originTopLeftDistance = originTopRight2D.distance(nearestNodeCurrentSimple2D.bounds.getMinX(), nearestNodeCurrentSimple2D.bounds.getMinY());
        }
        if (nearestNodeOnOriginY != null) {
            nearestNodeOnOriginY.originTopLeftDistance = originTopRight2D.distance(nearestNodeOnOriginY.bounds.getMinX(), nearestNodeOnOriginY.bounds.getMinY());
        }
        if (nearestNodeOnCurrentY != null) {
            nearestNodeOnCurrentY.originTopLeftDistance = originTopRight2D.distance(nearestNodeOnCurrentY.bounds.getMinX(), nearestNodeOnCurrentY.bounds.getMinY());
        }
        if (nearestNodeAverageRight != null) {
            nearestNodeAverageRight.originTopLeftDistance = originTopRight2D.distance(nearestNodeAverageRight.bounds.getMinX(), nearestNodeAverageRight.bounds.getMinY());
        }
        if (nearestNodeTopRight != null) {
            nearestNodeTopRight.originTopLeftDistance = originTopRight2D.distance(nearestNodeTopRight.bounds.getMinX(), nearestNodeTopRight.bounds.getMinY());
        }
        if (nearestNodeAnythingAnywhereRight != null) {
            nearestNodeAnythingAnywhereRight.originTopLeftDistance = originTopRight2D.distance(nearestNodeAnythingAnywhereRight.bounds.getMinX(), nearestNodeAnythingAnywhereRight.bounds.getMinY());
        }

        if (nearestNodeOnCurrentY == null && nearestNodeOnOriginY == null && reversingTargetNode == null) {
            cacheStartTraversalNode = null;
            cacheStartTraversalDirection = null;
            reverseDirection = false;
            traversalNodeStack.clear();
        }

        if (focusLogger.isLoggable(PlatformLogger.FINER)) {
            if (reversingTargetNode != null) {
                focusLogger.finer("reversingTargetNode.node : "+reversingTargetNode.node);
            }
            if (nearestNodeOriginSimple2D != null) {
                focusLogger.finer("nearestNodeOriginSimple2D.node : "+nearestNodeOriginSimple2D.node);
            }
            if (nearestNodeCurrentSimple2D != null) {
                focusLogger.finer("nearestNodeCurrentSimple2D.node : "+nearestNodeCurrentSimple2D.node);
            }
            if (nearestNodeOnOriginY != null) {
                focusLogger.finer("nearestNodeOnOriginY.node : "+nearestNodeOnOriginY.node);
            }
            if (nearestNodeOnCurrentY != null) {
                focusLogger.finer("nearestNodeOnCurrentY.node : "+nearestNodeOnCurrentY.node);
            }
            if (nearestNodeAverageRight != null) {
                focusLogger.finer("nearestNodeAverageRight.node : "+nearestNodeAverageRight.node);
            }
            if (nearestNodeTopRight != null) {
                focusLogger.finer("nearestNodeTopRight.node : "+nearestNodeTopRight.node);
            }
            if (nearestNodeAnythingAnywhereRight != null) {
                focusLogger.finer("nearestNodeAnythingAnywhereRight.node : "+nearestNodeAnythingAnywhereRight.node);
            }
        }

        /*
        ** we've just changed direction, and have a node on stack.
        ** there is a strong preference for this node, just make sure
        ** it's not a bad choice, as sometimes we got here as a last-chance
        */
        if (reversingTargetNode != null) {
            return reversingNode;
        }
        if (nearestNodeOnOriginY != null && nearestNodeOnOriginY.biasShortestDistance < Double.MAX_VALUE) {
            /*
            ** there's a preference, all else being equal, to return nearestNodeOnOriginY
            */
            if (nearestNodeOnOriginY != null && nearestNodeOnCurrentY != null &&
                (nearestNodeOnOriginY.node == nearestNodeOnCurrentY.node)) {
                return nearestNodeOnOriginY.node;
            }
            if (nearestNodeOnOriginY != null && nearestNodeOnCurrentY != null && nearestNodeAverageRight != null &&
                (nearestNodeOnOriginY.node == nearestNodeOnCurrentY.node) && (nearestNodeOnOriginY.node == nearestNodeAverageRight.node)) {
                return nearestNodeOnOriginY.node;
            }
            if (nearestNodeOnOriginY != null && nearestNodeOnCurrentY != null && nearestNodeTopRight != null &&
                (nearestNodeOnOriginY.node == nearestNodeOnCurrentY.node) && (nearestNodeOnOriginY.node == nearestNodeTopRight.node)) {
                return nearestNodeOnOriginY.node;
            }
            if (nearestNodeOnOriginY != null && nearestNodeOnCurrentY != null && nearestNodeAnythingAnywhereRight != null &&
                (nearestNodeOnOriginY.node == nearestNodeOnCurrentY.node) && (nearestNodeOnOriginY.node == nearestNodeAnythingAnywhereRight.node)) {
                return nearestNodeOnOriginY.node;
            }
            if (nearestNodeOnOriginY != null && nearestNodeAverageRight != null &&
                (nearestNodeOnOriginY.node == nearestNodeAverageRight.node)) {
                return nearestNodeOnOriginY.node;
            }

            if (nearestNodeOnCurrentY != null && nearestNodeOnCurrentY.biasShortestDistance < Double.MAX_VALUE) {
                if (nearestNodeOnOriginY == null ||
                    (nearestNodeOnCurrentY.topLeftDistance < nearestNodeOnOriginY.topLeftDistance) &&
                    (nearestNodeOnCurrentY.originTopLeftDistance < nearestNodeOnOriginY.originTopLeftDistance) &&
                    (nearestNodeOnCurrentY.bounds.getMinY() - currentTopRight2D.getY()) < (nearestNodeOnOriginY.bounds.getMinY() - currentTopRight2D.getY())  )  {

                    return nearestNodeOnCurrentY.node;
                }
                else if (nearestNodeOnOriginY != null) {
                    if (nearestNodeAverageRight != null || nearestNodeOnOriginY.averageDistance < nearestNodeAverageRight.averageDistance) {
                        return nearestNodeOnOriginY.node;
                    }
                }
            }
        }
        else {
            if (nearestNodeOnOriginY == null && nearestNodeOnCurrentY == null && nearestNodeCurrentSimple2D != null) {
                if (nearestNodeAverageRight != null && nearestNodeTopRight != null && nearestNodeAverageRight != null && nearestNodeAnythingAnywhereRight != null &&
                    nearestNodeAverageRight.node == nearestNodeTopRight.node && nearestNodeAverageRight.node == nearestNodeAnythingAnywhereRight.node) {
                    return nearestNodeAverageRight.node;
                }
                return nearestNodeCurrentSimple2D.node;
            }
            else if (nearestNodeAverageRight != null && nearestNodeTopRight != null && nearestNodeAnythingAnywhereRight != null &&
                     nearestNodeAverageRight.biasShortestDistance == nearestNodeTopRight.biasShortestDistance &&
                     nearestNodeAverageRight.biasShortestDistance == nearestNodeAnythingAnywhereRight.biasShortestDistance &&
                     nearestNodeAverageRight.biasShortestDistance < Double.MAX_VALUE) {

                if (nearestNodeOnOriginY != null && nearestNodeOnOriginY.originTopLeftDistance < nearestNodeAverageRight.originTopLeftDistance) {
                    return nearestNodeOnOriginY.node;
                }
                else {
                    return nearestNodeAverageRight.node;
                }
            }
        }
        /*
        ** is the average closer?
        */
        if (nearestNodeAverageRight != null && (nearestNodeOnOriginY == null || nearestNodeAverageRight.biasShortestDistance < nearestNodeOnOriginY.biasShortestDistance)) {
            /*
            ** but is one in the way
            */
            if (nearestNodeOnOriginY != null && (nearestNodeOnOriginY.bounds.getMinX() >= nearestNodeAverageRight.bounds.getMinX())) {
                return nearestNodeOnOriginY.node;
            }
            /*
            ** maybe Origin is better than this?
            */
            if (nearestNodeOnOriginY != null && nearestNodeOnCurrentY != null &&  nearestNodeOnOriginY.biasShortestDistance < Double.MAX_VALUE && (nearestNodeOnOriginY.node == nearestNodeOnCurrentY.node)) {
                return nearestNodeOnOriginY.node;
            }

            if (nearestNodeOnCurrentY != null && nearestNodeOnOriginY != null && nearestNodeOnCurrentY.biasShortestDistance < Double.MAX_VALUE && (nearestNodeOnCurrentY.biasShortestDistance < nearestNodeOnOriginY.biasShortestDistance)) {
                return nearestNodeOnCurrentY.node;
            }
            if (nearestNodeOnOriginY != null &&  nearestNodeAverageRight != null && nearestNodeOnOriginY.biasShortestDistance < Double.MAX_VALUE && (nearestNodeOnOriginY.originTopLeftDistance < nearestNodeAverageRight.originTopLeftDistance)) {
                return nearestNodeOnOriginY.node;
            }
            return nearestNodeAverageRight.node;
        }

        if (nearestNodeOnOriginY != null && nearestNodeOnCurrentY != null && nearestNodeOnOriginY.bottomLeftDistance < nearestNodeOnCurrentY.bottomLeftDistance) {
            return nearestNodeOnOriginY.node;
        }

        /*
        ** if any of the remaining match we'll take that
        */
        if (nearestNodeOnCurrentY != null && nearestNodeTopRight != null && nearestNodeOnCurrentY.biasShortestDistance < Double.MAX_VALUE && (nearestNodeOnCurrentY.node == nearestNodeTopRight.node)) {
            return nearestNodeOnCurrentY.node;
        }
        /*
        ** There isn't a clear winner, just go to the one nearest the current
        ** focus owner, or if invalid then try the other contenders.
        */
        if (nearestNodeOnOriginY != null) {
            return nearestNodeOnOriginY.node;
        }
        else if (nearestNodeOriginSimple2D != null) {
            return nearestNodeOriginSimple2D.node;
        }
        else if (nearestNodeOnCurrentY != null) {
            return nearestNodeOnCurrentY.node;
        }
        else if (nearestNodeAverageRight != null) {
            return nearestNodeAverageRight.node;
        }
        else if (nearestNodeTopRight != null) {
            return nearestNodeTopRight.node;
        }
        else if (nearestNodeAnythingAnywhereRight != null) {
            return nearestNodeAnythingAnywhereRight.node;
        }
        return null;
    }


    static final class TargetNode {
        Node node = null;
        Bounds bounds = null;
        double biased2DMetric = Double.MAX_VALUE;
        double current2DMetric = Double.MAX_VALUE;

        double bottomLeftDistance = Double.MAX_VALUE;
        double midDistance = Double.MAX_VALUE;
        double bottomRightDistance = Double.MAX_VALUE;

        double shortestDistance = Double.MAX_VALUE;
        double biasShortestDistance = Double.MAX_VALUE;
        double averageDistance = Double.MAX_VALUE;

        double topLeftDistance = Double.MAX_VALUE;
        double topRightDistance = Double.MAX_VALUE;
        double originTopLeftDistance = Double.MAX_VALUE;
        double originTopRightDistance = Double.MAX_VALUE;
        double originBottomLeftDistance = Double.MAX_VALUE;
        double originBottomRightDistance = Double.MAX_VALUE;

        void copy(TargetNode source) {
            node = source.node;
            bounds = source.bounds;
            biased2DMetric = source.biased2DMetric;
            current2DMetric = source.current2DMetric;

            bottomLeftDistance = source.bottomLeftDistance;
            midDistance = source.midDistance;
            bottomRightDistance = source.bottomRightDistance;

            shortestDistance = source.shortestDistance;
            biasShortestDistance = source.biasShortestDistance;
            averageDistance = source.averageDistance;

            topLeftDistance = source.topLeftDistance;
            topRightDistance = source.topRightDistance;
            originTopLeftDistance = source.originTopLeftDistance;
            originTopRightDistance = source.originTopRightDistance;
            originBottomLeftDistance = source.originBottomLeftDistance;
            originBottomRightDistance = source.originBottomRightDistance;
        }
    }

    public static double findMin9(double d0, double d1, double d2, double d3, double d4, double d5, double d6, double d7, double d8) {
        double doubleArray[] = new double[9];
        doubleArray[1] = d1;
        doubleArray[2] = d2;
        doubleArray[3] = d3;
        doubleArray[4] = d4;
        doubleArray[5] = d5;
        doubleArray[6] = d6;
        doubleArray[7] = d7;
        doubleArray[8] = d8;

        double minValue = d0;
        for (int i = 1 ; i < doubleArray.length ; i++) {
            minValue = (minValue <= doubleArray[i]) ? minValue : doubleArray[i];
        }
        return minValue;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy