- Frontend v4 accessible sur réseau local (192.168.1.40) - Ports ouverts: 3002 (frontend), 5001 (backend), 5004 (dashboard) - Ollama GPU fonctionnel - Self-healing interactif - Dashboard confiance Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
101 lines
3.0 KiB
Python
101 lines
3.0 KiB
Python
# core/execution/spatial_index.py
|
|
"""
|
|
Index spatial par grille pour optimisation des requêtes géométriques UI.
|
|
|
|
Auteur : Dom, Alice Kiro - 19 décembre 2024
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Dict, Iterable, List, Optional, Set, Tuple
|
|
|
|
from ..models.ui_element import UIElement
|
|
|
|
|
|
def _right(b):
|
|
return b[0] + b[2]
|
|
|
|
|
|
def _bottom(b):
|
|
return b[1] + b[3]
|
|
|
|
|
|
def _intersects(a, b) -> bool:
|
|
ax1, ay1, aw, ah = a
|
|
bx1, by1, bw, bh = b
|
|
ax2, ay2 = _right(a), _bottom(a)
|
|
bx2, by2 = _right(b), _bottom(b)
|
|
return not (ax2 <= bx1 or bx2 <= ax1 or ay2 <= by1 or by2 <= ay1)
|
|
|
|
|
|
def _contains_point(b, x, y) -> bool:
|
|
return (b[0] <= x <= _right(b)) and (b[1] <= y <= _bottom(b))
|
|
|
|
|
|
@dataclass
|
|
class SpatialIndexGrid:
|
|
"""
|
|
Index spatial simple par grille (très efficace pour UI: rectangles).
|
|
- build: O(n)
|
|
- query_bbox / query_point: ~O(k) sur les cellules touchées
|
|
|
|
Auteur : Dom, Alice Kiro - 19 décembre 2024
|
|
"""
|
|
cell_size: int = 160
|
|
|
|
_cells: Dict[Tuple[int, int], List[UIElement]] = field(default_factory=dict)
|
|
_by_id: Dict[str, UIElement] = field(default_factory=dict)
|
|
_built: bool = False
|
|
|
|
def build(self, elements: List[UIElement]) -> "SpatialIndexGrid":
|
|
"""Construit l'index à partir d'une liste d'éléments UI"""
|
|
self._cells = {}
|
|
self._by_id = {}
|
|
for e in elements:
|
|
self._by_id[e.element_id] = e
|
|
for key in self._cells_for_bbox(e.bbox):
|
|
self._cells.setdefault(key, []).append(e)
|
|
self._built = True
|
|
return self
|
|
|
|
def _cells_for_bbox(self, bbox) -> Iterable[Tuple[int, int]]:
|
|
"""Retourne toutes les cellules touchées par une bbox"""
|
|
x, y, w, h = bbox
|
|
x2, y2 = x + w, y + h
|
|
|
|
cs = self.cell_size
|
|
cx1 = int(x // cs)
|
|
cy1 = int(y // cs)
|
|
cx2 = int(x2 // cs)
|
|
cy2 = int(y2 // cs)
|
|
|
|
for cy in range(cy1, cy2 + 1):
|
|
for cx in range(cx1, cx2 + 1):
|
|
yield (cx, cy)
|
|
|
|
def query_bbox(self, bbox) -> List[UIElement]:
|
|
"""Trouve tous les éléments qui intersectent avec la bbox donnée"""
|
|
if not self._built:
|
|
return []
|
|
seen: Set[str] = set()
|
|
out: List[UIElement] = []
|
|
for key in self._cells_for_bbox(bbox):
|
|
for e in self._cells.get(key, []):
|
|
if e.element_id in seen:
|
|
continue
|
|
seen.add(e.element_id)
|
|
if _intersects(e.bbox, bbox):
|
|
out.append(e)
|
|
return out
|
|
|
|
def query_point(self, x: int, y: int) -> List[UIElement]:
|
|
"""Trouve tous les éléments qui contiennent le point donné"""
|
|
if not self._built:
|
|
return []
|
|
cs = self.cell_size
|
|
key = (int(x // cs), int(y // cs))
|
|
out = []
|
|
for e in self._cells.get(key, []):
|
|
if _contains_point(e.bbox, x, y):
|
|
out.append(e)
|
|
return out |