PyParticles3 0.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.
Files changed (100) hide show
  1. pyparticles/__init__.py +26 -0
  2. pyparticles/__main__.py +7 -0
  3. pyparticles/animation/__init__.py +21 -0
  4. pyparticles/animation/animated_cli.py +53 -0
  5. pyparticles/animation/animated_ogl.py +691 -0
  6. pyparticles/animation/animated_ogl_compat.py +243 -0
  7. pyparticles/animation/animated_scatter.py +94 -0
  8. pyparticles/animation/animation.py +251 -0
  9. pyparticles/animation/test_animation.py +310 -0
  10. pyparticles/demo/__init__.py +33 -0
  11. pyparticles/demo/bubble.py +106 -0
  12. pyparticles/demo/electromagnetic_demo.py +128 -0
  13. pyparticles/demo/electrostatic_demo.py +126 -0
  14. pyparticles/demo/fountain.py +478 -0
  15. pyparticles/demo/gas_lennard_jones.py +87 -0
  16. pyparticles/demo/gravity_clusters.py +73 -0
  17. pyparticles/demo/solar_system.py +222 -0
  18. pyparticles/demo/springs.py +141 -0
  19. pyparticles/demo/springs_constr.py +135 -0
  20. pyparticles/demo/test.py +34 -0
  21. pyparticles/forces/__init__.py +21 -0
  22. pyparticles/forces/const_force.py +140 -0
  23. pyparticles/forces/damping.py +174 -0
  24. pyparticles/forces/drag.py +167 -0
  25. pyparticles/forces/electromagnetic.py +105 -0
  26. pyparticles/forces/electromagnetic_field.py +95 -0
  27. pyparticles/forces/electrostatic.py +72 -0
  28. pyparticles/forces/force.py +75 -0
  29. pyparticles/forces/force_constrained.py +32 -0
  30. pyparticles/forces/fused_const_drag.py +445 -0
  31. pyparticles/forces/gravity.py +345 -0
  32. pyparticles/forces/lennard_jones.py +74 -0
  33. pyparticles/forces/linear_spring.py +86 -0
  34. pyparticles/forces/linear_spring_constrained.py +72 -0
  35. pyparticles/forces/multiple_force.py +134 -0
  36. pyparticles/forces/pseudo_bubble.py +226 -0
  37. pyparticles/forces/van_der_waals_force.py +60 -0
  38. pyparticles/forces/vector_field_force.py +52 -0
  39. pyparticles/geometry/__init__.py +21 -0
  40. pyparticles/geometry/dist.py +24 -0
  41. pyparticles/geometry/intersection.py +62 -0
  42. pyparticles/geometry/transformations.py +387 -0
  43. pyparticles/main/__init__.py +21 -0
  44. pyparticles/main/main.py +466 -0
  45. pyparticles/measures/__init__.py +21 -0
  46. pyparticles/measures/elastic_potential_energy.py +67 -0
  47. pyparticles/measures/gravitational_potential_energy.py +69 -0
  48. pyparticles/measures/kinetic_energy.py +68 -0
  49. pyparticles/measures/mass.py +59 -0
  50. pyparticles/measures/measure.py +156 -0
  51. pyparticles/measures/momentum.py +144 -0
  52. pyparticles/measures/total_energy.py +68 -0
  53. pyparticles/ode/__init__.py +21 -0
  54. pyparticles/ode/euler_solver.py +214 -0
  55. pyparticles/ode/euler_solver_constrained.py +49 -0
  56. pyparticles/ode/leapfrog_solver.py +37 -0
  57. pyparticles/ode/leapfrog_solver_constrained.py +53 -0
  58. pyparticles/ode/midpoint_solver.py +43 -0
  59. pyparticles/ode/midpoint_solver_constrained.py +60 -0
  60. pyparticles/ode/ode_solver.py +134 -0
  61. pyparticles/ode/ode_solver_constrained.py +43 -0
  62. pyparticles/ode/runge_kutta_solver.py +79 -0
  63. pyparticles/ode/runge_kutta_solver_constrained.py +98 -0
  64. pyparticles/ode/sim_time.py +56 -0
  65. pyparticles/ode/stormer_verlet_solver.py +50 -0
  66. pyparticles/ode/stormer_verlet_solver_constrained.py +73 -0
  67. pyparticles/ogl/__init__.py +21 -0
  68. pyparticles/ogl/axis_ogl.py +210 -0
  69. pyparticles/ogl/draw_particles_ogl.py +313 -0
  70. pyparticles/ogl/draw_particles_ogl_compat.py +509 -0
  71. pyparticles/ogl/draw_vector_field.py +221 -0
  72. pyparticles/ogl/opencl_gl_vbo.py +485 -0
  73. pyparticles/ogl/trackball.py +131 -0
  74. pyparticles/ogl/translate_scene.py +87 -0
  75. pyparticles/pset/__init__.py +21 -0
  76. pyparticles/pset/boundary.py +69 -0
  77. pyparticles/pset/cluster.py +28 -0
  78. pyparticles/pset/constrained_force_interactions.py +63 -0
  79. pyparticles/pset/constrained_x.py +158 -0
  80. pyparticles/pset/constraint.py +42 -0
  81. pyparticles/pset/default_boundary.py +43 -0
  82. pyparticles/pset/file_cluster.py +131 -0
  83. pyparticles/pset/logger.py +152 -0
  84. pyparticles/pset/octree.py +451 -0
  85. pyparticles/pset/opencl_context.py +374 -0
  86. pyparticles/pset/particles_set.py +499 -0
  87. pyparticles/pset/periodic_boundary.py +36 -0
  88. pyparticles/pset/rand_cluster.py +188 -0
  89. pyparticles/pset/rebound_boundary.py +68 -0
  90. pyparticles/utils/__init__.py +21 -0
  91. pyparticles/utils/parse_args.py +72 -0
  92. pyparticles/utils/problem_config.py +608 -0
  93. pyparticles/utils/pypart_global.py +122 -0
  94. pyparticles/utils/time_formatter.py +50 -0
  95. pyparticles3-0.4.0.dist-info/METADATA +395 -0
  96. pyparticles3-0.4.0.dist-info/RECORD +100 -0
  97. pyparticles3-0.4.0.dist-info/WHEEL +5 -0
  98. pyparticles3-0.4.0.dist-info/entry_points.txt +3 -0
  99. pyparticles3-0.4.0.dist-info/licenses/LICENSE-gpl-3.0.txt +674 -0
  100. pyparticles3-0.4.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,509 @@
