or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-drawing.mdaudio-sound.mdcore-system.mddrawing-shapes.mdevent-input.mdgame-objects.mdgraphics-display.mdindex.mdinput-devices.mdjoystick-gamepad.mdmath-utils.mdsurface-image.mdtext-font.mdtime-animation.mdtransform-image.md

math-utils.mddocs/

0

# Mathematical Utilities

1

2

Mathematical functions and vector operations for game calculations. Pygame's math module provides essential mathematical tools including 2D and 3D vectors, utility functions, and geometric calculations commonly needed in game development.

3

4

## Capabilities

5

6

### Utility Functions

7

8

General mathematical functions for common game calculations.

9

10

```python { .api }

11

def clamp(value: float, min_value: float, max_value: float) -> float:

12

"""

13

Constrain value to range [min_value, max_value].

14

15

Args:

16

value (float): Value to clamp

17

min_value (float): Minimum allowed value

18

max_value (float): Maximum allowed value

19

20

Returns:

21

float: Clamped value

22

"""

23

24

def lerp(a: float, b: float, weight: float) -> float:

25

"""

26

Linear interpolation between two values.

27

28

Args:

29

a (float): Start value

30

b (float): End value

31

weight (float): Interpolation factor (0.0 to 1.0)

32

33

Returns:

34

float: Interpolated value

35

"""

36

```

37

38

### Vector2 Class

39

40

2D vector class for position, velocity, direction calculations.

41

42

