Skip to content

Conversation

Math-R
Copy link

@Math-R Math-R commented Oct 7, 2025

Description

This PR implements the collapsed nodes functionality for the Netzgrafik editor, allowing intermediate nodes to be hidden while maintaining proper visual representation of train routes.

Features Implemented

1. Node Model Enhancement

  • Added isCollapsed field to Node model with getter/setter methods
  • Integrated collapsed state into node serialization/deserialization
  • Maintains backward compatibility with existing data

2. Visual Filtering System

  • NodesView: Filter out collapsed nodes from visual display
  • ConnectionsView: Hide connections associated with collapsed nodes
  • TransitionsView: Hide transitions for collapsed nodes
  • Collapsed nodes become completely invisible in the editor

3. Intelligent Section Grouping

  • TrainrunSectionService.groupTrainrunSectionsIntoChains(): Algorithm to group sections with collapsed intermediate nodes
  • Automatically detects chains like A→B→C→D where B,C are collapsed
  • Creates grouped objects with proper start/end node references

4. Direct Path Calculation

  • TrainrunSectionsView.createViewObjectForCollapsedChain(): Creates direct visual paths A→D bypassing collapsed nodes
  • Automatic routing that circumvents collapsed intermediate nodes
  • Proper port calculation for direct connections

User Experience

  • Nodes marked as collapsed become invisible
  • Train routes A→B→C→D (with B,C collapsed) display as direct A→D connections
  • Interface remains coherent and schedules are preserved
  • No impact on existing data or workflows

Technical Implementation

  • Zero breaking changes to existing data structures
  • Clean separation between data model and visual representation
  • Proper TypeScript typing throughout

Issues

Related to feature request for collapsed nodes functionality to simplify complex network display.

Checklist

  • This PR contains a description of the changes I'm making
  • I've read the Contribution Guidelines
  • Core functionality implemented and tested: Node filtering, section grouping, path calculation working
  • I've added tests for changes or features I've introduced (comprehensive tests planned for follow-up PR)
  • I documented any high-level concepts I'm introducing in documentation/ (documentation to be added in follow-up)
  • TypeScript compilation clean: No compilation errors
  • Backward compatibility preserved: No breaking changes to existing data
  • Ready for functional testing: Core collapsed nodes feature fully operational

Next Steps (Future PRs)

  • Node creation UI for collapsed nodes
  • Legacy file compatibility (numberOfStops conversion)
  • Comprehensive unit test suite
  • User documentation and examples
  • Port assignment optimization for parallel routing

@Math-R Math-R force-pushed the mrd/modify-node-display branch 3 times, most recently from cd61d5d to 087bc7c Compare October 7, 2025 16:39
- Add createViewObjectForCollapsedChain() static method to handle path routing between start/end nodes

- Replace standard TrainrunSectionViewObject creation with custom method for collapsed chains

- Path now correctly goes from first visible node to last visible node instead of using primary section's path
@Math-R Math-R force-pushed the mrd/modify-node-display branch from 087bc7c to 185666b Compare October 7, 2025 16:41
@Math-R Math-R marked this pull request as ready for review October 7, 2025 16:41
@Math-R Math-R requested a review from aiAdrian as a code owner October 7, 2025 16:41
@Math-R Math-R self-assigned this Oct 7, 2025
@aiAdrian
Copy link
Contributor

aiAdrian commented Oct 7, 2025

Thanks you for the new feature - would it be possible to get a live demo?

@aiAdrian
Copy link
Contributor

aiAdrian commented Oct 7, 2025

I haven't done a full review yet, but at first glance I don't understand why:

  • TrainrunSectionViewObject → there's no change trigger for isCollapsed

  • NodeViewObject → there's no change trigger for isCollapsed

