pyvale 2025.4.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.

Potentially problematic release.


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

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