/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /** * AUTO-GENERATED FILE. DO NOT MODIFY. */ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import WeakMap from 'zrender/lib/core/WeakMap.js'; import LRU from 'zrender/lib/core/LRU.js'; import { defaults, map, isArray, isString, isNumber } from 'zrender/lib/core/util.js'; import { getLeastCommonMultiple } from './number.js'; import { createSymbol } from './symbol.js'; import { brushSingle } from 'zrender/lib/canvas/graphic.js'; import { platformApi } from 'zrender/lib/core/platform.js'; var decalMap = new WeakMap(); var decalCache = new LRU(100); var decalKeys = ['symbol', 'symbolSize', 'symbolKeepAspect', 'color', 'backgroundColor', 'dashArrayX', 'dashArrayY', 'maxTileWidth', 'maxTileHeight']; /** * Create or update pattern image from decal options * * @param {InnerDecalObject | 'none'} decalObject decal options, 'none' if no decal * @return {Pattern} pattern with generated image, null if no decal */ export function createOrUpdatePatternFromDecal(decalObject, api) { if (decalObject === 'none') { return null; } var dpr = api.getDevicePixelRatio(); var zr = api.getZr(); var isSVG = zr.painter.type === 'svg'; if (decalObject.dirty) { decalMap["delete"](decalObject); } var oldPattern = decalMap.get(decalObject); if (oldPattern) { return oldPattern; } var decalOpt = defaults(decalObject, { symbol: 'rect', symbolSize: 1, symbolKeepAspect: true, color: 'rgba(0, 0, 0, 0.2)', backgroundColor: null, dashArrayX: 5, dashArrayY: 5, rotation: 0, maxTileWidth: 512, maxTileHeight: 512 }); if (decalOpt.backgroundColor === 'none') { decalOpt.backgroundColor = null; } var pattern = { repeat: 'repeat' }; setPatternnSource(pattern); pattern.rotation = decalOpt.rotation; pattern.scaleX = pattern.scaleY = isSVG ? 1 : 1 / dpr; decalMap.set(decalObject, pattern); decalObject.dirty = false; return pattern; function setPatternnSource(pattern) { var keys = [dpr]; var isValidKey = true; for (var i = 0; i < decalKeys.length; ++i) { var value = decalOpt[decalKeys[i]]; if (value != null && !isArray(value) && !isString(value) && !isNumber(value) && typeof value !== 'boolean') { isValidKey = false; break; } keys.push(value); } var cacheKey; if (isValidKey) { cacheKey = keys.join(',') + (isSVG ? '-svg' : ''); var cache = decalCache.get(cacheKey); if (cache) { isSVG ? pattern.svgElement = cache : pattern.image = cache; } } var dashArrayX = normalizeDashArrayX(decalOpt.dashArrayX); var dashArrayY = normalizeDashArrayY(decalOpt.dashArrayY); var symbolArray = normalizeSymbolArray(decalOpt.symbol); var lineBlockLengthsX = getLineBlockLengthX(dashArrayX); var lineBlockLengthY = getLineBlockLengthY(dashArrayY); var canvas = !isSVG && platformApi.createCanvas(); var svgRoot = isSVG && { tag: 'g', attrs: {}, key: 'dcl', children: [] }; var pSize = getPatternSize(); var ctx; if (canvas) { canvas.width = pSize.width * dpr; canvas.height = pSize.height * dpr; ctx = canvas.getContext('2d'); } brushDecal(); if (isValidKey) { decalCache.put(cacheKey, canvas || svgRoot); } pattern.image = canvas; pattern.svgElement = svgRoot; pattern.svgWidth = pSize.width; pattern.svgHeight = pSize.height; /** * Get minimum length that can make a repeatable pattern. * * @return {Object} pattern width and height */ function getPatternSize() { /** * For example, if dash is [[3, 2], [2, 1]] for X, it looks like * |--- --- --- --- --- ... * |-- -- -- -- -- -- -- -- ... * |--- --- --- --- --- ... * |-- -- -- -- -- -- -- -- ... * So the minimum length of X is 15, * which is the least common multiple of `3 + 2` and `2 + 1` * |--- --- --- |--- --- ... * |-- -- -- -- -- |-- -- -- ... */ var width = 1; for (var i = 0, xlen = lineBlockLengthsX.length; i < xlen; ++i) { width = getLeastCommonMultiple(width, lineBlockLengthsX[i]); } var symbolRepeats = 1; for (var i = 0, xlen = symbolArray.length; i < xlen; ++i) { symbolRepeats = getLeastCommonMultiple(symbolRepeats, symbolArray[i].length); } width *= symbolRepeats; var height = lineBlockLengthY * lineBlockLengthsX.length * symbolArray.length; if (process.env.NODE_ENV !== 'production') { var warn = function (attrName) { /* eslint-disable-next-line */ console.warn("Calculated decal size is greater than " + attrName + " due to decal option settings so " + attrName + " is used for the decal size. Please consider changing the decal option to make a smaller decal or set " + attrName + " to be larger to avoid incontinuity."); }; if (width > decalOpt.maxTileWidth) { warn('maxTileWidth'); } if (height > decalOpt.maxTileHeight) { warn('maxTileHeight'); } } return { width: Math.max(1, Math.min(width, decalOpt.maxTileWidth)), height: Math.max(1, Math.min(height, decalOpt.maxTileHeight)) }; } function brushDecal() { if (ctx) { ctx.clearRect(0, 0, canvas.width, canvas.height); if (decalOpt.backgroundColor) { ctx.fillStyle = decalOpt.backgroundColor; ctx.fillRect(0, 0, canvas.width, canvas.height); } } var ySum = 0; for (var i = 0; i < dashArrayY.length; ++i) { ySum += dashArrayY[i]; } if (ySum <= 0) { // dashArrayY is 0, draw nothing return; } var y = -lineBlockLengthY; var yId = 0; var yIdTotal = 0; var xId0 = 0; while (y < pSize.height) { if (yId % 2 === 0) { var symbolYId = yIdTotal / 2 % symbolArray.length; var x = 0; var xId1 = 0; var xId1Total = 0; while (x < pSize.width * 2) { var xSum = 0; for (var i = 0; i < dashArrayX[xId0].length; ++i) { xSum += dashArrayX[xId0][i]; } if (xSum <= 0) { // Skip empty line break; } // E.g., [15, 5, 20, 5] draws only for 15 and 20 if (xId1 % 2 === 0) { var size = (1 - decalOpt.symbolSize) * 0.5; var left = x + dashArrayX[xId0][xId1] * size; var top_1 = y + dashArrayY[yId] * size; var width = dashArrayX[xId0][xId1] * decalOpt.symbolSize; var height = dashArrayY[yId] * decalOpt.symbolSize; var symbolXId = xId1Total / 2 % symbolArray[symbolYId].length; brushSymbol(left, top_1, width, height, symbolArray[symbolYId][symbolXId]); } x += dashArrayX[xId0][xId1]; ++xId1Total; ++xId1; if (xId1 === dashArrayX[xId0].length) { xId1 = 0; } } ++xId0; if (xId0 === dashArrayX.length) { xId0 = 0; } } y += dashArrayY[yId]; ++yIdTotal; ++yId; if (yId === dashArrayY.length) { yId = 0; } } function brushSymbol(x, y, width, height, symbolType) { var scale = isSVG ? 1 : dpr; var symbol = createSymbol(symbolType, x * scale, y * scale, width * scale, height * scale, decalOpt.color, decalOpt.symbolKeepAspect); if (isSVG) { var symbolVNode = zr.painter.renderOneToVNode(symbol); if (symbolVNode) { svgRoot.children.push(symbolVNode); } } else { // Paint to canvas for all other renderers. brushSingle(ctx, symbol); } } } } } /** * Convert symbol array into normalized array * * @param {string | (string | string[])[]} symbol symbol input * @return {string[][]} normolized symbol array */ function normalizeSymbolArray(symbol) { if (!symbol || symbol.length === 0) { return [['rect']]; } if (isString(symbol)) { return [[symbol]]; } var isAllString = true; for (var i = 0; i < symbol.length; ++i) { if (!isString(symbol[i])) { isAllString = false; break; } } if (isAllString) { return normalizeSymbolArray([symbol]); } var result = []; for (var i = 0; i < symbol.length; ++i) { if (isString(symbol[i])) { result.push([symbol[i]]); } else { result.push(symbol[i]); } } return result; } /** * Convert dash input into dashArray * * @param {DecalDashArrayX} dash dash input * @return {number[][]} normolized dash array */ function normalizeDashArrayX(dash) { if (!dash || dash.length === 0) { return [[0, 0]]; } if (isNumber(dash)) { var dashValue = Math.ceil(dash); return [[dashValue, dashValue]]; } /** * [20, 5] should be normalized into [[20, 5]], * while [20, [5, 10]] should be normalized into [[20, 20], [5, 10]] */ var isAllNumber = true; for (var i = 0; i < dash.length; ++i) { if (!isNumber(dash[i])) { isAllNumber = false; break; } } if (isAllNumber) { return normalizeDashArrayX([dash]); } var result = []; for (var i = 0; i < dash.length; ++i) { if (isNumber(dash[i])) { var dashValue = Math.ceil(dash[i]); result.push([dashValue, dashValue]); } else { var dashValue = map(dash[i], function (n) { return Math.ceil(n); }); if (dashValue.length % 2 === 1) { // [4, 2, 1] means |---- - -- |---- - -- | // so normalize it to be [4, 2, 1, 4, 2, 1] result.push(dashValue.concat(dashValue)); } else { result.push(dashValue); } } } return result; } /** * Convert dash input into dashArray * * @param {DecalDashArrayY} dash dash input * @return {number[]} normolized dash array */ function normalizeDashArrayY(dash) { if (!dash || typeof dash === 'object' && dash.length === 0) { return [0, 0]; } if (isNumber(dash)) { var dashValue_1 = Math.ceil(dash); return [dashValue_1, dashValue_1]; } var dashValue = map(dash, function (n) { return Math.ceil(n); }); return dash.length % 2 ? dashValue.concat(dashValue) : dashValue; } /** * Get block length of each line. A block is the length of dash line and space. * For example, a line with [4, 1] has a dash line of 4 and a space of 1 after * that, so the block length of this line is 5. * * @param {number[][]} dash dash array of X or Y * @return {number[]} block length of each line */ function getLineBlockLengthX(dash) { return map(dash, function (line) { return getLineBlockLengthY(line); }); } function getLineBlockLengthY(dash) { var blockLength = 0; for (var i = 0; i < dash.length; ++i) { blockLength += dash[i]; } if (dash.length % 2 === 1) { // [4, 2, 1] means |---- - -- |---- - -- | // So total length is (4 + 2 + 1) * 2 return blockLength * 2; } return blockLength; }