|
27 | 27 | h1 = 400, |
28 | 28 | h2 = 40, |
29 | 29 | p = 20, |
| 30 | + x0, // start of focus region |
| 31 | + x1, // end of focus region |
| 32 | + xx, // drag state |
30 | 33 | x = d3.scale.linear().range([0, w]), |
31 | 34 | y1 = d3.scale.linear().range([h1 - p, 0]), |
32 | 35 | y2 = d3.scale.linear().range([h2, 0]); |
|
36 | 39 | .attr("width", w) |
37 | 40 | .attr("height", h1 + h2); |
38 | 41 |
|
| 42 | +// Focus view. |
| 43 | +var focus = svg.append("svg:g"); |
| 44 | + |
| 45 | +// Context view. |
| 46 | +var context = svg.append("svg:g") |
| 47 | + .attr("transform", "translate(0," + h1 + ")"); |
| 48 | + |
39 | 49 | d3.csv("dji.csv", function(csv) { |
40 | 50 | var minX = Infinity, |
41 | 51 | maxX = -Infinity, |
|
50 | 60 | if (o.y > maxY) maxY = o.y; |
51 | 61 | } |
52 | 62 |
|
53 | | - // Update x- and y-scales. TODO y1 domain is filtered. |
| 63 | + // Update x- and y-scales. |
54 | 64 | x.domain([minX, maxX]); |
55 | 65 | y1.domain([0, maxY]); |
56 | 66 | y2.domain([0, maxY]); |
57 | 67 |
|
58 | 68 | // Focus view. |
59 | | - var focus = svg.append("svg:g"); |
60 | | - |
61 | 69 | focus.append("svg:path") |
62 | 70 | .data([csv]) |
63 | 71 | .attr("d", d3.svg.area() |
|
72 | 80 | .attr("y2", y1(0)); |
73 | 81 |
|
74 | 82 | // Context view. |
75 | | - var context = svg.append("svg:g") |
76 | | - .attr("transform", "translate(0," + h1 + ")"); |
| 83 | + context.append("svg:rect") |
| 84 | + .attr("width", w) |
| 85 | + .attr("height", h2) |
| 86 | + .attr("fill", "none") |
| 87 | + .attr("pointer-events", "all") |
| 88 | + .attr("cursor", "crosshair") |
| 89 | + .on("mousedown", mousedown); |
77 | 90 |
|
78 | 91 | context.append("svg:path") |
79 | 92 | .data([csv]) |
| 93 | + .attr("pointer-events", "none") |
80 | 94 | .attr("d", d3.svg.area() |
81 | 95 | .x(function(d) { return x(d.x); }) |
82 | 96 | .y0(y2(0)) |
|
87 | 101 | .attr("x2", w) |
88 | 102 | .attr("y1", y2(0)) |
89 | 103 | .attr("y2", y2(0)); |
| 104 | + |
| 105 | + // Active focus region. |
| 106 | + active = context.append("svg:rect") |
| 107 | + .attr("pointer-events", "none") |
| 108 | + .attr("id", "active") |
| 109 | + .attr("x", x(x0 = minX)) |
| 110 | + .attr("y", 0) |
| 111 | + .attr("height", h2) |
| 112 | + .attr("width", x(x1 = (minX + 1e11)) - x(x0)) |
| 113 | + .attr("fill", "lightcoral") |
| 114 | + .attr("fill-opacity", .5); |
90 | 115 | }); |
91 | 116 |
|
| 117 | +d3.select(window) |
| 118 | + .on("mousemove", mousemove) |
| 119 | + .on("mouseup", mouseup); |
| 120 | + |
| 121 | +function mousedown() { |
| 122 | + xx = x.invert(d3.svg.mouse(this).x); |
| 123 | +} |
| 124 | + |
| 125 | +function mousemove() { |
| 126 | + if (xx != null) { |
| 127 | + |
| 128 | + // Compute the new focus region. |
| 129 | + var xy = x.invert(d3.svg.mouse(active[0][0]).x); |
| 130 | + if (xx < xy) { x0 = xx; x1 = xy; } |
| 131 | + else if (xx > xy) { x0 = xy; x1 = xx; } |
| 132 | + else return; |
| 133 | + |
| 134 | + // Clamp the focus region. |
| 135 | + x0 = Math.max(x.domain()[0], x0); |
| 136 | + x1 = Math.min(x.domain()[1], x1); |
| 137 | + |
| 138 | + // Update the display. TODO Recycle this scale? |
| 139 | + var tx = d3.scale.linear() |
| 140 | + .domain([x0, x1]) |
| 141 | + .range([0, w]); |
| 142 | + |
| 143 | + focus.select("path") |
| 144 | + .attr("d", d3.svg.area() |
| 145 | + .x(function(d) { return tx(d.x); }) |
| 146 | + .y0(y1(0)) |
| 147 | + .y1(function(d) { return y1(d.y); })); |
| 148 | + |
| 149 | + active |
| 150 | + .attr("x", x(x0)) |
| 151 | + .attr("width", x(x1) - x(x0)); |
| 152 | + } |
| 153 | +} |
| 154 | + |
| 155 | +function mouseup() { |
| 156 | + xx = null; |
| 157 | +} |
| 158 | + |
92 | 159 | function parseDate(d) { |
93 | 160 | return Date.UTC.apply(Date, d.split("-")); |
94 | 161 | } |
|
0 commit comments