refactor(cxx): move igraph to the standard library#83
Merged
Conversation
Remove the `IGraph` and related classes from the dia diagram editor, and move it to the standard library components.
Extract graph hierarchy handling into its own dedicated class, support multiple hierarchies in a single graph.
Add constraint and group types for the algorithm implementation.
Partially complete rewrite of the graphviz library wrapper to the new IGraph interface.
Rewrite of the graphviz layout compiles. Next step is to add tests to ensure the layout is still working.
working on the test implementation for new layout logic.
- Can run test without crashing - Switched API to return IEdge/IVertex by pointer
Debugging runtime errors with missing edges.
Add operations tracer to the graph layout execution.
Remove separate methods to register and track vertices in the graph, and make the track/untrack method accept a single vertex argument instead of a full list.
Return the graphviz node/edge object from the setter functions to support method chaining.
Initial implementation of the layout algorithm works for the node positioning, and generates a correct debug preview file.
Test simple graphviz layout with two subgraphs and no algorithm switching logic.
Move group layout algorithm object to a private field.
Initial test to verify multiple separate layout algorithms executed in a graphviz visualization.
Add initial implementation of the standalone visualization primitives for the graph interface. The idea is to have a backend/frontend-agnostic representation of the graph structure.
Test visual layout data extraction from trivial graphviz layout.
Extract full visual data from the subgraph layout.
Reuse node attribute style enum for graphviz edges and graphs instead of reading the string values.
Add a partial specialization for types declared with the sub-variants. The JSON serialization will also create a `Kind` field with the enum value. The `.data` field is serialized as before; the change is mainly cosmetic to make it easier to understand what was serialized.
Add XML node builder to the hstd library and use to construct the output SVG for the node visualization, instead of manually interpolating strings.
Style updates for the SVG writer code.
Return the XML nodes from the writer instead of mutating the parent node.
Simplify shape builder logic and final SVG structure, place nodes directly in the output instead of wrapping in a secondary groups.
Executing layout on the graphviz graph with a single layout algorithm now generates correct SVG outpout for simple graphs.
For each sub-graph with its own layout algorithm, use custom graphviz contexts to avoid state pollution.
Running test with multiple mixed layouts in one graph correctly executes nested layouts and then brings them together in the main one. The only visual problem left over is the positioning of the nested nodes -- currently they are overlapping with each other in the graphviz layout.
Don't use "add root subgraph" for constructing nested layouts with the switch. Now the visualization for the multi-layout of graphviz looks correct, although the tests barely cover the relative positioning of the rectangels and nodes with respect to each other. The graph looks like this, approximately. The left side layout is done using 'dot' algorithm, the right one is the circular placement. ``` ............................................... : : : : +---------+ : +---------+..............: : | VERT-3 |<-\ : | VERT-1 | : : +---------+ | : +---------+ : : | | : ^ \ : : v | : | \ : : +---------+ | : | v : : | VERT-4 | | : | +---------+ : : +---------+ | : | | VERT-2 | : : | | : | +---------+ : : v | : | / : : +---------+ | : | / : : | VERT-5 |--/ : +---------+< : : +---------+ : | VERT-0 | : : : +---------+..............: : : : ............................................... ```
Add API to check relative geometry positions to verify the layout algorithm integration logic.
Add the AnchorSpec to bundle the x/y anchors for a given shape into a single structure.
initial test for the fixed absolute offset in the relative constraint.
Preserve the structure of the kiwi expressions until the final layout run. This will simplify debugging and visualization a lot.
- Add a simplified representation of the variable constraints generated by the kiwi constraint build() function - add `checkDistance()` function to the geometry testing to verify the relative positions of the geometries - add DistanceCheck enum to optionally check the distance only along one of the dimensions instead `sqrt(x*x+y*y)` all the time. - some formatting adjustments in the geometry testing header.
Put functions for writing kiwi IR representation to own file.
Update and verify the usage of the arguments for the relative constraint class: the order of arguments must be "fixed, relative". Add test to verify the relative position of the vertex in relation to the parent other vertex.
Make the even gap constraint rectangle anchors configurable at construction instead of using fixed anchor for every element in the constraint.
Implement linear constraint API for the graph and verify trivial relative positioning configuration.
checkpoint
Introduce grouping type for the separate constraint arguments.
Create two set of rectangle visualizations for the graphviz debug output for the kiwi constraint.
Add more information to the constraint fail representation, now it will show all preceding constraints directly in the exception message.
Kiwi IR test failure was caused by incorrectly used vertex ID during
the graph construction. The multi-layout diagram element lacked and
defensive diagnostics to prevent duplicate rectangle IDs in the layout
lanes, and because of that the code
```
root->addConstraint<kw::MultiSeparateConstraint>(root)
->separateHorizontally()
->setSeparationDistance(60)
->addFullLane({g1, g5})
->addFullLane({g2, g6})
->addFullLane({g3, g7})
->addFullLane({g4, g8});
```
created an invalid solution that caused the lowered IR to generate
equation which was reducible to `60 == 0`, which is unsolvable.
```
g2.x + g2.width * 0.5 + 60 == g6.x + g6.width * 0.5
g6.x + g6.width * 0.5 == g2.x + g2.width * 0.5
```
The updated diagnostics representation for the equation helped to
resolve the issue, but it is clear there is a lot work to be done to
make the diagnostics truly useful: current implementation with an
ad-hoc loop that repeatedly tries to diagnose the problems still
generates an overly verbose output that is hard to reason about in
case of complicated graphs.
Exted test coverage for the mixed test graph. Verify the basic visual elements are properly positioned in relation to each other.
Remove code examples and standalone test app from the imgui example app. They used outdated graph abstraction layer and were otherwise very un-intuitive and cumbersome to maintain, far exceeding the complexity of what would reasonably constitute and "example".
The example was incomplete, with the new data model for diagramming it is largely redudant. Properly implementing editing capabilities for the application would be very difficult.
Initial implementation of the eager AST diff algorithm from the org diagram editor component removed in the earlier commit. The new implementation is under-tested but the original code worked correctly.
Fix the missing protobuf headers in the source genration command in the workflow and re-run the source code generation for the current repository.
Add protobuf serialization and deserialization logic for the basic graph geometric elements (rect/point/polygon) and visual elements.
WIP serialization logic for protobuf and avoid graph routing.
Pure virtual method for constraint serialization and de-serialization.
added more protobuf types, added missing serialization
test for KiwiSeparateMultiSeparate passes the write and can generate mostly correct JSON output with the full graph description.
checkpoint
test fixes
add missing ifdef around protobuf, kiwi and adaptagrams usage.
add missing preprocessor guards around protobuf logic.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR completely overhauls and unifies handling of the graph-like structures and visualizations on the library. This is a Phase 1 -- functionally complete, with the core tests added and working, but there are some leftover issues and polish that can be added in some follow-up PR.
The changes in this PR are split into several categories
hstd::library: geometry types, geometry checks, various assertions etc, required to test or implement the main graph structureThe code removed
org_diagramtool -- the idea of using org-mode as a description for the diagram is sound, but theadaptagrams_irand the adaptagrams wrapeprs: the libadaptagrams library, specifically the libcola component, proved to be too buggy and unreliable to use them consistently, so the whole part of the code was completely removed and replaced with the new, kiwi-based layout solution, which is much more precise and flexible (at the expense of having to specify a lot more underlying elements and not having any overlap avoidance mechanism)The python interoperability for the new implementation is going to be done through protobuf data exchange.
More complex parts of the imgui demo applications, or parts that relied on the old graph IR/visualization to function. This code was a poor example (too complex) and a poor tool (not polished enough, unlikely to be finished).
Overall architecture of the new code
Base graph structure
The graph structure is split into three parts: graph containers, graph objects and graphs IDs. Overall API is similar to the DOD in a sense that objects stored in the collection don't have access to the IDs, and relations between objects are managed through a separate lookup tables/map. The design differs from the regular DOD in a way the data is stored: the stores are virtual classes and may implement set/get functionality in any way they can. Additionally, each object is required to have a unique string ID and it might be used for the reverse lookup.
All collection types may store the associated objects, but are not required to.
IGraph: main storage class, holds a collection of vertices and managesVertex <> IDassociation. Holds the list of other collections: vertex hierarchies, edge collections and port collections. TheIGraphhas a single collection of vertices and vertex IDs that are used in any other operations.IEdgeProvider: base class for edge operations. Edges are managed as an additional overlay on top of the existing vertex collections. Does not support hyper-edges.IVertexHierarchy: tree-like arrangement of the nodes in the graph. Primarily used for the visualization purposes, but is designed in a way that would make it usable for a general tree-like data structures.IEdgeCollection: free-form collection of the edges. Each edge has its own unique associated ID, so the multi-edges and self-loops are possible.IPortCollection: collection of the ports associated with every vertex. The port connection can be related to a vertex or an intersection of a vertex and edge. The ports are not structural elements of the graph, instead they are managed as another overlay on top of the edges+vertices structure.Collections associate IDs (
VertexID,PortID,EdgeID) with the corresponding objects (IVertex,IPort,IEdge) respectively, and provide a way to track and un-track the elements in the graph.Each object has a list of attributes: extra data associated with the graph object, in addition to the fields in the derived object itself. The attributes are currently used in the visualization backend to provide a general way to access the visualization data.
If protobuf build is enabled, every graph element: collection and object, can be serialized into a predefined protobuf schema, and de-serialized back from it.
Visualization logic
All graph visualization backends were rewritten under a unified API structure, now it is possible to combine the different visualization backends together, and even partially route the edges across different backends.
The general algorithm for the visualization is based on the
IVisualAttributeand its derivatives.IGroupVisualAttributeholds additional context in form of theIPlacementAlgorithm-derived type. This type implements the actual layout logic and returns a mapping(Vertex|Edge|Port)ID -> ILayoutAttributefor placing the nodes. All results are relative to the parent, so the each layout algorithm does not require any additional context.Visualization backends
org_diagramtool, now it is moved and integrated into the full package.