ConditionalNode.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. import Node from '../core/Node.js';
  2. import { property } from '../core/PropertyNode.js';
  3. import { addMethodChaining, nodeProxy } from '../tsl/TSLCore.js';
  4. import { warn } from '../../utils.js';
  5. /**
  6. * Represents a logical `if/else` statement. Can be used as an alternative
  7. * to the `If()`/`Else()` syntax.
  8. *
  9. * The corresponding TSL `select()` looks like so:
  10. * ```js
  11. * velocity = position.greaterThanEqual( limit ).select( velocity.negate(), velocity );
  12. * ```
  13. * The `select()` method is called in a chaining fashion on a condition. The parameter nodes of `select()`
  14. * determine the outcome of the entire statement.
  15. *
  16. * @augments Node
  17. */
  18. class ConditionalNode extends Node {
  19. static get type() {
  20. return 'ConditionalNode';
  21. }
  22. /**
  23. * Constructs a new conditional node.
  24. *
  25. * @param {Node} condNode - The node that defines the condition.
  26. * @param {Node} ifNode - The node that is evaluate when the condition ends up `true`.
  27. * @param {?Node} [elseNode=null] - The node that is evaluate when the condition ends up `false`.
  28. */
  29. constructor( condNode, ifNode, elseNode = null ) {
  30. super();
  31. /**
  32. * The node that defines the condition.
  33. *
  34. * @type {Node}
  35. */
  36. this.condNode = condNode;
  37. /**
  38. * The node that is evaluate when the condition ends up `true`.
  39. *
  40. * @type {Node}
  41. */
  42. this.ifNode = ifNode;
  43. /**
  44. * The node that is evaluate when the condition ends up `false`.
  45. *
  46. * @type {?Node}
  47. * @default null
  48. */
  49. this.elseNode = elseNode;
  50. }
  51. /**
  52. * This method is overwritten since the node type is inferred from the if/else
  53. * nodes.
  54. *
  55. * @param {NodeBuilder} builder - The current node builder.
  56. * @return {string} The node type.
  57. */
  58. generateNodeType( builder ) {
  59. const { ifNode, elseNode } = builder.getNodeProperties( this );
  60. if ( ifNode === undefined ) {
  61. // fallback setup
  62. builder.flowBuildStage( this, 'setup' );
  63. return this.getNodeType( builder );
  64. }
  65. const ifType = ifNode.getNodeType( builder );
  66. if ( elseNode !== null ) {
  67. const elseType = elseNode.getNodeType( builder );
  68. if ( builder.getTypeLength( elseType ) > builder.getTypeLength( ifType ) ) {
  69. return elseType;
  70. }
  71. }
  72. return ifType;
  73. }
  74. setup( builder ) {
  75. const condNode = this.condNode;
  76. const ifNode = this.ifNode.isolate();
  77. const elseNode = this.elseNode ? this.elseNode.isolate() : null;
  78. //
  79. const currentNodeBlock = builder.context.nodeBlock;
  80. builder.getDataFromNode( ifNode ).parentNodeBlock = currentNodeBlock;
  81. if ( elseNode !== null ) builder.getDataFromNode( elseNode ).parentNodeBlock = currentNodeBlock;
  82. //
  83. const isUniformFlow = builder.context.uniformFlow;
  84. const properties = builder.getNodeProperties( this );
  85. properties.condNode = condNode;
  86. properties.ifNode = isUniformFlow ? ifNode : ifNode.context( { nodeBlock: ifNode } );
  87. properties.elseNode = elseNode ? ( isUniformFlow ? elseNode : elseNode.context( { nodeBlock: elseNode } ) ) : null;
  88. }
  89. generate( builder, output ) {
  90. const type = this.getNodeType( builder );
  91. const nodeData = builder.getDataFromNode( this );
  92. if ( nodeData.nodeProperty !== undefined ) {
  93. return nodeData.nodeProperty;
  94. }
  95. const { condNode, ifNode, elseNode } = builder.getNodeProperties( this );
  96. const functionNode = builder.currentFunctionNode;
  97. const needsOutput = output !== 'void';
  98. const nodeProperty = needsOutput ? property( type ).build( builder ) : '';
  99. nodeData.nodeProperty = nodeProperty;
  100. const nodeSnippet = condNode.build( builder, 'bool' );
  101. const isUniformFlow = builder.context.uniformFlow;
  102. if ( isUniformFlow && elseNode !== null ) {
  103. const ifSnippet = ifNode.build( builder, type );
  104. const elseSnippet = elseNode.build( builder, type );
  105. const mathSnippet = builder.getTernary( nodeSnippet, ifSnippet, elseSnippet );
  106. // TODO: If node property already exists return something else
  107. return builder.format( mathSnippet, type, output );
  108. }
  109. builder.addFlowCode( `\n${ builder.tab }if ( ${ nodeSnippet } ) {\n\n` ).addFlowTab();
  110. let ifSnippet = ifNode.build( builder, type );
  111. if ( ifSnippet ) {
  112. if ( needsOutput ) {
  113. ifSnippet = nodeProperty + ' = ' + ifSnippet + ';';
  114. } else {
  115. ifSnippet = 'return ' + ifSnippet + ';';
  116. if ( functionNode === null ) {
  117. warn( 'TSL: Return statement used in an inline \'Fn()\'. Define a layout struct to allow return values.', this.stackTrace );
  118. ifSnippet = '// ' + ifSnippet;
  119. }
  120. }
  121. }
  122. builder.removeFlowTab().addFlowCode( builder.tab + '\t' + ifSnippet + '\n\n' + builder.tab + '}' );
  123. if ( elseNode !== null ) {
  124. builder.addFlowCode( ' else {\n\n' ).addFlowTab();
  125. let elseSnippet = elseNode.build( builder, type );
  126. if ( elseSnippet ) {
  127. if ( needsOutput ) {
  128. elseSnippet = nodeProperty + ' = ' + elseSnippet + ';';
  129. } else {
  130. elseSnippet = 'return ' + elseSnippet + ';';
  131. if ( functionNode === null ) {
  132. warn( 'TSL: Return statement used in an inline \'Fn()\'. Define a layout struct to allow return values.', this.stackTrace );
  133. elseSnippet = '// ' + elseSnippet;
  134. }
  135. }
  136. }
  137. builder.removeFlowTab().addFlowCode( builder.tab + '\t' + elseSnippet + '\n\n' + builder.tab + '}\n\n' );
  138. } else {
  139. builder.addFlowCode( '\n\n' );
  140. }
  141. return builder.format( nodeProperty, type, output );
  142. }
  143. }
  144. export default ConditionalNode;
  145. /**
  146. * TSL function for creating a conditional node.
  147. *
  148. * @tsl
  149. * @function
  150. * @param {Node} condNode - The node that defines the condition.
  151. * @param {Node} ifNode - The node that is evaluate when the condition ends up `true`.
  152. * @param {?Node} [elseNode=null] - The node that is evaluate when the condition ends up `false`.
  153. * @returns {ConditionalNode}
  154. */
  155. export const select = /*@__PURE__*/ nodeProxy( ConditionalNode ).setParameterLength( 2, 3 );
  156. addMethodChaining( 'select', select );
粤ICP备19079148号