|
@@ -0,0 +1,186 @@
|
|
|
+import Node, { addNodeClass } from '../core/Node.js';
|
|
|
+import { expression } from '../code/ExpressionNode.js';
|
|
|
+import { bypass } from '../core/BypassNode.js';
|
|
|
+import { context as contextNode } from '../core/ContextNode.js';
|
|
|
+import { addNodeElement, nodeObject, nodeArray } from '../shadernode/ShaderNode.js';
|
|
|
+
|
|
|
+class LoopNode extends Node {
|
|
|
+
|
|
|
+ constructor( params = [] ) {
|
|
|
+
|
|
|
+ super();
|
|
|
+
|
|
|
+ this.params = params;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getVarName( index ) {
|
|
|
+
|
|
|
+ return String.fromCharCode( 'i'.charCodeAt() + index );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getProperties( builder ) {
|
|
|
+
|
|
|
+ const properties = builder.getNodeProperties( this );
|
|
|
+
|
|
|
+ if ( properties.stackNode !== undefined ) return properties;
|
|
|
+
|
|
|
+ //
|
|
|
+
|
|
|
+ const inputs = {};
|
|
|
+
|
|
|
+ for ( let i = 0, l = this.params.length - 1; i < l; i ++ ) {
|
|
|
+
|
|
|
+ const prop = this.getVarName( i );
|
|
|
+
|
|
|
+ inputs[ prop ] = expression( prop, 'int' );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ properties.returnsNode = this.params[ this.params.length - 1 ].call( inputs, builder.addStack(), builder );
|
|
|
+ properties.stackNode = builder.removeStack();
|
|
|
+
|
|
|
+ return properties;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getNodeType( builder ) {
|
|
|
+
|
|
|
+ const { returnsNode } = this.getProperties( builder );
|
|
|
+
|
|
|
+ return returnsNode ? returnsNode.getNodeType( builder ) : 'void';
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ construct( builder ) {
|
|
|
+
|
|
|
+ // construct properties
|
|
|
+
|
|
|
+ this.getProperties( builder );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ generate( builder ) {
|
|
|
+
|
|
|
+ const properties = this.getProperties( builder );
|
|
|
+
|
|
|
+ const context = { tempWrite: false };
|
|
|
+
|
|
|
+ const params = this.params;
|
|
|
+ const stackNode = properties.stackNode;
|
|
|
+
|
|
|
+ const returnsSnippet = properties.returnsNode ? properties.returnsNode.build( builder ) : '';
|
|
|
+
|
|
|
+ for ( let i = 0, l = params.length - 1; i < l; i ++ ) {
|
|
|
+
|
|
|
+ const param = params[ i ];
|
|
|
+ const property = this.getVarName( i );
|
|
|
+
|
|
|
+ let start = null, end = null, direction = null;
|
|
|
+
|
|
|
+ if ( param.isNode ) {
|
|
|
+
|
|
|
+ start = '0';
|
|
|
+ end = param.generate( builder, 'int' );
|
|
|
+ direction = 'forward';
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ start = param.start;
|
|
|
+ end = param.end;
|
|
|
+ direction = param.direction;
|
|
|
+
|
|
|
+ if ( typeof start === 'number' ) start = start.toString();
|
|
|
+ else if ( start && start.isNode ) start = start.generate( builder, 'int' );
|
|
|
+
|
|
|
+ if ( typeof end === 'number' ) end = end.toString();
|
|
|
+ else if ( end && end.isNode ) end = end.generate( builder, 'int' );
|
|
|
+
|
|
|
+ if ( start !== undefined && end === undefined ) {
|
|
|
+
|
|
|
+ start = start + ' - 1';
|
|
|
+ end = '0';
|
|
|
+ direction = 'backwards';
|
|
|
+
|
|
|
+ } else if ( end !== undefined && start === undefined ) {
|
|
|
+
|
|
|
+ start = '0';
|
|
|
+ direction = 'forward';
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( direction === undefined ) {
|
|
|
+
|
|
|
+ if ( Number( start ) > Number( end ) ) {
|
|
|
+
|
|
|
+ direction = 'backwards';
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ direction = 'forward';
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ const internalParam = { start, end, direction };
|
|
|
+
|
|
|
+ //
|
|
|
+
|
|
|
+ const startSnippet = internalParam.start;
|
|
|
+ const endSnippet = internalParam.end;
|
|
|
+
|
|
|
+ let declarationSnippet = '';
|
|
|
+ let conditionalSnippet = '';
|
|
|
+ let updateSnippet = '';
|
|
|
+
|
|
|
+ declarationSnippet += builder.getVar( 'int', property ) + ' = ' + startSnippet;
|
|
|
+
|
|
|
+ if ( internalParam.direction === 'backwards' ) {
|
|
|
+
|
|
|
+ conditionalSnippet += property + ' >= ' + endSnippet;
|
|
|
+ updateSnippet += property + ' --';
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ // forward
|
|
|
+
|
|
|
+ conditionalSnippet += property + ' < ' + endSnippet;
|
|
|
+ updateSnippet += property + ' ++';
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ const forSnippet = `for ( ${ declarationSnippet }; ${ conditionalSnippet }; ${ updateSnippet } )`;
|
|
|
+
|
|
|
+ builder.addFlowCode( ( i === 0 ? '\n' : '' ) + builder.tab + forSnippet + ' {\n\n' ).addFlowTab();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ const stackSnippet = contextNode( stackNode, context ).build( builder, 'void' );
|
|
|
+
|
|
|
+ builder.removeFlowTab().addFlowCode( '\n' + builder.tab + stackSnippet );
|
|
|
+
|
|
|
+ for ( let i = 0, l = this.params.length - 1; i < l; i ++ ) {
|
|
|
+
|
|
|
+ builder.addFlowCode( ( i === 0 ? '' : builder.tab ) + '}\n\n' ).removeFlowTab();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ builder.addFlowTab();
|
|
|
+
|
|
|
+ return returnsSnippet;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+export default LoopNode;
|
|
|
+
|
|
|
+export const loop = ( ...params ) => nodeObject( new LoopNode( nodeArray( params, 'int' ) ) );
|
|
|
+
|
|
|
+addNodeElement( 'loop', ( returns, ...params ) => bypass( returns, loop( ...params ) ) );
|
|
|
+
|
|
|
+addNodeClass( LoopNode );
|