@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
JavaScript
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