dygraphs - limiting the change of y axis range when zooming - javascript

When I am zooming the chart then the Y scale is changing automatically to select the range fitting to the values limited by x.
I know valueRange param where I can fix the range, but I am searching for an option to define some maximal range.
So that the Y scale is changing when zooming , but the scale is limited by some parameter

In the interest of clarity and brevity, I have omitted some things - such as the creation process.
You would be able to do something similar to limit the minimum range.
g = new Dygraph(
... your parameters ...
);
window.intervalId = setInterval(
function(){
var extents = g.yAxisRange();
var lowerExtent = extents[0];
var upperExtent = extents[1];
if(upperExtent > MY_MAX_DESIRED){
var range = [lowerExtent, MY_MAX_DESIRED];
g.updateOptions('valueRange': range);
}
},
500
);
Using the above solution, you would notice a half-second 'glitch' on zooming when the range it set. As an alternative, you could also use the zoomCallBack, specified when you create the graph object:
g = new Dygraph(
document.getElementById("div_g"),
[[0,0]],
{
zoomCallBack: function(minDate, maxDate, yRanges){
var extents = yRanges;
var lowerExtent = extents[0];
var upperExtent = extents[1];
if(upperExtent > MY_MAX_DESIRED){
var range = [lowerExtent, MY_MAX_DESIRED];
g.updateOptions('valueRange': range);
}
}
}
);
One caveat, I haven't tested the above solutions. I just believe they should work. Good luck!

thank you!
I have tried to use your suggestion
I have some issues:
"http://jsfiddle.net/stojak/zL6es924/4/"
my goal: e.g when y values in data are in between 10 and 11 then I want to zoom on Y to 0 (10-10) to 21 (11+10)
the code is nearly working, however I do not know how to let say reset my valueRange.
the result is that after each one zoom action the range increase.

Related

Is it possible to draw function like tangens wihout knowing its domain? [duplicate]

I am making a graphing program in C++ using the SFML library. So far I have been able to draw a function to the screen. I have run into two problems along the way.
The first is a line which seems to return to the origin of my the plane, starting from the end of my function.
You can see it in this image:
As you can see this "rogue" line seems to change colour as it nears the origin. My first question is what is this line and how may I eradicate it from my window?
The second problem which is slightly unrelated and more mathematical can be seen in this image:
As you can see the asymptotes which are points where the graph is undefined or non continuous are being drawn. This leads me to my second question: is there a way ( in code ) to identify an asymptote and not draw it to the window.
My code for anything drawn to the window is:
VertexArray axis(Lines, 4);
VertexArray curve(PrimitiveType::LinesStrip, 1000);
axis[0].position = Vector2f(100000, 0);
axis[1].position = Vector2f(-100000, 0);
axis[2].position = Vector2f(0, -100000);
axis[3].position = Vector2f(0, 100000);
float x;
for (x = -pi; x < pi; x += .0005f)
{
curve.append(Vertex(Vector2f(x, -tan(x)), Color::Green));
}
I would very much appreciate any input : )
Update:
Thanks to the input of numerous people this code seems to work fine in fixing the asymptote problem:
for (x = -30*pi; x < 30*pi; x += .0005f)
{
x0 = x1; y0 = y1;
x1 = x; y1 = -1/sin(x);
a = 0;
a = fabs(atan2(y1 - y0, x1 - x0));
if (a > .499f*pi)
{
curve.append(Vertex(Vector2f(x1, y1), Color::Transparent));
}
else
{
curve.append(Vertex(Vector2f(x1, y1), Color::Green));
}
}
Update 2:
The following code gets rid of the rogue line:
VertexArray curve(Lines, 1000);
float x,y;
for (x = -30 * pi; x < 30 * pi; x += .0005f)
{
y = -asin(x);
curve.append(Vertex(Vector2f(x, y)));
}
for (x = -30 * pi + .0005f; x < 30 * pi; x += .0005f)
{
y = -asin(x);
curve.append(Vertex(Vector2f(x, y)));
}
The first problem looks like a wrong polyline/curve handling. Don't know what API are you using for rendering but some like GDI need to start the pen position properly. For example if you draw like this:
Canvas->LineTo(x[0],y[0]);
Canvas->LineTo(x[1],y[1]);
Canvas->LineTo(x[2],y[2]);
Canvas->LineTo(x[3],y[3]);
...
Then you should do this instead:
Canvas->MoveTo(x[0],y[0]);
Canvas->LineTo(x[1],y[1]);
Canvas->LineTo(x[2],y[2]);
Canvas->LineTo(x[3],y[3]);
...
In case your API needs MoveTo command and you are not setting it then last position is used (or default (0,0)) which will connect start of your curve with straight line from last draw or default pen position.
Second problem
In continuous data you can threshold the asymptotes or discontinuities by checking the consequent y values. If your curve render looks like this:
Canvas->MoveTo(x[0],y[0]);
for (i=1;i<n;i++) Canvas->LineTo(x[i],y[i]);
Then you can change it to something like this:
y0=y[0]+2*threshold;
for (i=0;i<n;i++)
{
if (y[1]-y0>=threshold) Canvas->MoveTo(x[i],y[i]);
else Canvas->LineTo(x[i],y[i]);
y0=y[i];
}
The problem is selection of the threshold because it is dependent on x density of sampled points and on the first derivation of your y data by x (slope angles)
If you are stacking up more functions the curve append will create your unwanted line ... instead handle each data as separate draw or put MoveTo command in between them
[Edit1]
I see it like this (fake split):
double x0,y0,x1,y1,a;
for (e=1,x = -pi; x < pi; x += .0005f)
{
// last point
x0=x1; y0=y1;
// your actual point
x1=x; y1=-tan(x);
// test discontinuity
if (e) { a=0; e=0; } else a=fabs(atan2(y1-y0,x1-x0));
if (a>0.499*M_PI) curve.append(Vertex(Vector2f(x1,y1), Color::Black));
else curve.append(Vertex(Vector2f(x1,y1), Color::Green));
}
the 0.499*M_PI is you threshold the more closer is to 0.5*M_PIthe bigger jumps it detects... I faked the curve split by black color (background) it will create gaps on axis intersections (unless transparency is used) ... but no need for list of curves ...
Those artifacts are due to the way sf::PrimitiveType::LinesStrip works (or more specific lines strips in general).
In your second example, visualizing y = -tan(x), you're jumping from positive infinity to negative infinity, which is the line you're seeing. You can't get rid of this, unless you're using a different primitive type or splitting your rendering into multiple draw calls.
Imagine a line strip as one long thread you're pinning with pushpins (representing your vertices). There's no (safe) way to go from positive infinity to negative infinity without those artifacts. Of course you could move outside the visible area, but then again that's really specific to this one function.

