or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

bodies-shapes.mdconstraints.mdgeometry.mdindex.mdphysics-world.mdutilities.mdvisualization.md

utilities.mddocs/

0

# Mathematical Utilities

1

2

Pymunk provides comprehensive 2D mathematics utilities including vectors, bounding boxes, transformations, and collision filters. These classes form the foundation for all physics calculations and spatial operations.

3

4

## Vec2d - 2D Vector Class

5

6

The `Vec2d` class is an immutable 2D vector with extensive mathematical operations and geometric functions.

7

8

### Class Definition

9

10

```python { .api }

11

class Vec2d(NamedTuple):

12

"""

13

Immutable 2D vector class with extensive mathematical operations.

14

15

Supports vector and scalar operators, geometric calculations,

16

and provides high-level functions for common operations.

17

"""

18

19

x: float

20

y: float

21

22

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

23

"""

24

Create a 2D vector.

25

26

Args:

27

x: X component

28

y: Y component

29

30

Examples:

31

Vec2d(1, 2) # Direct creation

32

Vec2d(*xy) # From tuple unpacking

33

Vec2d() # Zero vector (0, 0)

34

"""

35

```

36

37

### Properties

38

39

```python { .api }

40

# Vector components (inherited from NamedTuple)

41

v.x: float

42

"""X component of the vector"""

43

44

v.y: float

45

"""Y component of the vector"""

46

47

# Magnitude properties

48

v.length: float

49

"""Magnitude/length of the vector: sqrt(x² + y²)"""

50

51

v.length_squared: float

52

"""Squared magnitude (faster than length): x² + y²"""

53

54

# Angular properties

55

v.angle: float

56

"""Angle in radians calculated with atan2(y, x)"""

57

58

v.angle_degrees: float

59

"""Angle in degrees"""

60

```

61

62

### Arithmetic Operations

63

64

```python { .api }

65

# Vector arithmetic (returns new Vec2d)

66

v1 + v2 # Vector addition

67

v1 - v2 # Vector subtraction

68

v1 * scalar # Scalar multiplication

69

scalar * v1 # Scalar multiplication (commutative)

70

v1 / scalar # Scalar division

71

v1 // scalar # Integer division

72

73

# Unary operations

74

+v1 # Positive (returns copy)

75

-v1 # Negation

76

abs(v1) # Magnitude as float

77

78

# Comparison

79

v1 == v2 # Equality

80

v1 != v2 # Inequality

81

82

# Examples:

83

v1 = Vec2d(10, 20)

84

v2 = Vec2d(5, 15)

85

result = v1 + v2 # Vec2d(15, 35)

86

scaled = v1 * 2 # Vec2d(20, 40)

87

length = abs(v1) # 22.36 (approximately)

88

```

89

90

### Vector Operations

91

92

```python { .api }

93

def normalized(self) -> 'Vec2d':

94

"""

95

Return unit vector (length 1) in same direction.

96

97

Returns Vec2d(0, 0) for zero-length vectors.

98

99

Example:

100

v = Vec2d(3, 4)

101

unit = v.normalized() # Vec2d(0.6, 0.8)

102

"""

103

104

def normalized_and_length(self) -> tuple['Vec2d', float]:

105

"""

106

Return normalized vector and original length.

107

108

More efficient than calling normalized() and length separately.

109

110

Returns:

111

(normalized_vector, original_length)

112

113

Example:

114

v = Vec2d(3, 4)

115

unit, length = v.normalized_and_length() # (Vec2d(0.6, 0.8), 5.0)

116

"""

117

118

def scale_to_length(self, length: float) -> 'Vec2d':

119

"""

120

Return vector scaled to specific length.

121

122

Args:

123

length: Desired length

124

125

Raises:

126

ZeroDivisionError: If vector has zero length

127

128

Example:

129

v = Vec2d(3, 4)

130

scaled = v.scale_to_length(10) # Length 10 vector in same direction

131

"""

132

133

def perpendicular(self) -> 'Vec2d':

134

"""

135

Return perpendicular vector (rotated 90° counter-clockwise).

136

137

Example:

138

v = Vec2d(1, 0)

139

perp = v.perpendicular() # Vec2d(0, 1)

140

"""

141

142

def perpendicular_normal(self) -> 'Vec2d':

143

"""Return normalized perpendicular vector."""

144

145

def projection(self, other: tuple[float, float]) -> 'Vec2d':

146

"""

147

Return vector projection onto another vector.

148

149

Args:

150

other: Vector to project onto

151

152

Example:

153

v1 = Vec2d(10, 5)

154

v2 = Vec2d(1, 0)

155

proj = v1.projection(v2) # Vec2d(10, 0) - projection onto x-axis

156

"""

157

```

