60 lines
2.1 KiB
Markdown
60 lines
2.1 KiB
Markdown
# Step 05 — Sweeping in 1D (entry & exit time)
|
|
|
|
This is the seed of the whole engine. Get this one *in your bones* and the scary
|
|
2D `sweptAABB` becomes "do this twice and combine."
|
|
|
|
## Concept
|
|
|
|
Forget 2D. Forget boxes. We have:
|
|
|
|
- a **point** sitting at position `p` on a number line,
|
|
- moving with velocity `v` — meaning over this one frame it travels a total of
|
|
`v` units (so at fraction `t` of the frame, it's at `p + v*t`, for `t` from 0 to 1),
|
|
- and a static **interval** `[min, max]` on that same line.
|
|
|
|
Question: **during this frame, for which `t` is the point inside `[min, max]`?**
|
|
|
|
### The slab math
|
|
|
|
The point reaches `min` when `p + v*t = min`, i.e. `t = (min - p) / v`.
|
|
Likewise it reaches `max` at `t = (max - p) / v`.
|
|
```
|
|
t1 = (min - p) / v
|
|
t2 = (max - p) / v
|
|
```
|
|
|
|
If `v` is **negative** (moving left), the point hits `max` *before* `min`, so
|
|
`t1 > t2`. We always want `entry` to be the smaller and `exit` the larger, so
|
|
**swap them if they're out of order**. Then:
|
|
|
|
- `entry` = the time the point *enters* the interval,
|
|
- `exit` = the time it *leaves*.
|
|
|
|
> These can be negative or greater than 1 — that just means the crossing happens
|
|
> before this frame started or after it ends. Don't clamp here; the caller (step
|
|
> 06/08) decides whether `entry` falls within `[0, 1]`. Keeping the raw numbers
|
|
> is what lets us combine axes later.
|
|
|
|
### The `v == 0` edge case
|
|
|
|
If the point isn't moving (`v == 0`), it never *crosses* an edge — dividing by
|
|
zero is meaningless. Instead: it's either already inside the interval for the
|
|
whole frame, or never. So:
|
|
|
|
- if `min <= p <= max`: it's inside the entire time → `entry = -Infinity`,
|
|
`exit = +Infinity`.
|
|
- otherwise: it never overlaps → return `null`.
|
|
|
|
(Those infinities are deliberate: in 2D they let a non-moving axis say "I'm not
|
|
the axis that limits the collision," without breaking the `max`/`min` combine
|
|
step. You'll see why in step 06.)
|
|
|
|
## Task
|
|
|
|
Implement `sweepInterval(p, v, min, max)` in `sweep1d.ts`. Return
|
|
`{ entry, exit }`, or `null` only in the not-moving-and-outside case.
|
|
|
|
```sh
|
|
bun test workshop/steps/05-sweep-1d
|
|
```
|