ncca-ngl 0.1.4__py3-none-any.whl → 0.2.1__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.
ncca/ngl/primitives.py CHANGED
@@ -6,66 +6,18 @@ We need to create the data first which is stored in a map as part of the class,
6
6
  which will generate a pipeline for this object and draw into the current context.
7
7
  """
8
8
 
9
- import enum
10
- from pathlib import Path
11
9
  from typing import Dict, Union
12
10
 
13
11
  import numpy as np
14
12
  import OpenGL.GL as gl
15
13
 
16
14
  from .log import logger
15
+ from .prim_data import PrimData, Prims
17
16
  from .simple_vao import VertexData
18
17
  from .vao_factory import VAOFactory, VAOType # noqa
19
18
  from .vec3 import Vec3
20
19
 
21
20
 
22
- class Prims(enum.Enum):
23
- """Enum for the default primitives that can be loaded."""
24
-
25
- BUDDAH = "buddah"
26
- BUNNY = "bunny"
27
- CUBE = "cube"
28
- DODECAHEDRON = "dodecahedron"
29
- DRAGON = "dragon"
30
- FOOTBALL = "football"
31
- ICOSAHEDRON = "icosahedron"
32
- OCTAHEDRON = "octahedron"
33
- TEAPOT = "teapot"
34
- TETRAHEDRON = "tetrahedron"
35
- TROLL = "troll"
36
-
37
-
38
- def _circle_table(n: int) -> np.ndarray:
39
- """
40
- Generates a table of sine and cosine values for a circle divided into n segments.
41
-
42
- Args:
43
- n: The number of segments to divide the circle into.
44
-
45
- Returns:
46
- A numpy array of shape (n+1, 2) containing the cosine and sine values.
47
- """
48
- # Determine the angle between samples
49
- angle = 2.0 * np.pi / (n if n != 0 else 1)
50
-
51
- # Allocate list for n samples, plus duplicate of first entry at the end
52
- cs = np.zeros((n + 1, 2), dtype=np.float32)
53
-
54
- # Compute cos and sin around the circle
55
- cs[0, 0] = 1.0 # cost
56
- cs[0, 1] = 0.0 # sint
57
-
58
- for i in range(1, n):
59
- cs[i, 1] = np.sin(angle * i) # sint
60
- cs[i, 0] = np.cos(angle * i) # cost
61
-
62
- # Last sample is duplicate of the first
63
- cs[n, 1] = cs[0, 1] # sint
64
- cs[n, 0] = cs[0, 0] # cost
65
-
66
- return cs
67
-
68
-
69
21
  class _primitive:
70
22
  """A private class to hold VAO data for a primitive."""
71
23
 
@@ -82,12 +34,8 @@ class _primitive:
82
34
  self.vao.set_data(data)
83
35
  vert_data_size = 8 * 4 # 4 is sizeof float and 8 is x,y,z,nx,ny,nz,uv
84
36
  self.vao.set_vertex_attribute_pointer(0, 3, gl.GL_FLOAT, vert_data_size, 0)
85
- self.vao.set_vertex_attribute_pointer(
86
- 1, 3, gl.GL_FLOAT, vert_data_size, Vec3.sizeof()
87
- )
88
- self.vao.set_vertex_attribute_pointer(
89
- 2, 2, gl.GL_FLOAT, vert_data_size, 2 * Vec3.sizeof()
90
- )
37
+ self.vao.set_vertex_attribute_pointer(1, 3, gl.GL_FLOAT, vert_data_size, Vec3.sizeof())
38
+ self.vao.set_vertex_attribute_pointer(2, 2, gl.GL_FLOAT, vert_data_size, 2 * Vec3.sizeof())
91
39
  self.vao.set_num_indices(prim_data.size // 8)
92
40
 
93
41
 
@@ -104,18 +52,14 @@ class Primitives:
104
52
  """Loads the default primitives from the PrimData directory."""
105
53
  logger.info("Loading default primitives...")
106
54
  if not cls._loaded:
107
- prim_folder = Path(__file__).parent / "PrimData"
108
- prims = np.load(prim_folder / "Primitives.npz")
109
- for p in prims.items():
110
- prim_data = p[1]
55
+ for p in Prims:
56
+ prim_data = PrimData.primitive(p.value)
111
57
  prim = _primitive(prim_data)
112
- cls._primitives[p[0]] = prim
58
+ cls._primitives[p.value] = prim
113
59
  cls._loaded = True
114
60
 
115
61
  @classmethod
116
- def create_line_grid(
117
- cls, name: str, width: float, depth: float, steps: int
118
- ) -> None:
62
+ def create_line_grid(cls, name: str, width: float, depth: float, steps: int) -> None:
119
63
  """
