or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdaudio-system.mdcore-system.mdcursor-management.mddisplay-graphics.mdevent-handling.mdindex.mdinput-systems.mdmath-operations.mdmidi-support.mdsprites-game-objects.mdtyping-support.md

math-operations.mddocs/

0

# Mathematical Operations

1

2

Vector mathematics and geometric utilities for game development. Provides 2D and 3D vector operations, rectangle manipulation, and collision detection.

3

4

## Capabilities

5

6

### 2D Vector Mathematics

7

8

The Vector2 class provides comprehensive 2D vector operations for game mathematics.

9

10

```python { .api }

11

class Vector2:

12

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

13

"""

14

Initialize 2D vector.

15

16

Parameters:

17

x: X component

18

y: Y component

19

"""

20

21

x: float # X component

22

y: float # Y component

23

24

# Properties

25

length: float # Vector magnitude (read-only)

26

length_squared: float # Squared magnitude (read-only, faster than length)

27

magnitude: float # Alias for length

28

magnitude_squared: float # Alias for length_squared

29

30

def normalize(self) -> Vector2:

31

"""

32

Get normalized vector (length 1).

33

34

Returns:

35

Vector2: New normalized vector

36

"""

37

38

def normalize_ip(self) -> None:

39

"""Normalize vector in place."""

40

41

def is_normalized(self, tolerance: float = 1e-6) -> bool:

42

"""

43

Check if vector is normalized.

44

45

Parameters:

46

tolerance: Tolerance for comparison

47

48

Returns:

49

bool: True if vector length is approximately 1

50

"""

51

52

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

53

"""

54

Scale vector to specific length.

55

56

Parameters:

57

length: Target length

58

59

Returns:

60

Vector2: New vector with specified length

61

"""

62

63

def distance_to(self, other: Vector2) -> float:

64

"""

65

Calculate distance to another vector.

66

67

Parameters:

68

other: Other vector

69

70

Returns:

71

float: Distance between vectors

72

"""

73

74

def distance_squared_to(self, other: Vector2) -> float:

75

"""

76

Calculate squared distance (faster than distance_to).

77

78

Parameters:

79

other: Other vector

80

81

Returns:

82

float: Squared distance

83

"""

84

85

def dot(self, other: Vector2) -> float:

86

"""

87

Calculate dot product.

88

89

Parameters:

90

other: Other vector

91

92

Returns:

93

float: Dot product

94

"""

95

96

def cross(self, other: Vector2) -> float:

97

"""

98

Calculate cross product (returns scalar in 2D).

99

100

Parameters:

101

other: Other vector

102

103

Returns:

104

float: Cross product magnitude

105

"""

106

107

def angle_to(self, other: Vector2) -> float:

108

"""

109

Calculate angle between vectors in degrees.

110

111

Parameters:

112

other: Other vector

113

114

Returns:

115

float: Angle in degrees

116

"""

117

118

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

119

"""

120

Rotate vector by angle in degrees.

121

122

Parameters:

123

angle: Rotation angle in degrees

124

125

Returns:

126

Vector2: New rotated vector

127

"""

128

129

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

130

"""

131

Rotate vector in place.

132

133

Parameters:

134

angle: Rotation angle in degrees

135

"""

136

137

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

138

"""

139

Rotate vector by angle in radians.

140

141

Parameters:

142

angle: Rotation angle in radians

143

144

Returns:

145

Vector2: New rotated vector

146

"""

147

148

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

149

"""

150

Rotate vector in place by radians.

151

152

Parameters:

153

angle: Rotation angle in radians

154

"""

155

156

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

157

"""

158

Reflect vector across normal.

159

160

Parameters:

161

normal: Surface normal vector

162

163

Returns:

164

Vector2: Reflected vector

165

"""

166

167

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

168

"""

169

Reflect vector in place.

170

171

Parameters:

172

normal: Surface normal vector

173

"""

174

175

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

176

"""

177

Linear interpolation between vectors.

178

179

Parameters:

180

other: Target vector

181

t: Interpolation factor (0.0 to 1.0)

182

183

Returns:

184

Vector2: Interpolated vector

185

"""

186

187

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

188

"""

189

Spherical linear interpolation.

190

191

Parameters:

192

other: Target vector

193

t: Interpolation factor (0.0 to 1.0)

194

195

Returns:

196

Vector2: Interpolated vector

197

"""

198

199

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

200

"""

201

Move towards target by maximum distance.

202

203

Parameters:

204

target: Target vector

205

max_distance: Maximum distance to move

206

207

Returns:

208

Vector2: New vector moved towards target

209

"""

210

211

def move_towards_ip(self, target: Vector2, max_distance: float) -> None:

212

"""

213

Move towards target in place.

214

215

Parameters:

216

target: Target vector

217

max_distance: Maximum distance to move

218

"""

219

220

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

221

"""

222

Clamp vector magnitude to maximum length.

223

224

Parameters:

225

max_length: Maximum allowed length

226

227

Returns:

228

Vector2: Clamped vector

229

"""

230

231

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

232

"""

233

Clamp magnitude in place.

234

235

Parameters:

236

max_length: Maximum allowed length

237

"""

238

239

def copy(self) -> Vector2:

240

"""

241

Create copy of vector.

242

243

Returns:

244

Vector2: Vector copy

245

"""

246

247

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

248

"""

249

Convert to polar coordinates.

250

251

Returns:

252

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

253

"""

254

255

@classmethod

256

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

257

"""

258

Create vector from polar coordinates.

259

260

Parameters:

261

polar: (radius, angle_degrees)

262

263

Returns:

264

Vector2: New vector from polar coordinates

265

"""

266

267

def elementwise(self) -> VectorElementwiseProxy:

268

"""

269

Get elementwise operations proxy.

270

271

Returns:

272

VectorElementwiseProxy: Proxy for elementwise operations

273

"""

274

```

