diff options
Diffstat (limited to 'ui')
| -rw-r--r-- | ui/windows/plot_window.py | 76 |
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() |
