Possibility for zooming

Hello,
I would appreciate your help with a little challenge I am having with my GraphViz implementation.
My resource creates undirected graphs comprising a central node from which (click-able and labelled) edges radiate to other nodes. Sometimes there are also edges linking these secondary nodes to each other. For my needs I use the 'twopi' layout, and the output is rendered as an svg image in a web browser.
Given the possible size of these networks however I find that sometimes the resulting image is much larger than the browser window and I have to scroll around to see it all. Sometimes also I notice that an edge may overlap with another making it difficult to associate a label with a specific edge or to click on it if necessary.

I would love to be able to implement zooming (and maybe resizing) on the resulting image, to possibly force entire images onto the browser window (if possible) and to possibly prevent any overlaps in the edges.

Your help will be highly appreciated.

Thanks.

You don't need

You don't need to combine both scroll views. You can extend any View for this purpose. Basically the exploit lies in GestureDetector to detect all motions like pinch or pan (as in scroll). For example ScaleGestureDetector with onScale method which can be used for scaling or zooming. VCP550D Exam Question

Is there a way, in the input

Is there a way, in the input file or command line args to dot, to cause it to generate a suitable viewport in the emitted svg file?  I was hoping to be able to do this, as right now, the browser window usually opens up blank, and I have to zoom out until I can see some part of the graph so I know which way to pan.  It is quite disorienting and annoying.  I need a way to cause the initial display of an svg file emitted by do to be viewed in a browser, prefferably with the entire graph on the screen.

What about specifying the

What about specifying the size attribute? This will scale down the drawing to whatever size you want, so the whole graph will be visible. You can then use the viewer to pan and zoom.

Re: Possibility for zooming

Zooming SVG is the responsibilty of the client browser.  In Firefox, <ctrl>+ and <ctrl>- provide some amount of zooming.

The x11 renderer (e.g.  dot -Tx11 some.gv )  provides zooming using the scroll-wheel on your mouse, and panning while depressing the scroll-wheel.

PDF and PS are also potentially zoomable formats, depending on the browser.

If you are looking for a one-time "zoom", for example to fit any graph into a fixed sized image on a web-page, you can use the "viewport" attribute.  (See docs).

I am working on software to

I am working on software to view call trees in a large program.  I previously (6 years ago) wrote this as a set of bash scripts and produced pdf file, as my experimentation with svg back then told me the browsers svg capabilities were not yet ready for prime time.  Now I am using svg, but I too needed pan and zoom like what acroread gave me with pdf's.  I found a javascript routine that does the trick.  I can't remember where I found it, but it is rather short, so I will post the whole script here:

------------------------ cut here ------------------------

/**
 *  SVGPan library 1.2.2
 * ======================
 *
 * Given an unique existing element with id "viewport" (or when missing, the
 * first g-element), including the the library into any SVG adds the following
 * capabilities:
 *
 *  - Mouse panning
 *  - Mouse zooming (using the wheel)
 *  - Object dragging
 *
 * You can configure the behaviour of the pan/zoom/drag with the variables
 * listed in the CONFIGURATION section of this file.
 *
 * Known issues:
 *
 *  - Zooming (while panning) on Safari has still some issues
 *
 * Releases:
 *
 * 1.2.2, Tue Aug 30 17:21:56 CEST 2011, Andrea Leofreddi
 *    - Fixed viewBox on root tag (#7)
 *    - Improved zoom speed (#2)
 *
 * 1.2.1, Mon Jul  4 00:33:18 CEST 2011, Andrea Leofreddi
 *    - Fixed a regression with mouse wheel (now working on Firefox 5)
 *    - Working with viewBox attribute (#4)
 *    - Added "use strict;" and fixed resulting warnings (#5)
 *    - Added configuration variables, dragging is disabled by default (#3)
 *
 * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui
 *    Fixed a bug with browser mouse handler interaction
 *
 * 1.1, Wed Feb  3 17:39:33 GMT 2010, Zeng Xiaohui
 *    Updated the zoom code to support the mouse wheel on Safari/Chrome
 *
 * 1.0, Andrea Leofreddi
 *    First release
 *
 * This code is licensed under the following BSD license:
 *
 * Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of Andrea Leofreddi.
 */

"use strict";

/// CONFIGURATION
/// ====>

var enablePan = 1; // 1 or 0: enable or disable panning (default enabled)
var enableZoom = 1; // 1 or 0: enable or disable zooming (default enabled)
var enableDrag = 0; // 1 or 0: enable or disable dragging (default disabled)
var zoomScale = 0.2; // Zoom sensitivity

/// <====
/// END OF CONFIGURATION

var root = document.documentElement;

