gmmoraesbr / flot

Automatically exported from code.google.com/p/flot
0 stars 0 forks source link

set zoom differently on x and y axes #355

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
While trying to figure out how to zoom just one axis in my plot graph, I 
came up with this extension of the functionality to allow you to specify 
both an x and y axis zoom.

I made some minor changes to the plot.zoom() and plot.zoomOut() methods as 
well as adding a new private function to the plot object, getZoomAmount().

getZoomAmount() processes the zoom amount, which can accept both a number 
for the zoom amount as well as an object with x and/or y properties set. If 
just one property is set, the other property defaults to 1, so you can set 
amount.x = 2 to only scale the x-axes.

I also added in a conditional preventing scaling in the case of amount 
= 1, fixing a bug that caused flot to recalculate the axes and end up 
changing them despite the zoom being set to 1, which shouldn't change 
them at all.

Here's the relevant code snippet from flot.navigate

        // supports an amount value or amount with x and y specified
        // also defaults an unspecified amount to 1 (or no zoom)       
        function getZoomAmount(args) {
            var options,
                amount = {};
            if (!args)
                args = {};
            if (args.amount) {
                if (typeof args.amount === 'number') {
                    amount = {'x':args.amount,'y':args.amount};
                } else {
                    amount.x = args.amount.x ? args.amount.x : 1;
                    amount.y = args.amount.y ? args.amount.y : 1;
                }
            } else {
                options = plot.getOptions();
                if (typeof options.zoom.amount === 'number') {
                    amount = 
{'x':options.zoom.amount,'y':options.zoom.amount};
                } else {
                    amount.x = options.zoom.amount.x ? 
options.zoom.amount.x : 1;
                    amount.y = options.zoom.amount.y ? 
options.zoom.amount.y : 1;
                }
            }
            return amount;
        }

        plot.zoomOut = function (args) {
            if (!args)
                args = {};

            args.amount = getZoomAmount(args);
            //if (!args.amount) {}
            //    args.amount = plot.getOptions().zoom.amount

            args.amount.x = 1 / args.amount.x;
            args.amount.y = 1 / args.amount.y;
            //args.amount = 1 / args.amount;
            plot.zoom(args);
        }

        plot.zoom = function (args) {
            if (!args)
                args = {};

            var axes = plot.getAxes(),
                options = plot.getOptions(),
                c = args.center,
                amount = getZoomAmount(args),
                //amount = args.amount ? args.amount : options.zoom.amount,
                w = plot.width(), h = plot.height();

            if (!c)
                c = { left: w / 2, top: h / 2 };

            var xf = c.left / w,
                x1 = c.left - xf * w / amount.x,
                //x1 = c.left - xf * w / amount,
                x2 = c.left + (1 - xf) * w / amount.x,
                //x2 = c.left + (1 - xf) * w / amount,
                yf = c.top / h,
                y1 = c.top - yf * h / amount.y,
                //y1 = c.top - yf * h / amount,
                y2 = c.top + (1 - yf) * h / amount.y;
                //y2 = c.top + (1 - yf) * h / amount;

            function scaleAxis(min, max, name, dryRun) {
                var axis = axes[name],
                    axisOptions = options[name];

                if (!axis.used)
                    return;

                min = axis.c2p(min);
                max = axis.c2p(max);
                if (max < min) { // make sure min < max
                    var tmp = min
                    min = max;
                    max = tmp;
                }

                var range = max - min, zr = axisOptions.zoomRange;
                var zr0 = (zr[0][0] == "*") ? zr[0].slice(1) * 
(axis.datamax - axis.datamin) : zr[0];
                var zr1 = (zr[1][0] == "*") ? zr[1].slice(1) * 
(axis.datamax - axis.datamin) : zr[1];
                if (zr && dryRun) {
                    if (zr1 != null && range > zr1) {
                        return range / zr1;
                    }
                    if (zr0 != null && range < zr0)  {
                        return range / zr0;
                    }
                }

                if (!dryRun) {
                    var shift = 0;
                    if (axis.datamax < max) shift = axis.datamax - max;
                    if (axis.datamin > min) shift = axis.datamin - min;
                    max += shift;
                    min += shift; 
                    if (axis.datamax < max) max = axis.datamax;

                    axisOptions.min = min;
                    axisOptions.max = max;
                }

                return 1;
            }

            var selection = plot.getSelection && plot.getSelection();

            var amountX = amount.x, amountY = amount.y;
            //var amountX = amount, amountY = amount; //orig

            var suggestedX  = amount.x * scaleAxis(x1, x2, 'xaxis',  true);
            var suggestedX2 = amount.x * scaleAxis(x1, x2, 'x2axis', true);
            var suggestedY  = amount.y * scaleAxis(y1, y2, 'yaxis',  true);
            var suggestedY2 = amount.y * scaleAxis(y1, y2, 'y2axis', true);

            var minmax = (amountX >= 1) ? Math.min : Math.max; 

            if (suggestedX ) amountX = minmax(suggestedX,  amountX);
            if (suggestedX2) amountX = minmax(suggestedX2, amountX);
            if (suggestedY ) amountY = minmax(suggestedY,  amountY);
            if (suggestedY2) amountY = minmax(suggestedY2, amountY);

            x1 = c.left - xf * w / amountX,
            x2 = c.left + (1 - xf) * w / amountX,
            y1 = c.top - yf * h / amountY,
            y2 = c.top + (1 - yf) * h / amountY;

            // if amount = 1, do not scale the axes
            if (amount.x !== 1) {
                scaleAxis(x1, x2, 'xaxis');
                scaleAxis(x1, x2, 'x2axis');
            }
            if (amount.y !== 1) {
                scaleAxis(y1, y2, 'yaxis');
                scaleAxis(y1, y2, 'y2axis');
            }

            plot.setupGrid();
            plot.draw();

            if (selection)
                plot.setSelection(selection, true);

            if (!args.preventEvent)
                plot.getPlaceholder().trigger("plotzoom", [ plot ]);
        }

