Quickstart

Track generation

The following minimal example generates a batch of 64 closed-loop race tracks on CUDA.

import warp as wp
wp.init()

from track_gen import TrackGenerator, TrackGenConfig, PerEnvSeededRNG

E, device = 64, "cuda"  # or "cpu"
config = TrackGenConfig(num_envs=E, half_width=0.03, device=device)
rng = PerEnvSeededRNG(seeds=0, num_envs=E, device=device)

generator = TrackGenerator(config, rng)
track = generator.generate()  # fixed batch: config.num_envs tracks

center = wp.to_torch(track.center).view(E, config.N_max, 3)
outer = wp.to_torch(track.outer).view(E, config.N_max, 3)
inner = wp.to_torch(track.inner).view(E, config.N_max, 3)
valid = wp.to_torch(track.valid).bool()
count = wp.to_torch(track.count)

center[0, : int(count[0])]  # real arc-uniform centerline points for env 0

TrackGenerator is fixed-batch. Omit generate()’s argument, or pass the integer E == config.num_envs; explicit environment-id sequences are rejected because the CUDA graph captures one fixed batch shape. Results are read via wp.to_torch, which provides a zero-copy view of the underlying Warp array as a PyTorch tensor. The same Track instance and Warp buffers are reused on every call, so use track.clone() when you need an independent snapshot.

Gate sequence generation

For drone-style courses where track width is irrelevant, use GateGenerator. It emits gate centres and orientations directly from native centerline-generator anchors and skips constant-spacing, XPBD relaxation, and inflation.

import warp as wp
wp.init()

from track_gen import GateGenConfig, GateGenerator, PerEnvSeededRNG

E, device = 64, "cuda"
config = GateGenConfig(
    generator="bezier",
    gate_ordering="random_pairs",
    num_envs=E,
    max_gates=32,
    gate_width=0.4,
    gate_radius=0.025,
    gate_solve_iters=8,
    device=device,
)
rng = PerEnvSeededRNG(seeds=0, num_envs=E, device=device)

gates = GateGenerator(config, rng).generate()
position = wp.to_torch(gates.position).view(E, config.max_gates, 3)
tangent = wp.to_torch(gates.tangent).view(E, config.max_gates, 3)
valid = wp.to_torch(gates.valid).bool()

GateGenerator follows the same fixed-batch contract as TrackGenerator: the output batch size is always config.num_envs. Results are read via wp.to_torch for zero-copy access as PyTorch tensors. The same GateSequence instance and its Warp buffers are reused on every generate() call; use gates.clone() when you need an independent snapshot.

The GateSequence fields are:

Field

Shape

Meaning

position

[E, G, 2]

gate centers (G = max_gates)

tangent, normal

[E, G, 2]

unit tangent and left-normal at each gate

left, right

[E, G, 2]

gate endpoints (center ± 0.5 * gate_width * normal)

valid

[E] bool

per-sequence validity

count

[E] int

real gates per env; slots i >= count[e] are NaN padding