Skip to content

⬅️ Back to Table of Contents

📄 OperatorNode.js

📊 Analysis Summary

Metric Count
🔧 Functions 6
🧱 Classes 1
📦 Imports 6
📊 Variables & Constants 12

📚 Table of Contents

🛠️ File Location:

📂 src/nodes/math/OperatorNode.js

📦 Imports

Name Source
WebGLCoordinateSystem ../../constants.js
TempNode ../core/TempNode.js
addMethodChaining ../tsl/TSLCore.js
Fn ../tsl/TSLCore.js
int ../tsl/TSLCore.js
nodeProxyIntent ../tsl/TSLCore.js

Variables & Constants

Name Type Kind Value Exported
_vectorOperators { '==': string; '!=': string; '<': st... let/var { '==': 'equal', '!=': 'notEqual', '<': 'lessThan', '>': 'greaterThan', '<=':...
finalOp OperatorNode let/var new OperatorNode( op, aNode, bNode )
op string let/var this.op
aNode Node let/var this.aNode
bNode Node let/var this.bNode
typeB any let/var bNode ? bNode.getNodeType( builder ) : null
op string let/var this.op
typeA any let/var null
typeB any let/var null
b any let/var bNode ? bNode.build( builder, typeB ) : null
isGLSL boolean let/var builder.renderer.coordinateSystem === WebGLCoordinateSystem
snippet string let/var ( ${ a } ${ op } ${ b } )

Functions

OperatorNode.getOperatorMethod(builder: NodeBuilder, output: string): string

JSDoc:

/**
     * Returns the operator method name.
     *
     * @param {NodeBuilder} builder - The current node builder.
     * @param {string} output - The output type.
     * @returns {string} The operator method name.
     */

Parameters:

  • builder NodeBuilder
  • output string

Returns: string

Calls:

  • builder.getMethod
Code
getOperatorMethod( builder, output ) {

        return builder.getMethod( _vectorOperators[ this.op ], output );

    }

OperatorNode.getNodeType(builder: NodeBuilder): string

JSDoc:

/**
     * 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.
     * @return {string} The node type.
     */

Parameters:

  • builder NodeBuilder

Returns: string

Calls:

  • aNode.getNodeType
  • bNode.getNodeType
  • builder.getIntegerType
  • Math.max
  • builder.getTypeLength
  • builder.isMatrix
  • builder.isVector
  • builder.getVectorFromMatrix

Internal Comments:

// Handle matrix operations
// Handle non-matrix cases
// anytype x anytype: use the greater length vector

Code
getNodeType( builder ) {

        const op = this.op;

        const aNode = this.aNode;
        const bNode = this.bNode;

        const typeA = aNode.getNodeType( builder );
        const typeB = bNode ? 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 === '^^' ) {

            return 'bool';

        } else if ( op === '==' || op === '!=' || op === '<' || op === '>' || op === '<=' || op === '>=' ) {

            const typeLength = 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;

        }

    }

OperatorNode.generate(builder: any, output: any): any

Parameters:

  • builder any
  • output any

Returns: any

Calls:

  • this.getNodeType
  • aNode.getNodeType
  • bNode.getNodeType
  • builder.isVector
  • builder.changeComponentType
  • builder.isInteger
  • builder.isMatrix
  • builder.getVectorFromMatrix
  • aNode.build
  • bNode.build
  • builder.getFunctionOperator
  • builder.format
  • this.getOperatorMethod

Internal Comments:

// Keep matrix type for typeA, but ensure typeB stays float (x3)
// matrix x vector (x3)
// Keep matrix type for typeB, but ensure typeA stays float (x3)
// vector x matrix (x3)
// anytype x anytype (x3)
// WGSL
// Handle matrix operations

Code
generate( builder, output ) {

        const op = this.op;

        const { aNode, bNode } = this;

        const type = this.getNodeType( builder );

        let typeA = null;
        let typeB = null;

        if ( type !== 'void' ) {

            typeA = aNode.getNodeType( builder );
            typeB = bNode ? bNode.getNodeType( builder ) : null;

            if ( op === '<' || op === '>' || op === '<=' || op === '>=' || op === '==' || op === '!=' ) {

                if ( builder.isVector( typeA ) ) {

                    typeB = typeA;

                } else if ( builder.isVector( typeB ) ) {

                    typeA = typeB;

                } 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 = bNode ? bNode.build( builder, typeB ) : null;

        const fnOpSnippet = builder.getFunctionOperator( op );

        if ( output !== 'void' ) {

            const isGLSL = builder.renderer.coordinateSystem === WebGLCoordinateSystem;

            if ( op === '==' || op === '!=' || op === '<' || op === '>' || op === '<=' || op === '>=' ) {

                if ( isGLSL ) {

                    if ( builder.isVector( typeA ) ) {

                        return builder.format( `${ this.getOperatorMethod( builder, output ) }( ${ a }, ${ b } )`, type, output );

                    } else {

                        return builder.format( `( ${ a } ${ op } ${ b } )`, type, output );

                    }

                } else {

                    // WGSL

                    return builder.format( `( ${ a } ${ op } ${ b } )`, type, output );

                }

            } else if ( op === '%' ) {

                if ( builder.isInteger( typeB ) ) {

                    return builder.format( `( ${ a } % ${ b } )`, type, output );

                } else {

                    return builder.format( `${ this.getOperatorMethod( builder, 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 );

                }

            }

        }

    }

OperatorNode.serialize(data: any): void

Parameters:

  • data any

Returns: void

Calls:

  • super.serialize
Code
serialize( data ) {

        super.serialize( data );

        data.op = this.op;

    }

OperatorNode.deserialize(data: any): void

Parameters:

  • data any

Returns: void

Calls:

  • super.deserialize
Code
deserialize( data ) {

        super.deserialize( data );

        this.op = data.op;

    }

modInt(a: Node, b: Node): OperatorNode

Parameters:

  • a Node
  • b Node

Returns: OperatorNode

Calls:

  • console.warn
  • mod
  • int (from ../tsl/TSLCore.js)
Code
( a, b ) => { // @deprecated, r175

    console.warn( 'THREE.TSL: "modInt()" is deprecated. Use "mod( int( ... ) )" instead.' );
    return mod( int( a ), int( b ) );

}

Classes

OperatorNode

Class Code
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;

    }

    /**
     * Returns the operator method name.
     *
     * @param {NodeBuilder} builder - The current node builder.
     * @param {string} output - The output type.
     * @returns {string} The operator method name.
     */
    getOperatorMethod( builder, output ) {

        return builder.getMethod( _vectorOperators[ this.op ], output );

    }

    /**
     * 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.
     * @return {string} The node type.
     */
    getNodeType( builder ) {

        const op = this.op;

        const aNode = this.aNode;
        const bNode = this.bNode;

        const typeA = aNode.getNodeType( builder );
        const typeB = bNode ? 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 === '^^' ) {

            return 'bool';

        } else if ( op === '==' || op === '!=' || op === '<' || op === '>' || op === '<=' || op === '>=' ) {

            const typeLength = 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, bNode } = this;

        const type = this.getNodeType( builder );

        let typeA = null;
        let typeB = null;

        if ( type !== 'void' ) {

            typeA = aNode.getNodeType( builder );
            typeB = bNode ? bNode.getNodeType( builder ) : null;

            if ( op === '<' || op === '>' || op === '<=' || op === '>=' || op === '==' || op === '!=' ) {

                if ( builder.isVector( typeA ) ) {

                    typeB = typeA;

                } else if ( builder.isVector( typeB ) ) {

                    typeA = typeB;

                } 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 = bNode ? bNode.build( builder, typeB ) : null;

        const fnOpSnippet = builder.getFunctionOperator( op );

        if ( output !== 'void' ) {

            const isGLSL = builder.renderer.coordinateSystem === WebGLCoordinateSystem;

            if ( op === '==' || op === '!=' || op === '<' || op === '>' || op === '<=' || op === '>=' ) {

                if ( isGLSL ) {

                    if ( builder.isVector( typeA ) ) {

                        return builder.format( `${ this.getOperatorMethod( builder, output ) }( ${ a }, ${ b } )`, type, output );

                    } else {

                        return builder.format( `( ${ a } ${ op } ${ b } )`, type, output );

                    }

                } else {

                    // WGSL

                    return builder.format( `( ${ a } ${ op } ${ b } )`, type, output );

                }

            } else if ( op === '%' ) {

                if ( builder.isInteger( typeB ) ) {

                    return builder.format( `( ${ a } % ${ b } )`, type, output );

                } else {

                    return builder.format( `${ this.getOperatorMethod( builder, 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;

    }

}

Methods

getOperatorMethod(builder: NodeBuilder, output: string): string
Code
getOperatorMethod( builder, output ) {

        return builder.getMethod( _vectorOperators[ this.op ], output );

    }
getNodeType(builder: NodeBuilder): string
Code
getNodeType( builder ) {

        const op = this.op;

        const aNode = this.aNode;
        const bNode = this.bNode;

        const typeA = aNode.getNodeType( builder );
        const typeB = bNode ? 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 === '^^' ) {

            return 'bool';

        } else if ( op === '==' || op === '!=' || op === '<' || op === '>' || op === '<=' || op === '>=' ) {

            const typeLength = 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: any, output: any): any
Code
generate( builder, output ) {

        const op = this.op;

        const { aNode, bNode } = this;

        const type = this.getNodeType( builder );

        let typeA = null;
        let typeB = null;

        if ( type !== 'void' ) {

            typeA = aNode.getNodeType( builder );
            typeB = bNode ? bNode.getNodeType( builder ) : null;

            if ( op === '<' || op === '>' || op === '<=' || op === '>=' || op === '==' || op === '!=' ) {

                if ( builder.isVector( typeA ) ) {

                    typeB = typeA;

                } else if ( builder.isVector( typeB ) ) {

                    typeA = typeB;

                } 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 = bNode ? bNode.build( builder, typeB ) : null;

        const fnOpSnippet = builder.getFunctionOperator( op );

        if ( output !== 'void' ) {

            const isGLSL = builder.renderer.coordinateSystem === WebGLCoordinateSystem;

            if ( op === '==' || op === '!=' || op === '<' || op === '>' || op === '<=' || op === '>=' ) {

                if ( isGLSL ) {

                    if ( builder.isVector( typeA ) ) {

                        return builder.format( `${ this.getOperatorMethod( builder, output ) }( ${ a }, ${ b } )`, type, output );

                    } else {

                        return builder.format( `( ${ a } ${ op } ${ b } )`, type, output );

                    }

                } else {

                    // WGSL

                    return builder.format( `( ${ a } ${ op } ${ b } )`, type, output );

                }

            } else if ( op === '%' ) {

                if ( builder.isInteger( typeB ) ) {

                    return builder.format( `( ${ a } % ${ b } )`, type, output );

                } else {

                    return builder.format( `${ this.getOperatorMethod( builder, 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: any): void
Code
serialize( data ) {

        super.serialize( data );

        data.op = this.op;

    }
deserialize(data: any): void
Code
deserialize( data ) {

        super.deserialize( data );

        this.op = data.op;

    }