Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
bd74795
nodes: add the iscollapsed param to the node diplay filtering function
Math-R Oct 7, 2025
211188a
feat: add grouping function for collapsed nodes in TrainrunSectionSer…
Math-R Oct 7, 2025
1ce1c11
feat: implement TrainrunSection grouping in view layer for collapsed …
Math-R Oct 7, 2025
615848a
fix: implement custom path calculation for collapsed node chains
Math-R Oct 7, 2025
16a75d5
fix : calc without temporal dto changes + avoid manual handroll
Math-R Oct 16, 2025
7942551
fixup! fix start node in groupTrainrunSectionsIntoChains()
emersion Oct 31, 2025
d991738
fixup! only create a single TrainrunSectionViewObject per collapsed c…
emersion Oct 31, 2025
b976eff
Add TrainrunSectionViewObject.getTrainrun()
emersion Oct 31, 2025
b6d3c54
Store full trainrun section chain in TrainrunSectionViewObject
emersion Oct 31, 2025
a32d2cc
fixup! simplify text nodes position computation
emersion Oct 31, 2025
b28b18f
fixup! drop unnecessary getCollapsedChainValueToShow() wrapper
emersion Oct 31, 2025
bc3998d
fixup! drop getAllSectionsInCollapsedChain()
emersion Oct 31, 2025
64512f0
fixup! fix path target position in TrainrunSectionViewObject.generate…
emersion Nov 5, 2025
6658713
Fix missing source arrival in TrainrunSectionViewObject.generateKey()
emersion Nov 5, 2025
8df2822
fixup! use last section in TrainrunSectionViewObject.generateKey()
emersion Nov 5, 2025
e127d6b
fixup! drop TrainrunSectionsView.getCollapsedChainPath()
emersion Nov 5, 2025
09524ec
fixup! drop updateTrainrunSectionPathForCollapsedChain()
emersion Nov 5, 2025
dd80b4c
Drop duplicate frequency in TrainrunSectionViewObject.generateKey()
emersion Nov 7, 2025
e3dc786
Fix missing source arrival consecutive time in TrainrunSectionViewObj…
emersion Nov 7, 2025
1aa6c9a
Implement new getTravelTime method to compute total travel time to Tr…
Math-R Nov 13, 2025
8aa047c
Merge getTrainrunSectionValueToShow and getTrainrunSectionValueToShow…
Math-R Nov 13, 2025
4be737e
Drop applyBasicFiltering after merging the logic in transformPath
Math-R Nov 13, 2025
ed4f922
refactor: throw error on cycle detection in trainrun section chains
Math-R Nov 14, 2025
2279e88
refactor: merge duplicate text positioning functions
Math-R Nov 17, 2025
2ae95ab
fix: fix the calculation of collapsedsection total travel time
Math-R Nov 17, 2025
203e3ad
Update trainrunsectionViewObject.gettraveltime to handle stop times
Math-R Nov 19, 2025
8ea0098
refactor : (gettrainrunvaluetoshow) merge switch cases and remove Tr…
Math-R Nov 19, 2025
328e7e5
refactor: remove special case for multi-section paths in transformPath
Math-R Nov 19, 2025
3f64cfc
fix copy/paste behaviour to keep node collapsed if they are
Math-R Nov 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/app/services/data/node.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export class NodeService implements OnDestroy {
node.betriebspunktName,
node.fullName,
node.labelIds,
node.isCollapsed,
);
} else {
const existingLabels = existingNode.getLabelIds();
Expand Down Expand Up @@ -223,6 +224,7 @@ export class NodeService implements OnDestroy {
betriebspunktName?: string,
fullName?: string,
labelIds?: number[],
isCollapsed?: boolean,
enforceUpdate = true,
): Node {
const alignedPosition = NodeService.alginNodeToRaster(new Vec2D(positionX, positionY));
Expand All @@ -246,6 +248,9 @@ export class NodeService implements OnDestroy {
if (labelIds !== undefined) {
node.setLabelIds(labelIds);
}
if (isCollapsed !== undefined) {
node.setIsCollapsed(isCollapsed);
}
this.nodesStore.nodes.push(node);
if (enforceUpdate) {
this.nodesUpdated();
Expand Down
5 changes: 5 additions & 0 deletions src/app/services/data/trainrun.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
BackwardTrainrunIterator,
NextExpandedStopIterator,
TrainrunIterator,
ExpandedTrainrunIterator,
} from "../util/trainrun.iterator";
import {LogService} from "../../logger/log.service";
import {LabelService} from "./label.service";
Expand Down Expand Up @@ -805,6 +806,10 @@ export class TrainrunService {
public getBackwardNonStopIterator(node: Node, trainrunSection: TrainrunSection) {
return new BackwardNonStopTrainrunIterator(this.logService, node, trainrunSection);
}

public getExpandedIterator(node: Node, trainrunSection: TrainrunSection) {
Copy link

Choose a reason for hiding this comment

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

I'm not sure this function is used anymore

Copy link
Author

Choose a reason for hiding this comment

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

True, good catch i'll remove it

return new ExpandedTrainrunIterator(this.logService, node, trainrunSection);
}

// For each trainrun, get iterator from the smallest consecutiveTime.
public getRootIterators(): Map<number, TrainrunIterator> {
Expand Down
57 changes: 57 additions & 0 deletions src/app/services/data/trainrunsection.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1490,4 +1490,61 @@ export class TrainrunSectionService implements OnDestroy {
trainrunSection.routeEdgeAndPlaceText();
trainrunSection.convertVec2DToPath();
}

/**
* Groups consecutive TrainrunSections that have collapsed nodes between them
* into chains. Each chain starts and ends with a non-collapsed node.
* Start and end nodes can be accessed via: sections[0].getSourceNode() and sections[sections.length - 1].getTargetNode()
* @param trainrunSections List of TrainrunSections to group
* @returns Array of section chains
*/
groupTrainrunSectionsIntoChains(trainrunSections: TrainrunSection[]): TrainrunSection[][] {
const groups: TrainrunSection[][] = [];
const visitedSections = new Set<number>();

trainrunSections.forEach((section) => {
if (visitedSections.has(section.getId())) {
return;
}

const backwardIterator = this.trainrunService.getBackwardIterator(
section.getTargetNode(),
section,
);
while (backwardIterator.hasNext() && backwardIterator.current().node.getIsCollapsed()) {
backwardIterator.next();
}
const startNode = backwardIterator.current().node;
const startSection = backwardIterator.current().trainrunSection;

// Build chain using TrainrunIterator to leverage existing graph traversal
const chain: TrainrunSection[] = [];
const iterator = this.trainrunService.getIterator(startNode, startSection);

// Traverse the trainrun and collect sections with collapsed intermediate nodes
while (iterator.hasNext()) {
const pair = iterator.next();

if (visitedSections.has(pair.trainrunSection.getId())) {
throw new Error(
`Cycle detected in trainrun section chain: section ${pair.trainrunSection.getId()} already visited for trainrun ${pair.trainrunSection.getTrainrunId()}`,
);
}

chain.push(pair.trainrunSection);
visitedSections.add(pair.trainrunSection.getId());

// Stop if we reach a non-collapsed node (end of collapsed chain)
if (!pair.node.getIsCollapsed()) {
break;
}
}

if (chain.length > 0) {
groups.push(chain);
}
});

return groups;
}
}
14 changes: 14 additions & 0 deletions src/app/services/util/trainrun.iterator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,17 @@ export class BackwardNonStopTrainrunIterator extends BackwardTrainrunIterator {
return super.next();
}
}

export class ExpandedTrainrunIterator extends TrainrunIterator {
public next(): TrainrunSectionNodePair {
// Continue traversing only if the current node is collapsed
// Stop when we reach an expanded (non-collapsed) node
if (!this.pointerElement.node.getIsCollapsed()) {
// The trainrun has reached an expanded (non-collapsed) node and break the forward iteration
this.currentElement = Object.assign({}, this.pointerElement);
this.pointerElement = new TrainrunSectionNodePair(undefined, undefined);
return this.currentElement;
}
return super.next();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,12 @@ export class ConnectionsView {
}

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?

if (node.getIsCollapsed()) {
return false;
}

const trainrunSection1: TrainrunSection = node.getPort(con.getPortId1()).getTrainrunSection();
const trainrunSection2: TrainrunSection = node.getPort(con.getPortId2()).getTrainrunSection();
const filterTrainrun1 = this.editorView.filterTrainrun(trainrunSection1.getTrainrun());
Expand Down
44 changes: 22 additions & 22 deletions src/app/view/editor-main-view/data-views/d3.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,21 @@ export class D3Utils {
d3.selectAll(StaticDomTags.EDGE_LINE_ARROW_DOM_REF)
.filter(
(d: TrainrunSectionViewObject) =>
d !== undefined && d.trainrunSection.getTrainrunId() === trainrunSection.getTrainrunId(),
d !== undefined && d.getTrainrun().getId() === trainrunSection.getTrainrunId(),
)
.classed(StaticDomTags.TAG_HOVER, true);

d3.selectAll(StaticDomTags.EDGE_LINE_DOM_REF)
.filter(
(d: TrainrunSectionViewObject) =>
d !== undefined && d.trainrunSection.getTrainrunId() === trainrunSection.getTrainrunId(),
d !== undefined && d.getTrainrun().getId() === trainrunSection.getTrainrunId(),
)
.classed(StaticDomTags.TAG_HOVER, true);

d3.selectAll(StaticDomTags.EDGE_ROOT_CONTAINER_DOM_REF)
.filter(
(d: TrainrunSectionViewObject) =>
d !== undefined && d.trainrunSection.getTrainrunId() === trainrunSection.getTrainrunId(),
d !== undefined && d.getTrainrun().getId() === trainrunSection.getTrainrunId(),
)
.classed(StaticDomTags.TAG_HOVER, true);

Expand Down Expand Up @@ -90,20 +90,20 @@ export class D3Utils {
d3.selectAll(StaticDomTags.EDGE_LINE_ARROW_DOM_REF)
.filter(
(d: TrainrunSectionViewObject) =>
d !== undefined && d.trainrunSection.getTrainrunId() === trainrunSection.getTrainrunId(),
d !== undefined && d.trainrunSections[0].getTrainrunId() === trainrunSection.getTrainrunId(),
)
.classed(StaticDomTags.TAG_HOVER, false);
d3.selectAll(StaticDomTags.EDGE_LINE_DOM_REF)
.filter(
(d: TrainrunSectionViewObject) =>
d !== undefined && d.trainrunSection.getTrainrunId() === trainrunSection.getTrainrunId(),
d !== undefined && d.trainrunSections[0].getTrainrunId() === trainrunSection.getTrainrunId(),
)
.classed(StaticDomTags.TAG_HOVER, false);

d3.selectAll(StaticDomTags.EDGE_ROOT_CONTAINER_DOM_REF)
.filter(
(d: TrainrunSectionViewObject) =>
d !== undefined && d.trainrunSection.getTrainrunId() === trainrunSection.getTrainrunId(),
d !== undefined && d.trainrunSections[0].getTrainrunId() === trainrunSection.getTrainrunId(),
)
.classed(StaticDomTags.TAG_HOVER, false);

Expand Down Expand Up @@ -347,7 +347,7 @@ export class D3Utils {
if (d === undefined) {
return false;
}
return d.trainrunSection.getId() === trainrunSection.getId();
return d.trainrunSections[0].getId() === trainrunSection.getId();
})
.classed(StaticDomTags.TAG_SELECTED, false)
.classed(StaticDomTags.TAG_HOVER, false)
Expand All @@ -359,7 +359,7 @@ export class D3Utils {
if (d === undefined) {
return false;
}
return d.trainrunSection.getId() === trainrunSection.getId();
return d.trainrunSections[0].getId() === trainrunSection.getId();
})
.classed(StaticDomTags.TAG_SELECTED, false)
.classed(StaticDomTags.TAG_HOVER, false)
Expand All @@ -371,7 +371,7 @@ export class D3Utils {
if (d === undefined) {
return false;
}
return d.trainrunSection.getId() === trainrunSection.getId();
return d.trainrunSections[0].getId() === trainrunSection.getId();
})
.classed(StaticDomTags.TAG_SELECTED, false)
.classed(StaticDomTags.TAG_HOVER, false)
Expand All @@ -383,7 +383,7 @@ export class D3Utils {
if (d === undefined) {
return false;
}
return d.trainrunSection.getId() === trainrunSection.getId();
return d.trainrunSections[0].getId() === trainrunSection.getId();
})
.classed(StaticDomTags.EDGE_LINE_GRAYEDOUT, true);

Expand All @@ -394,8 +394,8 @@ export class D3Utils {
return false;
}
return (
d.trainrunSection.getId() === trainrunSection.getId() &&
d.trainrunSection.getSourceNodeId() === grayoutEdgeLinePinNode.getId()
d.trainrunSections[0].getId() === trainrunSection.getId() &&
d.trainrunSections[0].getSourceNodeId() === grayoutEdgeLinePinNode.getId()
);
})
.classed(StaticDomTags.EDGE_LINE_GRAYEDOUT, true);
Expand All @@ -405,8 +405,8 @@ export class D3Utils {
return false;
}
return (
d.trainrunSection.getId() === trainrunSection.getId() &&
d.trainrunSection.getTargetNodeId() === grayoutEdgeLinePinNode.getId()
d.trainrunSections[0].getId() === trainrunSection.getId() &&
d.trainrunSections[0].getTargetNodeId() === grayoutEdgeLinePinNode.getId()
);
})
.classed(StaticDomTags.EDGE_LINE_GRAYEDOUT, true);
Expand All @@ -419,7 +419,7 @@ export class D3Utils {
if (d === undefined) {
return false;
}
return d.trainrunSection.getId() === trainrunSection.getId();
return d.trainrunSections[0].getId() === trainrunSection.getId();
})
.classed(StaticDomTags.TAG_SELECTED, true)
.classed(StaticDomTags.TAG_HOVER, false)
Expand All @@ -431,7 +431,7 @@ export class D3Utils {
if (d === undefined) {
return false;
}
return d.trainrunSection.getId() === trainrunSection.getId();
return d.trainrunSections[0].getId() === trainrunSection.getId();
})
.classed(StaticDomTags.TAG_SELECTED, true)
.classed(StaticDomTags.TAG_HOVER, false)
Expand All @@ -443,7 +443,7 @@ export class D3Utils {
if (d === undefined) {
return false;
}
return d.trainrunSection.getId() === trainrunSection.getId();
return d.trainrunSections[0].getId() === trainrunSection.getId();
})
.classed(StaticDomTags.TAG_SELECTED, true)
.classed(StaticDomTags.TAG_HOVER, false)
Expand All @@ -455,7 +455,7 @@ export class D3Utils {
if (d === undefined) {
return false;
}
return d.trainrunSection.getId() === trainrunSection.getId();
return d.trainrunSections[0].getId() === trainrunSection.getId();
})
.classed(StaticDomTags.EDGE_LINE_GRAYEDOUT, false);

