UNPKG

@babylonjs/viewer

Version:

The Babylon Viewer aims to simplify a specific but common Babylon.js use case: loading, viewing, and interacting with a 3D model.

295 lines (290 loc) 14.2 kB
import { F as FlowGraphInteger } from './objectModelMapping-OlchA9xj.esm.js'; import { R as RichTypeAny } from './declarationMapper-mPOKbCS_.esm.js'; import { i as Color3, b1 as Color4, V as Vector3, bc as Vector4, u as RegisterClass } from './index-HyNDfLMI.esm.js'; import { F as FlowGraphCachedOperationBlock } from './flowGraphCachedOperationBlock-DSj_4V2c.esm.js'; import './spotLight.pure-C65PiYTZ.esm.js'; import './KHR_interactivity-CnR665Qq.esm.js'; // JSON Pointer templates may use either bracket style: // {name} → a reference template parameter. // [name] → an integer template parameter. // Real-world assets mix both conventions, so the bracket style by itself is not enough to determine // the input's type. We therefore accept both and decide how to substitute at resolution time based // on the runtime value supplied to the input socket (FlowGraphInteger / number → int substitution, // string → reference substitution by extracting the matching JSON-Pointer segment). // These are constructed per scan rather than shared, because a global regex carries `lastIndex` // state across calls and a scan that throws part-way would otherwise leave it set for the next one. const CreateRefTemplateRegex = () => new RegExp(/\/\{(\w+)\}(?=\/|$)/g); const CreateIntTemplateRegex = () => new RegExp(/\/\[(\w+)\](?=\/|$)/g); /** * @experimental * A component that converts a path to an object accessor. */ class FlowGraphPathConverterComponent { constructor(path, ownerBlock) { this.path = path; this.ownerBlock = ownerBlock; /** * The templated inputs for the provided path. Values may be FlowGraphInteger, number, or * string (an opaque reference encoded as a JSON Pointer). */ this.templatedInputs = []; /** Per-template metadata (name + bracket style + input connection). */ this.templateInfos = []; const templateSet = new Set(); const collect = (regex, style) => { let match = regex.exec(path); while (match) { const [, name] = match; if (templateSet.has(name)) { throw new Error("Duplicate template variable detected."); } templateSet.add(name); // Use RichTypeAny so the same socket can receive either an integer (legacy / // [name] style) or a string ref (post-ref-update {name} style); the value's // runtime type drives the substitution behaviour in getAccessor. Default to // FlowGraphInteger(0) — not undefined — so an unconnected index input still // resolves to index 0 (the natural default) instead of throwing at path resolution. const conn = ownerBlock.registerDataInput(name, RichTypeAny, new FlowGraphInteger(0)); this.templatedInputs.push(conn); this.templateInfos.push({ name, style, connection: conn }); match = regex.exec(path); } }; collect(CreateRefTemplateRegex(), "curly"); collect(CreateIntTemplateRegex(), "square"); } /** * Get the accessor for the path. * @param pathConverter the path converter to use to convert the path to an object accessor. * @param context the context to use. * @returns the accessor for the path. * @throws if the value for a templated input is invalid. */ getAccessor(pathConverter, context) { let finalPath = this.path; for (const info of this.templateInfos) { const raw = info.connection.getValue(context); const placeholder = info.style === "curly" ? `{${info.name}}` : `[${info.name}]`; const substitution = ResolveTemplateSubstitution(this.path, info.name, raw, context); finalPath = finalPath.replace(placeholder, substitution); } return pathConverter.convert(finalPath); } } /** * Decide what string to splice into a templated path for a given runtime value. * * - FlowGraphInteger / number → use the integer's decimal representation. * - string → treat as a JSON Pointer to an object and pull the segment whose position * in the ref matches the position of `{name}` (or `[name]`) in the surrounding template. * Falls back to the last non-empty segment, then to the raw ref string. * - object → ask the host environment for the reference addressing it, then substitute as above. * @param template the original templated path (used to locate the placeholder position) * @param name the name of the template parameter being resolved * @param raw the runtime value supplied for the template parameter * @param context the context used to reach the host resolver for object values * @returns the substring to splice into the templated path in place of the placeholder */ function ResolveTemplateSubstitution(template, name, raw, context) { if (raw instanceof FlowGraphInteger) { AssertNonNegativeInt(raw.value, name); return raw.value.toString(); } if (typeof raw === "number") { AssertNonNegativeInt(raw, name); return raw.toString(); } if (typeof raw === "string") { if (raw === "") { throw new Error(`Templated reference input "${name}" is null.`); } return ExtractRefSubstitution(template, name, raw); } // A runtime object (e.g. a Mesh delivered by a selection event). Mapping it back to a // reference is knowledge the host environment owns, so it is delegated to the host resolver. // The segment preceding the placeholder (e.g. "nodes" in `/nodes/{nodeRef}/globalMatrix`) is // passed as a hint, because an object may be addressable in more than one way. if (raw && typeof raw === "object") { const pointer = context.getObjectReference(raw, GetPlaceholderParentSegment(template, name)); if (pointer) { return ExtractRefSubstitution(template, name, pointer); } } throw new Error(`Invalid value for templated input "${name}": got ${typeof raw}.`); } function GetPlaceholderIndex(template, name) { const placeholders = [`{${name}}`, `[${name}]`]; return template.split("/").findIndex((segment) => placeholders.indexOf(segment) >= 0); } function GetPlaceholderParentSegment(template, name) { const placeholderIndex = GetPlaceholderIndex(template, name); return placeholderIndex > 0 ? template.split("/")[placeholderIndex - 1] : undefined; } function AssertNonNegativeInt(value, name) { if (typeof value !== "number" || value < 0 || !Number.isFinite(value)) { throw new Error(`Invalid value for templated input "${name}": ${value}.`); } } function ExtractRefSubstitution(template, name, refValue) { const templateSegments = template.split("/"); const placeholders = [`{${name}}`, `[${name}]`]; const placeholderIndex = templateSegments.findIndex((s) => placeholders.indexOf(s) >= 0); const refSegments = refValue.split("/"); if (placeholderIndex >= 0 && placeholderIndex < refSegments.length && refSegments[placeholderIndex] !== "") { return refSegments[placeholderIndex]; } for (let i = refSegments.length - 1; i >= 0; i--) { if (refSegments[i] !== "") { return refSegments[i]; } } return refValue; } /** This file must only contain pure code and pure imports */ /** * This block will take a JSON pointer and parse it to get the value from the JSON object. * The output is an object and a property name. * Optionally, the block can also output the value of the property. This is configurable. */ class FlowGraphJsonPointerParserBlock extends FlowGraphCachedOperationBlock { constructor( /** * the configuration of the block */ config) { super(RichTypeAny, config); this.config = config; this.object = this.registerDataOutput("object", RichTypeAny); this.propertyName = this.registerDataOutput("propertyName", RichTypeAny); this.setterFunction = this.registerDataOutput("setFunction", RichTypeAny, this._setPropertyValue.bind(this)); this.getterFunction = this.registerDataOutput("getFunction", RichTypeAny, this._getPropertyValue.bind(this)); this.generateAnimationsFunction = this.registerDataOutput("generateAnimationsFunction", RichTypeAny, this._getInterpolationAnimationPropertyInfo.bind(this)); this.templateComponent = new FlowGraphPathConverterComponent(config.jsonPointer, this); } _doOperation(context) { const accessorContainer = this.templateComponent.getAccessor(this.config.pathConverter, context); // Pass the context as the accessor payload so context-aware object-model accessors, such as // one validating a delay handle against the runtime active-delay set, can resolve. const value = accessorContainer.info.get(accessorContainer.object, undefined, context); const object = accessorContainer.info.getTarget?.(accessorContainer.object); const propertyName = accessorContainer.info.getPropertyName?.[0](accessorContainer.object); if (!object) { throw new Error("Object is undefined"); } else { this.object.setValue(object, context); if (propertyName) { this.propertyName.setValue(propertyName, context); } } return value; } _setPropertyValue(_target, _propertyName, value, context) { const accessorContainer = this.templateComponent.getAccessor(this.config.pathConverter, context); const type = accessorContainer.info.type; if (type.startsWith("Color")) { value = ToColor(value, type); } // Unwrap FlowGraphInteger to plain number for numeric setters if (typeof value?.value === "number" && value?.getClassName?.() === "FlowGraphInteger") { value = value.value; } accessorContainer.info.set?.(value, accessorContainer.object); } _getPropertyValue(_target, _propertyName, context) { const accessorContainer = this.templateComponent.getAccessor(this.config.pathConverter, context); const type = accessorContainer.info.type; // Pass the context as the accessor payload (see _doOperation) so context-aware accessors // can resolve. const value = accessorContainer.info.get(accessorContainer.object, undefined, context); if (type.startsWith("Color")) { return FromColor(value); } return value; } _getInterpolationAnimationPropertyInfo(_target, _propertyName, context) { const accessorContainer = this.templateComponent.getAccessor(this.config.pathConverter, context); return (keys, fps, animationType, easingFunction) => { const animations = []; // make sure keys are of the right type (in case of float3 color/vector) const type = accessorContainer.info.type; if (type.startsWith("Color")) { keys = keys.map((key) => { return { frame: key.frame, value: ToColor(key.value, type), }; }); } accessorContainer.info.interpolation?.forEach((info, index) => { const name = accessorContainer.info.getPropertyName?.[index](accessorContainer.object) || "Animation-interpolation-" + index; // generate the keys based on interpolation info let newKeys = keys; if (animationType !== info.type) { // convert the keys to the right type newKeys = keys.map((key) => { return { frame: key.frame, value: info.getValue(undefined, key.value.asArray ? key.value.asArray() : [key.value], 0, 1), }; }); } const animationData = info.buildAnimations(accessorContainer.object, name, 60, newKeys); for (const animation of animationData) { if (easingFunction) { animation.babylonAnimation.setEasingFunction(easingFunction); } animations.push(animation.babylonAnimation); } }); return animations; }; } /** * Gets the class name of this block * @returns the class name */ getClassName() { return "FlowGraphJsonPointerParserBlock" /* FlowGraphBlockNames.JsonPointerParser */; } } function ToColor(value, expectedValue) { if (value.getClassName().startsWith("Color")) { return value; } if (expectedValue === "Color3") { return new Color3(value.x, value.y, value.z); } else if (expectedValue === "Color4") { return new Color4(value.x, value.y, value.z, value.w); } return value; } function FromColor(value) { if (value instanceof Color3) { return new Vector3(value.r, value.g, value.b); } else if (value instanceof Color4) { return new Vector4(value.r, value.g, value.b, value.a); } throw new Error("Invalid color type"); } let _Registered = false; /** * Register side effects for flowGraphJsonPointerParserBlock. * Safe to call multiple times; only the first call has an effect. */ function RegisterFlowGraphJsonPointerParserBlock() { if (_Registered) { return; } _Registered = true; RegisterClass("FlowGraphJsonPointerParserBlock" /* FlowGraphBlockNames.JsonPointerParser */, FlowGraphJsonPointerParserBlock); } /** * Re-exports pure implementation and applies runtime side effects. * Import flowGraphJsonPointerParserBlock.pure for tree-shakeable, side-effect-free usage. */ RegisterFlowGraphJsonPointerParserBlock(); export { FlowGraphJsonPointerParserBlock, RegisterFlowGraphJsonPointerParserBlock }; //# sourceMappingURL=flowGraphJsonPointerParserBlock-ahphPax2.esm.js.map