275

276

### 3D Vector Mathematics

277

278

The Vector3 class extends vector operations to three dimensions.

279

280

```python { .api }

281

class Vector3:

282

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

283

"""

284

Initialize 3D vector.

285

286

Parameters:

287

x: X component

288

y: Y component

289

z: Z component

290

"""

291

292

x: float # X component

293

y: float # Y component

294

z: float # Z component

295

296

# Properties (similar to Vector2)

297

length: float

298

length_squared: float

299

magnitude: float

300

magnitude_squared: float

301

302

# Methods (similar to Vector2 with 3D extensions)

303

def normalize(self) -> Vector3: ...

304

def normalize_ip(self) -> None: ...

305

def scale_to_length(self, length: float) -> Vector3: ...

306

def distance_to(self, other: Vector3) -> float: ...

307

def distance_squared_to(self, other: Vector3) -> float: ...

308

def dot(self, other: Vector3) -> float: ...

309

310

def cross(self, other: Vector3) -> Vector3:

311

"""

312

Calculate cross product (returns Vector3 in 3D).

313

314

Parameters:

315

other: Other vector

316

317

Returns:

318

Vector3: Cross product vector

319

"""

320

321

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

322

"""

323

Rotate around X axis.

324

325

Parameters:

326

angle: Rotation angle in degrees

327

328

Returns:

329

Vector3: Rotated vector

330

"""

331

332

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

333

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

334

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

335

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

336

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

337

338

def reflect(self, normal: Vector3) -> Vector3: ...

339

def reflect_ip(self, normal: Vector3) -> None: ...

340

def lerp(self, other: Vector3, t: float) -> Vector3: ...

341

def slerp(self, other: Vector3, t: float) -> Vector3: ...

342

def copy(self) -> Vector3: ...

343

```

344

345

### Rectangle Mathematics

346

347

Enhanced rectangle operations for collision detection and positioning.

348

349

