Last active
January 29, 2026 14:35
-
-
Save romualdrichard/0b10efcc5fd72196ca2d505f3d5e9905 to your computer and use it in GitHub Desktop.
#jarchi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Distribute relationships between multiple selected elements | |
| * Enhanced version - supports multiple elements selection | |
| * Based on: https://forum.archimatetool.com/index.php?topic=1174.0 | |
| */ | |
| console.log("Distribute relations - Multi-element version"); | |
| var view = selection.parents().filter('archimate-diagram-model').first(); | |
| var elements = selection.filter('element'); | |
| // Verification: at least 2 elements must be selected | |
| if(elements.size() < 2) { | |
| window.alert('At least 2 elements must be selected'); | |
| exit(); | |
| } | |
| // Function to calculate the absolute position of an element | |
| function getAbsolutePosition(element) { | |
| function absolutePosition(accumulator, current) { | |
| return { | |
| x: (accumulator.x + current.bounds.x), | |
| y: (accumulator.y + current.bounds.y) | |
| }; | |
| } | |
| return Java.from($(element).parents().not('folder').not('archimate-diagram-model')) | |
| .reduce(absolutePosition, {x: element.bounds.x, y: element.bounds.y}); | |
| } | |
| // Function to get the center position of an element | |
| function getCenterPosition(element, absPosition) { | |
| return { | |
| x: absPosition.x + Math.round(element.bounds.width/2), | |
| y: absPosition.y + Math.round(element.bounds.height/2) | |
| }; | |
| } | |
| // Function to find all relations between two elements | |
| function getRelationsBetween(elem1, elem2) { | |
| return $(elem1).rels().filter(function(r) { | |
| return r.source.id === elem2.id || r.target.id === elem2.id; | |
| }); | |
| } | |
| // Function to determine the layout between two elements | |
| function determineLayout(e1AbsPos, e1, e2AbsPos, e2) { | |
| // Check for horizontal alignment (vertical overlap) | |
| if ((e1AbsPos.y >= e2AbsPos.y && e1AbsPos.y <= e2AbsPos.y + e2.bounds.height) | |
| || (e2AbsPos.y >= e1AbsPos.y && e2AbsPos.y <= e1AbsPos.y + e1.bounds.height)) { | |
| return 'horizontal'; | |
| } | |
| // Check for vertical alignment (horizontal overlap) | |
| else if((e1AbsPos.x >= e2AbsPos.x && e1AbsPos.x <= e2AbsPos.x + e2.bounds.width) | |
| || (e2AbsPos.x >= e1AbsPos.x && e2AbsPos.x <= e1AbsPos.x + e1.bounds.width)) { | |
| return 'vertical'; | |
| } | |
| else { | |
| return 'angled'; | |
| } | |
| } | |
| // Function to distribute relations in horizontal layout | |
| function distributeHorizontal(e1, e1AbsPos, e1Center, e2, e2AbsPos, e2Center, rels) { | |
| var verticalMaxStep = 20; | |
| // Calculate X positions for bendpoints (same for all relations) | |
| var startX, endX; | |
| if(e1AbsPos.x <= e2AbsPos.x) { | |
| startX = e2AbsPos.x - (e1AbsPos.x + e1.bounds.width) + e1.bounds.width/2; | |
| endX = - (e2AbsPos.x - (e1AbsPos.x + e1.bounds.width) + e2.bounds.width/2); | |
| } else { | |
| startX = e1AbsPos.x - (e2AbsPos.x + e2.bounds.width) + e2.bounds.width/2; | |
| endX = - (e1AbsPos.x - (e2AbsPos.x + e2.bounds.width) + e1.bounds.width/2); | |
| } | |
| // Determine vertical intersection | |
| var minAbsY = Math.max(e1AbsPos.y, e2AbsPos.y); | |
| var maxAbsY = Math.min((e1AbsPos.y + e1.bounds.height), (e2AbsPos.y + e2.bounds.height)); | |
| var sharedHeight = maxAbsY - minAbsY; | |
| // Calculate spacing between relations | |
| var step = Math.round(Math.min(sharedHeight/(rels.size()+1), verticalMaxStep)); | |
| var currentStep = Math.round(- step * (rels.size()-1)/2); | |
| // Apply bendpoints to each relation | |
| rels.each(function (r) { | |
| r.deleteAllBendpoints(); | |
| var startY, endY; | |
| if (r.source.id === e1.id) { | |
| startY = minAbsY + sharedHeight/2 - e1Center.y + currentStep; | |
| endY = minAbsY + sharedHeight/2 - e2Center.y + currentStep; | |
| } else { | |
| startY = minAbsY + sharedHeight/2 - e2Center.y + currentStep; | |
| endY = minAbsY + sharedHeight/2 - e1Center.y + currentStep; | |
| } | |
| r.addRelativeBendpoint({ startX:startX, startY:startY, endX:endX, endY:endY }, 0); | |
| currentStep += step; | |
| }); | |
| } | |
| // Function to distribute relations in vertical layout | |
| function distributeVertical(e1, e1AbsPos, e1Center, e2, e2AbsPos, e2Center, rels) { | |
| var verticalMaxStep = 20; | |
| var horizontalMaxStep = 30; | |
| // Determine horizontal intersection | |
| var minAbsX = Math.max(e1AbsPos.x, e2AbsPos.x); | |
| var maxAbsX = Math.min((e1AbsPos.x + e1.bounds.width), (e2AbsPos.x + e2.bounds.width)); | |
| var sharedWidth = maxAbsX - minAbsX; | |
| // Calculate horizontal spacing | |
| var stepX = Math.round(Math.min(sharedWidth/(rels.size()+1), horizontalMaxStep)); | |
| var currentStepX = Math.round(- stepX * (rels.size()-1)/2); | |
| // Calculate vertical spacing | |
| var minAbsY = Math.min((e1AbsPos.y + e1.bounds.height), (e2AbsPos.y + e2.bounds.height)); | |
| var maxAbsY = Math.max(e1AbsPos.y, e2AbsPos.y); | |
| var relationDistance = maxAbsY - minAbsY; | |
| var stepY = Math.round(Math.min(relationDistance/(rels.size()+1), verticalMaxStep)); | |
| var currentStepY = Math.round(- stepY * (rels.size()-1)/2); | |
| // Apply bendpoints to each relation | |
| rels.each(function (r) { | |
| r.deleteAllBendpoints(); | |
| var startX, endX; | |
| if (r.source.id === e1.id) { | |
| startX = minAbsX + sharedWidth/2 - e1Center.x + currentStepX; | |
| endX = minAbsX + sharedWidth/2 - e2Center.x + currentStepX; | |
| } else { | |
| startX = minAbsX + sharedWidth/2 - e2Center.x + currentStepX; | |
| endX = minAbsX + sharedWidth/2 - e1Center.x + currentStepX; | |
| } | |
| var startY, endY; | |
| if(e1AbsPos.y <= e2AbsPos.y) { | |
| startY = e2AbsPos.y - (e1AbsPos.y + e1.bounds.height) + e1.bounds.height/2 + currentStepY; | |
| endY = - (e2AbsPos.y - (e1AbsPos.y + e1.bounds.height) + e2.bounds.height/2) + currentStepY; | |
| } else { | |
| startY = e1AbsPos.y - (e2AbsPos.y + e2.bounds.height) + e2.bounds.height/2 + currentStepY; | |
| endY = - (e1AbsPos.y - (e2AbsPos.y + e2.bounds.height) + e1.bounds.height/2) + currentStepY; | |
| } | |
| r.addRelativeBendpoint({ startX:startX, startY:startY, endX:endX, endY:endY }, 0); | |
| currentStepX += stepX; | |
| currentStepY += stepY; | |
| }); | |
| } | |
| // Process all pairs of elements | |
| var processedPairs = 0; | |
| var angledPairs = []; | |
| elements.each(function(e1) { | |
| var e1AbsPos = getAbsolutePosition(e1); | |
| var e1Center = getCenterPosition(e1, e1AbsPos); | |
| elements.each(function(e2) { | |
| // Avoid processing the same pair twice | |
| if(e1.id >= e2.id) return; | |
| // Search for relations between e1 and e2 | |
| var rels = getRelationsBetween(e1, e2); | |
| if(!rels.size()) return; | |
| var e2AbsPos = getAbsolutePosition(e2); | |
| var e2Center = getCenterPosition(e2, e2AbsPos); | |
| // Determine the appropriate layout | |
| var layout = determineLayout(e1AbsPos, e1, e2AbsPos, e2); | |
| if (layout === 'horizontal') { | |
| distributeHorizontal(e1, e1AbsPos, e1Center, e2, e2AbsPos, e2Center, rels); | |
| processedPairs++; | |
| } else if(layout === 'vertical') { | |
| distributeVertical(e1, e1AbsPos, e1Center, e2, e2AbsPos, e2Center, rels); | |
| processedPairs++; | |
| } else { | |
| angledPairs.push({e1: e1.name, e2: e2.name, count: rels.size()}); | |
| } | |
| }); | |
| }); | |
| // Display a summary | |
| var message = processedPairs + " element pair(s) processed"; | |
| if(angledPairs.length > 0) { | |
| message += "\n\nWarning: " + angledPairs.length + " pair(s) skipped (no common surface):"; | |
| angledPairs.forEach(function(pair) { | |
| message += "\n- " + pair.e1 + " ↔ " + pair.e2 + " (" + pair.count + " relation(s))"; | |
| }); | |
| } | |
| if(processedPairs === 0 && angledPairs.length === 0) { | |
| window.alert("No relations found between selected elements"); | |
| } else { | |
| console.log(message); | |
| if(angledPairs.length > 0) { | |
| window.alert(message); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment