Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[udig-devel] Pan tool leaks commands, and pan stutter solved, too

Hi,
today I tried to determine what causes the stutter when fast dragging a map.

First of all, steps to reproduce, and what happens.
Steps:
* press down mouse button on the map
* move the mouse
* release the button, but keep on moving
* stop moving after
Result:the map moves with the mouse until you release the mouse, then jumps back to its original position for the time it takes to render the
new map, then it appears again.

The issue is a threading one as far as I can tell. Now, debugging
uDig I noticed that Pan.java did create TranslateCommand that ended
up in the in the ViewportPainter, and accumulated there, because
the various commands were never invalidated (setValid(false)).
So, after 1000 pans, you have 1000 TranslateCommand inside
the ViewportPainter.

The quick patch for this is to change command.setTranslation(0, 0)
with command.expire in Pan.mouseReleased.

This won't fix the stutter thought. Basically, what I think is happening, is that something listening to the mouse movement
is forcing the map to update itself, so when the release and
keep on moving happens, the following occurrs:
* the translate command gets invalidated or set to (0,0)
* the rendering process starts, but takes time
* something forces map repaint, which is served with the old
  rendered map, now un-translated (thus the jump back)
* the rendering finally ends, and the map jumps forward to
  the position it was dragged to.

The fix I propose, which may be wrong, is to wait invalidating
the translate command only when the PanCommand generated in the
Pan.mouseReleased method has been processed.

The Pan.java attached to this mail has a possible implementation
of the above, using a command wrapper that first executes the
pan, then invalidates the translate.

Cheers
Andrea
/*
 *    uDig - User Friendly Desktop Internet GIS client
 *    http://udig.refractions.net
 *    (C) 2004, Refractions Research Inc.
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 */
package net.refractions.udig.tools.internal;

import java.awt.Point;
import java.awt.Rectangle;

import org.eclipse.core.runtime.IProgressMonitor;

import net.refractions.udig.project.IMap;
import net.refractions.udig.project.command.AbstractCommand;
import net.refractions.udig.project.command.Command;
import net.refractions.udig.project.command.NavCommand;
import net.refractions.udig.project.internal.Map;
import net.refractions.udig.project.internal.render.ViewportModel;
import net.refractions.udig.project.render.displayAdapter.IMapDisplay;
import net.refractions.udig.project.ui.commands.AbstractDrawCommand;
import net.refractions.udig.project.ui.commands.IDrawCommand;
import net.refractions.udig.project.ui.internal.commands.draw.TranslateCommand;
import net.refractions.udig.project.ui.render.displayAdapter.MapMouseEvent;
import net.refractions.udig.project.ui.render.displayAdapter.ViewportPane;
import net.refractions.udig.project.ui.tool.AbstractModalTool;
import net.refractions.udig.project.ui.tool.ModalTool;
import net.refractions.udig.ui.graphics.ViewportGraphics;


/**
 * Provides Pan functionality for MapViewport
 *
 * @author Jesse Eichar
 * @version $Revision: 1.9 $
 */
public class Pan extends AbstractModalTool implements ModalTool {
    private boolean dragging=false;
    private Point start=null;

    private TranslateCommand command;
    /**
     * Creates an new instance of Pan
     */
    public Pan() {
        super(MOUSE | MOTION);

        //TODO set Cursor
    }

    /**
     * @see net.refractions.udig.project.ui.tool.AbstractTool#mouseDragged(net.refractions.udig.project.render.displayAdapter.MapMouseEvent)
     */
    public void mouseDragged(MapMouseEvent e) {
        if (dragging) {
            command.setTranslation(e.x- start.x, e.y - start.y);
            context.getViewportPane().repaint();
        }
    }

    /**
     * @see net.refractions.udig.project.ui.tool.AbstractTool#mousePressed(net.refractions.udig.project.render.displayAdapter.MapMouseEvent)
     */
    public void mousePressed(MapMouseEvent e) {
    	
        if (e.state == MapMouseEvent.BUTTON1
                && !(e.state == (MapMouseEvent.ALT_DOWN_MASK))) {
        	((ViewportPane)context.getMapDisplay()).enableDrawCommands(false);
            dragging = true;
            start = e.getPoint();
            command=context.getDrawFactory().createTranslateCommand(0,0);
            context.sendASyncCommand(command);
        }
    }

    /**
     * @see net.refractions.udig.project.ui.tool.AbstractTool#mouseReleased(net.refractions.udig.project.render.displayAdapter.MapMouseEvent)
     */
    public void mouseReleased(MapMouseEvent e) {
        if (dragging) {
        	((ViewportPane)context.getMapDisplay()).enableDrawCommands(true);
            Point end=e.getPoint();
            NavCommand finalPan = context.getNavigationFactory().createPanCommandUsingScreenCoords(start.x-end.x, start.y-end.y);
            context.sendASyncCommand(new PanAndInvalidate(finalPan, command));

            dragging = false;

        }
    }
    /**
     * @see net.refractions.udig.project.ui.tool.Tool#dispose()
     */
    public void dispose() {
        super.dispose();
    }
    
    /**
     * Executes the specified pan command, and only after it is executed, expires the last translate command
     */
    private class PanAndInvalidate implements Command, NavCommand {
        
        private NavCommand command;
        private TranslateCommand expire;

        PanAndInvalidate(NavCommand command, TranslateCommand expire) {
            this.command = command;
            this.expire = expire;
        }

        public Command copy() {
            return new PanAndInvalidate(command, expire);
        }

        public String getName() {
            return "PanAndDiscard";
        }

        public void run( IProgressMonitor monitor ) throws Exception {
            try {
                command.run(monitor);
            } finally {
                expire.setValid(false);
            }
        }

        public void setViewportModel( ViewportModel model ) {
            command.setViewportModel(model);
        }

        public Map getMap() {
            return command.getMap();
        }

        public void setMap( IMap map ) {
            command.setMap(map);
        }

        public void rollback( IProgressMonitor monitor ) throws Exception {
            command.rollback(monitor);
        }
        
    }
}

Back to the top