Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjustable port sides for reactors #1807

Merged
merged 5 commits into from
Jun 10, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions org.lflang/src/org/lflang/AttributeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,11 @@ public static String getLabel(EObject node) {
public static String getIconPath(EObject node) {
return getAttributeValue(node, "icon");
}

/** Return the declared side of the port, as given by the @side annotation. */
public static String getPortSide(EObject node) {
return getAttributeValue(node, "side");
}

/**
* Return the {@code @enclave} attribute annotated on the given node.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
import org.lflang.diagram.synthesis.action.MemorizingExpandCollapseAction;
import org.lflang.diagram.synthesis.action.ShowCycleAction;
import org.lflang.diagram.synthesis.postprocessor.ReactionPortAdjustment;
import org.lflang.diagram.synthesis.postprocessor.ReactorPortAdjustment;
import org.lflang.diagram.synthesis.styles.LinguaFrancaShapeExtensions;
import org.lflang.diagram.synthesis.styles.LinguaFrancaStyleExtensions;
import org.lflang.diagram.synthesis.styles.ReactorFigureComponents;
Expand Down Expand Up @@ -163,6 +164,8 @@ public class LinguaFrancaSynthesis extends AbstractDiagramSynthesis<Model> {
"org.lflang.linguafranca.diagram.synthesis.reactor.recursive.instantiation", false);
public static final Property<Boolean> REACTOR_HAS_BANK_PORT_OFFSET =
new Property<>("org.lflang.linguafranca.diagram.synthesis.reactor.bank.offset", false);
public static final Property<Boolean> REACTOR_MULTIPORT =
new Property<>("org.lflang.linguafranca.diagram.synthesis.reactor.multiport", false);
public static final Property<Boolean> REACTOR_INPUT =
new Property<>("org.lflang.linguafranca.diagram.synthesis.reactor.input", false);
public static final Property<Boolean> REACTOR_OUTPUT =
Expand Down Expand Up @@ -244,6 +247,8 @@ public class LinguaFrancaSynthesis extends AbstractDiagramSynthesis<Model> {
SynthesisOption.<Integer>createRangeOption("Reactor Parameter/Variable Columns", 1, 10, 1)
.setCategory(APPEARANCE);

public static final SynthesisOption FIXED_PORT_SIDE =
SynthesisOption.createCheckOption("Fixed Port Sides", true).setCategory(LAYOUT);
public static final SynthesisOption SPACING =
SynthesisOption.<Integer>createRangeOption("Spacing (%)", 0, 150, 5, 75).setCategory(LAYOUT);

Expand Down Expand Up @@ -280,6 +285,7 @@ public List<SynthesisOption> getDisplayedSynthesisOptions() {
SHOW_STATE_VARIABLES,
REACTOR_BODY_TABLE_COLS,
LAYOUT,
FIXED_PORT_SIDE,
LayoutPostProcessing.MODEL_ORDER,
SPACING);
}
Expand Down Expand Up @@ -481,6 +487,11 @@ private Collection<KNode> createReactorNode(
_kRenderingExtensions.addDoubleClickAction(figure, MemorizingExpandCollapseAction.ID);
}
_reactorIcons.handleIcon(comps.getReactor(), reactor, false);

if (!getBooleanValue(FIXED_PORT_SIDE)) {
// Port figures will need post-processing to fix IO indication if portside is not fixed
ReactorPortAdjustment.apply(node, comps.getFigures());
}

if (getBooleanValue(SHOW_HYPERLINKS)) {
// Collapse button
Expand Down Expand Up @@ -607,6 +618,11 @@ private Collection<KNode> createReactorNode(
}
}
_reactorIcons.handleIcon(comps.getReactor(), reactor, true);

if (!getBooleanValue(FIXED_PORT_SIDE)) {
// Port figures will need post-processing to fix IO indication if portside is not fixed
ReactorPortAdjustment.apply(node, comps.getFigures());
}

if (getBooleanValue(SHOW_HYPERLINKS)) {
// Expand button
Expand Down Expand Up @@ -728,16 +744,24 @@ public KNode configureReactorNodeLayout(KNode node, boolean main) {
node,
CoreOptions.NODE_SIZE_CONSTRAINTS,
EnumSet.of(SizeConstraint.MINIMUM_SIZE, SizeConstraint.PORTS));

// Allows to freely shuffle ports on each side
setLayoutOption(node, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE);
// Adjust port label spacing to be closer to edge but not overlap with port figure
// TODO: Add PortLabelPlacement.NEXT_TO_PORT_IF_POSSIBLE back into the configuration, as soon as
// ELK provides a fix for LF issue #1273


if (getBooleanValue(FIXED_PORT_SIDE)) {
// Allows to freely shuffle ports on each side
setLayoutOption(node, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE);
} else {
// Ports are no longer fixed based on input or output
setLayoutOption(node, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FREE);
}

setLayoutOption(
node,
CoreOptions.PORT_LABELS_PLACEMENT,
EnumSet.of(PortLabelPlacement.ALWAYS_OTHER_SAME_SIDE, PortLabelPlacement.OUTSIDE));
// TODO: Add PortLabelPlacement.NEXT_TO_PORT_IF_POSSIBLE back into the configuration, as soon as
// ELK provides a fix for LF issue #1273

// Adjust port label spacing to be closer to edge but not overlap with port figure
setLayoutOption(node, CoreOptions.SPACING_LABEL_PORT_HORIZONTAL, 2.0);
setLayoutOption(node, CoreOptions.SPACING_LABEL_PORT_VERTICAL, -3.0);

Expand Down Expand Up @@ -1034,8 +1058,8 @@ private Collection<KNode> transformReactorNetwork(
int outputSize = reaction.effects.size();
if (!getBooleanValue(REACTIONS_USE_HYPEREDGES) && (inputSize > 1 || outputSize > 1)) {
// If this node will have more than one input/output port, the port positions must be
// adjusted to the
// pointy shape. However, this is only possible after the layout.
// adjusted to the pointy shape.
// However, this is only possible after the layout.
ReactionPortAdjustment.apply(node, figure);
}

Expand Down Expand Up @@ -1577,27 +1601,32 @@ private KEdge connect(KEdge edge, KPort src, KPort dst) {
}

/** Translate an input/output into a port. */
private KPort addIOPort(
KNode node, PortInstance lfPort, boolean input, boolean multiport, boolean bank) {
private KPort addIOPort(KNode node, PortInstance lfPort,
boolean input, boolean multiport, boolean bank) {
KPort port = _kPortExtensions.createPort();
node.getPorts().add(port);
associateWith(port, lfPort.getDefinition());
NamedInstanceUtil.linkInstance(port, lfPort);
_kPortExtensions.setPortSize(port, 6, 6);

if (input) {
// multiports are smaller by an offset at the right, hence compensate in inputs
double offset = multiport ? -3.4 : -3.3;
setLayoutOption(port, CoreOptions.PORT_SIDE, PortSide.WEST);
setLayoutOption(port, CoreOptions.PORT_BORDER_OFFSET, offset);
} else {
double offset = multiport ? -2.6 : -3.3; // multiports are smaller
offset =
bank
? offset - LinguaFrancaShapeExtensions.BANK_FIGURE_X_OFFSET_SUM
: offset; // compensate bank figure width
setLayoutOption(port, CoreOptions.PORT_SIDE, PortSide.EAST);
setLayoutOption(port, CoreOptions.PORT_BORDER_OFFSET, offset);

var side = input ? PortSide.WEST : PortSide.EAST;
var userSideAttr = AttributeUtils.getPortSide(lfPort.getDefinition());
if (userSideAttr != null) {
try {
var userSide = PortSide.valueOf(userSideAttr.toUpperCase());
if (userSide != null) {
side = userSide;
}
} catch(Exception e) {
// ignore and use default
}
}
double offset = getReactorPortOffset(side == PortSide.WEST, multiport, bank);
setLayoutOption(port, CoreOptions.PORT_SIDE, side);
setLayoutOption(port, CoreOptions.PORT_BORDER_OFFSET, offset);

if (multiport) {
node.setProperty(REACTOR_MULTIPORT, true);
}

if (bank && !node.getProperty(REACTOR_HAS_BANK_PORT_OFFSET)) { // compensate bank figure height
Expand All @@ -1608,7 +1637,9 @@ private KPort addIOPort(
node.setProperty(REACTOR_HAS_BANK_PORT_OFFSET, true); // only once
}

_linguaFrancaShapeExtensions.addTrianglePort(port, multiport);
// If fixed port sides are active and the port is put on the opposite side, reverse it
var reverse = getBooleanValue(FIXED_PORT_SIDE) && input != (side == PortSide.WEST);
_linguaFrancaShapeExtensions.addTrianglePort(port, multiport, reverse);

String label = lfPort.getName();
if (!getBooleanValue(SHOW_PORT_NAMES)) {
Expand All @@ -1622,6 +1653,21 @@ private KPort addIOPort(
associateWith(_kLabelExtensions.addOutsidePortLabel(port, label, 8), lfPort.getDefinition());
return port;
}

public static double getReactorPortOffset(boolean sideLeft, boolean multiport, boolean bank) {
var offset = -3.3;

if (multiport) {
offset = sideLeft ? -3.4 : -2.6;
}

if (bank && !sideLeft) {
// compensate bank figure width
offset -= LinguaFrancaShapeExtensions.BANK_FIGURE_X_OFFSET_SUM;
}

return offset;
}

private KPort addInvisiblePort(KNode node) {
KPort port = _kPortExtensions.createPort();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package org.lflang.diagram.synthesis;

import de.cau.cs.kieler.klighd.IKlighdStartupHook;
import de.cau.cs.kieler.klighd.KlighdDataManager;
import org.lflang.diagram.synthesis.action.CollapseAllReactorsAction;
import org.lflang.diagram.synthesis.action.ExpandAllReactorsAction;
import org.lflang.diagram.synthesis.action.FilterCycleAction;
import org.lflang.diagram.synthesis.action.MemorizingExpandCollapseAction;
import org.lflang.diagram.synthesis.action.ShowCycleAction;
import org.lflang.diagram.synthesis.postprocessor.ReactionPortAdjustment;
import org.lflang.diagram.synthesis.postprocessor.ReactorPortAdjustment;
import org.lflang.diagram.synthesis.styles.LinguaFrancaShapeExtensions;
import org.lflang.diagram.synthesis.styles.LinguaFrancaStyleExtensions;
import org.lflang.diagram.synthesis.util.NamedInstanceUtil;

import de.cau.cs.kieler.klighd.IKlighdStartupHook;
import de.cau.cs.kieler.klighd.KlighdDataManager;

/**
* Registration of all diagram synthesis related classes in Klighd.
*
Expand All @@ -35,6 +37,7 @@ public void execute() {

// Style Mod
reg.registerStyleModifier(ReactionPortAdjustment.ID, new ReactionPortAdjustment());
reg.registerStyleModifier(ReactorPortAdjustment.ID, new ReactorPortAdjustment());

// Blacklist LF-specific properties that should be removed when a diagram is sent from the
// diagram server to a client.
Expand All @@ -46,8 +49,7 @@ public void execute() {
reg.registerBlacklistedProperty(ReactionPortAdjustment.PROCESSED);
reg.registerBlacklistedProperty(LinguaFrancaShapeExtensions.REACTOR_CONTENT_CONTAINER);
reg.registerBlacklistedProperty(LinguaFrancaStyleExtensions.LABEL_PARENT_BACKGROUND);
reg.registerBlacklistedProperty(
NamedInstanceUtil
.LINKED_INSTANCE); // Very important since its values can not be synthesized easily!
// Very important since its values can not be synthesized easily!
reg.registerBlacklistedProperty(NamedInstanceUtil.LINKED_INSTANCE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ public class ReactionPortAdjustment implements IStyleModifier {
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
// make it ineffective (just for purpose of holding modifier)
invisible.setInvisible(false);
// Add modifier to receive callback after layout
invisible.setModifierId(ReactionPortAdjustment.ID);
rendering.getStyles().add(invisible);
node.setProperty(PROCESSED, false);
}
Expand Down
Loading