v1.0 - Version stable: multi-PC, détection UI-DETR-1, 3 modes exécution
- 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>
This commit is contained in:
101
core/execution/spatial_index.py
Normal file
101
core/execution/spatial_index.py
Normal file
@@ -0,0 +1,101 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user