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

visualization.mddocs/

0

# Debug Visualization and Drawing

1

2

Pymunk provides comprehensive debug visualization capabilities for popular graphics libraries, enabling easy debugging and prototyping of physics simulations with visual feedback.

3

4

## SpaceDebugDrawOptions - Base Class

5

6

The foundation for all debug drawing implementations, providing customizable rendering options and colors.

7

8

### Class Definition

9

10

```python { .api }

11

class SpaceDebugDrawOptions:

12

"""

13

Base class for configuring debug drawing of physics spaces.

14

15

Provides customizable colors, drawing flags, and transformation options.

16

Subclassed by library-specific implementations (pygame, pyglet, matplotlib).

17

"""

18

19

# Drawing flags (combine with bitwise OR)

20

DRAW_SHAPES: int # Draw collision shapes

21

DRAW_CONSTRAINTS: int # Draw joints/constraints

22

DRAW_COLLISION_POINTS: int # Draw collision contact points

23

24

def __init__(self) -> None:

25

"""

26

Create debug draw options with default settings.

27

28

Default flags enable drawing of shapes, constraints, and collision points.

29

"""

30

```

31

32

### Drawing Control Flags

33

34

```python { .api }

35

# Control what elements are drawn

36

options.flags: int = (

37

SpaceDebugDrawOptions.DRAW_SHAPES |

38

SpaceDebugDrawOptions.DRAW_CONSTRAINTS |

39

SpaceDebugDrawOptions.DRAW_COLLISION_POINTS

40

)

41

"""

42

Bitmask controlling what elements to draw.

43

Combine flags with bitwise OR to enable multiple elements.

44

45

Examples:

46

options.flags = SpaceDebugDrawOptions.DRAW_SHAPES # Only shapes

47

options.flags = SpaceDebugDrawOptions.DRAW_SHAPES | SpaceDebugDrawOptions.DRAW_CONSTRAINTS # Shapes + constraints

48

options.flags = 0 # Draw nothing

49

"""

50

```

51

52

### Color Configuration

53

54

```python { .api }

55

# Shape colors based on body type

56

options.shape_dynamic_color: SpaceDebugColor = SpaceDebugColor(52, 152, 219, 255)

57

"""Color for dynamic body shapes (blue)"""

58

59

options.shape_static_color: SpaceDebugColor = SpaceDebugColor(149, 165, 166, 255)

60

"""Color for static body shapes (gray)"""

61

62

options.shape_kinematic_color: SpaceDebugColor = SpaceDebugColor(39, 174, 96, 255)

63

"""Color for kinematic body shapes (green)"""

64

65

options.shape_sleeping_color: SpaceDebugColor = SpaceDebugColor(114, 148, 168, 255)

66

"""Color for sleeping body shapes (darker blue)"""

67

68

# Element-specific colors

69

options.shape_outline_color: SpaceDebugColor = SpaceDebugColor(44, 62, 80, 255)

70

"""Color for shape outlines (dark blue-gray)"""

71

72

options.constraint_color: SpaceDebugColor = SpaceDebugColor(142, 68, 173, 255)

73

"""Color for constraints/joints (purple)"""

74

75

options.collision_point_color: SpaceDebugColor = SpaceDebugColor(231, 76, 60, 255)

76

"""Color for collision points (red)"""

77

```

78

79

### Coordinate Transformation

80

81

```python { .api }

82

options.transform: Transform = Transform.identity()

83

"""

84

Transformation applied to all drawing coordinates.

85

86

Use to implement camera/viewport transformations:

87

- Translation: Move view to follow objects

88

- Scaling: Zoom in/out

89

- Rotation: Rotate view

90

91

Example:

92

# Camera following player

93

camera_pos = player.position

94

options.transform = Transform.translation(-camera_pos.x, -camera_pos.y)

95

96

# Zoom and center

97

zoom = 2.0

98

center = (400, 300) # Screen center

99

options.transform = (

100

Transform.translation(*center) @

101

Transform.scaling(zoom) @

102

Transform.translation(-camera_pos.x, -camera_pos.y)

103

)

104

"""

105

```

106

107

## SpaceDebugColor - Color Utility

108

109

