chore: version inicial del codigo fuente del LMS Estafeta
This commit is contained in:
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user