openplot 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ """Shared domain helpers for OpenPlot."""
@@ -0,0 +1,45 @@
1
+ """Shared helpers for annotation ordering and context filtering."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Mapping
6
+
7
+ from ..models import Annotation, AnnotationStatus, PlotSession
8
+
9
+
10
+ def pending_annotations_for_context(session: PlotSession) -> list[Annotation]:
11
+ """Pending annotations in the active branch, ordered FIFO."""
12
+ active_branch_id = session.active_branch_id
13
+ pending = [
14
+ ann
15
+ for ann in session.annotations
16
+ if ann.status == AnnotationStatus.pending
17
+ and (not ann.branch_id or ann.branch_id == active_branch_id)
18
+ ]
19
+ return sorted(pending, key=lambda ann: (ann.created_at, ann.id))
20
+
21
+
22
+ def pending_annotation_dicts_for_context(
23
+ session: Mapping[str, Any],
24
+ ) -> list[dict[str, Any]]:
25
+ """Pending annotation payloads in the active branch, ordered FIFO."""
26
+ annotations = session.get("annotations", [])
27
+ if not isinstance(annotations, list):
28
+ return []
29
+
30
+ active_branch_id = session.get("active_branch_id")
31
+ pending = [
32
+ ann
33
+ for ann in annotations
34
+ if isinstance(ann, dict)
35
+ and ann.get("status") == AnnotationStatus.pending.value
36
+ and (
37
+ not active_branch_id
38
+ or not ann.get("branch_id")
39
+ or ann.get("branch_id") == active_branch_id
40
+ )
41
+ ]
42
+ return sorted(
43
+ pending,
44
+ key=lambda ann: (str(ann.get("created_at") or ""), str(ann.get("id") or "")),
45
+ )
@@ -0,0 +1,52 @@
1
+ """Shared helpers for raster-region normalization and descriptions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+
8
+ def clamp_01(value: float) -> float:
9
+ return max(0.0, min(1.0, value))
10
+
11
+
12
+ def region_bounds_from_points(
13
+ points: Any,
14
+ ) -> tuple[float, float, float, float] | None:
15
+ """Return normalized region bounds as (x0, y0, x1, y1)."""
16
+ if not isinstance(points, list) or not points:
17
+ return None
18
+
19
+ xs: list[float] = []
20
+ ys: list[float] = []
21
+ for point in points:
22
+ if not isinstance(point, dict):
23
+ continue
24
+ raw_x = point.get("x")
25
+ raw_y = point.get("y")
26
+ if not isinstance(raw_x, (int, float)) or not isinstance(raw_y, (int, float)):
27
+ continue
28
+ xs.append(clamp_01(float(raw_x)))
29
+ ys.append(clamp_01(float(raw_y)))
30
+
31
+ if not xs or not ys:
32
+ return None
33
+
34
+ return min(xs), min(ys), max(xs), max(ys)
35
+
36
+
37
+ def region_zone_hint_from_bounds(bounds: tuple[float, float, float, float]) -> str:
38
+ """A simple vertical zone hint to reduce subplot-scope ambiguity."""
39
+ _x0, y0, _x1, y1 = bounds
40
+ y_mid = (y0 + y1) / 2.0
41
+ if y_mid < 1 / 3:
42
+ return "upper figure zone"
43
+ if y_mid < 2 / 3:
44
+ return "middle figure zone"
45
+ return "lower figure zone"
46
+
47
+
48
+ def region_zone_hint_from_points(points: Any) -> str:
49
+ bounds = region_bounds_from_points(points)
50
+ if bounds is None:
51
+ return "unknown figure zone"
52
+ return region_zone_hint_from_bounds(bounds)