Skip to content

Instantly share code, notes, and snippets.

@ivanopagano
Created October 15, 2012 21:04
Show Gist options
  • Save ivanopagano/3895471 to your computer and use it in GitHub Desktop.
Save ivanopagano/3895471 to your computer and use it in GitHub Desktop.
Huffman Jit Visualizer
package patmat
/*
* This file should be used to generate an easy visualization of huffman trees implemented
* during the course of the Coursera "Principles of Functional Programming" class, held by prof.M.Odersky.
*
* It's intended as an aid to the students.
* Take notice that you need to have access to the course material, so it's almost useless for any other purpose.
*
* To correctly compile you need to have the patmat.Huffman object visible in your class-path.
* The easiest way is to include this class alongside your assignment project.
*
* To correctly visualize the output file, you need to download an unzip the excellent javascript visualization library
* "JavaScript InfoVis Toolkit"
* available from http://philogb.github.com/jit/index.html
*
* You can freely distribute and modify this software for the better
* Enjoy
*
*/
import Huffman._
import java.io.{ File, FileWriter, Writer }
object HuffmanViz {
/*
* You should call the application passing two parameters:
* 1. the absolute path to the folder where you unzipped the Javascript Infoviz Toolkit
* 2. the (absolute or relative) path to the destination html file.
*/
def main(args: Array[String]) {
if (args.size < 2) println("""|Please specify:
| 1. the absolute path of the folder where I can find the Javascript Infoviz Toolkit file "jit-yc.js"
| 2. the path to an output .html file which will show the huffman tree
|""".stripMargin)
else {
//Check the destination file
val jitFile = args(0)
val dest = new File(args(1))
if (dest.isDirectory() || !dest.getName().endsWith(".html")) throw new Error("The given path corresponds to a directory, please choose a valid .html filename")
val jsonTree: String = treeToJson(buildTree)
//passing the destination file, the lambda expression gives me back an auto-closing writer to the file
val result = withResource(dest) { writer =>
val output = outputTemplate.format(jitFile, jsonTree, chars(buildTree).mkString.toUpperCase)
writer.write(output)
}
println("You can see the generated tree opening the file " + dest.getAbsolutePath())
}
}
//Change this method implementation to define the code tree to visualize
def buildTree: CodeTree = frenchCode
/*
* A method wrapping operations with I/O to guarantee correct resource cleaning
*/
private def withResource(file: File)(f: Writer => Unit) {
val writer = new FileWriter(file, false)
try {
f(writer)
} finally {
writer.close()
}
}
//This is the json template for each codetree node
private val nodeTemplate: String = """{
id: "%1$s",
name: "(%1$s) %2$d",
data: {},
children: [%3$s]
}"""
/*
* Converts the tree to a json format for the javascript infoviz library
*/
def treeToJson(tree: CodeTree): String = tree match {
case Leaf(c, w) => nodeTemplate.format(c.toUpper, w, "")
case Fork(l, r, chars, w) => nodeTemplate.format(chars.mkString.toUpperCase, w, treeToJson(l) + "," + treeToJson(r))
}
//This is the template for the final html file
private val outputTemplate = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Huffman Trees</title>
<style>
html, body {
margin:0;
padding:0;
font-family: "Lucida Grande", Verdana;
font-size: 0.9em;
text-align: center;
background-color:#F2F2F2;
}
#container {
width: 1000px;
height: 600px;
margin:0 auto;
position:relative;
border: thin solid grey;
cursor: move;
background-color: white;
}
.text {
margin: 2px;
}
#infovis {
position:relative;
width:1000px;
height:600px;
margin:auto;
overflow:hidden;
}
</style>
<!-- If you need to include this script, it's available in the JavaScript InfoVis Toolkit bundle -->
<!--[if IE]><script language="javascript" type="text/javascript" src="./Extras/excanvas.js"></script><![endif]-->
<!-- JIT Library File -->
<script language="javascript" type="text/javascript" src="%s/jit-yc.js"></script>
<script>
var labelType, useGradients, nativeTextSupport, animate;
(function() {
var ua = navigator.userAgent,
iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i),
typeOfCanvas = typeof HTMLCanvasElement,
nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'),
textSupport = nativeCanvasSupport
&& (typeof document.createElement('canvas').getContext('2d').fillText == 'function');
//I'm setting this based on the fact that ExCanvas provides text support for IE
//and that as of today iPhone/iPad current text support is lame
labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? 'Native' : 'HTML';
nativeTextSupport = labelType == 'Native';
useGradients = nativeCanvasSupport;
animate = !(iStuff || !nativeCanvasSupport);
})();
function init(){
//init data
var json = %s;
//end
//init Spacetree
//Create a new ST instance
var st = new $jit.ST({
//id of viz container element
injectInto: 'infovis',
//set duration for the animation
duration: 800,
//set animation transition type
transition: $jit.Trans.Quart.easeInOut,
//set distance between node and its children
levelDistance: 30,
orientation: 'top',
width: 0,
height: 0,
constrained: false,
levelsToShow: 100,
//enable panning
Navigation: {
enable:true,
panning:true
},
//set node and edge styles
Node: {
height: 20,
autoWidth: true,
type: 'rectangle',
color: 'none',
overridable: false
},
Edge: {
color: 'navy',
overridable: false
},
onBeforeCompute: function(node){
},
onAfterCompute: function(){
},
//This method is called on DOM label creation.
//Use this method to add event handlers and styles to
//your node.
onCreateLabel: function(label, node){
label.id = node.id;
label.innerHTML = node.name;
label.onclick = function(){
st.onClick(node.id);
};
//set label styles
var style = label.style;
/*style.width = 100 + 'px';*/
style.width = node.name.width * 1.5 + 'px';
style.height = 17 + 'px';
style.fontWeight = 'bold';
style.cursor = 'pointer';
style.color = '#333';
style.fontSize = '0.8em';
style.textAlign= 'center';
style.paddingTop = '3px';
},
onBeforePlotNode: function(node){
},
onBeforePlotLine: function(adj){
}
});
//load json data
st.loadJSON(json);
//compute node positions and layout
st.compute();
st.setRoot("%s", "animate");
//end
}
</script>
</head>
<body onload="init();">
<div id="container">
<div id="infovis"></div>
</div>
</body>
</html>"""
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment