0.1.8
Loading...
Searching...
No Matches
geometry.py
Go to the documentation of this file.
1"""
2Validation for Context geometry operations.
3
4Provides comprehensive validation for all geometry creation methods,
5ensuring parameters are valid before reaching C++ code.
6"""
7
8from typing import List, Union, Any, Optional
9from .core import validate_input, coerce_to_vec3, coerce_to_vec2, validate_positive_value
10from .datatypes import (validate_rgb_color, validate_vec3, validate_vec2,
11 validate_spherical_coord)
12from .exceptions import ValidationError, create_validation_error
13
14
15def validate_geometry_center(center: Any, param_name: str = "center", function_name: str = None):
16 """Validate geometry center is a valid vec3."""
17 validate_vec3(center, param_name, function_name)
18
19
20def validate_geometry_size2d(size: Any, param_name: str = "size", function_name: str = None):
21 """Validate 2D size is a valid vec2 with positive values."""
22 validate_vec2(size, param_name, function_name)
23
24 if hasattr(size, 'x') and hasattr(size, 'y'):
25 if size.x <= 0:
26 raise create_validation_error(
27 f"Size x-component must be positive, got {size.x}",
28 param_name=f"{param_name}.x",
29 function_name=function_name,
30 expected_type="positive number",
31 actual_value=size.x,
32 suggestion="Size dimensions must be greater than 0."
33 )
34
35 if size.y <= 0:
36 raise create_validation_error(
37 f"Size y-component must be positive, got {size.y}",
38 param_name=f"{param_name}.y",
39 function_name=function_name,
40 expected_type="positive number",
41 actual_value=size.y,
42 suggestion="Size dimensions must be greater than 0."
43 )
44
45
46def validate_geometry_size3d(size: Any, param_name: str = "size", function_name: str = None):
47 """Validate 3D size is a valid vec3 with positive values."""
48 validate_vec3(size, param_name, function_name)
49
50 if hasattr(size, 'x') and hasattr(size, 'y') and hasattr(size, 'z'):
51 if size.x <= 0:
52 raise create_validation_error(
53 f"Size x-component must be positive, got {size.x}",
54 param_name=f"{param_name}.x",
55 function_name=function_name,
56 expected_type="positive number",
57 actual_value=size.x,
58 suggestion="Size dimensions must be greater than 0."
59 )
60
61 if size.y <= 0:
62 raise create_validation_error(
63 f"Size y-component must be positive, got {size.y}",
64 param_name=f"{param_name}.y",
65 function_name=function_name,
66 expected_type="positive number",
67 actual_value=size.y,
68 suggestion="Size dimensions must be greater than 0."
69 )
70
71 if size.z <= 0:
72 raise create_validation_error(
73 f"Size z-component must be positive, got {size.z}",
74 param_name=f"{param_name}.z",
75 function_name=function_name,
76 expected_type="positive number",
77 actual_value=size.z,
78 suggestion="Size dimensions must be greater than 0."
79 )
80
81
82def validate_tube_nodes(nodes: List[Any], param_name: str = "nodes", function_name: str = None):
83 """Validate tube nodes list."""
84 if not isinstance(nodes, list):
85 raise create_validation_error(
86 f"Parameter must be a list of vec3 positions",
87 param_name=param_name,
88 function_name=function_name,
89 expected_type="list of vec3",
90 actual_value=nodes,
91 suggestion="Provide a list of vec3 objects representing tube node positions."
92 )
93
94 if len(nodes) < 2:
95 raise create_validation_error(
96 f"Parameter must contain at least 2 nodes for tube creation, got {len(nodes)}",
97 param_name=param_name,
98 function_name=function_name,
99 expected_type="list with >= 2 elements",
100 actual_value=nodes,
101 suggestion="Tubes require at least 2 nodes to define a path."
102 )
103
104 for i, node in enumerate(nodes):
105 validate_vec3(node, f"{param_name}[{i}]", function_name)
106
107
108def validate_tube_radii(radii: Union[float, List[float]], nodes_count: int,
109 param_name: str = "radii", function_name: str = None):
110 """Validate tube radii specification."""
111 if isinstance(radii, (int, float)):
112 validate_positive_value(radii, param_name, function_name)
113 elif isinstance(radii, list):
114 if len(radii) != nodes_count:
115 raise create_validation_error(
116 f"Radii list must have same length as nodes ({nodes_count}), got {len(radii)} radii",
117 param_name=param_name,
118 function_name=function_name,
119 expected_type=f"list with {nodes_count} elements",
120 actual_value=radii,
121 suggestion="Provide one radius value per node, or use a single radius for all nodes."
122 )
123
124 for i, radius in enumerate(radii):
125 validate_positive_value(radius, f"{param_name}[{i}]", function_name)
126 else:
127 raise create_validation_error(
128 f"Parameter must be a positive number or list of positive numbers",
129 param_name=param_name,
130 function_name=function_name,
131 expected_type="positive number or list of positive numbers",
132 actual_value=radii,
133 suggestion="Use a single positive radius or a list of positive radii (one per node)."
134 )
135
136
137def validate_ndivs_parameter(ndivs: Any, param_name: str = "ndivs", function_name: str = None, min_value: int = 3):
138 """Validate ndivs parameter for geometry subdivision."""
139 if not isinstance(ndivs, int):
140 raise create_validation_error(
141 f"Parameter must be an integer",
142 param_name=param_name,
143 function_name=function_name,
144 expected_type="integer",
145 actual_value=ndivs,
146 suggestion="Use an integer value for subdivision count."
147 )
148
149 if ndivs < min_value:
150 raise create_validation_error(
151 f"Parameter must be >= {min_value}, got {ndivs}",
152 param_name=param_name,
153 function_name=function_name,
154 expected_type=f"integer >= {min_value}",
155 actual_value=ndivs,
156 suggestion=f"Use at least {min_value} subdivisions for valid geometry."
157 )
158
159 if ndivs > 1000: # Reasonable upper limit to prevent performance issues
160 raise create_validation_error(
161 f"Parameter {ndivs} is very large and may cause performance issues",
162 param_name=param_name,
163 function_name=function_name,
164 expected_type="integer <= 1000",
165 actual_value=ndivs,
166 suggestion="Consider using fewer subdivisions (typically < 100) for better performance."
167 )
168
169
170# Geometry validation decorators
171
172def validate_patch_params(func):
173 """Decorator for addPatch method validation."""
174 return validate_input(
175 param_validators={
176 'center': validate_geometry_center,
177 'size': validate_geometry_size2d,
178 'rotation': validate_spherical_coord,
179 'color': validate_rgb_color
180 },
181 type_coercions={
182 'center': coerce_to_vec3,
183 'size': coerce_to_vec2
184 }
185 )(func)
186
187
189 """Decorator for addTriangle method validation."""
190 return validate_input(
191 param_validators={
192 'vertex0': lambda v, **kw: validate_geometry_center(v, 'vertex0', kw.get('function_name')),
193 'vertex1': lambda v, **kw: validate_geometry_center(v, 'vertex1', kw.get('function_name')),
194 'vertex2': lambda v, **kw: validate_geometry_center(v, 'vertex2', kw.get('function_name')),
195 'color': validate_rgb_color
196 },
197 type_coercions={
198 'vertex0': lambda v, **kw: coerce_to_vec3(v, 'vertex0'),
199 'vertex1': lambda v, **kw: coerce_to_vec3(v, 'vertex1'),
200 'vertex2': lambda v, **kw: coerce_to_vec3(v, 'vertex2')
201 }
202 )(func)
203
204
205def validate_sphere_params(func):
206 """Decorator for addSphere method validation."""
207 def validate_sphere_specific(radius, param_name="radius", function_name=None):
208 validate_positive_value(radius, param_name, function_name)
209
210 return validate_input(
211 param_validators={
212 'center': validate_geometry_center,
213 'radius': validate_sphere_specific,
214 'color': validate_rgb_color,
215 'ndivs': validate_ndivs_parameter
216 },
217 type_coercions={
218 'center': coerce_to_vec3
219 }
220 )(func)
221
222
223def validate_tube_params(func):
224 """Decorator for addTube method validation."""
225 def validate_tube_wrapper(self, nodes=None, radii=None, ndivs=6, colors=None, **kwargs):
226 # Custom validation that can access nodes to validate radii
227 if nodes is not None:
228 validate_tube_nodes(nodes, "nodes", func.__name__)
229
230 if radii is not None and nodes is not None:
231 validate_tube_radii(radii, len(nodes), "radii", func.__name__)
232 elif radii is not None:
233 # Single radius value
234 if isinstance(radii, (int, float)):
235 validate_positive_value(radii, "radii", func.__name__)
236 else:
237 validate_tube_radii(radii, 2, "radii", func.__name__) # Default minimum
238
239 if colors is not None:
240 if isinstance(colors, list):
241 for i, color in enumerate(colors):
242 validate_rgb_color(color, f"colors[{i}]", func.__name__)
243 else:
244 validate_rgb_color(colors, "colors", func.__name__)
245
246 return func(self, nodes, radii, ndivs, colors, **kwargs)
247
248 return validate_tube_wrapper
249
250
251def validate_box_params(func):
252 """Decorator for addBox method validation."""
253 return validate_input(
254 param_validators={
255 'center': validate_geometry_center,
256 'size': validate_geometry_size3d,
257 'rotation': validate_spherical_coord,
258 'color': validate_rgb_color
259 },
260 type_coercions={
261 'center': coerce_to_vec3
262 }
263 )(func)
264
265
266def validate_disk_params(func):
267 """Decorator for addDisk method validation."""
268 def validate_disk_specific(radius, param_name="radius", function_name=None):
269 validate_positive_value(radius, param_name, function_name)
270
271 return validate_input(
272 param_validators={
273 'center': validate_geometry_center,
274 'size': validate_disk_specific,
275 'rotation': validate_spherical_coord,
276 'color': validate_rgb_color,
277 'ndivs': validate_ndivs_parameter
278 },
279 type_coercions={
280 'center': coerce_to_vec3
281 }
282 )(func)
283
284
285def validate_cone_params(func):
286 """Decorator for addCone method validation."""
287 def validate_cone_specific(radius, param_name="radius", function_name=None):
288 validate_positive_value(radius, param_name, function_name)
289
290 def validate_height(height, param_name="height", function_name=None):
291 validate_positive_value(height, param_name, function_name)
292
293 return validate_input(
294 param_validators={
295 'center': validate_geometry_center,
296 'radius': validate_cone_specific,
297 'height': validate_height,
298 'rotation': validate_spherical_coord,
299 'color': validate_rgb_color,
300 'ndivs': validate_ndivs_parameter
301 },
302 type_coercions={
303 'center': coerce_to_vec3
304 }
305 )(func)
validate_geometry_center(Any center, str param_name="center", str function_name=None)
Validate geometry center is a valid vec3.
Definition geometry.py:16
validate_sphere_params(func)
Decorator for addSphere method validation.
Definition geometry.py:206
validate_tube_radii(Union[float, List[float]] radii, int nodes_count, str param_name="radii", str function_name=None)
Validate tube radii specification.
Definition geometry.py:110
validate_tube_params(func)
Decorator for addTube method validation.
Definition geometry.py:224
validate_box_params(func)
Decorator for addBox method validation.
Definition geometry.py:252
validate_triangle_params(func)
Decorator for addTriangle method validation.
Definition geometry.py:189
validate_disk_params(func)
Decorator for addDisk method validation.
Definition geometry.py:267
validate_geometry_size3d(Any size, str param_name="size", str function_name=None)
Validate 3D size is a valid vec3 with positive values.
Definition geometry.py:47
validate_geometry_size2d(Any size, str param_name="size", str function_name=None)
Validate 2D size is a valid vec2 with positive values.
Definition geometry.py:21
validate_patch_params(func)
Decorator for addPatch method validation.
Definition geometry.py:173
validate_ndivs_parameter(Any ndivs, str param_name="ndivs", str function_name=None, int min_value=3)
Validate ndivs parameter for geometry subdivision.
Definition geometry.py:138
validate_tube_nodes(List[Any] nodes, str param_name="nodes", str function_name=None)
Validate tube nodes list.
Definition geometry.py:83
validate_cone_params(func)
Decorator for addCone method validation.
Definition geometry.py:286