API reference¶
Runtime facades¶
- class TrackGenerator[source]¶
Bases:
objectTop-level facade: run the pure-Warp track-generation pipeline and return a
Track.Generation, resample, relaxation and inflation are all expressed as NVIDIA Warp kernels (via
_run_pipeline), runnable on the Warpcpuandcudadevices.config.generatormust be a registered generator (seegenerator_registry.available()).The generator is fixed-batch: it always operates on exactly
config.num_envsenvironments. The outputTrackis pre-allocated once in__init__(via_inflate_warp_alloc) and reused across calls:generate()always returns the sameself._trackinstance, writing new results into the same wp.array buffers in place. This ensures stable.ptrpointers so downstream CUDA graph consumers can bake the device addresses once. UseTrack.clone()to obtain an independent snapshot.On a CUDA device the pipeline is auto-captured into a
wp.Graphon the firstgenerate()call and replayed on every subsequent call. On the Warpcpudevice the pipeline runs eagerly.Only
relax_solver="xpbd"andsmooth_finish=Falseare supported; construction raisesAssertionErrorfor other combinations.- __init__(config, rng)[source]¶
- Parameters:
config (TrackGenConfig) – The pipeline configuration.
config.generatormust be a registered generator (seegenerator_registry.available()).rng – A
PerEnvSeededRNGinstance; its per-env seed values seed the pipeline’s built-in Warp RNG (one base seed per env).
- Raises:
ValueError – if
rngisNone.AssertionError – if
config.relax_solveris not"xpbd", or ifconfig.smooth_finishisTrue.
- Return type:
None
- generate(num_or_ids=None)[source]¶
Generate a batch of tracks for the fixed configured batch.
This generator is fixed-batch: it always operates on exactly
config.num_envsenvironments. If integernum_or_idsis provided it is validated against the configured batch size and aValueErroris raised if they disagree (so existing call sites passingE == num_envscontinue to work unchanged). Explicit environment-id sequences are rejected withTypeErrorbecause selected-env execution is not part of the fixed-batch graph-captured contract.On the first CUDA call: warms up kernel loading then captures the pipeline into a
wp.Graph(viawp.ScopedCapture), then immediately replays it. On subsequent CUDA calls: writes new seeds into the seed buffer in place and replays the graph. Oncpu: runs_run()eagerly every call.Writes results into
self._trackin place and returns the SAME instance every call (stable.ptrpointers). UseTrack.clone()to obtain an independent copy.Determinism:
generate()re-copies the rng’s CURRENT seeds each call, so repeated calls with an unchanged rng return the IDENTICAL batch. To vary the batch between calls, reseed the rng first (e.g.rng.set_seeds_warp(new_seeds, None)).- Parameters:
num_or_ids – Optional. Either
Noneor the integer batch size. When an integer is provided, it must equalconfig.num_envs. Explicit environment-id sequences are not supported by this fixed-batch, graph-capturable facade.- Returns:
self._track— the sameTrackinstance every call (stable pointers).- Raises:
TypeError – if
num_or_idsis an explicit environment-id sequence.ValueError – if an integer
num_or_idsdiffers fromconfig.num_envs.
- Return type:
- class GateGenerator[source]¶
Bases:
objectFacade for fixed-batch native gate sequence generation.
Mirrors the fixed-batch contract of
TrackGenerator: construction allocates persistent output and scratch buffers once (viawarp_gate._gate_warp_alloc), andgenerate()refreshes the per-env seed buffer then writes results into the sameGateSequenceinstance on every call (stable.ptrpointers). UseGateSequence.clone()when an independent snapshot is needed.On a CUDA device the gate pipeline is auto-captured into a
wp.Graphon the firstgenerate()call and replayed on subsequent calls. On the Warpcpudevice the pipeline runs eagerly.- __init__(config, rng)[source]¶
- Parameters:
config (GateGenConfig) – The gate generation configuration.
config.generatormust be a registered gate generator;config.gate_orderingmust be supported by that generator; andconfig.max_gatesmust satisfy the generator’s capacity requirements.rng – A
PerEnvSeededRNGinstance with one seed per configured env.
- Raises:
ValueError – if
rngisNone.ValueError – if the number of seeds in
rngdoes not matchconfig.num_envs.ValueError – if
config.gate_orderingis not supported by the selected gate generator.ValueError – if
config.min_gatesexceeds the maximum gate count the generator can produce, or ifconfig.max_gatesis too small for the generator’s required capacity.
- Return type:
None
- generate(num_or_ids=None)[source]¶
Generate gates for the fixed configured batch.
GateGeneratoralways operates on exactlyconfig.num_envsenvironments. Passing an integer is accepted only when it matches that batch size. Explicit environment-id sequences are rejected because selected-env execution is outside the fixed-batch graph-capturable contract.Writes results into
self._gate_sequencein place and returns the SAME instance every call (stable.ptrpointers). UseGateSequence.clone()to obtain an independent snapshot.- Parameters:
num_or_ids – Optional. Either
Noneor the integer batch size. When an integer is provided, it must equalconfig.num_envs. Explicit environment-id sequences are not supported.- Returns:
self._gate_sequence— the sameGateSequenceinstance every call (stable pointers).- Raises:
TypeError – if
num_or_idsis not anint(i.e. an explicit environment-id sequence was passed).ValueError – if an integer
num_or_idsdiffers fromconfig.num_envs.
- Return type:
Seeded RNG¶
- class PerEnvSeededRNG[source]¶
Bases:
objectPer-environment seeded random number generator backed by NVIDIA Warp kernels.
Each of the
num_envsenvironments owns an independent RNG state so that parallel sampling yields reproducible, environment-diverse results. A scalarseedsint is expanded toseed + arange(num_envs)(diversity expansion): because Warp seeds its state purely from the seed value, a flat broadcast would make every environment identical; the offset guarantees each env starts from a distinct stream. Pass awp.arrayofint32to control every env’s seed explicitly.After construction the states are immediately initialised from the seeds, so no separate
set_seeds_warpcall is required before the first sample.- __init__(seeds, num_envs, device)[source]¶
Initialize the random number generator.
- Parameters:
seeds (int | array) – Per-env seeds. An int scalar seeds the whole BATCH: it expands to distinct per-env seeds
seed + arange(num_envs)so the batch is reproducible AND diverse. (Each env’s RNG state iswp.rand_init(seed)with no env-index folding, so identical per-env seeds would yield identical tracks — hence the expansion.) Pass a wp.array of int32 to control every env’s seed explicitly.num_envs (int) – The number of environments.
device (str) – The device to use.
- property seeds_warp: array¶
The
[num_envs]int32Warp array of per-environment base seeds.
- property states_warp: array¶
The
[num_envs]uint32Warp array of live per-environment RNG states.
- set_seeds_warp(seeds, ids)[source]¶
Set the seeds for each environment. :param seeds: The seeds for each environment. :param ids: The ids of the environments.
- Parameters:
seeds (array)
ids (array | None)
- Return type:
None
- sample_uniform_warp(low, high, shape, ids=None)[source]¶
Sample from a uniform distribution. Warp implementation.
If low and high are arrays, their shapes need to match that of the ids.
- sample_sign_warp(dtype, shape, ids=None)[source]¶
Sample a sign. Warp implementation. :param dtype: The data type of the output tensor. :param shape: The shape of the output tensor. :param ids: The ids of the environments.
- sample_integer_warp(low, high, shape, ids=None)[source]¶
Sample for a random integer. Warp implementation. :param low: The lower bound of the distribution. :param high: The upper bound of the distribution. :param shape: The shape of the output tensor. :param ids: The ids of the environments.
- sample_normal_warp(mean, std, shape, ids=None)[source]¶
Sample from a normal distribution. Warp implementation. :param mean: The mean of the distribution. :param std: The standard deviation of the distribution. :param shape: The shape of the output tensor. :param ids: The ids of the environments.
Result types¶
- class Track[source]¶
Bases:
objectFinal batched result of the track generation pipeline.
All boundary arrays are index-aligned:
outer[i],center[i]andinner[i]share a single cross-section normal. Half-width is not stored; recover it as the outer-center norm along dim=-1. Fields arewp.array; convert at the boundary via the wp bridge.Warning
TrackGenerator.generate()returns the SAMETrackinstance on every call and overwrites its buffers in place. A reference held across twogenerate()calls will see mutated data. CallTrack.clone()to obtain a fully-owned deep copy before the next call.- outer¶
Flat
[E * N_max]vec3fouter boundary points. Each real point’szfollows the configuredTrackGenConfig.z_profile— 0 only for the default"flat"profile atz_base=0— and shares thecenterpoint’s altitude at the same index (level cross-sections; see 2.5D tracks). Reshape viawp.to_torch(...).view(E, N_max, 3). Points ati >= count[e]are NaN-padded (NaN xy; z carries the flat path’sz_base, 0 by default).- Type:
wp.array
- center¶
Flat
[E * N_max]vec3fcenterline points.zfollows the configuredTrackGenConfig.z_profile(seeouter). Reshape viawp.to_torch(...).view(E, N_max, 3). Index-aligned withouterandinner;‖outer[i] - center[i]‖gives the per-point half-width (an XY-plane distance: level cross-sections share a Z, so the vertical component cancels). Points ati >= count[e]are NaN-padded.- Type:
wp.array
- inner¶
Flat
[E * N_max]vec3finner boundary points.zfollows the configuredTrackGenConfig.z_profile(seeouter). Reshape viawp.to_torch(...).view(E, N_max, 3). Points ati >= count[e]are NaN-padded.- Type:
wp.array
- tangent¶
Flat
[E * N_max]vec3funit tangent vectors at each centerline point. For the default"flat"profile this is the planar tangent (z = 0) from central differences over the 2D centerline; for a non-flatz_profileit is instead a true 3D central difference over the LIFTED centerline (see 2.5D tracks). NaN-padded pastcount[e].- Type:
wp.array
- normal¶
Flat
[E * N_max]vec3funit left-normals at each centerline point — still the planar left-normal, alwaysz = 0, computed asnormal = (-tangent2d.y, tangent2d.x, 0)(the PLAN-VIEW tangent rotated +90° about +z) BEFORE the elevation lift. For a non-flatz_profilethis predates the 3D recompute described undertangent: the two fields are no longer literally related by that rotation formula (normal’s XY direction still matchestangent’s XY projection, buttangentis unit-length in 3D, so its XY projection alone is sub-unit and rotating it will not reproduce a unitnormal). Which boundary it faces is winding-dependent (seewinding): it points towardouterfor clockwise loops and towardinnerfor counter-clockwise loops. Theouter/innersplit itself is winding-agnostic (assigned by signed-area magnitude), so useouter/inner— not the normal’s sign — to identify the boundaries. NaN-padded pastcount[e].- Type:
wp.array
- arclen¶
Flat
[E * N_max]float32cumulative arc length along the centerline at each point. For the default"flat"profile this is the plan-view (2D) arc length; for a non-flatz_profileit is the true 3D arc length of the LIFTED centerline (strictly >= the plan-view length whenever the profile is non-constant; see 2.5D tracks). Reshape viawp.to_torch(...).view(E, N_max). NaN-padded pastcount[e].- Type:
wp.array
- length¶
[E]float32total arc length (perimeter) of each track’s centerline loop — plan-view for the default"flat"profile, true 3D length otherwise (seearclen).- Type:
wp.array
- valid¶
[E]int32environment validity flags (0 or 1). A track is valid when: turning number ≈ 1, no NaN points, width floor exceeded everywhere, minimum thickness ≥(1 − relax_tol) * half_width, and (whenvalidity_border_check=True) zero border self-intersections. Convert viawp.to_torch(...).bool().- Type:
wp.array
- count¶
[E]int32real point counts, derived from constant-spacing resampling ascount[e] = floor(perimeter / spacing) + 1, capped atN_max. All other arrays are NaN-padded for indicesi >= count[e].- Type:
wp.array
- winding¶
[E]float32signed loop winding:+1.0for a counter-clockwise centerline,-1.0for clockwise (the sign of the loop’s signed area), and0.0for degenerate/empty loops. Winding is generator-dependent (bezier and hull wind clockwise; polar, voronoi and checkpoint wind counter-clockwise), so this field lets a consumer orient itself: for a CCW loopouterlies to the right of increasing arc length, for a CW loop to the left.- Type:
wp.array
- clone()[source]¶
Return a fully-owned deep copy of this Track.
Each field is cloned via
wp.clone(torch-free), so the returned Track owns independent buffers unaffected by futuregenerate()calls that overwrite this instance in place.- Return type:
- __init__(outer, center, inner, tangent, normal, arclen, length, valid, count, winding)¶
- Parameters:
outer (array)
center (array)
inner (array)
tangent (array)
normal (array)
arclen (array)
length (array)
valid (array)
count (array)
winding (array)
- Return type:
None
- classmethod __new__(*args, **kwargs)¶
- class GateSequence[source]¶
Bases:
objectBatched fixed-stride gate result returned by
GateGenerator.Gate pose arrays are flat
[E * max_gates]vec3fbuffers; reshape viawp.to_torch(...).view(E, max_gates, 3).count[e]gives the real gate count for environmenteand slotsi >= count[e]are NaN-padded (any NaN component marks a padded slot). Each real slot’szfollows the configuredGateGenConfig.z_profile; it is 0 only for the default"flat"profile atz_base=0.Warning
GateGenerator.generate()returns the SAMEGateSequenceinstance on every call and overwrites its buffers in place. A reference held across twogenerate()calls will see mutated data. CallGateSequence.clone()to obtain a fully-owned deep copy before the next call.- position¶
Flat
[E * max_gates]vec3fgate centres. Reshape viawp.to_torch(...).view(E, max_gates, 3). Slotsi >= count[e]are NaN-padded.- Type:
wp.array
- tangent¶
Flat
[E * max_gates]vec3funit tangent vectors at each gate centre. NaN-padded pastcount[e].- Type:
wp.array
- forward¶
Flat
[E * max_gates]vec3funit pose forward (x-axis oforientation): the physical gate-plane normal used for pass detection; equalstangentforfull_tangentand for planaryaw_onlygates, the horizontal projection oftangentfor slopedyaw_onlygates; NaN-padded pastcount[e].- Type:
wp.array
- orientation¶
Flat
[E * max_gates]quatfgate poses (wp.quatf(x, y, z, w)): the roll-free frame with local x = gate forward, y = left, z = up. NaN-padded pastcount[e].- Type:
wp.array
- half_size¶
Flat
[E * max_gates]float32square-opening half-extents (0.5 * gate_width). NaN-padded pastcount[e].- Type:
wp.array
- left¶
Flat
[E * max_gates]vec3fleft gate endpoints (position + half_size * left_axis). NaN-padded pastcount[e].- Type:
wp.array
- right¶
Flat
[E * max_gates]vec3fright gate endpoints (position - half_size * left_axis). NaN-padded pastcount[e].- Type:
wp.array
- valid¶
[E]int32environment validity flags (0 or 1). Convert to a boolean tensor viawp.to_torch(...).bool().- Type:
wp.array
- count¶
[E]int32real gate counts per env. All other arrays are NaN-padded for slotsi >= count[e].- Type:
wp.array
- __init__(position, tangent, forward, orientation, half_size, left, right, valid, count)¶
- Parameters:
position (array)
tangent (array)
forward (array)
orientation (array)
half_size (array)
left (array)
right (array)
valid (array)
count (array)
- Return type:
None
- classmethod __new__(*args, **kwargs)¶
Collision queries¶
Box-vs-track out-of-bounds checks with full contact info. See
track_gen.collision for backend trade-offs (exact segments scan vs
baked sdf grids).
The query/instancing utilities on one generated track: track_gen.props
cones (points mode) and wall pieces (segments mode) on both boundaries,
the effect of the spacing knob, and track_gen.collision’s baked SDF
field with boxes classified by the exact segments backend (green inside,
red out of bounds, dotted lines to the nearest boundary point).
Regenerate with python -m viz.render_utility_assets.¶
Public collision-query API: box-vs-track out-of-bounds checks.
CollisionChecker answers, for batches of oriented boxes against a batch of
generated tracks, whether each box has left the drivable band — with full
contact info (BoxContact): OOB flag, signed clearance, nearest
boundary point, inward normal, and boundary id. It has two Warp backends:
method="segments"(default): exact, zero precompute; reads the boundTrackbuffers directly (fresh after everygenerate()).method="sdf": bakes per-env signed-distance grids for O(1) queries; approximate within one grid cell near boundaries and requiresbake()after eachgenerate(). Memory ~E * sdf_resolution**2 * 5bytes.
DiscChecker is a sibling checker in this module, not a third backend of
CollisionChecker: it answers the same oriented-box query against disc
obstacles instead of a track (gate posts, physical cones, point props).
This module is the template for future query utilities: each gets its own
public sibling module (flat namespace, no grab-bag utils).
- class CollisionChecker[source]¶
Bases:
objectBatched box-vs-track out-of-bounds checker bound to a
Track.Because
TrackGenerator.generate()overwrites itsTrackbuffers in place, thesegmentsbackend always reads the CURRENT track batch with no rebind step. Thesdfbackend requiresbake()after eachgenerate()call.query()is allocation-free and, under graph capture (enabled viatrack_gen.set_capturing), host-sync-free (CUDA-graph capturable). In normal (non-capturing) use awp.synchronize()follows the launch, per the codebase idiom.query()returns the same preallocatedBoxContacton every call.- __init__(track, max_boxes, method='segments', sdf_resolution=128, sdf_padding=None, position=None, orientation=None, half_extents=None)[source]¶
Bind to a
Trackbatch and allocate the result buffers.- Parameters:
track (Track) – the bound track batch; the
segmentsbackend reads its buffers directly on everyquery()(no rebind needed aftergenerate()).max_boxes (int) – query stride (boxes per env); must be >= 1.
method (str) –
"segments"(exact, default) or"sdf"(baked, approximate near boundaries; see the module docstring).sdf_resolution (int) –
method="sdf"only — grid side length in texels per env (>= 8; default 128). Ignored for"segments".sdf_padding (float | None) –
method="sdf"only — per-side AABB padding in world units the grid is expanded by.None(default) selects AUTO padding: 10% of the larger AABB extent (max(width, height)) of each env’s track, computed at bake time. Pass a positive float to override with a fixed padding for every env.position (array | None) – optional constructor binding (all-or-none); equivalent to calling
bind_inputs()right after construction.orientation (array | None) – optional constructor binding (all-or-none); equivalent to calling
bind_inputs()right after construction.half_extents (array | None) – optional constructor binding (all-or-none); equivalent to calling
bind_inputs()right after construction.
- Return type:
None
- bake()[source]¶
(Re)bake the per-env SDF grids from the bound Track.
Required after every
TrackGenerator.generate()call whenmethod="sdf"(the segments backend needs no rebake). Pure kernel launches — CUDA-graph capturable.- Raises:
ValueError – if this checker was constructed with
method="segments".- Return type:
None
- bind_inputs(position, orientation, half_extents)[source]¶
Bind stable per-step input buffers (validated once, here).
After binding,
query()takes no arguments and reads these arrays in place each call — the natural CUDA-graph pattern: the sim writes poses into its stable buffers and replays the captured query. The arrays must keep the same.ptrfor the binding’s lifetime.- Parameters:
position (array)
orientation (array)
half_extents (array)
- Return type:
None
- query(position=None, orientation=None, half_extents=None)[source]¶
Compute contact info for
E * max_boxesoriented boxes.Two modes:
Bound mode: after
bind_inputs(), callquery()with no arguments; it reads the bound arrays in place each call (the arrays must keep the same.ptrfor the binding’s lifetime — write new poses into them rather than rebinding, e.g. under CUDA-graph capture).Per-call mode: if not bound, pass
position,orientationandhalf_extentsexplicitly on every call.
- Parameters:
position (array | None) –
[E*max_boxes]vec3fbox centers (the query projects to xy; OOB semantics stay planar). NaN marks an inactive slot (NaN outputs,oob=0,boundary=-1). Required in per-call mode; omit in bound mode.orientation (array | None) –
[E*max_boxes]quatfbox orientations (wp.quatf(x, y, z, w); the query uses the yaw about +z). Required in per-call mode; omit in bound mode.half_extents (array | None) –
[E*max_boxes]vec2fper-box half sizes (planar boxes). Required in per-call mode; omit in bound mode.
- Returns:
The checker’s preallocated
BoxContact(same instance every call; buffers overwritten in place).- Raises:
ValueError – on shape/dtype/device mismatch, or on mode misuse (passing arguments while bound, or omitting them while not bound).
- Return type:
- class BoxContact[source]¶
Bases:
objectBatched box-vs-track contact result, flat
[E * max_boxes]per field.Warning
CollisionChecker.query()returns the SAMEBoxContactinstance on every call and overwrites its buffers in place. Callclone()for a fully-owned snapshot.- oob¶
int32— 1 if the box crosses a boundary or lies outside the band.- Type:
wp.array
- distance¶
float32signed clearance: positive = margin from the box to the nearest boundary; negative = deepest corner penetration when OOB (0.0 when the box only edge-crosses a boundary with all corners inside). NaN for inactive slots. Cf.DiscContact.depth, which reports an UNSIGNED penetration depth (0.0 = no hit) rather than a signed clearance.- Type:
wp.array
- nearest¶
vec2fnearest point on the boundary polylines to the box.- Type:
wp.array
- normal¶
vec2fboundary normal atnearest, pointing INTO the band.- Type:
wp.array
- boundary¶
int32— 0 = inner boundary, 1 = outer boundary, -1 = inactive slot or degenerate track (count < 3).- Type:
wp.array
- __init__(oob, distance, nearest, normal, boundary)¶
- Parameters:
oob (array)
distance (array)
nearest (array)
normal (array)
boundary (array)
- Return type:
None
- classmethod __new__(*args, **kwargs)¶
- class DiscChecker[source]¶
Bases:
objectBatched oriented-box vs disc-obstacle checker (collision family).
See the module docstring.
num_envsis required whencountis not given (a flat disc array alone cannot determine the env split).- __init__(discs, radius, max_boxes, num_envs=None, count=None, position=None, orientation=None, half_extents=None)[source]¶
Bind a flat disc array and allocate the result buffers.
- Parameters:
discs (array) –
[E * D]vec3f disc centers (ALIASED — regenerated buffers are seen automatically). NaN-marked slots are skipped.radius (float) – shared disc radius (> 0); every disc uses the same radius.
max_boxes (int) – query stride (boxes per env); must be >= 1.
num_envs (int | None) – required when
countis not given (a flat disc array alone cannot determine the env split); must matchcount.shape[0]when both are given.count (array | None) – optional
[E]int32 real-disc-count array (aliased, stable user buffer); omit to rely on NaN padding alone.position (array | None) – optional constructor binding (all-or-none); equivalent to calling
bind_inputs()right after construction.orientation (array | None) – optional constructor binding (all-or-none); equivalent to calling
bind_inputs()right after construction.half_extents (array | None) – optional constructor binding (all-or-none); equivalent to calling
bind_inputs()right after construction.
- Return type:
None
- bind_inputs(position, orientation, half_extents)[source]¶
Bind (or rebind) stable per-step input buffers (validated once).
After binding,
query()takes no arguments and reads these arrays in place; same-.ptrrule applies under CUDA-graph capture.- Parameters:
position (array)
orientation (array)
half_extents (array)
- Return type:
None
- query(position=None, orientation=None, half_extents=None)[source]¶
Box-vs-disc contact for
E * max_boxesboxes.Bound mode (inputs bound at construction): call with no arguments. Per-call mode: pass all three arrays; under CUDA-graph capture the SAME arrays must be used at capture and every replay.
- Parameters:
position (array | None)
orientation (array | None)
half_extents (array | None)
- Return type:
- class DiscContact[source]¶
Bases:
objectBatched box-vs-disc result, flat
[E * max_boxes]per field.Warning
DiscChecker.query()returns the SAME instance every call and overwrites its buffers in place;clone()for snapshots.- hit¶
int32— 1 iff any disc touches the box (distance <= radius).- Type:
wp.array
- disc¶
int32— index of the deepest-penetrating disc, -1 when none.- Type:
wp.array
- depth¶
float32— penetration depth of that disc (>= 0; 0 on graze or no hit). Saturates atradiusonce the disc center lies inside the box (ties broken by lowest disc index). Cf.BoxContact.distance, which reports a SIGNED clearance (positive margin / negative penetration) rather than an unsigned depth.- Type:
wp.array
- nearest¶
vec2f— point on that disc’s boundary nearest the box (approximate when the disc center lies deep inside the box). NaN when no hit or the box slot is inactive.- Type:
wp.array
- __init__(hit, disc, depth, nearest)¶
- Parameters:
hit (array)
disc (array)
depth (array)
nearest (array)
- Return type:
None
- classmethod __new__(*args, **kwargs)¶
Performance¶
Measured numbers, backend trade-offs, and the reproduce command live in the collision utility page.
Boundary props¶
Rendering-only instancing poses along track boundaries — cone lines
(mode="points") and wall pieces (mode="segments"). Complementary to
the collision utility: props never collide. Results for invalid envs
(Track.valid == 0) are undefined — always gate on valid.
Public boundary prop-sampling API: instancing poses along track boundaries.
PropSampler resamples the inner or outer boundary at a set spacing into a
PropSet of per-prop poses (position, tangent, yaw, length) for
rendering-only instancing — cone lines (mode="points") or wall pieces
(mode="segments"). The complement of track_gen.collision: these props
never collide; use CollisionChecker for out-of-bounds queries.
- class PropSampler[source]¶
Bases:
objectResample one track boundary into instancing poses at a set spacing.
One sampler binds one
Track, one boundary curve, one mode, and one spacing, so all output shapes are fixed andsample()is CUDA-graph capturable (allocation-free; host-sync-free while capturing is enabled viatrack_gen.set_capturing). BecauseTrackGenerator.generate()overwrites itsTrackbuffers in place, everysample()reads the CURRENT batch — no rebind after regeneration. Props are rendering-only; they are not colliders. Results for envs withvalid[e] == 0are undefined — an invalid track can still have >= 3 boundary points and will yield well-formed-looking props with no NaN signal; callers must gate onTrack.valid, as everywhere in the library.PropSet.truncated/PropSet.steplive on the RESULT becausePropSampleris the ONLY producer of aPropSet— unlikeCheckpointSet, which is also produced zero-copy byCheckpointSet.from_gates(no sampling, so no truncation/step to report);track_gen.checkpoints.CheckpointSamplerkeeps its analogous diagnostics on the sampler instead, to avoid meaningless fields on gate-derived checkpoint sets.- __init__(track, spacing, boundary='outer', mode='points', max_props=None)[source]¶
Bind to a
Trackboundary and allocate the result buffers.- Parameters:
track (Track) – the bound track batch;
sample()reads its buffers directly on every call (no rebind needed aftergenerate()).spacing (float) – target arc-length spacing between props (> 0); snapped per env so each ring closes without a seam (see module docstring).
boundary (str) –
"inner"or"outer"(default) — which track boundary curve to resample.mode (str) –
"points"(default, one pose per sample) or"segments"(one pose per chord between samples).max_props (int | None) – buffer capacity per env;
None(default) derives it from the CURRENT batch (see_derive_max_props()). Must be >= 3 when given explicitly.
- Return type:
None
- class PropSet[source]¶
Bases:
objectBatched prop poses, flat
[E * max_props]per pose field.Warning
PropSampler.sample()returns the SAMEPropSetinstance on every call and overwrites its buffers in place. Callclone()for a fully-owned snapshot.- position¶
vec3fpose positions on the (possibly lifted) boundary: on-curve sample (points mode) or chord midpoint (segments mode); z tracks the boundary elevation (0 on flat tracks). NaN pastcount[e].- Type:
wp.array
- tangent¶
vec2funit directions: curve tangent at the sample (points) or chord direction (segments). NaN pastcount[e].- Type:
wp.array
- yaw¶
float32heading,atan2(tangent.y, tangent.x).- Type:
wp.array
- length¶
float32per-prop extent: effective arc step (points mode) or chord length (segments mode). Both are measured along the (possibly lifted) boundary polyline: on a flat track this is a planar distance, and on a non-flatz_profiletrack it is the true 3D distance (the 3D chord, in segments mode) between samples that may sit at different altitudes.- Type:
wp.array
- count¶
[E]int32real prop counts (0 for degenerate envs). Meaningful only for envs withvalid[e] == 1.- Type:
wp.array
- truncated¶
[E]int32— 1 ifmax_propsclipped this env’s ring (the ring still closes, at a coarser effective spacing).- Type:
wp.array
- step¶
[E]float32effective arc spacingperimeter / count.- Type:
wp.array
- __init__(position, tangent, yaw, length, count, truncated, step)¶
- Parameters:
position (array)
tangent (array)
yaw (array)
length (array)
count (array)
truncated (array)
step (array)
- Return type:
None
- classmethod __new__(*args, **kwargs)¶
Checkpoints¶
Ordered course goals from gates (zero-copy) or subsampled track centerlines.
Public checkpoint API: ordered course goals from gates or tracks.
CheckpointSet is the shared contract consumed by
track_gen.progress.ProgressTracker. Build one zero-copy from a
GateSequence (CheckpointSet.from_gates) or by subsampling a track’s
centerline at a coarse spacing (CheckpointSampler — each checkpoint’s
crossing segment is the road cross-section, a “virtual gate”).
- class CheckpointSampler[source]¶
Bases:
objectResample a track’s centerline into coarse course checkpoints.
One sampler binds one
Trackand one spacing;sample()refreshes the ownedCheckpointSetin place from the CURRENT track batch (no rebind aftergenerate(); two kernel launches, allocation-free, CUDA-graph capturable via the shared capture flag —track_gen.set_capturing). Spacing is expected to be coarse — checkpoint counts similar to gate counts; nothing enforces this. Progress state bound to the sampled set must be reset after resampling a regenerated track.Producer diagnostics live on the sampler (not the set):
truncated([E]int32, 1 ifmax_checkpointsclipped the ring) andstep([E]float32 effective arc spacing). They live here rather than onCheckpointSetbecause the set has a SECOND, sampling-free producer (CheckpointSet.from_gates) that has no truncation or step concept — keeping the diagnostics on the sampler avoids meaningless fields on gate-derived sets (contrasttrack_gen.props.PropSet, whose only producer isPropSampler, so its diagnostics live on the result).- __init__(track, spacing, max_checkpoints=None)[source]¶
Bind to a
Trackcenterline and allocate the result buffers.- Parameters:
track (Track) – the bound track batch;
sample()reads its centerline buffer directly on every call (no rebind needed aftergenerate()).spacing (float) – target arc-length spacing between checkpoints (> 0); snapped per env so each ring closes without a seam (same snap rule as
track_gen.props).max_checkpoints (int | None) – buffer capacity per env;
None(default) derives it from the CURRENT batch (see_derive_max_checkpoints()). Must be >= 3 when given explicitly.
- Return type:
None
- class CheckpointSet[source]¶
Bases:
objectOrdered per-env checkpoints, flat
[E * M]per field.The consumer contract for
track_gen.progress.ProgressTracker. Sets produced byfrom_gates()ALIAS the gate buffers (mutations to the gates are visible here); sets produced byCheckpointSamplerare owned by the sampler and refreshed in place bysample().- position¶
vec3fcheckpoint centers. NaN pastcount[e].- Type:
wp.array
- left¶
vec3fcrossing-segment endpoints (gate left / track inner).- Type:
wp.array
- right¶
vec3fcrossing-segment endpoints (gate right / track outer).- Type:
wp.array
- tangent¶
vec3funit forward (travel) directions. For gate-derived sets this is the gate’sforward(the physical gate-plane normal), not the 3D spline tangent.- Type:
wp.array
- up_half¶
[E * M]float32vertical half-openings for the crossing test:_BIG(unbounded) for track cross-sections, and the gate’shalf_sizefor gate-derived sets (aliasesGateSequence.half_size). NaN pastcount[e].- Type:
wp.array
- count¶
[E]int32real checkpoint counts. Meaningful only for envs withvalid[e] == 1on the source batch.- Type:
wp.array
- classmethod from_gates(seq)[source]¶
Zero-copy view of a
GateSequenceas checkpoints.Aliases (does not copy)
position,left,right,forward(astangent: the physical gate-plane normal),half_size(asup_half), andcount. Progress state bound to this set must be reset after the gates are regenerated.- Parameters:
seq (GateSequence)
- Return type:
- __init__(position, left, right, tangent, up_half, count)¶
- Parameters:
position (array)
left (array)
right (array)
tangent (array)
up_half (array)
count (array)
- Return type:
None
- classmethod __new__(*args, **kwargs)¶
Progress tracking¶
Stateful per-env course progress over any CheckpointSet.
Public progress-tracking API: ordered course progress + reward signals.
ProgressTracker consumes any CheckpointSet (gate sequences via
CheckpointSet.from_gates, or subsampled track centerlines via
CheckpointSampler) and emits per-step ProgressEvents: pass events,
laps, wrong-way / wrong-checkpoint flags, and dist_to_next — difference
it across steps for the classic negative-delta-distance reward.
- class ProgressTracker[source]¶
Bases:
objectTrack ordered progress of one agent per env through a CheckpointSet.
See the module docstring for semantics. Construct with
position=(a stable[E]vec3f wp.array owned by your sim) for bound mode —update()then takes no arguments and reads the buffer in place; or leave unbound and pass positions per call. Mixing modes raisesValueError.- __init__(checkpoints, position=None)[source]¶
Bind to a
CheckpointSetand allocate the tracker’s state.- Parameters:
checkpoints (CheckpointSet) – the ordered per-env checkpoint set to track against (from
CheckpointSet.from_gatesorCheckpointSampler.sample()). Aliased, not copied — later mutation (e.g. a resampled or regenerated set) is seen on the nextupdate().position (array | None) – optional stable
[E]vec3f buffer to bind at construction (bound mode); equivalent to callingbind()right after construction.
- Return type:
None
- property next_checkpoint_state: array¶
Aliased read-only view of the
[E]int32 current-target state.Exposes the tracker’s live
_nextbuffer (updated in place by everyupdate()) for consumers that window their queries around the current target checkpoint (e.g.FrameChecker.bind_window). Do NOT mutate — the tracker owns this state.
- bind(position)[source]¶
Bind (or rebind) a stable
[E]vec3f position buffer.After binding,
update()takes no arguments and reads the buffer in place. Validation happens here, once; the array must keep the same.ptrfor the binding’s lifetime (CUDA-graph contract).- Parameters:
position (array)
- Return type:
None
- update(position=None)[source]¶
Advance one step; returns the tracker’s preallocated events.
Bound mode (constructed with
position=): call with no arguments. Per-call mode: pass the[E]vec3f position array — the SAME array (identical.ptr) must be used across a CUDA-graph capture and its replays.NaN-position semantics: a NaN
position[e]PAUSES envefor this step rather than erroring — no crossing/wrong-way/wrong-checkpoint event can fire (the crossing test always misses against a NaN endpoint),dist_to_next[e]is NaN, and the NaN gets written intoprev_pos[e], which re-arms the same sentinelreset()uses: the FIRST update after a finite position resumes also reports no event (it only re-primesprev_pos), and normal crossing detection resumes the update after that. Useful for envs mid-teleport or awaiting a fresh spawn without a fullreset().- Parameters:
position (array | None)
- Return type:
- class ProgressEvents[source]¶
Bases:
objectPer-env progress events, all
[E]; overwritten in place per update.Warning
ProgressTracker.update()returns the SAME instance every call.clone()for snapshots.- passed¶
int32— 1 iff the target checkpoint was crossed forward this step.- Type:
wp.array
- checkpoint_passed¶
int32— index of the checkpoint passed this step, -1 otherwise.- Type:
wp.array
- next_checkpoint¶
int32— current target AFTER any advance this step.- Type:
wp.array
- laps¶
int32— completed laps.- Type:
wp.array
- progress¶
int32— total checkpoints passed since construction/reset.- Type:
wp.array
- wrong_way¶
int32— 1 iff the target was crossed BACKWARD this step.- Type:
wp.array
- wrong_checkpoint¶
int32— index of a non-target checkpoint crossed this step (either direction; first in index order), -1 otherwise.- Type:
wp.array
- dist_to_next¶
float32—|position - next checkpoint center|after any advance. NaN for envs with no checkpoints.- Type:
wp.array
- __init__(passed, checkpoint_passed, next_checkpoint, laps, progress, wrong_way, wrong_checkpoint, dist_to_next)¶
- Parameters:
passed (array)
checkpoint_passed (array)
next_checkpoint (array)
laps (array)
progress (array)
wrong_way (array)
wrong_checkpoint (array)
dist_to_next (array)
- Return type:
None
- classmethod __new__(*args, **kwargs)¶
Track-frame localization¶
Per-env (s, n) centerline projection with an optional warm start, plus the per-generation curvature and speed-profile helpers.
Public track-frame localization API: (s, n) projection + speed hints.
TrackLocalizer projects one point per env onto the bound Track’s
centerline and returns a TrackFrame: arc length s, signed
lateral offset n (positive to the RIGHT of the direction of travel;
which boundary that is depends on the loop’s generator-dependent winding),
and the nearest segment index — the Frenet-style frame
racing controllers and reward shapers consume every sim step. An optional
warm start (warm_window=) narrows each scan to a window around the
previous result; reset(mask) drops that memory after regenerations and
teleports.
curvature() and speed_profile() are per-generation companions:
per-point signed centerline curvature and the curvature-limited target
speed (steady-state lateral limit, then forward-acceleration and
backward-braking passes over the closed loop).
- class TrackLocalizer[source]¶
Bases:
objectProject one point per env onto the bound Track’s centerline.
Because
TrackGenerator.generate()overwrites itsTrackbuffers in place,query()always reads the CURRENT track batch — but a warm-started localizer remembers segment indices of the OLD geometry, so callers MUSTreset()all envs after regenerating (cold-scan localizers,warm_window=None, need nothing).query()is allocation-free and, under graph capture (enabled viatrack_gen.set_capturing), host-sync-free (CUDA-graph capturable). In normal (non-capturing) use awp.synchronize()follows the launch, per the codebase idiom.query()returns the same preallocatedTrackFrameon every call.Warm vs cold results are IDENTICAL whenever the nearest centerline segment stays within
warm_windowsegments of the previous query’s result — the usual regime (per-step motion small againstwarm_window * spacing). Small motion alone does NOT guarantee it: where the loop pinches close to itself, the nearest segment can jump half a lap for arbitrarily small motion, and the warm result then stays on the traveled branch (often preferable for racing consumers — s stays continuous — but not the cold answer). Outside the contract (teleports, huge steps), a warm query may likewise lock onto a local minimum; callreset()on the affected envs to force a full rescan.- __init__(track, warm_window=None, position=None)[source]¶
Bind to a
Trackbatch and allocate the result buffers.- Parameters:
track (Track) – the bound track batch;
query()reads its centerline buffers directly on every call.warm_window (int | None) –
None(default) scans all segments every query; an int >= 1 scans only the2 * warm_window + 1segments centered on the previous result (per env; the first query after construction orreset()is always a full scan).position (array | None) – optional stable
[E]vec3f buffer to bind at construction (bound mode); equivalent to callingbind()right after construction.
- Return type:
None
- bind(position)[source]¶
Bind (or rebind) a stable
[E]vec3f position buffer.After binding,
query()takes no arguments and reads the buffer in place. Validation happens here, once; the array must keep the same.ptrfor the binding’s lifetime (CUDA-graph contract).- Parameters:
position (array)
- Return type:
None
- query(position=None)[source]¶
Localize the batch; returns the localizer’s preallocated frame.
Bound mode (constructed with
position=or afterbind()): call with no arguments. Per-call mode: pass the[E]vec3f position array — the SAME array (identical.ptr) must be used across a CUDA-graph capture and its replays.NaN
position[e]yields NaNs/nandsegment = -1for that env, and drops its warm-start memory (the next finite query does a full scan) — safe for envs mid-teleport or awaiting respawn.- Returns:
The localizer’s preallocated
TrackFrame(same instance every call; buffers overwritten in place).- Raises:
ValueError – on shape/dtype/device mismatch, or on mode misuse (passing a position while bound, or omitting it while not bound).
- Parameters:
position (array | None)
- Return type:
- reset(mask)[source]¶
Drop warm-start memory where
mask[e]is nonzero ([E]int32).The next query for those envs performs a full centerline scan. Required for ALL envs after regenerating the bound track when
warm_windowis set, and for any env that teleports further than the warm window covers. A no-op for cold-scan localizers (but valid to call, so regeneration code need not special-case).- Parameters:
mask (array)
- Return type:
None
- class TrackFrame[source]¶
Bases:
objectPer-env track-frame coordinates, all
[E]; overwritten per query.Warning
TrackLocalizer.query()returns the SAME instance every call.clone()for snapshots.- s¶
float32— arc length along the centerline at the projected point, in[0, Track.length[e]), same units as the track coordinates. NaN for NaN positions and degenerate tracks (count < 3).- Type:
wp.array
- n¶
float32— signed lateral offset from the centerline: positive to the RIGHT of the centerline’s direction of travel (the direction of increasings), negative to the left. Whether the right side is the outer or the inner boundary depends on the loop’s winding, which is generator-dependent (CCW loops haveouteron the right, CW loops on the left);|n| <= half_widthmeans on the road either way. NaN for NaN positions and degenerate tracks.- Type:
wp.array
- n_up¶
float32— signed vertical offset in the roll-free frame at the foot point; equalsposition.zfor planar tracks. NaN for NaN positions and degenerate tracks.- Type:
wp.array
- segment¶
int32— indexiof the nearest centerline segment (from pointitoi + 1, the closing segment beingcount[e] - 1 -> 0). -1 for NaN positions and degenerate tracks.- Type:
wp.array
- __init__(s, n, n_up, segment)¶
- Parameters:
s (array)
n (array)
n_up (array)
segment (array)
- Return type:
None
- classmethod __new__(*args, **kwargs)¶
- curvature(track, window=2)[source]¶
Per-point signed centerline curvature, flat
[E * N_max]float32.Discrete curvature at point
i: the turn angle between the incoming and outgoing centerline edges over the mean incident edge length,atan2(cross(v1, v2), dot(v1, v2)) / max(0.5 * (|v1| + |v2|), eps), then a wrap-aware moving average over2 * window + 1points. Units are 1 / track units. The sign follows the loop orientation: positive where the centerline turns counter-clockwise — a CCW circle of radiusRgiveskappa ~ +1/Reverywhere. NaN pastcount[e]and for degenerate envs (count[e] < 3); results are undefined for envs withvalid[e] == 0.On a flat track (the default
z_profile)v1/v2are planar edge vectors and this is exactly the familiar 2D signed curvature. On a lifted track (a non-flatz_profile),centercarries real per-point altitude, sov1/v2are full 3D edge vectors, and the formula picks up grade in two ways: the numerator (cross(v1, v2)[2]) is still exactly the PLAN-VIEW cross product (unaffected by z on its own), but the denominator — bothdot(v1, v2)inside theatan2and thedsnormalizer — gains a z contribution. Net effect: a genuine plan-view turn taken on a graded section reports a slightly SMALLER magnitude than the same turn taken flat (the added 3D edge length dilutes it); and a perfectly straight-in-plan-view section with a steep enough grade reversal (a crest or dip sharp enough to flip the sign ofdot(v1, v2)) can register as if it were a sharp turn even though the centerline never bends in plan view. Mild grade changes (no sign flip in the dot product) still read as zero curvature, same as flat.Unlike the per-step utilities, this ALLOCATES its result (and a scratch when
window > 0) — a per-generation helper: call it aftergenerate(), outside capture regions.- Parameters:
- Returns:
A NEW flat
[E * N_max]float32 wp.array of signed curvatures on the track’s device (owned by the caller; not refreshed by latergenerate()calls).- Raises:
ValueError – on
window < 0or an invalid track batch layout.- Return type:
array
- speed_profile(track, a_lat_max, a_accel, a_brake, v_cap, kappa=None, window=2)[source]¶
Curvature-limited target speed per centerline point,
[E * N_max].Three stages, batched per env (one Warp thread per env; the passes are sequential by nature): the steady-state limit
min(sqrt(a_lat_max / |kappa|), v_cap), a forward pass capping acceleration out of corners (v[i+1]^2 <= v[i]^2 + 2 * a_accel * ds), and a backward pass capping braking into them (v[i]^2 <= v[i+1]^2 + 2 * a_brake * ds) — each run twice around the closed loop so the wrap converges. Speeds are in track units per second when the accelerations are in track units per second squared.ds(fromTrack.arclen/Track.length) is the true 3D segment length on a lifted (non-flatz_profile) track, so the accel/brake ramps size their distance budget off the actual climbing/descending path length, not its plan-view projection. The corner-speed limit still comes fromkappa(curvature()’s default, if not passed in), which on a lifted track can be perturbed by grade as documented there — this function adds no further elevation-specific handling of its own.Like
curvature(), this ALLOCATES its result — a per-generation helper: call it aftergenerate(), outside capture regions.- Parameters:
track (Track) – the track batch; segment lengths come from
Track.arclenandTrack.length.a_lat_max (float) – maximum lateral acceleration (> 0).
a_accel (float) – maximum forward acceleration (>= 0; 0 pins the profile to each point’s corner speed going forward).
a_brake (float) – maximum braking deceleration (>= 0).
v_cap (float) – straight-line speed cap (> 0).
kappa (array | None) – optional precomputed flat
[E * N_max]float32 curvature (e.g. fromcurvature(), possibly reused across several profiles).None(default) computes it internally.window (int) – smoothing half-width forwarded to
curvature()whenkappais None; ignored otherwise.
- Returns:
A NEW flat
[E * N_max]float32 wp.array of target speeds on the track’s device, NaN pastcount[e]and for degenerate envs (count[e] < 3). Undefined for envs withvalid[e] == 0.- Raises:
ValueError – on a non-positive
a_lat_max/v_cap, a negativea_accel/a_brake, akappashape/dtype/device mismatch, or an invalid track batch layout.- Return type:
array
Course facade¶
One object bundling generation, collision, and progress per mode; see the Course facade page for the lifecycle.
Public course facade: generation + collision + progress in one object.
See Course for the lifecycle (bind -> generate -> step/reset) and
set_capturing for one-switch CUDA-graph capture of the step path.
- class CourseConfig[source]¶
Bases:
objectConfiguration for
Course. Strict option applicability: inapplicable options raise instead of being silently ignored.- gen¶
Generator config; its type must match
mode.num_envsanddeviceare taken from here. Gates mode requiresgen.gate_width > 0(a width-0 gate can never be crossed).- Type:
- collision¶
Track mode only:
"segments","sdf", orNone(no out-of-bounds checking — progress-only bundles are legal).- Type:
str or None
- sdf_resolution¶
Track mode with
collision="sdf"only;None-> 128. The facade always usesCollisionChecker’s AUTO SDF padding (sdf_padding=None, i.e. 10% of each env’s larger AABB extent);sdf_paddingitself is not exposed here.- Type:
int or None
- heightfield_resolution¶
Track mode only; when set (
>= 8), builds aHeightFieldBakerexposed ascourse.heightfieldand re-baked on everygenerate()(nearest-edge road-surface grid for external physics solvers). Uses the baker’s AUTO padding (10% of each env’s larger AABB extent).None(default) means no heightfield.- Type:
int or None
- frame_collision¶
Gates mode only:
TrueenablesFrameCheckersphere-vs-gate-frame collision (each square gate = four thin oriented frame members). Mutually exclusive withpost_radius > 0(choose discs OR frames); requiresframe_thickness,frame_depthandagent_radiusall > 0.- Type:
- frame_thickness¶
Gates mode with
frame_collisiononly (required, > 0): thickness of the frame members (their extent across the opening plane).- Type:
- frame_depth¶
Gates mode with
frame_collisiononly (required, > 0): depth of the frame members along the gate-forward axis.- Type:
- agent_radius¶
Gates mode with
frame_collisiononly (required, > 0): the agent sphere radius for frame collision (posts are exclusive, so this cannot ridepost_radius).- Type:
- checkpoint_spacing¶
Track mode only (required there): centerline checkpoint spacing.
- Type:
float or None
- samples_per_gate¶
Gates mode only: centerline samples per gate for the
CourseLinespline (>= 2). Default 8.- Type:
- localize_window¶
Gates mode only: forwarded as
TrackLocalizer(warm_window=...);None(default) means full cold scans every query.- Type:
int or None
- __init__(mode, gen, seeds=0, collision=None, sdf_resolution=None, heightfield_resolution=None, post_radius=0.0, frame_collision=False, frame_thickness=0.0, frame_depth=0.0, agent_radius=0.0, checkpoint_spacing=None, max_checkpoints=None, max_boxes=1, samples_per_gate=8, localize_window=None)¶
- Parameters:
mode (str)
gen (TrackGenConfig | GateGenConfig)
seeds (int | array)
collision (str | None)
sdf_resolution (int | None)
heightfield_resolution (int | None)
post_radius (float)
frame_collision (bool)
frame_thickness (float)
frame_depth (float)
agent_radius (float)
checkpoint_spacing (float | None)
max_checkpoints (int | None)
max_boxes (int)
samples_per_gate (int)
localize_window (int | None)
- Return type:
None
- classmethod __new__(*args, **kwargs)¶
- class Course[source]¶
Bases:
objectOne object bundling generation, collision, and progress per mode.
Lifecycle: construct ->
bind()(stable sim buffers; required forstep()) ->generate()(whole batch) -> per-stepstep()/ per-envreset(). Sub-tools are constructed after the FIRSTgenerate()(their auto-derivations need a real batch) and are reachable as attributes (generator,rng,result,collision,checkpoints,checkpoint_sampler,progress, an optional track-modeheightfield(whenheightfield_resolutionis set), and — gates mode —course_line,localizer).generate()is whole-batch by generator design (fixed-batch captured pipelines); per-env respawn control isreset()’s mask. Results are undefined for envs withvalid[e] == 0onresult.- __init__(config)[source]¶
- Parameters:
config (CourseConfig)
- Return type:
None
- bind(position, orientation=None, half_extents=None, box_position=None)[source]¶
Bind stable sim buffers (required before
step()).positionis the[E]vec3f agent-position buffer driving progress. When a box-collision checker is enabled,orientation([E * max_boxes]quatf box poses) andhalf_extents([E * max_boxes]vec2f, planar boxes) are required too; withmax_boxes == 1the samepositionbuffer serves as the box positions, otherwise pass a separatebox_position[E * max_boxes]vec3f buffer. May be called before or after the firstgenerate(); rebinding replaces the previous binding.Do NOT rebind after capturing
step()into a sim graph: the captured graph replays against the buffer pointers live at capture time, so a later rebind leaves it reading the old buffers (silently divergent results) — keep writing into the SAME bound buffers and rebind only before (re)capturing.- Parameters:
position (array)
orientation (array | None)
half_extents (array | None)
box_position (array | None)
- Return type:
None
- generate(seeds=None)[source]¶
Whole-batch (re)generation plus a coherent downstream refresh.
Optional reseed, generator pipeline (its own captured graph on cuda), then: checkpoint resample / sdf bake / posts rebuild as applicable, and a FULL progress reset (every course changed). On cuda the refresh is captured once into a facade-owned graph and replayed on later calls. Returns
result.Determinism contract: the generators are deterministic under an unchanged RNG. Calling
generate()again WITHOUTseeds=reproduces the identical batch (plus a full progress reset); passseeds=to advance the RNG and get new courses.In track mode, also check
course.checkpoint_sampler.truncatedaftergenerate(): a per-env[E]int32 flag (1 ifmax_checkpointsclipped that env’s checkpoint ring), mirroringPropSet.truncatedfor prop rings.- Parameters:
seeds (int | array | None)
- reset(mask)[source]¶
Per-env respawn on the SAME course: clears progress state (and, in gates mode, the localizer’s warm-start memory) where
mask[e] == 1. Collision and checkpoints derive from the course geometry and are unaffected by respawns.- Parameters:
mask (array)
- Return type:
None
- static set_capturing(flag)¶
Toggle the family-wide capture flag.
While
True, no utility (collision, discs, props, checkpoints, progress, course) performs a host synchronize, soquery()/update()/sample()/bake()are safe to record into a CUDA graph. The flag is PROCESS-WIDE: exactly one capturing sim loop at a time is supported by design. Restore it (set_capturing(False)or to the saved previous value) after the capture region.- Parameters:
flag (bool)
- Return type:
None
- class StepResult[source]¶
Bases:
objectPer-step bundle returned by
Course.step().Holds the sub-tools’ preallocated in-place result instances — the SAME
StepResult(and the same underlying buffers) is returned on everystep(); useclone()for snapshots.- events¶
Progress events for this step.
- Type:
- contacts¶
Collision result (
Nonewhen the course has no collision checker).- Type:
BoxContact or DiscContact or FrameContact or None
- frame¶
Localization frame on the gates-mode
CourseLine(Nonein track mode).- Type:
TrackFrame or None
- __init__(events, contacts, frame)¶
- Parameters:
events (ProgressEvents)
contacts (BoxContact | DiscContact | FrameContact | None)
frame (TrackFrame | None)
- Return type:
None
- classmethod __new__(*args, **kwargs)¶
- set_capturing(flag)[source]¶
Toggle the family-wide capture flag.
While
True, no utility (collision, discs, props, checkpoints, progress, course) performs a host synchronize, soquery()/update()/sample()/bake()are safe to record into a CUDA graph. The flag is PROCESS-WIDE: exactly one capturing sim loop at a time is supported by design. Restore it (set_capturing(False)or to the saved previous value) after the capture region.- Parameters:
flag (bool)
- Return type:
None