Skip to content

Commit 1e1b144

Browse files
committed
#36 parent child relationship logic moved to application
1 parent 556b1eb commit 1e1b144

File tree

7 files changed

+256
-31
lines changed

7 files changed

+256
-31
lines changed

installation.sh

-5
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,6 @@ function parse() {
3131
parse "$@"
3232

3333
echo "=== Install / Update script for PsyNeuLinkViewer and meta-diagram ==="
34-
#echo "Install is"
35-
#echo $INSTALL
36-
#echo "Update is"
37-
#echo $UPDATE
38-
3934

4035

4136
if [ "$INSTALL" = true ]; then

src/components/Main.js

+11-10
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import Composition from './views/compositions/Composition';
77
import GenericMechanism from './views/mechanisms/GenericMechanism';
88
import MetaDiagram, { CallbackTypes, ComponentsMap, EventTypes, Position } from "@metacell/meta-diagram";
99
import CustomLinkWidget from './views/projections/CustomLinkWidget';
10+
import { generateMetaGraph } from '../model/utils';
1011
const mockModel = require('../resources/model').mockModel;
1112

13+
1214
const styles = () => ({
1315
root: {
1416
height: 'calc(100vh - 3.5rem)',
@@ -40,23 +42,22 @@ class Main extends React.Component {
4042
this.handlePreUpdates = this.handlePreUpdates.bind(this);
4143
this.handlePostUpdates = this.handlePostUpdates.bind(this);
4244
this.mouseMoveCallback = this.mouseMoveCallback.bind(this);
43-
}
4445

45-
calculateDelta(metaNode, metaNodeModel) {
46-
let oldPosition = new Position(metaNode.position.x, metaNode.position.y);
47-
let newPosition = new Position(metaNodeModel.position.x, metaNodeModel.position.y);
48-
return oldPosition.sub(newPosition)
46+
this.metaGraph = generateMetaGraph([...this.metaModel[PNLClasses.COMPOSITION], ...this.metaModel[PNLClasses.MECHANISM]]);
47+
this.metaGraph.addLinks(this.metaModel[PNLClasses.PROJECTION]);
4948
}
5049

5150
handlePostUpdates(event) {
5251
switch(event.function) {
5352
case CallbackTypes.POSITION_CHANGED: {
54-
this.interpreter.updateModel(event.entity);
55-
break;
53+
const node = event.entity;
54+
this.metaGraph.handleNodePositionChanged(node, this.mousePos.x, this.mousePos.y);
55+
this.interpreter.updateModel(node);
56+
return true;
5657
}
5758
default: {
5859
console.log('Function callback type not yet implemented ' + event.function);
59-
break;
60+
return false;
6061
}
6162
}
6263
}
@@ -94,8 +95,8 @@ class Main extends React.Component {
9495
<MetaDiagram
9596
metaCallback={this.metaCallback}
9697
componentsMap={this.componentsMap}
97-
metaLinks={this.metaModel[PNLClasses.PROJECTION]}
98-
metaNodes={[...this.metaModel[PNLClasses.COMPOSITION], ...this.metaModel[PNLClasses.MECHANISM]]}
98+
metaLinks={this.metaGraph.getLinks()}
99+
metaNodes={this.metaGraph.getNodes()}
99100
metaTheme={{
100101
customThemeVariables: {},
101102
canvasClassName: classes.canvasBG,

src/components/graph/MetaGraph.ts

+220
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
// import {MetaNodeModel} from "../react-diagrams/MetaNodeModel";
2+
import {MetaLink, MetaNodeModel, MetaLinkModel} from "@metacell/meta-diagram"
3+
4+
class Graph {
5+
private readonly node: MetaNodeModel;
6+
private readonly children: Map<string, Graph>;
7+
8+
constructor(metaNodeModel: MetaNodeModel) {
9+
this.node = metaNodeModel;
10+
this.children = new Map<string, Graph>()
11+
}
12+
13+
getID() : string{
14+
return this.node.getID()
15+
}
16+
17+
getNode() : MetaNodeModel{
18+
return this.node
19+
}
20+
21+
getChild(id:string) {
22+
return this.children.get(id)
23+
}
24+
25+
addChild(graph: Graph) : void {
26+
this.children.set(graph.getID(), graph)
27+
}
28+
29+
getChildren(): MetaNodeModel[] {
30+
return Array.from(this.children.values()).map(g => g.getNode())
31+
}
32+
33+
getDescendancy(): MetaNodeModel[] {
34+
const descendancy = this.getChildren()
35+
for(const graph of Array.from(this.children.values())){
36+
descendancy.push(...graph.getDescendancy())
37+
}
38+
return descendancy
39+
}
40+
41+
dfs(id: string): MetaNodeModel | boolean {
42+
if(this.getID() === id){
43+
return this.node
44+
}
45+
for (let node of Array.from(this.children.values())) {
46+
const found = node.dfs(id)
47+
if(found){
48+
return found
49+
}
50+
}
51+
return false
52+
}
53+
54+
getContainerBoundingBox() : any {
55+
return this.node.getNodeBoundingBox();
56+
}
57+
}
58+
59+
60+
export class MetaGraph {
61+
private readonly roots: Map<string, Graph>;
62+
private readonly links: MetaLinkModel[];
63+
64+
constructor() {
65+
this.roots = new Map<string, Graph>()
66+
this.links = [];
67+
}
68+
69+
addLinks(links: MetaLink[]) {
70+
links.forEach( (child: MetaLink) => {
71+
const link = child.toModel();
72+
const source = this.getNodeDFS(child.getSourceId());
73+
const target = this.getNodeDFS(child.getTargetId());
74+
if (source && target) {
75+
link.setSourcePort(source.getPort(child.getSourcePortId()));
76+
link.setTargetPort(target.getPort(child.getTargetPortId()));
77+
this.links.push(link);
78+
}
79+
});
80+
}
81+
82+
getLinks(): MetaLinkModel[] {
83+
return this.links;
84+
}
85+
86+
addNode(metaNodeModel:MetaNodeModel): void {
87+
const path = metaNodeModel.getGraphPath()
88+
if(path.length === 1){
89+
this.roots.set(metaNodeModel.getID(), new Graph(metaNodeModel))
90+
}else{
91+
path.pop() // Removes own id from path
92+
const parentGraph = this.findNodeGraph(path)
93+
parentGraph.addChild(new Graph(metaNodeModel))
94+
}
95+
}
96+
97+
getNodes() : MetaNodeModel[] {
98+
const nodes = []
99+
for(const graph of Array.from(this.roots.values())){
100+
nodes.push(graph.getNode())
101+
nodes.push(...graph.getDescendancy())
102+
}
103+
return nodes
104+
}
105+
106+
getAncestors(node : MetaNodeModel): MetaNodeModel[] {
107+
const path = node.getGraphPath()
108+
const oldestAncestor = this.getRoot(path[0])
109+
return [oldestAncestor.getNode(), ...oldestAncestor.getChildren()]
110+
}
111+
112+
getRoot(rootId: string) : Graph{
113+
const root = this.roots.get(rootId)
114+
if(root === undefined){
115+
throw new Error('unknown parent ' + rootId);
116+
}
117+
return root
118+
}
119+
120+
getChildren(parent : MetaNodeModel): MetaNodeModel[] {
121+
const path = parent.getGraphPath()
122+
if (path.length === 1) {
123+
const root = this.getRoot(parent.getID())
124+
return root.getChildren()
125+
} else {
126+
const graph = this.findNodeGraph(path)
127+
return graph.getChildren()
128+
}
129+
}
130+
131+
getParent(node : MetaNodeModel): MetaNodeModel | undefined {
132+
const path = node.getGraphPath()
133+
if (path.length === 1) {
134+
return undefined
135+
} else {
136+
path.pop() // removes own id from path
137+
const parentGraph = this.findNodeGraph(path)
138+
return parentGraph.getNode()
139+
}
140+
}
141+
142+
getNodeDFS(nodeId: string): MetaNodeModel | undefined {
143+
for (let root of Array.from(this.roots.values())) {
144+
const found = root.dfs(nodeId)
145+
if(found){
146+
// @ts-ignore
147+
return found
148+
}
149+
}
150+
return undefined
151+
}
152+
153+
getNodeContainerBoundingBox(node: MetaNodeModel) : any {
154+
const graph = this.findNodeGraph(node.getGraphPath())
155+
return graph.getContainerBoundingBox()
156+
}
157+
158+
private findNodeGraph(path: string[]) : Graph {
159+
const rootId = path.shift()
160+
// @ts-ignore
161+
let parent = this.getRoot(rootId)
162+
while(path.length > 0){
163+
const next = path.shift()
164+
// @ts-ignore
165+
parent = parent.getChild(next)
166+
if (parent === undefined){
167+
throw new Error('unknown parent ' + rootId);
168+
}
169+
}
170+
return parent
171+
}
172+
173+
handleNodePositionChanged(metaNodeModel: MetaNodeModel, cursorX: number, cursorY: number){
174+
// TODO: Update node parent (add or remove parent)
175+
// update node graph path,
176+
// bounding boxes of parents
177+
178+
// Update children position (children should move the same delta as node)
179+
this.updateChildrenPosition(metaNodeModel)
180+
// Update local position / relative position to the parent
181+
this.updateNodeLocalPosition(metaNodeModel)
182+
// update the graph for right parent children relationship
183+
this.updateGraph(metaNodeModel, cursorX, cursorY);
184+
}
185+
186+
updateGraph(metaNodeModel: MetaNodeModel, cursorX: number, cursorY: number) {
187+
let parent = undefined;
188+
let search = true;
189+
this.roots.forEach((node, id) => {
190+
if (node.getContainerBoundingBox().containsPoint(cursorX, cursorY)) {
191+
parent = node;
192+
}
193+
});
194+
// TODO add the new child to the graph and update graphPath for the metaNodeModel instance
195+
}
196+
197+
private updateChildrenPosition(metaNodeModel: MetaNodeModel){
198+
const children = this.getChildren(metaNodeModel);
199+
200+
children.forEach(n => {
201+
/*
202+
No need to explicitly call updateChildrenPosition for n children because it will happen automatically in
203+
the event listener
204+
*/
205+
// @ts-ignore
206+
const localPosition = n.getLocalPosition()
207+
n.setPosition(metaNodeModel.getX() + localPosition.x, metaNodeModel.getY() + localPosition.y)
208+
209+
})
210+
}
211+
212+
private updateNodeLocalPosition(metaNodeModel: MetaNodeModel){
213+
const parent = this.getParent(metaNodeModel)
214+
metaNodeModel.updateLocalPosition(parent)
215+
}
216+
217+
updateNodesContainerBoundingBoxes(nodes: MetaNodeModel[]): void {
218+
nodes.forEach(n => n.setContainerBoundingBox(this.getNodeContainerBoundingBox(n)))
219+
}
220+
}

src/components/views/compositions/Composition.js

+2-7
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,6 @@ class Composition extends React.Component {
8383
expanded: false,
8484
width: props.model.options.width,
8585
height: props.model.options.height,
86-
x: props.model.options.x,
87-
y: props.model.options.y
8886
}
8987
this.changeVisibility = this.changeVisibility.bind(this);
9088
}
@@ -101,12 +99,9 @@ class Composition extends React.Component {
10199
<Box className={`${classes.root} ${expanded ? classes.selected : ''}`}>
102100
<Rnd
103101
size={{ width: this.state.width, height: this.state.height }}
104-
position={{ x: this.state.x, y: this.state.y }}
105-
onDragStop={(e, d) => {
106-
this.setState({ x: d.x, y: d.y });
107-
}}
102+
position={{ x: this.props.model.options.x, y: this.props.model.options.y }}
108103
onResizeStop={(e, direction, ref, delta, position) => {
109-
this.props.model.updateSize(ref.style.width, ref.style.height);
104+
this.props.model.updateSize(parseFloat(ref.style.width), parseFloat(ref.style.height));
110105
this.setState({
111106
width: ref.style.width,
112107
height: ref.style.height,

src/model/Interpreter.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,11 @@ export default class ModelInterpreter {
101101
}
102102

103103
updateModel(item: MetaNode|MetaLink) {
104+
// TODO: here we sync the MetaModel node with the MetaNodeModel, question is, do we need it?
105+
// the MetaNodeModel has already serialization implemented and we don't need anything else
106+
// from the metamodel once it's passed to meta-diagram, to investigate whether we need this sync
107+
// or we can simply rely on the metaNodeModel to be serialised and passed to the backend.
104108
// if (this.metaModelMap[item.getShape()].has(item.getId())) {
105-
console.log('this is where I update the node');
106-
console.log(item);
107109
// }
108110
}
109111

src/model/nodes/composition/CompositionNode.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ export default class CompositionNode extends MechanismNode {
4444
}
4545

4646
if (this.extra?.boundingBox) {
47+
console.log(this.extra.boundingBox);
4748
this.extra.position = {
48-
x: this.extra.boundingBox.llx + 75,
49-
y: this.extra.boundingBox.lly + 75
49+
x: this.extra.boundingBox.llx,
50+
y: this.extra.boundingBox.lly
5051
}
5152
}
5253

src/model/utils.js

+16-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { PNLClasses } from '../constants';
2-
// const html2json = require('html2json').html2json;
3-
2+
import {MetaNode} from '@metacell/meta-diagram';
3+
import { MetaGraph } from '../components/graph/MetaGraph';
44

55
export function buildModel(frontendModel, coord, prevModel) {
66
let finalModel = {
@@ -18,8 +18,6 @@ export function buildModel(frontendModel, coord, prevModel) {
1818
y: 150,
1919
};
2020

21-
let linkCounter = 1;
22-
2321
if (coord) {
2422
coordinates.x = coord.x;
2523
coordinates.y = coord.y;
@@ -58,4 +56,17 @@ export function buildModel(frontendModel, coord, prevModel) {
5856
});
5957

6058
return finalModel;
61-
}
59+
}
60+
61+
export function generateMetaGraph(metaNodes) {
62+
const metaGraph = new MetaGraph()
63+
metaNodes.sort(function(a, b) {
64+
return a.getDepth() - b.getDepth();
65+
});
66+
67+
for(const mn of metaNodes){
68+
const metaNodeModel = mn.toModel()
69+
metaGraph.addNode(metaNodeModel)
70+
}
71+
return metaGraph
72+
}

0 commit comments

Comments
 (0)