@openhps/core
Version:
Open Hybrid Positioning System - Core component
563 lines (516 loc) • 17 kB
JavaScript
import { WebGLCoordinateSystem } from '../../constants.js';
import TempNode from '../core/TempNode.js';
import { addMethodChaining, int, nodeProxy } from '../tsl/TSLCore.js';
/**
* This node represents basic mathematical and logical operations like addition,
* subtraction or comparisons (e.g. `equal()`).
*
* @augments TempNode
*/
class OperatorNode extends TempNode {
static get type() {
return 'OperatorNode';
}
/**
* Constructs a new operator node.
*
* @param {string} op - The operator.
* @param {Node} aNode - The first input.
* @param {Node} bNode - The second input.
* @param {...Node} params - Additional input parameters.
*/
constructor(op, aNode, bNode, ...params) {
super();
if (params.length > 0) {
let finalOp = new OperatorNode(op, aNode, bNode);
for (let i = 0; i < params.length - 1; i++) {
finalOp = new OperatorNode(op, finalOp, params[i]);
}
aNode = finalOp;
bNode = params[params.length - 1];
}
/**
* The operator.
*
* @type {string}
*/
this.op = op;
/**
* The first input.
*
* @type {Node}
*/
this.aNode = aNode;
/**
* The second input.
*
* @type {Node}
*/
this.bNode = bNode;
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isOperatorNode = true;
}
/**
* This method is overwritten since the node type is inferred from the operator
* and the input node types.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {string} output - The current output string.
* @return {string} The node type.
*/
getNodeType(builder, output) {
const op = this.op;
const aNode = this.aNode;
const bNode = this.bNode;
const typeA = aNode.getNodeType(builder);
const typeB = typeof bNode !== 'undefined' ? bNode.getNodeType(builder) : null;
if (typeA === 'void' || typeB === 'void') {
return 'void';
} else if (op === '%') {
return typeA;
} else if (op === '~' || op === '&' || op === '|' || op === '^' || op === '>>' || op === '<<') {
return builder.getIntegerType(typeA);
} else if (op === '!' || op === '==' || op === '!=' || op === '&&' || op === '||' || op === '^^') {
return 'bool';
} else if (op === '<' || op === '>' || op === '<=' || op === '>=') {
const typeLength = output ? builder.getTypeLength(output) : Math.max(builder.getTypeLength(typeA), builder.getTypeLength(typeB));
return typeLength > 1 ? `bvec${typeLength}` : 'bool';
} else {
// Handle matrix operations
if (builder.isMatrix(typeA)) {
if (typeB === 'float') {
return typeA; // matrix * scalar = matrix
} else if (builder.isVector(typeB)) {
return builder.getVectorFromMatrix(typeA); // matrix * vector
} else if (builder.isMatrix(typeB)) {
return typeA; // matrix * matrix
}
} else if (builder.isMatrix(typeB)) {
if (typeA === 'float') {
return typeB; // scalar * matrix = matrix
} else if (builder.isVector(typeA)) {
return builder.getVectorFromMatrix(typeB); // vector * matrix
}
}
// Handle non-matrix cases
if (builder.getTypeLength(typeB) > builder.getTypeLength(typeA)) {
// anytype x anytype: use the greater length vector
return typeB;
}
return typeA;
}
}
generate(builder, output) {
const op = this.op;
const aNode = this.aNode;
const bNode = this.bNode;
const type = this.getNodeType(builder, output);
let typeA = null;
let typeB = null;
if (type !== 'void') {
typeA = aNode.getNodeType(builder);
typeB = typeof bNode !== 'undefined' ? bNode.getNodeType(builder) : null;
if (op === '<' || op === '>' || op === '<=' || op === '>=' || op === '==' || op === '!=') {
if (builder.isVector(typeA)) {
typeB = typeA;
} else if (typeA !== typeB) {
typeA = typeB = 'float';
}
} else if (op === '>>' || op === '<<') {
typeA = type;
typeB = builder.changeComponentType(typeB, 'uint');
} else if (op === '%') {
typeA = type;
typeB = builder.isInteger(typeA) && builder.isInteger(typeB) ? typeB : typeA;
} else if (builder.isMatrix(typeA)) {
if (typeB === 'float') {
// Keep matrix type for typeA, but ensure typeB stays float
typeB = 'float';
} else if (builder.isVector(typeB)) {
// matrix x vector
typeB = builder.getVectorFromMatrix(typeA);
} else if (builder.isMatrix(typeB)) {
// matrix x matrix - keep both types
} else {
typeA = typeB = type;
}
} else if (builder.isMatrix(typeB)) {
if (typeA === 'float') {
// Keep matrix type for typeB, but ensure typeA stays float
typeA = 'float';
} else if (builder.isVector(typeA)) {
// vector x matrix
typeA = builder.getVectorFromMatrix(typeB);
} else {
typeA = typeB = type;
}
} else {
// anytype x anytype
typeA = typeB = type;
}
} else {
typeA = typeB = type;
}
const a = aNode.build(builder, typeA);
const b = typeof bNode !== 'undefined' ? bNode.build(builder, typeB) : null;
const outputLength = builder.getTypeLength(output);
const fnOpSnippet = builder.getFunctionOperator(op);
if (output !== 'void') {
const isGLSL = builder.renderer.coordinateSystem === WebGLCoordinateSystem;
if (op === '==') {
if (isGLSL) {
if (outputLength > 1) {
return builder.format(`${builder.getMethod('equal', output)}( ${a}, ${b} )`, type, output);
} else {
return builder.format(`( ${a} ${op} ${b} )`, type, output);
}
} else {
// WGSL
if (outputLength > 1 || !builder.isVector(typeA)) {
return builder.format(`( ${a} == ${b} )`, type, output);
} else {
return builder.format(`all( ${a} == ${b} )`, type, output);
}
}
} else if (op === '<' && outputLength > 1) {
if (isGLSL) {
return builder.format(`${builder.getMethod('lessThan', output)}( ${a}, ${b} )`, type, output);
} else {
// WGSL
return builder.format(`( ${a} < ${b} )`, type, output);
}
} else if (op === '<=' && outputLength > 1) {
if (isGLSL) {
return builder.format(`${builder.getMethod('lessThanEqual', output)}( ${a}, ${b} )`, type, output);
} else {
// WGSL
return builder.format(`( ${a} <= ${b} )`, type, output);
}
} else if (op === '>' && outputLength > 1) {
if (isGLSL) {
return builder.format(`${builder.getMethod('greaterThan', output)}( ${a}, ${b} )`, type, output);
} else {
// WGSL
return builder.format(`( ${a} > ${b} )`, type, output);
}
} else if (op === '>=' && outputLength > 1) {
if (isGLSL) {
return builder.format(`${builder.getMethod('greaterThanEqual', output)}( ${a}, ${b} )`, type, output);
} else {
// WGSL
return builder.format(`( ${a} >= ${b} )`, type, output);
}
} else if (op === '%') {
if (builder.isInteger(typeB)) {
return builder.format(`( ${a} % ${b} )`, type, output);
} else {
return builder.format(`${builder.getMethod('mod', type)}( ${a}, ${b} )`, type, output);
}
} else if (op === '!' || op === '~') {
return builder.format(`(${op}${a})`, typeA, output);
} else if (fnOpSnippet) {
return builder.format(`${fnOpSnippet}( ${a}, ${b} )`, type, output);
} else {
// Handle matrix operations
if (builder.isMatrix(typeA) && typeB === 'float') {
return builder.format(`( ${b} ${op} ${a} )`, type, output);
} else if (typeA === 'float' && builder.isMatrix(typeB)) {
return builder.format(`${a} ${op} ${b}`, type, output);
} else {
let snippet = `( ${a} ${op} ${b} )`;
if (!isGLSL && type === 'bool' && builder.isVector(typeA) && builder.isVector(typeB)) {
snippet = `all${snippet}`;
}
return builder.format(snippet, type, output);
}
}
} else if (typeA !== 'void') {
if (fnOpSnippet) {
return builder.format(`${fnOpSnippet}( ${a}, ${b} )`, type, output);
} else {
if (builder.isMatrix(typeA) && typeB === 'float') {
return builder.format(`${b} ${op} ${a}`, type, output);
} else {
return builder.format(`${a} ${op} ${b}`, type, output);
}
}
}
}
serialize(data) {
super.serialize(data);
data.op = this.op;
}
deserialize(data) {
super.deserialize(data);
this.op = data.op;
}
}
export default OperatorNode;
/**
* Returns the addition of two or more value.
*
* @tsl
* @function
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @param {...Node} params - Additional input parameters.
* @returns {OperatorNode}
*/
export const add = /*@__PURE__*/nodeProxy(OperatorNode, '+').setParameterLength(2, Infinity).setName('add');
/**
* Returns the subtraction of two or more value.
*
* @tsl
* @function
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @param {...Node} params - Additional input parameters.
* @returns {OperatorNode}
*/
export const sub = /*@__PURE__*/nodeProxy(OperatorNode, '-').setParameterLength(2, Infinity).setName('sub');
/**
* Returns the multiplication of two or more value.
*
* @tsl
* @function
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @param {...Node} params - Additional input parameters.
* @returns {OperatorNode}
*/
export const mul = /*@__PURE__*/nodeProxy(OperatorNode, '*').setParameterLength(2, Infinity).setName('mul');
/**
* Returns the division of two or more value.
*
* @tsl
* @function
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @param {...Node} params - Additional input parameters.
* @returns {OperatorNode}
*/
export const div = /*@__PURE__*/nodeProxy(OperatorNode, '/').setParameterLength(2, Infinity).setName('div');
/**
* Computes the remainder of dividing the first node by the second one.
*
* @tsl
* @function
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @returns {OperatorNode}
*/
export const mod = /*@__PURE__*/nodeProxy(OperatorNode, '%').setParameterLength(2).setName('mod');
/**
* Checks if two nodes are equal.
*
* @tsl
* @function
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @returns {OperatorNode}
*/
export const equal = /*@__PURE__*/nodeProxy(OperatorNode, '==').setParameterLength(2).setName('equal');
/**
* Checks if two nodes are not equal.
*
* @tsl
* @function
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @returns {OperatorNode}
*/
export const notEqual = /*@__PURE__*/nodeProxy(OperatorNode, '!=').setParameterLength(2).setName('notEqual');
/**
* Checks if the first node is less than the second.
*
* @tsl
* @function
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @returns {OperatorNode}
*/
export const lessThan = /*@__PURE__*/nodeProxy(OperatorNode, '<').setParameterLength(2).setName('lessThan');
/**
* Checks if the first node is greater than the second.
*
* @tsl
* @function
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @returns {OperatorNode}
*/
export const greaterThan = /*@__PURE__*/nodeProxy(OperatorNode, '>').setParameterLength(2).setName('greaterThan');
/**
* Checks if the first node is less than or equal to the second.
*
* @tsl
* @function
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @returns {OperatorNode}
*/
export const lessThanEqual = /*@__PURE__*/nodeProxy(OperatorNode, '<=').setParameterLength(2).setName('lessThanEqual');
/**
* Checks if the first node is greater than or equal to the second.
*
* @tsl
* @function
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @returns {OperatorNode}
*/
export const greaterThanEqual = /*@__PURE__*/nodeProxy(OperatorNode, '>=').setParameterLength(2).setName('greaterThanEqual');
/**
* Performs a logical AND operation on multiple nodes.
*
* @tsl
* @function
* @param {...Node} nodes - The input nodes to be combined using AND.
* @returns {OperatorNode}
*/
export const and = /*@__PURE__*/nodeProxy(OperatorNode, '&&').setParameterLength(2, Infinity).setName('and');
/**
* Performs a logical OR operation on multiple nodes.
*
* @tsl
* @function
* @param {...Node} nodes - The input nodes to be combined using OR.
* @returns {OperatorNode}
*/
export const or = /*@__PURE__*/nodeProxy(OperatorNode, '||').setParameterLength(2, Infinity).setName('or');
/**
* Performs logical NOT on a node.
*
* @tsl
* @function
* @param {Node} value - The value.
* @returns {OperatorNode}
*/
export const not = /*@__PURE__*/nodeProxy(OperatorNode, '!').setParameterLength(1).setName('not');
/**
* Performs logical XOR on two nodes.
*
* @tsl
* @function
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @returns {OperatorNode}
*/
export const xor = /*@__PURE__*/nodeProxy(OperatorNode, '^^').setParameterLength(2).setName('xor');
/**
* Performs bitwise AND on two nodes.
*
* @tsl
* @function
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @returns {OperatorNode}
*/
export const bitAnd = /*@__PURE__*/nodeProxy(OperatorNode, '&').setParameterLength(2).setName('bitAnd');
/**
* Performs bitwise NOT on a node.
*
* @tsl
* @function
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @returns {OperatorNode}
*/
export const bitNot = /*@__PURE__*/nodeProxy(OperatorNode, '~').setParameterLength(2).setName('bitNot');
/**
* Performs bitwise OR on two nodes.
*
* @tsl
* @function
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @returns {OperatorNode}
*/
export const bitOr = /*@__PURE__*/nodeProxy(OperatorNode, '|').setParameterLength(2).setName('bitOr');
/**
* Performs bitwise XOR on two nodes.
*
* @tsl
* @function
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @returns {OperatorNode}
*/
export const bitXor = /*@__PURE__*/nodeProxy(OperatorNode, '^').setParameterLength(2).setName('bitXor');
/**
* Shifts a node to the left.
*
* @tsl
* @function
* @param {Node} a - The node to shift.
* @param {Node} b - The value to shift.
* @returns {OperatorNode}
*/
export const shiftLeft = /*@__PURE__*/nodeProxy(OperatorNode, '<<').setParameterLength(2).setName('shiftLeft');
/**
* Shifts a node to the right.
*
* @tsl
* @function
* @param {Node} a - The node to shift.
* @param {Node} b - The value to shift.
* @returns {OperatorNode}
*/
export const shiftRight = /*@__PURE__*/nodeProxy(OperatorNode, '>>').setParameterLength(2).setName('shiftRight');
addMethodChaining('add', add);
addMethodChaining('sub', sub);
addMethodChaining('mul', mul);
addMethodChaining('div', div);
addMethodChaining('mod', mod);
addMethodChaining('equal', equal);
addMethodChaining('notEqual', notEqual);
addMethodChaining('lessThan', lessThan);
addMethodChaining('greaterThan', greaterThan);
addMethodChaining('lessThanEqual', lessThanEqual);
addMethodChaining('greaterThanEqual', greaterThanEqual);
addMethodChaining('and', and);
addMethodChaining('or', or);
addMethodChaining('not', not);
addMethodChaining('xor', xor);
addMethodChaining('bitAnd', bitAnd);
addMethodChaining('bitNot', bitNot);
addMethodChaining('bitOr', bitOr);
addMethodChaining('bitXor', bitXor);
addMethodChaining('shiftLeft', shiftLeft);
addMethodChaining('shiftRight', shiftRight);
/**
* @tsl
* @function
* @deprecated since r168. Use {@link mod} instead.
*
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @returns {OperatorNode}
*/
export const remainder = (a, b) => {
// @deprecated, r168
console.warn('THREE.TSL: "remainder()" is deprecated. Use "mod( int( ... ) )" instead.');
return mod(a, b);
};
/**
* @tsl
* @function
* @deprecated since r175. Use {@link mod} instead.
*
* @param {Node} a - The first input.
* @param {Node} b - The second input.
* @returns {OperatorNode}
*/
export const modInt = (a, b) => {
// @deprecated, r175
console.warn('THREE.TSL: "modInt()" is deprecated. Use "mod( int( ... ) )" instead.');
return mod(int(a), int(b));
};
addMethodChaining('remainder', remainder);
addMethodChaining('modInt', modInt);