Skip to content

Commit a475cd5

Browse files
committed
Query the brush selection.
You can now query the brush for its selection. This commit also includes a new d3.random.normal for generating random numbers with a normal distribution. This is useful for jittering points for display.
1 parent 42a59ac commit a475cd5

9 files changed

Lines changed: 189 additions & 82 deletions

File tree

‎Makefile‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ d3.core.js: \
4848
src/core/median.js \
4949
src/core/min.js \
5050
src/core/max.js \
51+
src/core/random.js \
5152
src/core/number.js \
5253
src/core/sum.js \
5354
src/core/quantile.js \

‎d3.js‎

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,21 @@ d3.max = function(array, f) {
105105
}
106106
return a;
107107
};
108+
d3.random = {
109+
normal: function(mean, deviation) {
110+
if (arguments.length < 2) deviation = 1;
111+
if (arguments.length < 1) mean = 0;
112+
return function() {
113+
var x = 0, y = 0, r;
114+
do {
115+
x = Math.random() * 2 - 1;
116+
y = Math.random() * 2 - 1;
117+
r = x * x + y * y;
118+
} while (!r || r > 1);
119+
return mean + deviation * x * Math.sqrt(-2 * Math.log(r) / r);
120+
};
121+
}
122+
};
108123
function d3_number(x) {
109124
return x != null && !isNaN(x);
110125
}
@@ -3897,11 +3912,10 @@ d3.svg.brush = function() {
38973912
function down() {
38983913
var target = d3.select(d3.event.target);
38993914

3915+
d3_svg_brush = brush;
39003916
d3_svg_brushEvent = event.brush;
39013917
d3_svg_brushTarget = this;
39023918
d3_svg_brushArguments = arguments;
3903-
d3_svg_brushX = x;
3904-
d3_svg_brushY = y;
39053919
d3_svg_brushSelection = selection;
39063920
d3_svg_brushOffset = d3.svg.mouse(d3_svg_brushTarget);
39073921
d3_eventCancel();
@@ -3920,6 +3934,10 @@ d3.svg.brush = function() {
39203934
d3_svg_brushOffset[0] = selection[+/w$/.test(d3_svg_brushMode)][0];
39213935
d3_svg_brushOffset[1] = selection[+/^n/.test(d3_svg_brushMode)][1];
39223936
}
3937+
3938+
// Restrict which dimensions are resized.
3939+
d3_svg_brushX = !/^(n|s)$/.test(d3_svg_brushMode) && x;
3940+
d3_svg_brushY = !/^(e|w)$/.test(d3_svg_brushMode) && y;
39233941
}
39243942

39253943
brush.x = function(z) {
@@ -3934,6 +3952,17 @@ d3.svg.brush = function() {
39343952
return brush;
39353953
};
39363954

3955+
brush.selection = function() {
3956+
var sx = d3_svg_brushInvert(x, selection, 0),
3957+
sy = d3_svg_brushInvert(y, selection, 1);
3958+
return x && y ? d3.zip(sx, sy) : sx || sy;
3959+
};
3960+
3961+
brush.empty = function() {
3962+
return (x && selection[0][0] === selection[1][0])
3963+
|| (y && selection[0][1] === selection[1][1]);
3964+
};
3965+
39373966
brush.on = function(type, listener) {
39383967
event.on(type, listener);
39393968
return brush;
@@ -3949,36 +3978,51 @@ d3.svg.brush = function() {
39493978
var d3_svg_brushResizeXY = ["n", "e", "s", "w", "nw", "ne", "se", "sw"],
39503979
d3_svg_brushResizeX = ["e", "w"],
39513980
d3_svg_brushResizeY = ["n", "s"],
3981+
d3_svg_brush,
39523982
d3_svg_brushEvent,
39533983
d3_svg_brushTarget,
39543984
d3_svg_brushArguments,
39553985
d3_svg_brushX,
39563986
d3_svg_brushY,
3987+
d3_svg_brushSelection,
39573988
d3_svg_brushMode,
39583989
d3_svg_brushOffset;
39593990

3991+
function d3_svg_brushInvert(scale, selection, i) {
3992+
if (scale) {
3993+
selection = [scale.invert(selection[0][i]), scale.invert(selection[1][i])];
3994+
return selection[1] < selection[0] ? selection.reverse() : selection;
3995+
}
3996+
}
3997+
39603998
function d3_svg_brushMove() {
39613999
if (d3_svg_brushOffset) {
39624000
var mouse = d3.svg.mouse(d3_svg_brushTarget),
3963-
g = d3.select(d3_svg_brushTarget);
4001+
g = d3.select(d3_svg_brushTarget),
4002+
e = d3.event;
39644003

3965-
if (d3_svg_brushX && !/^(n|s)$/.test(d3_svg_brushMode)) {
4004+
if (d3_svg_brushX) {
39664005
d3_svg_brushMove1(mouse, d3_svg_brushX, 0);
39674006
g.select(".selection").attr("x", d3_svg_brushSelection[0][0]);
39684007
g.selectAll(".n,.s,.w,.nw,.sw").attr("x", d3_svg_brushSelection[0][0] - 2);
39694008
g.selectAll(".e,.ne,.se").attr("x", d3_svg_brushSelection[1][0] - 3);
39704009
g.selectAll(".selection,.n,.s").attr("width", d3_svg_brushSelection[1][0] - d3_svg_brushSelection[0][0]);
39714010
}
39724011

3973-
if (d3_svg_brushY && !/^(e|w)$/.test(d3_svg_brushMode)) {
4012+
if (d3_svg_brushY) {
39744013
d3_svg_brushMove1(mouse, d3_svg_brushY, 1);
39754014
g.select(".selection").attr("y", d3_svg_brushSelection[0][1]);
39764015
g.selectAll(".n,.e,.w,.nw,.ne").attr("y", d3_svg_brushSelection[0][1] - 3);
39774016
g.selectAll(".s,.se,.sw").attr("y", d3_svg_brushSelection[1][1] - 4);
39784017
g.selectAll(".selection,.e,.w").attr("height", d3_svg_brushSelection[1][1] - d3_svg_brushSelection[0][1]);
39794018
}
39804019

3981-
d3_svg_brushEvent.apply(d3_svg_brushTarget, d3_svg_brushArguments);
4020+
try {
4021+
d3.event = {target: d3_svg_brush};
4022+
d3_svg_brushEvent.apply(d3_svg_brushTarget, d3_svg_brushArguments);
4023+
} finally {
4024+
d3.event = e;
4025+
}
39824026
}
39834027
}
39844028

@@ -4016,11 +4060,13 @@ function d3_svg_brushMove1(mouse, scale, i) {
40164060
function d3_svg_brushUp() {
40174061
if (d3_svg_brushOffset) {
40184062
d3_svg_brushMove();
4063+
d3_svg_brush =
40194064
d3_svg_brushEvent =
40204065
d3_svg_brushTarget =
40214066
d3_svg_brushArguments =
40224067
d3_svg_brushX =
40234068
d3_svg_brushY =
4069+
d3_svg_brushSelection =
40244070
d3_svg_brushMode =
40254071
d3_svg_brushOffset = null;
40264072
d3_eventCancel();

‎d3.min.js‎

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎examples/brush/brush-parallel.html‎

Lines changed: 0 additions & 60 deletions
This file was deleted.

‎examples/brush/brush-x.html‎

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@
1010
font: 10px sans-serif;
1111
}
1212

13+
.selecting circle {
14+
fill: #ccc;
15+
}
16+
17+
.selecting circle.selected {
18+
fill: #f00;
19+
stroke: #000;
20+
}
21+
1322
.axis path, .axis line {
1423
fill: none;
1524
stroke: #000;
@@ -27,11 +36,14 @@
2736
<body>
2837
<script type="text/javascript">
2938

39+
var data = d3.range(800).map(Math.random);
40+
3041
var m = [10, 10, 20, 10],
3142
w = 960 - m[1] - m[3],
32-
h = 500 - m[0] - m[2];
43+
h = 100 - m[0] - m[2];
3344

34-
var x = d3.scale.linear().range([0, w]);
45+
var x = d3.scale.linear().range([0, w]),
46+
y = d3.random.normal(h / 2, h / 8);
3547

3648
var svg = d3.select("body").append("svg:svg")
3749
.attr("width", w + m[1] + m[3])
@@ -44,12 +56,24 @@
4456
.attr("transform", "translate(0," + h + ")")
4557
.call(d3.svg.axis().scale(x).orient("bottom"));
4658

59+
var circle = svg.selectAll("circle")
60+
.data(data)
61+
.enter().append("svg:circle")
62+
.attr("transform", function(d) { return "translate(" + x(d) + "," + y() + ")"; })
63+
.attr("r", 3.5);
64+
4765
svg.append("svg:g")
4866
.attr("class", "brush")
49-
.call(d3.svg.brush().x(x))
67+
.call(d3.svg.brush().x(x).on("brush", brush))
5068
.selectAll("rect")
5169
.attr("height", h);
5270

71+
function brush() {
72+
var s = d3.event.target.selection();
73+
circle.classed("selected", function(d) { return s[0] <= d && d < s[1]; });
74+
svg.classed("selecting", !d3.event.target.empty());
75+
}
76+
5377
</script>
5478
</body>
5579
</html>

‎examples/brush/brush-y.html‎

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@
1010
font: 10px sans-serif;
1111
}
1212

13+
.selecting circle {
14+
fill: #ccc;
15+
}
16+
17+
.selecting circle.selected {
18+
fill: red;
19+
stroke: black;
20+
}
21+
1322
.axis path, .axis line {
1423
fill: none;
1524
stroke: #000;
@@ -27,11 +36,14 @@
2736
<body>
2837
<script type="text/javascript">
2938

39+
var data = d3.range(800).map(Math.random);
40+
3041
var m = [10, 10, 10, 40],
31-
w = 960 - m[1] - m[3],
32-
h = 500 - m[0] - m[2];
42+
w = 120 - m[1] - m[3],
43+
h = 960 - m[0] - m[2];
3344

34-
var y = d3.scale.linear().range([h, 0]);
45+
var x = d3.random.normal(w / 2, w / 8),
46+
y = d3.scale.linear().range([h, 0]);
3547

3648
var svg = d3.select("body").append("svg:svg")
3749
.attr("width", w + m[1] + m[3])
@@ -43,12 +55,24 @@
4355
.attr("class", "y axis")
4456
.call(d3.svg.axis().scale(y).orient("left"));
4557

58+
var circle = svg.selectAll("circle")
59+
.data(data)
60+
.enter().append("svg:circle")
61+
.attr("transform", function(d) { return "translate(" + x() + "," + y(d) + ")"; })
62+
.attr("r", 3.5);
63+
4664
svg.append("svg:g")
4765
.attr("class", "brush")
48-
.call(d3.svg.brush().y(y))
66+
.call(d3.svg.brush().y(y).on("brush", brush))
4967
.selectAll("rect")
5068
.attr("width", w);
5169

70+
function brush() {
71+
var s = d3.event.target.selection();
72+
circle.classed("selected", function(d) { return s[0] <= d && d < s[1]; });
73+
svg.classed("selecting", !d3.event.target.empty());
74+
}
75+
5276
</script>
5377
</body>
5478
</html>

‎examples/brush/brush.html‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@
1010
font: 10px sans-serif;
1111
}
1212

13+
.selecting circle {
14+
fill: #ccc;
15+
}
16+
17+
.selecting circle.selected {
18+
fill: #f00;
19+
stroke: #000;
20+
}
21+
1322
.axis path, .axis line {
1423
fill: none;
1524
stroke: #000;
@@ -27,6 +36,8 @@
2736
<body>
2837
<script type="text/javascript">
2938

39+
var data = d3.range(800).map(function() { return [Math.random(), Math.random()]; });
40+
3041
var m = [10, 10, 20, 40],
3142
w = 960 - m[1] - m[3],
3243
h = 500 - m[0] - m[2];
@@ -49,9 +60,24 @@
4960
.attr("class", "y axis")
5061
.call(d3.svg.axis().scale(y).orient("left"));
5162

63+
var circle = svg.selectAll("circle")
64+
.data(data)
65+
.enter().append("svg:circle")
66+
.attr("transform", function(d) { return "translate(" + x(d[0]) + "," + y(d[1]) + ")"; })
67+
.attr("r", 3.5);
68+
5269
svg.append("svg:g")
5370
.attr("class", "brush")
54-
.call(d3.svg.brush().x(x).y(y));
71+
.call(d3.svg.brush().x(x).y(y).on("brush", brush));
72+
73+
function brush() {
74+
var s = d3.event.target.selection();
75+
circle.classed("selected", function(d) {
76+
return s[0][0] <= d[0] && d[0] < s[1][0]
77+
&& s[0][1] <= d[1] && d[1] < s[1][1];
78+
});
79+
svg.classed("selecting", !d3.event.target.empty());
80+
}
5581

5682
</script>
5783
</body>

‎src/core/random.js‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
d3.random = {
2+
normal: function(mean, deviation) {
3+
if (arguments.length < 2) deviation = 1;
4+
if (arguments.length < 1) mean = 0;
5+
return function() {
6+
var x, y, r;
7+
do {
8+
x = Math.random() * 2 - 1;
9+
y = Math.random() * 2 - 1;
10+
r = x * x + y * y;
11+
} while (!r || r > 1);
12+
return mean + deviation * x * Math.sqrt(-2 * Math.log(r) / r);
13+
};
14+
}
15+
};

0 commit comments

Comments
 (0)