LoopNode.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import Node, { addNodeClass } from '../core/Node.js';
  2. import { expression } from '../code/ExpressionNode.js';
  3. import { bypass } from '../core/BypassNode.js';
  4. import { context } from '../core/ContextNode.js';
  5. import { addNodeElement, nodeObject, nodeArray } from '../shadernode/ShaderNode.js';
  6. class LoopNode extends Node {
  7. constructor( params = [] ) {
  8. super();
  9. this.params = params;
  10. }
  11. getVarName( index ) {
  12. return String.fromCharCode( 'i'.charCodeAt() + index );
  13. }
  14. getProperties( builder ) {
  15. const properties = builder.getNodeProperties( this );
  16. if ( properties.stackNode !== undefined ) return properties;
  17. //
  18. const inputs = {};
  19. for ( let i = 0, l = this.params.length - 1; i < l; i ++ ) {
  20. const prop = this.getVarName( i );
  21. inputs[ prop ] = expression( prop, 'int' );
  22. }
  23. properties.returnsNode = this.params[ this.params.length - 1 ]( inputs, builder.addStack(), builder );
  24. properties.stackNode = builder.removeStack();
  25. return properties;
  26. }
  27. getNodeType( builder ) {
  28. const { returnsNode } = this.getProperties( builder );
  29. return returnsNode ? returnsNode.getNodeType( builder ) : 'void';
  30. }
  31. setup( builder ) {
  32. // setup properties
  33. this.getProperties( builder );
  34. }
  35. generate( builder ) {
  36. const properties = this.getProperties( builder );
  37. const contextData = { tempWrite: false };
  38. const params = this.params;
  39. const stackNode = properties.stackNode;
  40. for ( let i = 0, l = params.length - 1; i < l; i ++ ) {
  41. const param = params[ i ];
  42. const property = this.getVarName( i );
  43. let start = null, end = null, direction = null;
  44. if ( param.isNode ) {
  45. start = '0';
  46. end = param.build( builder, 'int' );
  47. direction = 'forward';
  48. } else {
  49. start = param.start;
  50. end = param.end;
  51. direction = param.direction;
  52. if ( typeof start === 'number' ) start = start.toString();
  53. else if ( start && start.isNode ) start = start.build( builder, 'int' );
  54. if ( typeof end === 'number' ) end = end.toString();
  55. else if ( end && end.isNode ) end = end.build( builder, 'int' );
  56. if ( start !== undefined && end === undefined ) {
  57. start = start + ' - 1';
  58. end = '0';
  59. direction = 'backwards';
  60. } else if ( end !== undefined && start === undefined ) {
  61. start = '0';
  62. direction = 'forward';
  63. }
  64. if ( direction === undefined ) {
  65. if ( Number( start ) > Number( end ) ) {
  66. direction = 'backwards';
  67. } else {
  68. direction = 'forward';
  69. }
  70. }
  71. }
  72. const internalParam = { start, end, direction };
  73. //
  74. const startSnippet = internalParam.start;
  75. const endSnippet = internalParam.end;
  76. let declarationSnippet = '';
  77. let conditionalSnippet = '';
  78. let updateSnippet = '';
  79. declarationSnippet += builder.getVar( 'int', property ) + ' = ' + startSnippet;
  80. if ( internalParam.direction === 'backwards' ) {
  81. conditionalSnippet += property + ' >= ' + endSnippet;
  82. updateSnippet += property + ' --';
  83. } else {
  84. // forward
  85. conditionalSnippet += property + ' < ' + endSnippet;
  86. updateSnippet += property + ' ++';
  87. }
  88. const forSnippet = `for ( ${ declarationSnippet }; ${ conditionalSnippet }; ${ updateSnippet } )`;
  89. builder.addFlowCode( ( i === 0 ? '\n' : '' ) + builder.tab + forSnippet + ' {\n\n' ).addFlowTab();
  90. }
  91. const stackSnippet = context( stackNode, contextData ).build( builder, 'void' );
  92. const returnsSnippet = properties.returnsNode ? properties.returnsNode.build( builder ) : '';
  93. builder.removeFlowTab().addFlowCode( '\n' + builder.tab + stackSnippet );
  94. for ( let i = 0, l = this.params.length - 1; i < l; i ++ ) {
  95. builder.addFlowCode( ( i === 0 ? '' : builder.tab ) + '}\n\n' ).removeFlowTab();
  96. }
  97. builder.addFlowTab();
  98. return returnsSnippet;
  99. }
  100. }
  101. export default LoopNode;
  102. export const loop = ( ...params ) => nodeObject( new LoopNode( nodeArray( params, 'int' ) ) );
  103. addNodeElement( 'loop', ( returns, ...params ) => bypass( returns, loop( ...params ) ) );
  104. addNodeClass( LoopNode );