dimple tickFormat ignores grouping comma

I have a dimple chart displaying some large numbers. I want the precise values to appear in the toolbox (not truncated to 14k) so I changed the tickFormat. However, dimple/d3 seems to ignore the grouping comma, which makes large numbers hard to process.
// this prints "12,345" as expected
var f = d3.format(",.0f");
console.log(f(12345.123));
var data = [{"date":"2016-01-18","completed":1234123.100},{"date":"2016-01-19","completed":1345123.0},{"date":"2016-01-20","completed":2123123.1}]
var svg = dimple.newSvg("#foo", "100%", "100%");
var myChart = new dimple.chart(svg, data);
var x = myChart.addTimeAxis("x", "date", "%Y-%m-%d", "%d/%m");
var y = myChart.addMeasureAxis("y", "completed");
// this does not work
// tooltips and y axis values appear without grouping comma
y.tickFormat = d3.format(",.0f");
myChart.setMargins("60px", "30px", "30px", "70px");
series = myChart.addSeries(null, dimple.plot.line);
myChart.draw();
According to the API documentation the property dimple.axis.tickFormat will accept the format specifier string (i.e. ",.0f"). as it is passed to d3.format(). You don't have to call d3.format() yourself; this is done internally in function _getFormat(). Thus your code becomes
y.tickFormat = ",.0f";

nvd3 tickValues() not drawing ticks close to boundary values

