chore: checkpoint web Blender migration source, tools and docs
Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer, web workflow engine and protocol, tooling, docs and task records. Large generated evidence, user-testing archives, build snapshots and imported test assets remain untracked. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -12,31 +12,53 @@ function fnv1a(value: string): number {
|
||||
export type ModifierParameterMetadata = {
|
||||
key: string;
|
||||
value: unknown;
|
||||
kind: "boolean" | "number" | "string" | "enum" | "object";
|
||||
kind: "boolean" | "number" | "string" | "enum" | "enum_set" | "object" | "collection" | "texture" | "cachefile" | "curve_mapping" | "profile";
|
||||
options?: string[];
|
||||
items?: Array<{ id: string; label: string }>;
|
||||
label?: string;
|
||||
description?: string;
|
||||
/** Native MOD_subsurf panel text for derived, read-only values. */
|
||||
display?: string;
|
||||
visible?: boolean;
|
||||
enabled?: boolean;
|
||||
editable?: boolean;
|
||||
nullable?: boolean;
|
||||
suggestionsOnly?: boolean;
|
||||
maxLength?: number;
|
||||
unit?: 'ANGLE';
|
||||
integer?: boolean;
|
||||
length?: number;
|
||||
minimum?: number;
|
||||
maximum?: number;
|
||||
};
|
||||
|
||||
export type ModifierCurveProfile = {
|
||||
view: [number, number, number, number];
|
||||
clip: [number, number, number, number];
|
||||
points: Array<{ location: [number, number]; handle1: [number, number]; handle2: [number, number]; handleType: 'AUTO' | 'VECTOR' | 'FREE' | 'ALIGN' }>;
|
||||
samples: Array<[number, number]>;
|
||||
use_clip: boolean;
|
||||
use_sample_straight_edges: boolean;
|
||||
use_sample_even_lengths: boolean;
|
||||
};
|
||||
|
||||
export function isModifierCurveProfile(value: unknown): value is ModifierCurveProfile {
|
||||
if (!value || typeof value !== 'object') return false;
|
||||
const profile = value as ModifierCurveProfile;
|
||||
const pair = (point: unknown) => Array.isArray(point) && point.length === 2 && point.every(n => typeof n === 'number' && Number.isFinite(n));
|
||||
const rect = (value: unknown) => Array.isArray(value) && value.length === 4 && value.every(n => typeof n === 'number' && Number.isFinite(n)) && value[2] > value[0] && value[3] > value[1];
|
||||
return rect(profile.view) && rect(profile.clip) && typeof profile.use_clip === 'boolean' && typeof profile.use_sample_straight_edges === 'boolean' &&
|
||||
typeof profile.use_sample_even_lengths === 'boolean' && Array.isArray(profile.points) &&
|
||||
profile.points.length >= 2 && profile.points.length <= 32767 && profile.points.every(point => point &&
|
||||
pair(point.location) && pair(point.handle1) && pair(point.handle2) && ['AUTO', 'VECTOR', 'FREE', 'ALIGN'].includes(point.handleType)) &&
|
||||
Array.isArray(profile.samples) && profile.samples.length <= 1048576 && profile.samples.every(pair);
|
||||
}
|
||||
|
||||
/** Derives editable fields from the metadata carried by the authoritative modifier IR. */
|
||||
export function modifierParameterMetadata(modifier: ModifierIR): ModifierParameterMetadata[] {
|
||||
return Object.entries(modifier.parameters ?? {}).map(([key, rawValue]) => {
|
||||
// Main may carry a descriptor when RNA exposes enum choices alongside the
|
||||
// current value. Keep the descriptor opaque to callers while deriving the
|
||||
// editable value and choices from it.
|
||||
const descriptor = rawValue && typeof rawValue === "object" && !Array.isArray(rawValue) &&
|
||||
"value" in rawValue && ("kind" in rawValue || "options" in rawValue)
|
||||
? rawValue as { value: unknown; kind?: ModifierParameterMetadata["kind"]; options?: unknown[] }
|
||||
: undefined;
|
||||
const rawOptions = descriptor?.options ?? (Array.isArray(rawValue) && rawValue.every((entry) => typeof entry === "string") ? rawValue : undefined);
|
||||
const options = rawOptions?.map(String);
|
||||
const value = descriptor?.value ?? (options ? options[0] : rawValue);
|
||||
const kind = descriptor?.kind ?? (
|
||||
typeof value === "boolean" ? "boolean" :
|
||||
typeof value === "number" ? "number" :
|
||||
typeof value === "string" ? (options ? "enum" : "string") :
|
||||
"object"
|
||||
);
|
||||
return { key, value, kind, options: options ?? undefined };
|
||||
});
|
||||
return (modifier.editableParameters ?? []).map((field) => ({ ...field,
|
||||
options: field.items?.map((item) => item.id),
|
||||
}));
|
||||
}
|
||||
|
||||
/** Hashes evaluated modifier identity/state, excluding display timestamps. */
|
||||
|
||||
Reference in New Issue
Block a user