158

159

### Rotation Operations

160

161

```python { .api }

162

def rotated(self, angle_radians: float) -> 'Vec2d':

163

"""

164

Return vector rotated by angle in radians.

165

166

Args:

167

angle_radians: Rotation angle (positive = counter-clockwise)

168

169

Example:

170

v = Vec2d(1, 0)

171

rotated = v.rotated(math.pi/2) # Vec2d(0, 1) - 90° rotation

172

"""

173

174

def rotated_degrees(self, angle_degrees: float) -> 'Vec2d':

175

"""

176

Return vector rotated by angle in degrees.

177

178

Args:

179

angle_degrees: Rotation angle (positive = counter-clockwise)

180

181

Example:

182

v = Vec2d(2, 0)

183

rotated = v.rotated_degrees(90) # Vec2d(0, 2)

184

"""

185

186

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

187

"""

188

Get angle between this vector and another (radians).

189

190

Returns angle in range [-π, π].

191

192

Example:

193

v1 = Vec2d(1, 0)

194

v2 = Vec2d(0, 1)

195

angle = v1.get_angle_between(v2) # π/2 (90 degrees)

196

"""

197

198

def get_angle_degrees_between(self, other: 'Vec2d') -> float:

199

"""Get angle between vectors in degrees."""

200

```

201

202

### Dot and Cross Products

203

204

```python { .api }

205

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

206

"""

207

Calculate dot product with another vector.

208

209

Dot product measures similarity of direction:

210

- Positive: vectors point in similar directions

211

- Zero: vectors are perpendicular

212

- Negative: vectors point in opposite directions

213

214

Example:

215

v1 = Vec2d(1, 0)

216

v2 = Vec2d(0, 1)

217

dot = v1.dot(v2) # 0.0 (perpendicular)

218

"""

219

220

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

221

"""

222

Calculate 2D cross product (returns scalar).

223

224

Cross product measures perpendicularity:

225

- Positive: other is counter-clockwise from self

226

- Zero: vectors are parallel/anti-parallel

227

- negative: other is clockwise from self

228

229

Example:

230

v1 = Vec2d(1, 0)

231

v2 = Vec2d(0, 1)

232

cross = v1.cross(v2) # 1.0 (v2 is 90° CCW from v1)

233

"""

234

```

235

236

### Static Factory Methods

237

238

```python { .api }

239

@staticmethod

240

def from_polar(length: float, angle_radians: float) -> 'Vec2d':

241

"""

242

Create vector from polar coordinates.

243

244

Args:

245

length: Vector magnitude

246

angle_radians: Angle from positive x-axis

247

248

Example:

249

v = Vec2d.from_polar(5, math.pi/4) # 45° angle, length 5

250

# Result: Vec2d(3.54, 3.54) approximately

251

"""

252

253

@staticmethod

254

def unit() -> 'Vec2d':

255

"""Return unit vector pointing along positive x-axis: Vec2d(1, 0)"""

256

257

@staticmethod

258

def zero() -> 'Vec2d':

259

"""Return zero vector: Vec2d(0, 0)"""

260

```

261

262

### Indexing and Conversion

263

264

```python { .api }

265

# Indexing support

266

v[0] # Same as v.x

267

v[1] # Same as v.y

268

len(v) # Always 2

269

270

# Conversion

271

list(v) # [x, y]

272

tuple(v) # (x, y)

273

iter(v) # Iterator over (x, y)

274

275

# String representation

276

str(v) # "Vec2d(x, y)"

277

repr(v) # "Vec2d(x, y)"

278

```

279

280

## BB - Bounding Box Class

281

282

Axis-aligned bounding box for efficient spatial operations and collision detection.

283

284

### Class Definition

285

286