120
64
  Creates a line grid primitive.
121
65
 
@@ -125,42 +69,13 @@ class Primitives:
125
69
  depth: The depth of the grid.
126
70
  steps: The number of steps in the grid.
127
71
  """
128
- # Calculate the step size for each grid value
129
- wstep = width / steps
130
- ws2 = width / 2.0
131
- v1 = -ws2
132
-
133
- dstep = depth / steps
134
- ds2 = depth / 2.0
135
- v2 = -ds2
136
-
137
- # Create a list to store the vertex data
138
- data = []
139
-
140
- for _ in range(steps + 1):
141
- # Vertex 1 x, y, z
142
- data.append([-ws2, 0.0, v1])
143
- # Vertex 2 x, y, z
144
- data.append([ws2, 0.0, v1])
145
-
146
- # Vertex 1 x, y, z
147
- data.append([v2, 0.0, ds2])
148
- # Vertex 2 x, y, z
149
- data.append([v2, 0.0, -ds2])
150
-
151
- # Now change our step value
152
- v1 += wstep
153
- v2 += dstep
154
-
155
72
  # Convert the list to a NumPy array
156
- data_array = np.array(data, dtype=np.float32)
73
+ data_array = PrimData.line_grid(width, depth, steps)
157
74
  prim = _primitive(data_array)
158
75
  cls._primitives[name] = prim
159
76
 
160
77
  @classmethod
161
- def create_triangle_plane(
162
- cls, name: str, width: float, depth: float, w_p: int, d_p: int, v_n: Vec3
163
- ) -> None:
78
+ def create_triangle_plane(cls, name: str, width: float, depth: float, w_p: int, d_p: int, v_n: Vec3) -> None:
164
79
  """
165
80
  Creates a triangle plane primitive.
166
81
 
@@ -172,46 +87,8 @@ class Primitives:
172
87
  d_p: The number of depth partitions.
173
88
  v_n: The normal vector for the plane.
174
89
  """
175
- w2 = width / 2.0
176
- d2 = depth / 2.0
177
- w_step = width / w_p
178
- d_step = depth / d_p
179
-
180
- du = 0.9 / w_p
181
- dv = 0.9 / d_p
182
-
183
- data = []
184
- v = 0.0
185
- d = -d2
186
- for _ in range(d_p):
187
- u = 0.0
188
- w = -w2
189
- for _ in range(w_p):
190
- # tri 1
191
- # vert 1
192
- data.extend([w, 0.0, d + d_step, v_n.x, v_n.y, v_n.z, u, v + dv])
193
- # vert 2
194
- data.extend(
195
- [w + w_step, 0.0, d + d_step, v_n.x, v_n.y, v_n.z, u + du, v + dv]
196
- )
197
- # vert 3
198
- data.extend([w, 0.0, d, v_n.x, v_n.y, v_n.z, u, v])
199
-
200
- # tri 2
201
- # vert 1
202
- data.extend(
203
- [w + w_step, 0.0, d + d_step, v_n.x, v_n.y, v_n.z, u + du, v + dv]
204
- )
205
- # vert 2
206
- data.extend([w + w_step, 0.0, d, v_n.x, v_n.y, v_n.z, u + du, v])
207
- # vert 3
208
- data.extend([w, 0.0, d, v_n.x, v_n.y, v_n.z, u, v])
209
- u += du
210
- w += w_step
211
- v += dv
212
- d += d_step
213
-
214
- data_array = np.array(data, dtype=np.float32)
90
+
91
+ data_array = PrimData.triangle_plane(width, depth, w_p, d_p, v_n)
215
92
  prim = _primitive(data_array)
216
93
  cls._primitives[name] = prim
217
94
 
@@ -242,82 +119,13 @@ class Primitives:
242
119
  radius: The radius of the sphere.
243
120
  precision: The precision of the sphere (number of slices).
244
121
  """