```python { .api }

43

class Vector2:

44

def __init__(self, x: float = 0, y: float = 0):

45

"""

46

Create 2D vector.

47

48

Args:

49

x (float): X component

50

y (float): Y component

51

"""

52

53

# Properties

54

x: float # X component

55

y: float # Y component

56

57

# Vector Operations

58

def dot(self, vector: 'Vector2') -> float:

59

"""

60

Calculate dot product with another vector.

61

62

Args:

63

vector (Vector2): Other vector

64

65

Returns:

66

float: Dot product (v1.x * v2.x + v1.y * v2.y)

67

"""

68

69

def cross(self, vector: 'Vector2') -> float:

70

"""

71

Calculate 2D cross product (scalar).

72

73

Args:

74

vector (Vector2): Other vector

75

76

Returns:

77

float: Cross product magnitude

78

"""

79

80

def magnitude(self) -> float:

81

"""

82

Get vector length/magnitude.

83

84

Returns:

85

float: Vector magnitude

86

"""

87

88

def magnitude_squared(self) -> float:

89

"""

90

Get squared magnitude (faster than magnitude()).

91

92

Returns:

93

float: Squared magnitude

94

"""

95

96

def length(self) -> float:

97

"""

98

Alias for magnitude().

99

100

Returns:

101

float: Vector length

102

"""

103

104

def length_squared(self) -> float:

105

"""

106

Alias for magnitude_squared().

107

108

Returns:

109

float: Squared length

110

"""

111

112

def normalize(self) -> 'Vector2':

113

"""

114

Get normalized vector (length = 1).

115

116

Returns:

117

Vector2: Unit vector in same direction

118

"""

119

120

def normalize_ip(self) -> None:

121

"""Normalize this vector in place."""

122

123

def is_normalized(self) -> bool:

124

"""

125

Check if vector is normalized.

126

127

Returns:

128

bool: True if length is approximately 1

129

"""

130

131

def safe_normalize(self) -> 'Vector2':

132

"""

133

Normalize vector, returns zero vector if length is zero.

134

135

Returns:

136

Vector2: Normalized vector or zero vector

137

"""

138

139

def scale_to_length(self, length: float) -> None:

140

"""

141

Scale vector to specific length.

142

143

Args:

144

length (float): Desired length

145

"""

146

147

# Distance Functions

148

def distance_to(self, vector: 'Vector2') -> float:

149

"""

150

Calculate distance to another vector.

151

152

Args:

153

vector (Vector2): Target vector

154

155

Returns:

156

float: Distance between vectors

157

"""

158

159

def distance_squared_to(self, vector: 'Vector2') -> float:

160

"""

161

Calculate squared distance (faster than distance_to()).

162

163

Args:

164

vector (Vector2): Target vector

165

166

Returns:

167

float: Squared distance

168

"""

169

170

# Interpolation

171

def lerp(self, vector: 'Vector2', t: float) -> 'Vector2':

172

"""

173

Linear interpolation to another vector.

174

175

Args:

176

vector (Vector2): Target vector

177

t (float): Interpolation factor (0.0 to 1.0)

178

179

Returns:

180

Vector2: Interpolated vector

181

"""

182

183

def slerp(self, vector: 'Vector2', t: float) -> 'Vector2':

184

"""

185

Spherical linear interpolation (smoother rotation).

186

187

Args:

188

vector (Vector2): Target vector

189

t (float): Interpolation factor (0.0 to 1.0)

190

191

Returns:

192

Vector2: Interpolated vector

193

"""

194

195

def move_towards(self, vector: 'Vector2', max_distance: float) -> 'Vector2':

196

"""

197

Move towards target by maximum distance.

198

199

Args:

200

vector (Vector2): Target vector

201

max_distance (float): Maximum distance to move

202

203

Returns:

204

Vector2: New position

205

"""

206

207

# Rotation and Reflection

208

def rotate(self, angle: float) -> 'Vector2':

209

"""

210

Rotate vector by angle (degrees).

211

212

Args:

213

angle (float): Rotation angle in degrees

214

215

Returns:

216

Vector2: Rotated vector

217

"""

218

219

def rotate_ip(self, angle: float) -> None:

220

"""

221

Rotate vector in place.

222

223

Args:

224

angle (float): Rotation angle in degrees

225

"""

226

227

def rotate_rad(self, angle: float) -> 'Vector2':

228

"""

229

Rotate vector by angle (radians).

230

231

Args:

232

angle (float): Rotation angle in radians

233

234

Returns:

235

Vector2: Rotated vector

236

"""

237

238

def rotate_rad_ip(self, angle: float) -> None:

239

"""

240

Rotate vector in place (radians).

241

242

Args:

243

angle (float): Rotation angle in radians

244

"""

245

246

def reflect(self, normal: 'Vector2') -> 'Vector2':

247

"""

248

Reflect vector across surface normal.

249

250

Args:

251

normal (Vector2): Surface normal vector

252

253

Returns:

254

Vector2: Reflected vector

255

"""

256

257

def reflect_ip(self, normal: 'Vector2') -> None:

258

"""

259

Reflect vector in place.

260

261

Args:

262

normal (Vector2): Surface normal vector

263

"""

264

265

# Polar Coordinates

266

def as_polar(self) -> tuple[float, float]:

267

"""

268

Convert to polar coordinates.

269

270

Returns:

271

tuple[float, float]: (radius, angle_in_degrees)

272

"""

273

274

@classmethod

275

def from_polar(cls, polar: tuple[float, float]) -> 'Vector2':

276

"""

277

Create vector from polar coordinates.

278

279

Args:

280

polar (tuple[float, float]): (radius, angle_in_degrees)

281

282

Returns:

283

Vector2: New vector from polar coordinates

284

"""

285

286

# Angles

287

def angle_to(self, vector: 'Vector2') -> float:

288

"""

289

Calculate angle to another vector.

290

291

Args:

292

vector (Vector2): Target vector

293

294

Returns:

295

float: Angle in degrees (-180 to 180)

296

"""

297

298

# Utility

299

def copy(self) -> 'Vector2':

300

"""

301

Create copy of vector.

302

303

Returns:

304

Vector2: Copy of this vector

305

"""

306

307

def update(self, x: float, y: float) -> None:

308

"""

309

Update vector components.

310

311

Args:

312

x (float): New X component

313

y (float): New Y component

314

"""

315

316

def clamp_magnitude(self, min_length: float, max_length: float) -> 'Vector2':

317

"""

318

Clamp vector magnitude to range.

319

320

Args:

321

min_length (float): Minimum magnitude

322

max_length (float): Maximum magnitude

323

324

Returns:

325

Vector2: Vector with clamped magnitude

326

"""

327

328

def clamp_magnitude_ip(self, min_length: float, max_length: float) -> None:

329

"""

330

Clamp magnitude in place.

331

332

Args:

333

min_length (float): Minimum magnitude

334

max_length (float): Maximum magnitude

335

"""

336

337

# Arithmetic Operations (supports +, -, *, /, //, %, **)

338

def __add__(self, other) -> 'Vector2': ...

339

def __sub__(self, other) -> 'Vector2': ...

340

def __mul__(self, scalar: float) -> 'Vector2': ...

341

def __truediv__(self, scalar: float) -> 'Vector2': ...

342

def __floordiv__(self, scalar: float) -> 'Vector2': ...

343

def __mod__(self, scalar: float) -> 'Vector2': ...

344

def __pow__(self, exponent: float) -> 'Vector2': ...

345

346

# Comparison Operations

347

def __eq__(self, other) -> bool: ...

348

def __ne__(self, other) -> bool: ...

349

350

# Sequence Operations

351

def __getitem__(self, index: int) -> float: ...

352

def __setitem__(self, index: int, value: float) -> None: ...

353

def __len__(self) -> int: ... # Always returns 2

354

def __iter__(self): ...

355

```

