@mapgis/webclient-es6-mapboxgl
Version:
1,081 lines (1,006 loc) • 38.9 kB
JavaScript
import { Common } from '@mapgis/webclient-es6-service';
const { Zondy } = Common;
import {Util} from './Util';
import {Curve as LevelRendererCurve} from './Curve';
/**
* @private
* @class Zondy.LevelRenderer.Tool.Area
* @classdesc LevelRenderer 工具-图形范围判断
*/
class Area {
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.constructor
* @description 构造函数。
*/
constructor() {
/**
* @member {Zondy.LevelRenderer.Tool.Util} Zondy.LevelRenderer.Tool.Areal.prototype.util
* @description 基础工具对象。
*/
this.util = new Util();
/**
* @member {Zondy.LevelRenderer.Tool.Curve} Zondy.LevelRenderer.Tool.Areal.prototype.curve
* @description 曲线工具对象
*/
this.curve = new LevelRendererCurve();
/**
* @member {Object} Zondy.LevelRenderer.Tool.Areal.prototype._ctx
* @description Cavans2D 渲染上下文
*/
this._ctx = null;
/**
* @member {Object} Zondy.LevelRenderer.Tool.Areal.prototype._textWidthCache
* @description 文本宽度缓存
*/
this._textWidthCache = {};
/**
* @member {Object} Zondy.LevelRenderer.Tool.Areal.prototype._textHeightCache
* @description 文本高度缓存
*/
this._textHeightCache = {};
/**
* @member {number} Zondy.LevelRenderer.Tool.Areal.prototype._textWidthCacheCounter
* @description 文本宽度缓存数量
*/
this._textWidthCacheCounter = 0;
/**
* @member {number} Zondy.LevelRenderer.Tool.Areal.prototype._textHeightCacheCounter
* @description 文本高度缓存数量
*/
this._textHeightCacheCounter = 0;
/**
* @member {number} Zondy.LevelRenderer.Tool.Areal.prototype.TEXT_CACHE_MAX
* @description 文本最大缓存数量
*/
this.TEXT_CACHE_MAX = 5000;
/**
* @member {number} Zondy.LevelRenderer.Tool.Areal.prototype.PI2
* @description 2*PI 的值
*/
this.PI2 = Math.PI * 2;
/**
* @member {Array} Zondy.LevelRenderer.Tool.Areal.prototype.roots
* @description 临时数组
*/
this.roots = [-1, -1, -1];
/**
* @member {Array} Zondy.LevelRenderer.Tool.Areal.prototype.extrema
* @description 临时数组
*/
this.extrema = [-1, -1];
this.CLASS_NAME = "Zondy.LevelRenderer.Tool.Area";
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.normalizeRadian
* @description 弧度标准化函数。
* @param {number} angle - 弧度值。
* @returns {number} 标准化后的弧度值。
*/
normalizeRadian(angle) {
angle %= this.PI2;
if (angle < 0) {
angle += this.PI2;
}
return angle;
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.isInside
* @description 包含判断。
* @param {Object} shape - 图形。
* @param {number} area - 目标区域。
* @param {number} x - 横坐标。
* @param {number} y - 纵坐标。
* @returns {boolean} 图形是否包含鼠标位置。
*/
isInside(shape, area, x, y) {
if (!area || !shape) {
// 无参数或不支持类型
return false;
}
var zoneType = shape.type;
this._ctx = this._ctx || this.util.getContext();
// 未实现或不可用时则数学运算,主要是line,brokenLine,ring
var _mathReturn = this._mathMethod(shape, area, x, y);
if (typeof _mathReturn != 'undefined') {
return _mathReturn;
}
if (shape.buildPath && this._ctx.isPointInPath) {
return this._buildPathMethod(shape, this._ctx, area, x, y);
}
// 上面的方法都行不通时
switch (zoneType) {
case 'ellipse': // Todo,不精确
case 'smicellipse': // Todo,不精确
return true;
// 旋轮曲线 不准确
case 'trochoid':
var _r = area.location === 'out'
? area.r1 + area.r2 + area.d
: area.r1 - area.r2 + area.d;
return this.isInsideCircle(area, x, y, _r);
// 玫瑰线 不准确
case 'rose' :
return this.isInsideCircle(area, x, y, area.maxr);
// 路径,椭圆,曲线等-----------------13
default:
return false; // Todo,暂不支持
}
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype._mathMethod
* @description 包含判断。用数学方法判断,三个方法中最快,但是支持的shape少。
* @param {Object} shape - 图形。
* @param {number} area - 目标区域。
* @param {number} x - 横坐标。
* @param {number} y - 纵坐标。
* @returns {boolean} 图形是否包含鼠标位置,true表示坐标处在图形中。
*/
_mathMethod(shape, area, x, y) {
var zoneType = shape.type;
// 在矩形内则部分图形需要进一步判断
switch (zoneType) {
// 贝塞尔曲线
case 'bezier-curve':
if (typeof (area.cpX2) === 'undefined') {
return this.isInsideQuadraticStroke(
area.xStart, area.yStart,
area.cpX1, area.cpY1,
area.xEnd, area.yEnd,
area.lineWidth, x, y
);
}
return this.isInsideCubicStroke(
area.xStart, area.yStart,
area.cpX1, area.cpY1,
area.cpX2, area.cpY2,
area.xEnd, area.yEnd,
area.lineWidth, x, y
);
// 线
case 'line':
return this.isInsideLine(
area.xStart, area.yStart,
area.xEnd, area.yEnd,
area.lineWidth, x, y
);
// 折线
case 'broken-line':
return this.isInsideBrokenLine(
area.pointList, area.lineWidth, x, y
);
// 扩展折线
case 'smicbroken-line': {
// SMIC-修改 - start
let icX = x;
let icY = y;
if (shape.refOriginalPosition) {
icX = x - shape.refOriginalPosition[0];
icY = y - shape.refOriginalPosition[1];
}
return this.isInsideBrokenLine(
area.pointList, area.lineWidth, icX, icY
);
}
//初始代码:
// return isInsideBrokenLine(
// area.pointList, area.lineWidth, x, y
// );
// SMIC-修改 - end
// 圆环
case 'ring':
return this.isInsideRing(
area.x, area.y, area.r0, area.r, x, y
);
case 'smicring': {
let areaX = area.x;
let areaY = area.y;
if (shape.refOriginalPosition) {
areaX = area.x + shape.refOriginalPosition[0];
areaY = area.y + shape.refOriginalPosition[1];
}
return this.isInsideRing(
areaX, areaY, area.r0, area.r, x, y
);
}
// 圆形
case 'circle':
return this.isInsideCircle(
area.x, area.y, area.r, x, y
);
// 扩展-点
case 'smicpoint': {
// SMIC-修改 - start
let icX = x;
let icY = y;
if (shape.refOriginalPosition) {
icX = x - shape.refOriginalPosition[0];
icY = y - shape.refOriginalPosition[1];
}
return this.isInsideCircle(
area.x, area.y, area.r, icX, icY
);
}
//初始代码:
// 无
// SMIC-修改 - end
// 扇形
case 'sector': {
let startAngle = area.startAngle * Math.PI / 180;
let endAngle = area.endAngle * Math.PI / 180;
if (!area.clockWise) {
startAngle = -startAngle;
endAngle = -endAngle;
}
return this.isInsideSector(
area.x, area.y, area.r0, area.r,
startAngle, endAngle,
!area.clockWise,
x, y
);
}
//初始代码:
// 无
// SMIC-增加 - end
// 扇形
case 'smicsector': {
let startAngle = area.startAngle * Math.PI / 180;
let endAngle = area.endAngle * Math.PI / 180;
if (!area.clockWise) {
startAngle = -startAngle;
endAngle = -endAngle;
}
let areaX = area.x;
let areaY = area.y;
if (shape.refOriginalPosition) {
areaX = area.x + shape.refOriginalPosition[0];
areaY = area.y + shape.refOriginalPosition[1];
}
return this.isInsideSector(
areaX, areaY, area.r0, area.r,
startAngle, endAngle,
!area.clockWise,
x, y
);
}
// 多边形
case 'path':
return this.isInsidePath(
area.pathArray, Math.max(area.lineWidth, 5),
area.brushType, x, y
);
case 'polygon':
case 'star':
case 'smicstar':
case 'isogon':
case 'smicisogon':
return this.isInsidePolygon(area.pointList, x, y);
// 扩展多边形
case 'smicpolygon': {
// SMIC-修改 - start
let icX = x;
let icY = y;
if (shape.refOriginalPosition) {
icX = x - shape.refOriginalPosition[0];
icY = y - shape.refOriginalPosition[1];
}
//岛洞面
if (shape.holePolygonPointLists && shape.holePolygonPointLists.length > 0) {
var isOnBase = this.isInsidePolygon(area.pointList, icX, icY);
// 遍历岛洞子面
var holePLS = shape.holePolygonPointLists;
var isOnHole = false;
for (var i = 0, holePLSen = holePLS.length; i < holePLSen; i++) {
var holePL = holePLS[i];
var isOnSubHole = this.isInsidePolygon(holePL, icX, icY);
if (isOnSubHole === true) {
isOnHole = true;
}
}
// 捕获判断
return isOnBase === true && isOnHole === false;
} else {
return this.isInsidePolygon(area.pointList, icX, icY);
}
}
// 初始代码:
// 无
// SMIC-修改 - end
// 文本
case 'text':
var rect = area.__rect || shape.getRect(area);
return this.isInsideRect(
rect.x, rect.y, rect.width, rect.height, x, y
);
// 扩展文本
case 'smictext':
//用文本背景矩形判断
var textBg = shape.getTextBackground(area);
return this.isInsidePolygon(textBg, x, y);
//初始代码:
// 无
// SMIC-修改 - end
// 矩形
case 'rectangle':
case 'image':
// 图片
return this.isInsideRect(
area.x, area.y, area.width, area.height, x, y
);
case 'smicimage': {
let areaX = area.x;
let areaY = area.y;
if (shape.refOriginalPosition) {
areaX = area.x + shape.refOriginalPosition[0];
areaY = area.y + shape.refOriginalPosition[1];
}
return this.isInsideRect(
areaX, areaY, area.width, area.height, x, y
);
}
//// 扩展矩形
//case 'smicpolygon':
// // SMIC-修改 - start
// var icX = x;
// var icY = y;
// if(shape.refOriginalPosition) {
// icX = x - shape.refOriginalPosition[0];
// icY = y - shape.refOriginalPosition[1];
// }
// return this.isInsideRect(
// area.x, area.y, area.width, area.height, icX, icY
// );
//初始代码:
// 无
// SMIC-修改 - end
}
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype._buildPathMethod
* @description 包含判断。通过buildPath方法来判断,三个方法中较快,但是不支持线条类型的 shape。
* @param {Object} shape - 图形。
* @param {Object} context - 上下文。
* @param {number} area - 目标区域。
* @param {number} x - 横坐标。
* @param {number} y - 纵坐标。
* @returns {boolean} 图形是否包含鼠标位置,true表示坐标处在图形中。
*/
_buildPathMethod(shape, context, area, x, y) {
// 图形类实现路径创建了则用类的path
context.beginPath();
shape.buildPath(context, area);
context.closePath();
return context.isPointInPath(x, y);
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.isOutside
* @description 图形是否不包含鼠标位置。
* @param {Object} shape - 图形。
* @param {number} area - 目标区域。
* @param {number} x - 横坐标。
* @param {number} y - 纵坐标。
* @returns {boolean} 图形是否不包含鼠标位置, true表示坐标处在图形外。
*/
isOutside(shape, area, x, y) {
return !this.isInside(shape, area, x, y);
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.isInsideLine
* @description 线段包含判断。
* @param {number} x0 - 线起始点横坐标。
* @param {number} y0 - 线起始点纵坐标。
* @param {number} x1 - 线终点横坐标。
* @param {number} y1 - 线终点纵坐标。
* @param {number} lineWidth - 线宽。
* @param {number} x - 鼠标位置横坐标。
* @param {number} y - 鼠标位置纵坐标。
* @returns {boolean} 图形是否包含鼠标位置,true表示坐标处在图形内。
*/
isInsideLine(x0, y0, x1, y1, lineWidth, x, y) {
if (lineWidth === 0) {
return false;
}
var _l = Math.max(lineWidth, 5);
var _a = 0;
var _b = 0;
// Quick reject
if (
(y > y0 + _l && y > y1 + _l)
|| (y < y0 - _l && y < y1 - _l)
|| (x > x0 + _l && x > x1 + _l)
|| (x < x0 - _l && x < x1 - _l)
) {
return false;
}
if (x0 !== x1) {
_a = (y0 - y1) / (x0 - x1);
_b = (x0 * y1 - x1 * y0) / (x0 - x1);
} else {
return Math.abs(x - x0) <= _l / 2;
}
var tmp = _a * x - y + _b;
var _s = tmp * tmp / (_a * _a + 1);
return _s <= _l / 2 * _l / 2;
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.isInsideCubicStroke
* @description 三次贝塞尔曲线描边包含判断。
* @param {number} x0 - 点1横坐标。
* @param {number} y0 - 点1纵坐标。
* @param {number} x1 - 点2横坐标。
* @param {number} y1 - 点2纵坐标。
* @param {number} x2 - 点3纵坐标。
* @param {number} y2 - 点3纵坐标。
* @param {number} lineWidth - 线宽。
* @param {number} x - 鼠标位置横坐标。
* @param {number} y - 鼠标位置纵坐标。
* @returns {boolean} 图形是否包含鼠标位置, true表示坐标处在图形内。
*/
isInsideCubicStroke(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) {
if (lineWidth === 0) {
return false;
}
var _l = Math.max(lineWidth, 5);
// Quick reject
if (
(y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l)
|| (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l)
|| (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l)
|| (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l)
) {
return false;
}
var d = this.curve.cubicProjectPoint(
x0, y0, x1, y1, x2, y2, x3, y3,
x, y, null
);
return d <= _l / 2;
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.isInsideQuadraticStroke
* @description 二次贝塞尔曲线描边包含判断。
* @param {number} x0 - 点1横坐标。
* @param {number} y0 - 点1纵坐标。
* @param {number} x1 - 点2横坐标。
* @param {number} y1 - 点2纵坐标。
* @param {number} x2 - 点3纵坐标。
* @param {number} y2 - 点3纵坐标。
* @param {number} lineWidth - 线宽。
* @param {number} x - 鼠标位置横坐标。
* @param {number} y - 鼠标位置纵坐标。
* @returns {boolean} 图形是否包含鼠标位置, true表示坐标处在图形内。
*/
isInsideQuadraticStroke(x0, y0, x1, y1, x2, y2, lineWidth, x, y) {
if (lineWidth === 0) {
return false;
}
var _l = Math.max(lineWidth, 5);
// Quick reject
if (
(y > y0 + _l && y > y1 + _l && y > y2 + _l)
|| (y < y0 - _l && y < y1 - _l && y < y2 - _l)
|| (x > x0 + _l && x > x1 + _l && x > x2 + _l)
|| (x < x0 - _l && x < x1 - _l && x < x2 - _l)
) {
return false;
}
var d = this.curve.quadraticProjectPoint(
x0, y0, x1, y1, x2, y2,
x, y, null
);
return d <= _l / 2;
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.isInsideArcStroke
* @description 圆弧描边包含判断。
* @param {number} cx - 圆心横坐标。
* @param {number} cy - 圆心纵坐标。
* @param {number} r - 圆半径。
* @param {number} startAngle - 起始角度。
* @param {number} endAngle - 终止角度。
* @param {number} anticlockwise - 顺时针还是逆时针。
* @param {number} lineWidth - 线宽。
* @param {number} x - 鼠标位置横坐标。
* @param {number} y - 鼠标位置纵坐标。
* @returns {boolean} 图形是否包含鼠标位置, true表示坐标处在图形内。
*/
isInsideArcStroke(cx, cy, r, startAngle, endAngle, anticlockwise, lineWidth, x, y) {
var PI2 = this.PI2;
if (lineWidth === 0) {
return false;
}
var _l = Math.max(lineWidth, 5);
x -= cx;
y -= cy;
var d = Math.sqrt(x * x + y * y);
if ((d - _l > r) || (d + _l < r)) {
return false;
}
if (Math.abs(startAngle - endAngle) >= PI2) {
// Is a circle
return true;
}
if (anticlockwise) {
var tmp = startAngle;
startAngle = this.normalizeRadian(endAngle);
endAngle = this.normalizeRadian(tmp);
} else {
startAngle = this.normalizeRadian(startAngle);
endAngle = this.normalizeRadian(endAngle);
}
if (startAngle > endAngle) {
endAngle += PI2;
}
var angle = Math.atan2(y, x);
if (angle < 0) {
angle += PI2;
}
return (angle >= startAngle && angle <= endAngle)
|| (angle + PI2 >= startAngle && angle + PI2 <= endAngle);
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.isInsideBrokenLine
* @description 图形 BrokenLine 是否包含鼠标位置, true表示坐标处在图形内。
* @param {Array} points - 曲线点对象。
* @param {number} lineWidth - 线宽。
* @param {number} x - 鼠标位置横坐标。
* @param {number} y - 鼠标位置纵坐标。
* @returns {boolean} 图形是否包含鼠标位置, true表示坐标处在图形内。
*/
isInsideBrokenLine(points, lineWidth, x, y) {
var _lineWidth = Math.max(lineWidth, 10);
for (var i = 0, l = points.length - 1; i < l; i++) {
var x0 = points[i][0];
var y0 = points[i][1];
var x1 = points[i + 1][0];
var y1 = points[i + 1][1];
if (this.isInsideLine(x0, y0, x1, y1, _lineWidth, x, y)) {
return true;
}
}
return false;
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.isInsideRing
* @description 图形 Ring 是否包含鼠标位置, true表示坐标处在图形内。
* @returns {boolean} 图形是否包含鼠标位置, true表示坐标处在图形内。
*/
isInsideRing(cx, cy, r0, r, x, y) {
var d = (x - cx) * (x - cx) + (y - cy) * (y - cy);
return (d < r * r) && (d > r0 * r0);
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.isInsideRect
* @description 图形 Rect 是否包含鼠标位置, true表示坐标处在图形内。
* @returns {boolean} 图形是否包含鼠标位置, true表示坐标处在图形内。
*/
isInsideRect(x0, y0, width, height, x, y) {
return x >= x0 && x <= (x0 + width) && y >= y0 && y <= (y0 + height);
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.isInsideCircle
* @description 图形 Circle 是否包含鼠标位置, true表示坐标处在图形内。
* @returns {boolean} 图形是否包含鼠标位置, true表示坐标处在图形内。
*/
isInsideCircle(x0, y0, r, x, y) {
return (x - x0) * (x - x0) + (y - y0) * (y - y0) < r * r;
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.isInsideSector
* @description 图形 Sector 是否包含鼠标位置, true表示坐标处在图形内。
* @returns {boolean} 图形是否包含鼠标位置, true表示坐标处在图形内。
*/
isInsideSector(cx, cy, r0, r, startAngle, endAngle, anticlockwise, x, y) {
return this.isInsideArcStroke(cx, cy, (r0 + r) / 2, startAngle, endAngle, anticlockwise, r - r0, x, y);
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.isInsidePolygon
* @description 图形 Polygon 是否包含鼠标位置, true表示坐标处在图形内。与 canvas 一样采用 non-zero winding rule
* @returns {boolean} 图形是否包含鼠标位置, true表示坐标处在图形内。
*/
isInsidePolygon(points, x, y) {
var N = points.length;
var w = 0;
for (var i = 0, j = N - 1; i < N; i++) {
var x0 = points[j][0];
var y0 = points[j][1];
var x1 = points[i][0];
var y1 = points[i][1];
w += this.windingLine(x0, y0, x1, y1, x, y);
j = i;
}
return w !== 0;
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.windingLine
*/
windingLine(x0, y0, x1, y1, x, y) {
if ((y > y0 && y > y1) || (y < y0 && y < y1)) {
return 0;
}
if (y1 == y0) {
return 0;
}
var dir = y1 < y0 ? 1 : -1;
var t = (y - y0) / (y1 - y0);
var x_ = t * (x1 - x0) + x0;
return x_ > x ? dir : 0;
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.swapExtrema
*/
swapExtrema() {
var tmp = this.extrema[0];
this.extrema[0] = this.extrema[1];
this.extrema[1] = tmp;
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.windingCubic
*/
windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) {
var curve = this.curve;
var roots = this.roots;
var extrema = this.extrema;
// Quick reject
if (
(y > y0 && y > y1 && y > y2 && y > y3)
|| (y < y0 && y < y1 && y < y2 && y < y3)
) {
return 0;
}
var nRoots = curve.cubicRootAt(y0, y1, y2, y3, y, roots);
if (nRoots === 0) {
return 0;
} else {
var w = 0;
var nExtrema = -1;
var y0_,
y1_;
for (var i = 0; i < nRoots; i++) {
var t = roots[i];
var x_ = curve.cubicAt(x0, x1, x2, x3, t);
if (x_ < x) { // Quick reject
continue;
}
if (nExtrema < 0) {
nExtrema = curve.cubicExtrema(y0, y1, y2, y3, extrema);
if (extrema[1] < extrema[0] && nExtrema > 1) {
this.swapExtrema();
}
y0_ = curve.cubicAt(y0, y1, y2, y3, extrema[0]);
if (nExtrema > 1) {
y1_ = curve.cubicAt(y0, y1, y2, y3, extrema[1]);
}
}
if (nExtrema === 2) {
// 分成三段单调函数
if (t < extrema[0]) {
w += y0_ < y0 ? 1 : -1;
} else if (t < extrema[1]) {
w += y1_ < y0_ ? 1 : -1;
} else {
w += y3 < y1_ ? 1 : -1;
}
} else {
// 分成两段单调函数
if (t < extrema[0]) {
w += y0_ < y0 ? 1 : -1;
} else {
w += y3 < y0_ ? 1 : -1;
}
}
}
return w;
}
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.windingQuadratic
*/
windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) {
var curve = this.curve;
var roots = this.roots;
// Quick reject
if (
(y > y0 && y > y1 && y > y2)
|| (y < y0 && y < y1 && y < y2)
) {
return 0;
}
var nRoots = curve.quadraticRootAt(y0, y1, y2, y, roots);
if (nRoots === 0) {
return 0;
} else {
var t = curve.quadraticExtremum(y0, y1, y2);
if (t >= 0 && t <= 1) {
var w = 0;
var y_ = curve.quadraticAt(y0, y1, y2, t);
for (let i = 0; i < nRoots; i++) {
let x_ = curve.quadraticAt(x0, x1, x2, roots[i]);
if (x_ > x) {
continue;
}
if (roots[i] < t) {
w += y_ < y0 ? 1 : -1;
} else {
w += y2 < y_ ? 1 : -1;
}
}
return w;
} else {
let x_ = curve.quadraticAt(x0, x1, x2, roots[0]);
if (x_ > x) {
return 0;
}
return y2 < y0 ? 1 : -1;
}
}
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.windingArc
* // TODO Arc 旋转
*/
windingArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y) {
var roots = this.roots;
var PI2 = this.PI2;
y -= cy;
if (y > r || y < -r) {
return 0;
}
let tmp = Math.sqrt(r * r - y * y);
roots[0] = -tmp;
roots[1] = tmp;
if (Math.abs(startAngle - endAngle) >= PI2) {
// Is a circle
startAngle = 0;
endAngle = PI2;
var dir = anticlockwise ? 1 : -1;
if (x >= roots[0] + cx && x <= roots[1] + cx) {
return dir;
} else {
return 0;
}
}
if (anticlockwise) {
let tmp = startAngle;
startAngle = this.normalizeRadian(endAngle);
endAngle = this.normalizeRadian(tmp);
} else {
startAngle = this.normalizeRadian(startAngle);
endAngle = this.normalizeRadian(endAngle);
}
if (startAngle > endAngle) {
endAngle += PI2;
}
var w = 0;
for (let i = 0; i < 2; i++) {
var x_ = roots[i];
if (x_ + cx > x) {
let angle = Math.atan2(y, x_);
let dir = anticlockwise ? 1 : -1;
if (angle < 0) {
angle = PI2 + angle;
}
if (
(angle >= startAngle && angle <= endAngle)
|| (angle + PI2 >= startAngle && angle + PI2 <= endAngle)
) {
if (angle > Math.PI / 2 && angle < Math.PI * 1.5) {
dir = -dir;
}
w += dir;
}
}
}
return w;
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.isInsidePath
* @description 与 canvas 一样采用 non-zero winding rule
*/
isInsidePath(pathArray, lineWidth, brushType, x, y) {
var w = 0;
var xi = 0;
var yi = 0;
var x0 = 0;
var y0 = 0;
var beginSubpath = true;
var firstCmd = true;
brushType = brushType || 'fill';
var hasStroke = brushType === 'stroke' || brushType === 'both';
var hasFill = brushType === 'fill' || brushType === 'both';
// var roots = [-1, -1, -1];
for (var i = 0; i < pathArray.length; i++) {
var seg = pathArray[i];
var p = seg.points;
// Begin a new subpath
if (beginSubpath || seg.command === 'M') {
if (i > 0) {
// Close previous subpath
if (hasFill) {
w += this.windingLine(xi, yi, x0, y0, x, y);
}
if (w !== 0) {
return true;
}
}
x0 = p[p.length - 2];
y0 = p[p.length - 1];
beginSubpath = false;
if (firstCmd && seg.command !== 'A') {
// 如果第一个命令不是M, 是lineTo, bezierCurveTo
// 等绘制命令的话,是会从该绘制的起点开始算的
// Arc 会在之后做单独处理所以这里忽略
firstCmd = false;
xi = x0;
yi = y0;
}
}
switch (seg.command) {
case 'M':
xi = p[0];
yi = p[1];
break;
case 'L':
if (hasStroke) {
if (this.isInsideLine(
xi, yi, p[0], p[1], lineWidth, x, y
)) {
return true;
}
}
if (hasFill) {
w += this.windingLine(xi, yi, p[0], p[1], x, y);
}
xi = p[0];
yi = p[1];
break;
case 'C':
if (hasStroke) {
if (this.isInsideCubicStroke(
xi, yi, p[0], p[1], p[2], p[3], p[4], p[5],
lineWidth, x, y
)) {
return true;
}
}
if (hasFill) {
w += this.windingCubic(
xi, yi, p[0], p[1], p[2], p[3], p[4], p[5], x, y
);
}
xi = p[4];
yi = p[5];
break;
case 'Q':
if (hasStroke) {
if (this.isInsideQuadraticStroke(
xi, yi, p[0], p[1], p[2], p[3],
lineWidth, x, y
)) {
return true;
}
}
if (hasFill) {
w += this.windingQuadratic(
xi, yi, p[0], p[1], p[2], p[3], x, y
);
}
xi = p[2];
yi = p[3];
break;
case 'A':
// TODO Arc 旋转
// TODO Arc 判断的开销比较大
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 x1 = Math.cos(theta) * rx + cx;
var y1 = Math.sin(theta) * ry + cy;
// 不是直接使用 arc 命令
if (!firstCmd) {
w += this.windingLine(xi, yi, x1, y1);
} else {
firstCmd = false;
// 第一个命令起点还未定义
x0 = x1;
y0 = y1;
}
// zr 使用scale来模拟椭圆, 这里也对x做一定的缩放
var _x = (x - cx) * ry / rx + cx;
if (hasStroke) {
if (this.isInsideArcStroke(
cx, cy, ry, theta, theta + dTheta, 1 - p[7],
lineWidth, _x, y
)) {
return true;
}
}
if (hasFill) {
w += this.windingArc(
cx, cy, ry, theta, theta + dTheta, 1 - p[7],
_x, y
);
}
xi = Math.cos(theta + dTheta) * rx + cx;
yi = Math.sin(theta + dTheta) * ry + cy;
break;
case 'z':
if (hasStroke) {
if (this.isInsideLine(
xi, yi, x0, y0, lineWidth, x, y
)) {
return true;
}
}
beginSubpath = true;
break;
}
}
if (hasFill) {
w += this.windingLine(xi, yi, x0, y0, x, y);
}
return w !== 0;
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.getTextWidth
* @description 测算多行文本宽度
*/
getTextWidth(text, textFont) {
var key = text + ':' + textFont;
if (this._textWidthCache[key]) {
return this._textWidthCache[key];
}
this._ctx = this._ctx || this.util.getContext();
this._ctx.save();
if (textFont) {
this._ctx.font = textFont;
}
text = (text + '').split('\n');
var width = 0;
for (var i = 0, l = text.length; i < l; i++) {
width = Math.max(
this._ctx.measureText(text[i]).width,
width
);
}
this._ctx.restore();
this._textWidthCache[key] = width;
if (++this._textWidthCacheCounter > this.TEXT_CACHE_MAX) {
// 内存释放
this._textWidthCacheCounter = 0;
this._textWidthCache = {};
}
return width;
}
/**
* @function Zondy.LevelRenderer.Tool.Areal.prototype.getTextHeight
* @description 测算多行文本高度
*/
getTextHeight(text, textFont) {
var key = text + ':' + textFont;
if (this._textHeightCache[key]) {
return this._textHeightCache[key];
}
this._ctx = this._ctx || this.util.getContext();
this._ctx.save();
if (textFont) {
this._ctx.font = textFont;
}
text = (text + '').split('\n');
// 比较粗暴
//var height = (this._ctx.measureText('国').width + 2) * text.length; //打包不支持中文,替换掉
var height = (this._ctx.measureText('ZH').width + 2) * text.length;
this._ctx.restore();
this._textHeightCache[key] = height;
if (++this._textHeightCacheCounter > this.TEXT_CACHE_MAX) {
// 内存释放
this._textHeightCacheCounter = 0;
this._textHeightCache = {};
}
return height;
}
}
export {Area};
Zondy.LevelRenderer.Tool.Area = Area;