```python { .api }

110

class SpaceDebugColor(NamedTuple):

111

"""

112

RGBA color tuple for debug drawing.

113

114

Values are typically 0-255 for integer colors or 0.0-1.0 for float colors.

115

"""

116

117

r: float # Red component

118

g: float # Green component

119

b: float # Blue component

120

a: float # Alpha component (opacity)

121

122

def as_int(self) -> tuple[int, int, int, int]:

123

"""

124

Return color as integer tuple (0-255 range).

125

126

Example:

127

color = SpaceDebugColor(1.0, 0.5, 0.0, 1.0)

128

rgba_int = color.as_int() # (255, 128, 0, 255)

129

"""

130

131

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

132

"""

133

Return color as float tuple (0.0-1.0 range).

134

135

Example:

136

color = SpaceDebugColor(255, 128, 0, 255)

137

rgba_float = color.as_float() # (1.0, 0.5, 0.0, 1.0)

138

"""

139

140

# Common colors

141

RED = SpaceDebugColor(255, 0, 0, 255)

142

GREEN = SpaceDebugColor(0, 255, 0, 255)

143

BLUE = SpaceDebugColor(0, 0, 255, 255)

144

WHITE = SpaceDebugColor(255, 255, 255, 255)

145

BLACK = SpaceDebugColor(0, 0, 0, 255)

146

TRANSPARENT = SpaceDebugColor(0, 0, 0, 0)

147

```

148

149

## Pygame Integration

150

151

High-performance debug drawing for Pygame applications with coordinate system handling.

152

153

### Setup and Usage

154

155

```python { .api }

156

import pygame

157

import pymunk

158

import pymunk.pygame_util

159

160

# Initialize Pygame

161

pygame.init()

162

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

163

clock = pygame.time.Clock()

164

165

# Create physics space

166

space = pymunk.Space()

167

space.gravity = (0, -981)

168

169

# Create debug drawing options

170

draw_options = pymunk.pygame_util.DrawOptions(screen)

171

172

# Main loop

173

running = True

174

while running:

175

for event in pygame.event.get():

176

if event.type == pygame.QUIT:

177

running = False

178

179

# Clear screen

180

screen.fill((255, 255, 255)) # White background

181

182

# Step physics

183

dt = clock.tick(60) / 1000.0

184

space.step(dt)

185

186

# Draw physics debug visualization

187

space.debug_draw(draw_options)

188

189

pygame.display.flip()

190

191

pygame.quit()

192

```

193

194

### Coordinate System Configuration

195

196

```python { .api }

197

import pymunk.pygame_util

198

199

# Pygame uses Y-down coordinates, physics often uses Y-up

200

# Configure coordinate system behavior:

201

202

pymunk.pygame_util.positive_y_is_up = False # Default - Y increases downward

203

"""

204

Control Y-axis direction:

205

- False (default): Y increases downward (standard Pygame)

206

- True: Y increases upward (mathematical convention)

207

208

When True: When False:

209

y ^ +------ > x

210

| |

211

+---> x v y

212

"""

213

214

# If using Y-up physics, set gravity appropriately:

215

if pymunk.pygame_util.positive_y_is_up:

216

space.gravity = (0, -981) # Downward gravity

217

else:

218

space.gravity = (0, 981) # "Upward" gravity (actually downward in screen coords)

219

```

220

221

### Coordinate Conversion Utilities

222

223

```python { .api }

224

def get_mouse_pos(surface: pygame.Surface) -> Vec2d:

225

"""

226

Get mouse position converted to physics coordinates.

227

228

Handles coordinate system conversion based on positive_y_is_up setting.

229

230

Example:

231

mouse_pos = pymunk.pygame_util.get_mouse_pos(screen)

232

hit = space.point_query_nearest(mouse_pos, 0, pymunk.ShapeFilter())

233

"""

234

235

def to_pygame(point: Vec2d, surface: pygame.Surface) -> tuple[int, int]:

236

"""

237

Convert physics coordinates to Pygame screen coordinates.

238

239

Args:

240

point: Physics world coordinate

241

surface: Pygame surface for height reference

242

243

Returns:

244

(x, y) tuple in Pygame coordinates

245

"""

246

247

def from_pygame(point: tuple[int, int], surface: pygame.Surface) -> Vec2d:

248

"""

249

Convert Pygame screen coordinates to physics coordinates.

250

251

Args:

252

point: Pygame coordinate (x, y)

253

surface: Pygame surface for height reference

254

255

Returns:

256

Vec2d in physics world coordinates

257

"""

258

```

259

260

### Custom Colors

261

262

