201 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			201 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								import Node, { addNodeClass } from '../core/Node.js';
							 | 
						||
| 
								 | 
							
								import { expression } from '../code/ExpressionNode.js';
							 | 
						||
| 
								 | 
							
								import { bypass } from '../core/BypassNode.js';
							 | 
						||
| 
								 | 
							
								import { context } 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 param = this.params[ i ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const name = ( param.isNode !== true && param.name ) || this.getVarName( i );
							 | 
						||
| 
								 | 
							
											const type = ( param.isNode !== true && param.type ) || 'int';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											inputs[ name ] = expression( name, type );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										properties.returnsNode = this.params[ this.params.length - 1 ]( inputs, builder.addStack(), builder );
							 | 
						||
| 
								 | 
							
										properties.stackNode = builder.removeStack();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return properties;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									getNodeType( builder ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const { returnsNode } = this.getProperties( builder );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return returnsNode ? returnsNode.getNodeType( builder ) : 'void';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setup( builder ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// setup properties
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.getProperties( builder );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									generate( builder ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const properties = this.getProperties( builder );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const contextData = { tempWrite: false };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const params = this.params;
							 | 
						||
| 
								 | 
							
										const stackNode = properties.stackNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for ( let i = 0, l = params.length - 1; i < l; i ++ ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const param = params[ i ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											let start = null, end = null, name = null, type = null, condition = null, update = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( param.isNode ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												type = 'int';
							 | 
						||
| 
								 | 
							
												name = this.getVarName( i );
							 | 
						||
| 
								 | 
							
												start = '0';
							 | 
						||
| 
								 | 
							
												end = param.build( builder, type );
							 | 
						||
| 
								 | 
							
												condition = '<';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											} else {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												type = param.type || 'int';
							 | 
						||
| 
								 | 
							
												name = param.name || this.getVarName( i );
							 | 
						||
| 
								 | 
							
												start = param.start;
							 | 
						||
| 
								 | 
							
												end = param.end;
							 | 
						||
| 
								 | 
							
												condition = param.condition;
							 | 
						||
| 
								 | 
							
												update = param.update;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( typeof start === 'number' ) start = start.toString();
							 | 
						||
| 
								 | 
							
												else if ( start && start.isNode ) start = start.build( builder, type );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( typeof end === 'number' ) end = end.toString();
							 | 
						||
| 
								 | 
							
												else if ( end && end.isNode ) end = end.build( builder, type );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( start !== undefined && end === undefined ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													start = start + ' - 1';
							 | 
						||
| 
								 | 
							
													end = '0';
							 | 
						||
| 
								 | 
							
													condition = '>=';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												} else if ( end !== undefined && start === undefined ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													start = '0';
							 | 
						||
| 
								 | 
							
													condition = '<';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( condition === undefined ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													if ( Number( start ) > Number( end ) ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														condition = '>=';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													} else {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														condition = '<';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const internalParam = { start, end, condition };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const startSnippet = internalParam.start;
							 | 
						||
| 
								 | 
							
											const endSnippet = internalParam.end;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											let declarationSnippet = '';
							 | 
						||
| 
								 | 
							
											let conditionalSnippet = '';
							 | 
						||
| 
								 | 
							
											let updateSnippet = '';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( ! update ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( type === 'int' || type === 'uint' ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													if ( condition.includes( '<' ) ) update = '++';
							 | 
						||
| 
								 | 
							
													else update = '--';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												} else {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													if ( condition.includes( '<' ) ) update = '+= 1.';
							 | 
						||
| 
								 | 
							
													else update = '-= 1.';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											declarationSnippet += builder.getVar( type, name ) + ' = ' + startSnippet;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											conditionalSnippet += name + ' ' + condition + ' ' + endSnippet;
							 | 
						||
| 
								 | 
							
											updateSnippet += name + ' ' + update;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const forSnippet = `for ( ${ declarationSnippet }; ${ conditionalSnippet }; ${ updateSnippet } )`;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											builder.addFlowCode( ( i === 0 ? '\n' : '' ) + builder.tab + forSnippet + ' {\n\n' ).addFlowTab();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const stackSnippet = context( stackNode, contextData ).build( builder, 'void' );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const returnsSnippet = properties.returnsNode ? properties.returnsNode.build( builder ) : '';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										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' ) ) ).append();
							 | 
						||
| 
								 | 
							
								export const Continue = () => expression( 'continue' ).append();
							 | 
						||
| 
								 | 
							
								export const Break = () => expression( 'break' ).append();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								addNodeElement( 'loop', ( returns, ...params ) => bypass( returns, loop( ...params ) ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								addNodeClass( 'LoopNode', LoopNode );
							 |