```python { .api }

287

class BB(NamedTuple):

288

"""

289

Axis-aligned bounding box stored as left, bottom, right, top values.

290

291

Uses mathematical coordinate system (bottom < top).

292

"""

293

294

left: float = 0

295

bottom: float = 0

296

right: float = 0

297

top: float = 0

298

299

# Examples:

300

BB(left=10, bottom=20, right=100, top=80)

301

BB(right=50, top=30) # Partial specification (left=0, bottom=0)

302

```

303

304

### Factory Methods

305

306

```python { .api }

307

@staticmethod

308

def newForCircle(center: tuple[float, float], radius: float) -> 'BB':

309

"""

310

Create bounding box for a circle.

311

312

Args:

313

center: Circle center point

314

radius: Circle radius

315

316

Example:

317

bb = BB.newForCircle((100, 200), 25)

318

# Result: BB(left=75, bottom=175, right=125, top=225)

319

"""

320

```

321

322

### Query Operations

323

324

```python { .api }

325

def intersects(self, other: 'BB') -> bool:

326

"""

327

Test if two bounding boxes intersect.

328

329

Example:

330

bb1 = BB(0, 0, 50, 50)

331

bb2 = BB(25, 25, 75, 75)

332

overlaps = bb1.intersects(bb2) # True

333

"""

334

335

def intersects_segment(

336

self,

337

a: tuple[float, float],

338

b: tuple[float, float]

339

) -> bool:

340

"""

341

Test if line segment intersects bounding box.

342

343

Args:

344

a: Segment start point

345

b: Segment end point

346

"""

347

348

def contains(self, other: 'BB') -> bool:

349

"""Test if this bounding box completely contains another."""

350

351

def contains_vect(self, point: tuple[float, float]) -> bool:

352

"""

353

Test if point is inside bounding box.

354

355

Example:

356

bb = BB(0, 0, 100, 100)

357

inside = bb.contains_vect((50, 50)) # True

358

outside = bb.contains_vect((150, 50)) # False

359

"""

360

```

361

362

### Geometric Operations

363

364

```python { .api }

365

def merge(self, other: 'BB') -> 'BB':

366

"""

367

Return bounding box that contains both this and other.

368

369

Example:

370

bb1 = BB(0, 0, 50, 50)

371

bb2 = BB(25, 25, 100, 100)

372

merged = bb1.merge(bb2) # BB(0, 0, 100, 100)

373

"""

374

375

def expand(self, v: tuple[float, float]) -> 'BB':

376

"""

377

Return bounding box expanded to contain point v.

378

379

Example:

380

bb = BB(0, 0, 50, 50)

381

expanded = bb.expand((75, 25)) # BB(0, 0, 75, 50)

382

"""

383

384

def center(self) -> Vec2d:

385

"""

386

Return center point of bounding box.

387

388

Example:

389

bb = BB(0, 0, 100, 100)

390

center = bb.center() # Vec2d(50, 50)

391

"""

392

393

def area(self) -> float:

394

"""

395

Calculate area of bounding box.

396

397

Example:

398

bb = BB(0, 0, 50, 40)

399

area = bb.area() # 2000.0

400

"""

401

402

def merged_area(self, other: 'BB') -> float:

403

"""Calculate area of bounding box that would contain both."""

404

```

405

406

### Advanced Operations

407

408

```python { .api }

409

def segment_query(

410

self,

411

a: tuple[float, float],

412

b: tuple[float, float]

413

) -> float:

414

"""

415

Return fraction along segment where bounding box is hit.

416

417

Returns infinity if segment doesn't hit.

418

419

Args:

420

a: Segment start

421

b: Segment end

422

423

Returns:

424

Fraction t where hit_point = a + t * (b - a)

425

"""

426

427

def clamp_vect(self, v: tuple[float, float]) -> Vec2d:

428

"""

429

Clamp point to bounding box (find closest point inside).

430

431

Example:

432

bb = BB(0, 0, 100, 100)

433

clamped = bb.clamp_vect((150, 50)) # Vec2d(100, 50)

434

"""

435

436

def wrap_vect(self, v: tuple[float, float]) -> Vec2d:

437

"""

438

Wrap point around bounding box (modulo operation).

439

440

Useful for implementing wrapping/toroidal spaces.

441

"""

442

```

443

444

## Transform - 2D Transformation Matrix

445

446

Represents 2D affine transformations for scaling, rotation, translation, and skewing operations.

447

448

### Class Definition

449

450

```python { .api }

451

class Transform(NamedTuple):

452

"""

453

2x3 affine transformation matrix for 2D operations.

454

455

Matrix layout:

456

| a c tx | | x |

457

| b d ty | @ | y |

458

| 0 0 1 | | 1 |

459

460

Supports composition via matrix multiplication operator (@).

461

"""

462

463

a: float = 1 # X-axis scaling/rotation

464

b: float = 0 # X-axis skew

465

c: float = 0 # Y-axis skew

466

d: float = 1 # Y-axis scaling/rotation

467

tx: float = 0 # X translation

468

ty: float = 0 # Y translation

469

470

# Examples:

471

Transform(a=2, d=2) # 2x scaling

472

Transform(tx=100, ty=50) # Translation

473

Transform(1, 2, 3, 4, 5, 6) # Full specification

474

```

475

476

### Static Factory Methods

477

478

```python { .api }

479

@staticmethod

480

def identity() -> 'Transform':

481

"""

482

Return identity transform (no transformation).

483

484

Example:

485

t = Transform.identity() # Transform(1, 0, 0, 1, 0, 0)

486

"""

487

488

@staticmethod

489

def translation(tx: float, ty: float) -> 'Transform':

490

"""

491

Create translation transform.

492

493

Args:

494

tx: X translation

495

ty: Y translation

496

497

Example:

498

t = Transform.translation(100, 50)

499

point = t @ Vec2d(0, 0) # Vec2d(100, 50)

500

"""

501

502

@staticmethod

503

def scaling(s: float) -> 'Transform':

504

"""

505

Create uniform scaling transform.

506

507

Args:

508

s: Scale factor (2.0 = double size, 0.5 = half size)

509

510

Example:

511

t = Transform.scaling(2)

512

point = t @ Vec2d(10, 20) # Vec2d(20, 40)

513

"""

514

515

@staticmethod

516

def rotation(angle_radians: float) -> 'Transform':

517

"""

518

Create rotation transform.

519

520

Args:

521

angle_radians: Rotation angle (positive = counter-clockwise)

522

523

Example:

524

t = Transform.rotation(math.pi/2) # 90° rotation

525

point = t @ Vec2d(1, 0) # Vec2d(0, 1)

526

"""

527

```

528

529

### Transform Operations

530

531

```python { .api }

532

def __matmul__(self, other: Union[Vec2d, tuple, 'Transform']) -> Union[Vec2d, 'Transform']:

533

"""

534

Apply transform using @ operator.

535

536

Can transform points/vectors or compose with other transforms.

537

538

Examples:

539

# Transform points

540

t = Transform.scaling(2)

541

result = t @ Vec2d(10, 20) # Vec2d(20, 40)

542

543

# Compose transforms

544

t1 = Transform.scaling(2)

545

t2 = Transform.translation(10, 5)

546

composed = t1 @ t2 # Scale then translate

547

"""

548

549

def translated(self, tx: float, ty: float) -> 'Transform':

550

"""

551

Return transform with additional translation.

552

553

Example:

554

t = Transform.scaling(2)

555

t_moved = t.translated(100, 50) # Scale and translate

556

"""

557

558

def scaled(self, s: float) -> 'Transform':

559

"""

560

Return transform with additional scaling.

561

562

Example:

563

t = Transform.translation(100, 50)

564

t_scaled = t.scaled(2) # Translate and scale

565

"""

566

567

def rotated(self, angle_radians: float) -> 'Transform':

568

"""

569

Return transform with additional rotation.

570

571

Example:

572

t = Transform.scaling(2)

573

t_rotated = t.rotated(math.pi/4) # Scale and rotate 45°

574

"""

575

```

576

577

## ShapeFilter - Collision Filtering

578

579

Controls which shapes can collide with each other using groups, categories, and masks.

580

581

### Class Definition

582

583

```python { .api }

584

class ShapeFilter(NamedTuple):

585

"""

586

Collision filter using groups, categories, and category masks.

587

588

Provides efficient collision filtering before expensive collision detection.

589

"""

590

591

group: int = 0

592

"""

593

Collision group - shapes with same non-zero group don't collide.

594

Use for complex objects where parts shouldn't collide with each other.

595

"""

596

597

categories: int = 0xFFFFFFFF # All categories by default

598

"""

599

Categories this shape belongs to (bitmask).

600

Shape can belong to multiple categories.

601

"""

602

603

mask: int = 0xFFFFFFFF # Collide with all categories by default

604

"""

605

Categories this shape collides with (bitmask).

606

Must have overlapping bits with other shape's categories to collide.

607

"""

608

```

