Skip to content

Instantly share code, notes, and snippets.

@ethanve
Created March 15, 2015 21:02
Show Gist options
  • Select an option

  • Save ethanve/f285b876e312143b2867 to your computer and use it in GitHub Desktop.

Select an option

Save ethanve/f285b876e312143b2867 to your computer and use it in GitHub Desktop.
package ParseTree;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.util.*;
import java.util.List;
public class Node<T> {
private double width;
private double height;
private static final int padding = 50;
private T data;
private Node<T> parent;
private List<Node<T>> children;
private Point location;
private Color color;
/**
* Construct a new Node.
*
* @param data The data the node contains.
*/
public Node(T data) {
this.data = data;
parent = null;
children = new ArrayList<>();
color = UIManager.getColor("Panel.background");
}
/**
* Add a node to this Node at the end of the list of children.
*
* @param n The node.
*/
public void addChild(Node<T> n) {
children.add(n);
n.parent = this;
}
/**
* @return True if the node is a leaf, false otherwise.
*/
public boolean isLeaf() {
return children.size() == 0;
}
/**
* A list of the children of this node.
*
* @return
*/
public List<Node<T>> getChildren() {
return children;
}
/**
* @return The parent of the node.
*/
public Node<T> getParent() {
return parent != null ? parent : null;
}
/**
* Get a child at a particular index.
*
* @param i The index.
* @return The node.
*/
public Node<T> childAtIndex(int i) throws IndexOutOfBoundsException {
return children.get(i);
}
/**
* Gets the data of the node
*
* @return The data of the node.
*/
public T getData() {
return data;
}
/**
* Gets the location of the node in the panel
*
* @return The data of the node.
*/
public Point getLocation() {
return location;
}
/**
* Sets the location of the node in the panel
*
* @return The data of the node.
*/
public void setLocation(Point location) {
this.location = location;
}
/**
* Gets the height of the node in the panel
*
* @return The data of the node.
*/
public double getHeight() {
return height;
}
/**
* Gets the width of the node in the panel
*
* @return The data of the node.
*/
public double getWidth() {
return width;
}
/**
* Gets the padding of the node in the panel
*
* @return The data of the node.
*/
public int getPadding() {
return padding;
}
/**
* Sets the color of the node in the panel
*
* @return The data of the node.
*/
public void setColor(Color color) {
this.color = color;
}
/**
* Gets the location of the node in the panel
*
* @return The data of the node.
*/
public Color getColor() {
return color;
}
/**
* Sets the height of the node in the panel
*
* @return The data of the node.
*/
public void setHeight(double height) {
this.height = height;
}
/**
* Sets the height of the node in the panel
*
* @return The data of the node.
*/
public void setWidth(double width) {
this.width = width;
}
/**
* Gets the total width of the node's children
*
* @return total width of the node's children
*/
public double getLevelWidth() {
if (children.size() <= 1) {
return 0;
}
return children.stream().mapToDouble(n -> n.getWidth() + n.getPadding()).sum();
}
/**
* Gets the width of the node's children ending with end
*
* @param end the ending
* @return Width of the node's children
*/
public int getLevelWidth(int end) {
if (children.size() <= 1) {
return 0;
}
int sum = 0;
for (int i = 0; i < end; i++) {
Node n = children.get(i);
sum += (n.getWidth() + n.getPadding());
}
return sum;
}
/**
* Paints the node in the panel
*
* @return The data of the node.
*/
public void paint(JComponent parent, Graphics2D g2d) {
FontMetrics fm = g2d.getFontMetrics();
double width = getWidth();
double height = getHeight();
Point p = getLocation();
double x = p.getX();
double y = p.getY();
Ellipse2D node = new Ellipse2D.Double(x, y, width, height);
g2d.setColor(getColor());
g2d.fill(node);
g2d.setColor(Color.GRAY);
g2d.draw(node);
String text = this.getData().toString();
x = x + ((width - fm.stringWidth(text)) / 2);
y = y + (((height - fm.getHeight()) / 2) + fm.getAscent());
g2d.drawString(text, (int) x, (int) y);
}
public String toString() {
return this.getData().toString() + " @ " + getLocation();
}
}
package ParseTree;
import LexicalAnalyzer.Token;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Line2D;
public class ParseTree<T> {
protected Node<T> root;
protected double zoomFactor = .5;
/**
* Set the root of the tree.
*
* @param n The node.
*/
public void setRoot(Node<T> n) {
root = n;
}
/**
* @return The root of the tree.
*/
public Node<T> getRoot() {
return root;
}
public void printTree() {
EventQueue.invokeLater(() -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Parse Tree Visualization");
ParseTreePane panel = new ParseTreePane();
frame.add(panel, BorderLayout.CENTER);
JButton buttonMinus = new JButton(" Zoom Out (-) ");
JButton buttonPlus = new JButton(" Zoom In (+) ");
buttonMinus.addActionListener(e -> {
zoomFactor -= 0.1;
buttonMinus.setEnabled(zoomFactor > 0.1);
buttonPlus.setEnabled(zoomFactor < 4.0);
panel.doLayout();
});
buttonPlus.addActionListener(e -> {
zoomFactor += 0.1;
buttonMinus.setEnabled(zoomFactor > 0.1);
buttonPlus.setEnabled(zoomFactor < 4.0);
panel.doLayout();
});
Box box = Box.createHorizontalBox();
box.add(Box.createHorizontalGlue());
box.add(buttonPlus);
box.add(Box.createHorizontalStrut(20));
box.add(buttonMinus);
box.add(Box.createHorizontalGlue());
frame.add(box, BorderLayout.SOUTH);
Toolkit tk = Toolkit.getDefaultToolkit();
int xSize = ((int) tk.getScreenSize().getWidth());
int ySize = ((int) tk.getScreenSize().getHeight());
panel.setPreferredSize(new Dimension(xSize, ySize));
JScrollPane scroll = new JScrollPane(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
frame.add(scroll);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
public class ParseTreePane extends JPanel {
public ParseTreePane() {
super(null);
setNodeDimensions(root);
}
protected void layoutNode(Node<T> node, int x, int y) {
if (node != null) {
node.setLocation(new Point(x, y));
//We get the level width by adding each node's width
// as well as the right padding. We then divide it by
// 2, negate the parent's X value by that (start variable)
// and start printing the level at that location
// We then keep on adding the printed node's width
// to the next node
double levelWidth = node.getLevelWidth();
int children = node.getChildren().size();
int start = (x - (int) Math.ceil(levelWidth / 2));
for (int i = 0; i < children; i++) {
Node n = node.childAtIndex(i);
int childX = start + node.getLevelWidth(i);
int childY = (int) Math.ceil(y + node.getPadding() + node.getHeight());
layoutNode(n, childX, childY);
}
}
}
/**
* On init, we set all node dimensions
*
* @param node the node
*/
public void setNodeDimensions(Node<T> node) {
FontMetrics fm = getFontMetrics(getFont());
double width = (fm.stringWidth(node.getData().toString()) + 30) * zoomFactor;
double height = (fm.getHeight() * 2) * zoomFactor;
node.setWidth(width);
node.setHeight(height);
node.getChildren().forEach(this::setNodeDimensions);
}
@Override
public void doLayout() {
setNodeDimensions(root);
setFont(new Font("TimesRoman", Font.PLAIN, (int) Math.floor(12 * zoomFactor)));
int x = getWidth() / 2;
int y = (int) root.getHeight() + 10;
root.setLocation(new Point(x, y));
layoutNode(root, x, y);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (root != null) {
Graphics2D g2d = (Graphics2D) g.create();
paintConnectors(root, g2d);
paintNode(root, g2d);
g2d.dispose();
}
}
protected void paintNode(Node<T> node, Graphics2D g2d) {
if (node != null && node.getLocation() != null) {
node.paint(this, g2d);
for (Node n : node.getChildren()) {
paintNode(n, g2d);
}
}
}
protected void paintConnectors(Node<T> node, Graphics2D g2d) {
if (node != null && node.getLocation() != null) {
Node parent = node.getParent();
if (parent != null) {
g2d.setColor(Color.GRAY);
if (parent.getLocation() != null && node.getLocation() != null) {
double parentX = parent.getLocation().getX() + (parent.getWidth() / 2);
double parentY = parent.getLocation().getY();
double nodeX = node.getLocation().getX() + (node.getWidth() / 2);
double nodeY = node.getLocation().getY();
g2d.draw(new Line2D.Double(parentX, parentY, nodeX, nodeY));
}
}
for (Node n : node.getChildren()) {
paintConnectors(n, g2d);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment