Add validation and normalization utilities for ScenePathRepeater properties
This commit is contained in:
@@ -562,6 +562,189 @@ export function createScenePathRoadSettings(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isScenePathRepeaterPlacement(
|
||||||
|
value: unknown
|
||||||
|
): value is ScenePathRepeaterPlacement {
|
||||||
|
return (
|
||||||
|
typeof value === "string" &&
|
||||||
|
SCENE_PATH_REPEATER_PLACEMENTS.includes(
|
||||||
|
value as ScenePathRepeaterPlacement
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeScenePathRepeaterPlacement(
|
||||||
|
value: unknown
|
||||||
|
): ScenePathRepeaterPlacement {
|
||||||
|
if (isScenePathRepeaterPlacement(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("Path repeater placement must be center, left, or right.");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeScenePathRepeaterAssetId(value: string): string {
|
||||||
|
const trimmedValue = value.trim();
|
||||||
|
|
||||||
|
if (trimmedValue.length === 0) {
|
||||||
|
throw new Error("Path repeater asset id must be non-empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return trimmedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeScenePathRepeaterOffset(value: number): number {
|
||||||
|
return normalizeFiniteRange(
|
||||||
|
value,
|
||||||
|
MIN_SCENE_PATH_REPEATER_OFFSET,
|
||||||
|
MAX_SCENE_PATH_REPEATER_OFFSET,
|
||||||
|
"Path repeater offset"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeScenePathRepeaterSpacing(value: number): number {
|
||||||
|
return normalizeFiniteRange(
|
||||||
|
value,
|
||||||
|
MIN_SCENE_PATH_REPEATER_SPACING,
|
||||||
|
MAX_SCENE_PATH_REPEATER_SPACING,
|
||||||
|
"Path repeater spacing"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeScenePathRepeaterInset(value: number): number {
|
||||||
|
return normalizeFiniteRange(
|
||||||
|
value,
|
||||||
|
MIN_SCENE_PATH_REPEATER_INSET,
|
||||||
|
MAX_SCENE_PATH_REPEATER_INSET,
|
||||||
|
"Path repeater inset"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeScenePathRepeaterScale(value: number): number {
|
||||||
|
return normalizeFiniteRange(
|
||||||
|
value,
|
||||||
|
MIN_SCENE_PATH_REPEATER_SCALE,
|
||||||
|
MAX_SCENE_PATH_REPEATER_SCALE,
|
||||||
|
"Path repeater scale"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeScenePathRepeaterRandomScale(value: number): number {
|
||||||
|
return normalizeFiniteRange(
|
||||||
|
value,
|
||||||
|
MIN_SCENE_PATH_REPEATER_RANDOM_SCALE,
|
||||||
|
MAX_SCENE_PATH_REPEATER_RANDOM_SCALE,
|
||||||
|
"Path repeater random scale"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeScenePathRepeaterRandomYawDegrees(
|
||||||
|
value: number
|
||||||
|
): number {
|
||||||
|
return normalizeFiniteRange(
|
||||||
|
value,
|
||||||
|
MIN_SCENE_PATH_REPEATER_RANDOM_YAW_DEGREES,
|
||||||
|
MAX_SCENE_PATH_REPEATER_RANDOM_YAW_DEGREES,
|
||||||
|
"Path repeater random yaw"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeScenePathRepeaterYawOffsetDegrees(
|
||||||
|
value: number
|
||||||
|
): number {
|
||||||
|
if (!Number.isFinite(value)) {
|
||||||
|
throw new Error("Path repeater yaw offset must be a finite number.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeScenePathRepeaterHeightOffset(value: number): number {
|
||||||
|
if (!Number.isFinite(value)) {
|
||||||
|
throw new Error("Path repeater height offset must be a finite number.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createScenePathRepeater(
|
||||||
|
overrides: ScenePathRepeaterOverrides = {}
|
||||||
|
): ScenePathRepeater {
|
||||||
|
const enabled = overrides.enabled ?? DEFAULT_SCENE_PATH_REPEATER_ENABLED;
|
||||||
|
const terrainConform =
|
||||||
|
overrides.terrainConform ?? DEFAULT_SCENE_PATH_REPEATER_TERRAIN_CONFORM;
|
||||||
|
const alignToSpline =
|
||||||
|
overrides.alignToSpline ?? DEFAULT_SCENE_PATH_REPEATER_ALIGN_TO_SPLINE;
|
||||||
|
const seed = overrides.seed ?? 1;
|
||||||
|
|
||||||
|
if (typeof enabled !== "boolean") {
|
||||||
|
throw new Error("Path repeater enabled must be a boolean.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof terrainConform !== "boolean") {
|
||||||
|
throw new Error("Path repeater terrain conform must be a boolean.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof alignToSpline !== "boolean") {
|
||||||
|
throw new Error("Path repeater align to spline must be a boolean.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Number.isFinite(seed) || !Number.isInteger(seed)) {
|
||||||
|
throw new Error("Path repeater seed must be a finite integer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: overrides.id ?? createOpaqueId("path_repeater"),
|
||||||
|
name: normalizeScenePathName(overrides.name),
|
||||||
|
enabled,
|
||||||
|
assetId: normalizeScenePathRepeaterAssetId(
|
||||||
|
overrides.assetId ?? DEFAULT_SCENE_PATH_REPEATER_ASSET_ID
|
||||||
|
),
|
||||||
|
placement:
|
||||||
|
overrides.placement === undefined
|
||||||
|
? DEFAULT_SCENE_PATH_REPEATER_PLACEMENT
|
||||||
|
: normalizeScenePathRepeaterPlacement(overrides.placement),
|
||||||
|
offset: normalizeScenePathRepeaterOffset(
|
||||||
|
overrides.offset ?? DEFAULT_SCENE_PATH_REPEATER_OFFSET
|
||||||
|
),
|
||||||
|
spacing: normalizeScenePathRepeaterSpacing(
|
||||||
|
overrides.spacing ?? DEFAULT_SCENE_PATH_REPEATER_SPACING
|
||||||
|
),
|
||||||
|
startInset: normalizeScenePathRepeaterInset(
|
||||||
|
overrides.startInset ?? DEFAULT_SCENE_PATH_REPEATER_START_INSET
|
||||||
|
),
|
||||||
|
endInset: normalizeScenePathRepeaterInset(
|
||||||
|
overrides.endInset ?? DEFAULT_SCENE_PATH_REPEATER_END_INSET
|
||||||
|
),
|
||||||
|
scale: normalizeScenePathRepeaterScale(
|
||||||
|
overrides.scale ?? DEFAULT_SCENE_PATH_REPEATER_SCALE
|
||||||
|
),
|
||||||
|
randomScale: normalizeScenePathRepeaterRandomScale(
|
||||||
|
overrides.randomScale ?? DEFAULT_SCENE_PATH_REPEATER_RANDOM_SCALE
|
||||||
|
),
|
||||||
|
randomYawDegrees: normalizeScenePathRepeaterRandomYawDegrees(
|
||||||
|
overrides.randomYawDegrees ??
|
||||||
|
DEFAULT_SCENE_PATH_REPEATER_RANDOM_YAW_DEGREES
|
||||||
|
),
|
||||||
|
yawOffsetDegrees: normalizeScenePathRepeaterYawOffsetDegrees(
|
||||||
|
overrides.yawOffsetDegrees ??
|
||||||
|
DEFAULT_SCENE_PATH_REPEATER_YAW_OFFSET_DEGREES
|
||||||
|
),
|
||||||
|
terrainConform,
|
||||||
|
heightOffset: normalizeScenePathRepeaterHeightOffset(
|
||||||
|
overrides.heightOffset ?? DEFAULT_SCENE_PATH_REPEATER_HEIGHT_OFFSET
|
||||||
|
),
|
||||||
|
alignToSpline,
|
||||||
|
seed
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cloneScenePathRepeater(
|
||||||
|
repeater: ScenePathRepeater
|
||||||
|
): ScenePathRepeater {
|
||||||
|
return createScenePathRepeater(repeater);
|
||||||
|
}
|
||||||
|
|
||||||
function resolvePathSegmentSample(
|
function resolvePathSegmentSample(
|
||||||
path: ResolvedPathLike<PathPointLike, ResolvedPathSegmentLike>,
|
path: ResolvedPathLike<PathPointLike, ResolvedPathSegmentLike>,
|
||||||
progress: number
|
progress: number
|
||||||
|
|||||||
Reference in New Issue
Block a user