356

357

### Vector3 Class

358

359

3D vector class extending Vector2 functionality with Z component.

360

361

```python { .api }

362

class Vector3:

363

def __init__(self, x: float = 0, y: float = 0, z: float = 0):

364

"""

365

Create 3D vector.

366

367

Args:

368

x (float): X component

369

y (float): Y component

370

z (float): Z component

371

"""

372

373

# Properties

374

x: float # X component

375

y: float # Y component

376

z: float # Z component

377

378

# All Vector2 methods plus 3D-specific operations:

379

380

def cross(self, vector: 'Vector3') -> 'Vector3':

381

"""

382

Calculate 3D cross product.

383

384

Args:

385

vector (Vector3): Other vector

386

387

Returns:

388

Vector3: Cross product vector (perpendicular to both inputs)

389

"""

390

391

def dot(self, vector: 'Vector3') -> float:

392

"""

393

Calculate dot product.

394

395

Args:

396

vector (Vector3): Other vector

397

398

Returns:

399

float: Dot product

400

"""

401

402

# 3D Rotation

403

def rotate_x(self, angle: float) -> 'Vector3':

404

"""

405

Rotate around X axis.

406

407

Args:

408

angle (float): Rotation angle in degrees

409

410

Returns:

411

Vector3: Rotated vector

412

"""

413

414

def rotate_x_ip(self, angle: float) -> None:

415

"""Rotate around X axis in place."""

416

417

def rotate_y(self, angle: float) -> 'Vector3':

418

"""

419

Rotate around Y axis.

420

421

Args:

422

angle (float): Rotation angle in degrees

423

424

Returns:

425

Vector3: Rotated vector

426

"""

427

428

def rotate_y_ip(self, angle: float) -> None:

429

"""Rotate around Y axis in place."""

430

431

def rotate_z(self, angle: float) -> 'Vector3':

432

"""

433

Rotate around Z axis.

434

435

Args:

436

angle (float): Rotation angle in degrees

437

438

Returns:

439

Vector3: Rotated vector

440

"""

441

442

def rotate_z_ip(self, angle: float) -> None:

443

"""Rotate around Z axis in place."""

444

445

def rotate(self, axis: 'Vector3', angle: float) -> 'Vector3':

446

"""

447

Rotate around arbitrary axis.

448

449

Args:

450

axis (Vector3): Rotation axis (should be normalized)

451

angle (float): Rotation angle in degrees

452

453

Returns:

454

Vector3: Rotated vector

455

"""

456

457

def rotate_ip(self, axis: 'Vector3', angle: float) -> None:

458

"""

459

Rotate around arbitrary axis in place.

460

461

Args:

462

axis (Vector3): Rotation axis (should be normalized)

463

angle (float): Rotation angle in degrees

464

"""

465

466

# Spherical Coordinates

467

def as_spherical(self) -> tuple[float, float, float]:

468

"""

469

Convert to spherical coordinates.

470

471

Returns:

472

tuple[float, float, float]: (radius, theta, phi) in degrees

473

"""

474

475

@classmethod

476

def from_spherical(cls, spherical: tuple[float, float, float]) -> 'Vector3':

477

"""

478

Create vector from spherical coordinates.

479

480

Args:

481

spherical (tuple[float, float, float]): (radius, theta, phi) in degrees

482

483

Returns:

484

Vector3: New vector from spherical coordinates

485

"""

486

487

def update(self, x: float, y: float, z: float) -> None:

488

"""

489

Update vector components.

490

491

Args:

492

x (float): New X component

493

y (float): New Y component

494

z (float): New Z component

495

"""

496

497

# All arithmetic and comparison operations from Vector2

498

def __len__(self) -> int: ... # Always returns 3

499

```

500

501

## Usage Examples

