diff --git a/samples/data/famdata.js b/samples/data/famdata.js index 08a971c..869437b 100644 --- a/samples/data/famdata.js +++ b/samples/data/famdata.js @@ -954,11 +954,137 @@ var labelsSelection = { ] }; +var treesSharingNodes = { + cursorItem: 2, + itemsOrderType: primitives.ItemsOrderType.MergedTrees, + annotations: [ + { + annotationType: primitives.AnnotationType.Connector, + fromItem: 1, + toItem: 2, + label: "1Connector annotation", + labelSize: { width: 80, height: 30 }, //new primitives.Size(80, 30) + connectorShapeType: primitives.ConnectorShapeType.OneWay, + color: primitives.Colors.Red, + offset: 5, + lineWidth: 2, + lineType: primitives.LineType.Dashed + } + ], + items: [ + { id: 1, title: "Global Headquarters", label: "HQ", description: "Primary coordination and governance entity.", image: "../images/photos/e.png", itemTitleColor: primitives.Colors.Navy }, + { id: 2, title: "Regional Operations Center", label: "Ops Center", description: "Manages regional execution and oversight.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + + { id: 3, parents: [1], title: "Northern Division", label: "North Div", description: "Supervises activities across the northern region.", image: "../images/photos/j.png", itemTitleColor: primitives.Colors.Navy }, + { id: 107, parents: [2], title: "Routing Node 107", label: "Node 107", templateName: "dot", description: "Logical connector for downstream entities.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + + { id: 4, parents: [107], title: "Operations Unit Alpha", label: "Ops Alpha", description: "Executes operational workflows for assigned scope.", image: "../images/photos/a.png", itemTitleColor: primitives.Colors.Red }, + { id: 5, parents: [107], title: "Operations Unit Beta", label: "Ops Beta", description: "Provides specialized operational support.", image: "../images/photos/f.png", itemTitleColor: primitives.Colors.Red }, + { id: 6, parents: [107], title: "Operations Unit Gamma", label: "Ops Gamma", description: "Handles advanced or specialized initiatives.", image: "../images/photos/s.png", itemTitleColor: primitives.Colors.Red }, + { id: 7, parents: [3], title: "Partner Coordination Group", label: "Partners", description: "Coordinates collaboration with external groups.", image: "../images/photos/i.png", itemTitleColor: primitives.Colors.Navy }, + { id: 8, parents: [107], title: "Domestic Services Unit", label: "Domestic", description: "Oversees nationally scoped activities.", image: "../images/photos/a.png", itemTitleColor: primitives.Colors.Red }, + { id: 9, parents: [107], title: "Logistics Division", label: "Logistics", description: "Manages transport, supply, and distribution.", image: "../images/photos/n.png", itemTitleColor: primitives.Colors.Red }, + { id: 10, parents: [107], title: "Infrastructure Division", label: "Infrastructure", description: "Maintains physical and digital infrastructure.", image: "../images/photos/m.png", itemTitleColor: primitives.Colors.Red }, + { id: 11, parents: [107], title: "Special Projects Office", label: "Projects", description: "Runs time-bound and cross-functional projects.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + + { id: 12, parents: [5], title: "Service Cluster A", label: "Service A", description: "Delivers coordinated service capabilities.", image: "../images/photos/e.png", itemTitleColor: primitives.Colors.Red }, + { id: 13, parents: [7], title: "Connector Node 13", label: "Node 13", templateName: "dot", description: "Structural connector for partner units.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Navy }, + { id: 14, parents: [8], title: "Connector Node 14", label: "Node 14", templateName: "dot", description: "Logical routing element.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 15, parents: [11], title: "Connector Node 15", label: "Node 15", templateName: "dot", description: "Execution routing placeholder.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + + { id: 16, parents: [12], title: "Connector Node 16", label: "Node 16", templateName: "dot", description: "Service distribution connector.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 17, parents: [13], title: "Advisory Group", label: "Advisory", description: "Provides expert consultation and guidance.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Navy }, + { id: 18, parents: [14], title: "Health Services Team", label: "Health Team", description: "Supports health and wellbeing initiatives.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 19, parents: [14], title: "Forward Support Team", label: "Support", description: "Provides rapid on-site assistance.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 20, parents: [13], title: "Transition Management Group", label: "Transition", description: "Manages organizational change activities.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 21, parents: [14], title: "Training Office", label: "Training", description: "Delivers training and capability development.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 22, parents: [14], title: "Connector Node 22", label: "Node 22", templateName: "dot", description: "Governance routing placeholder.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Navy }, + { id: 23, parents: [14], title: "Compliance Task Group", label: "Compliance", description: "Ensures policy and regulatory adherence.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 24, parents: [13], title: "Unified Coordination Cell", label: "Unified Cell", description: "Aligns multiple parallel initiatives.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 25, parents: [14], title: "Connector Node 25", label: "Node 25", templateName: "dot", description: "Execution flow connector.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 26, parents: [14], title: "Process Improvement Team", label: "Process", description: "Optimizes operational processes.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 27, parents: [14], title: "Medical Services Unit", label: "Medical", description: "Provides medical and support services.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 28, parents: [14], title: "Connector Node 28", label: "Node 28", templateName: "dot", description: "Resource distribution connector.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 29, parents: [14], title: "Digital Enablement Team", label: "Digital", description: "Implements digital initiatives.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 30, parents: [14], title: "Technical Support Group", label: "Tech Support", description: "Provides specialized technical assistance.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 45, parents: [14], title: "Connector Node 45", label: "Node 45", templateName: "dot", description: "Intermediate routing connector.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 31, parents: [14], title: "Information Services Unit", label: "Info Services", description: "Manages information flow and reporting.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 32, parents: [14], title: "Security Operations Office", label: "Security", description: "Oversees security-related operations.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 33, parents: [15], title: "Connector Node 33", label: "Node 33", templateName: "dot", description: "Program execution connector.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + + { id: 34, parents: [16], title: "Service Team Alpha", label: "Team Alpha", description: "Provides core service delivery.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 35, parents: [16], title: "Service Team Beta", label: "Team Beta", description: "Supports extended service functions.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 36, parents: [16], title: "Service Team Gamma", label: "Team Gamma", description: "Handles specialized service tasks.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 37, parents: [16], title: "Service Team Delta", label: "Team Delta", description: "Delivers regional service support.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 38, parents: [16], title: "Service Team Epsilon", label: "Team Epsilon", description: "Focuses on advisory and oversight services.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 39, parents: [22], title: "Transition Cell A", label: "Cell A", description: "Supports localized transition activities.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 40, parents: [23], title: "Operations Extension Unit", label: "Ops Ext", description: "Extends operational reach and capacity.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 41, parents: [24], title: "Connector Node 41", label: "Node 41", templateName: "dot", description: "Coordination routing node.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Navy }, + { id: 42, parents: [25], title: "Connector Node 42", label: "Node 42", templateName: "dot", description: "Execution branching connector.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 43, parents: [28], title: "Integration Nexus", label: "Nexus", description: "Central integration and coordination hub.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 44, parents: [28], title: "Coordination Group Three", label: "Group 3", description: "Handles regional coordination tasks.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 46, parents: [45], title: "Network Operations Cell", label: "Net Ops", description: "Manages network-level operations.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 47, parents: [32], title: "Joint Operations Team", label: "Joint Ops", description: "Executes joint operational initiatives.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 48, parents: [33], title: "Task Group 3-10", label: "Task 3-10", description: "Performs assigned task-based activities.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + + { id: 49, parents: [41], title: "Infrastructure Support Cell", label: "Infra Support", description: "Provides infrastructure assistance.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 50, parents: [41], title: "Regional Cell Southwest", label: "Cell SW", description: "Coordinates southwest regional activities.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 51, parents: [42], title: "Western Operations Group", label: "West Ops", description: "Manages western operational scope.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 62, parents: [41], title: "Local Coordination Office", label: "Local Office", description: "Handles local-level coordination.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 63, parents: [41], title: "Connector Node 63", label: "Node 63", templateName: "dot", description: "Subdivision routing connector.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Navy }, + { id: 64, parents: [41], title: "Regional Cell South", label: "Cell South", description: "Coordinates southern region activities.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 65, parents: [42], title: "Mountain Operations Group", label: "Mountain Ops", description: "Handles high-complexity operational areas.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 66, parents: [41], title: "Regional Cell West", label: "Cell West", description: "Coordinates western region services.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 67, parents: [42], title: "Connector Node 67", label: "Node 67", templateName: "dot", description: "Auxiliary routing connector.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 68, parents: [41], title: "Regional Cell North", label: "Cell North", description: "Coordinates northern region activities.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 69, parents: [42], title: "Northern Operations Group", label: "North Ops", description: "Manages northern operational scope.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 70, parents: [41], title: "Regional Cell Central", label: "Cell Central", description: "Coordinates central region operations.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 71, parents: [42], title: "Connector Node 71", label: "Node 71", templateName: "dot", description: "Branching logic connector.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 72, parents: [41], title: "Regional Cell East", label: "Cell East", description: "Coordinates eastern region services.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 73, parents: [42], title: "Eastern Operations Group", label: "East Ops", description: "Manages eastern operational scope.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 74, parents: [42], title: "Special Deployment Unit", label: "Deployment", description: "Handles rapid deployment activities.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 75, parents: [42], title: "Connector Node 75", label: "Node 75", templateName: "dot", description: "Structural placeholder node.", image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 76, parents: [41], title: "Connector Node 76", label: "Node 76", templateName: "dot", description: "Supplementary routing connector.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 77, parents: [42], title: "Engineering Support Team", label: "Engineering", description: "Provides engineering and technical support.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + { id: 78, parents: [42], title: "Construction Services Unit", label: "Construction", description: "Handles construction and maintenance tasks.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red }, + + { id: 79, parents: [50], title: "Southwest Cell A", label: "SW A", description: "Operational subgroup A.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 80, parents: [50, 51], title: "Southwest Cell B", label: "SW B", description: "Operational subgroup B.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 81, parents: [63], title: "Local Advisory Cell", label: "Advisory Cell", description: "Provides localized advisory services.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 82, parents: [64, 65], title: "South Cell A", label: "South A", description: "Southern region subgroup A.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 83, parents: [64, 65], title: "South Cell B", label: "South B", description: "Southern region subgroup B.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 84, parents: [64, 65], title: "South Cell C", label: "South C", description: "Southern region subgroup C.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 85, parents: [64, 65], title: "South Cell D", label: "South D", description: "Southern region subgroup D.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 86, parents: [64, 65], title: "South Cell E", label: "South E", description: "Southern region subgroup E.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 87, parents: [64, 65], title: "South Cell F", label: "South F", description: "Southern region subgroup F.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 88, parents: [64, 65], title: "South Cell J", label: "South J", description: "Southern region subgroup J.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 89, parents: [66], title: "West Cell A", label: "West A", description: "Western region subgroup A.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 90, parents: [66], title: "West Cell B", label: "West B", description: "Western region subgroup B.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 91, parents: [66, 67], title: "West Cell C", label: "West C", description: "Western region subgroup C.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 92, parents: [66, 67], title: "West Cell D", label: "West D", description: "Western region subgroup D.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 93, parents: [68], title: "North Cell A", label: "North A", description: "Northern region subgroup A.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 94, parents: [68, 69], title: "North Cell B", label: "North B", description: "Northern region subgroup B.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 95, parents: [68, 69], title: "North Cell C", label: "North C", description: "Northern region subgroup C.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 96, parents: [70], title: "Central Cell A", label: "Central A", description: "Central region subgroup A.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 97, parents: [70, 71], title: "Central Cell B", label: "Central B", description: "Central region subgroup B.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 98, parents: [72], title: "East Cell A", label: "East A", description: "Eastern region subgroup A.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 99, parents: [72], title: "East Cell B", label: "East B", description: "Eastern region subgroup B.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 100, parents: [72, 73], title: "East Cell C", label: "East C", description: "Eastern region subgroup C.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 101, parents: [72, 73], title: "East Cell D", label: "East D", description: "Eastern region subgroup D.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 102, parents: [72, 73], title: "East Cell E", label: "East E", description: "Eastern region subgroup E.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 103, parents: [72, 73], title: "East Cell F", label: "East F", description: "Eastern region subgroup F.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 104, parents: [72, 73], title: "East Cell J", label: "East J", description: "Eastern region subgroup J.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 105, parents: [72, 73], title: "East Cell H", label: "East H", description: "Eastern region subgroup H.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy }, + { id: 106, parents: [75, 76], title: "Coordination Group 25", label: "Group 25", description: "Joint coordination across multiple branches.", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red } + ] + +}; var dataSetNames = { "2 Cross Relations": "crossShape", "3 Cross Relations": "famdata2", "Rombus": "rombusShape", + "Trees Sharing Nodes": "treesSharingNodes", "Sand Clock": "sandClockShape", "Mix": "famdata", "Side By Side": "sideBySide", diff --git a/samples/javascript.controls/CaseFirstFamilyChart.html b/samples/javascript.controls/CaseFirstFamilyChart.html index 74e774e..98e49bc 100644 --- a/samples/javascript.controls/CaseFirstFamilyChart.html +++ b/samples/javascript.controls/CaseFirstFamilyChart.html @@ -12,19 +12,139 @@ document.addEventListener('DOMContentLoaded', function () { var options = new primitives.FamConfig(); - var items = [ - { id: 1, title: "Thomas Williams", label: "Thomas Williams", description: "1st husband", image: "../images/photos/t.png" }, - { id: 2, title: "Mary Spencer", label: "Mary Spencer", description: "The Mary",image: "../images/photos/m.png" }, - { id: 3, title: "David Kirby", label: "David Kirby", description: "2nd Husband", image: "../images/photos/d.png" }, - { id: 4, parents: [1, 2], title: "Brad Williams", label: "Brad Williams", description: "1st son", image: "../images/photos/b.png" }, - { id: 5, parents: [2, 3], title: "Mike Kirby", label: "Mike Kirby", description: "2nd son, having 2 spouses", image: "../images/photos/m.png"}, - { id: 6, title: "Lynette Maloney", label: "Lynette Maloney", description: "Spouse I", image: "../images/photos/m.png" }, - { id: 11, parents: [5, 6], title: "Steven Powell", label: "Steven Powell", description: "1st son", image: "../images/photos/s.png" }, - { id: 7, title: "Sara Kemp", label: "Sara Kemp", description: "Spouse II", image: "../images/photos/s.png" }, - { id: 12, parents: [5, 7], title: "John Smith", label: "John Smith", description: "2ns son", image: "../images/photos/j.png" }, - { id: 8, parents: [7], title: "Leon Kemp", label: "Leon Kemp", description: "", image: "../images/photos/l.png" } + var itemsDirect = [ + { id: 1, title: "SHAPE", label: "SHAPE", description: "", image: "../images/photos/e.png", itemTitleColor: primitives.Colors.Navy }, + { id: 2, title: "CENTCOM", label: "CENTCOM", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + + { id: 3, parents: [1], title: "JFC Brunssum", label: "JFC Brunssum", description: "", image: "../images/photos/j.png", itemTitleColor: primitives.Colors.Navy }, + { id: 107, parents: [2], title: "NONE 107", label: "NONE 107", templateName: "dot", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + + { id: 4, parents: [107], title: "ARCENT", label: "ARCENT", description: "", image: "../images/photos/a.png", itemTitleColor: primitives.Colors.Red}, + { id: 5, parents: [107], title: "AFCENT", label: "AFCENT", description: "", image: "../images/photos/f.png", itemTitleColor: primitives.Colors.Red}, + { id: 6, parents: [107], title: "SOCCENT", label: "SOCCENT", description: "", image: "../images/photos/s.png", itemTitleColor: primitives.Colors.Red}, + { id: 7, parents: [3], title: "ISAF", label: "ISAF", description: "", image: "../images/photos/i.png", itemTitleColor: primitives.Colors.Navy}, + { id: 8, parents: [107], title: "USFOR.A", label: "USFOR.A", description: "", image: "../images/photos/a.png", itemTitleColor: primitives.Colors.Red}, + { id: 9, parents: [107], title: "NAVCENT", label: "NAVCENT", description: "", image: "../images/photos/n.png", itemTitleColor: primitives.Colors.Red}, + { id: 10, parents: [107], title: "MARCENT", label: "MARCENT", description: "", image: "../images/photos/m.png", itemTitleColor: primitives.Colors.Red}, + { id: 11, parents: [107], title: "TFS-35", label: "TFS-35", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + + { id: 12, parents: [5], title: "AETF-A", label: "AETF-A", description: "", image: "../images/photos/e.png", itemTitleColor: primitives.Colors.Red}, + { id: 13, parents: [7], title: "NONE 13", label: "NONE 13", templateName: "dot", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Navy }, + { id: 14, parents: [8], title: "NONE 14", label: "NONE 14", templateName: "dot", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 15, parents: [11], title: "NONE 15", label: "NONE 15", templateName: "dot", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + + { id: 16, parents: [12], title: "NONE 16", label: "NONE 16", templateName: "dot", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 17, parents: [13], title: "ISAF SOF", label: "ISAF SOF", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Navy }, + { id: 18, parents: [14], title: "TF YANKEE 26 MED", label: "TF YANKEE 26 MED", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 19, parents: [14], title: "TAD FWD-A", label: "TAD FWD-A", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 20, parents: [13], title: "MTM-A", label: "MTM-A", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 21, parents: [14], title: "CSTC-A", label: "CSTC-A", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 22, parents: [14], title: "NONE 22", label: "NONE 22", templateName: "dot", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Navy }, + { id: 23, parents: [14], title: "CRATF 435", label: "CRATF 435", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 24, parents: [13], title: "UC", label: "UC", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 25, parents: [14], title: "NONE 25", label: "NONE 25", templateName: "dot", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 26, parents: [14], title: "TF-46", label: "TF-46", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 27, parents: [14], title: "44-MED", label: "44-MED", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 28, parents: [14], title: "NONE 28", label: "NONE 28", templateName: "dot", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 29, parents: [14], title: "TF DDN", label: "TF DDN", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 30, parents: [14], title: "359 TT58", label: "359 TT58", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 45, parents: [14], title: "NONE 45", label: "NONE 45", templateName: "dot", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 31, parents: [14], title: "ISC-A", label: "ISC-A", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 32, parents: [14], title: "CFSOCC-A", label: "CFSOCC-A", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 33, parents: [15], title: "NONE 33", label: "NONE 33", templateName: "dot", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + + { id: 34, parents: [16], title: "AGG AFS", label: "AGG AFS", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 35, parents: [16], title: "ADS EASOG", label: "ADS EASOG", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 36, parents: [16], title: "ESS AEW", label: "ESS AEW", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 37, parents: [16], title: "438 AEW", label: "438 AEW", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 38, parents: [16], title: "451 AEW", label: "451 AEW", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 39, parents: [22], title: "MTM-A", label: "MTM-A", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 40, parents: [23], title: "RCX FF-A", label: "RCX-FF-A", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 41, parents: [24], title: "NONE 41", label: "NONE 41", templateName: "dot", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Navy }, + { id: 42, parents: [25], title: "NONE 42", label: "NONE 42", templateName: "dot", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 43, parents: [28], title: "CILATS NEXUS", label: "CILATS NEXUS", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 44, parents: [28], title: "3 NCR", label: "3 NCR", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 46, parents: [45], title: "CNOC-A", label: "CNOC-A", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 47, parents: [32], title: "CJSOTF-A", label: "CJSOTF-A", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 48, parents: [33], title: "TF 3-10", label: "TF 3-10", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + + { id: 49, parents: [41], title: "RED HORSE ROS CRES", label: "RED HORSE ROS CRES", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 50, parents: [41], title: "RC (SW)", label: "RC (SW)", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 51, parents: [42], title: "W MEF", label: "W MEF", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 62, parents: [41], title: "COMKAF", label: "COMKAF", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 63, parents: [41], title: "NONE 63", label: "NONE 63", templateName: "dot", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Navy }, + { id: 64, parents: [41], title: "RC (S)", label: "RC (S)", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 65, parents: [42], title: "10 MTN", label: "10 MTB", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 66, parents: [41], title: "RC (W)", label: "RC (W)", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 67, parents: [42], title: "NONE 67", label: "NONE 67", templateName: "dot", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 68, parents: [41], title: "RC (N)", label: "RC (N)", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 69, parents: [42], title: "USFOR (N)", label: "USFOR (N)", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 70, parents: [41], title: "RC (C)", label: "RC (C)", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 71, parents: [42], title: "NONE 71", label: "NONE 71", templateName: "dot", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 72, parents: [41], title: "RC (E)", label: "RC (E)", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 73, parents: [42], title: "1 CAV", label: "1 CAV", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 74, parents: [42], title: "JEF PALADIN", label: "JEF PALADIN", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 75, parents: [42], title: "NONE 75", label: "NONE 75", templateName: "dot", description: "",image: "../images/photos/u.png", itemTitleColor: primitives.Colors.Red }, + { id: 76, parents: [41], title: "NONE 76", label: "NONE 76", templateName: "dot", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 77, parents: [42], title: "PRIME BEEF 527 EPOG", label: "PRIME BEEF 527 EPOG", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + { id: 78, parents: [42], title: "175 EN", label: "175 EN", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red}, + + { id: 79, parents: [50], title: "RC (SW) A", label: "RC (SW) A", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 80, parents: [50, 51], title: "RC (SW) B", label: "RC (SW) B", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 81, parents: [63], title: "COMCALA", label: "COMCALA", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 82, parents: [64, 65], title: "RC (S) A", label: "RC (S) A", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 83, parents: [64, 65], title: "RC (S) B", label: "RC (S) B", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 84, parents: [64, 65], title: "RC (S) C", label: "RC (S) C", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 85, parents: [64, 65], title: "RC (S) D", label: "RC (S) D", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 86, parents: [64, 65], title: "RC (S) E", label: "RC (S) E", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 87, parents: [64, 65], title: "RC (S) F", label: "RC (S) F", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 88, parents: [64, 65], title: "RC (S) J", label: "RC (S) J", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 89, parents: [66], title: "RC (W) A", label: "RC (W) A", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 90, parents: [66], title: "RC (W) B", label: "RC (W) B", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 91, parents: [66, 67], title: "RC (W) C", label: "RC (W) C", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 92, parents: [66, 67], title: "RC (W) D", label: "RC (W) D", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 93, parents: [68], title: "RC (N) A", label: "RC (N) A", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 94, parents: [68, 69], title: "RC (N) B", label: "RC (N) B", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 95, parents: [68, 69], title: "RC (N) C", label: "RC (N) C", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 96, parents: [70], title: "RC (C) A", label: "RC (C) A", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 97, parents: [70, 71], title: "RC (C) B", label: "RC (C) B", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 98, parents: [72], title: "RC (E) A", label: "RC (E) A", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 99, parents: [72], title: "RC (E) B", label: "RC (E) B", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 100, parents: [72, 73], title: "RC (E) C", label: "RC (E) C", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 101, parents: [72, 73], title: "RC (E) D", label: "RC (E) D", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 102, parents: [72, 73], title: "RC (E) E", label: "RC (E) E", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 103, parents: [72, 73], title: "RC (E) F", label: "RC (E) F", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 104, parents: [72, 73], title: "RC (E) J", label: "RC (E) J", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 105, parents: [72, 73], title: "RC (E) H", label: "RC (E) H", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Navy}, + { id: 106, parents: [75, 76], title: "25 NCR", label: "25 NCR", description: "", image: "../images/photos/t.png", itemTitleColor: primitives.Colors.Red} ]; + // var items = []; + // var newParents = {}; + // for( var index = 0; index < itemsDirect.length; index++) { + // var item = itemsDirect[index]; + // var parents = item.parents || []; + // for(var pIndex = 0; pIndex < parents.length; pIndex++) { + // var parent = parents[pIndex]; + // if(newParents.hasOwnProperty(parent)) { + // newParents[parent].push(item.id); + // } else { + // newParents[parent] = [item.id]; + // } + // } + // } + // for( var index = 0; index < itemsDirect.length; index++) { + // var item = itemsDirect[index]; + // item.parents = newParents[item.id] || []; + + // items.push(item); + // } + + var items = itemsDirect; + + options.itemsOrderType = primitives.ItemsOrderType.MergedTrees; + options.enableMatrixLayout = false; + options.minimumMatrixSize = 5; options.pageFitMode = primitives.PageFitMode.None; options.items = items; options.cursorItem = 2; @@ -39,13 +159,87 @@ options.lineItemsInterval = 10; options.arrowsDirection = primitives.GroupByType.Parents; options.showExtraArrows = false; + options.itemTitleFirstFontColor = primitives.Colors.White; + options.itemTitleSecondFontColor = primitives.Colors.White; + options.templates = [getContactTemplate(), getDotTemplate()]; + options.onItemRender = onTemplateRender; + options.defaultTemplateName = "contactTemplate"; + control = primitives.FamDiagram(document.getElementById("basicdiagram"), options); }); + function onTemplateRender(event, data) { + switch (data.renderingMode) { + case primitives.RenderingMode.Create: + /* Initialize template content here */ + break; + case primitives.RenderingMode.Update: + /* Update template content here */ + break; + } + + var itemConfig = data.context; + + if (data.templateName == "contactTemplate") { + var titleBackground = data.element.firstChild; + titleBackground.style.backgroundColor = itemConfig.itemTitleColor || primitives.Colors.RoyalBlue; + + var title = data.element.firstChild.firstChild; + title.textContent = itemConfig.title; + } else if (data.templateName == "dot") { + var titleBackground = data.element; + titleBackground.style.backgroundColor = itemConfig.itemTitleColor || primitives.Colors.RoyalBlue; + } + } + + function getContactTemplate() { + var result = new primitives.TemplateConfig(); + result.name = "contactTemplate"; + result.itemSize = new primitives.Size(60, 26); + result.minimizedItemSize = new primitives.Size(3, 3); + result.hasButtons = primitives.Enabled.False; + + result.itemTemplate = + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + return result; + } + + function getDotTemplate() { + var result = new primitives.TemplateConfig(); + result.name = "dot"; + + result.isActive = false; + result.itemSize = new primitives.Size(2, 2); + result.minimizedItemSize = new primitives.Size(2, 2); + result.minimizedItemCornerRadius = 4; + result.minimizedItemLineWidth = 1; + result.minimizedItemLineType = primitives.LineType.Solid; + result.minimizedItemBorderColor = null; // Shape border line has the same color as item title background color + result.minimizedItemFillColor = null; // Shape background has the same color as item title background color + result.minimizedItemOpacity = 0.7; // Shape background opacity + + result.itemTemplate = ["div", { + "style": { + top: "2px", + left: "2px", + width: result.itemSize.width + "px", + height: result.itemSize.height + "px" + }, + "class": ["bp-corner-all"] + }]; + + return result; + } -
+
\ No newline at end of file diff --git a/samples/javascript.controls/DemoDependencies.html b/samples/javascript.controls/DemoDependencies.html index f9255cd..bd5da16 100644 --- a/samples/javascript.controls/DemoDependencies.html +++ b/samples/javascript.controls/DemoDependencies.html @@ -20,6 +20,7 @@ var optionsRender = javascriptsamples.getFamDiagramOptionsRender([], { /* Family Diagram Specific Options */ + itemsOrderType: primitives.ItemsOrderType.Families, neighboursSelectionMode: primitives.NeighboursSelectionMode.ParentsChildrenSiblingsAndSpouses, groupByType: primitives.GroupByType.Children, alignBylevels: true, diff --git a/samples/javascript.controls/DemoFamily.html b/samples/javascript.controls/DemoFamily.html index 4c3f2ac..70027e5 100644 --- a/samples/javascript.controls/DemoFamily.html +++ b/samples/javascript.controls/DemoFamily.html @@ -29,6 +29,7 @@ dataSet: defaultDataSetName, /* Family Diagram Specific Options */ + itemsOrderType: primitives.ItemsOrderType.Families, neighboursSelectionMode: primitives.NeighboursSelectionMode.ParentsChildrenSiblingsAndSpouses, groupByType: primitives.GroupByType.Children, alignBylevels: true, diff --git a/samples/javascript.controls/DemoFamilyOrdering.html b/samples/javascript.controls/DemoFamilyOrdering.html index 5be90c9..2808fb5 100644 --- a/samples/javascript.controls/DemoFamilyOrdering.html +++ b/samples/javascript.controls/DemoFamilyOrdering.html @@ -23,6 +23,7 @@ var optionsRender = javascriptsamples.getFamDiagramOptionsRender([], { /* Family Diagram Specific Options */ + itemsOrderType: primitives.ItemsOrderType.Families, neighboursSelectionMode: primitives.NeighboursSelectionMode.ParentsChildrenSiblingsAndSpouses, groupByType: primitives.GroupByType.Children, alignBylevels: true, diff --git a/samples/javascript.controls/DemoTechTree.html b/samples/javascript.controls/DemoTechTree.html index 04fd316..3a35baa 100644 --- a/samples/javascript.controls/DemoTechTree.html +++ b/samples/javascript.controls/DemoTechTree.html @@ -17,6 +17,7 @@ var optionsRender = javascriptsamples.getFamDiagramOptionsRender([], { /* Family Diagram Specific Options */ + itemsOrderType: primitives.ItemsOrderType.Families, neighboursSelectionMode: primitives.NeighboursSelectionMode.ParentsChildrenSiblingsAndSpouses, groupByType: primitives.GroupByType.Parents, alignBylevels: true, diff --git a/samples/javascript.controls/common/panels.js b/samples/javascript.controls/common/panels.js index 7c00449..10a9329 100644 --- a/samples/javascript.controls/common/panels.js +++ b/samples/javascript.controls/common/panels.js @@ -14,7 +14,7 @@ import { ValueType } from './enums'; import { NeighboursSelectionMode, GroupByType, ConnectorPlacementType, ConnectorShapeType, ConnectorLabelPlacementType, LineType, Colors, ZOrderType, AdviserPlacementType, TextOrientationType, VerticalAlignmentType, HorizontalAlignmentType, ConnectorType, ElbowType, PageFitMode, OrientationType, ChildrenPlacementType, Visibility, SelectionPathMode, Enabled, ShapeType, PlacementType, - NavigationMode + NavigationMode, ItemsOrderType } from '../../../src/enums'; import Size from '../../../src/graphics/structs/Size'; @@ -48,6 +48,7 @@ export function getFamDiagramOptionsRender(extraPanels, defaultOptions, onUpdate function getFamDiagramOptionsPanels(onUpdate) { return [ new PanelConfig("Family layout", [ + new RadioBoxConfig("itemsOrderType", NeighboursSelectionMode.ParentsChildrenSiblingsAndSpouses, "Items horizontal order type", ItemsOrderType, ValueType.Integer, onUpdate), new RadioBoxConfig("neighboursSelectionMode", NeighboursSelectionMode.ParentsChildrenSiblingsAndSpouses, "Neighbors Selection Modes", NeighboursSelectionMode, ValueType.Integer, onUpdate), new RadioBoxConfig("groupByType", GroupByType.Children, "Group by option defines node placement in layout close to its parents or children when node is linked across multiple levels in hierarchy. See \"alignment\" data set.", { Children: 2, Parents: 1 }, ValueType.Integer, onUpdate), new CheckBoxConfig("alignBylevels", true, "Keep items at the same levels after connections bundling", onUpdate), diff --git a/src/algorithms/Family.getReversedFamily.test.js b/src/algorithms/Family.getReversedFamily.test.js new file mode 100644 index 0000000..f071c2b --- /dev/null +++ b/src/algorithms/Family.getReversedFamily.test.js @@ -0,0 +1,77 @@ +import Family from './Family'; + +var items = [ + { id: 1, name: "1" }, + { id: 2, name: "2" }, + { id: 3, name: "3" }, + { id: 4, parents: [1, 2, 3], name: "4" }, + { id: 5, parents: [1, 2, 3], name: "5" }, + { id: 6, parents: [4], name: "6" }, + { id: 7, parents: [4, 5], name: "7" }, + { id: 8, parents: [5], name: "8" }, + { id: 9, parents: [6], name: "9" }, + { id: 10, parents: [7], name: "10" }, + { id: 11, parents: [8], name: "11" } +]; + +function getFamily(items) { + var family = Family(); + for (var index = 0; index < items.length; index += 1) { + var item = items[index]; + family.add(item.parents, item.id, item); + } + return family; +} + +function getLevels(family) { + var levels = []; + family.loopLevels(this, true, function (itemid, item, level) { + var newItem = { id: itemid }; + var parents = []; + family.loopParents(this, itemid, function (itemid, item, levelIndex) { + if (levelIndex > 0) { + return family.BREAK; + } + parents.push(itemid); + }); + if (parents.length > 0) { + newItem.parents = parents; + } + levels.push(newItem); + }); + return levels; +} + +test('getReversedFamily -Trivial', () => { + const family = getFamily([ + { id: 1 }, { id: 2, parents: [1]} + ]); + const reversedFamily = family.getReversedFamily(); + + + var levels = getLevels(reversedFamily); + var expectedResults = [{"id": "2"}, {"id": "1", "parents": ["2"] }]; + + expect(reversedFamily.validate()).toBe(true); + expect(levels).toEqual(expectedResults); +}); + +test('getReversedFamily - Less trivial', () => { + const family = getFamily([ + { id: 1 }, { id: 2 }, { id: 3 }, + { id: 4, parents: [1, 2, 3] }, { id: 5, parents: [1, 2, 3] }, + { id: 6, parents: [4, 5] }, { id: 7, parents: [4, 5] }, { id: 8, parents: [4, 5] } + ]); + const reversedFamily = family.getReversedFamily(); + + + var levels = getLevels(reversedFamily); + var expectedResults = [ + { "id": "6" }, { "id": "8" }, { "id": "7" }, + { "id": "4", "parents": ["6", "7", "8"] }, { "id": "5", "parents": ["6", "7", "8"] }, + { "id": "1", "parents": ["4", "5"] }, { "id": "2", "parents": ["4", "5"] }, { "id": "3", "parents": ["4", "5"] } + ]; + + expect(reversedFamily.validate()).toBe(true); + expect(levels).toEqual(expectedResults); +}); \ No newline at end of file diff --git a/src/algorithms/Family.js b/src/algorithms/Family.js index 73df2da..0f3ea5f 100644 --- a/src/algorithms/Family.js +++ b/src/algorithms/Family.js @@ -418,6 +418,100 @@ export default function Family(source) { } } + /** + * Callback for iterating family parents level by level + * + * @callback onFamilyItemsWithLevelCallback + * @param {string[]} parents1 The collection of parent ids for the first node + * @param {string[]} parents2 The collection of parent ids for the second node + * @param {number} levelIndex The node level index + * @returns {boolean} Returns true to break the loop and exit. + */ + + /** + * Loops through parent nodes level by level from 2 starting nodes returning parent family nodes at level as arrays + * + * @param {Object} thisArg The callback function invocation context + * @param {string} nodeid1 First node id to start parents traversing + * @param {string} nodeid2 Second node id to start parents traversing + * @param {onFamilyItemsWithLevelCallback} onItems A callback function to call for every parent node + */ + function zipParents(thisArg, nodeid1, nodeid2, onItems) { + if (onItems != null) { + if (nodeid1 != null && _nodes[nodeid1] != null && _parents[nodeid1] != null + && nodeid2 != null && _nodes[nodeid2] != null && _parents[nodeid2] != null + ) { + _zipItems(thisArg, _parents, nodeid1, nodeid2, onItems); + } + } + } + + /** + * Loops through child nodes level by level from 2 starting nodes returning child family nodes at level as arrays + * + * @param {Object} thisArg The callback function invocation context + * @param {string} nodeid1 First node id to start parents traversing + * @param {string} nodeid2 Second node id to start parents traversing + * @param {onFamilyItemsWithLevelCallback} onItems A callback function to call for every parent node + */ + function zipChildren(thisArg, nodeid1, nodeid2, onItems) { + if (onItems != null) { + if (nodeid1 != null && _nodes[nodeid1] != null && _children[nodeid1] != null + && nodeid2 != null && _nodes[nodeid2] != null && _children[nodeid2] != null + ) { + _zipItems(thisArg, _children, nodeid1, nodeid2, onItems); + } + } + } + + function _zipItems(thisArg, collection, nodeid1, nodeid2, onItems) { // onItems([items1], [items2], levelIndex) + var items1, items2, + newItems1, newItems2, + processed1 = {}, + processed2 = {}, + levelIndex = 0, + index, itemid; + + items1 = [nodeid1]; + items2 = [nodeid2]; + processed1[nodeid1] = true; + processed2[nodeid2] = true; + while (items1.length > 0 && items2.length > 0) { + newItems1 = []; + for (index = 0; index < items1.length; index += 1) { + itemid = items1[index]; + _loop(this, collection, itemid, function (newItemId) { + if (!processed1[newItemId]) { + newItems1.push(newItemId); + processed1[newItemId] = true; + } + }); + } + + newItems2 = []; + for (index = 0; index < items2.length; index += 1) { + itemid = items2[index]; + _loop(this, collection, itemid, function (newItemId) { + if (!processed2[newItemId]) { + newItems2.push(newItemId); + processed2[newItemId] = true; + } + }); + } + + if (newItems1.length > 0 && newItems2.length > 0) { + if (onItems.call(thisArg, newItems1, newItems2, levelIndex)) { + break; + } + } else { + break; + } + items1 = newItems1; + items2 = newItems2; + levelIndex += 1; + } + } + function _loopTopo(thisArg, backwardCol, backwardCount, forwardCol, forwardCount, onItem) { // onItem(itemid, item, position) var index, len, nodeid, references, queue, newQueue, position; @@ -1265,6 +1359,25 @@ export default function Family(source) { this.key = parentid + "," + childid; } + /** + * Creates a new Family structure with parent–child relationships reversed. + * Every original parent becomes a child, and every child becomes a parent. + * The original Family structure is not modified. + * + * @returns {family} Returns a Family structure with inverted parent–child relations. + */ + function getReversedFamily() { + return Family({ + roots: {}, + rootsCount: {}, + children: _parents, + childrenCount: _parentsCount, + parents: _children, + parentsCount: _childrenCount, + nodes: _nodes + }); + } + /** * Eliminates crossing parent child relations between nodes based of nodes order in treeLevels structure. * @param {treeLevels} treeLevels Tree levels structure keeps orders of nodes level by level. @@ -1880,6 +1993,7 @@ export default function Family(source) { groupBy: groupBy, getPlanarFamily: getPlanarFamily, getFamilyWithoutGrandParentsRelations: getFamilyWithoutGrandParentsRelations, + getReversedFamily: getReversedFamily, getGraph: getGraph, removeNode: removeNode, @@ -1903,6 +2017,8 @@ export default function Family(source) { countParents: countParents, firstParent: firstParent, firstChild: firstChild, + zipParents: zipParents, + zipChildren: zipChildren, /* force validation */ validate: validate, diff --git a/src/algorithms/TreeLevels.getReversedTreeLevels.test.js b/src/algorithms/TreeLevels.getReversedTreeLevels.test.js new file mode 100644 index 0000000..3f51571 --- /dev/null +++ b/src/algorithms/TreeLevels.getReversedTreeLevels.test.js @@ -0,0 +1,44 @@ +import TreeLevels from './TreeLevels'; + +function getTreeLevels(levels) { + var treeLevels = TreeLevels(); + for (var levelIndex = 0, levelLen = levels.length; levelIndex < levelLen; levelIndex += 1) { + var level = levels[levelIndex]; + if (!treeLevels.hasLevel(levelIndex)) { + treeLevels.addlevel(levelIndex, {}); + } + for (var index = 0, len = level.length; index < len; index += 1) { + treeLevels.addItem(levelIndex, level[index], { id: level[index] }); + } + } + return treeLevels; +}; + +var items = [ + [1], + [2, 3], + [4, 5, 6], + [], + [7, 8, 9, 10] +]; + +test('loopLevels function indexes levels', () => { + var treeLevels = getTreeLevels(items); + var reversed = treeLevels.getReversedTreeLevels(); + var result = []; + reversed.loopLevels(this, function (index, level) { + var levelItems = []; + reversed.loopLevelItems(this, index, function (itemid, context, position) { + levelItems.push(itemid); + }); + result.push(levelItems); + }) + var expectedResult = [ + [7, 8, 9, 10], + [], + [4, 5, 6], + [2, 3], + [1] + ]; + expect(result).toEqual(expectedResult); +}); diff --git a/src/algorithms/TreeLevels.js b/src/algorithms/TreeLevels.js index 8e69e97..01e6c00 100644 --- a/src/algorithms/TreeLevels.js +++ b/src/algorithms/TreeLevels.js @@ -370,6 +370,54 @@ export default function TreeLevels(source) { } } + /** + * Loops elements level by level reversed + * + * @param {Object} thisArg The callback function invocation context + * @param {onTreeLevelsItemCallback} onItem A callback function to call for every item + */ + function loopItemsReversed(thisArg, onItem) { // function onItem(itemid, item, position, levelIndex, level) + var index, len, + level, levelIndex, + items, + itemid, + processed = {}; + if (onItem != null) { + for (levelIndex = _maximum; levelIndex >= _minimum; levelIndex -= 1) { + level = _levels[levelIndex]; + if (level != null) { + items = level.items; + for (index = 0, len = items.length; index < len; index += 1) { + itemid = items[index]; + if (!processed.hasOwnProperty(itemid)) { + processed[itemid] = true; + if (onItem.call(thisArg, itemid, _items[itemid].context, index, levelIndex, level.context)) { + return; + } + } + } + } + } + } + } + + /** + * Returns TreeLevel structure with reversed levels + * + * @returns {family} Returns a TreeLevels structure with reversed levels. + */ + function getReversedTreeLevels() { + var result = TreeLevels(); + this.loopLevelsReversed(this, function (levelIndex, level) { + var newLevelIndex = _minimum + _maximum - levelIndex; + result.addlevel(newLevelIndex, level); + this.loopLevelItems(this, levelIndex, function (itemid, item, position) { + result.addItem(newLevelIndex, itemid, item); + }) + }); + return result; + } + /** * Callback for finding distance for element * @@ -555,6 +603,8 @@ export default function TreeLevels(source) { loopLevelItems: loopLevelItems, getLevelLength: getLevelLength, loopItems: loopItems, + loopItemsReversed: loopItemsReversed, + getReversedTreeLevels: getReversedTreeLevels, binarySearch: binarySearch, loopMerged: loopMerged, loopFromItem: loopFromItem, diff --git a/src/configs/FamConfig.js b/src/configs/FamConfig.js index f8f8fe8..4a00803 100644 --- a/src/configs/FamConfig.js +++ b/src/configs/FamConfig.js @@ -1,7 +1,8 @@ import {NavigationMode, PageFitMode, Visibility, OrientationType, VerticalAlignmentType, GroupByType, ElbowType, Enabled, SelectionPathMode, NeighboursSelectionMode, Colors, ShapeType, LineType, AdviserPlacementType, TextOrientationType, HorizontalAlignmentType, - PlacementType + PlacementType, + ItemsOrderType } from '../enums'; import Thickness from '../graphics/structs/Thickness'; import Size from '../graphics/structs/Size'; @@ -120,6 +121,17 @@ export default function FamConfig(name) { */ this.groupByType = GroupByType.Children; + /** + * Defines ordering of items in the family diagram layout. + * Items can either be arranged left-to-right in the same order as they appear + * in the `items` collection, or be automatically reordered to minimize + * connector crossings. + * + * @group Auto Layout + * @type {ItemsOrderType} + */ + this.itemsOrderType = ItemsOrderType.Families; + /** * The align by levels option keeps items at the same levels after bundling connection lines between parents and children. * diff --git a/src/connectors/VerticalConnectorBundle.js b/src/connectors/VerticalConnectorBundle.js index 41f93ea..416541c 100644 --- a/src/connectors/VerticalConnectorBundle.js +++ b/src/connectors/VerticalConnectorBundle.js @@ -12,6 +12,9 @@ export default function VerticalConnectorBundle(fromItems, toItems, dotId) { this.fromOffset = 0; this.fromStackSize = 0; + + this.toOffset = 0; + this.toStackSize = 0; }; VerticalConnectorBundle.prototype = new BaseConnectorBundle(); @@ -85,7 +88,7 @@ VerticalConnectorBundle.prototype.trace = function (data, params, options) { children.sort(function (a, b) { return a.x - b.x; }); /* Find offset of horizontal connector line between children */ - childrenConnectorOffset = treeItemPosition.topConnectorShift; + childrenConnectorOffset = treeItemPosition.topConnectorShift - treeItemPosition.topConnectorInterval * (this.toStackSize - this.toOffset); } if (children.length == 1) { @@ -120,7 +123,7 @@ VerticalConnectorBundle.prototype.trace = function (data, params, options) { if (topCenterPoint != null && bottomCenterPoint.y == topCenterPoint.y) { bottomCenterPoint = topCenterPoint; } - this.traceFork(data, params, options, bottomCenterPoint, children, hasSquared, false, 0, options.showExtraArrows); + this.traceFork(data, params, options, bottomCenterPoint, children, hasSquared, false, this.toOffset, options.showExtraArrows); } /* draw connector line between children and parents */ diff --git a/src/enums.js b/src/enums.js index 0a60f46..3f97777 100644 --- a/src/enums.js +++ b/src/enums.js @@ -941,3 +941,26 @@ export const ZOrderType = { */ Foreground: 2 }; + +/** + * Defines automatic horizontal item ordering in a family diagram layout. + * + * @enum {ItemsOrderType} + */ +export const ItemsOrderType = { + /** + * Balance family tree so parents sharing the most children stay close to each other + * + * Takes into account user-defined item `position` and the + * `primaryParent` options. + */ + Families: 0, + /** + * Balances the family tree so children who share the most parents + * are placed close to each other. + * + * Takes into account user-defined item `position` and the + * `primaryParent` options. + */ + MergedTrees: 1 +}; \ No newline at end of file diff --git a/src/tasks/options/OrderFamilyNodesOptionTask.js b/src/tasks/options/OrderFamilyNodesOptionTask.js index b16f76e..7b94ca3 100644 --- a/src/tasks/options/OrderFamilyNodesOptionTask.js +++ b/src/tasks/options/OrderFamilyNodesOptionTask.js @@ -2,13 +2,14 @@ import ValueReader from '../../readers/ValueReader'; import ObjectReader from '../../readers/ObjectReader'; import ArrayReader from '../../readers/ArrayReader'; import EnumerationReader from '../../readers/EnumerationReader'; -import { AdviserPlacementType } from '../../enums'; +import { AdviserPlacementType, ItemsOrderType } from '../../enums'; export default function OrderFamilyNodesOptionTask(optionsTask, defaultConfig, defaultItemConfig) { var _data = {}, _hash = {}; var _dataTemplate = new ObjectReader({ + itemsOrderType: new EnumerationReader(ItemsOrderType, false, defaultConfig.itemsOrderType), enableMatrixLayout: new ValueReader(["boolean"], false, defaultConfig.enableMatrixLayout), minimumMatrixSize: new ValueReader(["number"], false, defaultConfig.minimumMatrixSize), maximumColumnsInMatrix: new ValueReader(["number"], false, defaultConfig.maximumColumnsInMatrix), diff --git a/src/tasks/transformations/OrderFamilyNodesTask.js b/src/tasks/transformations/OrderFamilyNodesTask.js index 358a8fb..9409533 100644 --- a/src/tasks/transformations/OrderFamilyNodesTask.js +++ b/src/tasks/transformations/OrderFamilyNodesTask.js @@ -4,6 +4,7 @@ import FamilyBalance from './familyTransformations/FamilyBalance'; import UserDefinedPrimaryParents from './familyTransformations/UserDefinedPrimaryParents'; import TreeLevelConnectorStackSize from '../../models/TreeLevelConnectorStackSize'; +import { ItemsOrderType } from '../../enums'; export default function OrderFamilyNodesTask(orderFamilyNodesOptionTask, userDefinedNodesOrderTask, normalizeLogicalFamilyTask) { var _data = { @@ -22,21 +23,26 @@ export default function OrderFamilyNodesTask(orderFamilyNodesOptionTask, userDef maximumId = normalizeLogicalFamilyTask.getMaximumId(), orderFamilyNodesOptions = orderFamilyNodesOptionTask.getOptions(); - var balanceParams = { - logicalFamily: logicalFamily, - maximumId: maximumId, - itemsPositions: userDefinedNodesOrderTask.getPositions(), - itemsGroups: userDefinedNodesOrderTask.getGroups(), - primaryParents: _userDefinedPrimaryParents.getUserDefinedPrimaryParents(orderFamilyNodesOptions.items, logicalFamily) - }; + switch (orderFamilyNodesOptions.itemsOrderType) { + case ItemsOrderType.Families: + case ItemsOrderType.MergedTrees: + var isReversed = orderFamilyNodesOptions.itemsOrderType === ItemsOrderType.MergedTrees + var params = { + logicalFamily, + maximumId, + itemsPositions: userDefinedNodesOrderTask.getPositions(), + itemsGroups: userDefinedNodesOrderTask.getGroups(), + primaryParents: _userDefinedPrimaryParents.getUserDefinedPrimaryParents(orderFamilyNodesOptions.items, logicalFamily) + } + var {maximumId, treeLevels, bundles, connectorStacks} = _familyBalance.balance(params, isReversed); - var {maximumId, treeLevels, bundles, connectorStacks} = _familyBalance.balance(balanceParams); - - _data.maximumId = maximumId; - _data.treeLevels = treeLevels; - _data.bundles = bundles; - _data.connectorStacks = connectorStacks; - _data.logicalFamily = logicalFamily; + _data.maximumId = maximumId; + _data.treeLevels = treeLevels; + _data.bundles = bundles; + _data.connectorStacks = connectorStacks; + _data.logicalFamily = logicalFamily; + break; + } return true; } diff --git a/src/tasks/transformations/familyTransformations/FamilyBalance.js b/src/tasks/transformations/familyTransformations/FamilyBalance.js index 84c147e..46ab434 100644 --- a/src/tasks/transformations/familyTransformations/FamilyBalance.js +++ b/src/tasks/transformations/familyTransformations/FamilyBalance.js @@ -1,29 +1,58 @@ -/* This class transforms normalized logical family into levels of nodes. - The current approach to optimize items placement is to transform family into hierarchy of nodes and order - children of every node in the way minimizing number of intersections between connection lines. - 1. Extract families into _families array of type FamilyItem. Family is sub tree of items logicalFamily. - In order to extract families out of logicalFamily we count from bottom to roots total number of descendants for evry item and then extract - sub hierarchy having minimum number of members. This process is repeated till all nodes are extracted into separate families. - orgPartners - When we extract families we store links to parents in other branches having the same children of - some already extracted item as partner in orgPartners hash - This hash table is used to create links collections between families - The orgTree collection is used to define final org hierarchy used to balance nodes in levels. - 2. Use links in families to build family graph - 3. Find maximum spanning tree of family graph - 4. Since spanning tree is the tree we calculate number of descendants in every branch. So when we join families into one - org chart we sort them taking first child family having maximum number of links to its parent family - sortedFamilies collection - 5. Using sortedFamilies collection we merge roots of families back to primary org chart. The rule of that backward merging is - to find ancestor in target tree having level less then root item of merged family. - this is done without extra collection creation via making changes in orgTree - If family has no links it is added to root of orgTree - 6. Balance organizational chart in order to place items having extra connections close to each other. - Assign every extra link to every pair of parent nodes up to the root. - 7. Scan orgTree hierarchy from root to bottom and balance children using extra links collected from children - So at the top most level we know number of links between children, so we sort them, then number of overlappings between branches should be minimal - Balancing algorithms finds maximum spanning tree in connections between children and groups them from bottom of that tree up to the root - In the way when groups having maximum mutual links placed close to each other. -*/ +/* + * This class transforms a normalized logical family into levels of nodes. + * + * The current approach to optimize item placement is to transform the family + * into a hierarchy of nodes and order the children of every node in a way that + * minimizes the number of intersections between connection lines. + * + * 1. Extract families into the `_families` array of type `FamilyItem`. + * A family is a subtree of items from `logicalFamily`. + * To extract families from `logicalFamily`, we count from bottom to root + * the total number of descendants for every item and then extract a + * sub-hierarchy with the minimum number of members. + * This process is repeated until all nodes are extracted into separate families. + * + * `orgPartners` — while extracting families, we store links to parents in + * other branches that share the same children with an already extracted item + * as partners in the `orgPartners` hash. + * + * This hash table is used to create link collections between families. + * The `orgTree` collection is used to define the final organizational hierarchy + * used to balance nodes across levels. + * + * 2. Use links between families to build a family graph. + * + * 3. Find the maximum spanning tree of the family graph. + * + * 4. Since a spanning tree is a hierarchy, we calculate the number of descendants + * in every branch. When joining families into a single organizational chart, + * we sort them by taking first the child family with the maximum number of links + * to its parent family. + * + * The result is stored in the `sortedFamilies` collection. + * + * 5. Using the `sortedFamilies` collection, we merge family roots back into the + * primary organizational chart. The rule of backward merging is to find an + * ancestor in the target tree whose level is less than the root item of the + * merged family. + * + * This is done without creating extra collections, by applying changes + * directly to `orgTree`. + * If a family has no links, it is added to the root of `orgTree`. + * + * 6. Balance the organizational chart in order to place items with extra + * connections close to each other. + * Assign every extra link to every pair of parent nodes up to the root. + * + * 7. Scan the `orgTree` hierarchy from root to bottom and balance children using + * extra links collected from their descendants. + * At the topmost level, the number of links between children is known, so we + * sort them to minimize overlaps between branches. + * + * The balancing algorithm finds a maximum spanning tree of connections between + * children and groups them from the bottom of that tree up to the root, so that + * groups with the maximum number of mutual links are placed close to each other. + */ import TreeLevels from '../../../algorithms/TreeLevels'; import LinkedHashItems from '../../../algorithms/LinkedHashItems'; import Tree from '../../../algorithms/Tree'; @@ -39,15 +68,10 @@ export default function FamilyBalance() { }; -//var params = { -// logicalFamily, -// maximumId, -// items -//}; -FamilyBalance.prototype.balance = function (params) { +FamilyBalance.prototype.balance = function (params, isReversed) { var result = { maximumId: null, - treeLevels: TreeLevels(), + treeLevels: null, // TreeLevels() bundles: [], connectorStacks: [] }; @@ -61,9 +85,41 @@ FamilyBalance.prototype.balance = function (params) { maximumLevel: null }; - this.createOrgTree(params, data); + var logicalFamily = params.logicalFamily; + var minimum = null; + var maximum = null; + if (isReversed) { + logicalFamily = logicalFamily.getReversedFamily(); + + // find minimum and maximum level logicalFamily + var levels = {}; + logicalFamily.loop(this, (itemId, item) => { + var level = item.level; + if (!levels.hasOwnProperty(level)) { + levels[level] = true; + if(minimum === null || minimum > level) { + minimum = level; + } + if(maximum === null || maximum < level) { + maximum = level; + } + } + }); + // reverse levels for nodes + logicalFamily.loop(this, (itemId, item) => { + var level = item.level; + item.level = minimum + maximum - level; + }); + } + var orgTreeParams = { + ...params, + logicalFamily: isReversed ? params.logicalFamily.getReversedFamily() : params.logicalFamily + } + this.createOrgTree(orgTreeParams, data); - var currentLevelIndex, index = -1; + var currentLevelIndex, + index = -1, + treeLevels = TreeLevels(); data.orgTree.loopLevels(this, function (treeItemId, treeItem, levelIndex) { var familyItem = params.logicalFamily.node(treeItemId); if (familyItem != null) { @@ -71,10 +127,20 @@ FamilyBalance.prototype.balance = function (params) { currentLevelIndex = levelIndex; index += 1; } - result.treeLevels.addItem(index, treeItemId, familyItem); + treeLevels.addItem(index, treeItemId, familyItem); } }); + result.treeLevels = isReversed ? treeLevels.getReversedTreeLevels() : treeLevels; + + if (isReversed) { + // reverse levels of individual nodes + logicalFamily.loop(this, (itemId, item) => { + var level = item.level; + item.level = minimum + maximum - level; + }); + } + this.recalcLevelsDepth(result.bundles, result.connectorStacks, result.treeLevels, params.logicalFamily); result.maximumId = data.maximumId; @@ -882,7 +948,7 @@ FamilyBalance.prototype.recalcLevelsDepth = function (bundles, connectorStacks, bundles.push(bundle); - if (fromItems.length > 1) { + if (fromItems.length > 1 || toItems.length > 1) { bundlesToStack.push(bundle); } } @@ -897,18 +963,36 @@ FamilyBalance.prototype.recalcLevelsDepth = function (bundles, connectorStacks, startIndex = null; endIndex = null; - for (index3 = 0, len3 = bundle.fromItems.length; index3 < len3; index3 += 1) { - itemPosition = treeLevels.getItemPosition(bundle.fromItems[index3]); + if (bundle.fromItems.length > 1) { + for (index3 = 0, len3 = bundle.fromItems.length; index3 < len3; index3 += 1) { + itemPosition = treeLevels.getItemPosition(bundle.fromItems[index3]); - startIndex = (startIndex != null) ? Math.min(startIndex, itemPosition) : itemPosition; - endIndex = (endIndex != null) ? Math.max(endIndex, itemPosition) : itemPosition; + startIndex = (startIndex != null) ? Math.min(startIndex, itemPosition) : itemPosition; + endIndex = (endIndex != null) ? Math.max(endIndex, itemPosition) : itemPosition; + } + } + if (bundle.toItems.length > 1) { + for (index3 = 0, len3 = bundle.toItems.length; index3 < len3; index3 += 1) { + itemPosition = treeLevels.getItemPosition(bundle.toItems[index3]); + + startIndex = (startIndex != null) ? Math.min(startIndex, itemPosition) : itemPosition; + endIndex = (endIndex != null) ? Math.max(endIndex, itemPosition) : itemPosition; + } } stackSegments.add(startIndex, endIndex, bundle); } stacksSizes.parentsStackSize = stackSegments.resolve(this, function (from, to, bundle, offset, stackSize) { - bundle.fromOffset = offset + 1; - bundle.fromStackSize = stackSize; + if (stackSize > 1) { + if (bundle.fromItems.length > 1) { + bundle.fromOffset = offset + 1; + bundle.fromStackSize = stackSize; + } + if (bundle.toItems.length > 1) { + bundle.toOffset = offset + 1; + bundle.toStackSize = stackSize; + } + } });//ignore jslint } });