609

610

### Static Methods

611

612

```python { .api }

613

@staticmethod

614

def ALL_CATEGORIES() -> int:

615

"""Return bitmask with all category bits set (0xFFFFFFFF)"""

616

617

@staticmethod

618

def ALL_MASKS() -> int:

619

"""Return bitmask matching all categories (0xFFFFFFFF)"""

620

```

621

622

### Collision Logic

623

624

```python { .api }

625

def rejects_collision(self, other: 'ShapeFilter') -> bool:

626

"""

627

Test if this filter rejects collision with another filter.

628

629

Collision rejected if:

630

1. Same non-zero group (shapes in same group don't collide)

631

2. No category overlap (self.categories & other.mask == 0)

632

3. No mask overlap (self.mask & other.categories == 0)

633

634

Example:

635

player_filter = ShapeFilter(categories=0b1, mask=0b1110) # Category 1, collides with 2,3,4

636

enemy_filter = ShapeFilter(categories=0b10, mask=0b1101) # Category 2, collides with 1,3,4

637

638

can_collide = not player_filter.rejects_collision(enemy_filter) # True

639

"""

640

```

641

642

## Usage Examples

643

644

### Vector Mathematics

645

646

```python { .api }

647

import pymunk

648

import math

649

650

# Basic vector operations

651

v1 = pymunk.Vec2d(10, 20)

652

v2 = pymunk.Vec2d(5, 15)

653

654

# Arithmetic

655

sum_v = v1 + v2 # Vec2d(15, 35)

656

diff_v = v1 - v2 # Vec2d(5, 5)

657

scaled = v1 * 2.5 # Vec2d(25, 50)

658

magnitude = abs(v1) # 22.36...

659

660

# Normalization and direction

661

unit_v = v1.normalized() # Vec2d(0.447, 0.894)

662

direction = (v2 - v1).normalized() # Direction from v1 to v2

663

664

# Angles and rotation

665

angle = v1.angle # Angle in radians

666

angle_deg = v1.angle_degrees # Angle in degrees

667

rotated = v1.rotated(math.pi/4) # Rotate 45°

668

669

# Dot product for projections

670

proj_length = v1.dot(v2.normalized()) # Length of v1 projected onto v2 direction

671

projection = v2.normalized() * proj_length

672

673

# Cross product for perpendicularity

674

perp_v = v1.perpendicular() # Vec2d(-20, 10) - 90° CCW rotation

675

cross = v1.cross(v2) # Scalar cross product

676

```

677

678

### Polar Coordinates

679

680

```python { .api }

681

import pymunk

682

import math

683

684

# Create vectors from polar coordinates

685

radius = 100

686

angle = math.pi / 3 # 60 degrees

687

688

velocity = pymunk.Vec2d.from_polar(radius, angle)

689

print(f"Velocity: {velocity}") # Vec2d(50.0, 86.6)

690

691

# Convert back to polar

692

length = abs(velocity)

693

angle = velocity.angle

694

695

# Circular motion

696

def circular_motion(time, radius, angular_velocity):

697

angle = angular_velocity * time

698

return pymunk.Vec2d.from_polar(radius, angle)

699

700

# Orbit positions

701

for t in range(10):

702

position = circular_motion(t * 0.1, 50, math.pi)

703

print(f"t={t*0.1:.1f}: {position}")

704

```

705

706

### Bounding Box Operations

707

708

```python { .api }

709

import pymunk

710

711

# Create bounding boxes for shapes

712

circle_bb = pymunk.BB.newForCircle((100, 100), 25)

713

box_bb = pymunk.BB(left=50, bottom=50, right=150, top=150)

714

715

# Test intersections

716

if circle_bb.intersects(box_bb):

717

print("Circle and box overlap")

718

719

# Spatial partitioning

720

def get_grid_cell(position, cell_size):

721

"""Get grid cell coordinates for spatial partitioning"""

722

x = int(position.x // cell_size) * cell_size

723

y = int(position.y // cell_size) * cell_size

724

return pymunk.BB(x, y, x + cell_size, y + cell_size)

725

726

# Expand bounding box to contain objects

727

world_bb = pymunk.BB(0, 0, 0, 0)

728

objects = [pymunk.Vec2d(100, 200), pymunk.Vec2d(300, 150), pymunk.Vec2d(50, 400)]

729

730

for obj_pos in objects:

731

world_bb = world_bb.expand(obj_pos)

732

733

print(f"World bounds: {world_bb}")

734

```