502

503

### Basic Vector Operations

504

505

```python

506

import pygame

507

import pygame.math

508

509

# Create vectors

510

pos = pygame.math.Vector2(100, 50)

511

velocity = pygame.math.Vector2(5, -3)

512

target = pygame.math.Vector2(400, 300)

513

514

print(f"Position: {pos}")

515

print(f"Velocity: {velocity}")

516

517

# Vector arithmetic

518

new_pos = pos + velocity

519

print(f"New position: {new_pos}")

520

521

# Vector magnitude

522

speed = velocity.magnitude()

523

print(f"Speed: {speed}")

524

525

# Distance calculation

526

distance = pos.distance_to(target)

527

print(f"Distance to target: {distance}")

528

529

# Normalize vector

530

direction = (target - pos).normalize()

531

print(f"Direction to target: {direction}")

532

```

533

534

### Movement and Steering

535

536

```python

537

import pygame

538

import pygame.math

539

import random

540

541

class Entity:

542

def __init__(self, x, y):

543

self.position = pygame.math.Vector2(x, y)

544

self.velocity = pygame.math.Vector2(0, 0)

545

self.max_speed = 3.0

546

self.acceleration = pygame.math.Vector2(0, 0)

547

548

def seek(self, target):

549

"""Steer towards target"""

550

desired = (target - self.position).normalize() * self.max_speed

551

steer = desired - self.velocity

552

return steer

553

554

def flee(self, target):

555

"""Steer away from target"""

556

return -self.seek(target)

557

558

def wander(self):

559

"""Random wandering behavior"""

560

random_angle = random.uniform(-30, 30)

561

forward = self.velocity.normalize() if self.velocity.magnitude() > 0 else pygame.math.Vector2(1, 0)

562

wander_direction = forward.rotate(random_angle)

563

return wander_direction * 0.5

564

565

def update(self):

566

# Apply acceleration

567

self.velocity += self.acceleration

568

569

# Limit speed

570

if self.velocity.magnitude() > self.max_speed:

571

self.velocity.scale_to_length(self.max_speed)

572

573

# Update position

574

self.position += self.velocity

575

576

# Reset acceleration

577

self.acceleration = pygame.math.Vector2(0, 0)

578

579

def apply_force(self, force):

580

self.acceleration += force

581

582

pygame.init()

583

screen = pygame.display.set_mode((800, 600))

584

clock = pygame.time.Clock()

585

586

# Create entities

587

entities = [Entity(random.randint(50, 750), random.randint(50, 550)) for _ in range(10)]

588

mouse_pos = pygame.math.Vector2(400, 300)

589

590

running = True

591

while running:

592

for event in pygame.event.get():

593

if event.type == pygame.QUIT:

594

running = False

595

elif event.type == pygame.MOUSEMOTION:

596

mouse_pos = pygame.math.Vector2(event.pos)

597

598

screen.fill((0, 0, 0))

599

600

for entity in entities:

601

# Calculate steering forces

602

seek_force = entity.seek(mouse_pos) * 0.1

603

wander_force = entity.wander() * 0.5

604

605

# Combine forces

606

total_force = seek_force + wander_force

607

entity.apply_force(total_force)

608

entity.update()

609

610

# Wrap around screen edges

611

if entity.position.x < 0:

612

entity.position.x = 800

613

elif entity.position.x > 800:

614

entity.position.x = 0

615

if entity.position.y < 0:

616

entity.position.y = 600

617

elif entity.position.y > 600:

618

entity.position.y = 0

619

620

# Draw entity

621

pygame.draw.circle(screen, (255, 255, 255), (int(entity.position.x), int(entity.position.y)), 5)

622

623

# Draw velocity vector

624

end_pos = entity.position + entity.velocity * 10

625

pygame.draw.line(screen, (255, 0, 0), entity.position, end_pos, 2)

626

627

# Draw mouse cursor

628

pygame.draw.circle(screen, (0, 255, 0), (int(mouse_pos.x), int(mouse_pos.y)), 8)

629

630

pygame.display.flip()

631

clock.tick(60)

632

633

pygame.quit()

634

```

635

636

### 3D Vector Math

637

638