```python { .api }

263

import pygame

264

import pymunk.pygame_util

265

266

# Create debug options

267

draw_options = pymunk.pygame_util.DrawOptions(screen)

268

269

# Customize colors

270

draw_options.shape_outline_color = pymunk.SpaceDebugColor(255, 0, 0, 255) # Red outlines

271

draw_options.constraint_color = pymunk.SpaceDebugColor(0, 255, 0, 255) # Green constraints

272

draw_options.collision_point_color = pymunk.SpaceDebugColor(255, 255, 0, 255) # Yellow collision points

273

274

# Set individual shape colors

275

circle_shape.color = pygame.Color("pink")

276

box_shape.color = (0, 255, 255, 255) # Cyan

277

278

# Control what gets drawn

279

draw_options.flags = pymunk.SpaceDebugDrawOptions.DRAW_SHAPES # Only shapes, no constraints

280

```

281

282

## Pyglet Integration

283

284

OpenGL-based debug drawing with batched rendering for performance.

285

286

### Setup and Usage

287

288

```python { .api }

289

import pyglet

290

import pymunk

291

import pymunk.pyglet_util

292

293

# Create window

294

window = pyglet.window.Window(800, 600, "Physics Debug")

295

296

# Create physics space

297

space = pymunk.Space()

298

space.gravity = (0, -981)

299

300

# Create debug drawing options

301

draw_options = pymunk.pyglet_util.DrawOptions()

302

303

@window.event

304

def on_draw():

305

window.clear()

306

307

# Draw physics debug visualization

308

space.debug_draw(draw_options)

309

310

def update(dt):

311

space.step(dt)

312

313

# Schedule physics updates

314

pyglet.clock.schedule_interval(update, 1/60.0)

315

316

# Run application

317

pyglet.app.run()

318

```

319

320

### Batched Drawing for Performance

321

322

```python { .api }

323

import pyglet

324

import pymunk.pyglet_util

325

326

# Create custom batch for optimized rendering

327

batch = pyglet.graphics.Batch()

328

329

# Create debug options with custom batch

330

draw_options = pymunk.pyglet_util.DrawOptions(batch=batch)

331

332

@window.event

333

def on_draw():

334

window.clear()

335

336

# Draw physics (adds to batch, doesn't render immediately)

337

space.debug_draw(draw_options)

338

339

# Render entire batch efficiently

340

batch.draw()

341

```

342

343

### Context Manager Support

344

345

```python { .api }

346

import pyglet

347

import pymunk.pyglet_util

348

349

draw_options = pymunk.pyglet_util.DrawOptions()

350

351

@window.event

352

def on_draw():

353

window.clear()

354

355

# Use context manager for automatic OpenGL state management

356

with draw_options:

357

space.debug_draw(draw_options)

358

```

359

360

### Custom Shape Colors

361

362

```python { .api }

363

import pyglet

364

import pymunk.pyglet_util

365

366

# Set shape-specific colors

367

circle_shape.color = (255, 0, 0, 255) # Red circle

368

box_shape.color = (0, 255, 0, 255) # Green box

369

segment_shape.color = (0, 0, 255, 255) # Blue segment

370

371

# Use Pyglet color utilities

372

import pyglet.image

373

circle_shape.color = pyglet.image.get_color("pink")

374

```

375

376

## Matplotlib Integration

377

378

Publication-quality debug visualization for analysis and documentation.

379

380

### Setup and Usage

381

382

```python { .api }

383

import matplotlib.pyplot as plt

384

import pymunk

385

import pymunk.matplotlib_util

386

387

# Create figure and axes

388

fig, ax = plt.subplots(figsize=(10, 8))

389

390

# Create physics space

391

space = pymunk.Space()

392

space.gravity = (0, -981)

393

394

# Add some physics objects...

395

# ... physics simulation code ...

396

397

# Create debug drawing options

398

draw_options = pymunk.matplotlib_util.DrawOptions(ax)

399

400

# Draw physics state

401

space.debug_draw(draw_options)

402

403

# Configure plot

404

ax.set_xlim(0, 800)

405

ax.set_ylim(0, 600)

406

ax.set_aspect('equal')

407

ax.grid(True, alpha=0.3)

408

ax.set_title('Physics Simulation Debug View')

409

410

plt.show()

411

```

412

413

### Animation and Time Series

414

415

