308 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
		
			
		
	
	
			308 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
| 
								 | 
							
								import PathProxy from '../core/PathProxy.js';
							 | 
						||
| 
								 | 
							
								import * as line from './line.js';
							 | 
						||
| 
								 | 
							
								import * as cubic from './cubic.js';
							 | 
						||
| 
								 | 
							
								import * as quadratic from './quadratic.js';
							 | 
						||
| 
								 | 
							
								import * as arc from './arc.js';
							 | 
						||
| 
								 | 
							
								import * as curve from '../core/curve.js';
							 | 
						||
| 
								 | 
							
								import windingLine from './windingLine.js';
							 | 
						||
| 
								 | 
							
								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) {
							 | 
						||
| 
								 | 
							
								    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_ = void 0;
							 | 
						||
| 
								 | 
							
								        var y1_ = void 0;
							 | 
						||
| 
								 | 
							
								        for (var i = 0; i < nRoots; i++) {
							 | 
						||
| 
								 | 
							
								            var t = roots[i];
							 | 
						||
| 
								 | 
							
								            var unit = (t === 0 || t === 1) ? 0.5 : 1;
							 | 
						||
| 
								 | 
							
								            var x_ = curve.cubicAt(x0, x1, x2, x3, t);
							 | 
						||
| 
								 | 
							
								            if (x_ < x) {
							 | 
						||
| 
								 | 
							
								                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) {
							 | 
						||
| 
								 | 
							
								    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++) {
							 | 
						||
| 
								 | 
							
								                var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1;
							 | 
						||
| 
								 | 
							
								                var x_ = curve.quadraticAt(x0, x1, x2, roots[i]);
							 | 
						||
| 
								 | 
							
								                if (x_ < x) {
							 | 
						||
| 
								 | 
							
								                    continue;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                if (roots[i] < t) {
							 | 
						||
| 
								 | 
							
								                    w += y_ < y0 ? unit : -unit;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                else {
							 | 
						||
| 
								 | 
							
								                    w += y2 < y_ ? unit : -unit;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            return w;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else {
							 | 
						||
| 
								 | 
							
								            var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1;
							 | 
						||
| 
								 | 
							
								            var x_ = curve.quadraticAt(x0, x1, x2, roots[0]);
							 | 
						||
| 
								 | 
							
								            if (x_ < x) {
							 | 
						||
| 
								 | 
							
								                return 0;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            return y2 < y0 ? unit : -unit;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								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 dTheta = Math.abs(startAngle - endAngle);
							 | 
						||
| 
								 | 
							
								    if (dTheta < 1e-4) {
							 | 
						||
| 
								 | 
							
								        return 0;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (dTheta >= PI2 - 1e-4) {
							 | 
						||
| 
								 | 
							
								        startAngle = 0;
							 | 
						||
| 
								 | 
							
								        endAngle = PI2;
							 | 
						||
| 
								 | 
							
								        var dir = anticlockwise ? 1 : -1;
							 | 
						||
| 
								 | 
							
								        if (x >= roots[0] + cx && x <= roots[1] + cx) {
							 | 
						||
| 
								 | 
							
								            return dir;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else {
							 | 
						||
| 
								 | 
							
								            return 0;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (startAngle > endAngle) {
							 | 
						||
| 
								 | 
							
								        var tmp_1 = startAngle;
							 | 
						||
| 
								 | 
							
								        startAngle = endAngle;
							 | 
						||
| 
								 | 
							
								        endAngle = tmp_1;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (startAngle < 0) {
							 | 
						||
| 
								 | 
							
								        startAngle += PI2;
							 | 
						||
| 
								 | 
							
								        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(path, lineWidth, isStroke, x, y) {
							 | 
						||
| 
								 | 
							
								    var data = path.data;
							 | 
						||
| 
								 | 
							
								    var len = path.len();
							 | 
						||
| 
								 | 
							
								    var w = 0;
							 | 
						||
| 
								 | 
							
								    var xi = 0;
							 | 
						||
| 
								 | 
							
								    var yi = 0;
							 | 
						||
| 
								 | 
							
								    var x0 = 0;
							 | 
						||
| 
								 | 
							
								    var y0 = 0;
							 | 
						||
| 
								 | 
							
								    var x1;
							 | 
						||
| 
								 | 
							
								    var y1;
							 | 
						||
| 
								 | 
							
								    for (var i = 0; i < len;) {
							 | 
						||
| 
								 | 
							
								        var cmd = data[i++];
							 | 
						||
| 
								 | 
							
								        var isFirst = i === 1;
							 | 
						||
| 
								 | 
							
								        if (cmd === CMD.M && i > 1) {
							 | 
						||
| 
								 | 
							
								            if (!isStroke) {
							 | 
						||
| 
								 | 
							
								                w += windingLine(xi, yi, x0, y0, x, y);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (isFirst) {
							 | 
						||
| 
								 | 
							
								            xi = data[i];
							 | 
						||
| 
								 | 
							
								            yi = data[i + 1];
							 | 
						||
| 
								 | 
							
								            x0 = xi;
							 | 
						||
| 
								 | 
							
								            y0 = yi;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        switch (cmd) {
							 | 
						||
| 
								 | 
							
								            case CMD.M:
							 | 
						||
| 
								 | 
							
								                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 {
							 | 
						||
| 
								 | 
							
								                    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:
							 | 
						||
| 
								 | 
							
								                var cx = data[i++];
							 | 
						||
| 
								 | 
							
								                var cy = data[i++];
							 | 
						||
| 
								 | 
							
								                var rx = data[i++];
							 | 
						||
| 
								 | 
							
								                var ry = data[i++];
							 | 
						||
| 
								 | 
							
								                var theta = data[i++];
							 | 
						||
| 
								 | 
							
								                var dTheta = data[i++];
							 | 
						||
| 
								 | 
							
								                i += 1;
							 | 
						||
| 
								 | 
							
								                var anticlockwise = !!(1 - data[i++]);
							 | 
						||
| 
								 | 
							
								                x1 = Math.cos(theta) * rx + cx;
							 | 
						||
| 
								 | 
							
								                y1 = Math.sin(theta) * ry + cy;
							 | 
						||
| 
								 | 
							
								                if (!isFirst) {
							 | 
						||
| 
								 | 
							
								                    w += windingLine(xi, yi, x1, y1, x, y);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                else {
							 | 
						||
| 
								 | 
							
								                    x0 = x1;
							 | 
						||
| 
								 | 
							
								                    y0 = y1;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                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++];
							 | 
						||
| 
								 | 
							
								                x1 = x0 + width;
							 | 
						||
| 
								 | 
							
								                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 {
							 | 
						||
| 
								 | 
							
								                    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 {
							 | 
						||
| 
								 | 
							
								                    w += windingLine(xi, yi, x0, y0, x, y);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                xi = x0;
							 | 
						||
| 
								 | 
							
								                yi = y0;
							 | 
						||
| 
								 | 
							
								                break;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (!isStroke && !isAroundEqual(yi, y0)) {
							 | 
						||
| 
								 | 
							
								        w += windingLine(xi, yi, x0, y0, x, y) || 0;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return w !== 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								export function contain(pathProxy, x, y) {
							 | 
						||
| 
								 | 
							
								    return containPath(pathProxy, 0, false, x, y);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								export function containStroke(pathProxy, lineWidth, x, y) {
							 | 
						||
| 
								 | 
							
								    return containPath(pathProxy, lineWidth, true, x, y);
							 | 
						||
| 
								 | 
							
								}
							 |