GithubHelp home page GithubHelp logo

Create a PanZoomTool about chaco HOT 12 OPEN

enthought avatar enthought commented on August 18, 2024
Create a PanZoomTool

from chaco.

Comments (12)

jdmarch avatar jdmarch commented on August 18, 2024

A PanZoomTool should have the option to respect the mapper's domain limits. (Pan does this, but Zoom does not.)

from chaco.

WarrenWeckesser avatar WarrenWeckesser commented on August 18, 2024

Part 1: Basic Operations

The PanZoomTool controls a window over an underlying data space. By "data space", I mean the usual (x,y) plane, or a subset of the plane. A subset of the plain may be a <= x <= b, or c <= y <= d, or both. Any of a, b, c, or d may be infinity.

The "window" is simply a rectangular subset of the data space that determines what is shown in a plot.

The PanZoomTool should control the size and position of the window.

Desired operations (without reference to the actual UI implementation details):


PAN

Pan left, right, up and down--that is, move the window without changing its size. The control may be restricted to allow only left/right or up/down pans.

MULTIPLICATIVE ZOOM

Zoom in or zoom out by a multiplicative factor. To "zoom in" means to decrease the size of the window by some factor. This might be applied to both the width and height, or to just one of the dimensions. For example, if the data space is a geographic map, then it makes sense to always zoom both dimensions by the same factor. In general, though, when applied to both dimensions, it might be better to allow for different multiplicative factors for each dimension.

This type of zoom requires a center point within the window that remains fixed; this point is not necessarily the center of the window.

BOX ZOOM

To zoom in, a rectangular region within the current window becomes the new window.

To zoom out, the current window is shrunk to a rectangular region within the current window. The affine transformation required to fit the current window into the rectangle determines the shape and location of the new window. (Note that the "box" might be infinite in one dimension. For example, if zooming is only allowed along the x axis, then the box would be an x interval, and in effect the vertical dimension of the box would be the entire y axis.)

UNDO

Undo the previous change to the window (either pan or zoom). Multiple undos should be possible--i.e. there should be a history of changes to the window.

RESET

Reset the window to an initial size and location.


Are there other primitive operations that should be part of a PanZoomTool?

The pan and zoom operations are all affine transformations, so one can think of the PanZoomTool as a controller for a sequence of affine transformations. But I'm not sure how much abstraction is really necessary or helpful. The separation of the zoom operation into "multiplicative" and "box" already anticipates different UI implementations (e.g. scroll wheel and click-and-drag selection box, respectively).

