Skip to content

Commit

Permalink
diagrams: Improved stability of positioning edges connected to reactions
Browse files Browse the repository at this point in the history
  • Loading branch information
a-sr committed Mar 14, 2022
1 parent 5be0825 commit fe17be8
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@
***************/
package org.lflang.diagram.synthesis.postprocessor;

import java.util.stream.Collectors;

import org.eclipse.elk.core.options.CoreOptions;
import org.eclipse.elk.core.options.PortSide;
import org.eclipse.elk.graph.properties.Property;
import org.eclipse.xtext.xbase.lib.Extension;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.eclipse.xtext.xbase.lib.Pair;
import org.lflang.diagram.synthesis.styles.LinguaFrancaShapeExtensions;

import de.cau.cs.kieler.klighd.IStyleModifier;
import de.cau.cs.kieler.klighd.IViewer;
import de.cau.cs.kieler.klighd.internal.ILayoutRecorder;
Expand All @@ -33,16 +43,8 @@
import de.cau.cs.kieler.klighd.kgraph.KNode;
import de.cau.cs.kieler.klighd.kgraph.KPoint;
import de.cau.cs.kieler.klighd.kgraph.KPort;
import java.util.List;
import java.util.Map;
import org.eclipse.elk.core.options.CoreOptions;
import org.eclipse.elk.core.options.PortSide;
import org.eclipse.elk.graph.properties.IProperty;
import org.eclipse.elk.graph.properties.Property;
import org.eclipse.xtext.xbase.lib.Extension;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.eclipse.xtext.xbase.lib.Pair;
import org.lflang.diagram.synthesis.styles.LinguaFrancaShapeExtensions;
import de.cau.cs.kieler.klighd.krendering.KRendering;
import de.cau.cs.kieler.klighd.krendering.KRenderingFactory;

/**
* Adjusts the port position of reactions node AFTER layout, to allow free port order but also adapt (snuggle) to pointy shape of reaction node.
Expand All @@ -57,6 +59,19 @@ public class ReactionPortAdjustment implements IStyleModifier {

@Extension
private KGraphFactory _kGraphFactory = KGraphFactory.eINSTANCE;
private static KRenderingFactory _kRenderingFactory = KRenderingFactory.eINSTANCE;

/**
* Register this modifier on a reaction rendering.
*/
public static void apply(KNode node, KRendering rendering) {
// Add modifier that fixes port positions such that edges are properly attached to the shape
var invisible = _kRenderingFactory.createKInvisibility();
invisible.setInvisible(false); // make it ineffective (just for purpose of holding modifier)
invisible.setModifierId(ReactionPortAdjustment.ID); // Add modifier to receive callback after layout
rendering.getStyles().add(invisible);
node.setProperty(PROCESSED, false);
}

@Override
public boolean modify(IStyleModifier.StyleModificationContext context) {
Expand All @@ -72,54 +87,40 @@ public boolean modify(IStyleModifier.StyleModificationContext context) {
}

// Get viewer (this is a bit brittle because it fetches the viewer from some internal property)
Map.Entry<IProperty<?>, Object> first = IterableExtensions.findFirst(
parent.getAllProperties().entrySet(),
it -> {
return it.getKey().getId().equals("de.cau.cs.kieler.klighd.viewer") ||
it.getKey().getId().equals("klighd.layout.viewer");
}
);
Object viewer = first != null ? first.getValue() : null;

Object viewer =
parent.getAllProperties().entrySet().stream().filter(entry ->
entry.getKey().getId().equals("de.cau.cs.kieler.klighd.viewer")
|| entry.getKey().getId().equals("klighd.layout.viewer"))
.findAny().map(entry -> entry.getValue()).orElse(null);

ILayoutRecorder recorder = null;
if (viewer instanceof IViewer) {
recorder = ((IViewer) viewer).getViewContext().getLayoutRecorder();
}


if (!knode.getPorts().isEmpty()) {
if (IterableExtensions.head(knode.getPorts()).getYpos() != 0 &&
!knode.getProperty(ReactionPortAdjustment.PROCESSED)) { // Only adjust if layout is already applied
// important for incremental update animation
// Only adjust if layout is already applied important for incremental update animation
!knode.getProperty(ReactionPortAdjustment.PROCESSED)) {
if (recorder != null) {
recorder.startRecording();
}

List<KPort> in = IterableExtensions.toList(
IterableExtensions.sortBy(
IterableExtensions.filter(
knode.getPorts(),
it -> {
return it.getProperty(CoreOptions.PORT_SIDE) == PortSide.WEST &&
!it.hasProperty(CoreOptions.PORT_BORDER_OFFSET);
}),
it -> { return it.getYpos(); })
);
var in = knode.getPorts().stream().filter(p ->
p.getProperty(CoreOptions.PORT_SIDE) == PortSide.WEST).sorted((p1, p2) ->
Float.compare(p1.getYpos(), p2.getYpos())).collect(Collectors.toList());

List<KPort> out = IterableExtensions.toList(
IterableExtensions.sortBy(
IterableExtensions.filter(
knode.getPorts(),
it -> {
return it.getProperty(CoreOptions.PORT_SIDE) == PortSide.EAST &&
!it.hasProperty(CoreOptions.PORT_BORDER_OFFSET);
}),
it -> { return it.getYpos(); })
);
var out = knode.getPorts().stream().filter(p ->
p.getProperty(CoreOptions.PORT_SIDE) == PortSide.EAST).sorted((p1, p2) ->
Float.compare(p1.getYpos(), p2.getYpos())).collect(Collectors.toList());

// Adjust
adjustPositions(IterableExtensions.indexed(in), in.size(), true);
adjustPositions(IterableExtensions.indexed(out), out.size(), false);
if (in.stream().anyMatch(p -> !p.hasProperty(CoreOptions.PORT_BORDER_OFFSET))) {
adjustPositions(IterableExtensions.indexed(in), in.size(), true);
}
if (out.stream().anyMatch(p -> !p.hasProperty(CoreOptions.PORT_BORDER_OFFSET))) {
adjustPositions(IterableExtensions.indexed(out), out.size(), false);
}
knode.setProperty(ReactionPortAdjustment.PROCESSED, true);

if (recorder!=null) {
Expand Down Expand Up @@ -166,6 +167,9 @@ public void adjustPositions(Iterable<Pair<Integer, KPort>> indexedPorts, int cou
edge.setSourcePoint(adjustedKPoint(edge.getSourcePoint(), offset));
}
}

// Save for future layout
port.setProperty(CoreOptions.PORT_BORDER_OFFSET, (double) (input ? -offset : offset));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,9 @@ public KPolygon addReactionFigure(KNode node, ReactionInstance reaction) {
_kRenderingExtensions.createKPosition(LEFT, REACTION_POINTINESS, 0, BOTTOM, 0, 0.5f)
)
);
IterableExtensions.head(baseShape.getStyles()).setModifierId(ReactionPortAdjustment.ID);

// For a shape like this, ports can only positioned correctly after the layout.
ReactionPortAdjustment.apply(node, baseShape);

KRectangle contentContainer = _kContainerRenderingExtensions.addRectangle(baseShape);
associateWith(contentContainer, reaction);
Expand Down

0 comments on commit fe17be8

Please # to comment.