BinarySearchTupleLocator.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. 'use strict';
  2. var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  3. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  4. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  5. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  6. /**
  7. *+------------------------------------------------------------------------+
  8. *| Licensed Materials - Property of IBM
  9. *| IBM Cognos Products: Dashboard
  10. *| (C) Copyright IBM Corp. 2018, 2019
  11. *|
  12. *| US Government Users Restricted Rights - Use, duplication or disclosure
  13. *| restricted by GSA ADP Schedule Contract with IBM Corp.
  14. *+------------------------------------------------------------------------+
  15. */
  16. define(['underscore', './AbstractTupleLocator'], function (_, AbstractTupleLocator) {
  17. var BinarySearchTupleLocator = function (_AbstractTupleLocator) {
  18. _inherits(BinarySearchTupleLocator, _AbstractTupleLocator);
  19. /**
  20. * A tuple locator which deals with tuples which the VIDA getItemsInPolygon
  21. * API returns.
  22. */
  23. function BinarySearchTupleLocator(_ref) {
  24. var viprWidget = _ref.viprWidget;
  25. _classCallCheck(this, BinarySearchTupleLocator);
  26. var _this = _possibleConstructorReturn(this, _AbstractTupleLocator.call(this));
  27. _this.viprWidget = viprWidget;
  28. return _this;
  29. }
  30. /* For JSDocs, see parent class */
  31. BinarySearchTupleLocator.prototype.findTupleVisualInfo = function findTupleVisualInfo(filter, _ref2) {
  32. var _this2 = this;
  33. var includeInfo = _ref2.includeInfo,
  34. includePoints = _ref2.includePoints;
  35. var viprItems = _.flatten(
  36. //Get all items in an area
  37. this._getVisAreas().map(function (_ref3) {
  38. var x = _ref3.x,
  39. y = _ref3.y,
  40. w = _ref3.w,
  41. h = _ref3.h;
  42. return _this2._getVIDAItemsInRectangle({ x0: x, y0: y, x1: x + w, y1: y + h })
  43. //Filter items to ones which satisfy specified filter
  44. .filter(function (item) {
  45. return _this2._itemSatisfiesFilter({ item: item, filter: filter });
  46. });
  47. }));
  48. return viprItems.map(function (item) {
  49. var result = {};
  50. if (includeInfo) {
  51. result.info = _this2._getValueInfoFromItem(item);
  52. }
  53. if (includePoints) {
  54. result.point = _this2._getPoint(item);
  55. }
  56. return result;
  57. });
  58. };
  59. /*
  60. * Finds the areas (e.g. "visualization", "legend") and their extents which
  61. * VIPR partitions the entire visualization into.
  62. *
  63. * Motivation: It is important to know their extents, as if a
  64. * getItemsInPolygon query draws a polygon with points straddling multiple
  65. * areas, VIPR will return no items.
  66. *
  67. * @return {object[]} area information will include 'area' (name), 'x', 'y',
  68. * 'w' and 'h'
  69. */
  70. BinarySearchTupleLocator.prototype._getVisAreas = function _getVisAreas() {
  71. var _this3 = this;
  72. var bounds = this.viprWidget.domNode.getBoundingClientRect();
  73. var areaToExtents = {};
  74. //Assume all areas we care about begin in one of the four corners of the
  75. //visualization.
  76. var findAreasFromCorner = function findAreasFromCorner(bx, by) {
  77. var w0 = bx ? bounds.width - 1 : 0,
  78. h0 = by ? bounds.height - 1 : 0;
  79. var getArea = function getArea(w, h) {
  80. return _this3.viprWidget.getVisCoordinate(bounds.x + w, bounds.y + h).area;
  81. };
  82. //What area is in this corner?
  83. var area = getArea(w0, h0);
  84. //'none' is not a useful area; and if we've already found the extents of
  85. //this area, no need to scan again
  86. if (area === 'none' || areaToExtents[area]) {
  87. return;
  88. }
  89. var dx = bx ? -1 : 1,
  90. dy = by ? -1 : 1;
  91. var wt = void 0,
  92. ht = void 0;
  93. //Scan x axis to see how wide this area is
  94. for (wt = w0; 0 <= wt && wt < bounds.width; wt += dx) {
  95. if (getArea(wt, h0) !== area) {
  96. break;
  97. }
  98. }
  99. wt -= dx;
  100. //Scan y axis to see how tall this area is
  101. for (ht = h0; 0 <= ht && ht < bounds.height; ht += dy) {
  102. if (getArea(w0, ht) !== area) {
  103. break;
  104. }
  105. }
  106. ht -= dy;
  107. //Assume areas are rectangular
  108. areaToExtents[area] = {
  109. x: Math.min(w0, wt),
  110. y: Math.min(h0, ht),
  111. w: Math.abs(w0 - wt),
  112. h: Math.abs(h0 - ht)
  113. };
  114. };
  115. //Search for areas in all four corners
  116. findAreasFromCorner(false, false); //top-left
  117. findAreasFromCorner(true, false); //top-right
  118. findAreasFromCorner(false, true); //bottom-left
  119. findAreasFromCorner(true, true); //bottom-right
  120. //In general, the only area we use is 'visualization'. If it's not found
  121. //we won't be able to proceed
  122. if (!areaToExtents.visualization) {
  123. throw new Error('Cannot find "visualization" area of widget');
  124. }
  125. return Object.keys(areaToExtents).map(function (area) {
  126. return { extents: areaToExtents[area], area: area };
  127. }).map(function (_ref4) {
  128. var extents = _ref4.extents,
  129. area = _ref4.area;
  130. return _extends({}, extents, { area: area });
  131. });
  132. };
  133. /*
  134. * Simplifies the VIPR getItemsInPolygon API to a rectangle - and takes care
  135. * of the mapping to VIPR coordinate space via getVisCoordinate.
  136. * @param {object} coords
  137. * @param {number} coords.x0 - top-left corner x coordinate
  138. * @param {number} coords.y0 - top-left corner y coordinate
  139. * @param {number} coords.x1 - bottom-right corner x coordinate
  140. * @param {number} coords.y1 - bottom-right corner y coordinate
  141. */
  142. BinarySearchTupleLocator.prototype._getVIDAItemsInRectangle = function _getVIDAItemsInRectangle(_ref5) {
  143. var _this4 = this;
  144. var x0 = _ref5.x0,
  145. y0 = _ref5.y0,
  146. x1 = _ref5.x1,
  147. y1 = _ref5.y1;
  148. var origin = this.viprWidget.domNode.getBoundingClientRect();
  149. //Make a 'rectangle' polygon:
  150. // - top-left corner
  151. // - top-right corner
  152. // - bottom-right corner
  153. // - bottom-left corner
  154. var points = [[x0, y0], [x1, y0], [x1, y1], [x0, y1]].map(function (_ref6) {
  155. var x = _ref6[0],
  156. y = _ref6[1];
  157. return [x + origin.x, y + origin.y];
  158. }).map(function (_ref7) {
  159. var x = _ref7[0],
  160. y = _ref7[1];
  161. return _this4.viprWidget.getVisCoordinate(x, y);
  162. });
  163. return this.viprWidget.getItemsInPolygon(points);
  164. };
  165. /*
  166. * Finds a single clickable point which will click an item
  167. * @param {object} item
  168. */
  169. BinarySearchTupleLocator.prototype._getPoint = function _getPoint(item) {
  170. var _this5 = this;
  171. //Find which area this item is contained in and extract its extents
  172. var extent = this._getVisAreas().find(function (_ref8) {
  173. var x = _ref8.x,
  174. y = _ref8.y,
  175. w = _ref8.w,
  176. h = _ref8.h;
  177. return _this5._isItemContainedIn(item, { x0: x, y0: y, x1: x + w, y1: y + h });
  178. });
  179. if (!extent) {
  180. throw new Error('Item not found at any position within visualization');
  181. }
  182. var x0 = extent.x,
  183. y0 = extent.y,
  184. x1 = x0 + extent.w,
  185. y1 = y0 + extent.h;
  186. //Find the boundary between where this item does and does not exist in the x axis
  187. var x = this._binarySearch(x1, x0, function (x) {
  188. return _this5._isItemContainedIn(item, { x0: x0, y0: y0, x1: x, y1: y1 });
  189. });
  190. //Find the boundary between where this item does and does not exist along the strip of x axis found above
  191. var y = this._binarySearch(y1, y0, function (y) {
  192. return _this5._isItemContainedIn(item, { x0: x - 1, y0: y0, x1: x, y1: y });
  193. });
  194. return {
  195. selectedNode: this.viprWidget.domNode,
  196. xOffset: x,
  197. yOffset: y
  198. };
  199. };
  200. /*
  201. * Perform a binary search for the boundary between a condition succeeding and failing
  202. * @param {number} maxPass - the maximum value where the condition is known to pass
  203. * @param {number} maxFail - the maximum value where the condition is known to fail
  204. * @param {function} test - condition
  205. */
  206. BinarySearchTupleLocator.prototype._binarySearch = function _binarySearch(maxPass, maxFail, test) {
  207. while (Math.abs(maxPass - maxFail) > 1) {
  208. var candidate = Math.round((maxPass + maxFail) / 2);
  209. if (test(candidate)) {
  210. maxPass = candidate;
  211. } else {
  212. maxFail = candidate;
  213. }
  214. }
  215. return maxPass;
  216. };
  217. /*
  218. * Determines whether the item exists inside a particular rectangle in the visualization
  219. * @param {object} item
  220. * @param {object} extents - rectangle
  221. */
  222. BinarySearchTupleLocator.prototype._isItemContainedIn = function _isItemContainedIn(item, _ref9) {
  223. var x0 = _ref9.x0,
  224. y0 = _ref9.y0,
  225. x1 = _ref9.x1,
  226. y1 = _ref9.y1;
  227. return this._getVIDAItemsInRectangle({ x0: x0, y0: y0, x1: x1, y1: y1 }).some(function (foundItem) {
  228. return item.key === foundItem.key;
  229. });
  230. };
  231. return BinarySearchTupleLocator;
  232. }(AbstractTupleLocator);
  233. return BinarySearchTupleLocator;
  234. });
  235. //# sourceMappingURL=BinarySearchTupleLocator.js.map