chore: version inicial del codigo fuente del LMS Estafeta

This commit is contained in:
2026-10-02 20:33:12 -06:00
commit c8bb6b97b9
1572 changed files with 457520 additions and 0 deletions
@@ -0,0 +1,427 @@
/* *
*
* (c) 2010-2020 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
'use strict';
import H from '../../Globals.js';
import SVGElement from '../SVG/SVGElement.js';
import SVGRenderer from '../SVG/SVGRenderer.js';
import U from '../../Utilities.js';
var attr = U.attr, createElement = U.createElement, css = U.css, defined = U.defined, extend = U.extend, pick = U.pick, pInt = U.pInt;
var isFirefox = H.isFirefox, isMS = H.isMS, isWebKit = H.isWebKit, win = H.win;
/* eslint-disable valid-jsdoc */
// Extend SvgElement for useHTML option.
extend(SVGElement.prototype, /** @lends SVGElement.prototype */ {
/**
* Apply CSS to HTML elements. This is used in text within SVG rendering and
* by the VML renderer
*
* @private
* @function Highcharts.SVGElement#htmlCss
*
* @param {Highcharts.CSSObject} styles
*
* @return {Highcharts.SVGElement}
*/
htmlCss: function (styles) {
var wrapper = this, element = wrapper.element,
// When setting or unsetting the width style, we need to update
// transform (#8809)
isSettingWidth = (element.tagName === 'SPAN' &&
styles &&
'width' in styles), textWidth = pick(isSettingWidth && styles.width, void 0), doTransform;
if (isSettingWidth) {
delete styles.width;
wrapper.textWidth = textWidth;
doTransform = true;
}
if (styles && styles.textOverflow === 'ellipsis') {
styles.whiteSpace = 'nowrap';
styles.overflow = 'hidden';
}
wrapper.styles = extend(wrapper.styles, styles);
css(wrapper.element, styles);
// Now that all styles are applied, to the transform
if (doTransform) {
wrapper.htmlUpdateTransform();
}
return wrapper;
},
/**
* VML and useHTML method for calculating the bounding box based on offsets.
*
* @private
* @function Highcharts.SVGElement#htmlGetBBox
*
* @param {boolean} refresh
* Whether to force a fresh value from the DOM or to use the cached
* value.
*
* @return {Highcharts.BBoxObject}
* A hash containing values for x, y, width and height.
*/
htmlGetBBox: function () {
var wrapper = this, element = wrapper.element;
return {
x: element.offsetLeft,
y: element.offsetTop,
width: element.offsetWidth,
height: element.offsetHeight
};
},
/**
* VML override private method to update elements based on internal
* properties based on SVG transform.
*
* @private
* @function Highcharts.SVGElement#htmlUpdateTransform
* @return {void}
*/
htmlUpdateTransform: function () {
// aligning non added elements is expensive
if (!this.added) {
this.alignOnAdd = true;
return;
}
var wrapper = this, renderer = wrapper.renderer, elem = wrapper.element, translateX = wrapper.translateX || 0, translateY = wrapper.translateY || 0, x = wrapper.x || 0, y = wrapper.y || 0, align = wrapper.textAlign || 'left', alignCorrection = {
left: 0, center: 0.5, right: 1
}[align], styles = wrapper.styles, whiteSpace = styles && styles.whiteSpace;
/**
* @private
* @return {number}
*/
function getTextPxLength() {
// Reset multiline/ellipsis in order to read width (#4928,
// #5417)
css(elem, {
width: '',
whiteSpace: whiteSpace || 'nowrap'
});
return elem.offsetWidth;
}
// apply translate
css(elem, {
marginLeft: translateX,
marginTop: translateY
});
if (!renderer.styledMode && wrapper.shadows) { // used in labels/tooltip
wrapper.shadows.forEach(function (shadow) {
css(shadow, {
marginLeft: translateX + 1,
marginTop: translateY + 1
});
});
}
// apply inversion
if (wrapper.inverted) { // wrapper is a group
[].forEach.call(elem.childNodes, function (child) {
renderer.invertChild(child, elem);
});
}
if (elem.tagName === 'SPAN') {
var rotation = wrapper.rotation, baseline, textWidth = wrapper.textWidth && pInt(wrapper.textWidth), currentTextTransform = [
rotation,
align,
elem.innerHTML,
wrapper.textWidth,
wrapper.textAlign
].join(',');
// Update textWidth. Use the memoized textPxLength if possible, to
// avoid the getTextPxLength function using elem.offsetWidth.
// Calling offsetWidth affects rendering time as it forces layout
// (#7656).
if (textWidth !== wrapper.oldTextWidth &&
((textWidth > wrapper.oldTextWidth) ||
(wrapper.textPxLength || getTextPxLength()) > textWidth) && (
// Only set the width if the text is able to word-wrap, or
// text-overflow is ellipsis (#9537)
/[ \-]/.test(elem.textContent || elem.innerText) ||
elem.style.textOverflow === 'ellipsis')) { // #983, #1254
css(elem, {
width: textWidth + 'px',
display: 'block',
whiteSpace: whiteSpace || 'normal' // #3331
});
wrapper.oldTextWidth = textWidth;
wrapper.hasBoxWidthChanged = true; // #8159
}
else {
wrapper.hasBoxWidthChanged = false; // #8159
}
// Do the calculations and DOM access only if properties changed
if (currentTextTransform !== wrapper.cTT) {
baseline = renderer.fontMetrics(elem.style.fontSize, elem).b;
// Renderer specific handling of span rotation, but only if we
// have something to update.
if (defined(rotation) &&
((rotation !== (wrapper.oldRotation || 0)) ||
(align !== wrapper.oldAlign))) {
wrapper.setSpanRotation(rotation, alignCorrection, baseline);
}
wrapper.getSpanCorrection(
// Avoid elem.offsetWidth if we can, it affects rendering
// time heavily (#7656)
((!defined(rotation) && wrapper.textPxLength) || // #7920
elem.offsetWidth), baseline, alignCorrection, rotation, align);
}
// apply position with correction
css(elem, {
left: (x + (wrapper.xCorr || 0)) + 'px',
top: (y + (wrapper.yCorr || 0)) + 'px'
});
// record current text transform
wrapper.cTT = currentTextTransform;
wrapper.oldRotation = rotation;
wrapper.oldAlign = align;
}
},
/**
* Set the rotation of an individual HTML span.
*
* @private
* @function Highcharts.SVGElement#setSpanRotation
* @param {number} rotation
* @param {number} alignCorrection
* @param {number} baseline
* @return {void}
*/
setSpanRotation: function (rotation, alignCorrection, baseline) {
var rotationStyle = {}, cssTransformKey = this.renderer.getTransformKey();
rotationStyle[cssTransformKey] = rotationStyle.transform =
'rotate(' + rotation + 'deg)';
rotationStyle[cssTransformKey + (isFirefox ? 'Origin' : '-origin')] =
rotationStyle.transformOrigin =
(alignCorrection * 100) + '% ' + baseline + 'px';
css(this.element, rotationStyle);
},
/**
* Get the correction in X and Y positioning as the element is rotated.
*
* @private
* @function Highcharts.SVGElement#getSpanCorrection
* @param {number} width
* @param {number} baseline
* @param {number} alignCorrection
* @return {void}
*/
getSpanCorrection: function (width, baseline, alignCorrection) {
this.xCorr = -width * alignCorrection;
this.yCorr = -baseline;
}
});
// Extend SvgRenderer for useHTML option.
extend(SVGRenderer.prototype, /** @lends SVGRenderer.prototype */ {
/**
* @private
* @function Highcharts.SVGRenderer#getTransformKey
*
* @return {string}
*/
getTransformKey: function () {
return isMS && !/Edge/.test(win.navigator.userAgent) ?
'-ms-transform' :
isWebKit ?
'-webkit-transform' :
isFirefox ?
'MozTransform' :
win.opera ?
'-o-transform' :
'';
},
/**
* Create HTML text node. This is used by the VML renderer as well as the
* SVG renderer through the useHTML option.
*
* @private
* @function Highcharts.SVGRenderer#html
*
* @param {string} str
* The text of (subset) HTML to draw.
*
* @param {number} x
* The x position of the text's lower left corner.
*
* @param {number} y
* The y position of the text's lower left corner.
*
* @return {Highcharts.HTMLDOMElement}
*/
html: function (str, x, y) {
var wrapper = this.createElement('span'), element = wrapper.element, renderer = wrapper.renderer, isSVG = renderer.isSVG, addSetters = function (gWrapper, style) {
// These properties are set as attributes on the SVG group, and
// as identical CSS properties on the div. (#3542)
['opacity', 'visibility'].forEach(function (prop) {
gWrapper[prop + 'Setter'] = function (value, key, elem) {
var styleObject = gWrapper.div ?
gWrapper.div.style :
style;
SVGElement.prototype[prop + 'Setter']
.call(this, value, key, elem);
if (styleObject) {
styleObject[key] = value;
}
};
});
gWrapper.addedSetters = true;
};
// Text setter
wrapper.textSetter = function (value) {
if (value !== element.innerHTML) {
delete this.bBox;
delete this.oldTextWidth;
}
this.textStr = value;
element.innerHTML = pick(value, '');
wrapper.doTransform = true;
};
// Add setters for the element itself (#4938)
if (isSVG) { // #4938, only for HTML within SVG
addSetters(wrapper, wrapper.element.style);
}
// Various setters which rely on update transform
wrapper.xSetter =
wrapper.ySetter =
wrapper.alignSetter =
wrapper.rotationSetter =
function (value, key) {
if (key === 'align') {
// Do not overwrite the SVGElement.align method. Same as VML.
wrapper.alignValue = wrapper.textAlign = value;
}
else {
wrapper[key] = value;
}
wrapper.doTransform = true;
};
// Runs at the end of .attr()
wrapper.afterSetters = function () {
// Update transform. Do this outside the loop to prevent redundant
// updating for batch setting of attributes.
if (this.doTransform) {
this.htmlUpdateTransform();
this.doTransform = false;
}
};
// Set the default attributes
wrapper
.attr({
text: str,
x: Math.round(x),
y: Math.round(y)
})
.css({
position: 'absolute'
});
if (!renderer.styledMode) {
wrapper.css({
fontFamily: this.style.fontFamily,
fontSize: this.style.fontSize
});
}
// Keep the whiteSpace style outside the wrapper.styles collection
element.style.whiteSpace = 'nowrap';
// Use the HTML specific .css method
wrapper.css = wrapper.htmlCss;
// This is specific for HTML within SVG
if (isSVG) {
wrapper.add = function (svgGroupWrapper) {
var htmlGroup, container = renderer.box.parentNode, parentGroup, parents = [];
this.parentGroup = svgGroupWrapper;
// Create a mock group to hold the HTML elements
if (svgGroupWrapper) {
htmlGroup = svgGroupWrapper.div;
if (!htmlGroup) {
// Read the parent chain into an array and read from top
// down
parentGroup = svgGroupWrapper;
while (parentGroup) {
parents.push(parentGroup);
// Move up to the next parent group
parentGroup = parentGroup.parentGroup;
}
// Ensure dynamically updating position when any parent
// is translated
parents.reverse().forEach(function (parentGroup) {
var htmlGroupStyle, cls = attr(parentGroup.element, 'class');
/**
* Common translate setter for X and Y on the HTML
* group. Reverted the fix for #6957 du to
* positioning problems and offline export (#7254,
* #7280, #7529)
* @private
* @param {*} value
* @param {string} key
* @return {void}
*/
function translateSetter(value, key) {
parentGroup[key] = value;
if (key === 'translateX') {
htmlGroupStyle.left = value + 'px';
}
else {
htmlGroupStyle.top = value + 'px';
}
parentGroup.doTransform = true;
}
// Create a HTML div and append it to the parent div
// to emulate the SVG group structure
htmlGroup =
parentGroup.div =
parentGroup.div || createElement('div', cls ? { className: cls } : void 0, {
position: 'absolute',
left: (parentGroup.translateX || 0) + 'px',
top: (parentGroup.translateY || 0) + 'px',
display: parentGroup.display,
opacity: parentGroup.opacity,
pointerEvents: (parentGroup.styles &&
parentGroup.styles.pointerEvents) // #5595
// the top group is appended to container
}, htmlGroup || container);
// Shortcut
htmlGroupStyle = htmlGroup.style;
// Set listeners to update the HTML div's position
// whenever the SVG group position is changed.
extend(parentGroup, {
// (#7287) Pass htmlGroup to use
// the related group
classSetter: (function (htmlGroup) {
return function (value) {
this.element.setAttribute('class', value);
htmlGroup.className = value;
};
}(htmlGroup)),
on: function () {
if (parents[0].div) { // #6418
wrapper.on.apply({ element: parents[0].div }, arguments);
}
return parentGroup;
},
translateXSetter: translateSetter,
translateYSetter: translateSetter
});
if (!parentGroup.addedSetters) {
addSetters(parentGroup);
}
});
}
}
else {
htmlGroup = container;
}
htmlGroup.appendChild(element);
// Shared with VML:
wrapper.added = true;
if (wrapper.alignOnAdd) {
wrapper.htmlUpdateTransform();
}
return wrapper;
};
}
return wrapper;
}
});
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,411 @@
/* *
*
* (c) 2010-2020 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
'use strict';
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
import SVGElement from './SVGElement.js';
import U from '../../Utilities.js';
var defined = U.defined, extend = U.extend, isNumber = U.isNumber, merge = U.merge, removeEvent = U.removeEvent;
/**
* SVG label to render text.
* @private
* @class
* @name Highcharts.SVGLabel
* @augments Highcharts.SVGElement
*/
var SVGLabel = /** @class */ (function (_super) {
__extends(SVGLabel, _super);
/* *
*
* Constructors
*
* */
function SVGLabel(renderer, str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
var _this = _super.call(this) || this;
_this.init(renderer, 'g');
_this.textStr = str;
_this.x = x;
_this.y = y;
_this.anchorX = anchorX;
_this.anchorY = anchorY;
_this.baseline = baseline;
_this.className = className;
if (className !== 'button') {
_this.addClass('highcharts-label');
}
if (className) {
_this.addClass('highcharts-' + className);
}
_this.text = renderer.text('', 0, 0, useHTML)
.attr({
zIndex: 1
});
// Validate the shape argument
var hasBGImage;
if (typeof shape === 'string') {
hasBGImage = /^url\((.*?)\)$/.test(shape);
if (_this.renderer.symbols[shape] || hasBGImage) {
_this.symbolKey = shape;
}
}
_this.bBox = SVGLabel.emptyBBox;
_this.padding = 3;
_this.paddingLeft = 0;
_this.baselineOffset = 0;
_this.needsBox = renderer.styledMode || hasBGImage;
_this.deferredAttr = {};
_this.alignFactor = 0;
return _this;
}
/* *
*
* Functions
*
* */
SVGLabel.prototype.alignSetter = function (value) {
var alignFactor = {
left: 0,
center: 0.5,
right: 1
}[value];
if (alignFactor !== this.alignFactor) {
this.alignFactor = alignFactor;
// Bounding box exists, means we're dynamically changing
if (this.bBox && isNumber(this.xSetting)) {
this.attr({ x: this.xSetting }); // #5134
}
}
};
SVGLabel.prototype.anchorXSetter = function (value, key) {
this.anchorX = value;
this.boxAttr(key, Math.round(value) - this.getCrispAdjust() - this.xSetting);
};
SVGLabel.prototype.anchorYSetter = function (value, key) {
this.anchorY = value;
this.boxAttr(key, value - this.ySetting);
};
/*
* Set a box attribute, or defer it if the box is not yet created
*/
SVGLabel.prototype.boxAttr = function (key, value) {
if (this.box) {
this.box.attr(key, value);
}
else {
this.deferredAttr[key] = value;
}
};
/*
* Pick up some properties and apply them to the text instead of the
* wrapper.
*/
SVGLabel.prototype.css = function (styles) {
if (styles) {
var textStyles = {}, isWidth, isFontStyle;
// Create a copy to avoid altering the original object
// (#537)
styles = merge(styles);
SVGLabel.textProps.forEach(function (prop) {
if (typeof styles[prop] !== 'undefined') {
textStyles[prop] = styles[prop];
delete styles[prop];
}
});
this.text.css(textStyles);
isWidth = 'width' in textStyles;
isFontStyle = 'fontSize' in textStyles ||
'fontWeight' in textStyles;
// Update existing text, box (#9400, #12163)
if (isWidth || isFontStyle) {
this.updateBoxSize();
// Keep updated (#9400, #12163)
if (isFontStyle) {
this.updateTextPadding();
}
}
}
return SVGElement.prototype.css.call(this, styles);
};
/*
* Destroy and release memory.
*/
SVGLabel.prototype.destroy = function () {
// Added by button implementation
removeEvent(this.element, 'mouseenter');
removeEvent(this.element, 'mouseleave');
if (this.text) {
this.text.destroy();
}
if (this.box) {
this.box = this.box.destroy();
}
// Call base implementation to destroy the rest
SVGElement.prototype.destroy.call(this);
return void 0;
};
SVGLabel.prototype.fillSetter = function (value, key) {
if (value) {
this.needsBox = true;
}
// for animation getter (#6776)
this.fill = value;
this.boxAttr(key, value);
};
/*
* Return the bounding box of the box, not the group.
*/
SVGLabel.prototype.getBBox = function () {
var bBox = this.bBox;
var padding = this.padding;
return {
width: bBox.width + 2 * padding,
height: bBox.height + 2 * padding,
x: bBox.x - padding,
y: bBox.y - padding
};
};
SVGLabel.prototype.getCrispAdjust = function () {
return this.renderer.styledMode && this.box ?
this.box.strokeWidth() % 2 / 2 :
(this['stroke-width'] ? parseInt(this['stroke-width'], 10) : 0) % 2 / 2;
};
SVGLabel.prototype.heightSetter = function (value) {
this.heightSetting = value;
};
// Event handling. In case of useHTML, we need to make sure that events
// are captured on the span as well, and that mouseenter/mouseleave
// between the SVG group and the HTML span are not treated as real
// enter/leave events. #13310.
SVGLabel.prototype.on = function (eventType, handler) {
var label = this;
var text = label.text;
var span = text && text.element.tagName === 'SPAN' ? text : void 0;
var selectiveHandler;
if (span) {
selectiveHandler = function (e) {
if ((eventType === 'mouseenter' ||
eventType === 'mouseleave') &&
e.relatedTarget instanceof Element &&
(label.element.contains(e.relatedTarget) ||
span.element.contains(e.relatedTarget))) {
return;
}
handler.call(label.element, e);
};
span.on(eventType, selectiveHandler);
}
SVGElement.prototype.on.call(label, eventType, selectiveHandler || handler);
return label;
};
/*
* After the text element is added, get the desired size of the border
* box and add it before the text in the DOM.
*/
SVGLabel.prototype.onAdd = function () {
var str = this.textStr;
this.text.add(this);
this.attr({
// Alignment is available now (#3295, 0 not rendered if given
// as a value)
text: (defined(str) ? str : ''),
x: this.x,
y: this.y
});
if (this.box && defined(this.anchorX)) {
this.attr({
anchorX: this.anchorX,
anchorY: this.anchorY
});
}
};
SVGLabel.prototype.paddingSetter = function (value) {
if (defined(value) && value !== this.padding) {
this.padding = value;
this.updateTextPadding();
}
};
SVGLabel.prototype.paddingLeftSetter = function (value) {
if (defined(value) && value !== this.paddingLeft) {
this.paddingLeft = value;
this.updateTextPadding();
}
};
SVGLabel.prototype.rSetter = function (value, key) {
this.boxAttr(key, value);
};
SVGLabel.prototype.shadow = function (b) {
if (b && !this.renderer.styledMode) {
this.updateBoxSize();
if (this.box) {
this.box.shadow(b);
}
}
return this;
};
SVGLabel.prototype.strokeSetter = function (value, key) {
// for animation getter (#6776)
this.stroke = value;
this.boxAttr(key, value);
};
SVGLabel.prototype['stroke-widthSetter'] = function (value, key) {
if (value) {
this.needsBox = true;
}
this['stroke-width'] = value;
this.boxAttr(key, value);
};
SVGLabel.prototype['text-alignSetter'] = function (value) {
this.textAlign = value;
};
SVGLabel.prototype.textSetter = function (text) {
if (typeof text !== 'undefined') {
// Must use .attr to ensure transforms are done (#10009)
this.text.attr({ text: text });
}
this.updateBoxSize();
this.updateTextPadding();
};
/*
* This function runs after the label is added to the DOM (when the bounding
* box is available), and after the text of the label is updated to detect
* the new bounding box and reflect it in the border box.
*/
SVGLabel.prototype.updateBoxSize = function () {
var style = this.text.element.style, crispAdjust, attribs = {};
var padding = this.padding;
var paddingLeft = this.paddingLeft;
// #12165 error when width is null (auto)
// #12163 when fontweight: bold, recalculate bBox withot cache
// #3295 && 3514 box failure when string equals 0
var bBox = ((!isNumber(this.widthSetting) || !isNumber(this.heightSetting) || this.textAlign) &&
defined(this.text.textStr)) ?
this.text.getBBox() : SVGLabel.emptyBBox;
this.width = ((this.widthSetting || bBox.width || 0) +
2 * padding +
paddingLeft);
this.height = (this.heightSetting || bBox.height || 0) + 2 * padding;
// Update the label-scoped y offset. Math.min because of inline
// style (#9400)
this.baselineOffset = padding + Math.min(this.renderer.fontMetrics(style && style.fontSize, this.text).b,
// When the height is 0, there is no bBox, so go with the font
// metrics. Highmaps CSS demos.
bBox.height || Infinity);
if (this.needsBox) {
// Create the border box if it is not already present
if (!this.box) {
// Symbol definition exists (#5324)
var box = this.box = this.symbolKey ?
this.renderer.symbol(this.symbolKey) :
this.renderer.rect();
box.addClass(// Don't use label className for buttons
(this.className === 'button' ? '' : 'highcharts-label-box') +
(this.className ? ' highcharts-' + this.className + '-box' : ''));
box.add(this);
crispAdjust = this.getCrispAdjust();
attribs.x = crispAdjust;
attribs.y = (this.baseline ? -this.baselineOffset : 0) + crispAdjust;
}
// Apply the box attributes
attribs.width = Math.round(this.width);
attribs.height = Math.round(this.height);
this.box.attr(extend(attribs, this.deferredAttr));
this.deferredAttr = {};
}
this.bBox = bBox;
};
/*
* This function runs after setting text or padding, but only if padding
* is changed.
*/
SVGLabel.prototype.updateTextPadding = function () {
var text = this.text;
// Determine y based on the baseline
var textY = this.baseline ? 0 : this.baselineOffset;
var textX = this.paddingLeft + this.padding;
// compensate for alignment
if (defined(this.widthSetting) &&
this.bBox &&
(this.textAlign === 'center' || this.textAlign === 'right')) {
textX += { center: 0.5, right: 1 }[this.textAlign] *
(this.widthSetting - this.bBox.width);
}
// update if anything changed
if (textX !== text.x || textY !== text.y) {
text.attr('x', textX);
// #8159 - prevent misplaced data labels in treemap
// (useHTML: true)
if (text.hasBoxWidthChanged) {
this.bBox = text.getBBox(true);
this.updateBoxSize();
}
if (typeof textY !== 'undefined') {
text.attr('y', textY);
}
}
// record current values
text.x = textX;
text.y = textY;
};
SVGLabel.prototype.widthSetter = function (value) {
// width:auto => null
this.widthSetting = isNumber(value) ? value : void 0;
};
SVGLabel.prototype.xSetter = function (value) {
this.x = value; // for animation getter
if (this.alignFactor) {
value -= this.alignFactor * ((this.widthSetting || this.bBox.width) +
2 * this.padding);
// Force animation even when setting to the same value (#7898)
this['forceAnimate:x'] = true;
}
this.xSetting = Math.round(value);
this.attr('translateX', this.xSetting);
};
SVGLabel.prototype.ySetter = function (value) {
this.ySetting = this.y = Math.round(value);
this.attr('translateY', this.ySetting);
};
/* *
*
* Static Properties
*
* */
SVGLabel.emptyBBox = { width: 0, height: 0, x: 0, y: 0 };
/* *
*
* Properties
*
* */
/**
* For labels, these CSS properties are applied to the `text` node directly.
*
* @private
* @name Highcharts.SVGLabel#textProps
* @type {Array<string>}
*/
SVGLabel.textProps = [
'color', 'cursor', 'direction', 'fontFamily', 'fontSize', 'fontStyle',
'fontWeight', 'lineHeight', 'textAlign', 'textDecoration',
'textOutline', 'textOverflow', 'width'
];
return SVGLabel;
}(SVGElement));
export default SVGLabel;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,882 @@
/* *
*
* (c) 2010-2020 Torstein Honsi
*
* Extensions to the SVGRenderer class to enable 3D shapes
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
'use strict';
import Color from '../../Color.js';
var color = Color.parse;
import H from '../../Globals.js';
import Math3D from '../../../Extensions/Math3D.js';
var perspective = Math3D.perspective, shapeArea = Math3D.shapeArea;
import SVGElement from './SVGElement.js';
import SVGRenderer from './SVGRenderer.js';
import U from '../../Utilities.js';
var animObject = U.animObject, defined = U.defined, extend = U.extend, merge = U.merge, objectEach = U.objectEach, pick = U.pick;
var cos = Math.cos, PI = Math.PI, sin = Math.sin;
var charts = H.charts, deg2rad = H.deg2rad,
// internal:
dFactor, element3dMethods, cuboidMethods;
/*
EXTENSION TO THE SVG-RENDERER TO ENABLE 3D SHAPES
*/
// HELPER METHODS
dFactor = (4 * (Math.sqrt(2) - 1) / 3) / (PI / 2);
/* eslint-disable no-invalid-this, valid-jsdoc */
/**
* Method to construct a curved path. Can 'wrap' around more then 180 degrees.
* @private
*/
function curveTo(cx, cy, rx, ry, start, end, dx, dy) {
var result = [], arcAngle = end - start;
if ((end > start) && (end - start > Math.PI / 2 + 0.0001)) {
result = result.concat(curveTo(cx, cy, rx, ry, start, start + (Math.PI / 2), dx, dy));
result = result.concat(curveTo(cx, cy, rx, ry, start + (Math.PI / 2), end, dx, dy));
return result;
}
if ((end < start) && (start - end > Math.PI / 2 + 0.0001)) {
result = result.concat(curveTo(cx, cy, rx, ry, start, start - (Math.PI / 2), dx, dy));
result = result.concat(curveTo(cx, cy, rx, ry, start - (Math.PI / 2), end, dx, dy));
return result;
}
return [[
'C',
cx + (rx * Math.cos(start)) -
((rx * dFactor * arcAngle) * Math.sin(start)) + dx,
cy + (ry * Math.sin(start)) +
((ry * dFactor * arcAngle) * Math.cos(start)) + dy,
cx + (rx * Math.cos(end)) +
((rx * dFactor * arcAngle) * Math.sin(end)) + dx,
cy + (ry * Math.sin(end)) -
((ry * dFactor * arcAngle) * Math.cos(end)) + dy,
cx + (rx * Math.cos(end)) + dx,
cy + (ry * Math.sin(end)) + dy
]];
}
SVGRenderer.prototype.toLinePath = function (points, closed) {
var result = [];
// Put "L x y" for each point
points.forEach(function (point) {
result.push(['L', point.x, point.y]);
});
if (points.length) {
// Set the first element to M
result[0][0] = 'M';
// If it is a closed line, add Z
if (closed) {
result.push(['Z']);
}
}
return result;
};
SVGRenderer.prototype.toLineSegments = function (points) {
var result = [], m = true;
points.forEach(function (point) {
result.push(m ? ['M', point.x, point.y] : ['L', point.x, point.y]);
m = !m;
});
return result;
};
// A 3-D Face is defined by it's 3D vertexes, and is only visible if it's
// vertexes are counter-clockwise (Back-face culling). It is used as a
// polyhedron Element
SVGRenderer.prototype.face3d = function (args) {
var renderer = this, ret = this.createElement('path');
ret.vertexes = [];
ret.insidePlotArea = false;
ret.enabled = true;
ret.attr = function (hash) {
if (typeof hash === 'object' &&
(defined(hash.enabled) ||
defined(hash.vertexes) ||
defined(hash.insidePlotArea))) {
this.enabled = pick(hash.enabled, this.enabled);
this.vertexes = pick(hash.vertexes, this.vertexes);
this.insidePlotArea = pick(hash.insidePlotArea, this.insidePlotArea);
delete hash.enabled;
delete hash.vertexes;
delete hash.insidePlotArea;
var chart = charts[renderer.chartIndex], vertexes2d = perspective(this.vertexes, chart, this.insidePlotArea), path = renderer.toLinePath(vertexes2d, true), area = shapeArea(vertexes2d), visibility = (this.enabled && area > 0) ? 'visible' : 'hidden';
hash.d = path;
hash.visibility = visibility;
}
return SVGElement.prototype.attr.apply(this, arguments);
};
ret.animate = function (params) {
if (typeof params === 'object' &&
(defined(params.enabled) ||
defined(params.vertexes) ||
defined(params.insidePlotArea))) {
this.enabled = pick(params.enabled, this.enabled);
this.vertexes = pick(params.vertexes, this.vertexes);
this.insidePlotArea = pick(params.insidePlotArea, this.insidePlotArea);
delete params.enabled;
delete params.vertexes;
delete params.insidePlotArea;
var chart = charts[renderer.chartIndex], vertexes2d = perspective(this.vertexes, chart, this.insidePlotArea), path = renderer.toLinePath(vertexes2d, true), area = shapeArea(vertexes2d), visibility = (this.enabled && area > 0) ? 'visible' : 'hidden';
params.d = path;
this.attr('visibility', visibility);
}
return SVGElement.prototype.animate.apply(this, arguments);
};
return ret.attr(args);
};
// A Polyhedron is a handy way of defining a group of 3-D faces. It's only
// attribute is `faces`, an array of attributes of each one of it's Face3D
// instances.
SVGRenderer.prototype.polyhedron = function (args) {
var renderer = this, result = this.g(), destroy = result.destroy;
if (!this.styledMode) {
result.attr({
'stroke-linejoin': 'round'
});
}
result.faces = [];
// destroy all children
result.destroy = function () {
for (var i = 0; i < result.faces.length; i++) {
result.faces[i].destroy();
}
return destroy.call(this);
};
result.attr = function (hash, val, complete, continueAnimation) {
if (typeof hash === 'object' && defined(hash.faces)) {
while (result.faces.length > hash.faces.length) {
result.faces.pop().destroy();
}
while (result.faces.length < hash.faces.length) {
result.faces.push(renderer.face3d().add(result));
}
for (var i = 0; i < hash.faces.length; i++) {
if (renderer.styledMode) {
delete hash.faces[i].fill;
}
result.faces[i].attr(hash.faces[i], null, complete, continueAnimation);
}
delete hash.faces;
}
return SVGElement.prototype.attr.apply(this, arguments);
};
result.animate = function (params, duration, complete) {
if (params && params.faces) {
while (result.faces.length > params.faces.length) {
result.faces.pop().destroy();
}
while (result.faces.length < params.faces.length) {
result.faces.push(renderer.face3d().add(result));
}
for (var i = 0; i < params.faces.length; i++) {
result.faces[i].animate(params.faces[i], duration, complete);
}
delete params.faces;
}
return SVGElement.prototype.animate.apply(this, arguments);
};
return result.attr(args);
};
// Base, abstract prototype member for 3D elements
element3dMethods = {
/**
* The init is used by base - renderer.Element
* @private
*/
initArgs: function (args) {
var elem3d = this, renderer = elem3d.renderer, paths = renderer[elem3d.pathType + 'Path'](args), zIndexes = paths.zIndexes;
// build parts
elem3d.parts.forEach(function (part) {
elem3d[part] = renderer.path(paths[part]).attr({
'class': 'highcharts-3d-' + part,
zIndex: zIndexes[part] || 0
}).add(elem3d);
});
elem3d.attr({
'stroke-linejoin': 'round',
zIndex: zIndexes.group
});
// store original destroy
elem3d.originalDestroy = elem3d.destroy;
elem3d.destroy = elem3d.destroyParts;
// Store information if any side of element was rendered by force.
elem3d.forcedSides = paths.forcedSides;
},
/**
* Single property setter that applies options to each part
* @private
*/
singleSetterForParts: function (prop, val, values, verb, duration, complete) {
var elem3d = this, newAttr = {}, optionsToApply = [null, null, (verb || 'attr'), duration, complete], hasZIndexes = values && values.zIndexes;
if (!values) {
newAttr[prop] = val;
optionsToApply[0] = newAttr;
}
else {
// It is needed to deal with the whole group zIndexing
// in case of graph rotation
if (hasZIndexes && hasZIndexes.group) {
this.attr({
zIndex: hasZIndexes.group
});
}
objectEach(values, function (partVal, part) {
newAttr[part] = {};
newAttr[part][prop] = partVal;
// include zIndexes if provided
if (hasZIndexes) {
newAttr[part].zIndex = values.zIndexes[part] || 0;
}
});
optionsToApply[1] = newAttr;
}
return elem3d.processParts.apply(elem3d, optionsToApply);
},
/**
* Calls function for each part. Used for attr, animate and destroy.
* @private
*/
processParts: function (props, partsProps, verb, duration, complete) {
var elem3d = this;
elem3d.parts.forEach(function (part) {
// if different props for different parts
if (partsProps) {
props = pick(partsProps[part], false);
}
// only if something to set, but allow undefined
if (props !== false) {
elem3d[part][verb](props, duration, complete);
}
});
return elem3d;
},
/**
* Destroy all parts
* @private
*/
destroyParts: function () {
this.processParts(null, null, 'destroy');
return this.originalDestroy();
}
};
// CUBOID
cuboidMethods = merge(element3dMethods, {
parts: ['front', 'top', 'side'],
pathType: 'cuboid',
attr: function (args, val, complete, continueAnimation) {
// Resolve setting attributes by string name
if (typeof args === 'string' && typeof val !== 'undefined') {
var key = args;
args = {};
args[key] = val;
}
if (args.shapeArgs || defined(args.x)) {
return this.singleSetterForParts('d', null, this.renderer[this.pathType + 'Path'](args.shapeArgs || args));
}
return SVGElement.prototype.attr.call(this, args, void 0, complete, continueAnimation);
},
animate: function (args, duration, complete) {
if (defined(args.x) && defined(args.y)) {
var paths = this.renderer[this.pathType + 'Path'](args), forcedSides = paths.forcedSides;
this.singleSetterForParts('d', null, paths, 'animate', duration, complete);
this.attr({
zIndex: paths.zIndexes.group
});
// If sides that are forced to render changed, recalculate colors.
if (forcedSides !== this.forcedSides) {
this.forcedSides = forcedSides;
cuboidMethods.fillSetter.call(this, this.fill);
}
}
else {
SVGElement.prototype.animate.call(this, args, duration, complete);
}
return this;
},
fillSetter: function (fill) {
var elem3d = this;
elem3d.forcedSides = elem3d.forcedSides || [];
elem3d.singleSetterForParts('fill', null, {
front: fill,
// Do not change color if side was forced to render.
top: color(fill).brighten(elem3d.forcedSides.indexOf('top') >= 0 ? 0 : 0.1).get(),
side: color(fill).brighten(elem3d.forcedSides.indexOf('side') >= 0 ? 0 : -0.1).get()
});
// fill for animation getter (#6776)
elem3d.color = elem3d.fill = fill;
return elem3d;
}
});
// set them up
SVGRenderer.prototype.elements3d = {
base: element3dMethods,
cuboid: cuboidMethods
};
/**
* return result, generalization
* @private
* @requires highcharts-3d
*/
SVGRenderer.prototype.element3d = function (type, shapeArgs) {
// base
var ret = this.g();
// extend
extend(ret, this.elements3d[type]);
// init
ret.initArgs(shapeArgs);
// return
return ret;
};
// generelized, so now use simply
SVGRenderer.prototype.cuboid = function (shapeArgs) {
return this.element3d('cuboid', shapeArgs);
};
// Generates a cuboid path and zIndexes
SVGRenderer.prototype.cuboidPath = function (shapeArgs) {
var x = shapeArgs.x, y = shapeArgs.y, z = shapeArgs.z || 0,
// For side calculation (right/left)
// there is a need for height (and other shapeArgs arguments)
// to be at least 1px
h = shapeArgs.height, w = shapeArgs.width, d = shapeArgs.depth, chart = charts[this.chartIndex], front, back, top, bottom, left, right, shape, path1, path2, path3, isFront, isTop, isRight, options3d = chart.options.chart.options3d, alpha = options3d.alpha,
// Priority for x axis is the biggest,
// because of x direction has biggest influence on zIndex
incrementX = 1000000,
// y axis has the smallest priority in case of our charts
// (needs to be set because of stacking)
incrementY = 10, incrementZ = 100, zIndex = 0,
// The 8 corners of the cube
pArr = [{
x: x,
y: y,
z: z
}, {
x: x + w,
y: y,
z: z
}, {
x: x + w,
y: y + h,
z: z
}, {
x: x,
y: y + h,
z: z
}, {
x: x,
y: y + h,
z: z + d
}, {
x: x + w,
y: y + h,
z: z + d
}, {
x: x + w,
y: y,
z: z + d
}, {
x: x,
y: y,
z: z + d
}], forcedSides = [], pickShape;
// apply perspective
pArr = perspective(pArr, chart, shapeArgs.insidePlotArea);
/**
* helper method to decide which side is visible
* @private
*/
function mapSidePath(i) {
// Added support for 0 value in columns, where height is 0
// but the shape is rendered.
// Height is used from 1st to 6th element of pArr
if (h === 0 && i > 1 && i < 6) { // [2, 3, 4, 5]
return {
x: pArr[i].x,
// when height is 0 instead of cuboid we render plane
// so it is needed to add fake 10 height to imitate cuboid
// for side calculation
y: pArr[i].y + 10,
z: pArr[i].z
};
}
// It is needed to calculate dummy sides (front/back) for breaking
// points in case of x and depth values. If column has side,
// it means that x values of front and back side are different.
if (pArr[0].x === pArr[7].x && i >= 4) { // [4, 5, 6, 7]
return {
x: pArr[i].x + 10,
// when height is 0 instead of cuboid we render plane
// so it is needed to add fake 10 height to imitate cuboid
// for side calculation
y: pArr[i].y,
z: pArr[i].z
};
}
// Added dummy depth
if (d === 0 && i < 2 || i > 5) { // [0, 1, 6, 7]
return {
x: pArr[i].x,
// when height is 0 instead of cuboid we render plane
// so it is needed to add fake 10 height to imitate cuboid
// for side calculation
y: pArr[i].y,
z: pArr[i].z + 10
};
}
return pArr[i];
}
/**
* method creating the final side
* @private
*/
function mapPath(i) {
return pArr[i];
}
/**
* First value - path with specific face
* Second value - added information about side for later calculations.
* Possible second values are 0 for path1, 1 for path2 and -1 for no path
* chosen.
* Third value - string containing information about current side
* of cuboid for forcing side rendering.
* @private
*/
pickShape = function (verticesIndex1, verticesIndex2, side) {
var ret = [[], -1],
// An array of vertices for cuboid face
face1 = verticesIndex1.map(mapPath), face2 = verticesIndex2.map(mapPath),
// dummy face is calculated the same way as standard face,
// but if cuboid height is 0 additional height is added so it is
// possible to use this vertices array for visible face calculation
dummyFace1 = verticesIndex1.map(mapSidePath), dummyFace2 = verticesIndex2.map(mapSidePath);
if (shapeArea(face1) < 0) {
ret = [face1, 0];
}
else if (shapeArea(face2) < 0) {
ret = [face2, 1];
}
else if (side) {
forcedSides.push(side);
if (shapeArea(dummyFace1) < 0) {
ret = [face1, 0];
}
else if (shapeArea(dummyFace2) < 0) {
ret = [face2, 1];
}
else {
ret = [face1, 0]; // force side calculation.
}
}
return ret;
};
// front or back
front = [3, 2, 1, 0];
back = [7, 6, 5, 4];
shape = pickShape(front, back, 'front');
path1 = shape[0];
isFront = shape[1];
// top or bottom
top = [1, 6, 7, 0];
bottom = [4, 5, 2, 3];
shape = pickShape(top, bottom, 'top');
path2 = shape[0];
isTop = shape[1];
// side
right = [1, 2, 5, 6];
left = [0, 7, 4, 3];
shape = pickShape(right, left, 'side');
path3 = shape[0];
isRight = shape[1];
/* New block used for calculating zIndex. It is basing on X, Y and Z
position of specific columns. All zIndexes (for X, Y and Z values) are
added to the final zIndex, where every value has different priority. The
biggest priority is in X and Z directions, the lowest index is for
stacked columns (Y direction and the same X and Z positions). Big
differences between priorities is made because we need to ensure that
even for big changes in Y and Z parameters all columns will be drawn
correctly. */
if (isRight === 1) {
// It is needed to connect value with current chart width
// for big chart size.
zIndex += incrementX * (chart.plotWidth - x);
}
else if (!isRight) {
zIndex += incrementX * x;
}
zIndex += incrementY * (!isTop ||
// Numbers checked empirically
(alpha >= 0 && alpha <= 180 || alpha < 360 && alpha > 357.5) ?
chart.plotHeight - y : 10 + y);
if (isFront === 1) {
zIndex += incrementZ * (z);
}
else if (!isFront) {
zIndex += incrementZ * (1000 - z);
}
return {
front: this.toLinePath(path1, true),
top: this.toLinePath(path2, true),
side: this.toLinePath(path3, true),
zIndexes: {
group: Math.round(zIndex)
},
forcedSides: forcedSides,
// additional info about zIndexes
isFront: isFront,
isTop: isTop
}; // #4774
};
// SECTORS //
SVGRenderer.prototype.arc3d = function (attribs) {
var wrapper = this.g(), renderer = wrapper.renderer, customAttribs = ['x', 'y', 'r', 'innerR', 'start', 'end', 'depth'];
/**
* Get custom attributes. Don't mutate the original object and return an
* object with only custom attr.
* @private
*/
function suckOutCustom(params) {
var hasCA = false, ca = {}, key;
params = merge(params); // Don't mutate the original object
for (key in params) {
if (customAttribs.indexOf(key) !== -1) {
ca[key] = params[key];
delete params[key];
hasCA = true;
}
}
return hasCA ? [ca, params] : false;
}
attribs = merge(attribs);
attribs.alpha = (attribs.alpha || 0) * deg2rad;
attribs.beta = (attribs.beta || 0) * deg2rad;
// Create the different sub sections of the shape
wrapper.top = renderer.path();
wrapper.side1 = renderer.path();
wrapper.side2 = renderer.path();
wrapper.inn = renderer.path();
wrapper.out = renderer.path();
// Add all faces
wrapper.onAdd = function () {
var parent = wrapper.parentGroup, className = wrapper.attr('class');
wrapper.top.add(wrapper);
// These faces are added outside the wrapper group because the z index
// relates to neighbour elements as well
['out', 'inn', 'side1', 'side2'].forEach(function (face) {
wrapper[face]
.attr({
'class': className + ' highcharts-3d-side'
})
.add(parent);
});
};
// Cascade to faces
['addClass', 'removeClass'].forEach(function (fn) {
wrapper[fn] = function () {
var args = arguments;
['top', 'out', 'inn', 'side1', 'side2'].forEach(function (face) {
wrapper[face][fn].apply(wrapper[face], args);
});
};
});
/**
* Compute the transformed paths and set them to the composite shapes
* @private
*/
wrapper.setPaths = function (attribs) {
var paths = wrapper.renderer.arc3dPath(attribs), zIndex = paths.zTop * 100;
wrapper.attribs = attribs;
wrapper.top.attr({ d: paths.top, zIndex: paths.zTop });
wrapper.inn.attr({ d: paths.inn, zIndex: paths.zInn });
wrapper.out.attr({ d: paths.out, zIndex: paths.zOut });
wrapper.side1.attr({ d: paths.side1, zIndex: paths.zSide1 });
wrapper.side2.attr({ d: paths.side2, zIndex: paths.zSide2 });
// show all children
wrapper.zIndex = zIndex;
wrapper.attr({ zIndex: zIndex });
// Set the radial gradient center the first time
if (attribs.center) {
wrapper.top.setRadialReference(attribs.center);
delete attribs.center;
}
};
wrapper.setPaths(attribs);
/**
* Apply the fill to the top and a darker shade to the sides
* @private
*/
wrapper.fillSetter = function (value) {
var darker = color(value).brighten(-0.1).get();
this.fill = value;
this.side1.attr({ fill: darker });
this.side2.attr({ fill: darker });
this.inn.attr({ fill: darker });
this.out.attr({ fill: darker });
this.top.attr({ fill: value });
return this;
};
// Apply the same value to all. These properties cascade down to the
// children when set to the composite arc3d.
['opacity', 'translateX', 'translateY', 'visibility'].forEach(function (setter) {
wrapper[setter + 'Setter'] = function (value, key) {
wrapper[key] = value;
['out', 'inn', 'side1', 'side2', 'top'].forEach(function (el) {
wrapper[el].attr(key, value);
});
};
});
// Override attr to remove shape attributes and use those to set child paths
wrapper.attr = function (params) {
var ca, paramArr;
if (typeof params === 'object') {
paramArr = suckOutCustom(params);
if (paramArr) {
ca = paramArr[0];
arguments[0] = paramArr[1];
extend(wrapper.attribs, ca);
wrapper.setPaths(wrapper.attribs);
}
}
return SVGElement.prototype.attr.apply(wrapper, arguments);
};
// Override the animate function by sucking out custom parameters related to
// the shapes directly, and update the shapes from the animation step.
wrapper.animate = function (params, animation, complete) {
var paramArr, from = this.attribs, to, anim, randomProp = 'data-' + Math.random().toString(26).substring(2, 9);
// Attribute-line properties connected to 3D. These shouldn't have been
// in the attribs collection in the first place.
delete params.center;
delete params.z;
delete params.alpha;
delete params.beta;
anim = animObject(pick(animation, this.renderer.globalAnimation));
if (anim.duration) {
paramArr = suckOutCustom(params);
// Params need to have a property in order for the step to run
// (#5765, #7097, #7437)
wrapper[randomProp] = 0;
params[randomProp] = 1;
wrapper[randomProp + 'Setter'] = H.noop;
if (paramArr) {
to = paramArr[0]; // custom attr
anim.step = function (a, fx) {
/**
* @private
*/
function interpolate(key) {
return from[key] + (pick(to[key], from[key]) -
from[key]) * fx.pos;
}
if (fx.prop === randomProp) {
fx.elem.setPaths(merge(from, {
x: interpolate('x'),
y: interpolate('y'),
r: interpolate('r'),
innerR: interpolate('innerR'),
start: interpolate('start'),
end: interpolate('end'),
depth: interpolate('depth')
}));
}
};
}
animation = anim; // Only when duration (#5572)
}
return SVGElement.prototype.animate.call(this, params, animation, complete);
};
// destroy all children
wrapper.destroy = function () {
this.top.destroy();
this.out.destroy();
this.inn.destroy();
this.side1.destroy();
this.side2.destroy();
return SVGElement.prototype.destroy.call(this);
};
// hide all children
wrapper.hide = function () {
this.top.hide();
this.out.hide();
this.inn.hide();
this.side1.hide();
this.side2.hide();
};
wrapper.show = function (inherit) {
this.top.show(inherit);
this.out.show(inherit);
this.inn.show(inherit);
this.side1.show(inherit);
this.side2.show(inherit);
};
return wrapper;
};
// Generate the paths required to draw a 3D arc
SVGRenderer.prototype.arc3dPath = function (shapeArgs) {
var cx = shapeArgs.x, // x coordinate of the center
cy = shapeArgs.y, // y coordinate of the center
start = shapeArgs.start, // start angle
end = shapeArgs.end - 0.00001, // end angle
r = shapeArgs.r, // radius
ir = shapeArgs.innerR || 0, // inner radius
d = shapeArgs.depth || 0, // depth
alpha = shapeArgs.alpha, // alpha rotation of the chart
beta = shapeArgs.beta; // beta rotation of the chart
// Derived Variables
var cs = Math.cos(start), // cosinus of the start angle
ss = Math.sin(start), // sinus of the start angle
ce = Math.cos(end), // cosinus of the end angle
se = Math.sin(end), // sinus of the end angle
rx = r * Math.cos(beta), // x-radius
ry = r * Math.cos(alpha), // y-radius
irx = ir * Math.cos(beta), // x-radius (inner)
iry = ir * Math.cos(alpha), // y-radius (inner)
dx = d * Math.sin(beta), // distance between top and bottom in x
dy = d * Math.sin(alpha); // distance between top and bottom in y
// TOP
var top = [
['M', cx + (rx * cs), cy + (ry * ss)]
];
top = top.concat(curveTo(cx, cy, rx, ry, start, end, 0, 0));
top.push([
'L', cx + (irx * ce), cy + (iry * se)
]);
top = top.concat(curveTo(cx, cy, irx, iry, end, start, 0, 0));
top.push(['Z']);
// OUTSIDE
var b = (beta > 0 ? Math.PI / 2 : 0), a = (alpha > 0 ? 0 : Math.PI / 2);
var start2 = start > -b ? start : (end > -b ? -b : start), end2 = end < PI - a ? end : (start < PI - a ? PI - a : end), midEnd = 2 * PI - a;
// When slice goes over bottom middle, need to add both, left and right
// outer side. Additionally, when we cross right hand edge, create sharp
// edge. Outer shape/wall:
//
// -------
// / ^ \
// 4) / / \ \ 1)
// / / \ \
// / / \ \
// (c)=> ==== ==== <=(d)
// \ \ / /
// \ \<=(a)/ /
// \ \ / / <=(b)
// 3) \ v / 2)
// -------
//
// (a) - inner side
// (b) - outer side
// (c) - left edge (sharp)
// (d) - right edge (sharp)
// 1..n - rendering order for startAngle = 0, when set to e.g 90, order
// changes clockwise (1->2, 2->3, n->1) and counterclockwise for negative
// startAngle
var out = [
['M', cx + (rx * cos(start2)), cy + (ry * sin(start2))]
];
out = out.concat(curveTo(cx, cy, rx, ry, start2, end2, 0, 0));
// When shape is wide, it can cross both, (c) and (d) edges, when using
// startAngle
if (end > midEnd && start < midEnd) {
// Go to outer side
out.push([
'L', cx + (rx * cos(end2)) + dx, cy + (ry * sin(end2)) + dy
]);
// Curve to the right edge of the slice (d)
out = out.concat(curveTo(cx, cy, rx, ry, end2, midEnd, dx, dy));
// Go to the inner side
out.push([
'L', cx + (rx * cos(midEnd)), cy + (ry * sin(midEnd))
]);
// Curve to the true end of the slice
out = out.concat(curveTo(cx, cy, rx, ry, midEnd, end, 0, 0));
// Go to the outer side
out.push([
'L', cx + (rx * cos(end)) + dx, cy + (ry * sin(end)) + dy
]);
// Go back to middle (d)
out = out.concat(curveTo(cx, cy, rx, ry, end, midEnd, dx, dy));
out.push([
'L', cx + (rx * cos(midEnd)), cy + (ry * sin(midEnd))
]);
// Go back to the left edge
out = out.concat(curveTo(cx, cy, rx, ry, midEnd, end2, 0, 0));
// But shape can cross also only (c) edge:
}
else if (end > PI - a && start < PI - a) {
// Go to outer side
out.push([
'L',
cx + (rx * Math.cos(end2)) + dx,
cy + (ry * Math.sin(end2)) + dy
]);
// Curve to the true end of the slice
out = out.concat(curveTo(cx, cy, rx, ry, end2, end, dx, dy));
// Go to the inner side
out.push([
'L', cx + (rx * Math.cos(end)), cy + (ry * Math.sin(end))
]);
// Go back to the artifical end2
out = out.concat(curveTo(cx, cy, rx, ry, end, end2, 0, 0));
}
out.push([
'L', cx + (rx * Math.cos(end2)) + dx, cy + (ry * Math.sin(end2)) + dy
]);
out = out.concat(curveTo(cx, cy, rx, ry, end2, start2, dx, dy));
out.push(['Z']);
// INSIDE
var inn = [
['M', cx + (irx * cs), cy + (iry * ss)]
];
inn = inn.concat(curveTo(cx, cy, irx, iry, start, end, 0, 0));
inn.push([
'L', cx + (irx * Math.cos(end)) + dx, cy + (iry * Math.sin(end)) + dy
]);
inn = inn.concat(curveTo(cx, cy, irx, iry, end, start, dx, dy));
inn.push(['Z']);
// SIDES
var side1 = [
['M', cx + (rx * cs), cy + (ry * ss)],
['L', cx + (rx * cs) + dx, cy + (ry * ss) + dy],
['L', cx + (irx * cs) + dx, cy + (iry * ss) + dy],
['L', cx + (irx * cs), cy + (iry * ss)],
['Z']
];
var side2 = [
['M', cx + (rx * ce), cy + (ry * se)],
['L', cx + (rx * ce) + dx, cy + (ry * se) + dy],
['L', cx + (irx * ce) + dx, cy + (iry * se) + dy],
['L', cx + (irx * ce), cy + (iry * se)],
['Z']
];
// correction for changed position of vanishing point caused by alpha and
// beta rotations
var angleCorr = Math.atan2(dy, -dx), angleEnd = Math.abs(end + angleCorr), angleStart = Math.abs(start + angleCorr), angleMid = Math.abs((start + end) / 2 + angleCorr);
/**
* set to 0-PI range
* @private
*/
function toZeroPIRange(angle) {
angle = angle % (2 * Math.PI);
if (angle > Math.PI) {
angle = 2 * Math.PI - angle;
}
return angle;
}
angleEnd = toZeroPIRange(angleEnd);
angleStart = toZeroPIRange(angleStart);
angleMid = toZeroPIRange(angleMid);
// *1e5 is to compensate pInt in zIndexSetter
var incPrecision = 1e5, a1 = angleMid * incPrecision, a2 = angleStart * incPrecision, a3 = angleEnd * incPrecision;
return {
top: top,
// max angle is PI, so this is always higher
zTop: Math.PI * incPrecision + 1,
out: out,
zOut: Math.max(a1, a2, a3),
inn: inn,
zInn: Math.max(a1, a2, a3),
side1: side1,
zSide1: a3 * 0.99,
side2: side2,
zSide2: a2 * 0.99
};
};
@@ -0,0 +1,37 @@
/* *
*
* (c) 2010-2020 Torstein Honsi
*
* Extension to the VML Renderer
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
'use strict';
import Axis from '../../Axis/Axis.js';
import H from '../../Globals.js';
import SVGRenderer from '../../Renderer/SVG/SVGRenderer.js';
import U from '../../Utilities.js';
var setOptions = U.setOptions;
import VMLAxis3D from '../../Axis/VMLAxis3D.js';
var VMLRenderer = H.VMLRenderer;
if (VMLRenderer) {
setOptions({ animate: false });
VMLRenderer.prototype.face3d = SVGRenderer.prototype.face3d;
VMLRenderer.prototype.polyhedron = SVGRenderer.prototype.polyhedron;
VMLRenderer.prototype.elements3d = SVGRenderer.prototype.elements3d;
VMLRenderer.prototype.element3d = SVGRenderer.prototype.element3d;
VMLRenderer.prototype.cuboid = SVGRenderer.prototype.cuboid;
VMLRenderer.prototype.cuboidPath = SVGRenderer.prototype.cuboidPath;
VMLRenderer.prototype.toLinePath = SVGRenderer.prototype.toLinePath;
VMLRenderer.prototype.toLineSegments = SVGRenderer.prototype.toLineSegments;
VMLRenderer.prototype.arc3d = function (shapeArgs) {
var result = SVGRenderer.prototype.arc3d.call(this, shapeArgs);
result.css({ zIndex: result.zIndex });
return result;
};
H.VMLRenderer.prototype.arc3dPath = SVGRenderer.prototype.arc3dPath;
VMLAxis3D.compose(Axis);
}