686 lines
27 KiB
JavaScript
686 lines
27 KiB
JavaScript
|
define('echarts/chart/venn', [
|
||
|
'require',
|
||
|
'./base',
|
||
|
'zrender/shape/Text',
|
||
|
'zrender/shape/Circle',
|
||
|
'zrender/shape/Path',
|
||
|
'../config',
|
||
|
'../util/ecData',
|
||
|
'zrender/tool/util',
|
||
|
'../chart'
|
||
|
], function (require) {
|
||
|
var ChartBase = require('./base');
|
||
|
var TextShape = require('zrender/shape/Text');
|
||
|
var CircleShape = require('zrender/shape/Circle');
|
||
|
var PathShape = require('zrender/shape/Path');
|
||
|
var ecConfig = require('../config');
|
||
|
ecConfig.venn = {
|
||
|
zlevel: 0,
|
||
|
z: 1,
|
||
|
calculable: false
|
||
|
};
|
||
|
var ecData = require('../util/ecData');
|
||
|
var zrUtil = require('zrender/tool/util');
|
||
|
function Venn(ecTheme, messageCenter, zr, option, myChart) {
|
||
|
ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
|
||
|
this.refresh(option);
|
||
|
}
|
||
|
Venn.prototype = {
|
||
|
type: ecConfig.CHART_TYPE_VENN,
|
||
|
_buildShape: function () {
|
||
|
this.selectedMap = {};
|
||
|
this._symbol = this.option.symbolList;
|
||
|
this._queryTarget;
|
||
|
this._dropBoxList = [];
|
||
|
this._vennDataCounter = 0;
|
||
|
var series = this.series;
|
||
|
var legend = this.component.legend;
|
||
|
for (var i = 0; i < series.length; i++) {
|
||
|
if (series[i].type === ecConfig.CHART_TYPE_VENN) {
|
||
|
series[i] = this.reformOption(series[i]);
|
||
|
var serieName = series[i].name || '';
|
||
|
this.selectedMap[serieName] = legend ? legend.isSelected(serieName) : true;
|
||
|
if (!this.selectedMap[serieName]) {
|
||
|
continue;
|
||
|
}
|
||
|
this._buildVenn(i);
|
||
|
}
|
||
|
}
|
||
|
this.addShapeList();
|
||
|
},
|
||
|
_buildVenn: function (seriesIndex) {
|
||
|
var r0;
|
||
|
var r1;
|
||
|
var serie = this.series[seriesIndex];
|
||
|
var data = serie.data;
|
||
|
if (data[0].value > data[1].value) {
|
||
|
r0 = this.zr.getHeight() / 3;
|
||
|
r1 = r0 * Math.sqrt(data[1].value) / Math.sqrt(data[0].value);
|
||
|
} else {
|
||
|
r1 = this.zr.getHeight() / 3;
|
||
|
r0 = r1 * Math.sqrt(data[0].value) / Math.sqrt(data[1].value);
|
||
|
}
|
||
|
var x0 = this.zr.getWidth() / 2 - r0;
|
||
|
var coincideLengthAnchor = (r0 + r1) / 2 * Math.sqrt(data[2].value) / Math.sqrt((data[0].value + data[1].value) / 2);
|
||
|
var coincideLength = r0 + r1;
|
||
|
if (data[2].value !== 0) {
|
||
|
coincideLength = this._getCoincideLength(data[0].value, data[1].value, data[2].value, r0, r1, coincideLengthAnchor, Math.abs(r0 - r1), r0 + r1);
|
||
|
}
|
||
|
var x1 = x0 + coincideLength;
|
||
|
var y = this.zr.getHeight() / 2;
|
||
|
this._buildItem(seriesIndex, 0, data[0], x0, y, r0);
|
||
|
this._buildItem(seriesIndex, 1, data[1], x1, y, r1);
|
||
|
if (data[2].value !== 0 && data[2].value !== data[0].value && data[2].value !== data[1].value) {
|
||
|
var xLeft = (r0 * r0 - r1 * r1) / (2 * coincideLength) + coincideLength / 2;
|
||
|
var xRight = coincideLength / 2 - (r0 * r0 - r1 * r1) / (2 * coincideLength);
|
||
|
var h = Math.sqrt(r0 * r0 - xLeft * xLeft);
|
||
|
var rightLargeArcFlag = 0;
|
||
|
var leftLargeArcFlag = 0;
|
||
|
if (data[0].value > data[1].value && x1 < x0 + xLeft) {
|
||
|
leftLargeArcFlag = 1;
|
||
|
}
|
||
|
if (data[0].value < data[1].value && x1 < x0 + xRight) {
|
||
|
rightLargeArcFlag = 1;
|
||
|
}
|
||
|
this._buildCoincideItem(seriesIndex, 2, data[2], x0 + xLeft, y - h, y + h, r0, r1, rightLargeArcFlag, leftLargeArcFlag);
|
||
|
}
|
||
|
},
|
||
|
_getCoincideLength: function (value0, value1, value2, r0, r1, coincideLengthAnchor, coincideLengthAnchorMin, coincideLengthAnchorMax) {
|
||
|
var x = (r0 * r0 - r1 * r1) / (2 * coincideLengthAnchor) + coincideLengthAnchor / 2;
|
||
|
var y = coincideLengthAnchor / 2 - (r0 * r0 - r1 * r1) / (2 * coincideLengthAnchor);
|
||
|
var alfa = Math.acos(x / r0);
|
||
|
var beta = Math.acos(y / r1);
|
||
|
var area0 = r0 * r0 * Math.PI;
|
||
|
var area2 = alfa * r0 * r0 - x * r0 * Math.sin(alfa) + beta * r1 * r1 - y * r1 * Math.sin(beta);
|
||
|
var scaleAnchor = area2 / area0;
|
||
|
var scale = value2 / value0;
|
||
|
var approximateValue = Math.abs(scaleAnchor / scale);
|
||
|
if (approximateValue > 0.999 && approximateValue < 1.001) {
|
||
|
return coincideLengthAnchor;
|
||
|
} else if (approximateValue <= 0.999) {
|
||
|
coincideLengthAnchorMax = coincideLengthAnchor;
|
||
|
coincideLengthAnchor = (coincideLengthAnchor + coincideLengthAnchorMin) / 2;
|
||
|
return this._getCoincideLength(value0, value1, value2, r0, r1, coincideLengthAnchor, coincideLengthAnchorMin, coincideLengthAnchorMax);
|
||
|
} else {
|
||
|
coincideLengthAnchorMin = coincideLengthAnchor;
|
||
|
coincideLengthAnchor = (coincideLengthAnchor + coincideLengthAnchorMax) / 2;
|
||
|
return this._getCoincideLength(value0, value1, value2, r0, r1, coincideLengthAnchor, coincideLengthAnchorMin, coincideLengthAnchorMax);
|
||
|
}
|
||
|
},
|
||
|
_buildItem: function (seriesIndex, dataIndex, dataItem, x, y, r) {
|
||
|
var series = this.series;
|
||
|
var serie = series[seriesIndex];
|
||
|
var circle = this.getCircle(seriesIndex, dataIndex, dataItem, x, y, r);
|
||
|
ecData.pack(circle, serie, seriesIndex, dataItem, dataIndex, dataItem.name);
|
||
|
this.shapeList.push(circle);
|
||
|
if (serie.itemStyle.normal.label.show) {
|
||
|
var label = this.getLabel(seriesIndex, dataIndex, dataItem, x, y, r);
|
||
|
ecData.pack(label, serie, seriesIndex, serie.data[dataIndex], dataIndex, serie.data[dataIndex].name);
|
||
|
this.shapeList.push(label);
|
||
|
}
|
||
|
},
|
||
|
_buildCoincideItem: function (seriesIndex, dataIndex, dataItem, x, y0, y1, r0, r1, rightLargeArcFlag, leftLargeArcFlag) {
|
||
|
var series = this.series;
|
||
|
var serie = series[seriesIndex];
|
||
|
var queryTarget = [
|
||
|
dataItem,
|
||
|
serie
|
||
|
];
|
||
|
var normal = this.deepMerge(queryTarget, 'itemStyle.normal') || {};
|
||
|
var emphasis = this.deepMerge(queryTarget, 'itemStyle.emphasis') || {};
|
||
|
var normalColor = normal.color || this.zr.getColor(dataIndex);
|
||
|
var emphasisColor = emphasis.color || this.zr.getColor(dataIndex);
|
||
|
var path = 'M' + x + ',' + y0 + 'A' + r0 + ',' + r0 + ',0,' + rightLargeArcFlag + ',1,' + x + ',' + y1 + 'A' + r1 + ',' + r1 + ',0,' + leftLargeArcFlag + ',1,' + x + ',' + y0;
|
||
|
var style = {
|
||
|
color: normalColor,
|
||
|
path: path
|
||
|
};
|
||
|
var shape = {
|
||
|
zlevel: serie.zlevel,
|
||
|
z: serie.z,
|
||
|
style: style,
|
||
|
highlightStyle: {
|
||
|
color: emphasisColor,
|
||
|
lineWidth: emphasis.borderWidth,
|
||
|
strokeColor: emphasis.borderColor
|
||
|
}
|
||
|
};
|
||
|
shape = new PathShape(shape);
|
||
|
if (shape.buildPathArray) {
|
||
|
shape.style.pathArray = shape.buildPathArray(style.path);
|
||
|
}
|
||
|
ecData.pack(shape, series[seriesIndex], 0, dataItem, dataIndex, dataItem.name);
|
||
|
this.shapeList.push(shape);
|
||
|
},
|
||
|
getCircle: function (seriesIndex, dataIndex, dataItem, x, y, r) {
|
||
|
var serie = this.series[seriesIndex];
|
||
|
var queryTarget = [
|
||
|
dataItem,
|
||
|
serie
|
||
|
];
|
||
|
var normal = this.deepMerge(queryTarget, 'itemStyle.normal') || {};
|
||
|
var emphasis = this.deepMerge(queryTarget, 'itemStyle.emphasis') || {};
|
||
|
var normalColor = normal.color || this.zr.getColor(dataIndex);
|
||
|
var emphasisColor = emphasis.color || this.zr.getColor(dataIndex);
|
||
|
var circle = {
|
||
|
zlevel: serie.zlevel,
|
||
|
z: serie.z,
|
||
|
clickable: true,
|
||
|
style: {
|
||
|
x: x,
|
||
|
y: y,
|
||
|
r: r,
|
||
|
brushType: 'fill',
|
||
|
opacity: 1,
|
||
|
color: normalColor
|
||
|
},
|
||
|
highlightStyle: {
|
||
|
color: emphasisColor,
|
||
|
lineWidth: emphasis.borderWidth,
|
||
|
strokeColor: emphasis.borderColor
|
||
|
}
|
||
|
};
|
||
|
if (this.deepQuery([
|
||
|
dataItem,
|
||
|
serie,
|
||
|
this.option
|
||
|
], 'calculable')) {
|
||
|
this.setCalculable(circle);
|
||
|
circle.draggable = true;
|
||
|
}
|
||
|
return new CircleShape(circle);
|
||
|
},
|
||
|
getLabel: function (seriesIndex, dataIndex, dataItem, x, y, r) {
|
||
|
var serie = this.series[seriesIndex];
|
||
|
var itemStyle = serie.itemStyle;
|
||
|
var queryTarget = [
|
||
|
dataItem,
|
||
|
serie
|
||
|
];
|
||
|
var normal = this.deepMerge(queryTarget, 'itemStyle.normal') || {};
|
||
|
var status = 'normal';
|
||
|
var labelControl = itemStyle[status].label;
|
||
|
var textStyle = labelControl.textStyle || {};
|
||
|
var text = this.getLabelText(dataIndex, dataItem, status);
|
||
|
var textFont = this.getFont(textStyle);
|
||
|
var textColor = normal.color || this.zr.getColor(dataIndex);
|
||
|
var textSize = textStyle.fontSize || 12;
|
||
|
var textShape = {
|
||
|
zlevel: serie.zlevel,
|
||
|
z: serie.z,
|
||
|
style: {
|
||
|
x: x,
|
||
|
y: y - r - textSize,
|
||
|
color: textStyle.color || textColor,
|
||
|
text: text,
|
||
|
textFont: textFont,
|
||
|
textAlign: 'center'
|
||
|
}
|
||
|
};
|
||
|
return new TextShape(textShape);
|
||
|
},
|
||
|
getLabelText: function (dataIndex, dataItem, status) {
|
||
|
var series = this.series;
|
||
|
var serie = series[0];
|
||
|
var formatter = this.deepQuery([
|
||
|
dataItem,
|
||
|
serie
|
||
|
], 'itemStyle.' + status + '.label.formatter');
|
||
|
if (formatter) {
|
||
|
if (typeof formatter == 'function') {
|
||
|
return formatter(serie.name, dataItem.name, dataItem.value);
|
||
|
} else if (typeof formatter == 'string') {
|
||
|
formatter = formatter.replace('{a}', '{a0}').replace('{b}', '{b0}').replace('{c}', '{c0}');
|
||
|
formatter = formatter.replace('{a0}', serie.name).replace('{b0}', dataItem.name).replace('{c0}', dataItem.value);
|
||
|
return formatter;
|
||
|
}
|
||
|
} else {
|
||
|
return dataItem.name;
|
||
|
}
|
||
|
},
|
||
|
refresh: function (newOption) {
|
||
|
if (newOption) {
|
||
|
this.option = newOption;
|
||
|
this.series = newOption.series;
|
||
|
}
|
||
|
this._buildShape();
|
||
|
}
|
||
|
};
|
||
|
zrUtil.inherits(Venn, ChartBase);
|
||
|
require('../chart').define('venn', Venn);
|
||
|
return Venn;
|
||
|
});define('zrender/shape/Path', [
|
||
|
'require',
|
||
|
'./Base',
|
||
|
'./util/PathProxy',
|
||
|
'../tool/util'
|
||
|
], function (require) {
|
||
|
var Base = require('./Base');
|
||
|
var PathProxy = require('./util/PathProxy');
|
||
|
var PathSegment = PathProxy.PathSegment;
|
||
|
var vMag = function (v) {
|
||
|
return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
|
||
|
};
|
||
|
var vRatio = function (u, v) {
|
||
|
return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
|
||
|
};
|
||
|
var vAngle = function (u, v) {
|
||
|
return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v));
|
||
|
};
|
||
|
var Path = function (options) {
|
||
|
Base.call(this, options);
|
||
|
};
|
||
|
Path.prototype = {
|
||
|
type: 'path',
|
||
|
buildPathArray: function (data, x, y) {
|
||
|
if (!data) {
|
||
|
return [];
|
||
|
}
|
||
|
x = x || 0;
|
||
|
y = y || 0;
|
||
|
var cs = data;
|
||
|
var cc = [
|
||
|
'm',
|
||
|
'M',
|
||
|
'l',
|
||
|
'L',
|
||
|
'v',
|
||
|
'V',
|
||
|
'h',
|
||
|
'H',
|
||
|
'z',
|
||
|
'Z',
|
||
|
'c',
|
||
|
'C',
|
||
|
'q',
|
||
|
'Q',
|
||
|
't',
|
||
|
'T',
|
||
|
's',
|
||
|
'S',
|
||
|
'a',
|
||
|
'A'
|
||
|
];
|
||
|
cs = cs.replace(/-/g, ' -');
|
||
|
cs = cs.replace(/ /g, ' ');
|
||
|
cs = cs.replace(/ /g, ',');
|
||
|
cs = cs.replace(/,,/g, ',');
|
||
|
var n;
|
||
|
for (n = 0; n < cc.length; n++) {
|
||
|
cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
|
||
|
}
|
||
|
var arr = cs.split('|');
|
||
|
var ca = [];
|
||
|
var cpx = 0;
|
||
|
var cpy = 0;
|
||
|
for (n = 1; n < arr.length; n++) {
|
||
|
var str = arr[n];
|
||
|
var c = str.charAt(0);
|
||
|
str = str.slice(1);
|
||
|
str = str.replace(new RegExp('e,-', 'g'), 'e-');
|
||
|
var p = str.split(',');
|
||
|
if (p.length > 0 && p[0] === '') {
|
||
|
p.shift();
|
||
|
}
|
||
|
for (var i = 0; i < p.length; i++) {
|
||
|
p[i] = parseFloat(p[i]);
|
||
|
}
|
||
|
while (p.length > 0) {
|
||
|
if (isNaN(p[0])) {
|
||
|
break;
|
||
|
}
|
||
|
var cmd = null;
|
||
|
var points = [];
|
||
|
var ctlPtx;
|
||
|
var ctlPty;
|
||
|
var prevCmd;
|
||
|
var rx;
|
||
|
var ry;
|
||
|
var psi;
|
||
|
var fa;
|
||
|
var fs;
|
||
|
var x1 = cpx;
|
||
|
var y1 = cpy;
|
||
|
switch (c) {
|
||
|
case 'l':
|
||
|
cpx += p.shift();
|
||
|
cpy += p.shift();
|
||
|
cmd = 'L';
|
||
|
points.push(cpx, cpy);
|
||
|
break;
|
||
|
case 'L':
|
||
|
cpx = p.shift();
|
||
|
cpy = p.shift();
|
||
|
points.push(cpx, cpy);
|
||
|
break;
|
||
|
case 'm':
|
||
|
cpx += p.shift();
|
||
|
cpy += p.shift();
|
||
|
cmd = 'M';
|
||
|
points.push(cpx, cpy);
|
||
|
c = 'l';
|
||
|
break;
|
||
|
case 'M':
|
||
|
cpx = p.shift();
|
||
|
cpy = p.shift();
|
||
|
cmd = 'M';
|
||
|
points.push(cpx, cpy);
|
||
|
c = 'L';
|
||
|
break;
|
||
|
case 'h':
|
||
|
cpx += p.shift();
|
||
|
cmd = 'L';
|
||
|
points.push(cpx, cpy);
|
||
|
break;
|
||
|
case 'H':
|
||
|
cpx = p.shift();
|
||
|
cmd = 'L';
|
||
|
points.push(cpx, cpy);
|
||
|
break;
|
||
|
case 'v':
|
||
|
cpy += p.shift();
|
||
|
cmd = 'L';
|
||
|
points.push(cpx, cpy);
|
||
|
break;
|
||
|
case 'V':
|
||
|
cpy = p.shift();
|
||
|
cmd = 'L';
|
||
|
points.push(cpx, cpy);
|
||
|
break;
|
||
|
case 'C':
|
||
|
points.push(p.shift(), p.shift(), p.shift(), p.shift());
|
||
|
cpx = p.shift();
|
||
|
cpy = p.shift();
|
||
|
points.push(cpx, cpy);
|
||
|
break;
|
||
|
case 'c':
|
||
|
points.push(cpx + p.shift(), cpy + p.shift(), cpx + p.shift(), cpy + p.shift());
|
||
|
cpx += p.shift();
|
||
|
cpy += p.shift();
|
||
|
cmd = 'C';
|
||
|
points.push(cpx, cpy);
|
||
|
break;
|
||
|
case 'S':
|
||
|
ctlPtx = cpx;
|
||
|
ctlPty = cpy;
|
||
|
prevCmd = ca[ca.length - 1];
|
||
|
if (prevCmd.command === 'C') {
|
||
|
ctlPtx = cpx + (cpx - prevCmd.points[2]);
|
||
|
ctlPty = cpy + (cpy - prevCmd.points[3]);
|
||
|
}
|
||
|
points.push(ctlPtx, ctlPty, p.shift(), p.shift());
|
||
|
cpx = p.shift();
|
||
|
cpy = p.shift();
|
||
|
cmd = 'C';
|
||
|
points.push(cpx, cpy);
|
||
|
break;
|
||
|
case 's':
|
||
|
ctlPtx = cpx, ctlPty = cpy;
|
||
|
prevCmd = ca[ca.length - 1];
|
||
|
if (prevCmd.command === 'C') {
|
||
|
ctlPtx = cpx + (cpx - prevCmd.points[2]);
|
||
|
ctlPty = cpy + (cpy - prevCmd.points[3]);
|
||
|
}
|
||
|
points.push(ctlPtx, ctlPty, cpx + p.shift(), cpy + p.shift());
|
||
|
cpx += p.shift();
|
||
|
cpy += p.shift();
|
||
|
cmd = 'C';
|
||
|
points.push(cpx, cpy);
|
||
|
break;
|
||
|
case 'Q':
|
||
|
points.push(p.shift(), p.shift());
|
||
|
cpx = p.shift();
|
||
|
cpy = p.shift();
|
||
|
points.push(cpx, cpy);
|
||
|
break;
|
||
|
case 'q':
|
||
|
points.push(cpx + p.shift(), cpy + p.shift());
|
||
|
cpx += p.shift();
|
||
|
cpy += p.shift();
|
||
|
cmd = 'Q';
|
||
|
points.push(cpx, cpy);
|
||
|
break;
|
||
|
case 'T':
|
||
|
ctlPtx = cpx, ctlPty = cpy;
|
||
|
prevCmd = ca[ca.length - 1];
|
||
|
if (prevCmd.command === 'Q') {
|
||
|
ctlPtx = cpx + (cpx - prevCmd.points[0]);
|
||
|
ctlPty = cpy + (cpy - prevCmd.points[1]);
|
||
|
}
|
||
|
cpx = p.shift();
|
||
|
cpy = p.shift();
|
||
|
cmd = 'Q';
|
||
|
points.push(ctlPtx, ctlPty, cpx, cpy);
|
||
|
break;
|
||
|
case 't':
|
||
|
ctlPtx = cpx, ctlPty = cpy;
|
||
|
prevCmd = ca[ca.length - 1];
|
||
|
if (prevCmd.command === 'Q') {
|
||
|
ctlPtx = cpx + (cpx - prevCmd.points[0]);
|
||
|
ctlPty = cpy + (cpy - prevCmd.points[1]);
|
||
|
}
|
||
|
cpx += p.shift();
|
||
|
cpy += p.shift();
|
||
|
cmd = 'Q';
|
||
|
points.push(ctlPtx, ctlPty, cpx, cpy);
|
||
|
break;
|
||
|
case 'A':
|
||
|
rx = p.shift();
|
||
|
ry = p.shift();
|
||
|
psi = p.shift();
|
||
|
fa = p.shift();
|
||
|
fs = p.shift();
|
||
|
x1 = cpx, y1 = cpy;
|
||
|
cpx = p.shift(), cpy = p.shift();
|
||
|
cmd = 'A';
|
||
|
points = this._convertPoint(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
|
||
|
break;
|
||
|
case 'a':
|
||
|
rx = p.shift();
|
||
|
ry = p.shift();
|
||
|
psi = p.shift();
|
||
|
fa = p.shift();
|
||
|
fs = p.shift();
|
||
|
x1 = cpx, y1 = cpy;
|
||
|
cpx += p.shift();
|
||
|
cpy += p.shift();
|
||
|
cmd = 'A';
|
||
|
points = this._convertPoint(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
|
||
|
break;
|
||
|
}
|
||
|
for (var j = 0, l = points.length; j < l; j += 2) {
|
||
|
points[j] += x;
|
||
|
points[j + 1] += y;
|
||
|
}
|
||
|
ca.push(new PathSegment(cmd || c, points));
|
||
|
}
|
||
|
if (c === 'z' || c === 'Z') {
|
||
|
ca.push(new PathSegment('z', []));
|
||
|
}
|
||
|
}
|
||
|
return ca;
|
||
|
},
|
||
|
_convertPoint: function (x1, y1, x2, y2, fa, fs, rx, ry, psiDeg) {
|
||
|
var psi = psiDeg * (Math.PI / 180);
|
||
|
var xp = Math.cos(psi) * (x1 - x2) / 2 + Math.sin(psi) * (y1 - y2) / 2;
|
||
|
var yp = -1 * Math.sin(psi) * (x1 - x2) / 2 + Math.cos(psi) * (y1 - y2) / 2;
|
||
|
var lambda = xp * xp / (rx * rx) + yp * yp / (ry * ry);
|
||
|
if (lambda > 1) {
|
||
|
rx *= Math.sqrt(lambda);
|
||
|
ry *= Math.sqrt(lambda);
|
||
|
}
|
||
|
var f = Math.sqrt((rx * rx * (ry * ry) - rx * rx * (yp * yp) - ry * ry * (xp * xp)) / (rx * rx * (yp * yp) + ry * ry * (xp * xp)));
|
||
|
if (fa === fs) {
|
||
|
f *= -1;
|
||
|
}
|
||
|
if (isNaN(f)) {
|
||
|
f = 0;
|
||
|
}
|
||
|
var cxp = f * rx * yp / ry;
|
||
|
var cyp = f * -ry * xp / rx;
|
||
|
var cx = (x1 + x2) / 2 + Math.cos(psi) * cxp - Math.sin(psi) * cyp;
|
||
|
var cy = (y1 + y2) / 2 + Math.sin(psi) * cxp + Math.cos(psi) * cyp;
|
||
|
var theta = vAngle([
|
||
|
1,
|
||
|
0
|
||
|
], [
|
||
|
(xp - cxp) / rx,
|
||
|
(yp - cyp) / ry
|
||
|
]);
|
||
|
var u = [
|
||
|
(xp - cxp) / rx,
|
||
|
(yp - cyp) / ry
|
||
|
];
|
||
|
var v = [
|
||
|
(-1 * xp - cxp) / rx,
|
||
|
(-1 * yp - cyp) / ry
|
||
|
];
|
||
|
var dTheta = vAngle(u, v);
|
||
|
if (vRatio(u, v) <= -1) {
|
||
|
dTheta = Math.PI;
|
||
|
}
|
||
|
if (vRatio(u, v) >= 1) {
|
||
|
dTheta = 0;
|
||
|
}
|
||
|
if (fs === 0 && dTheta > 0) {
|
||
|
dTheta = dTheta - 2 * Math.PI;
|
||
|
}
|
||
|
if (fs === 1 && dTheta < 0) {
|
||
|
dTheta = dTheta + 2 * Math.PI;
|
||
|
}
|
||
|
return [
|
||
|
cx,
|
||
|
cy,
|
||
|
rx,
|
||
|
ry,
|
||
|
theta,
|
||
|
dTheta,
|
||
|
psi,
|
||
|
fs
|
||
|
];
|
||
|
},
|
||
|
buildPath: function (ctx, style) {
|
||
|
var path = style.path;
|
||
|
var x = style.x || 0;
|
||
|
var y = style.y || 0;
|
||
|
style.pathArray = style.pathArray || this.buildPathArray(path, x, y);
|
||
|
var pathArray = style.pathArray;
|
||
|
var pointList = style.pointList = [];
|
||
|
var singlePointList = [];
|
||
|
for (var i = 0, l = pathArray.length; i < l; i++) {
|
||
|
if (pathArray[i].command.toUpperCase() == 'M') {
|
||
|
singlePointList.length > 0 && pointList.push(singlePointList);
|
||
|
singlePointList = [];
|
||
|
}
|
||
|
var p = pathArray[i].points;
|
||
|
for (var j = 0, k = p.length; j < k; j += 2) {
|
||
|
singlePointList.push([
|
||
|
p[j],
|
||
|
p[j + 1]
|
||
|
]);
|
||
|
}
|
||
|
}
|
||
|
singlePointList.length > 0 && pointList.push(singlePointList);
|
||
|
for (var i = 0, l = pathArray.length; i < l; i++) {
|
||
|
var c = pathArray[i].command;
|
||
|
var p = pathArray[i].points;
|
||
|
switch (c) {
|
||
|
case 'L':
|
||
|
ctx.lineTo(p[0], p[1]);
|
||
|
break;
|
||
|
case 'M':
|
||
|
ctx.moveTo(p[0], p[1]);
|
||
|
break;
|
||
|
case 'C':
|
||
|
ctx.bezierCurveTo(p[0], p[1], p[2], p[3], p[4], p[5]);
|
||
|
break;
|
||
|
case 'Q':
|
||
|
ctx.quadraticCurveTo(p[0], p[1], p[2], p[3]);
|
||
|
break;
|
||
|
case 'A':
|
||
|
var cx = p[0];
|
||
|
var cy = p[1];
|
||
|
var rx = p[2];
|
||
|
var ry = p[3];
|
||
|
var theta = p[4];
|
||
|
var dTheta = p[5];
|
||
|
var psi = p[6];
|
||
|
var fs = p[7];
|
||
|
var r = rx > ry ? rx : ry;
|
||
|
var scaleX = rx > ry ? 1 : rx / ry;
|
||
|
var scaleY = rx > ry ? ry / rx : 1;
|
||
|
ctx.translate(cx, cy);
|
||
|
ctx.rotate(psi);
|
||
|
ctx.scale(scaleX, scaleY);
|
||
|
ctx.arc(0, 0, r, theta, theta + dTheta, 1 - fs);
|
||
|
ctx.scale(1 / scaleX, 1 / scaleY);
|
||
|
ctx.rotate(-psi);
|
||
|
ctx.translate(-cx, -cy);
|
||
|
break;
|
||
|
case 'z':
|
||
|
ctx.closePath();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return;
|
||
|
},
|
||
|
getRect: function (style) {
|
||
|
if (style.__rect) {
|
||
|
return style.__rect;
|
||
|
}
|
||
|
var lineWidth;
|
||
|
if (style.brushType == 'stroke' || style.brushType == 'fill') {
|
||
|
lineWidth = style.lineWidth || 1;
|
||
|
} else {
|
||
|
lineWidth = 0;
|
||
|
}
|
||
|
var minX = Number.MAX_VALUE;
|
||
|
var maxX = Number.MIN_VALUE;
|
||
|
var minY = Number.MAX_VALUE;
|
||
|
var maxY = Number.MIN_VALUE;
|
||
|
var x = style.x || 0;
|
||
|
var y = style.y || 0;
|
||
|
var pathArray = style.pathArray || this.buildPathArray(style.path);
|
||
|
for (var i = 0; i < pathArray.length; i++) {
|
||
|
var p = pathArray[i].points;
|
||
|
for (var j = 0; j < p.length; j++) {
|
||
|
if (j % 2 === 0) {
|
||
|
if (p[j] + x < minX) {
|
||
|
minX = p[j];
|
||
|
}
|
||
|
if (p[j] + x > maxX) {
|
||
|
maxX = p[j];
|
||
|
}
|
||
|
} else {
|
||
|
if (p[j] + y < minY) {
|
||
|
minY = p[j];
|
||
|
}
|
||
|
if (p[j] + y > maxY) {
|
||
|
maxY = p[j];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
var rect;
|
||
|
if (minX === Number.MAX_VALUE || maxX === Number.MIN_VALUE || minY === Number.MAX_VALUE || maxY === Number.MIN_VALUE) {
|
||
|
rect = {
|
||
|
x: 0,
|
||
|
y: 0,
|
||
|
width: 0,
|
||
|
height: 0
|
||
|
};
|
||
|
} else {
|
||
|
rect = {
|
||
|
x: Math.round(minX - lineWidth / 2),
|
||
|
y: Math.round(minY - lineWidth / 2),
|
||
|
width: maxX - minX + lineWidth,
|
||
|
height: maxY - minY + lineWidth
|
||
|
};
|
||
|
}
|
||
|
style.__rect = rect;
|
||
|
return rect;
|
||
|
}
|
||
|
};
|
||
|
require('../tool/util').inherits(Path, Base);
|
||
|
return Path;
|
||
|
});
|