A few other details:

  • Of course, all pan and zoom operation must respect the data space bounds.
  • Some plots will have a prescribed aspect ratio. A zoom operation should be aware of this and handle it properly.
  • A multiplicative zoom in, followed by a multiplicative zoom out, should result in no net change (shouldn't it?). The will be the case if the center points of the two zooms are the same.

from chaco.

jdmarch avatar jdmarch commented on August 18, 2024

Also: the tools should be able to coexist with panning and zooming which is done by another actor (e.g. a scaling widget or zooming from a context menu.) A counter-example: BetterZoomTool has min/max zooms, but just keeps track of how much it itself has zoomed, not how much another actor has, which can result in those limits being greatly exceeded.

And the tools should be generic Enable tools, subclassed to be Chaco-aware, rather than depending on Chaco-specific traits.

@warren: yes, zoom in + zoom out should be no net change.

from chaco.

corranwebster avatar corranwebster commented on August 18, 2024

There should be an option that makes zoom in (at least with the mouse-based zooms) zoom in to the point under the mouse pointer. When zooming in this fashion, the target point should remain underneath the mouse pointer, rather than being centered.

The current behaviour of the zoom tool when zoom_to_mouse is True, is to center the target point, which causes some odd effects. This maybe should be a issue on its own for the BetterZoomTool.

from chaco.

WarrenWeckesser avatar WarrenWeckesser commented on August 18, 2024

Part 2: A Few UI Notes

PAN

Panning in Chaco has generally been implemented by click-and-drag. The plot can be configured so that this results in panning along just one dimension, if desired.

When we have an implementation that includes panning in the history, all the individual steps taken while dragging should not be saved. The history should only contain the single net change from mouse-down to mouse-up.

MULTIPLICATIVE ZOOM

This is usually implemented with the scroll wheel. Again the plot can be configured to allow zooming in just one axis.

(In some cases, I've added special keys to zoom just one axis. Works great, but it's not standard or discoverable. :( )

BOX ZOOM

Chaco has a mode in which hitting the 'z' key puts the plot in 'zoom box' mode: clicking-and-dragging draws the zoom box, and releasing the mouse does the zoom.

I don't think there is a 'box zoom out' in Chaco.

Matplotlib has a zoom mode that is selected by clicking on the magnifying glass button. In this mode clicking-and-dragging works like Chaco's 'z'-key mode; right-clicking-and-dragging in zoom mode does a 'box zoom out'.

(Editorial comment: for general plots of y vs. x, I find that a 2D zoom with the scroll wheel is almost never what I want, and I much prefer a box zoom. For 2D plots of geographic maps, or for zooming in just one axis, the scroll wheel is great.)

UNDO

Chaco usually uses ctrl-z to undo a zoom. I don't think there is currently an undo for panning.

RESET

The 'escape' key will reset Chaco's zoom, but there is no reset for panning.

from chaco.

WarrenWeckesser avatar WarrenWeckesser commented on August 18, 2024

@corran: Yes, definitely (and "target point" is probably a better term for what I called the "center point" of the zoom). Shouldn't that be the default?

If you are using the scroll wheel to zoom, and you scroll forward one notch and then back one notch--without moving the cursor--you should end up back where you started (i.e. the target point remains at the cursor). Annoying details: if you scroll back one notch and bump into the data space bounds, you won't get a full zoom out, but if you then scroll forward one notch, you will get a full zoom in, so this is an edge case where you might not end up back where you started.

from chaco.

corranwebster avatar corranwebster commented on August 18, 2024

Since I've just been mucking around with keyboard events:

Arrow keys should pan in the appropriate direction.
Arrow keys with some modifier should zoom in or out in a particular axis (h or v). Currently the modifier key is Shift.
Should left and right arrow keys with some modifier should step forward/backwards through history. Currently the modifier is Control for the base history tool. 'x' and 'y' do the same in the current BetterZoom.

In terms of zoom selection to zoom out, I've seen a selection that goes up-left instead of down-right (ie. dragging in the "wrong" direction, do a zoom-out of the appropriate proportions. It works pretty well, but can be surprising until you get used to it.

It would help Jonathon M if most of this could be made to work with Enable as well as Chaco. It would help Scott and Simon if this could be made to work with multiple axes. This should mean that there should be an opportunity to provide the mapper(s) for the x and y values, rather than having the current code which looks things up (and special-cases GridMappers).

from chaco.

brycehendrix avatar brycehendrix commented on August 18, 2024

I had been experimenting with tools keeping a state stack, which could be applied, reverted and reset. I had implemented the zoom state, pan state and a grouped state. If you read through the source for the BetterZoom, you'll see these in use. For Pan & Zoom, there are two ways to handle state:

  1. keep the data bounds so a change in state will change the bounds
  2. keep some transform info and re-apply the transform.

I opted for the latter because I initially wasn't resetting the pan state. It could be (and Scott will) argue that #1 is better for pan and zoom because it is simpler. In any case, I still believe the tool state is the direction chaco should be heading so that other tools can benefit. My goal was not to make each tool maintain its own state, but to move the tool state onto the Enable canvas, but before doing so I used the BetterZoom as a means of experimenting.

from chaco.

WarrenWeckesser avatar WarrenWeckesser commented on August 18, 2024

Bryce, now that you mention it, I recall that you explained some of this to me a long time ago.

If I recall correctly, one crucial reason for doing this is that a Chaco plot may have many tools, each of which can undo its operations. When the user hits ctrl-z (or whatever action ultimately triggers an 'undo'), he will expect his most recent action to be undone, and not care which tool it was. So a unified history of tool operations must be maintained... somewhere.

from chaco.

corranwebster avatar corranwebster commented on August 18, 2024

Agreed - and this seems like its fundamental enough that it should be in Enable rather than Chaco at least for the basic machinery. The interface here should probably be fairly simple - Undo and Redo methods may be enough.

At the Chaco level, we probably need a 'PlotState' (or several subclasses) for holding changes to Chaco-specific things like ranges, selections, etc.

from chaco.

WarrenWeckesser avatar WarrenWeckesser commented on August 18, 2024

Let's say something (a canvas in Enable, a separate tool, whatever) is responsible for maintaining the history. I'll call this the HistoryTool. It seems the Undo/Redo machinery could work as follows: tools that want to participate in undo/redo have a reference to the HistoryTool. When they perform an undoable operation, they add an item to the HistoryTool's list of actions along with a reference back to themselves. The HistoryTool will receive the UI events that indicate an undo or redo should be performed. (If it is implemented as a tool, and special keys are used for undo/redo, the HistoryTool should be the only tool that handles the special keys. Or it should be a the front of the tool list, and absorb those key events when it gets them.) The HistoryTool will get the appropriate item from the list of actions, and call the object's undo() or redo() method, passing as an argument the data that is was originally given by the other tool. The HistoryTool doesn't know or care about the contents of these actions. It is up to the tools themselves to handle the undo or redo operation.

Since we can't anticipate all the types of operations tools might consider "undoable", this seems like a reasonable approach.

It might not be necessary to include the action in the history list. A tool could simply report to the HistoryTool "I did something", and maintain its own history. When the HistoryTool gets, say, an undo event, it gets the object from the history list and calls its undo() or redo() method, as appropriate, with no arguments. As long as nothing else calls the object's undo() or redo() methods, it should be able to stay synchronized with the HistoryTool. I think this is closer to Bryce's ToolHistoryMixin.

It would be good, however, for the objects in the HistoryTool's history list to have nice str() representations, so if the undo and redo actions also show up in a menu, the menu could display, say, "Undo Pan" instead of just "Undo".

from chaco.

aterrel avatar aterrel commented on August 18, 2024

Hmm this has completely left the scope of a PanZoom tool. We should consider moving it to a more general place.

from chaco.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.