From d0c64f675b24ae23db4439bb8b1f8018f3597a4d Mon Sep 17 00:00:00 2001 From: Behrang Saeedzadeh Date: Sat, 21 Oct 2023 23:35:18 +1100 Subject: [PATCH] Housekeeping --- build.gradle | 2 + .../algorithm/tree/DimensionCalculator.java | 20 +++--- .../org/behrang/algorithm/tree/Functions.java | 40 +++++++++++ .../java/org/behrang/algorithm/tree/Node.java | 70 ++++++++++++------- .../algorithm/tree/WalkerAlgorithm.java | 48 ++++++------- .../data/FixedDimensionNodeGenerator.java | 29 ++++++++ .../algorithm/tree/data/NodeGenerator.java | 7 ++ .../data/RandomDimensionNodeGenerator.java | 53 ++++++++++++++ .../tree/data/SampleTreeGenerator.java | 60 ++++++++++++++++ .../algorithm/tree/demo/DemoFrame.java | 12 ++-- .../algorithm/tree/demo/SampleTree.java | 60 ---------------- .../algorithm/tree/demo/TreePanel.java | 41 ++++++----- .../tree/DimensionCalculatorTest.java | 6 +- .../behrang/algorithm/tree/FunctionsTest.java | 27 +++++++ 14 files changed, 329 insertions(+), 146 deletions(-) create mode 100644 src/main/java/org/behrang/algorithm/tree/Functions.java create mode 100644 src/main/java/org/behrang/algorithm/tree/data/FixedDimensionNodeGenerator.java create mode 100644 src/main/java/org/behrang/algorithm/tree/data/NodeGenerator.java create mode 100644 src/main/java/org/behrang/algorithm/tree/data/RandomDimensionNodeGenerator.java create mode 100644 src/main/java/org/behrang/algorithm/tree/data/SampleTreeGenerator.java delete mode 100644 src/main/java/org/behrang/algorithm/tree/demo/SampleTree.java create mode 100644 src/test/java/org/behrang/algorithm/tree/FunctionsTest.java diff --git a/build.gradle b/build.gradle index 12cca5b..8100863 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,8 @@ repositories { } dependencies { + implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.13.0' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.1' } diff --git a/src/main/java/org/behrang/algorithm/tree/DimensionCalculator.java b/src/main/java/org/behrang/algorithm/tree/DimensionCalculator.java index 357728c..1b32e04 100644 --- a/src/main/java/org/behrang/algorithm/tree/DimensionCalculator.java +++ b/src/main/java/org/behrang/algorithm/tree/DimensionCalculator.java @@ -6,10 +6,10 @@ public class DimensionCalculator { public static Dimension2D calculateTreeDimension(Node root) { - final AtomicReference minX = new AtomicReference<>(Double.MAX_VALUE); - final AtomicReference minY = new AtomicReference<>(Double.MAX_VALUE); - final AtomicReference maxX = new AtomicReference<>(Double.MIN_VALUE); - final AtomicReference maxY = new AtomicReference<>(Double.MIN_VALUE); + final AtomicReference minX = new AtomicReference<>(Float.MAX_VALUE); + final AtomicReference minY = new AtomicReference<>(Float.MAX_VALUE); + final AtomicReference maxX = new AtomicReference<>(Float.MIN_VALUE); + final AtomicReference maxY = new AtomicReference<>(Float.MIN_VALUE); TreeTraversal.preorder(root, n -> { if (n.getX() < minX.get()) { @@ -20,19 +20,19 @@ public static Dimension2D calculateTreeDimension(Node root) { minY.set(n.getY()); } - double x2 = n.getX() + n.getWidth(); + float x2 = n.getX() + n.getWidth(); if (x2 > maxX.get()) { maxX.set(x2); } - double y2 = n.getY() + n.getHeight(); + float y2 = n.getY() + n.getHeight(); if (y2 > maxY.get()) { maxY.set(y2); } }); - double width = (maxX.get() - minX.get()); - double height = (maxY.get() - minY.get()); + float width = (maxX.get() - minX.get()); + float height = (maxY.get() - minY.get()); Dimension dim = new Dimension(); dim.setSize(width, height); @@ -41,8 +41,8 @@ public static Dimension2D calculateTreeDimension(Node root) { } public static Dimension2D calculateMaxNodeDimension(Node root) { - final AtomicReference width = new AtomicReference<>(Double.MIN_VALUE); - final AtomicReference height = new AtomicReference<>(Double.MIN_VALUE); + final AtomicReference width = new AtomicReference<>(Float.MIN_VALUE); + final AtomicReference height = new AtomicReference<>(Float.MIN_VALUE); TreeTraversal.preorder(root, n -> { if (width.get() < n.getWidth()) { diff --git a/src/main/java/org/behrang/algorithm/tree/Functions.java b/src/main/java/org/behrang/algorithm/tree/Functions.java new file mode 100644 index 0000000..a6010d6 --- /dev/null +++ b/src/main/java/org/behrang/algorithm/tree/Functions.java @@ -0,0 +1,40 @@ +package org.behrang.algorithm.tree; + +import java.util.function.Consumer; + +public final class Functions { + private Functions() { + throw new RuntimeException(); + } + + public static Node leftmostDescendantAtDepth(Node node, int depth) { + return leftmostDescendantAtDepth(node, 0, depth); + } + + private static Node leftmostDescendantAtDepth(Node node, int level, int depth) { + if (level >= depth) { + return node; + } else if (node.isLeaf()) { + return null; + } else { + var leftChild = node.getLeftChild(); + var leftmost = leftmostDescendantAtDepth(leftChild, level + 1, depth); + while (leftmost == null && leftChild.hasRightSibling()) { + leftChild = leftChild.getRightSibling(); + leftmost = leftmostDescendantAtDepth(leftChild, level + 1, depth); + } + + return leftmost; + } + } + + public static void prePostOrder(Node node, Consumer> preConsumer, Consumer> postConsumer) { + preConsumer.accept(node); + + node.getChildren().forEach(n -> { + prePostOrder(n, preConsumer, postConsumer); + }); + + postConsumer.accept(node); + } +} diff --git a/src/main/java/org/behrang/algorithm/tree/Node.java b/src/main/java/org/behrang/algorithm/tree/Node.java index 46eeaf4..19c3f14 100755 --- a/src/main/java/org/behrang/algorithm/tree/Node.java +++ b/src/main/java/org/behrang/algorithm/tree/Node.java @@ -1,14 +1,20 @@ package org.behrang.algorithm.tree; +import org.apache.commons.lang3.Validate; + import java.util.ArrayList; import java.util.List; import java.util.Stack; public class Node { - private static final double DEFAULT_MODIFIER = 0.0; + private static final float DEFAULT_MODIFIER = 0.0f; + + private static final float DEFAULT_PRELIM = 0.0f; - private static final double DEFAULT_PRELIM = 0.0; + private static final float DEFAULT_WIDTH = 2.0f; + + private static final float DEFAULT_HEIGHT = 2.0f; private Node parent; @@ -16,17 +22,17 @@ public class Node { private T value; - private double x; + private float x; - private double y; + private float y; - private double width; + private float width; - private double height; + private float height; - private double prelim = DEFAULT_PRELIM; + private float prelim = DEFAULT_PRELIM; - private double modifier = DEFAULT_MODIFIER; + private float modifier = DEFAULT_MODIFIER; private Node leftNeighbor; @@ -35,7 +41,17 @@ public Node() { } public Node(T value) { + this(value, DEFAULT_WIDTH, DEFAULT_HEIGHT); + } + + public Node(T value, float width, float height) { + Validate.notNull(value, "value must be non-null"); + Validate.isTrue(width > 0); + Validate.isTrue(height > 0); + this.value = value; + this.width = width; + this.height = height; this.children = new ArrayList<>(); } @@ -67,51 +83,51 @@ public void setValue(T value) { this.value = value; } - public double getX() { + public float getX() { return x; } - public void setX(double x) { + public void setX(float x) { this.x = x; } - public double getY() { + public float getY() { return y; } - public void setY(double y) { + public void setY(float y) { this.y = y; } - public double getWidth() { + public float getWidth() { return width; } - public void setWidth(double width) { + public void setWidth(float width) { this.width = width; } - public double getHeight() { + public float getHeight() { return height; } - public void setHeight(double height) { + public void setHeight(float height) { this.height = height; } - public double getPrelim() { + public float getPrelim() { return prelim; } - public void setPrelim(double prelim) { + public void setPrelim(float prelim) { this.prelim = prelim; } - public double getModifier() { + public float getModifier() { return modifier; } - public void setModifier(double modifier) { + public void setModifier(float modifier) { this.modifier = modifier; } @@ -217,25 +233,25 @@ public Node getLeftmost(int targetLevel) { return null; } - public void incrementPrelimBy(double delta) { + public void incrementPrelimBy(float delta) { setPrelim(prelim + delta); } - public void incrementModifierBy(double delta) { + public void incrementModifierBy(float delta) { setModifier(modifier + delta); } - public int getMaxDepth() { + public int getDepth() { Node tree = this; if (tree.getChildren().isEmpty()) { return 1; } return tree.getChildren() - .stream() - .mapToInt(Node::getMaxDepth) - .max() - .orElse(0) + 1; + .stream() + .mapToInt(Node::getDepth) + .max() + .orElse(0) + 1; } @Override diff --git a/src/main/java/org/behrang/algorithm/tree/WalkerAlgorithm.java b/src/main/java/org/behrang/algorithm/tree/WalkerAlgorithm.java index 68c3da2..5a0dc7d 100755 --- a/src/main/java/org/behrang/algorithm/tree/WalkerAlgorithm.java +++ b/src/main/java/org/behrang/algorithm/tree/WalkerAlgorithm.java @@ -5,21 +5,19 @@ public class WalkerAlgorithm { - private static final long MAX_DEPTH = Long.MAX_VALUE; - private final Map> previousNodeAtLevel; - private final double siblingSeparation; + private final float siblingSeparation; - private final double subtreeSeparation; + private final float subtreeSeparation; - private final double levelSeparation; + private final float levelSeparation; - private final double xTopAdjustment; + private final float xTopAdjustment; - private final double yTopAdjustment; + private final float yTopAdjustment; - public WalkerAlgorithm(double siblingSeparation, double subtreeSeparation, double xTopAdjustment, double yTopAdjustment, double levelSeparation) { + public WalkerAlgorithm(float siblingSeparation, float subtreeSeparation, float xTopAdjustment, float yTopAdjustment, float levelSeparation) { this.previousNodeAtLevel = new HashMap<>(); this.siblingSeparation = siblingSeparation; this.subtreeSeparation = subtreeSeparation; @@ -41,6 +39,7 @@ public void position(Node node) { } void firstWalk(Node thisNode, int currentLevel) { + // We need to maintain Node leftNeighbor = getPreviousNodeAtLevel(currentLevel); thisNode.setLeftNeighbor(leftNeighbor); setPreviousNodeAtLevel(currentLevel, thisNode); @@ -48,14 +47,14 @@ void firstWalk(Node thisNode, int currentLevel) { firstWalk(child, currentLevel + 1); }); - if (thisNode.isLeaf() || currentLevel == MAX_DEPTH) { + if (thisNode.isLeaf()) { if (thisNode.hasLeftSibling()) { thisNode.setPrelim(thisNode.getLeftSibling().getPrelim() + siblingSeparation + meanWidth(thisNode.getLeftSibling(), thisNode)); } else { thisNode.setPrelim(0); } } else { - double midpoint = (thisNode.getLeftChild().getPrelim() + thisNode.getRightChild().getPrelim()) / 2; + float midpoint = (thisNode.getLeftChild().getPrelim() + thisNode.getRightChild().getPrelim()) / 2; if (thisNode.hasLeftSibling()) { thisNode.setPrelim(thisNode.getLeftSibling().getPrelim() + siblingSeparation + meanWidth(thisNode.getLeftSibling(), thisNode)); @@ -68,9 +67,9 @@ void firstWalk(Node thisNode, int currentLevel) { } } - void secondWalk(Node node, int level, double modSum) { - double xTemp = xTopAdjustment + node.getPrelim() + modSum; - double yTemp = yTopAdjustment + (level * levelSeparation); + void secondWalk(Node node, int level, float modSum) { + float xTemp = xTopAdjustment + node.getPrelim() + modSum; + float yTemp = yTopAdjustment + (level * levelSeparation); node.setX(xTemp); node.setY(yTemp); @@ -84,15 +83,15 @@ void apportion(Node treeNode, int baseLevel) { Node leftMost = treeNode.getLeftChild(); Node leftNeighbor; - for (int level = baseLevel; level < treeNode.getMaxDepth(); level++) { + for (int level = baseLevel; level < treeNode.getDepth(); level++) { leftNeighbor = leftMost.getLeftNeighbor(); if (leftMost == null || leftNeighbor == null) { return; } - double leftModSum = 0.0; - double rightModSum = 0.0; + float leftModSum = 0.0f; + float rightModSum = 0.0f; Node ancestorLeftmost = leftMost; Node ancestorNeighbor = leftNeighbor; while (ancestorLeftmost != treeNode) { @@ -102,11 +101,11 @@ void apportion(Node treeNode, int baseLevel) { leftModSum += ancestorNeighbor.getModifier(); } - double moveDistance = ( - leftNeighbor.getPrelim() + - leftModSum + - subtreeSeparation + - meanWidth(treeNode.getLeftSibling(), treeNode) + float moveDistance = ( + leftNeighbor.getPrelim() + + leftModSum + + subtreeSeparation + + meanWidth(treeNode.getLeftSibling(), treeNode) ) - (leftMost.getPrelim() + rightModSum); if (moveDistance > 0) { @@ -119,7 +118,7 @@ void apportion(Node treeNode, int baseLevel) { } if (tempNode != null) { - double portion = moveDistance / numLeftSiblings; + float portion = moveDistance / numLeftSiblings; tempNode = treeNode; while (tempNode != ancestorNeighbor) { @@ -133,7 +132,6 @@ void apportion(Node treeNode, int baseLevel) { } } - if (leftMost.getChildren().isEmpty()) { leftMost = treeNode.getLeftmost(level + 2); } else { @@ -170,7 +168,7 @@ Node getLeftmostDescendant(Node node, int level, int depth) { return leftmost; } - double meanWidth(Node n1, Node n2) { - return (n1.getWidth() + n2.getWidth()) / 2.0; + float meanWidth(Node n1, Node n2) { + return (n1.getWidth() + n2.getWidth()) / 2.0f; } } diff --git a/src/main/java/org/behrang/algorithm/tree/data/FixedDimensionNodeGenerator.java b/src/main/java/org/behrang/algorithm/tree/data/FixedDimensionNodeGenerator.java new file mode 100644 index 0000000..b5f9be4 --- /dev/null +++ b/src/main/java/org/behrang/algorithm/tree/data/FixedDimensionNodeGenerator.java @@ -0,0 +1,29 @@ +package org.behrang.algorithm.tree.data; + +import org.apache.commons.lang3.Validate; +import org.behrang.algorithm.tree.Node; + +public class FixedDimensionNodeGenerator implements NodeGenerator { + + private float width; + + private float height; + + public FixedDimensionNodeGenerator(float width, float height) { + Validate.notNaN(width); + Validate.notNaN(height); + + Validate.isTrue(width > 0); + Validate.isTrue(height > 0); + + this.width = width; + this.height = height; + } + + @Override + public Node generate(T value) { + Validate.notNull(value, "value must be non-null"); + + return new Node<>(value, width, height); + } +} diff --git a/src/main/java/org/behrang/algorithm/tree/data/NodeGenerator.java b/src/main/java/org/behrang/algorithm/tree/data/NodeGenerator.java new file mode 100644 index 0000000..42b52bd --- /dev/null +++ b/src/main/java/org/behrang/algorithm/tree/data/NodeGenerator.java @@ -0,0 +1,7 @@ +package org.behrang.algorithm.tree.data; + +import org.behrang.algorithm.tree.Node; + +public interface NodeGenerator { + Node generate(T value); +} diff --git a/src/main/java/org/behrang/algorithm/tree/data/RandomDimensionNodeGenerator.java b/src/main/java/org/behrang/algorithm/tree/data/RandomDimensionNodeGenerator.java new file mode 100644 index 0000000..831356a --- /dev/null +++ b/src/main/java/org/behrang/algorithm/tree/data/RandomDimensionNodeGenerator.java @@ -0,0 +1,53 @@ +package org.behrang.algorithm.tree.data; + +import org.apache.commons.lang3.Validate; +import org.behrang.algorithm.tree.Node; + +import java.util.Random; + +public class RandomDimensionNodeGenerator implements NodeGenerator { + private final Random random; + + private float minWidth; + + private float maxWidth; + + private float minHeight; + + private float maxHeight; + + public RandomDimensionNodeGenerator(Random random, float minWidth, float maxWidth, float minHeight, float maxHeight) { + Validate.notNull(random, "random must be non-null"); + + Validate.notNaN(minWidth); + Validate.notNaN(maxWidth); + Validate.notNaN(minHeight); + Validate.notNaN(maxHeight); + + Validate.isTrue(minWidth > 0); + Validate.isTrue(maxWidth > minWidth); + + Validate.isTrue(minHeight > 0); + Validate.isTrue(maxHeight > minHeight); + + this.random = random; + this.minWidth = minWidth; + this.maxWidth = maxWidth; + this.minHeight = minHeight; + this.maxHeight = maxHeight; + } + + public RandomDimensionNodeGenerator(float minWidth, float maxWidth, float minHeight, float maxHeight) { + this(new Random(), minWidth, maxWidth, minHeight, maxHeight); + } + + @Override + public Node generate(T value) { + Validate.notNull(value, "value must be non-null"); + + float width = random.nextFloat(minWidth, maxWidth); + float height = random.nextFloat(minHeight, maxHeight); + + return new Node<>(value, width, height); + } +} diff --git a/src/main/java/org/behrang/algorithm/tree/data/SampleTreeGenerator.java b/src/main/java/org/behrang/algorithm/tree/data/SampleTreeGenerator.java new file mode 100644 index 0000000..484f1a1 --- /dev/null +++ b/src/main/java/org/behrang/algorithm/tree/data/SampleTreeGenerator.java @@ -0,0 +1,60 @@ +package org.behrang.algorithm.tree.data; + +import org.behrang.algorithm.tree.Node; + +public final class SampleTreeGenerator { + + private SampleTreeGenerator() { + } + + public static Node newUniformInstance() { + return newUniformInstance(2, 2); + } + + public static Node newUniformInstance(float nodeWidth, float nodeHeight) { + return newInstance(new FixedDimensionNodeGenerator<>(nodeWidth, nodeHeight)); + } + + public static Node newMultiformInstance(float minWidth, float maxWidth, float minHeight, float maxHeight) { + return newInstance(new RandomDimensionNodeGenerator<>(minWidth, maxWidth, minHeight, maxHeight)); + } + + static Node newInstance(NodeGenerator generator) { + Node a, b, c, d, e, f, g, h, i, j, k, l, m, n, o; + + o = generator.generate("O"); + e = generator.generate("E"); + f = generator.generate("F"); + n = generator.generate("N"); + link(o, e, f, n); + + a = generator.generate("A"); + d = generator.generate("D"); + link(e, a, d); + + b = generator.generate("B"); + c = generator.generate("C"); + link(d, b, c); + + g = generator.generate("G"); + m = generator.generate("M"); + link(n, g, m); + + h = generator.generate("H"); + i = generator.generate("I"); + j = generator.generate("J"); + k = generator.generate("K"); + l = generator.generate("L"); + link(m, h, i, j, k, l); + + return o; + } + + @SafeVarargs + static void link(Node parent, Node... children) { + for (var child : children) { + parent.getChildren().add(child); + child.setParent(parent); + } + } +} diff --git a/src/main/java/org/behrang/algorithm/tree/demo/DemoFrame.java b/src/main/java/org/behrang/algorithm/tree/demo/DemoFrame.java index 3bdcc2b..fbaa55b 100644 --- a/src/main/java/org/behrang/algorithm/tree/demo/DemoFrame.java +++ b/src/main/java/org/behrang/algorithm/tree/demo/DemoFrame.java @@ -1,21 +1,25 @@ package org.behrang.algorithm.tree.demo; +import org.behrang.algorithm.tree.Node; + import javax.swing.*; import java.awt.*; +import static org.behrang.algorithm.tree.data.SampleTreeGenerator.newMultiformInstance; + public class DemoFrame extends JFrame { private JPanel mainPanel; - public DemoFrame() { - mainPanel = new TreePanel<>(SampleTree.newUniformInstance(64, 64)); + public DemoFrame(Node root) { + mainPanel = new TreePanel<>(root); add(mainPanel, BorderLayout.CENTER); } public static void main(String[] args) { - DemoFrame demoFrame = new DemoFrame(); + var demoFrame = new DemoFrame(newMultiformInstance(40, 80, 40, 80)); demoFrame.setTitle("Walker Algorithm Demo"); - demoFrame.setSize(1024, 768); + demoFrame.setSize(1280, 1024); demoFrame.setResizable(false); demoFrame.setLocationByPlatform(true); demoFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); diff --git a/src/main/java/org/behrang/algorithm/tree/demo/SampleTree.java b/src/main/java/org/behrang/algorithm/tree/demo/SampleTree.java deleted file mode 100644 index 89a786e..0000000 --- a/src/main/java/org/behrang/algorithm/tree/demo/SampleTree.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.behrang.algorithm.tree.demo; - -import org.behrang.algorithm.tree.Node; - -public final class SampleTree { - - private SampleTree() { - } - - public static Node newUniformInstance() { - return newUniformInstance(2, 2); - } - - public static Node newUniformInstance(double nodeWidth, double nodeHeight) { - Node a, b, c, d, e, f, g, h, i, j, k, l, m, n, o; - - o = node("O", nodeWidth, nodeHeight); - e = node("E", nodeWidth, nodeHeight); - f = node("F", nodeWidth, nodeHeight); - n = node("N", nodeWidth, nodeHeight); - link(o, e, f, n); - - a = node("A", nodeWidth, nodeHeight); - d = node("D", nodeWidth, nodeHeight); - link(e, a, d); - - b = node("B", nodeWidth, nodeHeight); - c = node("C", nodeWidth, nodeHeight); - link(d, b, c); - - g = node("G", nodeWidth, nodeHeight); - m = node("M", nodeWidth, nodeHeight); - link(n, g, m); - - h = node("H", nodeWidth, nodeHeight); - i = node("I", nodeWidth, nodeHeight); - j = node("J", nodeWidth, nodeHeight); - k = node("K", nodeWidth, nodeHeight); - l = node("L", nodeWidth, nodeHeight); - link(m, h, i, j, k, l); - - return o; - } - - static Node node(String value, double nodeWidth, double nodeHeight) { - Node node = new Node<>(value); - node.setWidth(nodeWidth); - node.setHeight(nodeHeight); - - return node; - } - - @SafeVarargs - static void link(Node parent, Node... children) { - for (var child : children) { - parent.getChildren().add(child); - child.setParent(parent); - } - } -} diff --git a/src/main/java/org/behrang/algorithm/tree/demo/TreePanel.java b/src/main/java/org/behrang/algorithm/tree/demo/TreePanel.java index 6735aca..a3be1c6 100644 --- a/src/main/java/org/behrang/algorithm/tree/demo/TreePanel.java +++ b/src/main/java/org/behrang/algorithm/tree/demo/TreePanel.java @@ -5,7 +5,6 @@ import org.behrang.algorithm.tree.WalkerAlgorithm; import javax.swing.*; -import javax.swing.border.EmptyBorder; import java.awt.*; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; @@ -13,7 +12,6 @@ import static org.behrang.algorithm.tree.DimensionCalculator.calculateMaxNodeDimension; public class TreePanel extends JPanel { - private Node root; public TreePanel(Node root) { @@ -33,34 +31,43 @@ protected void paintComponent(Graphics g) { return; } + Graphics2D g2 = (Graphics2D) g; + g2.setRenderingHint( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON + ); + + g2.setRenderingHint( + RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_GASP + ); + var dim = calculateMaxNodeDimension(root); var walker = new WalkerAlgorithm( - dim.getWidth(), - dim.getWidth(), - 20, - 20, - dim.getHeight() * 2 + (float) dim.getWidth(), + (float) dim.getWidth(), + 20, + 20, + (float) (dim.getHeight() * 3) ); walker.position(root); - - var g2 = (Graphics2D) g; TreeTraversal.preorder(root, n -> { drawNode(g2, n); }); } void drawNode(Graphics2D g, Node node) { - double labelMarginX = 12.0; - double labelMarginY = 1.0; + float labelMarginX = 12.0f; + float labelMarginY = 1.0f; g.setColor(Color.BLACK); Rectangle2D rect = new Rectangle2D.Double( - node.getX(), - node.getY(), - node.getWidth(), - node.getHeight() + node.getX(), + node.getY(), + node.getWidth(), + node.getHeight() ); g.draw(rect); @@ -74,8 +81,8 @@ void drawNode(Graphics2D g, Node node) { Rectangle2D labelBounds = fontMetrics.getStringBounds(label, g); g.drawString(label, - (float) (node.getX() + labelMarginX), - (float) (node.getY() + labelMarginY + labelBounds.getHeight()) + (float) (node.getX() + labelMarginX), + (float) (node.getY() + labelMarginY + labelBounds.getHeight()) ); if (node.hasParent()) { diff --git a/src/test/java/org/behrang/algorithm/tree/DimensionCalculatorTest.java b/src/test/java/org/behrang/algorithm/tree/DimensionCalculatorTest.java index 4dcc93d..4074c8d 100644 --- a/src/test/java/org/behrang/algorithm/tree/DimensionCalculatorTest.java +++ b/src/test/java/org/behrang/algorithm/tree/DimensionCalculatorTest.java @@ -1,6 +1,6 @@ package org.behrang.algorithm.tree; -import org.behrang.algorithm.tree.demo.SampleTree; +import org.behrang.algorithm.tree.data.SampleTreeGenerator; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -8,7 +8,7 @@ class DimensionCalculatorTest { @Test void testCalculateDimensions() { - var root = SampleTree.newUniformInstance(); + var root = SampleTreeGenerator.newUniformInstance(); var algorithm = new WalkerAlgorithm(4, 4, 0, 0, 4); algorithm.position(root); @@ -20,7 +20,7 @@ void testCalculateDimensions() { @Test void testCalculateMaxNodeDimension() { - var root = SampleTree.newUniformInstance(); + var root = SampleTreeGenerator.newUniformInstance(); var algorithm = new WalkerAlgorithm(4, 4, 0, 0, 4); algorithm.position(root); diff --git a/src/test/java/org/behrang/algorithm/tree/FunctionsTest.java b/src/test/java/org/behrang/algorithm/tree/FunctionsTest.java new file mode 100644 index 0000000..168995a --- /dev/null +++ b/src/test/java/org/behrang/algorithm/tree/FunctionsTest.java @@ -0,0 +1,27 @@ +package org.behrang.algorithm.tree; + +import org.behrang.algorithm.tree.data.SampleTreeGenerator; +import org.junit.jupiter.api.Test; + +import static org.behrang.algorithm.tree.Functions.leftmostDescendantAtDepth; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class FunctionsTest { + + @Test + void testLeftmostDescendantAtDepth() { + var o = SampleTreeGenerator.newUniformInstance(); + + assertEquals("O", leftmostDescendantAtDepth(o, 0).getValue()); + assertEquals("E", leftmostDescendantAtDepth(o, 1).getValue()); + assertEquals("A", leftmostDescendantAtDepth(o, 2).getValue()); + assertEquals("B", leftmostDescendantAtDepth(o, 3).getValue()); + + var n = o.getChildren().get(2); + + assertEquals("N", leftmostDescendantAtDepth(n, 0).getValue()); + assertEquals("G", leftmostDescendantAtDepth(n, 1).getValue()); + assertEquals("H", leftmostDescendantAtDepth(n, 2).getValue()); + } + +} \ No newline at end of file