Skip to content

Instantly share code, notes, and snippets.

@romualdrichard
Last active January 29, 2026 14:35
Show Gist options
  • Select an option

  • Save romualdrichard/0b10efcc5fd72196ca2d505f3d5e9905 to your computer and use it in GitHub Desktop.

Select an option

Save romualdrichard/0b10efcc5fd72196ca2d505f3d5e9905 to your computer and use it in GitHub Desktop.
#jarchi
/*
* 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