1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-09-28 07:08:20 +00:00

Fixed up the D3 demo plugin

This commit is contained in:
Jeremy Ruston 2013-10-24 20:45:53 +01:00
parent 3251aa191f
commit a5f5f1bd9c
3 changed files with 207 additions and 154 deletions

View File

@ -37,6 +37,14 @@ node ./tiddlywiki.js \
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/encrypted.html text/plain \ --new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/encrypted.html text/plain \
|| exit 1 || exit 1
# Fifth, d3demo.html: wiki to demo d3 plugin
node ./tiddlywiki.js \
./editions/d3demo \
--verbose \
--new_rendertiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/d3demo.html text/plain \
|| exit 1
# Run tests # Run tests
./test.sh ./test.sh

View File

@ -1,7 +1,7 @@
/*\ /*\
title: $:/plugins/tiddlywiki/d3/barwidget.js title: $:/plugins/tiddlywiki/d3/barwidget.js
type: application/javascript type: application/javascript
module-type: widget module-type: new_widget
A widget for displaying stacked or grouped bar charts. Derived from http://bl.ocks.org/mbostock/3943967 A widget for displaying stacked or grouped bar charts. Derived from http://bl.ocks.org/mbostock/3943967
@ -12,104 +12,94 @@ A widget for displaying stacked or grouped bar charts. Derived from http://bl.oc
/*global $tw: false */ /*global $tw: false */
"use strict"; "use strict";
var d3 = require("$:/plugins/tiddlywiki/d3/d3.js").d3; var Widget = require("$:/core/modules/new_widgets/widget.js").widget,
d3 = require("$:/plugins/tiddlywiki/d3/d3.js").d3;
var BarWidget = function(renderer) { var BarWidget = function(parseTreeNode,options) {
// Save state this.initialise(parseTreeNode,options);
this.renderer = renderer;
// Generate child nodes
this.generate();
}; };
BarWidget.prototype.generate = function() { /*
// Get the parameters Inherit from the base widget class
this.data = this.renderer.getAttribute("data"); */
this.grouped = this.renderer.getAttribute("grouped","no"); BarWidget.prototype = new Widget();
// Set the return element
this.tag = "div";
this.attributes = {
"class": "tw-barwidget"
};
};
BarWidget.prototype.postRenderInDom = function() { /*
this.updateChart = this.createChart(); Render this widget into the DOM
}; */
BarWidget.prototype.render = function(parent,nextSibling) {
BarWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) { // Save the parent dom node
// Reexecute the widget if the data reference attributes have changed this.parentDomNode = parent;
if(changedAttributes.data || changedTiddlers[this.data]) { // Compute our attributes
// Regenerate and rerender the widget and replace the existing DOM node this.computeAttributes();
this.generate(); // Execute our logic
var oldDomNode = this.renderer.domNode, this.execute();
newDomNode = this.renderer.renderInDom(); // Create the chart
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode); var chart = this.createChart(parent,nextSibling);
} else if(changedAttributes.grouped) { this.updateChart = chart.updateChart;
// Update the chart if the grouping setting has changed if(this.updateChart) {
this.grouped = this.renderer.getAttribute("grouped","no"); this.updateChart();
if(this.updateChart) {
this.updateChart();
}
} }
// Insert the chart into the DOM and render any children
parent.insertBefore(chart.domNode,nextSibling);
this.domNodes.push(chart.domNode);
}; };
BarWidget.prototype.createChart = function(parent,nextSibling) {
BarWidget.prototype.createChart = function() {
var n,m,stack,layers;
// Get the data we're plotting // Get the data we're plotting
var data = this.renderer.renderTree.wiki.getTiddlerData(this.data); var data = this.wiki.getTiddlerData(this.barData),
n,m,stack,layers;
if(data) { if(data) {
n = data.layers; n = data.layers;
m = data.samples; m = data.samples;
layers = data.data; layers = data.data;
} else { } else { // Use randomly generated data if we don't have any
n = 4; // number of layers n = 4; // number of layers
m = 58; // number of samples per layer m = 58; // number of samples per layer
stack = d3.layout.stack(); stack = d3.layout.stack();
layers = stack(d3.range(n).map(function() { return bumpLayer(m, .1); })); layers = stack(d3.range(n).map(function() { return bumpLayer(m, .1); }));
} }
// Calculate the maximum data values
var yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }), var yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); }); yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
// Calculate margins and width and height
var margin = {top: 40, right: 10, bottom: 20, left: 10}, var margin = {top: 40, right: 10, bottom: 20, left: 10},
width = 960 - margin.left - margin.right, width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom; height = 500 - margin.top - margin.bottom;
// x-scale
var x = d3.scale.ordinal() var x = d3.scale.ordinal()
.domain(d3.range(m)) .domain(d3.range(m))
.rangeRoundBands([0, width], .08); .rangeRoundBands([0, width], .08);
// y-scale
var y = d3.scale.linear() var y = d3.scale.linear()
.domain([0, yStackMax]) .domain([0, yStackMax])
.range([height, 0]); .range([height, 0]);
// Array of colour values
var color = d3.scale.linear() var color = d3.scale.linear()
.domain([0, n - 1]) .domain([0, n - 1])
.range(["#aad", "#556"]); .range(["#aad", "#556"]);
// x-axis
var xAxis = d3.svg.axis() var xAxis = d3.svg.axis()
.scale(x) .scale(x)
.tickSize(0) .tickSize(0)
.tickPadding(6) .tickPadding(6)
.orient("bottom"); .orient("bottom");
// Create SVG element
var svg = d3.select(this.renderer.domNode).append("svg") var svgElement = d3.select(parent).insert("svg",nextSibling)
.attr("viewBox", "0 0 960 500") .attr("viewBox", "0 0 960 500")
.attr("preserveAspectRatio", "xMinYMin meet") .attr("preserveAspectRatio", "xMinYMin meet")
.attr("width", width + margin.left + margin.right) .attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom) .attr("height", height + margin.top + margin.bottom);
.append("g") // Create main group
var mainGroup = svgElement.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Create the layers
var layer = svg.selectAll(".layer") var layer = mainGroup.selectAll(".layer")
.data(layers) .data(layers)
.enter().append("g") .enter().append("g")
.attr("class", "layer") .attr("class", "layer")
.style("fill", function(d, i) { return color(i); }); .style("fill", function(d, i) { return color(i); });
// Create the rectangles in each layer
var rect = layer.selectAll("rect") var rect = layer.selectAll("rect")
.data(function(d) { return d; }) .data(function(d) { return d; })
.enter().append("rect") .enter().append("rect")
@ -117,33 +107,31 @@ BarWidget.prototype.createChart = function() {
.attr("y", height) .attr("y", height)
.attr("width", x.rangeBand()) .attr("width", x.rangeBand())
.attr("height", 0); .attr("height", 0);
// Transition the rectangles to their final height
rect.transition() rect.transition()
.delay(function(d, i) { return i * 10; }) .delay(function(d, i) { return i * 10; })
.attr("y", function(d) { return y(d.y0 + d.y); }) .attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); }); .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); });
// Add to the DOM
svg.append("g") mainGroup.append("g")
.attr("class", "x axis") .attr("class", "x axis")
.attr("transform", "translate(0," + height + ")") .attr("transform", "translate(0," + height + ")")
.call(xAxis); .call(xAxis);
var self = this;
var self = this, // Return the svg node
updateChart = function() { return {
if (self.grouped !== "no") { domNode: svgElement[0][0],
transitionGrouped(); updateChart: function() {
} else { if (self.barGrouped !== "no") {
transitionStacked(); transitionGrouped();
} } else {
transitionStacked();
}
}
}; };
// Update the chart according to the grouped setting
updateChart();
// Return the update function
return updateChart;
function transitionGrouped() { function transitionGrouped() {
y.domain([0, yGroupMax]); y.domain([0, yGroupMax]);
rect.transition() rect.transition()
.duration(500) .duration(500)
.delay(function(d, i) { return i * 10; }) .delay(function(d, i) { return i * 10; })
@ -156,7 +144,6 @@ BarWidget.prototype.createChart = function() {
function transitionStacked() { function transitionStacked() {
y.domain([0, yStackMax]); y.domain([0, yStackMax]);
rect.transition() rect.transition()
.duration(500) .duration(500)
.delay(function(d, i) { return i * 10; }) .delay(function(d, i) { return i * 10; })
@ -169,7 +156,6 @@ BarWidget.prototype.createChart = function() {
// Inspired by Lee Byron's test data generator. // Inspired by Lee Byron's test data generator.
function bumpLayer(n, o) { function bumpLayer(n, o) {
function bump(a) { function bump(a) {
var x = 1 / (.1 + Math.random()), var x = 1 / (.1 + Math.random()),
y = 2 * Math.random() - .5, y = 2 * Math.random() - .5,
@ -179,13 +165,48 @@ BarWidget.prototype.createChart = function() {
a[i] += x * Math.exp(-w * w); a[i] += x * Math.exp(-w * w);
} }
} }
var a = [], i; var a = [], i;
for (i = 0; i < n; ++i) a[i] = o + o * Math.random(); for (i = 0; i < n; ++i) a[i] = o + o * Math.random();
for (i = 0; i < 5; ++i) bump(a); for (i = 0; i < 5; ++i) bump(a);
return a.map(function(d, i) { return {x: i, y: Math.max(0, d)}; }); return a.map(function(d, i) { return {x: i, y: Math.max(0, d)}; });
} }
};
/*
Compute the internal state of the widget
*/
BarWidget.prototype.execute = function() {
// Get the parameters from the attributes
this.barData = this.getAttribute("data");
this.barGrouped = this.getAttribute("grouped","no");
};
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
BarWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.data || changedTiddlers[this.barData]) {
this.refreshSelf();
return true;
} else if(changedAttributes.grouped) {
this.execute();
if(this.updateChart) {
this.updateChart();
}
return true;
}
return false;
};
/*
Remove any DOM nodes created by this widget or its children
*/
BarWidget.prototype.removeChildDomNodes = function() {
$tw.utils.each(this.domNodes,function(domNode) {
domNode.parentNode.removeChild(domNode);
});
this.domNodes = [];
}; };
exports.d3bar = BarWidget; exports.d3bar = BarWidget;

View File

@ -1,7 +1,7 @@
/*\ /*\
title: $:/plugins/tiddlywiki/d3/cloudwidget.js title: $:/plugins/tiddlywiki/d3/cloudwidget.js
type: application/javascript type: application/javascript
module-type: widget module-type: new_widget
A widget for displaying word clouds. Derived from https://github.com/jasondavies/d3-cloud A widget for displaying word clouds. Derived from https://github.com/jasondavies/d3-cloud
@ -12,7 +12,8 @@ A widget for displaying word clouds. Derived from https://github.com/jasondavies
/*global $tw: false */ /*global $tw: false */
"use strict"; "use strict";
var d3 = require("$:/plugins/tiddlywiki/d3/d3.js").d3; var Widget = require("$:/core/modules/new_widgets/widget.js").widget,
d3 = require("$:/plugins/tiddlywiki/d3/d3.js").d3;
if($tw.browser) { if($tw.browser) {
// Frightful hack to give the cloud plugin the global d3 variable it needs // Frightful hack to give the cloud plugin the global d3 variable it needs
@ -20,98 +21,121 @@ if($tw.browser) {
d3.layout.cloud = require("$:/plugins/tiddlywiki/d3/d3.layout.cloud.js").cloud; d3.layout.cloud = require("$:/plugins/tiddlywiki/d3/d3.layout.cloud.js").cloud;
} }
var CloudWidget = function(renderer) { var CloudWidget = function(parseTreeNode,options) {
// Save state this.initialise(parseTreeNode,options);
this.renderer = renderer;
// Generate child nodes
this.generate();
}; };
CloudWidget.prototype.generate = function() { /*
// Get the parameters Inherit from the base widget class
this.data = this.renderer.getAttribute("data"); */
this.spiral = this.renderer.getAttribute("spiral","archimedean"); CloudWidget.prototype = new Widget();
// Set the return element
this.tag = "div"; /*
this.attributes = { Render this widget into the DOM
"class": "tw-cloudwidget" */
CloudWidget.prototype.render = function(parent,nextSibling) {
// Save the parent dom node
this.parentDomNode = parent;
// Compute our attributes
this.computeAttributes();
// Execute our logic
this.execute();
// Create the chart
var chart = this.createChart(parent,nextSibling);
this.updateChart = chart.updateChart;
if(this.updateChart) {
this.updateChart();
}
// Insert the chart into the DOM and render any children
parent.insertBefore(chart.domNode,nextSibling);
this.domNodes.push(chart.domNode);
};
CloudWidget.prototype.createChart = function(parent,nextSibling) {
var self = this,
fill = d3.scale.category20(),
data = this.wiki.getTiddlerData(this.cloudData);
// Use dummy data if none provided
if(!data) {
data = "This word cloud does not have any data in it".split(" ").map(function(d) {
return {text: d, size: 10 + Math.random() * 90};
});
}
// Create the svg element
var svgElement = d3.select(parent).insert("svg",nextSibling)
.attr("width", 600)
.attr("height", 400);
// Create the main group
var mainGroup = svgElement
.append("g")
.attr("transform", "translate(300,200)");
// Create the layout
var layout = d3.layout.cloud().size([600, 400])
.words(data)
.padding(5)
.rotate(function() { return ~~(Math.random() * 5) * 30 - 60; })
.font("Impact")
.fontSize(function(d) { return d.size*2; })
.on("end", draw)
.start();
// Function to draw all the words
function draw(words) {
mainGroup.selectAll("text")
.data(words)
.enter().append("text")
.style("font-size", function(d) { return d.size + "px"; })
.style("font-family", "Impact")
.style("fill", function(d, i) { return fill(i); })
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.text(function(d) { return d.text; });
}
function updateChart() {
layout.spiral(self.spiral);
}
return {
domNode: svgElement[0][0],
updateChart: updateChart
}; };
}; };
CloudWidget.prototype.postRenderInDom = function() { /*
this.updateChart = this.createChart(); Compute the internal state of the widget
*/
CloudWidget.prototype.execute = function() {
// Get the parameters from the attributes
this.cloudData = this.getAttribute("data");
this.cloudSpiral = this.getAttribute("spiral","archimedean");
}; };
CloudWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) { /*
// Reexecute the widget if the data reference attributes have changed Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
if(changedAttributes.data || changedTiddlers[this.data]) { */
// Regenerate and rerender the widget and replace the existing DOM node CloudWidget.prototype.refresh = function(changedTiddlers) {
this.generate(); var changedAttributes = this.computeAttributes();
var oldDomNode = this.renderer.domNode, if(changedAttributes.data || changedTiddlers[this.cloudData]) {
newDomNode = this.renderer.renderInDom(); this.refreshSelf();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode); return true;
} else if(changedAttributes.spiral) { } else if(changedAttributes.spiral) {
// Update the chart if the spiral setting has changed this.execute();
this.spiral = this.renderer.getAttribute("spiral","archimedean");
if(this.updateChart) { if(this.updateChart) {
this.updateChart(); this.updateChart();
} }
return true;
} }
return false;
}; };
CloudWidget.prototype.createChart = function() { /*
Remove any DOM nodes created by this widget or its children
var self = this; */
CloudWidget.prototype.removeChildDomNodes = function() {
var domNode = this.renderer.domNode; $tw.utils.each(this.domNodes,function(domNode) {
domNode.parentNode.removeChild(domNode);
var fill = d3.scale.category20(); });
this.domNodes = [];
var data = this.renderer.renderTree.wiki.getTiddlerData(this.data);
if(!data) {
// Use dummy data if none provided
data = "This word cloud does not have any data in it".split(" ").map(function(d) {
return {text: d, size: 10 + Math.random() * 90};
})
}
var svg = d3.select(domNode).append("svg")
.attr("width", 600)
.attr("height", 400)
.append("g")
.attr("transform", "translate(300,200)");
var layout = d3.layout.cloud().size([600, 400])
.words(data)
.padding(5)
.rotate(function() { return ~~(Math.random() * 5) * 30 - 60; })
.font("Impact")
.fontSize(function(d) { return d.size*2; })
.on("end", draw)
.start();
function draw(words) {
svg.selectAll("text")
.data(words)
.enter().append("text")
.style("font-size", function(d) { return d.size + "px"; })
.style("font-family", "Impact")
.style("fill", function(d, i) { return fill(i); })
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.text(function(d) { return d.text; });
}
function updateChart() {
layout.spiral(self.spiral);
}
return updateChart;
}; };
exports.d3cloud = CloudWidget; exports.d3cloud = CloudWidget;