pyvale 2025.5.3__cp311-cp311-macosx_14_0_arm64.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.

Potentially problematic release.


This version of pyvale might be problematic. Click here for more details.

Files changed (175) hide show
  1. pyvale/.dylibs/libomp.dylib +0 -0
  2. pyvale/__init__.py +89 -0
  3. pyvale/analyticmeshgen.py +102 -0
  4. pyvale/analyticsimdatafactory.py +91 -0
  5. pyvale/analyticsimdatagenerator.py +323 -0
  6. pyvale/blendercalibrationdata.py +15 -0
  7. pyvale/blenderlightdata.py +26 -0
  8. pyvale/blendermaterialdata.py +15 -0
  9. pyvale/blenderrenderdata.py +30 -0
  10. pyvale/blenderscene.py +488 -0
  11. pyvale/blendertools.py +420 -0
  12. pyvale/camera.py +146 -0
  13. pyvale/cameradata.py +69 -0
  14. pyvale/cameradata2d.py +84 -0
  15. pyvale/camerastereo.py +217 -0
  16. pyvale/cameratools.py +522 -0
  17. pyvale/cython/rastercyth.c +32211 -0
  18. pyvale/cython/rastercyth.cpython-311-darwin.so +0 -0
  19. pyvale/cython/rastercyth.py +640 -0
  20. pyvale/data/__init__.py +5 -0
  21. pyvale/data/cal_target.tiff +0 -0
  22. pyvale/data/case00_HEX20_out.e +0 -0
  23. pyvale/data/case00_HEX27_out.e +0 -0
  24. pyvale/data/case00_HEX8_out.e +0 -0
  25. pyvale/data/case00_TET10_out.e +0 -0
  26. pyvale/data/case00_TET14_out.e +0 -0
  27. pyvale/data/case00_TET4_out.e +0 -0
  28. pyvale/data/case13_out.e +0 -0
  29. pyvale/data/case16_out.e +0 -0
  30. pyvale/data/case17_out.e +0 -0
  31. pyvale/data/case18_1_out.e +0 -0
  32. pyvale/data/case18_2_out.e +0 -0
  33. pyvale/data/case18_3_out.e +0 -0
  34. pyvale/data/case25_out.e +0 -0
  35. pyvale/data/case26_out.e +0 -0
  36. pyvale/data/optspeckle_2464x2056px_spec5px_8bit_gblur1px.tiff +0 -0
  37. pyvale/dataset.py +325 -0
  38. pyvale/errorcalculator.py +109 -0
  39. pyvale/errordriftcalc.py +146 -0
  40. pyvale/errorintegrator.py +336 -0
  41. pyvale/errorrand.py +607 -0
  42. pyvale/errorsyscalib.py +134 -0
  43. pyvale/errorsysdep.py +327 -0
  44. pyvale/errorsysfield.py +414 -0
  45. pyvale/errorsysindep.py +808 -0
  46. pyvale/examples/__init__.py +5 -0
  47. pyvale/examples/basics/ex1_1_basicscalars_therm2d.py +131 -0
  48. pyvale/examples/basics/ex1_2_sensormodel_therm2d.py +158 -0
  49. pyvale/examples/basics/ex1_3_customsens_therm3d.py +216 -0
  50. pyvale/examples/basics/ex1_4_basicerrors_therm3d.py +153 -0
  51. pyvale/examples/basics/ex1_5_fielderrs_therm3d.py +168 -0
  52. pyvale/examples/basics/ex1_6_caliberrs_therm2d.py +133 -0
  53. pyvale/examples/basics/ex1_7_spatavg_therm2d.py +123 -0
  54. pyvale/examples/basics/ex2_1_basicvectors_disp2d.py +112 -0
  55. pyvale/examples/basics/ex2_2_vectorsens_disp2d.py +111 -0
  56. pyvale/examples/basics/ex2_3_sensangle_disp2d.py +139 -0
  57. pyvale/examples/basics/ex2_4_chainfielderrs_disp2d.py +196 -0
  58. pyvale/examples/basics/ex2_5_vectorfields3d_disp3d.py +109 -0
  59. pyvale/examples/basics/ex3_1_basictensors_strain2d.py +114 -0
  60. pyvale/examples/basics/ex3_2_tensorsens2d_strain2d.py +111 -0
  61. pyvale/examples/basics/ex3_3_tensorsens3d_strain3d.py +182 -0
  62. pyvale/examples/basics/ex4_1_expsim2d_thermmech2d.py +171 -0
  63. pyvale/examples/basics/ex4_2_expsim3d_thermmech3d.py +252 -0
  64. pyvale/examples/genanalyticdata/ex1_1_scalarvisualisation.py +35 -0
  65. pyvale/examples/genanalyticdata/ex1_2_scalarcasebuild.py +43 -0
  66. pyvale/examples/genanalyticdata/ex2_1_analyticsensors.py +80 -0
  67. pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +79 -0
  68. pyvale/examples/renderblender/ex1_1_blenderscene.py +121 -0
  69. pyvale/examples/renderblender/ex1_2_blenderdeformed.py +119 -0
  70. pyvale/examples/renderblender/ex2_1_stereoscene.py +128 -0
  71. pyvale/examples/renderblender/ex2_2_stereodeformed.py +131 -0
  72. pyvale/examples/renderblender/ex3_1_blendercalibration.py +120 -0
  73. pyvale/examples/renderrasterisation/ex_rastenp.py +153 -0
  74. pyvale/examples/renderrasterisation/ex_rastercyth_oneframe.py +218 -0
  75. pyvale/examples/renderrasterisation/ex_rastercyth_static_cypara.py +187 -0
  76. pyvale/examples/renderrasterisation/ex_rastercyth_static_pypara.py +190 -0
  77. pyvale/examples/visualisation/ex1_1_plot_traces.py +102 -0
  78. pyvale/examples/visualisation/ex2_1_animate_sim.py +89 -0
  79. pyvale/experimentsimulator.py +175 -0
  80. pyvale/field.py +128 -0
  81. pyvale/fieldconverter.py +351 -0
  82. pyvale/fieldsampler.py +111 -0
  83. pyvale/fieldscalar.py +166 -0
  84. pyvale/fieldtensor.py +218 -0
  85. pyvale/fieldtransform.py +388 -0
  86. pyvale/fieldvector.py +213 -0
  87. pyvale/generatorsrandom.py +505 -0
  88. pyvale/imagedef2d.py +569 -0
  89. pyvale/integratorfactory.py +240 -0
  90. pyvale/integratorquadrature.py +217 -0
  91. pyvale/integratorrectangle.py +165 -0
  92. pyvale/integratorspatial.py +89 -0
  93. pyvale/integratortype.py +43 -0
  94. pyvale/output.py +17 -0
  95. pyvale/pyvaleexceptions.py +11 -0
  96. pyvale/raster.py +31 -0
  97. pyvale/rastercy.py +77 -0
  98. pyvale/rasternp.py +603 -0
  99. pyvale/rendermesh.py +147 -0
  100. pyvale/sensorarray.py +178 -0
  101. pyvale/sensorarrayfactory.py +196 -0
  102. pyvale/sensorarraypoint.py +278 -0
  103. pyvale/sensordata.py +71 -0
  104. pyvale/sensordescriptor.py +213 -0
  105. pyvale/sensortools.py +142 -0
  106. pyvale/simcases/case00_HEX20.i +242 -0
  107. pyvale/simcases/case00_HEX27.i +242 -0
  108. pyvale/simcases/case00_HEX8.i +242 -0
  109. pyvale/simcases/case00_TET10.i +242 -0
  110. pyvale/simcases/case00_TET14.i +242 -0
  111. pyvale/simcases/case00_TET4.i +242 -0
  112. pyvale/simcases/case01.i +101 -0
  113. pyvale/simcases/case02.i +156 -0
  114. pyvale/simcases/case03.i +136 -0
  115. pyvale/simcases/case04.i +181 -0
  116. pyvale/simcases/case05.i +234 -0
  117. pyvale/simcases/case06.i +305 -0
  118. pyvale/simcases/case07.geo +135 -0
  119. pyvale/simcases/case07.i +87 -0
  120. pyvale/simcases/case08.geo +144 -0
  121. pyvale/simcases/case08.i +153 -0
  122. pyvale/simcases/case09.geo +204 -0
  123. pyvale/simcases/case09.i +87 -0
  124. pyvale/simcases/case10.geo +204 -0
  125. pyvale/simcases/case10.i +257 -0
  126. pyvale/simcases/case11.geo +337 -0
  127. pyvale/simcases/case11.i +147 -0
  128. pyvale/simcases/case12.geo +388 -0
  129. pyvale/simcases/case12.i +329 -0
  130. pyvale/simcases/case13.i +140 -0
  131. pyvale/simcases/case14.i +159 -0
  132. pyvale/simcases/case15.geo +337 -0
  133. pyvale/simcases/case15.i +150 -0
  134. pyvale/simcases/case16.geo +391 -0
  135. pyvale/simcases/case16.i +357 -0
  136. pyvale/simcases/case17.geo +135 -0
  137. pyvale/simcases/case17.i +144 -0
  138. pyvale/simcases/case18.i +254 -0
  139. pyvale/simcases/case18_1.i +254 -0
  140. pyvale/simcases/case18_2.i +254 -0
  141. pyvale/simcases/case18_3.i +254 -0
  142. pyvale/simcases/case19.geo +252 -0
  143. pyvale/simcases/case19.i +99 -0
  144. pyvale/simcases/case20.geo +252 -0
  145. pyvale/simcases/case20.i +250 -0
  146. pyvale/simcases/case21.geo +74 -0
  147. pyvale/simcases/case21.i +155 -0
  148. pyvale/simcases/case22.geo +82 -0
  149. pyvale/simcases/case22.i +140 -0
  150. pyvale/simcases/case23.geo +164 -0
  151. pyvale/simcases/case23.i +140 -0
  152. pyvale/simcases/case24.geo +79 -0
  153. pyvale/simcases/case24.i +123 -0
  154. pyvale/simcases/case25.geo +82 -0
  155. pyvale/simcases/case25.i +140 -0
  156. pyvale/simcases/case26.geo +166 -0
  157. pyvale/simcases/case26.i +140 -0
  158. pyvale/simcases/run_1case.py +61 -0
  159. pyvale/simcases/run_all_cases.py +69 -0
  160. pyvale/simcases/run_build_case.py +64 -0
  161. pyvale/simcases/run_example_cases.py +69 -0
  162. pyvale/simtools.py +67 -0
  163. pyvale/visualexpplotter.py +191 -0
  164. pyvale/visualimagedef.py +74 -0
  165. pyvale/visualimages.py +76 -0
  166. pyvale/visualopts.py +493 -0
  167. pyvale/visualsimanimator.py +111 -0
  168. pyvale/visualsimsensors.py +318 -0
  169. pyvale/visualtools.py +136 -0
  170. pyvale/visualtraceplotter.py +142 -0
  171. pyvale-2025.5.3.dist-info/METADATA +144 -0
  172. pyvale-2025.5.3.dist-info/RECORD +175 -0
  173. pyvale-2025.5.3.dist-info/WHEEL +6 -0
  174. pyvale-2025.5.3.dist-info/licenses/LICENSE +21 -0
  175. pyvale-2025.5.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,640 @@
