xxxxxxxxxx
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
body {
margin: 1px;
}
div {
text-align: center;
}
p {
text-align: left;
padding-left: 1em;
text-indent: -1em;
line-height: 1;
padding-top: .35em;
font-size: 12px;
}
svg {
outline: 1px solid red;
overflow: visible;
display: inline-block;
}
.axis path {
stroke: #ccc;
}
.axis line {
stroke: #ccc;
stroke-opacity: .5;
}
.axis path {
fill: none;
}
.axis text {
font-size: 8px;
}
.min-nodes .datum {
/*fill: url(#filter-bubble-green-white);*/
fill: url(#filter-spherical-shading-black-white);
opacity: 1;
/*-webkit-filter: drop-shadow(10px 5px 5px blue);*/
}
.min-traffic .datum.visible {
/*fill: url(#filter-spherical-shading);*/
opacity: 1;
}
.min-traffic .datum {
opacity: 0;
}
</style>
<link rel="stylesheet" type="text/css" href="https://gitcdn.xyz/repo/cool-Blue/d3-lib/1dcef5868b3e774e8303d0c6eeaaef2528867893/inputs/button/style.css">
<!--<link rel="stylesheet" type="text/css" href="inputs/button/style.css">-->
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tinycolor/1.1.2/tinycolor.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/repo/cool-blue/d3-lib/filters/shadow.js"></script>
<script src="https://cdn.jsdelivr.net/gh/repo/cool-blue/d3-lib/inputs/button/button.js"></script>
<script>
var size = 500, r = 1.75, n = 1000;
var inputDiv = d3.select("body").append("div")
.attr({id: "inputs"})
.style({position: "absolute", margin: "20px"})
.call(inputs.number, [{
name: "nodeCount",
label: "nodes: ",
min:"1000",
max: "100,000",
step: "1000",
value: n,
onchange: function() {
dataSet = initData(+this.value);
quadtree = d3.geom.quadtree(dataSet);
minNodesRedraw();
minTrafficRedraw();
this.blur();
}
}]);
var iFrame = {width: 960, height: 406},
margin = {top: 30, right: 40, bottom: 30, left: 50},
geom = {outerWidth: iFrame.width/ 2, outerHeight: 200, margin: margin};
function initData(n){
return d3.range(n).map(function(d, idx) {
return {
x: d3.random.normal(100 / 2, 100 / 10)(),
y: d3.random.normal(100 / 2, 100 / 10)(),
uuid: idx
};
});
}
var dataSet = initData(n),
quadtree = d3.geom.quadtree(dataSet);
d3.select(self.frameElement).style({height: "406px", width: [iFrame.width,"px"].join("")});
function graph(selector, Owner, geom){
var margin = geom.margin,
width = geom.outerWidth - margin.left - margin.right,
height = geom.outerHeight - margin.top - margin.bottom,
_dropShadow = true,
_debug = true;
// Init Scales
var xScale = d3.scale.linear()
.domain([0, 100])
.range([0, width])
.nice(10);
var yScale = d3.scale.linear()
.domain([0, 100])
.range([height, 0])
.nice(10);
// Init Axes
var xAxis = d3.svg.axis()
.scale(xScale)
.ticks(10)
.orient("bottom")
.tickSize(-height);
var yAxis = d3.svg.axis()
.scale(yScale)
.ticks(10)
.orient("left")
.tickSize(-width);
// Init Zoom
var d3Zoom = d3.behavior.zoom()
.x(xScale)
.y(yScale)
.scaleExtent([0.99, Infinity])
.on("zoom", onZoom);
var root = d3.select(selector).selectAll("svg").data([geom]),
rootEnter = root.enter().append("svg")
.attr("width", geom.outerWidth)
.attr("height", geom.outerHeight),
title = textBox(root,
{width: width, height: margin.top/2, x: margin.left, y: 0},
selector),
containerClass = "body-container",
svg = root.append("g")
.attr("class", ["chart-body", containerClass].join(" "))
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(d3Zoom),
plotSurface = svg.append("rect")
.attr("class", "chart-body overlay")
.attr("width", width)
.attr("height", height)
.style({"fill": "none", opacity: 0.8})
.style("pointer-events", "all"),
clipRect = svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height),
circles = svg.append("g")
.attr("class", "chart-body data-points")
.style("filter", _dropShadow ? filters.drop(svg) : null)
.attr({"clip-path": "url(#clip)"}),
owner = Owner({root: root, xScale: xScale, yScale: yScale, title: title }),
redraw = function(data, nodes){
owner.redraw(data, nodes);
if(_debug) getBBox(".chart-body, .axis, .tick text")
},
bubble = Bubble(root);
d3Zoom.xAxis = svg.insert("g", function(){return circles.node()})
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
d3Zoom.yAxis = svg.insert("g", function(){return circles.node()})
.attr("class", "y axis")
.call(yAxis);
var dropShadow = {
label : "drop shadow",
onclick: function(d) {
circles.style("filter", d.value ? filters.drop(svg) : null);
onZoom();
this.blur();
},
value : true
},
debugMode = {
label : "debug mode",
onclick: function(d) {
if(!(_debug = d.value)) getBBox();
onZoom();
this.blur();
},
value : _debug
},
_controls = [dropShadow, debugMode],
controls = owner.controls && owner.controls.length ? _controls.concat(owner.controls)
: _controls,
bodyBB = getBBox("." + containerClass)[0],
footer = bodyBB.y + bodyBB.height,
controlPanel = htmlPanel(root, {
width: width, height: margin.top / 2, x: margin.left, y: footer
})
.call(inputs.toggle, controls)
.style({"font-size": "8px", padding: "3px 0 0 3px"});
owner.draw(circles);
onZoom();
return onZoom;
function onZoom() {
//TODO fix safari zoom bug: change zoom and zoom focus is not translated
// Store zoom extent
d3Zoom.extent = [
[xScale.invert(0), yScale.invert(height)],
[xScale.invert(width), yScale.invert(0)]
];
d3Zoom.scaleFactor = d3.event && d3.event.scale ? d3.event.scale : 1;
d3Zoom.translation = d3.event && d3.event.translate ? d3.event.translate : [0, 0];
// Update axes
d3Zoom.xAxis.call(xAxis);
d3Zoom.yAxis.call(yAxis);
// Prune non visible data
redraw(search(quadtree, d3Zoom.extent), circles);
}
function htmlPanel(root, attributes) {
return root.append("foreignObject")
.attr(attributes)
.append("xhtml:div")
}
function textBox(root, attributes, defaultText) {
return htmlPanel(root, attributes)
.append("p")
.text(defaultText);
}
function getBBox(types){
// create rectangles in the parent svg coordinate system for
// selected classes of chart components
//todo convert geometry to svg points and apply the transform to other attributes - eliminate the transform attribute
// if(true) return;
if(!types) {root.select(".g-chart-component").remove(); return}
// parse the types and build an array of bounding box attributes
var bBoxes = [], classTag = "chart-component",
elements = root.selectAll(types)
.each(function(d, i) {
var el = d3.select(this), title;
el.attr("title", title = (el.attr("class") || el.node().tagName).replace(classTag, ""));
var bBox = {indx: i, title: title};
bBox.relBB = d3.select(this).node().getBBox();
bBox.CTM = this.getCTM();
bBoxes.push(bBox)
}),
components = root.selectAll(".g-" + classTag).data([bBoxes]),
componentsEnter = components.enter().append("g")
.attr("class", "g-" + classTag),
boxes = components.selectAll("." + classTag).data(function(d){return d});
boxes.enter().append("rect")
.style("pointer-events", "none");
boxes.exit().remove();
boxes.attr("id", function(d){
return [d.title, d.indx].join("_")
})
.classed("chart-component", true)
.each(function(d) {
d3.select(this).attr(d.relBB)
})
.each(function(d) {
var svg = this.ownerSVGElement,
s = d3.select(this), pt = svg.createSVGPoint();
pt.x = s.attr("x"); pt.y = s.attr("y");
pt = pt.matrixTransform(d.CTM);
s.attr({x: pt.x, y: pt.y})
d.bb = this.getBBox();
})
.style({"stroke": "green", fill: "none", opacity: 0.3});
return bBoxes.map(function(d){return d.bb});
}
}
var minNodes = function(config) {
var fill = filters.sphere(config.root, "black");
config.title.text(config.title.text() + ":\tdelete nodes on zoom in, re-insert and sort nodes on zoom out")
function drawCircles(circles) {
return redraw((fill.map ? dataSet.map(fill.map) : dataSet), circles)
}
function redraw(subset, circles) {
// Attach new data
// keep the original order for object constancy
var elements = circles.selectAll("circle")
.data(subset.sort(function compareNumbers(a, b) {
return a.uuid - b.uuid;
}), function(d) {
return d.uuid
});
//enter
elements.enter().append("circle")
.style("fill", fill)
.attr("class", "datum")
.attr("r", r);
//exit
elements.exit().remove();
//update
updateSelection(elements);
return elements;
function updateSelection(elements) {
// some not so heavy duty stuff
elements.order().attr("transform", scaleData);
}
function scaleData(d) {
return "translate(" + [config.xScale(d.x), config.yScale(d.y)] + ")";
}
}
return {
draw : drawCircles,
redraw: redraw,
}
};
d3.select("body").append("div").attr("class", "min-nodes");
var minNodesRedraw = graph(".min-nodes", minNodes, geom);
var minTraffic = function(config) {
var fill = filters.sphere(config.root, "black"), //init uri for CSS
bubble = Bubble(config.root);
config.title.text(config.title.text()
+ ":\tnodes are never deleted, excluded nodes classed invisible and disconnected from transform")
function drawCircles(circles) {
return redraw(dataSet, circles);
}
function redraw(data, circles) {
var circle = circles
.selectAll("circle")
.data(dataSet, function(d) {
return d.uuid;
});
circle.enter().append("circle")
.attr("r", r)
.attr("class", "datum")
.attr("transform", ScaleData)
.call(bubble.call);
//exit
circle.exit().remove();
markSubset(data, circle);
updateSelection(circle);
return circle;
function markSubset(data, nodes) {
var marked = nodes.data(data, function(d) {
return d.uuid;
});
marked.enter();
marked.classed("visible", true)
marked.exit().classed("visible", false)
}
function updateSelection(elements) {
elements
.select(function() {
return d3.select(this)
.classed("visible") ? this : null;
})
.attr("transform", ScaleData);
}
}
function ScaleData(d) {
return "translate(" + [config.xScale(d.x), config.yScale(d.y)] + ")";
}
return {
draw : drawCircles,
redraw: redraw,
}
};
d3.select("body").append("div").attr("class", "min-traffic");
var minTrafficRedraw = graph(".min-traffic", minTraffic, geom);
function Bubble(svg){
var colors = d3.range(20).map(d3.scale.category20()).map(function(d){
return filters.sphere(svg, d)
});
return {
call: function(selection){
selection.style("fill", function(){
return colors[~~(Math.random()*20)]
})
},
map: function(d, i, data){
d.fill = colors[~~(Math.random()*20)];
},
fill: function(d){return d.fill}
}
};
// search quadtree
function search(qt, extent) {
var pts = [],
x0=extent[0][0], y0=extent[0][1],
x3=extent[1][0], y3=extent[1][1];
qt.visit(function(node, x1, y1, x2, y2) {
var p = node.point;
if ((p) && (p.x >= x0) && (p.x <= x3) && (p.y >= y0) && (p.y <= y3)) {
pts.push(p);
}
return x1 >= x3 || y1 >= y3 || x2 < x0 || y2 < y0;
});
return pts;
}
</script>
</body>
</html>
Updated missing url https://gitcdn.xyz/repo/cool-Blue/d3-lib/master/filters/shadow.js to https://cdn.jsdelivr.net/gh/repo/cool-blue/d3-lib/filters/shadow.js
Updated missing url https://gitcdn.xyz/repo/cool-Blue/d3-lib/master/inputs/button/button.js to https://cdn.jsdelivr.net/gh/repo/cool-blue/d3-lib/inputs/button/button.js
https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js
https://cdnjs.cloudflare.com/ajax/libs/tinycolor/1.1.2/tinycolor.min.js
https://gitcdn.xyz/repo/cool-Blue/d3-lib/master/filters/shadow.js
https://gitcdn.xyz/repo/cool-Blue/d3-lib/master/inputs/button/button.js