addLink method
This method is used to add a link between two ports.
The method takes the IDs of the two nodes and the two ports and creates a link between them. The method also checks if the link is valid based on the port types and the number of links allowed on each port. Moreover, the method enforces the direction of the link based on the port types, i.e., an output port can only be connected to an input port guaranteeing that the graph is directed the right way.
Emits an AddLinkEvent event.
Implementation
Link? addLink(
String node1Id,
String port1IdName,
String node2Id,
String port2IdName, {
String? eventId,
}) {
bool areTypesCompatible(Type type1, Type type2) {
if (type1 == dynamic || type2 == dynamic) return true;
if ((type1 == int || type1 == double) &&
(type2 == int || type2 == double)) {
return true;
}
return type1 == type2;
}
// Check for self-links
if (node1Id == node2Id) return null;
final node1 = nodes[node1Id]!;
final port1 = node1.ports[port1IdName]!;
final node2 = nodes[node2Id]!;
final port2 = node2.ports[port2IdName]!;
if (!areTypesCompatible(
port1.prototype.dataType,
port2.prototype.dataType,
)) {
nodeLog(
'Cannot connect ports of different data types: ${port1.prototype.dataType} and ${port2.prototype.dataType}',
SnackbarType.error,
);
return null;
} else if (port1.prototype.type != port2.prototype.type) {
nodeLog(
'Cannot connect ports of different types: ${port1.prototype.type} and ${port2.prototype.type}',
SnackbarType.error,
);
return null;
} else if (port1.prototype.direction == port2.prototype.direction) {
nodeLog(
'Cannot connect two ports with the same direction: ${port1.prototype.displayName} and ${port2.prototype.displayName}',
SnackbarType.error,
);
return null;
} else if (port1.links.any(
(link) =>
link.fromTo.from == node2Id && link.fromTo.to == port2IdName,
) ||
port2.links.any(
(link) =>
link.fromTo.from == node1Id && link.fromTo.to == port1IdName,
)) {
return null;
}
late FromTo fromTo;
// Determine the order to insert the node references in the link based on the port direction.
if (port1.prototype.direction == PortDirection.output) {
fromTo = (
from: node1Id,
to: port1IdName,
fromPort: node2Id,
toPort: port2IdName
);
} else {
fromTo = (
from: node2Id,
to: port2IdName,
fromPort: node1Id,
toPort: port1IdName
);
}
bool canConnect(FromTo fromTo) {
final fromNode = nodes[fromTo.from]!;
final fromPort = fromNode.ports[fromTo.to]!;
final toNode = nodes[fromTo.fromPort]!;
final toPort = toNode.ports[fromTo.toPort]!;
// Check if the ports are compatible
if (fromPort.prototype.direction == toPort.prototype.direction) {
nodeLog(
'Cannot connect two ports of the same type: ${fromPort.prototype.displayName} and ${toPort.prototype.displayName}',
SnackbarType.error,
);
return false;
}
// Check if the input port already has a link
if (toPort.prototype.type == PortType.data && toPort.links.isNotEmpty) {
nodeLog(
'Cannot connect multiple links to an data input port: ${toPort.prototype.displayName} in node ${toNode.prototype.displayName}',
SnackbarType.error,
);
return false;
}
return true;
}
if (!canConnect(fromTo)) return null;
final link = Link(
id: const Uuid().v4(),
fromTo: fromTo,
state: LinkState(),
);
port1.links.add(link);
port2.links.add(link);
linksById.putIfAbsent(
link.id,
() => link,
);
linksDataDirty = true;
eventBus.emit(
AddLinkEvent(id: eventId ?? const Uuid().v4(), link),
);
return link;
}