Parallel.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. /**
  41. * Parallel Coordinates
  42. * <https://en.wikipedia.org/wiki/Parallel_coordinates>
  43. */
  44. import * as zrUtil from 'zrender/lib/core/util.js';
  45. import * as matrix from 'zrender/lib/core/matrix.js';
  46. import * as layoutUtil from '../../util/layout.js';
  47. import * as axisHelper from '../../coord/axisHelper.js';
  48. import ParallelAxis from './ParallelAxis.js';
  49. import * as graphic from '../../util/graphic.js';
  50. import * as numberUtil from '../../util/number.js';
  51. import sliderMove from '../../component/helper/sliderMove.js';
  52. var each = zrUtil.each;
  53. var mathMin = Math.min;
  54. var mathMax = Math.max;
  55. var mathFloor = Math.floor;
  56. var mathCeil = Math.ceil;
  57. var round = numberUtil.round;
  58. var PI = Math.PI;
  59. var Parallel =
  60. /** @class */
  61. function () {
  62. function Parallel(parallelModel, ecModel, api) {
  63. this.type = 'parallel';
  64. /**
  65. * key: dimension
  66. */
  67. this._axesMap = zrUtil.createHashMap();
  68. /**
  69. * key: dimension
  70. * value: {position: [], rotation, }
  71. */
  72. this._axesLayout = {};
  73. this.dimensions = parallelModel.dimensions;
  74. this._model = parallelModel;
  75. this._init(parallelModel, ecModel, api);
  76. }
  77. Parallel.prototype._init = function (parallelModel, ecModel, api) {
  78. var dimensions = parallelModel.dimensions;
  79. var parallelAxisIndex = parallelModel.parallelAxisIndex;
  80. each(dimensions, function (dim, idx) {
  81. var axisIndex = parallelAxisIndex[idx];
  82. var axisModel = ecModel.getComponent('parallelAxis', axisIndex);
  83. var axis = this._axesMap.set(dim, new ParallelAxis(dim, axisHelper.createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisIndex));
  84. var isCategory = axis.type === 'category';
  85. axis.onBand = isCategory && axisModel.get('boundaryGap');
  86. axis.inverse = axisModel.get('inverse'); // Injection
  87. axisModel.axis = axis;
  88. axis.model = axisModel;
  89. axis.coordinateSystem = axisModel.coordinateSystem = this;
  90. }, this);
  91. };
  92. /**
  93. * Update axis scale after data processed
  94. */
  95. Parallel.prototype.update = function (ecModel, api) {
  96. this._updateAxesFromSeries(this._model, ecModel);
  97. };
  98. Parallel.prototype.containPoint = function (point) {
  99. var layoutInfo = this._makeLayoutInfo();
  100. var axisBase = layoutInfo.axisBase;
  101. var layoutBase = layoutInfo.layoutBase;
  102. var pixelDimIndex = layoutInfo.pixelDimIndex;
  103. var pAxis = point[1 - pixelDimIndex];
  104. var pLayout = point[pixelDimIndex];
  105. return pAxis >= axisBase && pAxis <= axisBase + layoutInfo.axisLength && pLayout >= layoutBase && pLayout <= layoutBase + layoutInfo.layoutLength;
  106. };
  107. Parallel.prototype.getModel = function () {
  108. return this._model;
  109. };
  110. /**
  111. * Update properties from series
  112. */
  113. Parallel.prototype._updateAxesFromSeries = function (parallelModel, ecModel) {
  114. ecModel.eachSeries(function (seriesModel) {
  115. if (!parallelModel.contains(seriesModel, ecModel)) {
  116. return;
  117. }
  118. var data = seriesModel.getData();
  119. each(this.dimensions, function (dim) {
  120. var axis = this._axesMap.get(dim);
  121. axis.scale.unionExtentFromData(data, data.mapDimension(dim));
  122. axisHelper.niceScaleExtent(axis.scale, axis.model);
  123. }, this);
  124. }, this);
  125. };
  126. /**
  127. * Resize the parallel coordinate system.
  128. */
  129. Parallel.prototype.resize = function (parallelModel, api) {
  130. this._rect = layoutUtil.getLayoutRect(parallelModel.getBoxLayoutParams(), {
  131. width: api.getWidth(),
  132. height: api.getHeight()
  133. });
  134. this._layoutAxes();
  135. };
  136. Parallel.prototype.getRect = function () {
  137. return this._rect;
  138. };
  139. Parallel.prototype._makeLayoutInfo = function () {
  140. var parallelModel = this._model;
  141. var rect = this._rect;
  142. var xy = ['x', 'y'];
  143. var wh = ['width', 'height'];
  144. var layout = parallelModel.get('layout');
  145. var pixelDimIndex = layout === 'horizontal' ? 0 : 1;
  146. var layoutLength = rect[wh[pixelDimIndex]];
  147. var layoutExtent = [0, layoutLength];
  148. var axisCount = this.dimensions.length;
  149. var axisExpandWidth = restrict(parallelModel.get('axisExpandWidth'), layoutExtent);
  150. var axisExpandCount = restrict(parallelModel.get('axisExpandCount') || 0, [0, axisCount]);
  151. var axisExpandable = parallelModel.get('axisExpandable') && axisCount > 3 && axisCount > axisExpandCount && axisExpandCount > 1 && axisExpandWidth > 0 && layoutLength > 0; // `axisExpandWindow` is According to the coordinates of [0, axisExpandLength],
  152. // for sake of consider the case that axisCollapseWidth is 0 (when screen is narrow),
  153. // where collapsed axes should be overlapped.
  154. var axisExpandWindow = parallelModel.get('axisExpandWindow');
  155. var winSize;
  156. if (!axisExpandWindow) {
  157. winSize = restrict(axisExpandWidth * (axisExpandCount - 1), layoutExtent);
  158. var axisExpandCenter = parallelModel.get('axisExpandCenter') || mathFloor(axisCount / 2);
  159. axisExpandWindow = [axisExpandWidth * axisExpandCenter - winSize / 2];
  160. axisExpandWindow[1] = axisExpandWindow[0] + winSize;
  161. } else {
  162. winSize = restrict(axisExpandWindow[1] - axisExpandWindow[0], layoutExtent);
  163. axisExpandWindow[1] = axisExpandWindow[0] + winSize;
  164. }
  165. var axisCollapseWidth = (layoutLength - winSize) / (axisCount - axisExpandCount); // Avoid axisCollapseWidth is too small.
  166. axisCollapseWidth < 3 && (axisCollapseWidth = 0); // Find the first and last indices > ewin[0] and < ewin[1].
  167. var winInnerIndices = [mathFloor(round(axisExpandWindow[0] / axisExpandWidth, 1)) + 1, mathCeil(round(axisExpandWindow[1] / axisExpandWidth, 1)) - 1]; // Pos in ec coordinates.
  168. var axisExpandWindow0Pos = axisCollapseWidth / axisExpandWidth * axisExpandWindow[0];
  169. return {
  170. layout: layout,
  171. pixelDimIndex: pixelDimIndex,
  172. layoutBase: rect[xy[pixelDimIndex]],
  173. layoutLength: layoutLength,
  174. axisBase: rect[xy[1 - pixelDimIndex]],
  175. axisLength: rect[wh[1 - pixelDimIndex]],
  176. axisExpandable: axisExpandable,
  177. axisExpandWidth: axisExpandWidth,
  178. axisCollapseWidth: axisCollapseWidth,
  179. axisExpandWindow: axisExpandWindow,
  180. axisCount: axisCount,
  181. winInnerIndices: winInnerIndices,
  182. axisExpandWindow0Pos: axisExpandWindow0Pos
  183. };
  184. };
  185. Parallel.prototype._layoutAxes = function () {
  186. var rect = this._rect;
  187. var axes = this._axesMap;
  188. var dimensions = this.dimensions;
  189. var layoutInfo = this._makeLayoutInfo();
  190. var layout = layoutInfo.layout;
  191. axes.each(function (axis) {
  192. var axisExtent = [0, layoutInfo.axisLength];
  193. var idx = axis.inverse ? 1 : 0;
  194. axis.setExtent(axisExtent[idx], axisExtent[1 - idx]);
  195. });
  196. each(dimensions, function (dim, idx) {
  197. var posInfo = (layoutInfo.axisExpandable ? layoutAxisWithExpand : layoutAxisWithoutExpand)(idx, layoutInfo);
  198. var positionTable = {
  199. horizontal: {
  200. x: posInfo.position,
  201. y: layoutInfo.axisLength
  202. },
  203. vertical: {
  204. x: 0,
  205. y: posInfo.position
  206. }
  207. };
  208. var rotationTable = {
  209. horizontal: PI / 2,
  210. vertical: 0
  211. };
  212. var position = [positionTable[layout].x + rect.x, positionTable[layout].y + rect.y];
  213. var rotation = rotationTable[layout];
  214. var transform = matrix.create();
  215. matrix.rotate(transform, transform, rotation);
  216. matrix.translate(transform, transform, position); // TODO
  217. // tick layout info
  218. // TODO
  219. // update dimensions info based on axis order.
  220. this._axesLayout[dim] = {
  221. position: position,
  222. rotation: rotation,
  223. transform: transform,
  224. axisNameAvailableWidth: posInfo.axisNameAvailableWidth,
  225. axisLabelShow: posInfo.axisLabelShow,
  226. nameTruncateMaxWidth: posInfo.nameTruncateMaxWidth,
  227. tickDirection: 1,
  228. labelDirection: 1
  229. };
  230. }, this);
  231. };
  232. /**
  233. * Get axis by dim.
  234. */
  235. Parallel.prototype.getAxis = function (dim) {
  236. return this._axesMap.get(dim);
  237. };
  238. /**
  239. * Convert a dim value of a single item of series data to Point.
  240. */
  241. Parallel.prototype.dataToPoint = function (value, dim) {
  242. return this.axisCoordToPoint(this._axesMap.get(dim).dataToCoord(value), dim);
  243. };
  244. /**
  245. * Travel data for one time, get activeState of each data item.
  246. * @param start the start dataIndex that travel from.
  247. * @param end the next dataIndex of the last dataIndex will be travel.
  248. */
  249. Parallel.prototype.eachActiveState = function (data, callback, start, end) {
  250. start == null && (start = 0);
  251. end == null && (end = data.count());
  252. var axesMap = this._axesMap;
  253. var dimensions = this.dimensions;
  254. var dataDimensions = [];
  255. var axisModels = [];
  256. zrUtil.each(dimensions, function (axisDim) {
  257. dataDimensions.push(data.mapDimension(axisDim));
  258. axisModels.push(axesMap.get(axisDim).model);
  259. });
  260. var hasActiveSet = this.hasAxisBrushed();
  261. for (var dataIndex = start; dataIndex < end; dataIndex++) {
  262. var activeState = void 0;
  263. if (!hasActiveSet) {
  264. activeState = 'normal';
  265. } else {
  266. activeState = 'active';
  267. var values = data.getValues(dataDimensions, dataIndex);
  268. for (var j = 0, lenj = dimensions.length; j < lenj; j++) {
  269. var state = axisModels[j].getActiveState(values[j]);
  270. if (state === 'inactive') {
  271. activeState = 'inactive';
  272. break;
  273. }
  274. }
  275. }
  276. callback(activeState, dataIndex);
  277. }
  278. };
  279. /**
  280. * Whether has any activeSet.
  281. */
  282. Parallel.prototype.hasAxisBrushed = function () {
  283. var dimensions = this.dimensions;
  284. var axesMap = this._axesMap;
  285. var hasActiveSet = false;
  286. for (var j = 0, lenj = dimensions.length; j < lenj; j++) {
  287. if (axesMap.get(dimensions[j]).model.getActiveState() !== 'normal') {
  288. hasActiveSet = true;
  289. }
  290. }
  291. return hasActiveSet;
  292. };
  293. /**
  294. * Convert coords of each axis to Point.
  295. * Return point. For example: [10, 20]
  296. */
  297. Parallel.prototype.axisCoordToPoint = function (coord, dim) {
  298. var axisLayout = this._axesLayout[dim];
  299. return graphic.applyTransform([coord, 0], axisLayout.transform);
  300. };
  301. /**
  302. * Get axis layout.
  303. */
  304. Parallel.prototype.getAxisLayout = function (dim) {
  305. return zrUtil.clone(this._axesLayout[dim]);
  306. };
  307. /**
  308. * @return {Object} {axisExpandWindow, delta, behavior: 'jump' | 'slide' | 'none'}.
  309. */
  310. Parallel.prototype.getSlidedAxisExpandWindow = function (point) {
  311. var layoutInfo = this._makeLayoutInfo();
  312. var pixelDimIndex = layoutInfo.pixelDimIndex;
  313. var axisExpandWindow = layoutInfo.axisExpandWindow.slice();
  314. var winSize = axisExpandWindow[1] - axisExpandWindow[0];
  315. var extent = [0, layoutInfo.axisExpandWidth * (layoutInfo.axisCount - 1)]; // Out of the area of coordinate system.
  316. if (!this.containPoint(point)) {
  317. return {
  318. behavior: 'none',
  319. axisExpandWindow: axisExpandWindow
  320. };
  321. } // Convert the point from global to expand coordinates.
  322. var pointCoord = point[pixelDimIndex] - layoutInfo.layoutBase - layoutInfo.axisExpandWindow0Pos; // For dragging operation convenience, the window should not be
  323. // slided when mouse is the center area of the window.
  324. var delta;
  325. var behavior = 'slide';
  326. var axisCollapseWidth = layoutInfo.axisCollapseWidth;
  327. var triggerArea = this._model.get('axisExpandSlideTriggerArea'); // But consider touch device, jump is necessary.
  328. var useJump = triggerArea[0] != null;
  329. if (axisCollapseWidth) {
  330. if (useJump && axisCollapseWidth && pointCoord < winSize * triggerArea[0]) {
  331. behavior = 'jump';
  332. delta = pointCoord - winSize * triggerArea[2];
  333. } else if (useJump && axisCollapseWidth && pointCoord > winSize * (1 - triggerArea[0])) {
  334. behavior = 'jump';
  335. delta = pointCoord - winSize * (1 - triggerArea[2]);
  336. } else {
  337. (delta = pointCoord - winSize * triggerArea[1]) >= 0 && (delta = pointCoord - winSize * (1 - triggerArea[1])) <= 0 && (delta = 0);
  338. }
  339. delta *= layoutInfo.axisExpandWidth / axisCollapseWidth;
  340. delta ? sliderMove(delta, axisExpandWindow, extent, 'all') // Avoid nonsense triger on mousemove.
  341. : behavior = 'none';
  342. } // When screen is too narrow, make it visible and slidable, although it is hard to interact.
  343. else {
  344. var winSize2 = axisExpandWindow[1] - axisExpandWindow[0];
  345. var pos = extent[1] * pointCoord / winSize2;
  346. axisExpandWindow = [mathMax(0, pos - winSize2 / 2)];
  347. axisExpandWindow[1] = mathMin(extent[1], axisExpandWindow[0] + winSize2);
  348. axisExpandWindow[0] = axisExpandWindow[1] - winSize2;
  349. }
  350. return {
  351. axisExpandWindow: axisExpandWindow,
  352. behavior: behavior
  353. };
  354. };
  355. return Parallel;
  356. }();
  357. function restrict(len, extent) {
  358. return mathMin(mathMax(len, extent[0]), extent[1]);
  359. }
  360. function layoutAxisWithoutExpand(axisIndex, layoutInfo) {
  361. var step = layoutInfo.layoutLength / (layoutInfo.axisCount - 1);
  362. return {
  363. position: step * axisIndex,
  364. axisNameAvailableWidth: step,
  365. axisLabelShow: true
  366. };
  367. }
  368. function layoutAxisWithExpand(axisIndex, layoutInfo) {
  369. var layoutLength = layoutInfo.layoutLength;
  370. var axisExpandWidth = layoutInfo.axisExpandWidth;
  371. var axisCount = layoutInfo.axisCount;
  372. var axisCollapseWidth = layoutInfo.axisCollapseWidth;
  373. var winInnerIndices = layoutInfo.winInnerIndices;
  374. var position;
  375. var axisNameAvailableWidth = axisCollapseWidth;
  376. var axisLabelShow = false;
  377. var nameTruncateMaxWidth;
  378. if (axisIndex < winInnerIndices[0]) {
  379. position = axisIndex * axisCollapseWidth;
  380. nameTruncateMaxWidth = axisCollapseWidth;
  381. } else if (axisIndex <= winInnerIndices[1]) {
  382. position = layoutInfo.axisExpandWindow0Pos + axisIndex * axisExpandWidth - layoutInfo.axisExpandWindow[0];
  383. axisNameAvailableWidth = axisExpandWidth;
  384. axisLabelShow = true;
  385. } else {
  386. position = layoutLength - (axisCount - 1 - axisIndex) * axisCollapseWidth;
  387. nameTruncateMaxWidth = axisCollapseWidth;
  388. }
  389. return {
  390. position: position,
  391. axisNameAvailableWidth: axisNameAvailableWidth,
  392. axisLabelShow: axisLabelShow,
  393. nameTruncateMaxWidth: nameTruncateMaxWidth
  394. };
  395. }
  396. export default Parallel;