735

736

### Transform Compositions

737

738

```python { .api }

739

import pymunk

740

import math

741

742

# Object transformation pipeline

743

def transform_object(position, scale, rotation, translation):

744

"""Apply scale, rotation, then translation"""

745

746

# Create individual transforms

747

scale_t = pymunk.Transform.scaling(scale)

748

rotate_t = pymunk.Transform.rotation(rotation)

749

translate_t = pymunk.Transform.translation(*translation)

750

751

# Compose transforms (order matters!)

752

combined = translate_t @ rotate_t @ scale_t

753

754

# Apply to position

755

return combined @ position

756

757

# Example: Scale by 2, rotate 45°, move to (100, 100)

758

original = pymunk.Vec2d(10, 0)

759

transformed = transform_object(

760

original,

761

scale=2,

762

rotation=math.pi/4,

763

translation=(100, 100)

764

)

765

766

# Camera/viewport transform

767

def screen_to_world(screen_pos, camera_pos, camera_zoom):

768

"""Convert screen coordinates to world coordinates"""

769

770

# Screen transform: translate to center, scale by zoom, translate by camera

771

screen_center = pymunk.Transform.translation(-400, -300) # Assume 800x600 screen

772

zoom_t = pymunk.Transform.scaling(1.0 / camera_zoom)

773

camera_t = pymunk.Transform.translation(*camera_pos)

774

775

world_transform = camera_t @ zoom_t @ screen_center

776

return world_transform @ screen_pos

777

```

778

779

### Collision Filtering System

780

781

```python { .api }

782

import pymunk

783

784

# Define collision categories (using powers of 2 for bitmasks)

785

PLAYER = 0b00001 # 1

786

ENEMY = 0b00010 # 2

787

PLAYER_BULLET = 0b00100 # 4

788

ENEMY_BULLET = 0b01000 # 8

789

WALLS = 0b10000 # 16

790

791

# Create collision filters

792

player_filter = pymunk.ShapeFilter(

793

categories=PLAYER,

794

mask=ENEMY | ENEMY_BULLET | WALLS # Collide with enemies, enemy bullets, walls

795

)

796

797

enemy_filter = pymunk.ShapeFilter(

798

categories=ENEMY,

799

mask=PLAYER | PLAYER_BULLET | WALLS # Collide with player, player bullets, walls

800

)

801

802

player_bullet_filter = pymunk.ShapeFilter(

803

categories=PLAYER_BULLET,

804

mask=ENEMY | WALLS # Only hit enemies and walls

805

)

806

807

enemy_bullet_filter = pymunk.ShapeFilter(

808

categories=ENEMY_BULLET,

809

mask=PLAYER | WALLS # Only hit player and walls

810

)

811

812

wall_filter = pymunk.ShapeFilter(

813

categories=WALLS,

814

mask=PLAYER | ENEMY | PLAYER_BULLET | ENEMY_BULLET # Hit everything

815

)

816

817

# Ragdoll example - body parts don't collide with each other

818

RAGDOLL_GROUP = 1

819

820

head_filter = pymunk.ShapeFilter(group=RAGDOLL_GROUP, categories=PLAYER, mask=WALLS)

821

torso_filter = pymunk.ShapeFilter(group=RAGDOLL_GROUP, categories=PLAYER, mask=WALLS)

822

arm_filter = pymunk.ShapeFilter(group=RAGDOLL_GROUP, categories=PLAYER, mask=WALLS)

823

824

# All ragdoll parts have same group, so they won't collide with each other

825

826

# Team-based filtering

827

RED_TEAM = 0b100000 # 32

828

BLUE_TEAM = 0b1000000 # 64

829

830

red_player_filter = pymunk.ShapeFilter(

831

categories=RED_TEAM | PLAYER,

832

mask=BLUE_TEAM | WALLS # Only collide with blue team and walls

833

)

834

835

blue_player_filter = pymunk.ShapeFilter(

836

categories=BLUE_TEAM | PLAYER,

837

mask=RED_TEAM | WALLS # Only collide with red team and walls

838

)

839

```

