use of org.eclipse.elk.core.util.IGraphElementVisitor in project elk by eclipse.
the class DiagramLayoutEngine method layout.
/**
* Perform layout on the given layout graph mapping. If zero or one layout configurator is
* passed, the layout engine is executed exactly once. If multiple layout configurators are
* passed, the layout engine is executed accordingly often, but the resulting layout is applied
* only once. This is useful for composition of multiple algorithms that process only parts of
* the graph. Layout listeners are notified before and after the layout has been computed.
*
* @param mapping
* a mapping for the layout graph
* @param progressMonitor
* a progress monitor to which progress of the layout algorithm is reported
* @param params
* layout parameters
* @return a status indicating success or failure
*/
public IStatus layout(final LayoutMapping mapping, final IElkProgressMonitor progressMonitor, final Parameters params) {
mapping.setProperty(MAPPING_CONNECTOR, connector);
handleAncestors(mapping, params);
LinkedList<IGraphElementVisitor> visitors = new LinkedList<IGraphElementVisitor>();
visitors.add(algorithmResolver);
// Set up graph validators
if (params.getGlobalSettings().getProperty(CoreOptions.VALIDATE_OPTIONS)) {
visitors.add(layoutOptionValidatorProvider.get());
}
if (params.getGlobalSettings().getProperty(CoreOptions.VALIDATE_GRAPH)) {
visitors.add(graphValidatorProvider.get());
}
// Notify listeners of the to-be-executed layout
LayoutConnectorsService.getInstance().fireLayoutAboutToStart(mapping, progressMonitor);
IStatus status = null;
if (params.configurators.isEmpty()) {
// Perform layout without any extra configuration
IGraphElementVisitor[] visitorsArray = visitors.toArray(new IGraphElementVisitor[visitors.size()]);
status = layout(mapping, progressMonitor, visitorsArray);
} else if (params.configurators.size() == 1) {
// Perform layout once with an extra configuration
visitors.addFirst(params.configurators.get(0));
IGraphElementVisitor[] visitorsArray = visitors.toArray(new IGraphElementVisitor[visitors.size()]);
status = layout(mapping, progressMonitor, visitorsArray);
} else {
// Perform layout multiple times with different configurations
progressMonitor.begin("Diagram layout engine", params.configurators.size());
ListIterator<IGraphElementVisitor> configIter = params.configurators.listIterator();
while (configIter.hasNext()) {
visitors.addFirst(configIter.next());
IGraphElementVisitor[] visitorsArray = visitors.toArray(new IGraphElementVisitor[visitors.size()]);
status = layout(mapping, progressMonitor, visitorsArray);
if (!status.isOK()) {
break;
}
visitors.removeFirst();
// If an additional layout configurator is attached to the graph, consider it in the future
LayoutConfigurator addConfig = mapping.getLayoutGraph().getProperty(LayoutConfigurator.ADD_LAYOUT_CONFIG);
if (addConfig != null) {
ListIterator<IGraphElementVisitor> configIter2 = params.configurators.listIterator(configIter.nextIndex());
while (configIter2.hasNext()) {
IGraphElementVisitor c = configIter2.next();
if (c instanceof LayoutConfigurator) {
((LayoutConfigurator) c).overrideWith(addConfig);
}
}
}
}
progressMonitor.done();
// Log the final result to be displayed in our debug views
progressMonitor.logGraph(mapping.getLayoutGraph(), "Result");
}
mapping.setProperty(MAPPING_STATUS, status);
// Notify listeners of the executed layout
LayoutConnectorsService.getInstance().fireLayoutDone(mapping, progressMonitor);
return status;
}
use of org.eclipse.elk.core.util.IGraphElementVisitor in project elk by eclipse.
the class DiagramLayoutEngine method handleAncestors.
/**
* Handle the ancestors of the parent element if {@link CoreOptions#LAYOUT_ANCESTORS} is set.
* For every ancestor node of the parent element (i.e. {@link LayoutMapping#getParentElement()}),
* all containing elements that are not ancestors are excluded from layout.
*
* @param mapping
* a mapping for the layout graph
* @param params
* layout parameters
*/
protected void handleAncestors(final LayoutMapping mapping, final Parameters params) {
boolean layoutAncestors = params.getGlobalSettings().getProperty(CoreOptions.LAYOUT_ANCESTORS);
if (layoutAncestors) {
// Mark all parallel areas for exclusion from layout
ElkGraphElement graphElem = mapping.getGraphMap().inverse().get(mapping.getParentElement());
if (graphElem instanceof ElkNode && ((ElkNode) graphElem).getParent() != null) {
if (params.configurators.isEmpty()) {
params.configurators.add(new LayoutConfigurator());
}
ElkNode node = (ElkNode) graphElem;
do {
ElkNode parent = node.getParent();
for (ElkNode child : parent.getChildren()) {
if (child != node) {
for (IGraphElementVisitor c : params.configurators) {
if (c instanceof LayoutConfigurator) {
IPropertyHolder childConfig = ((LayoutConfigurator) c).configure(child);
// Do not layout the content of the child node
childConfig.setProperty(CoreOptions.NO_LAYOUT, true);
// Do not change the size of the child node
childConfig.setProperty(CoreOptions.NODE_SIZE_CONSTRAINTS, SizeConstraint.fixed());
// Do not move the ports of the child node
childConfig.setProperty(CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_POS);
}
}
}
}
node = parent;
} while (node.getParent() != null);
}
}
}
use of org.eclipse.elk.core.util.IGraphElementVisitor in project elk by eclipse.
the class DiagramLayoutEngine method addDiagramConfig.
/**
* Create a diagram layout configuration and add it to the setup.
*/
protected void addDiagramConfig(final Parameters params, final LayoutMapping layoutMapping) {
LayoutConfigurator diagramConfig = configManager.createConfigurator(layoutMapping);
if (params.configurators.isEmpty()) {
params.addLayoutRun(diagramConfig);
} else {
ListIterator<IGraphElementVisitor> configIter = params.configurators.listIterator();
while (configIter.hasNext()) {
boolean isFirstConfig = !configIter.hasPrevious();
IGraphElementVisitor setupConfig = configIter.next();
if (setupConfig instanceof LayoutConfigurator) {
LayoutConfigurator layoutConfigurator = (LayoutConfigurator) setupConfig;
if (params.overrideDiagramConfig) {
if (isFirstConfig || layoutConfigurator.isClearLayout()) {
LayoutConfigurator newConfig;
if (configIter.hasNext()) {
newConfig = new LayoutConfigurator().overrideWith(diagramConfig);
} else {
newConfig = diagramConfig;
}
configIter.set(newConfig.overrideWith(layoutConfigurator));
}
} else {
layoutConfigurator.overrideWith(diagramConfig);
}
}
}
}
}
use of org.eclipse.elk.core.util.IGraphElementVisitor in project osate2 by osate.
the class DiagramElementLayoutUtil method applyProperties.
/**
* Sets the ELK properties of elements in the specified layout mapping based on the layout options.
* @param layoutMapping
*/
private static void applyProperties(final DiagramNode rootDiagramNode, final LayoutMapping layoutMapping) {
// Set the minimum node size based on the ports and their assigned sides.
final IGraphElementVisitor minNodeSizeVisitor = element -> {
if (element instanceof ElkNode) {
final ElkNode n = (ElkNode) element;
final double maxLabelWidth = n.getLabels().stream().mapToDouble(l -> l.getWidth()).max().orElse(0.0);
final double labelHeightSum = n.getLabels().stream().mapToDouble(l -> l.getHeight()).sum();
// Determine max width for ports on the left and right sides
final double maxLeftPortWidth = n.getPorts().stream().filter(p -> p.getProperty(CoreOptions.PORT_SIDE) == PortSide.WEST).mapToDouble(p -> p.getWidth()).max().orElse(0.0);
final double maxRightPortWidth = n.getPorts().stream().filter(p -> p.getProperty(CoreOptions.PORT_SIDE) == PortSide.EAST).mapToDouble(p -> p.getWidth()).max().orElse(0.0);
final DiagramNode dn = (DiagramNode) layoutMapping.getGraphMap().get(n);
double minWidth = 0;
if (n.getProperty(CoreOptions.NODE_LABELS_PLACEMENT).contains(NodeLabelPlacement.H_CENTER)) {
// Ensure the minimum width is such that the label can be centered without overlapping with ports.
// This happens because ports are inside the node due to the PORT_BORDER_OFFSET and ELK centers the labels in the remaining space.
final double widthForPorts = 2 * Math.max(maxLeftPortWidth, maxRightPortWidth);
minWidth = Math.max(40, maxLabelWidth + widthForPorts + PORT_WIDTH_PADDING);
} else {
final double widthForPorts = maxLeftPortWidth + maxRightPortWidth + PORT_WIDTH_PADDING;
minWidth = Math.max(40, Math.max(maxLabelWidth, widthForPorts));
}
double minHeight = Math.max(35, labelHeightSum);
if (dn instanceof DiagramElement) {
final DiagramElement de = ((DiagramElement) dn);
// Special min height handling for initial modes
final Graphic graphic = de.getGraphic();
if (graphic instanceof AgeShape && !((AgeShape) graphic).isResizeable() && de.hasSize()) {
final Dimension dim = de.getSize();
minHeight = dim.height;
minWidth = dim.width;
// Adjust size constraints for fixed sized shapes which do not have contents.
if (n.getChildren().size() == 0 || n.getLabels().size() == 0 && n.getPorts().size() == 0) {
final EnumSet<SizeConstraint> nodeSizeConstraints = EnumSet.of(SizeConstraint.MINIMUM_SIZE);
n.setProperty(CoreOptions.NODE_SIZE_CONSTRAINTS, nodeSizeConstraints);
}
}
if (graphic instanceof ModeGraphic && ((ModeGraphic) graphic).isInitialMode) {
minHeight += ModeGraphic.INITIAL_MODE_AREA_HEIGHT;
}
// Special min size handling for elements shown as image
final Style style = de.getStyle();
if (style != null && Boolean.TRUE.equals(style.getShowAsImage())) {
final Dimension dim = ((DiagramElement) dn).getSize();
minHeight = dim.height;
minWidth = dim.width;
}
}
// Increase min width and min height for top level nodes.
if (dn != null && dn.getParent() instanceof AgeDiagram) {
minWidth = Math.max(minWidth, 200);
minHeight = Math.max(minHeight, 100);
}
n.setProperty(CoreOptions.NODE_SIZE_MINIMUM, new KVector(minWidth, minHeight));
}
};
ElkUtil.applyVisitors(layoutMapping.getLayoutGraph(), minNodeSizeVisitor);
// If the top level element has a size set, don't shrink it.
if (rootDiagramNode instanceof DiagramElement) {
final DiagramElement rootDiagramElement = (DiagramElement) rootDiagramNode;
final ElkGraphElement rootGraphElement = layoutMapping.getGraphMap().inverse().get(rootDiagramNode);
if (rootGraphElement != null && rootDiagramElement.hasSize() && DiagramElementPredicates.isResizeable(rootDiagramElement)) {
final KVector minSize = rootGraphElement.getProperty(CoreOptions.NODE_SIZE_MINIMUM);
final double newMinWidth = Math.max(rootDiagramElement.getWidth(), minSize == null ? 0.0 : minSize.x);
final double newMinHeight = Math.max(rootDiagramElement.getHeight(), minSize == null ? 0.0 : minSize.y);
rootGraphElement.setProperty(CoreOptions.NODE_SIZE_MINIMUM, new KVector(newMinWidth, newMinHeight));
}
}
}
Aggregations