UNPKG

polygonjs-engine

Version:

node-based webgl 3D engine https://polygonjs.com

367 lines (366 loc) 12.3 kB
import {LiteralConstructsController as LiteralConstructsController2} from "../LiteralConstructsController"; import {CoreAttribute} from "../../../core/geometry/Attribute"; const NATIVE_MATH_METHODS = [ "abs", "acos", "acosh", "asin", "asinh", "atan", "atan2", "atanh", "ceil", "cos", "cosh", "exp", "expm1", "floor", "log", "log1p", "log2", "log10", "max", "min", "pow", "round", "sign", "sin", "sinh", "sqrt", "tan", "tanh" ]; const NATIVE_ES6_MATH_METHODS = ["cbrt", "hypot", "log10", "trunc"]; const NATIVE_MATH_METHODS_RENAMED = { math_random: "random" }; const CORE_MATH_METHODS = ["fit", "fit01", "fract", "deg2rad", "rad2deg", "rand", "clamp"]; import {Easing as Easing2} from "../../../core/math/Easing"; const EASING_METHODS = Object.keys(Easing2); const CORE_STRING_METHODS = ["precision"]; const NATIVE_MATH_CONSTANTS = ["E", "LN2", "LN10", "LOG10E", "LOG2E", "PI", "SQRT1_2", "SQRT2"]; const DIRECT_EXPRESSION_FUNCTIONS = {}; NATIVE_MATH_METHODS.forEach((name) => { DIRECT_EXPRESSION_FUNCTIONS[name] = `Math.${name}`; }); NATIVE_ES6_MATH_METHODS.forEach((name) => { DIRECT_EXPRESSION_FUNCTIONS[name] = `Math.${name}`; }); Object.keys(NATIVE_MATH_METHODS_RENAMED).forEach((name) => { const remaped = NATIVE_MATH_METHODS_RENAMED[name]; DIRECT_EXPRESSION_FUNCTIONS[name] = `Math.${remaped}`; }); CORE_MATH_METHODS.forEach((name) => { DIRECT_EXPRESSION_FUNCTIONS[name] = `Core.Math.${name}`; }); EASING_METHODS.forEach((name) => { DIRECT_EXPRESSION_FUNCTIONS[name] = `Core.Math.Easing.${name}`; }); CORE_STRING_METHODS.forEach((name) => { DIRECT_EXPRESSION_FUNCTIONS[name] = `Core.String.${name}`; }); const LITERAL_CONSTRUCT = { if: LiteralConstructsController2.if }; const GLOBAL_CONSTANTS = {}; NATIVE_MATH_CONSTANTS.forEach((name) => { GLOBAL_CONSTANTS[name] = `Math.${name}`; }); const QUOTE = "'"; const ARGUMENTS_SEPARATOR = ", "; const ATTRIBUTE_PREFIX = "@"; import {VARIABLE_PREFIX} from "./_Base"; const PROPERTY_OFFSETS = { x: 0, y: 1, z: 2, w: 3, r: 0, g: 1, b: 2 }; import {BaseTraverser} from "./_Base"; import {AttributeRequirementsController as AttributeRequirementsController2} from "../AttributeRequirementsController"; import {CoreMath} from "../../../core/math/_Module"; import {CoreString} from "../../../core/String"; import {Poly as Poly2} from "../../Poly"; import {CoreType} from "../../../core/Type"; export class FunctionGenerator extends BaseTraverser { constructor(param) { super(param); this.param = param; this._attribute_requirements_controller = new AttributeRequirementsController2(); this.methods = []; this.method_index = -1; this.method_dependencies = []; this.immutable_dependencies = []; } parse_tree(parsed_tree) { this.reset(); if (parsed_tree.error_message == null) { try { this._attribute_requirements_controller.reset(); if (parsed_tree.node) { const function_main_string = this.traverse_node(parsed_tree.node); if (function_main_string && !this.is_errored()) { this.function_main_string = function_main_string; } } else { console.warn("no parsed_tree.node"); } } catch (e) { console.warn(`error in expression for param ${this.param.fullPath()}`); console.warn(e); } if (this.function_main_string) { try { this.function = new Function("Core", "param", "methods", "_set_error_from_error", ` try { ${this.function_body()} } catch(e) { _set_error_from_error(e) return null; }`); } catch (e) { console.warn(e); this.set_error("cannot generate function"); } } else { this.set_error("cannot generate function body"); } } else { this.set_error("cannot parse expression"); } } reset() { super.reset(); this.function_main_string = void 0; this.methods = []; this.method_index = -1; this.function = void 0; this.method_dependencies = []; this.immutable_dependencies = []; } function_body() { if (this.param.options.is_expression_for_entities()) { return ` const entities = param.expression_controller.entities(); if(entities){ return new Promise( async (resolve, reject)=>{ let entity; const entity_callback = param.expression_controller.entity_callback(); ${this._attribute_requirements_controller.assign_attributes_lines()} if( ${this._attribute_requirements_controller.attribute_presence_check_line()} ){ ${this._attribute_requirements_controller.assign_arrays_lines()} for(let index=0; index < entities.length; index++){ entity = entities[index]; result = ${this.function_main_string}; entity_callback(entity, result); } resolve() } else { const error = new Error('attribute not found') _set_error_from_error(error) reject(error) } }) } return []`; } else { return ` return new Promise( async (resolve, reject)=>{ try { const value = ${this.function_main_string} resolve(value) } catch(e) { _set_error_from_error(e) reject() } }) `; } } eval_allowed() { return this.function != null; } eval_function() { if (this.function) { this.clear_error(); const Core = { Math: CoreMath, String: CoreString }; const result = this.function(Core, this.param, this.methods, this._set_error_from_error_bound); return result; } } traverse_CallExpression(node) { const method_arguments = node.arguments.map((arg) => { return this.traverse_node(arg); }); const callee = node.callee; const method_name = callee.name; if (method_name) { const literal_contruct = LITERAL_CONSTRUCT[method_name]; if (literal_contruct) { return literal_contruct(method_arguments); } const arguments_joined = `${method_arguments.join(ARGUMENTS_SEPARATOR)}`; const direct_function_name = DIRECT_EXPRESSION_FUNCTIONS[method_name]; if (direct_function_name) { return `${direct_function_name}(${arguments_joined})`; } const expressionRegister = Poly2.expressionsRegister; const indirect_method = expressionRegister.getMethod(method_name); if (indirect_method) { const path_node = node.arguments[0]; const function_string = `return ${method_arguments[0]}`; let path_argument_function; let path_argument = []; try { path_argument_function = new Function(function_string); path_argument = path_argument_function(); } catch { } this._create_method_and_dependencies(method_name, path_argument, path_node); return `(await methods[${this.method_index}].process_arguments([${arguments_joined}]))`; } else { const available_methods = expressionRegister.availableMethods().join(", "); const message = `method not found (${method_name}), available methods are: ${available_methods}`; Poly2.warn(message); } } this.set_error(`unknown method: ${method_name}`); } traverse_BinaryExpression(node) { return `(${this.traverse_node(node.left)} ${node.operator} ${this.traverse_node(node.right)})`; } traverse_LogicalExpression(node) { return `(${this.traverse_node(node.left)} ${node.operator} ${this.traverse_node(node.right)})`; } traverse_MemberExpression(node) { return `${this.traverse_node(node.object)}.${this.traverse_node(node.property)}`; } traverse_UnaryExpression(node) { if (node.operator === ATTRIBUTE_PREFIX) { let argument = node.argument; let attribute_name; let property; switch (argument.type) { case "Identifier": { const argument_identifier = argument; attribute_name = argument_identifier.name; break; } case "MemberExpression": { const argument_member_expression = argument; const attrib_node = argument_member_expression.object; const property_node = argument_member_expression.property; attribute_name = attrib_node.name; property = property_node.name; break; } } if (attribute_name) { attribute_name = CoreAttribute.remap_name(attribute_name); if (attribute_name == "ptnum") { return "((entity != null) ? entity.index() : 0)"; } else { const var_attribute_size = this._attribute_requirements_controller.var_attribute_size(attribute_name); const var_array = this._attribute_requirements_controller.var_array(attribute_name); this._attribute_requirements_controller.add(attribute_name); if (property) { const property_offset = PROPERTY_OFFSETS[property]; return `${var_array}[entity.index()*${var_attribute_size}+${property_offset}]`; } else { return `${var_array}[entity.index()*${var_attribute_size}]`; } } } else { console.warn("attribute not found"); return ""; } } else { return `${node.operator}${this.traverse_node(node.argument)}`; } } traverse_Literal(node) { return `${node.raw}`; } traverse_Identifier(node) { const identifier_first_char = node.name[0]; if (identifier_first_char == VARIABLE_PREFIX) { const identifier_name_without_dollar_sign = node.name.substr(1); const direct_constant_name = GLOBAL_CONSTANTS[identifier_name_without_dollar_sign]; if (direct_constant_name) { return direct_constant_name; } const method_name = `traverse_Identifier_${identifier_name_without_dollar_sign}`; const method = this[method_name]; if (method) { return this[method_name](); } else { this.set_error(`identifier unknown: ${node.name}`); } } else { return node.name; } } traverse_Identifier_F() { this.immutable_dependencies.push(this.param.scene().timeController.graph_node); return `param.scene().timeController.frame`; } traverse_Identifier_FPS() { this.immutable_dependencies.push(this.param.scene().timeController.graph_node); return `param.scene().timeController.fps`; } traverse_Identifier_T() { this.immutable_dependencies.push(this.param.scene().timeController.graph_node); return `param.scene().timeController.time`; } traverse_Identifier_CH() { return `${QUOTE}${this.param.name()}${QUOTE}`; } traverse_Identifier_CEX() { return this._method_centroid("x"); } traverse_Identifier_CEY() { return this._method_centroid("y"); } traverse_Identifier_CEZ() { return this._method_centroid("z"); } _method_centroid(component) { const method_arguments = [0, `${QUOTE}${component}${QUOTE}`]; const arguments_joined = method_arguments.join(ARGUMENTS_SEPARATOR); this._create_method_and_dependencies("centroid", 0); return `(await methods[${this.method_index}].process_arguments([${arguments_joined}]))`; } _create_method_and_dependencies(method_name, path_argument, path_node) { const expressionRegister = Poly2.expressionsRegister; const method_constructor = expressionRegister.getMethod(method_name); if (!method_constructor) { const available_methods = expressionRegister.availableMethods(); const message = `method not found (${method_name}), available methods are: ${available_methods.join(", ")}`; this.set_error(message); Poly2.warn(message); return; } const method = new method_constructor(this.param); this.method_index += 1; this.methods[this.method_index] = method; if (method.require_dependency()) { const method_dependency = method.find_dependency(path_argument); if (method_dependency) { if (path_node) { method_dependency.set_jsep_node(path_node); } this.method_dependencies.push(method_dependency); } else { if (path_node && CoreType.isString(path_argument)) { this.param.scene().missingExpressionReferencesController.register(this.param, path_node, path_argument); } } } } }