```python { .api }

416

import matplotlib.pyplot as plt

417

import matplotlib.animation as animation

418

import pymunk

419

import pymunk.matplotlib_util

420

421

fig, ax = plt.subplots()

422

space = pymunk.Space()

423

draw_options = pymunk.matplotlib_util.DrawOptions(ax)

424

425

def animate(frame):

426

ax.clear()

427

428

# Step physics

429

space.step(1/60.0)

430

431

# Draw current state

432

space.debug_draw(draw_options)

433

434

# Configure axes

435

ax.set_xlim(0, 800)

436

ax.set_ylim(0, 600)

437

ax.set_aspect('equal')

438

ax.set_title(f'Frame {frame}')

439

440

# Create animation

441

anim = animation.FuncAnimation(fig, animate, interval=16, blit=False)

442

plt.show()

443

444

# Save as video

445

# anim.save('physics_simulation.mp4', writer='ffmpeg', fps=60)

446

```

447

448

### Custom Shape Styling

449

450

```python { .api }

451

import matplotlib.pyplot as plt

452

import pymunk.matplotlib_util

453

454

# Set shape colors using matplotlib color formats

455

circle_shape.color = (1.0, 0.0, 0.0, 1.0) # Red (RGBA floats)

456

box_shape.color = 'blue' # Named color

457

segment_shape.color = '#00FF00' # Hex color

458

459

# Create debug options with custom styling

460

draw_options = pymunk.matplotlib_util.DrawOptions(ax)

461

draw_options.shape_outline_color = pymunk.SpaceDebugColor(0, 0, 0, 255) # Black outlines

462

```

463

464

## Advanced Visualization Techniques

465

466

### Camera and Viewport Control

467

468

```python { .api }

469

import pymunk

470

471

class Camera:

472

def __init__(self, position=(0, 0), zoom=1.0):

473

self.position = pymunk.Vec2d(*position)

474

self.zoom = zoom

475

476

def follow_body(self, body, screen_size):

477

"""Make camera follow a body"""

478

self.position = body.position

479

480

def get_transform(self, screen_size):

481

"""Get transform matrix for debug drawing"""

482

screen_center = pymunk.Vec2d(*screen_size) * 0.5

483

484

return (

485

pymunk.Transform.translation(*screen_center) @

486

pymunk.Transform.scaling(self.zoom) @

487

pymunk.Transform.translation(-self.position.x, -self.position.y)

488

)

489

490

# Usage with any debug drawing system

491

camera = Camera()

492

camera.follow_body(player_body, (800, 600))

493

494

draw_options.transform = camera.get_transform((800, 600))

495

space.debug_draw(draw_options)

496

```

497

498

### Selective Drawing

499

500

```python { .api }

501

import pymunk

502

503

class SelectiveDrawOptions(pymunk.pygame_util.DrawOptions):

504

"""Custom draw options that only draw specific shapes"""

505

506

def __init__(self, surface, shape_filter=None):

507

super().__init__(surface)

508

self.shape_filter = shape_filter or (lambda shape: True)

509

510

def draw_shape(self, shape):

511

"""Override to implement selective drawing"""

512

if self.shape_filter(shape):

513

super().draw_shape(shape)

514

515

# Filter functions

516

def draw_only_dynamic(shape):

517

return shape.body.body_type == pymunk.Body.DYNAMIC

518

519

def draw_only_circles(shape):

520

return isinstance(shape, pymunk.Circle)

521

522

# Usage

523

draw_options = SelectiveDrawOptions(screen, draw_only_dynamic)

524

space.debug_draw(draw_options)

525

```

526

527

### Performance Monitoring

528

529

```python { .api }

530

import time

531

import pymunk

532

533

class ProfiledDrawOptions(pymunk.pygame_util.DrawOptions):

534

"""Draw options with performance monitoring"""

535

536

def __init__(self, surface):

537

super().__init__(surface)

538

self.draw_time = 0

539

self.shape_count = 0

540

541

def draw_shape(self, shape):

542

start_time = time.perf_counter()

543

super().draw_shape(shape)

544

self.draw_time += time.perf_counter() - start_time

545

self.shape_count += 1

546

547

def reset_stats(self):

548

self.draw_time = 0

549

self.shape_count = 0

550

551

# Usage

552

draw_options = ProfiledDrawOptions(screen)

553

space.debug_draw(draw_options)

554

555

print(f"Drew {draw_options.shape_count} shapes in {draw_options.draw_time:.3f}s")

556

draw_options.reset_stats()

557

```

558

559

## Integration Examples

560

561

### Complete Pygame Example

562

563

