Files
DiceCloud/app/imports/client/ui/creature/dependencyGraph/DependencyGraphDialog.vue
2023-05-29 13:18:27 +02:00

215 lines
5.9 KiB
Vue

<template lang="html">
<dialog-base class="dependency-graph">
<div slot="toolbar">
Dependency Graph
</div>
<div
id="dependency-graph-container"
slot="unwrapped-content"
ref="container"
class="graph-container"
/>
</dialog-base>
</template>
<script lang="js">
import buildCreatureComputation from '/imports/api/engine/computation/buildCreatureComputation.js';
import DialogBase from '/imports/client/ui/dialogStack/DialogBase.vue';
import computeCreatureComputation from '/imports/api/engine/computation/computeCreatureComputation.js';
import cytoscape from 'cytoscape';
import klay from 'cytoscape-klay';
import PROPERTIES from '/imports/constants/PROPERTIES.js';
cytoscape.use(klay);
function getNodeName(node) {
let model = node.data;
if (!model) return node.id;
if (model.name) return model.name;
if (model.type === '_calculation') {
return model._key;
}
let prop = PROPERTIES[model.type]
if (model.type === 'classLevel' && model.name && model.level) {
return model.name + ' ' + model.level
}
return prop?.name || prop?.type || node.id;
}
function getLoopNodes(computation) {
const loopNodes = [];
if (!computation.errors) return loopNodes;
computation.errors.forEach(err => {
if (err.type !== 'dependencyLoop') return;
err.details?.nodes?.forEach(nodeId => loopNodes.push(nodeId));
});
return loopNodes;
}
export default {
components: {
DialogBase,
},
props: {
creatureId: {
type: String,
required: true,
}
},
mounted() {
// Convert ngraph to cytoscape
// Convert Nodes
const nodes = [];
const loopNodes = getLoopNodes(this.computation);
this.computation.dependencyGraph.forEachNode(function (node) {
nodes.push({
data: {
id: node.id,
label: getNodeName(node) || node.id,
variable: !node.data?.type,
inLoop: loopNodes.includes(node.id),
propId: node.data?._id,
prop: node.data,
},
});
});
// Convert edges
const edges = [];
this.computation.dependencyGraph.forEachLink(function (link) {
edges.push({
data: {
target: link.fromId,
source: link.toId,
linkType: link.data,
inLoop: loopNodes.includes(link.fromId) && loopNodes.includes(link.toId),
},
})
//{ data: { source: 'n0', target: 'n1' } }
});
this.cytoscape = cytoscape({
container: this.$refs.container,
boxSelectionEnabled: false,
autounselectify: true,
layout: {
name: 'klay',
nodeDimensionsIncludeLabels: false,
klay: {
addUnnecessaryBendpoints: false,
aspectRatio: 1.6,
direction: 'RIGHT',
edgeRouting: 'ORTHOGONAL', //'ORTHOGONAL',
edgeSpacingFactor: 0.5,
feedbackEdges: true,
inLayerSpacingFactor: 1.0,
layoutHierarchy: true,
linearSegmentsDeflectionDampening: 0.3,
compactComponents: true,
mergeEdges: false,
mergeHierarchyCrossingEdges: false,
nodeLayering: 'NETWORK_SIMPLEX',
nodePlacement: 'LINEAR_SEGMENTS', //default 'BRANDES_KOEPF'
spacing: 20,//20,
thoroughness: 12,//7,
separateConnectedComponents: true,
},
},
style: [
{
selector: 'node',
style: {
'content': 'data(label)',
//'text-opacity': 0.8,
'text-valign': 'center',
'text-halign': 'center',
'background-color': '#303030',
'text-background-color': '#303030',
'color': '#fff',
//'text-outline-color': '#888',
//'text-outline-width': 3,
'text-background-opacity': 1,
'text-background-shape': 'roundrectangle',
'text-background-padding': 2,
'shape': 'round-rectangle',
'width': 80,
'height': 20,
'compound-sizing-wrt-labels': 'include',
'font-family': '"Roboto",sans-serif',
}
}, {
selector: ele => ele.data().variable,
style: {
'color': '#f44336',
'font-family': 'monospace',
}
}, {
selector: ele => !!ele.data().propId,
style: {
'background-color': '#B71C1C',
'text-background-color': '#B71C1C',
}
}, {
selector: 'edge',
style: {
'width': 4,
'target-arrow-shape': 'triangle-backcurve',
'color': '#fff',
'text-opacity': 0.8,
'line-color': '#555',
'target-arrow-color': '#555',
'curve-style': 'unbundled-bezier',//'unbundled-bezier',
'label': 'data(linkType)',
'text-rotation': 'autorotate',
'source-endpoint': 'outside-to-line-or-label',
'target-endpoint': 'outside-to-line-or-label',
}
}, {
selector: ele => ele.data().inLoop,
style: {
'color': '#fff',
'background-color': '#FF6D00',
'text-background-color': '#FF6D00',
'line-color': '#FF6D00',
'target-arrow-color': '#FF6D00',
}
},
],
elements: {
nodes,
edges,
},
});
this.cytoscape.on('tap', 'node', function(evt){
var node = evt.target;
console.log( node.data() );
});
this.cytoscape.on('tap', 'edge', function(evt){
var edge = evt.target;
console.log( edge.data() );
});
},
meteor: {
computation() {
const computation = buildCreatureComputation(this.creatureId);
computeCreatureComputation(computation);
return computation;
},
},
}
</script>
<style lang="css">
.graph-container {
width: 100%;
height: 100%;
background-color: #151515;
}
.graph-container svg {
width: 100%;
height: 100%;
}
</style>