It seems there's no change status (generateKey) related to isCollapsed. These objects are supposed to signal that something has changed so that a visual update can be triggered (i.e., only render what has changed to improve performance). I assume that if we don't explicitly trigger the update (mark the object as changed), it won't be updated (rendered).

  displayNodes(inputNodes: Node[]) {
    const nodes = inputNodes.filter(
      (n) =>
        this.editorView.doCullCheckPositionsInViewport([
          new Vec2D(n.getPositionX(), n.getPositionY()),
          new Vec2D(n.getPositionX() + n.getNodeWidth(), n.getPositionY()),
          new Vec2D(n.getPositionX(), n.getPositionY() + n.getNodeHeight()),
          new Vec2D(n.getPositionX() + n.getNodeWidth(), n.getPositionY() + n.getNodeHeight()),
        ]) && this.filterNodesToDisplay(n),
    );

    const group = this.nodeGroup
      .selectAll(StaticDomTags.NODE_ROOT_CONTAINER_DOM_REF)
      .data(this.createViewNodeDataObjects(nodes), (n: NodeViewObject) => n.key);

... 

(see: https://github.com/OpenRailAssociation/netzgrafik-editor-frontend/blob/main/src/app/view/editor-main-view/data-views/nodes.view.ts#L106)


It would be very helpful if we could add some tests to verify that changes to isCollapsed and it's functionality are functioning as intended.

Even if this doesn't directly affect the visualization layer, it’s still important for the underlying service methods, especially in areas like data migration. The base functionality should be properly tested to ensure robustness and maintainability.


const node: Node = this.editorView.getNodeFromConnection(con);

// filter if node is collapsed - do not show connections for collapsed nodes
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiny nit: why the comment here and not in transitions.view.ts?

@louisgreiner
Copy link
Contributor

Context: This PR implements part of the 3 first sections of the implementation plan given here

Copy link
Contributor

@louisgreiner louisgreiner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, not tested.

Maybe, instead of fully hiding the nodes when isCollapsed is true, you should display them as a "Dot", as it is shown here (mock-up from main issue). But maybe you've planned to do it in the next PR, ignore this comment if so.


filterNodesToDisplay(node: Node): boolean {
return this.editorView.isNodeVisible(node);
return this.editorView.isNodeVisible(node) && !node.getIsCollapsed();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: isCollapsed() would be nice instead of getIsCollapsed()

Comment on lines 1479 to 1483
groupTrainrunSectionsIntoChains(trainrunSections: TrainrunSection[]): Array<{
sections: TrainrunSection[];
startNode: Node;
endNode: Node;
}> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice function, very readable. I wonder if we could/should create a new type for the group object, if it will be reused elsewhere

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion, as I simplified it thanks to @emersion it's no longer necessary

Copy link
Member

@emersion emersion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for splitting the changes into small commits! Here are a few comments.

try {
// Set temporary nodes for path calculation
primarySection.setSourceNode(startNode);
primarySection.setTargetNode(endNode);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I'm not a fan of temporarily mutating a trainrun section to make existing functions such as routeEdgeAndPlaceText() behave like we want. This mutates the DTO and doesn't only affect rendering. I would prefer to stick to the solution outlined in the implementation plan instead.
  2. I don't think this shows times correctly. I've created this trainrun:
out

After adding "isCollapsed": true to the "OL" node, I get this:

out

The total time as well as the arrival/departure times on the top left of "OL" are wrong. They should read: travel time 2 (1 + 1), arrival time 4, departure time 56.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried something without modiying the dto. Plus i adpt the horaries to respect the stop even if they are collapsed. Tell me what you think of thoses changes.

Comment on lines 1481 to 1482
startNode: Node;
endNode: Node;
Copy link
Member

@emersion emersion Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: do we really need to store startNode/endNode if they're that easy to grab from the chain?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true removed it

}> = [];
const visitedSections = new Set<number>();

trainrunSections.forEach((section) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of hand-rolling our own graph traversal, can we use TrainrunService.getIterator()? (Perhaps even introduce a new ExpandedTrainrunIterator, just like we already have NonStopTrainrunIterator? Not sure it's worth the effort.)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure i modified it thanks

Copy link

@Synar Synar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made a pass, nothing to add for now. I'll do another pass after you resolved @emersion's comments.

@Math-R Math-R force-pushed the mrd/modify-node-display branch 3 times, most recently from c4cc009 to 6145bff Compare October 16, 2025 13:06
@Math-R
Copy link
Author

Math-R commented Oct 16, 2025

I haven't done a full review yet, but at first glance I don't understand why:

  • TrainrunSectionViewObject → there's no change trigger for isCollapsed
  • NodeViewObject → there's no change trigger for isCollapsed

It seems there's no change status (generateKey) related to isCollapsed. These objects are supposed to signal that something has changed so that a visual update can be triggered (i.e., only render what has changed to improve performance). I assume that if we don't explicitly trigger the update (mark the object as changed), it won't be updated (rendered).

  displayNodes(inputNodes: Node[]) {
    const nodes = inputNodes.filter(
      (n) =>
        this.editorView.doCullCheckPositionsInViewport([
          new Vec2D(n.getPositionX(), n.getPositionY()),
          new Vec2D(n.getPositionX() + n.getNodeWidth(), n.getPositionY()),
          new Vec2D(n.getPositionX(), n.getPositionY() + n.getNodeHeight()),
          new Vec2D(n.getPositionX() + n.getNodeWidth(), n.getPositionY() + n.getNodeHeight()),
        ]) && this.filterNodesToDisplay(n),
    );

    const group = this.nodeGroup
      .selectAll(StaticDomTags.NODE_ROOT_CONTAINER_DOM_REF)
      .data(this.createViewNodeDataObjects(nodes), (n: NodeViewObject) => n.key);

... 

(see: https://github.com/OpenRailAssociation/netzgrafik-editor-frontend/blob/main/src/app/view/editor-main-view/data-views/nodes.view.ts#L106)

It would be very helpful if we could add some tests to verify that changes to isCollapsed and it's functionality are functioning as intended.

Even if this doesn't directly affect the visualization layer, it’s still important for the underlying service methods, especially in areas like data migration. The base functionality should be properly tested to ensure robustness and maintainability.

Sorry for the late answer. i'm not sure to completly understand your first point. And sure, I will add some test in the next commit

@Math-R Math-R force-pushed the mrd/modify-node-display branch from 6145bff to a1d2b05 Compare October 16, 2025 14:42
@Math-R Math-R force-pushed the mrd/modify-node-display branch from a1d2b05 to fa9b231 Compare October 16, 2025 15:03
@Math-R Math-R requested review from Synar and emersion October 16, 2025 15:04
@Math-R Math-R requested a review from louisgreiner October 16, 2025 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants