matrixCoordHelper.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  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. import { eqNaN, isArray, isNumber } from 'zrender/lib/core/util.js';
  41. import { WH, XY } from '../../util/graphic.js';
  42. import { mathMax, mathMin } from '../../util/number.js';
  43. export var MatrixCellLayoutInfoType = {
  44. level: 1,
  45. leaf: 2,
  46. nonLeaf: 3
  47. };
  48. /**
  49. * @public Public to users in `chart.convertFromPixel`.
  50. */
  51. export var MatrixClampOption = {
  52. // No clamp, be falsy, equals to null/undefined. It means if the input part is
  53. // null/undefined/NaN/outOfBoundary, the result part is NaN, rather than clamp to
  54. // the boundary of the matrix.
  55. none: 0,
  56. // Clamp, where null/undefined/NaN/outOfBoundary can be used to cover the entire row/column.
  57. all: 1,
  58. body: 2,
  59. corner: 3
  60. };
  61. /**
  62. * For the x direction,
  63. * - find dimension cell from `xMatrixDim`,
  64. * - If `xDimCell` or `yDimCell` is not a leaf, return the non-leaf cell itself.
  65. * - otherwise find level from `yMatrixDim`.
  66. * - otherwise return `NullUndefined`.
  67. *
  68. * For the y direction, it's the opposite.
  69. */
  70. export function coordDataToAllCellLevelLayout(coordValue, dims, thisDimIdx // 0 | 1
  71. ) {
  72. // Find in body.
  73. var result = dims[XY[thisDimIdx]].getCell(coordValue);
  74. // Find in corner or dimension area.
  75. if (!result && isNumber(coordValue) && coordValue < 0) {
  76. result = dims[XY[1 - thisDimIdx]].getUnitLayoutInfo(thisDimIdx, Math.round(coordValue));
  77. }
  78. return result;
  79. }
  80. export function resetXYLocatorRange(out) {
  81. var rg = out || [];
  82. rg[0] = rg[0] || [];
  83. rg[1] = rg[1] || [];
  84. rg[0][0] = rg[0][1] = rg[1][0] = rg[1][1] = NaN;
  85. return rg;
  86. }
  87. /**
  88. * If illegal or out of boundary, set NaN to `locOut`. See `isXYLocatorRangeInvalidOnDim`.
  89. * x dimension and y dimension are calculated separately.
  90. */
  91. export function parseCoordRangeOption(locOut,
  92. // If illegal input or can not find any target, save reason to it.
  93. // Do nothing if `NullUndefined`.
  94. reasonOut, data, dims, clamp) {
  95. // x and y are supported to be handled separately - if one dimension is invalid
  96. // (may be users do not need that), the other one should also be calculated.
  97. parseCoordRangeOptionOnOneDim(locOut[0], reasonOut, clamp, data, dims, 0);
  98. parseCoordRangeOptionOnOneDim(locOut[1], reasonOut, clamp, data, dims, 1);
  99. }
  100. function parseCoordRangeOptionOnOneDim(locDimOut, reasonOut, clamp, data, dims, dimIdx) {
  101. locDimOut[0] = Infinity;
  102. locDimOut[1] = -Infinity;
  103. var dataOnDim = data[dimIdx];
  104. var coordValArr = isArray(dataOnDim) ? dataOnDim : [dataOnDim];
  105. var len = coordValArr.length;
  106. var hasClamp = !!clamp;
  107. if (len >= 1) {
  108. parseCoordRangeOptionOnOneDimOnePart(locDimOut, reasonOut, coordValArr, hasClamp, dims, dimIdx, 0);
  109. if (len > 1) {
  110. // Users may intuitively input the coords like `[[x1, x2, x3], ...]`;
  111. // consider the range as `[x1, x3]` in this case.
  112. parseCoordRangeOptionOnOneDimOnePart(locDimOut, reasonOut, coordValArr, hasClamp, dims, dimIdx, len - 1);
  113. }
  114. } else {
  115. if (process.env.NODE_ENV !== 'production') {
  116. if (reasonOut) {
  117. reasonOut.push('Should be like [["x1", "x2"], ["y1", "y2"]], or ["x1", "y1"], rather than empty.');
  118. }
  119. }
  120. locDimOut[0] = locDimOut[1] = NaN;
  121. }
  122. if (hasClamp) {
  123. // null/undefined/NaN or illegal data represents the entire row/column;
  124. // Cover the entire locator regardless of body or corner, and confine it later.
  125. var locLowerBound = -dims[XY[1 - dimIdx]].getLocatorCount(dimIdx);
  126. var locUpperBound = dims[XY[dimIdx]].getLocatorCount(dimIdx) - 1;
  127. if (clamp === MatrixClampOption.body) {
  128. locLowerBound = mathMax(0, locLowerBound);
  129. } else if (clamp === MatrixClampOption.corner) {
  130. locUpperBound = mathMin(-1, locUpperBound);
  131. }
  132. if (locUpperBound < locLowerBound) {
  133. // Also considered that both x and y has no cell.
  134. locLowerBound = locUpperBound = NaN;
  135. }
  136. if (eqNaN(locDimOut[0])) {
  137. locDimOut[0] = locLowerBound;
  138. }
  139. if (eqNaN(locDimOut[1])) {
  140. locDimOut[1] = locUpperBound;
  141. }
  142. locDimOut[0] = mathMax(mathMin(locDimOut[0], locUpperBound), locLowerBound);
  143. locDimOut[1] = mathMax(mathMin(locDimOut[1], locUpperBound), locLowerBound);
  144. }
  145. }
  146. // The return val must be finite or NaN.
  147. function parseCoordRangeOptionOnOneDimOnePart(locDimOut, reasonOut, coordValArr, hasClamp, dims, dimIdx, partIdx) {
  148. var layout = coordDataToAllCellLevelLayout(coordValArr[partIdx], dims, dimIdx);
  149. if (!layout) {
  150. if (process.env.NODE_ENV !== 'production') {
  151. if (!hasClamp && reasonOut) {
  152. reasonOut.push("Can not find cell by coord[" + dimIdx + "][" + partIdx + "].");
  153. }
  154. }
  155. locDimOut[0] = locDimOut[1] = NaN;
  156. return;
  157. }
  158. var locatorA = layout.id[XY[dimIdx]];
  159. var locatorB = locatorA;
  160. var dimCell = cellLayoutInfoToDimCell(layout);
  161. if (dimCell) {
  162. // Handle non-leaf
  163. locatorB += dimCell.span[XY[dimIdx]] - 1;
  164. }
  165. locDimOut[0] = mathMin(locDimOut[0], locatorA, locatorB);
  166. locDimOut[1] = mathMax(locDimOut[1], locatorA, locatorB);
  167. }
  168. /**
  169. * @param locatorRange Must be the return of `parseCoordRangeOption`,
  170. * where if not NaN, it must be a valid locator.
  171. */
  172. export function isXYLocatorRangeInvalidOnDim(locatorRange, dimIdx) {
  173. return eqNaN(locatorRange[dimIdx][0]) || eqNaN(locatorRange[dimIdx][1]);
  174. }
  175. // `locatorRange` will be expanded (modified) if an intersection is encountered.
  176. export function resolveXYLocatorRangeByCellMerge(inOutLocatorRange,
  177. // Item indices coorespond to mergeDefList (len: mergeDefListTravelLen).
  178. // Indicating whether each item has be merged into the `locatorRange`
  179. outMergedMarkList, mergeDefList, mergeDefListTravelLen) {
  180. outMergedMarkList = outMergedMarkList || _tmpOutMergedMarkList;
  181. for (var idx = 0; idx < mergeDefListTravelLen; idx++) {
  182. outMergedMarkList[idx] = false;
  183. }
  184. // In most case, cell merging definition list length is smaller than the range extent,
  185. // therefore, to detection intersection, travelling cell merging definition list is probably
  186. // performant than traveling the four edges of the rect formed by the locator range.
  187. while (true) {
  188. var expanded = false;
  189. for (var idx = 0; idx < mergeDefListTravelLen; idx++) {
  190. var mergeDef = mergeDefList[idx];
  191. if (!outMergedMarkList[idx] && mergeDef.cellMergeOwner && expandXYLocatorRangeIfIntersect(inOutLocatorRange, mergeDef.locatorRange)) {
  192. outMergedMarkList[idx] = true;
  193. expanded = true;
  194. }
  195. }
  196. if (!expanded) {
  197. break;
  198. }
  199. }
  200. }
  201. var _tmpOutMergedMarkList = [];
  202. // Return whether intersect.
  203. // `thisLocRange` will be expanded (modified) if an intersection is encountered.
  204. function expandXYLocatorRangeIfIntersect(thisLocRange, otherLocRange) {
  205. if (!locatorRangeIntersectOneDim(thisLocRange[0], otherLocRange[0]) || !locatorRangeIntersectOneDim(thisLocRange[1], otherLocRange[1])) {
  206. return false;
  207. }
  208. thisLocRange[0][0] = mathMin(thisLocRange[0][0], otherLocRange[0][0]);
  209. thisLocRange[0][1] = mathMax(thisLocRange[0][1], otherLocRange[0][1]);
  210. thisLocRange[1][0] = mathMin(thisLocRange[1][0], otherLocRange[1][0]);
  211. thisLocRange[1][1] = mathMax(thisLocRange[1][1], otherLocRange[1][1]);
  212. return true;
  213. }
  214. // Notice: If containing NaN, not intersect.
  215. function locatorRangeIntersectOneDim(locRange1OneDim, locRange2OneDim) {
  216. return locRange1OneDim[1] >= locRange2OneDim[0] && locRange1OneDim[0] <= locRange2OneDim[1];
  217. }
  218. export function fillIdSpanFromLocatorRange(owner, locatorRange) {
  219. owner.id.set(locatorRange[0][0], locatorRange[1][0]);
  220. owner.span.set(locatorRange[0][1] - owner.id.x + 1, locatorRange[1][1] - owner.id.y + 1);
  221. }
  222. export function cloneXYLocatorRange(target, source) {
  223. target[0][0] = source[0][0];
  224. target[0][1] = source[0][1];
  225. target[1][0] = source[1][0];
  226. target[1][1] = source[1][1];
  227. }
  228. /**
  229. * If illegal, the corresponding x/y/width/height is set to `NaN`.
  230. * `x/width` or `y/height` is supported to be calculated separately,
  231. * i.e., one side are NaN, the other side are normal.
  232. * @param oneDimOut only write to `x/width` or `y/height`, depending on `dimIdx`.
  233. */
  234. export function xyLocatorRangeToRectOneDim(oneDimOut, locRange, dims, dimIdx) {
  235. var layoutMin = coordDataToAllCellLevelLayout(locRange[dimIdx][0], dims, dimIdx);
  236. var layoutMax = coordDataToAllCellLevelLayout(locRange[dimIdx][1], dims, dimIdx);
  237. oneDimOut[XY[dimIdx]] = oneDimOut[WH[dimIdx]] = NaN;
  238. if (layoutMin && layoutMax) {
  239. oneDimOut[XY[dimIdx]] = layoutMin.xy;
  240. oneDimOut[WH[dimIdx]] = layoutMax.xy + layoutMax.wh - layoutMin.xy;
  241. }
  242. }
  243. // No need currently, since `span` is not allowed to be defined directly by users.
  244. // /**
  245. // * If either span x or y is valid and > 1, return parsed span, otherwise return `NullUndefined`.
  246. // */
  247. // export function parseSpanOption(
  248. // spanOptionHost: MatrixCellSpanOptionHost,
  249. // dimCellPair: MatrixCellLayoutInfo[]
  250. // ): Point | NullUndefined {
  251. // const spanX = parseSpanOnDim(spanOptionHost.spanX, dimCellPair[0], 0);
  252. // const spanY = parseSpanOnDim(spanOptionHost.spanY, dimCellPair[1], 1);
  253. // if (!eqNaN(spanX) || !eqNaN(spanY)) {
  254. // return new Point(spanX || 1, spanY || 1);
  255. // }
  256. // function parseSpanOnDim(spanOption: unknown, dimCell: MatrixCellLayoutInfo, dimIdx: number): number {
  257. // if (!isNumber(spanOption)) {
  258. // return NaN;
  259. // }
  260. // // Ensure positive integer (not NaN) to avoid dead loop.
  261. // const span = mathMax(1, Math.round(spanOption || 1)) || 1;
  262. // // Clamp, and consider may also be specified as `Infinity` to span the entire col/row.
  263. // return mathMin(span, mathMax(1, dimCell.dim.getLocatorCount(dimIdx) - dimCell.id[XY[dimIdx]]));
  264. // }
  265. // }
  266. /**
  267. * @usage To get/set on dimension, use:
  268. * `xyVal[XY[dim]] = val;` // set on this dimension.
  269. * `xyVal[XY[1 - dim]] = val;` // set on the perpendicular dimension.
  270. */
  271. export function setDimXYValue(out, dimIdx,
  272. // 0 | 1
  273. valueOnThisDim, valueOnOtherDim) {
  274. out[XY[dimIdx]] = valueOnThisDim;
  275. out[XY[1 - dimIdx]] = valueOnOtherDim;
  276. return out;
  277. }
  278. /**
  279. * Return NullUndefined if not dimension cell.
  280. */
  281. function cellLayoutInfoToDimCell(cellLayoutInfo) {
  282. return cellLayoutInfo && (cellLayoutInfo.type === MatrixCellLayoutInfoType.leaf || cellLayoutInfo.type === MatrixCellLayoutInfoType.nonLeaf) ? cellLayoutInfo : null;
  283. }
  284. export function createNaNRectLike() {
  285. return {
  286. x: NaN,
  287. y: NaN,
  288. width: NaN,
  289. height: NaN
  290. };
  291. }