You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
9.6 KiB
307 lines
9.6 KiB
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); |
|
}
|
|
|