840

841

## Area Calculation Functions

842

843

Pymunk provides utilities for calculating areas of geometric shapes, useful for physics calculations and shape analysis.

844

845

### Area Functions

846

847

```python { .api }

848

def area_for_circle(inner_radius: float, outer_radius: float) -> float:

849

"""

850

Calculate area of a hollow circle.

851

852

For solid circles, use inner_radius=0.

853

854

Args:

855

inner_radius: Inner radius (0 for solid circle)

856

outer_radius: Outer radius

857

858

Returns:

859

Area of the hollow circle shape

860

861

Example:

862

solid_area = area_for_circle(0, 25) # Solid circle, radius 25

863

ring_area = area_for_circle(20, 25) # Ring from radius 20 to 25

864

"""

865

866

def area_for_segment(

867

a: tuple[float, float],

868

b: tuple[float, float],

869

radius: float

870

) -> float:

871

"""

872

Calculate area of a beveled segment.

873

874

Will always be zero if radius is zero (line has no area).

875

876

Args:

877

a: Start point of the segment

878

b: End point of the segment

879

radius: Radius/thickness of the segment

880

881

Returns:

882

Area of the beveled segment

883

884

Example:

885

# Thick line segment from (0,0) to (100,0) with radius 5

886

area = area_for_segment((0, 0), (100, 0), 5)

887

"""

888

889

def area_for_poly(

890

vertices: Sequence[tuple[float, float]],

891

radius: float = 0

892

) -> float:

893

"""

894

Calculate signed area of a polygon shape.

895

896

Returns negative number for polygons with clockwise winding.

897

898

Args:

899

vertices: List of vertex coordinates

900

radius: Optional radius for rounded corners

901

902

Returns:

903

Signed area (negative for clockwise polygons)

904

905

Example:

906

# Rectangle vertices (counter-clockwise)

907

vertices = [(0, 0), (50, 0), (50, 30), (0, 30)]

908

area = area_for_poly(vertices) # Positive area: 1500.0

909

910

# Same vertices clockwise

911

vertices_cw = [(0, 0), (0, 30), (50, 30), (50, 0)]

912

area_cw = area_for_poly(vertices_cw) # Negative area: -1500.0

913

"""

914

```

915

916

## Moment of Inertia Calculation Functions

917

918

Pymunk provides functions to calculate moments of inertia for different shape types, essential for realistic physics simulations.

919

920

### Moment Functions

921

922

