@glandais/vcyclist-engine 4.3.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/fit-kotlin-sdk.js +13 -7
- package/fit-kotlin-sdk.js.map +1 -1
- package/index.cjs +70 -0
- package/index.d.ts +725 -0
- package/index.mjs +70 -0
- package/kotlin-kotlin-stdlib.js +43 -43
- package/kotlin-kotlin-stdlib.js.map +1 -1
- package/kotlinx-coroutines-core.js +2 -2
- package/package.json +11 -3
- package/vcyclist-elevation.js +160 -44
- package/vcyclist-elevation.js.map +1 -1
- package/vcyclist-engine.d.ts +8 -4
- package/vcyclist-engine.js +420 -242
- package/vcyclist-engine.js.map +1 -1
- package/vcyclist-fit.js +2 -2
- package/vcyclist-fit.js.map +1 -1
- package/vcyclist-gpx.js +658 -257
- package/vcyclist-gpx.js.map +1 -1
package/index.d.ts
ADDED
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
// GENERATED by `./gradlew :codegen:generateTsFacade` — do not edit.
|
|
2
|
+
// Mirrors src/jsMain/kotlin/io/github/glandais/engine/EngineJsApi.kt.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* An opaque Kotlin/JS `Path` instance. It is a real JS object, but it is not
|
|
6
|
+
* `@JsExport`ed and has no TypeScript surface — reach into it through the
|
|
7
|
+
* accessor functions below, never by property.
|
|
8
|
+
*
|
|
9
|
+
* Branded rather than `object` or `any` so two handle kinds cannot be swapped,
|
|
10
|
+
* and so a typo dereferencing one is a compile error.
|
|
11
|
+
*/
|
|
12
|
+
declare const PathBrand: unique symbol;
|
|
13
|
+
export type Path = { readonly [PathBrand]: 'Path' };
|
|
14
|
+
|
|
15
|
+
/** The spellings every door parses, derived from the `CorridorMode` catalogue in `commonMain`. */
|
|
16
|
+
export type CorridorMode =
|
|
17
|
+
| 'lane'
|
|
18
|
+
| 'lane-left'
|
|
19
|
+
| 'full-road';
|
|
20
|
+
|
|
21
|
+
/** The spellings every door parses, derived from the `ElevationGainPreset` catalogue in `commonMain`. */
|
|
22
|
+
export type ElevationGainPreset =
|
|
23
|
+
| 'raw'
|
|
24
|
+
| 'barometric'
|
|
25
|
+
| 'dem'
|
|
26
|
+
| 'gps';
|
|
27
|
+
|
|
28
|
+
/** The spellings every door parses, derived from the `RoadCondition` catalogue in `commonMain`. */
|
|
29
|
+
export type RoadCondition =
|
|
30
|
+
| 'dry'
|
|
31
|
+
| 'wet';
|
|
32
|
+
|
|
33
|
+
/** The spellings every door parses, derived from the `PowerModel` catalogue in `commonMain`. */
|
|
34
|
+
export type PowerModel =
|
|
35
|
+
| 'constant'
|
|
36
|
+
| 'durability'
|
|
37
|
+
| 'critical-power'
|
|
38
|
+
| 'from_data';
|
|
39
|
+
|
|
40
|
+
/** The spellings every door parses, derived from the `PointField` catalogue in `commonMain`. */
|
|
41
|
+
export type PointFieldProp =
|
|
42
|
+
| 'latitude'
|
|
43
|
+
| 'longitude'
|
|
44
|
+
| 'distance'
|
|
45
|
+
| 'dx'
|
|
46
|
+
| 'time'
|
|
47
|
+
| 'elapsed'
|
|
48
|
+
| 'dt'
|
|
49
|
+
| 'bearing'
|
|
50
|
+
| 'elevation'
|
|
51
|
+
| 'grade'
|
|
52
|
+
| 'radius'
|
|
53
|
+
| 'aeroCoef'
|
|
54
|
+
| 'windBearing'
|
|
55
|
+
| 'windAlpha'
|
|
56
|
+
| 'pAero'
|
|
57
|
+
| 'pGravity'
|
|
58
|
+
| 'pRollingResistance'
|
|
59
|
+
| 'pWheelBearings'
|
|
60
|
+
| 'pInputPower'
|
|
61
|
+
| 'pCyclistProvidedOptimalPower'
|
|
62
|
+
| 'pCyclistProvidedOptimalPowerWithHarmonics'
|
|
63
|
+
| 'pCyclistPowerNeeded'
|
|
64
|
+
| 'pCyclistProvidedMuscular'
|
|
65
|
+
| 'pCyclistProvidedWheel'
|
|
66
|
+
| 'pComputedTotalPower'
|
|
67
|
+
| 'pComputedWheelPower'
|
|
68
|
+
| 'pComputedPower'
|
|
69
|
+
| 'speed'
|
|
70
|
+
| 'speedMax'
|
|
71
|
+
| 'speedMaxIncline'
|
|
72
|
+
| 'virtSpeedCurrent'
|
|
73
|
+
| 'temperature'
|
|
74
|
+
| 'windSpeed'
|
|
75
|
+
| 'windDirection'
|
|
76
|
+
| 'heartRate'
|
|
77
|
+
| 'cadence'
|
|
78
|
+
| 'wPrimeBalance'
|
|
79
|
+
| 'pBrake'
|
|
80
|
+
| 'trajectoryCurvature'
|
|
81
|
+
| 'roadWidth'
|
|
82
|
+
| 'lateralOffset'
|
|
83
|
+
| 'sourceLatitude'
|
|
84
|
+
| 'sourceLongitude'
|
|
85
|
+
| 'sourceElevation';
|
|
86
|
+
|
|
87
|
+
/** The spellings every door parses, derived from the `CornerKind` catalogue in `commonMain`. */
|
|
88
|
+
export type CornerKind =
|
|
89
|
+
| 'GENTLE'
|
|
90
|
+
| 'CORNER'
|
|
91
|
+
| 'HAIRPIN';
|
|
92
|
+
|
|
93
|
+
/** The spellings every door parses, derived from the `GpxPowerSource` catalogue in `commonMain`. */
|
|
94
|
+
export type GpxPowerSource =
|
|
95
|
+
| 'input'
|
|
96
|
+
| 'computed'
|
|
97
|
+
| 'computed-or-input';
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* JS-facing snapshot of a single path point. Read-only ; built lazily by `pointAt`. Field units
|
|
101
|
+
* are degrees, meters, m/s, watts, epoch ms, slope ratio.
|
|
102
|
+
*/
|
|
103
|
+
export interface PointDto {
|
|
104
|
+
readonly latitudeDeg: number;
|
|
105
|
+
readonly longitudeDeg: number;
|
|
106
|
+
readonly elevation: number;
|
|
107
|
+
readonly timeMs: number;
|
|
108
|
+
readonly speed: number;
|
|
109
|
+
readonly pComputedPower: number;
|
|
110
|
+
readonly distance: number;
|
|
111
|
+
readonly grade: number;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* JS-facing snapshot of a `<wpt>` waypoint (see `GpxWaypoint`).
|
|
116
|
+
* Flat DTO ; built by `parseGpxWaypoints`. `timeEpochMs` is `null` when `<time>` is absent, same
|
|
117
|
+
* as the Kotlin model.
|
|
118
|
+
*/
|
|
119
|
+
export interface WaypointDto {
|
|
120
|
+
readonly latitudeDeg: number;
|
|
121
|
+
readonly longitudeDeg: number;
|
|
122
|
+
readonly elevationM?: number | null;
|
|
123
|
+
readonly name?: string | null;
|
|
124
|
+
readonly description?: string | null;
|
|
125
|
+
readonly symbol?: string | null;
|
|
126
|
+
readonly type?: string | null;
|
|
127
|
+
readonly timeEpochMs?: number | null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* JS-side mirror of `EnhanceOptions` / `SimplifyPathOptions`. Every flag is optional ; when a
|
|
132
|
+
* caller leaves a field `undefined`, `defaultJsOptions` picks the JS-safe default (skip elevation
|
|
133
|
+
* fetch, skip 1 Hz resample, skip simplify — see task 27 for the timestamp pitfall).
|
|
134
|
+
*/
|
|
135
|
+
export interface EnhanceOptionsDto {
|
|
136
|
+
readonly fixElevation?: boolean | null;
|
|
137
|
+
readonly computeMaxSpeeds?: boolean | null;
|
|
138
|
+
readonly virtualizeTrack?: boolean | null;
|
|
139
|
+
readonly computeOnePointPerSecond?: boolean | null;
|
|
140
|
+
readonly simplifyEnabled?: boolean | null;
|
|
141
|
+
readonly simplifyToleranceM?: number | null;
|
|
142
|
+
readonly simplifyZExaggeration?: number | null;
|
|
143
|
+
/**
|
|
144
|
+
* W′ balance annotation pass (ledger R15). Defaults to `true`, which is what JS callers have
|
|
145
|
+
* been getting all along — `WPrimeBalanceOptions` is enabled by default and nothing here ever
|
|
146
|
+
* overrode it, so the `wPrimeBalance` field was already being written, at CP 250 W / W′ 20 kJ.
|
|
147
|
+
* These three fields make it *calibrable*, they do not turn it on.
|
|
148
|
+
*/
|
|
149
|
+
readonly wPrimeBalanceEnabled?: boolean | null;
|
|
150
|
+
/**
|
|
151
|
+
* CP (W) for the W′ balance **field**.
|
|
152
|
+
*
|
|
153
|
+
* This is **not** automatically `criticalPower`. The two are independent by
|
|
154
|
+
* design (see `WPrimeBalanceOptions` for why CP/W′ live on the options rather than on
|
|
155
|
+
* `Cyclist`), so passing different values here and on the power provider yields a W′bal trace
|
|
156
|
+
* that does not describe the rider being simulated. Legitimate — reporting one rider's effort
|
|
157
|
+
* against another's physiology — but rarely intended.
|
|
158
|
+
*/
|
|
159
|
+
readonly wPrimeBalanceCriticalPower?: number | null;
|
|
160
|
+
/**
|
|
161
|
+
* W′ (J) for the W′ balance field. Same independence caveat as `wPrimeBalanceCriticalPower`.
|
|
162
|
+
*/
|
|
163
|
+
readonly wPrimeBalanceWPrime?: number | null;
|
|
164
|
+
/**
|
|
165
|
+
* Curvature estimation. Defaults to on — it corrects `radius` and `speedMax`, so turning it
|
|
166
|
+
* off restores the historical windowed estimate rather than saving work worth saving.
|
|
167
|
+
*/
|
|
168
|
+
readonly curvatureEnabled?: boolean | null;
|
|
169
|
+
/**
|
|
170
|
+
* Optimal-trajectory ("racing line") stage. Off by default: enabling it **moves every
|
|
171
|
+
* coordinate** of the result. The original positions are preserved in `sourceLatitude` /
|
|
172
|
+
* `sourceLongitude`.
|
|
173
|
+
*/
|
|
174
|
+
readonly racingLineEnabled?: boolean | null;
|
|
175
|
+
/**
|
|
176
|
+
* `"lane"` (default), `"lane-left"`, or `"full-road"` — closed roads and time trials only.
|
|
177
|
+
*/
|
|
178
|
+
readonly racingLineCorridor?: CorridorMode | null;
|
|
179
|
+
/**
|
|
180
|
+
* Road width assumed where the GPX supplies none, in metres.
|
|
181
|
+
*/
|
|
182
|
+
readonly racingLineRoadWidthM?: number | null;
|
|
183
|
+
/**
|
|
184
|
+
* Scale the reported climbing is measured at: `"raw"`, `"barometric"`, `"dem"`, `"gps"`.
|
|
185
|
+
* See `docs/guides/elevation.md`.
|
|
186
|
+
*/
|
|
187
|
+
readonly elevationGainPreset?: ElevationGainPreset | null;
|
|
188
|
+
/**
|
|
189
|
+
* Override the preset's hysteresis dead band, in metres. `0` disables it.
|
|
190
|
+
*/
|
|
191
|
+
readonly elevationGainThresholdM?: number | null;
|
|
192
|
+
/**
|
|
193
|
+
* Whether to measure the dead-banded climbing at all. Defaults to `true`.
|
|
194
|
+
*/
|
|
195
|
+
readonly elevationGainEnabled?: boolean | null;
|
|
196
|
+
/**
|
|
197
|
+
* Triangular-kernel half-width for the elevation smoother, in metres.
|
|
198
|
+
*
|
|
199
|
+
* The single largest determinant of both the reported climbing and the gradients the
|
|
200
|
+
* simulation rides — it costs a clean route ~1.5 % and a noisy GPS trace ~48 %.
|
|
201
|
+
*/
|
|
202
|
+
readonly elevationSmoothWindowM?: number | null;
|
|
203
|
+
/**
|
|
204
|
+
* Web-Mercator zoom for the DEM lookup, `0..15`, used only when `fixElevation` is on.
|
|
205
|
+
*
|
|
206
|
+
* Not part of `EnhanceOptions` — it configures the provider, not the pipeline — so it is read
|
|
207
|
+
* where the provider is built rather than in `toEnhanceOptions`.
|
|
208
|
+
*/
|
|
209
|
+
readonly demZoom?: number | null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* JS-side mirror of `Cyclist`. All fields required when the DTO is passed (Kotlin/JS does not
|
|
214
|
+
* gracefully tolerate optional fields on `external interface` without `?`). Callers can pass
|
|
215
|
+
* `null` for the whole DTO to fall back to engine defaults (80 kg / 0.7 Cd / 0.5 m² / 35° / 100
|
|
216
|
+
* km/h / 0.4 g brake).
|
|
217
|
+
*
|
|
218
|
+
* Passing a key this interface does not declare is an **error** since task 43 — see
|
|
219
|
+
* `requireOnlyKeys`.
|
|
220
|
+
*/
|
|
221
|
+
export interface CyclistDto {
|
|
222
|
+
readonly massKg: number;
|
|
223
|
+
readonly cd: number;
|
|
224
|
+
readonly frontalAreaM2: number;
|
|
225
|
+
readonly maxLeanAngleDeg: number;
|
|
226
|
+
readonly maxBrakeG: number;
|
|
227
|
+
readonly maxSpeedKmH: number;
|
|
228
|
+
/**
|
|
229
|
+
* Road surface preset (ledger R9) : `"dry"` (the default) or `"wet"`, case-insensitive. Sets
|
|
230
|
+
* cornering grip and braking together — wet cuts cornering speed by 1.58× and braking to
|
|
231
|
+
* 0.23 g. Any other value throws.
|
|
232
|
+
*
|
|
233
|
+
* **The preset wins over `maxLeanAngleDeg` and `maxBrakeG` when present**, which is the
|
|
234
|
+
* opposite of the CLI, where an explicit `--cyclist-max-angle` overrides `--road-condition`.
|
|
235
|
+
* The CLI can tell "not given" from "given" because its fields are nullable ; here they are
|
|
236
|
+
* not, so a JS caller always supplies all six and "explicit wins" would make the preset
|
|
237
|
+
* unreachable. Omit `roadCondition` to keep the raw values — that is the pre-R9 behaviour, and
|
|
238
|
+
* `"dry"` reproduces the shipped defaults bit-for-bit anyway.
|
|
239
|
+
*/
|
|
240
|
+
readonly roadCondition?: RoadCondition | null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* JS-side mirror of `Bike`. Pass `null` to use defaults (Crr 0.004, road-bike inertias,
|
|
245
|
+
* 0.7 m wheel radius, 0.95 drivetrain efficiency).
|
|
246
|
+
*/
|
|
247
|
+
export interface BikeDto {
|
|
248
|
+
readonly crr: number;
|
|
249
|
+
readonly inertiaFront: number;
|
|
250
|
+
readonly inertiaRear: number;
|
|
251
|
+
readonly wheelRadiusM: number;
|
|
252
|
+
readonly efficiency: number;
|
|
253
|
+
/**
|
|
254
|
+
* Lean angle (°) past which the rider stops pedalling; `90` disables the cut-off.
|
|
255
|
+
*/
|
|
256
|
+
readonly maxPedalingLeanAngleDeg?: number | null;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* JS-side mirror of constant-wind input. `windDirection` is in **degrees**, `0` = north,
|
|
261
|
+
* `90` = east — `toWindProvider` converts to the radians the engine uses internally.
|
|
262
|
+
*
|
|
263
|
+
* The angle names the direction the wind blows **toward**, not the meteorological "from" it was
|
|
264
|
+
* long documented as. Task g31 established this by simulation; see
|
|
265
|
+
* `PathWind.dominantHeadwindAzimuthDeg`, whose output goes into this field unchanged.
|
|
266
|
+
*/
|
|
267
|
+
export interface WindDto {
|
|
268
|
+
readonly windSpeed: number;
|
|
269
|
+
readonly windDirection: number;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* JS-side description of which `CyclistPowerProvider` to instantiate.
|
|
274
|
+
*
|
|
275
|
+
* `type` takes any `PowerModel` wire name — `"constant"`, `"durability"`, `"critical-power"` or
|
|
276
|
+
* `"from_data"` — and the catalog is the single source of that list, so a model added to the
|
|
277
|
+
* engine is reachable here without editing this file.
|
|
278
|
+
*
|
|
279
|
+
* `pacing` and `maxSlewWPerS` are **decorators**, not types : they compose over whichever `type`
|
|
280
|
+
* was chosen, in the order `toProvider` defines.
|
|
281
|
+
*/
|
|
282
|
+
export interface PowerProviderDto {
|
|
283
|
+
readonly type: PowerModel;
|
|
284
|
+
readonly power?: number | null;
|
|
285
|
+
readonly useHarmonics?: boolean | null;
|
|
286
|
+
/**
|
|
287
|
+
* CP (W) for `"durability"` and `"critical-power"`. Defaults to 250 W.
|
|
288
|
+
*/
|
|
289
|
+
readonly criticalPower?: number | null;
|
|
290
|
+
/**
|
|
291
|
+
* W′ (J) for `"critical-power"` — the reserve spendable above CP. Defaults to 20 kJ.
|
|
292
|
+
*/
|
|
293
|
+
readonly wPrime?: number | null;
|
|
294
|
+
/**
|
|
295
|
+
* Terrain pacing (ledger R19) : harder uphill and into headwind, easier downhill, with a causal
|
|
296
|
+
* energy account so the effort is redistributed rather than added. Off by default.
|
|
297
|
+
*
|
|
298
|
+
* A heuristic with **no lookahead** — the rider reacts to the road it is on. Its magnitudes
|
|
299
|
+
* (gradient gain, the ~300 m ramp, the energy-budget window) are the project's own rather than
|
|
300
|
+
* sourced, so they are not exposed here ; use `PowerProviderTerrainPacing` directly from Kotlin
|
|
301
|
+
* to change them.
|
|
302
|
+
*/
|
|
303
|
+
readonly pacing?: boolean | null;
|
|
304
|
+
/**
|
|
305
|
+
* Power slew-rate limit (ledger R18) in W/s ; `0` or omitted disables it. 50 is the value
|
|
306
|
+
* Zignoli & Biral use. Same semantics as the CLI's `--cyclist-slew`.
|
|
307
|
+
*
|
|
308
|
+
* Applied **outermost**, so it smooths whatever `type` and `pacing` produced.
|
|
309
|
+
*/
|
|
310
|
+
readonly maxSlewWPerS?: number | null;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Catalog entry for a `PointField`. Exposed to JS
|
|
315
|
+
* via `fieldDefinitions` so consumers can render generic field-pickers without hard-coding
|
|
316
|
+
* the 43-entry list.
|
|
317
|
+
*/
|
|
318
|
+
export interface FieldDefinitionDto {
|
|
319
|
+
readonly prop: PointFieldProp;
|
|
320
|
+
readonly unit: string;
|
|
321
|
+
readonly shortDescription: string;
|
|
322
|
+
readonly categoryId: string;
|
|
323
|
+
readonly categoryName: string;
|
|
324
|
+
readonly notSelectable: boolean;
|
|
325
|
+
readonly anglesInRadians: boolean;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* A homogeneous-grade segment of a climb. Distances are absolute along the path, in meters;
|
|
330
|
+
* `grade` is dimensionless (`0.08` = 8 %), matching `averageGrade`.
|
|
331
|
+
*/
|
|
332
|
+
export interface ClimbPartDto {
|
|
333
|
+
readonly startDistanceM: number;
|
|
334
|
+
readonly endDistanceM: number;
|
|
335
|
+
readonly startElevationM: number;
|
|
336
|
+
readonly endElevationM: number;
|
|
337
|
+
readonly lengthM: number;
|
|
338
|
+
readonly elevationGainM: number;
|
|
339
|
+
readonly grade: number;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* A detected climb, flattened for the JS boundary.
|
|
344
|
+
*/
|
|
345
|
+
export interface ClimbDto {
|
|
346
|
+
readonly startIndex: number;
|
|
347
|
+
readonly endIndex: number;
|
|
348
|
+
readonly startDistanceM: number;
|
|
349
|
+
readonly endDistanceM: number;
|
|
350
|
+
readonly startElevationM: number;
|
|
351
|
+
readonly endElevationM: number;
|
|
352
|
+
readonly lengthM: number;
|
|
353
|
+
readonly elevationGainM: number;
|
|
354
|
+
/**
|
|
355
|
+
* Dimensionless: `0.08` = 8 %.
|
|
356
|
+
*/
|
|
357
|
+
readonly averageGrade: number;
|
|
358
|
+
/**
|
|
359
|
+
* Dimensionless, counting only the rising sections.
|
|
360
|
+
*/
|
|
361
|
+
readonly climbingGrade: number;
|
|
362
|
+
readonly positiveElevationM: number;
|
|
363
|
+
readonly negativeElevationM: number;
|
|
364
|
+
readonly parts: ClimbPartDto[];
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* One detected bend, as the racing-line analysis sees it.
|
|
369
|
+
*
|
|
370
|
+
* @see analyzeRacingLine
|
|
371
|
+
*/
|
|
372
|
+
export interface CornerDto {
|
|
373
|
+
readonly fromIndex: number;
|
|
374
|
+
readonly untilIndex: number;
|
|
375
|
+
readonly apexIndex: number;
|
|
376
|
+
/**
|
|
377
|
+
* `"GENTLE"`, `"CORNER"` or `"HAIRPIN"`.
|
|
378
|
+
*/
|
|
379
|
+
readonly kind: CornerKind;
|
|
380
|
+
readonly turnRad: number;
|
|
381
|
+
/**
|
|
382
|
+
* `+1` turning left, `-1` turning right.
|
|
383
|
+
*/
|
|
384
|
+
readonly direction: number;
|
|
385
|
+
/**
|
|
386
|
+
* 20th percentile of `1/|κ|` over the span — robust to the clothoid ends a mean is biased by.
|
|
387
|
+
*/
|
|
388
|
+
readonly radiusQ20M: number;
|
|
389
|
+
readonly radiusMinM: number;
|
|
390
|
+
readonly lengthM: number;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* What the racing-line stage decided, without applying it.
|
|
395
|
+
*
|
|
396
|
+
* Every array has one entry per path station. `corridorLo`/`corridorHi` bound the lateral offset in
|
|
397
|
+
* metres, positive to the left; `lateralOffsetM` is the line that was solved for inside them.
|
|
398
|
+
*/
|
|
399
|
+
export interface RacingLineReportDto {
|
|
400
|
+
readonly size: number;
|
|
401
|
+
readonly corners: CornerDto[];
|
|
402
|
+
/**
|
|
403
|
+
* Curvature of the road itself, m⁻¹, positive turning left.
|
|
404
|
+
*/
|
|
405
|
+
readonly centerlineCurvature: Float64Array;
|
|
406
|
+
/**
|
|
407
|
+
* Curvature of the solved line, same convention.
|
|
408
|
+
*/
|
|
409
|
+
readonly trajectoryCurvature: Float64Array;
|
|
410
|
+
readonly corridorLo: Float64Array;
|
|
411
|
+
readonly corridorHi: Float64Array;
|
|
412
|
+
readonly roadHalfWidthM: Float64Array;
|
|
413
|
+
readonly lateralOffsetM: Float64Array;
|
|
414
|
+
/**
|
|
415
|
+
* Widest the corridor gets anywhere, `hi − lo`, in metres.
|
|
416
|
+
*/
|
|
417
|
+
readonly maxCorridorWidthM: number;
|
|
418
|
+
readonly newtonIterations: number;
|
|
419
|
+
/**
|
|
420
|
+
* Final residual relative to the initial gradient — scale-free, so comparable across routes.
|
|
421
|
+
*/
|
|
422
|
+
readonly relativeGradient: number;
|
|
423
|
+
readonly converged: boolean;
|
|
424
|
+
readonly activeConstraints: number;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Parse `xml` and return every `<wpt>` in document order, as flat `WaypointDto` objects.
|
|
429
|
+
*/
|
|
430
|
+
export declare function parseGpxWaypoints(xml: string): WaypointDto[];
|
|
431
|
+
|
|
432
|
+
export declare function parseGpx(xml: string): Path;
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Parse `xml` and return **one Path per `<trk>`**, in document order. Tracks with no point are
|
|
436
|
+
* skipped. Segments of a same track are concatenated — distance jumps across a segment
|
|
437
|
+
* boundary ; use `parseGpxSegments` if that artefact matters.
|
|
438
|
+
*
|
|
439
|
+
* **Since task g24 this includes `<rte>` routes**, which used to be dropped silently. Use
|
|
440
|
+
* `parseGpxTracksOnly` for the pre-g24 selection.
|
|
441
|
+
*/
|
|
442
|
+
export declare function parseGpxTracks(xml: string): Path[];
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Parse `xml` and return **one Path per `<trkseg>`**, across all tracks, in document order.
|
|
446
|
+
* Empty segments are skipped. Every returned Path is continuous.
|
|
447
|
+
*/
|
|
448
|
+
export declare function parseGpxSegments(xml: string): Path[];
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Like `parseGpxTracks` but **`<trk>` only** — routes are left out (task g29).
|
|
452
|
+
*
|
|
453
|
+
* `GpxPathKind` is deliberately not exported: an enum crosses to JavaScript as an object nobody
|
|
454
|
+
* wants to import just to filter a list. Two named functions say the same thing at the call site.
|
|
455
|
+
*/
|
|
456
|
+
export declare function parseGpxTracksOnly(xml: string): Path[];
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Like `parseGpxTracks` but **`<rte>` only** — recorded tracks are left out (task g29).
|
|
460
|
+
*/
|
|
461
|
+
export declare function parseGpxRoutesOnly(xml: string): Path[];
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Serialise `paths` as a multi-track GPX document — one `<trk>` per Path. `waypoints`, if given,
|
|
465
|
+
* is written as `<wpt>` entries before the tracks (typically the source document's
|
|
466
|
+
* `waypoints`, forwarded so a parse → enhance → write
|
|
467
|
+
* round-trip does not silently drop points of interest — see g03).
|
|
468
|
+
*
|
|
469
|
+
* `trackNames` names the tracks positionally; a shorter list — or `null` — leaves the remaining
|
|
470
|
+
* tracks unnamed, which is `pathsToGpxDocument`'s own contract and **not** the
|
|
471
|
+
* `DEFAULT_TRACK_NAME` that `writeGpx` applies to its single track. Extra names are ignored.
|
|
472
|
+
*
|
|
473
|
+
* `startTimeEpochMs` is what `writeGpxAt` does, applied to every track at once: `<time>` =
|
|
474
|
+
* `startTimeEpochMs + time(i)`. `null` (the default) writes the times as they stand. It is a
|
|
475
|
+
* `Double` for the reason `writeGpxAt` gives.
|
|
476
|
+
*
|
|
477
|
+
* Both arrived late — the WASI door had accepted `trackName` and `startTimeEpochMs` since w09 and
|
|
478
|
+
* this façade had no way to say either, so a multi-track export was the one GPX a JS caller could
|
|
479
|
+
* not date or name.
|
|
480
|
+
*/
|
|
481
|
+
export declare function writeGpxTracks(paths: Path[], waypoints?: WaypointDto[], writeExtensions?: boolean, powerSource?: GpxPowerSource | null, trackNames?: string[] | null, startTimeEpochMs?: number | null): string;
|
|
482
|
+
|
|
483
|
+
export declare function pathSize(path: Path): number;
|
|
484
|
+
|
|
485
|
+
export declare function pathTotalDistance(path: Path): number;
|
|
486
|
+
|
|
487
|
+
export declare function pathDurationMs(path: Path): number;
|
|
488
|
+
|
|
489
|
+
export declare function pathElevationGain(path: Path): number;
|
|
490
|
+
|
|
491
|
+
export declare function pathElevationLoss(path: Path): number;
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Cumulative ascent with the dead band applied, or `NaN` if the stage did not run.
|
|
495
|
+
*
|
|
496
|
+
* `NaN` rather than `null` so the export stays a plain `Double` on the JS side; use
|
|
497
|
+
* `pathReportedElevationGain` to get the raw sum as a fallback instead.
|
|
498
|
+
*/
|
|
499
|
+
export declare function pathElevationGainFiltered(path: Path): number;
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Counterpart of `pathElevationGainFiltered`. NEGATIVE by convention, or `NaN`.
|
|
503
|
+
*/
|
|
504
|
+
export declare function pathElevationLossFiltered(path: Path): number;
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* The figure to show a human: dead-banded when measured, the raw sum otherwise.
|
|
508
|
+
*/
|
|
509
|
+
export declare function pathReportedElevationGain(path: Path): number;
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Counterpart of `pathReportedElevationGain`. NEGATIVE by convention.
|
|
513
|
+
*/
|
|
514
|
+
export declare function pathReportedElevationLoss(path: Path): number;
|
|
515
|
+
|
|
516
|
+
export declare function pointAt(path: Path, i: number): PointDto;
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Serialise `path` as a single-track GPX.
|
|
520
|
+
*
|
|
521
|
+
* `writeExtensions` `false` drops `<extensions>` — no power, heart rate, cadence or temperature,
|
|
522
|
+
* and no `gpxtpx` namespace (task g23). `<ele>` and `<time>` are standard GPX and stay. Useful in
|
|
523
|
+
* a browser for a download meant for a platform with a strict schema.
|
|
524
|
+
*
|
|
525
|
+
* `powerSource` selects which of the path's two power fields lands in `<power>`: `"input"`,
|
|
526
|
+
* `"computed"` or `"computed-or-input"` — the CLI's `--gpx-power-source` values, parsed through
|
|
527
|
+
* the one catalogue in `GpxPowerSource`. `null` keeps the default, which writes the *input*
|
|
528
|
+
* power: emitting simulated data into a format the ecosystem reads as a recording is the caller's
|
|
529
|
+
* decision, not a default's.
|
|
530
|
+
*
|
|
531
|
+
* `trackName` names the `<trk><name>`; `null` keeps `"virtualized"`.
|
|
532
|
+
*/
|
|
533
|
+
export declare function writeGpx(path: Path, writeExtensions?: boolean, powerSource?: GpxPowerSource | null, trackName?: string | null): string;
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Serialise `path` with an absolute `<time>` on every point : `<time> = startTimeEpochMs +
|
|
537
|
+
* time(i)` milliseconds (see `startTime` / task g05).
|
|
538
|
+
*
|
|
539
|
+
* `startTimeEpochMs` is a `Double`, not a `Long` : on Kotlin/JS `Long` crosses the boundary as a
|
|
540
|
+
* `BigInt`, which is awkward for callers building it from `Date.now()` or `new Date(...).getTime()`
|
|
541
|
+
* (both already `number`/`Double`). Epoch milliseconds fit exactly in a `Double` until the year
|
|
542
|
+
* 287396 — the same reasoning already used by `pathDurationMs`.
|
|
543
|
+
*/
|
|
544
|
+
export declare function writeGpxAt(path: Path, startTimeEpochMs: number, writeExtensions?: boolean, powerSource?: GpxPowerSource | null, trackName?: string | null): string;
|
|
545
|
+
|
|
546
|
+
export declare function enhance(path: Path, options: EnhanceOptionsDto | null): Promise<Path>;
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Enhance `path` using a fully custom `CoursePhysics` (cyclist + bike + wind + power provider).
|
|
550
|
+
*
|
|
551
|
+
* Any DTO parameter may be `null` ; defaults are picked from the engine
|
|
552
|
+
* (see `CyclistDto`, `BikeDto`, `WindDto`, `PowerProviderDto` KDoc for specifics).
|
|
553
|
+
*
|
|
554
|
+
* Like `enhance`, when `options.fixElevation == true` a default `ElevationProvider` is
|
|
555
|
+
* auto-instantiated (Terrarium tiles via HTTP).
|
|
556
|
+
*/
|
|
557
|
+
export declare function enhanceWithCourse(path: Path, cyclist: CyclistDto | null, bike: BikeDto | null, wind: WindDto | null, power: PowerProviderDto | null, options: EnhanceOptionsDto | null): Promise<Path>;
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Read field `fieldProp` (camelCase, e.g. `"elevation"` / `"speed"`) at point `i`. Throws if
|
|
561
|
+
* the field name is unknown.
|
|
562
|
+
*/
|
|
563
|
+
export declare function getField(path: Path, i: number, fieldProp: PointFieldProp): number;
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Enumerate the 43-entry `PointField` catalog as JS-friendly `FieldDefinitionDto` objects.
|
|
567
|
+
* UIs use this to render generic field-pickers without hard-coding the list.
|
|
568
|
+
*/
|
|
569
|
+
export declare function fieldDefinitions(): FieldDefinitionDto[];
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Latitude of point `i` in degrees (convenience wrapper around `latitudeDeg`).
|
|
573
|
+
*/
|
|
574
|
+
export declare function pathLatitudeDeg(path: Path, i: number): number;
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Longitude of point `i` in degrees (convenience wrapper around `longitudeDeg`).
|
|
578
|
+
*/
|
|
579
|
+
export declare function pathLongitudeDeg(path: Path, i: number): number;
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Serialise `path` to CSV (all 36 `PointField`s, one row per point) — see `CsvWriter` / task g06.
|
|
583
|
+
* Lets a browser demo offer an "Export CSV" button without a server round-trip : the caller
|
|
584
|
+
* wraps the returned `String` in a `Blob` and triggers a download via
|
|
585
|
+
* `URL.createObjectURL`.
|
|
586
|
+
*
|
|
587
|
+
* `separator` takes only its first character (JS has no `Char` type at the interop boundary) ;
|
|
588
|
+
* an empty string falls back to `,`.
|
|
589
|
+
*
|
|
590
|
+
* `decimals` and `lineSeparator` arrived in S4 of the surface-alignment work. The WASI door had
|
|
591
|
+
* accepted `decimals` since w09 while this signature had three parameters, so the same core writer
|
|
592
|
+
* was configurable from one wire door and not the other. `null` on either keeps the `CsvOptions`
|
|
593
|
+
* default rather than a literal restated here.
|
|
594
|
+
*/
|
|
595
|
+
export declare function pathToCsv(path: Path, separator: string, unitsInHeader: boolean, decimals?: number | null, lineSeparator?: string | null): string;
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Serialise `path` to JSON, column-oriented (one array per `PointField`) — see `JsonWriter` /
|
|
599
|
+
* task g07. Lets a browser demo hand the result straight to `JSON.parse` and feed a chart
|
|
600
|
+
* library (e.g. Chart.js) without a server round-trip.
|
|
601
|
+
*
|
|
602
|
+
* `decimals` rounds every value; `includeMeta` drops the `size` / `fields` preamble when `false`.
|
|
603
|
+
* Both were reachable from the WASI door and not from here until S4 — `null` keeps the
|
|
604
|
+
* `JsonOptions` default, which is where the default lives.
|
|
605
|
+
*/
|
|
606
|
+
export declare function pathToJson(path: Path, pretty: boolean, decimals?: number | null, includeMeta?: boolean | null): string;
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Encode `path` as a Garmin FIT Course file.
|
|
610
|
+
*
|
|
611
|
+
* @param startTimeEpochMs absolute start instant in Unix epoch milliseconds. FIT has no relative
|
|
612
|
+
* clock, so this is mandatory — `Double` rather than `Long` to avoid a BigInt at the JS
|
|
613
|
+
* boundary, matching the convention `pathDurationMs` and `writeGpxAt` already use.
|
|
614
|
+
* @return the complete FIT file. On Kotlin/JS a `ByteArray` surfaces as a JS `Int8Array`.
|
|
615
|
+
*/
|
|
616
|
+
export declare function pathToFit(path: Path, name: string, startTimeEpochMs: number): Int8Array;
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Encode **several** paths into one FIT course — one lap and one `TIMER`/`START`…`STOP` event
|
|
620
|
+
* pair per path (task g25, exported here by g29).
|
|
621
|
+
*
|
|
622
|
+
* A separate function rather than a parameter on `pathToFit`: the shape of the first argument
|
|
623
|
+
* changes, and Kotlin/JS has no overloading to lean on.
|
|
624
|
+
*
|
|
625
|
+
* `interPathGapMs` shifts each path after the first by that many milliseconds; `0` (the default)
|
|
626
|
+
* runs them straight on from one another. It is **not** a pause — FIT expresses those with
|
|
627
|
+
* `TIMER`/`PAUSE` events, which this port does not emit.
|
|
628
|
+
*/
|
|
629
|
+
export declare function pathsToFit(paths: Path[], name: string, startTimeEpochMs: number, interPathGapMs?: number): Int8Array;
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Azimuth in degrees of the constant wind that makes `path` hardest on average — the opposite of
|
|
633
|
+
* the course's dominant orientation (task g26, exported by g31).
|
|
634
|
+
*
|
|
635
|
+
* The value goes **straight into** a `WindDto.directionDeg` or `Wind(speedMS, directionRad)`: no
|
|
636
|
+
* 180° flip, no unit conversion beyond degrees → radians. The engine's angle convention is not the
|
|
637
|
+
* meteorological one it claims to be, which is why this is exposed as a ready-to-use number rather
|
|
638
|
+
* than as a vector the caller would have to interpret.
|
|
639
|
+
*
|
|
640
|
+
* This function says nothing about wind **speed** — only about the direction that hurts most.
|
|
641
|
+
*
|
|
642
|
+
* @return `NaN` when the question has no answer: fewer than 4 points, or a course whose mean
|
|
643
|
+
* direction cancels out (a perfectly symmetric loop). `0` is a valid answer, so check with
|
|
644
|
+
* `Number.isNaN`.
|
|
645
|
+
*/
|
|
646
|
+
export declare function dominantHeadwindAzimuth(path: Path): number;
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Multi-path form of `dominantHeadwindAzimuth` — the tracks of one file weigh equally.
|
|
650
|
+
*/
|
|
651
|
+
export declare function dominantHeadwindAzimuthOfTracks(paths: Path[]): number;
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Detect climbs on `path` with the default options — see `ClimbOptions`.
|
|
655
|
+
*/
|
|
656
|
+
export declare function detectClimbs(path: Path): ClimbDto[];
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Detect climbs with explicit parameters. Flat scalars rather than an options DTO to keep the
|
|
660
|
+
* JS boundary simple.
|
|
661
|
+
*/
|
|
662
|
+
export declare function detectClimbsWithOptions(path: Path, minMinClimbElevationM: number, maxMinClimbElevationM: number, minClimbElevationRatio: number, minGradePercent: number, maxDiffRealGrade: number, booster: number, maxAnalysisPoints?: number | null): ClimbDto[];
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Analyse what the racing line *would* do to `path`, without touching it.
|
|
666
|
+
*
|
|
667
|
+
* Read-only by construction — it takes a `Path` and returns numbers, so asking the question can
|
|
668
|
+
* never move a coordinate. That is the point: the stage rewrites every position it is applied to,
|
|
669
|
+
* and being able to inspect the corridor it assumed and the offset it chose is what makes that
|
|
670
|
+
* inspectable rather than merely optional.
|
|
671
|
+
*
|
|
672
|
+
* Returns `null` for a path too short or too degenerate to analyse, matching the Kotlin API's
|
|
673
|
+
* contract of declining rather than guessing.
|
|
674
|
+
*
|
|
675
|
+
* The `options` are the same `racingLine*` keys `enhance` accepts; `enabled` is ignored here, since
|
|
676
|
+
* calling this *is* the request.
|
|
677
|
+
*/
|
|
678
|
+
export declare function analyzeRacingLine(path: Path, options?: EnhanceOptionsDto | null): RacingLineReportDto | null;
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* The engine's own defaults for `detectClimbsWithOptions`, read off `ClimbOptions`.
|
|
682
|
+
*
|
|
683
|
+
* `detectClimbsWithOptions` takes its options **positionally**, so changing the last one means
|
|
684
|
+
* naming all the others and there is no field to omit. These are those values, generated
|
|
685
|
+
* from the same catalogue the CLI and WASI doors resolve their defaults through — never
|
|
686
|
+
* transcribed. Spread it and override what you need.
|
|
687
|
+
*/
|
|
688
|
+
export declare const climbDefaults: {
|
|
689
|
+
readonly minMinClimbElevationM: number;
|
|
690
|
+
readonly maxMinClimbElevationM: number;
|
|
691
|
+
readonly minClimbElevationRatio: number;
|
|
692
|
+
readonly minGradePercent: number;
|
|
693
|
+
readonly maxDiffRealGrade: number;
|
|
694
|
+
readonly booster: number;
|
|
695
|
+
readonly maxAnalysisPoints: number;
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* The engine's own defaults for `pathToCsv`, read off `CsvOptions`.
|
|
700
|
+
*
|
|
701
|
+
* `pathToCsv` takes its options **positionally**, so changing the last one means
|
|
702
|
+
* naming all the others and there is no field to omit. These are those values, generated
|
|
703
|
+
* from the same catalogue the CLI and WASI doors resolve their defaults through — never
|
|
704
|
+
* transcribed. Spread it and override what you need.
|
|
705
|
+
*/
|
|
706
|
+
export declare const csvDefaults: {
|
|
707
|
+
readonly separator: ',';
|
|
708
|
+
readonly unitsInHeader: boolean;
|
|
709
|
+
readonly decimals: null;
|
|
710
|
+
readonly lineSeparator: '\n';
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* The engine's own defaults for `pathToJson`, read off `JsonOptions`.
|
|
715
|
+
*
|
|
716
|
+
* `pathToJson` takes its options **positionally**, so changing the last one means
|
|
717
|
+
* naming all the others and there is no field to omit. These are those values, generated
|
|
718
|
+
* from the same catalogue the CLI and WASI doors resolve their defaults through — never
|
|
719
|
+
* transcribed. Spread it and override what you need.
|
|
720
|
+
*/
|
|
721
|
+
export declare const jsonDefaults: {
|
|
722
|
+
readonly pretty: boolean;
|
|
723
|
+
readonly decimals: null;
|
|
724
|
+
readonly includeMeta: boolean;
|
|
725
|
+
};
|