math.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  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. /**
  28. * Common HSV hue for all blocks in this category.
  29. */
  30. // var DATA_HUE = 100, LOGIC_HUE = 230, VARIABLE_HUE = 240;
  31. var DATA_HUE = 100, LOGIC_HUE = 230, VARIABLE_HUE = "#f3af4c";
  32. Blockly.Blocks.math.HUE = "#5472ea";
  33. Blockly.Blocks['math_number'] = {
  34. /**
  35. * Block for numeric value.
  36. * @this Blockly.Block
  37. */
  38. init: function() {
  39. this.setHelpUrl(Blockly.Msg.MATH_NUMBER_HELPURL);
  40. this.setColour(Blockly.Blocks.math.HUE);
  41. this.appendDummyInput()
  42. .appendField(new Blockly.FieldNumber('0'), 'NUM');
  43. this.setOutput(true, 'Number');
  44. // Assign 'this' to a variable for use in the tooltip closure below.
  45. var thisBlock = this;
  46. // Number block is trivial. Use tooltip of parent block if it exists.
  47. this.setTooltip(function() {
  48. var parent = thisBlock.getParent();
  49. return (parent && parent.getInputsInline() && parent.tooltip) ||
  50. Blockly.Msg.MATH_NUMBER_TOOLTIP;
  51. });
  52. }
  53. };
  54. Blockly.Blocks['math_arithmetic'] = {
  55. /**
  56. * Block for basic arithmetic operator.
  57. * @this Blockly.Block
  58. */
  59. init: function() {
  60. this.jsonInit({
  61. "message0": "%1 %2 %3",
  62. "args0": [
  63. {
  64. "type": "input_value",
  65. "name": "A",
  66. "check": ["Number", "String"]
  67. },
  68. {
  69. "type": "field_dropdown",
  70. "name": "OP",
  71. "options":
  72. [[Blockly.Msg.MATH_ADDITION_SYMBOL, 'ADD'],
  73. [Blockly.Msg.MATH_SUBTRACTION_SYMBOL, 'MINUS'],
  74. [Blockly.Msg.MATH_MULTIPLICATION_SYMBOL, 'MULTIPLY'],
  75. [Blockly.Msg.MATH_DIVISION_SYMBOL, 'DIVIDE'],
  76. [Blockly.Msg.MATH_POWER_SYMBOL, 'POWER'],
  77. ['%', 'MODULO']]
  78. },
  79. {
  80. "type": "input_value",
  81. "name": "B",
  82. "check": ["Number", "String"]
  83. }
  84. ],
  85. "inputsInline": true,
  86. "output": "Number",
  87. "colour": Blockly.Blocks.math.HUE,
  88. "helpUrl": Blockly.Msg.MATH_ARITHMETIC_HELPURL
  89. });
  90. // Assign 'this' to a variable for use in the tooltip closure below.
  91. var thisBlock = this;
  92. this.setTooltip(function() {
  93. var mode = thisBlock.getFieldValue('OP');
  94. var TOOLTIPS = {
  95. 'ADD': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_ADD,
  96. 'MINUS': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MINUS,
  97. 'MULTIPLY': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MULTIPLY,
  98. 'DIVIDE': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_DIVIDE,
  99. 'POWER': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_POWER,
  100. 'MODULO': 'Computes the remainder of division.'
  101. };
  102. return TOOLTIPS[mode];
  103. });
  104. }
  105. };
  106. Blockly.Blocks['math_single'] = {
  107. /**
  108. * Block for advanced math operators with single operand.
  109. * @this Blockly.Block
  110. */
  111. init: function() {
  112. this.jsonInit({
  113. "message0": "%1 %2",
  114. "args0": [
  115. {
  116. "type": "field_dropdown",
  117. "name": "OP",
  118. "options": [
  119. [Blockly.Msg.MATH_SINGLE_OP_ROOT, 'ROOT'],
  120. [Blockly.Msg.MATH_SINGLE_OP_ABSOLUTE, 'ABS'],
  121. ['degrees', 'DEGRAD_DEG'],
  122. ['radians', 'DEGRAD_RAD'],
  123. ['-', 'NEG'],
  124. ['ln', 'LN'],
  125. ['log10', 'LOG10'],
  126. ['e^', 'EXP'],
  127. ['10^', 'POW10']
  128. ]
  129. },
  130. {
  131. "type": "input_value",
  132. "name": "NUM",
  133. "check": "Number"
  134. }
  135. ],
  136. "output": "Number",
  137. "colour": Blockly.Blocks.math.HUE,
  138. "helpUrl": Blockly.Msg.MATH_SINGLE_HELPURL
  139. });
  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. 'ROOT': Blockly.Msg.MATH_SINGLE_TOOLTIP_ROOT,
  146. 'ABS': Blockly.Msg.MATH_SINGLE_TOOLTIP_ABS,
  147. 'NEG': Blockly.Msg.MATH_SINGLE_TOOLTIP_NEG,
  148. 'LN': Blockly.Msg.MATH_SINGLE_TOOLTIP_LN,
  149. 'LOG10': Blockly.Msg.MATH_SINGLE_TOOLTIP_LOG10,
  150. 'EXP': Blockly.Msg.MATH_SINGLE_TOOLTIP_EXP,
  151. 'POW10': Blockly.Msg.MATH_SINGLE_TOOLTIP_POW10
  152. };
  153. return TOOLTIPS[mode];
  154. });
  155. }
  156. };
  157. Blockly.Blocks['math_trig'] = {
  158. /**
  159. * Block for trigonometry operators.
  160. * @this Blockly.Block
  161. */
  162. init: function() {
  163. this.jsonInit({
  164. "message0": "%1 %2",
  165. "args0": [
  166. {
  167. "type": "field_dropdown",
  168. "name": "OP",
  169. "options": [
  170. [Blockly.Msg.MATH_TRIG_SIN, 'SIN'],
  171. [Blockly.Msg.MATH_TRIG_COS, 'COS'],
  172. [Blockly.Msg.MATH_TRIG_TAN, 'TAN'],
  173. [Blockly.Msg.MATH_TRIG_ASIN, 'ASIN'],
  174. [Blockly.Msg.MATH_TRIG_ACOS, 'ACOS'],
  175. [Blockly.Msg.MATH_TRIG_ATAN, 'ATAN']
  176. ]
  177. },
  178. {
  179. "type": "input_value",
  180. "name": "NUM",
  181. "check": "Number"
  182. }
  183. ],
  184. "output": "Number",
  185. "colour": Blockly.Blocks.math.HUE,
  186. "helpUrl": Blockly.Msg.MATH_TRIG_HELPURL
  187. });
  188. // Assign 'this' to a variable for use in the tooltip closure below.
  189. var thisBlock = this;
  190. this.setTooltip(function() {
  191. var mode = thisBlock.getFieldValue('OP');
  192. var TOOLTIPS = {
  193. 'SIN': Blockly.Msg.MATH_TRIG_TOOLTIP_SIN,
  194. 'COS': Blockly.Msg.MATH_TRIG_TOOLTIP_COS,
  195. 'TAN': Blockly.Msg.MATH_TRIG_TOOLTIP_TAN,
  196. 'ASIN': Blockly.Msg.MATH_TRIG_TOOLTIP_ASIN,
  197. 'ACOS': Blockly.Msg.MATH_TRIG_TOOLTIP_ACOS,
  198. 'ATAN': Blockly.Msg.MATH_TRIG_TOOLTIP_ATAN
  199. };
  200. return TOOLTIPS[mode];
  201. });
  202. }
  203. };
  204. Blockly.Blocks['math_constant'] = {
  205. /**
  206. * Block for constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY.
  207. * @this Blockly.Block
  208. */
  209. init: function() {
  210. this.jsonInit({
  211. "message0": "%1",
  212. "args0": [
  213. {
  214. "type": "field_dropdown",
  215. "name": "CONSTANT",
  216. "options": [
  217. ['\u03c0', 'PI'],
  218. ['e', 'E'],
  219. ['\u03c6', 'GOLDEN_RATIO'],
  220. ['sqrt(2)', 'SQRT2'],
  221. ['sqrt(\u00bd)', 'SQRT1_2'],
  222. ['\u221e', 'INFINITY']
  223. ]
  224. }
  225. ],
  226. "output": "Number",
  227. "colour": Blockly.Blocks.math.HUE,
  228. "tooltip": Blockly.Msg.MATH_CONSTANT_TOOLTIP,
  229. "helpUrl": Blockly.Msg.MATH_CONSTANT_HELPURL
  230. });
  231. }
  232. };
  233. Blockly.Blocks['math_number_property'] = {
  234. /**
  235. * Block for checking if a number is even, odd, prime, whole, positive,
  236. * negative or if it is divisible by certain number.
  237. * @this Blockly.Block
  238. */
  239. init: function() {
  240. var PROPERTIES =
  241. [[Blockly.Msg.MATH_IS_EVEN, 'EVEN'],
  242. [Blockly.Msg.MATH_IS_ODD, 'ODD'],
  243. [Blockly.Msg.MATH_IS_PRIME, 'PRIME'],
  244. [Blockly.Msg.MATH_IS_WHOLE, 'WHOLE'],
  245. [Blockly.Msg.MATH_IS_POSITIVE, 'POSITIVE'],
  246. [Blockly.Msg.MATH_IS_NEGATIVE, 'NEGATIVE'],
  247. [Blockly.Msg.MATH_IS_DIVISIBLE_BY, 'DIVISIBLE_BY']];
  248. this.setColour(Blockly.Blocks.math.HUE);
  249. this.appendValueInput('NUMBER_TO_CHECK')
  250. .setCheck('Number');
  251. var dropdown = new Blockly.FieldDropdown(PROPERTIES, function(option) {
  252. var divisorInput = (option == 'DIVISIBLE_BY');
  253. this.sourceBlock_.updateShape_(divisorInput);
  254. });
  255. this.appendDummyInput()
  256. .appendField(dropdown, 'PROPERTY');
  257. this.setInputsInline(true);
  258. this.setOutput(true, 'Boolean');
  259. this.setTooltip(Blockly.Msg.MATH_IS_TOOLTIP);
  260. },
  261. /**
  262. * Create XML to represent whether the 'divisorInput' should be present.
  263. * @return {Element} XML storage element.
  264. * @this Blockly.Block
  265. */
  266. mutationToDom: function() {
  267. var container = document.createElement('mutation');
  268. var divisorInput = (this.getFieldValue('PROPERTY') == 'DIVISIBLE_BY');
  269. container.setAttribute('divisor_input', divisorInput);
  270. return container;
  271. },
  272. /**
  273. * Parse XML to restore the 'divisorInput'.
  274. * @param {!Element} xmlElement XML storage element.
  275. * @this Blockly.Block
  276. */
  277. domToMutation: function(xmlElement) {
  278. var divisorInput = (xmlElement.getAttribute('divisor_input') == 'true');
  279. this.updateShape_(divisorInput);
  280. },
  281. /**
  282. * Modify this block to have (or not have) an input for 'is divisible by'.
  283. * @param {boolean} divisorInput True if this block has a divisor input.
  284. * @private
  285. * @this Blockly.Block
  286. */
  287. updateShape_: function(divisorInput) {
  288. // Add or remove a Value Input.
  289. var inputExists = this.getInput('DIVISOR');
  290. if (divisorInput) {
  291. if (!inputExists) {
  292. this.appendValueInput('DIVISOR')
  293. .setCheck('Number');
  294. }
  295. } else if (inputExists) {
  296. this.removeInput('DIVISOR');
  297. }
  298. }
  299. };
  300. Blockly.Blocks['math_change'] = {
  301. /**
  302. * Block for adding to a variable in place.
  303. * @this Blockly.Block
  304. */
  305. init: function() {
  306. this.jsonInit({
  307. "message0": "increase %1 by %2", //Blockly.Msg.MATH_CHANGE_TITLE,
  308. "args0": [
  309. {
  310. "type": "field_variable",
  311. "name": "VAR",
  312. "variable": Blockly.Msg.MATH_CHANGE_TITLE_ITEM
  313. },
  314. {
  315. "type": "input_value",
  316. "name": "DELTA",
  317. "check": "Number"
  318. }
  319. ],
  320. "previousStatement": null,
  321. "nextStatement": null,
  322. "colour": VARIABLE_HUE,
  323. "helpUrl": Blockly.Msg.MATH_CHANGE_HELPURL
  324. });
  325. // Assign 'this' to a variable for use in the tooltip closure below.
  326. var thisBlock = this;
  327. this.setTooltip(function() {
  328. return Blockly.Msg.MATH_CHANGE_TOOLTIP.replace('%1',
  329. thisBlock.getFieldValue('VAR'));
  330. });
  331. }
  332. };
  333. Blockly.Blocks['math_round'] = {
  334. /**
  335. * Block for rounding functions.
  336. * @this Blockly.Block
  337. */
  338. init: function() {
  339. this.jsonInit({
  340. "message0": "%1 %2",
  341. "args0": [
  342. {
  343. "type": "field_dropdown",
  344. "name": "OP",
  345. "options": [
  346. [Blockly.Msg.MATH_ROUND_OPERATOR_ROUND, 'ROUND'],
  347. [Blockly.Msg.MATH_ROUND_OPERATOR_ROUNDUP, 'ROUNDUP'],
  348. [Blockly.Msg.MATH_ROUND_OPERATOR_ROUNDDOWN, 'ROUNDDOWN']
  349. ]
  350. },
  351. {
  352. "type": "input_value",
  353. "name": "NUM",
  354. "check": "Number"
  355. }
  356. ],
  357. "output": "Number",
  358. "colour": Blockly.Blocks.math.HUE,
  359. "tooltip": Blockly.Msg.MATH_ROUND_TOOLTIP,
  360. "helpUrl": Blockly.Msg.MATH_ROUND_HELPURL
  361. });
  362. }
  363. };
  364. Blockly.Blocks['math_on_list'] = {
  365. /**
  366. * Block for evaluating a list of numbers to return sum, average, min, max,
  367. * etc. Some functions also work on text (min, max, mode, median).
  368. * @this Blockly.Block
  369. */
  370. init: function() {
  371. var OPERATORS =
  372. [[Blockly.Msg.MATH_ONLIST_OPERATOR_SUM, 'SUM'],
  373. [Blockly.Msg.MATH_ONLIST_OPERATOR_MIN, 'MIN'],
  374. [Blockly.Msg.MATH_ONLIST_OPERATOR_MAX, 'MAX'],
  375. [Blockly.Msg.MATH_ONLIST_OPERATOR_AVERAGE, 'AVERAGE'],
  376. [Blockly.Msg.MATH_ONLIST_OPERATOR_MEDIAN, 'MEDIAN'],
  377. [Blockly.Msg.MATH_ONLIST_OPERATOR_MODE, 'MODE'],
  378. [Blockly.Msg.MATH_ONLIST_OPERATOR_STD_DEV, 'STD_DEV'],
  379. [Blockly.Msg.MATH_ONLIST_OPERATOR_RANDOM, 'RANDOM']];
  380. // Assign 'this' to a variable for use in the closures below.
  381. var thisBlock = this;
  382. this.setHelpUrl(Blockly.Msg.MATH_ONLIST_HELPURL);
  383. this.setColour(Blockly.Blocks.math.HUE);
  384. this.setOutput(true, 'Number');
  385. var dropdown = new Blockly.FieldDropdown(OPERATORS, function(newOp) {
  386. thisBlock.updateType_(newOp);
  387. });
  388. this.appendValueInput('LIST')
  389. .setCheck('Array')
  390. .appendField(dropdown, 'OP');
  391. this.setTooltip(function() {
  392. var mode = thisBlock.getFieldValue('OP');
  393. var TOOLTIPS = {
  394. 'SUM': Blockly.Msg.MATH_ONLIST_TOOLTIP_SUM,
  395. 'MIN': Blockly.Msg.MATH_ONLIST_TOOLTIP_MIN,
  396. 'MAX': Blockly.Msg.MATH_ONLIST_TOOLTIP_MAX,
  397. 'AVERAGE': Blockly.Msg.MATH_ONLIST_TOOLTIP_AVERAGE,
  398. 'MEDIAN': Blockly.Msg.MATH_ONLIST_TOOLTIP_MEDIAN,
  399. 'MODE': Blockly.Msg.MATH_ONLIST_TOOLTIP_MODE,
  400. 'STD_DEV': Blockly.Msg.MATH_ONLIST_TOOLTIP_STD_DEV,
  401. 'RANDOM': Blockly.Msg.MATH_ONLIST_TOOLTIP_RANDOM
  402. };
  403. return TOOLTIPS[mode];
  404. });
  405. },
  406. /**
  407. * Modify this block to have the correct output type.
  408. * @param {string} newOp Either 'MODE' or some op than returns a number.
  409. * @private
  410. * @this Blockly.Block
  411. */
  412. updateType_: function(newOp) {
  413. if (newOp == 'MODE') {
  414. this.outputConnection.setCheck('Array');
  415. } else {
  416. this.outputConnection.setCheck('Number');
  417. }
  418. },
  419. /**
  420. * Create XML to represent the output type.
  421. * @return {Element} XML storage element.
  422. * @this Blockly.Block
  423. */
  424. mutationToDom: function() {
  425. var container = document.createElement('mutation');
  426. container.setAttribute('op', this.getFieldValue('OP'));
  427. return container;
  428. },
  429. /**
  430. * Parse XML to restore the output type.
  431. * @param {!Element} xmlElement XML storage element.
  432. * @this Blockly.Block
  433. */
  434. domToMutation: function(xmlElement) {
  435. this.updateType_(xmlElement.getAttribute('op'));
  436. }
  437. };
  438. Blockly.Blocks['math_modulo'] = {
  439. /**
  440. * Block for remainder of a division.
  441. * @this Blockly.Block
  442. */
  443. init: function() {
  444. this.jsonInit({
  445. "message0": Blockly.Msg.MATH_MODULO_TITLE,
  446. "args0": [
  447. {
  448. "type": "input_value",
  449. "name": "DIVIDEND",
  450. "check": "Number"
  451. },
  452. {
  453. "type": "input_value",
  454. "name": "DIVISOR",
  455. "check": "Number"
  456. }
  457. ],
  458. "inputsInline": true,
  459. "output": "Number",
  460. "colour": Blockly.Blocks.math.HUE,
  461. "tooltip": Blockly.Msg.MATH_MODULO_TOOLTIP,
  462. "helpUrl": Blockly.Msg.MATH_MODULO_HELPURL
  463. });
  464. }
  465. };
  466. Blockly.Blocks['math_constrain'] = {
  467. /**
  468. * Block for constraining a number between two limits.
  469. * @this Blockly.Block
  470. */
  471. init: function() {
  472. this.jsonInit({
  473. "message0": Blockly.Msg.MATH_CONSTRAIN_TITLE,
  474. "args0": [
  475. {
  476. "type": "input_value",
  477. "name": "VALUE",
  478. "check": "Number"
  479. },
  480. {
  481. "type": "input_value",
  482. "name": "LOW",
  483. "check": "Number"
  484. },
  485. {
  486. "type": "input_value",
  487. "name": "HIGH",
  488. "check": "Number"
  489. }
  490. ],
  491. "inputsInline": true,
  492. "output": "Number",
  493. "colour": Blockly.Blocks.math.HUE,
  494. "tooltip": Blockly.Msg.MATH_CONSTRAIN_TOOLTIP,
  495. "helpUrl": Blockly.Msg.MATH_CONSTRAIN_HELPURL
  496. });
  497. }
  498. };
  499. Blockly.Blocks['math_random_int'] = {
  500. /**
  501. * Block for random integer between [X] and [Y].
  502. * @this Blockly.Block
  503. */
  504. init: function() {
  505. this.jsonInit({
  506. "message0": Blockly.Msg.MATH_RANDOM_INT_TITLE,
  507. "args0": [
  508. {
  509. "type": "input_value",
  510. "name": "FROM",
  511. "check": "Number"
  512. },
  513. {
  514. "type": "input_value",
  515. "name": "TO",
  516. "check": "Number"
  517. }
  518. ],
  519. "inputsInline": true,
  520. "output": "Number",
  521. "colour": Blockly.Blocks.math.HUE,
  522. "tooltip": Blockly.Msg.MATH_RANDOM_INT_TOOLTIP,
  523. "helpUrl": Blockly.Msg.MATH_RANDOM_INT_HELPURL
  524. });
  525. }
  526. };
  527. Blockly.Blocks['math_random_float'] = {
  528. /**
  529. * Block for random fraction between 0 and 1.
  530. * @this Blockly.Block
  531. */
  532. init: function() {
  533. this.jsonInit({
  534. "message0": Blockly.Msg.MATH_RANDOM_FLOAT_TITLE_RANDOM,
  535. "output": "Number",
  536. "colour": Blockly.Blocks.math.HUE,
  537. "tooltip": Blockly.Msg.MATH_RANDOM_FLOAT_TOOLTIP,
  538. "helpUrl": Blockly.Msg.MATH_RANDOM_FLOAT_HELPURL
  539. });
  540. }
  541. };
  542. Blockly.Blocks['math_sum_values'] = {
  543. init:function() {
  544. this.setColour(Blockly.Blocks.math.HUE);
  545. this.appendDummyInput()
  546. .appendField(Blockly.Msg.MATH_SUM)
  547. this.appendValueInput("SUM1")
  548. this.appendValueInput("SUM2");
  549. this.setOutput(true,'Number');
  550. this.setInputsInline(true);
  551. },
  552. }
  553. Blockly.Blocks['math_sum_common'] = {
  554. init: function() {
  555. this.setColour(Blockly.Blocks.math.HUE)
  556. this.appendValueInput("SUM1")
  557. .appendField(Blockly.Msg.MATH_SUM);
  558. this.setOutput(true,'Number');
  559. this.setInputsInline(true);
  560. }
  561. }