245
- # Sphere code based on a function Written by Paul Bourke.
246
- # http://astronomy.swin.edu.au/~pbourke/opengl/sphere/
247
- # the next part of the code calculates the P,N,UV of the sphere for triangles
248
-
249
- # Disallow a negative number for radius.
250
- if radius < 0.0:
251
- radius = -radius
252
-
253
- # Disallow a negative number for precision.
254
- if precision < 4:
255
- precision = 4
256
-
257
- # Create a numpy array to store our verts
258
- data = []
259
-
260
- for i in range(precision // 2):
261
- theta1 = i * 2.0 * np.pi / precision - np.pi / 2.0
262
- theta2 = (i + 1) * 2.0 * np.pi / precision - np.pi / 2.0
263
-
264
- for j in range(precision):
265
- theta3 = j * 2.0 * np.pi / precision
266
- theta4 = (j + 1) * 2.0 * np.pi / precision
267
-
268
- # First triangle
269
- nx1 = np.cos(theta2) * np.cos(theta3)
270
- ny1 = np.sin(theta2)
271
- nz1 = np.cos(theta2) * np.sin(theta3)
272
- x1 = radius * nx1
273
- y1 = radius * ny1
274
- z1 = radius * nz1
275
- u1 = j / precision
276
- v1 = 2.0 * (i + 1) / precision
277
- data.append([x1, y1, z1, nx1, ny1, nz1, u1, v1])
278
-
279
- nx2 = np.cos(theta1) * np.cos(theta3)
280
- ny2 = np.sin(theta1)
281
- nz2 = np.cos(theta1) * np.sin(theta3)
282
- x2 = radius * nx2
283
- y2 = radius * ny2
284
- z2 = radius * nz2
285
- u2 = j / precision
286
- v2 = 2.0 * i / precision
287
- data.append([x2, y2, z2, nx2, ny2, nz2, u2, v2])
288
-
289
- nx3 = np.cos(theta1) * np.cos(theta4)
290
- ny3 = np.sin(theta1)
291
- nz3 = np.cos(theta1) * np.sin(theta4)
292
- x3 = radius * nx3
293
- y3 = radius * ny3
294
- z3 = radius * nz3
295
- u3 = (j + 1) / precision
296
- v3 = 2.0 * i / precision
297
- data.append([x3, y3, z3, nx3, ny3, nz3, u3, v3])
298
-
299
- # Second triangle
300
- nx4 = np.cos(theta2) * np.cos(theta4)
301
- ny4 = np.sin(theta2)
302
- nz4 = np.cos(theta2) * np.sin(theta4)
303
- x4 = radius * nx4
304
- y4 = radius * ny4
305
- z4 = radius * nz4
306
- u4 = (j + 1) / precision
307
- v4 = 2.0 * (i + 1) / precision
308
- data.append([x4, y4, z4, nx4, ny4, nz4, u4, v4])
309
-
310
- data.append([x1, y1, z1, nx1, ny1, nz1, u1, v1])
311
- data.append([x3, y3, z3, nx3, ny3, nz3, u3, v3])
312
-
313
- data_array = np.array(data, dtype=np.float32)
122
+
123
+ data_array = PrimData.sphere(radius, precision)
314
124
  prim = _primitive(data_array)
315
125
  cls._primitives[name] = prim
316
126
 
317
127
  @classmethod
318
- def create_cone(
319
- cls, name: str, base: float, height: float, slices: int, stacks: int
320
- ) -> None:
128
+ def create_cone(cls, name: str, base: float, height: float, slices: int, stacks: int) -> None:
321
129
  """
322
130
  Creates a cone primitive.
323
131
 
@@ -328,247 +136,46 @@ class Primitives:
328
136
  slices: The number of divisions around the cone.
329
137
  stacks: The number of divisions along the cone's height.
330
138
  """
331
- z_step = height / (stacks if stacks > 0 else 1)
332
- r_step = base / (stacks if stacks > 0 else 1)
333
-
334
- cosn = height / np.sqrt(height * height + base * base)
335
- sinn = base / np.sqrt(height * height + base * base)
336
-
337
- cs = _circle_table(slices)
338
-
339
- z0 = 0.0
340
- z1 = z_step
341
-
342
- r0 = base
343
- r1 = r0 - r_step
344
-
345
- du = 1.0 / stacks
346
- dv = 1.0 / slices
347
-
348
- u = 1.0
349
- v = 1.0
350
-
351
- data = []
352
-
353
- for _ in range(stacks):
354
- for j in range(slices):
355
- # First triangle
356
- d1 = [0] * 8
357
- d1[6] = u
358
- d1[7] = v
359
- d1[3] = cs[j, 0] * cosn # nx
360
- d1[4] = cs[j, 1] * sinn # ny
361
- d1[5] = sinn # nz
362
- d1[0] = cs[j, 0] * r0 # x
363
- d1[1] = cs[j, 1] * r0 # y
364
- d1[2] = z0 # z
365
- data.append(d1)
366
-
367
- d2 = [0] * 8
368
- d2[6] = u
369
- d2[7] = v - dv
370
- d2[3] = cs[j, 0] * cosn # nx
371
- d2[4] = cs[j, 1] * sinn # ny
372
- d2[5] = sinn # nz
373
- d2[0] = cs[j, 0] * r1 # x
374
- d2[1] = cs[j, 1] * r1 # y
375
- d2[2] = z1 # z
376
- data.append(d2)
377
-
378
- d3 = [0] * 8
379
- d3[6] = u - du
380
- d3[7] = v - dv
381
- d3[3] = cs[j + 1, 0] * cosn # nx
382
- d3[4] = cs[j + 1, 1] * sinn # ny
383
- d3[5] = sinn # nz
384
- d3[0] = cs[j + 1, 0] * r1 # x
385
- d3[1] = cs[j + 1, 1] * r1 # y
386
- d3[2] = z1 # z
387
- data.append(d3)
388
-
389
- # Second triangle
390
- d4 = [0] * 8
391
- d4[6] = u
392
- d4[7] = v
393
- d4[3] = cs[j, 0] * cosn # nx
394
- d4[4] = cs[j, 1] * sinn # ny
395
- d4[5] = sinn # nz
396
- d4[0] = cs[j, 0] * r0 # x
397
- d4[1] = cs[j, 1] * r0 # y
398
- d4[2] = z0 # z
399
- data.append(d4)
400
-
401
- d5 = [0] * 8
402
- d5[6] = u - du
403
- d5[7] = v - dv
404
- d5[3] = cs[j + 1, 0] * cosn # nx
405
- d5[4] = cs[j + 1, 1] * sinn # ny
406
- d5[5] = sinn # nz
407
- d5[0] = cs[j + 1, 0] * r1 # x
408
- d5[1] = cs[j + 1, 1] * r1 # y
409
- d5[2] = z1 # z
410
- data.append(d5)
411
-
412
- d6 = [0] * 8
413
- d6[6] = u - du
414
- d6[7] = v
415
- d6[3] = cs[j + 1, 0] * cosn # nx
416
- d6[4] = cs[j + 1, 1] * sinn # ny
417
- d6[5] = sinn # nz
418
- d6[0] = cs[j + 1, 0] * r0 # x
419
- d6[1] = cs[j + 1, 1] * r0 # y
420
- d6[2] = z0 # z
421
- data.append(d6)
422
-
423
- u -= du
424
-
425
- v -= dv
426
- u = 1.0
427
- z0 = z1
428
- z1 += z_step
429
- r0 = r1
430
- r1 -= r_step
431
-
432
- data_array = np.array(data, dtype=np.float32)
139
+ data_array = PrimData.cone(base, height, slices, stacks)
433
140
  prim = _primitive(data_array)
434
141
  cls._primitives[name] = prim
435
142
 
436
143
  @classmethod
437
- def create_capsule(
438
- cls, name: str, radius: float, height: float, precision: int
439
- ) -> None:
144
+ def create_capsule(cls, name: str, radius: float, height: float, precision: int) -> None:
440
145
  """
441
146
  Creates a capsule primitive.
442
147
  The capsule is aligned along the y-axis.
443
148
  It is composed of a cylinder and two hemispherical caps.
444
149
  based on code from here https://code.google.com/p/rgine/source/browse/trunk/RGine/opengl/src/RGLShapes.cpp
445
150
  and adapted
151
+
152
+ Args:
153
+ name: The name of the primitive.
154
+ radius: The radius of the capsule.
155
+ height: The height of the capsule.
156
+ precision: The precision of the capsule.
446
157
  """
447
- if radius <= 0.0:
448
- raise ValueError("Radius must be positive")
449
- if height < 0.0:
450
- raise ValueError("Height must be non-negative")
451
- if precision < 4:
452
- precision = 4
453
-
454
- data = []
455
- h = height / 2.0
456
- ang = np.pi / precision
457
-
458
- # Cylinder sides
459
- for i in range(2 * precision):
460
- c = radius * np.cos(ang * i)
461
- c1 = radius * np.cos(ang * (i + 1))
462
- s = radius * np.sin(ang * i)
463
- s1 = radius * np.sin(ang * (i + 1))
464
-
465
- # normals for cylinder sides
466
- nc = np.cos(ang * i)
467
- ns = np.sin(ang * i)
468
- nc1 = np.cos(ang * (i + 1))
469
- ns1 = np.sin(ang * (i + 1))
470
-
471
- # side top
472
- data.extend([c1, h, s1, nc1, 0.0, ns1, 0.0, 0.0])
473
- data.extend([c, h, s, nc, 0.0, ns, 0.0, 0.0])
474
- data.extend([c, -h, s, nc, 0.0, ns, 0.0, 0.0])
475
-
476
- # side bot
477
- data.extend([c, -h, s, nc, 0.0, ns, 0.0, 0.0])
478
- data.extend([c1, -h, s1, nc1, 0.0, ns1, 0.0, 0.0])
479
- data.extend([c1, h, s1, nc1, 0.0, ns1, 0.0, 0.0])
480
- # Hemispherical caps
481
- for i in range(2 * precision):
482
- # longitude
483
- s = -np.sin(ang * i)
484
- s1 = -np.sin(ang * (i + 1))
485
- c = np.cos(ang * i)
486
- c1 = np.cos(ang * (i + 1))
487
-
488
- for j in range(precision + 1):
489
- o = h if j < precision / 2 else -h
490
-
491
- # latitude
492
- sb = radius * np.sin(ang * j)
493
- sb1 = radius * np.sin(ang * (j + 1))
494
- cb = radius * np.cos(ang * j)
495
- cb1 = radius * np.cos(ang * (j + 1))
496
-
497
- if j != precision - 1:
498
- nx, ny, nz = sb * c, cb, sb * s
499
- data.extend([nx, ny + o, nz, nx, ny, nz, 0.0, 0.0])
500
- nx, ny, nz = sb1 * c, cb1, sb1 * s
501
- data.extend([nx, ny + o, nz, nx, ny, nz, 0.0, 0.0])
502
- nx, ny, nz = sb1 * c1, cb1, sb1 * s1
503
- data.extend([nx, ny + o, nz, nx, ny, nz, 0.0, 0.0])
504
-
505
- if j != 0:
506
- nx, ny, nz = sb * c, cb, sb * s
507
- data.extend([nx, ny + o, nz, nx, ny, nz, 0.0, 0.0])
508
- nx, ny, nz = sb1 * c1, cb1, sb1 * s1
509
- data.extend([nx, ny + o, nz, nx, ny, nz, 0.0, 0.0])
510
- nx, ny, nz = sb * c1, cb, sb * s1
511
- data.extend([nx, ny + o, nz, nx, ny, nz, 0.0, 0.0])
512
-
513
- data_array = np.array(data, dtype=np.float32)
158
+
159
+ data_array = PrimData.capsule(radius, height, precision)
514
160
  prim = _primitive(data_array)
515
161
  cls._primitives[name] = prim
516
162
 
517
163
  @classmethod
518
- def create_cylinder(
519
- cls, name: str, radius: float, height: float, slices: int, stacks: int
520
- ) -> None:
164
+ def create_cylinder(cls, name: str, radius: float, height: float, slices: int, stacks: int) -> None:
521
165
  """
522
166
  Creates a cylinder primitive.
523
167
  The cylinder is aligned along the y-axis.
524
168
  This method generates the cylinder walls, but not the top and bottom caps.
169
+
170
+ Args:
171
+ name: The name of the primitive.
172
+ radius: The radius of the cylinder.
173
+ height: The height of the cylinder.
174
+ slices: The number of slices.
175
+ stacks: The number of stacks.
525
176
  """
526
- if radius <= 0.0:
527
- raise ValueError("Radius must be positive")
528
- if height < 0.0:
529
- raise ValueError("Height must be non-negative")
530
- if slices < 3:
531
- slices = 3
532
- if stacks < 1:
533
- stacks = 1
534
-
535
- data = []
536
- h2 = height / 2.0
537
- y_step = height / stacks
538
-
539
- cs = _circle_table(slices)
540
-
541
- du = 1.0 / slices
542
- dv = 1.0 / stacks
543
-
544
- for i in range(stacks):
545
- y0 = -h2 + i * y_step
546
- y1 = -h2 + (i + 1) * y_step
547
- v = i * dv
548
- for j in range(slices):
549
- u = j * du
550
-
551
- nx1, nz1 = cs[j, 0], cs[j, 1]
552
- x1, z1 = radius * nx1, radius * nz1
553
-
554
- nx2, nz2 = cs[j + 1, 0], cs[j + 1, 1]
555
- x2, z2 = radius * nx2, radius * nz2
556
-
557
- p_bl = [x1, y0, z1, nx1, 0, nz1, u, v]
558
- p_br = [x2, y0, z2, nx2, 0, nz2, u + du, v]
559
- p_tl = [x1, y1, z1, nx1, 0, nz1, u, v + dv]
560
- p_tr = [x2, y1, z2, nx2, 0, nz2, u + du, v + dv]
561
-
562
- # Triangle 1
563
- data.extend(p_bl)
564
- data.extend(p_tl)
565
- data.extend(p_br)
566
- # Triangle 2
567
- data.extend(p_br)
568
- data.extend(p_tl)
569
- data.extend(p_tr)
570
-
571
- data_array = np.array(data, dtype=np.float32)
177
+
178
+ data_array = PrimData.cylinder(radius, height, slices, stacks)
572
179
  prim = _primitive(data_array)
573
180
  cls._primitives[name] = prim
574
181
 
@@ -582,43 +189,8 @@ class Primitives:
582
189
  radius: The radius of the disk.
583
190
  slices: The number of slices to divide the disk into.
584
191
  """
585
- if radius <= 0.0:
586
- raise ValueError("Radius must be positive")
587
- if slices < 3:
588
- slices = 3
589
-
590
- data = []
591
- cs = _circle_table(slices)
592
-
593
- center = [0, 0, 0, 0, 1, 0, 0.5, 0.5]
594
-
595
- for i in range(slices):
596
- p1 = [
597
- radius * cs[i, 0],
598
- 0,
599
- radius * cs[i, 1],
600
- 0,
601
- 1,
602
- 0,
603
- cs[i, 0] * 0.5 + 0.5,
604
- cs[i, 1] * 0.5 + 0.5,
605
- ]
606
- p2 = [
607
- radius * cs[i + 1, 0],
608
- 0,
609
- radius * cs[i + 1, 1],
610
- 0,
611
- 1,
612
- 0,
613
- cs[i + 1, 0] * 0.5 + 0.5,
614
- cs[i + 1, 1] * 0.5 + 0.5,
615
- ]
616
-
617
- data.extend(center)
618
- data.extend(p2)
619
- data.extend(p1)
620
-
621
- data_array = np.array(data, dtype=np.float32)
192
+
193
+ data_array = PrimData.disk(radius, slices)
622
194
  prim = _primitive(data_array)
623
195
  cls._primitives[name] = prim
624
196
 
@@ -641,66 +213,7 @@ class Primitives:
641
213
  sides: The number of sides for each ring.
642
214
  rings: The number of rings for the torus.
643
215
  """
644
- if minor_radius <= 0 or major_radius <= 0:
645
- raise ValueError("Radii must be positive")
646
- if sides < 3 or rings < 3:
647
- raise ValueError("Sides and rings must be at least 3")
648
-
649
- d_psi = 2.0 * np.pi / rings
650
- d_phi = -2.0 * np.pi / sides
651
-
652
- psi = 0.0
653
-
654
- vertices = []
655
- normals = []
656
- uvs = []
657
-
658
- for j in range(rings + 1):
659
- c_psi = np.cos(psi)
660
- s_psi = np.sin(psi)
661
- phi = 0.0
662
- for i in range(sides + 1):
663
- c_phi = np.cos(phi)
664
- s_phi = np.sin(phi)
665
-
666
- x = c_psi * (major_radius + c_phi * minor_radius)
667
- z = s_psi * (major_radius + c_phi * minor_radius)
668
- y = s_phi * minor_radius
669
- vertices.append([x, y, z])
670
-
671
- nx = c_psi * c_phi
672
- nz = s_psi * c_phi
673
- ny = s_phi
674
- normals.append([nx, ny, nz])
675
-
676
- u = i / sides
677
- v = j / rings
678
- uvs.append([u, v])
679
-
680
- phi += d_phi
681
- psi += d_psi
682
-
683
- data = []
684
- for j in range(rings):
685
- for i in range(sides):
686
- idx1 = j * (sides + 1) + i
687
- idx2 = j * (sides + 1) + (i + 1)
688
- idx3 = (j + 1) * (sides + 1) + i
689
- idx4 = (j + 1) * (sides + 1) + (i + 1)
690
-
691
- p1 = vertices[idx1] + normals[idx1] + uvs[idx1]
692
- p2 = vertices[idx2] + normals[idx2] + uvs[idx2]
693
- p3 = vertices[idx3] + normals[idx3] + uvs[idx3]
694
- p4 = vertices[idx4] + normals[idx4] + uvs[idx4]
695
-
696
- data.extend(p1)
697
- data.extend(p3)
698
- data.extend(p2)
699
-
700
- data.extend(p2)
701
- data.extend(p3)
702
- data.extend(p4)
703
-
704
- data_array = np.array(data, dtype=np.float32)
216
+
217
+ data_array = PrimData.torus(minor_radius, major_radius, sides, rings)
705
218
  prim = _primitive(data_array)
706
219
  cls._primitives[name] = prim
ncca/ngl/quaternion.py CHANGED
@@ -75,7 +75,9 @@ class Quaternion:
75
75
  return Quaternion(s, x, y, z)
76
76
 
77
77
  def __add__(self, rhs):
78
- return Quaternion(self.s + rhs.s, self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
78
+ return Quaternion(
79
+ self.s + rhs.s, self.x + rhs.x, self.y + rhs.y, self.z + rhs.z
80
+ )
79
81
 
80
82
  def __iadd__(self, rhs):
81
83
  self.s += rhs.s
@@ -85,7 +87,9 @@ class Quaternion:
85
87
  return self
86
88
 
87
89
  def __sub__(self, rhs):
88
- return Quaternion(self.s - rhs.s, self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
90
+ return Quaternion(
91
+ self.s - rhs.s, self.x - rhs.x, self.y - rhs.y, self.z - rhs.z
92
+ )
89
93
 
90
94
  def __isub__(self, rhs):
91
95
  return self.__sub__(rhs)