var state = 'none', svgRoot = null, stateTarget, stateOrigin, stateTf;

setupHandlers(root);

/**
 * Register handlers
 */
function setupHandlers(root){
    setAttributes(root, {
        "onmouseup" : "handleMouseUp(evt)",
        "onmousedown" : "handleMouseDown(evt)",
        "onmousemove" : "handleMouseMove(evt)",
        //"onmouseout" : "handleMouseUp(evt)", // Decomment this to stop the pan functionality when dragging out of the SVG element
    });

    if(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0)
        window.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari
    else
        window.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others
}

/**
 * Retrieves the root element for SVG manipulation. The element is then cached into the svgRoot global variable.
 */
function getRoot(root) {
    if(svgRoot == null) {
        var r = root.getElementById("viewport") ? root.getElementById("viewport") : root.documentElement, t = r;

        while(t != root) {
            if(t.getAttribute("viewBox")) {
                setCTM(r, t.getCTM());

                t.removeAttribute("viewBox");
            }

            t = t.parentNode;
        }

        svgRoot = r;
    }

    return svgRoot;
}

/**
 * Instance an SVGPoint object with given event coordinates.
 */
function getEventPoint(evt) {
    var p = root.createSVGPoint();

    p.x = evt.clientX;
    p.y = evt.clientY;

    return p;
}

/**
 * Sets the current transform matrix of an element.
 */
function setCTM(element, matrix) {
    var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")";

    element.setAttribute("transform", s);
}

/**
 * Dumps a matrix to a string (useful for debug).
 */
function dumpMatrix(matrix) {
    var s = "[ " + matrix.a + ", " + matrix.c + ", " + matrix.e + "\n  " + matrix.b + ", " + matrix.d + ", " + matrix.f + "\n  0, 0, 1 ]";

    return s;
}

/**
 * Sets attributes of an element.
 */
function setAttributes(element, attributes){
    for (var i in attributes)
        element.setAttributeNS(null, i, attributes[i]);
}

/**
 * Handle mouse wheel event.
 */
function handleMouseWheel(evt) {
    if(!enableZoom)
        return;

    if(evt.preventDefault)
        evt.preventDefault();

    evt.returnValue = false;

    var svgDoc = evt.target.ownerDocument;

    var delta;

    if(evt.wheelDelta)
        delta = evt.wheelDelta / 360; // Chrome/Safari
    else
        delta = evt.detail / -9; // Mozilla

    var z = Math.pow(1 + zoomScale, delta);

    var g = getRoot(svgDoc);
    
    var p = getEventPoint(evt);

    p = p.matrixTransform(g.getCTM().inverse());

    // Compute new scale matrix in current mouse position
    var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y);

        setCTM(g, g.getCTM().multiply(k));

    if(typeof(stateTf) == "undefined")
        stateTf = g.getCTM().inverse();

    stateTf = stateTf.multiply(k.inverse());
}

/**
 * Handle mouse move event.
 */
function handleMouseMove(evt) {
    if(evt.preventDefault)
        evt.preventDefault();

    evt.returnValue = false;

    var svgDoc = evt.target.ownerDocument;

    var g = getRoot(svgDoc);

    if(state == 'pan' && enablePan) {
        // Pan mode
        var p = getEventPoint(evt).matrixTransform(stateTf);

        setCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y));
    } else if(state == 'drag' && enableDrag) {
        // Drag mode
        var p = getEventPoint(evt).matrixTransform(g.getCTM().inverse());

        setCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM()));

        stateOrigin = p;
    }
}

/**
 * Handle click event.
 */
function handleMouseDown(evt) {
    if(evt.preventDefault)
        evt.preventDefault();

    evt.returnValue = false;

    var svgDoc = evt.target.ownerDocument;

    var g = getRoot(svgDoc);

    if(
        evt.target.tagName == "svg"
        || !enableDrag // Pan anyway when drag is disabled and the user clicked on an element
    ) {
        // Pan mode
        state = 'pan';

        stateTf = g.getCTM().inverse();

        stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
    } else {
        // Drag mode
        state = 'drag';

        stateTarget = evt.target;

        stateTf = g.getCTM().inverse();

        stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
    }
}

/**
 * Handle mouse button release event.
 */
function handleMouseUp(evt) {
    if(evt.preventDefault)
        evt.preventDefault();

    evt.returnValue = false;

    var svgDoc = evt.target.ownerDocument;

    if(state == 'pan' || state == 'drag') {
        // Quit pan mode
        state = '';
    }
}

 

Re: SVGPan.js

Thanks, that does look useful. 

I believe the original comes from:   https://code.google.com/p/svgpan/

Recent comments