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

is it possible to create a tree with nodes on either side #379

Open
dagda1 opened this issue Nov 2, 2018 · 5 comments
Open

is it possible to create a tree with nodes on either side #379

dagda1 opened this issue Nov 2, 2018 · 5 comments

Comments

@dagda1
Copy link

dagda1 commented Nov 2, 2018

I am looking to create something like this:

I need a tree but with the nodes on either side. I've had create success with vxtree but that has one root node.

Is there a way I could spread them out like the diagram below?

tree

@techniq
Copy link
Collaborator

techniq commented Nov 3, 2018

I believe the technique I’ve seen using straight d3.js is to render 2 trees with each root at the middle/overlapping.

For example: http://bl.ocks.org/jdarling/2503502

You should be able to use 2 <Tree /> components to accomplish the same thing. See
https://vx-demo.now.sh/linkTypes for an example to change direction (you should be able to swap the x-scale’s min/max to go right/left.

@dagda1
Copy link
Author

dagda1 commented Nov 22, 2018

@techniq the 2 trees approach did not work for me because I need zooming and panning which was difficult to keep the trees together so I ended up with 1 tree:

https://codesandbox.io/s/znj3ykqwj3

I ended up doing the calculations for the nodes in the ./src/components/NodesFanout component but this feels wrong, is there a better way:

import * as React from "react";
import NodeGroup from "react-move/NodeGroup";
import { StructureItem } from "../../types";
import { Node } from "../Node";
import { HierarchyPointNode } from "d3-hierarchy";
import { HierarchyLabelProps } from "../HierarchyLabel";
import { findIndex } from "lodash";
import { NodeHeight } from "../Tree";
import { findCollapsedParent } from "../../util/node";

const { Group } = require("@vx/group");

export interface NodesProps {
  nodes: HierarchyPointNode<StructureItem>[];
  clickHandler: any;
  shapeLength: number;
  collapse: boolean;
  Label: React.ComponentType<HierarchyLabelProps>;
  LabelCollapsed: React.ComponentType<HierarchyLabelProps>;
}

const positionNodes = (
  node: HierarchyPointNode<StructureItem>,
  nodes: HierarchyPointNode<StructureItem>[]
) => {
  let left: number;
  let top: number;

  if (!node.parent) {
    left = node.y / 2 - NodeHeight;
    top = node.x - NodeHeight;
  } else if (node.data && node.data.isRight) {
    const index = findIndex(nodes, node);
    const lastLeft = nodes[index - 1];

    top = lastLeft.x;
    left = NodeHeight;
  } else {
    top = node.x;
    left = node.y;
  }

  return {
    top: [top],
    left: [left],
    opacity: [1]
  };
};

export const NodesFanout: React.SFC<NodesProps> = ({
  Label,
  LabelCollapsed,
  nodes,
  shapeLength,
  clickHandler,
  collapse
}) => {
  return (
    <NodeGroup
      data={nodes}
      keyAccessor={(d: HierarchyPointNode<StructureItem>) => d.data.id}
      start={(node: HierarchyPointNode<StructureItem>) => {
        let left: number;
        let top: number;

        if (!node.parent) {
          left = node.y / 2 - NodeHeight;
          top = node.x - NodeHeight;
        } else {
          left = node.parent.y / 2;
          top = node.parent.x;
        }

        return {
          top,
          left,
          opacity: 0
        };
      }}
      enter={node => positionNodes(node, nodes)}
      update={node => positionNodes(node, nodes)}
      leave={node => {
        let left: number;
        let top: number;

        const collapsedParent = findCollapsedParent(
          node.parent
        ) as HierarchyPointNode<StructureItem>;

        if (!collapsedParent.parent) {
          left = collapsedParent.y / 2;
          top = collapsedParent.x - NodeHeight;
        } else {
          left = collapsedParent.parent.y / 2;
          top = collapsedParent.parent.x - NodeHeight;
        }

        return {
          top: [top],
          left: [left],
          opacity: [0]
        };
      }}
    >
      {nodes => (
        <Group>
          {nodes.map(({ key, data: node, state }, index) => {
            return (
              <Group
                top={state.top}
                left={state.left}
                key={key}
                opacity={state.opacity}
                className={`node__${index}`}
              >
                <Node
                  Label={Label}
                  LabelCollapsed={LabelCollapsed}
                  node={node}
                  shapeLength={shapeLength}
                  clickHandler={(e: any) => {
                    clickHandler({ e, node });
                  }}
                  key={key}
                  collapse={collapse}
                />
              </Group>
            );
          })}
        </Group>
      )}
    </NodeGroup>
  );
};

My animation is a bit wrong also.

@techniq
Copy link
Collaborator

techniq commented Nov 22, 2018

Hmm, I've not done this myself so I can't be of much more help right now.

You might be able get away using a Polar coordinate system and adjust the size and separation props.

See: #162 (comment) for some experimentation with separation

@aoloo
Copy link

aoloo commented Jan 5, 2021

@dagda1 @techniq I know this is a old post, but I have a use case requiring a tree with nodes on each side using vx/hierarchy. Has anyone come up with a solution for this? I haven't had any luck finding a good example.

@techniq
Copy link
Collaborator

techniq commented Jan 5, 2021

@aoloo Sorry I haven't had this use case myself thus far.

When I'm looking for new examples / approaches I'll typically look at d3 observables and port to React / visx.

This NCAA Bracket example might give you a start, I just quickly read through it but it does use d3.hierarchy / d3.tree:

It appears to be well written and has some good notes. I think it's rendering 4 trees (for each quadrant) and blending them together as 1, but I didn't analyze it deeply.

With hierarchies/trees, you need to have a single root node. You can sometimes work around this by creating a "pseudo" root that you hide visually. You could try that approach, but you'd have to create a tree left to right, as opposed to inside out (with the center being at the middle of the nodes, at opposed to at the root).

This layout might be better represented as a graph structure made of nodes and links, but I think searching for "bracket" helps to identify this type of layout.

Sorry I can't be of more help.

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

4 participants