math.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. /**
  2. * @license
  3. * Visual Blocks Editor
  4. *
  5. * Copyright 2012 Google Inc.
  6. * https://developers.google.com/blockly/
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. /**
  21. * @fileoverview Math blocks for Blockly.
  22. * @author q.neutron@gmail.com (Quynh Neutron)
  23. */
  24. 'use strict';
  25. goog.provide('Blockly.Blocks.math');
  26. goog.require('Blockly.Blocks');
  27. Blockly.Blocks['math_number'] = {
  28. /**
  29. * Block for numeric value.
  30. * @this Blockly.Block
  31. */
  32. init: function() {
  33. this.setHelpUrl(Blockly.Msg.MATH_NUMBER_HELPURL);
  34. this.setColour(Blockscad.Toolbox.HEX_MATH);
  35. this.appendDummyInput()
  36. .appendField(new Blockly.FieldNumber('0'), 'NUM');
  37. this.setOutput(true, 'Number');
  38. // Assign 'this' to a variable for use in the tooltip closure below.
  39. var thisBlock = this;
  40. // Number block is trivial. Use tooltip of parent block if it exists.
  41. this.setTooltip(function() {
  42. var parent = thisBlock.getParent();
  43. return (parent && parent.getInputsInline() && parent.tooltip) ||
  44. Blockly.Msg.MATH_NUMBER_TOOLTIP;
  45. });
  46. }
  47. };
  48. Blockly.Blocks['math_arithmetic'] = {
  49. /**
  50. * Block for basic arithmetic operator.
  51. * @this Blockly.Block
  52. */
  53. init: function() {
  54. var OPERATORS =
  55. [[Blockly.Msg.MATH_ADDITION_SYMBOL, 'ADD'],
  56. [Blockly.Msg.MATH_SUBTRACTION_SYMBOL, 'MINUS'],
  57. [Blockly.Msg.MATH_MULTIPLICATION_SYMBOL, 'MULTIPLY'],
  58. [Blockly.Msg.MATH_DIVISION_SYMBOL, 'DIVIDE'],
  59. [Blockly.Msg.MATH_POWER_SYMBOL, 'POWER']];
  60. this.setHelpUrl(Blockly.Msg.MATH_ARITHMETIC_HELPURL);
  61. this.setColour(Blockscad.Toolbox.HEX_MATH);
  62. this.setOutput(true, 'Number');
  63. this.appendValueInput('A')
  64. .setCheck('Number');
  65. this.appendValueInput('B')
  66. .setCheck('Number')
  67. .appendField(new Blockly.FieldDropdown(OPERATORS), 'OP');
  68. this.setInputsInline(true);
  69. // Assign 'this' to a variable for use in the tooltip closure below.
  70. var thisBlock = this;
  71. this.setTooltip(function() {
  72. var mode = thisBlock.getFieldValue('OP');
  73. var TOOLTIPS = {
  74. 'ADD': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_ADD,
  75. 'MINUS': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MINUS,
  76. 'MULTIPLY': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MULTIPLY,
  77. 'DIVIDE': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_DIVIDE,
  78. 'POWER': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_POWER
  79. };
  80. return TOOLTIPS[mode];
  81. });
  82. }
  83. };
  84. Blockly.Blocks['math_single'] = {
  85. /**
  86. * Block for advanced math operators with single operand.
  87. * @this Blockly.Block
  88. */
  89. init: function() {
  90. var OPERATORS =
  91. [[Blockly.Msg.MATH_SINGLE_OP_ROOT, 'ROOT'],
  92. [Blockly.Msg.MATH_SINGLE_OP_ABSOLUTE, 'ABS'],
  93. ['-', 'NEG'],
  94. ['ln', 'LN'],
  95. ['log10', 'LOG10'],
  96. ['e^', 'EXP'],
  97. ['10^', 'POW10']];
  98. this.setHelpUrl(Blockly.Msg.MATH_SINGLE_HELPURL);
  99. this.setColour(Blockscad.Toolbox.HEX_MATH);
  100. this.setOutput(true, 'Number');
  101. this.appendValueInput('NUM')
  102. .setCheck('Number')
  103. .appendField(new Blockly.FieldDropdown(OPERATORS), 'OP');
  104. // Assign 'this' to a variable for use in the tooltip closure below.
  105. var thisBlock = this;
  106. this.setTooltip(function() {
  107. var mode = thisBlock.getFieldValue('OP');
  108. var TOOLTIPS = {
  109. 'ROOT': Blockly.Msg.MATH_SINGLE_TOOLTIP_ROOT,
  110. 'ABS': Blockly.Msg.MATH_SINGLE_TOOLTIP_ABS,
  111. 'NEG': Blockly.Msg.MATH_SINGLE_TOOLTIP_NEG,
  112. 'LN': Blockly.Msg.MATH_SINGLE_TOOLTIP_LN,
  113. 'LOG10': Blockly.Msg.MATH_SINGLE_TOOLTIP_LOG10,
  114. 'EXP': Blockly.Msg.MATH_SINGLE_TOOLTIP_EXP,
  115. 'POW10': Blockly.Msg.MATH_SINGLE_TOOLTIP_POW10
  116. };
  117. return TOOLTIPS[mode];
  118. });
  119. }
  120. };
  121. Blockly.Blocks['math_trig'] = {
  122. /**
  123. * Block for trigonometry operators.
  124. * @this Blockly.Block
  125. */
  126. init: function() {
  127. var OPERATORS =
  128. [[Blockly.Msg.MATH_TRIG_SIN, 'SIN'],
  129. [Blockly.Msg.MATH_TRIG_COS, 'COS'],
  130. [Blockly.Msg.MATH_TRIG_TAN, 'TAN'],
  131. [Blockly.Msg.MATH_TRIG_ASIN, 'ASIN'],
  132. [Blockly.Msg.MATH_TRIG_ACOS, 'ACOS'],
  133. [Blockly.Msg.MATH_TRIG_ATAN, 'ATAN']];
  134. this.setHelpUrl(Blockly.Msg.MATH_TRIG_HELPURL);
  135. this.setColour(Blockscad.Toolbox.HEX_MATH);
  136. this.setOutput(true, 'Number');
  137. this.appendValueInput('NUM')
  138. .setCheck('Number')
  139. .appendField(new Blockly.FieldDropdown(OPERATORS), 'OP');
  140. // Assign 'this' to a variable for use in the tooltip closure below.
  141. var thisBlock = this;
  142. this.setTooltip(function() {
  143. var mode = thisBlock.getFieldValue('OP');
  144. var TOOLTIPS = {
  145. 'SIN': Blockly.Msg.MATH_TRIG_TOOLTIP_SIN,
  146. 'COS': Blockly.Msg.MATH_TRIG_TOOLTIP_COS,
  147. 'TAN': Blockly.Msg.MATH_TRIG_TOOLTIP_TAN,
  148. 'ASIN': Blockly.Msg.MATH_TRIG_TOOLTIP_ASIN,
  149. 'ACOS': Blockly.Msg.MATH_TRIG_TOOLTIP_ACOS,
  150. 'ATAN': Blockly.Msg.MATH_TRIG_TOOLTIP_ATAN
  151. };
  152. return TOOLTIPS[mode];
  153. });
  154. }
  155. };
  156. Blockly.Blocks['math_constant'] = {
  157. /**
  158. * Block for constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY.
  159. * @this Blockly.Block
  160. */
  161. init: function() {
  162. var CONSTANTS =
  163. [['\u03c0', 'PI'],
  164. ['e', 'E'],
  165. ['\u03c6', 'GOLDEN_RATIO'],
  166. ['sqrt(2)', 'SQRT2'],
  167. ['sqrt(\u00bd)', 'SQRT1_2'],
  168. ['\u221e', 'INFINITY']];
  169. this.setHelpUrl(Blockly.Msg.MATH_CONSTANT_HELPURL);
  170. this.setColour(Blockscad.Toolbox.HEX_MATH);
  171. this.setOutput(true, 'Number');
  172. this.appendDummyInput()
  173. .appendField(new Blockly.FieldDropdown(CONSTANTS), 'CONSTANT');
  174. this.setTooltip(Blockly.Msg.MATH_CONSTANT_TOOLTIP);
  175. }
  176. };
  177. Blockly.Blocks['math_number_property'] = {
  178. /**
  179. * Block for checking if a number is even, odd, prime, whole, positive,
  180. * negative or if it is divisible by certain number.
  181. * @this Blockly.Block
  182. */
  183. init: function() {
  184. var PROPERTIES =
  185. [[Blockly.Msg.MATH_IS_EVEN, 'EVEN'],
  186. [Blockly.Msg.MATH_IS_ODD, 'ODD'],
  187. //[Blockly.Msg.MATH_IS_PRIME, 'PRIME'],
  188. [Blockly.Msg.MATH_IS_WHOLE, 'WHOLE'],
  189. [Blockly.Msg.MATH_IS_POSITIVE, 'POSITIVE'],
  190. [Blockly.Msg.MATH_IS_NEGATIVE, 'NEGATIVE'],
  191. [Blockly.Msg.MATH_IS_DIVISIBLE_BY, 'DIVISIBLE_BY']];
  192. this.setColour(Blockscad.Toolbox.HEX_MATH);
  193. this.appendValueInput('NUMBER_TO_CHECK')
  194. .setCheck('Number');
  195. var dropdown = new Blockly.FieldDropdown(PROPERTIES, function(option) {
  196. var divisorInput = (option == 'DIVISIBLE_BY');
  197. this.sourceBlock_.updateShape_(divisorInput);
  198. });
  199. this.appendDummyInput()
  200. .appendField(dropdown, 'PROPERTY');
  201. this.setInputsInline(true);
  202. this.setOutput(true, 'Boolean');
  203. this.setTooltip(Blockly.Msg.MATH_IS_TOOLTIP);
  204. },
  205. /**
  206. * Create XML to represent whether the 'divisorInput' should be present.
  207. * @return {Element} XML storage element.
  208. * @this Blockly.Block
  209. */
  210. mutationToDom: function() {
  211. var container = document.createElement('mutation');
  212. var divisorInput = (this.getFieldValue('PROPERTY') == 'DIVISIBLE_BY');
  213. container.setAttribute('divisor_input', divisorInput);
  214. return container;
  215. },
  216. /**
  217. * Parse XML to restore the 'divisorInput'.
  218. * @param {!Element} xmlElement XML storage element.
  219. * @this Blockly.Block
  220. */
  221. domToMutation: function(xmlElement) {
  222. var divisorInput = (xmlElement.getAttribute('divisor_input') == 'true');
  223. this.updateShape_(divisorInput);
  224. },
  225. /**
  226. * Modify this block to have (or not have) an input for 'is divisible by'.
  227. * @param {boolean} divisorInput True if this block has a divisor input.
  228. * @private
  229. * @this Blockly.Block
  230. */
  231. updateShape_: function(divisorInput) {
  232. // Add or remove a Value Input.
  233. var inputExists = this.getInput('DIVISOR');
  234. if (divisorInput) {
  235. if (!inputExists) {
  236. this.appendValueInput('DIVISOR')
  237. .setCheck('Number');
  238. }
  239. } else if (inputExists) {
  240. this.removeInput('DIVISOR');
  241. }
  242. }
  243. };
  244. Blockly.Blocks['math_change'] = {
  245. /**
  246. * Block for adding to a variable in place.
  247. * @this Blockly.Block
  248. */
  249. init: function() {
  250. this.setHelpUrl(Blockly.Msg.MATH_CHANGE_HELPURL);
  251. this.setColour(Blockscad.Toolbox.HEX_MATH);
  252. this.interpolateMsg(
  253. // TODO: Combine these messages instead of using concatenation.
  254. Blockly.Msg.MATH_CHANGE_TITLE_CHANGE + ' %1 ' +
  255. Blockly.Msg.MATH_CHANGE_INPUT_BY + ' %2',
  256. ['VAR', new Blockly.FieldVariable(Blockly.Msg.MATH_CHANGE_TITLE_ITEM)],
  257. ['DELTA', 'Number', Blockly.ALIGN_RIGHT],
  258. Blockly.ALIGN_RIGHT);
  259. this.setPreviousStatement(true);
  260. this.setNextStatement(true);
  261. // Assign 'this' to a variable for use in the tooltip closure below.
  262. var thisBlock = this;
  263. this.setTooltip(function() {
  264. return Blockly.Msg.MATH_CHANGE_TOOLTIP.replace('%1',
  265. thisBlock.getFieldValue('VAR'));
  266. });
  267. },
  268. /**
  269. * Return all variables referenced by this block.
  270. * @return {!Array.<string>} List of variable names.
  271. * @this Blockly.Block
  272. */
  273. getVars: function() {
  274. return [this.getFieldValue('VAR')];
  275. },
  276. /**
  277. * Notification that a variable is renaming.
  278. * If the name matches one of this block's variables, rename it.
  279. * @param {string} oldName Previous name of variable.
  280. * @param {string} newName Renamed variable.
  281. * @this Blockly.Block
  282. */
  283. renameVar: function(oldName, newName) {
  284. if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) {
  285. this.setFieldValue(newName, 'VAR');
  286. }
  287. }
  288. };
  289. Blockly.Blocks['math_round'] = {
  290. /**
  291. * Block for rounding functions.
  292. * @this Blockly.Block
  293. */
  294. init: function() {
  295. var OPERATORS =
  296. [[Blockly.Msg.MATH_ROUND_OPERATOR_ROUND, 'ROUND'],
  297. [Blockly.Msg.MATH_ROUND_OPERATOR_ROUNDUP, 'ROUNDUP'],
  298. [Blockly.Msg.MATH_ROUND_OPERATOR_ROUNDDOWN, 'ROUNDDOWN']];
  299. this.setHelpUrl(Blockly.Msg.MATH_ROUND_HELPURL);
  300. this.setColour(Blockscad.Toolbox.HEX_MATH);
  301. this.setOutput(true, 'Number');
  302. this.appendValueInput('NUM')
  303. .setCheck('Number')
  304. .appendField(new Blockly.FieldDropdown(OPERATORS), 'OP');
  305. this.setTooltip(Blockly.Msg.MATH_ROUND_TOOLTIP);
  306. }
  307. };
  308. Blockly.Blocks['math_on_list'] = {
  309. /**
  310. * Block for evaluating a list of numbers to return sum, average, min, max,
  311. * etc. Some functions also work on text (min, max, mode, median).
  312. * @this Blockly.Block
  313. */
  314. init: function() {
  315. var OPERATORS =
  316. [[Blockly.Msg.MATH_ONLIST_OPERATOR_SUM, 'SUM'],
  317. [Blockly.Msg.MATH_ONLIST_OPERATOR_MIN, 'MIN'],
  318. [Blockly.Msg.MATH_ONLIST_OPERATOR_MAX, 'MAX'],
  319. [Blockly.Msg.MATH_ONLIST_OPERATOR_AVERAGE, 'AVERAGE'],
  320. [Blockly.Msg.MATH_ONLIST_OPERATOR_MEDIAN, 'MEDIAN'],
  321. [Blockly.Msg.MATH_ONLIST_OPERATOR_MODE, 'MODE'],
  322. [Blockly.Msg.MATH_ONLIST_OPERATOR_STD_DEV, 'STD_DEV'],
  323. [Blockly.Msg.MATH_ONLIST_OPERATOR_RANDOM, 'RANDOM']];
  324. // Assign 'this' to a variable for use in the closures below.
  325. var thisBlock = this;
  326. this.setHelpUrl(Blockly.Msg.MATH_ONLIST_HELPURL);
  327. this.setColour(Blockscad.Toolbox.HEX_MATH);
  328. this.setOutput(true, 'Number');
  329. var dropdown = new Blockly.FieldDropdown(OPERATORS, function(newOp) {
  330. thisBlock.updateType_(newOp);
  331. });
  332. this.appendValueInput('LIST')
  333. .setCheck('Array')
  334. .appendField(dropdown, 'OP');
  335. this.setTooltip(function() {
  336. var mode = thisBlock.getFieldValue('OP');
  337. var TOOLTIPS = {
  338. 'SUM': Blockly.Msg.MATH_ONLIST_TOOLTIP_SUM,
  339. 'MIN': Blockly.Msg.MATH_ONLIST_TOOLTIP_MIN,
  340. 'MAX': Blockly.Msg.MATH_ONLIST_TOOLTIP_MAX,
  341. 'AVERAGE': Blockly.Msg.MATH_ONLIST_TOOLTIP_AVERAGE,
  342. 'MEDIAN': Blockly.Msg.MATH_ONLIST_TOOLTIP_MEDIAN,
  343. 'MODE': Blockly.Msg.MATH_ONLIST_TOOLTIP_MODE,
  344. 'STD_DEV': Blockly.Msg.MATH_ONLIST_TOOLTIP_STD_DEV,
  345. 'RANDOM': Blockly.Msg.MATH_ONLIST_TOOLTIP_RANDOM
  346. };
  347. return TOOLTIPS[mode];
  348. });
  349. },
  350. /**
  351. * Modify this block to have the correct output type.
  352. * @param {string} newOp Either 'MODE' or some op than returns a number.
  353. * @private
  354. * @this Blockly.Block
  355. */
  356. updateType_: function(newOp) {
  357. if (newOp == 'MODE') {
  358. this.outputConnection.setCheck('Array');
  359. } else {
  360. this.outputConnection.setCheck('Number');
  361. }
  362. },
  363. /**
  364. * Create XML to represent the output type.
  365. * @return {Element} XML storage element.
  366. * @this Blockly.Block
  367. */
  368. mutationToDom: function() {
  369. var container = document.createElement('mutation');
  370. container.setAttribute('op', this.getFieldValue('OP'));
  371. return container;
  372. },
  373. /**
  374. * Parse XML to restore the output type.
  375. * @param {!Element} xmlElement XML storage element.
  376. * @this Blockly.Block
  377. */
  378. domToMutation: function(xmlElement) {
  379. this.updateType_(xmlElement.getAttribute('op'));
  380. }
  381. };
  382. Blockly.Blocks['math_modulo'] = {
  383. /**
  384. * Block for remainder of a division.
  385. * @this Blockly.Block
  386. */
  387. init: function() {
  388. this.setHelpUrl(Blockly.Msg.MATH_MODULO_HELPURL);
  389. this.setColour(Blockscad.Toolbox.HEX_MATH);
  390. this.setOutput(true, 'Number');
  391. this.interpolateMsg(Blockly.Msg.MATH_MODULO_TITLE,
  392. ['DIVIDEND', 'Number', Blockly.ALIGN_RIGHT],
  393. ['DIVISOR', 'Number', Blockly.ALIGN_RIGHT],
  394. Blockly.ALIGN_RIGHT);
  395. this.setInputsInline(true);
  396. this.setTooltip(Blockly.Msg.MATH_MODULO_TOOLTIP);
  397. }
  398. };
  399. Blockly.Blocks['math_constrain'] = {
  400. /**
  401. * Block for constraining a number between two limits.
  402. * @this Blockly.Block
  403. */
  404. init: function() {
  405. this.setHelpUrl(Blockly.Msg.MATH_CONSTRAIN_HELPURL);
  406. this.setColour(Blockscad.Toolbox.HEX_MATH);
  407. this.setOutput(true, 'Number');
  408. this.interpolateMsg(Blockly.Msg.MATH_CONSTRAIN_TITLE,
  409. ['VALUE', 'Number', Blockly.ALIGN_RIGHT],
  410. ['LOW', 'Number', Blockly.ALIGN_RIGHT],
  411. ['HIGH', 'Number', Blockly.ALIGN_RIGHT],
  412. Blockly.ALIGN_RIGHT);
  413. this.setInputsInline(true);
  414. this.setTooltip(Blockly.Msg.MATH_CONSTRAIN_TOOLTIP);
  415. }
  416. };
  417. Blockly.Blocks['math_random_int'] = {
  418. /**
  419. * Block for random integer between [X] and [Y].
  420. * @this Blockly.Block
  421. */
  422. init: function() {
  423. this.setHelpUrl(Blockly.Msg.MATH_RANDOM_INT_HELPURL);
  424. this.setColour(Blockscad.Toolbox.HEX_MATH);
  425. this.setOutput(true, 'Number');
  426. this.interpolateMsg(Blockly.Msg.MATH_RANDOM_INT_TITLE,
  427. ['FROM', 'Number', Blockly.ALIGN_RIGHT],
  428. ['TO', 'Number', Blockly.ALIGN_RIGHT],
  429. Blockly.ALIGN_RIGHT);
  430. this.setInputsInline(true);
  431. this.setTooltip(Blockly.Msg.MATH_RANDOM_INT_TOOLTIP);
  432. }
  433. };
  434. Blockly.Blocks['math_random_float'] = {
  435. /**
  436. * Block for random fraction between 0 and 1.
  437. * @this Blockly.Block
  438. */
  439. init: function() {
  440. this.setHelpUrl(Blockly.Msg.MATH_RANDOM_FLOAT_HELPURL);
  441. this.setColour(Blockscad.Toolbox.HEX_MATH);
  442. this.setOutput(true, 'Number');
  443. this.appendDummyInput()
  444. .appendField(Blockly.Msg.MATH_RANDOM_FLOAT_TITLE_RANDOM);
  445. this.setTooltip(Blockly.Msg.MATH_RANDOM_FLOAT_TOOLTIP);
  446. }
  447. };