natural-agi-common 0.1.33__tar.gz → 0.1.35__tar.gz
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.
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/PKG-INFO +1 -1
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/model/point.py +4 -0
- natural_agi_common-0.1.35/common/traversal/graph_traversal.py +73 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/traversal/visitors/angle_visitor.py +10 -4
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/traversal/visitors/direction_visitor.py +8 -2
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/traversal/visitors/length_comparison_visitor.py +2 -1
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/traversal/visitors/quadrant_visitor.py +13 -5
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/traversal/visitors/visitor.py +1 -1
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/natural_agi_common.egg-info/PKG-INFO +1 -1
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/natural_agi_common.egg-info/SOURCES.txt +0 -2
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/setup.py +1 -1
- natural_agi_common-0.1.33/common/traversal/graph_traversal.py +0 -52
- natural_agi_common-0.1.33/common/traversal/visitors/half_plane_visitor.py +0 -64
- natural_agi_common-0.1.33/common/traversal/visitors/relative_position_visitor.py +0 -209
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/README.md +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/__init__.py +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/critical_graph_utils.py +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/critical_point.py +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/decorator.py +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/graph_utils.py +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/model/__init__.py +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/model/dlq.py +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/model/enums.py +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/model/half_plane.py +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/model/length_comparison_result.py +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/model/vector.py +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/params.py +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/traversal/__init__.py +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/traversal/start_point_selector.py +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/traversal/visitors/__init__.py +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/traversal/visitors/visitor_result_persistence_service.py +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/natural_agi_common.egg-info/dependency_links.txt +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/natural_agi_common.egg-info/requires.txt +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/natural_agi_common.egg-info/top_level.txt +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/pyproject.toml +0 -0
- {natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/setup.cfg +0 -0
|
@@ -6,6 +6,8 @@ class Point:
|
|
|
6
6
|
id: str
|
|
7
7
|
x: float
|
|
8
8
|
y: float
|
|
9
|
+
normalized_x: float
|
|
10
|
+
normalized_y: float
|
|
9
11
|
|
|
10
12
|
@classmethod
|
|
11
13
|
def from_node_data(cls, node_data: dict) -> "Point":
|
|
@@ -13,6 +15,8 @@ class Point:
|
|
|
13
15
|
x=node_data["x"],
|
|
14
16
|
y=node_data["y"],
|
|
15
17
|
id=node_data["id"],
|
|
18
|
+
normalized_x=node_data["normalized_x"],
|
|
19
|
+
normalized_y=node_data["normalized_y"],
|
|
16
20
|
)
|
|
17
21
|
|
|
18
22
|
def __hash__(self):
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from typing import Generator, Any, Tuple, Optional, List
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
import networkx as nx
|
|
4
|
+
import logging
|
|
5
|
+
from ..model.point import Point
|
|
6
|
+
from ..model.vector import Vector
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class TraversalSequence:
|
|
11
|
+
start_point: Point
|
|
12
|
+
vector: Vector
|
|
13
|
+
end_point: Point
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class GraphTraversal:
|
|
17
|
+
def __init__(self, graph: nx.Graph):
|
|
18
|
+
self.graph = graph
|
|
19
|
+
self.logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
def dfs_traversal(
|
|
22
|
+
self, start_node: Any
|
|
23
|
+
) -> Generator[TraversalSequence, None, None]:
|
|
24
|
+
visited_vectors = set()
|
|
25
|
+
visited_nodes = set()
|
|
26
|
+
|
|
27
|
+
def dfs_recursive(current_node: Any, path: List[Any]) -> None:
|
|
28
|
+
if current_node in visited_nodes:
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
visited_nodes.add(current_node)
|
|
32
|
+
path.append(current_node)
|
|
33
|
+
|
|
34
|
+
# If we have a sequence of 3 nodes, check if it follows point-vector-point pattern
|
|
35
|
+
if len(path) >= 3:
|
|
36
|
+
start_node_id = path[-3]
|
|
37
|
+
vector_node_id = path[-2]
|
|
38
|
+
end_node_id = path[-1]
|
|
39
|
+
|
|
40
|
+
start_data = self.graph.nodes[start_node_id]
|
|
41
|
+
vector_data = self.graph.nodes[vector_node_id]
|
|
42
|
+
end_data = self.graph.nodes[end_node_id]
|
|
43
|
+
|
|
44
|
+
# Check if sequence follows point-vector-point pattern
|
|
45
|
+
if (
|
|
46
|
+
self._is_point(start_data)
|
|
47
|
+
and self._is_vector(vector_data)
|
|
48
|
+
and self._is_point(end_data)
|
|
49
|
+
and vector_node_id not in visited_vectors
|
|
50
|
+
):
|
|
51
|
+
self.logger.info(
|
|
52
|
+
f"Found sequence: {start_node_id} -> {vector_node_id} -> {end_node_id}"
|
|
53
|
+
)
|
|
54
|
+
visited_vectors.add(vector_node_id)
|
|
55
|
+
|
|
56
|
+
start_point = Point.from_node_data(start_data)
|
|
57
|
+
vector = Vector.from_node_data(vector_data)
|
|
58
|
+
end_point = Point.from_node_data(end_data)
|
|
59
|
+
|
|
60
|
+
yield TraversalSequence(start_point, vector, end_point)
|
|
61
|
+
|
|
62
|
+
# Continue DFS to neighbors
|
|
63
|
+
for neighbor in self.graph.neighbors(current_node):
|
|
64
|
+
if neighbor not in visited_nodes:
|
|
65
|
+
yield from dfs_recursive(neighbor, path.copy())
|
|
66
|
+
|
|
67
|
+
yield from dfs_recursive(start_node, [])
|
|
68
|
+
|
|
69
|
+
def _is_vector(self, node_data: dict) -> bool:
|
|
70
|
+
return "Vector" in node_data["labels"]
|
|
71
|
+
|
|
72
|
+
def _is_point(self, node_data: dict) -> bool:
|
|
73
|
+
return "Point" in node_data["labels"]
|
{natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/traversal/visitors/angle_visitor.py
RENAMED
|
@@ -29,8 +29,8 @@ class AngleVisitor(Visitor):
|
|
|
29
29
|
return {"angles": angles, "point_id": point.id}
|
|
30
30
|
return None
|
|
31
31
|
|
|
32
|
-
def visit_line(self, line: Vector) -> Dict[str, Any]:
|
|
33
|
-
angle_with_ox = self._calculate_angle_with_ox(line)
|
|
32
|
+
def visit_line(self, line: Vector, start_point: Point) -> Dict[str, Any]:
|
|
33
|
+
angle_with_ox = self._calculate_angle_with_ox(line, start_point)
|
|
34
34
|
self.line_angles[line.id] = angle_with_ox
|
|
35
35
|
self.graph.nodes[line.id]["angle_with_ox"] = angle_with_ox
|
|
36
36
|
return {"angle_with_ox": angle_with_ox, "line_id": line.id}
|
|
@@ -159,8 +159,14 @@ class AngleVisitor(Visitor):
|
|
|
159
159
|
angle2 = round(angle2 / 10) * 10
|
|
160
160
|
return [angle1, angle2]
|
|
161
161
|
|
|
162
|
-
def _calculate_angle_with_ox(self, line: Vector) -> float:
|
|
163
|
-
|
|
162
|
+
def _calculate_angle_with_ox(self, line: Vector, start_point: Point) -> float:
|
|
163
|
+
start_coords = (start_point.x, start_point.y)
|
|
164
|
+
end_coords = (
|
|
165
|
+
(line.x2, line.y2)
|
|
166
|
+
if line.x1 == start_point.x and line.y1 == start_point.y
|
|
167
|
+
else (line.x1, line.y1)
|
|
168
|
+
)
|
|
169
|
+
vector = (end_coords[0] - start_coords[0], end_coords[1] - start_coords[1])
|
|
164
170
|
dot_product = vector[0] * 1 + vector[1] * 0
|
|
165
171
|
magnitude = math.sqrt(vector[0] ** 2 + vector[1] ** 2)
|
|
166
172
|
cos_angle = dot_product / magnitude
|
|
@@ -53,10 +53,16 @@ class DirectionVisitor(Visitor):
|
|
|
53
53
|
# No specific operation for points in this visitor
|
|
54
54
|
return None
|
|
55
55
|
|
|
56
|
-
def visit_line(self, line: Vector) -> Dict[str, Any]:
|
|
56
|
+
def visit_line(self, line: Vector, start_point: Point) -> Dict[str, Any]:
|
|
57
57
|
# Extract direction information
|
|
58
|
+
start_coords = (start_point.x, start_point.y)
|
|
59
|
+
end_coords = (
|
|
60
|
+
(line.x2, line.y2)
|
|
61
|
+
if line.x1 == start_point.x and line.y1 == start_point.y
|
|
62
|
+
else (line.x1, line.y1)
|
|
63
|
+
)
|
|
58
64
|
h_direction, v_direction = self.calculate_direction(
|
|
59
|
-
|
|
65
|
+
start_coords[0], start_coords[1], end_coords[0], end_coords[1]
|
|
60
66
|
)
|
|
61
67
|
line.horizontal_direction = h_direction
|
|
62
68
|
line.vertical_direction = v_direction
|
|
@@ -2,6 +2,7 @@ from typing import Dict, Any, List
|
|
|
2
2
|
|
|
3
3
|
from neo4j import ManagedTransaction
|
|
4
4
|
from .visitor import Visitor
|
|
5
|
+
from ...model.point import Point
|
|
5
6
|
from ...model.vector import Vector
|
|
6
7
|
from ...model.length_comparison_result import LengthComparisonResult
|
|
7
8
|
|
|
@@ -16,7 +17,7 @@ class LengthComparisonVisitor(Visitor):
|
|
|
16
17
|
# This visitor does not handle points
|
|
17
18
|
return None
|
|
18
19
|
|
|
19
|
-
def visit_line(self, line: Vector) -> Dict[str, Any]:
|
|
20
|
+
def visit_line(self, line: Vector, start_point: Point) -> Dict[str, Any]:
|
|
20
21
|
comparison = LengthComparisonResult.N_A
|
|
21
22
|
if self.previous_length:
|
|
22
23
|
if line.length > self.previous_length:
|
|
@@ -13,8 +13,10 @@ class QuadrantVisitor(Visitor):
|
|
|
13
13
|
self.quadrants: Dict[str, int] = {}
|
|
14
14
|
|
|
15
15
|
def visit_point(self, point: Point) -> None:
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
x = point.normalized_x
|
|
17
|
+
y = point.normalized_y
|
|
18
|
+
quadrant = self.determine_quadrant(x, y)
|
|
19
|
+
self.graph.nodes[point.id]["quadrant"] = quadrant
|
|
18
20
|
|
|
19
21
|
def determine_vector_type(self, dx: float, dy: float) -> str:
|
|
20
22
|
"""Determine vector type based on relative dimensions.
|
|
@@ -37,9 +39,15 @@ class QuadrantVisitor(Visitor):
|
|
|
37
39
|
else:
|
|
38
40
|
return "VerticalVector"
|
|
39
41
|
|
|
40
|
-
def visit_line(self, line: Vector) -> Dict[str, Any]:
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
def visit_line(self, line: Vector, start_point: Point) -> Dict[str, Any]:
|
|
43
|
+
start_coords = (start_point.x, start_point.y)
|
|
44
|
+
end_coords = (
|
|
45
|
+
(line.x2, line.y2)
|
|
46
|
+
if line.x1 == start_point.x and line.y1 == start_point.y
|
|
47
|
+
else (line.x1, line.y1)
|
|
48
|
+
)
|
|
49
|
+
dx = end_coords[0] - start_coords[0]
|
|
50
|
+
dy = end_coords[1] - start_coords[1]
|
|
43
51
|
quadrant = self.determine_quadrant(dx, dy)
|
|
44
52
|
vector_type = self.determine_vector_type(dx, dy)
|
|
45
53
|
|
{natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/natural_agi_common.egg-info/SOURCES.txt
RENAMED
|
@@ -20,10 +20,8 @@ common/traversal/start_point_selector.py
|
|
|
20
20
|
common/traversal/visitors/__init__.py
|
|
21
21
|
common/traversal/visitors/angle_visitor.py
|
|
22
22
|
common/traversal/visitors/direction_visitor.py
|
|
23
|
-
common/traversal/visitors/half_plane_visitor.py
|
|
24
23
|
common/traversal/visitors/length_comparison_visitor.py
|
|
25
24
|
common/traversal/visitors/quadrant_visitor.py
|
|
26
|
-
common/traversal/visitors/relative_position_visitor.py
|
|
27
25
|
common/traversal/visitors/visitor.py
|
|
28
26
|
common/traversal/visitors/visitor_result_persistence_service.py
|
|
29
27
|
natural_agi_common.egg-info/PKG-INFO
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
from typing import Generator, Any, Tuple, Optional
|
|
2
|
-
import networkx as nx
|
|
3
|
-
import logging
|
|
4
|
-
from ..model.point import Point
|
|
5
|
-
from ..model.vector import Vector
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class GraphTraversal:
|
|
9
|
-
def __init__(self, graph: nx.Graph):
|
|
10
|
-
self.graph = graph
|
|
11
|
-
self.logger = logging.getLogger(__name__)
|
|
12
|
-
|
|
13
|
-
def dfs_traversal(
|
|
14
|
-
self, start_node: Any
|
|
15
|
-
) -> Generator[Tuple[Point, Optional[Vector]], None, None]:
|
|
16
|
-
visited_vector = set()
|
|
17
|
-
for edge in nx.dfs_edges(self.graph, start_node):
|
|
18
|
-
self.logger.info(f"Visiting edge: {edge}")
|
|
19
|
-
source_node, target_node = edge
|
|
20
|
-
if source_node in visited_vector or target_node in visited_vector:
|
|
21
|
-
self.logger.info(f"Skipping edge: {edge} because vector was already visited")
|
|
22
|
-
continue
|
|
23
|
-
|
|
24
|
-
# Convert to your Point and Vector objects
|
|
25
|
-
source_data = self.graph.nodes[source_node]
|
|
26
|
-
target_data = self.graph.nodes[target_node]
|
|
27
|
-
|
|
28
|
-
point = None
|
|
29
|
-
vector = None
|
|
30
|
-
|
|
31
|
-
if self._is_point(source_data):
|
|
32
|
-
point = Point.from_node_data(source_data)
|
|
33
|
-
elif self._is_vector(source_data):
|
|
34
|
-
vector = Vector.from_node_data(source_data)
|
|
35
|
-
visited_vector.add(source_node)
|
|
36
|
-
|
|
37
|
-
if self._is_point(target_data):
|
|
38
|
-
point = Point.from_node_data(target_data)
|
|
39
|
-
elif self._is_vector(target_data):
|
|
40
|
-
vector = Vector.from_node_data(target_data)
|
|
41
|
-
visited_vector.add(target_node)
|
|
42
|
-
|
|
43
|
-
if point is None and vector is None:
|
|
44
|
-
raise ValueError(f"Invalid node data: {source_data} or {target_data}")
|
|
45
|
-
|
|
46
|
-
yield point, vector
|
|
47
|
-
|
|
48
|
-
def _is_vector(self, node_data: dict) -> bool:
|
|
49
|
-
return "Vector" in node_data["labels"]
|
|
50
|
-
|
|
51
|
-
def _is_point(self, node_data: dict) -> bool:
|
|
52
|
-
return "Point" in node_data["labels"]
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
from typing import Any, Dict
|
|
2
|
-
|
|
3
|
-
from neo4j import ManagedTransaction
|
|
4
|
-
from ...model.half_plane import HalfPlane
|
|
5
|
-
from .visitor import Visitor
|
|
6
|
-
from ...model.point import Point
|
|
7
|
-
from ...model.vector import Vector
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class HalfPlaneVisitor(Visitor):
|
|
11
|
-
def __init__(self, graph):
|
|
12
|
-
super().__init__(graph)
|
|
13
|
-
self.half_planes: Dict[str, HalfPlane] = {}
|
|
14
|
-
|
|
15
|
-
def visit_point(self, _: Point) -> None:
|
|
16
|
-
# This visitor does not handle points
|
|
17
|
-
return None
|
|
18
|
-
|
|
19
|
-
def visit_line(self, line: Vector) -> Dict[str, Any]:
|
|
20
|
-
dx = line.x2 - line.x1
|
|
21
|
-
dy = line.y2 - line.y1
|
|
22
|
-
half_plane = self.determine_half_plane(dx, dy)
|
|
23
|
-
self.half_planes[line.id] = half_plane
|
|
24
|
-
self.graph.nodes[line.id]["half_plane"] = half_plane.value
|
|
25
|
-
|
|
26
|
-
def save_result(
|
|
27
|
-
self,
|
|
28
|
-
tx: ManagedTransaction,
|
|
29
|
-
image_id: str,
|
|
30
|
-
session_id: str,
|
|
31
|
-
result: Dict[str, Any],
|
|
32
|
-
) -> None:
|
|
33
|
-
query = """
|
|
34
|
-
MATCH (v:Vector {id: $id})
|
|
35
|
-
MERGE (hp:HalfPlane:Feature {value: $half_plane, session_id: $session_id})
|
|
36
|
-
ON CREATE SET hp.samples = [$image_id], v.half_plane = $half_plane
|
|
37
|
-
ON MATCH SET hp.samples = CASE
|
|
38
|
-
WHEN NOT $image_id IN hp.samples THEN hp.samples + $image_id
|
|
39
|
-
ELSE hp.samples
|
|
40
|
-
END, v.half_plane = $half_plane
|
|
41
|
-
MERGE (v)-[:IS_IN_HALF_PLANE]->(hp)
|
|
42
|
-
"""
|
|
43
|
-
tx.run(
|
|
44
|
-
query,
|
|
45
|
-
id=result["line_id"],
|
|
46
|
-
half_plane=result["half_plane"],
|
|
47
|
-
session_id=session_id,
|
|
48
|
-
image_id=image_id,
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
def get_results(self) -> Dict[str, str]:
|
|
52
|
-
return {k: v.value for k, v in self.half_planes.items()}
|
|
53
|
-
|
|
54
|
-
def reset(self) -> None:
|
|
55
|
-
self.half_planes.clear()
|
|
56
|
-
|
|
57
|
-
@staticmethod
|
|
58
|
-
def determine_half_plane(dx: float, dy: float) -> HalfPlane:
|
|
59
|
-
if dx == 0 and dy == 0:
|
|
60
|
-
return HalfPlane.ORIGIN
|
|
61
|
-
elif abs(dy) > abs(dx):
|
|
62
|
-
return HalfPlane.UPPER if dy > 0 else HalfPlane.LOWER
|
|
63
|
-
else:
|
|
64
|
-
return HalfPlane.RIGHT if dx > 0 else HalfPlane.LEFT
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from enum import Enum
|
|
3
|
-
from typing import Dict, Any, List
|
|
4
|
-
import math
|
|
5
|
-
import networkx as nx
|
|
6
|
-
from neo4j import ManagedTransaction
|
|
7
|
-
|
|
8
|
-
from .visitor import Visitor
|
|
9
|
-
from ...model.point import Point
|
|
10
|
-
from ...model.vector import Vector
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class RelativeSegment(Enum):
|
|
14
|
-
TOP = "top"
|
|
15
|
-
BOTTOM = "bottom"
|
|
16
|
-
LEFT = "left"
|
|
17
|
-
RIGHT = "right"
|
|
18
|
-
CENTER_VERTICAL = "center_vertical"
|
|
19
|
-
CENTER_HORIZONTAL = "center_horizontal"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@dataclass
|
|
23
|
-
class RelativePosition:
|
|
24
|
-
distance_from_center: float
|
|
25
|
-
segments: List[RelativeSegment]
|
|
26
|
-
normalized_x: float # -1 to 1
|
|
27
|
-
normalized_y: float # -1 to 1
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class RelativePositionVisitor(Visitor):
|
|
31
|
-
def __init__(self, graph: nx.Graph):
|
|
32
|
-
super().__init__(graph)
|
|
33
|
-
|
|
34
|
-
# Find the bounding box and calculate center
|
|
35
|
-
self._calculate_bounding_box_center()
|
|
36
|
-
|
|
37
|
-
self.segment_threshold = 0.05 # 20% threshold for center segments
|
|
38
|
-
self.point_positions: Dict[str, RelativePosition] = {}
|
|
39
|
-
self.vector_positions: Dict[str, RelativePosition] = {}
|
|
40
|
-
|
|
41
|
-
def _calculate_bounding_box_center(self) -> None:
|
|
42
|
-
"""Calculate the center based on the bounding box of all points in the graph."""
|
|
43
|
-
min_x = float("inf")
|
|
44
|
-
min_y = float("inf")
|
|
45
|
-
max_x = float("-inf")
|
|
46
|
-
max_y = float("-inf")
|
|
47
|
-
|
|
48
|
-
# Find min and max coordinates to determine the bounding box
|
|
49
|
-
for node_id, node_data in self.graph.nodes(data=True):
|
|
50
|
-
if "Point" not in node_data["labels"]:
|
|
51
|
-
continue
|
|
52
|
-
|
|
53
|
-
x = node_data["x"]
|
|
54
|
-
y = node_data["y"]
|
|
55
|
-
min_x = min(min_x, x)
|
|
56
|
-
min_y = min(min_y, y)
|
|
57
|
-
max_x = max(max_x, x)
|
|
58
|
-
max_y = max(max_y, y)
|
|
59
|
-
|
|
60
|
-
# Calculate center of the bounding box
|
|
61
|
-
self.center_x = (min_x + max_x) / 2
|
|
62
|
-
self.center_y = (min_y + max_y) / 2
|
|
63
|
-
|
|
64
|
-
# Calculate half-width and half-height of the bounding box
|
|
65
|
-
half_width = (max_x - min_x) / 2
|
|
66
|
-
half_height = (max_y - min_y) / 2
|
|
67
|
-
|
|
68
|
-
# Maximum distance is from center to the corner of the bounding box
|
|
69
|
-
self.max_distance = math.sqrt(half_width**2 + half_height**2)
|
|
70
|
-
|
|
71
|
-
def visit_point(self, point: Point) -> Dict[str, Any]:
|
|
72
|
-
position = self._calculate_relative_position(point.x, point.y)
|
|
73
|
-
self.point_positions[point.id] = position
|
|
74
|
-
node = self.graph.nodes[point.id]
|
|
75
|
-
node["relative_distance"] = position.distance_from_center
|
|
76
|
-
node["normalized_x"] = position.normalized_x
|
|
77
|
-
node["normalized_y"] = position.normalized_y
|
|
78
|
-
node["segments"] = [seg.value for seg in position.segments]
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
"point_id": point.id,
|
|
82
|
-
"distance": position.distance_from_center,
|
|
83
|
-
"segments": [seg.value for seg in position.segments],
|
|
84
|
-
"normalized_x": position.normalized_x,
|
|
85
|
-
"normalized_y": position.normalized_y,
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
def visit_line(self, line: Vector) -> Dict[str, Any]:
|
|
89
|
-
# Calculate midpoint of the line
|
|
90
|
-
mid_x = (line.x1 + line.x2) / 2
|
|
91
|
-
mid_y = (line.y1 + line.y2) / 2
|
|
92
|
-
|
|
93
|
-
position = self._calculate_relative_position(mid_x, mid_y)
|
|
94
|
-
self.vector_positions[line.id] = position
|
|
95
|
-
node = self.graph.nodes[line.id]
|
|
96
|
-
node["relative_distance"] = position.distance_from_center
|
|
97
|
-
node["normalized_x"] = position.normalized_x
|
|
98
|
-
node["normalized_y"] = position.normalized_y
|
|
99
|
-
node["segments"] = [seg.value for seg in position.segments]
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
"line_id": line.id,
|
|
103
|
-
"distance": position.distance_from_center,
|
|
104
|
-
"segments": [seg.value for seg in position.segments],
|
|
105
|
-
"normalized_x": position.normalized_x,
|
|
106
|
-
"normalized_y": position.normalized_y,
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
def _calculate_relative_position(self, x: float, y: float) -> RelativePosition:
|
|
110
|
-
# Calculate normalized coordinates (-1 to 1) relative to bounding box center
|
|
111
|
-
normalized_x = (x - self.center_x) / (
|
|
112
|
-
self.max_distance if self.max_distance > 0 else 1
|
|
113
|
-
)
|
|
114
|
-
normalized_y = (y - self.center_y) / (
|
|
115
|
-
self.max_distance if self.max_distance > 0 else 1
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
# Calculate distance from center
|
|
119
|
-
dx = x - self.center_x
|
|
120
|
-
dy = y - self.center_y
|
|
121
|
-
distance = math.sqrt(dx**2 + dy**2) / self.max_distance
|
|
122
|
-
|
|
123
|
-
# Determine segments
|
|
124
|
-
segments = []
|
|
125
|
-
|
|
126
|
-
# Vertical segments
|
|
127
|
-
if abs(normalized_y) < self.segment_threshold:
|
|
128
|
-
segments.append(RelativeSegment.CENTER_HORIZONTAL)
|
|
129
|
-
elif normalized_y < 0:
|
|
130
|
-
segments.append(RelativeSegment.TOP)
|
|
131
|
-
else:
|
|
132
|
-
segments.append(RelativeSegment.BOTTOM)
|
|
133
|
-
|
|
134
|
-
# Horizontal segments
|
|
135
|
-
if abs(normalized_x) < self.segment_threshold:
|
|
136
|
-
segments.append(RelativeSegment.CENTER_VERTICAL)
|
|
137
|
-
elif normalized_x < 0:
|
|
138
|
-
segments.append(RelativeSegment.LEFT)
|
|
139
|
-
else:
|
|
140
|
-
segments.append(RelativeSegment.RIGHT)
|
|
141
|
-
|
|
142
|
-
return RelativePosition(
|
|
143
|
-
distance_from_center=round(distance, 2),
|
|
144
|
-
segments=segments,
|
|
145
|
-
normalized_x=round(normalized_x, 2),
|
|
146
|
-
normalized_y=round(normalized_y, 2),
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
def save_result(
|
|
150
|
-
self,
|
|
151
|
-
tx: ManagedTransaction,
|
|
152
|
-
image_id: str,
|
|
153
|
-
session_id: str,
|
|
154
|
-
result: Dict[str, Any],
|
|
155
|
-
) -> None:
|
|
156
|
-
if "point_id" in result:
|
|
157
|
-
self._save_point_position(tx, result)
|
|
158
|
-
elif "line_id" in result:
|
|
159
|
-
self._save_line_position(tx, result)
|
|
160
|
-
|
|
161
|
-
def _save_point_position(
|
|
162
|
-
self,
|
|
163
|
-
tx: ManagedTransaction,
|
|
164
|
-
result: Dict[str, Any],
|
|
165
|
-
) -> None:
|
|
166
|
-
query = """
|
|
167
|
-
MATCH (p:Point {id: $point_id})
|
|
168
|
-
SET p.relative_distance = $distance,
|
|
169
|
-
p.normalized_x = $normalized_x,
|
|
170
|
-
p.normalized_y = $normalized_y,
|
|
171
|
-
p.segments = $segments
|
|
172
|
-
RETURN p
|
|
173
|
-
"""
|
|
174
|
-
tx.run(
|
|
175
|
-
query,
|
|
176
|
-
point_id=result["point_id"],
|
|
177
|
-
distance=result["distance"],
|
|
178
|
-
normalized_x=result["normalized_x"],
|
|
179
|
-
normalized_y=result["normalized_y"],
|
|
180
|
-
segments=result["segments"],
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
def _save_line_position(
|
|
184
|
-
self,
|
|
185
|
-
tx: ManagedTransaction,
|
|
186
|
-
result: Dict[str, Any],
|
|
187
|
-
) -> None:
|
|
188
|
-
query = """
|
|
189
|
-
MATCH (v:Vector {id: $line_id})
|
|
190
|
-
SET v.relative_distance = $distance,
|
|
191
|
-
v.normalized_x = $normalized_x,
|
|
192
|
-
v.normalized_y = $normalized_y,
|
|
193
|
-
v.segments = $segments
|
|
194
|
-
"""
|
|
195
|
-
tx.run(
|
|
196
|
-
query,
|
|
197
|
-
line_id=result["line_id"],
|
|
198
|
-
distance=result["distance"],
|
|
199
|
-
normalized_x=result["normalized_x"],
|
|
200
|
-
normalized_y=result["normalized_y"],
|
|
201
|
-
segments=result["segments"],
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
def get_results(self) -> Dict[str, Dict[str, RelativePosition]]:
|
|
205
|
-
return {"points": self.point_positions, "vectors": self.vector_positions}
|
|
206
|
-
|
|
207
|
-
def reset(self) -> None:
|
|
208
|
-
self.point_positions.clear()
|
|
209
|
-
self.vector_positions.clear()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/model/length_comparison_result.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/traversal/start_point_selector.py
RENAMED
|
File without changes
|
{natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/common/traversal/visitors/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/natural_agi_common.egg-info/requires.txt
RENAMED
|
File without changes
|
{natural_agi_common-0.1.33 → natural_agi_common-0.1.35}/natural_agi_common.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|