```python

639

import pygame.math

640

import math

641

642

# Create 3D vectors

643

position = pygame.math.Vector3(1, 2, 3)

644

forward = pygame.math.Vector3(0, 0, -1) # Looking down negative Z

645

up = pygame.math.Vector3(0, 1, 0)

646

647

print(f"Position: {position}")

648

print(f"Forward: {forward}")

649

print(f"Up: {up}")

650

651

# Calculate right vector using cross product

652

right = forward.cross(up)

653

print(f"Right: {right}")

654

655

# Rotate around Y axis (yaw)

656

rotated_forward = forward.rotate_y(45)

657

print(f"Rotated 45° around Y: {rotated_forward}")

658

659

# Convert to spherical coordinates

660

spherical = position.as_spherical()

661

print(f"Spherical (r, theta, phi): {spherical}")

662

663

# Create vector from spherical coordinates

664

from_spherical = pygame.math.Vector3.from_spherical((5, 45, 30))

665

print(f"From spherical: {from_spherical}")

666

667

# 3D distance and interpolation

668

target = pygame.math.Vector3(5, 8, 2)

669

distance_3d = position.distance_to(target)

670

print(f"3D distance: {distance_3d}")

671

672

# Interpolate between positions

673

interpolated = position.lerp(target, 0.5)

674

print(f"Halfway point: {interpolated}")

675

```

676

677

### Collision Detection with Vectors

678

679

```python

680

import pygame

681

import pygame.math

682

683

def circle_collision(pos1, radius1, pos2, radius2):

684

"""Check collision between two circles using vectors"""

685

distance = pos1.distance_to(pos2)

686

return distance <= (radius1 + radius2)

687

688

def line_intersection(line1_start, line1_end, line2_start, line2_end):

689

"""Find intersection point of two lines"""

690

# Convert to vector form

691

d1 = line1_end - line1_start

692

d2 = line2_end - line2_start

693

d3 = line1_start - line2_start

694

695

# Calculate cross products

696

cross_d2_d3 = d2.cross(d3)

697

cross_d1_d2 = d1.cross(d2)

698

699

if abs(cross_d1_d2) < 1e-10: # Lines are parallel

700

return None

701

702

t1 = cross_d2_d3 / cross_d1_d2

703

if 0 <= t1 <= 1:

704

intersection = line1_start + d1 * t1

705

return intersection

706

return None

707

708

def point_in_triangle(point, a, b, c):

709

"""Check if point is inside triangle using barycentric coordinates"""

710

v0 = c - a

711

v1 = b - a

712

v2 = point - a

713

714

dot00 = v0.dot(v0)

715

dot01 = v0.dot(v1)

716

dot02 = v0.dot(v2)

717

dot11 = v1.dot(v1)

718

dot12 = v1.dot(v2)

719

720

inv_denom = 1 / (dot00 * dot11 - dot01 * dot01)

721

u = (dot11 * dot02 - dot01 * dot12) * inv_denom

722

v = (dot00 * dot12 - dot01 * dot02) * inv_denom

723

724

return (u >= 0) and (v >= 0) and (u + v <= 1)

725

726

# Example usage

727

pygame.init()

728

screen = pygame.display.set_mode((800, 600))

729

clock = pygame.time.Clock()

730

731

# Test objects

732

circle1 = {"pos": pygame.math.Vector2(200, 200), "radius": 30}

733

circle2 = {"pos": pygame.math.Vector2(400, 300), "radius": 40}

734

735

triangle_points = [

736

pygame.math.Vector2(500, 100),

737

pygame.math.Vector2(600, 250),

738

pygame.math.Vector2(450, 200)

739

]

740

741

mouse_pos = pygame.math.Vector2(0, 0)

742

743

running = True

744

while running:

745

for event in pygame.event.get():

746

if event.type == pygame.QUIT:

747

running = False

748

elif event.type == pygame.MOUSEMOTION:

749

mouse_pos = pygame.math.Vector2(event.pos)

750

751

screen.fill((0, 0, 0))

752

753

# Check circle collision

754

circles_colliding = circle_collision(

755

circle1["pos"], circle1["radius"],

756

circle2["pos"], circle2["radius"]

757

)

758

759

# Draw circles

760

color1 = (255, 100, 100) if circles_colliding else (100, 100, 255)

761

color2 = (255, 100, 100) if circles_colliding else (100, 255, 100)

762

763

pygame.draw.circle(screen, color1, (int(circle1["pos"].x), int(circle1["pos"].y)), circle1["radius"])

764

pygame.draw.circle(screen, color2, (int(circle2["pos"].x), int(circle2["pos"].y)), circle2["radius"])

765

766

# Check point in triangle

767

mouse_in_triangle = point_in_triangle(mouse_pos, *triangle_points)

768

triangle_color = (255, 255, 100) if mouse_in_triangle else (100, 100, 100)

769

770

# Draw triangle

771

triangle_coords = [(int(p.x), int(p.y)) for p in triangle_points]

772

pygame.draw.polygon(screen, triangle_color, triangle_coords)

773

774

# Draw mouse position

775

pygame.draw.circle(screen, (255, 255, 255), (int(mouse_pos.x), int(mouse_pos.y)), 5)

776

777

pygame.display.flip()

778

clock.tick(60)

779

780

pygame.quit()

781

```