Original issue reported on code.google.com by brooksw...@gmail.com on 26 May 2010 at 7:23

GoogleCodeExporter commented 8 years ago
Hi!

My thoughts are:

- Elaborate patch - why do you need the dryrun stuff?
- I don't understand the rationale fully - could you maybe provide a screenshot 
of your use case?
- While it's conceivable that some people might need to zoom differently on x 
and y, I'd rather not spend complexity on it before I've actually seen it 
happen; maybe a simple "don't zoom this axis" fits the requirements better
- This code has changed a bit in the development version because of multi-axis 
support.

So this might not be for the stock Flot. But thanks for posting the code.

Original comment by olau%iol...@gtempaccount.com on 13 Dec 2010 at 4:51

GoogleCodeExporter commented 8 years ago
Here is an example of the types of charts I needed this for: A graph of heart 
rate over time. You always want the same zoom on the y-axis, but you do want to 
zoom in and out on the x-axis. I was working on a project graphing a large 
number of similar items that all shared the one zoom at a time requirement. 
Unfortunately I cannot produce a screenshot for you, as I did this work on a 
contract under and NDA and cannot release information specific to the project. 

I believe that a "don't zoom on this axis" fits the requirements just as well, 
but I figured I would leave the option open for other possible use cases, such 
as applying transformation matrices.

Original comment by brooksw...@gmail.com on 17 Feb 2011 at 6:55

GoogleCodeExporter commented 8 years ago
I have the same requirement for the types of plots I need to create.  I have 
much need for a timeseries graph that only zooms in x.  In our case, it is a 
history plot of device availability over time.  Y is a boolean value (up/down, 
here/not here, installed/not installed, etc) and thus there is no reason to 
zoom Y.

Adding a draggable interface without selection to the same chart would make 
flot unique in the realm of plotting tools (with the only exception being 
Chronoscope, as best as I can tell.)

I just wanted to chime in to support the notion that it is a useful feature.

Thank you for the hard work on flot thus far.

(I hope this does not exactly qualify as a "me too" post..)

Original comment by TMEmbed...@gmail.com on 17 Feb 2011 at 8:45

GoogleCodeExporter commented 8 years ago
Can't you just use selection in the x or y "mode" and then fix the axis min/max 
for the other direction?

Original comment by ryl...@gmail.com on 18 Feb 2011 at 10:35

GoogleCodeExporter commented 8 years ago
Actually, I just landed support in the development release for restricting 
zooming/panning to one axis (there were a couple of tickets on that). So I 
think I'll just close this. Sorry if I sounded rude, I do appreciate the work 
and thought you put into it. Thanks for that!

Original comment by olau%iol...@gtempaccount.com on 10 Mar 2011 at 4:26