# RoadEdge

> Talos next · Since 0.6

The road's edges at one distance: what {@link RoadLine.edge} returns.

Every number is metres from the centre line, always positive (the left edge is the mirror of the right).

## Signature

```ts
export interface RoadEdge {
  /** Edge of the driving surface: `at(s).halfWidth`. */
  readonly halfWidth: number
  /** Outer edge of the kerb (`halfWidth` when the road has no kerb). */
  readonly kerbOuter: number
  /** The wall here, or null when the road has none or `s` is in a `gapsAt` range. A car's centre stays inside `inner - radius`. */
  readonly barrier: {
    /** The wall's inner face, the side facing the road. */
    readonly inner: number
    /** The wall's outer face. */
    readonly outer: number
    /** Height above the road surface. */
    readonly height: number
    /** Whether the left side has a wall here. */
    readonly left: boolean
    /** Whether the right side has a wall here. */
    readonly right: boolean
  } | null
}
```

Import: `import type { RoadEdge } from "@argus/api"`

## Members

| Member | Signature | Description |
|---|---|---|
| `halfWidth` | `readonly halfWidth: number` | Edge of the driving surface: `at(s).halfWidth`. |
| `kerbOuter` | `readonly kerbOuter: number` | Outer edge of the kerb (`halfWidth` when the road has no kerb). |
| `barrier` | `readonly barrier: {` | The wall here, or null when the road has none or `s` is in a `gapsAt` range. A car's centre stays inside `inner - radius`. |
| `inner` | `readonly inner: number` | The wall's inner face, the side facing the road. |
| `outer` | `readonly outer: number` | The wall's outer face. |
| `height` | `readonly height: number` | Height above the road surface. |
| `left` | `readonly left: boolean` | Whether the left side has a wall here. |
| `right` | `readonly right: boolean` | Whether the right side has a wall here. |

## Example

The tested fixture `fixtures/recipes/jump-pad-and-elevator/scenes/behaviors/elevator.ts`.

```ts
import type { Behavior } from "@argus/api"

/**
 * A lift: a kinematic slab that rides between two heights and waits at each end.
 *
 * It only moves itself, by `position`. Whoever stands on it is carried by the engine — the character
 * controller follows moving ground on its own — so there is no rider list and no parenting here.
 */
interface State {
  /** +1 going up, -1 going down, 0 waiting. */
  heading: number
  /** Seconds left to wait at the current end. */
  wait: number
  /** Where the next leg goes once the wait is over. */
  next: number
}

interface Props {
  /** Height of the slab's origin at the bottom and the top, metres. */
  bottom?: number
  top?: number
  /** Metres per second. Default 1.5. */
  speed?: number
  /** Seconds to wait at each end. Default 1. */
  pause?: number
}

export default {
  init(ctx) {
    if (typeof ctx.state.heading !== "number") ctx.state = { heading: 0, wait: ctx.props.pause ?? 1, next: 1 }
  },

  update(ctx, dt) {
    const bottom = ctx.props.bottom ?? 0
    const top = ctx.props.top ?? 4
    const p = ctx.entity.position
    if (ctx.state.heading === 0) {
      ctx.state.wait -= dt
      if (ctx.state.wait <= 0) ctx.state.heading = ctx.state.next
      return
    }
    let y = p[1] + ctx.state.heading * (ctx.props.speed ?? 1.5) * dt
    const end = ctx.state.heading > 0 ? top : bottom
    if ((ctx.state.heading > 0 && y >= end) || (ctx.state.heading < 0 && y <= end)) {
      y = end
      ctx.state.next = -ctx.state.heading
      ctx.state.heading = 0
      ctx.state.wait = ctx.props.pause ?? 1
    }
    // `position`, not `teleport`: this is a move, so it is smoothed between ticks and the rider is
    // carried by exactly the distance the slab travelled.
    ctx.entity.position = [p[0], y, p[2]]
  },
} satisfies Behavior<State, Props>
```
