utils.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. /**
  2. * DevExtreme (viz/core/utils.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. var noop = require("../../core/utils/common").noop;
  11. var typeUtils = require("../../core/utils/type");
  12. var extend = require("../../core/utils/extend").extend;
  13. var each = require("../../core/utils/iterator").each;
  14. var adjust = require("../../core/utils/math").adjust;
  15. var dateToMilliseconds = require("../../core/utils/date").dateToMilliseconds;
  16. var isDefined = typeUtils.isDefined;
  17. var isNumber = typeUtils.isNumeric;
  18. var isExponential = typeUtils.isExponential;
  19. var _math = Math;
  20. var _round = _math.round;
  21. var _sqrt = Math.sqrt;
  22. var PI = Math.PI;
  23. var MAX_PIXEL_COUNT = 1e10;
  24. var PI_DIV_180 = PI / 180;
  25. var LN10 = Math.LN10;
  26. var cosFunc = Math.cos;
  27. var sinFunc = Math.sin;
  28. var abs = Math.abs;
  29. var log = Math.log;
  30. var floor = Math.floor;
  31. var ceil = Math.ceil;
  32. var max = Math.max;
  33. var _isNaN = isNaN;
  34. var _Number = Number;
  35. var _NaN = NaN;
  36. var getLog = function(value, base) {
  37. if (!value) {
  38. return _NaN
  39. }
  40. return Math.log(value) / Math.log(base)
  41. };
  42. var getAdjustedLog10 = function(value) {
  43. return adjust(getLog(value, 10))
  44. };
  45. var raiseTo = function(power, base) {
  46. return Math.pow(base, power)
  47. };
  48. var normalizeAngle = function(angle) {
  49. return (angle % 360 + 360) % 360
  50. };
  51. var convertAngleToRendererSpace = function(angle) {
  52. return 90 - angle
  53. };
  54. var degreesToRadians = function(value) {
  55. return PI * value / 180
  56. };
  57. var getCosAndSin = function(angle) {
  58. var angleInRadians = degreesToRadians(angle);
  59. return {
  60. cos: cosFunc(angleInRadians),
  61. sin: sinFunc(angleInRadians)
  62. }
  63. };
  64. var DECIMAL_ORDER_THRESHOLD = 1e-14;
  65. var getDistance = function(x1, y1, x2, y2) {
  66. var diffX = x2 - x1;
  67. var diffY = y2 - y1;
  68. return Math.sqrt(diffY * diffY + diffX * diffX)
  69. };
  70. var getDecimalOrder = function(number) {
  71. var n = abs(number);
  72. var cn;
  73. if (!_isNaN(n)) {
  74. if (n > 0) {
  75. n = log(n) / LN10;
  76. cn = ceil(n);
  77. return cn - n < DECIMAL_ORDER_THRESHOLD ? cn : floor(n)
  78. }
  79. return 0
  80. }
  81. return _NaN
  82. };
  83. var getAppropriateFormat = function(start, end, count) {
  84. var order = max(getDecimalOrder(start), getDecimalOrder(end));
  85. var precision = -getDecimalOrder(abs(end - start) / count);
  86. var format;
  87. if (!_isNaN(order) && !_isNaN(precision)) {
  88. if (abs(order) <= 4) {
  89. format = "fixedPoint";
  90. precision < 0 && (precision = 0);
  91. precision > 4 && (precision = 4)
  92. } else {
  93. format = "exponential";
  94. precision += order - 1;
  95. precision > 3 && (precision = 3)
  96. }
  97. return {
  98. type: format,
  99. precision: precision
  100. }
  101. }
  102. return null
  103. };
  104. var roundValue = function(value, precision) {
  105. if (precision > 20) {
  106. precision = 20
  107. }
  108. if (isNumber(value)) {
  109. if (isExponential(value)) {
  110. return _Number(value.toExponential(precision))
  111. } else {
  112. return _Number(value.toFixed(precision))
  113. }
  114. }
  115. };
  116. var getPower = function(value) {
  117. return value.toExponential().split("e")[1]
  118. };
  119. function map(array, callback) {
  120. var i = 0;
  121. var len = array.length;
  122. var result = [];
  123. while (i < len) {
  124. var value = callback(array[i], i);
  125. if (null !== value) {
  126. result.push(value)
  127. }
  128. i++
  129. }
  130. return result
  131. }
  132. function selectByKeys(object, keys) {
  133. return map(keys, function(key) {
  134. return object[key] ? object[key] : null
  135. })
  136. }
  137. function decreaseFields(object, keys, eachDecrease, decrease) {
  138. var dec = decrease;
  139. each(keys, function(_, key) {
  140. if (object[key]) {
  141. object[key] -= eachDecrease;
  142. dec -= eachDecrease
  143. }
  144. });
  145. return dec
  146. }
  147. function normalizeEnum(value) {
  148. return String(value).toLowerCase()
  149. }
  150. function setCanvasValues(canvas) {
  151. if (canvas) {
  152. canvas.originalTop = canvas.top;
  153. canvas.originalBottom = canvas.bottom;
  154. canvas.originalLeft = canvas.left;
  155. canvas.originalRight = canvas.right
  156. }
  157. return canvas
  158. }
  159. function normalizeBBoxField(value) {
  160. return -MAX_PIXEL_COUNT < value && value < +MAX_PIXEL_COUNT ? value : 0
  161. }
  162. function normalizeBBox(bBox) {
  163. var xl = normalizeBBoxField(floor(bBox.x));
  164. var yt = normalizeBBoxField(floor(bBox.y));
  165. var xr = normalizeBBoxField(ceil(bBox.width + bBox.x));
  166. var yb = normalizeBBoxField(ceil(bBox.height + bBox.y));
  167. var result = {
  168. x: xl,
  169. y: yt,
  170. width: xr - xl,
  171. height: yb - yt
  172. };
  173. result.isEmpty = !result.x && !result.y && !result.width && !result.height;
  174. return result
  175. }
  176. function rotateBBox(bBox, center, angle) {
  177. var cos = _Number(cosFunc(angle * PI_DIV_180).toFixed(3));
  178. var sin = _Number(sinFunc(angle * PI_DIV_180).toFixed(3));
  179. var w2 = bBox.width / 2;
  180. var h2 = bBox.height / 2;
  181. var centerX = bBox.x + w2;
  182. var centerY = bBox.y + h2;
  183. var w2_ = abs(w2 * cos) + abs(h2 * sin);
  184. var h2_ = abs(w2 * sin) + abs(h2 * cos);
  185. var centerX_ = center[0] + (centerX - center[0]) * cos + (centerY - center[1]) * sin;
  186. var centerY_ = center[1] - (centerX - center[0]) * sin + (centerY - center[1]) * cos;
  187. return normalizeBBox({
  188. x: centerX_ - w2_,
  189. y: centerY_ - h2_,
  190. width: 2 * w2_,
  191. height: 2 * h2_
  192. })
  193. }
  194. extend(exports, {
  195. decreaseGaps: function(object, keys, decrease) {
  196. var arrayGaps;
  197. do {
  198. arrayGaps = selectByKeys(object, keys);
  199. arrayGaps.push(_math.ceil(decrease / arrayGaps.length));
  200. decrease = decreaseFields(object, keys, _math.min.apply(null, arrayGaps), decrease)
  201. } while (decrease > 0 && arrayGaps.length > 1);
  202. return decrease
  203. },
  204. normalizeEnum: normalizeEnum,
  205. parseScalar: function(value, defaultValue) {
  206. return void 0 !== value ? value : defaultValue
  207. },
  208. enumParser: function(values) {
  209. var stored = {};
  210. var i;
  211. var ii;
  212. for (i = 0, ii = values.length; i < ii; ++i) {
  213. stored[normalizeEnum(values[i])] = 1
  214. }
  215. return function(value, defaultValue) {
  216. var _value = normalizeEnum(value);
  217. return stored[_value] ? _value : defaultValue
  218. }
  219. },
  220. patchFontOptions: function(options) {
  221. var fontOptions = {};
  222. each(options || {}, function(key, value) {
  223. if (/^(cursor|opacity)$/i.test(key)) {} else {
  224. if ("color" === key) {
  225. key = "fill"
  226. } else {
  227. key = "font-" + key
  228. }
  229. }
  230. fontOptions[key] = value
  231. });
  232. return fontOptions
  233. },
  234. convertPolarToXY: function(centerCoords, startAngle, angle, radius) {
  235. var shiftAngle = 90;
  236. angle = isDefined(angle) ? angle + startAngle - shiftAngle : 0;
  237. var cosSin = getCosAndSin(angle);
  238. return {
  239. x: _round(centerCoords.x + radius * cosSin.cos),
  240. y: _round(centerCoords.y + radius * cosSin.sin)
  241. }
  242. },
  243. convertXYToPolar: function(centerCoords, x, y) {
  244. var radius = getDistance(centerCoords.x, centerCoords.y, x, y);
  245. var angle = _math.atan2(y - centerCoords.y, x - centerCoords.x);
  246. return {
  247. phi: _round(normalizeAngle(180 * angle / _math.PI)),
  248. r: _round(radius)
  249. }
  250. },
  251. processSeriesTemplate: function(seriesTemplate, items) {
  252. var customizeSeries = typeUtils.isFunction(seriesTemplate.customizeSeries) ? seriesTemplate.customizeSeries : noop;
  253. var nameField = seriesTemplate.nameField;
  254. var generatedSeries = {};
  255. var seriesOrder = [];
  256. var series;
  257. var i = 0;
  258. items = items || [];
  259. for (var length = items.length; i < length; i++) {
  260. var data = items[i];
  261. if (nameField in data) {
  262. series = generatedSeries[data[nameField]];
  263. if (!series) {
  264. series = generatedSeries[data[nameField]] = {
  265. name: data[nameField],
  266. nameFieldValue: data[nameField]
  267. };
  268. seriesOrder.push(series.name)
  269. }
  270. }
  271. }
  272. return map(seriesOrder, function(orderedName) {
  273. var group = generatedSeries[orderedName];
  274. return extend(group, customizeSeries.call(null, group.name))
  275. })
  276. },
  277. getCategoriesInfo: function(categories, startValue, endValue) {
  278. if (0 === categories.length) {
  279. return {
  280. categories: []
  281. }
  282. }
  283. startValue = isDefined(startValue) ? startValue : categories[0];
  284. endValue = isDefined(endValue) ? endValue : categories[categories.length - 1];
  285. var categoriesValue = map(categories, function(category) {
  286. return isDefined(category) ? category.valueOf() : null
  287. });
  288. var indexStartValue = categoriesValue.indexOf(startValue.valueOf());
  289. var indexEndValue = categoriesValue.indexOf(endValue.valueOf());
  290. var inverted = false;
  291. indexStartValue < 0 && (indexStartValue = 0);
  292. indexEndValue < 0 && (indexEndValue = categories.length - 1);
  293. if (indexEndValue < indexStartValue) {
  294. var swapBuf = indexEndValue;
  295. indexEndValue = indexStartValue;
  296. indexStartValue = swapBuf;
  297. inverted = true
  298. }
  299. var visibleCategories = categories.slice(indexStartValue, indexEndValue + 1);
  300. var lastIdx = visibleCategories.length - 1;
  301. return {
  302. categories: visibleCategories,
  303. start: visibleCategories[inverted ? lastIdx : 0],
  304. end: visibleCategories[inverted ? 0 : lastIdx],
  305. inverted: inverted
  306. }
  307. },
  308. setCanvasValues: setCanvasValues,
  309. updatePanesCanvases: function(panes, canvas, rotated) {
  310. var weightSum = 0;
  311. each(panes, function(_, pane) {
  312. pane.weight = pane.weight || 1;
  313. weightSum += pane.weight
  314. });
  315. var distributedSpace = 0;
  316. var padding = panes.padding || 10;
  317. var paneSpace = rotated ? canvas.width - canvas.left - canvas.right : canvas.height - canvas.top - canvas.bottom;
  318. var oneWeight = (paneSpace - padding * (panes.length - 1)) / weightSum;
  319. var startName = rotated ? "left" : "top";
  320. var endName = rotated ? "right" : "bottom";
  321. each(panes, function(_, pane) {
  322. var calcLength = _round(pane.weight * oneWeight);
  323. pane.canvas = pane.canvas || {};
  324. extend(pane.canvas, canvas);
  325. pane.canvas[startName] = canvas[startName] + distributedSpace;
  326. pane.canvas[endName] = canvas[endName] + (paneSpace - calcLength - distributedSpace);
  327. distributedSpace = distributedSpace + calcLength + padding;
  328. setCanvasValues(pane.canvas)
  329. })
  330. },
  331. unique: function(array) {
  332. var values = {};
  333. return map(array, function(item) {
  334. var result = !values[item] ? item : null;
  335. values[item] = true;
  336. return result
  337. })
  338. },
  339. map: map,
  340. getVerticallyShiftedAngularCoords: function(bBox, dy, center) {
  341. var isPositive = bBox.x + bBox.width / 2 >= center.x;
  342. var horizontalOffset1 = (isPositive ? bBox.x : bBox.x + bBox.width) - center.x;
  343. var verticalOffset1 = bBox.y - center.y;
  344. var verticalOffset2 = verticalOffset1 + dy;
  345. var horizontalOffset2 = _round(_sqrt(horizontalOffset1 * horizontalOffset1 + verticalOffset1 * verticalOffset1 - verticalOffset2 * verticalOffset2));
  346. var dx = (isPositive ? +horizontalOffset2 : -horizontalOffset2) || horizontalOffset1;
  347. return {
  348. x: center.x + (isPositive ? dx : dx - bBox.width),
  349. y: bBox.y + dy
  350. }
  351. },
  352. mergeMarginOptions: function(opt1, opt2) {
  353. return {
  354. checkInterval: opt1.checkInterval || opt2.checkInterval,
  355. size: Math.max(opt1.size || 0, opt2.size || 0),
  356. percentStick: opt1.percentStick || opt2.percentStick,
  357. sizePointNormalState: Math.max(opt1.sizePointNormalState || 0, opt2.sizePointNormalState || 0)
  358. }
  359. }
  360. });
  361. function getVizRangeObject(value) {
  362. if (Array.isArray(value)) {
  363. return {
  364. startValue: value[0],
  365. endValue: value[1]
  366. }
  367. } else {
  368. return value || {}
  369. }
  370. }
  371. function convertVisualRangeObject(visualRange, convertToVisualRange) {
  372. if (convertToVisualRange) {
  373. return visualRange
  374. }
  375. return [visualRange.startValue, visualRange.endValue]
  376. }
  377. function getAddFunction(range, correctZeroLevel) {
  378. if ("datetime" === range.dataType) {
  379. return function(rangeValue, marginValue) {
  380. var sign = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : 1;
  381. return new Date(rangeValue.getTime() + sign * marginValue)
  382. }
  383. }
  384. if ("logarithmic" === range.axisType) {
  385. return function(rangeValue, marginValue) {
  386. var sign = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : 1;
  387. var log = getLog(rangeValue, range.base) + sign * marginValue;
  388. return raiseTo(log, range.base)
  389. }
  390. }
  391. return function(rangeValue, marginValue) {
  392. var sign = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : 1;
  393. var newValue = rangeValue + sign * marginValue;
  394. return correctZeroLevel && newValue * rangeValue <= 0 ? 0 : newValue
  395. }
  396. }
  397. function adjustVisualRange(options, visualRange, wholeRange, dataRange) {
  398. var minDefined = typeUtils.isDefined(visualRange.startValue);
  399. var maxDefined = typeUtils.isDefined(visualRange.endValue);
  400. var nonDiscrete = "discrete" !== options.axisType;
  401. dataRange = dataRange || wholeRange;
  402. var add = getAddFunction(options, false);
  403. var min = minDefined ? visualRange.startValue : dataRange.min;
  404. var max = maxDefined ? visualRange.endValue : dataRange.max;
  405. var rangeLength = visualRange.length;
  406. var categories = dataRange.categories;
  407. if (nonDiscrete && !typeUtils.isDefined(min) && !typeUtils.isDefined(max)) {
  408. return {
  409. startValue: min,
  410. endValue: max
  411. }
  412. }
  413. if (isDefined(rangeLength)) {
  414. if (nonDiscrete) {
  415. if ("datetime" === options.dataType && !isNumber(rangeLength)) {
  416. rangeLength = dateToMilliseconds(rangeLength)
  417. }
  418. if (maxDefined && !minDefined || !maxDefined && !minDefined) {
  419. isDefined(wholeRange.max) && (max = max > wholeRange.max ? wholeRange.max : max);
  420. min = add(max, rangeLength, -1)
  421. } else {
  422. if (minDefined && !maxDefined) {
  423. isDefined(wholeRange.min) && (min = min < wholeRange.min ? wholeRange.min : min);
  424. max = add(min, rangeLength)
  425. }
  426. }
  427. } else {
  428. rangeLength = parseInt(rangeLength);
  429. if (!isNaN(rangeLength) && isFinite(rangeLength)) {
  430. rangeLength--;
  431. if (!maxDefined && !minDefined) {
  432. max = categories[categories.length - 1];
  433. min = categories[categories.length - 1 - rangeLength]
  434. } else {
  435. if (minDefined && !maxDefined) {
  436. var categoriesInfo = exports.getCategoriesInfo(categories, min, void 0);
  437. max = categoriesInfo.categories[rangeLength]
  438. } else {
  439. if (!minDefined && maxDefined) {
  440. var _categoriesInfo = exports.getCategoriesInfo(categories, void 0, max);
  441. min = _categoriesInfo.categories[_categoriesInfo.categories.length - 1 - rangeLength]
  442. }
  443. }
  444. }
  445. }
  446. }
  447. }
  448. if (nonDiscrete) {
  449. if (isDefined(wholeRange.max) && max > wholeRange.max) {
  450. max = wholeRange.max
  451. }
  452. if (isDefined(wholeRange.min) && min < wholeRange.min) {
  453. min = wholeRange.min
  454. }
  455. }
  456. return {
  457. startValue: min,
  458. endValue: max
  459. }
  460. }
  461. exports.getVizRangeObject = getVizRangeObject;
  462. exports.convertVisualRangeObject = convertVisualRangeObject;
  463. exports.adjustVisualRange = adjustVisualRange;
  464. exports.getAddFunction = getAddFunction;
  465. exports.getLog = getLog;
  466. exports.getAdjustedLog10 = getAdjustedLog10;
  467. exports.raiseTo = raiseTo;
  468. exports.normalizeAngle = normalizeAngle;
  469. exports.convertAngleToRendererSpace = convertAngleToRendererSpace;
  470. exports.degreesToRadians = degreesToRadians;
  471. exports.getCosAndSin = getCosAndSin;
  472. exports.getDecimalOrder = getDecimalOrder;
  473. exports.getAppropriateFormat = getAppropriateFormat;
  474. exports.getDistance = getDistance;
  475. exports.roundValue = roundValue;
  476. exports.getPower = getPower;
  477. exports.rotateBBox = rotateBBox;
  478. exports.normalizeBBox = normalizeBBox;