(a fork of RedEye's Graph Component)
The RedEye Graph has three main functional parts:
- The
HierarchicalGraphData
store - responsible for parsing the hierarchical graph data, storing state, and issuing state change events - The multiple
GraphRenderer
- responsible for d3 force-directed simulations and layout - The
GraphHandler
- responsible for managing the relationships between the HierarchicalGraphData store and all the GraphRenderers
The GraphHandler
manages relationships between the HierarchicalGraphData
store and all the GraphRenderers
. It also handles global d3 based events like pan, zoom, drag, and text occlusion.
Events update state in the following order:
- d3 events (click, hover, drag) occur in the GraphHierarchical renderers, wired to methods of the GraphHandler.
- The GraphHandler calls update methods on the HierarchicalGraphData store.
- HierarchicalGraphData store updates the relevant data, and fires an on{Data}Change event to the GraphHandler
- The GraphHandler calls update methods on the GraphHierarchical renderers to update the relevant visual aspects
This circular flow provides consistent data and visual updates (steps 3 & 4) when HierarchicalGraphData update methods are called from outside the GraphHandler (instead of step 2). S
The Hierarchial GraphData class is responsible for parsing unstructured data in to the correct format, and for maintaining interaction state state via methods and events
The hierarchicalGraphDataParser
combines a parent-child hierarchical tree graph data structure with an unstructured node-link graph.
TODO: this is a super confusing explanation. This needs to be combined with the Terms section, cross linked with the code, and some diagrams would probably be helpful.
GraphData is supplied to the initial parser that will produce serializable js object. This Serializable object can optionally contain the predefined x,y coordinates of each node to restore an pre-existing layout. The resulting serializable data is then passed to a stratify method that replaces string id references with pointers to their respective js objects.
The parsing groups nodes based on their given 'parent' id. This parsing also creates a third intermediary level that further groups nodes within each parent based on their 'signature.' A signature is a property given to a node based on how it shares links to other nodes on the parent level, ie how it connects to a cousin node: through its parent to its uncle to its uncle's child, the cousin. This grouping can significant simplify the simulation computation required to layout the nodes by drawing all grouped child nodes as a single unit.
The resulting data is a combination of two trees. Each tree having four levels: root > super > group > sub
- a node-link-tree - with each tree-node containing child nodes links between those child noes. This data is used to power each nested d3 node-link simulation layout.
- a link-tree - where each tree-node is a link containing the sub simulation's child links.
These two trees are blended together where
- each link in the node-link-tree is a node in the link-tree
- each the source-node and target-node of each link in the link-tree are nodes in the node-link-tree
There is an additional base-link data item representing the original link between to nodes before parsing occurred. These base links contain references to two leaf nodes of the link-tree representing the lowest level of the subdivided link. The ancestors of these two leaves will contain the full visual link between any two nodes in the hierarchical graph.
- Hierarchical Prefixes
- Root - A single node that is the root of the tree hierarchy
- Super - The top level of hierarchy representing nodes that contain sets of child subnodes. These are machines,computers,hosts,servers,etc...
- Group - The middle level of the hierarchy representing 'groups' of subnodes. This level is generated during data parsing to group subnodes a common 'link signature.' Read more about signatures [HERE].
- Sub - The bottom level of the hierarchy.
- BaseLink - the original link between 2 nodes before parsing in the data structure. this is not directly visualized, but contains references to all ParentLinks and SiblingLinks at all levels of the graph that represent that BaseLink
- BaseNode - an original Node - same as a SubNode
- KeyNode - a node meant to be visualized. ie not a ParentLinkNode
- SiblingLink - a link between 2 KeyNodes that share a parent
- ParentLink - a link between a KeyNode and a ParentLinkNode
- ParentLinkNode - a node representing a SiblingLink between a parent node and an uncle node. This node type's x,y coordinates are determined by the layout of the parent simulation's related SiblingLink
The responsibility for updating the HierarchicalGraphData
state is shared between the HierarchicalGraphData
and the GraphRenderer
simulations.
HierarchicalGraphData
is responsible for updating Interaction and Time states- Interaction state - Preview, Selected - which are analogous to hover and click respectively
- Time state - Past, Present, Future - which are analogous to a beacon being dead, active, or future respectively
GraphRenderer
s are responsible for updating the layout properties from d3'sSimulationNodeDatum
andSimulationLinkDatum
: x, y, vx, vy, fx, fy, and r.
HierarchicalGraphRenderer
s use d3 force directed simulations to render a node link diagram of their data and also to create a nested set of child HierarchicalGraphRenderer
s where appropriate.
There are 3 GraphRenderers that extend an 'abstract' HierarchicalGraphRenderer
base class. They follow the same hierarchical structure as the HierarchicalGraphData:
SuperGraphRenderer
- renders the master graph. Nodes are 'expandable,' and hide/show their contents on interaction.GroupGraphRenderer
- responsible for visually grouping the SubNodes. These nodes are not intractable, and only exist to improve layout and render performance.SubGraphRenderer
- These node's have a much more forgiving force layout, typically ending up in an evenly dispersed cloud. Dragging one of these nodes actually moves its parentGroupGraphRenderer
node.- (The
GraphHandler
manages the Root data node)
The graph layout is computed using d3 force directed simulations. The forces applied are different for each GraphRenderer type, but the alpha related settings are kept in sync for all GraphRenderers via the HierarchicalGraphRenderer
base class. There are also methods for managing the starting and stopping the simulation: reheat
, cool
, and freeze
.
Chunking the simulations into a hierarchical tree reduces the number of objects in each simulation. Simulations perform a many-to-many comparison for each tick
at O(n!), so the fewer the objects, the better the performance. While the simulation performance is still a major consideration, they only calculate the x,y coordinates and run relatively quickly. The svg DOM update cycle is much slower. DOM update performance is improved here by removing non-visible elements from the DOM tree. See the hideLayout
/showLayout
rendering methods below.
Typically referred to as Draw{Method}
in the code, these methods control the layout and styling of svg via d3's declarative selection chains.
There are several types of DrawMethods responsible for different types of rendering updates:
drawLayout
- updates the x,y layout when any simulation updates occurdrawInteraction
state - updates the interaction classes (preview and selection) based on the current data statedrawTime
state - updates the time classes (past, present, future) based on the current data statehideLayout
/showLayout
- Removes the graph from the DOM tree to improve performance and visualization legibility
Each Draw methods also has an option to recursively draw updates to all its children, typically referred to as {drawMethod}AllChildren
.
- The
textOcclusion
utility manages hiding text for all rendered text in allGraphRenderer
s after each layout. It is managed by theGraphHandler
There is no exported default style.css sheet. The one in this project is for testing purposes only. a js object of css classNames is exported instead as RedEyeGraphClassNames
.