diff toolboxes/graph_visualisation/lib/lefty/dotty_edit.lefty @ 0:e9a9cd732c1e tip

first hg version after svn
author wolffd
date Tue, 10 Feb 2015 15:05:51 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toolboxes/graph_visualisation/lib/lefty/dotty_edit.lefty	Tue Feb 10 15:05:51 2015 +0000
@@ -0,0 +1,593 @@
+#
+# dotty_edit: editing functions and data structures
+#
+dotty.protogt.getnodesbyattr = function (gt, key, val) {
+    local nid, node, nlist;
+
+    nlist = [];
+    for (nid in gt.graph.nodes) {
+        node = gt.graph.nodes[nid];
+        if (node.attr[key] == val)
+            nlist[nid] = node;
+    }
+    return nlist;
+};
+dotty.protogt.reachablenodes = function (gt, node) {
+    local nlist, stack, eid, edge, i;
+
+    stack[0] = node;
+    i = 1;
+    while (i > 0) {
+        node = stack[i - 1];
+        i = i - 1;
+        nlist[node.nid] = node;
+        for (eid in node.edges) {
+            edge = node.edges[eid];
+            if (~nlist[edge.head.nid]) {
+                nlist[edge.head.nid] = edge.head;
+                stack[i] = edge.head;
+                i = i + 1;
+            }
+        }
+    }
+    return nlist;
+};
+dotty.protogt.mergegraph = function (gt, graph, show) {
+    local nameid, onode, pos, size, eid, eid2, tnode, hnode, oedge;
+
+    if (~gt.noundo)
+        gt.startadd2undo (gt);
+    for (nameid in graph.nodedict) {
+        pos = null;
+        size = null;
+        onode = graph.nodes[graph.nodedict[nameid]];
+        if (onode.pos)
+            pos = node.pos;
+        if (onode.size)
+            size = node.size;
+        if (~(gt.graph.nodedict[nameid] >= 0)) {
+            pos = null;
+            size = null;
+            if (onode.pos)
+                pos = node.pos;
+            if (onode.size)
+                size = node.size;
+            gt.insertnode (gt, pos, size, nameid, onode.attr, show);
+        }
+    }
+    for (eid in graph.edges) {
+        oedge = graph.edges[eid];
+        tnode = gt.graph.nodes[gt.graph.nodedict[oedge.tail.name]];
+        hnode = gt.graph.nodes[gt.graph.nodedict[oedge.head.name]];
+        for (eid2 in tnode.edges)
+            if (
+                tnode.edges[eid2].tail == tnode &
+                tnode.edges[eid2].head == hnode
+            ) {
+                oedge = null;
+                break;
+            }
+        if (oedge)
+            gt.insertedge (gt, tnode, null, hnode, null, oedge.attr, show);
+    }
+    if (~gt.noundo)
+        gt.endadd2undo (gt);
+};
+dotty.protogt.insertsgraph = function (gt, name, attr, show) {
+    local gid, sgraph, aid;
+
+    if (~gt)
+        return null;
+    gid = gt.graph.maxgid;
+    if (~name) {
+        while (gt.graph.graphdict[(name = concat ('g', gid))] >= 0)
+            gid = gid + 1;
+    } else if (gt.graph.graphdict[name]) {
+        dotty.message (0, concat ('graph: ', name, ' exists'));
+        return null;
+    }
+    gt.graph.graphdict[name] = gid;
+    gt.graph.maxgid = gid + 1;
+    gt.graph.graphs[gid] = [
+        dotty.keys.gid   = gid;
+        dotty.keys.name  = name;
+        dotty.keys.gattr = copy (gt.graph.graphattr);
+        dotty.keys.nattr = copy (gt.graph.nodeattr);
+        dotty.keys.eattr = copy (gt.graph.edgeattr);
+    ];
+    sgraph = gt.graph.graphs[gid];
+    if (~attr)
+        attr = [];
+    if (~attr.label)
+        attr.label = '\N';
+    for (aid in attr)
+        sgraph.graphattr[aid] = attr[aid];
+    gt.unpacksgraphattr (gt, sgraph);
+    if (show)
+        gt.drawsgraph (gt, gt.views, sgraph);
+    return sgraph;
+};
+dotty.protogt.removesgraph = function (gt, sgraph) {
+    gt.undrawsgraph (gt, gt.views, sgraph);
+    remove (sgraph.name, gt.graph.graphdict);
+    remove (sgraph.gid, gt.graph.graphs);
+};
+dotty.protogt.insertnode = function (gt, pos, size, name, attr, show) {
+    local nid, node, aid;
+
+    nid = gt.graph.maxnid;
+    if (~name) {
+        while (gt.graph.nodedict[(name = concat ('n', nid))] >= 0)
+            nid = nid + 1;
+    } else if (gt.graph.nodedict[name] >= 0) {
+        dotty.message (0, concat ('node: ', name, ' exists'));
+        return null;
+    }
+    gt.graph.nodedict[name] = nid;
+    gt.graph.maxnid = nid + 1;
+    gt.graph.nodes[nid] = [
+        dotty.keys.nid   = nid;
+        dotty.keys.name  = name;
+        dotty.keys.attr  = copy (gt.graph.nodeattr);
+        dotty.keys.edges = [];
+    ];
+    node = gt.graph.nodes[nid];
+    if (~attr)
+        attr = [];
+    if (~attr.label)
+        attr.label = '\N';
+    for (aid in attr)
+        node.attr[aid] = attr[aid];
+    gt.unpacknodeattr (gt, node);
+    if (~pos)
+        pos = ['x' = 10; 'y' = 10;];
+    node[dotty.keys.pos] = copy (pos);
+    if (~size)
+        size = ['x' = strlen (attr.label) * 30; 'y' = 30;];
+    if (size.x == 0)
+        size.x = 30;
+    node[dotty.keys.size] = copy (size);
+    node[dotty.keys.rect] = [
+        0 = ['x' = pos.x - size.x / 2; 'y' = pos.y - size.y / 2;];
+        1 = ['x' = pos.x + size.x / 2; 'y' = pos.y + size.y / 2;];
+    ];
+    node.draws = gt.simplenodedraw (node, pos, size);
+    if (show)
+        gt.drawnode (gt, gt.views, node);
+    if (~gt.noundo) {
+        gt.startadd2undo (gt);
+        gt.currundo.inserted.nodes[nid] = node;
+        gt.endadd2undo (gt);
+    }
+    return node;
+};
+dotty.protogt.removenode = function (gt, node) {
+    local eid, list, edge, gid;
+
+    if (~gt.noundo)
+        gt.startadd2undo (gt);
+    for (eid in node.edges)
+        list[eid] = node.edges[eid];
+    for (eid in list)
+        gt.removeedge (gt, list[eid]);
+    gt.undrawnode (gt, gt.views, node);
+    for (gid in gt.graph.graphs)
+        remove (node.nid, gt.graph.graphs[gid].nodes);
+    remove (node.name, gt.graph.nodedict);
+    remove (node.nid, gt.graph.nodes);
+    if (~gt.noundo) {
+        gt.currundo.deleted.nodes[node.nid] = node;
+        gt.endadd2undo (gt);
+    }
+};
+dotty.protogt.insertedge = function (
+    gt, nodea, porta, nodeb, portb, attr, show
+) {
+    local eid, edge, aid, tport, hport;
+
+    if (~nodea | ~nodeb)
+        return null;
+    if (porta)
+        tport = porta;
+    if (portb)
+        hport = portb;
+    eid = gt.graph.maxeid;
+    while (gt.graph.edges[eid])
+        eid = eid + 1;
+    gt.graph.maxeid = eid + 1;
+    gt.graph.edges[eid] = [
+        dotty.keys.eid   = eid;
+        dotty.keys.tail  = nodea;
+        dotty.keys.tport = porta;
+        dotty.keys.head  = nodeb;
+        dotty.keys.hport = portb;
+        dotty.keys.attr  = copy (gt.graph.edgeattr);
+    ];
+    edge = gt.graph.edges[eid];
+    if (~attr)
+        attr = [];
+    for (aid in attr)
+        edge.attr[aid] = attr[aid];
+    nodea.edges[eid] = edge;
+    nodeb.edges[eid] = edge;
+    gt.unpackedgeattr (gt, edge);
+    edge.draws = gt.simpleedgedraw (edge, nodea.pos, nodeb.pos);
+    if (show)
+        gt.drawedge (gt, gt.views, edge);
+    if (~gt.noundo) {
+        gt.startadd2undo (gt);
+        gt.currundo.inserted.edges[eid] = edge;
+        gt.endadd2undo (gt);
+    }
+    return edge;
+};
+dotty.protogt.removeedge = function (gt, edge) {
+    local head, tail;
+
+    if (~gt.noundo)
+        gt.startadd2undo (gt);
+    if (edge.head.attr.support == 1)
+        head = edge.head;
+    if (edge.tail.attr.support == 1)
+        if (head ~= edge.tail)
+            tail = edge.tail;
+    gt.undrawedge (gt, gt.views, edge);
+    remove (edge.eid, edge.head.edges);
+    remove (edge.eid, edge.tail.edges);
+    remove (edge.eid, gt.graph.edges);
+    if (head & tablesize (head.edges) == 0)
+        gt.removenode (gt, head);
+    if (tail & tablesize (tail.edges) == 0)
+        gt.removenode (gt, tail);
+    if (~gt.noundo) {
+        gt.currundo.deleted.edges[edge.eid] = edge;
+        gt.endadd2undo (gt);
+    }
+};
+dotty.protogt.swapedgeids = function (gt, edge1, edge2) {
+    local eid1, eid2;
+
+    if (edge1.eid == edge2.eid)
+        return;
+    if (~gt.noundo)
+        gt.startadd2undo (gt);
+    eid1 = edge1.eid;
+    eid2 = edge2.eid;
+    gt.graph.edges[eid1] = edge2;
+    gt.graph.edges[eid2] = edge1;
+    remove (eid1, edge1.tail.edges);
+    remove (eid1, edge1.head.edges);
+    remove (eid2, edge2.tail.edges);
+    remove (eid2, edge2.head.edges);
+    edge1.tail.edges[eid2] = edge1;
+    edge1.head.edges[eid2] = edge1;
+    edge2.tail.edges[eid1] = edge2;
+    edge2.head.edges[eid1] = edge2;
+    edge1.eid = eid2;
+    edge2.eid = eid1;
+    if (~gt.noundo) {
+        gt.currundo.swapped.edges[eid1] = edge1;
+        gt.currundo.swapped.edges[eid2] = edge2;
+        gt.endadd2undo (gt);
+    }
+};
+dotty.protogt.removesubtree = function (gt, obj) {
+    local nlist, node, head, nid, edge, eid;
+
+    if (~gt.noundo)
+        gt.startadd2undo (gt);
+    if (obj.nid >= 0)
+        node = obj;
+    else if (obj.eid >= 0) {
+        node = obj.head;
+        gt.removeedge (gt, obj);
+        if (~gt.graph.nodes[node.nid]) {
+            if (~gt.noundo)
+                gt.endadd2undo (gt);
+            return;
+        }
+        for (eid in node.edges) {
+            edge = node.edges[eid];
+            if (edge.head == node & edge.tail ~= node) {
+                if (~gt.noundo)
+                    gt.endadd2undo (gt);
+                return;
+            }
+        }
+    } else {
+        dotty.message (0, 'bad object type in gt.removesubtree');
+        return;
+    }
+    nlist = [node.nid = node;];
+    while (node) {
+        for (eid in node.edges) {
+            head = node.edges[eid].head;
+            if (head ~= node)
+                nlist[head.nid] = head;
+        }
+        gt.removenode (gt, node);
+        remove (node.nid, nlist);
+        node = null;
+        for (nid in nlist) {
+            node = nlist[nid];
+            for (eid in node.edges) {
+                edge = node.edges[eid];
+                if (edge.head == node & edge.tail ~= node) {
+                    node = null;
+                    break;
+                }
+            }
+            if (node)
+                break;
+        }
+    }
+    if (~gt.noundo)
+        gt.endadd2undo (gt);
+};
+dotty.protogt.removenodesbyattr = function (gt, key, val) {
+    local nlist, nid;
+
+    if (~gt.noundo)
+        gt.startadd2undo (gt);
+    nlist = gt.getnodesbyattr (gt, key, val);
+    for (nid in nlist)
+        gt.removenode (gt, nlist[nid]);
+    if (~gt.noundo)
+        gt.endadd2undo (gt);
+};
+dotty.protogt.removesubtreesbyattr = function (gt, key, val) {
+    local nlist, nid;
+
+    if (~gt.noundo)
+        gt.startadd2undo (gt);
+    nlist = gt.getnodesbyattr (gt, key, val);
+    for (nid in nlist)
+        if (gt.graph.nodes[nid])
+            gt.removesubtree (gt, nlist[nid]);
+    if (~gt.noundo)
+        gt.endadd2undo (gt);
+};
+dotty.protogt.groupnodes = function (
+    gt, nlist, gnode, pos, size, attr, keepmulti, show
+) {
+    local nid, node, elist, eid, edge, nodea, nodeb, inlist, outlist;
+
+    if (~nlist | tablesize (nlist) == 0)
+        return;
+    if (gnode.attr.support) {
+        dotty.message (0, 'cannot group nodes in a support node');
+        return;
+    }
+    if (~gt.noundo)
+        gt.startadd2undo (gt);
+    if (~gnode)
+        gnode = gt.insertnode (gt, pos, size, null, attr, show);
+    inlist = [];
+    outlist = [];
+    for (nid in nlist) {
+        if ((node = nlist[nid]) == gnode)
+            continue;
+        elist = [];
+        for (eid in node.edges)
+            elist[eid] = node.edges[eid];
+        for (eid in elist) {
+            edge = elist[eid];
+            if (edge.head == node) {
+                nodea = edge.tail;
+                nodeb = gnode;
+                if (~keepmulti) {
+                    if (inlist[nodea.nid])
+                        continue;
+                    inlist[nodea.nid] = nodea;
+                    if (nodea == gnode)
+                        outlist[nodea.nid] = nodea;
+                }
+            } else {
+                nodea = gnode;
+                nodeb = edge.head;
+                if (~keepmulti) {
+                    if (outlist[nodeb.nid])
+                        continue;
+                    outlist[nodeb.nid] = nodeb;
+                    if (nodeb == gnode)
+                        inlist[nodeb.nid] = nodeb;
+                }
+            }
+            gt.insertedge (gt, nodea, null, nodeb, null, edge.attr, show);
+        }
+        gt.removenode (gt, node);
+    }
+    if (~gt.noundo)
+        gt.endadd2undo (gt);
+    return gnode;
+};
+dotty.protogt.groupnodesbyattr = function (
+    gt, key, val, attr, keepmulti, show
+) {
+    local nlist, nid, pos, size;
+
+    pos = null;
+    size = null;
+    nlist = gt.getnodesbyattr (gt, key, val);
+    if (show)
+        for (nid in nlist) {
+            pos = nlist[nid].pos;
+            size = nlist[nid].size;
+            break;
+        }
+    return gt.groupnodes (gt, nlist, null, pos, size, attr, keepmulti, show);
+};
+dotty.protogt.cut = function (gt, obj, set, mode, op) {
+    local clipgt, list, node, nid, edge, eid, clipnode;
+
+    clipgt = dotty.clipgt;
+    clipgt.graph = copy (dotty.protogt.graph);
+    if (obj.eid >= 0) { # it's an edge
+        list.edges[obj.eid] = obj;
+        node = obj.head;
+    } else if (obj.nid >= 0) {
+        list.nodes[obj.nid] = obj;
+        node = obj;
+        for (eid in node.edges)
+            list.edges[eid] = node.edges[eid];
+    } else {
+        dotty.message (0, 'unknown object type in gt.cut');
+        return;
+    }
+    if (set == 'reachable') {
+        list.nodes = gt.reachablenodes (gt, node);
+        for (nid in list.nodes) {
+            node = list.nodes[nid];
+            for (eid in node.edges) {
+                edge = node.edges[eid];
+                list.edges[edge.eid] = edge;
+            }
+        }
+    }
+    if (mode == 'support') {
+        for (eid in list.edges) {
+            edge = list.edges[eid];
+            if (~list.nodes[edge.tail.nid]) {
+                list.support[edge.tail.nid] = edge.tail;
+                list.nodes[edge.tail.nid] = edge.tail;
+            }
+            if (~list.nodes[edge.head.nid]) {
+                list.support[edge.head.nid] = edge.head;
+                list.nodes[edge.head.nid] = edge.head;
+            }
+        }
+    }
+    for (nid = 0; nid < gt.graph.maxnid; nid = nid + 1) {
+        if (~list.nodes[nid])
+            continue;
+        node = list.nodes[nid];
+        clipnode = gt.insertnode (clipgt, null, null, node.name, node.attr, 0);
+        if (list.support[nid])
+            clipnode.support = 1;
+        list.clipnodes[nid] = clipnode;
+    }
+    for (eid = 0; eid < gt.graph.maxeid; eid = eid + 1) {
+        if (~list.edges[eid])
+            continue;
+        edge = list.edges[eid];
+        if (~list.nodes[edge.tail.nid] | ~list.nodes[edge.head.nid])
+            continue;
+        gt.insertedge (
+            clipgt, list.clipnodes[edge.tail.nid], null,
+            list.clipnodes[edge.head.nid], null, edge.attr, 0
+        );
+    }
+    if (op ~= 'cut')
+        return;
+    if (~gt.noundo)
+        gt.startadd2undo (gt);
+    for (eid in list.edges)
+        gt.removeedge (gt, list.edges[eid]);
+    for (nid in list.nodes)
+        if (~list.support[nid] & gt.graph.nodes[nid])
+            gt.removenode (gt, list.nodes[nid]);
+    if (~gt.noundo)
+        gt.endadd2undo (gt);
+};
+dotty.protogt.paste = function (gt, pos, show) {
+    local clipgt, offset, center, nid, node, eid, edge, nodes;
+
+    if (~gt.noundo)
+        gt.startadd2undo (gt);
+    clipgt = dotty.clipgt;
+    if (clipgt.graph.rect)
+        center = [
+            'x' = (clipgt.graph.rect[1].x + clipgt.graph.rect[0].x) / 2;
+            'y' = (clipgt.graph.rect[1].y + clipgt.graph.rect[0].y) / 2;
+        ];
+    else
+        center = pos;
+    offset = [
+        'x' = center.x - pos.x;
+        'y' = center.y - pos.y;
+    ];
+    for (nid = 0; clipgt.graph.nodes[nid]; nid = nid + 1) {
+        node = clipgt.graph.nodes[nid];
+        if (node.attr.label == '\N' | ~node.attr.label)
+            node.attr.label = node.name;
+        if (node.support == 1)
+            nodes[nid] = gt.insertnode (gt, [
+                'x' = node.pos.x - offset.x;
+                'y' = node.pos.y - offset.y;
+            ], null, null, [
+                'support' = 1; 'shape' = 'circle';
+                'label' = ''; 'width' = 0.2;
+            ], show);
+        else
+            nodes[nid] = gt.insertnode (gt, [
+                'x' = node.pos.x - offset.x;
+                'y' = node.pos.y - offset.y;
+            ], node.size, null, node.attr, show);
+    }
+    for (eid = 0; clipgt.graph.edges[eid]; eid = eid + 1) {
+        edge = clipgt.graph.edges[eid];
+        gt.insertedge (
+            gt, nodes[edge.tail.nid], null,
+            nodes[edge.head.nid], null, edge.attr, show
+        );
+    }
+    if (~gt.noundo)
+        gt.endadd2undo (gt);
+};
+dotty.protogt.startadd2undo = function (gt) {
+    if (~gt.undoarray.level)
+        gt.currundo = (
+            gt.undoarray.entries[tablesize (gt.undoarray.entries)] = []
+        );
+    gt.undoarray.level = gt.undoarray.level + 1;
+};
+dotty.protogt.endadd2undo = function (gt) {
+    gt.undoarray.level = gt.undoarray.level - 1;
+};
+dotty.protogt.undo = function (gt, show) {
+    local entry, n, eid, edge, nid, node, edges;
+
+    if ((n = tablesize (gt.undoarray.entries)) < 1)
+        return;
+    entry = gt.undoarray.entries[n - 1];
+    remove (n - 1, gt.undoarray.entries);
+    remove ('currundo', gt);
+    gt.noundo = 1;
+    # hardwire nodes and edges back with the same id's as the originals
+    for (nid in entry.deleted.nodes) {
+        node = entry.deleted.nodes[nid];
+        gt.graph.nodedict[node.name] = node.nid;
+        gt.graph.nodes[node.nid] = node;
+        node.edges = [];
+        if (show)
+            gt.drawnode (gt, gt.views, node);
+    }
+    for (eid in entry.deleted.edges) {
+        edge = entry.deleted.edges[eid];
+        gt.graph.edges[edge.eid] = edge;
+        edge.head.edges[edge.eid] = edge;
+        edge.tail.edges[edge.eid] = edge;
+        if (show)
+            gt.drawedge (gt, gt.views, edge);
+    }
+    if (entry.swapped.edges) {
+        if (tablesize (entry.swapped.edges) == 2) {
+            n = 0;
+            for (eid in entry.swapped.edges) {
+                edges[n] = entry.swapped.edges[eid];
+                n = n + 1;
+            }
+            gt.swapedgeids (gt, edges[0], edges[1]);
+        } else
+            dotty.message (0, 'cannot handle undoing swap of > 2 edges');
+    }
+    for (eid in entry.inserted.edges) {
+        edge = entry.inserted.edges[eid];
+        gt.removeedge (gt, edge);
+    }
+    for (nid in entry.inserted.nodes) {
+        node = entry.inserted.nodes[nid];
+        gt.removenode (gt, node);
+    }
+    gt.noundo = 0;
+};