Search code examples
javadrawingswteclipse-gefzest

How do I copy part of Zest Graph to SWT Canvas


I am developing a new Eclipse plug-in, using the GEF (Draw2d 3.9, Zest 1.5.0) with custom figures in order to represent some data. I am now trying to develop an adjustable lens view. The desired functionality is that the mouse will be dragged on the main window, and the "lens view window" will represent the selected area.

To do that I use a window with a SWT canvas for the lens view. The main window's (graph) contents are painted in the lens canvas. Then I copy another area (could be outside from the visible area) of the lens canvas to the point 0,0 (of the lens canvas). The problem is that the lens canvas ignores everything that is painted outside its visible area and when I copy the above mentioned area I get black instead of contents.

Copying the graph's contents to image and after that to drawing a part of the image is not a solution, because my graph might be gigantic (> 200000 x 200000).

Below you can find a part of my code:

import org.eclipse.draw2d.SWTGraphics;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.zest.core.widgets.Graph;

public class LensView {
private final Shell shell;
private final Canvas canvas;

private final Point focalPoint;
private float lensScaleFactor;
private float mainScaleFactor;

public LensView(final Graph graph, Display d) {
    this.visualizer = visualizer;
    focalPoint = new Point(0, 0);

    lensScaleFactor     = 1.0f;
    mainScaleFactor = 1.0f;

    shell = new Shell(d, SWT.SHELL_TRIM);
    shell.setSize(640, 480);
    shell.setLayout(new FillLayout());  
    canvas = new Canvas(shell, SWT.NO_REDRAW_RESIZE);
    canvas.setLayout(new FillLayout());

    canvas.addPaintListener(new PaintListener() {   
        @Override
        public void paintControl(PaintEvent e) {
            Rectangle area  = canvas.getClientArea();
            //Scale the origin to 1.0 and apply our scale factor
            float scale = lensScaleFactor/mainScaleFactor; 

            Point viewLocation = graph.getViewport().getViewLocation();
            int tmpx = normaliseDec(Math.round((focalPoint.x + viewLocation.x) * scale) - Math.round(area.width/2.0f));
            int tmpy = normaliseDec(Math.round((focalPoint.y + viewLocation.y) * scale) - Math.round(area.height/2.0f));

            GC gc = new GC(canvas);
            SWTGraphics graphics = new SWTGraphics(gc);
            graphics.scale(scale);  
            graph.getContents().paint(graphics);                

            e.gc.copyArea(tmpx, tmpy, area.width, area.height, 0, 0);

            graphics.dispose();
            gc.dispose();
        }
    });
}

public void redraw (int x, int y) {
    focalPoint.x = normaliseDec(x);
    focalPoint.y = normaliseDec(y);
    canvas.redraw();
    canvas.update();
}

private int normaliseDec (int i) {
    return i < 0 ? 0 : i;
}

}

Solution

  • Think all you need to do is create ScaledGraphics object from the GC that come with the PaintEvent, set the scaling factor for the graphics, set the clipping rectangle and then paint you Graph. Something like that (didn't test it, roughly something like that):

    ScaledGraphics graphics = new ScaledGraphics(e.gc);
    graphics.scale(scale);
    graphics.setClip(new Rectangle(focalPoint, getClientArea().getSize()));
    graph.getContents().paint(graphics);
    graphics.dispose();
    

    A few points: - ScaledGraphics should do the scaling of the clipping Rectangle for you - Should paint on the controls GC and not create a new one