├── .hgignore
├── img
├── pause.png
├── play.png
├── replay.png
├── favicon.png
├── goToEnd.png
├── arrow_white.png
├── cross_white.png
├── nextFrame.png
├── prevFrame.png
├── goToBeginning.png
├── translate-32.png
├── arrow_black_right.png
└── arrow_white_right.png
├── js
├── graph_library
│ ├── misc.js
│ ├── Widget.js
│ ├── constant.js
│ ├── helperObjects.js
│ ├── properties.js
│ ├── GraphEdgeWidget.js
│ ├── GraphVertexWidget.js
│ └── GraphWidget.js
├── actions
│ └── list_actions.js
├── common.js
└── viz.js
├── css
├── normalize.css
├── common.css
├── list.css
└── viz.css
├── fonts
└── silkscreen
│ └── stylesheet.css
├── readme.md
└── list.html
/.hgignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/img/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulonglong/VisuAlgo-List/HEAD/img/pause.png
--------------------------------------------------------------------------------
/img/play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulonglong/VisuAlgo-List/HEAD/img/play.png
--------------------------------------------------------------------------------
/img/replay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulonglong/VisuAlgo-List/HEAD/img/replay.png
--------------------------------------------------------------------------------
/img/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulonglong/VisuAlgo-List/HEAD/img/favicon.png
--------------------------------------------------------------------------------
/img/goToEnd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulonglong/VisuAlgo-List/HEAD/img/goToEnd.png
--------------------------------------------------------------------------------
/img/arrow_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulonglong/VisuAlgo-List/HEAD/img/arrow_white.png
--------------------------------------------------------------------------------
/img/cross_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulonglong/VisuAlgo-List/HEAD/img/cross_white.png
--------------------------------------------------------------------------------
/img/nextFrame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulonglong/VisuAlgo-List/HEAD/img/nextFrame.png
--------------------------------------------------------------------------------
/img/prevFrame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulonglong/VisuAlgo-List/HEAD/img/prevFrame.png
--------------------------------------------------------------------------------
/img/goToBeginning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulonglong/VisuAlgo-List/HEAD/img/goToBeginning.png
--------------------------------------------------------------------------------
/img/translate-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulonglong/VisuAlgo-List/HEAD/img/translate-32.png
--------------------------------------------------------------------------------
/img/arrow_black_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulonglong/VisuAlgo-List/HEAD/img/arrow_black_right.png
--------------------------------------------------------------------------------
/img/arrow_white_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yulonglong/VisuAlgo-List/HEAD/img/arrow_white_right.png
--------------------------------------------------------------------------------
/js/graph_library/misc.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Miscellaneous functions
3 | */
4 |
5 | function deepCopy(obj){
6 | var newObj;
7 |
8 | if(obj instanceof Array){
9 | var i;
10 |
11 | newObj = [];
12 |
13 | for (i = 0; i < obj.length; i++) {
14 | newObj.push(deepCopy(obj[i]));
15 | }
16 | }
17 |
18 | else if(obj instanceof Object){
19 | newObj = {};
20 |
21 | for(keys in obj){
22 | newObj[keys] = deepCopy(obj[keys]);
23 | }
24 | }
25 |
26 | else{
27 | newObj = obj;
28 | }
29 |
30 | return newObj;
31 | }
--------------------------------------------------------------------------------
/css/normalize.css:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | html, body, p {
4 | margin: 0px;
5 | padding: 0px;
6 | }
7 |
8 | a {
9 | color: inherit;
10 | text-decoration: none;
11 | display: inline-block;
12 | }
13 |
14 | u {
15 | text-decoration: underline;
16 | }
17 |
18 | h1,h2,h3,h4,h5,h6 {
19 | font-size: inherit;
20 | font-weight: normal;
21 | margin: 0px;
22 | padding: 0px;
23 | }
24 |
25 | img {
26 | display: block;
27 | }
28 |
29 | ul, li {
30 | padding: 0px;
31 | margin: 0px;
32 | }
33 |
34 | form {
35 | margin: 0px;
36 | padding: 0px;
37 | }
38 |
39 | input {
40 | border: none;
41 | outline: none;
42 | margin: 0px;
43 | }
--------------------------------------------------------------------------------
/js/graph_library/Widget.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Initialization of SVG elements for the other widgets to draw on
3 | * TODO: Think of a better use for this file
4 | */
5 |
6 | var mainSvg = d3.select("#viz")
7 | .append("svg")
8 | .attr("width", MAIN_SVG_WIDTH)
9 | .attr("height", MAIN_SVG_HEIGHT);
10 |
11 | // Currently pseudocodeSvg is not used; pseudocodes are handled by front end
12 | var pseudocodeSvg = d3.select("#pseudocode")
13 | .append("svg")
14 | .attr("width", PSEUDOCODE_SVG_WIDTH)
15 | .attr("height", PSEUDOCODE_SVG_HEIGHT);
16 |
17 | var markerSvg = mainSvg.append("g")
18 | .attr("id", "marker");
--------------------------------------------------------------------------------
/fonts/silkscreen/stylesheet.css:
--------------------------------------------------------------------------------
1 | /* Generated by Font Squirrel (http://www.fontsquirrel.com) on October 13, 2012 01:09:36 PM America/New_York */
2 |
3 | @font-face {
4 | font-family: 'SilkscreenNormal';
5 | src: url('slkscr-webfont.eot');
6 | src: url('slkscr-webfont.eot?#iefix') format('embedded-opentype'),
7 | url('slkscr-webfont.woff') format('woff'),
8 | url('slkscr-webfont.ttf') format('truetype'),
9 | url('slkscr-webfont.svg#SilkscreenNormal') format('svg');
10 | font-weight: normal;
11 | font-style: normal;
12 |
13 | }
14 |
15 | /*
16 | @font-face {
17 | font-family: 'SilkscreenBold';
18 | src: url('slkscrb-webfont.eot');
19 | src: url('slkscrb-webfont.eot?#iefix') format('embedded-opentype'),
20 | url('slkscrb-webfont.woff') format('woff'),
21 | url('slkscrb-webfont.ttf') format('truetype'),
22 | url('slkscrb-webfont.svg#SilkscreenBold') format('svg');
23 | font-weight: normal;
24 | font-style: normal;
25 |
26 | }
27 |
28 | @font-face {
29 | font-family: 'SilkscreenExpandedNormal';
30 | src: url('slkscre-webfont.eot');
31 | src: url('slkscre-webfont.eot?#iefix') format('embedded-opentype'),
32 | url('slkscre-webfont.woff') format('woff'),
33 | url('slkscre-webfont.ttf') format('truetype'),
34 | url('slkscre-webfont.svg#SilkscreenExpandedNormal') format('svg');
35 | font-weight: normal;
36 | font-style: normal;
37 |
38 | }
39 |
40 | @font-face {
41 | font-family: 'SilkscreenExpandedBold';
42 | src: url('slkscreb-webfont.eot');
43 | src: url('slkscreb-webfont.eot?#iefix') format('embedded-opentype'),
44 | url('slkscreb-webfont.woff') format('woff'),
45 | url('slkscreb-webfont.ttf') format('truetype'),
46 | url('slkscreb-webfont.svg#SilkscreenExpandedBold') format('svg');
47 | font-weight: normal;
48 | font-style: normal;
49 |
50 | }
51 | */
--------------------------------------------------------------------------------
/js/graph_library/constant.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Variables that SHOULD NOT be modified once initialized
3 | * These variables can be defined without the HTML elements,
4 | * and SHOULD be defined before calling any of the visualization scripts
5 | * Make sure you know what you're doing before modifying this file
6 | */
7 |
8 | const OBJ_HIDDEN = -1;
9 |
10 | const VERTEX_SHAPE_CIRCLE = "circle";
11 | const VERTEX_SHAPE_RECT = "rect";
12 | // const VERTEX_SHAPE_ELLIPSE = "ellipse"; // Currently not implemented
13 | // IDEA: VERTEX_SHAPE_POLYLINE (Very low priority, might not be backward-compatible if implemented)
14 |
15 | const VERTEX_DEFAULT = "default";
16 | const VERTEX_NORMAL_BLUE = "normal_blue";
17 | const VERTEX_NORMAL_GREEN = "normal_green";
18 | const VERTEX_HIGHLIGHTED = "highlighted";
19 | const VERTEX_HIGHLIGHTED_RECT = "highlighted_rect";
20 | const VERTEX_TRAVERSED = "traversed";
21 | const VERTEX_RESULT = "result";
22 | const VERTEX_RESULT_RECT = "result_rect";
23 | const VERTEX_RECT = "rect";
24 | const VERTEX_BLUE_FILL = "blueFill";
25 | const VERTEX_GREEN_FILL = "greenFill";
26 | const VERTEX_GREY_FILL = "greyFill";
27 | const VERTEX_PINK_FILL = "pinkFill";
28 | const VERTEX_RED_FILL = "redFill";
29 | const VERTEX_BLUE_OUTLINE = "blueOutline";
30 | const VERTEX_GREEN_OUTLINE = "greenOutline";
31 | const VERTEX_GREY_OUTLINE = "greyOutline";
32 | const VERTEX_PINK_OUTLINE = "pinkOutline";
33 | const VERTEX_RED_OUTLINE = "redOutline";
34 |
35 | const EDGE_DEFAULT = "default";
36 | const EDGE_HIGHLIGHTED = "highlighted";
37 | const EDGE_TRAVERSED = "traversed";
38 | const EDGE_BLUE = "blue";
39 | const EDGE_GREEN = "green";
40 | const EDGE_GREY = "grey";
41 | const EDGE_PINK = "pink";
42 | const EDGE_RED = "red";
43 |
44 | const EDGE_TYPE_UDE = 0;
45 | const EDGE_TYPE_DE = 1;
46 | const EDGE_TYPE_BDE = 2;
47 |
48 | const NO_ITERATION = -1;
49 | const NO_STATELIST = {};
50 |
51 | const ANIMATION_PLAY = 1;
52 | const ANIMATION_PAUSE = 0;
53 | const ANIMATION_STOP = -1;
54 |
55 | const UPDATE_FORWARD = true;
56 | const UPDATE_BACKWARD = false;
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | VisuAlgo (List)
2 | ================================
3 |
4 | Visualising data structures and algorithms through animation.
5 | [http://visualgo.net](http://visualgo.net)
6 |
7 | Developed a portion of the project, List data structure visualization.
8 | [http://visualgo.net/list](http://visualgo.net/list)
9 | It includes Linked List, Stack, Queue, Doubly Linked List, and Double-Ended Queue
10 |
11 | VisuAlgo currently contains the following data structures and algorithms:
12 |
13 |
14 | * Sorting
15 | * Bitmask
16 | * Linked List, Stack, Queue, Doubly List, Deque
17 | * Binary Search Tree, AVL Tree
18 | * Binary Heap
19 | * Graph Data Structures
20 | * Union-Find Disjoint Sets
21 | * Segment Tree
22 | * Binary Indexed Tree (Fenwick Tree)
23 | * Generic Recursion Tree/DAG
24 | * Graph Traversal
25 | * Minimum Spanning Tree
26 | * Single-Source Shortest Paths
27 | * Network Flow
28 | * Graph Matching
29 | * Suffix Tree
30 | * Suffix Array
31 | * (Computational) Geometry
32 |
33 |
34 |
35 | # About VisuAlgo
36 |
37 | **Motivation**
38 |
39 | VisuAlgo was conceptualised in 2011 by Dr Steven Halim as a tool to help his students better understand data structures and algorithms, by allowing them to learn the basics on their own and at their own pace.
40 | Together with some of his students from the National University of Singapore (see "Team"), a series of visualisations were developed and consolidated, from simple sorting algorithms to complex graph data structures and algorithms.
41 | Though specifically designed for the use of NUS students taking various data structure and algorithm classes (CS1010, CS1020, CS2010, CS2020, CS3230, and CS3233), as advocators of online learning, we hope that curious minds around the world will find these visualisations useful as well.
42 |
43 |
44 | **Ongoing developments**
45 |
46 | VisuAlgo is an ongoing project, and more complex visualisations are still being developed.
47 | The most exciting new development is an automated question generator and verifier (the online quiz system) that allows student to test their knowledge of basic data structures and algorithms.
48 | The questions are randomly generated via some rules and students' answers are quickly and automatically graded upon submission to our grading server.
49 | To try this online quiz feature, click the "Start training!" button on the top right corner of this page.
50 |
51 |
52 | **Publications**
53 |
54 | This work has been presented briefly at the CLI Workshop at the ACM ICPC World Finals 2012 (Poland, Warsaw) and at the IOI Conference at IOI 2012 (Sirmione-Montichiari, Italy).
55 |
56 |
57 | **Acknowledgements**
58 |
59 | This project is made possible by the generous Teaching Enhancement Grant from NUS Centre for Development of Teaching and Learning.
60 |
61 | **Contact**
62 |
63 | Please contact Dr. Steven Halim (stevenhalim@gmail.com) for any VisuAlgo related queries.
64 |
--------------------------------------------------------------------------------
/js/actions/list_actions.js:
--------------------------------------------------------------------------------
1 | //@author Steven Kester Yuwono
2 | //actions panel related files for List VisuAlgo
3 | var actionsWidth = 150;
4 | var statusCodetraceWidth = 420;
5 |
6 | var isCreateOpen = false;
7 | var isSearchOpen = false;
8 | var isInsertOpen = false;
9 | var isRemoveOpen = false;
10 |
11 | function openCreate() {
12 | $(".create").css("bottom","146px");
13 | $('#createfixedsize-input').hide();
14 | $('#createuserdefined-input').hide();
15 | if(!isCreateOpen) {
16 | $('.create').fadeIn('fast');
17 | isCreateOpen = true;
18 | }
19 | }
20 | function closeCreate() {
21 | if(isCreateOpen) {
22 | $('.create').fadeOut('fast');
23 | $('#create-err').html("");
24 | isCreateOpen = false;
25 | }
26 | }
27 | function openSearch() {
28 | if(!isSearchOpen) {
29 | $('.search').fadeIn('fast');
30 | isSearchOpen = true;
31 | }
32 | }
33 | function closeSearch() {
34 | if(isSearchOpen) {
35 | $('.search').fadeOut('fast');
36 | $('#search-err').html("");
37 | isSearchOpen = false;
38 | }
39 | }
40 | function openInsert() {
41 | $(".insert").css("bottom","92px");
42 | $('#insertkth-input').hide();
43 | $('#inserthead-input').hide();
44 | $('#inserttail-input').hide();
45 | if(!isInsertOpen) {
46 | $('.insert').fadeIn('fast');
47 | isInsertOpen = true;
48 | }
49 | }
50 | function closeInsert() {
51 | if(isInsertOpen) {
52 | $('.insert').fadeOut('fast');
53 | $('#insert-err').html("");
54 | isInsertOpen = false;
55 | }
56 | }
57 | function openRemove() {
58 | $(".remove").css("bottom","65px");
59 | $('#removekth-input').hide();
60 | if(!isRemoveOpen) {
61 | $('.remove').fadeIn('fast');
62 | isRemoveOpen = true;
63 | }
64 | }
65 | function closeRemove() {
66 | if(isRemoveOpen) {
67 | $('.remove').fadeOut('fast');
68 | $('#remove-err').html("");
69 | isRemoveOpen = false;
70 | }
71 | }
72 |
73 | //
74 | function hideEntireActionsPanel() {
75 | closeCreate();
76 | closeSearch();
77 | closeInsert();
78 | closeRemove();
79 | hideActionsPanel();
80 | }
81 |
82 | $( document ).ready(function() {
83 |
84 | //action pullouts
85 | $('#create').click(function() {
86 | closeSearch();
87 | closeInsert();
88 | closeRemove();
89 | openCreate();
90 | });
91 | $('#search').click(function() {
92 | closeCreate();
93 | closeInsert();
94 | closeRemove();
95 | openSearch();
96 | });
97 | $('#insert').click(function() {
98 | closeCreate();
99 | closeSearch();
100 | closeRemove();
101 | openInsert();
102 | });
103 | $('#remove').click(function() {
104 | closeCreate();
105 | closeSearch();
106 | closeInsert();
107 | openRemove();
108 | });
109 |
110 |
111 | //tutorial mode
112 | $('#list-tutorial-2 .tutorial-next').click(function() {
113 | showActionsPanel();
114 | });
115 | $('#list-tutorial-3 .tutorial-next').click(function() {
116 | hideEntireActionsPanel();
117 | });
118 | $('#list-tutorial-4 .tutorial-next').click(function() {
119 | showStatusPanel();
120 | });
121 | $('#list-tutorial-5 .tutorial-next').click(function() {
122 | hideStatusPanel();
123 | showCodetracePanel();
124 | });
125 | $('#list-tutorial-6 .tutorial-next').click(function() {
126 | hideCodetracePanel();
127 | });
128 | });
--------------------------------------------------------------------------------
/css/common.css:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | html { overflow-y: scroll; }
4 |
5 | body { background: #eeeeee; font-family: "PT Sans"; font-size: 13px; }
6 |
7 | #top-bar {
8 | height: 40px;
9 | padding: 0px 30px;
10 | background: black;
11 | color: white;
12 | }
13 | /*left-side*/
14 | #home {
15 | font-family: 'SilkscreenNormal';
16 | font-size: 20px;
17 | padding-top: 10px;
18 | }
19 | #title {
20 | padding-left: 20px;
21 | text-transform: uppercase;
22 | font-size: 16px;
23 | color: #aaaaaa;
24 | }
25 | #title a { cursor: pointer; margin-right: 10px; }
26 | #title .selected-viz { font-weight: bold; color: white; }
27 |
28 | /*right-side*/
29 | #fb-login {
30 | float: right;
31 | background: #516eab;
32 | padding: 11px 15px;
33 | font-size: 14px;
34 | }
35 | .right-links {
36 | float: right;
37 | position: relative;
38 | margin-left: 10px;
39 | padding: 15px 15px;
40 | color: white;
41 | text-align: center;
42 | font-size: 14px;
43 | cursor: pointer;
44 | }
45 | /*#training-link { padding: 15px 15px 30px; }
46 | #training-link:after {
47 | content: " ";
48 | position: absolute;
49 | bottom: 0px;
50 | right: 0px;
51 | height: 0px;
52 | width: 0px;
53 | border-bottom: 12px solid #eee;
54 | border-right: 116px solid transparent;
55 | }*/
56 |
57 | #main { position: relative; padding: 30px 30px 10px 10px; overflow: auto; }
58 |
59 | #bottom-bar {
60 | padding: 12px 30px;
61 | background: black;
62 | color: white;
63 | font-size: 14px;
64 | text-align: right;
65 | }
66 | #bottom-bar a { padding-left: 15px; cursor: pointer; }
67 | #bottom-bar a:hover { text-decoration: underline; }
68 |
69 | #dark-overlay {
70 | display: none;
71 | background: rgba(0,0,0,0.7);
72 | height: 100%;
73 | width: 100%;
74 | position: fixed;
75 | top: 0;
76 | left: 0;
77 | z-index: 1002;
78 | }
79 | .overlays {
80 | display: none;
81 | background: #eeeeee;
82 | position: fixed;
83 | top: 50%;
84 | left: 50%;
85 | z-index: 1003;
86 | }
87 | #about {
88 | height: 470px;
89 | width: 600px;
90 | margin-left: -300px;
91 | margin-top: -235px;
92 | }
93 | #team {
94 | height: 460px;
95 | width: 300px;
96 | margin-left: -150px;
97 | margin-top: -230px;
98 | }
99 | #termsofuse {
100 | height: 180px;
101 | width: 400px;
102 | margin-left: -200px;
103 | margin-top: -90px;
104 | }
105 | .overlays .close-overlay {
106 | position: absolute;
107 | top: 20px;
108 | right: 20px;
109 | cursor: pointer;
110 | }
111 | .overlays h4 {
112 | color: white;
113 | font-family: 'SilkscreenNormal';
114 | font-size: 20px;
115 | padding: 15px 20px;
116 | }
117 | .overlays .content { padding: 10px 20px 20px; }
118 | .overlays p { padding-top: 10px; font-size: 14px; }
119 | .overlays a { text-decoration: underline; }
120 |
121 | @media (min-width: 320px) { /* 320-480px */
122 |
123 | }
124 |
125 | @media (min-width: 480px) { /* 480-768px */
126 | #top-bar { padding: 0px 50px; }
127 | #bottom-bar { padding: 12px 50px; }
128 | #main { padding: 50px 50px 30px 30px; }
129 | }
130 |
131 | @media (min-width: 768px) { /* 768-1024px */
132 | #top-bar { padding: 0px 60px; }
133 | #bottom-bar { padding: 12px 60px; }
134 | #main { padding: 60px 60px 40px 40px; }
135 | }
136 |
137 | @media (min-width: 1024px) { /* more than 1024px */
138 |
139 | }
--------------------------------------------------------------------------------
/js/graph_library/helperObjects.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Data structures to assist internal implementation of algorithmic visualizations
3 | */
4 |
5 | // Make sure to check the type of the objects passed in to avoid bugs (especially if used for comparisons)
6 | var ObjectPair = function(objectOne, objectTwo){
7 | this.getFirst = function(){
8 | return objectOne;
9 | }
10 |
11 | this.getSecond = function(){
12 | return objectTwo;
13 | }
14 |
15 | this.setFirst = function(newObjectOne){
16 | objectOne = newObjectOne;
17 | }
18 |
19 | this.setSecond = function(newObjectTwo){
20 | objectTwo = newObjectTwo;
21 | }
22 | }
23 |
24 | ObjectPair.compare = function(objPairOne, objPairTwo){
25 | if(objPairOne.getFirst() > objPairTwo.getFirst()) return 1;
26 | else if(objPairOne.getFirst() < objPairTwo.getFirst()) return -1;
27 | else{
28 | if(objPairOne.getSecond() > objPairTwo.getSecond()) return 1;
29 | if(objPairOne.getSecond() < objPairTwo.getSecond()) return -1;
30 | else return 0;
31 | }
32 | }
33 |
34 | // Make sure to check the type of the objects passed in to avoid bugs (especially if used for comparisons)
35 | var ObjectTriple = function(objectOne, objectTwo, objectThree){
36 | this.getFirst = function(){
37 | return objectOne;
38 | }
39 |
40 | this.getSecond = function(){
41 | return objectTwo;
42 | }
43 |
44 | this.getThird = function(){
45 | return objectThree;
46 | }
47 |
48 | this.setFirst = function(newObjectOne){
49 | objectOne = newObjectOne;
50 | }
51 |
52 | this.setSecond = function(newObjectTwo){
53 | objectTwo = newObjectTwo;
54 | }
55 |
56 | this.setThird = function(newObjectThree){
57 | objectThree = newObjectThree;
58 | }
59 | }
60 |
61 | ObjectTriple.compare = function(objTripleOne, objTripleTwo){
62 | if(objTripleOne.getFirst() > objTripleTwo.getFirst()) return 1;
63 | else if(objTripleOne.getFirst() < objTripleTwo.getFirst()) return -1;
64 | else{
65 | if(objTripleOne.getSecond() > objTripleTwo.getSecond()) return 1;
66 | if(objTripleOne.getSecond() < objTripleTwo.getSecond()) return -1;
67 | else{
68 | if(objTripleOne.getThird() > objTripleTwo.getThird()) return 1;
69 | if(objTripleOne.getThird() < objTripleTwo.getThird()) return -1;
70 | else return 0;
71 | }
72 | }
73 | }
74 |
75 | var UfdsHelper = function(){
76 | /*
77 | * Structure of internalUfds:
78 | * - key: inserted key
79 | * - value: JS object with:
80 | * - "parent"
81 | * - "rank"
82 | */
83 | var self = this;
84 | var internalUfds = {};
85 |
86 | this.insert = function(insertedKey){
87 | if(internalUfds[insertedKey] != null) return false;
88 |
89 | var newElement = {};
90 | newElement["parent"] = insertedKey;
91 | newElement["rank"] = 0;
92 |
93 | internalUfds[insertedKey] = newElement;
94 | }
95 |
96 | this.findSet = function(key){
97 | if(internalUfds[key] == null) return false;
98 |
99 | var currentParent = internalUfds[key]["parent"];
100 | var currentElement = key;
101 | while(currentParent != currentElement){
102 | currentElement = currentParent;
103 | currentParent = internalUfds[currentElement]["parent"];
104 | }
105 | internalUfds[key]["parent"] = currentParent;
106 |
107 | return currentParent;
108 | }
109 |
110 | this.unionSet = function(firstKey, secondKey){
111 | if(internalUfds[firstKey] == null || internalUfds[secondKey] == null) return false;
112 | if(self.isSameSet(firstKey,secondKey)) return true;
113 |
114 | var firstSet = self.findSet(firstKey);
115 | var secondSet = self.findSet(secondKey);
116 |
117 | if(internalUfds[firstSet]["rank"] > internalUfds[secondSet]["rank"]){
118 | internalUfds[firstSet]["parent"] = secondSet;
119 | internalUfds[secondSet]["rank"]++;
120 | }
121 |
122 | else{
123 | internalUfds[secondSet]["parent"] = firstSet;
124 | internalUfds[firstSet]["rank"]++;
125 | }
126 | }
127 |
128 | this.isSameSet = function(firstKey, secondKey){
129 | if(internalUfds[firstKey] == null || internalUfds[secondKey] == null) return false;
130 |
131 | return self.findSet(firstKey) == self.findSet(secondKey);
132 | }
133 | }
--------------------------------------------------------------------------------
/css/list.css:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 |
4 | #createfixedsize-input input {
5 | width: 25px;
6 | padding: 8px;
7 | margin-right: 2px;
8 | float:left;
9 | }
10 |
11 | #createfixedsize-go p
12 | {
13 | padding: 8px 8px 7px;
14 | float:right;
15 | }
16 |
17 | #createuserdefined-input input {
18 | width: 75px;
19 | padding: 8px;
20 | margin-right: 2px;
21 | float:left;
22 | }
23 |
24 | #createuserdefined-go p
25 | {
26 | padding: 8px 8px 7px;
27 | float:right;
28 | }
29 |
30 | #insertkth-input input {
31 | width: 25px;
32 | padding: 8px;
33 | margin-right: 2px;
34 | float:left;
35 | }
36 | #insertkth-go p
37 | {
38 | padding: 8px 8px 7px;
39 | float:right;
40 | }
41 |
42 | #inserthead-input input {
43 | width: 25px;
44 | padding: 8px;
45 | margin-right: 2px;
46 | float:left;
47 | }
48 | #inserthead-go p
49 | {
50 | padding: 8px 8px 7px;
51 | float:right;
52 | }
53 |
54 | #inserttail-input input {
55 | width: 25px;
56 | padding: 8px;
57 | margin-right: 2px;
58 | float:left;
59 | }
60 | #inserttail-go p
61 | {
62 | padding: 8px 8px 7px;
63 | float:right;
64 | }
65 |
66 | #removekth-input input {
67 | width: 25px;
68 | padding: 8px;
69 | margin-right: 2px;
70 | float:left;
71 | }
72 | #removekth-go p
73 | {
74 | padding: 8px 8px 7px;
75 | float:right;
76 | }
77 |
78 |
79 |
80 | /*all action pullout (input) stuff here*/
81 | .create { bottom: 146px; }
82 | #create-random p { padding: 7px 8px 8px; }
83 | #create-input input {
84 | width: 25px;
85 | padding: 10px 8px 5px;
86 | }
87 | #create-input-arr input {
88 | width: 100px;
89 | padding: 10px 8px 5px;
90 | }
91 | #create-random-sorted p { padding: 7px 8px 8px; }
92 | #create-random-fixed-size p { padding: 7px 8px 8px; }
93 | #create-from-arr p { padding: 7px 8px 8px; }
94 | #create-err { padding: 8px 0px 7px; } /* currently unused*/
95 |
96 | .search { bottom: 119px; }
97 | #find-min p { padding: 5px 8px; }
98 | #find-max p { padding: 5px 8px; }
99 | #search-input input {
100 | width: 25px;
101 | padding: 6px 8px 5px;
102 | }
103 | #search-peek-front p { padding: 5px 8px; }
104 | #search-peek-back p { padding: 5px 8px; }
105 | #search-go p { padding: 5px 8px; }
106 | #search-err { padding: 5px 0px; } /* currently unused*/
107 |
108 | .insert { bottom: 92px; }
109 | #insert-head p { padding: 5px 8px; }
110 | #insert-tail p { padding: 5px 8px; }
111 | #insert-kth p { padding: 5px 8px; }
112 | #insert-input-kth input {
113 | width: 50px;
114 | padding: 6px 8px 5px;
115 | }
116 | #insert-input input {
117 | width: 140px;
118 | padding: 6px 8px 5px;
119 | }
120 | #insert-go p { padding: 5px 8px; }
121 | #insert-err { padding: 5px 0px; }
122 | #pushtop-input input{
123 | width: 25px;
124 | padding: 6px 8px 5px;
125 | }
126 | #pushtop-go p { padding: 5px 8px; }
127 | #pushtop-err { padding: 5px 0px; }
128 | #enqueueback-input input{
129 | width: 25px;
130 | padding: 6px 8px 5px;
131 | }
132 | #enqueueback-go p { padding: 5px 8px; }
133 | #enqueueback-err { padding: 5px 0px; }
134 |
135 | #insert-deque-input input{
136 | width: 25px;
137 | padding: 6px 8px 5px;
138 | }
139 | #insert-deque-front p { padding: 5px 8px; }
140 | #insert-deque-back { padding: 5px 8px; }
141 | #insert-deque-err { padding: 5px 0px; }
142 |
143 | .remove { bottom: 65px; }
144 | #remove-head p { padding: 5px 8px; }
145 | #remove-tail p { padding: 5px 8px; }
146 | #remove-kth p { padding: 5px 8px; }
147 | #remove-input-kth input {
148 | width: 50px;
149 | padding: 6px 8px 5px;
150 | }
151 | #remove-input input {
152 | width: 140px;
153 | padding: 6px 8px 5px;
154 | }
155 | #remove-deque-front p { padding: 5px 8px; }
156 | #remove-deque-back p { padding: 5px 8px; }
157 | #remove-go p { padding: 5px 8px; } /* currently unused*/
158 | #remove-err { padding: 5px 0px; } /* currently unused*/
159 |
160 | .inorder { bottom: 60px; }
161 | #inorder-err { padding: 8px 0px 7px; } /* currently unused*/
162 |
163 | /*all tutorial stuff here*/
164 | #list-tutorial-1 {
165 | top: 60px;
166 | left: 60px;
167 | width: 250px;
168 | }
169 | #list-tutorial-2 {
170 | top: 60px;
171 | left: 220px;
172 | width: 230px;
173 | }
174 | #list-tutorial-2:before {
175 | bottom: 100%;
176 | left: 50%;
177 | border-bottom-color: black;
178 | margin-left: -10px;
179 | }
180 | #list-tutorial-3 {
181 | bottom: 200px;
182 | left: 60px;
183 | width: 220px;
184 | }
185 | #list-tutorial-3:before {
186 | top: 100%;
187 | left: 20%;
188 | border-top-color: black;
189 | margin-left: -10px;
190 | }
191 | #list-tutorial-4 {
192 | top: 200px;
193 | left: 50%;
194 | width: 200px;
195 | margin-left: -110px;
196 | }
197 | #list-tutorial-4:before {
198 | bottom: 100%;
199 | left: 50%;
200 | border-bottom-color: black;
201 | margin-left: -10px;
202 | }
203 | #list-tutorial-5 {
204 | bottom: 335px;
205 | right: 150px;
206 | width: 200px;
207 | }
208 | #list-tutorial-5:before {
209 | top: 100%;
210 | left: 20%;
211 | border-top-color: black;
212 | margin-left: -10px;
213 | }
214 | #list-tutorial-6 {
215 | bottom: 275px;
216 | right: 170px;
217 | width: 180px;
218 | }
219 | #list-tutorial-6:before {
220 | top: 100%;
221 | left: 20%;
222 | border-top-color: black;
223 | margin-left: -10px;
224 | }
225 | #list-tutorial-7 {
226 | bottom: 70px;
227 | left: 50%;
228 | width: 220px;
229 | margin-left: -120px;
230 | }
231 | #list-tutorial-7:before {
232 | top: 100%;
233 | left: 50%;
234 | border-top-color: black;
235 | margin-left: -10px;
236 | }
237 | #list-tutorial-8 {
238 | top: 70px;
239 | right: 60px;
240 | width: 150px;
241 | }
242 | #list-tutorial-8:before {
243 | bottom: 100%;
244 | left: 50%;
245 | border-bottom-color: black;
246 | margin-left: -10px;
247 | }
--------------------------------------------------------------------------------
/js/common.js:
--------------------------------------------------------------------------------
1 | //surprise colour!
2 | //Referenced to in home.js and viz.js also
3 | var colourArray = ["#52bc69", "#ed5a7d", "#2ebbd1", "#d9513c", "#fec515", "#4b65ba", "#ff8a27", "#a7d41e"];
4 | //green, pink, blue, red, yellow, indigo, orange, lime
5 | //var colourArray = ["#15a346", "#ed5a7d", "#23b1bf", "#ce5226", "#deb003", "#574882", "#ff8a27", "#a7d41e"];
6 |
7 | function getColours() {
8 | var generatedColours = new Array();
9 | while(generatedColours.length < 4) {
10 | var n = (Math.floor(Math.random()*colourArray.length));
11 | if($.inArray(n, generatedColours) == -1) {
12 | generatedColours.push(n);
13 | }
14 | }
15 | return generatedColours;
16 | }
17 | var generatedColours = getColours();
18 | var surpriseColour = colourArray[generatedColours[0]];
19 | var colourTheSecond = colourArray[generatedColours[1]];
20 | var colourTheThird = colourArray[generatedColours[2]];
21 | var colourTheFourth = colourArray[generatedColours[3]];
22 |
23 | $( document ).ready(function() {
24 |
25 | $('#about').html('
About the project Motivation VisualGo was conceptualised in 2011 by Dr Steven Halim as a tool to help his students better understand data structures and algorithms, by allowing them to learn the basics on their own and at their own pace. Together with two of his students from the National University of Singapore, a series of visualisations were developed and consolidated, from simple sorting algorithms to complex graph data structures.
Though specifically designed for the use of NUS students taking various data structure and algorithm classes (CS1020, CS2010, CS2020, and CS3233), as advocators of online learning, we hope that curious minds around the world will find these visualisations useful as well.
Ongoing developments VisualGo is an ongoing project, and more complex visualisations are still being developed. Originally developed using HTML5 Canvas, we are currently redesigning the site to harness the power of Scalable Vector Graphics (SVG) instead. An automated testing system is also in the works.
Publications This work has been presented briefly at the CLI Workshop at the ACM ICPC World Finals 2012 (Poland, Warsaw) and at the IOI Conference at IOI 2012 (Sirmione-Montichiari, Italy).
');
26 |
27 | $('#team').html('The team Project leader Dr Steven Halim , Lecturer, SoC, NUS
Initial Programmers Koh Zi Chun Victor Loh Bo Huai
Past Final Year Project students Phan Thi Quynh Trang Peter Phandi Albert Millardo Tjindradinata
Present Final Year Project students Nguyen Hoang DuyRose Marie Tan Zhao Yun Ivan Reinaldo
Advisor Felix Halim
');
28 |
29 | $('#termsofuse').html('Terms of use If you are a data structure and algorithm teacher, you are allowed to use this website for your classes. You can take screen shots from this website and use the screen shots elsewhere as long as you cite this website as a reference.
');
30 |
31 | $('.colour').css("color", surpriseColour); //name
32 | $('h4').css("background-color", surpriseColour); //about, contact us etc. button background
33 |
34 | //title
35 | $('#title a').click(function() {
36 | $('#title a').removeClass('selected-viz');
37 | $(this).addClass('selected-viz');
38 | });
39 |
40 | //overlays stuff
41 | $('#trigger-about').click(function(){
42 | $('#dark-overlay').fadeIn(function(){
43 | $('#about').fadeIn();
44 | });
45 | });
46 | $('#trigger-team').click(function(){
47 | $('#dark-overlay').fadeIn(function(){
48 | $('#team').fadeIn();
49 | });
50 | });
51 | $('#trigger-terms').click(function(){
52 | $('#dark-overlay').fadeIn(function(){
53 | $('#termsofuse').fadeIn();
54 | });
55 | });
56 |
57 | $('.close-overlay').click(function() {
58 | $('.overlays').fadeOut(function(){
59 | $('#dark-overlay').fadeOut();
60 | });
61 | });
62 |
63 | //facebook login stuff
64 | /*
65 | $('#fb-login').hide();
66 | window.fbAsyncInit = function() {
67 | FB.init({
68 | appId : '192228707604746', // App ID
69 | channelUrl : '//www.rosemarietan.com/fyp/channel.html', // Channel File
70 | status : true, // check login status
71 | cookie : true, // enable cookies to allow the server to access the session
72 | xfbml : true // parse XFBML
73 | });
74 |
75 | // Here we subscribe to the auth.authResponseChange JavaScript event. This event is fired
76 | // for any authentication related change, such as login, logout or session refresh. This means that
77 | // whenever someone who was previously logged out tries to log in again, the correct case below
78 | // will be handled.
79 | FB.Event.subscribe('auth.authResponseChange', function(response) {
80 | // Here we specify what we do with the response anytime this event occurs.
81 | if (response.status === 'connected') {
82 | FB.api('/me', function(resp) {
83 | $('#fb-login').show();
84 | $('#fb-login').html(resp.name);
85 | $('#fb-login').attr('href', resp.link);
86 | $('#trick').hide();
87 | });
88 | } else if (response.status === 'not_authorized') {
89 | FB.login();
90 | } else {
91 | FB.login();
92 | }
93 | });
94 | };*/
95 |
96 | });
--------------------------------------------------------------------------------
/css/viz.css:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | html { overflow-y: auto; } /*override common.css*/
4 |
5 | /*top-bar right*/
6 | #mode-menu {
7 | z-index: 1001;
8 | float: right;
9 | background: black;
10 | font-size: 14px;
11 | width: 160px;
12 | position: relative;
13 | }
14 | #mode-button { cursor: pointer; padding: 11px 15px; }
15 | #mode-button img { /*down arrow*/
16 | position: absolute;
17 | top: 18px;
18 | right: 15px;
19 | display: inline-block;
20 | }
21 | #other-modes {
22 | position: absolute;
23 | width: 160px;
24 | background: black;
25 | display: none;
26 | }
27 | #other-modes a {
28 | display: block;
29 | padding: 8px 15px;
30 | }
31 |
32 | #left-bar {
33 | position: fixed;
34 | top: 0px;
35 | left: 0px;
36 | height: 100%;
37 | width: 40px;
38 | background: black;
39 | z-index: -1;
40 | }
41 |
42 | #right-bar {
43 | position: fixed;
44 | top: 0px;
45 | right: 0px;
46 | height: 100%;
47 | width: 40px;
48 | background: black;
49 | z-index: -2;
50 | }
51 |
52 | #bottom-bar { /*overwrite common.css*/
53 | position: fixed;
54 | bottom: 0px;
55 | box-sizing: border-box;
56 | -moz-box-sizing:border-box; /* Firefox */
57 | width: 100%;
58 | }
59 |
60 | #speed-control {
61 | position: fixed;
62 | left: 40px;
63 | bottom: 15px;
64 | color: white;
65 | font-size: 11px;
66 | line-height: 100%;
67 | }
68 | #speed-control .ui-slider {
69 | position: relative;
70 | display: inline-block;
71 | margin: 0px 10px;
72 | border-left: 5px solid #777;
73 | border-right: 5px solid #777;
74 | background-color: #777;
75 | height: 6px;
76 | width: 70px;
77 | }
78 | #speed-control .ui-slider-handle {
79 | position: absolute;
80 | margin-left: -5px;
81 | border: none;
82 | outline: none;
83 | background-color: white;
84 | height: 6px;
85 | width: 10px;
86 | }
87 |
88 | #media-controls {
89 |
90 | }
91 | .media-control-button {
92 | cursor: pointer;
93 | position: fixed;
94 | opacity: 0.75;
95 | }
96 | .media-control-button:hover {
97 | opacity: 1.0;
98 | }
99 | .media-control-button img {
100 | display: inline-block;
101 | }
102 | #go-to-end {
103 | bottom: 10px;
104 | left: 50%;
105 | margin-left: -205px; /*20px space*/
106 | }
107 | #next {
108 | bottom: 10px;
109 | left: 50%;
110 | margin-left: -237px;
111 | }
112 | #pause, #play {
113 | bottom: 8px;
114 | left: 50%;
115 | margin-left: -273px;
116 | opacity: 1.0;
117 | }
118 | #previous {
119 | bottom: 10px;
120 | left: 50%;
121 | margin-left: -305px;
122 | }
123 | #go-to-beginning {
124 | bottom: 10px;
125 | left: 50%;
126 | margin-left: -335px;
127 | }
128 | #stop {
129 | bottom: 10px;
130 | left: 50%;
131 | margin-left: 195px;
132 | opacity: 1.0;
133 | }
134 | #progress-bar.ui-slider {
135 | bottom: 17px;
136 | left: 50%;
137 | margin-left: -175px;
138 | background-color: #777;
139 | height: 6px;
140 | width: 350px;
141 | opacity: 1.0;
142 | }
143 | #progress-bar .ui-slider-handle {
144 | position: absolute;
145 | border: none;
146 | outline: none;
147 | background-color: #fff;
148 | height: 6px;
149 | width: 1px;
150 | }
151 | #progress-bar .ui-slider-range {
152 | position: absolute;
153 | height: 6px;
154 | }
155 |
156 | #viz {
157 | width: 100%;
158 | text-align: center;
159 | overflow: hidden;
160 | padding-top: 20px;
161 | -webkit-user-select: none;
162 | -moz-user-select: none;
163 | -ms-user-select: none;
164 | -o-user-select: none;
165 | user-select: none;
166 | }
167 |
168 | .execAction { cursor: pointer; }
169 | .execAction p:hover { background: black; color: white; }
170 |
171 | .panel {
172 | position: fixed;
173 | color: white;
174 | width: 0px;
175 | overflow: hidden;
176 | background: #777;
177 | white-space: nowrap;
178 | }
179 | .panel-hide { position: fixed; cursor: pointer; background: #777; }
180 |
181 | #current-action {
182 | bottom: 320px;
183 | right: 60px;
184 | width: 400px;
185 | display: none;
186 | background: none;
187 | overflow: normal;
188 | white-space: normal;
189 | text-align: right;
190 | }
191 | #current-action p{ color: black; font-weight: bold; font-size: 20px; }
192 |
193 | #actions {
194 | bottom: 60px;
195 | left: 43px;
196 | }
197 | #actions p { padding: 5px 10px; cursor: pointer; }
198 | #actions p:hover { background: black; color: white;}
199 | #actions p:first-of-type { padding-top: 10px; }
200 | #actions p:last-of-type { padding-bottom: 10px; }
201 | #actions-hide {
202 | bottom: 60px;
203 | left: 0px;
204 | padding-left: 14px;
205 | padding-right: 15px;
206 | }
207 | .action-menu-pullout { display: none; position: fixed; }
208 | .action-menu-pullout input { background: black; color: white; }
209 | .new-menu-option { margin-left: 3px; }
210 | .err { color: red; margin-left: 6px; }
211 |
212 | #status {
213 | bottom: 255px;
214 | right: 43px;
215 | height: 54px;
216 | white-space: normal;
217 | }
218 | #status p { padding: 10px; }
219 | #status-hide {
220 | bottom: 255px;
221 | right: 0px;
222 | padding: 19px 14px 19px 15px;
223 | transform: rotate(180deg);
224 | -ms-transform: rotate(180deg); /* IE 9 */
225 | -webkit-transform: rotate(180deg); /* Safari and Chrome */
226 | -o-transform: rotate(180deg); /* Opera */
227 | -moz-transform: rotate(180deg); /* Firefox */
228 | }
229 |
230 | #codetrace {
231 | bottom: 60px;
232 | right: 43px;
233 | height: 192px;
234 | font-family: "Courier New", Courier, monospace;
235 | }
236 | #codetrace p { padding: 5px 10px; }
237 | #codetrace p.highlighted { background: black; }
238 | #codetrace-hide {
239 | bottom: 60px;
240 | right: 0px;
241 | padding: 88px 14px 88px 15px;
242 | transform: rotate(180deg);
243 | -ms-transform: rotate(180deg); /* IE 9 */
244 | -webkit-transform: rotate(180deg); /* Safari and Chrome */
245 | -o-transform: rotate(180deg); /* Opera */
246 | -moz-transform: rotate(180deg); /* Firefox */
247 | }
248 |
249 | .rotateRight {
250 | transform: rotate(180deg);
251 | -ms-transform: rotate(180deg); /* IE 9 */
252 | -webkit-transform: rotate(180deg); /* Safari and Chrome */
253 | -o-transform: rotate(180deg); /* Opera */
254 | -moz-transform: rotate(180deg); /* Firefox */
255 |
256 | /* if you want to do this move with animate use transition */
257 | transition: .5s;
258 | -moz-transition: .5s; /* Firefox 4 */
259 | -webkit-transition: .5s; /* Safari and Chrome */
260 | -o-transition: .5s; /* Opera */
261 | }
262 | .rotateLeft {
263 | transform: rotate(0deg);
264 | -ms-transform: rotate(0deg); /* IE 9 */
265 | -webkit-transform: rotate(0deg); /* Safari and Chrome */
266 | -o-transform: rotate(0deg); /* Opera */
267 | -moz-transform: rotate(0deg); /* Firefox */
268 |
269 | /* if you want to do this move with animate use transition */
270 | transition: .5s;
271 | -moz-transition: .5s; /* Firefox 4 */
272 | -webkit-transition: .5s; /* Safari and Chrome */
273 | -o-transition: .5s; /* Opera */
274 | }
275 |
276 | /*tutorial stuff*/
277 | .tutorial-dialog {
278 | position: fixed;
279 | background: black;
280 | color: white;
281 | padding: 12px;
282 | border-radius: 3px;
283 | display: none;
284 | line-height: 150%;
285 | }
286 | .tutorial-dialog:before {
287 | content: " ";
288 | position: absolute;
289 | height: 0px;
290 | width: 0px;
291 | border-style:solid;
292 | border-width:10px;
293 | border-color: transparent;
294 | }
295 | .tutorial-next {
296 | position: absolute;
297 | bottom: -10px;
298 | right: -10px;
299 | padding: 3px 8px;
300 | background: #999;
301 | color: white;
302 | cursor: pointer;
303 | border-radius: 2px;
304 | }
305 | .tutorial-next img {
306 | display: inline-block;
307 | height: 9px;
308 | margin-left: 8px;
309 | }
--------------------------------------------------------------------------------
/js/viz.js:
--------------------------------------------------------------------------------
1 | var mode="exploration";
2 | var codetraceColor = 'white';
3 |
4 | //codetrace highlight
5 | function highlightLine(lineNumbers) { /* lineNumbers can be an array or a single number. Yay overloading! */
6 | $('#codetrace p').css('background-color', colourTheThird).css('color',codetraceColor);
7 | if (lineNumbers instanceof Array) {
8 | for(var i=0; i ');
171 |
172 | if(newMode=="Exploration Mode") {
173 | mode = "exploration";
174 | $('#status-hide').show();
175 | $('#codetrace-hide').show();
176 | $('#actions-hide').show();
177 | $('#status').show();
178 | $('#codetrace').show();
179 | $('#actions').show();
180 | $('.tutorial-dialog').hide();
181 | hideStatusPanel();
182 | hideCodetracePanel();
183 | showActionsPanel();
184 | /*} else if(newMode=="Training Mode") {
185 | mode = "training";
186 | $('#status').hide();
187 | $('#codetrace').hide();
188 | $('#actions').hide();
189 | $('#status-hide').hide();
190 | $('#codetrace-hide').hide();
191 | $('#actions-hide').hide();
192 | */
193 | } else if (newMode=="Tutorial Mode") {
194 | mode = "tutorial";
195 | $('#status-hide').show();
196 | $('#codetrace-hide').show();
197 | $('#actions-hide').show();
198 | $('#current-action').html("");
199 | $('#status').show();
200 | $('#codetrace').show();
201 | $('#actions').show();
202 | if(isPlaying) { stop(); }
203 | hideEntireActionsPanel();
204 | hideStatusPanel();
205 | hideCodetracePanel();
206 | $('.tutorial-dialog').first().fadeIn(500);
207 | }
208 | });
209 |
210 | //arrow buttons to show/hide panels
211 | $('#status-hide').click(function() {
212 | if(isStatusOpen) {
213 | hideStatusPanel();
214 | } else {
215 | showStatusPanel();
216 | }
217 | });
218 | $('#codetrace-hide').click(function() {
219 | if(isCodetraceOpen) {
220 | hideCodetracePanel();
221 | } else {
222 | showCodetracePanel();
223 | }
224 | });
225 | $('#actions-hide').click(function() {
226 | if(isActionsOpen) {
227 | hideEntireActionsPanel(); //must define hideEntireActionsPanel() function in vizname_actions.js file
228 | } else {
229 | showActionsPanel();
230 | }
231 | });
232 |
233 | //tutorial mode
234 | $('.tutorial-dialog .tutorial-next').click(function() {
235 | var vizname = $(this).parent().attr('id').split('-')[0];
236 | var nextNo = parseInt($(this).parent().attr('id').slice(-1))+1;
237 | var nextId = vizname+'-tutorial-'+nextNo;
238 | $(this).parent().fadeOut(500, function() {
239 | $('#'+nextId).fadeIn(500);
240 | });
241 | });
242 |
243 | });
--------------------------------------------------------------------------------
/js/graph_library/properties.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Contains visualization properties (SVG objects size, color, etc.)
3 | * Make sure to add the corresponding constants to the constant.js file
4 | */
5 |
6 | /*
7 | * Widget
8 | */
9 |
10 | const MAIN_SVG_WIDTH = 900;
11 | const MAIN_SVG_HEIGHT = 500;
12 | const PSEUDOCODE_SVG_WIDTH = 300;
13 | const PSEUDOCODE_SVG_HEIGHT = 400;
14 |
15 | /*
16 | * GraphVertexWidget
17 | */
18 |
19 | const graphVertexProperties = {
20 | "innerVertex":{
21 | "r": 14,
22 | "width":30,
23 | "height": 30,
24 | "stroke-width": 0,
25 | "default":{
26 | "fill": "#eee",
27 | "stroke": "#fff"
28 | },
29 | "normal_blue":{
30 | "fill": "#2ebbd1",
31 | "stroke": "#fff"
32 | },
33 | "highlighted":{
34 | "fill": "#ff8a27",
35 | "stroke": "#fff"
36 | },
37 | "highlighted_rect":{
38 | "fill": "#ff8a27",
39 | "stroke": "#fff"
40 | },
41 | "traversed":{
42 | "fill": "#eee",
43 | "stroke": "#fff"
44 | },
45 | "result":{
46 | "fill": "#f7e81e",
47 | "stroke": "#fff"
48 | },
49 | "rect": {
50 | "fill": "#eee",
51 | "stroke": "#fff"
52 | },
53 | "result_rect": {
54 | "fill": "#52bc69",
55 | "stroke": "#fff"
56 | },
57 | "greenFill":{
58 | "fill": "#52bc69",
59 | "stroke": "#fff"
60 | },
61 | "greenOutline":{
62 | "fill": "#eee",
63 | "stroke": "#fff"
64 | },
65 | "pinkFill":{
66 | "fill": "#ed5a7d",
67 | "stroke": "#fff"
68 | },
69 | "pinkOutline":{
70 | "fill": "#eee",
71 | "stroke": "#fff"
72 | },
73 | "blueFill":{
74 | "fill": "#2ebbd1",
75 | "stroke": "#fff"
76 | },
77 | "blueOutline":{
78 | "fill": "#eee",
79 | "stroke": "#fff"
80 | },
81 | "redFill":{
82 | "fill": "#d9513c",
83 | "stroke": "#fff"
84 | },
85 | "redOutline":{
86 | "fill": "#eee",
87 | "stroke": "#fff"
88 | },
89 | "greyFill":{
90 | "fill": "#cccccc",
91 | "stroke": "#fff"
92 | },
93 | "greyOutline":{
94 | "fill": "#eee",
95 | "stroke": "#fff"
96 | }
97 | },
98 | "outerVertex":{
99 | "r": 16,
100 | "width": 32,
101 | "height": 32,
102 | "stroke-width": 2,
103 | "default":{
104 | "fill": "#333",
105 | "stroke": "#333"
106 | },
107 | "normal_blue":{
108 | "fill": "#2ebbd1",
109 | "stroke": "#333"
110 | },
111 | "highlighted":{
112 | "fill": "#ff8a27",
113 | "stroke": "#ff8a27"
114 | },
115 | "highlighted_rect":{
116 | "fill": "#ff8a27",
117 | "stroke": "#333"
118 | },
119 | "traversed":{
120 | "fill": "#ff8a27",
121 | "stroke": "#ff8a27"
122 | },
123 | "result":{
124 | "fill": "#f7e81e",
125 | "stroke": "#f7e81e"
126 | },
127 | "rect":{
128 | "fill": "#333",
129 | "stroke": "#333"
130 | },
131 | "result_rect":{
132 | "fill": "#52bc69",
133 | "stroke": "#333"
134 | },
135 | "greenFill":{
136 | "fill": "#52bc69",
137 | "stroke": "#52bc69"
138 | },
139 | "greenOutline":{
140 | "fill": "#52bc69",
141 | "stroke": "#52bc69"
142 | },
143 | "pinkFill":{
144 | "fill": "#ed5a7d",
145 | "stroke": "#ed5a7d"
146 | },
147 | "pinkOutline":{
148 | "fill": "#ed5a7d",
149 | "stroke": "#ed5a7d"
150 | },
151 | "blueFill":{
152 | "fill": "#2ebbd1",
153 | "stroke": "#2ebbd1"
154 | },
155 | "blueOutline":{
156 | "fill": "#2ebbd1",
157 | "stroke": "#2ebbd1"
158 | },
159 | "redFill":{
160 | "fill": "#d9513c",
161 | "stroke": "#d9513c"
162 | },
163 | "redOutline":{
164 | "fill": "#d9513c",
165 | "stroke": "#d9513c"
166 | },
167 | "greyFill":{
168 | "fill": "#cccccc",
169 | "stroke": "#cccccc"
170 | },
171 | "greyOutline":{
172 | "fill": "#cccccc",
173 | "stroke": "#cccccc"
174 | }
175 | },
176 | "text":{
177 | "font-size": 16,
178 | "default":{
179 | "fill": "#333",
180 | "font-family": "'PT Sans', sans-serif",
181 | "font-weight": "bold",
182 | "text-anchor": "middle"
183 | },
184 | "normal_blue":{
185 | "fill": "#fff",
186 | "font-family": "'PT Sans', sans-serif",
187 | "font-weight": "bold",
188 | "text-anchor": "middle"
189 | },
190 | "highlighted":{
191 | "fill": "#fff",
192 | "font-family": "'PT Sans', sans-serif",
193 | "font-weight": "bold",
194 | "text-anchor": "middle"
195 | },
196 | "highlighted_rect":{
197 | "fill": "#fff",
198 | "font-family": "'PT Sans', sans-serif",
199 | "font-weight": "bold",
200 | "text-anchor": "left"
201 | },
202 | "traversed":{
203 | "fill": "#ff8a27",
204 | "font-family": "'PT Sans', sans-serif",
205 | "font-weight": "bold",
206 | "text-anchor": "middle"
207 | },
208 | "result":{
209 | "fill": "#fff",
210 | "font-family": "'PT Sans', sans-serif",
211 | "font-weight": "bold",
212 | "text-anchor": "middle"
213 | },
214 | "rect":{
215 | "fill": "#333",
216 | "font-family": "'PT Sans', sans-serif",
217 | "font-weight": "bold",
218 | "text-anchor": "left"
219 | },
220 | "result_rect":{
221 | "fill": "#fff",
222 | "font-family": "'PT Sans', sans-serif",
223 | "font-weight": "bold",
224 | "text-anchor": "left"
225 | },
226 | "greenFill":{
227 | "fill": "#fff",
228 | "font-family": "'PT Sans', sans-serif",
229 | "font-weight": "bold",
230 | "text-anchor": "middle"
231 | },
232 | "greenOutline":{
233 | "fill": "#52bc69",
234 | "font-family": "'PT Sans', sans-serif",
235 | "font-weight": "bold",
236 | "text-anchor": "middle"
237 | },
238 | "pinkFill":{
239 | "fill": "#fff",
240 | "font-family": "'PT Sans', sans-serif",
241 | "font-weight": "bold",
242 | "text-anchor": "middle"
243 | },
244 | "pinkOutline":{
245 | "fill": "#ed5a7d",
246 | "font-family": "'PT Sans', sans-serif",
247 | "font-weight": "bold",
248 | "text-anchor": "middle"
249 | },
250 | "blueFill":{
251 | "fill": "#fff",
252 | "font-family": "'PT Sans', sans-serif",
253 | "font-weight": "bold",
254 | "text-anchor": "middle"
255 | },
256 | "blueOutline":{
257 | "fill": "#2ebbd1",
258 | "font-family": "'PT Sans', sans-serif",
259 | "font-weight": "bold",
260 | "text-anchor": "middle"
261 | },
262 | "redFill":{
263 | "fill": "#fff",
264 | "font-family": "'PT Sans', sans-serif",
265 | "font-weight": "bold",
266 | "text-anchor": "middle"
267 | },
268 | "redOutline":{
269 | "fill": "#d9513c",
270 | "font-family": "'PT Sans', sans-serif",
271 | "font-weight": "bold",
272 | "text-anchor": "middle"
273 | },
274 | "greyFill":{
275 | "fill": "#fff",
276 | "font-family": "'PT Sans', sans-serif",
277 | "font-weight": "bold",
278 | "text-anchor": "middle"
279 | },
280 | "greyOutline":{
281 | "fill": "#cccccc",
282 | "font-family": "'PT Sans', sans-serif",
283 | "font-weight": "bold",
284 | "text-anchor": "middle"
285 | }
286 | }
287 | };
288 |
289 | /*
290 | * GraphEdgeWidget
291 | */
292 |
293 | const graphEdgeProperties = {
294 | "animateHighlightedPath":{
295 | "stroke": "#ff8a27",
296 | "stroke-width": 10
297 | },
298 | "path":{
299 | "stroke-width": 3,
300 | "default":{
301 | "stroke": "#333"
302 | },
303 | "highlighted":{
304 | "stroke": "#ff8a27"
305 | },
306 | "traversed":{
307 | "stroke": "#ff8a27"
308 | },
309 | "green":{
310 | "stroke": "#52bc69"
311 | },
312 | "pink":{
313 | "stroke": "#ed5a7d"
314 | },
315 | "blue":{
316 | "stroke": "#2ebbd1"
317 | },
318 | "red":{
319 | "stroke": "#d9513c"
320 | },
321 | "grey":{
322 | "stroke": "#cccccc"
323 | }
324 | },
325 | "weight":{
326 | "font-size": 16,
327 | "default":{
328 | "startOffset": "75%",
329 | "dy": -5,
330 | "fill": "#333",
331 | "font-family": "'PT Sans', sans-serif",
332 | "font-weight": "bold",
333 | "text-anchor": "middle"
334 | },
335 | "highlighted":{
336 | "startOffset": "75%",
337 | "dy": -5,
338 | "fill": "#ff8a27",
339 | "font-family": "'PT Sans', sans-serif",
340 | "font-weight": "bold",
341 | "text-anchor": "middle"
342 | },
343 | "traversed":{
344 | "startOffset": "75%",
345 | "dy": -5,
346 | "fill": "#ff8a27",
347 | "font-family": "'PT Sans', sans-serif",
348 | "font-weight": "bold",
349 | "text-anchor": "middle"
350 | },
351 | "green":{
352 | "startOffset": "75%",
353 | "dy": -5,
354 | "fill": "#52bc69",
355 | "font-family": "'PT Sans', sans-serif",
356 | "font-weight": "bold",
357 | "text-anchor": "middle"
358 | },
359 | "pink":{
360 | "startOffset": "75%",
361 | "dy": -5,
362 | "fill": "#ed5a7d",
363 | "font-family": "'PT Sans', sans-serif",
364 | "font-weight": "bold",
365 | "text-anchor": "middle"
366 | },
367 | "blue":{
368 | "startOffset": "75%",
369 | "dy": -5,
370 | "fill": "#2ebbd1",
371 | "font-family": "'PT Sans', sans-serif",
372 | "font-weight": "bold",
373 | "text-anchor": "middle"
374 | },
375 | "red":{
376 | "startOffset": "75%",
377 | "dy": -5,
378 | "fill": "#d9513c",
379 | "font-family": "'PT Sans', sans-serif",
380 | "font-weight": "bold",
381 | "text-anchor": "middle"
382 | },
383 | "grey":{
384 | "startOffset": "75%",
385 | "dy": -5,
386 | "fill": "#cccccc",
387 | "font-family": "'PT Sans', sans-serif",
388 | "font-weight": "bold",
389 | "text-anchor": "middle"
390 | }
391 | }
392 | }
393 |
394 | /*
395 | * marker.js
396 | * Currently this file doesn't exist; markers are placed in GraphEdgeWidget.js
397 | */
398 |
399 | const ARROW_MARKER_WIDTH = 3;
400 | const ARROW_MARKER_HEIGHT = 3;
401 | const ARROW_REFX = 9;
402 | const ARROW_FILL = "#333";
--------------------------------------------------------------------------------
/js/graph_library/GraphEdgeWidget.js:
--------------------------------------------------------------------------------
1 | // Defines ONE edge object
2 | // Direction of edge is a -> b
3 | // Set styles in properties.js and the CSS files!!!
4 |
5 | /*
6 | * Constants for "type":
7 | * EDGE_TYPE_UDE = UnDirected Edge
8 | * EDGE_TYPE_DE = Directed Edge
9 | * EDGE_TYPE_BDE = BiDirectional Edge
10 | */
11 |
12 | // Marker objects
13 |
14 | markerSvg.append("marker")
15 | .attr("id", "arrow")
16 | .attr("viewBox", "0 -5 10 10")
17 | .attr('refX', ARROW_REFX)
18 | .attr("markerWidth", ARROW_MARKER_WIDTH)
19 | .attr("markerHeight", ARROW_MARKER_HEIGHT)
20 | .attr("orient", "auto")
21 | .append("path")
22 | .attr("d", "M0,-5 L10,0 L0,5")
23 | .attr('fill', ARROW_FILL);
24 |
25 | markerSvg.append("marker")
26 | .attr("id", "backwardArrow")
27 | .attr("viewBox", "-10 -5 10 10")
28 | .attr('refX', -1*ARROW_REFX)
29 | .attr("markerWidth", ARROW_MARKER_WIDTH)
30 | .attr("markerHeight", ARROW_MARKER_HEIGHT)
31 | .attr("orient", "auto")
32 | .append("path")
33 | .attr("d", "M0,-5 L-10,0 L0,5")
34 | .attr('fill', ARROW_FILL);
35 |
36 | // GraphEdgeWidget object
37 | // TODO: Better implementation of edge weight
38 |
39 | var GraphEdgeWidget = function(graphVertexA, graphVertexB, edgeIdNumber, type, weight){
40 | if(weight == null || isNaN(weight)) weight = 1;
41 |
42 | var self = this;
43 |
44 | var defaultAnimationDuration = 250; // millisecond
45 |
46 | var line;
47 | var clickableArea;
48 | var weightText;
49 | var weightTextPath;
50 | var weightTextSpan
51 |
52 | // var vertexA = graphVertexA.getClassNumber();
53 | // var vertexB = graphVertexB.getClassNumber();
54 |
55 | var edgeGenerator = d3.svg.line()
56 | .x(function(d){return d.x;})
57 | .y(function(d){return d.y;})
58 | .interpolate("linear");
59 |
60 | var lineCommand = edgeGenerator(calculatePath());
61 | var initCommand = edgeGenerator([calculatePath()[0],calculatePath()[0]]);
62 |
63 | var attributeList = {
64 | "path":{
65 | "id": null,
66 | "class": null,
67 | "d": null,
68 | "stroke": null,
69 | "stroke-width": null
70 | },
71 | "weight":{
72 | "id": null,
73 | "startOffset": null,
74 | "dy": null,
75 | "fill": null,
76 | "font-family": null,
77 | "font-weight": null,
78 | "font-size": null,
79 | "text-anchor": null,
80 | "text": null
81 | }
82 | };
83 |
84 | updatePath();
85 | init();
86 |
87 | this.redraw = function(duration){
88 | draw(duration);
89 | }
90 |
91 | this.animateHighlighted = function(duration){
92 | if(duration == null || isNaN(duration)) duration = defaultAnimationDuration;
93 | if(duration <= 0) duration = 1;
94 |
95 | edgeSvg.append("path")
96 | .attr("id", "tempEdge" + line.attr("id"))
97 | .attr("stroke", graphEdgeProperties["animateHighlightedPath"]["stroke"])
98 | .attr("stroke-width", graphEdgeProperties["animateHighlightedPath"]["stroke-width"])
99 | .transition()
100 | .duration(duration)
101 | .each("start", function(){
102 | edgeSvg.select("#tempEdge" + line.attr("id"))
103 | .attr("d", initCommand);
104 | })
105 | .attr("d", lineCommand)
106 | .each("end", function(){
107 | line.attr("stroke", graphEdgeProperties["path"]["highlighted"]["stroke"])
108 | .attr("stroke-width", graphEdgeProperties["path"]["stroke-width"]);
109 |
110 | edgeSvg.select("#tempEdge" + line.attr("id"))
111 | .remove();
112 |
113 | draw(0);
114 | })
115 | }
116 |
117 | this.showEdge = function(){
118 | attributeList["path"]["d"] = lineCommand;
119 | attributeList["path"]["stroke-width"] = graphEdgeProperties["path"]["stroke-width"];
120 | }
121 |
122 | this.hideEdge = function(){
123 | // attributeList["path"]["stroke-width"] = 0;
124 | attributeList["path"]["d"] = initCommand;
125 | }
126 |
127 | this.showWeight = function(){
128 | attributeList["weight"]["font-size"] = graphEdgeProperties["weight"]["font-size"];
129 | }
130 |
131 | this.hideWeight = function(){
132 | attributeList["weight"]["font-size"] = 0;
133 | }
134 |
135 | this.stateEdge = function(stateName){
136 | var key;
137 |
138 | for(key in graphEdgeProperties["path"][stateName]){
139 | attributeList["path"][key] = graphEdgeProperties["path"][stateName][key];
140 | }
141 |
142 | for(key in graphEdgeProperties["weight"][stateName]){
143 | attributeList["weight"][key] = graphEdgeProperties["weight"][stateName][key];
144 | }
145 | }
146 |
147 | // Removes the edge (no animation)
148 | // If you want animation, hide & redraw the edge first, then call this function
149 | this.removeEdge = function(){
150 | graphVertexA.removeEdge(self);
151 | graphVertexB.removeEdge(self);
152 |
153 | line.remove();
154 | weightText.remove();
155 | }
156 |
157 | this.refreshPath = function(){
158 | var tempInit = initCommand;
159 |
160 | updatePath();
161 |
162 | if(attributeList["path"]["d"] == tempInit) attributeList["path"]["d"] = initCommand;
163 | else attributeList["path"]["d"] = lineCommand;
164 | }
165 |
166 | this.changeVertexA = function(newGraphVertexA){
167 | var edgeDrawn = false;
168 |
169 | if(attributeList["path"]["d"] == lineCommand) edgeDrawn = true;
170 |
171 | graphVertexA.removeEdge(self);
172 | graphVertexA = newGraphVertexA;
173 | // vertexA = graphVertexA.getClassNumber();
174 |
175 | updatePath();
176 |
177 | lineCommand = edgeGenerator(calculatePath());
178 | initCommand = edgeGenerator([calculatePath()[0]]);
179 |
180 | attributeList["path"]["d"] = initCommand;
181 |
182 | graphVertexA.addEdge(self);
183 |
184 | if(edgeDrawn) attributeList["path"]["d"] = lineCommand;
185 | }
186 |
187 | this.changeVertexB = function(newGraphVertexB){
188 | var edgeDrawn = false;
189 |
190 | if(attributeList["path"]["d"] == lineCommand) edgeDrawn = true;
191 |
192 | graphVertexB.removeEdge(self);
193 | graphVertexB = newGraphVertexB;
194 | // vertexB = graphVertexB.getClassNumber();
195 |
196 | updatePath();
197 |
198 | lineCommand = edgeGenerator(calculatePath());
199 | initCommand = edgeGenerator([calculatePath()[0]]);
200 |
201 | attributeList["path"]["d"] = initCommand;
202 |
203 | graphVertexB.addEdge(self);
204 |
205 | if(edgeDrawn) attributeList["path"]["d"] = lineCommand;
206 | }
207 |
208 | this.changeType = function(newType){
209 | type = newType;
210 |
211 | switch(type){
212 | case EDGE_TYPE_UDE:
213 | attributeList["path"]["class"] = "ude";
214 | break;
215 | case EDGE_TYPE_DE:
216 | attributeList["path"]["class"] = "de";
217 | break;
218 | case EDGE_TYPE_BDE:
219 | attributeList["path"]["class"] = "bde";
220 | break;
221 | default:
222 | break;
223 | }
224 | }
225 |
226 | this.changeWeight = function(newWeight){
227 | weight = newWeight;
228 | attributeList["weight"]["text"] = weight;
229 | }
230 |
231 | this.getVertex = function(){
232 | return [graphVertexA, graphVertexB];
233 | }
234 |
235 | this.getAttributes = function(){
236 | return deepCopy(attributeList["path"]);
237 | }
238 |
239 | this.getType = function(){
240 | return type;
241 | }
242 |
243 | // Helper Functions
244 |
245 | function init(){
246 | attributeList["path"]["id"] = "e" + edgeIdNumber;
247 | attributeList["path"]["d"] = initCommand;
248 | attributeList["path"]["stroke"] = graphEdgeProperties["path"]["default"]["stroke"];
249 | attributeList["path"]["stroke-width"] = graphEdgeProperties["path"]["default"]["stroke-width"];
250 |
251 | switch(type){
252 | case EDGE_TYPE_UDE:
253 | attributeList["path"]["class"] = "ude";
254 | break;
255 | case EDGE_TYPE_DE:
256 | attributeList["path"]["class"] = "de";
257 | break;
258 | case EDGE_TYPE_BDE:
259 | attributeList["path"]["class"] = "bde";
260 | break;
261 | default:
262 | break;
263 | }
264 |
265 | attributeList["weight"]["id"] = "ew" + edgeIdNumber;
266 | attributeList["weight"]["startOffset"] = graphEdgeProperties["weight"]["default"]["startOffset"];
267 | attributeList["weight"]["dy"] = graphEdgeProperties["weight"]["default"]["dy"];
268 | attributeList["weight"]["fill"] = graphEdgeProperties["weight"]["default"]["fill"];
269 | attributeList["weight"]["font-family"] = graphEdgeProperties["weight"]["default"]["font-family"];
270 | attributeList["weight"]["font-size"] = 0;
271 | attributeList["weight"]["font-weight"] = graphEdgeProperties["weight"]["default"]["font-weight"];
272 | attributeList["weight"]["text-anchor"] = graphEdgeProperties["weight"]["default"]["text-anchor"];
273 | attributeList["weight"]["text"] = weight;
274 |
275 | line = edgeSvg.append("path");
276 |
277 | line.attr("id", attributeList["path"]["id"])
278 | .attr("class", attributeList["path"]["class"]);
279 |
280 | try {
281 | if (attributeList["path"]["d"] != "MNaN,NaNLNaN,NaN")
282 | line.attr("d", attributeList["path"]["d"])
283 | .attr("stroke", attributeList["path"]["stroke"])
284 | .attr("stroke-width", attributeList["path"]["stroke-width"]);
285 | } catch(err) {}
286 | weightText = edgeWeightSvg.append("text");
287 |
288 | weightText.attr("id", attributeList["weight"]["id"]);
289 |
290 | weightText.attr("fill", attributeList["weight"]["fill"])
291 | .attr("font-family", attributeList["weight"]["font-family"])
292 | .attr("font-size", attributeList["weight"]["font-size"])
293 | //.attr("font-size", 16)
294 | .attr("font-weight", attributeList["weight"]["font-weight"])
295 | .attr("text-anchor", attributeList["weight"]["text-anchor"]);
296 |
297 | weightTextPath = weightText.append("textPath")
298 | .attr("xlink:href", function(){
299 | return "#" + attributeList["path"]["id"];
300 | })
301 | .attr("startOffset", attributeList["weight"]["startOffset"]);
302 |
303 | weightTextSpan = weightTextPath.append("tspan")
304 | .attr("dy", attributeList["weight"]["dy"])
305 | .text(function(){
306 | return attributeList["weight"]["text"];
307 | });
308 | }
309 |
310 | function cxA(){
311 | if (graphVertexA)
312 | return parseFloat(graphVertexA.getAttributes()["outerVertex"]["cx"]);
313 | }
314 |
315 | function cyA(){
316 | if (graphVertexA)
317 | return parseFloat(graphVertexA.getAttributes()["outerVertex"]["cy"]);
318 | }
319 |
320 | function rA(){
321 | if (graphVertexA)
322 | return parseFloat(graphVertexA.getAttributes()["outerVertex"]["r"]);
323 | }
324 |
325 | function cxB(){
326 | if (graphVertexA)
327 | return parseFloat(graphVertexB.getAttributes()["outerVertex"]["cx"]);
328 | }
329 |
330 | function cyB(){
331 | if (graphVertexA)
332 | return parseFloat(graphVertexB.getAttributes()["outerVertex"]["cy"]);
333 | }
334 |
335 | function rB(){
336 | if (graphVertexA)
337 | return parseFloat(graphVertexB.getAttributes()["outerVertex"]["r"]);
338 | }
339 |
340 | function calculatePath(){
341 | var x1 = cxA(), y1 = cyA();
342 | var x2 = cxB(), y2 = cyB();
343 |
344 | var pts = getVertexLineIntersectionPoint(x1, y1, x2, y2, rA(), x1, y1);
345 | var pts2 = getVertexLineIntersectionPoint(x1, y1, x2, y2, rB(), x2, y2);
346 | var min = 5000;
347 | var save1 = 0, save2 = 0;
348 | for (var i=1; i<=3; i+=2)
349 | for (var j=1; j<=3; j+=2)
350 | {
351 | var d = Math.sqrt((pts[i-1]-pts2[j-1])*(pts[i-1]-pts2[j-1]) + (pts[i] - pts2[j])*(pts[i] - pts2[j]));
352 | if (d < min) {
353 | min = d;
354 | save1 = i; save2 = j;
355 | }
356 | }
357 |
358 | var beginPoint = {"x": pts[save1-1], "y": pts[save1]};
359 | var endPoint = {"x": pts2[save2-1], "y": pts2[save2]};
360 |
361 | return [beginPoint, endPoint];
362 | }
363 |
364 | function getVertexLineIntersectionPoint(x1, y1, x2, y2, r, cx, cy) {
365 | var baX = x2 - x1; //pointB.x - pointA.x;
366 | var baY = y2 - y1; //pointB.y - pointA.y;
367 | var caX = cx - x1; //center.x - pointA.x;
368 | var caY = cy - y1; //center.y - pointA.y;
369 |
370 | var a = baX * baX + baY * baY;
371 | var bBy2 = baX * caX + baY * caY;
372 | var c = caX * caX + caY * caY - r * r;
373 |
374 | var pBy2 = bBy2 / a;
375 | var q = c / a;
376 |
377 | var disc = pBy2 * pBy2 - q;
378 | var tmpSqrt = Math.sqrt(disc);
379 | var abScalingFactor1 = -pBy2 + tmpSqrt;
380 | var abScalingFactor2 = -pBy2 - tmpSqrt;
381 |
382 | var r_x1 = x1 - baX * abScalingFactor1;
383 | var r_y1 = y1 - baY * abScalingFactor1
384 | //Point p1 = new Point(pointA.x - baX * abScalingFactor1, pointA.y
385 | // - baY * abScalingFactor1);
386 | var r_x2 = x1 - baX * abScalingFactor2;
387 | var r_y2 = y1 - baY * abScalingFactor2
388 |
389 | //Point p2 = new Point(pointA.x - baX * abScalingFactor2, pointA.y
390 | // - baY * abScalingFactor2);
391 | var res = new Array();
392 | res[0] = r_x1;
393 | res[1] = r_y1;
394 | res[2] = r_x2;
395 | res[3] = r_y2 ;
396 | return res;
397 | }
398 |
399 | function draw(dur){
400 | if(dur == null || isNaN(dur)) dur = defaultAnimationDuration;
401 | if(dur <= 0) dur = 1;
402 |
403 | line.attr("class", attributeList["path"]["class"]);
404 |
405 | line.transition()
406 | .duration(dur)
407 | .attr("d", attributeList["path"]["d"])
408 | .attr("stroke", attributeList["path"]["stroke"])
409 | .attr("stroke-width", attributeList["path"]["stroke-width"])
410 | .style("marker-start", function(){
411 | if(attributeList["path"]["d"] == initCommand) return null;
412 | if(attributeList["path"]["class"] == "bde") return "url(#backwardArrow)";
413 | return null;
414 | })
415 | .style("marker-end", function(){
416 | if(attributeList["path"]["d"] == initCommand) return null;
417 | if(attributeList["path"]["class"] == "de" || attributeList["path"]["class"] == "bde") return "url(#arrow)";
418 | return null;
419 | });
420 |
421 | weightText.transition()
422 | .duration(dur)
423 | .attr("fill", attributeList["weight"]["fill"])
424 | .attr("font-family", attributeList["weight"]["font-family"])
425 | .attr("font-size", attributeList["weight"]["font-size"])
426 | .attr("font-weight", attributeList["weight"]["font-weight"])
427 | .attr("text-anchor", attributeList["weight"]["text-anchor"])
428 | .attr("text-decoration", "underline");
429 |
430 | weightTextSpan.transition()
431 | .duration(dur)
432 | .text(function(){
433 | return attributeList["weight"]["text"];
434 | });
435 | }
436 |
437 | function updatePath(){
438 | lineCommand = edgeGenerator(calculatePath());
439 | initCommand = edgeGenerator([calculatePath()[0],calculatePath()[0]]);
440 | }
441 | }
--------------------------------------------------------------------------------
/js/graph_library/GraphVertexWidget.js:
--------------------------------------------------------------------------------
1 | // Defines ONE vertex object
2 | // Set styles in properties.js and the CSS files!!!
3 |
4 | var GraphVertexWidget = function(cx, cy, vertexShape, vertexText, vertexClassNumber){
5 | var self = this;
6 | var defaultAnimationDuration = 250; // millisecond
7 |
8 | var innerVertex;
9 | var outerVertex;
10 | var text;
11 |
12 | var textYaxisOffset = graphVertexProperties["text"]["font-size"]/3;
13 |
14 | var attributeList = {
15 | "innerVertex": {
16 | "class": null,
17 | "cx": null,
18 | "cy": null,
19 | "x": null,
20 | "y": null,
21 | "fill": null,
22 | "r": null,
23 | "width": null,
24 | "height": null,
25 | "stroke": null,
26 | "stroke-width": null
27 | },
28 |
29 | "outerVertex":{
30 | "class": null,
31 | "cx": null,
32 | "cy": null,
33 | "x": null,
34 | "y": null,
35 | "fill": null,
36 | "r": null,
37 | "width": null,
38 | "height": null,
39 | "stroke": null,
40 | "stroke-width": null
41 | },
42 |
43 | "text":{
44 | "class": null,
45 | "x": null,
46 | "y": null,
47 | "fill": null,
48 | "font-family": null,
49 | "font-weight": null,
50 | "font-size": null,
51 | "text-anchor": null,
52 | "text": null
53 | }
54 | }
55 |
56 | // JS object with IDs of all edges connected to this vertex as the key and boolean as the value
57 | // Everytime an edge is added, the value is set to true
58 | // Everytime an edge is deleted, the value is set to null
59 | var edgeList = {};
60 |
61 | init();
62 |
63 | this.redraw = function(duration){
64 | draw(duration);
65 | }
66 |
67 | // Specifies the duration of the animation in milliseconds
68 | // If unspecified or illegal value, default duration applies.
69 | this.showVertex = function(){
70 | attributeList["outerVertex"]["r"] = graphVertexProperties["outerVertex"]["r"];
71 | attributeList["outerVertex"]["width"] = graphVertexProperties["outerVertex"]["width"];
72 | attributeList["outerVertex"]["height"] = graphVertexProperties["outerVertex"]["height"];
73 | attributeList["outerVertex"]["stroke-width"] = graphVertexProperties["outerVertex"]["stroke-width"];
74 |
75 | attributeList["innerVertex"]["r"] = graphVertexProperties["innerVertex"]["r"];
76 | attributeList["innerVertex"]["width"] = graphVertexProperties["innerVertex"]["width"];
77 | attributeList["innerVertex"]["height"] = graphVertexProperties["innerVertex"]["height"];
78 | attributeList["innerVertex"]["stroke-width"] = graphVertexProperties["innerVertex"]["stroke-width"];
79 |
80 | attributeList["text"]["font-size"] = graphVertexProperties["text"]["font-size"];
81 | if (vertexShape == "rect_long") {
82 | attributeList["outerVertex"]["width"] = 200;
83 | attributeList["innerVertex"]["width"] = 198;
84 | } else if (vertexShape == "rect") {
85 | attributeList["outerVertex"]["width"] = 80;
86 | attributeList["innerVertex"]["width"] = 78;
87 | }
88 |
89 | }
90 |
91 | this.hideVertex = function(){
92 | attributeList["outerVertex"]["r"] = 0;
93 | attributeList["outerVertex"]["width"] = 0;
94 | attributeList["outerVertex"]["height"] = 0;
95 | attributeList["outerVertex"]["stroke-width"] = 0;
96 |
97 | attributeList["innerVertex"]["r"] = 0;
98 | attributeList["innerVertex"]["width"] = 0;
99 | attributeList["innerVertex"]["height"] = 0;
100 | attributeList["innerVertex"]["stroke-width"] = 0;
101 |
102 | attributeList["text"]["font-size"] = 0;
103 | }
104 |
105 | this.moveVertex = function(cx, cy){
106 | attributeList["outerVertex"]["cx"] = cx;
107 | attributeList["outerVertex"]["cy"] = cy;
108 | attributeList["outerVertex"]["x"] = cx-graphVertexProperties["outerVertex"]["width"]/2;
109 | attributeList["outerVertex"]["y"] = cy-graphVertexProperties["outerVertex"]["height"]/2;
110 |
111 | attributeList["innerVertex"]["cx"] = cx;
112 | attributeList["innerVertex"]["cy"] = cy;
113 | attributeList["innerVertex"]["x"] = cx-graphVertexProperties["innerVertex"]["width"]/2;
114 | attributeList["innerVertex"]["y"] = cy-graphVertexProperties["innerVertex"]["height"]/2;
115 |
116 | attributeList["text"]["x"] = cx;
117 | attributeList["text"]["y"] = cy + textYaxisOffset;
118 |
119 | var key;
120 |
121 | for(key in edgeList){
122 | edgeList[key].refreshPath();
123 | }
124 | }
125 |
126 | this.changeText = function(newVertexText){
127 | vertexText = newVertexText;
128 | attributeList["text"]["text"] = newVertexText;
129 | }
130 |
131 | this.changeTextFontSize = function(newFontSize){
132 | if(newTextSize == null || isNaN(newTextSize)) return;
133 | attributeList["text"]["font-size"] = newTextSize;
134 | }
135 |
136 | this.changeRadius = function(newRadiusInner, newRadiusOuter){
137 | if(newRadiusInner == null || isNaN(newRadiusInner)) return;
138 | attributeList["innerVertex"]["r"] = newRadiusInner;
139 | if(newRadiusOuter == null || isNaN(newRadiusOuter)) return;
140 | attributeList["outerVertex"]["r"] = newRadiusOuter;
141 | }
142 |
143 | this.changeWidth = function(newWidthInner, newWidthOuter){
144 | if(newWidthInner == null || isNaN(newWidthInner)) return;
145 | attributeList["innerVertex"]["width"] = newWidthInner;
146 | if(newWidthOuter == null || isNaN(newWidthOuter)) return;
147 | attributeList["outerVertex"]["width"] = newWidthOuter;
148 | }
149 |
150 | this.changeHeight = function(newHeightInner, newHeightOuter){
151 | if(newHeightInner == null || isNaN(newHeightInner)) return;
152 | attributeList["innerVertex"]["height"] = newHeightInner;
153 | if(newHeightOuter == null || isNaN(newHeightOuter)) return;
154 | attributeList["outerVertex"]["height"] = newHeightOuter;
155 | }
156 |
157 | this.changeStrokeWidth = function(newStrokeWidthInner, newStrokeWidthOuter){
158 | if(newStrokeWidthInner == null || isNaN(newStrokeWidthInner)) return;
159 | attributeList["innerVertex"]["stroke-width"] = newStrokeWidthInner;
160 | if(newStrokeWidthOuter == null || isNaN(newStrokeWidthOuter)) return;
161 | attributeList["outerVertex"]["stroke-width"] = newStrokeWidthOuter;
162 | }
163 |
164 | // Removes the vertex (no animation)
165 | // If you want animation, hide & redraw the vertex first, then call this function
166 | this.removeVertex = function(){
167 | outerVertex.remove();
168 | innerVertex.remove();
169 | text.remove();
170 | }
171 |
172 | // DEPRECATED
173 | /*
174 | this.highlightVertex = function(){
175 | var key;
176 |
177 | for(key in graphVertexProperties["innerVertex"]["highlighted"]){
178 | attributeList["innerVertex"][key] = graphVertexProperties["innerVertex"]["highlighted"][key];
179 | }
180 |
181 | for(key in graphVertexProperties["outerVertex"]["highlighted"]){
182 | attributeList["outerVertex"][key] = graphVertexProperties["outerVertex"]["highlighted"][key];
183 | }
184 |
185 | for(key in graphVertexProperties["text"]["highlighted"]){
186 | attributeList["text"][key] = graphVertexProperties["text"]["highlighted"][key];
187 | }
188 | }
189 |
190 | this.traversedVertex = function(){
191 | var key;
192 |
193 | for(key in graphVertexProperties["innerVertex"]["traversed"]){
194 | attributeList["innerVertex"][key] = graphVertexProperties["innerVertex"]["traversed"][key];
195 | }
196 |
197 | for(key in graphVertexProperties["outerVertex"]["traversed"]){
198 | attributeList["outerVertex"][key] = graphVertexProperties["outerVertex"]["traversed"][key];
199 | }
200 |
201 | for(key in graphVertexProperties["text"]["traversed"]){
202 | attributeList["text"][key] = graphVertexProperties["text"]["traversed"][key];
203 | }
204 | }
205 |
206 | this.resultVertex = function(){
207 | var key;
208 |
209 | for(key in graphVertexProperties["innerVertex"]["result"]){
210 | attributeList["innerVertex"][key] = graphVertexProperties["innerVertex"]["result"][key];
211 | }
212 |
213 | for(key in graphVertexProperties["outerVertex"]["result"]){
214 | attributeList["outerVertex"][key] = graphVertexProperties["outerVertex"]["result"][key];
215 | }
216 |
217 | for(key in graphVertexProperties["text"]["result"]){
218 | attributeList["text"][key] = graphVertexProperties["text"]["result"][key];
219 | }
220 | }
221 |
222 | this.defaultVertex = function(){
223 | var key;
224 |
225 | for(key in graphVertexProperties["innerVertex"]["default"]){
226 | attributeList["innerVertex"][key] = graphVertexProperties["innerVertex"]["default"][key];
227 | }
228 |
229 | for(key in graphVertexProperties["outerVertex"]["default"]){
230 | attributeList["outerVertex"][key] = graphVertexProperties["outerVertex"]["default"][key];
231 | }
232 |
233 | for(key in graphVertexProperties["text"]["default"]){
234 | attributeList["text"][key] = graphVertexProperties["text"]["default"][key];
235 | }
236 | }
237 | */
238 |
239 | this.stateVertex = function(stateName){
240 | var key;
241 |
242 | for(key in graphVertexProperties["innerVertex"][stateName]){
243 | attributeList["innerVertex"][key] = graphVertexProperties["innerVertex"][stateName][key];
244 | }
245 |
246 | for(key in graphVertexProperties["outerVertex"][stateName]){
247 | attributeList["outerVertex"][key] = graphVertexProperties["outerVertex"][stateName][key];
248 | }
249 |
250 | for(key in graphVertexProperties["text"][stateName]){
251 | attributeList["text"][key] = graphVertexProperties["text"][stateName][key];
252 | }
253 | }
254 |
255 | this.getAttributes = function(){
256 | return deepCopy(attributeList);
257 | }
258 |
259 | this.getClassNumber = function(){
260 | return vertexClassNumber;
261 | }
262 |
263 | this.addEdge = function(graphEdge){
264 | edgeList[graphEdge.getAttributes()["id"]] = graphEdge;
265 | }
266 |
267 | this.removeEdge = function(graphEdge){
268 | if(edgeList[graphEdge.getAttributes()["id"]] == null || edgeList[graphEdge.getAttributes()["id"]] == undefined) return;
269 |
270 | delete edgeList[graphEdge.getAttributes()["id"]];
271 | }
272 |
273 | this.getEdge = function(){
274 | var reply = [];
275 | var key;
276 |
277 | for(key in edgeList){
278 | reply.push(edgeList[key]);
279 | }
280 |
281 | return reply;
282 | }
283 |
284 | // this.connectVertex = function(secondVertex, directed, weight){
285 | // self.addEdge(new GraphEdgeWidget(self,secondVertex,directed,weight));
286 | // }
287 |
288 | // Initialize vertex and draw them, but the object will not be visible due to the radius of the vertex circle set to 0
289 | function init(){
290 | var tmp_vertexShape = vertexShape;
291 | if (vertexShape == "rect_long") tmp_vertexShape = "rect";
292 | outerVertex = vertexSvg.append(tmp_vertexShape);
293 | innerVertex = vertexSvg.append(tmp_vertexShape);
294 | text = vertexTextSvg.append("text");
295 |
296 | attributeList["innerVertex"]["class"] = "v" + vertexClassNumber
297 | attributeList["innerVertex"]["cx"] = cx;
298 | attributeList["innerVertex"]["cy"] = cy;
299 | attributeList["innerVertex"]["x"] = cx-graphVertexProperties["innerVertex"]["width"]/2;
300 | attributeList["innerVertex"]["y"] = cy-graphVertexProperties["innerVertex"]["height"]/2;
301 | attributeList["innerVertex"]["fill"] = graphVertexProperties["innerVertex"]["default"]["fill"];
302 | attributeList["innerVertex"]["r"] = 0;
303 | attributeList["innerVertex"]["width"] = 0;
304 | attributeList["innerVertex"]["height"] = 0;
305 | attributeList["innerVertex"]["stroke"] = graphVertexProperties["innerVertex"]["default"]["stroke"];
306 | attributeList["innerVertex"]["stroke-width"] = 0;
307 |
308 | attributeList["outerVertex"]["class"] = "v" + vertexClassNumber
309 | attributeList["outerVertex"]["cx"] = cx;
310 | attributeList["outerVertex"]["cy"] = cy;
311 | attributeList["outerVertex"]["x"] = cx-graphVertexProperties["outerVertex"]["width"]/2;
312 | attributeList["outerVertex"]["y"] = cy-graphVertexProperties["outerVertex"]["height"]/2;
313 | attributeList["outerVertex"]["fill"] = graphVertexProperties["outerVertex"]["default"]["fill"];
314 | attributeList["outerVertex"]["r"] = 0;
315 | attributeList["innerVertex"]["width"] = 0;
316 | attributeList["innerVertex"]["height"] = 0;
317 | attributeList["outerVertex"]["stroke"] = graphVertexProperties["outerVertex"]["default"]["stroke"];
318 | attributeList["outerVertex"]["stroke-width"] = 0;
319 |
320 | attributeList["text"]["class"] = "v" + vertexClassNumber
321 | attributeList["text"]["x"] = cx;
322 | attributeList["text"]["y"] = cy + textYaxisOffset;
323 | attributeList["text"]["fill"] = graphVertexProperties["text"]["default"]["fill"];
324 | attributeList["text"]["font-family"] = graphVertexProperties["text"]["default"]["font-family"];
325 | attributeList["text"]["font-size"] = 0;
326 | attributeList["text"]["font-weight"] = graphVertexProperties["text"]["default"]["font-weight"];
327 | attributeList["text"]["text-anchor"] = graphVertexProperties["text"]["default"]["text-anchor"];
328 | if (vertexShape == "rect_long") {
329 | attributeList["text"]["text-anchor"] = "left";
330 | }
331 | attributeList["text"]["text"] = vertexText;
332 |
333 | innerVertex.attr("class", attributeList["innerVertex"]["class"]);
334 | outerVertex.attr("class", attributeList["outerVertex"]["class"]);
335 | text.attr("class", attributeList["text"]["class"]);
336 |
337 | innerVertex.attr("cx", attributeList["innerVertex"]["cx"])
338 | .attr("cy", attributeList["innerVertex"]["cy"])
339 | .attr("x", attributeList["innerVertex"]["x"])
340 | .attr("y", attributeList["innerVertex"]["y"])
341 | .attr("fill", attributeList["innerVertex"]["fill"])
342 | .attr("r", attributeList["innerVertex"]["r"])
343 | .attr("width", attributeList["innerVertex"]["width"])
344 | .attr("height", attributeList["innerVertex"]["height"])
345 | .attr("stroke", attributeList["innerVertex"]["stroke"])
346 | .attr("stroke-width", attributeList["innerVertex"]["stroke-width"]);
347 |
348 | outerVertex.attr("cx", attributeList["outerVertex"]["cx"])
349 | .attr("cy", attributeList["outerVertex"]["cy"])
350 | .attr("x", attributeList["outerVertex"]["x"])
351 | .attr("y", attributeList["outerVertex"]["y"])
352 | .attr("fill", attributeList["outerVertex"]["fill"])
353 | .attr("r", attributeList["outerVertex"]["r"])
354 | .attr("width", attributeList["outerVertex"]["width"])
355 | .attr("height", attributeList["outerVertex"]["height"])
356 | .attr("stroke", attributeList["outerVertex"]["stroke"])
357 | .attr("stroke-width", attributeList["outerVertex"]["stroke-width"]);
358 |
359 | text.attr("x", attributeList["text"]["x"])
360 | .attr("y", attributeList["text"]["y"])
361 | .attr("fill", attributeList["text"]["fill"])
362 | .attr("font-family", attributeList["text"]["font-family"])
363 | .attr("font-size", attributeList["text"]["font-size"])
364 | .attr("font-weight", attributeList["text"]["font-weight"])
365 | .attr("text-anchor", attributeList["text"]["text-anchor"])
366 | .text(function(){
367 | return attributeList["text"]["text"];
368 | });
369 | }
370 |
371 | // Refreshes the vertex image
372 | // "dur" specifies the duration of the animation in milliseconds
373 | // If unspecified or illegal value, default duration applies.
374 | function draw(dur){
375 | if(dur == null || isNaN(dur)) dur = defaultAnimationDuration;
376 | if(dur <= 0) dur = 1;
377 |
378 | innerVertex.transition()
379 | .duration(dur)
380 | .attr("cx", attributeList["innerVertex"]["cx"])
381 | .attr("cy", attributeList["innerVertex"]["cy"])
382 | .attr("x", attributeList["innerVertex"]["x"])
383 | .attr("y", attributeList["innerVertex"]["y"])
384 | .attr("fill", attributeList["innerVertex"]["fill"])
385 | .attr("r", attributeList["innerVertex"]["r"])
386 | .attr("width", attributeList["innerVertex"]["width"])
387 | .attr("height", attributeList["innerVertex"]["height"])
388 | .attr("stroke", attributeList["innerVertex"]["stroke"])
389 | .attr("stroke-width", attributeList["innerVertex"]["stroke-width"]);
390 |
391 | outerVertex.transition()
392 | .duration(dur)
393 | .attr("cx", attributeList["outerVertex"]["cx"])
394 | .attr("cy", attributeList["outerVertex"]["cy"])
395 | .attr("x", attributeList["outerVertex"]["x"])
396 | .attr("y", attributeList["outerVertex"]["y"])
397 | .attr("fill", attributeList["outerVertex"]["fill"])
398 | .attr("r", attributeList["outerVertex"]["r"])
399 | .attr("width", attributeList["outerVertex"]["width"])
400 | .attr("height", attributeList["outerVertex"]["height"])
401 | .attr("stroke", attributeList["outerVertex"]["stroke"])
402 | .attr("stroke-width", attributeList["outerVertex"]["stroke-width"]);
403 |
404 | text.transition()
405 | .duration(dur)
406 | .attr("x", attributeList["text"]["x"])
407 | .attr("y", attributeList["text"]["y"])
408 | .attr("fill", attributeList["text"]["fill"])
409 | .attr("font-family", attributeList["text"]["font-family"])
410 | .attr("font-size", attributeList["text"]["font-size"])
411 | .attr("font-weight", attributeList["text"]["font-weight"])
412 | .attr("text-anchor", attributeList["text"]["text-anchor"])
413 | .text(function(){
414 | return attributeList["text"]["text"];
415 | });
416 | }
417 | }
--------------------------------------------------------------------------------
/js/graph_library/GraphWidget.js:
--------------------------------------------------------------------------------
1 | // @author Koh Zi Chun
2 | // Graph Widget. currently in a mess
3 | // add keyboard shortcut for animation
4 |
5 | // Ivan: Most (if not all) functions will be changed to accomodate new library (D3.js)
6 | // Only the algorithm & high-level design will be retained
7 |
8 | /*
9 | * TODO: Currently animation stuffs (play, pause, etc.) is tied to GraphWidget,
10 | * which means visualizations not related to graphs cannot be animated
11 | * Think of a way to separate the functions, preferably to another object
12 | * (perhaps to the currently stub Widget.js?)
13 | *
14 | * TODO: There's currently GraphDSWidget.js built on top of this file which allows graph drawing
15 | * Make graph drawing capabilities one of the backend library to allow visualizations
16 | * requiring drawing capabilities to be built on it
17 | */
18 |
19 |
20 | var vertexSvg = mainSvg.append("g")
21 | .attr("id", "vertex");
22 |
23 | var edgeSvg = mainSvg.append("g")
24 | .attr("id", "edge");
25 |
26 | var vertexTextSvg = mainSvg.append("g")
27 | .attr("id", "vertexText");
28 |
29 | var edgeWeightSvg = mainSvg.append("g")
30 | .attr("id", "edgeWeight");
31 |
32 | var edgeWeightPathSvg = mainSvg.append("g")
33 | .attr("id", "edgeWeightPath");
34 |
35 | var GraphWidget = function(){
36 | var self = this;
37 |
38 | var vertexList = {};
39 | var edgeList = {};
40 |
41 | var vertexUpdateList = {};
42 | var edgeUpdateList = {};
43 |
44 | var currentIteration = NO_ITERATION;
45 | var animationStateList = NO_STATELIST;
46 | var animationStatus = ANIMATION_STOP;
47 |
48 | var animationDuration = 500;
49 |
50 | // Show: true means the element will immediately appear on the html page
51 | // false means the element will remain hidden until told to appear
52 | // Duration: duration of the show animation, only used when show is true
53 |
54 | // Adds a CIRCLE vertex
55 | // TODO: Merge with addRectVertex
56 | this.addVertex = function(cx, cy, vertexText, vertexClassNumber, show){
57 | if(show != false) show = true;
58 |
59 | var newVertex = new GraphVertexWidget(cx,cy,VERTEX_SHAPE_CIRCLE,vertexText,vertexClassNumber);
60 |
61 | vertexList[vertexClassNumber] = newVertex;
62 | vertexUpdateList[vertexClassNumber] = false;
63 |
64 | if(show == true){
65 | vertexList[vertexClassNumber].showVertex();
66 | vertexList[vertexClassNumber].redraw();
67 | }
68 | }
69 |
70 | // Adds a RECTANGULAR vertex
71 | // TODO: Merge with addVertex
72 | this.addRectVertex = function(rx, ry, vertexText, vertexClassNumber, show, rect_type){
73 | if(show != false) show = true;
74 |
75 | console.log(VERTEX_SHAPE_RECT);
76 | if (typeof(rect_type) == "undefined") rect_type = VERTEX_SHAPE_RECT;
77 | var newVertex = new GraphVertexWidget(rx,ry,rect_type,vertexText,vertexClassNumber);
78 |
79 | vertexList[vertexClassNumber] = newVertex;
80 | vertexUpdateList[vertexClassNumber] = false;
81 |
82 | if(show == true){
83 | vertexList[vertexClassNumber].showVertex();
84 | vertexList[vertexClassNumber].redraw();
85 | }
86 | }
87 |
88 | // Default for weight is 1 and for type is EDGE_TYPE_UDE
89 | this.addEdge = function(vertexClassA, vertexClassB, edgeIdNumber, type, weight, show, showWeight){
90 | try {
91 | if(show != false) show = true;
92 | if(showWeight != true) showWeight = false;
93 | if(type == null || isNaN(type)) type = EDGE_TYPE_UDE;
94 | if(weight == null || isNaN(weight)) weight = 1;
95 |
96 | var vertexA = vertexList[vertexClassA];
97 | var vertexB = vertexList[vertexClassB];
98 |
99 | var newEdge = new GraphEdgeWidget(vertexA, vertexB, edgeIdNumber, type, weight);
100 |
101 | edgeList[edgeIdNumber] = newEdge;
102 | edgeUpdateList[edgeIdNumber] = false;
103 |
104 | vertexList[vertexClassA].addEdge(newEdge);
105 | vertexList[vertexClassB].addEdge(newEdge);
106 |
107 | if(show == true){
108 | edgeList[edgeIdNumber].showEdge();
109 | if(showWeight == true) edgeList[edgeIdNumber].showWeight();
110 | edgeList[edgeIdNumber].redraw();
111 | }
112 | }
113 | catch(err) {}
114 | }
115 |
116 | this.removeEdge = function(edgeIdNumber){
117 | if(edgeList[edgeIdNumber] == null || edgeList[edgeIdNumber] == undefined) return;
118 |
119 | edgeList[edgeIdNumber].removeEdge();
120 | delete edgeList[edgeIdNumber];
121 | delete edgeUpdateList[edgeIdNumber];
122 | }
123 |
124 | // Edges are assumed to have been removed
125 | this.removeVertex = function(vertexClassNumber){
126 | vertexList[vertexClassNumber].removeVertex();
127 | delete vertexList[vertexClassNumber];
128 | delete vertexUpdateList[vertexClassNumber];
129 | }
130 |
131 | // graphState object is equivalent to one element of the statelist.
132 | // See comments below this function
133 | this.updateGraph = function(graphState, duration){
134 | if(duration == null || isNaN(duration)) duration = animationDuration;
135 | updateDisplay(graphState, duration);
136 | }
137 |
138 | /*
139 | * stateList: List of JS object containing the states of the objects in the graph
140 | * Structure of stateList: List of JS object with the following keys and values:
141 | * - vl: JS object with vertex ID as keys and corresponding state positions and constants as value
142 | * - el: JS object with edge ID as keys and corresponding state connections constants as value
143 | *
144 | * Objects not present in the i-th iteration stateList will be hidden until the animation stops, where it will be removed
145 | * New objects present in the i-th iteration stateList will be automatically created
146 | *
147 | * State 0 should be the initial state, last state should be the end state
148 | */
149 |
150 | /*
151 | * Contents of "vl":
152 | * - cx
153 | * - cy
154 | * - text
155 | * - state
156 | *
157 | * Optional contents of "vl":
158 | * - inner-r : Customize the vertex's inner radius!
159 | * - outer-r : Customize the vertex's outer radius!
160 | * - inner-w : Customize the vertex's inner width!
161 | * - outer-w : Customize the vertex's outer width!
162 | * - inner-h : Customize the vertex's inner height!
163 | * - outer-h : Customize the vertex's outer height!
164 | * - inner-stroke-width : Customize the vertex's inner stroke width!
165 | * - outer-stroke-width : Customize the vertex's outer stroke width!
166 | * - text-font-size : Customize the vertex text's font size!
167 | */
168 |
169 | /*
170 | * Contents of "el":
171 | * - vertexA: id of vertex A
172 | * - vertexB: id of vertex B
173 | * - type
174 | * - weight
175 | * - state : Display state
176 | * - animateHighlighted : Determines whether highlighted animation should be played. True or false
177 | *
178 | * Optional contents of "el":
179 | * - displayWeight : Determines whether weight should be shown. True or false
180 | */
181 |
182 | /*
183 | * Notes:
184 | * - Vertex's elements will only affect vertexes that has that element
185 | * (example: radius will only affect circular vertex, width and height will only affect rectangular vertex)
186 | * Think of each vertex as an SVG element and see which components are present
187 | * - The optional contents has to be defined for EACH state objects
188 | * For example, if you define a custom radius in state 1 and didn't define it in state 2,
189 | * the vertex will revert to default radius upon reaching state 2
190 | */
191 |
192 | this.startAnimation = function(stateList){
193 | if(stateList != null) animationStateList = stateList;
194 | if(currentIteration == NO_ITERATION) currentIteration = 0;
195 |
196 | var key;
197 |
198 | self.play();
199 | }
200 |
201 | this.animate = function(){
202 | if(currentIteration >= animationStateList.length && animationStatus != ANIMATION_STOP) animationStatus = ANIMATION_PAUSE;
203 | if(animationStatus == ANIMATION_PAUSE || animationStatus == ANIMATION_STOP) return;
204 |
205 | self.next(animationDuration);
206 |
207 | setTimeout(function(){
208 | self.animate();
209 | }, animationDuration);
210 | }
211 |
212 | this.play = function(){
213 | if(currentIteration < 0) currentIteration = 0;
214 |
215 | if(animationStatus == ANIMATION_STOP){
216 | animationStatus = ANIMATION_PLAY;
217 | updateDisplay(animationStateList[currentIteration], animationDuration);
218 | setTimeout(function(){
219 | self.animate();
220 | }, animationDuration);
221 | }
222 |
223 | else{
224 | animationStatus = ANIMATION_PLAY;
225 | self.animate();
226 | }
227 | }
228 |
229 | this.pause = function(){
230 | animationStatus = ANIMATION_PAUSE;
231 | }
232 |
233 | this.stop = function(){
234 | // while(currentIteration < animationStateList.length - 1){
235 | // self.forceNext(0);
236 | // }
237 |
238 | self.jumpToIteration(animationStateList.length - 1, 0);
239 |
240 | currentIteration = animationStateList.length - 1;
241 | animationStatus = ANIMATION_STOP;
242 |
243 | var currentVertexState = animationStateList[currentIteration]["vl"];
244 | var currentEdgeState = animationStateList[currentIteration]["el"];
245 |
246 | var key;
247 |
248 | for(key in currentEdgeState){
249 | edgeUpdateList[key] = true;
250 | }
251 |
252 | for(key in edgeUpdateList){
253 | if(edgeUpdateList[key] == false){
254 | self.removeEdge(key);
255 | }
256 | }
257 |
258 | for(key in currentVertexState){
259 | vertexUpdateList[key] = true;
260 | }
261 |
262 | for(key in vertexUpdateList){
263 | if(vertexUpdateList[key] == false){
264 | self.removeVertex(key);
265 | }
266 | }
267 |
268 | for(key in edgeUpdateList){
269 | edgeUpdateList[key] = false;
270 | }
271 |
272 | for(key in vertexUpdateList){
273 | vertexUpdateList[key] = false;
274 | }
275 |
276 | animationStateList = NO_STATELIST;
277 | currentIteration = NO_ITERATION;
278 | }
279 |
280 | this.next = function(duration){
281 | if(currentIteration < 0) currentIteration = 0;
282 | currentIteration++;
283 | if(currentIteration >= animationStateList.length) {
284 | currentIteration = animationStateList.length-1;
285 | return;
286 | }
287 | updateDisplay(animationStateList[currentIteration], duration);
288 | }
289 |
290 | this.previous = function(duration){
291 | if(currentIteration >= animationStateList.length) currentIteration = animationStateList.length - 1;
292 | currentIteration--;
293 | if(currentIteration < 0) return;
294 | updateDisplay(animationStateList[currentIteration], duration);
295 | }
296 |
297 | this.forceNext = function(duration){
298 | self.pause();
299 | self.next(duration);
300 | }
301 |
302 | this.forcePrevious = function(duration){
303 | self.pause();
304 | self.previous(duration);
305 | }
306 |
307 | this.jumpToIteration = function(iteration, duration){
308 | self.pause();
309 | currentIteration = iteration;
310 | if(currentIteration >= animationStateList.length) currentIteration = animationStateList.length - 1;
311 | if(currentIteration < 0) currentIteration = 0;
312 | updateDisplay(animationStateList[currentIteration], duration);
313 | }
314 |
315 | this.replay = function(){
316 | self.jumpToIteration(0, 0);
317 | setTimeout(function(){self.play()}, 500);
318 | }
319 |
320 | this.getCurrentIteration = function(){
321 | return currentIteration;
322 | }
323 |
324 | this.getTotalIteration = function(){
325 | return Object.keys(animationStateList).length;
326 | }
327 |
328 | this.getAnimationDuration = function(){
329 | return animationDuration;
330 | }
331 |
332 | // Get the current state object of the animation. Useful to reproduce the graph.
333 | // DO NOT CALL THIS FUNCTION WHEN ANIMATION IS NOT STARTED YET
334 | this.getCurrentState = function(){
335 | return animationStateList[currentIteration];
336 | }
337 |
338 | this.setAnimationDuration = function(duration){
339 | animationDuration = duration;
340 | }
341 |
342 | function updateDisplay(graphState, duration){
343 | // Add boolean flag for vertexes and edges that exists in the current visualization
344 | // Check the boolean flags each time this function is called
345 | // If there are objects that are not updated, it means that the object is removed
346 | // If there are new objects that currently not in the flags, it means the object is created this turn
347 |
348 | console.log("iteration " + currentIteration);
349 | var lastIteration = Object.keys(animationStateList).length-1;
350 | try{
351 | $('#progress-bar').slider("value", currentIteration);
352 | $('#status p').html(/*"iteration " + currentIteration + ": " + */animationStateList[currentIteration]["status"]);
353 | highlightLine(animationStateList[currentIteration]["lineNo"]);
354 | if(currentIteration == lastIteration) {
355 | pause(); //in html file
356 | $('#play img').attr('src','img/replay.png').attr('alt','replay').attr('title','replay');
357 | } else {
358 | $('#play img').attr('src','img/play.png').attr('alt','play').attr('title','play');
359 | }
360 | } catch(error){
361 | // Status has not been integrated in most of my animation, so leave it like this
362 | }
363 |
364 | var currentVertexState = graphState["vl"];
365 | var currentEdgeState = graphState["el"];
366 |
367 | var key;
368 |
369 | for(key in currentVertexState){
370 | if(vertexList[key] == null || vertexList[key] == undefined){
371 | self.addVertex(currentVertexState[key]["cx"],currentVertexState[key]["cy"],currentVertexState[key]["text"],key,false);
372 | }
373 |
374 | var currentVertex = vertexList[key];
375 |
376 | currentVertex.showVertex();
377 |
378 | if(currentVertexState[key]["state"] == OBJ_HIDDEN) currentVertex.hideVertex();
379 | else if(currentVertexState[key]["state"] != null) currentVertex.stateVertex(currentVertexState[key]["state"]);
380 | else currentVertex.stateVertex(VERTEX_DEFAULT);
381 |
382 | currentVertex.moveVertex(currentVertexState[key]["cx"], currentVertexState[key]["cy"]);
383 | currentVertex.changeText(currentVertexState[key]["text"]);
384 |
385 | if(currentVertexState[key]["text-font-size"] != null){
386 | currentVertex.changeTextFontSize(currentVertexState[key]["text-font-size"]);
387 | }
388 | if(currentVertexState[key]["inner-r"] != null && currentVertexState[key]["outer-r"] != null){
389 | currentVertex.changeRadius(currentVertexState[key]["inner-r"], currentVertexState[key]["outer-r"]);
390 | }
391 | if(currentVertexState[key]["inner-w"] != null && currentVertexState[key]["outer-w"] != null){
392 | currentVertex.changeWidth(currentVertexState[key]["inner-w"], currentVertexState[key]["outer-w"]);
393 | }
394 | if(currentVertexState[key]["inner-h"] != null && currentVertexState[key]["outer-h"] != null){
395 | currentVertex.changeHeight(currentVertexState[key]["inner-h"], currentVertexState[key]["outer-h"]);
396 | }
397 | if(currentVertexState[key]["inner-stroke-width"] != null && currentVertexState[key]["outer-stroke-width"] != null){
398 | currentVertex.changeStrokeWidth(currentVertexState[key]["inner-stroke-width"], currentVertexState[key]["outer-stroke-width"]);
399 | }
400 |
401 | currentVertex.redraw(duration);
402 |
403 | vertexUpdateList[key] = true;
404 | }
405 |
406 | for(key in vertexUpdateList){
407 | if(vertexUpdateList[key] == false){
408 | vertexList[key].hideVertex();
409 | vertexList[key].redraw(duration);
410 | vertexUpdateList[key] = true;
411 | }
412 | }
413 |
414 | try {
415 | for(key in currentEdgeState){
416 | if(edgeList[key] == null || edgeList[key] == undefined){
417 | self.addEdge(currentEdgeState[key]["vertexA"],currentEdgeState[key]["vertexB"],key,currentEdgeState[key]["type"],currentEdgeState[key]["weight"],false);
418 | }
419 |
420 | var currentEdge = edgeList[key];
421 |
422 | currentEdge.showEdge();
423 |
424 | if(currentEdgeState[key]["state"] == OBJ_HIDDEN) currentEdge.hideEdge();
425 | else if(currentEdgeState[key]["state"] != null) currentEdge.stateEdge(currentEdgeState[key]["state"]);
426 | else currentEdge.stateEdge(EDGE_DEFAULT);
427 |
428 | currentEdge.hideWeight();
429 | if(currentEdgeState[key]["state"] != OBJ_HIDDEN && currentEdgeState[key]["displayWeight"] != null && currentEdgeState[key]["displayWeight"]){
430 | currentEdge.showWeight();
431 | }
432 |
433 | currentEdge.changeVertexA(vertexList[currentEdgeState[key]["vertexA"]]);
434 | currentEdge.changeVertexB(vertexList[currentEdgeState[key]["vertexB"]]);
435 | if(currentEdgeState[key]["type"] == null) currentEdgeState[key]["type"] = EDGE_TYPE_UDE;
436 | currentEdge.changeType(currentEdgeState[key]["type"]);
437 | if(currentEdgeState[key]["weight"] != null)currentEdge.changeWeight(currentEdgeState[key]["weight"]);
438 |
439 | currentEdge.refreshPath();
440 | if(currentEdgeState[key]["animateHighlighted"] == null || !currentEdgeState[key]["animateHighlighted"]) currentEdge.redraw(duration);
441 | else{
442 | currentEdge.animateHighlighted(duration*0.9);
443 | }
444 |
445 | edgeUpdateList[key] = true;
446 | }
447 |
448 | for(key in edgeUpdateList){
449 | if(edgeUpdateList[key] == false){
450 | edgeList[key].hideEdge();
451 | edgeList[key].redraw(duration);
452 | edgeUpdateList[key] = true;
453 | }
454 | }
455 | } catch(err) {}
456 | for(key in vertexUpdateList){
457 | vertexUpdateList[key] = false;
458 | }
459 |
460 | for(key in edgeUpdateList){
461 | edgeUpdateList[key] = false;
462 | }
463 | }
464 | }
465 |
--------------------------------------------------------------------------------
/list.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 | VisuAlgo - Linked List
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
Create
68 |
Search
69 |
Insert
70 |
Remove
71 |
72 |
73 |
74 |
75 |
76 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
203 |
204 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
Linked list is a data structure consisting of a group of vertices which together represent a sequence. Under the simplest form, each vertex is composed of a data and a reference (in other words, a link) to the next vertex in the sequence.
223 |
Next
224 |
225 |
226 | To toggle between the Linked List, Stack, Queue, Doubly Linked List, and Deque, select the respective header.
227 |
Next
228 |
229 |
230 | All available operations will be shown here. Select an action and provide the necessary input, and the action will be animated in the visualisation area.
231 |
Next
232 |
233 |
234 | View the visualisation here!
235 |
Next
236 |
237 |
238 | As the action is being carried out, each step will be described in the status panel.
239 |
Next
240 |
241 |
242 | You can also follow the psuedocode highlights to trace the algorithm.
243 |
Next
244 |
245 |
246 | Control the animation with the player controls! Keyboard shortcuts are:
247 |
Spacebar: play/pause/replay
248 |
Left/right arrows: step backward/step forward
249 |
-/+: decrease/increase speed
250 |
Next
251 |
252 |
253 | Return to "Exploration Mode" to start exploring!
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
988 |
989 |
990 |