```python { .api }

564

import pygame

565

import pymunk

566

import pymunk.pygame_util

567

import math

568

569

# Initialize Pygame

570

pygame.init()

571

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

572

pygame.display.set_caption("Physics Debug Visualization")

573

clock = pygame.time.Clock()

574

575

# Create physics world

576

space = pymunk.Space()

577

space.gravity = (0, 981) # Downward gravity for Y-down coordinate system

578

579

# Create ground

580

ground_body = space.static_body

581

ground = pymunk.Segment(ground_body, (0, 550), (800, 550), 5)

582

ground.friction = 0.7

583

space.add(ground)

584

585

# Create falling objects

586

def create_ball(x, y):

587

mass = 10

588

radius = 20

589

moment = pymunk.moment_for_circle(mass, 0, radius)

590

body = pymunk.Body(mass, moment)

591

body.position = x, y

592

shape = pymunk.Circle(body, radius)

593

shape.friction = 0.7

594

shape.elasticity = 0.8

595

shape.color = pygame.Color("red") # Custom color

596

space.add(body, shape)

597

598

def create_box(x, y):

599

mass = 15

600

size = (40, 40)

601

moment = pymunk.moment_for_box(mass, size)

602

body = pymunk.Body(mass, moment)

603

body.position = x, y

604

shape = pymunk.Poly.create_box(body, size)

605

shape.friction = 0.7

606

shape.elasticity = 0.3

607

shape.color = pygame.Color("blue")

608

space.add(body, shape)

609

610

# Create debug drawing options

611

draw_options = pymunk.pygame_util.DrawOptions(screen)

612

draw_options.flags = (

613

pymunk.SpaceDebugDrawOptions.DRAW_SHAPES |

614

pymunk.SpaceDebugDrawOptions.DRAW_CONSTRAINTS

615

) # Don't draw collision points for cleaner view

616

617

# Main game loop

618

running = True

619

while running:

620

dt = clock.tick(60) / 1000.0

621

622

for event in pygame.event.get():

623

if event.type == pygame.QUIT:

624

running = False

625

elif event.type == pygame.MOUSEBUTTONDOWN:

626

mouse_pos = pygame.mouse.get_pos()

627

if event.button == 1: # Left click - ball

628

create_ball(*mouse_pos)

629

elif event.button == 3: # Right click - box

630

create_box(*mouse_pos)

631

632

# Step physics

633

space.step(dt)

634

635

# Clear screen

636

screen.fill((255, 255, 255))

637

638

# Draw physics debug visualization

639

space.debug_draw(draw_options)

640

641

# Draw UI

642

font = pygame.font.Font(None, 36)

643

text = font.render("Left click: Ball, Right click: Box", True, (0, 0, 0))

644

screen.blit(text, (10, 10))

645

646

pygame.display.flip()

647

648

pygame.quit()

649

```

650

651

### Multi-View Debugging

652

653

```python { .api }

654

import pygame

655

import pymunk

656

import pymunk.pygame_util

657

658

# Create multiple views for different debug information

659

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

660

661

# Create subsurfaces for different views

662

main_view = screen.subsurface((0, 0, 800, 600))

663

detail_view = screen.subsurface((800, 0, 400, 300))

664

stats_view = screen.subsurface((800, 300, 400, 300))

665

666

# Create different draw options for each view

667

main_draw = pymunk.pygame_util.DrawOptions(main_view)

668

main_draw.flags = pymunk.SpaceDebugDrawOptions.DRAW_SHAPES

669

670

detail_draw = pymunk.pygame_util.DrawOptions(detail_view)

671

detail_draw.flags = (

672

pymunk.SpaceDebugDrawOptions.DRAW_SHAPES |

673

pymunk.SpaceDebugDrawOptions.DRAW_CONSTRAINTS |

674

pymunk.SpaceDebugDrawOptions.DRAW_COLLISION_POINTS

675

)

676

677

# Different zoom levels

678

main_draw.transform = pymunk.Transform.identity()

679

detail_draw.transform = pymunk.Transform.scaling(2.0) # 2x zoom

680

681

# In main loop:

682

main_view.fill((255, 255, 255))

683

detail_view.fill((240, 240, 240))

684

685

space.debug_draw(main_draw)

686

space.debug_draw(detail_draw)

687

688

# Draw stats on stats_view

689

# ... statistics rendering code ...

690

```

691

692

Pymunk's visualization utilities provide powerful debugging capabilities across multiple graphics libraries, enabling rapid prototyping, debugging, and analysis of physics simulations with minimal setup and maximum flexibility.