```python { .api }

923

def moment_for_circle(

924

mass: float,

925

inner_radius: float,

926

outer_radius: float,

927

offset: tuple[float, float] = (0, 0)

928

) -> float:

929

"""

930

Calculate moment of inertia for a hollow circle.

931

932

A solid circle has an inner radius of 0.

933

934

Args:

935

mass: Mass of the shape

936

inner_radius: Inner radius (0 for solid circle)

937

outer_radius: Outer radius

938

offset: Offset from body center (default: (0, 0))

939

940

Returns:

941

Moment of inertia value

942

943

Example:

944

# Solid disk

945

solid_moment = moment_for_circle(mass=10, inner_radius=0, outer_radius=25)

946

947

# Ring/hollow circle

948

ring_moment = moment_for_circle(mass=10, inner_radius=20, outer_radius=25)

949

950

# Off-center circle

951

offset_moment = moment_for_circle(mass=10, inner_radius=0, outer_radius=15, offset=(10, 5))

952

"""

953

954

def moment_for_segment(

955

mass: float,

956

a: tuple[float, float],

957

b: tuple[float, float],

958

radius: float

959

) -> float:

960

"""

961

Calculate moment of inertia for a line segment.

962

963

The endpoints a and b are relative to the body center.

964

965

Args:

966

mass: Mass of the segment

967

a: Start point relative to body

968

b: End point relative to body

969

radius: Thickness radius of the segment

970

971

Returns:

972

Moment of inertia value

973

974

Example:

975

# Horizontal line segment

976

moment = moment_for_segment(mass=5, a=(0, 0), b=(100, 0), radius=2)

977

978

# Vertical line segment

979

moment = moment_for_segment(mass=5, a=(0, -50), b=(0, 50), radius=3)

980

"""

981

982

def moment_for_box(mass: float, size: tuple[float, float]) -> float:

983

"""

984

Calculate moment of inertia for a solid box centered on the body.

985

986

Args:

987

mass: Mass of the box

988

size: (width, height) of the box

989

990

Returns:

991

Moment of inertia value

992

993

Example:

994

# Square box 50x50

995

moment = moment_for_box(mass=10, size=(50, 50))

996

997

# Rectangular box 100x30

998

moment = moment_for_box(mass=15, size=(100, 30))

999

"""

1000

1001

def moment_for_poly(

1002

mass: float,

1003

vertices: Sequence[tuple[float, float]],

1004

offset: tuple[float, float] = (0, 0),

1005

radius: float = 0

1006

) -> float:

1007

"""

1008

Calculate moment of inertia for a solid polygon shape.

1009

1010

Assumes the polygon center of gravity is at its centroid.

1011

The offset is added to each vertex.

1012

1013

Args:

1014

mass: Mass of the polygon

1015

vertices: List of vertex coordinates

1016

offset: Offset added to each vertex (default: (0, 0))

1017

radius: Optional radius for rounded corners

1018

1019

Returns:

1020

Moment of inertia value

1021

1022

Example:

1023

# Triangle

1024

triangle_vertices = [(0, 0), (50, 0), (25, 43)]

1025

moment = moment_for_poly(mass=8, vertices=triangle_vertices)

1026

1027

# Rectangle with offset

1028

rect_vertices = [(0, 0), (40, 0), (40, 20), (0, 20)]

1029

moment = moment_for_poly(mass=12, vertices=rect_vertices, offset=(10, 5))

1030

1031

# Rounded polygon

1032

moment = moment_for_poly(mass=10, vertices=rect_vertices, radius=3)

1033

"""

1034

```

1035

1036

## Utility Callback Functions

1037

1038

### Empty Callback Function

1039

1040

```python { .api }

1041

def empty_callback(*args: Any, **kwargs: Any) -> None:

1042

"""

1043

A default empty callback function.

1044

1045

Can be used to reset a collision callback to its original empty

1046

function. More efficient than defining your own empty/do-nothing method.

1047

1048

Args:

1049

*args: Any positional arguments (ignored)

1050

**kwargs: Any keyword arguments (ignored)

1051

1052

Example:

1053

# Reset collision handler to do nothing

1054

handler = space.add_collision_handler(1, 2)

1055

handler.begin = empty_callback

1056

handler.pre_solve = empty_callback

1057

1058

# Or use directly as a placeholder

1059

space.add_collision_handler(3, 4).begin = empty_callback

1060

"""

1061

```

1062

1063

### Moment of Inertia Calculations

1064

1065

```python { .api }

1066

import pymunk

1067

1068

# Calculate moments of inertia for different shapes

1069

mass = 10

1070

1071

# Circle (solid disk)

1072

circle_moment = pymunk.moment_for_circle(mass, 0, 25) # inner_radius=0, outer_radius=25

1073

1074

# Hollow circle (ring)

1075

ring_moment = pymunk.moment_for_circle(mass, 20, 25) # inner_radius=20, outer_radius=25

1076

1077

# Box

1078

box_moment = pymunk.moment_for_box(mass, (50, 30)) # width=50, height=30

1079

1080

# Polygon

1081

vertices = [(0, 0), (50, 0), (50, 30), (0, 30)]

1082

poly_moment = pymunk.moment_for_poly(mass, vertices)

1083

1084

# Line segment

1085

segment_moment = pymunk.moment_for_segment(mass, (0, 0), (100, 0), radius=5)

1086

1087

# Compound shape (multiple shapes on same body)

1088

def create_compound_body():

1089

# Let pymunk calculate total mass/moment from shapes

1090

body = pymunk.Body()

1091

1092

# Add multiple shapes with different densities

1093

circle = pymunk.Circle(body, 25, (0, 0))

1094

circle.density = 1.0

1095

1096

box = pymunk.Poly.create_box(body, (40, 20), radius=2)

1097

box.density = 2.0 # Denser material

1098

1099

return body, [circle, box]

1100

```

1101

1102

These mathematical utilities provide the foundation for all physics calculations, spatial queries, and geometric operations in Pymunk, offering both performance and ease of use for 2D physics applications.