Edit C:\Program Files\Java\jdk1.8.0_121\com\sun\javafx\scene\traversal\Hueristic2D.java
/* * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ 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.Node; import static com.sun.javafx.scene.traversal.Direction.*; import java.util.function.Function; public class Hueristic2D implements Algorithm { Hueristic2D() { } @Override public Node select(Node node, Direction dir, TraversalContext context) { Node newNode = null; cacheTraversal(node, dir); if (NEXT.equals(dir) || NEXT_IN_LINE.equals(dir)) { newNode = TabOrderHelper.findNextFocusablePeer(node, context.getRoot(), dir == NEXT); } else if (PREVIOUS.equals(dir)) { newNode = TabOrderHelper.findPreviousFocusablePeer(node, context.getRoot()); } 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(); } } if (newNode == null) { Bounds currentB = node.localToScene(node.getLayoutBounds()); if (cacheStartTraversalNode != null) { Bounds cachedB = cacheStartTraversalNode.localToScene(cacheStartTraversalNode.getLayoutBounds()); switch (dir) { case UP: case DOWN: newNode = getNearestNodeUpOrDown(currentB, cachedB, context, dir); break; case LEFT: case RIGHT: newNode = getNearestNodeLeftOrRight(currentB, cachedB, context, dir); break; default: break; } } } } /* ** 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; } @Override public Node selectFirst(TraversalContext context) { return TabOrderHelper.getFirstTargetNode(context.getRoot()); } @Override public Node selectLast(TraversalContext context) { return TabOrderHelper.getLastTargetNode(context.getRoot()); } 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<Node> traversalNodeStack = new Stack(); private void cacheTraversal(Node node, Direction dir) { 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; } } } private static final Function<Bounds, Double> BOUNDS_TOP_SIDE = t -> t.getMinY(); private static final Function<Bounds, Double> BOUNDS_BOTTOM_SIDE = t -> t.getMaxY(); protected Node getNearestNodeUpOrDown(Bounds currentB, Bounds originB, TraversalContext context, Direction dir) { List<Node> nodes = context.getAllTargetNodes(); Function<Bounds, Double> ySideInDirection = dir == DOWN ? BOUNDS_BOTTOM_SIDE : BOUNDS_TOP_SIDE; Function<Bounds, Double> ySideInOpositeDirection = dir == DOWN ? BOUNDS_TOP_SIDE : BOUNDS_BOTTOM_SIDE; Bounds biasedB = new BoundingBox(originB.getMinX(), currentB.getMinY(), originB.getWidth(), currentB.getHeight()); Point2D currentMid2D = new Point2D(currentB.getMinX()+(currentB.getWidth()/2), ySideInDirection.apply(currentB)); Point2D biasedMid2D = new Point2D(originB.getMinX()+(originB.getWidth()/2), ySideInDirection.apply(currentB)); Point2D currentLeftCorner2D = new Point2D(currentB.getMinX(),ySideInDirection.apply(currentB)); Point2D biasedLeftCorner2D = new Point2D(originB.getMinX(),ySideInDirection.apply(currentB)); Point2D currentRightCorner2D = new Point2D(currentB.getMaxX(), ySideInDirection.apply(currentB)); Point2D biasedRightCorner2D = new Point2D(originB.getMaxX(), ySideInDirection.apply(currentB)); Point2D originLeftCorner2D = new Point2D(originB.getMinX(), ySideInDirection.apply(originB)); TargetNode targetNode = new TargetNode(); TargetNode nearestNodeCurrentSimple2D = null; TargetNode nearestNodeOriginSimple2D = null; TargetNode nearestNodeAverage = null; TargetNode nearestNodeOnOriginX = null; TargetNode nearestNodeOnCurrentX = null; TargetNode nearestNodeLeft = null; TargetNode nearestNodeAnythingAnywhere = null; for (int nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) { final Node n = nodes.get(nodeIndex); Bounds targetBounds = n.localToScene(n.getLayoutBounds()); /* ** check that the target node starts after we ** and the target node ends after we end */ if (dir == UP ? (currentB.getMinY() > targetBounds.getMaxY()) : currentB.getMaxY() < targetBounds.getMinY()) { targetNode.node = n; targetNode.bounds = targetBounds; /* * closest biased : simple 2d * Negative means the Node is on the same Y axis. This will result in outdB == 0, making this a preferred Node. */ double outdB = Math.max(0, outDistance(dir, biasedB, targetBounds)); if (isOnAxis(dir, biasedB, targetBounds)) { targetNode.biased2DMetric = outdB + centerSideDistance(dir, biasedB, targetBounds) / 100; } else { final double cosd = cornerSideDistance(dir, biasedB, targetBounds); targetNode.biased2DMetric = 100000 + outdB * outdB + 9 * cosd * cosd; } /* * closest current : simple 2d * Negative means the Node is on the same Y axis. This will result in outdB == 0, making this a preferred Node. */ double outdC = Math.max(0, outDistance(dir, currentB, targetBounds)); if (isOnAxis(dir, currentB, targetBounds)) { targetNode.current2DMetric = outdC + centerSideDistance(dir, currentB, targetBounds) / 100; } else { final double cosd = cornerSideDistance(dir, currentB, targetBounds); targetNode.current2DMetric = 100000 + outdC * outdC + 9 * cosd * cosd; } targetNode.leftCornerDistance = currentLeftCorner2D.distance(targetBounds.getMinX(), ySideInOpositeDirection.apply(targetBounds)); targetNode.rightCornerDistance = currentRightCorner2D.distance(targetBounds.getMaxX(), ySideInOpositeDirection.apply(targetBounds)); double midDistance = currentMid2D.distance(targetBounds.getMinX() + (targetBounds.getWidth() / 2), ySideInOpositeDirection.apply(targetBounds)); double currentLeftToTargetMidDistance = currentLeftCorner2D.distance(targetBounds.getMinX() + (targetBounds.getWidth() / 2), ySideInOpositeDirection.apply(targetBounds)); double currentLeftToTargetRightDistance = currentLeftCorner2D.distance(targetBounds.getMaxX(), ySideInOpositeDirection.apply(targetBounds)); double currentRightToTargetLeftDistance = currentRightCorner2D.distance(targetBounds.getMinX(), ySideInOpositeDirection.apply(targetBounds)); double currentRightToTargetMidDistance = currentRightCorner2D.distance(targetBounds.getMinX() + (targetBounds.getWidth() / 2), ySideInOpositeDirection.apply(targetBounds)); double currentRightToTargetRightDistance = currentRightCorner2D.distance(targetBounds.getMaxX(), ySideInOpositeDirection.apply(targetBounds)); double currentMidToTargetLeftDistance = currentMid2D.distance(targetBounds.getMinX(), ySideInOpositeDirection.apply(targetBounds)); double currentMidToTargetMidDistance = currentMid2D.distance(targetBounds.getMinX() + (targetBounds.getWidth() / 2), ySideInOpositeDirection.apply(targetBounds)); double currentMidToTargetRightDistance = currentMid2D.distance(targetBounds.getMaxX(), ySideInOpositeDirection.apply(targetBounds)); double biasLeftToTargetMidDistance = biasedLeftCorner2D.distance(targetBounds.getMinX() + (targetBounds.getWidth() / 2), ySideInOpositeDirection.apply(targetBounds)); double biasLeftToTargetRightDistance = biasedLeftCorner2D.distance(targetBounds.getMaxX(), ySideInOpositeDirection.apply(targetBounds)); double biasRightToTargetMidDistance = biasedRightCorner2D.distance(targetBounds.getMinX() + (targetBounds.getWidth() / 2), ySideInOpositeDirection.apply(targetBounds)); double biasMidToTargetRightDistance = biasedMid2D.distance(targetBounds.getMaxX(), ySideInOpositeDirection.apply(targetBounds)); targetNode.averageDistance = (targetNode.leftCornerDistance + biasLeftToTargetMidDistance + biasLeftToTargetRightDistance + currentRightToTargetLeftDistance + targetNode.rightCornerDistance + biasRightToTargetMidDistance + midDistance) / 7; targetNode.biasShortestDistance = findMin(targetNode.leftCornerDistance, biasLeftToTargetMidDistance, biasLeftToTargetRightDistance, currentRightToTargetLeftDistance, biasRightToTargetMidDistance, targetNode.rightCornerDistance, currentMidToTargetLeftDistance, midDistance, biasMidToTargetRightDistance); targetNode.shortestDistance = findMin(targetNode.leftCornerDistance, currentLeftToTargetMidDistance, currentLeftToTargetRightDistance, currentRightToTargetLeftDistance, currentRightToTargetMidDistance, currentRightToTargetRightDistance, currentMidToTargetLeftDistance, currentMidToTargetMidDistance, currentMidToTargetRightDistance); /* ** 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 top left / bottom left corners. */ if (nearestNodeLeft == null || nearestNodeLeft.leftCornerDistance > targetNode.leftCornerDistance) { if (((originB.getMinX() >= currentB.getMinX()) && (targetBounds.getMinX() >= currentB.getMinX())) || ((originB.getMinX() <= currentB.getMinX()) && (targetBounds.getMinX() <= currentB.getMinX()))) { if (nearestNodeLeft == null) { nearestNodeLeft = new TargetNode(); } nearestNodeLeft.copy(targetNode); } } if (nearestNodeAverage == null || nearestNodeAverage.averageDistance > targetNode.averageDistance) { if (((originB.getMinX() >= currentB.getMinX()) && (targetBounds.getMinX() >= currentB.getMinX())) || ((originB.getMinX() <= currentB.getMinX()) && (targetBounds.getMinX() <= currentB.getMinX()))) { if (nearestNodeAverage == null) { nearestNodeAverage = new TargetNode(); } nearestNodeAverage.copy(targetNode); } } if (nearestNodeAnythingAnywhere == null || nearestNodeAnythingAnywhere.shortestDistance > targetNode.shortestDistance) { if (nearestNodeAnythingAnywhere == null) { nearestNodeAnythingAnywhere = new TargetNode(); } nearestNodeAnythingAnywhere.copy(targetNode); } } } nodes.clear(); if (nearestNodeOnOriginX != null) { nearestNodeOnOriginX.originLeftCornerDistance = originLeftCorner2D.distance(nearestNodeOnOriginX.bounds.getMinX(), ySideInOpositeDirection.apply(nearestNodeOnOriginX.bounds)); } if (nearestNodeOnCurrentX != null) { nearestNodeOnCurrentX.originLeftCornerDistance = originLeftCorner2D.distance(nearestNodeOnCurrentX.bounds.getMinX(), ySideInOpositeDirection.apply(nearestNodeOnCurrentX.bounds)); } if (nearestNodeAverage != null) { nearestNodeAverage.originLeftCornerDistance = originLeftCorner2D.distance(nearestNodeAverage.bounds.getMinX(), ySideInOpositeDirection.apply(nearestNodeAverage.bounds)); } if (nearestNodeOnOriginX != null) { /* ** there's a preference, all else being equal, to return nearestNodeOnOriginX */ if (nearestNodeOnCurrentX != null && nearestNodeOnOriginX.node == nearestNodeOnCurrentX.node && ((nearestNodeAverage != null && nearestNodeOnOriginX.node == nearestNodeAverage.node) || (nearestNodeOriginSimple2D != null && nearestNodeOnOriginX.node == nearestNodeOriginSimple2D.node) || (nearestNodeLeft != null && nearestNodeOnOriginX.node == nearestNodeLeft.node) || (nearestNodeAnythingAnywhere != null && nearestNodeOnOriginX.node == nearestNodeAnythingAnywhere.node))) { return nearestNodeOnOriginX.node; } if (nearestNodeAverage != null && nearestNodeOnOriginX.node == nearestNodeAverage.node) { return nearestNodeOnOriginX.node; } if (nearestNodeOnCurrentX != null) { if ((nearestNodeOnCurrentX.leftCornerDistance < nearestNodeOnOriginX.leftCornerDistance) && (nearestNodeOnCurrentX.originLeftCornerDistance < nearestNodeOnOriginX.originLeftCornerDistance) && (nearestNodeOnCurrentX.bounds.getMinX() - currentLeftCorner2D.getX()) < (nearestNodeOnOriginX.bounds.getMinX() - currentLeftCorner2D.getX())) { return nearestNodeOnCurrentX.node; } else if (nearestNodeAverage == null || nearestNodeOnOriginX.averageDistance < nearestNodeAverage.averageDistance) { return nearestNodeOnOriginX.node; } } } else { if (nearestNodeOnCurrentX == null && nearestNodeCurrentSimple2D != null) { if (nearestNodeAverage != null && nearestNodeLeft != null && (nearestNodeAverage.node == nearestNodeLeft.node && nearestNodeAverage.node == nearestNodeAnythingAnywhere.node)) { return nearestNodeAverage.node; } return nearestNodeCurrentSimple2D.node; } else if (nearestNodeAverage != null && nearestNodeLeft != null && nearestNodeAnythingAnywhere != null && nearestNodeAverage.biasShortestDistance == nearestNodeLeft.biasShortestDistance && nearestNodeAverage.biasShortestDistance == nearestNodeAnythingAnywhere.biasShortestDistance && nearestNodeAverage.biasShortestDistance < Double.MAX_VALUE) { return nearestNodeAverage.node; } } /* ** is the average closer? */ if (nearestNodeAverage != null && (nearestNodeOnOriginX == null || (nearestNodeAverage.biasShortestDistance < nearestNodeOnOriginX.biasShortestDistance))) { /* ** but is one in the way */ if (nearestNodeOnOriginX != null && (ySideInOpositeDirection.apply(nearestNodeOnOriginX.bounds) >= ySideInOpositeDirection.apply(nearestNodeAverage.bounds))) { return nearestNodeOnOriginX.node; } if (nearestNodeOriginSimple2D != null) { if (nearestNodeOriginSimple2D.current2DMetric <= nearestNodeAverage.current2DMetric) { return nearestNodeOriginSimple2D.node; } if (ySideInOpositeDirection.apply(nearestNodeOriginSimple2D.bounds) >= ySideInOpositeDirection.apply(nearestNodeAverage.bounds)) { return nearestNodeOriginSimple2D.node; } } return nearestNodeAverage.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 && nearestNodeAverage != null && nearestNodeLeft != null && nearestNodeAnythingAnywhere != null) && (nearestNodeCurrentSimple2D.node == nearestNodeOnCurrentX.node) && (nearestNodeCurrentSimple2D.node == nearestNodeAverage.node) && (nearestNodeCurrentSimple2D.node == nearestNodeLeft.node) && (nearestNodeCurrentSimple2D.node == nearestNodeAnythingAnywhere.node)) { return nearestNodeCurrentSimple2D.node; } if (nearestNodeOnOriginX != null && (nearestNodeOnCurrentX == null || (nearestNodeOnOriginX.rightCornerDistance < nearestNodeOnCurrentX.rightCornerDistance))) { 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 (nearestNodeAverage != null) { return nearestNodeAverage.node; } else if (nearestNodeLeft != null) { return nearestNodeLeft.node; } else if (nearestNodeAnythingAnywhere != null) { return nearestNodeAnythingAnywhere.node; } return null; } private static final Function<Bounds, Double> BOUNDS_LEFT_SIDE = t -> t.getMinX(); private static final Function<Bounds, Double> BOUNDS_RIGHT_SIDE = t -> t.getMaxX(); protected Node getNearestNodeLeftOrRight(Bounds currentB, Bounds originB, TraversalContext context, Direction dir) { List<Node> nodes = context.getAllTargetNodes(); Function<Bounds, Double> xSideInDirection = dir == LEFT ? BOUNDS_LEFT_SIDE : BOUNDS_RIGHT_SIDE; Function<Bounds, Double> xSideInOpositeDirection = dir == LEFT ? BOUNDS_RIGHT_SIDE : BOUNDS_LEFT_SIDE; Bounds biasedB = new BoundingBox(currentB.getMinX(), originB.getMinY(), currentB.getWidth(), originB.getHeight()); Point2D currentMid2D = new Point2D(xSideInDirection.apply(currentB), currentB.getMinY()+(currentB.getHeight()/2)); Point2D biasedMid2D = new Point2D(xSideInDirection.apply(currentB), originB.getMinY()+(originB.getHeight()/2)); Point2D currentTopCorner2D = new Point2D(xSideInDirection.apply(currentB), currentB.getMinY()); Point2D biasedTopCorner2D = new Point2D(xSideInDirection.apply(currentB), originB.getMinY()); Point2D currentBottomCorner2D = new Point2D(xSideInDirection.apply(currentB), currentB.getMaxY()); Point2D biasedBottomCorner2D = new Point2D(xSideInDirection.apply(currentB), originB.getMaxY()); Point2D originTopCorner2D = new Point2D(xSideInDirection.apply(originB), originB.getMinY()); TargetNode targetNode = new TargetNode(); TargetNode nearestNodeCurrentSimple2D = null; TargetNode nearestNodeOriginSimple2D = null; TargetNode nearestNodeAverage = null; TargetNode nearestNodeOnOriginY = null; TargetNode nearestNodeOnCurrentY = null; TargetNode nearestNodeTopLeft = null; TargetNode nearestNodeAnythingAnywhereLeft = null; for (int nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) { final Node n = nodes.get(nodeIndex); Bounds targetBounds = n.localToScene(n.getLayoutBounds()); /* ** check that the target node starts after we start ** and the target node ends after we end */ if (dir == LEFT ? currentB.getMinX() > targetBounds.getMinX() : currentB.getMaxX() < targetBounds.getMaxX()) { targetNode.node = n; targetNode.bounds = targetBounds; /* * closest biased : simple 2d. * Negative means the Node is on the same Y axis. This will result in outdB == 0, making this a preferred Node. */ double outdB = Math.max(0, outDistance(dir, biasedB, targetBounds)); if (isOnAxis(dir, biasedB, targetBounds)) { targetNode.biased2DMetric = outdB + centerSideDistance(dir, biasedB, targetBounds) / 100; } else { final double cosd = cornerSideDistance(dir, biasedB, targetBounds); targetNode.biased2DMetric = 100000 + outdB * outdB + 9 * cosd * cosd; } /* * closest current : simple 2d *Negative means the Node is on the same Y axis. This will result in outdB == 0, making this a preferred Node. */ double outdC = Math.max(0, outDistance(dir, currentB, targetBounds)); if (isOnAxis(dir, currentB, targetBounds)) { targetNode.current2DMetric = outdC + centerSideDistance(dir, currentB, targetBounds) / 100; } else { final double cosd = cornerSideDistance(dir, currentB, targetBounds); targetNode.current2DMetric = 100000 + outdC * outdC + 9 * cosd * cosd; } targetNode.topCornerDistance = currentTopCorner2D.distance(xSideInOpositeDirection.apply(targetBounds), targetBounds.getMinY()); targetNode.bottomCornerDistance = currentBottomCorner2D.distance(xSideInOpositeDirection.apply(targetBounds), targetBounds.getMaxY()); double midDistance = currentMid2D.distance(xSideInOpositeDirection.apply(targetBounds), targetBounds.getMinY() + (targetBounds.getHeight() / 2)); double currentTopToTargetBottomDistance = currentTopCorner2D.distance(xSideInOpositeDirection.apply(targetBounds), targetBounds.getMaxY()); double currentTopToTargetMidDistance = currentTopCorner2D.distance(xSideInOpositeDirection.apply(targetBounds), targetBounds.getMinY() + (targetBounds.getHeight() / 2)); double currentBottomToTargetTopDistance = currentBottomCorner2D.distance(xSideInOpositeDirection.apply(targetBounds), targetBounds.getMinY()); double currentBottomToTargetBottomDistance = currentBottomCorner2D.distance(xSideInOpositeDirection.apply(targetBounds), targetBounds.getMaxY()); double currentBottomToTargetMidDistance = currentBottomCorner2D.distance(xSideInOpositeDirection.apply(targetBounds), targetBounds.getMinY() + (targetBounds.getHeight() / 2)); double currentMidToTargetTopDistance = currentMid2D.distance(xSideInOpositeDirection.apply(targetBounds), targetBounds.getMinY()); double currentMidToTargetBottomDistance = currentMid2D.distance(xSideInOpositeDirection.apply(targetBounds), targetBounds.getMaxY()); double currentMidToTargetMidDistance = currentMid2D.distance(xSideInOpositeDirection.apply(targetBounds), targetBounds.getMinY() + (targetBounds.getHeight() / 2)); double biasTopToTargetBottomDistance = biasedTopCorner2D.distance(xSideInOpositeDirection.apply(targetBounds), targetBounds.getMaxY()); double biasTopToTargetMidDistance = biasedTopCorner2D.distance(xSideInOpositeDirection.apply(targetBounds), targetBounds.getMinY() + (targetBounds.getHeight() / 2)); double biasBottomToTargetMidDistance = biasedBottomCorner2D.distance(xSideInOpositeDirection.apply(targetBounds), targetBounds.getMinY() + (targetBounds.getHeight() / 2)); double biasMidToTargetBottomDistance = biasedMid2D.distance(xSideInOpositeDirection.apply(targetBounds), targetBounds.getMaxY()); targetNode.averageDistance = (targetNode.topCornerDistance + biasTopToTargetBottomDistance + biasTopToTargetMidDistance + currentBottomToTargetTopDistance + targetNode.bottomCornerDistance + biasBottomToTargetMidDistance + midDistance) / 7; targetNode.biasShortestDistance = findMin(targetNode.topCornerDistance, biasTopToTargetBottomDistance, biasTopToTargetMidDistance, currentBottomToTargetTopDistance, targetNode.bottomCornerDistance, biasBottomToTargetMidDistance, currentMidToTargetTopDistance, biasMidToTargetBottomDistance, midDistance); targetNode.shortestDistance = findMin(targetNode.topCornerDistance, currentTopToTargetBottomDistance, currentTopToTargetMidDistance, currentBottomToTargetTopDistance, currentBottomToTargetBottomDistance, currentBottomToTargetMidDistance, currentMidToTargetTopDistance, currentMidToTargetBottomDistance, 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.topCornerDistance > targetNode.topCornerDistance) { 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.topCornerDistance > targetNode.topCornerDistance) { if (nearestNodeOnCurrentY == null) { nearestNodeOnCurrentY = new TargetNode(); } nearestNodeOnCurrentY.copy(targetNode); } } /* ** Closest top left / top right corners. */ if (nearestNodeTopLeft == null || nearestNodeTopLeft.topCornerDistance > targetNode.topCornerDistance) { if (nearestNodeTopLeft == null) { nearestNodeTopLeft = new TargetNode(); } nearestNodeTopLeft.copy(targetNode); } if (nearestNodeAverage == null || nearestNodeAverage.averageDistance > targetNode.averageDistance) { if (nearestNodeAverage == null) { nearestNodeAverage = new TargetNode(); } nearestNodeAverage.copy(targetNode); } if (nearestNodeAnythingAnywhereLeft == null || nearestNodeAnythingAnywhereLeft.shortestDistance > targetNode.shortestDistance) { if (nearestNodeAnythingAnywhereLeft == null) { nearestNodeAnythingAnywhereLeft = new TargetNode(); } nearestNodeAnythingAnywhereLeft.copy(targetNode); } } } nodes.clear(); if (nearestNodeOnOriginY != null) { nearestNodeOnOriginY.originTopCornerDistance = originTopCorner2D.distance(xSideInOpositeDirection.apply(nearestNodeOnOriginY.bounds), nearestNodeOnOriginY.bounds.getMinY()); } if (nearestNodeOnCurrentY != null) { nearestNodeOnCurrentY.originTopCornerDistance = originTopCorner2D.distance(xSideInOpositeDirection.apply(nearestNodeOnCurrentY.bounds), nearestNodeOnCurrentY.bounds.getMinY()); } if (nearestNodeAverage != null) { nearestNodeAverage.originTopCornerDistance = originTopCorner2D.distance(xSideInOpositeDirection.apply(nearestNodeAverage.bounds), nearestNodeAverage.bounds.getMinY()); } if (nearestNodeOnCurrentY == null && nearestNodeOnOriginY == null) { cacheStartTraversalNode = null; cacheStartTraversalDirection = null; reverseDirection = false; traversalNodeStack.clear(); } if (nearestNodeOnOriginY != null) { /* ** there's a preference, all else being equal, to return nearestNodeOnOriginY */ if (nearestNodeOnCurrentY != null && nearestNodeOnOriginY.node == nearestNodeOnCurrentY.node && ((nearestNodeAverage != null && nearestNodeOnOriginY.node == nearestNodeAverage.node) || (nearestNodeTopLeft != null && nearestNodeOnOriginY.node == nearestNodeTopLeft.node) || (nearestNodeAnythingAnywhereLeft != null && nearestNodeOnOriginY.node == nearestNodeAnythingAnywhereLeft.node))) { return nearestNodeOnOriginY.node; } if (nearestNodeAverage != null && nearestNodeOnOriginY.node == nearestNodeAverage.node) { return nearestNodeOnOriginY.node; } if (nearestNodeOnCurrentY != null) { if ((nearestNodeOnCurrentY.bottomCornerDistance < nearestNodeOnOriginY.bottomCornerDistance) && (nearestNodeOnCurrentY.originTopCornerDistance < nearestNodeOnOriginY.originTopCornerDistance) && (nearestNodeOnCurrentY.bounds.getMinY() - currentTopCorner2D.getY()) < (nearestNodeOnOriginY.bounds.getMinY() - currentTopCorner2D.getY())) { return nearestNodeOnCurrentY.node; } else if (nearestNodeAverage == null || nearestNodeOnOriginY.averageDistance < nearestNodeAverage.averageDistance) { return nearestNodeOnOriginY.node; } } } else { if (nearestNodeOnCurrentY == null && nearestNodeCurrentSimple2D != null) { if (nearestNodeAverage != null && nearestNodeTopLeft != null && nearestNodeAverage.node == nearestNodeTopLeft.node && nearestNodeAverage.node == nearestNodeAnythingAnywhereLeft.node) { return nearestNodeAverage.node; } return nearestNodeCurrentSimple2D.node; } else if (nearestNodeAverage != null && nearestNodeTopLeft != null && nearestNodeAnythingAnywhereLeft != null && nearestNodeAverage.biasShortestDistance == nearestNodeTopLeft.biasShortestDistance && nearestNodeAverage.biasShortestDistance == nearestNodeAnythingAnywhereLeft.biasShortestDistance && nearestNodeAverage.biasShortestDistance < Double.MAX_VALUE) { return nearestNodeAverage.node; } } /* ** is the average closer? */ if (nearestNodeAverage != null && (nearestNodeOnOriginY == null || nearestNodeAverage.biasShortestDistance < nearestNodeOnOriginY.biasShortestDistance)) { /* ** but is one in the way */ if (nearestNodeOnOriginY != null && (xSideInOpositeDirection.apply(nearestNodeOnOriginY.bounds) >= xSideInOpositeDirection.apply(nearestNodeAverage.bounds))) { 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 && nearestNodeOnOriginY.biasShortestDistance < Double.MAX_VALUE && (nearestNodeOnOriginY.originTopCornerDistance < nearestNodeAverage.originTopCornerDistance)) { return nearestNodeOnOriginY.node; } return nearestNodeAverage.node; } if (nearestNodeOnOriginY != null && nearestNodeOnCurrentY != null && nearestNodeOnOriginY.bottomCornerDistance < nearestNodeOnCurrentY.bottomCornerDistance) { 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 (nearestNodeAverage != null) { return nearestNodeAverage.node; } else if (nearestNodeTopLeft != null) { return nearestNodeTopLeft.node; } else if (nearestNodeAnythingAnywhereLeft != null) { return nearestNodeAnythingAnywhereLeft.node; } return null; } static final class TargetNode { Node node = null; Bounds bounds = null; double biased2DMetric = Double.MAX_VALUE; double current2DMetric = Double.MAX_VALUE; double leftCornerDistance = Double.MAX_VALUE; double rightCornerDistance = Double.MAX_VALUE; double topCornerDistance = Double.MAX_VALUE; double bottomCornerDistance = Double.MAX_VALUE; double shortestDistance = Double.MAX_VALUE; double biasShortestDistance = Double.MAX_VALUE; double averageDistance = Double.MAX_VALUE; double originLeftCornerDistance = Double.MAX_VALUE; double originTopCornerDistance = Double.MAX_VALUE; void copy(TargetNode source) { node = source.node; bounds = source.bounds; biased2DMetric = source.biased2DMetric; current2DMetric = source.current2DMetric; leftCornerDistance = source.leftCornerDistance; rightCornerDistance = source.rightCornerDistance; shortestDistance = source.shortestDistance; biasShortestDistance = source.biasShortestDistance; averageDistance = source.averageDistance; topCornerDistance = source.topCornerDistance; bottomCornerDistance = source.bottomCornerDistance; originLeftCornerDistance = source.originLeftCornerDistance; originTopCornerDistance = source.originTopCornerDistance; } } public static double findMin(double... values) { double minValue = Double.MAX_VALUE; for (int i = 0 ; i < values.length ; i++) { minValue = (minValue < values[i]) ? minValue : values[i]; } return minValue; } }
Ms-Dos/Windows
Unix
Write backup
jsp File Browser version 1.2 by
www.vonloesch.de