| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908 |
- /**
- * Copyright (c) 2006-2018, JGraph Ltd
- * Copyright (c) 2006-2018, Gaudenz Alder
- */
- /**
- * Class: mxWebColaAdaptor
- *
- * Extends WebCola's cola object to act as both adaptor and layout in WebCola for mxGraph.
- *
- * Constructor: mxWebColaAdaptor
- *
- * Constructs a new WebCola-based adaptor for given mxGraph.
- *
- * Arguments:
- *
- * graph - <mxGraph> that contains the cells.
- * dimension - <[]> array containing [width, height] of canvas in points
- * movableVertices - <[]> IDs of vertices that are movable; if undefined all vertices are movable
- * options - <{}> WebCola options for layout/adapter
- *
- **/
- var doNothing = function()
- /**
- * Empty method for default event handlers
- */
- {
- }
- function mxWebColaAdaptor(graph, dimension, movableVertices, options)
- /**
- * Constructs a WebCola adaptor for mxGraph
- * @param graph mxGraph instance
- * @param dimension array containing [width, height] of drawing canvas in points
- * @param movableVertices set containing IDs of vertices that are movable; if undefined all vertices are movable
- * @param options WebCola options for layout/adapter
- * @constructor
- */
- {
- this.graph = graph;
- this.dimension = dimension;
- if (typeof dimension === 'undefined')
- {
- this.dimension = [600, 600];
- }
- // compute vertex/group degrees from incidence
- this.vertexDegrees = new mxDictionary();
- this.groupDegrees = new mxDictionary();
- this.computeVertexDegrees();
- // convert draw.io graph to WebCola's nodes/links
- var layoutResult = this.graphToLayout(graph, movableVertices);
- this.nodes = layoutResult.nodes;
- this.links = layoutResult.links;
- this.groups = layoutResult.groups;
- this.cellToNode = layoutResult.cellToNode;
- this.isStopped = false;
- this.options = {};
- // assign default values
- for (var key in this.defaultValues)
- {
- this.options[key] = this.defaultValues[key];
- }
- // if options were passed, override defaults for keys available in options
- if (options != null)
- {
- for (var key in options)
- {
- this.options[key] = options[key];
- }
- }
- }
- // default layout options
- mxWebColaAdaptor.prototype.defaultValues = {
- doAnimations: true, // whether to show the layout as it's running
- // doAnimations: false, // whether to show the layout as it's running
- skipFrames: 1, // number of ticks per frame; higher is faster but more jerky
- maxSimulationTime: 4000, // max length in ms to run the layout
- ungrabifyWhileSimulating: false, // so you can't drag nodes during layout
- fit: true, // on every layout reposition of nodes, fit the viewport
- padding: 30, // padding around the simulation
- boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
- nodeDimensionsIncludeLabels: false, // whether labels should be included in determining the space used by a node
- // layout event callbacks
- ready: function ready() {}, // on layoutready
- stop: function stop() {}, // on layoutstop
- // positioning options
- randomize: false, // use random node positions at beginning of layout
- avoidOverlap: true, // if true, prevents overlap of node bounding boxes
- handleDisconnected: true, // if true, avoids disconnected components from overlapping
- nodeSpacing: function nodeSpacing(node) {
- return 10;
- }, // extra spacing around nodes
- flow: undefined, // use DAG/tree flow layout if specified, e.g. { axis: 'y', minSeparation: 30 }
- alignment: undefined, // relative alignment constraints on nodes, e.g. function( node ){ return { x: 0, y: 1 } }
- gapInequalities: undefined, // list of inequality constraints for the gap between the nodes, e.g. [{"axis":"y", "left":node1, "right":node2, "gap":25}]
- // different methods of specifying edge length
- // each can be a constant numerical value or a function like `function( edge ){ return 2; }`
- edgeLength: undefined, // sets edge length directly in simulation
- edgeSymDiffLength: undefined, // symmetric diff edge length in simulation
- edgeJaccardLength: undefined, // jaccard edge length in simulation
- // iterations of cola algorithm; uses default values on undefined
- unconstrIter: undefined, // unconstrained initial layout iterations
- userConstIter: undefined, // initial layout iterations with user-specified constraints
- allConstIter: undefined, // initial layout iterations with all constraints including non-overlap
- // infinite layout options
- keepRunning: false // overrides all other options for a forces-all-the-time mode
- };
- mxWebColaAdaptor.prototype.updatePositions = function(isUndoable)
- /**
- * Default method for updating positions
- * Should be overridden by the caller/user of the adaptor
- */
- {
- console.log("colaAdaptor: updatePositions");
- // TODO: do all the positions here
- }
- mxWebColaAdaptor.prototype.kick = function (colaAdaptor)
- /**
- * Starts WebCola computation on the given adaptor
- */
- {
- console.log("colaAdaptor: step");
- if ('doAnimations' in this.options && this.options.doAnimations)
- {
- doRendering(this.callback);
- }
- else
- {
- // run until the end
- while (!this.process(colaAdaptor))
- {
- }
- }
- }
- mxWebColaAdaptor.prototype.step = function (colaAdaptor)
- /**
- * Notifies about a single layout computation step on WebCola adaptor
- */
- {
- if ('doAnimations' in this.options && this.options.doAnimations)
- {
- this.updatePositions(false);
- }
- }
- mxWebColaAdaptor.prototype.frameSteps = function(colaAdaptor)
- /**
- * Runs multiple ticks on WebCola adaptor until finished
- */
- {
- var result = void 0;
- for (var i = 0; i < this.options.skipFrames && !result; i++) {
- result = result || this.process(colaAdaptor);
- }
- return result;
- }
- mxWebColaAdaptor.prototype.process = function(colaAdaptor)
- /**
- * Executes the whole layout computation on WebCola adaptor
- */
- {
- if (this.isStopped)
- {
- this.finish();
- return true;
- }
- var result = colaAdaptor.tick();
- if (result && this.options.keepRunning) {
- colaAdaptor.resume();
- }
- return result;
- }
- mxWebColaAdaptor.prototype.renderingChain = function(colaAdaptor)
- /**
- * This keeps rendering new simulation frames until end is reached
- */
- {
- if (this.process(colaAdaptor))
- {
- return;
- }
- doRendering(this.callback);
- }
- mxWebColaAdaptor.prototype.finish = function()
- {
- }
- mxWebColaAdaptor.prototype.run = function()
- /**
- * Runs the layout computation on given nodes/links/groups
- * @returns Nothing
- */
- {
- var layout = this;
- var options = this.options;
- var colaAdaptor = layout.adaptor = cola.adaptor
- ({
- trigger: function (evt)
- {
- var START = cola.EventType ? cola.EventType.start : 'start';
- var TICK = cola.EventType ? cola.EventType.tick : 'tick';
- var END = cola.EventType ? cola.EventType.end : 'end';
- switch (evt.type)
- {
- case START:
- {
- // colaAdaptor.start();
- }
- break;
- case TICK:
- {
- layout.step();
- }
- break;
- case END:
- {
- console.log("colaAdaptor: end");
- layout.updatePositions(true);
- if (!options.keepRunning)
- {
- layout.finish();
- }
- }
- break;
- }
- },
- kick: function ()
- {
- layout.kick(colaAdaptor);
- },
- finish: function()
- {
- layout.finish();
- },
- on: doNothing,
- drag: doNothing
- });
- colaAdaptor.nodes(this.nodes)
- .links(this.links)
- .groups(this.groups)
- .size(this.dimension)
- .linkDistance(function (link)
- {
- return link.length;
- });
- layout.callback = function()
- {
- layout.renderingChain(colaAdaptor);
- }
- colaAdaptor.avoidOverlaps(options.avoidOverlap)
- .handleDisconnected(options.handleDisconnected)
- // .constraints(constraints)
- // .start(100, 100, 100);
- .start();
- return this.adaptor;
- }
- function getScreenConstraints(layout, width, height)
- /**
- * Returns a set of constraints covering limits of screen
- * @param layout
- * @param width
- * @param height
- * @returns {Array}
- */
- {
- var gap = 20;
- var size = layout._nodes.length;
- var topLeft = {x: 0, y: 0, fixed: true, index: size};
- var bottomRight = {x: width, y: height, fixed: true, index: size + 1};
- layout._nodes.push(topLeft);
- layout._nodes.push(bottomRight);
- var constraints = [];
- for (var i = 0; i < size; i++) {
- var index = layout._nodes[i].index;
- constraints.push({ axis: 'x', type: 'separation', left: topLeft.index, right: index, gap: gap });
- constraints.push({ axis: 'y', type: 'separation', left: topLeft.index, right: index, gap: gap });
- constraints.push({ axis: 'x', type: 'separation', left: index, right: bottomRight.index, gap: gap });
- constraints.push({ axis: 'y', type: 'separation', left: index, right: bottomRight.index, gap: gap });
- }
- return constraints;
- }
- mxWebColaAdaptor.prototype.graphToLayout = function(graph, movableVertices)
- /**
- * Returns a WebCola layout set up for the given Draw.io graph
- * In WebCola's TypeScript source: vertex cell -> InputNode
- * edge cell -> Link
- * parent/child -> Group
- * @param graph Draw.io graph object
- * @param fixedVertices Vertices that shouldn't be moved (dictionary with {id: True} pairs, id is vertex id)
- * optional, if undefined all vertices are considered movable
- * @returns list of WebCola nodes, list of WebCola links, and a dictionary from Draw.io cell ID to WebCola node ID
- * returned as a dictionary: {nodes: ..., links: ..., cellToNode: ...}
- */
- {
- var activeMaps = this.findActiveVertices(graph); // list of all active vertices, i.e. with no collapsed parents
- var activeVertices = activeMaps.activeVertices; // inactive vertex to its nearest active parent map
- var inactiveToActiveMap = activeMaps.inactiveToActiveMap;
- var model = graph.getModel();
- var cells = model.cells;
- var view = graph.getView();
- var cellSpacing = 20;
-
- // Ignores cells that have no states
- var tmp = {};
-
- for (var id in cells)
- {
- if (view.getState(cells[id]) != null)
- {
- tmp[id] = cells[id];
- }
- }
-
- cells = tmp;
-
- var nodeCells = {};
- var linkCells = {};
- var cellIds = {};
- var edgeIds = {};
- var colaId = 0;
- var nodes = [];
- var links = [];
- // process nodes first
- for (var id in cells)
- {
- var cell = cells[id];
- var state = view.getState(cell);
- var bounds = view.getBoundingBox(state, true);
- var bounds = model.getGeometry(cell);
- var isFirst = true;
- // if (cell.isVertex() && this.isLeafOrCollapsed(cell)) {
- // only active vertices should be considered (i.e. not hidden by a collapsed or layouted vertex)
- // if (cell.isVertex() && activeVertices[cell.id])
- if (cell.isVertex() && this.isLeafOrCollapsed(cell) && activeVertices[cell.id])
- {
- var node = {};
- // node.x = bounds.getCenterX();
- // node.y = bounds.getCenterY();
- node.width = bounds.width + cellSpacing;
- node.height = bounds.height + cellSpacing;
- node.index = colaId;
- node.name = cell.value;
- node.fixed = false;
- if (typeof movableVertices !== 'undefined' && !(id in movableVertices))
- {
- node.fixed = true;
- }
- nodes.push(node);
- cellIds[id] = colaId;
- nodeCells[colaId] = cell;
- colaId++;
- }
- }
- // now edges can be processed as well
- for (var id in cells)
- {
- var cell = cells[id];
- var state = view.getState(cell);
- if (cell.isEdge() && cell.getTerminal(true) != null && cell.getTerminal(false) != null)
- {
- // attach edges to lowest active vertex corresponding to each of their terminals
- var terminal_id1 = inactiveToActiveMap[cell.source.id];
- var terminal_id2 = inactiveToActiveMap[cell.target.id];
- if (terminal_id1 == terminal_id2)
- {
- // both terminals are under the same active parent, no need to make an invisible edge
- continue;
- }
- // if either of terminals are groups, we need to insert complete graph between nodes within these groups
- var terminal1 = cells[terminal_id1];
- var terminal2 = cells[terminal_id2];
- var addedLinks = [];
- if (this.isGroup(terminal1) || this.isGroup(terminal2))
- {
- addedLinks = this.addGroupConstraintLinks(terminal1, terminal2, activeVertices, inactiveToActiveMap, cellIds);
- }
- else
- {
- // link = {}
- // link.source = cellIds[cell.source.id];
- // link.target = cellIds[cell.target.id];
- var link = this.createLink(terminal_id1, terminal_id2, cellIds);
- addedLinks.push(link);
- }
- for (var i = 0; i < addedLinks.length; i++)
- {
- var link = addedLinks[i];
- links.push(link);
- edgeIds[cell] = id;
- linkCells[link] = cell;
- }
- }
- }
- links = this.getUniqueLinks(links);
- // finally, groups need to be extracted
- // mxGraph.getCellsForGroup
- // mxGraphModel.getChildCount
- // mxGraph.getBoundsForGroup
- // first, get all possible parents and their children
- var groupParents = {};
- var directParentChildren = {};
- for (var id in cells)
- {
- var cell = cells[id];
- if (!cell.isVertex() || !this.isLeafOrCollapsed(cell))
- continue;
- var parent = cell.getParent();
- if (parent.isVertex())
- {
- groupParents[parent.id] = parent;
- if (!(parent.id in directParentChildren))
- {
- directParentChildren[parent.id] = {}
- }
- directParentChildren[parent.id][id] = cell;
- }
- }
- // now go through all parents/children and build a group hierarchy for WebCola
- var preliminaryGroups = [];
- var groupId = 0;
- var groupToParent = {}
- for (var parentId in groupParents)
- {
- var parentChildren = directParentChildren[parentId];
- var groupNodes = []
- for (var childId in parentChildren)
- {
- if (activeVertices[childId])
- {
- groupNodes.push(cellIds[childId]);
- }
- }
- preliminaryGroups.push({id: groupId, parentId: parentId, nodes: parentChildren, leaves: groupNodes, groups: []});
- groupToParent[groupId] = parentId;
- groupId++;
- }
- // here scan newly formed groups if their parent is a child of any of the nodes in any of the groups
- for (var i = 0; i < preliminaryGroups.length; i++)
- {
- var parentGroup = preliminaryGroups[i];
- var parentId = parentGroup.parentId;
- for (var j = 0; j < preliminaryGroups.length; j++)
- {
- if (i == j)
- continue;
- var groupParentId = cells[preliminaryGroups[j].parentId].getParent().id;
- if (parentId == groupParentId)
- parentGroup.groups.push(j);
- }
- }
- // finalize groups
- var groups = [];
- for (var i = 0; i < preliminaryGroups.length; i++)
- {
- var group = preliminaryGroups[i];
- var graphGroup = {};
- if (group.leaves.length > 0)
- {
- graphGroup["leaves"] = group.leaves;
- }
- if (group.groups.length > 0)
- {
- graphGroup["groups"] = group.groups;
- }
- if (graphGroup.hasOwnProperty("leaves") || graphGroup.hasOwnProperty("groups"))
- {
- groups.push(graphGroup);
- }
- }
- return {nodes: nodes, links: links, groups: groups, cellToNode: cellIds};
- };
- mxWebColaAdaptor.prototype.createLink = function(sourceId, targetId, cellIds)
- /**
- * Creates a default version of a WebCola link/edge
- * @param sourceId ID of the edge's source vertex cell
- * @param targetId ID of the edge's target vertex cell
- * @param cellIds cell ID to WebCola's node ID mapping
- * @returns a WebCola link corresponding to the edge [sourceId, targetId]
- * in WebCola node IDs
- */
- {
- var link = {};
- link.source = cellIds[sourceId];
- link.target = cellIds[targetId];
- link.weight = 0.5;
- link.length = 200; //Graph.prototype.defaultEdgeLength;
- return link;
- }
- mxWebColaAdaptor.prototype.computeVertexDegrees = function()
- /**
- * Computes group vertex and vertex degrees. Useful to stop layouting for groups
- * with no internal, incoming or outgoing edges.
- */
- {
- var model = this.graph.getModel();
- var cells = model.cells;
-
- // compute individual vertex degrees
- for (var id in cells)
- {
- var cell = cells[id];
- if (cell.isEdge() && cell.source != null && cell.target != null)
- {
- // scan all edges, ignore other types
- var sourceId = cell.source.id;
- var targetId = cell.target.id;
- var source = cells[sourceId];
- var target = cells[targetId];
-
- if (sourceId == targetId)
- {
- // self-loops are irrelevant
- continue;
- }
-
- var sourceDegree = this.vertexDegrees.get(source);
- if (typeof sourceDegree == "undefined")
- {
- sourceDegree = 0;
- }
- sourceDegree++;
- this.vertexDegrees.put(source, sourceDegree);
- var targetDegree = this.vertexDegrees.get(target);
- if (typeof targetDegree == "undefined")
- {
- targetDegree = 0;
- }
- targetDegree++;
- this.vertexDegrees.put(target, targetDegree);
- }
- }
- // compute sub-group degree, i.e. sum of all degrees of children
- // algorithm goes through all vertices, then for each vertex it goes on its material
- // path to root and adds its contribution to all vertices on this path
- // this should for each vertex place exactly the sum of degrees of all its vertices
- // and itself, nothing less, nothing more
- for (var id in cells)
- {
- var cell = cells[id];
- if (cell.isVertex())
- {
- // scan all vertices, ignore other types
- var vertexDegree = this.vertexDegrees.get(cell);
- if (typeof vertexDegree == "undefined")
- {
- vertexDegree = 0;
- }
- var parent = cell;
- while (parent != null && typeof parent != "undefined")
- {
- var groupDegree = this.groupDegrees.get(parent);
- if (typeof groupDegree == "undefined")
- {
- groupDegree = 0;
- }
- groupDegree += vertexDegree;
- this.groupDegrees.put(parent, groupDegree);
- parent = parent.parent;
- }
- }
- }
- }
- mxWebColaAdaptor.prototype.isZeroConnected = function(groupCell)
- /**
- * Indicates if all group cell's vertices have no incidental edges
- * @params groupCell group cell
- * @returns true if the group cell doesn't contain any vertices with edges
- */
- {
- var groupDegree = this.groupDegrees.get(groupCell);
- console.log("Group " + groupCell.id + " degree: " + groupDegree);
- if (typeof groupDegree != "undefined" && groupDegree > 0)
- {
- return false;
- }
- return true;
- }
- mxWebColaAdaptor.prototype.isInZeroConnectedGroup = function(cell)
- {
- var parent = cell.parent;
- if (parent == null || typeof parent == "undefined")
- {
- return this.isZeroConnected(cell);
- }
- else
- {
- return this.isZeroConnected(parent);
- }
- }
- mxWebColaAdaptor.prototype.isLeafOrCollapsed = function(cell)
- /**
- * Returns true if a cell is either a leaf or a collapsed group
- * @param cell cell to investigate
- * @returns true if a cell is either a leaf or a collapsed group, false otherwise
- */
- {
- if (cell.isCollapsed() ||
- cell.children == null || cell.children.length == 0 ||
- typeof this.graph.getCellStyle(cell)['childLayout'] != 'undefined')
- {
- return true;
- }
- if (this.isZeroConnected(cell))
- {
- return true;
- }
- /*
- if (!cell.isCollapsed() && cell.children != null && cell.children.length > 0 && this.graph.getEdges(cell, null, true, true, true, true).length == 0)
- {
- console.log("cell " + cell.id + " is 0-connected.");
- return true;
- }
- */
- return false;
- }
- mxWebColaAdaptor.prototype.findActiveVertices = function(graph)
- /**
- * Scans all groups and finds active vertices, as well as an inactive-vertex-to-active-parent map
- * @param graph input graph
- */
- {
- var inactiveToActiveMap = {};
- var activeVertices = {};
- var root = graph.getModel().root;
- var cellsToExplore = [{vertex: root, isActive: true, activeParent: root}]
- while (cellsToExplore.length > 0)
- {
- var currentCellInfo = cellsToExplore.shift();
- var cell = currentCellInfo.vertex;
- if (cell.isEdge())
- {
- // cut at edge group, those are ignored
- continue;
- }
- var isActive = currentCellInfo.isActive;
- var activeParent = currentCellInfo.activeParent;
- if (cell.isVertex())
- {
- if (isActive)
- {
- activeVertices[cell.id] = true;
- }
- else
- {
- activeVertices[cell.id] = false;
- }
- }
- // prepare children
- // child can be active only if any of its parents is not collapsed
- var isActive = isActive && !this.isLeafOrCollapsed(cell);
- var children = cell.children;
- if (children != null && children.length > 0)
- {
- for (var i = 0; i < children.length; i++)
- {
- var child = children[i];
- var childActiveParent = isActive? child: activeParent;
- cellsToExplore.push({vertex: child, isActive: isActive, activeParent: childActiveParent});
- if (child.isVertex())
- {
- inactiveToActiveMap[child.id] = childActiveParent.id;
- }
- }
- }
- }
- return {activeVertices: activeVertices, inactiveToActiveMap: inactiveToActiveMap};
- }
- mxWebColaAdaptor.prototype.getActiveVerticesInGroup = function(groupCell, activeVertices, includeCollapsedGroups)
- /**
- * Scans all children in group and returns all active vertices inside group
- * This method is for creating redundant edges between members of groups to simulate group edges in WebCola
- * See https://github.com/tgdwyer/WebCola/issues/38
- * @param groupCell group cell
- */
- {
- var activeChildren = [];
- if (includeCollapsedGroups && this.isLeafOrCollapsed(groupCell))
- {
- activeChildren.push(groupCell);
- }
- var cellsToExplore = [groupCell];
- while (cellsToExplore.length > 0)
- {
- var cell = cellsToExplore.shift();
- if (!cell.isVertex() || !activeVertices[cell])
- {
- // cut at edge group, those are ignored
- continue;
- }
- if (this.isLeafOrCollapsed(cell))
- {
- activeChildren.push(cell);
- }
- else
- {
- var children = cell.children;
- if (children == null || children.length == 0)
- {
- continue;
- }
- cellsToExplore = cellsToExplore.concat(children);
- }
- }
- return activeChildren;
- }
- mxWebColaAdaptor.prototype.getAllVerticesInGroup = function(groupCell, includeCollapsedGroups)
- /**
- * Scans all children in group and returns all active vertices inside group
- * This method is for creating redundant edges between members of groups to simulate group edges in WebCola
- * See https://github.com/tgdwyer/WebCola/issues/38
- * @param groupCell group cell
- */
- {
- var result = [];
- if (includeCollapsedGroups && this.isLeafOrCollapsed(groupCell))
- {
- result.push(groupCell);
- }
- var cellsToExplore = [groupCell];
- while (cellsToExplore.length > 0)
- {
- var cell = cellsToExplore.shift();
- if (!cell.isVertex())
- {
- // cut at edge group, those are ignored
- continue;
- }
- if (this.isLeafOrCollapsed(cell))
- {
- result.push(cell);
- }
- else
- {
- var children = cell.children;
- if (children == null || children.length == 0)
- {
- continue;
- }
- cellsToExplore = cellsToExplore.concat(children);
- }
- }
- return result;
- }
- mxWebColaAdaptor.prototype.hasVertexChildren = function(cell)
- /**
- * Returns true if a (group) cell has vertex children in its subtree
- * @param cell (group) cell
- * @returns true if if a (group) cell has vertex children in its subtree, false otherwise
- */
- {
- if (cell.children == null || cell.children.length == 0)
- {
- return false;
- }
- var toBeExamined = []
- toBeExamined = toBeExamined.concat(cell.children);
- while (toBeExamined.length > 0)
- {
- var cell = toBeExamined.shift();
- if (cell.isVertex())
- return true;
- if (cell.children != null && cell.children.length > 0)
- {
- toBeExamined = toBeExamined.concat(cell.children);
- }
- }
- return false;
- }
- mxWebColaAdaptor.prototype.isInCollapsedTree = function(cell)
- {
- // scan the material path for collapsed group node
- while (cell != null)
- {
- cell = cell.getParent();
- if (cell != null && cell.isCollapsed())
- {
- return true;
- }
- }
- return false;
- }
- mxWebColaAdaptor.prototype.isGroup = function(cell)
- /**
- * Returns true if cell is a group (has children)
- * @param cell cell
- * @returns true if cell is a group (has children); false otherwise
- */
- {
- return cell.children != null && cell.children.length > 0;
- }
- mxWebColaAdaptor.prototype.addGroupConstraintLinks = function(groupA, groupB, activeVertices, inactiveToActiveMap, cellIds)
- /**
- * Adds edges between vertex and group or two groups. Each vertex child of a group must be connected to the vertex/
- * group, as this way WebCola simulates edges on the group level (as groups don't exist as vertices in WebCola)
- * @param rootCell root cell
- */
- {
- var result = []
- // var childrenA = this.getActiveVerticesInGroup(groupA, activeVertices, false);
- // var childrenB = this.getActiveVerticesInGroup(groupB, activeVertices, false);
- var childrenA = [groupA];
- var childrenB = [groupB];
- if (!groupA.isCollapsed())
- {
- childrenA = this.getAllVerticesInGroup(groupA, activeVertices, false);
- }
- if (!groupB.isCollapsed())
- {
- childrenB = this.getAllVerticesInGroup(groupB, activeVertices, false);
- }
- if (childrenA == null || childrenA.length == 0 || childrenB == null || childrenB.length == 0)
- return result;
- for (var i = 0; i < childrenA.length; i++)
- {
- var childA_Id = inactiveToActiveMap[childrenA[i].id];
- for (var j = 0; j < childrenB.length; j++)
- {
- var childB_Id = inactiveToActiveMap[childrenB[j].id];
- var link = this.createLink(childA_Id, childB_Id, cellIds);
- result.push(link);
- }
- }
- return result;
- }
- mxWebColaAdaptor.prototype.getUniqueLinks = function(links)
- /**
- * Returns an array of unique links from an array of links
- * @param links array of links containing duplicate links
- * @returns array of unique links
- */
- {
- var result = [];
- // TODO: this part is inefficient - O(n^2); Theta(n) should be possible with hashmap
- for (var i = 0; i < links.length; i++)
- {
- var link = links[i];
- var shouldBeAdded = true;
- for (var j = 0; j < result.length; j++)
- {
- var existingLink = result[j];
- if (link.source == existingLink.source && link.target == existingLink.target)
- {
- shouldBeAdded = false;
- break;
- }
- }
- if (shouldBeAdded)
- {
- result.push(link);
- }
- }
- return result;
- }
- var doRendering = void 0;
- if ((typeof window === "undefined" ? "undefined" : typeof(window)) !== ( true ? "undefined" : typeof(undefined)))
- {
- doRendering = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame;
- }
- else
- {
- // if not available, all you get is immediate calls
- function doRendering(callback)
- {
- callback();
- };
- }
|