Expand All @@ -466,8 +466,8 @@ export class D3Utils {
return false;
}
return (
d.trainrunSection.getId() === trainrunSection.getId() &&
d.trainrunSection.getSourceNodeId() === grayoutEdgeLinePinNode.getId()
d.trainrunSections[0].getId() === trainrunSection.getId() &&
d.trainrunSections[0].getSourceNodeId() === grayoutEdgeLinePinNode.getId()
);
})
.classed(StaticDomTags.EDGE_LINE_GRAYEDOUT, false);
Expand All @@ -477,8 +477,8 @@ export class D3Utils {
return false;
}
return (
d.trainrunSection.getId() === trainrunSection.getId() &&
d.trainrunSection.getTargetNodeId() === grayoutEdgeLinePinNode.getId()
d.trainrunSections[0].getId() === trainrunSection.getId() &&
d.trainrunSections[0].getTargetNodeId() === grayoutEdgeLinePinNode.getId()
);
})
.classed(StaticDomTags.EDGE_LINE_GRAYEDOUT, false);
Expand Down
2 changes: 1 addition & 1 deletion src/app/view/editor-main-view/data-views/editor.view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export class EditorView implements SVGMouseControllerObserver {
this.nodesView = new NodesView(this);
this.transitionsView = new TransitionsView(this);
this.connectionsView = new ConnectionsView(this);
this.trainrunSectionsView = new TrainrunSectionsView(this);
this.trainrunSectionsView = new TrainrunSectionsView(this, trainrunSectionService);
this.trainrunSectionPreviewLineView = new TrainrunSectionPreviewLineView(
nodeService,
filterService,
Expand Down
2 changes: 1 addition & 1 deletion src/app/view/editor-main-view/data-views/nodes.view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export class NodesView {
}

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

displayNodes(inputNodes: Node[]) {
Expand Down
Loading
Loading