```python { .api }

350

class Rect:

351

def __init__(self, left: int, top: int, width: int, height: int):

352

"""

353

Initialize rectangle.

354

355

Parameters:

356

left: Left coordinate

357

top: Top coordinate

358

width: Rectangle width

359

height: Rectangle height

360

"""

361

362

# Position and size attributes

363

x: int # Left coordinate (alias for left)

364

y: int # Top coordinate (alias for top)

365

left: int # Left edge

366

top: int # Top edge

367

width: int # Width

368

height: int # Height

369

w: int # Width alias

370

h: int # Height alias

371

372

# Computed attributes

373

right: int # Right edge (left + width)

374

bottom: int # Bottom edge (top + height)

375

376

# Corner positions

377

topleft: tuple[int, int] # (left, top)

378

topright: tuple[int, int] # (right, top)

379

bottomleft: tuple[int, int] # (left, bottom)

380

bottomright: tuple[int, int] # (right, bottom)

381

382

# Edge midpoints

383

midtop: tuple[int, int] # (centerx, top)

384

midleft: tuple[int, int] # (left, centery)

385

midbottom: tuple[int, int] # (centerx, bottom)

386

midright: tuple[int, int] # (right, centery)

387

388

# Center

389

center: tuple[int, int] # (centerx, centery)

390

centerx: int # Horizontal center

391

centery: int # Vertical center

392

393

# Size

394

size: tuple[int, int] # (width, height)

395

396

def copy(self) -> Rect:

397

"""Create copy of rectangle."""

398

399

def move(self, x: int, y: int) -> Rect:

400

"""

401

Create moved rectangle.

402

403

Parameters:

404

x: Horizontal offset

405

y: Vertical offset

406

407

Returns:

408

Rect: New moved rectangle

409

"""

410

411

def move_ip(self, x: int, y: int) -> None:

412

"""Move rectangle in place."""

413

414

def inflate(self, x: int, y: int) -> Rect:

415

"""

416

Create inflated rectangle (grow/shrink).

417

418

Parameters:

419

x: Horizontal size change

420

y: Vertical size change

421

422

Returns:

423

Rect: New inflated rectangle

424

"""

425

426

def inflate_ip(self, x: int, y: int) -> None:

427

"""Inflate rectangle in place."""

428

429

def scale_by(self, scalar: float) -> Rect:

430

"""

431

Scale rectangle by factor.

432

433

Parameters:

434

scalar: Scale factor

435

436

Returns:

437

Rect: Scaled rectangle

438

"""

439

440

def scale_by_ip(self, scalar: float) -> None:

441

"""Scale rectangle in place."""

442

443

def clamp(self, rect: Rect) -> Rect:

444

"""

445

Create rectangle clamped inside another rectangle.

446

447

Parameters:

448

rect: Bounding rectangle

449

450

Returns:

451

Rect: Clamped rectangle

452

"""

453

454

def clamp_ip(self, rect: Rect) -> None:

455

"""Clamp rectangle in place."""

456

457

def clip(self, rect: Rect) -> Rect:

458

"""

459

Get intersection with another rectangle.

460

461

Parameters:

462

rect: Rectangle to intersect with

463

464

Returns:

465

Rect: Intersection rectangle (may have zero area)

466

"""

467

468

def clipline(self, line: tuple[tuple[int, int], tuple[int, int]]) -> tuple[tuple[int, int], tuple[int, int]] | tuple[]:

469

"""

470

Clip line to rectangle boundaries.

471

472

Parameters:

473

line: ((x1, y1), (x2, y2)) line endpoints

474

475

Returns:

476

tuple: Clipped line endpoints or empty tuple if no intersection

477

"""

478

479

def union(self, rect: Rect) -> Rect:

480

"""

481

Get union with another rectangle.

482

483

Parameters:

484

rect: Rectangle to union with

485

486

Returns:

487

Rect: Bounding rectangle containing both

488

"""

489

490

def union_ip(self, rect: Rect) -> None:

491

"""Union rectangle in place."""

492

493

def unionall(self, rect_sequence: list[Rect]) -> Rect:

494

"""

495

Union with multiple rectangles.

496

497

Parameters:

498

rect_sequence: List of rectangles

499

500

Returns:

501

Rect: Bounding rectangle containing all

502

"""

503

504

def unionall_ip(self, rect_sequence: list[Rect]) -> None:

505

"""Union all rectangles in place."""

506

507

def fit(self, rect: Rect) -> Rect:

508

"""

509

Fit rectangle inside another rectangle, maintaining aspect ratio.

510

511

Parameters:

512

rect: Container rectangle

513

514

Returns:

515

Rect: Fitted rectangle

516

"""

517

518

def normalize(self) -> None:

519

"""Ensure width and height are positive."""

520

521

def contains(self, rect: Rect) -> bool:

522

"""

523

Check if rectangle completely contains another.

524

525

Parameters:

526

rect: Rectangle to test

527

528

Returns:

529

bool: True if rect is completely inside this rectangle

530

"""

531

532

def collidepoint(self, point: tuple[int, int]) -> bool:

533

"""

534

Check if point is inside rectangle.

535

536

Parameters:

537

point: (x, y) point to test

538

539

Returns:

540

bool: True if point is inside

541

"""

542

543

def colliderect(self, rect: Rect) -> bool:

544

"""

545

Check if rectangles overlap.

546

547

Parameters:

548

rect: Rectangle to test

549

550

Returns:

551

bool: True if rectangles overlap

552

"""

553

554

def collidelist(self, list: list[Rect]) -> int:

555

"""

556

Find first colliding rectangle in list.

557

558

Parameters:

559

list: List of rectangles to test

560

561

Returns:

562

int: Index of first collision, or -1 if none

563

"""

564

565

def collidelistall(self, list: list[Rect]) -> list[int]:

566

"""

567

Find all colliding rectangles in list.

568

569

Parameters:

570

list: List of rectangles to test

571

572

Returns:

573

list[int]: Indices of all colliding rectangles

574

"""

575

576

def collidedict(self, dict: dict) -> tuple | None:

577

"""

578

Find first colliding rectangle in dictionary.

579

580

Parameters:

581

dict: Dictionary with Rect values

582

583

Returns:

584

tuple | None: (key, value) of first collision or None

585

"""

586

587

def collidedictall(self, dict: dict) -> list[tuple]:

588

"""

589

Find all colliding rectangles in dictionary.

590

591

Parameters:

592

dict: Dictionary with Rect values

593

594

Returns:

595

list[tuple]: List of (key, value) pairs for collisions

596

"""

597

598

def update(self, *args) -> None:

599

"""Update rectangle with new position/size."""

600

```

