123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- var PathProxy = require("../core/PathProxy");
- var line = require("./line");
- var cubic = require("./cubic");
- var quadratic = require("./quadratic");
- var arc = require("./arc");
- var _util = require("./util");
- var normalizeRadian = _util.normalizeRadian;
- var curve = require("../core/curve");
- var windingLine = require("./windingLine");
- var CMD = PathProxy.CMD;
- var PI2 = Math.PI * 2;
- var EPSILON = 1e-4;
- function isAroundEqual(a, b) {
- return Math.abs(a - b) < EPSILON;
- } // 临时数组
- var roots = [-1, -1, -1];
- var extrema = [-1, -1];
- function swapExtrema() {
- var tmp = extrema[0];
- extrema[0] = extrema[1];
- extrema[1] = tmp;
- }
- function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) {
- // 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_;
- var y1_;
- for (var i = 0; i < nRoots; i++) {
- var t = roots[i]; // Avoid winding error when intersection point is the connect point of two line of polygon
- var unit = t === 0 || t === 1 ? 0.5 : 1;
- 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) {
- 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 ? unit : -unit;
- } else if (t < extrema[1]) {
- w += y1_ < y0_ ? unit : -unit;
- } else {
- w += y3 < y1_ ? unit : -unit;
- }
- } else {
- // 分成两段单调函数
- if (t < extrema[0]) {
- w += y0_ < y0 ? unit : -unit;
- } else {
- w += y3 < y0_ ? unit : -unit;
- }
- }
- }
- return w;
- }
- }
- function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) {
- // 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 (var i = 0; i < nRoots; i++) {
- // Remove one endpoint.
- var unit = roots[i] === 0 || roots[i] === 1 ? 0.5 : 1;
- var x_ = curve.quadraticAt(x0, x1, x2, roots[i]);
- if (x_ < x) {
- // Quick reject
- continue;
- }
- if (roots[i] < t) {
- w += y_ < y0 ? unit : -unit;
- } else {
- w += y2 < y_ ? unit : -unit;
- }
- }
- return w;
- } else {
- // Remove one endpoint.
- var unit = roots[0] === 0 || roots[0] === 1 ? 0.5 : 1;
- var x_ = curve.quadraticAt(x0, x1, x2, roots[0]);
- if (x_ < x) {
- // Quick reject
- return 0;
- }
- return y2 < y0 ? unit : -unit;
- }
- }
- } // TODO
- // Arc 旋转
- function windingArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y) {
- y -= cy;
- if (y > r || y < -r) {
- return 0;
- }
- var tmp = Math.sqrt(r * r - y * y);
- roots[0] = -tmp;
- roots[1] = tmp;
- var diff = Math.abs(startAngle - endAngle);
- if (diff < 1e-4) {
- return 0;
- }
- if (diff % PI2 < 1e-4) {
- // 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) {
- var tmp = startAngle;
- startAngle = normalizeRadian(endAngle);
- endAngle = normalizeRadian(tmp);
- } else {
- startAngle = normalizeRadian(startAngle);
- endAngle = normalizeRadian(endAngle);
- }
- if (startAngle > endAngle) {
- endAngle += PI2;
- }
- var w = 0;
- for (var i = 0; i < 2; i++) {
- var x_ = roots[i];
- if (x_ + cx > x) {
- var angle = Math.atan2(y, x_);
- var 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 containPath(data, lineWidth, isStroke, x, y) {
- var w = 0;
- var xi = 0;
- var yi = 0;
- var x0 = 0;
- var y0 = 0;
- for (var i = 0; i < data.length;) {
- var cmd = data[i++]; // Begin a new subpath
- if (cmd === CMD.M && i > 1) {
- // Close previous subpath
- if (!isStroke) {
- w += windingLine(xi, yi, x0, y0, x, y);
- } // 如果被任何一个 subpath 包含
- // if (w !== 0) {
- // return true;
- // }
- }
- if (i === 1) {
- // 如果第一个命令是 L, C, Q
- // 则 previous point 同绘制命令的第一个 point
- //
- // 第一个命令为 Arc 的情况下会在后面特殊处理
- xi = data[i];
- yi = data[i + 1];
- x0 = xi;
- y0 = yi;
- }
- switch (cmd) {
- case CMD.M:
- // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
- // 在 closePath 的时候使用
- x0 = data[i++];
- y0 = data[i++];
- xi = x0;
- yi = y0;
- break;
- case CMD.L:
- if (isStroke) {
- if (line.containStroke(xi, yi, data[i], data[i + 1], lineWidth, x, y)) {
- return true;
- }
- } else {
- // NOTE 在第一个命令为 L, C, Q 的时候会计算出 NaN
- w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0;
- }
- xi = data[i++];
- yi = data[i++];
- break;
- case CMD.C:
- if (isStroke) {
- if (cubic.containStroke(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) {
- return true;
- }
- } else {
- w += windingCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], x, y) || 0;
- }
- xi = data[i++];
- yi = data[i++];
- break;
- case CMD.Q:
- if (isStroke) {
- if (quadratic.containStroke(xi, yi, data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) {
- return true;
- }
- } else {
- w += windingQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], x, y) || 0;
- }
- xi = data[i++];
- yi = data[i++];
- break;
- case CMD.A:
- // TODO Arc 判断的开销比较大
- var cx = data[i++];
- var cy = data[i++];
- var rx = data[i++];
- var ry = data[i++];
- var theta = data[i++];
- var dTheta = data[i++]; // TODO Arc 旋转
- i += 1;
- var anticlockwise = 1 - data[i++];
- var x1 = Math.cos(theta) * rx + cx;
- var y1 = Math.sin(theta) * ry + cy; // 不是直接使用 arc 命令
- if (i > 1) {
- w += windingLine(xi, yi, x1, y1, x, y);
- } else {
- // 第一个命令起点还未定义
- x0 = x1;
- y0 = y1;
- } // zr 使用scale来模拟椭圆, 这里也对x做一定的缩放
- var _x = (x - cx) * ry / rx + cx;
- if (isStroke) {
- if (arc.containStroke(cx, cy, ry, theta, theta + dTheta, anticlockwise, lineWidth, _x, y)) {
- return true;
- }
- } else {
- w += windingArc(cx, cy, ry, theta, theta + dTheta, anticlockwise, _x, y);
- }
- xi = Math.cos(theta + dTheta) * rx + cx;
- yi = Math.sin(theta + dTheta) * ry + cy;
- break;
- case CMD.R:
- x0 = xi = data[i++];
- y0 = yi = data[i++];
- var width = data[i++];
- var height = data[i++];
- var x1 = x0 + width;
- var y1 = y0 + height;
- if (isStroke) {
- if (line.containStroke(x0, y0, x1, y0, lineWidth, x, y) || line.containStroke(x1, y0, x1, y1, lineWidth, x, y) || line.containStroke(x1, y1, x0, y1, lineWidth, x, y) || line.containStroke(x0, y1, x0, y0, lineWidth, x, y)) {
- return true;
- }
- } else {
- // FIXME Clockwise ?
- w += windingLine(x1, y0, x1, y1, x, y);
- w += windingLine(x0, y1, x0, y0, x, y);
- }
- break;
- case CMD.Z:
- if (isStroke) {
- if (line.containStroke(xi, yi, x0, y0, lineWidth, x, y)) {
- return true;
- }
- } else {
- // Close a subpath
- w += windingLine(xi, yi, x0, y0, x, y); // 如果被任何一个 subpath 包含
- // FIXME subpaths may overlap
- // if (w !== 0) {
- // return true;
- // }
- }
- xi = x0;
- yi = y0;
- break;
- }
- }
- if (!isStroke && !isAroundEqual(yi, y0)) {
- w += windingLine(xi, yi, x0, y0, x, y) || 0;
- }
- return w !== 0;
- }
- function contain(pathData, x, y) {
- return containPath(pathData, 0, false, x, y);
- }
- function containStroke(pathData, lineWidth, x, y) {
- return containPath(pathData, lineWidth, true, x, y);
- }
- exports.contain = contain;
- exports.containStroke = containStroke;
|