/*
* Copyright (C) 2016 Naman Dwivedi
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This software 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 for more details.
*/
package com.naman14.algovisualizer.visualizer;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
import com.naman14.algovisualizer.DataUtils;
import com.naman14.algovisualizer.algorithm.tree.bst.BinarySearchTree;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class BSTVisualizer extends AlgorithmVisualizer {
private Paint textPaint;
private Paint circlePaint;
private Paint linePaint;
private Paint circleHighlightPaint;
private Paint lineHighlightPaint;
private Rect bounds;
private int height = 0;
private BinarySearchTree b;
private int[] array = DataUtils.bst_array;
private int[][] bst = DataUtils.bst;
private HashMap<Integer, Point> nodes = new HashMap<>();
private List<Integer> visibleNodes = new ArrayList<>();
private int highlightNode = -1;
private int highlighLineStart = -1, highlightLineEnd = -1;
public BSTVisualizer(Context context) {
super(context);
initialise();
}
public BSTVisualizer(Context context, int height) {
super(context);
this.height = height;
initialise();
}
public BSTVisualizer(Context context, AttributeSet attrs) {
super(context, attrs);
initialise();
}
private void initialise() {
textPaint = new Paint();
circlePaint = new Paint();
textPaint.setColor(Color.WHITE);
textPaint.setTextSize(getDimensionInPixelFromSP(15));
textPaint.setAntiAlias(true);
textPaint.setTextAlign(Paint.Align.CENTER);
bounds = new Rect();
textPaint.getTextBounds("0", 0, 1, bounds);
circlePaint.setColor(Color.RED);
circlePaint.setAntiAlias(true);
linePaint = new Paint();
linePaint.setStrokeWidth(5);
linePaint.setColor(Color.BLACK);
circleHighlightPaint = new Paint(circlePaint);
circleHighlightPaint.setColor(Color.BLUE);
lineHighlightPaint = new Paint(linePaint);
lineHighlightPaint.setColor(Color.RED);
lineHighlightPaint.setStrokeWidth(10);
for (int i : array) {
visibleNodes.add(i);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (b != null) {
drawBst(canvas);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (height != 0)
setMeasuredDimension(getMeasuredWidth(), getDimensionInPixel(height));
}
public void setData(BinarySearchTree b) {
this.b = b;
invalidate();
}
private void drawBst(Canvas canvas) {
int root = bst[0][0];
int numLeftNodes = 0, numRightNodes = 0;
for (int i = 0; i < array.length; i++) {
int parentnode = bst[0][i];
int leftnode = bst[1][i];
int rightnode = bst[2][i];
Point p = new Point();
Point p0 = new Point();
Point p1 = new Point();
Point p2 = new Point();
p0.x = getWidth() / 2 + getDimensionInPixel(25);
p0.y = getDimensionInPixel(40);
if (parentnode == root) {
p.x = getWidth() / 2 + getDimensionInPixel(25);
p.y = getDimensionInPixel(40);
} else if (parentnode < root) {
numLeftNodes++;
p.x = p0.x - numLeftNodes * getDimensionInPixel(60);
p.y = p0.y + numLeftNodes * getDimensionInPixel(70);
} else if (parentnode > root) {
numRightNodes++;
if (parentnode == 6) {
p.x = p0.x;
p.y = p0.y + 2 * getDimensionInPixel(70);
} else {
p.x = p0.x + numRightNodes * getDimensionInPixel(60);
p.y = p0.y + numRightNodes * getDimensionInPixel(70);
}
}
boolean shouldDraw;
if (leftnode != -1 && rightnode != -1 && leftnode == rightnode) {
boolean highlight = false;
p1.x = p.x;
p1.y = p.y + getDimensionInPixel(70);
if (parentnode == highlighLineStart && leftnode == highlightLineEnd && rightnode == highlightLineEnd) {
highlight = true;
}
shouldDraw = visibleNodes.contains(leftnode);
drawNodeLine(canvas, p, p1, highlight, shouldDraw);
addNode(p1, leftnode);
} else {
if (leftnode != -1) {
boolean highlight = false;
p1.x = p.x - getDimensionInPixel(60);
p1.y = p.y + getDimensionInPixel(70);
if (parentnode == highlighLineStart && leftnode == highlightLineEnd) {
highlight = true;
}
shouldDraw = visibleNodes.contains(leftnode);
drawNodeLine(canvas, p, p1, highlight, shouldDraw);
addNode(p1, leftnode);
}
if (rightnode != -1) {
boolean highlight = false;
p2.x = p.x + getDimensionInPixel(60);
p2.y = p.y + getDimensionInPixel(70);
if (parentnode == highlighLineStart && rightnode == highlightLineEnd) {
highlight = true;
}
shouldDraw = visibleNodes.contains(rightnode);
drawNodeLine(canvas, p, p2, highlight, shouldDraw);
addNode(p2, rightnode);
}
}
addNode(p, parentnode);
}
drawNodes(canvas);
}
private void addNode(Point point, int number) {
if (!nodes.containsKey(number)) {
nodes.put(number, point);
}
}
private void drawNodes(Canvas canvas) {
for (HashMap.Entry<Integer, Point> entry : nodes.entrySet()) {
Integer i = entry.getKey();
Point p = entry.getValue();
if (visibleNodes.contains(i))
drawCircleTextNode(canvas, p, i);
}
}
private void drawCircleTextNode(Canvas canvas, Point p, int number) {
String text = String.valueOf(number);
if (number == highlightNode) {
canvas.drawCircle(p.x, p.y, getDimensionInPixel(15), circleHighlightPaint);
} else {
canvas.drawCircle(p.x, p.y, getDimensionInPixel(15), circlePaint);
}
int yOffset = bounds.height() / 2;
canvas.drawText(text, p.x, p.y + yOffset, textPaint);
}
private void drawNodeLine(Canvas canvas, Point start, Point end, boolean highlight, boolean draw) {
if (draw) {
if (highlight) {
canvas.drawLine(start.x, start.y, end.x, end.y, lineHighlightPaint);
} else {
canvas.drawLine(start.x, start.y, end.x, end.y, linePaint);
}
}
}
public void highlightNode(int node) {
this.highlightNode = node;
invalidate();
}
public void highlightLine(int start, int end) {
this.highlighLineStart = start;
this.highlightLineEnd = end;
invalidate();
}
//methods for bst insert
public void removeAllNodes() {
visibleNodes.clear();
invalidate();
}
public void addNode(int n) {
visibleNodes.add(n);
invalidate();
}
@Override
public void onCompleted() {
this.highlighLineStart = -1;
this.highlightLineEnd = -1;
invalidate();
}
}