fix: getTopologyValue off-by-one bug in clause-based topology evaluators

getTopologyValue used subject ID as direct array index (arr[subjectId] ?? 0),
but solver subject IDs are 1-indexed while topology arrays are 0-indexed parallel
arrays.  This produced wrong results for any subjectId != position+1.

Fixed by using world.subjects.ids.indexOf(subjectId) to map subject ID to array
position, matching the predicate evaluator's getTopologyValueBySubject pattern.

Affected callers: evaluateAttachedToScrollContainer, evaluateInStackingContext.
The predicate-based evaluators in predicates.ts were already correct.
This commit is contained in:
John Dvorak
2026-05-22 16:40:01 -07:00
parent 8f823d959b
commit 2bdda12030
+12 -6
View File
@@ -33,11 +33,16 @@ function result(
}
/**
* Safely read a topology value indexed by subject id.
* Returns 0 when the subject is out of bounds (treated as "no relation").
* Safely read a topology value for a given solver subject ID.
*
* Solver subject IDs are 1-indexed; topology arrays are 0-indexed parallel
* to world.subjects.ids. Returns 0 when the subject is not found (treated as
* "no relation").
*/
function getTopologyValue(arr: number[], subjectId: number): number {
return arr[subjectId] ?? 0;
function getTopologyValue(world: GeometryWorld, arr: number[], subjectId: number): number {
const idx = world.subjects.ids.indexOf(subjectId);
if (idx < 0 || idx >= arr.length) return 0;
return arr[idx];
}
/**
@@ -109,6 +114,7 @@ export function evaluateAttachedToScrollContainer(
}
const scrollContainerId = getTopologyValue(
world,
world.topology.scrollContainerOf,
subjectRef,
);
@@ -139,7 +145,7 @@ export function evaluateInStackingContext(
});
}
const sCtx = getTopologyValue(world.topology.stackingContextOf, subjectRef);
const sCtx = getTopologyValue(world, world.topology.stackingContextOf, subjectRef);
if (referenceRef === undefined) {
const pass = sCtx !== 0;
@@ -152,7 +158,7 @@ export function evaluateInStackingContext(
);
}
const rCtx = getTopologyValue(world.topology.stackingContextOf, referenceRef);
const rCtx = getTopologyValue(world, world.topology.stackingContextOf, referenceRef);
const pass = sCtx !== 0 && sCtx === rCtx;
return result(