I am drawing a line chart using nvd3 and specifying a set of xAxis values to draw ticks for using:
chart.xAxis.tickValues(tickArr)
where tickArr has the list of points to draw ticks for.
For some reason, the ticks close to the beginning or the end values for the x axis are not being drawn. I am guessing this is because of some default setting for the boundary value ticks but am not able to find a way to override it and show all specified ticks.
Here is the link to the fiddle . You can see that though tickArr has 7 data points, only 3 ticks are shown.
Any help as to which parameter should I change or why this is happening would be really appreciated.
I modified the source to get around this situation.
In the nv.models.axis(), there is a buffer given when showMaxMin is true for a bottom/top orientation:
if (showMaxMin && (axis.orient() === 'top' || axis.orient() === 'bottom')) {
var maxMinRange = [];
wrap.selectAll('g.nv-axisMaxMin')
.each(function(d,i) {
try {
if (i) // i== 1, max position
maxMinRange.push(scale(d) - this.getBoundingClientRect().width - 4); //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
else // i==0, min position
maxMinRange.push(scale(d) + this.getBoundingClientRect().width + 4)
}catch (err) {
if (i) // i== 1, max position
maxMinRange.push(scale(d) - 4); //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
else // i==0, min position
maxMinRange.push(scale(d) + 4);
}
});
// the g's wrapping each tick
g.selectAll('g').each(function(d, i) {
if (scale(d) < maxMinRange[0] || scale(d) > maxMinRange[1]) {
if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
d3.select(this).remove();
else
d3.select(this).select('text').remove(); // Don't remove the ZERO line!!
}
});
}
I just removed these buffers:
try {
if (i) // i== 1, max position
maxMinRange.push(scale(d));
else // i==0, min position
maxMinRange.push(scale(d))
}catch (err) {
if (i) // i== 1, max position
maxMinRange.push(scale(d));
else // i==0, min position
maxMinRange.push(scale(d));
}
There is another post talking about a similar issue. The solution posted there is to remove the boundary ticks but this seems like the wrong approach as the boundary ticks are very helpful when considering the perceptual aspects of the visualization. I hope this answer helps someone who faces a similar situation in the future.

ThreeJS Molecules - Double Bonds

Working with threejs, I am trying to use the following example:
http://threejs.org/examples/#css3d_molecules
The problem is that in that example, the bonds from the grey to the red balls are supposed to be double bonds. I have found a few links that suggest how this is done, but it doesn't seem supported by threejs. Here is what I was talking about:
https://www.umass.edu/microbio/rasmol/faq_em.htm#doublebonds
There you can see the fumerate molecule (it is a text file) and how they structured it. Any tips on how to add a double bond either visually, or by changing the class of the line so I can change the size or color?
In the function loadMolecule, there is a loader.load function, some lines from it are pasted below, this is where you would add a second bond next to the first. You'd offset the start and end positions by some vector amount and draw another one.
for ( var i = 0; i < geometryBonds.vertices.length; i += 2 ) {
var start = geometryBonds.vertices[ i ];
var end = geometryBonds.vertices[ i + 1 ];
start.multiplyScalar( 75 );
end.multiplyScalar( 75 );
tmpVec1.subVectors( end, start );
var bondLength = tmpVec1.length() - 50;
}

Updating domain values of multiline graph after line drag event

I am developing a simple multi-line graph with dual time axes and zooming/dragging features. Please take a look at my JSFiddle.
I am trying to implement the drag feature on the line graph, whereupon dragging a particular line will result in its respective axis also getting updated. Every time the drag is applied to the graph, I am trying to update the domain values of its respective axis, and redraw both the axis and the line graph.
Here is the logic that I implemented to update the domain values (referenced from a D3 Example):
var mousePoint = d3.mouse(this);
x1 = x1Scale.domain()[0],
x2 = x1Scale.domain()[1],
console.log("x1 = "+x1+", x2 = " +x2);
xextent = x1 - x2;
x1 += mousePoint[0];
x2 += mousePoint[0];
var newDomain = [x1, x2];
x1Scale.domain(newDomain);
When I implement this logic, I get a NaN error. Is this the correct way to update the domain values after the drag? If so, how do I solve the NaN error and achieve the desired functionality?
It is important to convert numbers into date objects, there was a typo in your code (data1[0].time instead of data1[0].date). Also you shouldn't multiply by 1000, since your data was already in milliseconds.
In your drag code, it is also important to convert your date objects back to numbers, in order that += will work on them. Of course you also need to convert them back to date when setting the domain again.
function draggedData1(d) {
console.log("dragging of data1 going on!!")
var mousePoint = d3.mouse(this);
var x1 = x1Scale.domain()[0].valueOf(); //date to number
var x2 = x1Scale.domain()[1].valueOf();
var xextent = x1 - x2;
x1 += mousePoint[0];
x2 += mousePoint[0];
var newDomain = [new Date(x1), new Date(x2)]; //number back to date
x1Scale.domain(newDomain);
redraw();
zoomBottom.x(x1Scale);
zoom.x(x2Scale);
}
I've created a fiddle with the full code and fixes here:
http://jsfiddle.net/pb3cod6q/2/

Categories

Resources