1
+ # ==============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ # ==============================================================================
6
+
7
+ """
8
+ NOTE: this module is a feature under developement.
9
+ """
10
+
11
+ import numpy as np
12
+ import cython
13
+ from cython.parallel import prange, parallel, threadid
14
+ from cython.cimports.libc.math import floor, ceil
15
+
16
+ from pyvale.rendermesh import RenderMeshData
17
+ from pyvale.cameradata import CameraData
18
+
19
+ # NOTE: This module is a feature under developement.
20
+
21
+ @cython.nogil
22
+ @cython.cfunc # python+C or cython.cfunc for C only
23
+ @cython.boundscheck(False) # Turn off array bounds checking
24
+ @cython.wraparound(False) # Turn off negative indexing
25
+ @cython.cdivision(True) # Turn off divide by zero check
26
+ @cython.inline
27
+ @cython.exceptval(check=False)
28
+ def range_len_double(start: cython.double,
29
+ stop: cython.double,
30
+ step: cython.double) -> cython.size_t:
31
+ return int(ceil((stop - start) / step))
32
+
33
+
34
+ @cython.nogil
35
+ @cython.cfunc # python+C or cython.cfunc for C only
36
+ @cython.boundscheck(False) # Turn off array bounds checking
37
+ @cython.wraparound(False) # Turn off negative indexing
38
+ @cython.cdivision(True) # Turn off divide by zero check
39
+ @cython.inline
40
+ @cython.exceptval(check=False)
41
+ def vec_range_int(start: cython.int,
42
+ stop: cython.int,
43
+ step: cython.int,
44
+ vec_buffer: cython.long[:]) -> cython.long[:]:
45
+
46
+ num_vals: cython.size_t = int(ceil((stop - start) / step))
47
+
48
+ vec_buffer[0] = start
49
+ ii: cython.size_t
50
+ for ii in range(1,num_vals):
51
+ vec_buffer[ii] = vec_buffer[ii-1] + step
52
+
53
+ return vec_buffer[0:num_vals]
54
+
55
+
56
+ @cython.nogil
57
+ @cython.cfunc # python+C or cython.cfunc for C only
58
+ @cython.boundscheck(False) # Turn off array bounds checking
59
+ @cython.wraparound(False) # Turn off negative indexing
60
+ @cython.cdivision(True) # Turn off divide by zero check
61
+ @cython.inline
62
+ @cython.exceptval(check=False)
63
+ def vec_max_double(vals: cython.double[:]) -> cython.double:
64
+
65
+ num_vals: cython.size_t = vals.shape[0]
66
+
67
+ ii: cython.size_t = 0
68
+ max_val: cython.double = vals[ii]
69
+
70
+ for ii in range(1,num_vals):
71
+ if vals[ii] > max_val:
72
+ max_val = vals[ii]
73
+
74
+ return max_val
75
+
76
+
77
+ @cython.nogil
78
+ @cython.cfunc # python+C or cython.cfunc for C only
79
+ @cython.boundscheck(False) # Turn off array bounds checking
80
+ @cython.wraparound(False) # Turn off negative indexing
81
+ @cython.cdivision(True) # Turn off divide by zero check
82
+ @cython.inline
83
+ @cython.exceptval(check=False)
84
+ def vec_min_double(vals: cython.double[:]) -> cython.double:
85
+
86
+ num_vals: cython.size_t = vals.shape[0]
87
+
88
+ ii: cython.size_t = 0
89
+ min_val: cython.double = vals[ii]
90
+
91
+ for ii in range(1,num_vals):
92
+ if vals[ii] < min_val:
93
+ min_val = vals[ii]
94
+
95
+ return min_val
96
+
97
+
98
+ @cython.nogil
99
+ @cython.cfunc # python+C or cython.cfunc for C only
100
+ @cython.boundscheck(False) # Turn off array bounds checking
101
+ @cython.wraparound(False) # Turn off negative indexing
102
+ @cython.cdivision(True) # Turn off divide by zero check
103
+ @cython.inline
104
+ @cython.exceptval(check=False)
105
+ def vec_dot_double(vec0: cython.double[:], vec1: cython.double[:]
106
+ ) -> cython.double:
107
+ vec0_len: cython.size_t = vec0.shape[0]
108
+ vec1_len: cython.size_t = vec1.shape[0]
109
+ if vec0_len != vec1_len:
110
+ return 0.0
111
+
112
+ ii: cython.size_t = 0
113
+ dot: cython.double = 0.0
114
+ for ii in range(vec0_len):
115
+ dot += vec0[ii]*vec1[ii]
116
+
117
+ return dot
118
+
119
+
120
+ @cython.nogil
121
+ @cython.cfunc # python+C or cython.cfunc for C only
122
+ @cython.boundscheck(False) # Turn off array bounds checking
123
+ @cython.wraparound(False) # Turn off negative indexing
124
+ @cython.cdivision(True) # Turn off divide by zero check
125
+ @cython.inline
126
+ @cython.exceptval(check=False)
127
+ def bound_index_min(min_val: cython.double) -> cython.int:
128
+ min_ind: cython.int = int(floor(min_val))
129
+ if min_ind < 0:
130
+ min_ind = 0
131
+ return min_ind
132
+
133
+
134
+ @cython.nogil
135
+ @cython.cfunc # python+C or cython.cfunc for C only
136
+ @cython.boundscheck(False) # Turn off array bounds checking
137
+ @cython.wraparound(False) # Turn off negative indexing
138
+ @cython.cdivision(True) # Turn off divide by zero check
139
+ @cython.inline
140
+ @cython.exceptval(check=False)
141
+ def bound_index_max(max_val: cython.double,
142
+ num_pixels: cython.int) -> cython.int:
143
+ max_ind: cython.int = int(ceil(max_val))
144
+ if max_ind > (num_pixels-1):
145
+ max_ind = (num_pixels-1)
146
+ return max_ind
147
+
148
+
149
+ @cython.nogil
150
+ @cython.cfunc # python+C or cython.cfunc for C only
151
+ @cython.boundscheck(False) # Turn off array bounds checking
152
+ @cython.wraparound(False) # Turn off negative indexing
153
+ @cython.cdivision(True) # Turn off divide by zero check
154
+ @cython.inline
155
+ @cython.exceptval(check=False)
156
+ def mult_mat44_by_vec3(mat44: cython.double[:,:], vec3_in: cython.double[:],
157
+ vec3_out: cython.double[:]) -> cython.double[:]:
158
+
159
+ vec3_out[0] = (mat44[0,0]*vec3_in[0]
160
+ + mat44[0,1]*vec3_in[1]
161
+ + mat44[0,2]*vec3_in[2]
162
+ + mat44[0,3])
163
+ vec3_out[1] = (mat44[1,0]*vec3_in[0]
164
+ + mat44[1,1]*vec3_in[1]
165
+ + mat44[1,2]*vec3_in[2]
166
+ + mat44[1,3])
167
+ vec3_out[2] = (mat44[2,0]*vec3_in[0]
168
+ + mat44[2,1]*vec3_in[1]
169
+ + mat44[2,2]*vec3_in[2]
170
+ + mat44[2,3])
171
+ vec3_out[3] = (mat44[3,0]*vec3_in[0]
172
+ + mat44[3,1]*vec3_in[1]
173
+ + mat44[3,2]*vec3_in[2]
174
+ + mat44[3,3])
175
+
176
+ return vec3_out
177
+
178
+
179
+ @cython.nogil
180
+ @cython.cfunc # python+C or cython.cfunc for C only
181
+ @cython.boundscheck(False) # Turn off array bounds checking
182
+ @cython.wraparound(False) # Turn off negative indexing
183
+ @cython.cdivision(True) # Turn off divide by zero check
184
+ @cython.exceptval(check=False)
185
+ def world_to_raster_coords(coords_world: cython.double[:],
186
+ world_to_cam_mat: cython.double[:,:],
187
+ image_dist: cython.double,
188
+ image_dims: cython.double[:],
189
+ num_pixels: cython.int[:],
190
+ coords_raster: cython.double[:]
191
+ ) -> cython.double[:]:
192
+ xx: cython.size_t = 0
193
+ yy: cython.size_t = 1
194
+ zz: cython.size_t = 2
195
+ ww: cython.size_t = 3
196
+
197
+ coords_raster = mult_mat44_by_vec3(world_to_cam_mat,
198
+ coords_world,
199
+ coords_raster)
200
+
201
+ coords_raster[xx] = coords_raster[xx] / coords_raster[ww]
202
+ coords_raster[yy] = coords_raster[yy] / coords_raster[ww]
203
+ coords_raster[zz] = coords_raster[zz] / coords_raster[ww]
204
+
205
+ coords_raster[xx] = (image_dist * coords_raster[xx]
206
+ / -coords_raster[zz])
207
+ coords_raster[yy] = (image_dist * coords_raster[yy]
208
+ / -coords_raster[zz])
209
+
210
+ coords_raster[xx] = 2*coords_raster[xx] / image_dims[xx]
211
+ coords_raster[yy] = 2*coords_raster[yy] / image_dims[yy]
212
+
213
+ coords_raster[xx] = (coords_raster[xx] + 1)/2 * num_pixels[xx]
214
+ coords_raster[yy] = (1-coords_raster[yy])/2 * num_pixels[yy]
215
+ coords_raster[zz] = -coords_raster[zz]
216
+
217
+ return coords_raster
218
+
219
+ @cython.cfunc
220
+ @cython.nogil
221
+ @cython.boundscheck(False)
222
+ @cython.wraparound(False)
223
+ @cython.inline
224
+ @cython.exceptval(check=False)
225
+ def edge_function(vert_0: cython.double[:],
226
+ vert_1: cython.double[:],
227
+ vert_2: cython.double[:]) -> cython.double:
228
+ edge_fun: cython.double = (
229
+ (vert_2[0] - vert_0[0]) * (vert_1[1] - vert_0[1])
230
+ - (vert_2[1] - vert_0[1]) * (vert_1[0] - vert_0[0]))
231
+ return edge_fun
232
+
233
+
234
+ @cython.cfunc
235
+ @cython.nogil
236
+ @cython.boundscheck(False)
237
+ @cython.wraparound(False)
238
+ @cython.inline
239
+ @cython.exceptval(check=False)
240
+ def edge_function_pt(vert_0: cython.double[:],
241
+ vert_1: cython.double[:],
242
+ vert_2_x: cython.double,
243
+ vert_2_y: cython.double) -> cython.double:
244
+ edge_fun: cython.double = (
245
+ (vert_2_x - vert_0[0]) * (vert_1[1] - vert_0[1])
246
+ - (vert_2_y - vert_0[1]) * (vert_1[0] - vert_0[0]))
247
+ return edge_fun
248
+
249
+
250
+ @cython.ccall
251
+ @cython.boundscheck(False)
252
+ @cython.wraparound(False)
253
+ @cython.cdivision(True)
254
+ def average_image(image_subpx: cython.double[:,:],
255
+ sub_samp: cython.int,
256
+ ) -> cython.double[:,:]:
257
+
258
+ if sub_samp <= 1:
259
+ return np.asarray(image_subpx[:,:])
260
+
261
+ px_num_y: cython.size_t = int(ceil(image_subpx.shape[0]/sub_samp))
262
+ px_num_x: cython.size_t = int(ceil(image_subpx.shape[1]/sub_samp))
263
+
264
+ image_buff_avg_np = np.full((px_num_y,px_num_x),0.0,dtype=np.float64)
265
+ image_buff_avg: cython.double[:,:] = image_buff_avg_np
266
+
267
+ num_subpx_y: cython.size_t = image_subpx.shape[0]
268
+ num_subpx_x: cython.size_t = image_subpx.shape[1]
269
+ subpx_per_px: cython.double = float(sub_samp*sub_samp)
270
+ ss_size: cython.size_t = sub_samp
271
+
272
+ num_px_y: cython.size_t = int(num_subpx_y/sub_samp)
273
+ num_px_x: cython.size_t = int(num_subpx_x/sub_samp)
274
+
275
+ px_sum: cython.double = 0.0
276
+
277
+ ix: cython.size_t = 0
278
+ iy: cython.size_t = 0
279
+ sx: cython.size_t = 0
280
+ sy: cython.size_t = 0
281
+
282
+ for iy in range(num_px_y):
283
+ for ix in range(num_px_x):
284
+ px_sum = 0.0
285
+ for sy in range(ss_size):
286
+ for sx in range(ss_size):
287
+ px_sum += image_subpx[ss_size*iy+sy,ss_size*ix+sx]
288
+
289
+ image_buff_avg[iy,ix] = px_sum / subpx_per_px
290
+
291
+ return image_buff_avg
292
+
293
+
294
+ @cython.nogil
295
+ @cython.cfunc
296
+ @cython.boundscheck(False)
297
+ @cython.wraparound(False)
298
+ @cython.cdivision(True)
299
+ @cython.exceptval(check=False)
300
+ def _average_image(image_buff_subpx_in: cython.double[:,:],
301
+ sub_samp: cython.int,
302
+ image_buff_avg_out: cython.double[:,:]
303
+ ) -> cython.int:
304
+
305
+ num_subpx_y: cython.size_t = image_buff_subpx_in.shape[0]
306
+ num_subpx_x: cython.size_t = image_buff_subpx_in.shape[1]
307
+ subpx_per_px: cython.double = float(sub_samp*sub_samp)
308
+ ss_size: cython.size_t = sub_samp
309
+
310
+ num_px_y: cython.size_t = int(num_subpx_y/sub_samp)
311
+ num_px_x: cython.size_t = int(num_subpx_x/sub_samp)
312
+
313
+ px_sum: cython.double = 0.0
314
+
315
+ ix: cython.size_t = 0
316
+ iy: cython.size_t = 0
317
+ sx: cython.size_t = 0
318
+ sy: cython.size_t = 0
319
+
320
+ for iy in range(num_px_y):
321
+ for ix in range(num_px_x):
322
+ px_sum = 0.0
323
+ for sy in range(ss_size):
324
+ for sx in range(ss_size):
325
+ px_sum += image_buff_subpx_in[ss_size*iy+sy,ss_size*ix+sx]
326
+
327
+ image_buff_avg_out[iy,ix] = px_sum / subpx_per_px
328
+
329
+ return 0
330
+
331
+
332
+ #///////////////////////////////////////////////////////////////////////////////
333
+ @cython.ccall # python+C or cython.cfunc for C only
334
+ @cython.boundscheck(False) # Turn off array bounds checking
335
+ @cython.wraparound(False) # Turn off negative indexing
336
+ @cython.cdivision(True) # Turn off divide by zero check
337
+ def raster_frame(coords: cython.double[:,:],
338
+ connect: cython.size_t[:,:],
339
+ fields_to_render: cython.double[:,:],
340
+ cam_data: CameraData,
341
+ ) -> tuple[np.ndarray,np.ndarray,int]:
342
+
343
+
344
+ world_to_cam_mat: cython.double[:,:] = cam_data.world_to_cam_mat
345
+ pixels_num: cython.int[:] = cam_data.pixels_num
346
+ image_dims: cython.double[:] = cam_data.image_dims
347
+ image_dist: cython.double = cam_data.image_dist
348
+ sub_samp: cython.int = cam_data.sub_samp
349
+
350
+ nodes_per_elem: cython.size_t = connect.shape[1]
351
+ fields_num: cython.size_t = fields_to_render.shape[1]
352
+ sub_pix_x: cython.int = pixels_num[0]*sub_samp
353
+ sub_pix_y: cython.int = pixels_num[1]*sub_samp
354
+
355
+ #---------------------------------------------------------------------------
356
+ # Final image buffer memory allocation
357
+ image_buff_avg_np = np.full((pixels_num[1],pixels_num[0],fields_num),0.0,dtype=np.float64)
358
+ image_buff_avg: cython.double[:,:,:] = image_buff_avg_np
359
+
360
+ depth_buff_avg_np = np.full((pixels_num[1],pixels_num[0]),0.0,dtype=np.float64)
361
+ depth_buff_avg: cython.double[:,:] = depth_buff_avg_np
362
+
363
+ #---------------------------------------------------------------------------
364
+ # Per-thread scratch memory allocations
365
+ depth_buffer_np = np.full((sub_pix_y,sub_pix_x),1.0e6,dtype=np.float64)
366
+ depth_buff_subpx: cython.double[:,:] = depth_buffer_np
367
+
368
+ image_buffer_np = np.full((sub_pix_y,sub_pix_x,fields_num),0.0,dtype=np.float64)
369
+ image_buff_subpx: cython.double[:,:,:] = image_buffer_np
370
+
371
+ # shape=(nodes_per_elem, coord[X,Y,Z,W])
372
+ nodes_raster_np = np.empty((nodes_per_elem,4),dtype=np.float64)
373
+ nodes_raster_buff: cython.double[:,:] = nodes_raster_np
374
+
375
+ field_raster_np = np.empty((nodes_per_elem,),dtype=np.float64)
376
+ field_raster_buff: cython.double[:] = field_raster_np
377
+
378
+ px_coord_np = np.zeros((nodes_per_elem,),np.float64)
379
+ px_coord_buff: cython.double[:] = px_coord_np
380
+
381
+ weights_np = np.zeros((nodes_per_elem,),np.float64)
382
+ weights_buff: cython.double[:] = weights_np
383
+ #---------------------------------------------------------------------------
384
+
385
+ elems_in_image: cython.size_t = _raster_frame(coords[:,:],
386
+ connect[:,:],
387
+ fields_to_render[:,:],
388
+ world_to_cam_mat[:,:],
389
+ pixels_num[:],
390
+ image_dims[:],
391
+ image_dist,
392
+ sub_samp,
393
+ image_buff_avg[:,:,:],
394
+ depth_buff_avg[:,:],
395
+ image_buff_subpx[:,:,:],
396
+ depth_buff_subpx[:,:],
397
+ nodes_raster_buff[:,:],
398
+ field_raster_buff[:],
399
+ px_coord_buff[:],
400
+ weights_buff[:])
401
+
402
+ return (image_buff_avg_np,depth_buff_avg_np,elems_in_image)
403
+
404
+
405
+ #///////////////////////////////////////////////////////////////////////////////
406
+ #@cython.nogil
407
+ @cython.cfunc # python+C or cython.cfunc for C only
408
+ @cython.boundscheck(False) # Turn off array bounds checking
409
+ @cython.wraparound(False) # Turn off negative indexing
410
+ @cython.cdivision(True) # Turn off divide by zero check
411
+ @cython.exceptval(check=False) # Turn off exceptions
412
+ def _raster_frame(coords: cython.double[:,:],
413
+ connect: cython.size_t[:,:],
414
+ fields_to_render: cython.double[:,:],
415
+ world_to_cam_mat: cython.double[:,:],
416
+ num_pixels: cython.int[:],
417
+ image_dims: cython.double[:],
418
+ image_dist: cython.double,
419
+ sub_samp: cython.int,
420
+ # From here these are memory buffers that will be written into
421
+ image_buff_avg: cython.double[:,:,:],
422
+ depth_buff_avg: cython.double[:,:],
423
+ image_buff_subpx: cython.double[:,:,:],
424
+ depth_buff_subpx: cython.double[:,:],
425
+ nodes_raster_buff: cython.double[:,:],
426
+ field_raster_buff: cython.double[:],
427
+ px_coord_buff: cython.double[:],
428
+ weights_buff: cython.double[:],
429
+ ) -> cython.size_t:
430
+ """Rasters a single frame and all associated fields into the image and depth
431
+ buffer provided as inputs to the function. This is a pure cython function
432
+ with the GIL released for parallelisation. All fields (textures) are
433
+ rendered in a sub-loop so that the depth buffer and inside/outside test is
434
+ only performed once for all fields to be rendered.
435
+
436
+ Parameters
437
+ ----------
438
+ coords : cython.double[:,:]
439
+ Input. shape=(num_nodes,coords[x,y,z,w])
440
+ connect : cython.size_t[:,:]
441
+ Input. shape=(num_elems,nodes_per_elem)
442
+ fields_to_render : cython.double[:,:]
443
+ Input. shape=(num_nodes,num_fields)
444
+ world_to_cam_mat : cython.double[:,:]
445
+ Input. Homogeneous coordinate transformation matrix from world to camera
446
+ coordinates. shape=(4,4).
447
+ num_pixels : cython.int[:]
448
+ Input. shape=(2 [num_px_x,num_px_y],)
449
+ image_dims : cython.double[:]
450
+ Input. shape=(2 [fov_size_x,fov_size_y],)
451
+ image_dist : cython.double
452
+ Input.
453
+ sub_samp : cython.int
454
+ Number of subsamples per pixel for anti-aliasing.
455
+ image_buff_avg : cython.double[:,:,:]
456
+ Output buffer. shape=(num_px_y,num_px_x,num_fields)
457
+ depth_buff_avg : cython.double[:,:]
458
+ Output buffer. shape=(num_px_y,num_px_x)
459
+ image_buff_subpx : cython.double[:,:,:]
460
+ Processing buffer (output). shape=(num_subpx_y,num_subpx_x,num_fields)
461
+ depth_buff_subpx : cython.double[:,:]
462
+ Processing buffer (output). shape=(num_subpx_y,num_subpx_x)
463
+ nodes_raster_buff : cython.double[:,:]
464
+ Processing buffer (output). shape=(nodes_per_elem, 4 coord[x,y,z,w])
465
+ field_raster_buff : cython.double[:]
466
+ Processing buffer (output). shape=(nodes_per_elem,)
467
+ px_coord_buff : cython.double[:]
468
+ Processing buffer (output). shape=(nodes_per_elem,)
469
+ weights_buff : cython.double[:]
470
+ Processing buffer (output). shape=(nodes_per_elem,)
471
+
472
+ Returns
473
+ -------
474
+ cython.size_t
475
+ Number of rendered elements after backface culling and cropping.
476
+ """
477
+
478
+ xx: cython.size_t = 0
479
+ yy: cython.size_t = 1
480
+ zz: cython.size_t = 2
481
+
482
+ elem_count: cython.size_t = connect.shape[0]
483
+ nodes_per_elem: cython.size_t = connect.shape[1]
484
+ fields_num: cython.size_t = fields_to_render.shape[1]
485
+
486
+ # tolerance for floating point zero dot product
487
+ tol: cython.double = 1e-12
488
+
489
+ #elem_count: cython.size_t = 1
490
+ elems_in_image: cython.size_t = 0
491
+
492
+ ee: cython.size_t = 0
493
+ nn: cython.size_t = 0
494
+ ii: cython.size_t = 0
495
+ jj: cython.size_t = 0
496
+ ww: cython.size_t = 0
497
+ ff: cython.size_t = 0
498
+
499
+ for ee in range(elem_count):
500
+
501
+ for nn in range(nodes_per_elem):
502
+ # shape=(nodes_per_elem, coord[X,Y,Z,W])
503
+ nodes_raster_buff[nn,:] = world_to_raster_coords(coords[connect[ee,nn],:],
504
+ world_to_cam_mat,
505
+ image_dist,
506
+ image_dims,
507
+ num_pixels,
508
+ nodes_raster_buff[nn,:])
509
+
510
+
511
+ elem_area: cython.double = edge_function(nodes_raster_buff[0,:],
512
+ nodes_raster_buff[1,:],
513
+ nodes_raster_buff[2,:])
514
+
515
+ if elem_area < -tol: # Backface culling
516
+ continue
517
+
518
+ print(f"{nodes_raster_buff[0,0]},{nodes_raster_buff[0,1]},{nodes_raster_buff[0,2]}")
519
+ print(f"{nodes_raster_buff[1,0]},{nodes_raster_buff[1,1]},{nodes_raster_buff[1,2]}")
520
+ print(f"{nodes_raster_buff[2,0]},{nodes_raster_buff[2,1]},{nodes_raster_buff[2,2]}")
521
+ print(f"{ee} ELEM AREA : {elem_area}")
522
+ print()
523
+
524
+ x_min: cython.double = vec_min_double(nodes_raster_buff[:,xx])
525
+ x_max: cython.double = vec_max_double(nodes_raster_buff[:,xx])
526
+
527
+ if ((x_min > num_pixels[xx]-1) or (x_max < 0)): # x crop
528
+ continue
529
+
530
+ y_min: cython.double = vec_min_double(nodes_raster_buff[:,yy])
531
+ y_max: cython.double = vec_max_double(nodes_raster_buff[:,yy])
532
+
533
+ if ((y_min > num_pixels[yy]-1) or (y_max < 0)): # y crop
534
+ continue
535
+
536
+ elems_in_image += 1
537
+
538
+ xi_min: cython.size_t = bound_index_min(x_min)
539
+ xi_max: cython.size_t = bound_index_max(x_max,num_pixels[xx])
540
+ yi_min: cython.size_t = bound_index_min(y_min)
541
+ yi_max: cython.size_t = bound_index_max(y_max,num_pixels[yy])
542
+
543
+ for nn in range(nodes_per_elem):
544
+ nodes_raster_buff[nn,zz] = 1/nodes_raster_buff[nn,zz]
545
+
546
+ num_bound_x: cython.size_t = range_len_double(float(xi_min),
547
+ float(xi_max),
548
+ 1.0/float(sub_samp))
549
+ num_bound_y: cython.size_t = range_len_double(float(yi_min),
550
+ float(yi_max),
551
+ 1.0/float(sub_samp))
552
+
553
+ bound_coord_x: cython.double = float(xi_min) + 1.0/(2.0*float(sub_samp))
554
+ bound_coord_y: cython.double = float(yi_min) + 1.0/(2.0*float(sub_samp))
555
+ coord_step: cython.double = 1.0/float(sub_samp)
556
+ bound_ind_x: cython.size_t = sub_samp*xi_min
557
+ bound_ind_y: cython.size_t = sub_samp*yi_min
558
+
559
+
560
+ for jj in range(num_bound_y):
561
+
562
+ bound_coord_x = float(xi_min) + 1.0/(2.0*float(sub_samp))
563
+ bound_ind_x: cython.size_t = sub_samp*xi_min
564
+
565
+ for ii in range(num_bound_x):
566
+
567
+ px_coord_buff[xx] = bound_coord_x
568
+ px_coord_buff[yy] = bound_coord_y
569
+
570
+ # Check the edge functions for each edge one at a time, as soon
571
+ # as one is outside we don't need to do anymore work
572
+ weights_buff[0] = edge_function(nodes_raster_buff[1,:],
573
+ nodes_raster_buff[2,:],
574
+ px_coord_buff)
575
+ if (weights_buff[0] < -tol):
576
+ bound_coord_x += coord_step
577
+ bound_ind_x += 1
578
+ continue
579
+
580
+ weights_buff[1] = edge_function(nodes_raster_buff[2,:],
581
+ nodes_raster_buff[0,:],
582
+ px_coord_buff)
583
+ if (weights_buff[1] < -tol):
584
+ bound_coord_x += coord_step
585
+ bound_ind_x += 1
586
+ continue
587
+
588
+
589
+ weights_buff[2] = edge_function(nodes_raster_buff[0,:],
590
+ nodes_raster_buff[1,:],
591
+ px_coord_buff)
592
+ if (weights_buff[2] < -tol):
593
+ bound_coord_x += coord_step
594
+ bound_ind_x += 1
595
+ continue
596
+
597
+
598
+ for ww in range(nodes_per_elem):
599
+ weights_buff[ww] = weights_buff[ww] / elem_area
600
+
601
+ weight_dot_nodes: cython.double = vec_dot_double(
602
+ weights_buff,
603
+ nodes_raster_buff[:,zz])
604
+
605
+ # Check the depth buffer, if the element is behind move on
606
+ px_coord_z: cython.double = 1/weight_dot_nodes
607
+ if px_coord_z >= depth_buff_subpx[bound_ind_y,bound_ind_x]:
608
+ continue
609
+
610
+ # We only need one depth buffer for all fields
611
+ depth_buff_subpx[bound_ind_y,bound_ind_x] = px_coord_z
612
+
613
+ for ff in range(fields_num):
614
+ for nn in range(nodes_per_elem):
615
+
616
+ field_raster_buff[nn] = (fields_to_render[connect[ee,nn],ff]
617
+ *nodes_raster_buff[nn,zz])
618
+
619
+ px_field: cython.double = (vec_dot_double(field_raster_buff,
620
+ weights_buff)
621
+ *px_coord_z)
622
+
623
+ image_buff_subpx[bound_ind_y,bound_ind_x,ff] = px_field
624
+
625
+ # end for(x) - increment the x coords
626
+ bound_coord_x += coord_step
627
+ bound_ind_x += 1
628
+
629
+ # end for(y) - increment the y coords
630
+ bound_coord_y += coord_step
631
+ bound_ind_y += 1
632
+
633
+ _average_image(depth_buff_subpx,sub_samp,depth_buff_avg)
634
+
635
+ for ff in range(fields_num):
636
+ _average_image(image_buff_subpx[:,:,ff],
637
+ sub_samp,
638
+ image_buff_avg[:,:,ff])
639
+
640
+ return elems_in_image
@@ -0,0 +1,5 @@
1
+ #===============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ #===============================================================================
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file