@its-not-rocket-science/ananke 0.1.0
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/CHANGELOG.md +135 -0
- package/LICENSE +21 -0
- package/README.md +2199 -0
- package/STABLE_API.md +266 -0
- package/dist/src/anatomy/anatomy-compiler.d.ts +14 -0
- package/dist/src/anatomy/anatomy-compiler.js +277 -0
- package/dist/src/anatomy/anatomy-contracts.d.ts +94 -0
- package/dist/src/anatomy/anatomy-contracts.js +1 -0
- package/dist/src/anatomy/anatomy-helpers.d.ts +82 -0
- package/dist/src/anatomy/anatomy-helpers.js +233 -0
- package/dist/src/anatomy/anatomy-schema.d.ts +28 -0
- package/dist/src/anatomy/anatomy-schema.js +388 -0
- package/dist/src/anatomy/index.d.ts +4 -0
- package/dist/src/anatomy/index.js +4 -0
- package/dist/src/archetypes.d.ts +87 -0
- package/dist/src/archetypes.js +285 -0
- package/dist/src/arena.d.ts +173 -0
- package/dist/src/arena.js +695 -0
- package/dist/src/bridge/bridge-engine.d.ts +46 -0
- package/dist/src/bridge/bridge-engine.js +252 -0
- package/dist/src/bridge/index.d.ts +4 -0
- package/dist/src/bridge/index.js +5 -0
- package/dist/src/bridge/interpolation.d.ts +64 -0
- package/dist/src/bridge/interpolation.js +130 -0
- package/dist/src/bridge/mapping.d.ts +33 -0
- package/dist/src/bridge/mapping.js +54 -0
- package/dist/src/bridge/types.d.ts +94 -0
- package/dist/src/bridge/types.js +2 -0
- package/dist/src/campaign.d.ts +141 -0
- package/dist/src/campaign.js +235 -0
- package/dist/src/channels.d.ts +15 -0
- package/dist/src/channels.js +20 -0
- package/dist/src/chronicle.d.ts +124 -0
- package/dist/src/chronicle.js +232 -0
- package/dist/src/collective-activities.d.ts +154 -0
- package/dist/src/collective-activities.js +247 -0
- package/dist/src/competence/acoustic.d.ts +101 -0
- package/dist/src/competence/acoustic.js +242 -0
- package/dist/src/competence/catalogue.d.ts +30 -0
- package/dist/src/competence/catalogue.js +241 -0
- package/dist/src/competence/crafting.d.ts +35 -0
- package/dist/src/competence/crafting.js +88 -0
- package/dist/src/competence/engineering.d.ts +53 -0
- package/dist/src/competence/engineering.js +108 -0
- package/dist/src/competence/framework.d.ts +68 -0
- package/dist/src/competence/framework.js +694 -0
- package/dist/src/competence/index.d.ts +12 -0
- package/dist/src/competence/index.js +13 -0
- package/dist/src/competence/interspecies.d.ts +81 -0
- package/dist/src/competence/interspecies.js +108 -0
- package/dist/src/competence/language.d.ts +79 -0
- package/dist/src/competence/language.js +115 -0
- package/dist/src/competence/naturalist.d.ts +97 -0
- package/dist/src/competence/naturalist.js +187 -0
- package/dist/src/competence/navigation.d.ts +24 -0
- package/dist/src/competence/navigation.js +48 -0
- package/dist/src/competence/performance.d.ts +125 -0
- package/dist/src/competence/performance.js +210 -0
- package/dist/src/competence/teaching.d.ts +64 -0
- package/dist/src/competence/teaching.js +121 -0
- package/dist/src/competence/willpower.d.ts +74 -0
- package/dist/src/competence/willpower.js +114 -0
- package/dist/src/crafting/index.d.ts +55 -0
- package/dist/src/crafting/index.js +229 -0
- package/dist/src/crafting/manufacturing.d.ts +83 -0
- package/dist/src/crafting/manufacturing.js +165 -0
- package/dist/src/crafting/materials.d.ts +53 -0
- package/dist/src/crafting/materials.js +120 -0
- package/dist/src/crafting/recipes.d.ts +75 -0
- package/dist/src/crafting/recipes.js +233 -0
- package/dist/src/crafting/workshops.d.ts +61 -0
- package/dist/src/crafting/workshops.js +170 -0
- package/dist/src/debug.d.ts +86 -0
- package/dist/src/debug.js +76 -0
- package/dist/src/derive.d.ts +21 -0
- package/dist/src/derive.js +88 -0
- package/dist/src/describe.d.ts +29 -0
- package/dist/src/describe.js +276 -0
- package/dist/src/dialogue.d.ts +122 -0
- package/dist/src/dialogue.js +266 -0
- package/dist/src/dist.d.ts +20 -0
- package/dist/src/dist.js +39 -0
- package/dist/src/downtime.d.ts +89 -0
- package/dist/src/downtime.js +391 -0
- package/dist/src/economy.d.ts +116 -0
- package/dist/src/economy.js +182 -0
- package/dist/src/emotional-contagion.d.ts +142 -0
- package/dist/src/emotional-contagion.js +274 -0
- package/dist/src/equipment.d.ts +206 -0
- package/dist/src/equipment.js +598 -0
- package/dist/src/faction.d.ts +102 -0
- package/dist/src/faction.js +237 -0
- package/dist/src/generate.d.ts +35 -0
- package/dist/src/generate.js +166 -0
- package/dist/src/index.d.ts +42 -0
- package/dist/src/index.js +54 -0
- package/dist/src/inheritance.d.ts +69 -0
- package/dist/src/inheritance.js +136 -0
- package/dist/src/inventory.d.ts +194 -0
- package/dist/src/inventory.js +637 -0
- package/dist/src/item-durability.d.ts +69 -0
- package/dist/src/item-durability.js +308 -0
- package/dist/src/legend.d.ts +97 -0
- package/dist/src/legend.js +269 -0
- package/dist/src/lod.d.ts +9 -0
- package/dist/src/lod.js +84 -0
- package/dist/src/metrics.d.ts +51 -0
- package/dist/src/metrics.js +91 -0
- package/dist/src/model3d.d.ts +138 -0
- package/dist/src/model3d.js +214 -0
- package/dist/src/mythology.d.ts +101 -0
- package/dist/src/mythology.js +308 -0
- package/dist/src/narrative-render.d.ts +42 -0
- package/dist/src/narrative-render.js +194 -0
- package/dist/src/narrative-stress.d.ts +123 -0
- package/dist/src/narrative-stress.js +183 -0
- package/dist/src/narrative.d.ts +44 -0
- package/dist/src/narrative.js +257 -0
- package/dist/src/party.d.ts +70 -0
- package/dist/src/party.js +226 -0
- package/dist/src/polity.d.ts +262 -0
- package/dist/src/polity.js +398 -0
- package/dist/src/presets.d.ts +42 -0
- package/dist/src/presets.js +170 -0
- package/dist/src/progression.d.ts +170 -0
- package/dist/src/progression.js +256 -0
- package/dist/src/quest-generators.d.ts +76 -0
- package/dist/src/quest-generators.js +534 -0
- package/dist/src/quest.d.ts +239 -0
- package/dist/src/quest.js +520 -0
- package/dist/src/relationships-effects.d.ts +75 -0
- package/dist/src/relationships-effects.js +219 -0
- package/dist/src/relationships.d.ts +104 -0
- package/dist/src/relationships.js +347 -0
- package/dist/src/replay.d.ts +47 -0
- package/dist/src/replay.js +82 -0
- package/dist/src/rng.d.ts +9 -0
- package/dist/src/rng.js +37 -0
- package/dist/src/settlement-services.d.ts +67 -0
- package/dist/src/settlement-services.js +267 -0
- package/dist/src/settlement.d.ts +143 -0
- package/dist/src/settlement.js +419 -0
- package/dist/src/sim/action.d.ts +28 -0
- package/dist/src/sim/action.js +12 -0
- package/dist/src/sim/aging.d.ts +95 -0
- package/dist/src/sim/aging.js +243 -0
- package/dist/src/sim/ai/decide.d.ts +10 -0
- package/dist/src/sim/ai/decide.js +267 -0
- package/dist/src/sim/ai/perception.d.ts +12 -0
- package/dist/src/sim/ai/perception.js +54 -0
- package/dist/src/sim/ai/personality.d.ts +54 -0
- package/dist/src/sim/ai/personality.js +202 -0
- package/dist/src/sim/ai/presets.d.ts +2 -0
- package/dist/src/sim/ai/presets.js +28 -0
- package/dist/src/sim/ai/system.d.ts +6 -0
- package/dist/src/sim/ai/system.js +13 -0
- package/dist/src/sim/ai/targeting.d.ts +8 -0
- package/dist/src/sim/ai/targeting.js +42 -0
- package/dist/src/sim/ai/types.d.ts +14 -0
- package/dist/src/sim/ai/types.js +1 -0
- package/dist/src/sim/body.d.ts +9 -0
- package/dist/src/sim/body.js +32 -0
- package/dist/src/sim/bodyplan.d.ts +161 -0
- package/dist/src/sim/bodyplan.js +677 -0
- package/dist/src/sim/capability.d.ts +135 -0
- package/dist/src/sim/capability.js +8 -0
- package/dist/src/sim/combat.d.ts +21 -0
- package/dist/src/sim/combat.js +77 -0
- package/dist/src/sim/commandBuilders.d.ts +11 -0
- package/dist/src/sim/commandBuilders.js +39 -0
- package/dist/src/sim/commands.d.ts +71 -0
- package/dist/src/sim/commands.js +8 -0
- package/dist/src/sim/condition.d.ts +35 -0
- package/dist/src/sim/condition.js +21 -0
- package/dist/src/sim/cone.d.ts +40 -0
- package/dist/src/sim/cone.js +44 -0
- package/dist/src/sim/context.d.ts +68 -0
- package/dist/src/sim/context.js +1 -0
- package/dist/src/sim/density.d.ts +14 -0
- package/dist/src/sim/density.js +33 -0
- package/dist/src/sim/disease.d.ts +141 -0
- package/dist/src/sim/disease.js +353 -0
- package/dist/src/sim/entity.d.ts +251 -0
- package/dist/src/sim/entity.js +19 -0
- package/dist/src/sim/events.d.ts +25 -0
- package/dist/src/sim/events.js +5 -0
- package/dist/src/sim/explosion.d.ts +40 -0
- package/dist/src/sim/explosion.js +40 -0
- package/dist/src/sim/formation-unit.d.ts +138 -0
- package/dist/src/sim/formation-unit.js +197 -0
- package/dist/src/sim/formation.d.ts +12 -0
- package/dist/src/sim/formation.js +54 -0
- package/dist/src/sim/frontage.d.ts +30 -0
- package/dist/src/sim/frontage.js +84 -0
- package/dist/src/sim/grapple.d.ts +100 -0
- package/dist/src/sim/grapple.js +480 -0
- package/dist/src/sim/hazard.d.ts +104 -0
- package/dist/src/sim/hazard.js +201 -0
- package/dist/src/sim/hydrostatic.d.ts +58 -0
- package/dist/src/sim/hydrostatic.js +117 -0
- package/dist/src/sim/impairment.d.ts +20 -0
- package/dist/src/sim/impairment.js +162 -0
- package/dist/src/sim/indexing.d.ts +7 -0
- package/dist/src/sim/indexing.js +7 -0
- package/dist/src/sim/injury.d.ts +54 -0
- package/dist/src/sim/injury.js +66 -0
- package/dist/src/sim/intent.d.ts +26 -0
- package/dist/src/sim/intent.js +7 -0
- package/dist/src/sim/kernel.d.ts +45 -0
- package/dist/src/sim/kernel.js +1992 -0
- package/dist/src/sim/kinds.d.ts +64 -0
- package/dist/src/sim/kinds.js +56 -0
- package/dist/src/sim/knockback.d.ts +50 -0
- package/dist/src/sim/knockback.js +82 -0
- package/dist/src/sim/limb.d.ts +48 -0
- package/dist/src/sim/limb.js +78 -0
- package/dist/src/sim/medical.d.ts +32 -0
- package/dist/src/sim/medical.js +33 -0
- package/dist/src/sim/morale.d.ts +69 -0
- package/dist/src/sim/morale.js +92 -0
- package/dist/src/sim/mount.d.ts +150 -0
- package/dist/src/sim/mount.js +225 -0
- package/dist/src/sim/nutrition.d.ts +74 -0
- package/dist/src/sim/nutrition.js +168 -0
- package/dist/src/sim/occlusion.d.ts +8 -0
- package/dist/src/sim/occlusion.js +71 -0
- package/dist/src/sim/push.d.ts +11 -0
- package/dist/src/sim/push.js +79 -0
- package/dist/src/sim/ranged.d.ts +44 -0
- package/dist/src/sim/ranged.js +69 -0
- package/dist/src/sim/seeds.d.ts +3 -0
- package/dist/src/sim/seeds.js +16 -0
- package/dist/src/sim/sensory-extended.d.ts +103 -0
- package/dist/src/sim/sensory-extended.js +181 -0
- package/dist/src/sim/sensory.d.ts +38 -0
- package/dist/src/sim/sensory.js +109 -0
- package/dist/src/sim/skills.d.ts +70 -0
- package/dist/src/sim/skills.js +69 -0
- package/dist/src/sim/sleep.d.ts +107 -0
- package/dist/src/sim/sleep.js +215 -0
- package/dist/src/sim/spatial.d.ts +8 -0
- package/dist/src/sim/spatial.js +59 -0
- package/dist/src/sim/step/capability.d.ts +8 -0
- package/dist/src/sim/step/capability.js +77 -0
- package/dist/src/sim/step/concentration.d.ts +9 -0
- package/dist/src/sim/step/concentration.js +25 -0
- package/dist/src/sim/step/effects.d.ts +17 -0
- package/dist/src/sim/step/effects.js +96 -0
- package/dist/src/sim/step/energy.d.ts +3 -0
- package/dist/src/sim/step/energy.js +31 -0
- package/dist/src/sim/step/hazards.d.ts +4 -0
- package/dist/src/sim/step/hazards.js +19 -0
- package/dist/src/sim/step/injury.d.ts +10 -0
- package/dist/src/sim/step/injury.js +353 -0
- package/dist/src/sim/step/morale.d.ts +11 -0
- package/dist/src/sim/step/morale.js +130 -0
- package/dist/src/sim/step/movement.d.ts +5 -0
- package/dist/src/sim/step/movement.js +172 -0
- package/dist/src/sim/step/push.d.ts +11 -0
- package/dist/src/sim/step/push.js +79 -0
- package/dist/src/sim/step/substances.d.ts +3 -0
- package/dist/src/sim/step/substances.js +75 -0
- package/dist/src/sim/substance.d.ts +38 -0
- package/dist/src/sim/substance.js +57 -0
- package/dist/src/sim/systemic-toxicology.d.ts +109 -0
- package/dist/src/sim/systemic-toxicology.js +263 -0
- package/dist/src/sim/team.d.ts +9 -0
- package/dist/src/sim/team.js +37 -0
- package/dist/src/sim/tech.d.ts +36 -0
- package/dist/src/sim/tech.js +46 -0
- package/dist/src/sim/terrain.d.ts +121 -0
- package/dist/src/sim/terrain.js +141 -0
- package/dist/src/sim/testing.d.ts +13 -0
- package/dist/src/sim/testing.js +100 -0
- package/dist/src/sim/thermoregulation.d.ts +77 -0
- package/dist/src/sim/thermoregulation.js +161 -0
- package/dist/src/sim/tick.d.ts +3 -0
- package/dist/src/sim/tick.js +3 -0
- package/dist/src/sim/toxicology.d.ts +52 -0
- package/dist/src/sim/toxicology.js +104 -0
- package/dist/src/sim/trace.d.ts +141 -0
- package/dist/src/sim/trace.js +1 -0
- package/dist/src/sim/tuning.d.ts +16 -0
- package/dist/src/sim/tuning.js +42 -0
- package/dist/src/sim/vec3.d.ts +14 -0
- package/dist/src/sim/vec3.js +31 -0
- package/dist/src/sim/weapon_dynamics.d.ts +102 -0
- package/dist/src/sim/weapon_dynamics.js +142 -0
- package/dist/src/sim/weather.d.ts +95 -0
- package/dist/src/sim/weather.js +105 -0
- package/dist/src/sim/world.d.ts +52 -0
- package/dist/src/sim/world.js +1 -0
- package/dist/src/sim/wound-aging.d.ts +120 -0
- package/dist/src/sim/wound-aging.js +223 -0
- package/dist/src/species.d.ts +106 -0
- package/dist/src/species.js +664 -0
- package/dist/src/story-arcs.d.ts +17 -0
- package/dist/src/story-arcs.js +276 -0
- package/dist/src/tech-diffusion.d.ts +80 -0
- package/dist/src/tech-diffusion.js +185 -0
- package/dist/src/traits.d.ts +25 -0
- package/dist/src/traits.js +178 -0
- package/dist/src/types.d.ts +117 -0
- package/dist/src/types.js +1 -0
- package/dist/src/units.d.ts +41 -0
- package/dist/src/units.js +64 -0
- package/dist/src/weapons.d.ts +20 -0
- package/dist/src/weapons.js +824 -0
- package/dist/src/world-generation.d.ts +52 -0
- package/dist/src/world-generation.js +301 -0
- package/package.json +74 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { BridgeConfig, InterpolatedState } from "./types.js";
|
|
2
|
+
import type { RigSnapshot } from "../model3d.js";
|
|
3
|
+
import type { MotionVector, ConditionSample } from "../debug.js";
|
|
4
|
+
export declare class BridgeEngine {
|
|
5
|
+
private config;
|
|
6
|
+
private entities;
|
|
7
|
+
/** Simulation time of the previous snapshot (seconds) */
|
|
8
|
+
private prevTime_s;
|
|
9
|
+
/** Simulation time of the current snapshot (seconds) */
|
|
10
|
+
private currTime_s;
|
|
11
|
+
/** Corresponding tick numbers */
|
|
12
|
+
private prevTick;
|
|
13
|
+
private currTick;
|
|
14
|
+
/** Default bone name for unmapped segments */
|
|
15
|
+
private defaultBoneName;
|
|
16
|
+
constructor(config: BridgeConfig);
|
|
17
|
+
/** Update the bridge configuration (does not affect already stored snapshots). */
|
|
18
|
+
updateConfig(config: BridgeConfig): void;
|
|
19
|
+
/** Register or update the body plan ID for an entity. */
|
|
20
|
+
setEntityBodyPlan(entityId: number, bodyPlanId: string): void;
|
|
21
|
+
/**
|
|
22
|
+
* Ingests new simulation tick data.
|
|
23
|
+
* Called at simulation rate (typically 20 Hz).
|
|
24
|
+
* @param snapshots Rig snapshots from extractRigSnapshots()
|
|
25
|
+
* @param motion Motion vectors from extractMotionVectors() (optional)
|
|
26
|
+
* @param condition Condition samples from extractConditionSamples() (optional)
|
|
27
|
+
*/
|
|
28
|
+
update(snapshots: RigSnapshot[], motion?: MotionVector[], condition?: ConditionSample[]): void;
|
|
29
|
+
/**
|
|
30
|
+
* Compute interpolated state for an entity at a given render time.
|
|
31
|
+
* @param entityId Entity ID
|
|
32
|
+
* @param renderTime_s Render time in real seconds (monotonically increasing)
|
|
33
|
+
* @returns Interpolated state, or null if entity not found or insufficient data
|
|
34
|
+
*/
|
|
35
|
+
getInterpolatedState(entityId: number, renderTime_s: number): InterpolatedState | null;
|
|
36
|
+
/** Get the simulation time (seconds) of the latest tick. */
|
|
37
|
+
getLatestSimTime(): number;
|
|
38
|
+
/** Get the simulation tick of the latest snapshot. */
|
|
39
|
+
getLatestTick(): number;
|
|
40
|
+
/** Check if an entity has at least one snapshot. */
|
|
41
|
+
hasEntity(entityId: number): boolean;
|
|
42
|
+
/** Remove an entity from the bridge (e.g., after death and removal from sim). */
|
|
43
|
+
removeEntity(entityId: number): void;
|
|
44
|
+
/** Clear all stored snapshots (reset). */
|
|
45
|
+
clear(): void;
|
|
46
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// src/bridge/bridge‑engine.ts — Main bridge engine (double‑buffered, interpolating)
|
|
2
|
+
import { SCALE } from "../units.js";
|
|
3
|
+
import { DT_S } from "../sim/tick.js";
|
|
4
|
+
import { lerpVec3, slerpFacing, interpolatePoseModifiers, interpolateAnimationHints, interpolateCondition } from "./interpolation.js";
|
|
5
|
+
import { findBodyPlanMapping, mapPoseModifiers } from "./mapping.js";
|
|
6
|
+
// ─── BridgeEngine ─────────────────────────────────────────────────────────────
|
|
7
|
+
export class BridgeEngine {
|
|
8
|
+
config;
|
|
9
|
+
entities = new Map();
|
|
10
|
+
/** Simulation time of the previous snapshot (seconds) */
|
|
11
|
+
prevTime_s = 0;
|
|
12
|
+
/** Simulation time of the current snapshot (seconds) */
|
|
13
|
+
currTime_s = 0;
|
|
14
|
+
/** Corresponding tick numbers */
|
|
15
|
+
prevTick = 0;
|
|
16
|
+
currTick = 0;
|
|
17
|
+
/** Default bone name for unmapped segments */
|
|
18
|
+
defaultBoneName;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.config = config;
|
|
21
|
+
this.defaultBoneName = config.defaultBoneName ?? "root";
|
|
22
|
+
}
|
|
23
|
+
// ─── Configuration ───────────────────────────────────────────────────────────
|
|
24
|
+
/** Update the bridge configuration (does not affect already stored snapshots). */
|
|
25
|
+
updateConfig(config) {
|
|
26
|
+
this.config = config;
|
|
27
|
+
this.defaultBoneName = config.defaultBoneName ?? "root";
|
|
28
|
+
}
|
|
29
|
+
/** Register or update the body plan ID for an entity. */
|
|
30
|
+
setEntityBodyPlan(entityId, bodyPlanId) {
|
|
31
|
+
let rec = this.entities.get(entityId);
|
|
32
|
+
if (!rec) {
|
|
33
|
+
rec = { bodyPlanId, prev: null, curr: null };
|
|
34
|
+
this.entities.set(entityId, rec);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
rec.bodyPlanId = bodyPlanId;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// ─── Tick data ingestion ────────────────────────────────────────────────────
|
|
41
|
+
/**
|
|
42
|
+
* Ingests new simulation tick data.
|
|
43
|
+
* Called at simulation rate (typically 20 Hz).
|
|
44
|
+
* @param snapshots Rig snapshots from extractRigSnapshots()
|
|
45
|
+
* @param motion Motion vectors from extractMotionVectors() (optional)
|
|
46
|
+
* @param condition Condition samples from extractConditionSamples() (optional)
|
|
47
|
+
*/
|
|
48
|
+
update(snapshots, motion, condition) {
|
|
49
|
+
// Shift current → previous
|
|
50
|
+
for (const rec of this.entities.values()) {
|
|
51
|
+
rec.prev = rec.curr;
|
|
52
|
+
rec.curr = null;
|
|
53
|
+
}
|
|
54
|
+
// Update simulation time: assume each tick advances by DT_S
|
|
55
|
+
if (this.currTick > 0) {
|
|
56
|
+
this.prevTime_s = this.currTime_s;
|
|
57
|
+
this.prevTick = this.currTick;
|
|
58
|
+
}
|
|
59
|
+
// Use the first snapshot's tick as reference (all snapshots share same tick)
|
|
60
|
+
const tick = snapshots[0]?.tick ?? this.currTick + 1;
|
|
61
|
+
this.currTick = tick;
|
|
62
|
+
this.currTime_s = tick * (DT_S / SCALE.s); // convert fixed‑point seconds to real seconds
|
|
63
|
+
// Merge data into TickSnapshot per entity
|
|
64
|
+
const motionMap = new Map();
|
|
65
|
+
const conditionMap = new Map();
|
|
66
|
+
if (motion)
|
|
67
|
+
for (const m of motion)
|
|
68
|
+
motionMap.set(m.entityId, m);
|
|
69
|
+
if (condition)
|
|
70
|
+
for (const c of condition)
|
|
71
|
+
conditionMap.set(c.entityId, c);
|
|
72
|
+
for (const rig of snapshots) {
|
|
73
|
+
const mv = motionMap.get(rig.entityId);
|
|
74
|
+
const cond = conditionMap.get(rig.entityId);
|
|
75
|
+
const snapshot = {
|
|
76
|
+
entityId: rig.entityId,
|
|
77
|
+
teamId: rig.teamId,
|
|
78
|
+
tick: rig.tick,
|
|
79
|
+
position_m: mv?.position_m ?? { x: 0, y: 0, z: 0 },
|
|
80
|
+
velocity_mps: mv?.velocity_mps ?? { x: 0, y: 0, z: 0 },
|
|
81
|
+
facing: mv?.facing ?? { x: SCALE.Q, y: 0, z: 0 }, // default forward
|
|
82
|
+
animation: rig.animation,
|
|
83
|
+
poseModifiers: rig.pose,
|
|
84
|
+
grapple: rig.grapple,
|
|
85
|
+
condition: {
|
|
86
|
+
shockQ: cond?.shock ?? rig.animation.shockQ,
|
|
87
|
+
fearQ: cond?.fearQ ?? rig.animation.fearQ,
|
|
88
|
+
consciousness: cond?.consciousness ?? SCALE.Q,
|
|
89
|
+
fluidLoss: cond?.fluidLoss ?? 0,
|
|
90
|
+
dead: cond?.dead ?? rig.animation.dead,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
let rec = this.entities.get(rig.entityId);
|
|
94
|
+
if (!rec) {
|
|
95
|
+
// Body plan unknown; default to humanoid (host should call setEntityBodyPlan)
|
|
96
|
+
rec = { bodyPlanId: "humanoid", prev: null, curr: snapshot };
|
|
97
|
+
this.entities.set(rig.entityId, rec);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
rec.curr = snapshot;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Cleanup entities that disappeared (optional)
|
|
104
|
+
}
|
|
105
|
+
// ─── Interpolated state retrieval ───────────────────────────────────────────
|
|
106
|
+
/**
|
|
107
|
+
* Compute interpolated state for an entity at a given render time.
|
|
108
|
+
* @param entityId Entity ID
|
|
109
|
+
* @param renderTime_s Render time in real seconds (monotonically increasing)
|
|
110
|
+
* @returns Interpolated state, or null if entity not found or insufficient data
|
|
111
|
+
*/
|
|
112
|
+
getInterpolatedState(entityId, renderTime_s) {
|
|
113
|
+
const rec = this.entities.get(entityId);
|
|
114
|
+
if (!rec) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
// Determine which snapshots are available
|
|
118
|
+
const prev = rec.prev;
|
|
119
|
+
const curr = rec.curr;
|
|
120
|
+
if (!prev && !curr) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
// If only one snapshot is available, treat it as both prev and curr (hold)
|
|
124
|
+
const usePrev = prev ?? curr;
|
|
125
|
+
const useCurr = curr ?? prev;
|
|
126
|
+
const singleSnapshot = !prev || !curr;
|
|
127
|
+
// Find mapping; if none, fall back to default bone name (still produce state)
|
|
128
|
+
const mapping = findBodyPlanMapping(this.config, rec.bodyPlanId);
|
|
129
|
+
// Determine interpolation factor t ∈ [0, SCALE.Q]
|
|
130
|
+
let t;
|
|
131
|
+
let fromTick;
|
|
132
|
+
let toTick;
|
|
133
|
+
let mode;
|
|
134
|
+
if (singleSnapshot) {
|
|
135
|
+
// Only one snapshot: hold it regardless of render time
|
|
136
|
+
t = SCALE.Q;
|
|
137
|
+
fromTick = useCurr.tick;
|
|
138
|
+
toTick = useCurr.tick;
|
|
139
|
+
mode = "hold";
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
// Both snapshots available
|
|
143
|
+
if (renderTime_s <= this.prevTime_s) {
|
|
144
|
+
// Render time before previous snapshot: hold previous
|
|
145
|
+
t = 0;
|
|
146
|
+
fromTick = prev.tick;
|
|
147
|
+
toTick = prev.tick;
|
|
148
|
+
mode = "hold";
|
|
149
|
+
}
|
|
150
|
+
else if (renderTime_s >= this.currTime_s) {
|
|
151
|
+
// Render time at or after current snapshot
|
|
152
|
+
if (this.config.extrapolationAllowed) {
|
|
153
|
+
// Extrapolate forward using velocity
|
|
154
|
+
const delta = renderTime_s - this.currTime_s;
|
|
155
|
+
t = SCALE.Q;
|
|
156
|
+
fromTick = curr.tick;
|
|
157
|
+
toTick = curr.tick;
|
|
158
|
+
mode = "extrapolate";
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
// Hold current snapshot
|
|
162
|
+
t = SCALE.Q;
|
|
163
|
+
fromTick = curr.tick;
|
|
164
|
+
toTick = curr.tick;
|
|
165
|
+
mode = "hold";
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
// Normal interpolation between prev and curr
|
|
170
|
+
const interval = this.currTime_s - this.prevTime_s;
|
|
171
|
+
if (interval <= 0) {
|
|
172
|
+
t = SCALE.Q;
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
const frac = (renderTime_s - this.prevTime_s) / interval;
|
|
176
|
+
t = Math.max(0, Math.min(SCALE.Q, Math.round(frac * SCALE.Q)));
|
|
177
|
+
}
|
|
178
|
+
fromTick = prev.tick;
|
|
179
|
+
toTick = curr.tick;
|
|
180
|
+
mode = "lerp";
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Interpolate each component (if single snapshot, lerp with t = SCALE.Q yields curr)
|
|
184
|
+
const position_m = lerpVec3(usePrev.position_m, useCurr.position_m, t);
|
|
185
|
+
const velocity_mps = lerpVec3(usePrev.velocity_mps, useCurr.velocity_mps, t);
|
|
186
|
+
const facing = slerpFacing(usePrev.facing, useCurr.facing, t);
|
|
187
|
+
const animation = interpolateAnimationHints(usePrev.animation, useCurr.animation, t);
|
|
188
|
+
const pose = interpolatePoseModifiers(usePrev.poseModifiers, useCurr.poseModifiers, t);
|
|
189
|
+
const condition = interpolateCondition(usePrev.condition, useCurr.condition, t);
|
|
190
|
+
// Apply extrapolation if needed
|
|
191
|
+
if (mode === "extrapolate" && this.config.extrapolationAllowed) {
|
|
192
|
+
const delta = renderTime_s - this.currTime_s;
|
|
193
|
+
const deltaFixed = Math.round(delta * SCALE.s);
|
|
194
|
+
const dx = Math.trunc((curr.velocity_mps.x * deltaFixed) / SCALE.s);
|
|
195
|
+
const dy = Math.trunc((curr.velocity_mps.y * deltaFixed) / SCALE.s);
|
|
196
|
+
const dz = Math.trunc((curr.velocity_mps.z * deltaFixed) / SCALE.s);
|
|
197
|
+
position_m.x += dx;
|
|
198
|
+
position_m.y += dy;
|
|
199
|
+
position_m.z += dz;
|
|
200
|
+
}
|
|
201
|
+
// Map pose modifiers to bone names
|
|
202
|
+
const mappedPose = mapping
|
|
203
|
+
? mapPoseModifiers(pose, mapping, this.defaultBoneName)
|
|
204
|
+
: pose.map(p => ({
|
|
205
|
+
segmentId: p.segmentId,
|
|
206
|
+
boneName: this.defaultBoneName,
|
|
207
|
+
impairmentQ: p.impairmentQ,
|
|
208
|
+
structuralQ: p.structuralQ,
|
|
209
|
+
surfaceQ: p.surfaceQ,
|
|
210
|
+
}));
|
|
211
|
+
return {
|
|
212
|
+
entityId,
|
|
213
|
+
teamId: usePrev.teamId,
|
|
214
|
+
position_m,
|
|
215
|
+
velocity_mps,
|
|
216
|
+
facing,
|
|
217
|
+
animation,
|
|
218
|
+
poseModifiers: mappedPose,
|
|
219
|
+
grapple: t < SCALE.Q / 2 ? usePrev.grapple : useCurr.grapple,
|
|
220
|
+
condition,
|
|
221
|
+
interpolationFactor: t,
|
|
222
|
+
fromTick,
|
|
223
|
+
toTick,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
// ─── Utilities ───────────────────────────────────────────────────────────────
|
|
227
|
+
/** Get the simulation time (seconds) of the latest tick. */
|
|
228
|
+
getLatestSimTime() {
|
|
229
|
+
return this.currTime_s;
|
|
230
|
+
}
|
|
231
|
+
/** Get the simulation tick of the latest snapshot. */
|
|
232
|
+
getLatestTick() {
|
|
233
|
+
return this.currTick;
|
|
234
|
+
}
|
|
235
|
+
/** Check if an entity has at least one snapshot. */
|
|
236
|
+
hasEntity(entityId) {
|
|
237
|
+
const rec = this.entities.get(entityId);
|
|
238
|
+
return !!(rec && (rec.prev || rec.curr));
|
|
239
|
+
}
|
|
240
|
+
/** Remove an entity from the bridge (e.g., after death and removal from sim). */
|
|
241
|
+
removeEntity(entityId) {
|
|
242
|
+
this.entities.delete(entityId);
|
|
243
|
+
}
|
|
244
|
+
/** Clear all stored snapshots (reset). */
|
|
245
|
+
clear() {
|
|
246
|
+
this.entities.clear();
|
|
247
|
+
this.prevTime_s = 0;
|
|
248
|
+
this.currTime_s = 0;
|
|
249
|
+
this.prevTick = 0;
|
|
250
|
+
this.currTick = 0;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Q } from "../units.js";
|
|
2
|
+
import type { Vec3 } from "../sim/vec3.js";
|
|
3
|
+
import type { PoseModifier } from "../model3d.js";
|
|
4
|
+
/**
|
|
5
|
+
* Linear interpolation between two fixed‑point scalars.
|
|
6
|
+
* @param prev Value at t = 0
|
|
7
|
+
* @param curr Value at t = SCALE.Q
|
|
8
|
+
* @param t Interpolation factor Q ∈ [0, SCALE.Q]
|
|
9
|
+
* @returns Interpolated value (deterministic, using mulDiv)
|
|
10
|
+
*/
|
|
11
|
+
export declare function lerpQ(prev: Q, curr: Q, t: Q): Q;
|
|
12
|
+
/**
|
|
13
|
+
* Clamped linear interpolation; result stays within [prev, curr] (or [curr, prev]).
|
|
14
|
+
*/
|
|
15
|
+
export declare function lerpQClamped(prev: Q, curr: Q, t: Q): Q;
|
|
16
|
+
/**
|
|
17
|
+
* Linear interpolation between two fixed‑point vectors.
|
|
18
|
+
* @param prev Vec3 at t = 0
|
|
19
|
+
* @param curr Vec3 at t = SCALE.Q
|
|
20
|
+
* @param t Interpolation factor Q ∈ [0, SCALE.Q]
|
|
21
|
+
* @returns Interpolated Vec3 (component‑wise lerp)
|
|
22
|
+
*/
|
|
23
|
+
export declare function lerpVec3(prev: Vec3, curr: Vec3, t: Q): Vec3;
|
|
24
|
+
/**
|
|
25
|
+
* Cheap spherical interpolation for facing directions (unit vectors in Q‑space).
|
|
26
|
+
* Uses normaliseDirCheapQ on the linearly interpolated vector.
|
|
27
|
+
* Not truly slerp but good enough for facing interpolation over small angles.
|
|
28
|
+
*/
|
|
29
|
+
export declare function slerpFacing(prev: Vec3, curr: Vec3, t: Q): Vec3;
|
|
30
|
+
/**
|
|
31
|
+
* Interpolates two pose modifier arrays by matching segmentId.
|
|
32
|
+
* If a segment appears only in one snapshot, its weights are held constant
|
|
33
|
+
* (no cross‑fade). Returns a new array with all segmentIds from both inputs.
|
|
34
|
+
*/
|
|
35
|
+
export declare function interpolatePoseModifiers(prev: PoseModifier[], curr: PoseModifier[], t: Q): PoseModifier[];
|
|
36
|
+
/**
|
|
37
|
+
* Interpolates between two AnimationHints.
|
|
38
|
+
* Boolean flags (prone, unconscious, dead) snap at t >= SCALE.Q/2.
|
|
39
|
+
* Scalar weights are lerpQ.
|
|
40
|
+
*/
|
|
41
|
+
export declare function interpolateAnimationHints(prev: import("../model3d.js").AnimationHints, curr: import("../model3d.js").AnimationHints, t: Q): import("../model3d.js").AnimationHints;
|
|
42
|
+
/**
|
|
43
|
+
* Interpolates condition fields (shock, fear, consciousness, fluidLoss).
|
|
44
|
+
* Dead flag snaps at t >= SCALE.Q/2.
|
|
45
|
+
*/
|
|
46
|
+
export declare function interpolateCondition(prev: {
|
|
47
|
+
shockQ: Q;
|
|
48
|
+
fearQ: Q;
|
|
49
|
+
consciousness: Q;
|
|
50
|
+
fluidLoss: Q;
|
|
51
|
+
dead: boolean;
|
|
52
|
+
}, curr: {
|
|
53
|
+
shockQ: Q;
|
|
54
|
+
fearQ: Q;
|
|
55
|
+
consciousness: Q;
|
|
56
|
+
fluidLoss: Q;
|
|
57
|
+
dead: boolean;
|
|
58
|
+
}, t: Q): {
|
|
59
|
+
shockQ: number;
|
|
60
|
+
fearQ: number;
|
|
61
|
+
consciousness: number;
|
|
62
|
+
fluidLoss: number;
|
|
63
|
+
dead: boolean;
|
|
64
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// src/bridge/interpolation.ts — Deterministic fixed‑point interpolation utilities
|
|
2
|
+
import { SCALE, mulDiv, clampQ } from "../units.js";
|
|
3
|
+
import { normaliseDirCheapQ } from "../sim/vec3.js";
|
|
4
|
+
// ─── Scalar interpolation ──────────────────────────────────────────────────────
|
|
5
|
+
/**
|
|
6
|
+
* Linear interpolation between two fixed‑point scalars.
|
|
7
|
+
* @param prev Value at t = 0
|
|
8
|
+
* @param curr Value at t = SCALE.Q
|
|
9
|
+
* @param t Interpolation factor Q ∈ [0, SCALE.Q]
|
|
10
|
+
* @returns Interpolated value (deterministic, using mulDiv)
|
|
11
|
+
*/
|
|
12
|
+
export function lerpQ(prev, curr, t) {
|
|
13
|
+
// t ∈ [0, SCALE.Q]; compute (prev * (SCALE.Q - t) + curr * t) / SCALE.Q
|
|
14
|
+
const tInv = SCALE.Q - t;
|
|
15
|
+
const a = mulDiv(prev, tInv, SCALE.Q);
|
|
16
|
+
const b = mulDiv(curr, t, SCALE.Q);
|
|
17
|
+
return a + b;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Clamped linear interpolation; result stays within [prev, curr] (or [curr, prev]).
|
|
21
|
+
*/
|
|
22
|
+
export function lerpQClamped(prev, curr, t) {
|
|
23
|
+
const r = lerpQ(prev, curr, t);
|
|
24
|
+
return prev <= curr
|
|
25
|
+
? clampQ(r, prev, curr)
|
|
26
|
+
: clampQ(r, curr, prev);
|
|
27
|
+
}
|
|
28
|
+
// ─── Vector interpolation ──────────────────────────────────────────────────────
|
|
29
|
+
/**
|
|
30
|
+
* Linear interpolation between two fixed‑point vectors.
|
|
31
|
+
* @param prev Vec3 at t = 0
|
|
32
|
+
* @param curr Vec3 at t = SCALE.Q
|
|
33
|
+
* @param t Interpolation factor Q ∈ [0, SCALE.Q]
|
|
34
|
+
* @returns Interpolated Vec3 (component‑wise lerp)
|
|
35
|
+
*/
|
|
36
|
+
export function lerpVec3(prev, curr, t) {
|
|
37
|
+
const dx = curr.x - prev.x;
|
|
38
|
+
const dy = curr.y - prev.y;
|
|
39
|
+
const dz = curr.z - prev.z;
|
|
40
|
+
return {
|
|
41
|
+
x: prev.x + mulDiv(dx, t, SCALE.Q),
|
|
42
|
+
y: prev.y + mulDiv(dy, t, SCALE.Q),
|
|
43
|
+
z: prev.z + mulDiv(dz, t, SCALE.Q),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Cheap spherical interpolation for facing directions (unit vectors in Q‑space).
|
|
48
|
+
* Uses normaliseDirCheapQ on the linearly interpolated vector.
|
|
49
|
+
* Not truly slerp but good enough for facing interpolation over small angles.
|
|
50
|
+
*/
|
|
51
|
+
export function slerpFacing(prev, curr, t) {
|
|
52
|
+
const interp = lerpVec3(prev, curr, t);
|
|
53
|
+
return normaliseDirCheapQ(interp);
|
|
54
|
+
}
|
|
55
|
+
// ─── Pose modifier interpolation ───────────────────────────────────────────────
|
|
56
|
+
/**
|
|
57
|
+
* Interpolates two pose modifier arrays by matching segmentId.
|
|
58
|
+
* If a segment appears only in one snapshot, its weights are held constant
|
|
59
|
+
* (no cross‑fade). Returns a new array with all segmentIds from both inputs.
|
|
60
|
+
*/
|
|
61
|
+
export function interpolatePoseModifiers(prev, curr, t) {
|
|
62
|
+
const prevMap = new Map();
|
|
63
|
+
const currMap = new Map();
|
|
64
|
+
for (const p of prev)
|
|
65
|
+
prevMap.set(p.segmentId, p);
|
|
66
|
+
for (const c of curr)
|
|
67
|
+
currMap.set(c.segmentId, c);
|
|
68
|
+
const result = [];
|
|
69
|
+
const allIds = new Set([...prevMap.keys(), ...currMap.keys()]);
|
|
70
|
+
for (const id of allIds) {
|
|
71
|
+
const p = prevMap.get(id);
|
|
72
|
+
const c = currMap.get(id);
|
|
73
|
+
if (p && c) {
|
|
74
|
+
// Both present: interpolate each component
|
|
75
|
+
result.push({
|
|
76
|
+
segmentId: id,
|
|
77
|
+
structuralQ: lerpQ(p.structuralQ, c.structuralQ, t),
|
|
78
|
+
surfaceQ: lerpQ(p.surfaceQ, c.surfaceQ, t),
|
|
79
|
+
impairmentQ: lerpQ(p.impairmentQ, c.impairmentQ, t),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
else if (p) {
|
|
83
|
+
// Only in previous snapshot: hold constant
|
|
84
|
+
result.push(p);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
// Only in current snapshot: hold constant (should not happen if t ∈ [0, SCALE.Q])
|
|
88
|
+
result.push(c);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
// ─── Animation hints interpolation ─────────────────────────────────────────────
|
|
94
|
+
/**
|
|
95
|
+
* Interpolates between two AnimationHints.
|
|
96
|
+
* Boolean flags (prone, unconscious, dead) snap at t >= SCALE.Q/2.
|
|
97
|
+
* Scalar weights are lerpQ.
|
|
98
|
+
*/
|
|
99
|
+
export function interpolateAnimationHints(prev, curr, t) {
|
|
100
|
+
const tMid = SCALE.Q / 2;
|
|
101
|
+
return {
|
|
102
|
+
idle: lerpQ(prev.idle, curr.idle, t),
|
|
103
|
+
walk: lerpQ(prev.walk, curr.walk, t),
|
|
104
|
+
run: lerpQ(prev.run, curr.run, t),
|
|
105
|
+
sprint: lerpQ(prev.sprint, curr.sprint, t),
|
|
106
|
+
crawl: lerpQ(prev.crawl, curr.crawl, t),
|
|
107
|
+
guardingQ: lerpQ(prev.guardingQ, curr.guardingQ, t),
|
|
108
|
+
attackingQ: lerpQ(prev.attackingQ, curr.attackingQ, t),
|
|
109
|
+
shockQ: lerpQ(prev.shockQ, curr.shockQ, t),
|
|
110
|
+
fearQ: lerpQ(prev.fearQ, curr.fearQ, t),
|
|
111
|
+
prone: t < tMid ? prev.prone : curr.prone,
|
|
112
|
+
unconscious: t < tMid ? prev.unconscious : curr.unconscious,
|
|
113
|
+
dead: t < tMid ? prev.dead : curr.dead,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// ─── Condition interpolation ───────────────────────────────────────────────────
|
|
117
|
+
/**
|
|
118
|
+
* Interpolates condition fields (shock, fear, consciousness, fluidLoss).
|
|
119
|
+
* Dead flag snaps at t >= SCALE.Q/2.
|
|
120
|
+
*/
|
|
121
|
+
export function interpolateCondition(prev, curr, t) {
|
|
122
|
+
const tMid = SCALE.Q / 2;
|
|
123
|
+
return {
|
|
124
|
+
shockQ: lerpQ(prev.shockQ, curr.shockQ, t),
|
|
125
|
+
fearQ: lerpQ(prev.fearQ, curr.fearQ, t),
|
|
126
|
+
consciousness: lerpQ(prev.consciousness, curr.consciousness, t),
|
|
127
|
+
fluidLoss: lerpQ(prev.fluidLoss, curr.fluidLoss, t),
|
|
128
|
+
dead: t < tMid ? prev.dead : curr.dead,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { BodyPlanMapping, BridgeConfig } from "./types.js";
|
|
2
|
+
import type { PoseModifier } from "../model3d.js";
|
|
3
|
+
/**
|
|
4
|
+
* Find the bone name for a segment ID according to the given mapping.
|
|
5
|
+
* Falls back to defaultBoneName if not found.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveBoneName(segmentId: string, mapping: BodyPlanMapping, defaultBoneName?: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the full mapping for a body plan ID.
|
|
10
|
+
* Returns the first mapping whose bodyPlanId matches, or undefined.
|
|
11
|
+
*/
|
|
12
|
+
export declare function findBodyPlanMapping(config: BridgeConfig, bodyPlanId: string): BodyPlanMapping | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Apply mapping to a pose modifier array, converting segmentId → boneName.
|
|
15
|
+
* If a segment is not mapped, it is omitted (or can be kept with default bone name).
|
|
16
|
+
*/
|
|
17
|
+
export declare function mapPoseModifiers(poseModifiers: PoseModifier[], mapping: BodyPlanMapping, defaultBoneName?: string): Array<{
|
|
18
|
+
segmentId: string;
|
|
19
|
+
boneName: string;
|
|
20
|
+
impairmentQ: number;
|
|
21
|
+
structuralQ: number;
|
|
22
|
+
surfaceQ: number;
|
|
23
|
+
}>;
|
|
24
|
+
/**
|
|
25
|
+
* Create a default mapping that maps each segment ID to itself as bone name.
|
|
26
|
+
* Useful for testing or as a fallback when no explicit mapping is provided.
|
|
27
|
+
*/
|
|
28
|
+
export declare function createIdentityMapping(bodyPlanId: string, segmentIds: string[]): BodyPlanMapping;
|
|
29
|
+
/**
|
|
30
|
+
* Validate that a mapping covers all segment IDs of a body plan.
|
|
31
|
+
* Returns missing segment IDs.
|
|
32
|
+
*/
|
|
33
|
+
export declare function validateMappingCoverage(mapping: BodyPlanMapping, segmentIds: string[]): string[];
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/bridge/mapping.ts — Segment‑to‑bone mapping resolution
|
|
2
|
+
// ─── Mapping resolution ────────────────────────────────────────────────────────
|
|
3
|
+
/**
|
|
4
|
+
* Find the bone name for a segment ID according to the given mapping.
|
|
5
|
+
* Falls back to defaultBoneName if not found.
|
|
6
|
+
*/
|
|
7
|
+
export function resolveBoneName(segmentId, mapping, defaultBoneName = "root") {
|
|
8
|
+
const seg = mapping.segments.find(s => s.segmentId === segmentId);
|
|
9
|
+
return seg?.boneName ?? defaultBoneName;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Resolve the full mapping for a body plan ID.
|
|
13
|
+
* Returns the first mapping whose bodyPlanId matches, or undefined.
|
|
14
|
+
*/
|
|
15
|
+
export function findBodyPlanMapping(config, bodyPlanId) {
|
|
16
|
+
return config.mappings.find(m => m.bodyPlanId === bodyPlanId);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Apply mapping to a pose modifier array, converting segmentId → boneName.
|
|
20
|
+
* If a segment is not mapped, it is omitted (or can be kept with default bone name).
|
|
21
|
+
*/
|
|
22
|
+
export function mapPoseModifiers(poseModifiers, mapping, defaultBoneName = "root") {
|
|
23
|
+
const result = [];
|
|
24
|
+
for (const pm of poseModifiers) {
|
|
25
|
+
const boneName = resolveBoneName(pm.segmentId, mapping, defaultBoneName);
|
|
26
|
+
result.push({
|
|
27
|
+
segmentId: pm.segmentId,
|
|
28
|
+
boneName,
|
|
29
|
+
impairmentQ: pm.impairmentQ,
|
|
30
|
+
structuralQ: pm.structuralQ,
|
|
31
|
+
surfaceQ: pm.surfaceQ,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Create a default mapping that maps each segment ID to itself as bone name.
|
|
38
|
+
* Useful for testing or as a fallback when no explicit mapping is provided.
|
|
39
|
+
*/
|
|
40
|
+
export function createIdentityMapping(bodyPlanId, segmentIds) {
|
|
41
|
+
return {
|
|
42
|
+
bodyPlanId,
|
|
43
|
+
segments: segmentIds.map(id => ({ segmentId: id, boneName: id })),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// ─── Validation ────────────────────────────────────────────────────────────────
|
|
47
|
+
/**
|
|
48
|
+
* Validate that a mapping covers all segment IDs of a body plan.
|
|
49
|
+
* Returns missing segment IDs.
|
|
50
|
+
*/
|
|
51
|
+
export function validateMappingCoverage(mapping, segmentIds) {
|
|
52
|
+
const mapped = new Set(mapping.segments.map(s => s.segmentId));
|
|
53
|
+
return segmentIds.filter(id => !mapped.has(id));
|
|
54
|
+
}
|