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

How to Fix Nodes' Positions? NodePosition? is not working #322

Closed
mrshahidmehmood opened this issue Jun 12, 2020 · 11 comments
Closed

How to Fix Nodes' Positions? NodePosition? is not working #322

mrshahidmehmood opened this issue Jun 12, 2020 · 11 comments

Comments

@mrshahidmehmood
Copy link

I am trying to fix the position of nodes but I could not do so. I tried using NodePosition property from Node model but it has no effect even if I give it 0 or 100 or 1000.
Please Can anybody help?

@AsimNet
Copy link

AsimNet commented Jun 17, 2020

same thing here
I credited a customDager layout
so that I can change the position of a node...
and couldn't

@assafsun
Copy link
Contributor

assafsun commented Jun 27, 2020

Look here how to build the node object inside a custom layout in order to position the nodes.

for (const dagreNodeId in this.dagreGraph._nodes) {

@matndev
Copy link

matndev commented Jan 14, 2021

Issue still relevant. When you provide position key in a Node object the lib set it to 0 anyway.
I tried custom layout to set position on initial nodes but graph object on run(graph: Graph) contains nodes with position (x: 0, y: 0).
If return graph; is the only line implemented in it, a translate(-15, -15) is even added to the tag created.
Need demo and more documentation on this subject if possible.

I think the problem is there :
`
update(): void {
...
this.createGraph();
...
}

createGraph(): void {
...
const initializeNode = (n: Node) => {
...
n.position = {
x: 0,
y: 0
};
...
};

this.graph = {
nodes: this.nodes.length > 0 ? [...this.nodes].map(initializeNode) : [],
...
}
requestAnimationFrame(() => this.draw());
}

draw(): void {
....
const result = this.layout.run(this.graph);
...
}
`

@marjan-georgiev
Copy link
Member

@matndev could you create a stackblitz where this can be reproduced please? Will make it easier for us to see the issue and debug.

@ilannn
Copy link

ilannn commented Feb 5, 2021

@marjan-georgiev See stackblitz example here: https://stackblitz.com/edit/ngx-graph-demo-cqnamk?file=src/app/data.ts
When passing position or dimensions to a cluster it has no effect.
Thu passing dimension to a node does affect it, but position doesn't.

The problem indeed could be originated by the sole initialization of position to { x: 0, y: 0} like @matndev mentioned.

@bogend
Copy link

bogend commented Mar 26, 2021

I need to rearrange nodes per drag 'n drop and save their positions.
Nodes must be aligned with the saved positions on next load. I hope fixing this issue will provide the functionality to do so.
Is there any update about this issue?
Thx in advance

@Cafezinho
Copy link

Any updates to this issue? I also need to save the nodes' positions, but initiating the object with the coordinates don't work.
Thanks!

@Kr0san89
Copy link
Contributor

Hi,

until the PR (#463) is merged you can come around the issue, by adding a directive to the Component which overwrites the createGraph function. There you just use the function which is provided in the PR + some rewrites for, this to the component and the id function which is not public. (But just use the code)

function id(): string {
  let newId = (
    '0000' + ((Math.random() * Math.pow(36, 4)) << 0).toString(36)
  ).slice(-4);

  newId = `a${newId}`;

  // ensure not already used
  if (!cache[newId]) {
    cache[newId] = true;
    return newId;
  }

  return id();
}

@Directive({
  selector: '[customGraph]',
})
export class CustomGraphDirective {
  constructor(@Host() @Self() @Optional() public hostSel: GraphComponent) {
    // overwrite custom graph
    hostSel.createGraph = () => {
      hostSel.graphSubscription.unsubscribe();
      hostSel.graphSubscription = new Subscription();
      const initializeNode = (n: Node) => {
        if (!n.meta) {
          n.meta = {};
        }
        if (!n.id) {
          n.id = id();
        }
        if (!n.dimension) {
          n.dimension = {
            width: hostSel.nodeWidth ? hostSel.nodeWidth : 30,
            height: hostSel.nodeHeight ? hostSel.nodeHeight : 30,
          };
          n.meta.forceDimensions = false;
        } else {
          n.meta.forceDimensions =
            n.meta.forceDimensions === undefined
              ? true
              : n.meta.forceDimensions;
        }
        if (!n.position) {
          n.position = {
            x: 0,
            y: 0,
          };
        }

        n.data = n.data ? n.data : {};
        return n;
      };
      hostSel.graph = {
        nodes:
          hostSel.nodes.length > 0
            ? [...hostSel.nodes].map(initializeNode)
            : [],
        clusters:
          hostSel.clusters && hostSel.clusters.length > 0
            ? [...hostSel.clusters].map(initializeNode)
            : [],
        edges:
          hostSel.links.length > 0
            ? [...hostSel.links].map((e) => {
                if (!e.id) {
                  e.id = id();
                }
                return e;
              })
            : [],
      };
      requestAnimationFrame(() => hostSel.draw());
    };
  }
}

Afterwards you can create your own "Layout" which is just an extension of the existing one, but does not use the "layout" function which places the nodes, but rather just recalculates the egdes.

export class GraphLayout extends DagreLayout {
override run(graph: Graph): Graph {
    this.createDagreGraph(graph);
    // dagre.layout(this.dagreGraph); 
    graph.edges.forEach((edge) => this.updateEdge(graph, edge)); // edges have to be computed
    graph.edgeLabels = this.dagreGraph._edgeLabels;

    for (const dagreNodeId in this.dagreGraph._nodes) {
      const dagreNode = this.dagreGraph._nodes[dagreNodeId];
      const node = graph.nodes.find((n) => n.id === dagreNode.id);
      node!.position = {
        x: dagreNode.x,
        y: dagreNode.y,
      };
      node!.dimension = {
        width: dagreNode.width,
        height: dagreNode.height,
      };
    }

    return graph;
  }
}

By adding this layout to ngx-graph and the workaround above you can now position the nodes on your "own".

Hope this helps.

@marjan-georgiev
Copy link
Member

Released in 8.0.3

@ghost
Copy link

ghost commented Jan 12, 2023

@marjan-georgiev - just out of curiosity - is there a way to set the position of nodes without the Layout overriding that position?

I've tried a variety of techniques to get the NodePosition in Node to be set such that it would be passed along to the D3Node x + y fields - but it seems like the layout (exp dagre) always just stomps on that.

I guess in ngx-graph terminology - is there a "none/null" layout - so that your Node positions actually get preserved?

@bruno-lopes
Copy link

Afterwards you can create your own "Layout" which is just an extension of the existing one, but does not use the "layout" function which places the nodes, but rather just recalculates the egdes.

It was my solution. Initially, I don't have the nodes starting positions, so I let my custom layout call layout function. The next times, the custom layout uses the already defined nodes positions, and does not call layout function.

Very thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants