id: 2043d16d9a9342d3b2cb65f7ccdd0413
parent_id: 82dec358bdf548f894d0b78c0ac26945
item_type: 1
item_id: 0c837f4e6b7e462a997cbc19e47c864a
item_updated_time: 1780925371169
title_diff: "[]"
body_diff: "[{\"diffs\":[[0,\"* v0.6.0\"],[1,\" (with track mapping additions)\"],[0,\"\\\n\\\n---\\\n\\\n#\"]],\"start1\":327,\"start2\":327,\"length1\":16,\"length2\":47},{\"diffs\":[[0,\"d binary |\\\n\\\n\"],[1,\"### Tracks\\\n\\\n| Method | Endpoint | Description |\\\n|--------|----------|-------------|\\\n| GET | `/api/tracks` | List all stored track models (summaries with game, track_name, corner_count) |\\\n| GET | `/api/tracks/{game}/{track_name}` | Get full track model (geometry, corners, sectors, curvature) |\\\n| DELETE | `/api/tracks/{game}/{track_name}` | Delete track model |\\\n| GET | `/api/tracks/{game}/{track_name}/corners` | Get corners only for a track model |\\\n| POST | `/api/tracks/build` | Build track model from completed `track_mapping` session |\\\n\\\n\"],[0,\"### Metadata\"]],\"start1\":1693,\"start2\":1693,\"length1\":24,\"length2\":566},{\"diffs\":[[0,\"ack_map,\"],[1,\" track_mapping,\"],[0,\" general\"]],\"start1\":2457,\"start2\":2457,\"length1\":16,\"length2\":31},{\"diffs\":[[0,\"stamp.\\\n\\\n\"],[1,\"### Build Track Model\\\n\\\n```json\\\n// POST /api/tracks/build\\\n{\\\n  \\\"session_id\\\": \\\"uuid\\\",\\\n  \\\"game\\\": \\\"optional override (defaults to recording's game)\\\",\\\n  \\\"track_name\\\": \\\"optional override (defaults to recording's track)\\\"\\\n}\\\n```\\\n\\\nRequires a completed `track_mapping` session with exactly 2 boundary runs (labeled \\\"left\\\" and \\\"right\\\"). Returns the full `TrackModel` with 201 Created on success.\\\n\\\n\"],[0,\"### Run \"]],\"start1\":4268,\"start2\":4268,\"length1\":16,\"length2\":400},{\"diffs\":[[0,\"ull\\\"\\\n}\\\n```\\\n\\\n\"],[1,\"### Track Model Response\\\n\\\n```json\\\n// GET /api/tracks/{game}/{track_name}\\\n{\\\n  \\\"game\\\": \\\"assetto_corsa\\\",\\\n  \\\"track_name\\\": \\\"monza\\\",\\\n  \\\"center_line\\\": [[0.35, 0.72], ...],     // normalized (x, z) pairs\\\n  \\\"left_boundary\\\": [[0.33, 0.74], ...],\\\n  \\\"right_boundary\\\": [[0.37, 0.70], ...],\\\n  \\\"track_width\\\": [12.5, ...],              // meters per point\\\n  \\\"heading\\\": [1.57, ...],                  // radians per point\\\n  \\\"curvature\\\": [0.002, ...],               // 1/radius per point (smoothed)\\\n  \\\"corners\\\": [\\\n    {\\\n      \\\"index\\\": 234,\\\n      \\\"number\\\": 1,\\\n      \\\"entry_s\\\": 0.117,\\\n      \\\"apex_s\\\": 0.125,\\\n      \\\"exit_s\\\": 0.133,\\\n      \\\"curvature_peak\\\": 0.018\\\n    }\\\n  ],\\\n  \\\"sectors\\\": [\\\n    { \\\"index\\\": 0, \\\"start_s\\\": 0.0, \\\"end_s\\\": 0.333 },\\\n    { \\\"index\\\": 1, \\\"start_s\\\": 0.333, \\\"end_s\\\": 0.667 },\\\n    { \\\"index\\\": 2, \\\"start_s\\\": 0.667, \\\"end_s\\\": 1.0 }\\\n  ],\\\n  \\\"source\\\": \\\"boundary_recording\\\",\\\n  \\\"created_at\\\": \\\"2026-06-08T14:17:39+02:00\\\"\\\n}\\\n```\\\n\\\n### Track Summary (List)\\\n\\\n```json\\\n// GET /api/tracks\\\n[\\\n  {\\\n    \\\"game\\\": \\\"assetto_corsa\\\",\\\n    \\\"track_name\\\": \\\"monza\\\",\\\n    \\\"source\\\": \\\"boundary_recording\\\",\\\n    \\\"created_at\\\": \\\"2026-06-08T14:17:39+02:00\\\",\\\n    \\\"corner_count\\\": 11\\\n  }\\\n]\\\n```\\\n\\\n\"],[0,\"### Delete R\"]],\"start1\":5180,\"start2\":5180,\"length1\":24,\"length2\":1169},{\"diffs\":[[0,\"ues\\\n\"],[-1,\"\\\n---\\\n\\\n## Version History\"],[1,\"- **V1 position data** (added 2026-06-08): reads `world_position` (3×i16), `current_lap_distance`, derives `spline_position`; enables track mapping for PCARS\\\n\\\n---\\\n\\\n## Track Model Storage\\\n\\\nTrack models are persisted as JSON at `data/tracks/<game>/<track_name>.json`.\\\n\\\n| Method | Description |\\\n|--------|-------------|\\\n| `TrackModelStore::new(data_dir)` | Create store rooted at `data_dir/tracks/` |\\\n| `list()` | List all track model summaries |\\\n| `load(game, track_name)` | Load full TrackModel |\\\n| `save(&model)` | Save model (creates directories) |\\\n| `delete(game, track_name)` | Delete model file |\\\n| `exists(game, track_name)` | Check existence |\\\n\\\nPath traversal protection: `game` and `track_name` are validated to prevent `..`, `/`, `\\\\` components.\\\n\\\n---\\\n\\\n## Version History\\\n\\\n### Post-v0.6.0 (2026-06-08) — Track Mapping Implementation\\\n- `GET /api/tracks` — list all stored track models\\\n- `GET /api/tracks/{game}/{track_name}` — get full track model geometry\\\n- `DELETE /api/tracks/{game}/{track_name}` — delete track model\\\n- `GET /api/tracks/{game}/{track_name}/corners` — get auto-detected corners\\\n- `POST /api/tracks/build` — build TrackModel from completed boundary recording session\\\n- New use case: `track_mapping` — deliberate boundary recording workflow (left + right laps)\\\n- PCARS V1 position data: world_position (3×i16), current_lap_distance, derived spline_position\\\n- Dual position mode: auto-detect spline vs. coordinate, cumulative distance as pseudo-spline\\\n- Track model builder: resample → center line → heading → curvature → corner detection → sectors → normalize\\\n- New module: `track_model.rs` — TrackModel, Corner, Sector, TrackModelStore\\\n- New field: `TelemetryFrame::current_lap_distance` (Option<u16>)\\\n- 33 unit tests total (6 ksudp + 7 pcars + 7 telemetry_tool + 14 feed_state)\"],[0,\"\\\n\\\n##\"]],\"start1\":7942,\"start2\":7942,\"length1\":32,\"length2\":1810},{\"diffs\":[[0,\"06-0\"],[-1,\"6 — v0.6.0: run deletion, session reopen, feed merging\"],[1,\"8 — Track mapping: TrackModel persistence, track API endpoints, PCARS V1 position, dual position mode, corner detection\"],[0,\"*\"]],\"start1\":11095,\"start2\":11095,\"length1\":59,\"length2\":124}]"
metadata_diff: {"new":{},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2026-06-08T13:33:34.105Z
created_time: 2026-06-08T13:33:34.105Z
type_: 13