@hypersoniclabs/helix-cli 0.1.25-helix3.524 → 0.1.25-helix3.525
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/dist/device.js +96 -4
- package/dist/device.js.map +1 -1
- package/dist/deviceActionValidation.js +1 -1
- package/dist/deviceActionValidation.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_abs.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_audio.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_engine.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_expression.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_jbeam.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_materials.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_stock_events.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/fmod_studio.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/fsb5.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/sim_audio.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/sim_audio_ac.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/sim_audio_beamng.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/sim_audio_fixtures.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/test_beamng_engine.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/test_sim_audio.cpython-312.pyc +0 -0
package/dist/device.js
CHANGED
|
@@ -477,8 +477,36 @@ function validateGraphSteps(steps, context, path, depth) {
|
|
|
477
477
|
graphNode(context, step.node, stepPath);
|
|
478
478
|
if (!['x', 'y', 'z'].includes(String(step.axis)))
|
|
479
479
|
context.errors.push(`${stepPath}: rotateNode axis must be x, y or z`);
|
|
480
|
-
if (!['toAngle', 'stop'].includes(String(step.mode)))
|
|
481
|
-
context.errors.push(`${stepPath}: rotateNode mode must be toAngle or
|
|
480
|
+
if (!['toAngle', 'stop', 'spin', 'sweep'].includes(String(step.mode)))
|
|
481
|
+
context.errors.push(`${stepPath}: rotateNode mode must be toAngle, stop, spin or sweep`);
|
|
482
|
+
// spin turns continuously at `rpm` (an expression, so Speed can drive it); sweep swings `amplitudeDeg` either side every `periodMs`
|
|
483
|
+
// (device audio r7 engine). Both are presentation only.
|
|
484
|
+
if (step.mode === 'spin') {
|
|
485
|
+
if (step.rpm === undefined)
|
|
486
|
+
context.errors.push(`${stepPath}: rotateNode spin needs rpm`);
|
|
487
|
+
else
|
|
488
|
+
validateGraphExpr(step.rpm, context, `${stepPath}.rpm`);
|
|
489
|
+
}
|
|
490
|
+
else if (step.rpm !== undefined) {
|
|
491
|
+
context.errors.push(`${stepPath}: rotateNode rpm applies to mode "spin" only`);
|
|
492
|
+
}
|
|
493
|
+
if (step.mode === 'sweep') {
|
|
494
|
+
if (step.amplitudeDeg === undefined || step.periodMs === undefined)
|
|
495
|
+
context.errors.push(`${stepPath}: rotateNode sweep needs amplitudeDeg and periodMs`);
|
|
496
|
+
if (step.amplitudeDeg !== undefined &&
|
|
497
|
+
(!isFiniteNumber(step.amplitudeDeg) ||
|
|
498
|
+
step.amplitudeDeg < 0 ||
|
|
499
|
+
step.amplitudeDeg > 180))
|
|
500
|
+
context.errors.push(`${stepPath}: rotateNode amplitudeDeg must be a number from 0 to 180`);
|
|
501
|
+
if (step.periodMs !== undefined &&
|
|
502
|
+
(!isFiniteNumber(step.periodMs) ||
|
|
503
|
+
step.periodMs < 200 ||
|
|
504
|
+
step.periodMs > 60_000))
|
|
505
|
+
context.errors.push(`${stepPath}: rotateNode periodMs must be a number from 200 to 60000`);
|
|
506
|
+
}
|
|
507
|
+
else if (step.amplitudeDeg !== undefined || step.periodMs !== undefined) {
|
|
508
|
+
context.errors.push(`${stepPath}: rotateNode amplitudeDeg and periodMs apply to mode "sweep" only`);
|
|
509
|
+
}
|
|
482
510
|
if (step.targetDeg !== undefined)
|
|
483
511
|
validateGraphExpr(step.targetDeg, context, `${stepPath}.targetDeg`);
|
|
484
512
|
if (step.durationMs !== undefined && !isFiniteNumber(step.durationMs))
|
|
@@ -1256,7 +1284,8 @@ function applySteps(steps, definition, state, budget) {
|
|
|
1256
1284
|
}
|
|
1257
1285
|
function invoke(definition, state, interaction, value) {
|
|
1258
1286
|
const control = object(interaction.control);
|
|
1259
|
-
|
|
1287
|
+
// a time control's value is a time of day, not the state value: its action writes the instant
|
|
1288
|
+
if (control.stateKey && control.kind !== 'time' && value !== undefined) {
|
|
1260
1289
|
const key = String(control.stateKey);
|
|
1261
1290
|
state[key] = coerce(object(object(definition.state)[key]), value);
|
|
1262
1291
|
}
|
|
@@ -1274,6 +1303,8 @@ function exerciseValue(interaction, state) {
|
|
|
1274
1303
|
const control = object(interaction.control);
|
|
1275
1304
|
if (control.kind === 'number')
|
|
1276
1305
|
return Number(control.max ?? control.min ?? 1);
|
|
1306
|
+
if (control.kind === 'time')
|
|
1307
|
+
return 7 * 3600 + 35 * 60; // seconds after the viewer's local midnight
|
|
1277
1308
|
if (control.kind === 'choice') {
|
|
1278
1309
|
const options = Array.isArray(control.options)
|
|
1279
1310
|
? control.options.map(object)
|
|
@@ -1486,6 +1517,41 @@ function validateDeviceProject(input, options = {}) {
|
|
|
1486
1517
|
if (part.pivot !== undefined && part.pivot !== 'bottom') {
|
|
1487
1518
|
errors.push(`${partPath}.pivot must be bottom`);
|
|
1488
1519
|
}
|
|
1520
|
+
// `parent` (device audio r7 engine): position and rotationDeg are then in that part's frame and a step that moves the parent carries its subtree.
|
|
1521
|
+
if (part.parent !== undefined) {
|
|
1522
|
+
if (typeof part.parent !== 'string' || !part.parent.length || part.parent.length > 64) {
|
|
1523
|
+
errors.push(`${partPath}.parent must be the name of another part`);
|
|
1524
|
+
}
|
|
1525
|
+
else if (part.parent === name) {
|
|
1526
|
+
errors.push(`${partPath}.parent cannot be the part itself`);
|
|
1527
|
+
}
|
|
1528
|
+
else if (!Object.prototype.hasOwnProperty.call(parts, part.parent)) {
|
|
1529
|
+
errors.push(`${partPath}.parent "${part.parent}" is not a part of this model`);
|
|
1530
|
+
}
|
|
1531
|
+
else {
|
|
1532
|
+
let depth = 0;
|
|
1533
|
+
let cursor = name;
|
|
1534
|
+
const seen = new Set();
|
|
1535
|
+
while (cursor !== undefined && object(parts[cursor]).parent !== undefined) {
|
|
1536
|
+
if (seen.has(cursor)) {
|
|
1537
|
+
errors.push(`${partPath}.parent chain loops back on "${cursor}"`);
|
|
1538
|
+
break;
|
|
1539
|
+
}
|
|
1540
|
+
seen.add(cursor);
|
|
1541
|
+
const next = object(parts[cursor]).parent;
|
|
1542
|
+
cursor = typeof next === 'string' ? next : undefined;
|
|
1543
|
+
depth += 1;
|
|
1544
|
+
if (depth > 8) {
|
|
1545
|
+
errors.push(`${partPath}.parent chain is deeper than 8`);
|
|
1546
|
+
break;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
// `origin`: the shape-local point that sits at `position` and that the part (and its children) turns about (generalises pivot "bottom").
|
|
1552
|
+
if (part.origin !== undefined && !isNumberTriple(part.origin)) {
|
|
1553
|
+
errors.push(`${partPath}.origin must be three numbers`);
|
|
1554
|
+
}
|
|
1489
1555
|
if (part.color !== undefined && !Number.isInteger(part.color)) {
|
|
1490
1556
|
errors.push(`${partPath}.color must be an integer`);
|
|
1491
1557
|
}
|
|
@@ -1666,6 +1732,32 @@ function validateDeviceProject(input, options = {}) {
|
|
|
1666
1732
|
errors.push(`interaction ${id} disabled choice "${String(option.value)}" needs a reason`);
|
|
1667
1733
|
}
|
|
1668
1734
|
}
|
|
1735
|
+
else if (control.kind === 'time') {
|
|
1736
|
+
// A time of day set and shown in the VIEWER's zone (device audio r7 engine). The room keeps one absolute instant in `stateKey`; the control's
|
|
1737
|
+
// `value` is seconds after the viewer's local midnight and is NOT written to the state: the action turns it into the instant.
|
|
1738
|
+
if (!['float', 'integer'].includes(String(spec.type)))
|
|
1739
|
+
errors.push(`interaction ${id} time control stateKey "${key}" must be a declared integer or float state (epoch milliseconds)`);
|
|
1740
|
+
if (control.setStateKey !== undefined) {
|
|
1741
|
+
const setSpec = object(state[String(control.setStateKey)]);
|
|
1742
|
+
if (!['float', 'integer'].includes(String(setSpec.type)))
|
|
1743
|
+
errors.push(`interaction ${id} time control setStateKey "${String(control.setStateKey)}" must be a declared integer or float state (epoch milliseconds)`);
|
|
1744
|
+
}
|
|
1745
|
+
if (control.stepMinutes !== undefined &&
|
|
1746
|
+
(!Number.isInteger(control.stepMinutes) ||
|
|
1747
|
+
Number(control.stepMinutes) < 1 ||
|
|
1748
|
+
Number(control.stepMinutes) > 60))
|
|
1749
|
+
errors.push(`interaction ${id} time stepMinutes must be an integer 1..60`);
|
|
1750
|
+
const action = object(interaction.action);
|
|
1751
|
+
if (interaction.action === undefined) {
|
|
1752
|
+
errors.push(`interaction ${id} a time control needs an action that turns value (seconds after the viewer's local midnight) into the instant`);
|
|
1753
|
+
}
|
|
1754
|
+
else if (!Object.values(object(action.args)).some((arg) => {
|
|
1755
|
+
const a = object(arg);
|
|
1756
|
+
return a.type === 'number' && a.fill === 'viewer.localMidnightSec';
|
|
1757
|
+
})) {
|
|
1758
|
+
errors.push(`interaction ${id} a time control's action needs a number arg with fill "viewer.localMidnightSec" to turn the time of day into an instant`);
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1669
1761
|
else if (control.kind === 'text') {
|
|
1670
1762
|
if (spec.type !== 'string')
|
|
1671
1763
|
errors.push(`interaction ${id} text control must target string state`);
|
|
@@ -1684,7 +1776,7 @@ function validateDeviceProject(input, options = {}) {
|
|
|
1684
1776
|
}
|
|
1685
1777
|
}
|
|
1686
1778
|
else if (control.kind === undefined) {
|
|
1687
|
-
errors.push(`interaction ${id} control requires a kind (number|choice|text)`);
|
|
1779
|
+
errors.push(`interaction ${id} control requires a kind (number|choice|text|time)`);
|
|
1688
1780
|
}
|
|
1689
1781
|
else {
|
|
1690
1782
|
errors.push(`interaction ${id} uses unsupported control kind "${String(control.kind)}"`);
|