1
+ """
2
+ Node wrappers for coordinate conversion in ComfyUI.
3
+
4
+ These nodes provide user-friendly interfaces for the coordinate system functionality.
5
+ """
6
+
7
+ import torch
8
+ import logging
9
+ from typing import Union , List , Tuple
10
+
11
+ from ...src .coordinates import CoordinateSystem
12
+
13
+ logger = logging .getLogger (__name__ )
14
+
15
+ class CoordinateConverterNode :
16
+ """
17
+ Fast coordinate conversion between different coordinate spaces.
18
+ """
19
+ CATEGORY = "Realtime Nodes/Coordinates"
20
+ FUNCTION = "convert_coordinates"
21
+ RETURN_TYPES = ("FLOAT" , "FLOAT" )
22
+ RETURN_NAMES = ("x_out" , "y_out" )
23
+
24
+ @classmethod
25
+ def INPUT_TYPES (cls ):
26
+ return {
27
+ "required" : {
28
+ "x" : ("FLOAT" , {"default" : 0.0 , "forceInput" : True , "tooltip" : "X coordinate(s) to convert" }),
29
+ "y" : ("FLOAT" , {"default" : 0.0 , "forceInput" : True , "tooltip" : "Y coordinate(s) to convert" }),
30
+ "image_for_dimensions" : ("IMAGE" , {"tooltip" : "Reference image for dimensions" }),
31
+ "from_space" : (["pixel" , "normalized" ],),
32
+ "to_space" : (["pixel" , "normalized" ],),
33
+ }
34
+ }
35
+
36
+ def convert_coordinates (self , x , y , image_for_dimensions , from_space , to_space ):
37
+ """Convert coordinates using the unified coordinate system."""
38
+ if image_for_dimensions .dim () != 4 :
39
+ logger .error ("Input image must be BHWC format." )
40
+ return ([0.0 ] * len (x ), [0.0 ] * len (y )) if isinstance (x , list ) else (0.0 , 0.0 )
41
+
42
+ try :
43
+ # Get dimensions from image
44
+ dimensions = CoordinateSystem .get_dimensions_from_tensor (image_for_dimensions )
45
+
46
+ # Map string inputs to CoordinateSystem constants
47
+ space_map = {
48
+ "pixel" : CoordinateSystem .PIXEL ,
49
+ "normalized" : CoordinateSystem .NORMALIZED ,
50
+ }
51
+
52
+ from_space_const = space_map [from_space ]
53
+ to_space_const = space_map [to_space ]
54
+
55
+ # Convert coordinates
56
+ x_out = CoordinateSystem .convert (x , dimensions [0 ], from_space_const , to_space_const )
57
+ y_out = CoordinateSystem .convert (y , dimensions [1 ], from_space_const , to_space_const )
58
+
59
+ return (x_out , y_out )
60
+ except Exception as e :
61
+ logger .error (f"Error in coordinate conversion: { e } " )
62
+ return ([0.0 ] * len (x ), [0.0 ] * len (y )) if isinstance (x , list ) else (0.0 , 0.0 )
63
+
64
+
65
+ class Point2DNode :
66
+ """
67
+ Creates a 2D point with coordinate space awareness.
68
+ """
69
+ CATEGORY = "Realtime Nodes/Coordinates"
70
+ FUNCTION = "create_point"
71
+ RETURN_TYPES = ("POINT" ,)
72
+ RETURN_NAMES = ("point" ,)
73
+
74
+ @classmethod
75
+ def INPUT_TYPES (cls ):
76
+ return {
77
+ "required" : {
78
+ "x" : ("FLOAT" , {"default" : 0.5 , "min" : 0.0 , "max" : 1.0 , "step" : 0.01 , "tooltip" : "X coordinate" }),
79
+ "y" : ("FLOAT" , {"default" : 0.5 , "min" : 0.0 , "max" : 1.0 , "step" : 0.01 , "tooltip" : "Y coordinate" }),
80
+ "space" : (["normalized" , "pixel" ],),
81
+ },
82
+ "optional" : {
83
+ "image_for_dimensions" : ("IMAGE" , {"tooltip" : "Reference image for dimensions (required for pixel space)" }),
84
+ }
85
+ }
86
+
87
+ def create_point (self , x , y , space , image_for_dimensions = None ):
88
+ """Create a Point object."""
89
+ from ...src .coordinates import Point
90
+
91
+ space_map = {
92
+ "pixel" : CoordinateSystem .PIXEL ,
93
+ "normalized" : CoordinateSystem .NORMALIZED ,
94
+ }
95
+
96
+ space_const = space_map [space ]
97
+
98
+ # Validate dimensions if using pixel space
99
+ if space == "pixel" and image_for_dimensions is None :
100
+ logger .warning ("Pixel space selected but no image provided for dimensions. Using normalized space." )
101
+ space_const = CoordinateSystem .NORMALIZED
102
+
103
+ return (Point (x , y , None , space_const ),)
104
+
105
+
106
+ class PointListNode :
107
+ """
108
+ Creates a list of points from coordinate lists.
109
+ """
110
+ CATEGORY = "Realtime Nodes/Coordinates"
111
+ FUNCTION = "create_point_list"
112
+ RETURN_TYPES = ("POINT_LIST" ,)
113
+ RETURN_NAMES = ("point_list" ,)
114
+
115
+ @classmethod
116
+ def INPUT_TYPES (cls ):
117
+ return {
118
+ "required" : {
119
+ "x_coords" : ("FLOAT" , {"default" : [0.25 , 0.5 , 0.75 ], "forceInput" : True , "tooltip" : "List of X coordinates" }),
120
+ "y_coords" : ("FLOAT" , {"default" : [0.25 , 0.5 , 0.75 ], "forceInput" : True , "tooltip" : "List of Y coordinates" }),
121
+ "space" : (["normalized" , "pixel" ],),
122
+ },
123
+ "optional" : {
124
+ "z_coords" : ("FLOAT" , {"default" : None , "forceInput" : True , "tooltip" : "List of Z coordinates (optional)" }),
125
+ "image_for_dimensions" : ("IMAGE" , {"tooltip" : "Reference image for dimensions (required for pixel space)" }),
126
+ }
127
+ }
128
+
129
+ def create_point_list (self , x_coords , y_coords , space , z_coords = None , image_for_dimensions = None ):
130
+ """Create a PointList object."""
131
+ from ...src .coordinates import PointList
132
+
133
+ space_map = {
134
+ "pixel" : CoordinateSystem .PIXEL ,
135
+ "normalized" : CoordinateSystem .NORMALIZED ,
136
+ }
137
+
138
+ space_const = space_map [space ]
139
+
140
+ # Validate dimensions if using pixel space
141
+ if space == "pixel" and image_for_dimensions is None :
142
+ logger .warning ("Pixel space selected but no image provided for dimensions. Using normalized space." )
143
+ space_const = CoordinateSystem .NORMALIZED
144
+
145
+ # Ensure inputs are lists
146
+ if not isinstance (x_coords , list ):
147
+ x_coords = [x_coords ]
148
+ if not isinstance (y_coords , list ):
149
+ y_coords = [y_coords ]
150
+ if z_coords is not None and not isinstance (z_coords , list ):
151
+ z_coords = [z_coords ]
152
+
153
+ return (PointList .from_coordinates (x_coords , y_coords , z_coords , space_const ),)
154
+
155
+
156
+ # Node mapping for ComfyUI
157
+ NODE_CLASS_MAPPINGS = {
158
+ "CoordinateConverter" : CoordinateConverterNode ,
159
+ "Point2D" : Point2DNode ,
160
+ "PointList" : PointListNode ,
161
+ }
162
+
163
+ # Display names for ComfyUI
164
+ NODE_DISPLAY_NAME_MAPPINGS = {
165
+ "CoordinateConverter" : "Coordinate Converter" ,
166
+ "Point2D" : "Create 2D Point" ,
167
+ "PointList" : "Create Point List" ,
168
+ }
0 commit comments