浏览代码

WIP: Rountangle -> Statecharts parser

Joeri Exelmans 2 年之前
父节点
当前提交
7d7698fa87
共有 2 个文件被更改,包括 93 次插入73 次删除
  1. 34 39
      src/frontend/app.tsx
  2. 59 34
      src/parser/trivial_parser.ts

+ 34 - 39
src/frontend/app.tsx

@@ -84,6 +84,10 @@ export interface VersionedModelProps {
 
   state: VersionedModelState;
   setState: (callback: (VersionedModelState) => VersionedModelState) => void;
+
+  defaultTab0: string;
+  defaultTab1: string;
+  tabs: string[];
 }
 
 class VersionedModel extends React.Component<VersionedModelProps, {}> {
@@ -160,24 +164,22 @@ class VersionedModel extends React.Component<VersionedModelProps, {}> {
       );
     }
 
-    const makeConcreteSyntaxTabs = defaultTab => (
+    const makeTabs = (defaultTab, tabs) => (
       <Tabs defaultValue={defaultTab} keepMounted={false}>
-        {this.props.readonly?(
-          <Tabs.List>
-            <Tabs.Tab value="state">State</Tabs.Tab>
-            <Tabs.Tab value="history">History</Tabs.Tab>
-            <Tabs.Tab value="dependencyL1">Deltas (L1)</Tabs.Tab>
-            <Tabs.Tab value="dependencyL0">Deltas (L0)</Tabs.Tab>
-          </Tabs.List>
-        ):(
-          <Tabs.List>
-            <Tabs.Tab value="editor">Editor</Tabs.Tab>
-            <Tabs.Tab value="state">State</Tabs.Tab>
-            <Tabs.Tab value="history">History</Tabs.Tab>
-            <Tabs.Tab value="dependencyL1">Deltas (L1)</Tabs.Tab>
-            <Tabs.Tab value="dependencyL0">Deltas (L0)</Tabs.Tab>
-          </Tabs.List>
-        )}
+        <Tabs.List>
+          {tabs.map(tab => {
+            if (tab === "editor")
+              return <Tabs.Tab key={tab} value="editor">Editor</Tabs.Tab>;
+            if (tab === "state")
+              return <Tabs.Tab key={tab} value="state">State</Tabs.Tab>;
+            if (tab === "history")
+              return <Tabs.Tab key={tab} value="history">History</Tabs.Tab>;
+            if (tab === "dependencyL1")
+              return <Tabs.Tab key={tab} value="dependencyL1">Deltas (L1)</Tabs.Tab>;
+            if (tab === "dependencyL0")
+              return <Tabs.Tab key={tab} value="dependencyL0">Deltas (L0)</Tabs.Tab>;
+          })}
+        </Tabs.List>
 
         <Tabs.Panel value="state">
           {makeInfoHoverCardIcon(<>
@@ -263,10 +265,8 @@ class VersionedModel extends React.Component<VersionedModelProps, {}> {
       <>
         <Title order={4}>{this.props.title}</Title>
         <Stack>
-          {/*{makeConcreteSyntaxTabs(this.props.readonly?"state":"editor")}*/}
-          {/*{makeConcreteSyntaxTabs("history")}*/}
-          {makeConcreteSyntaxTabs("state")}
-          {makeConcreteSyntaxTabs("dependencyL1")}
+          {makeTabs(this.props.defaultTab0, this.props.tabs)}
+          {makeTabs(this.props.defaultTab1, this.props.tabs)}
         </Stack>
 
         <SimpleGrid cols={2}>
@@ -439,6 +439,8 @@ export class App extends React.Component<{}, AppState> {
           mapping, parse: boolean) => {
       return (sourceDeltas: PrimitiveDelta[], description: string) => {
 
+        const targetDescription = (parse ? "parse:" : "render:") + description;
+
         const sourceParent = sourceState.version;
         const {corr: corrParent, target: targetParent} = mapping.get(sourceParent) || (()=>{throw new Error("X")})();
 
@@ -451,26 +453,10 @@ export class App extends React.Component<{}, AppState> {
 
         console.log({corrDeltas})
 
-        // if (parse) {
-        //   const {corrDeltas: corrDeltasExtra, targetDeltas: targetDeltasExtra, sourceOverrides: sourceOverridesExtra, targetOverrides: targetOverridesExtra} =
-        //   (new InsidenessParser(this.primitiveRegistry, this.generateUUID)).parse(sourceDeltas, sourceGraphState, corrGraphState, targetGraphState);
-        //   console.log({corrDeltasExtra})
-        //   corrDeltas.push(...corrDeltasExtra);
-        //   targetDeltas.push(...targetDeltasExtra);
-        //   for (const [key,val] of sourceOverridesExtra.entries()) {
-        //     if (sourceOverrides.has(key)) throw new Error("Double override");
-        //     sourceOverrides.set(key, val);
-        //   }
-        //   for (const [key,val] of targetOverridesExtra.entries()) {
-        //     if (targetOverrides.has(key)) throw new Error("Double override");
-        //     targetOverrides.set(key, val);
-        //   }
-        // }
-
         const sourceComposite = sourceState.compositeLevel.createComposite(sourceDeltas, description);
         const newSourceVersion = sourceState.versionRegistry.createVersion(sourceState.version, sourceComposite);
 
-        const targetComposite = targetState.compositeLevel.createComposite(targetDeltas, description);
+        const targetComposite = targetState.compositeLevel.createComposite(targetDeltas, targetDescription);
         const newTargetVersion =  (targetComposite.deltas.length > 0) ?
           targetState.versionRegistry.createVersion(targetParent, targetComposite)
           : targetState.version; // do not create a new target version
@@ -493,7 +479,7 @@ export class App extends React.Component<{}, AppState> {
 
         console.log({corrDeltasOrderedByDependency});
 
-        const corrComposite = this.state.corr.compositeLevel.createComposite(corrDeltasOrderedByDependency, description);
+        const corrComposite = this.state.corr.compositeLevel.createComposite(corrDeltasOrderedByDependency, targetDescription);
         const newCorrVersion = this.state.corr.versionRegistry.createVersion(corrParent, corrComposite,
           embed(
             ["cs", newCsVersion, csOverrides],
@@ -528,6 +514,9 @@ export class App extends React.Component<{}, AppState> {
           <VersionedModel title="Concrete Syntax"
             generateUUID={this.generateUUID}
             primitiveRegistry={this.primitiveRegistry}
+            tabs={["editor", "state", "history", "dependencyL1", "dependencyL0"]}
+            defaultTab0="editor"
+            defaultTab1="history"
             state={this.state.cs}
             setState={this.setCsState.bind(this)}
             setNextNodePosition={(x,y)=>{this.state.cs.d3StateUpdater.x = x; this.state.cs.d3StateUpdater.y = y;}}
@@ -541,6 +530,9 @@ export class App extends React.Component<{}, AppState> {
           <VersionedModel title="Correspondence" readonly
             generateUUID={this.generateUUID}
             primitiveRegistry={this.primitiveRegistry}
+            tabs={["state", "history", "dependencyL1", "dependencyL0"]}
+            defaultTab0="state"
+            defaultTab1="history"
             setNextNodePosition={(x,y)=>{this.state.corr.d3StateUpdater.x = x; this.state.corr.d3StateUpdater.y = y;}}
             state={this.state.corr}
             setState={this.setCorrState.bind(this)}
@@ -554,6 +546,9 @@ export class App extends React.Component<{}, AppState> {
           <VersionedModel title="Abstract Syntax"
             generateUUID={this.generateUUID}
             primitiveRegistry={this.primitiveRegistry}
+            tabs={["state", "history", "dependencyL1", "dependencyL0"]}
+            defaultTab0="state"
+            defaultTab1="history"
             setNextNodePosition={(x,y)=>{this.state.as.d3StateUpdater.x = x; this.state.as.d3StateUpdater.y = y;}}
             state={this.state.as}
             setState={this.setAsState.bind(this)}

+ 59 - 34
src/parser/trivial_parser.ts

@@ -178,68 +178,93 @@ export class TrivialParser  {
               catch(e) {}
             }
           }
-          function findCorrespondingAsNode(csNode: INodeState) {
-            const pair = csNode.getIncomingEdges().find(([label]) => label==="cs");
+          function findCorrespondingAsNode(csNode: INodeState, reverse: boolean = false) {
+            const pair = csNode.getIncomingEdges().find(([label]) => label===(reverse?"as":"cs"));
             if (pair === undefined) {
               throw new Error("No incoming 'cs' edge.");
             }
             const [_, corrNodeState] = pair;
-            const asState = corrNodeState.getOutgoingEdges().get("as");
+            const asState = corrNodeState.getOutgoingEdges().get(reverse?"cs":"as");
             if (asState === undefined) {
               throw new Error("Found correspondence node, but it has no outgoing 'as' edge.")
             }
             return asState as INodeState;
           }
 
-
           if (geometryLabels.includes(label)) {
             const updatedNodeId = edgeCreation.source.id.value;
             const newGeometry = getGeometry(updatedNodeId);
             if (newGeometry !== undefined) {
-              console.log({newGeometry});
-              // compare the new geometry to every other rountangle
-              // let parentId;
-              // let smallestArea = Infinity;
+              const updatedAsNode = findCorrespondingAsNode(corrState.nodes.get(updatedNodeId) as INodeState);
+
+              // 1. Check if existing parent links still hold
+              // 1.a. outgoing parent links
+              const otherAsNodeState = updatedAsNode.getOutgoingEdges().get("hasParent");
+              if (otherAsNodeState !== undefined && otherAsNodeState.type === "node") {
+                const otherCsNodeState = findCorrespondingAsNode(otherAsNodeState, true);
+                const otherNodeId = otherCsNodeState.creation.id.value;
+                const otherGeometry = getGeometry(otherNodeId);
+                if (otherGeometry === undefined || !isInside(newGeometry, otherGeometry)) {
+                  // parent relation no longer holds
+                  console.log("deleting outgoing link...")
+                  const deleteLink = updatedAsNode.getDeltasForSetEdge(this.primitiveRegistry, "hasParent", null); // deletes the edge
+                  applyToState(corrState, deleteLink);
+                  targetDeltas.push(...deleteLink);
+                }
+              }
+              // 1.b. incoming parent links
+              for (const [_, otherAsNodeState] of updatedAsNode.getIncomingEdges().filter(([label, ns]) => label === "hasParent")) {
+                const otherCsNodeState = findCorrespondingAsNode(otherAsNodeState, true);
+                const otherNodeId = otherCsNodeState.creation.id.value;
+                const otherGeometry = getGeometry(otherNodeId);
+                // console.log({otherNodeState, otherNodeId, otherGeometry})
+                if (otherGeometry === undefined || !isInside(otherGeometry, newGeometry)) {
+                  // parent relation no longer holds
+                  const otherAsNode = findCorrespondingAsNode(corrState.nodes.get(otherNodeId) as INodeState);
+                  console.log("deleting incoming link...")
+                  const deleteLink = otherAsNode.getDeltasForSetEdge(this.primitiveRegistry, "hasParent", null); // deletes the edge
+                  applyToState(corrState, deleteLink);
+                  targetDeltas.push(...deleteLink);
+                }
+              }
+
+              // 2. Compare the new geometry to every other rountangle
+              let smallestParent: INodeState | null = null;
+              let smallestSurface = Infinity;
               for (const [otherNodeId,otherNodeState] of sourceState.nodes.entries()) {
                 if (otherNodeState.creation === edgeCreation.source) {
                   continue; // don't compare with ourselves
                 }
                 const otherGeometry = getGeometry(otherNodeId);
-
-                // Our updated Rountangle may be "inside" many other Rountangles, but we're only interested in the one with the smallest surface area (this should be the direct 'parent', not some further ancestor)
-                // const otherArea = otherGeometry.w * otherGeometry * h;
-                // if (otherArea < smallestArea) {
-                //   smallestArea = otherArea;
-                //   parentId = otherNodeId;
-                // }
-
                 if (otherGeometry !== undefined) {
-                  console.log({otherGeometry})
                   const inside = isInside(newGeometry, otherGeometry);
+                  if (inside) {
+                    const surface = otherGeometry.w * otherGeometry.h;
+                    if (surface < smallestSurface) {
+                      smallestSurface = surface;
+                      smallestParent = findCorrespondingAsNode(corrState.nodes.get(otherNodeId) as INodeState);
+                    }
+                  }
                   const outside = isInside(otherGeometry, newGeometry);
-                  if (inside || outside) {
-                    console.log(inside, outside);
-                    const updatedAsNode = findCorrespondingAsNode(corrState.nodes.get(updatedNodeId) as INodeState);
+                  if (outside) {
+                    console.log("creating link...")
                     const otherAsNode = findCorrespondingAsNode(corrState.nodes.get(otherNodeId) as INodeState);
-                    console.log({updatedAsNode, otherAsNode});
-                    const insideAsNode = inside ? updatedAsNode : otherAsNode;
-                    const outsideAsNode = inside ? otherAsNode : updatedAsNode;
-
-                    // if (insideAsNode.getOutgoingEdges().
-
-                    const asParentLink = insideAsNode.getDeltasForSetEdge(this.primitiveRegistry, "hasParent", outsideAsNode.asTarget());
-
-                    applyToState(corrState, asParentLink);
-
-                    targetDeltas.push(...asParentLink);
+                    if (otherAsNode.getOutgoingEdges().get("hasParent") === undefined) {
+                      console.log("creating link...")
+                      const asParentLink = updatedAsNode.getDeltasForSetEdge(this.primitiveRegistry, "hasParent", otherAsNode.asTarget());
+                      applyToState(corrState, asParentLink);
+                      targetDeltas.push(...asParentLink);
+                    }
                   }
                 }
               }
+              if (smallestParent !== null) {
+                const asParentLink = updatedAsNode.getDeltasForSetEdge(this.primitiveRegistry, "hasParent", smallestParent.asTarget());
+                applyToState(corrState, asParentLink);
+                targetDeltas.push(...asParentLink);
+              }
             }
           }
-          
-
-
         }
       }
     }