math.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. /**
  2. * @license Licensed under the Apache License, Version 2.0 (the "License"):
  3. * http://www.apache.org/licenses/LICENSE-2.0
  4. */
  5. /**
  6. * @fileoverview Generating Arduino code for the Math blocks.
  7. *
  8. * TODO: Math on list needs lists to be implemented.
  9. * math_constant and math_change needs to be tested in compiler.
  10. */
  11. 'use strict';
  12. goog.provide('Blockly.Python.math');
  13. goog.require('Blockly.Python');
  14. /**
  15. * Generator for a numeric value (X).
  16. * Arduino code: loop { X }
  17. * @param {!Blockly.Block} block Block to generate the code from.
  18. * @return {array} Completed code with order of operation.
  19. */
  20. Blockly.Python['math_number'] = function(block) {
  21. // Numeric value.
  22. var code = parseFloat(block.getFieldValue('NUM'));
  23. if (code == Infinity) {
  24. code = 'INFINITY';
  25. } else if (code == -Infinity) {
  26. code = '-INFINITY';
  27. }
  28. return [code, Blockly.Python.ORDER_ATOMIC];
  29. };
  30. /**
  31. * Generator for a basic arithmetic operators (X and Y) and power function
  32. * (X ^ Y).
  33. * Arduino code: loop { X operator Y }
  34. * @param {!Blockly.Block} block Block to generate the code from.
  35. * @return {array} Completed code with order of operation.
  36. */
  37. Blockly.Python['math_arithmetic'] = function(block) {
  38. var OPERATORS = {
  39. ADD: [' + ', Blockly.Python.ORDER_ADDITIVE],
  40. MINUS: [' - ', Blockly.Python.ORDER_ADDITIVE],
  41. MULTIPLY: [' * ', Blockly.Python.ORDER_MULTIPLICATIVE],
  42. DIVIDE: [' / ', Blockly.Python.ORDER_MULTIPLICATIVE],
  43. POWER: [null, Blockly.Python.ORDER_NONE] // Handle power separately.
  44. };
  45. var tuple = OPERATORS[block.getFieldValue('OP')];
  46. var operator = tuple[0];
  47. var order = tuple[1];
  48. var argument0 = Blockly.Python.valueToCode(block, 'A', order) || '0';
  49. var argument1 = Blockly.Python.valueToCode(block, 'B', order) || '0';
  50. var code;
  51. // Power in C++ requires a special case since it has no operator.
  52. if (!operator) {
  53. code = 'Math.pow(' + argument0 + ', ' + argument1 + ')';
  54. return [code, Blockly.Python.ORDER_UNARY_POSTFIX];
  55. }
  56. code = argument0 + operator + argument1;
  57. return [code, order];
  58. };
  59. /**
  60. * Generator for math operators that contain a single operand (X).
  61. * Arduino code: loop { operator(X) }
  62. * @param {!Blockly.Block} block Block to generate the code from.
  63. * @return {array} Completed code with order of operation.
  64. */
  65. Blockly.Python['math_single'] = function(block) {
  66. var operator = block.getFieldValue('OP');
  67. var code;
  68. var arg;
  69. if (operator == 'NEG') {
  70. // Negation is a special case given its different operator precedents.
  71. arg = Blockly.Python.valueToCode(block, 'NUM',
  72. Blockly.Python.ORDER_UNARY_PREFIX) || '0';
  73. if (arg[0] == '-') {
  74. // --3 is not legal in C++ in this context.
  75. arg = ' ' + arg;
  76. }
  77. code = '-' + arg;
  78. return [code, Blockly.Python.ORDER_UNARY_PREFIX];
  79. }
  80. if (operator == 'ABS' || operator.substring(0, 5) == 'ROUND') {
  81. arg = Blockly.Python.valueToCode(block, 'NUM',
  82. Blockly.Python.ORDER_UNARY_POSTFIX) || '0';
  83. } else if (operator == 'SIN' || operator == 'COS' || operator == 'TAN') {
  84. arg = Blockly.Python.valueToCode(block, 'NUM',
  85. Blockly.Python.ORDER_MULTIPLICATIVE) || '0';
  86. } else {
  87. arg = Blockly.Python.valueToCode(block, 'NUM',
  88. Blockly.Python.ORDER_NONE) || '0';
  89. }
  90. // First, handle cases which generate values that don't need parentheses.
  91. switch (operator) {
  92. case 'ABS':
  93. code = 'abs(' + arg + ')';
  94. break;
  95. case 'ROOT':
  96. code = 'sqrt(' + arg + ')';
  97. break;
  98. case 'LN':
  99. code = 'log(' + arg + ')';
  100. break;
  101. case 'EXP':
  102. code = 'exp(' + arg + ')';
  103. break;
  104. case 'POW10':
  105. code = 'pow(10,' + arg + ')';
  106. break;
  107. case 'ROUND':
  108. code = 'round(' + arg + ')';
  109. break;
  110. case 'ROUNDUP':
  111. code = 'ceil(' + arg + ')';
  112. break;
  113. case 'ROUNDDOWN':
  114. code = 'floor(' + arg + ')';
  115. break;
  116. case 'SIN':
  117. code = 'sin(' + arg + ' / 180 * Math.PI)';
  118. break;
  119. case 'COS':
  120. code = 'cos(' + arg + ' / 180 * Math.PI)';
  121. break;
  122. case 'TAN':
  123. code = 'tan(' + arg + ' / 180 * Math.PI)';
  124. break;
  125. }
  126. if (code) {
  127. return [code, Blockly.Python.ORDER_UNARY_POSTFIX];
  128. }
  129. // Second, handle cases which generate values that may need parentheses.
  130. switch (operator) {
  131. case 'LOG10':
  132. code = 'log(' + arg + ') / log(10)';
  133. break;
  134. case 'ASIN':
  135. code = 'asin(' + arg + ') / M_PI * 180';
  136. break;
  137. case 'ACOS':
  138. code = 'acos(' + arg + ') / M_PI * 180';
  139. break;
  140. case 'ATAN':
  141. code = 'atan(' + arg + ') / M_PI * 180';
  142. break;
  143. default:
  144. throw 'Unknown math operator: ' + operator;
  145. }
  146. return [code, Blockly.Python.ORDER_MULTIPLICATIVE];
  147. };
  148. /**
  149. * Generator for math constants (PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2),
  150. * INFINITY).
  151. * Arduino code: loop { constant }
  152. * TODO: Might need to include "#define _USE_MATH_DEFINES"
  153. * The arduino header file already includes math.h
  154. * @param {!Blockly.Block} block Block to generate the code from.
  155. * @return {string} Completed code.
  156. */
  157. Blockly.Python['math_constant'] = function(block) {
  158. var CONSTANTS = {
  159. 'PI': ['M_PI', Blockly.Python.ORDER_UNARY_POSTFIX],
  160. 'E': ['M_E', Blockly.Python.ORDER_UNARY_POSTFIX],
  161. 'GOLDEN_RATIO': ['(1 + sqrt(5)) / 2', Blockly.Python.ORDER_MULTIPLICATIVE],
  162. 'SQRT2': ['M_SQRT2', Blockly.Python.ORDER_UNARY_POSTFIX],
  163. 'SQRT1_2': ['M_SQRT1_2', Blockly.Python.ORDER_UNARY_POSTFIX],
  164. 'INFINITY': ['INFINITY', Blockly.Python.ORDER_ATOMIC]
  165. };
  166. return CONSTANTS[block.getFieldValue('CONSTANT')];
  167. };
  168. /**
  169. * Generator for math checks: if a number is even, odd, prime, whole, positive,
  170. * negative, or if it is divisible by certain number. Returns true or false.
  171. * Arduino code: complex code, can create external functions.
  172. * @param {!Blockly.Block} block Block to generate the code from.
  173. * @return {array} Completed code with order of operation.
  174. */
  175. Blockly.Python['math_number_property'] = function(block) {
  176. var number_to_check = Blockly.Python.valueToCode(block, 'NUMBER_TO_CHECK',
  177. Blockly.Python.ORDER_MULTIPLICATIVE) || '0';
  178. var dropdown_property = block.getFieldValue('PROPERTY');
  179. var code;
  180. if (dropdown_property == 'PRIME') {
  181. var func = [
  182. 'boolean ' + Blockly.Python.DEF_FUNC_NAME + '(int n) {',
  183. ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods',
  184. ' if (n == 2 || n == 3) {',
  185. ' return true;',
  186. ' }',
  187. ' // False if n is NaN, negative, is 1.',
  188. ' // And false if n is divisible by 2 or 3.',
  189. ' if (isnan(n) || (n <= 1) || (n == 1) || (n % 2 == 0) || ' +
  190. '(n % 3 == 0)) {',
  191. ' return false;',
  192. ' }',
  193. ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).',
  194. ' for (int x = 6; x <= sqrt(n) + 1; x += 6) {',
  195. ' if (n % (x - 1) == 0 || n % (x + 1) == 0) {',
  196. ' return false;',
  197. ' }',
  198. ' }',
  199. ' return true;',
  200. '}'];
  201. var funcName = Blockly.Python.addFunction('mathIsPrime', func.join('\n'));
  202. Blockly.Python.addInclude('math', '#include <math.h>');
  203. code = funcName + '(' + number_to_check + ')';
  204. return [code, Blockly.Python.ORDER_UNARY_POSTFIX];
  205. }
  206. switch (dropdown_property) {
  207. case 'EVEN':
  208. code = number_to_check + ' % 2 == 0';
  209. break;
  210. case 'ODD':
  211. code = number_to_check + ' % 2 == 1';
  212. break;
  213. case 'WHOLE':
  214. Blockly.Python.addInclude('math', '#include <math.h>');
  215. code = '(floor(' + number_to_check + ') == ' + number_to_check + ')';
  216. break;
  217. case 'POSITIVE':
  218. code = number_to_check + ' > 0';
  219. break;
  220. case 'NEGATIVE':
  221. code = number_to_check + ' < 0';
  222. break;
  223. case 'DIVISIBLE_BY':
  224. var divisor = Blockly.Python.valueToCode(block, 'DIVISOR',
  225. Blockly.Python.ORDER_MULTIPLICATIVE) || '0';
  226. code = number_to_check + ' % ' + divisor + ' == 0';
  227. break;
  228. }
  229. return [code, Blockly.Python.ORDER_EQUALITY];
  230. };
  231. /**
  232. * Generator to add (Y) to a variable (X).
  233. * If variable X has not been declared before this block it will be declared as
  234. * a (not initialised) global int, however globals are 0 initialised in C/C++.
  235. * Arduino code: loop { X += Y; }
  236. * @param {!Blockly.Block} block Block to generate the code from.
  237. * @return {array} Completed code with order of operation.
  238. */
  239. Blockly.Python['math_change'] = function(block) {
  240. var argument0 = Blockly.Python.valueToCode(block, 'DELTA',
  241. Blockly.Python.ORDER_ADDITIVE) || '0';
  242. var varName = Blockly.Python.variableDB_.getName(
  243. block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE);
  244. return varName + ' += ' + argument0 + ';\n';
  245. };
  246. /** Rounding functions have a single operand. */
  247. Blockly.Python['math_round'] = Blockly.Python['math_single'];
  248. /** Trigonometry functions have a single operand. */
  249. Blockly.Python['math_trig'] = Blockly.Python['math_single'];
  250. /**
  251. * Generator for the math function to a list.
  252. * Arduino code: ???
  253. * TODO: List have to be implemented first. Removed from toolbox for now.
  254. * @param {!Blockly.Block} block Block to generate the code from.
  255. * @return {array} Completed code with order of operation.
  256. */
  257. Blockly.Python['math_on_list'] = Blockly.Python.noGeneratorCodeInline;
  258. /**
  259. * Generator for the math modulo function (calculates remainder of X/Y).
  260. * Arduino code: loop { X % Y }
  261. * @param {!Blockly.Block} block Block to generate the code from.
  262. * @return {array} Completed code with order of operation.
  263. */
  264. Blockly.Python['math_modulo'] = function(block) {
  265. var argument0 = Blockly.Python.valueToCode(block, 'DIVIDEND',
  266. Blockly.Python.ORDER_MULTIPLICATIVE) || '0';
  267. var argument1 = Blockly.Python.valueToCode(block, 'DIVISOR',
  268. Blockly.Python.ORDER_MULTIPLICATIVE) || '0';
  269. var code = argument0 + ' % ' + argument1;
  270. return [code, Blockly.Python.ORDER_MULTIPLICATIVE];
  271. };
  272. /**
  273. * Generator for clipping a number(X) between two limits (Y and Z).
  274. * Arduino code: loop { (X < Y ? Y : ( X > Z ? Z : X)) }
  275. * @param {!Blockly.Block} block Block to generate the code from.
  276. * @return {array} Completed code with order of operation.
  277. */
  278. Blockly.Python['math_constrain'] = function(block) {
  279. // Constrain a number between two limits.
  280. var argument0 = Blockly.Python.valueToCode(block, 'VALUE',
  281. Blockly.Python.ORDER_NONE) || '0';
  282. var argument1 = Blockly.Python.valueToCode(block, 'LOW',
  283. Blockly.Python.ORDER_NONE) || '0';
  284. var argument2 = Blockly.Python.valueToCode(block, 'HIGH',
  285. Blockly.Python.ORDER_NONE) || '0';
  286. var code = '(' + argument0 + ' < ' + argument1 + ' ? ' + argument1 +
  287. ' : ( ' + argument0 + ' > ' + argument2 + ' ? ' + argument2 + ' : ' +
  288. argument0 + '))';
  289. return [code, Blockly.Python.ORDER_UNARY_POSTFIX];
  290. };
  291. /**
  292. * Generator for a random integer between two numbers (X and Y).
  293. * Arduino code: loop { math_random_int(X, Y); }
  294. * and an aditional math_random_int function
  295. * @param {!Blockly.Block} block Block to generate the code from.
  296. * @return {array} Completed code with order of operation.
  297. */
  298. Blockly.Python['math_random_int'] = function(block) {
  299. var argument0 = Blockly.Python.valueToCode(block, 'FROM',
  300. Blockly.Python.ORDER_NONE) || '0';
  301. var argument1 = Blockly.Python.valueToCode(block, 'TO',
  302. Blockly.Python.ORDER_NONE) || '0';
  303. var functionName = Blockly.Python.variableDB_.getDistinctName(
  304. 'math_random_int', Blockly.Generator.NAME_TYPE);
  305. Blockly.Python.math_random_int.random_function = functionName;
  306. var func = [
  307. 'int ' + Blockly.Python.DEF_FUNC_NAME + '(int min, int max) {',
  308. ' if (min > max) {',
  309. ' // Swap min and max to ensure min is smaller.',
  310. ' int temp = min;',
  311. ' min = max;',
  312. ' max = temp;',
  313. ' }',
  314. ' return min + (rand() % (max - min + 1));',
  315. '}'];
  316. var funcName = Blockly.Python.addFunction('mathRandomInt', func.join('\n'));
  317. var code = funcName + '(' + argument0 + ', ' + argument1 + ')';
  318. return [code, Blockly.Python.ORDER_UNARY_POSTFIX];
  319. };
  320. /**
  321. * Generator for a random float from 0 to 1.
  322. * Arduino code: loop { (rand() / RAND_MAX) }
  323. * @param {!Blockly.Block} block Block to generate the code from.
  324. * @return {string} Completed code.
  325. */
  326. Blockly.Python['math_random_float'] = function(block) {
  327. return ['(rand() / RAND_MAX)', Blockly.Python.ORDER_UNARY_POSTFIX];
  328. };