782

783

### Vector Field Visualization

784

785

```python

786

import pygame

787

import pygame.math

788

import math

789

790

def vector_field(pos):

791

"""Generate vector field - spiral pattern"""

792

center = pygame.math.Vector2(400, 300)

793

to_center = center - pos

794

795

if to_center.magnitude() > 0:

796

# Spiral field

797

perpendicular = pygame.math.Vector2(-to_center.y, to_center.x).normalize()

798

radial = to_center.normalize()

799

800

spiral_strength = 0.3

801

radial_strength = 0.1

802

803

field_vector = perpendicular * spiral_strength + radial * radial_strength

804

return field_vector

805

else:

806

return pygame.math.Vector2(0, 0)

807

808

pygame.init()

809

screen = pygame.display.set_mode((800, 600))

810

clock = pygame.time.Clock()

811

812

# Create grid of vectors

813

grid_size = 40

814

vectors = []

815

816

for x in range(0, 800, grid_size):

817

for y in range(0, 600, grid_size):

818

pos = pygame.math.Vector2(x, y)

819

field_vec = vector_field(pos)

820

vectors.append((pos, field_vec))

821

822

# Particles

823

particles = []

824

for _ in range(50):

825

particle = {

826

'pos': pygame.math.Vector2(

827

pygame.math.Vector2(400, 300).x + (random.uniform(-100, 100)),

828

pygame.math.Vector2(400, 300).y + (random.uniform(-100, 100))

829

),

830

'vel': pygame.math.Vector2(0, 0),

831

'trail': []

832

}

833

particles.append(particle)

834

835

import random

836

837

running = True

838

while running:

839

for event in pygame.event.get():

840

if event.type == pygame.QUIT:

841

running = False

842

843

screen.fill((0, 0, 0))

844

845

# Draw vector field

846

for pos, field_vec in vectors:

847

if field_vec.magnitude() > 0.01:

848

end_pos = pos + field_vec * 30

849

pygame.draw.line(screen, (50, 50, 50), pos, end_pos)

850

# Draw arrowhead

851

arrow_size = 5

852

if field_vec.magnitude() > 0:

853

arrow_dir = field_vec.normalize()

854

left_arrow = end_pos - arrow_dir.rotate(150) * arrow_size

855

right_arrow = end_pos - arrow_dir.rotate(-150) * arrow_size

856

pygame.draw.polygon(screen, (50, 50, 50), [end_pos, left_arrow, right_arrow])

857

858

# Update and draw particles

859

for particle in particles:

860

# Apply field force

861

field_force = vector_field(particle['pos'])

862

particle['vel'] += field_force * 0.5

863

864

# Apply damping

865

particle['vel'] *= 0.98

866

867

# Update position

868

particle['pos'] += particle['vel']

869

870

# Add to trail

871

particle['trail'].append(particle['pos'].copy())

872

if len(particle['trail']) > 20:

873

particle['trail'].pop(0)

874

875

# Wrap around screen

876

if particle['pos'].x < 0: particle['pos'].x = 800

877

if particle['pos'].x > 800: particle['pos'].x = 0

878

if particle['pos'].y < 0: particle['pos'].y = 600

879

if particle['pos'].y > 600: particle['pos'].y = 0

880

881

# Draw trail

882

for i, trail_pos in enumerate(particle['trail']):

883

alpha = i / len(particle['trail'])

884

color_intensity = int(255 * alpha)

885

pygame.draw.circle(screen, (color_intensity, color_intensity, 255),

886

(int(trail_pos.x), int(trail_pos.y)), 2)

887

888

pygame.display.flip()

889

clock.tick(60)

890

891

pygame.quit()

892

```