summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ui/windows/plot_window.py76
1 files changed, 59 insertions, 17 deletions
diff --git a/ui/windows/plot_window.py b/ui/windows/plot_window.py
index b933df1..80a1624 100644
--- a/ui/windows/plot_window.py
+++ b/ui/windows/plot_window.py
@@ -19,6 +19,7 @@ from __future__ import annotations
import json
from copy import deepcopy
from dataclasses import dataclass, field, asdict
+from math import gcd
from typing import Dict, List, Optional, Tuple
from PyQt6.QtWidgets import (
@@ -120,20 +121,22 @@ def build_default_layout(registry: DeviceRegistry,
# hsplit = side-by-side (first=left, second=right)
# vsplit = top-bottom (first=top, second=bottom)
+def _tree_default_range(indices: List[int]) -> dict:
+ if len(indices) == 1:
+ return {"kind": "leaf", "pane": indices[0]}
+ mid = len(indices) // 2
+ return {"kind": "vsplit", "ratio": 0.5,
+ "first": _tree_default_range(indices[:mid]),
+ "second": _tree_default_range(indices[mid:])}
+
+
def _tree_default(n: int) -> Optional[dict]:
- """Build default equal vsplit chain for n panes."""
+ """Build balanced binary vsplit tree with equal ratios for n panes."""
if n == 0:
return None
- leaves = [{"kind": "leaf", "pane": i} for i in range(n)]
if n == 1:
- return leaves[0]
- node = leaves[-1]
- for i in range(n - 2, -1, -1):
- remaining = n - i
- ratio = 1.0 / remaining
- node = {"kind": "vsplit", "ratio": ratio,
- "first": leaves[i], "second": node}
- return node
+ return {"kind": "leaf", "pane": 0}
+ return _tree_equalize_ratios(_tree_default_range(list(range(n))))
def _tree_depth(node: dict) -> int:
@@ -186,6 +189,32 @@ def _tree_reindex(node: dict, removed_idx: int) -> dict:
"second": _tree_reindex(node["second"], removed_idx)}
+def _tree_leaf_count(node: dict) -> int:
+ if node["kind"] == "leaf":
+ return 1
+ return _tree_leaf_count(node["first"]) + _tree_leaf_count(node["second"])
+
+
+def _tree_equalize_ratios(node: dict) -> dict:
+ """Reset each split's ratio so both subtrees get equal space per leaf."""
+ if node["kind"] == "leaf":
+ return node
+ first = _tree_equalize_ratios(node["first"])
+ second = _tree_equalize_ratios(node["second"])
+ n1 = _tree_leaf_count(first)
+ n2 = _tree_leaf_count(second)
+ return {**node, "ratio": n1 / (n1 + n2), "first": first, "second": second}
+
+
+def _collect_denoms(node: dict, out: List[int]) -> None:
+ if node["kind"] != "leaf":
+ n1 = _tree_leaf_count(node["first"])
+ n2 = _tree_leaf_count(node["second"])
+ out.append(n1 + n2)
+ _collect_denoms(node["first"], out)
+ _collect_denoms(node["second"], out)
+
+
def tree_to_grid(node: dict, row: int, col: int, rowspan: int, colspan: int,
result: Dict[int, Tuple[int, int, int, int]]) -> None:
"""Recursively compute (row, col, rowspan, colspan) for each leaf."""
@@ -206,8 +235,17 @@ def tree_to_grid(node: dict, row: int, col: int, rowspan: int, colspan: int,
def _tree_grid_size(node: dict) -> int:
- """Grid dimension = 2^depth, capped at 8."""
- return 2 ** min(_tree_depth(node), 3)
+ """Minimal grid = LCM of all split denominators (leaf counts), capped at 12."""
+ denoms: List[int] = []
+ _collect_denoms(node, denoms)
+ if not denoms:
+ return 1
+ result = 1
+ for d in denoms:
+ result = result * d // gcd(result, d)
+ if result >= 12:
+ return 12
+ return result
# ══════════════════════════════════════════════════════════════════════════════
@@ -605,7 +643,7 @@ class LayoutCanvas(QFrame):
# Only one pane
self._reposition_tiles()
return
- new_tree = _tree_insert(new_tree, target_idx, dragged_idx, zone)
+ new_tree = _tree_equalize_ratios(_tree_insert(new_tree, target_idx, dragged_idx, zone))
self._tree = new_tree
if self._cfg_ref is not None:
self._cfg_ref.tree = new_tree
@@ -770,9 +808,11 @@ class PlotWindow(QWidget):
if self.cfg.tree is None:
self.cfg.tree = {"kind": "leaf", "pane": 0}
else:
- self.cfg.tree = {"kind": "vsplit", "ratio": 0.5,
- "first": self.cfg.tree,
- "second": {"kind": "leaf", "pane": new_idx}}
+ self.cfg.tree = _tree_equalize_ratios({
+ "kind": "vsplit", "ratio": 0.5,
+ "first": self.cfg.tree,
+ "second": {"kind": "leaf", "pane": new_idx},
+ })
self._insert(spec)
self.cfg.layout_mode = "free"
self._canvas.sync_panes(self.cfg.panes, self.cfg.tree)
@@ -786,7 +826,9 @@ class PlotWindow(QWidget):
if self.cfg.tree:
self.cfg.tree = _tree_remove(self.cfg.tree, removed_idx)
if self.cfg.tree:
- self.cfg.tree = _tree_reindex(self.cfg.tree, removed_idx)
+ self.cfg.tree = _tree_equalize_ratios(
+ _tree_reindex(self.cfg.tree, removed_idx)
+ )
self._canvas.sync_panes(self.cfg.panes, self.cfg.tree)
self._maybe_live()