From b590467f60c6acc454d850b9283d988c9cd70784 Mon Sep 17 00:00:00 2001 From: "Janus C. H. Knudsen" Date: Mon, 6 Oct 2025 18:46:07 +0200 Subject: [PATCH] Improves event grid layout logic Refines the event grid layout algorithm to more accurately identify conflicting events for stacking within the grid. Now uses an expanding search to find chains of conflicting events, ensuring that all events that should be grouped together are correctly identified. --- .workbench/image.png | Bin 13607 -> 10482 bytes .workbench/scenarie9.html | 11 ++++++ src/managers/EventLayoutCoordinator.ts | 47 +++++++++++++++++++++---- 3 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 .workbench/scenarie9.html diff --git a/.workbench/image.png b/.workbench/image.png index 17aa388c8a8dd58a365c560496d2c2764de19619..2d7c2696e9984c0e4649da4840b94746c2ae03e5 100644 GIT binary patch literal 10482 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 U)wTpafWniPQMs2ZrSJKF07&9w*#H0l diff --git a/.workbench/scenarie9.html b/.workbench/scenarie9.html new file mode 100644 index 0000000..1c6faec --- /dev/null +++ b/.workbench/scenarie9.html @@ -0,0 +1,11 @@ +
+ 12:00 - 13:00 + Scenario 9: Event A +
+ 12:30 - 13:30 + Scenario 9: Event B +
+ + 13:15 - 15:00 + Scenario 9: Event C + \ No newline at end of file diff --git a/src/managers/EventLayoutCoordinator.ts b/src/managers/EventLayoutCoordinator.ts index 8d4da38..b210876 100644 --- a/src/managers/EventLayoutCoordinator.ts +++ b/src/managers/EventLayoutCoordinator.ts @@ -54,18 +54,51 @@ export class EventLayoutCoordinator { const firstEvent = remaining[0]; // Find events that could be in GRID with first event - // (start within threshold AND overlap) + // Use expanding search to find chains (A→B→C where each conflicts with next) const gridSettings = calendarConfig.getGridSettings(); const thresholdMinutes = gridSettings.gridStartThresholdMinutes; const gridCandidates = [firstEvent]; - for (let i = 1; i < remaining.length; i++) { - const candidate = remaining[i]; - const startDiff = Math.abs(candidate.start.getTime() - firstEvent.start.getTime()) / (1000 * 60); + let candidatesChanged = true; - // Only add if starts within threshold AND overlaps with firstEvent - if (startDiff <= thresholdMinutes && this.stackManager.doEventsOverlap(firstEvent, candidate)) { - gridCandidates.push(candidate); + // Keep expanding until no new candidates can be added + while (candidatesChanged) { + candidatesChanged = false; + + for (let i = 1; i < remaining.length; i++) { + const candidate = remaining[i]; + + // Skip if already in candidates + if (gridCandidates.includes(candidate)) continue; + + // Check if candidate conflicts with ANY event in gridCandidates + for (const existingCandidate of gridCandidates) { + let hasConflict = false; + + // Check 1: Start-to-start conflict (starts within threshold) + const startToStartDiff = Math.abs(candidate.start.getTime() - existingCandidate.start.getTime()) / (1000 * 60); + if (startToStartDiff <= thresholdMinutes && this.stackManager.doEventsOverlap(candidate, existingCandidate)) { + hasConflict = true; + } + + // Check 2: End-to-start conflict (candidate starts within threshold before existingCandidate ends) + const endToStartMinutes = (existingCandidate.end.getTime() - candidate.start.getTime()) / (1000 * 60); + if (endToStartMinutes > 0 && endToStartMinutes <= thresholdMinutes) { + hasConflict = true; + } + + // Check 3: Reverse end-to-start (existingCandidate starts within threshold before candidate ends) + const reverseEndToStart = (candidate.end.getTime() - existingCandidate.start.getTime()) / (1000 * 60); + if (reverseEndToStart > 0 && reverseEndToStart <= thresholdMinutes) { + hasConflict = true; + } + + if (hasConflict) { + gridCandidates.push(candidate); + candidatesChanged = true; + break; // Found conflict, move to next candidate + } + } } }