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

Feedback #353

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
19 changes: 16 additions & 3 deletions src/main/java/nodebox/client/NetworkView.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ public class NetworkView extends ZoomableView implements PaneView, Zoom {
public static final Color DRAG_SELECTION_COLOR = new Color(255, 255, 255, 100);
public static final BasicStroke DRAG_SELECTION_STROKE = new BasicStroke(1f);
public static final BasicStroke CONNECTION_STROKE = new BasicStroke(2);

public static final BasicStroke FEEDBACK_STROKE = new BasicStroke(1.0f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER,
10.0f, new float[] {10.0f}, 0.0f);
private final NodeBoxDocument document;

private JPopupMenu networkMenu;
Expand Down Expand Up @@ -324,6 +327,10 @@ private void paintConnection(Graphics2D g, Connection connection) {
Node inputNode = findNodeWithName(connection.getInputNode());
Port inputPort = inputNode.getInput(connection.getInputPort());
g.setColor(portTypeColor(outputNode.getOutputType()));
if (connection.isFeedbackLoop())
g.setStroke(FEEDBACK_STROKE);
else
g.setStroke(CONNECTION_STROKE);
Rectangle outputRect = nodeRect(outputNode);
Rectangle inputRect = nodeRect(inputNode);
paintConnectionLine(g, outputRect.x + 4, outputRect.y + outputRect.height + 1, inputRect.x + portOffset(inputNode, inputPort) + 4, inputRect.y - 4);
Expand All @@ -335,6 +342,10 @@ private void paintCurrentConnection(Graphics2D g) {
if (connectionOutput != null) {
Rectangle outputRect = nodeRect(connectionOutput);
g.setColor(portTypeColor(connectionOutput.getOutputType()));
if (isShiftPressed)
g.setStroke(FEEDBACK_STROKE);
else
g.setStroke(CONNECTION_STROKE);
paintConnectionLine(g, outputRect.x + 4, outputRect.y + outputRect.height + 1, (int) connectionPoint.getX(), (int) connectionPoint.getY());
}
}
Expand Down Expand Up @@ -803,7 +814,8 @@ public void mousePressed(MouseEvent e) {
connectionInput = getInputPortAt(pt);
if (connectionInput != null) {
// We're over a port, but is it connected?
Connection c = getActiveNetwork().getConnection(connectionInput.node, connectionInput.port);
Connection.Type type = isShiftPressed ? Connection.Type.FEEDBACK : Connection.Type.STANDARD;
Connection c = getActiveNetwork().getConnection(connectionInput.node, connectionInput.port, type);
// Disconnect it, but start a new connection on the same node immediately.
if (c != null) {
getDocument().disconnect(c);
Expand Down Expand Up @@ -837,7 +849,8 @@ public void mouseReleased(MouseEvent e) {
if (isAltPressed)
getDocument().stopEditing();
if (connectionOutput != null && connectionInput != null) {
getDocument().connect(connectionOutput.getName(), connectionInput.node, connectionInput.port);
Connection.Type connectionType = isShiftPressed ? Connection.Type.FEEDBACK : Connection.Type.STANDARD;
getDocument().connect(connectionOutput.getName(), connectionInput.node, connectionInput.port, connectionType);
}
connectionOutput = null;
repaint();
Expand Down
5 changes: 2 additions & 3 deletions src/main/java/nodebox/client/NodeBoxDocument.java
Original file line number Diff line number Diff line change
Expand Up @@ -555,9 +555,9 @@ private void removeNodeImpl(Node node) {
* @param inputNode The input node.
* @param inputPort The input port.
*/
public void connect(String outputNode, String inputNode, String inputPort) {
public void connect(String outputNode, String inputNode, String inputPort, Connection.Type connectionType) {
addEdit("Connect");
controller.connect(activeNetworkPath, outputNode, inputNode, inputPort);
controller.connect(activeNetworkPath, outputNode, inputNode, inputPort, connectionType);

portView.updateAll();
viewerPane.updateHandle();
Expand Down Expand Up @@ -1193,7 +1193,6 @@ private void render() {
@Override
protected List<?> doInBackground() throws Exception {
List<?> results = context.renderNode(renderNetwork);
context.renderAlwaysRenderedNodes(renderNetwork);
renderResults = context.getRenderResults();
return results;
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/nodebox/client/PortView.java
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ private void rebuildInterface() {
Class widgetClass = CONTROL_MAP.get(p.getWidget());
JComponent control;
if (getDocument().isConnected(portName)) {
// todo in case of feedback mechanism (and no regular connection present) we dont want to show just
// "connected" but be able to set the initial value. Also something to trigger the initial value again.
control = new JLabel("<connected>");
control.setMinimumSize(new Dimension(10, 35));
control.setFont(Theme.SMALL_FONT);
Expand Down
39 changes: 34 additions & 5 deletions src/main/java/nodebox/node/Connection.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,44 @@
* Connections are made between ports on the nodes. The connection goes from the output port of the output node
* (there is only one output port) to an input port on the input node.
* <p/>
* This class can only store the connection between one output and one input. Some nodes, such as the merge node,
* have multiple outputs that connect to the same input. These are connected using multiple connection objects.
* This class can only store the connection between one output and one input.
*/
public class Connection {

public enum Type { STANDARD, FEEDBACK }

private final String outputNode;
private final String inputNode;
private final String inputPort;
private final Type type;

/**
* Creates a connection between the output (upstream) node and input (downstream) node.
* Creates a standard connection between the output (upstream) node and input (downstream) node.
*
* @param outputNode The name of the output (upstream) Node.
* @param inputNode The name of the input (downstream) Node.
* @param inputPort The name of the input (downstream) Port.
*/
public Connection(String outputNode, String inputNode, String inputPort) {
this(outputNode, inputNode, inputPort, Type.STANDARD);
}

/**
* Creates a connection between the output (upstream) node and input (downstream) node.
*
* @param outputNode The name of the output (upstream) Node.
* @param inputNode The name of the input (downstream) Node.
* @param inputPort The name of the input (downstream) Port.
* @param connectionType The type of connection (regular connection or feedback loop).
*/
public Connection(String outputNode, String inputNode, String inputPort, Type connectionType) {
checkNotNull(outputNode);
checkNotNull(inputNode);
checkNotNull(inputPort);
this.outputNode = outputNode;
this.inputNode = inputNode;
this.inputPort = inputPort;
this.type = connectionType;
}

public String getOutputNode() {
Expand All @@ -70,11 +85,24 @@ public String getInputPort() {
return inputPort;
}


/**
* Gets type type of connection (regular or feedback)
* @return the connection type.
*/
public Type getType() {
return type;
}

public boolean isFeedbackLoop() {
return type == Type.FEEDBACK;
}

//// Object overrides ////

@Override
public int hashCode() {
return Objects.hashCode(outputNode, inputNode, inputPort);
return Objects.hashCode(outputNode, inputNode, inputPort, type);
}

@Override
Expand All @@ -83,7 +111,8 @@ public boolean equals(Object o) {
final Connection other = (Connection) o;
return Objects.equal(outputNode, other.outputNode)
&& Objects.equal(inputNode, other.inputNode)
&& Objects.equal(inputPort, other.inputPort);
&& Objects.equal(inputPort, other.inputPort)
&& Objects.equal(type, other.type);
}

@Override
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/nodebox/node/NDBXWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,8 @@ private static void writeConnection(Document doc, Element parent, Connection con
Element connElement = doc.createElement("conn");
connElement.setAttribute("output", String.format("%s", conn.getOutputNode()));
connElement.setAttribute("input", String.format("%s.%s", conn.getInputNode(), conn.getInputPort()));
if (conn.isFeedbackLoop())
connElement.setAttribute("type", conn.getType().toString().toLowerCase());
parent.appendChild(connElement);
}

Expand Down
49 changes: 42 additions & 7 deletions src/main/java/nodebox/node/Node.java
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,14 @@ public boolean hasChildren() {
return !children.isEmpty();
}

public boolean usesChildInputForNextFrame(Node child) {
for (Connection conn : getConnections()) {
if (conn.isFeedbackLoop() && child.getName().equals(conn.getOutputNode()))
return true;
}
return false;
}

public ImmutableList<Node> getChildren() {
return children;
}
Expand Down Expand Up @@ -596,9 +604,9 @@ public Node withChildRenamed(String childName, String newName) {

for (Connection c : getConnections()) {
if (c.getInputNode().equals(childName)) {
newParent = newParent.connect(c.getOutputNode(), newName, c.getInputPort());
newParent = newParent.connect(c.getOutputNode(), newName, c.getInputPort(), c.getType());
} else if (c.getOutputNode().equals(childName)) {
newParent = newParent.connect(newName, c.getInputNode(), c.getInputPort());
newParent = newParent.connect(newName, c.getInputNode(), c.getInputPort(), c.getType());
}
}

Expand Down Expand Up @@ -1046,16 +1054,31 @@ public Node withRenderedChild(Node renderedChild) {
* @return A new Node.
*/
public Node connect(String outputNode, String inputNode, String inputPort) {
return connect(outputNode, inputNode, inputPort, Connection.Type.STANDARD);
}

/**
* Create a new node that connects the given child nodes.
*
* @param outputNode The name of the output (upstream) Node.
* @param inputNode The name of the input (downstream) Node.
* @param inputPort The name of the input (downstream) Port.
* @param connectionType The type of connection (regular connection or feedback).
* @return A new Node.
*/
public Node connect(String outputNode, String inputNode, String inputPort, Connection.Type connectionType) {
checkArgument(isNetwork(), "Node %s is not a network node.", this);
checkArgument(hasChild(outputNode), "Node %s does not have a child named %s.", this, outputNode);
checkArgument(hasChild(inputNode), "Node %s does not have a child named %s.", this, inputNode);
Node input = getChild(inputNode);
checkArgument(input.hasInput(inputPort), "Node %s does not have an input port %s.", inputNode, inputPort);
checkArgument(!hasPublishedInput(inputNode, inputPort), "Node %s has a published input for port %s of child %s.", this, inputNode, inputPort);
Connection newConnection = new Connection(outputNode, inputNode, inputPort);
Connection newConnection = new Connection(outputNode, inputNode, inputPort, connectionType);
ImmutableList.Builder<Connection> b = ImmutableList.builder();
for (Connection c : getConnections()) {
if (c.getInputNode().equals(inputNode) && c.getInputPort().equals(inputPort)) {
if (c.getInputNode().equals(inputNode)
&& c.getInputPort().equals(inputPort)
&& c.getType().equals(connectionType)) {
// There was already a connection, on this input port.
// We "disconnect" it by not including it in the new list.
} else {
Expand Down Expand Up @@ -1128,7 +1151,7 @@ public Node disconnect(String node, String portName) {
}

public Node withConnectionAdded(Connection connection) {
return connect(connection.getOutputNode(), connection.getInputNode(), connection.getInputPort());
return connect(connection.getOutputNode(), connection.getInputNode(), connection.getInputPort(), connection.getType());
}

public boolean isConnected(String node) {
Expand Down Expand Up @@ -1186,7 +1209,7 @@ public Node withChildrenAdded(Node nodesParent, Iterable<Node> nodes) {
Node outputNode = newParent.getChild(outputNodeName);
Node inputNode = newParent.getChild(inputNodeName);
Port inputPort = inputNode.getInput(c.getInputPort());
newParent = newParent.connect(outputNode.getName(), inputNode.getName(), inputPort.getName());
newParent = newParent.connect(outputNode.getName(), inputNode.getName(), inputPort.getName(), c.getType());
}
}
}
Expand All @@ -1201,9 +1224,21 @@ public Node withChildrenAdded(Node nodesParent, Iterable<Node> nodes) {
* @return the Connection object, or null if the connection could not be found.
*/
public Connection getConnection(String inputNode, String inputPort) {
return getConnection(inputNode, inputPort, Connection.Type.STANDARD);
}

/**
* Find the connection with the given inputNode and port.
*
* @param inputNode The child input node
* @param inputPort The child input port
* @param connectionType The type of connection (regular or feedback)
* @return the Connection object, or null if the connection could not be found.
*/
public Connection getConnection(String inputNode, String inputPort, Connection.Type connectionType) {
if (!isNetwork()) return null;
for (Connection c : getConnections()) {
if (c.getInputNode().equals(inputNode) && c.getInputPort().equals(inputPort))
if (c.getInputNode().equals(inputNode) && c.getInputPort().equals(inputPort) && c.getType().equals(connectionType))
return c;

}
Expand Down
72 changes: 47 additions & 25 deletions src/main/java/nodebox/node/NodeContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,6 @@ public List<?> renderNode(Node node) throws NodeRenderException {
return renderNode(node, Collections.<Port, Object>emptyMap());
}

public void renderAlwaysRenderedNodes(Node node) throws NodeRenderException {
if (!node.isNetwork()) return;
for (Node child : node.getChildren()) {
if (child.isAlwaysRendered()) {
if (!renderResults.containsKey(child))
renderNode(child);
}
}
}

public List<?> renderNode(Node node, Map<Port, ?> argumentMap) {
checkNotNull(node);
checkNotNull(functionRepository);
Expand All @@ -113,6 +103,16 @@ public List<?> renderNode(Node node, Map<Port, ?> argumentMap) {
}
List<?> results = postProcessResult(node, result);
renderResults.put(node, results);

if (node.isNetwork()) {
for (Node child : node.getChildren()) {
if (child.isAlwaysRendered() || node.usesChildInputForNextFrame(child)) {
if (!renderResults.containsKey(child))
renderChild(node, child, argumentMap);
}
}
}

return results;
}

Expand Down Expand Up @@ -189,6 +189,7 @@ public List<?> renderChild(Node network, Node child, Map<Port, ?> networkArgumen
}
}
nodeArgumentsResults.put(nodeArguments, resultsList);
renderResults.put(nodeArguments.node, resultsList);
return resultsList;
}

Expand Down Expand Up @@ -238,31 +239,52 @@ private List<?> clampResultsForPort(Port port, List<?> values) {
return b.build();
}

private Node findOutputNode(Node network, Node inputNode, Port inputPort) {
private Connection findOutputConnection(Node network, Node inputNode, Port inputPort, boolean checkFeedback) {
if (checkFeedback) {
for (Connection c : network.getConnections()) {
if (c.isFeedbackLoop() && c.getInputNode().equals(inputNode.getName()) && c.getInputPort().equals(inputPort.getName())) {
return c;
}
}
}
for (Connection c : network.getConnections()) {
if (c.getInputNode().equals(inputNode.getName()) && c.getInputPort().equals(inputPort.getName())) {
return network.getChild(c.getOutputNode());
if ((! c.isFeedbackLoop()) && c.getInputNode().equals(inputNode.getName()) && c.getInputPort().equals(inputPort.getName())) {
return c;
}
}
return null;
}

private List<?> evaluatePort(Node network, Node child, Port childPort, Map<Port, ?> networkArgumentMap) {
Node outputNode = findOutputNode(network, child, childPort);
if (outputNode != null) {
List<?> result = renderChild(network, outputNode, networkArgumentMap);
if (childPort.isFileWidget()) {
return convertToFileNames(result);
Connection conn = findOutputConnection(network, child, childPort, true);
if (conn != null) {
Node outputNode = network.getChild(conn.getOutputNode());
if (conn.isFeedbackLoop()) {
List<?> previousResults = previousRenderResults.get(outputNode);
if (previousResults != null) {
return previousResults;
} else {
conn = findOutputConnection(network, child, childPort, false);
if (conn == null)
outputNode = null;
else
outputNode = network.getChild(conn.getOutputNode());
}
}
return result;
} else {
Object value = getPortValue(child, childPort);
if (value == null) {
return ImmutableList.of();
} else {
return ImmutableList.of(value);
if (outputNode != null) {
List<?> result = renderChild(network, outputNode, networkArgumentMap);
if (childPort.isFileWidget()) {
return convertToFileNames(result);
}
return result;
}
}
Object value = getPortValue(child, childPort);
if (value == null) {
return ImmutableList.of();
} else {
return ImmutableList.of(value);
}
}

private List<?> convertToFileNames(List<?> values) {
Expand Down
Loading