1
+ # PyParticles : Particles simulation in python
2
+ # Copyright (C) 2012 Simone Riva
3
+ #
4
+ # Modern compatibility helpers for the legacy OpenGL particle renderer.
5
+
6
+ """Modern PyOpenGL/NumPy compatibility for :mod:`draw_particles_ogl`.
7
+
8
+ The original renderer assumes Python-2-era scalar coercions and frequently
9
+ passes float64 NumPy storage to OpenGL pointers declared as GL_FLOAT. This
10
+ module preserves its public interface while normalizing scalar values and
11
+ client arrays for current PyOpenGL and NumPy releases.
12
+ """
13
+
14
+ import ctypes
15
+ import os
16
+ import time
17
+
18
+ import numpy as np
19
+
20
+ from OpenGL.GL import (
21
+ GL_ALPHA_TEST,
22
+ GL_ARRAY_BUFFER,
23
+ GL_BLEND,
24
+ GL_COLOR_ARRAY,
25
+ GL_DEPTH_TEST,
26
+ GL_FLOAT,
27
+ GL_FOG,
28
+ GL_LINES,
29
+ GL_MULTISAMPLE,
30
+ GL_POINTS,
31
+ GL_QUERY_RESULT,
32
+ GL_QUERY_RESULT_AVAILABLE,
33
+ GL_TEXTURE_2D,
34
+ GL_TIME_ELAPSED,
35
+ GL_UNSIGNED_INT,
36
+ GL_VERTEX_ARRAY,
37
+ glBegin,
38
+ glBeginQuery,
39
+ glBindBuffer,
40
+ glCallList,
41
+ glColor4f,
42
+ glColorPointer,
43
+ glDeleteQueries,
44
+ glDisable,
45
+ glDisableClientState,
46
+ glDrawArrays,
47
+ glDrawElements,
48
+ glEnable,
49
+ glEnableClientState,
50
+ glEnd,
51
+ glEndQuery,
52
+ glGenQueries,
53
+ glIsEnabled,
54
+ glPointSize,
55
+ glPopMatrix,
56
+ glPushMatrix,
57
+ glScalef,
58
+ glTranslatef,
59
+ glVertex3f,
60
+ glVertexPointer,
61
+ )
62
+ from OpenGL.raw.GL.VERSION.GL_1_5 import (
63
+ glGetQueryObjectiv as _raw_glGetQueryObjectiv,
64
+ )
65
+ from OpenGL.raw.GL.VERSION.GL_3_3 import (
66
+ glGetQueryObjectui64v as _raw_glGetQueryObjectui64v,
67
+ )
68
+
69
+ import pyparticles.ogl.draw_particles_ogl as legacy
70
+
71
+
72
+ def _scalar(value):
73
+ """Return a Python float from a scalar or one-element NumPy value."""
74
+ return float(np.asarray(value).reshape(-1)[0])
75
+
76
+
77
+ def charged_particles_color(pset, i):
78
+ a = 0.4
79
+ charge = _scalar(pset.Q[i])
80
+ if charge > 0.0:
81
+ return (1.0, a, a, 1.0)
82
+ if charge < 0.0:
83
+ return (a, a, 1.0, 1.0)
84
+ return (a, a, a, 1.0)
85
+
86
+
87
+ def charged_particles_vect_color(RGBA, pset):
88
+ a = 0.4
89
+ charges = np.asarray(pset.Q).reshape(-1)
90
+ RGBA[charges > 0.0, :] = (1.0, a, a, 1.0)
91
+ RGBA[charges < 0.0, :] = (a, a, 1.0, 1.0)
92
+ RGBA[charges == 0.0, :] = (a, a, a, 1.0)
93
+
94
+
95
+ class DrawParticlesGL(legacy.DrawParticlesGL):
96
+ """Legacy particle renderer with modern scalar and buffer handling."""
97
+
98
+ _GPU_QUERY_LIMIT = 64
99
+ _RENDER_BENCH_MODES = {
100
+ "legacy": (),
101
+ "no_msaa": (GL_MULTISAMPLE,),
102
+ "no_fog": (GL_FOG,),
103
+ "no_blend": (GL_BLEND,),
104
+ "no_alpha": (GL_ALPHA_TEST,),
105
+ "no_depth": (GL_DEPTH_TEST,),
106
+ "fast_points": (
107
+ GL_MULTISAMPLE,
108
+ GL_FOG,
109
+ GL_BLEND,
110
+ GL_ALPHA_TEST,
111
+ GL_DEPTH_TEST,
112
+ GL_TEXTURE_2D,
113
+ ),
114
+ }
115
+
116
+ def __init__(self, *args, **kwargs):
117
+ super(DrawParticlesGL, self).__init__(*args, **kwargs)
118
+ self.__shared_position_vbo = None
119
+ self.__shared_position_draw_complete_callback = None
120
+ self.__last_draw_submit_seconds = 0.0
121
+
122
+ requested_mode = os.environ.get(
123
+ "PYPARTICLES_GL_BENCH_MODE", "legacy"
124
+ ).strip().lower()
125
+ if requested_mode not in self._RENDER_BENCH_MODES:
126
+ requested_mode = "legacy"
127
+ self.__render_benchmark_mode = requested_mode
128
+
129
+ if "PYPARTICLES_GL_BENCH_MODE" in os.environ:
130
+ print("OpenGL particle draw mode:", self.__render_benchmark_mode)
131
+
132
+ # GL timer queries are opt-in and are polled asynchronously. Never ask
133
+ # for GL_QUERY_RESULT until GL_QUERY_RESULT_AVAILABLE says the result is
134
+ # ready, otherwise the profiler itself would insert a GPU/CPU stall.
135
+ self.__gpu_timing_enabled = False
136
+ self.__gpu_timing_available = True
137
+ self.__gpu_timing_error = None
138
+ self.__gpu_queries_pending = []
139
+ self.__gpu_draw_seconds_ready = []
140
+ self.__gpu_query_skipped = 0
141
+
142
+ def __del__(self):
143
+ # OpenGL contexts are frequently already gone during interpreter
144
+ # teardown. The driver releases display-list/query resources with
145
+ # context destruction, so avoid unsafe GL calls from __del__.
146
+ pass
147
+
148
+ def _get_color_fun(self):
149
+ return self._DrawParticlesGL__color_fun
150
+
151
+ def _set_color_fun(self, fun):
152
+ self._DrawParticlesGL__color_fun = fun
153
+
154
+ color_fun = property(_get_color_fun, _set_color_fun)
155
+
156
+ def _get_vect_color_fun(self):
157
+ return self._DrawParticlesGL__vect_color_fun
158
+
159
+ def _set_vect_color_fun(self, fun):
160
+ self._DrawParticlesGL__vect_color_fun = fun
161
+ self._DrawParticlesGL__vect_color_fun_fl = False
162
+
163
+ vect_color_fun = property(_get_vect_color_fun, _set_vect_color_fun)
164
+
165
+ def set_shared_position_vbo(self, vbo):
166
+ """Use *vbo* as the vectorized particle position source.
167
+
168
+ ``None`` restores the normal host NumPy client-array path. The VBO is
169
+ expected to contain tightly-packed float32 XYZ triples in particle
170
+ order and remains owned by the CL/GL bridge that created it.
171
+ """
172
+ self.__shared_position_vbo = None if vbo is None else int(vbo)
173
+
174
+ def get_shared_position_vbo(self):
175
+ return self.__shared_position_vbo
176
+
177
+ shared_position_vbo = property(
178
+ get_shared_position_vbo, set_shared_position_vbo
179
+ )
180
+
181
+ def set_shared_position_draw_complete_callback(self, callback):
182
+ """Set a callback invoked just after a shared-VBO draw is submitted."""
183
+ self.__shared_position_draw_complete_callback = callback
184
+
185
+ def get_shared_position_draw_complete_callback(self):
186
+ return self.__shared_position_draw_complete_callback
187
+
188
+ shared_position_draw_complete_callback = property(
189
+ get_shared_position_draw_complete_callback,
190
+ set_shared_position_draw_complete_callback,
191
+ )
192
+
193
+ def set_render_benchmark_mode(self, mode):
194
+ """Select particle-only GL state controls for renderer benchmarking."""
195
+ mode = str(mode).strip().lower()
196
+ if mode not in self._RENDER_BENCH_MODES:
197
+ raise ValueError(
198
+ "Unknown OpenGL benchmark mode %r; expected one of: %s"
199
+ % (mode, ", ".join(sorted(self._RENDER_BENCH_MODES)))
200
+ )
201
+ self.__render_benchmark_mode = mode
202
+
203
+ def get_render_benchmark_mode(self):
204
+ return self.__render_benchmark_mode
205
+
206
+ render_benchmark_mode = property(
207
+ get_render_benchmark_mode, set_render_benchmark_mode
208
+ )
209
+
210
+ def get_render_benchmark_modes(self):
211
+ return tuple(sorted(self._RENDER_BENCH_MODES))
212
+
213
+ render_benchmark_modes = property(get_render_benchmark_modes)
214
+
215
+ def _disable_benchmark_states(self):
216
+ """Disable selected GL states and return those that must be restored."""
217
+ disabled = []
218
+ for state in self._RENDER_BENCH_MODES[self.__render_benchmark_mode]:
219
+ try:
220
+ if bool(glIsEnabled(state)):
221
+ glDisable(state)
222
+ disabled.append(state)
223
+ except Exception:
224
+ # Benchmark controls must never make the renderer unusable.
225
+ pass
226
+ return disabled
227
+
228
+ @staticmethod
229
+ def _restore_benchmark_states(states):
230
+ for state in states:
231
+ try:
232
+ glEnable(state)
233
+ except Exception:
234
+ pass
235
+
236
+ def get_last_draw_submit_seconds(self):
237
+ """CPU time spent submitting the most recent glDrawArrays call."""
238
+ return self.__last_draw_submit_seconds
239
+
240
+ last_draw_submit_seconds = property(get_last_draw_submit_seconds)
241
+
242
+ def set_gpu_timing_enabled(self, enabled):
243
+ """Enable non-blocking GL_TIME_ELAPSED measurements for glDrawArrays."""
244
+ enabled = bool(enabled)
245
+ if enabled:
246
+ self.__gpu_timing_enabled = True
247
+ return
248
+
249
+ self.__gpu_timing_enabled = False
250
+ self.cleanup_gpu_timing()
251
+
252
+ def get_gpu_timing_enabled(self):
253
+ return self.__gpu_timing_enabled
254
+
255
+ gpu_timing_enabled = property(
256
+ get_gpu_timing_enabled, set_gpu_timing_enabled
257
+ )
258
+
259
+ def _disable_gpu_timing(self, exc):
260
+ self.__gpu_timing_available = False
261
+ self.__gpu_timing_error = "%s: %s" % (
262
+ exc.__class__.__name__, exc
263
+ )
264
+
265
+ def _poll_gpu_timing(self):
266
+ """Collect completed timer queries without waiting for the GPU.
267
+
268
+ Use raw OpenGL entry points with explicit ctypes output storage. The
269
+ high-level PyOpenGL wrappers for glGetQueryObject* are not consistently
270
+ auto-wrapped on all installations and may require the output pointer.
271
+ """
272
+ if not self.__gpu_queries_pending:
273
+ return
274
+
275
+ while self.__gpu_queries_pending:
276
+ query = self.__gpu_queries_pending[0]
277
+ try:
278
+ available_value = ctypes.c_int(0)
279
+ _raw_glGetQueryObjectiv(
280
+ query,
281
+ GL_QUERY_RESULT_AVAILABLE,
282
+ ctypes.byref(available_value),
283
+ )
284
+ available = bool(available_value.value)
285
+ except Exception as exc:
286
+ self._disable_gpu_timing(exc)
287
+ return
288
+
289
+ if not available:
290
+ break
291
+
292
+ self.__gpu_queries_pending.pop(0)
293
+ try:
294
+ elapsed_value = ctypes.c_uint64(0)
295
+ _raw_glGetQueryObjectui64v(
296
+ query,
297
+ GL_QUERY_RESULT,
298
+ ctypes.byref(elapsed_value),
299
+ )
300
+ self.__gpu_draw_seconds_ready.append(
301
+ int(elapsed_value.value) * 1.0e-9
302
+ )
303
+ except Exception as exc:
304
+ self._disable_gpu_timing(exc)
305
+ finally:
306
+ try:
307
+ glDeleteQueries(1, [query])
308
+ except Exception:
309
+ pass
310
+
311
+ def poll_gpu_timing(self):
312
+ self._poll_gpu_timing()
313
+
314
+ def drain_gpu_draw_times(self):
315
+ """Return completed draw timings accumulated since the previous drain."""
316
+ self._poll_gpu_timing()
317
+ values = list(self.__gpu_draw_seconds_ready)
318
+ self.__gpu_draw_seconds_ready[:] = []
319
+ return values
320
+
321
+ def get_gpu_timing_stats(self):
322
+ return {
323
+ "available": bool(self.__gpu_timing_available),
324
+ "pending": len(self.__gpu_queries_pending),
325
+ "ready": len(self.__gpu_draw_seconds_ready),
326
+ "skipped": int(self.__gpu_query_skipped),
327
+ "error": self.__gpu_timing_error,
328
+ }
329
+
330
+ gpu_timing_stats = property(get_gpu_timing_stats)
331
+
332
+ def cleanup_gpu_timing(self):
333
+ """Delete pending query objects while the GL context is still current."""
334
+ for query in self.__gpu_queries_pending:
335
+ try:
336
+ glDeleteQueries(1, [query])
337
+ except Exception:
338
+ pass
339
+ self.__gpu_queries_pending[:] = []
340
+ self.__gpu_draw_seconds_ready[:] = []
341
+
342
+ def _draw_arrays_profiled(self):
343
+ """Submit the point draw, optionally wrapped in an asynchronous timer."""
344
+ self._poll_gpu_timing()
345
+ query = None
346
+ disabled_states = self._disable_benchmark_states()
347
+
348
+ if (
349
+ self.__gpu_timing_enabled
350
+ and self.__gpu_timing_available
351
+ and len(self.__gpu_queries_pending) < self._GPU_QUERY_LIMIT
352
+ ):
353
+ try:
354
+ query = int(np.asarray(glGenQueries(1)).reshape(-1)[0])
355
+ glBeginQuery(GL_TIME_ELAPSED, query)
356
+ except Exception as exc:
357
+ self._disable_gpu_timing(exc)
358
+ query = None
359
+ elif self.__gpu_timing_enabled:
360
+ self.__gpu_query_skipped += 1
361
+
362
+ draw_start = time.perf_counter()
363
+ try:
364
+ glDrawArrays(GL_POINTS, 0, self.pset.size)
365
+ finally:
366
+ self.__last_draw_submit_seconds = time.perf_counter() - draw_start
367
+ if query is not None:
368
+ try:
369
+ glEndQuery(GL_TIME_ELAPSED)
370
+ self.__gpu_queries_pending.append(query)
371
+ except Exception as exc:
372
+ self._disable_gpu_timing(exc)
373
+ try:
374
+ glDeleteQueries(1, [query])
375
+ except Exception:
376
+ pass
377
+ self._restore_benchmark_states(disabled_states)
378
+
379
+ def draw_particle(self, pset, i):
380
+ mass = _scalar(pset.M[i])
381
+ glPointSize(float(0.01 + mass / pset.mass_unit))
382
+ glColor4f(*self._DrawParticlesGL__color_fun(pset, i))
383
+
384
+ glBegin(GL_POINTS)
385
+ glVertex3f(
386
+ float(pset.X[i, 0] / pset.unit),
387
+ float(pset.X[i, 1] / pset.unit),
388
+ float(pset.X[i, 2] / pset.unit),
389
+ )
390
+ glEnd()
391
+
392
+ def draw_particle_sphere(self, pset, i):
393
+ mass = _scalar(pset.M[i])
394
+ radius = 0.5 * (0.05 + 0.1 / (1.0 + np.exp(-mass / pset.mass_unit)))
395
+
396
+ glColor4f(*self._DrawParticlesGL__color_fun(pset, i))
397
+ glPushMatrix()
398
+ glTranslatef(
399
+ float(pset.X[i, 0] / pset.unit),
400
+ float(pset.X[i, 1] / pset.unit),
401
+ float(pset.X[i, 2] / pset.unit),
402
+ )
403
+ glScalef(float(radius), float(radius), float(radius))
404
+ glCallList(self._DrawParticlesGL__sph_dl)
405
+ glPopMatrix()
406
+
407
+ def draw_particle_teapot(self, pset, i):
408
+ mass = _scalar(pset.M[i])
409
+ radius = 0.5 * (0.05 + 0.1 / (1.0 + np.exp(-mass / pset.mass_unit)))
410
+
411
+ glColor4f(*self._DrawParticlesGL__color_fun(pset, i))
412
+ glPushMatrix()
413
+ glTranslatef(
414
+ float(pset.X[i, 0] / pset.unit),
415
+ float(pset.X[i, 1] / pset.unit),
416
+ float(pset.X[i, 2] / pset.unit),
417
+ )
418
+ glScalef(float(radius), float(radius), float(radius))
419
+ glCallList(self._DrawParticlesGL__tea_dl)
420
+ glPopMatrix()
421
+
422
+ def _draw_vectorized(self):
423
+ vect_color_fun = self._DrawParticlesGL__vect_color_fun
424
+ if vect_color_fun is not None:
425
+ colors = np.empty((self.pset.size, 4), dtype=np.float32)
426
+ vect_color_fun(colors, self.pset)
427
+ colors = np.ascontiguousarray(colors, dtype=np.float32)
428
+ # Client color memory must be captured while no array buffer is
429
+ # bound; otherwise the pointer is interpreted as a VBO offset.
430
+ glBindBuffer(GL_ARRAY_BUFFER, 0)
431
+ glEnableClientState(GL_COLOR_ARRAY)
432
+ glColorPointer(4, GL_FLOAT, 0, colors)
433
+ else:
434
+ colors = None
435
+
436
+ glEnableClientState(GL_VERTEX_ARRAY)
437
+ if self.__shared_position_vbo is None:
438
+ vertices = np.ascontiguousarray(
439
+ np.asarray(self.pset.X) / self.pset.unit,
440
+ dtype=np.float32,
441
+ )
442
+ glBindBuffer(GL_ARRAY_BUFFER, 0)
443
+ glVertexPointer(3, GL_FLOAT, 0, vertices)
444
+ self._draw_arrays_profiled()
445
+ else:
446
+ # Positions are already in GPU memory. Apply the unit conversion
447
+ # as a model-view scale instead of materializing a host array.
448
+ vbo = self.__shared_position_vbo
449
+ glBindBuffer(GL_ARRAY_BUFFER, vbo)
450
+ glVertexPointer(3, GL_FLOAT, 0, ctypes.c_void_p(0))
451
+ glPushMatrix()
452
+ unit_scale = 1.0 / float(self.pset.unit)
453
+ glScalef(unit_scale, unit_scale, unit_scale)
454
+ self._draw_arrays_profiled()
455
+
456
+ # The fence belongs immediately after the command that consumes
457
+ # this VBO. The CL/GL bridge uses it before reacquiring this same
458
+ # buffer on a later frame, avoiding a global glFinish().
459
+ callback = self.__shared_position_draw_complete_callback
460
+ if callback is not None:
461
+ callback(vbo)
462
+
463
+ glPopMatrix()
464
+ glBindBuffer(GL_ARRAY_BUFFER, 0)
465
+
466
+ glDisableClientState(GL_VERTEX_ARRAY)
467
+
468
+ if colors is not None:
469
+ glDisableClientState(GL_COLOR_ARRAY)
470
+
471
+ def draw_trajectory(self):
472
+ if self.pset.log_size < self.trajectory_step + 1:
473
+ return
474
+
475
+ indices = self._DrawParticlesGL__log_indices
476
+ if indices is None or len(indices) != max(0, 2 * self.pset.log_max_size - 2):
477
+ indices = self.pset.get_log_indices_segments(True)
478
+ self._DrawParticlesGL__log_indices = indices
479
+ self._DrawParticlesGL__log_array = np.zeros(
480
+ (self.pset.log_max_size, self.pset.dim),
481
+ dtype=np.float32,
482
+ )
483
+
484
+ log_array = self._DrawParticlesGL__log_array
485
+ for i in range(self.pset.size):
486
+ glColor4f(*self._DrawParticlesGL__color_fun(self.pset, i))
487
+ glEnableClientState(GL_VERTEX_ARRAY)
488
+
489
+ _, count = self.pset.read_log_array(i, (log_array,))
490
+ if count > 0:
491
+ vertices = np.ascontiguousarray(
492
+ log_array / self.pset.unit,
493
+ dtype=np.float32,
494
+ )
495
+ glBindBuffer(GL_ARRAY_BUFFER, 0)
496
+ glVertexPointer(3, GL_FLOAT, 0, vertices)
497
+ glDrawElements(
498
+ GL_LINES,
499
+ count,
500
+ GL_UNSIGNED_INT,
501
+ np.ascontiguousarray(indices[:count], dtype=np.uint32),
502
+ )
503
+
504
+ glDisableClientState(GL_VERTEX_ARRAY)
505
+
506
+
507
+ # Keep color helpers obtained from the historical module working as well.
508
+ legacy.charged_particles_color = charged_particles_color
509
+ legacy.charged_particles_vect_color = charged_particles_vect_color