601

602

### Floating-Point Rectangles

603

604

FRect provides the same interface as Rect but with floating-point precision.

605

606

```python { .api }

607

class FRect:

608

"""Floating-point rectangle with same methods as Rect."""

609

610

def __init__(self, left: float, top: float, width: float, height: float): ...

611

612

# All attributes and methods same as Rect but with float types

613

x: float

614

y: float

615

width: float

616

height: float

617

# ... (same interface as Rect)

618

```

619

620

## Usage Examples

621

622

### Vector Mathematics

623

624

```python

625

import pygame

626

import math

627

628

# Create vectors

629

velocity = pygame.Vector2(5, 3)

630

position = pygame.Vector2(100, 100)

631

target = pygame.Vector2(400, 300)

632

633

# Basic operations

634

print(f"Velocity magnitude: {velocity.length}")

635

print(f"Distance to target: {position.distance_to(target)}")

636

637

# Normalize for direction

638

direction = (target - position).normalize()

639

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

640

641

# Move towards target

642

max_speed = 200 # pixels per second

643

dt = 1/60 # delta time (60 FPS)

644

new_position = position.move_towards(target, max_speed * dt)

645

646

# Rotation

647

velocity_rotated = velocity.rotate(45) # 45 degrees

648

print(f"Rotated velocity: {velocity_rotated}")

649

650

# Reflection (for bouncing)

651

wall_normal = pygame.Vector2(0, -1) # Upward normal

652

reflected_velocity = velocity.reflect(wall_normal)

653

654

# Dot product (for angle calculations)

655

angle = math.degrees(math.acos(velocity.normalize().dot(direction)))

656

print(f"Angle between velocity and direction: {angle}")

657

```

658

659

### Advanced Movement

660

661

```python

662

import pygame

663

import math

664

665

class MovingObject:

666

def __init__(self, x, y):

667

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

668

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

669

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

670

self.max_speed = 200

671

self.friction = 0.95

672

673

def seek(self, target):

674

"""Steer towards target position"""

675

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

676

steer = desired - self.velocity

677

return steer.clamp_magnitude(5) # Max steering force

678

679

def update(self, dt):

680

# Apply physics

681

self.velocity += self.acceleration * dt

682

self.velocity *= self.friction # Apply friction

683

self.velocity = self.velocity.clamp_magnitude(self.max_speed)

684

self.position += self.velocity * dt

685

686

# Reset acceleration

687

self.acceleration *= 0

688

689

# Example usage

690

obj = MovingObject(100, 100)

691

target = pygame.Vector2(400, 300)

692

693

# Apply steering force

694

steering = obj.seek(target)

695

obj.acceleration += steering

696

697

obj.update(1/60) # Update with 60 FPS delta time

698

```

699

700

### Rectangle Collision Detection

701

702

