comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:e9a9cd732c1e
1 #
2 # dotty_edit: editing functions and data structures
3 #
4 dotty.protogt.getnodesbyattr = function (gt, key, val) {
5 local nid, node, nlist;
6
7 nlist = [];
8 for (nid in gt.graph.nodes) {
9 node = gt.graph.nodes[nid];
10 if (node.attr[key] == val)
11 nlist[nid] = node;
12 }
13 return nlist;
14 };
15 dotty.protogt.reachablenodes = function (gt, node) {
16 local nlist, stack, eid, edge, i;
17
18 stack[0] = node;
19 i = 1;
20 while (i > 0) {
21 node = stack[i - 1];
22 i = i - 1;
23 nlist[node.nid] = node;
24 for (eid in node.edges) {
25 edge = node.edges[eid];
26 if (~nlist[edge.head.nid]) {
27 nlist[edge.head.nid] = edge.head;
28 stack[i] = edge.head;
29 i = i + 1;
30 }
31 }
32 }
33 return nlist;
34 };
35 dotty.protogt.mergegraph = function (gt, graph, show) {
36 local nameid, onode, pos, size, eid, eid2, tnode, hnode, oedge;
37
38 if (~gt.noundo)
39 gt.startadd2undo (gt);
40 for (nameid in graph.nodedict) {
41 pos = null;
42 size = null;
43 onode = graph.nodes[graph.nodedict[nameid]];
44 if (onode.pos)
45 pos = node.pos;
46 if (onode.size)
47 size = node.size;
48 if (~(gt.graph.nodedict[nameid] >= 0)) {
49 pos = null;
50 size = null;
51 if (onode.pos)
52 pos = node.pos;
53 if (onode.size)
54 size = node.size;
55 gt.insertnode (gt, pos, size, nameid, onode.attr, show);
56 }
57 }
58 for (eid in graph.edges) {
59 oedge = graph.edges[eid];
60 tnode = gt.graph.nodes[gt.graph.nodedict[oedge.tail.name]];
61 hnode = gt.graph.nodes[gt.graph.nodedict[oedge.head.name]];
62 for (eid2 in tnode.edges)
63 if (
64 tnode.edges[eid2].tail == tnode &
65 tnode.edges[eid2].head == hnode
66 ) {
67 oedge = null;
68 break;
69 }
70 if (oedge)
71 gt.insertedge (gt, tnode, null, hnode, null, oedge.attr, show);
72 }
73 if (~gt.noundo)
74 gt.endadd2undo (gt);
75 };
76 dotty.protogt.insertsgraph = function (gt, name, attr, show) {
77 local gid, sgraph, aid;
78
79 if (~gt)
80 return null;
81 gid = gt.graph.maxgid;
82 if (~name) {
83 while (gt.graph.graphdict[(name = concat ('g', gid))] >= 0)
84 gid = gid + 1;
85 } else if (gt.graph.graphdict[name]) {
86 dotty.message (0, concat ('graph: ', name, ' exists'));
87 return null;
88 }
89 gt.graph.graphdict[name] = gid;
90 gt.graph.maxgid = gid + 1;
91 gt.graph.graphs[gid] = [
92 dotty.keys.gid = gid;
93 dotty.keys.name = name;
94 dotty.keys.gattr = copy (gt.graph.graphattr);
95 dotty.keys.nattr = copy (gt.graph.nodeattr);
96 dotty.keys.eattr = copy (gt.graph.edgeattr);
97 ];
98 sgraph = gt.graph.graphs[gid];
99 if (~attr)
100 attr = [];
101 if (~attr.label)
102 attr.label = '\N';
103 for (aid in attr)
104 sgraph.graphattr[aid] = attr[aid];
105 gt.unpacksgraphattr (gt, sgraph);
106 if (show)
107 gt.drawsgraph (gt, gt.views, sgraph);
108 return sgraph;
109 };
110 dotty.protogt.removesgraph = function (gt, sgraph) {
111 gt.undrawsgraph (gt, gt.views, sgraph);
112 remove (sgraph.name, gt.graph.graphdict);
113 remove (sgraph.gid, gt.graph.graphs);
114 };
115 dotty.protogt.insertnode = function (gt, pos, size, name, attr, show) {
116 local nid, node, aid;
117
118 nid = gt.graph.maxnid;
119 if (~name) {
120 while (gt.graph.nodedict[(name = concat ('n', nid))] >= 0)
121 nid = nid + 1;
122 } else if (gt.graph.nodedict[name] >= 0) {
123 dotty.message (0, concat ('node: ', name, ' exists'));
124 return null;
125 }
126 gt.graph.nodedict[name] = nid;
127 gt.graph.maxnid = nid + 1;
128 gt.graph.nodes[nid] = [
129 dotty.keys.nid = nid;
130 dotty.keys.name = name;
131 dotty.keys.attr = copy (gt.graph.nodeattr);
132 dotty.keys.edges = [];
133 ];
134 node = gt.graph.nodes[nid];
135 if (~attr)
136 attr = [];
137 if (~attr.label)
138 attr.label = '\N';
139 for (aid in attr)
140 node.attr[aid] = attr[aid];
141 gt.unpacknodeattr (gt, node);
142 if (~pos)
143 pos = ['x' = 10; 'y' = 10;];
144 node[dotty.keys.pos] = copy (pos);
145 if (~size)
146 size = ['x' = strlen (attr.label) * 30; 'y' = 30;];
147 if (size.x == 0)
148 size.x = 30;
149 node[dotty.keys.size] = copy (size);
150 node[dotty.keys.rect] = [
151 0 = ['x' = pos.x - size.x / 2; 'y' = pos.y - size.y / 2;];
152 1 = ['x' = pos.x + size.x / 2; 'y' = pos.y + size.y / 2;];
153 ];
154 node.draws = gt.simplenodedraw (node, pos, size);
155 if (show)
156 gt.drawnode (gt, gt.views, node);
157 if (~gt.noundo) {
158 gt.startadd2undo (gt);
159 gt.currundo.inserted.nodes[nid] = node;
160 gt.endadd2undo (gt);
161 }
162 return node;
163 };
164 dotty.protogt.removenode = function (gt, node) {
165 local eid, list, edge, gid;
166
167 if (~gt.noundo)
168 gt.startadd2undo (gt);
169 for (eid in node.edges)
170 list[eid] = node.edges[eid];
171 for (eid in list)
172 gt.removeedge (gt, list[eid]);
173 gt.undrawnode (gt, gt.views, node);
174 for (gid in gt.graph.graphs)
175 remove (node.nid, gt.graph.graphs[gid].nodes);
176 remove (node.name, gt.graph.nodedict);
177 remove (node.nid, gt.graph.nodes);
178 if (~gt.noundo) {
179 gt.currundo.deleted.nodes[node.nid] = node;
180 gt.endadd2undo (gt);
181 }
182 };
183 dotty.protogt.insertedge = function (
184 gt, nodea, porta, nodeb, portb, attr, show
185 ) {
186 local eid, edge, aid, tport, hport;
187
188 if (~nodea | ~nodeb)
189 return null;
190 if (porta)
191 tport = porta;
192 if (portb)
193 hport = portb;
194 eid = gt.graph.maxeid;
195 while (gt.graph.edges[eid])
196 eid = eid + 1;
197 gt.graph.maxeid = eid + 1;
198 gt.graph.edges[eid] = [
199 dotty.keys.eid = eid;
200 dotty.keys.tail = nodea;
201 dotty.keys.tport = porta;
202 dotty.keys.head = nodeb;
203 dotty.keys.hport = portb;
204 dotty.keys.attr = copy (gt.graph.edgeattr);
205 ];
206 edge = gt.graph.edges[eid];
207 if (~attr)
208 attr = [];
209 for (aid in attr)
210 edge.attr[aid] = attr[aid];
211 nodea.edges[eid] = edge;
212 nodeb.edges[eid] = edge;
213 gt.unpackedgeattr (gt, edge);
214 edge.draws = gt.simpleedgedraw (edge, nodea.pos, nodeb.pos);
215 if (show)
216 gt.drawedge (gt, gt.views, edge);
217 if (~gt.noundo) {
218 gt.startadd2undo (gt);
219 gt.currundo.inserted.edges[eid] = edge;
220 gt.endadd2undo (gt);
221 }
222 return edge;
223 };
224 dotty.protogt.removeedge = function (gt, edge) {
225 local head, tail;
226
227 if (~gt.noundo)
228 gt.startadd2undo (gt);
229 if (edge.head.attr.support == 1)
230 head = edge.head;
231 if (edge.tail.attr.support == 1)
232 if (head ~= edge.tail)
233 tail = edge.tail;
234 gt.undrawedge (gt, gt.views, edge);
235 remove (edge.eid, edge.head.edges);
236 remove (edge.eid, edge.tail.edges);
237 remove (edge.eid, gt.graph.edges);
238 if (head & tablesize (head.edges) == 0)
239 gt.removenode (gt, head);
240 if (tail & tablesize (tail.edges) == 0)
241 gt.removenode (gt, tail);
242 if (~gt.noundo) {
243 gt.currundo.deleted.edges[edge.eid] = edge;
244 gt.endadd2undo (gt);
245 }
246 };
247 dotty.protogt.swapedgeids = function (gt, edge1, edge2) {
248 local eid1, eid2;
249
250 if (edge1.eid == edge2.eid)
251 return;
252 if (~gt.noundo)
253 gt.startadd2undo (gt);
254 eid1 = edge1.eid;
255 eid2 = edge2.eid;
256 gt.graph.edges[eid1] = edge2;
257 gt.graph.edges[eid2] = edge1;
258 remove (eid1, edge1.tail.edges);
259 remove (eid1, edge1.head.edges);
260 remove (eid2, edge2.tail.edges);
261 remove (eid2, edge2.head.edges);
262 edge1.tail.edges[eid2] = edge1;
263 edge1.head.edges[eid2] = edge1;
264 edge2.tail.edges[eid1] = edge2;
265 edge2.head.edges[eid1] = edge2;
266 edge1.eid = eid2;
267 edge2.eid = eid1;
268 if (~gt.noundo) {
269 gt.currundo.swapped.edges[eid1] = edge1;
270 gt.currundo.swapped.edges[eid2] = edge2;
271 gt.endadd2undo (gt);
272 }
273 };
274 dotty.protogt.removesubtree = function (gt, obj) {
275 local nlist, node, head, nid, edge, eid;
276
277 if (~gt.noundo)
278 gt.startadd2undo (gt);
279 if (obj.nid >= 0)
280 node = obj;
281 else if (obj.eid >= 0) {
282 node = obj.head;
283 gt.removeedge (gt, obj);
284 if (~gt.graph.nodes[node.nid]) {
285 if (~gt.noundo)
286 gt.endadd2undo (gt);
287 return;
288 }
289 for (eid in node.edges) {
290 edge = node.edges[eid];
291 if (edge.head == node & edge.tail ~= node) {
292 if (~gt.noundo)
293 gt.endadd2undo (gt);
294 return;
295 }
296 }
297 } else {
298 dotty.message (0, 'bad object type in gt.removesubtree');
299 return;
300 }
301 nlist = [node.nid = node;];
302 while (node) {
303 for (eid in node.edges) {
304 head = node.edges[eid].head;
305 if (head ~= node)
306 nlist[head.nid] = head;
307 }
308 gt.removenode (gt, node);
309 remove (node.nid, nlist);
310 node = null;
311 for (nid in nlist) {
312 node = nlist[nid];
313 for (eid in node.edges) {
314 edge = node.edges[eid];
315 if (edge.head == node & edge.tail ~= node) {
316 node = null;
317 break;
318 }
319 }
320 if (node)
321 break;
322 }
323 }
324 if (~gt.noundo)
325 gt.endadd2undo (gt);
326 };
327 dotty.protogt.removenodesbyattr = function (gt, key, val) {
328 local nlist, nid;
329
330 if (~gt.noundo)
331 gt.startadd2undo (gt);
332 nlist = gt.getnodesbyattr (gt, key, val);
333 for (nid in nlist)
334 gt.removenode (gt, nlist[nid]);
335 if (~gt.noundo)
336 gt.endadd2undo (gt);
337 };
338 dotty.protogt.removesubtreesbyattr = function (gt, key, val) {
339 local nlist, nid;
340
341 if (~gt.noundo)
342 gt.startadd2undo (gt);
343 nlist = gt.getnodesbyattr (gt, key, val);
344 for (nid in nlist)
345 if (gt.graph.nodes[nid])
346 gt.removesubtree (gt, nlist[nid]);
347 if (~gt.noundo)
348 gt.endadd2undo (gt);
349 };
350 dotty.protogt.groupnodes = function (
351 gt, nlist, gnode, pos, size, attr, keepmulti, show
352 ) {
353 local nid, node, elist, eid, edge, nodea, nodeb, inlist, outlist;
354
355 if (~nlist | tablesize (nlist) == 0)
356 return;
357 if (gnode.attr.support) {
358 dotty.message (0, 'cannot group nodes in a support node');
359 return;
360 }
361 if (~gt.noundo)
362 gt.startadd2undo (gt);
363 if (~gnode)
364 gnode = gt.insertnode (gt, pos, size, null, attr, show);
365 inlist = [];
366 outlist = [];
367 for (nid in nlist) {
368 if ((node = nlist[nid]) == gnode)
369 continue;
370 elist = [];
371 for (eid in node.edges)
372 elist[eid] = node.edges[eid];
373 for (eid in elist) {
374 edge = elist[eid];
375 if (edge.head == node) {
376 nodea = edge.tail;
377 nodeb = gnode;
378 if (~keepmulti) {
379 if (inlist[nodea.nid])
380 continue;
381 inlist[nodea.nid] = nodea;
382 if (nodea == gnode)
383 outlist[nodea.nid] = nodea;
384 }
385 } else {
386 nodea = gnode;
387 nodeb = edge.head;
388 if (~keepmulti) {
389 if (outlist[nodeb.nid])
390 continue;
391 outlist[nodeb.nid] = nodeb;
392 if (nodeb == gnode)
393 inlist[nodeb.nid] = nodeb;
394 }
395 }
396 gt.insertedge (gt, nodea, null, nodeb, null, edge.attr, show);
397 }
398 gt.removenode (gt, node);
399 }
400 if (~gt.noundo)
401 gt.endadd2undo (gt);
402 return gnode;
403 };
404 dotty.protogt.groupnodesbyattr = function (
405 gt, key, val, attr, keepmulti, show
406 ) {
407 local nlist, nid, pos, size;
408
409 pos = null;
410 size = null;
411 nlist = gt.getnodesbyattr (gt, key, val);
412 if (show)
413 for (nid in nlist) {
414 pos = nlist[nid].pos;
415 size = nlist[nid].size;
416 break;
417 }
418 return gt.groupnodes (gt, nlist, null, pos, size, attr, keepmulti, show);
419 };
420 dotty.protogt.cut = function (gt, obj, set, mode, op) {
421 local clipgt, list, node, nid, edge, eid, clipnode;
422
423 clipgt = dotty.clipgt;
424 clipgt.graph = copy (dotty.protogt.graph);
425 if (obj.eid >= 0) { # it's an edge
426 list.edges[obj.eid] = obj;
427 node = obj.head;
428 } else if (obj.nid >= 0) {
429 list.nodes[obj.nid] = obj;
430 node = obj;
431 for (eid in node.edges)
432 list.edges[eid] = node.edges[eid];
433 } else {
434 dotty.message (0, 'unknown object type in gt.cut');
435 return;
436 }
437 if (set == 'reachable') {
438 list.nodes = gt.reachablenodes (gt, node);
439 for (nid in list.nodes) {
440 node = list.nodes[nid];
441 for (eid in node.edges) {
442 edge = node.edges[eid];
443 list.edges[edge.eid] = edge;
444 }
445 }
446 }
447 if (mode == 'support') {
448 for (eid in list.edges) {
449 edge = list.edges[eid];
450 if (~list.nodes[edge.tail.nid]) {
451 list.support[edge.tail.nid] = edge.tail;
452 list.nodes[edge.tail.nid] = edge.tail;
453 }
454 if (~list.nodes[edge.head.nid]) {
455 list.support[edge.head.nid] = edge.head;
456 list.nodes[edge.head.nid] = edge.head;
457 }
458 }
459 }
460 for (nid = 0; nid < gt.graph.maxnid; nid = nid + 1) {
461 if (~list.nodes[nid])
462 continue;
463 node = list.nodes[nid];
464 clipnode = gt.insertnode (clipgt, null, null, node.name, node.attr, 0);
465 if (list.support[nid])
466 clipnode.support = 1;
467 list.clipnodes[nid] = clipnode;
468 }
469 for (eid = 0; eid < gt.graph.maxeid; eid = eid + 1) {
470 if (~list.edges[eid])
471 continue;
472 edge = list.edges[eid];
473 if (~list.nodes[edge.tail.nid] | ~list.nodes[edge.head.nid])
474 continue;
475 gt.insertedge (
476 clipgt, list.clipnodes[edge.tail.nid], null,
477 list.clipnodes[edge.head.nid], null, edge.attr, 0
478 );
479 }
480 if (op ~= 'cut')
481 return;
482 if (~gt.noundo)
483 gt.startadd2undo (gt);
484 for (eid in list.edges)
485 gt.removeedge (gt, list.edges[eid]);
486 for (nid in list.nodes)
487 if (~list.support[nid] & gt.graph.nodes[nid])
488 gt.removenode (gt, list.nodes[nid]);
489 if (~gt.noundo)
490 gt.endadd2undo (gt);
491 };
492 dotty.protogt.paste = function (gt, pos, show) {
493 local clipgt, offset, center, nid, node, eid, edge, nodes;
494
495 if (~gt.noundo)
496 gt.startadd2undo (gt);
497 clipgt = dotty.clipgt;
498 if (clipgt.graph.rect)
499 center = [
500 'x' = (clipgt.graph.rect[1].x + clipgt.graph.rect[0].x) / 2;
501 'y' = (clipgt.graph.rect[1].y + clipgt.graph.rect[0].y) / 2;
502 ];
503 else
504 center = pos;
505 offset = [
506 'x' = center.x - pos.x;
507 'y' = center.y - pos.y;
508 ];
509 for (nid = 0; clipgt.graph.nodes[nid]; nid = nid + 1) {
510 node = clipgt.graph.nodes[nid];
511 if (node.attr.label == '\N' | ~node.attr.label)
512 node.attr.label = node.name;
513 if (node.support == 1)
514 nodes[nid] = gt.insertnode (gt, [
515 'x' = node.pos.x - offset.x;
516 'y' = node.pos.y - offset.y;
517 ], null, null, [
518 'support' = 1; 'shape' = 'circle';
519 'label' = ''; 'width' = 0.2;
520 ], show);
521 else
522 nodes[nid] = gt.insertnode (gt, [
523 'x' = node.pos.x - offset.x;
524 'y' = node.pos.y - offset.y;
525 ], node.size, null, node.attr, show);
526 }
527 for (eid = 0; clipgt.graph.edges[eid]; eid = eid + 1) {
528 edge = clipgt.graph.edges[eid];
529 gt.insertedge (
530 gt, nodes[edge.tail.nid], null,
531 nodes[edge.head.nid], null, edge.attr, show
532 );
533 }
534 if (~gt.noundo)
535 gt.endadd2undo (gt);
536 };
537 dotty.protogt.startadd2undo = function (gt) {
538 if (~gt.undoarray.level)
539 gt.currundo = (
540 gt.undoarray.entries[tablesize (gt.undoarray.entries)] = []
541 );
542 gt.undoarray.level = gt.undoarray.level + 1;
543 };
544 dotty.protogt.endadd2undo = function (gt) {
545 gt.undoarray.level = gt.undoarray.level - 1;
546 };
547 dotty.protogt.undo = function (gt, show) {
548 local entry, n, eid, edge, nid, node, edges;
549
550 if ((n = tablesize (gt.undoarray.entries)) < 1)
551 return;
552 entry = gt.undoarray.entries[n - 1];
553 remove (n - 1, gt.undoarray.entries);
554 remove ('currundo', gt);
555 gt.noundo = 1;
556 # hardwire nodes and edges back with the same id's as the originals
557 for (nid in entry.deleted.nodes) {
558 node = entry.deleted.nodes[nid];
559 gt.graph.nodedict[node.name] = node.nid;
560 gt.graph.nodes[node.nid] = node;
561 node.edges = [];
562 if (show)
563 gt.drawnode (gt, gt.views, node);
564 }
565 for (eid in entry.deleted.edges) {
566 edge = entry.deleted.edges[eid];
567 gt.graph.edges[edge.eid] = edge;
568 edge.head.edges[edge.eid] = edge;
569 edge.tail.edges[edge.eid] = edge;
570 if (show)
571 gt.drawedge (gt, gt.views, edge);
572 }
573 if (entry.swapped.edges) {
574 if (tablesize (entry.swapped.edges) == 2) {
575 n = 0;
576 for (eid in entry.swapped.edges) {
577 edges[n] = entry.swapped.edges[eid];
578 n = n + 1;
579 }
580 gt.swapedgeids (gt, edges[0], edges[1]);
581 } else
582 dotty.message (0, 'cannot handle undoing swap of > 2 edges');
583 }
584 for (eid in entry.inserted.edges) {
585 edge = entry.inserted.edges[eid];
586 gt.removeedge (gt, edge);
587 }
588 for (nid in entry.inserted.nodes) {
589 node = entry.inserted.nodes[nid];
590 gt.removenode (gt, node);
591 }
592 gt.noundo = 0;
593 };