UNPKG

highcharts

Version:
535 lines (534 loc) 13.2 kB
/* * * * (c) 2010-2026 Highsoft AS * Author: Torstein Hønsi * * A commercial license may be required depending on use. * See www.highcharts.com/license * * * */ 'use strict'; import { defined, isNumber, pick } from '../../../Shared/Utilities.js'; /* * * * Functions * * */ /* eslint-disable require-jsdoc, valid-jsdoc */ /** * Arc symbol path. * * @param {number} cx * Center X * @param {number} cy * Center Y * @param {number} w * Width * @param {number} h * Height * @param {Highcharts.SymbolOptions} [options] * Options * @return {Highcharts.SVGPathArray} * Path */ function arc(cx, cy, w, h, options) { const arc = []; if (options) { let start = options.start || 0, end = options.end || 0; const rx = pick(options.r, w), ry = pick(options.r, h || w), // Subtract a small number to prevent cos and sin of start and end // from becoming equal on 360 arcs (#1561). See "Arc proximity" // tests at samples/unit-tests/svgrenderer/symbol/demo.js proximity = 0.0001, fullCircle = (Math.abs(end - start - 2 * Math.PI) < proximity); if (fullCircle) { start = Math.PI / 2; end = Math.PI * 2.5 - proximity; } const innerRadius = options.innerR, open = pick(options.open, fullCircle), cosStart = fullCircle ? 0 : Math.cos(start), sinStart = fullCircle ? 1 : Math.sin(start), cosEnd = fullCircle ? 0 : Math.cos(end), sinEnd = fullCircle ? 1 : Math.sin(end), // Proximity takes care of rounding errors around PI (#6971) longArc = pick(options.longArc, end - start - Math.PI < proximity ? 0 : 1); let arcSegment = [ 'A', // ArcTo rx, // X radius ry, // Y radius 0, // Slanting longArc, // Long or short arc pick(options.clockwise, 1), // Clockwise // Use a static pixel offset for full circle (#21701) cx + (fullCircle ? 0.001 : rx * cosEnd), cy + ry * sinEnd ]; arcSegment.params = { start, end, cx, cy }; // Memo for border radius arc.push([ 'M', cx + rx * cosStart, cy + ry * sinStart ], arcSegment); if (defined(innerRadius)) { arcSegment = [ 'A', // ArcTo innerRadius, // X radius innerRadius, // Y radius 0, // Slanting longArc, // Long or short arc // Clockwise - opposite to the outer arc clockwise defined(options.clockwise) ? 1 - options.clockwise : 0, cx + (fullCircle ? -0.001 : innerRadius * cosStart), cy + innerRadius * sinStart ]; // Memo for border radius arcSegment.params = { start: end, end: start, cx, cy }; arc.push(open ? [ 'M', cx + innerRadius * cosEnd, cy + innerRadius * sinEnd ] : [ 'L', cx + innerRadius * cosEnd, cy + innerRadius * sinEnd ], arcSegment); } if (!open) { arc.push(['Z']); } } return arc; } /** * Callout shape used for default tooltips. * * @param {number} x * Center X * @param {number} y * Center Y * @param {number} w * Width * @param {number} h * Height * @param {Highcharts.SymbolOptions} [options] * Options * @return {Highcharts.SVGPathArray} * Path */ function callout(x, y, w, h, options) { const arrowLength = 6, halfDistance = 6, r = Math.min((options?.r) || 0, w, h), safeDistance = r + halfDistance, anchorX = options?.anchorX, anchorY = options?.anchorY || 0; const path = roundedRect(x, y, w, h, { r }); if (!isNumber(anchorX)) { return path; } // Do not render a connector, if anchor starts inside the label if (anchorX < w && anchorX > 0 && anchorY < h && anchorY > 0) { return path; } // Anchor on right side if (x + anchorX > w - safeDistance) { // Chevron if (anchorY > y + safeDistance && anchorY < y + h - safeDistance) { path.splice(3, 1, ['L', x + w, anchorY - halfDistance], ['L', x + w + arrowLength, anchorY], ['L', x + w, anchorY + halfDistance], ['L', x + w, y + h - r]); // Simple connector } else { if (anchorX < w) { // Corner connector const isTopCorner = anchorY < y + safeDistance, cornerY = isTopCorner ? y : y + h, sliceStart = isTopCorner ? 2 : 5; path.splice(sliceStart, 0, ['L', anchorX, anchorY], ['L', x + w - r, cornerY]); } else { // Side connector path.splice(3, 1, ['L', x + w, h / 2], ['L', anchorX, anchorY], ['L', x + w, h / 2], ['L', x + w, y + h - r]); } } // Anchor on left side } else if (x + anchorX < safeDistance) { // Chevron if (anchorY > y + safeDistance && anchorY < y + h - safeDistance) { path.splice(7, 1, ['L', x, anchorY + halfDistance], ['L', x - arrowLength, anchorY], ['L', x, anchorY - halfDistance], ['L', x, y + r]); // Simple connector } else { if (anchorX > 0) { // Corner connector const isTopCorner = anchorY < y + safeDistance, cornerY = isTopCorner ? y : y + h, sliceStart = isTopCorner ? 1 : 6; path.splice(sliceStart, 0, ['L', anchorX, anchorY], ['L', x + r, cornerY]); } else { // Side connector path.splice(7, 1, ['L', x, h / 2], ['L', anchorX, anchorY], ['L', x, h / 2], ['L', x, y + r]); } } } else if ( // Replace bottom anchorY > h && anchorX < w - safeDistance) { path.splice(5, 1, ['L', anchorX + halfDistance, y + h], ['L', anchorX, y + h + arrowLength], ['L', anchorX - halfDistance, y + h], ['L', x + r, y + h]); } else if ( // Replace top anchorY < 0 && anchorX > safeDistance) { path.splice(1, 1, ['L', anchorX - halfDistance, y], ['L', anchorX, y - arrowLength], ['L', anchorX + halfDistance, y], ['L', w - r, y]); } return path; } /** * Circle symbol path. * * @param {number} x * X coordinate * @param {number} y * Y coordinate * @param {number} w * Width * @param {number} h * Height * @return {Highcharts.SVGPathArray} * Path */ function circle(x, y, w, h) { // Return a full arc return arc(x + w / 2, y + h / 2, w / 2, h / 2, { start: Math.PI * 0.5, end: Math.PI * 2.5, open: false }); } /** * Diamond symbol path. * * @param {number} x * X coordinate * @param {number} y * Y coordinate * @param {number} w * Width * @param {number} h * Height * @return {Highcharts.SVGPathArray} * Path */ function diamond(x, y, w, h) { return [ ['M', x + w / 2, y], ['L', x + w, y + h / 2], ['L', x + w / 2, y + h], ['L', x, y + h / 2], ['Z'] ]; } // #15291 /** * Rect symbol path. * * @param {number} x * X coordinate * @param {number} y * Y coordinate * @param {number} w * Width * @param {number} h * Height * @param {Highcharts.SymbolOptions} [options] * Options * @return {Highcharts.SVGPathArray} * Path */ function rect(x, y, w, h, options) { if (options?.r) { return roundedRect(x, y, w, h, options); } return [ ['M', x, y], ['L', x + w, y], ['L', x + w, y + h], ['L', x, y + h], ['Z'] ]; } /** * Rounded rectangle symbol path. * * @param {number} x * X coordinate * @param {number} y * Y coordinate * @param {number} w * Width * @param {number} h * Height * @param {Highcharts.SymbolOptions} [options] * Options * @return {Highcharts.SVGPathArray} * Path */ function roundedRect(x, y, w, h, options) { const r = options?.r || 0; return [ ['M', x + r, y], ['L', x + w - r, y], // Top side ['A', r, r, 0, 0, 1, x + w, y + r], // Top-right corner ['L', x + w, y + h - r], // Right side ['A', r, r, 0, 0, 1, x + w - r, y + h], // Bottom-right corner ['L', x + r, y + h], // Bottom side ['A', r, r, 0, 0, 1, x, y + h - r], // Bottom-left corner ['L', x, y + r], // Left side ['A', r, r, 0, 0, 1, x + r, y], ['Z'] // Top-left corner ]; } /** * Triangle symbol path. * * @param {number} x * X coordinate * @param {number} y * Y coordinate * @param {number} w * Width * @param {number} h * Height * @return {Highcharts.SVGPathArray} * Path */ function triangle(x, y, w, h) { return [ ['M', x + w / 2, y], ['L', x + w, y + h], ['L', x, y + h], ['Z'] ]; } /** * Inverted triangle symbol path. * * @param {number} x * X coordinate * @param {number} y * Y coordinate * @param {number} w * Width * @param {number} h * Height * @return {Highcharts.SVGPathArray} * Path */ function triangleDown(x, y, w, h) { return [ ['M', x, y], ['L', x + w, y], ['L', x + w / 2, y + h], ['Z'] ]; } const Symbols = { /** * Arc symbol path. * * @param {number} cx * Center X * @param {number} cy * Center Y * @param {number} w * Width * @param {number} h * Height * @param {Highcharts.SymbolOptions} [options] * Options * @return {Highcharts.SVGPathArray} * Path */ arc, /** * Callout shape used for default tooltips. * * @param {number} cx * Center X * @param {number} cy * Center Y * @param {number} w * Width * @param {number} h * Height * @param {Highcharts.SymbolOptions} [options] * Options * @return {Highcharts.SVGPathArray} * Path */ callout, /** * Circle symbol path. * * @param {number} x * X coordinate * @param {number} y * Y coordinate * @param {number} w * Width * @param {number} h * Height * @return {Highcharts.SVGPathArray} * Path */ circle, /** * Diamond symbol path. * * @param {number} x * X coordinate * @param {number} y * Y coordinate * @param {number} w * Width * @param {number} h * Height * @return {Highcharts.SVGPathArray} * Path */ diamond, /** * Rect symbol path. * * @param {number} x * X coordinate * @param {number} y * Y coordinate * @param {number} w * Width * @param {number} h * Height * @param {Highcharts.SymbolOptions} [options] * Options * @return {Highcharts.SVGPathArray} * Path */ rect, /** * Rounded rectangle symbol path. * * @param {number} x * X coordinate * @param {number} y * Y coordinate * @param {number} w * Width * @param {number} h * Height * @param {Highcharts.SymbolOptions} [options] * Options * @return {Highcharts.SVGPathArray} * Path */ roundedRect, /** * Rect symbol path. * * @param {number} x * X coordinate * @param {number} y * Y coordinate * @param {number} w * Width * @param {number} h * Height * @param {Highcharts.SymbolOptions} [options] * Options * @return {Highcharts.SVGPathArray} * Path */ square: rect, /** * Triangle symbol path. * * @param {number} x * X coordinate * @param {number} y * Y coordinate * @param {number} w * Width * @param {number} h * Height * @return {Highcharts.SVGPathArray} * Path */ triangle, /** * Inverted triangle symbol path. * * @param {number} x * X coordinate * @param {number} number * Y coordinate * @param {number} w * Width * @param {number} h * Height * @return {Highcharts.SVGPathArray} * Path */ 'triangle-down': triangleDown }; /* * * * Default Export * * */ export default Symbols; /* * * * API Declarations * * */ /** * @interface Highcharts.SymbolOptions */ /** * @name anchorX * @type {number|undefined} */ /** * @name anchorY * @type {number|undefined} */ /** * @name backgroundSize * @type {"contain"|"cover"|"within"} */ /** * @name clockwise * @type {0|1|undefined} */ /** * @name context * @type {string|undefined} */ /** * @name end * @type {number|undefined} */ /** * @name height * @type {number|undefined} */ /** * @name innerR * @type {number|undefined} */ /** * @name longArc * @type {0|1|undefined} */ /** * @name open * @type {boolean|undefined} */ /** * @name r * @type {number|undefined} */ /** * @name start * @type {number|undefined} */ /** * @name width * @type {number|undefined} */ /** * @name x * @type {number|undefined} */ /** * @name y * @type {number|undefined} */ ''; // Keeps doclets above in file