```python

703

import pygame

704

705

# Create rectangles

706

player = pygame.Rect(100, 100, 50, 50)

707

wall = pygame.Rect(200, 150, 20, 100)

708

enemies = [

709

pygame.Rect(300, 120, 40, 40),

710

pygame.Rect(350, 180, 30, 30),

711

pygame.Rect(400, 200, 35, 35)

712

]

713

714

# Point collision

715

mouse_pos = pygame.mouse.get_pos()

716

if player.collidepoint(mouse_pos):

717

print("Mouse over player")

718

719

# Rectangle collision

720

if player.colliderect(wall):

721

print("Player hit wall")

722

723

# Multiple collision testing

724

hit_enemy = player.collidelist(enemies)

725

if hit_enemy >= 0:

726

print(f"Player hit enemy {hit_enemy}")

727

728

# Get all collisions

729

all_hits = player.collidelistall(enemies)

730

print(f"Player hit enemies: {all_hits}")

731

732

# Movement with collision

733

keys = pygame.key.get_pressed()

734

old_pos = player.topleft

735

736

if keys[pygame.K_LEFT]:

737

player.x -= 5

738

if keys[pygame.K_RIGHT]:

739

player.x += 5

740

if keys[pygame.K_UP]:

741

player.y -= 5

742

if keys[pygame.K_DOWN]:

743

player.y += 5

744

745

# Check for wall collision and revert if needed

746

if player.colliderect(wall):

747

player.topleft = old_pos # Revert movement

748

```

749

750

### Rectangle Manipulation

751

752

```python

753

import pygame

754

755

# Create rectangle

756

rect = pygame.Rect(100, 100, 200, 150)

757

758

# Position by different anchors

759

rect.center = (400, 300) # Center at point

760

rect.topleft = (50, 50) # Top-left corner

761

rect.bottomright = (800, 600) # Bottom-right corner

762

763

# Resize and move

764

larger_rect = rect.inflate(50, 30) # Grow by 50x30

765

moved_rect = rect.move(10, -5) # Move by offset

766

767

# Clamp inside screen

768

screen_rect = pygame.Rect(0, 0, 800, 600)

769

clamped_rect = rect.clamp(screen_rect)

770

771

# Get intersection

772

overlap = rect.clip(larger_rect)

773

if overlap.width > 0 and overlap.height > 0:

774

print(f"Overlap area: {overlap}")

775

776

# Union (bounding box)

777

bounding_box = rect.union(moved_rect)

778

print(f"Bounding box: {bounding_box}")

779

780

# Line clipping

781

line = ((0, 0), (800, 600)) # Diagonal line

782

clipped_line = rect.clipline(line)

783

if clipped_line:

784

print(f"Line enters/exits rect at: {clipped_line}")

785

```

786

787

### Game Physics Example

788

789

```python

790

import pygame

791

import math

792

793

class PhysicsObject:

794

def __init__(self, x, y, mass=1.0):

795

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

796

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

797

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

798

self.mass = mass

799

self.radius = 20

800

801

def apply_force(self, force):

802

"""Apply force (F = ma, so a = F/m)"""

803

self.acceleration += force / self.mass

804

805

def update(self, dt):

806

# Integrate physics

807

self.velocity += self.acceleration * dt

808

self.position += self.velocity * dt

809

810

# Clear acceleration

811

self.acceleration *= 0

812

813

# Boundary collision (simple bounce)

814

if self.position.x < self.radius or self.position.x > 800 - self.radius:

815

self.velocity.x *= -0.8 # Bounce with energy loss

816

if self.position.y < self.radius or self.position.y > 600 - self.radius:

817

self.velocity.y *= -0.8

818

819

# Clamp position to screen

820

self.position.x = max(self.radius, min(800 - self.radius, self.position.x))

821

self.position.y = max(self.radius, min(600 - self.radius, self.position.y))

822

823

# Create objects

824

ball1 = PhysicsObject(200, 100, mass=2.0)

825

ball2 = PhysicsObject(600, 100, mass=1.0)

826

827

# Apply different forces

828

gravity = pygame.Vector2(0, 500) # Downward gravity

829

wind = pygame.Vector2(-100, 0) # Left wind

830

831

# Game loop

832

clock = pygame.time.Clock()

833

dt = 0

834

835

while True:

836

dt = clock.tick(60) / 1000.0 # Delta time in seconds

837

838

# Apply forces

839

ball1.apply_force(gravity * ball1.mass) # Weight = mg

840

ball1.apply_force(wind)

841

842

ball2.apply_force(gravity * ball2.mass)

843

844

# Update physics

845

ball1.update(dt)

846

ball2.update(dt)

847

848

# Check collision between balls

849

distance = ball1.position.distance_to(ball2.position)

850

if distance < ball1.radius + ball2.radius:

851

# Simple collision response

852

normal = (ball2.position - ball1.position).normalize()

853

relative_velocity = ball2.velocity - ball1.velocity

854

impulse = 2 * relative_velocity.dot(normal) / (ball1.mass + ball2.mass)

855

856

ball1.velocity += impulse * ball2.mass * normal

857

ball2.velocity -= impulse * ball1.mass * normal

858

```