|
|
@@ -21,14 +21,18 @@ import {
|
|
|
VersionRegistry,
|
|
|
} from "../onion/version";
|
|
|
|
|
|
+// The stuff that we store in each (visible) node:
|
|
|
interface NodeObjType {
|
|
|
- nodeCreation: NodeCreation;
|
|
|
- outgoing: Map<string, [EdgeCreation|EdgeUpdate, NodeObjType]>;
|
|
|
+ nodeCreation: NodeCreation; // The delta that created the node
|
|
|
+ outgoing: Map<string, [EdgeCreation|EdgeUpdate, NodeObjType]>; // Outgoing edges: Mapping from edge label to the most recent EdgeCreation or EdgeUpdate.
|
|
|
+
|
|
|
incoming: Array<[EdgeCreation|EdgeUpdate|NodeDeletion, NodeObjType]>;
|
|
|
}
|
|
|
|
|
|
+type EdgeObjType = null; // We don't need to store anything special in edges.
|
|
|
+
|
|
|
interface EditableGraphProps {
|
|
|
- graph: d3Types.d3Graph<NodeObjType,EdgeCreation|EdgeUpdate>;
|
|
|
+ graph: d3Types.d3Graph<NodeObjType,EdgeObjType>;
|
|
|
forces: Forces;
|
|
|
getUuid: () => UUID;
|
|
|
newVersionHandler: (Version) => void;
|
|
|
@@ -39,7 +43,7 @@ interface EditableGraphState {
|
|
|
}
|
|
|
|
|
|
export class EditableGraph extends React.Component<EditableGraphProps, EditableGraphState> {
|
|
|
- graphRef: React.RefObject<Graph<NodeObjType,EdgeCreation|EdgeUpdate>> = React.createRef<Graph<NodeObjType,EdgeCreation|EdgeUpdate>>();
|
|
|
+ graphRef: React.RefObject<Graph<NodeObjType,EdgeObjType>> = React.createRef<Graph<NodeObjType,EdgeObjType>>();
|
|
|
mouseDownNode: d3Types.d3Node<NodeObjType> | null = null; // only while mouse button is down, does this record the d3Node that was under the cursor when the mouse went down.
|
|
|
|
|
|
nextId: number = 0;
|
|
|
@@ -81,21 +85,25 @@ export class EditableGraph extends React.Component<EditableGraphProps, EditableG
|
|
|
// an edge with the same source and label already exists:
|
|
|
const edgeUpdate = new EdgeUpdate(prevUpdate, target.nodeCreation);
|
|
|
this.graphRef.current.deleteLink({source: this.mouseDownNode, label});
|
|
|
- // remove item from array:
|
|
|
- target.incoming.splice(target.incoming.findIndex(([delta, _])=>delta===prevUpdate), 1);
|
|
|
+ // replace item in array:
|
|
|
+ console.log("replace item in array:");
|
|
|
+ prevTargetObj.incoming.splice(target.incoming.findIndex(([delta, _])=>delta===prevUpdate), 1, [edgeUpdate, prevTargetObj]);
|
|
|
+ console.log("prevTargetObj:", prevTargetObj);
|
|
|
return edgeUpdate;
|
|
|
- } else {
|
|
|
+ }
|
|
|
+ else {
|
|
|
const edgeCreation = new EdgeCreation(source.nodeCreation, label, target.nodeCreation);
|
|
|
return edgeCreation;
|
|
|
}
|
|
|
})();
|
|
|
source.outgoing.set(label, [edgeCreationOrUpdate, target]);
|
|
|
target.incoming.push([edgeCreationOrUpdate, source]);
|
|
|
+ console.log("target:", target);
|
|
|
|
|
|
- // console.log("target.incoming:", target.incoming);
|
|
|
+ // console.log("target.incoming:", target.incoming);
|
|
|
// console.log("edgeCreationOrUpdate.conflicts:", edgeCreationOrUpdate.getConflicts());
|
|
|
|
|
|
- this.graphRef.current.createLink({source: this.mouseDownNode, label, target: node, obj: edgeCreationOrUpdate});
|
|
|
+ this.graphRef.current.createLink({source: this.mouseDownNode, label, target: node, obj: null});
|
|
|
|
|
|
const tx = this.compositeLvl.createComposite([edgeCreationOrUpdate]);
|
|
|
this.currentVersion = this.versionRegistry.createVersion(this.currentVersion, tx);
|
|
|
@@ -122,30 +130,47 @@ export class EditableGraph extends React.Component<EditableGraphProps, EditableG
|
|
|
else if (event.button === 1) { // middle mouse button
|
|
|
if (node) {
|
|
|
// Delete node (and its incoming + outgoing edges):
|
|
|
- console.log("incoming:", node.obj.incoming);
|
|
|
- console.log("outgoing:", node.obj.outgoing);
|
|
|
- const incomingEdgeUnsettings = node.obj.incoming.map(([incomingEdge,sourceObj]) => {
|
|
|
+ // console.log("incoming:", node.obj.incoming);
|
|
|
+ // console.log("outgoing:", node.obj.outgoing);
|
|
|
+
|
|
|
+ const deleteDependencies: any[] = [];
|
|
|
+ const compositeDeltas: any[] = [];
|
|
|
+
|
|
|
+ for (const [incomingEdge,sourceObj] of node.obj.incoming) {
|
|
|
+ // We also depend on incoming edges that were deleted (because their source node was deleted):
|
|
|
if (incomingEdge instanceof NodeDeletion) {
|
|
|
- return incomingEdge;
|
|
|
+ console.log("edge was already deleted");
|
|
|
+ deleteDependencies.push(incomingEdge);
|
|
|
}
|
|
|
else {
|
|
|
- const edgeUnset = new EdgeUpdate(incomingEdge, null);
|
|
|
- sourceObj.outgoing.set(incomingEdge.getCreation().label, [edgeUnset,node.obj]);
|
|
|
- return edgeUnset;
|
|
|
+ if (incomingEdge.target.target === node.obj.nodeCreation) {
|
|
|
+ console.log("unsetting incoming edge...");
|
|
|
+ // Must set the value of every incoming edge to 'null' (with an EdgeUpdate):
|
|
|
+ const edgeUnset = new EdgeUpdate(incomingEdge, null);
|
|
|
+ sourceObj.outgoing.set(incomingEdge.getCreation().label, [edgeUnset,node.obj]);
|
|
|
+ deleteDependencies.push(edgeUnset);
|
|
|
+ compositeDeltas.push(edgeUnset);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ console.log("edge already pointing somewhere else");
|
|
|
+ // Edge is already pointing somewhere else: just include the operation as a dependency for the deletion.
|
|
|
+ deleteDependencies.push(incomingEdge);
|
|
|
+ }
|
|
|
}
|
|
|
- });
|
|
|
+ }
|
|
|
const targetIncomings: Array<[EdgeCreation|EdgeUpdate|NodeDeletion, NodeObjType]>[] = [];
|
|
|
- const deletedOutgoingEdges = [...node.obj.outgoing.values()].map(([outgoingEdge,targetObj]) => {
|
|
|
+ for (const [outgoingEdge, targetObj] of node.obj.outgoing.values()) {
|
|
|
targetObj.incoming.splice(targetObj.incoming.findIndex(([d,_])=>d===outgoingEdge), 1);
|
|
|
targetIncomings.push(targetObj.incoming);
|
|
|
- return outgoingEdge;
|
|
|
- });
|
|
|
- const nodeDeletion = new NodeDeletion(node.obj.nodeCreation, [...incomingEdgeUnsettings, ...deletedOutgoingEdges]);
|
|
|
+ deleteDependencies.push(outgoingEdge);
|
|
|
+ }
|
|
|
+ const nodeDeletion = new NodeDeletion(node.obj.nodeCreation, deleteDependencies);
|
|
|
+ compositeDeltas.push(nodeDeletion);
|
|
|
for (const targetIncoming of targetIncomings) {
|
|
|
targetIncoming.push([nodeDeletion, node.obj]);
|
|
|
}
|
|
|
this.graphRef.current.deleteNode(node.id);
|
|
|
- const tx = this.compositeLvl.createComposite([nodeDeletion, ...incomingEdgeUnsettings.filter(d=>!(d instanceof NodeDeletion))]);
|
|
|
+ const tx = this.compositeLvl.createComposite(compositeDeltas);
|
|
|
this.currentVersion = this.versionRegistry.createVersion(this.currentVersion, tx);
|
|
|
this.props.newDeltaHandler(tx);
|
|
|
this.props.newVersionHandler(this.currentVersion);
|