@@ -214,7 +214,7 @@ fragment FamilyProxyData on FamilyProxy {
214
214
isQueued
215
215
name
216
216
ancestors {
217
- name
217
+ name
218
218
}
219
219
childTasks {
220
220
id
@@ -337,6 +337,19 @@ export const childArray = (nodeArray) => {
337
337
})
338
338
}
339
339
340
+ /**
341
+ * Convert a mapping to a format that VTreeView can handle.
342
+ * @param {Map<string, { children: Map, diabled: Boolean }>} map - The mapping to convert
343
+ * @returns {Object[]} - The converted mapping
344
+ */
345
+ export function convertTree (map ) {
346
+ return Array .from (map, ([name , { children, disabled }]) => ({
347
+ name,
348
+ children: convertTree (children),
349
+ disabled,
350
+ }))
351
+ }
352
+
340
353
export default {
341
354
name: ' Graph' ,
342
355
@@ -484,7 +497,7 @@ export default {
484
497
* @returns {Family[]} array containing nested structure of families
485
498
*/
486
499
treeDropDownFamily () {
487
- return this .familyArrayStore . length ? this .getTree () : [{ name: ' No families' , disabled: true }]
500
+ return this .allParentLookUp . size ? this .getTree () : [{ name: ' No families' , disabled: true }]
488
501
},
489
502
/**
490
503
* Gets the array of cycles for use in vuetify toolbar drop down
@@ -498,11 +511,10 @@ export default {
498
511
*
499
512
* example return mapping:
500
513
* {
501
- * root: []
502
- * GREAT_GRANDPARENT_FAMILY : ['root'],
503
- * GRANDPARENT_FAMILY: [ 'GREAT_GRANDPARENT_FAMILY', 'root' ],
504
- * PARENT_FAMILY: [ 'GRANDPARENT_FAMILY', 'GREAT_GRANDPARENT_FAMILY', 'root' ],
505
- * FAMILY: ['PARENT_FAMILY', 'GRANDPARENT_FAMILY', 'GREAT_GRANDPARENT_FAMILY', 'root' ],
514
+ * GREAT_GRANDPARENT_FAMILY: [ ],
515
+ * GRANDPARENT_FAMILY: [ 'GREAT_GRANDPARENT_FAMILY' ],
516
+ * PARENT_FAMILY: [ 'GREAT_GRANDPARENT_FAMILY', 'GRANDPARENT_FAMILY' ],
517
+ * FAMILY: [ 'GREAT_GRANDPARENT_FAMILY', 'GRANDPARENT_FAMILY', 'PARENT_FAMILY' ],
506
518
* }
507
519
*
508
520
* note: object value arrays contain family names as strings only - not nodes
@@ -512,11 +524,12 @@ export default {
512
524
allParentLookUp () {
513
525
const lookup = []
514
526
for (const namespace of this .namespaces ) {
527
+ if (namespace .name === ' root' ) continue
515
528
const ancestors = []
516
529
let parent = namespace .node .firstParent
517
- while (parent) {
530
+ while (parent . name !== ' root ' ) {
518
531
const parentNode = this .cylcTree .$index [parent .id ]
519
- ancestors .push (parentNode .name )
532
+ ancestors .unshift (parentNode .name )
520
533
parent = parentNode .node .firstParent
521
534
}
522
535
lookup .push ([namespace .name , ancestors])
@@ -583,7 +596,7 @@ export default {
583
596
lookup[task .id ] = childArray ([task])
584
597
}
585
598
// for families
586
- for (const family of this .familyArrayStore ) {
599
+ for (const family of this .allParentLookUp . keys () ) {
587
600
const familyId = ` ${ cycle .id } /${ family} `
588
601
if (this .cylcTree .$index [familyId]) {
589
602
lookup[familyId] = childArray ([this .cylcTree .$index [familyId]])
@@ -593,13 +606,6 @@ export default {
593
606
}
594
607
return lookup
595
608
},
596
- /**
597
- * Get an array of family names
598
- * @returns {String[]} array of family names
599
- */
600
- familyArrayStore () {
601
- return this .namespaces .flatMap ((family ) => family .name !== ' root' ? family .name : [])
602
- },
603
609
controlGroups () {
604
610
return [
605
611
{
@@ -688,23 +694,20 @@ export default {
688
694
* @returns {Family[]} array containing nested structure of families
689
695
*/
690
696
getTree () {
691
- const tree = []
697
+ const tree = new Map ()
692
698
for (const [name , ancestors ] of this .allParentLookUp ) {
693
- if (name === ' root' ) continue
694
699
let pointer = tree
695
- let ancestorName, disabled
696
- for (let i = ancestors .length - 2 ; i >= 0 ; i-- ) {
697
- ancestorName = ancestors[i]
698
- pointer = pointer .find ((item ) => item .name === ancestorName).children
699
- disabled || = this .collapseFamily .includes (ancestorName)
700
+ let disabled = false
701
+ for (const ancestor of ancestors) {
702
+ pointer = pointer .get (ancestor).children
703
+ disabled || = this .collapseFamily .includes (ancestor)
700
704
}
701
- pointer .push ({
702
- name,
703
- children: [],
705
+ pointer .set (name, {
706
+ children: new Map (),
704
707
disabled,
705
708
})
706
709
}
707
- return tree
710
+ return convertTree ( tree)
708
711
},
709
712
710
713
/**
@@ -772,34 +775,18 @@ export default {
772
775
},
773
776
774
777
/**
775
- * Check if node is collapsed by family or ancestor
776
- * If not collapsed return null
777
- * If the node is collapsed by first parent return the parent name
778
- * If the node is collapsed by an ancestor further up the tree return the name of the ancestor
779
- * @property {String} nodeFirstParent - name of the first parent
780
- * @returns {String | null} name of the collapsed parent/ancestor or null
778
+ * Check if node is collapsed by an ancestor family
779
+ *
780
+ * @param {string} id - ID of the task/family/namespace
781
+ * @returns {string=} name of the collapsed ancestor if any
781
782
*/
782
- isNodeCollapsedByFamily (nodeFirstParent ) {
783
- // the nodes first parent is collapsed
784
- const firstParent = this .collapseFamily .includes (nodeFirstParent)
785
- // a family member up the tree is collapsed
786
- const ancestor = this .allParentLookUp .get (nodeFirstParent).some (element => {
787
- return this .collapseFamily .includes (element)
788
- })
789
- if (firstParent && ! ancestor) {
790
- // the node is collapsed by its first parent
791
- return nodeFirstParent
792
- } else if (ancestor) {
793
- // the node is collapsed by an ancestor
794
- const lookupParents = this .allParentLookUp .get (nodeFirstParent)
795
- for (let i = lookupParents .length - 1 ; i >= 0 ; i-- ) {
796
- if (this .collapseFamily .includes (lookupParents[i])) {
797
- return lookupParents[i]
798
- }
799
- }
800
- } else {
801
- // the node is not collapsed
802
- return null
783
+ getCollapsedAncestor (id ) {
784
+ const { name } = this .cylcTree .$index [id].node .firstParent
785
+ if (name !== ' root' ) {
786
+ // TODO: could use Set.intersection if browser support is good enough?
787
+ return this .collapseFamily .find ((family ) => (
788
+ name === family || this .allParentLookUp .get (name).includes (family)
789
+ ))
803
790
}
804
791
},
805
792
@@ -985,19 +972,14 @@ export default {
985
972
) {
986
973
// Take our array of grandchildren and remove nodes that we dont want to include
987
974
// nodeFormattedArray will be an array of string node ids to be included in the grouping
988
- const nodeFormattedArray = grandChildren .filter ((grandChild ) => {
989
- let isAncestor = true
990
- const nodeFirstParent = this .cylcTree .$index [grandChild .id ].node .firstParent .name
991
- isAncestor = ! this .isNodeCollapsedByFamily (nodeFirstParent)
992
- return (
993
- // if its not in the list of families (unless its been collapsed)
994
- (! this .familyArrayStore .includes (grandChild .name ) || this .collapseFamily .includes (grandChild .name )) &&
995
- // the node has been removed/collapsed
996
- ! removedNodes .has (grandChild .name ) &&
997
- // the node doesnt have a collapsed ancestor
998
- isAncestor
999
- )
1000
- }).map (a => ` "${ a .id } "` )
975
+ const nodeFormattedArray = grandChildren .filter ((grandChild ) => (
976
+ // if its not in the list of families (unless its been collapsed)
977
+ (! this .allParentLookUp .has (grandChild .name ) || this .collapseFamily .includes (grandChild .name )) &&
978
+ // the node has been removed/collapsed
979
+ ! removedNodes .has (grandChild .name ) &&
980
+ // the node doesnt have a collapsed ancestor
981
+ ! this .getCollapsedAncestor (grandChild .id )
982
+ )).map (a => ` "${ a .id } "` )
1001
983
// if there are any nodes left after the filtering step
1002
984
// make a dotcode subgraph string
1003
985
if (nodeFormattedArray .length ) {
@@ -1090,26 +1072,18 @@ export default {
1090
1072
}
1091
1073
}
1092
1074
}
1093
- const nodeFormattedArray = indexSearch .filter ((a ) => {
1094
- const isRoot = a .name !== ' root'
1095
- let isAncestor = true
1096
- if (isRoot) {
1097
- const nodeFirstParent = this .cylcTree .$index [a .id ].node .firstParent .name
1098
- isAncestor = ! this .isNodeCollapsedByFamily (nodeFirstParent)
1099
- }
1100
- return (
1101
- // if its not in the list of families (unless its been collapsed)
1102
- (! this .familyArrayStore .includes (a .name ) || this .collapseFamily .includes (a .name )) &&
1103
- // the node has been removed/collapsed
1104
- (! removedNodes .has (a .name ) || this .collapseFamily .includes (a .name )) &&
1105
- // its not a node representing this cycle
1106
- a .name !== cycle &&
1107
- // its not a root node
1108
- isRoot &&
1109
- // the node doesnt have a collapsed ancestor
1110
- isAncestor
1111
- )
1112
- }).map (a => ` "${ a .id } "` )
1075
+ const nodeFormattedArray = indexSearch .filter ((a ) => (
1076
+ // if its not in the list of families (unless its been collapsed)
1077
+ (! this .allParentLookUp .has (a .name ) || this .collapseFamily .includes (a .name )) &&
1078
+ // the node has been removed/collapsed
1079
+ (! removedNodes .has (a .name ) || this .collapseFamily .includes (a .name )) &&
1080
+ // its not a node representing this cycle
1081
+ a .name !== cycle &&
1082
+ // its not the root node
1083
+ a .name !== ' root' &&
1084
+ // the node doesnt have a collapsed ancestor
1085
+ ! this .getCollapsedAncestor (a .id )
1086
+ )).map (a => ` "${ a .id } "` )
1113
1087
ret .push (`
1114
1088
subgraph cluster_margin_family_${ cycle}
1115
1089
{
@@ -1192,24 +1166,24 @@ export default {
1192
1166
this .panZoomWidget .zoom (relativeZoom * width / desiredWidth)
1193
1167
},
1194
1168
1195
- edgeHasCollapsedTargetFamilyOnly ( targetFirstFamily , sourceFirstFamily ) {
1196
- const target = this .isNodeCollapsedByFamily (targetFirstFamily )
1197
- if (target && ! this .isNodeCollapsedByFamily (sourceFirstFamily )) {
1198
- return { target }
1169
+ edgeHasCollapsedTargetAncestorOnly ( edge ) {
1170
+ const target = this .getCollapsedAncestor ( edge . target . id )
1171
+ if (target && ! this .getCollapsedAncestor ( edge . source . id )) {
1172
+ return target
1199
1173
}
1200
1174
},
1201
1175
1202
- edgeHasCollapsedSourceFamilyOnly ( targetFirstFamily , sourceFirstFamily ) {
1203
- const source = this .isNodeCollapsedByFamily (sourceFirstFamily )
1204
- if (source && ! this .isNodeCollapsedByFamily (targetFirstFamily )) {
1205
- return { source }
1176
+ edgeHasCollapsedSourceAncestorOnly ( edge ) {
1177
+ const source = this .getCollapsedAncestor ( edge . source . id )
1178
+ if (source && ! this .getCollapsedAncestor ( edge . target . id )) {
1179
+ return source
1206
1180
}
1207
1181
},
1208
1182
1209
- edgeHasCollapsedTargetandSourceFamily ( targetFirstFamily , sourceFirstFamily ) {
1210
- const target = this .isNodeCollapsedByFamily (targetFirstFamily )
1183
+ edgeHasCollapsedTargetandSourceAncestor ( edge ) {
1184
+ const target = this .getCollapsedAncestor ( edge . target . id )
1211
1185
if (target) {
1212
- const source = this .isNodeCollapsedByFamily (sourceFirstFamily )
1186
+ const source = this .getCollapsedAncestor ( edge . source . id )
1213
1187
if (source) {
1214
1188
return { target, source }
1215
1189
}
@@ -1262,53 +1236,45 @@ export default {
1262
1236
}
1263
1237
// ...now we have removed any parts of child nodes that shouldnt be there we can add nodes and edges that should be...
1264
1238
// ---------------ADD NODES BASED ON FAMILY------------
1265
- if (! this .collapseCycle .includes (cycle)) { // cycle collapsing takes priority over family collapsing
1266
- if (! this .isNodeCollapsedByFamily (indexSearch .node .firstParent .name )) {
1267
- nodes .push (indexSearch)
1268
- }
1239
+ if (! this .collapseCycle .includes (cycle) && ! this .getCollapsedAncestor (indexSearch .id )) {
1240
+ nodes .push (indexSearch)
1269
1241
}
1270
1242
1271
1243
// ...this node is collapsed so need to remove any of its children (nodes and edges) from the graph if it has any...
1272
1244
for (const { id: childID } of this .allChildrenLookUp [indexSearch .id ]) {
1273
1245
for (const edge of removedEdges .filter (({ source }) => source .id === childID)) {
1274
- const sourceFamilyName = this .cylcTree .$index [edge .source .id ].node .firstParent .name
1275
- const targetFamilyName = this .cylcTree .$index [edge .target .id ].node .firstParent .name
1276
-
1277
1246
if (! this .collapseCycle .includes (edge .source .cycle )) {
1278
- const familyData = this .edgeHasCollapsedSourceFamilyOnly (targetFamilyName, sourceFamilyName )
1279
- if (familyData ) {
1247
+ const collapsedSourceAncestor = this .edgeHasCollapsedSourceAncestorOnly (edge )
1248
+ if (collapsedSourceAncestor ) {
1280
1249
edges .set (
1281
- ... this .createEdge (' noCollapsed' , familyData . source , edge .target .task , edge .source .cycle , edge .target .cycle )
1250
+ ... this .createEdge (' noCollapsed' , collapsedSourceAncestor , edge .target .task , edge .source .cycle , edge .target .cycle )
1282
1251
)
1283
1252
}
1284
1253
1285
1254
if (! this .collapseCycle .includes (edge .target .cycle )) {
1286
- const familyData = this .edgeHasCollapsedTargetandSourceFamily (targetFamilyName, sourceFamilyName )
1287
- if (familyData && (familyData .source !== familyData .target || edge .source .cycle !== edge .target .cycle )) {
1255
+ const collapsedAncestors = this .edgeHasCollapsedTargetandSourceAncestor (edge )
1256
+ if (collapsedAncestors && (collapsedAncestors .source !== collapsedAncestors .target || edge .source .cycle !== edge .target .cycle )) {
1288
1257
edges .set (
1289
- ... this .createEdge (' noCollapsed' , familyData .source , familyData .target , edge .source .cycle , edge .target .cycle )
1258
+ ... this .createEdge (' noCollapsed' , collapsedAncestors .source , collapsedAncestors .target , edge .source .cycle , edge .target .cycle )
1290
1259
)
1291
1260
}
1292
1261
}
1293
1262
}
1294
1263
}
1295
1264
for (const edge of removedEdges .filter (({ target }) => target .id === childID)) {
1296
- const sourceFamilyName = this .cylcTree .$index [edge .source .id ].node .firstParent .name
1297
- const targetFamilyName = this .cylcTree .$index [edge .target .id ].node .firstParent .name
1298
-
1299
1265
if (! this .collapseCycle .includes (edge .target .cycle )) {
1300
- const familyData = this .edgeHasCollapsedTargetFamilyOnly (targetFamilyName, sourceFamilyName )
1301
- if (familyData ) {
1266
+ const collapsedTargetAncestor = this .edgeHasCollapsedTargetAncestorOnly (edge )
1267
+ if (collapsedTargetAncestor ) {
1302
1268
edges .set (
1303
- ... this .createEdge (' noCollapsed' , edge .source .task , familyData . target , edge .source .cycle , edge .target .cycle )
1269
+ ... this .createEdge (' noCollapsed' , edge .source .task , collapsedTargetAncestor , edge .source .cycle , edge .target .cycle )
1304
1270
)
1305
1271
}
1306
1272
1307
1273
if (! this .collapseCycle .includes (edge .source .cycle )) {
1308
- const familyData = this .edgeHasCollapsedTargetandSourceFamily (targetFamilyName, sourceFamilyName )
1309
- if (familyData && (familyData .source !== familyData .target || edge .source .cycle !== edge .target .cycle )) {
1274
+ const collapsedAncestors = this .edgeHasCollapsedTargetandSourceAncestor (edge )
1275
+ if (collapsedAncestors && (collapsedAncestors .source !== collapsedAncestors .target || edge .source .cycle !== edge .target .cycle )) {
1310
1276
edges .set (
1311
- ... this .createEdge (' noCollapsed' , familyData .source , familyData .target , edge .source .cycle , edge .target .cycle )
1277
+ ... this .createEdge (' noCollapsed' , collapsedAncestors .source , collapsedAncestors .target , edge .source .cycle , edge .target .cycle )
1312
1278
)
1313
1279
}
1314
1280
}
@@ -1340,13 +1306,13 @@ export default {
1340
1306
// ---------------ADD EDGES BASED ON CYCLE POINT------------
1341
1307
for (const { id: childID } of this .allChildrenLookUp [indexSearch .id ]) {
1342
1308
for (const edge of removedEdges .filter (({ source }) => source .id === childID)) {
1343
- const targetFamilyName = this .cylcTree .$index [edge .target .id ].node .firstParent .name
1344
1309
if (edge .source .cycle === edge .target .cycle ) continue
1345
1310
// edge has collapsed source cycle only
1346
1311
if (! this .collapseCycle .includes (edge .target .cycle ) && this .collapseCycle .includes (edge .source .cycle )) {
1347
- if (this .isNodeCollapsedByFamily (targetFamilyName)) {
1312
+ const collapsedTargetAncestor = this .getCollapsedAncestor (edge .target .id )
1313
+ if (collapsedTargetAncestor) {
1348
1314
edges .set (
1349
- ... this .createEdge (' collapsedSource' , edge .source .cycle , this . isNodeCollapsedByFamily (targetFamilyName) , edge .source .cycle , edge .target .cycle )
1315
+ ... this .createEdge (' collapsedSource' , edge .source .cycle , collapsedTargetAncestor , edge .source .cycle , edge .target .cycle )
1350
1316
)
1351
1317
} else {
1352
1318
edges .set (
@@ -1363,14 +1329,13 @@ export default {
1363
1329
}
1364
1330
}
1365
1331
for (const edge of removedEdges .filter (({ target }) => target .id === childID)) {
1366
- const sourceFamilyName = this .cylcTree .$index [edge .source .id ].node .firstParent .name
1367
-
1368
1332
if (edge .source .cycle === edge .target .cycle ) continue
1369
1333
// edge has collapsed target cycle only
1370
1334
if (this .collapseCycle .includes (edge .target .cycle ) && ! this .collapseCycle .includes (edge .source .cycle )) {
1371
- if (this .isNodeCollapsedByFamily (sourceFamilyName)) {
1335
+ const collapsedSourceAncestor = this .getCollapsedAncestor (edge .source .id )
1336
+ if (collapsedSourceAncestor) {
1372
1337
edges .set (
1373
- ... this .createEdge (' collapsedTarget' , this . isNodeCollapsedByFamily (sourceFamilyName) , edge .target .cycle , edge .source .cycle , edge .target .cycle )
1338
+ ... this .createEdge (' collapsedTarget' , collapsedSourceAncestor , edge .target .cycle , edge .source .cycle , edge .target .cycle )
1374
1339
)
1375
1340
} else {
1376
1341
edges .set (
0 commit comments