diff --git a/index.html b/index.html index 0c696435beac64692f6af15aefb7fbc47c35c4e9..9a0f612f66f50beef1ebaa189382e9def77cb824 100644 --- a/index.html +++ b/index.html @@ -109,7 +109,8 @@ - + + diff --git a/js/module/array1d.js b/js/module/array1d.js index 202eb2d50fd2da264caf45d83159d66f9e462db9..82068e0dd5cad2d1de240a4e168c7b3679d6c2c6 100644 --- a/js/module/array1d.js +++ b/js/module/array1d.js @@ -2,70 +2,59 @@ function Array1DTracer(module) { return Array2DTracer.call(this, module || Array1DTracer); } -Array1DTracer.prototype = Object.create(Array2DTracer.prototype); -Array1DTracer.prototype.constructor = Array1DTracer; - -// Override -Array1DTracer.prototype._setData = function(D) { - return Array2DTracer.prototype._setData.call(this, [D]); -}; - -// Override -Array1DTracer.prototype._notify = function(idx1, idx2) { - if (idx2 === undefined) { - Array2DTracer.prototype._notify.call(this, 0, idx1); - } else { - Array2DTracer.prototype._notify.call(this, 0, idx1, 0, idx2); - } -}; - -// Override -Array1DTracer.prototype._select = function(s, e) { - if (e === undefined) { - Array2DTracer.prototype._select.call(this, 0, s); - } else { - Array2DTracer.prototype._selectRow.call(this, 0, s, e); - } -}; - -// Override -Array1DTracer.prototype._selectSet = function(indexes) { - var coords = []; - indexes.forEach(function(index) { - coords.push({ - x: 0, - y: index +Array1DTracer.prototype = $.extend(true, Object.create(Array2DTracer.prototype), { + constructor: Array1DTracer, + _setData: function (D) { + return Array2DTracer.prototype._setData.call(this, [D]); + }, + _notify: function (idx1, idx2) { + if (idx2 === undefined) { + Array2DTracer.prototype._notify.call(this, 0, idx1); + } else { + Array2DTracer.prototype._notify.call(this, 0, idx1, 0, idx2); + } + }, + _select: function (s, e) { + if (e === undefined) { + Array2DTracer.prototype._select.call(this, 0, s); + } else { + Array2DTracer.prototype._selectRow.call(this, 0, s, e); + } + }, + _selectSet: function (indexes) { + var coords = []; + indexes.forEach(function (index) { + coords.push({ + x: 0, + y: index + }); }); - }); - Array2DTracer.prototype._selectSet.call(this, coords); -}; - -// Override -Array1DTracer.prototype._deselect = function(s, e) { - if (e === undefined) { - Array2DTracer.prototype._deselect.call(this, 0, s); - } else { - Array2DTracer.prototype._deselectRow.call(this, 0, s, e); - } -}; - -// Override -Array1DTracer.prototype._deselectSet = function(indexes) { - var coords = []; - indexes.forEach(function(index) { - coords.push({ - x: 0, - y: index + Array2DTracer.prototype._selectSet.call(this, coords); + }, + _deselect: function (s, e) { + if (e === undefined) { + Array2DTracer.prototype._deselect.call(this, 0, s); + } else { + Array2DTracer.prototype._deselectRow.call(this, 0, s, e); + } + }, + _deselectSet: function (indexes) { + var coords = []; + indexes.forEach(function (index) { + coords.push({ + x: 0, + y: index + }); }); - }); - Array2DTracer.prototype._deselectSet.call(this, coords); -}; + Array2DTracer.prototype._deselectSet.call(this, coords); + } +}); var Array1D = { - random: function(N, min, max) { + random: function (N, min, max) { return Array2D.random(1, N, min, max)[0]; }, - randomSorted: function(N, min, max) { + randomSorted: function (N, min, max) { return Array2D.randomSorted(1, N, min, max)[0]; } }; \ No newline at end of file diff --git a/js/module/array2d.js b/js/module/array2d.js index 4b5c260a612ec4b2076f8260c7960b516bef63ad..368fb15b49d7a7fd31e2ef49e6469459dc9f85df 100644 --- a/js/module/array2d.js +++ b/js/module/array2d.js @@ -2,281 +2,273 @@ var $table = null; function Array2DTracer(module) { if (Tracer.call(this, module || Array2DTracer)) { - Array2D.initTable(); + Array2D.init(); return true; } return false; } -Array2DTracer.prototype = Object.create(Tracer.prototype); -Array2DTracer.prototype.constructor = Array2DTracer; - -// Override -Array2DTracer.prototype.resize = function () { - Tracer.prototype.resize.call(this); - - this.refresh(); -}; - -// Override -Array2DTracer.prototype.clear = function () { - Tracer.prototype.clear.call(this); - - Array2D.clearTableColor(); -}; +Array2DTracer.prototype = $.extend(true, Object.create(Tracer.prototype), { + constructor: Array2DTracer, + init: function () { + $table = $('
'); + $module_container.append($table); + }, + resize: function () { + Tracer.prototype.resize.call(this); -// Override -Array2DTracer.prototype._setData = function (D) { - this.D = D; - this.viewX = this.viewY = 0; - this.paddingH = 6; - this.paddingV = 3; - this.fontSize = 16; + this.refresh(); + }, + clear: function () { + Tracer.prototype.clear.call(this); - if (Tracer.prototype._setData.call(this, arguments)) { - $('.mtbl-row').each(function (i) { - $(this).children().each(function (j) { - $(this).text(D[i][j]); + this.clearColor(); + }, + _setData: function (D) { + this.D = D; + this.viewX = this.viewY = 0; + this.paddingH = 6; + this.paddingV = 3; + this.fontSize = 16; + + if (Tracer.prototype._setData.call(this, arguments)) { + $('.mtbl-row').each(function (i) { + $(this).children().each(function (j) { + $(this).text(D[i][j]); + }); }); - }); - return true; - } - - $table.empty(); - for (var i = 0; i < D.length; i++) { - var $row = $('
'); - $table.append($row); - for (var j = 0; j < D[i].length; j++) { - var $cell = $('
') - .css(this.getCellCss()) - .text(D[i][j]); - $row.append($cell); + return true; } - } - this.resize(); - - return false; -}; - -Array2DTracer.prototype._notify = function (x1, y1, x2, y2) { - var second = x2 !== undefined && y2 !== undefined; - this.pushStep({ - type: 'notifying', - x: x1, - y: y1, - value: this.D[x1][y1] - }, !second); - if (second) this.pushStep({ - type: 'notifying', - x: x2, - y: y2, - value: this.D[x2][y2] - }, true); - this.pushStep({ - type: 'notified', - x: x1, - y: y1 - }, false); - if (second) this.pushStep({ - type: 'notified', - x: x2, - y: y2 - }, false); -}; - -Array2DTracer.prototype._select = function (sx, sy, ex, ey) { - this.pushSelectingStep('select', null, arguments); -}; - -Array2DTracer.prototype._selectRow = function (x, sy, ey) { - this.pushSelectingStep('select', 'row', arguments); -}; -Array2DTracer.prototype._selectCol = function (y, sx, ex) { - this.pushSelectingStep('select', 'col', arguments); -}; - -Array2DTracer.prototype._selectSet = function (coords) { - this.pushSelectingStep('select', 'set', arguments); -}; - -Array2DTracer.prototype._deselect = function (sx, sy, ex, ey) { - this.pushSelectingStep('deselect', null, arguments); -}; - -Array2DTracer.prototype._deselectRow = function (x, sy, ey) { - this.pushSelectingStep('deselect', 'row', arguments); -}; - -Array2DTracer.prototype._deselectCol = function (y, sx, ex) { - this.pushSelectingStep('deselect', 'col', arguments); -}; - -Array2DTracer.prototype._deselectSet = function (coords) { - this.pushSelectingStep('deselect', 'set', arguments); -}; + $table.empty(); + for (var i = 0; i < D.length; i++) { + var $row = $('
'); + $table.append($row); + for (var j = 0; j < D[i].length; j++) { + var $cell = $('
') + .css(this.getCellCss()) + .text(D[i][j]); + $row.append($cell); + } + } + this.resize(); -Array2DTracer.prototype.pushSelectingStep = function () { - var args = Array.prototype.slice.call(arguments); - var type = args.shift(); - var mode = args.shift(); - args = Array.prototype.slice.call(args.shift()); - var coord; - switch (mode) { - case 'row': - coord = { - x: args[0], - sy: args[1], - ey: args[2] - }; - break; - case 'col': - coord = { - y: args[0], - sx: args[1], - ex: args[2] - }; - break; - case 'set': - coord = { - coords: args[0] - }; - break; - default: - if (args[2] === undefined && args[3] === undefined) { + return false; + }, + _notify: function (x1, y1, x2, y2) { + var second = x2 !== undefined && y2 !== undefined; + this.pushStep({ + type: 'notifying', + x: x1, + y: y1, + value: this.D[x1][y1] + }, !second); + if (second) this.pushStep({ + type: 'notifying', + x: x2, + y: y2, + value: this.D[x2][y2] + }, true); + this.pushStep({ + type: 'notified', + x: x1, + y: y1 + }, false); + if (second) this.pushStep({ + type: 'notified', + x: x2, + y: y2 + }, false); + }, + _select: function (sx, sy, ex, ey) { + this.pushSelectingStep('select', null, arguments); + }, + _selectRow: function (x, sy, ey) { + this.pushSelectingStep('select', 'row', arguments); + }, + _selectCol: function (y, sx, ex) { + this.pushSelectingStep('select', 'col', arguments); + }, + _selectSet: function (coords) { + this.pushSelectingStep('select', 'set', arguments); + }, + _deselect: function (sx, sy, ex, ey) { + this.pushSelectingStep('deselect', null, arguments); + }, + _deselectRow: function (x, sy, ey) { + this.pushSelectingStep('deselect', 'row', arguments); + }, + _deselectCol: function (y, sx, ex) { + this.pushSelectingStep('deselect', 'col', arguments); + }, + _deselectSet: function (coords) { + this.pushSelectingStep('deselect', 'set', arguments); + }, + pushSelectingStep: function () { + var args = Array.prototype.slice.call(arguments); + var type = args.shift(); + var mode = args.shift(); + args = Array.prototype.slice.call(args.shift()); + var coord; + switch (mode) { + case 'row': coord = { x: args[0], - y: args[1] + sy: args[1], + ey: args[2] }; - } else { + break; + case 'col': coord = { - sx: args[0], - sy: args[1], - ex: args[2], - ey: args[3] + y: args[0], + sx: args[1], + ex: args[2] }; - } - } - var step = { - type: type - }; - $.extend(step, coord); - this.pushStep(step, type == 'select'); -}; - -Array2DTracer.prototype.processStep = function (step, options) { - switch (step.type) { - case 'notifying': - var $row = $table.find('.mtbl-row').eq(step.x); - $row.find('.mtbl-cell').eq(step.y).text(step.value); - case 'notified': - case 'select': - case 'deselect': - var colorClass = step.type == 'select' || step.type == 'deselect' ? Array2D.tableColorClass.selected : Array2D.tableColorClass.notifying; - var addClass = step.type == 'select' || step.type == 'notifying'; - if (step.coords) { - step.coords.forEach(function (coord) { - var x = coord.x; - var y = coord.y; - Array2D.paintColor(x, y, x, y, colorClass, addClass); - }); - } else { - var sx = step.sx; - var sy = step.sy; - var ex = step.ex; - var ey = step.ey; - if (sx === undefined) sx = step.x; - if (sy === undefined) sy = step.y; - if (ex === undefined) ex = step.x; - if (ey === undefined) ey = step.y; - Array2D.paintColor(sx, sy, ex, ey, colorClass, addClass); - } - break; - } -}; - -Array2DTracer.prototype.getCellCss = function () { - return { - padding: this.paddingV.toFixed(1) + 'px ' + this.paddingH.toFixed(1) + 'px', - 'font-size': this.fontSize.toFixed(1) + 'px' - }; -}; - -// Override -Array2DTracer.prototype.refresh = function () { - Tracer.prototype.refresh.call(this); - - var $parent = $table.parent(); - var top = $parent.height() / 2 - $table.height() / 2 + this.viewY; - var left = $parent.width() / 2 - $table.width() / 2 + this.viewX; - $table.css('margin-top', top); - $table.css('margin-left', left); -}; - -// Override -Array2DTracer.prototype.prevStep = function () { - this.clear(); - $('#tab_trace .wrapper').empty(); - var finalIndex = this.traceIndex - 1; - if (finalIndex < 0) { - this.traceIndex = -1; - return; - } - for (var i = 0; i < finalIndex; i++) { - this.step(i, { - virtual: true - }); - } - this.step(finalIndex); -}; - -// Override -Array2DTracer.prototype.mousedown = function (e) { - Tracer.prototype.mousedown.call(this, e); - - this.dragX = e.pageX; - this.dragY = e.pageY; - this.dragging = true; -}; - -// Override -Array2DTracer.prototype.mousemove = function (e) { - Tracer.prototype.mousemove.call(this, e); + break; + case 'set': + coord = { + coords: args[0] + }; + break; + default: + if (args[2] === undefined && args[3] === undefined) { + coord = { + x: args[0], + y: args[1] + }; + } else { + coord = { + sx: args[0], + sy: args[1], + ex: args[2], + ey: args[3] + }; + } + } + var step = { + type: type + }; + $.extend(step, coord); + this.pushStep(step, type == 'select'); + }, + processStep: function (step, options) { + switch (step.type) { + case 'notifying': + var $row = $table.find('.mtbl-row').eq(step.x); + $row.find('.mtbl-cell').eq(step.y).text(step.value); + case 'notified': + case 'select': + case 'deselect': + var colorClass = step.type == 'select' || step.type == 'deselect' ? this.colorClass.selected : this.colorClass.notifying; + var addClass = step.type == 'select' || step.type == 'notifying'; + if (step.coords) { + step.coords.forEach(function (coord) { + var x = coord.x; + var y = coord.y; + this.paintColor(x, y, x, y, colorClass, addClass); + }); + } else { + var sx = step.sx; + var sy = step.sy; + var ex = step.ex; + var ey = step.ey; + if (sx === undefined) sx = step.x; + if (sy === undefined) sy = step.y; + if (ex === undefined) ex = step.x; + if (ey === undefined) ey = step.y; + this.paintColor(sx, sy, ex, ey, colorClass, addClass); + } + break; + } + }, + getCellCss: function () { + return { + padding: this.paddingV.toFixed(1) + 'px ' + this.paddingH.toFixed(1) + 'px', + 'font-size': this.fontSize.toFixed(1) + 'px' + }; + }, + refresh: function () { + Tracer.prototype.refresh.call(this); + + var $parent = $table.parent(); + var top = $parent.height() / 2 - $table.height() / 2 + this.viewY; + var left = $parent.width() / 2 - $table.width() / 2 + this.viewX; + $table.css('margin-top', top); + $table.css('margin-left', left); + }, + prevStep: function () { + this.clear(); + $('#tab_trace .wrapper').empty(); + var finalIndex = this.traceIndex - 1; + if (finalIndex < 0) { + this.traceIndex = -1; + return; + } + for (var i = 0; i < finalIndex; i++) { + this.step(i, { + virtual: true + }); + } + this.step(finalIndex); + }, + mousedown: function (e) { + Tracer.prototype.mousedown.call(this, e); - if (this.dragging) { - this.viewX += e.pageX - this.dragX; - this.viewY += e.pageY - this.dragY; this.dragX = e.pageX; this.dragY = e.pageY; + this.dragging = true; + }, + mousemove: function (e) { + Tracer.prototype.mousemove.call(this, e); + + if (this.dragging) { + this.viewX += e.pageX - this.dragX; + this.viewY += e.pageY - this.dragY; + this.dragX = e.pageX; + this.dragY = e.pageY; + this.refresh(); + } + }, + mouseup: function (e) { + Tracer.prototype.mouseup.call(this, e); + + this.dragging = false; + }, + mousewheel: function (e) { + Tracer.prototype.mousewheel.call(this, e); + + e.preventDefault(); + e = e.originalEvent; + var delta = (e.wheelDelta !== undefined && e.wheelDelta) || + (e.detail !== undefined && -e.detail); + var weight = 1.01; + var ratio = delta > 0 ? 1 / weight : weight; + if (this.fontSize < 4 && ratio < 1) return; + if (this.fontSize > 40 && ratio > 1) return; + this.paddingV *= ratio; + this.paddingH *= ratio; + this.fontSize *= ratio; + $('.mtbl-cell').css(this.getCellCss()); this.refresh(); + }, + paintColor: function (sx, sy, ex, ey, colorClass, addClass) { + for (var i = sx; i <= ex; i++) { + var $row = $table.find('.mtbl-row').eq(i); + for (var j = sy; j <= ey; j++) { + var $cell = $row.find('.mtbl-cell').eq(j); + if (addClass) $cell.addClass(colorClass); + else $cell.removeClass(colorClass); + } + } + }, + clearColor: function () { + $table.find('.mtbl-cell').removeClass(Object.keys(this.colorClass).join(' ')); + }, + colorClass: { + selected: 'selected', + notifying: 'notifying' } -}; - -// Override -Array2DTracer.prototype.mouseup = function (e) { - Tracer.prototype.mouseup.call(this, e); - - this.dragging = false; -}; - -// Override -Array2DTracer.prototype.mousewheel = function (e) { - Tracer.prototype.mousewheel.call(this, e); - - e.preventDefault(); - e = e.originalEvent; - var delta = (e.wheelDelta !== undefined && e.wheelDelta) || - (e.detail !== undefined && -e.detail); - var weight = 1.01; - var ratio = delta > 0 ? 1 / weight : weight; - if (this.fontSize < 4 && ratio < 1) return; - if (this.fontSize > 40 && ratio > 1) return; - this.paddingV *= ratio; - this.paddingH *= ratio; - this.fontSize *= ratio; - $('.mtbl-cell').css(this.getCellCss()); - this.refresh(); -}; +}); var Array2D = { random: function (N, M, min, max) { @@ -299,26 +291,5 @@ var Array2D = { return a - b; }); }); - }, - initTable: function () { - $table = $('
'); - $module_container.append($table); - }, - paintColor: function (sx, sy, ex, ey, colorClass, addClass) { - for (var i = sx; i <= ex; i++) { - var $row = $table.find('.mtbl-row').eq(i); - for (var j = sy; j <= ey; j++) { - var $cell = $row.find('.mtbl-cell').eq(j); - if (addClass) $cell.addClass(colorClass); - else $cell.removeClass(colorClass); - } - } - }, - clearTableColor: function () { - $table.find('.mtbl-cell').removeClass(Object.keys(Array2D.tableColorClass).join(' ')); - }, - tableColorClass: { - selected: 'selected', - notifying: 'notifying' } }; \ No newline at end of file diff --git a/js/module/directed_graph.js b/js/module/directed_graph.js index ad4e91d09a0840d30e0c14a15949cf042bcd849c..977e402451ee4d28cead6124f2e1baa16a0241b4 100644 --- a/js/module/directed_graph.js +++ b/js/module/directed_graph.js @@ -2,185 +2,17 @@ var s = null, graph = null, sigmaCanvas = null; function DirectedGraphTracer(module) { if (Tracer.call(this, module || DirectedGraphTracer)) { - DirectedGraph.initGraph(); + DirectedGraphTracer.prototype.init.call(this); return true; } return false; } -DirectedGraphTracer.prototype = Object.create(Tracer.prototype); -DirectedGraphTracer.prototype.constructor = DirectedGraphTracer; +DirectedGraphTracer.prototype = $.extend(true, Object.create(Tracer.prototype), { + constructor: DirectedGraphTracer, + init: function () { + var tracer = this; -// Override -DirectedGraphTracer.prototype.resize = function () { - Tracer.prototype.resize.call(this); - - this.refresh(); -}; - -// Override -DirectedGraphTracer.prototype.clear = function () { - Tracer.prototype.clear.call(this); - - DirectedGraph.clearGraphColor(); -}; - -DirectedGraphTracer.prototype._setTreeData = function (G, root) { - root = root || 0; - var maxDepth = -1; - - var chk = []; - for (var i = 0; i < G.length; i++) chk.push(false); - var getDepth = function (node, depth) { - if (chk[node]) throw "the given graph is not a tree because it forms a circuit"; - chk[node] = true; - if (maxDepth < depth) maxDepth = depth; - for (var i = 0; i < G[node].length; i++) { - if (G[node][i]) getDepth(i, depth + 1); - } - }; - getDepth(root, 1); - - if (this._setData(G, root)) return true; - - var place = function (node, x, y) { - var temp = graph.nodes(DirectedGraph.n(node)); - temp.x = x; - temp.y = y; - }; - - var wgap = 1 / (maxDepth - 1); - var dfs = function (node, depth, top, bottom) { - place(node, depth * wgap, (top + bottom) / 2); - var children = 0; - for (var i = 0; i < G[node].length; i++) { - if (G[node][i]) children++; - } - var vgap = (bottom - top) / children; - var cnt = 0; - for (var i = 0; i < G[node].length; i++) { - if (G[node][i]) dfs(i, depth + 1, top + vgap * cnt, top + vgap * ++cnt); - } - }; - dfs(root, 0, 0, 1); - - this.refresh(); -}; - -// Override -DirectedGraphTracer.prototype._setData = function (G) { - if (Tracer.prototype._setData.call(this, arguments)) return true; - - graph.clear(); - var nodes = []; - var edges = []; - var unitAngle = 2 * Math.PI / G.length; - var currentAngle = 0; - for (var i = 0; i < G.length; i++) { - currentAngle += unitAngle; - nodes.push({ - id: DirectedGraph.n(i), - label: '' + i, - x: .5 + Math.sin(currentAngle) / 2, - y: .5 + Math.cos(currentAngle) / 2, - size: 1, - color: DirectedGraph.graphColor.default - }); - for (var j = 0; j < G[i].length; j++) { - if (G[i][j]) { - edges.push({ - id: DirectedGraph.e(i, j), - source: DirectedGraph.n(i), - target: DirectedGraph.n(j), - color: DirectedGraph.graphColor.default, - size: 1 - }); - } - } - } - - graph.read({ - nodes: nodes, - edges: edges - }); - s.camera.goTo({ - x: 0, - y: 0, - angle: 0, - ratio: 1 - }); - this.refresh(); - - return false; -}; - -DirectedGraphTracer.prototype._visit = function (target, source) { - this.pushStep({type: 'visit', target: target, source: source}, true); -}; - -DirectedGraphTracer.prototype._leave = function (target, source) { - this.pushStep({type: 'leave', target: target, source: source}, true); -}; - -DirectedGraphTracer.prototype.processStep = function (step, options) { - switch (step.type) { - case 'visit': - case 'leave': - var visit = step.type == 'visit'; - var targetNode = graph.nodes(DirectedGraph.n(step.target)); - var color = visit ? DirectedGraph.graphColor.visited : DirectedGraph.graphColor.left; - targetNode.color = color; - if (step.source !== undefined) { - var edgeId = DirectedGraph.e(step.source, step.target); - var edge = graph.edges(edgeId); - edge.color = color; - graph.dropEdge(edgeId).addEdge(edge); - } - var source = step.source; - if (source === undefined) source = ''; - printTrace(visit ? source + ' -> ' + step.target : source + ' <- ' + step.target); - break; - } -}; - -// Override -DirectedGraphTracer.prototype.refresh = function () { - Tracer.prototype.refresh.call(this); - - s.refresh(); -}; - -// Override -DirectedGraphTracer.prototype.prevStep = function () { - this.clear(); - $('#tab_trace .wrapper').empty(); - var finalIndex = this.traceIndex - 1; - if (finalIndex < 0) { - this.traceIndex = -1; - this.refresh(); - return; - } - for (var i = 0; i < finalIndex; i++) { - this.step(i, {virtual: true}); - } - this.step(finalIndex); -}; - -var DirectedGraph = { - random: function (N, ratio) { - if (!N) N = 5; - if (!ratio) ratio = .3; - var G = []; - for (var i = 0; i < N; i++) { - G.push([]); - for (var j = 0; j < N; j++) { - if (i == j) G[i].push(0); - else G[i].push((Math.random() * (1 / ratio) | 0) == 0 ? 1 : 0); - } - } - return G; - }, - initGraph: function () { if (sigmaCanvas == null) { sigmaCanvas = $.extend(true, {}, sigma.canvas); } else { @@ -204,32 +36,178 @@ var DirectedGraph = { minNodeSize: .5, maxNodeSize: 12, labelSize: 'proportional', - labelSizeRatio: 1.3, - edgeLabelSize: 'proportional', - defaultEdgeLabelSize: 20, - edgeLabelSizePowRatio: 0.8 + labelSizeRatio: 1.3 } }); graph = s.graph; - sigma.canvas.labels.def = DirectedGraph.drawLabel; - sigma.canvas.hovers.def = DirectedGraph.drawOnHover; + sigma.canvas.labels.def = function (node, context, settings) { + tracer.drawLabel(node, context, settings); + }; + sigma.canvas.hovers.def = function (node, context, settings, next) { + tracer.drawOnHover(node, context, settings, next); + }; sigma.canvas.edges.arrow = function (edge, source, target, context, settings) { - var color = DirectedGraph.getColor(edge, source, target, settings); - DirectedGraph.drawArrow(edge, source, target, color, context, settings); + var color = tracer.getColor(edge, source, target, settings); + tracer.drawArrow(edge, source, target, color, context, settings); }; sigma.plugins.dragNodes(s, s.renderers[0]); }, - graphColor: { + resize: function () { + Tracer.prototype.resize.call(this); + + this.refresh(); + }, + clear: function () { + Tracer.prototype.clear.call(this); + + this.clearGraphColor(); + }, + _setTreeData: function (G, root) { + var tracer = this; + + root = root || 0; + var maxDepth = -1; + + var chk = []; + for (var i = 0; i < G.length; i++) chk.push(false); + var getDepth = function (node, depth) { + if (chk[node]) throw "the given graph is not a tree because it forms a circuit"; + chk[node] = true; + if (maxDepth < depth) maxDepth = depth; + for (var i = 0; i < G[node].length; i++) { + if (G[node][i]) getDepth(i, depth + 1); + } + }; + getDepth(root, 1); + + if (this._setData(G, root)) return true; + + var place = function (node, x, y) { + var temp = graph.nodes(tracer.n(node)); + temp.x = x; + temp.y = y; + }; + + var wgap = 1 / (maxDepth - 1); + var dfs = function (node, depth, top, bottom) { + place(node, depth * wgap, (top + bottom) / 2); + var children = 0; + for (var i = 0; i < G[node].length; i++) { + if (G[node][i]) children++; + } + var vgap = (bottom - top) / children; + var cnt = 0; + for (var i = 0; i < G[node].length; i++) { + if (G[node][i]) dfs(i, depth + 1, top + vgap * cnt, top + vgap * ++cnt); + } + }; + dfs(root, 0, 0, 1); + + this.refresh(); + }, + _setData: function (G) { + if (Tracer.prototype._setData.call(this, arguments)) return true; + + graph.clear(); + var nodes = []; + var edges = []; + var unitAngle = 2 * Math.PI / G.length; + var currentAngle = 0; + for (var i = 0; i < G.length; i++) { + currentAngle += unitAngle; + nodes.push({ + id: this.n(i), + label: '' + i, + x: .5 + Math.sin(currentAngle) / 2, + y: .5 + Math.cos(currentAngle) / 2, + size: 1, + color: this.color.default + }); + for (var j = 0; j < G[i].length; j++) { + if (G[i][j]) { + edges.push({ + id: this.e(i, j), + source: this.n(i), + target: this.n(j), + color: this.color.default, + size: 1 + }); + } + } + } + + graph.read({ + nodes: nodes, + edges: edges + }); + s.camera.goTo({ + x: 0, + y: 0, + angle: 0, + ratio: 1 + }); + this.refresh(); + + return false; + }, + _visit: function (target, source) { + this.pushStep({type: 'visit', target: target, source: source}, true); + }, + _leave: function (target, source) { + this.pushStep({type: 'leave', target: target, source: source}, true); + }, + processStep: function (step, options) { + switch (step.type) { + case 'visit': + case 'leave': + var visit = step.type == 'visit'; + var targetNode = graph.nodes(this.n(step.target)); + var color = visit ? this.color.visited : this.color.left; + targetNode.color = color; + if (step.source !== undefined) { + var edgeId = this.e(step.source, step.target); + var edge = graph.edges(edgeId); + edge.color = color; + graph.dropEdge(edgeId).addEdge(edge); + } + var source = step.source; + if (source === undefined) source = ''; + this.printTrace(visit ? source + ' -> ' + step.target : source + ' <- ' + step.target); + break; + } + }, + refresh: function () { + Tracer.prototype.refresh.call(this); + + s.refresh(); + }, + prevStep: function () { + this.clear(); + $('#tab_trace .wrapper').empty(); + var finalIndex = this.traceIndex - 1; + if (finalIndex < 0) { + this.traceIndex = -1; + this.refresh(); + return; + } + for (var i = 0; i < finalIndex; i++) { + this.step(i, {virtual: true}); + } + this.step(finalIndex); + }, + color: { visited: '#f00', left: '#000', default: '#888' }, clearGraphColor: function () { + var tracer = this; + graph.nodes().forEach(function (node) { - node.color = DirectedGraph.graphColor.default; + node.color = tracer.color.default; }); graph.edges().forEach(function (edge) { - edge.color = DirectedGraph.graphColor.default; + edge.color = tracer.color.default; }); }, n: function (v) { @@ -326,6 +304,8 @@ var DirectedGraph = { context.fill(); }, drawOnHover: function (node, context, settings, next) { + var tracer = this; + var nodeIdx = node.id.substring(1); graph.edges().forEach(function (edge) { var ends = edge.id.substring(1).split("_"); @@ -334,15 +314,31 @@ var DirectedGraph = { var color = '#0ff'; var source = node; var target = graph.nodes('n' + ends[1]); - DirectedGraph.drawArrow(edge, source, target, color, context, settings); + tracer.drawArrow(edge, source, target, color, context, settings); if (next) next(edge, source, target, color, context, settings); } else if (ends[1] == nodeIdx) { var color = '#ff0'; var source = graph.nodes('n' + ends[0]); var target = node; - DirectedGraph.drawArrow(edge, source, target, color, context, settings); + tracer.drawArrow(edge, source, target, color, context, settings); if (next) next(edge, source, target, color, context, settings); } }); } +}); + +var DirectedGraph = { + random: function (N, ratio) { + if (!N) N = 5; + if (!ratio) ratio = .3; + var G = []; + for (var i = 0; i < N; i++) { + G.push([]); + for (var j = 0; j < N; j++) { + if (i == j) G[i].push(0); + else G[i].push((Math.random() * (1 / ratio) | 0) == 0 ? 1 : 0); + } + } + return G; + } }; \ No newline at end of file diff --git a/js/module/directed_weighted_graph.js b/js/module/directed_weighted_graph.js deleted file mode 100644 index 99c889b813f6606f8d9dae21f5ce4370460e639b..0000000000000000000000000000000000000000 --- a/js/module/directed_weighted_graph.js +++ /dev/null @@ -1,235 +0,0 @@ -function WeightedDirectedGraphTracer(module) { - if (DirectedGraphTracer.call(this, module || WeightedDirectedGraphTracer)) { - WeightedDirectedGraph.initWeightedGraph(); - return true; - } - return false; -} - -WeightedDirectedGraphTracer.prototype = Object.create(DirectedGraphTracer.prototype); -WeightedDirectedGraphTracer.prototype.constructor = WeightedDirectedGraphTracer; - -// Override -WeightedDirectedGraphTracer.prototype.clear = function () { - DirectedGraphTracer.prototype.clear.call(this); - - WeightedDirectedGraph.clearWeights(); -}; - -// Override -WeightedDirectedGraphTracer.prototype._setData = function (G) { - if (Tracer.prototype._setData.call(this, arguments)) return true; - - graph.clear(); - var nodes = []; - var edges = []; - var unitAngle = 2 * Math.PI / G.length; - var currentAngle = 0; - for (var i = 0; i < G.length; i++) { - currentAngle += unitAngle; - nodes.push({ - id: DirectedGraph.n(i), - label: '' + i, - x: .5 + Math.sin(currentAngle) / 2, - y: .5 + Math.cos(currentAngle) / 2, - size: 1, - color: DirectedGraph.graphColor.default, - weight: 0 - }); - for (var j = 0; j < G[i].length; j++) { - if (G[i][j]) { - edges.push({ - id: DirectedGraph.e(i, j), - source: DirectedGraph.n(i), - target: DirectedGraph.n(j), - color: DirectedGraph.graphColor.default, - size: 1, - weight: G[i][j] - }); - } - } - } - - graph.read({ - nodes: nodes, - edges: edges - }); - s.camera.goTo({ - x: 0, - y: 0, - angle: 0, - ratio: 1 - }); - this.refresh(); - - return false; -}; - -DirectedGraphTracer.prototype._weight = function (target, weight, delay) { - this.pushStep({type: 'weight', target: target, weight: weight}, delay); -}; - -DirectedGraphTracer.prototype._visit = function (target, source, weight) { - this.pushStep({type: 'visit', target: target, source: source, weight: weight}, true); -}; - -DirectedGraphTracer.prototype._leave = function (target, source, weight) { - this.pushStep({type: 'leave', target: target, source: source, weight: weight}, true); -}; - -//Override -WeightedDirectedGraphTracer.prototype.processStep = function (step, options) { - switch (step.type) { - case 'weight': - var targetNode = graph.nodes(DirectedGraph.n(step.target)); - if (step.weight !== undefined) targetNode.weight = step.weight; - break; - case 'visit': - case 'leave': - var visit = step.type == 'visit'; - var targetNode = graph.nodes(DirectedGraph.n(step.target)); - var color = visit ? DirectedGraph.graphColor.visited : DirectedGraph.graphColor.left; - targetNode.color = color; - if (step.weight !== undefined) targetNode.weight = step.weight; - if (step.source !== undefined) { - var edgeId = DirectedGraph.e(step.source, step.target); - var edge = graph.edges(edgeId); - edge.color = color; - graph.dropEdge(edgeId).addEdge(edge); - } - var source = step.source; - if (source === undefined) source = ''; - printTrace(visit ? source + ' -> ' + step.target : source + ' <- ' + step.target); - break; - default: - DirectedGraphTracer.prototype.processStep.call(this, step, options); - } -}; - -var WeightedDirectedGraph = { - random: function (N, ratio, min, max) { - if (!N) N = 5; - if (!ratio) ratio = .3; - if (!min) min = 1; - if (!max) max = 5; - var G = []; - for (var i = 0; i < N; i++) { - G.push([]); - for (var j = 0; j < N; j++) { - if (i == j) G[i].push(0); - else if ((Math.random() * (1 / ratio) | 0) == 0) { - G[i].push((Math.random() * (max - min + 1) | 0) + min); - } else { - G[i].push(0); - } - } - } - return G; - }, - clearWeights: function () { - graph.nodes().forEach(function (node) { - node.weight = 0; - }); - }, - drawEdgeWeight: function (edge, source, target, color, context, settings) { - if (source == target) - return; - - var prefix = settings('prefix') || '', - size = edge[prefix + 'size'] || 1; - - if (size < settings('edgeLabelThreshold')) - return; - - if (0 === settings('edgeLabelSizePowRatio')) - throw '"edgeLabelSizePowRatio" must not be 0.'; - - var fontSize, - x = (source[prefix + 'x'] + target[prefix + 'x']) / 2, - y = (source[prefix + 'y'] + target[prefix + 'y']) / 2, - dX = target[prefix + 'x'] - source[prefix + 'x'], - dY = target[prefix + 'y'] - source[prefix + 'y'], - angle = Math.atan2(dY, dX); - - fontSize = (settings('edgeLabelSize') === 'fixed') ? - settings('defaultEdgeLabelSize') : - settings('defaultEdgeLabelSize') * - size * - Math.pow(size, -1 / settings('edgeLabelSizePowRatio')); - - context.save(); - - if (edge.active) { - context.font = [ - settings('activeFontStyle'), - fontSize + 'px', - settings('activeFont') || settings('font') - ].join(' '); - - context.fillStyle = color; - } - else { - context.font = [ - settings('fontStyle'), - fontSize + 'px', - settings('font') - ].join(' '); - - context.fillStyle = color; - } - - context.textAlign = 'center'; - context.textBaseline = 'alphabetic'; - - context.translate(x, y); - context.rotate(angle); - context.fillText( - edge.weight, - 0, - (-size / 2) - 3 - ); - - context.restore(); - }, - initWeightedGraph: function () { - sigma.canvas.edges.arrow = function (edge, source, target, context, settings) { - var color = DirectedGraph.getColor(edge, source, target, settings); - DirectedGraph.drawArrow(edge, source, target, color, context, settings); - WeightedDirectedGraph.drawEdgeWeight(edge, source, target, color, context, settings); - }; - sigma.canvas.hovers.def = function (node, context, settings) { - DirectedGraph.drawOnHover(node, context, settings, function (edge, source, target, color, context, settings) { - WeightedDirectedGraph.drawEdgeWeight(edge, source, target, color, context, settings); - }); - }; - sigma.canvas.labels.def = function (node, context, settings) { - WeightedDirectedGraph.drawNodeWeight(node, context, settings); - DirectedGraph.drawLabel(node, context, settings); - } - }, - drawNodeWeight: function (node, context, settings) { - var fontSize, - prefix = settings('prefix') || '', - size = node[prefix + 'size']; - - if (size < settings('labelThreshold')) - return; - - fontSize = (settings('labelSize') === 'fixed') ? - settings('defaultLabelSize') : - settings('labelSizeRatio') * size; - - context.font = (settings('fontStyle') ? settings('fontStyle') + ' ' : '') + - fontSize + 'px ' + settings('font'); - context.fillStyle = (settings('labelColor') === 'node') ? - (node.color || settings('defaultNodeColor')) : - settings('defaultLabelColor'); - - context.textAlign = 'left'; - context.fillText( - node.weight, - Math.round(node[prefix + 'x'] + size * 1.5), - Math.round(node[prefix + 'y'] + fontSize / 3) - ); - } -}; \ No newline at end of file diff --git a/js/module/tracer.js b/js/module/tracer.js index 0cc0f8a5b6100e6c84d241dca00a0128fc62ce0d..8fedaead6e36b7466abed83c880c4c74fca4a151 100644 --- a/js/module/tracer.js +++ b/js/module/tracer.js @@ -15,147 +15,128 @@ var Tracer = function (module) { return moduleChanged; }; -Tracer.prototype.resize = function () { -}; - -Tracer.prototype.clear = function () { -}; - -Tracer.prototype.reset = function () { - this.traces = []; - this.stepCnt = 0; - if (timer) clearTimeout(timer); - $('#tab_trace .wrapper').empty(); - this.clear(); -}; - -Tracer.prototype._setData = function (arguments) { - var data = JSON.stringify(arguments); - if (lastModule == this.module && lastData == data) return true; - lastData = data; - return false; -}; - -Tracer.prototype.pushStep = function (step, delay) { - if (this.stepCnt++ > stepLimit) throw "Tracer's stack overflow"; - var len = this.traces.length; - var last = []; - if (len == 0) { - this.traces.push(last); - } else { - last = this.traces[len - 1]; - } - last.push(step); - if (delay) this.traces.push([]); -}; - -Tracer.prototype._sleep = function (duration) { - this.pushStep({type: 'sleep', duration: duration}, true); -}; - -Tracer.prototype._print = function (msg, delay) { - this.pushStep({type: 'print', msg: msg}, delay); -}; - -Tracer.prototype._pace = function (interval) { - this.pushStep({type: 'pace', interval: interval}, false); -}; - -Tracer.prototype._clear = function () { - this.pushStep({type: 'clear'}, true); -}; - -Tracer.prototype.visualize = function (options) { - options = options || {}; - options.interval = options.interval || 500; - - $('#btn_trace').click(); - this.traceOptions = options; - this.traceIndex = -1; - this.resumeStep(); -}; - -Tracer.prototype.isPause = function () { - return this.pause; -}; - -Tracer.prototype.pauseStep = function () { - if (this.traceIndex < 0) return; - this.pause = true; - if (timer) clearTimeout(timer); - $('#btn_pause').addClass('active'); -}; - -Tracer.prototype.resumeStep = function () { - this.pause = false; - this.step(this.traceIndex + 1); - $('#btn_pause').removeClass('active'); -}; - -Tracer.prototype.step = function (i, options) { - if (isNaN(i) || i >= this.traces.length || i < 0) return; - options = options || {}; - - this.traceIndex = i; - var trace = this.traces[i]; - var tracer = this; - var sleepDuration = 0; - trace.forEach(function (step) { - switch (step.type) { - case 'sleep': - sleepDuration = step.duration; - break; - case 'print': - printTrace(step.msg); - break; - case 'pace': - tracer.traceOptions.interval = step.interval; - break; - case 'clear': - tracer.clear(); - printTrace('clear traces'); - break; - default: - tracer.module.prototype.processStep.call(tracer, step, options); +Tracer.prototype = { + resize: function () { + }, + clear: function () { + }, + reset: function () { + this.traces = []; + this.stepCnt = 0; + if (timer) clearTimeout(timer); + $('#tab_trace .wrapper').empty(); + this.clear(); + }, + _setData: function (arguments) { + var data = JSON.stringify(arguments); + if (lastModule == this.module && lastData == data) return true; + lastData = data; + return false; + }, + pushStep: function (step, delay) { + if (this.stepCnt++ > stepLimit) throw "Tracer's stack overflow"; + var len = this.traces.length; + var last = []; + if (len == 0) { + this.traces.push(last); + } else { + last = this.traces[len - 1]; } - }); - if (!options.virtual) { - this.refresh(); - scrollToEnd(Math.min(50, this.traceOptions.interval)); + last.push(step); + if (delay) this.traces.push([]); + }, + _sleep: function (duration) { + this.pushStep({type: 'sleep', duration: duration}, true); + }, + _print: function (msg, delay) { + this.pushStep({type: 'print', msg: msg}, delay); + }, + _pace: function (interval) { + this.pushStep({type: 'pace', interval: interval}, false); + }, + _clear: function () { + this.pushStep({type: 'clear'}, true); + }, + visualize: function (options) { + options = options || {}; + options.interval = options.interval || 500; + + $('#btn_trace').click(); + this.traceOptions = options; + this.traceIndex = -1; + this.resumeStep(); + }, + isPause: function () { + return this.pause; + }, + pauseStep: function () { + if (this.traceIndex < 0) return; + this.pause = true; + if (timer) clearTimeout(timer); + $('#btn_pause').addClass('active'); + }, + resumeStep: function () { + this.pause = false; + this.step(this.traceIndex + 1); + $('#btn_pause').removeClass('active'); + }, + step: function (i, options) { + var tracer = this; + + if (isNaN(i) || i >= this.traces.length || i < 0) return; + options = options || {}; + + this.traceIndex = i; + var trace = this.traces[i]; + var sleepDuration = 0; + trace.forEach(function (step) { + switch (step.type) { + case 'sleep': + sleepDuration = step.duration; + break; + case 'print': + tracer.printTrace(step.msg); + break; + case 'pace': + tracer.traceOptions.interval = step.interval; + break; + case 'clear': + tracer.clear(); + tracer.printTrace('clear traces'); + break; + default: + tracer.module.prototype.processStep.call(tracer, step, options); + } + }); + if (!options.virtual) { + this.refresh(); + this.scrollToEnd(Math.min(50, this.traceOptions.interval)); + } + if (this.pause) return; + timer = setTimeout(function () { + tracer.step(i + 1, options); + }, sleepDuration || this.traceOptions.interval); + }, + refresh: function () { + }, + prevStep: function () { + this.step(this.traceIndex - 1); + }, + nextStep: function () { + this.step(this.traceIndex + 1); + }, + mousedown: function (e) { + }, + mousemove: function (e) { + }, + mouseup: function (e) { + }, + mousewheel: function (e) { + }, + printTrace: function (message) { + $('#tab_trace .wrapper').append($('').append(message + '
')); + }, + scrollToEnd: function (duration) { + $('#tab_trace').animate({scrollTop: $('#tab_trace')[0].scrollHeight}, duration); } - if (this.pause) return; - timer = setTimeout(function () { - tracer.step(i + 1, options); - }, sleepDuration || this.traceOptions.interval); -}; - -Tracer.prototype.refresh = function () { -}; - -Tracer.prototype.prevStep = function () { - this.step(this.traceIndex - 1); -}; - -Tracer.prototype.nextStep = function () { - this.step(this.traceIndex + 1); -}; - -Tracer.prototype.mousedown = function (e) { -}; - -Tracer.prototype.mousemove = function (e) { -}; - -Tracer.prototype.mouseup = function (e) { -}; - -Tracer.prototype.mousewheel = function (e) { -}; - -var printTrace = function (message) { - $('#tab_trace .wrapper').append($('').append(message + '
')); -}; - -var scrollToEnd = function (duration) { - $('#tab_trace').animate({scrollTop: $('#tab_trace')[0].scrollHeight}, duration); }; \ No newline at end of file diff --git a/js/module/weighted_directed_graph.js b/js/module/weighted_directed_graph.js new file mode 100644 index 0000000000000000000000000000000000000000..7f0e34ac315dbd148322ff108610dcc49ffbd85e --- /dev/null +++ b/js/module/weighted_directed_graph.js @@ -0,0 +1,232 @@ +function WeightedDirectedGraphTracer(module) { + if (DirectedGraphTracer.call(this, module || WeightedDirectedGraphTracer)) { + WeightedDirectedGraphTracer.prototype.init.call(this); + return true; + } + return false; +} + +WeightedDirectedGraphTracer.prototype = $.extend(true, Object.create(DirectedGraphTracer.prototype), { + constructor: WeightedDirectedGraphTracer, + init: function () { + var tracer = this; + + s.settings({ + edgeLabelSize: 'proportional', + defaultEdgeLabelSize: 20, + edgeLabelSizePowRatio: 0.8 + }); + sigma.canvas.edges.arrow = function (edge, source, target, context, settings) { + var color = tracer.getColor(edge, source, target, settings); + tracer.drawArrow(edge, source, target, color, context, settings); + tracer.drawEdgeWeight(edge, source, target, color, context, settings); + }; + sigma.canvas.hovers.def = function (node, context, settings) { + tracer.drawOnHover(node, context, settings, tracer.drawEdgeWeight); + }; + sigma.canvas.labels.def = function (node, context, settings) { + tracer.drawNodeWeight(node, context, settings); + tracer.drawLabel(node, context, settings); + } + }, + clear: function () { + DirectedGraphTracer.prototype.clear.call(this); + + this.clearWeights(); + }, + _setData: function (G) { + if (Tracer.prototype._setData.call(this, arguments)) return true; + + graph.clear(); + var nodes = []; + var edges = []; + var unitAngle = 2 * Math.PI / G.length; + var currentAngle = 0; + for (var i = 0; i < G.length; i++) { + currentAngle += unitAngle; + nodes.push({ + id: this.n(i), + label: '' + i, + x: .5 + Math.sin(currentAngle) / 2, + y: .5 + Math.cos(currentAngle) / 2, + size: 1, + color: this.color.default, + weight: 0 + }); + for (var j = 0; j < G[i].length; j++) { + if (G[i][j]) { + edges.push({ + id: this.e(i, j), + source: this.n(i), + target: this.n(j), + color: this.color.default, + size: 1, + weight: G[i][j] + }); + } + } + } + + graph.read({ + nodes: nodes, + edges: edges + }); + s.camera.goTo({ + x: 0, + y: 0, + angle: 0, + ratio: 1 + }); + this.refresh(); + + return false; + }, + _weight: function (target, weight, delay) { + this.pushStep({type: 'weight', target: target, weight: weight}, delay); + }, + _visit: function (target, source, weight) { + this.pushStep({type: 'visit', target: target, source: source, weight: weight}, true); + }, + _leave: function (target, source, weight) { + this.pushStep({type: 'leave', target: target, source: source, weight: weight}, true); + }, + processStep: function (step, options) { + switch (step.type) { + case 'weight': + var targetNode = graph.nodes(this.n(step.target)); + if (step.weight !== undefined) targetNode.weight = step.weight; + break; + case 'visit': + case 'leave': + var visit = step.type == 'visit'; + var targetNode = graph.nodes(this.n(step.target)); + var color = visit ? this.color.visited : this.color.left; + targetNode.color = color; + if (step.weight !== undefined) targetNode.weight = step.weight; + if (step.source !== undefined) { + var edgeId = this.e(step.source, step.target); + var edge = graph.edges(edgeId); + edge.color = color; + graph.dropEdge(edgeId).addEdge(edge); + } + var source = step.source; + if (source === undefined) source = ''; + this.printTrace(visit ? source + ' -> ' + step.target : source + ' <- ' + step.target); + break; + default: + DirectedGraphTracer.prototype.processStep.call(this, step, options); + } + }, + clearWeights: function () { + graph.nodes().forEach(function (node) { + node.weight = 0; + }); + }, + drawEdgeWeight: function (edge, source, target, color, context, settings) { + if (source == target) + return; + + var prefix = settings('prefix') || '', + size = edge[prefix + 'size'] || 1; + + if (size < settings('edgeLabelThreshold')) + return; + + if (0 === settings('edgeLabelSizePowRatio')) + throw '"edgeLabelSizePowRatio" must not be 0.'; + + var fontSize, + x = (source[prefix + 'x'] + target[prefix + 'x']) / 2, + y = (source[prefix + 'y'] + target[prefix + 'y']) / 2, + dX = target[prefix + 'x'] - source[prefix + 'x'], + dY = target[prefix + 'y'] - source[prefix + 'y'], + angle = Math.atan2(dY, dX); + + fontSize = (settings('edgeLabelSize') === 'fixed') ? + settings('defaultEdgeLabelSize') : + settings('defaultEdgeLabelSize') * + size * + Math.pow(size, -1 / settings('edgeLabelSizePowRatio')); + + context.save(); + + if (edge.active) { + context.font = [ + settings('activeFontStyle'), + fontSize + 'px', + settings('activeFont') || settings('font') + ].join(' '); + + context.fillStyle = color; + } + else { + context.font = [ + settings('fontStyle'), + fontSize + 'px', + settings('font') + ].join(' '); + + context.fillStyle = color; + } + + context.textAlign = 'center'; + context.textBaseline = 'alphabetic'; + + context.translate(x, y); + context.rotate(angle); + context.fillText( + edge.weight, + 0, + (-size / 2) - 3 + ); + + context.restore(); + }, + drawNodeWeight: function (node, context, settings) { + var fontSize, + prefix = settings('prefix') || '', + size = node[prefix + 'size']; + + if (size < settings('labelThreshold')) + return; + + fontSize = (settings('labelSize') === 'fixed') ? + settings('defaultLabelSize') : + settings('labelSizeRatio') * size; + + context.font = (settings('fontStyle') ? settings('fontStyle') + ' ' : '') + + fontSize + 'px ' + settings('font'); + context.fillStyle = (settings('labelColor') === 'node') ? + (node.color || settings('defaultNodeColor')) : + settings('defaultLabelColor'); + + context.textAlign = 'left'; + context.fillText( + node.weight, + Math.round(node[prefix + 'x'] + size * 1.5), + Math.round(node[prefix + 'y'] + fontSize / 3) + ); + } +}); + +var WeightedDirectedGraph = { + random: function (N, ratio, min, max) { + if (!N) N = 5; + if (!ratio) ratio = .3; + if (!min) min = 1; + if (!max) max = 5; + var G = []; + for (var i = 0; i < N; i++) { + G.push([]); + for (var j = 0; j < N; j++) { + if (i == j) G[i].push(0); + else if ((Math.random() * (1 / ratio) | 0) == 0) { + G[i].push((Math.random() * (max - min + 1) | 0) + min); + } else { + G[i].push(0); + } + } + } + return G; + } +}; \ No newline at end of file