2
0

LoopNode.js 4.4 KB

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