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 ); |