(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@antv/g-lite')) : typeof define === 'function' && define.amd ? define(['exports', '@antv/g-lite'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.G = global.G || {}, global.G.CanvasPicker = {}), global.window.G)); }(this, (function (exports, gLite) { 'use strict'; function _regeneratorRuntime() { _regeneratorRuntime = function () { return exports; }; var exports = {}, Op = Object.prototype, hasOwn = Op.hasOwnProperty, defineProperty = Object.defineProperty || function (obj, key, desc) { obj[key] = desc.value; }, $Symbol = "function" == typeof Symbol ? Symbol : {}, iteratorSymbol = $Symbol.iterator || "@@iterator", asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator", toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag"; function define(obj, key, value) { return Object.defineProperty(obj, key, { value: value, enumerable: !0, configurable: !0, writable: !0 }), obj[key]; } try { define({}, ""); } catch (err) { define = function (obj, key, value) { return obj[key] = value; }; } function wrap(innerFn, outerFn, self, tryLocsList) { var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator, generator = Object.create(protoGenerator.prototype), context = new Context(tryLocsList || []); return defineProperty(generator, "_invoke", { value: makeInvokeMethod(innerFn, self, context) }), generator; } function tryCatch(fn, obj, arg) { try { return { type: "normal", arg: fn.call(obj, arg) }; } catch (err) { return { type: "throw", arg: err }; } } exports.wrap = wrap; var ContinueSentinel = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var IteratorPrototype = {}; define(IteratorPrototype, iteratorSymbol, function () { return this; }); var getProto = Object.getPrototypeOf, NativeIteratorPrototype = getProto && getProto(getProto(values([]))); NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol) && (IteratorPrototype = NativeIteratorPrototype); var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype); function defineIteratorMethods(prototype) { ["next", "throw", "return"].forEach(function (method) { define(prototype, method, function (arg) { return this._invoke(method, arg); }); }); } function AsyncIterator(generator, PromiseImpl) { function invoke(method, arg, resolve, reject) { var record = tryCatch(generator[method], generator, arg); if ("throw" !== record.type) { var result = record.arg, value = result.value; return value && "object" == typeof value && hasOwn.call(value, "__await") ? PromiseImpl.resolve(value.__await).then(function (value) { invoke("next", value, resolve, reject); }, function (err) { invoke("throw", err, resolve, reject); }) : PromiseImpl.resolve(value).then(function (unwrapped) { result.value = unwrapped, resolve(result); }, function (error) { return invoke("throw", error, resolve, reject); }); } reject(record.arg); } var previousPromise; defineProperty(this, "_invoke", { value: function (method, arg) { function callInvokeWithMethodAndArg() { return new PromiseImpl(function (resolve, reject) { invoke(method, arg, resolve, reject); }); } return previousPromise = previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(innerFn, self, context) { var state = "suspendedStart"; return function (method, arg) { if ("executing" === state) throw new Error("Generator is already running"); if ("completed" === state) { if ("throw" === method) throw arg; return doneResult(); } for (context.method = method, context.arg = arg;;) { var delegate = context.delegate; if (delegate) { var delegateResult = maybeInvokeDelegate(delegate, context); if (delegateResult) { if (delegateResult === ContinueSentinel) continue; return delegateResult; } } if ("next" === context.method) context.sent = context._sent = context.arg;else if ("throw" === context.method) { if ("suspendedStart" === state) throw state = "completed", context.arg; context.dispatchException(context.arg); } else "return" === context.method && context.abrupt("return", context.arg); state = "executing"; var record = tryCatch(innerFn, self, context); if ("normal" === record.type) { if (state = context.done ? "completed" : "suspendedYield", record.arg === ContinueSentinel) continue; return { value: record.arg, done: context.done }; } "throw" === record.type && (state = "completed", context.method = "throw", context.arg = record.arg); } }; } function maybeInvokeDelegate(delegate, context) { var methodName = context.method, method = delegate.iterator[methodName]; if (undefined === method) return context.delegate = null, "throw" === methodName && delegate.iterator.return && (context.method = "return", context.arg = undefined, maybeInvokeDelegate(delegate, context), "throw" === context.method) || "return" !== methodName && (context.method = "throw", context.arg = new TypeError("The iterator does not provide a '" + methodName + "' method")), ContinueSentinel; var record = tryCatch(method, delegate.iterator, context.arg); if ("throw" === record.type) return context.method = "throw", context.arg = record.arg, context.delegate = null, ContinueSentinel; var info = record.arg; return info ? info.done ? (context[delegate.resultName] = info.value, context.next = delegate.nextLoc, "return" !== context.method && (context.method = "next", context.arg = undefined), context.delegate = null, ContinueSentinel) : info : (context.method = "throw", context.arg = new TypeError("iterator result is not an object"), context.delegate = null, ContinueSentinel); } function pushTryEntry(locs) { var entry = { tryLoc: locs[0] }; 1 in locs && (entry.catchLoc = locs[1]), 2 in locs && (entry.finallyLoc = locs[2], entry.afterLoc = locs[3]), this.tryEntries.push(entry); } function resetTryEntry(entry) { var record = entry.completion || {}; record.type = "normal", delete record.arg, entry.completion = record; } function Context(tryLocsList) { this.tryEntries = [{ tryLoc: "root" }], tryLocsList.forEach(pushTryEntry, this), this.reset(!0); } function values(iterable) { if (iterable) { var iteratorMethod = iterable[iteratorSymbol]; if (iteratorMethod) return iteratorMethod.call(iterable); if ("function" == typeof iterable.next) return iterable; if (!isNaN(iterable.length)) { var i = -1, next = function next() { for (; ++i < iterable.length;) if (hasOwn.call(iterable, i)) return next.value = iterable[i], next.done = !1, next; return next.value = undefined, next.done = !0, next; }; return next.next = next; } } return { next: doneResult }; } function doneResult() { return { value: undefined, done: !0 }; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, defineProperty(Gp, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), defineProperty(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"), exports.isGeneratorFunction = function (genFun) { var ctor = "function" == typeof genFun && genFun.constructor; return !!ctor && (ctor === GeneratorFunction || "GeneratorFunction" === (ctor.displayName || ctor.name)); }, exports.mark = function (genFun) { return Object.setPrototypeOf ? Object.setPrototypeOf(genFun, GeneratorFunctionPrototype) : (genFun.__proto__ = GeneratorFunctionPrototype, define(genFun, toStringTagSymbol, "GeneratorFunction")), genFun.prototype = Object.create(Gp), genFun; }, exports.awrap = function (arg) { return { __await: arg }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, asyncIteratorSymbol, function () { return this; }), exports.AsyncIterator = AsyncIterator, exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) { void 0 === PromiseImpl && (PromiseImpl = Promise); var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl); return exports.isGeneratorFunction(outerFn) ? iter : iter.next().then(function (result) { return result.done ? result.value : iter.next(); }); }, defineIteratorMethods(Gp), define(Gp, toStringTagSymbol, "Generator"), define(Gp, iteratorSymbol, function () { return this; }), define(Gp, "toString", function () { return "[object Generator]"; }), exports.keys = function (val) { var object = Object(val), keys = []; for (var key in object) keys.push(key); return keys.reverse(), function next() { for (; keys.length;) { var key = keys.pop(); if (key in object) return next.value = key, next.done = !1, next; } return next.done = !0, next; }; }, exports.values = values, Context.prototype = { constructor: Context, reset: function (skipTempReset) { if (this.prev = 0, this.next = 0, this.sent = this._sent = undefined, this.done = !1, this.delegate = null, this.method = "next", this.arg = undefined, this.tryEntries.forEach(resetTryEntry), !skipTempReset) for (var name in this) "t" === name.charAt(0) && hasOwn.call(this, name) && !isNaN(+name.slice(1)) && (this[name] = undefined); }, stop: function () { this.done = !0; var rootRecord = this.tryEntries[0].completion; if ("throw" === rootRecord.type) throw rootRecord.arg; return this.rval; }, dispatchException: function (exception) { if (this.done) throw exception; var context = this; function handle(loc, caught) { return record.type = "throw", record.arg = exception, context.next = loc, caught && (context.method = "next", context.arg = undefined), !!caught; } for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i], record = entry.completion; if ("root" === entry.tryLoc) return handle("end"); if (entry.tryLoc <= this.prev) { var hasCatch = hasOwn.call(entry, "catchLoc"), hasFinally = hasOwn.call(entry, "finallyLoc"); if (hasCatch && hasFinally) { if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0); if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc); } else if (hasCatch) { if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0); } else { if (!hasFinally) throw new Error("try statement without catch or finally"); if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc); } } } }, abrupt: function (type, arg) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) { var finallyEntry = entry; break; } } finallyEntry && ("break" === type || "continue" === type) && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc && (finallyEntry = null); var record = finallyEntry ? finallyEntry.completion : {}; return record.type = type, record.arg = arg, finallyEntry ? (this.method = "next", this.next = finallyEntry.finallyLoc, ContinueSentinel) : this.complete(record); }, complete: function (record, afterLoc) { if ("throw" === record.type) throw record.arg; return "break" === record.type || "continue" === record.type ? this.next = record.arg : "return" === record.type ? (this.rval = this.arg = record.arg, this.method = "return", this.next = "end") : "normal" === record.type && afterLoc && (this.next = afterLoc), ContinueSentinel; }, finish: function (finallyLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.finallyLoc === finallyLoc) return this.complete(entry.completion, entry.afterLoc), resetTryEntry(entry), ContinueSentinel; } }, catch: function (tryLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc === tryLoc) { var record = entry.completion; if ("throw" === record.type) { var thrown = record.arg; resetTryEntry(entry); } return thrown; } } throw new Error("illegal catch attempt"); }, delegateYield: function (iterable, resultName, nextLoc) { return this.delegate = { iterator: values(iterable), resultName: resultName, nextLoc: nextLoc }, "next" === this.method && (this.arg = undefined), ContinueSentinel; } }, exports; } function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _createForOfIteratorHelperLoose(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (it) return (it = it.call(o)).next.bind(it); if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } /** * Common utilities * @module glMatrix */ var ARRAY_TYPE = typeof Float32Array !== 'undefined' ? Float32Array : Array; if (!Math.hypot) Math.hypot = function () { var y = 0, i = arguments.length; while (i--) { y += arguments[i] * arguments[i]; } return Math.sqrt(y); }; /** * 4x4 Matrix
Format: column-major, when typed out it looks like row-major
The matrices are being post multiplied. * @module mat4 */ /** * Creates a new identity mat4 * * @returns {mat4} a new 4x4 matrix */ function create() { var out = new ARRAY_TYPE(16); if (ARRAY_TYPE != Float32Array) { out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[6] = 0; out[7] = 0; out[8] = 0; out[9] = 0; out[11] = 0; out[12] = 0; out[13] = 0; out[14] = 0; } out[0] = 1; out[5] = 1; out[10] = 1; out[15] = 1; return out; } /** * Inverts a mat4 * * @param {mat4} out the receiving matrix * @param {ReadonlyMat4} a the source matrix * @returns {mat4} out */ function invert(out, a) { var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3]; var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7]; var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11]; var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; var b00 = a00 * a11 - a01 * a10; var b01 = a00 * a12 - a02 * a10; var b02 = a00 * a13 - a03 * a10; var b03 = a01 * a12 - a02 * a11; var b04 = a01 * a13 - a03 * a11; var b05 = a02 * a13 - a03 * a12; var b06 = a20 * a31 - a21 * a30; var b07 = a20 * a32 - a22 * a30; var b08 = a20 * a33 - a23 * a30; var b09 = a21 * a32 - a22 * a31; var b10 = a21 * a33 - a23 * a31; var b11 = a22 * a33 - a23 * a32; // Calculate the determinant var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; if (!det) { return null; } det = 1.0 / det; out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; return out; } /** * 3 Dimensional Vector * @module vec3 */ /** * Creates a new, empty vec3 * * @returns {vec3} a new 3D vector */ function create$1() { var out = new ARRAY_TYPE(3); if (ARRAY_TYPE != Float32Array) { out[0] = 0; out[1] = 0; out[2] = 0; } return out; } /** * Set the components of a vec3 to the given values * * @param {vec3} out the receiving vector * @param {Number} x X component * @param {Number} y Y component * @param {Number} z Z component * @returns {vec3} out */ function set(out, x, y, z) { out[0] = x; out[1] = y; out[2] = z; return out; } /** * Transforms the vec3 with a mat4. * 4th vector component is implicitly '1' * * @param {vec3} out the receiving vector * @param {ReadonlyVec3} a the vector to transform * @param {ReadonlyMat4} m matrix to transform with * @returns {vec3} out */ function transformMat4(out, a, m) { var x = a[0], y = a[1], z = a[2]; var w = m[3] * x + m[7] * y + m[11] * z + m[15]; w = w || 1.0; out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w; out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w; out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w; return out; } /** * Perform some operation over an array of vec3s. * * @param {Array} a the array of vectors to iterate over * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed * @param {Number} offset Number of elements to skip at the beginning of the array * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array * @param {Function} fn Function to call for each vector in the array * @param {Object} [arg] additional argument to pass to fn * @returns {Array} a * @function */ var forEach = function () { var vec = create$1(); return function (a, stride, offset, count, fn, arg) { var i, l; if (!stride) { stride = 3; } if (!offset) { offset = 0; } if (count) { l = Math.min(count * stride + offset, a.length); } else { l = a.length; } for (i = offset; i < l; i += stride) { vec[0] = a[i]; vec[1] = a[i + 1]; vec[2] = a[i + 2]; fn(vec, vec, arg); a[i] = vec[0]; a[i + 1] = vec[1]; a[i + 2] = vec[2]; } return a; }; }(); /** * 2 Dimensional Vector * @module vec2 */ /** * Creates a new, empty vec2 * * @returns {vec2} a new 2D vector */ function create$2() { var out = new ARRAY_TYPE(2); if (ARRAY_TYPE != Float32Array) { out[0] = 0; out[1] = 0; } return out; } /** * Normalize a vec2 * * @param {vec2} out the receiving vector * @param {ReadonlyVec2} a vector to normalize * @returns {vec2} out */ function normalize(out, a) { var x = a[0], y = a[1]; var len = x * x + y * y; if (len > 0) { //TODO: evaluate use of glm_invsqrt here? len = 1 / Math.sqrt(len); } out[0] = a[0] * len; out[1] = a[1] * len; return out; } /** * Calculates the dot product of two vec2's * * @param {ReadonlyVec2} a the first operand * @param {ReadonlyVec2} b the second operand * @returns {Number} dot product of a and b */ function dot(a, b) { return a[0] * b[0] + a[1] * b[1]; } /** * Returns whether or not the vectors exactly have the same elements in the same position (when compared with ===) * * @param {ReadonlyVec2} a The first vector. * @param {ReadonlyVec2} b The second vector. * @returns {Boolean} True if the vectors are equal, false otherwise. */ function exactEquals(a, b) { return a[0] === b[0] && a[1] === b[1]; } /** * Perform some operation over an array of vec2s. * * @param {Array} a the array of vectors to iterate over * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed * @param {Number} offset Number of elements to skip at the beginning of the array * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array * @param {Function} fn Function to call for each vector in the array * @param {Object} [arg] additional argument to pass to fn * @returns {Array} a * @function */ var forEach$1 = function () { var vec = create$2(); return function (a, stride, offset, count, fn, arg) { var i, l; if (!stride) { stride = 2; } if (!offset) { offset = 0; } if (count) { l = Math.min(count * stride + offset, a.length); } else { l = a.length; } for (i = offset; i < l; i += stride) { vec[0] = a[i]; vec[1] = a[i + 1]; fn(vec, vec, arg); a[i] = vec[0]; a[i + 1] = vec[1]; } return a; }; }(); var tmpVec3a = create$1(); var tmpVec3b = create$1(); var tmpVec3c = create$1(); var tmpMat4 = create(); /** * pick shape(s) with Mouse/Touch event * * 1. find AABB with r-tree * 2. do math calculation with geometry in an accurate way */ var CanvasPickerPlugin = /*#__PURE__*/function () { function CanvasPickerPlugin() { var _this = this; this.context = void 0; this.runtime = void 0; this.isHit = function (displayObject, position, worldTransform, isClipPath) { // use picker for current shape's type var pick = _this.context.pointInPathPickerFactory[displayObject.nodeName]; if (pick) { // invert with world matrix var invertWorldMat = invert(tmpMat4, worldTransform); // transform client position to local space, do picking in local space var localPosition = transformMat4(tmpVec3b, set(tmpVec3c, position[0], position[1], 0), invertWorldMat); // account for anchor var _displayObject$getGeo = displayObject.getGeometryBounds(), halfExtents = _displayObject$getGeo.halfExtents; var anchor = displayObject.parsedStyle.anchor; localPosition[0] += (anchor && anchor[0] || 0) * halfExtents[0] * 2; localPosition[1] += (anchor && anchor[1] || 0) * halfExtents[1] * 2; if (pick(displayObject, new gLite.Point(localPosition[0], localPosition[1]), isClipPath, _this.isPointInPath, _this.context, _this.runtime)) { return true; } } return false; }; /** * use native picking method * @see https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/isPointInPath */ this.isPointInPath = function (displayObject, position) { var context = _this.runtime.offscreenCanvas.getOrCreateContext(_this.context.config.offscreenCanvas); var generatePath = _this.context.pathGeneratorFactory[displayObject.nodeName]; if (generatePath) { context.beginPath(); generatePath(context, displayObject.parsedStyle); context.closePath(); } return context.isPointInPath(position.x, position.y); }; } var _proto = CanvasPickerPlugin.prototype; _proto.apply = function apply(context, runtime) { var _renderingContext$roo, _this2 = this; var renderingService = context.renderingService, renderingContext = context.renderingContext; this.context = context; this.runtime = runtime; var document = (_renderingContext$roo = renderingContext.root) === null || _renderingContext$roo === void 0 ? void 0 : _renderingContext$roo.ownerDocument; renderingService.hooks.pick.tapPromise(CanvasPickerPlugin.tag, /*#__PURE__*/function () { var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(result) { return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: return _context.abrupt("return", _this2.pick(document, result)); case 1: case "end": return _context.stop(); } }, _callee); })); return function (_x) { return _ref.apply(this, arguments); }; }()); renderingService.hooks.pickSync.tap(CanvasPickerPlugin.tag, function (result) { return _this2.pick(document, result); }); }; _proto.pick = function pick(document, result) { var topmost = result.topmost, _result$position = result.position, x = _result$position.x, y = _result$position.y; // position in world space var position = set(tmpVec3a, x, y, 0); // query by AABB first with spatial index(r-tree) var hitTestList = document.elementsFromBBox(position[0], position[1], position[0], position[1]); // test with clip path & origin shape // @see https://github.com/antvis/g/issues/1064 var pickedDisplayObjects = []; for (var _iterator = _createForOfIteratorHelperLoose(hitTestList), _step; !(_step = _iterator()).done;) { var displayObject = _step.value; var worldTransform = displayObject.getWorldTransform(); var isHitOriginShape = this.isHit(displayObject, position, worldTransform, false); if (isHitOriginShape) { // should look up in the ancestor node var clipped = gLite.findClosestClipPathTarget(displayObject); if (clipped) { var clipPath = clipped.parsedStyle.clipPath; var isHitClipPath = this.isHit(clipPath, position, clipPath.getWorldTransform(), true); if (isHitClipPath) { if (topmost) { result.picked = [displayObject]; return result; } else { pickedDisplayObjects.push(displayObject); } } } else { if (topmost) { result.picked = [displayObject]; return result; } else { pickedDisplayObjects.push(displayObject); } } } } result.picked = pickedDisplayObjects; return result; }; return CanvasPickerPlugin; }(); CanvasPickerPlugin.tag = 'CanvasPicker'; /** * 两点之间的距离 * @param {number} x1 起始点 x * @param {number} y1 起始点 y * @param {number} x2 结束点 x * @param {number} y2 结束点 y * @return {number} 距离 */ function distance(x1, y1, x2, y2) { var dx = x1 - x2; var dy = y1 - y2; return Math.sqrt(dx * dx + dy * dy); } function isNumberEqual(v1, v2) { return Math.abs(v1 - v2) < 0.001; } function getBBoxByArray(xArr, yArr) { var minX = Math.min.apply(Math, xArr); var minY = Math.min.apply(Math, yArr); var maxX = Math.max.apply(Math, xArr); var maxY = Math.max.apply(Math, yArr); return { x: minX, y: minY, width: maxX - minX, height: maxY - minY }; } function piMod(angle) { return (angle + Math.PI * 2) % (Math.PI * 2); } var line = { /** * 计算线段的包围盒 * @param {number} x1 起始点 x * @param {number} y1 起始点 y * @param {number} x2 结束点 x * @param {number} y2 结束点 y * @return {object} 包围盒对象 */ box: function box(x1, y1, x2, y2) { return getBBoxByArray([x1, x2], [y1, y2]); }, /** * 线段的长度 * @param {number} x1 起始点 x * @param {number} y1 起始点 y * @param {number} x2 结束点 x * @param {number} y2 结束点 y * @return {number} 距离 */ length: function length(x1, y1, x2, y2) { return distance(x1, y1, x2, y2); }, /** * 根据比例获取点 * @param {number} x1 起始点 x * @param {number} y1 起始点 y * @param {number} x2 结束点 x * @param {number} y2 结束点 y * @param {number} t 指定比例 * @return {object} 包含 x, y 的点 */ pointAt: function pointAt(x1, y1, x2, y2, t) { return { x: (1 - t) * x1 + t * x2, y: (1 - t) * y1 + t * y2 }; }, /** * 点到线段的距离 * @param {number} x1 起始点 x * @param {number} y1 起始点 y * @param {number} x2 结束点 x * @param {number} y2 结束点 y * @param {number} x 测试点 x * @param {number} y 测试点 y * @return {number} 距离 */ pointDistance: function pointDistance(x1, y1, x2, y2, x, y) { // 投影距离 x1, y1 的向量,假设 p, p1, p2 三个点,投影点为 a // p1a = p1p.p1p2/|p1p2| * (p1p 的单位向量) var cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1); if (cross < 0) { return distance(x1, y1, x, y); } var lengthSquare = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); if (cross > lengthSquare) { return distance(x2, y2, x, y); } return this.pointToLine(x1, y1, x2, y2, x, y); }, /** * 点到直线的距离,而不是点到线段的距离 * @param {number} x1 起始点 x * @param {number} y1 起始点 y * @param {number} x2 结束点 x * @param {number} y2 结束点 y * @param {number} x 测试点 x * @param {number} y 测试点 y * @return {number} 距离 */ pointToLine: function pointToLine(x1, y1, x2, y2, x, y) { var d = [x2 - x1, y2 - y1]; // 如果端点相等,则判定点到点的距离 if (exactEquals(d, [0, 0])) { return Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1)); } var u = [-d[1], d[0]]; normalize(u, u); var a = [x - x1, y - y1]; return Math.abs(dot(a, u)); }, /** * 线段的角度 * @param {number} x1 起始点 x * @param {number} y1 起始点 y * @param {number} x2 结束点 x * @param {number} y2 结束点 y * @return {number} 导数 */ tangentAngle: function tangentAngle(x1, y1, x2, y2) { return Math.atan2(y2 - y1, x2 - x1); } }; var EPSILON = 0.0001; /** * 使用牛顿切割法求最近的点 * @param {number[]} xArr 点的 x 数组 * @param {number[]} yArr 点的 y 数组 * @param {number} x 指定的点 x * @param {number} y 指定的点 y * @param {Function} tCallback 差值函数 */ function nearestPoint(xArr, yArr, x, y, tCallback, length) { var t = -1; var d = Infinity; var v0 = [x, y]; var segNum = 20; if (length && length > 200) { segNum = length / 10; } var increaseRate = 1 / segNum; var interval = increaseRate / 10; for (var i = 0; i <= segNum; i++) { var _t = i * increaseRate; var v1 = [tCallback.apply(void 0, xArr.concat([_t])), tCallback.apply(void 0, yArr.concat([_t]))]; var d1 = distance(v0[0], v0[1], v1[0], v1[1]); if (d1 < d) { t = _t; d = d1; } } // 提前终止 if (t === 0) { return { x: xArr[0], y: yArr[0] }; } if (t === 1) { var count = xArr.length; return { x: xArr[count - 1], y: yArr[count - 1] }; } d = Infinity; for (var _i = 0; _i < 32; _i++) { if (interval < EPSILON) { break; } var prev = t - interval; var next = t + interval; var _v = [tCallback.apply(void 0, xArr.concat([prev])), tCallback.apply(void 0, yArr.concat([prev]))]; var _d = distance(v0[0], v0[1], _v[0], _v[1]); if (prev >= 0 && _d < d) { t = prev; d = _d; } else { var v2 = [tCallback.apply(void 0, xArr.concat([next])), tCallback.apply(void 0, yArr.concat([next]))]; var d2 = distance(v0[0], v0[1], v2[0], v2[1]); if (next <= 1 && d2 < d) { t = next; d = d2; } else { interval *= 0.5; } } } return { x: tCallback.apply(void 0, xArr.concat([t])), y: tCallback.apply(void 0, yArr.concat([t])) }; } // 近似求解 https://community.khronos.org/t/3d-cubic-bezier-segment-length/62363/2 function snapLength(xArr, yArr) { var totalLength = 0; var count = xArr.length; for (var i = 0; i < count; i++) { var x = xArr[i]; var y = yArr[i]; var nextX = xArr[(i + 1) % count]; var nextY = yArr[(i + 1) % count]; totalLength += distance(x, y, nextX, nextY); } return totalLength / 2; } // 差值公式 function quadraticAt(p0, p1, p2, t) { var onet = 1 - t; return onet * onet * p0 + 2 * t * onet * p1 + t * t * p2; } // 求极值 function extrema(p0, p1, p2) { var a = p0 + p2 - 2 * p1; if (isNumberEqual(a, 0)) { return [0.5]; } var rst = (p0 - p1) / a; if (rst <= 1 && rst >= 0) { return [rst]; } return []; } function derivativeAt(p0, p1, p2, t) { return 2 * (1 - t) * (p1 - p0) + 2 * t * (p2 - p1); } // 分割贝塞尔曲线 function divideQuadratic(x1, y1, x2, y2, x3, y3, t) { // 划分点 var xt = quadraticAt(x1, x2, x3, t); var yt = quadraticAt(y1, y2, y3, t); // 分割的第一条曲线的控制点 var controlPoint1 = line.pointAt(x1, y1, x2, y2, t); // 分割的第二条曲线的控制点 var controlPoint2 = line.pointAt(x2, y2, x3, y3, t); return [[x1, y1, controlPoint1.x, controlPoint1.y, xt, yt], [xt, yt, controlPoint2.x, controlPoint2.y, x3, y3]]; } // 使用迭代法取贝塞尔曲线的长度 function quadraticLength(x1, y1, x2, y2, x3, y3, iterationCount) { if (iterationCount === 0) { return (distance(x1, y1, x2, y2) + distance(x2, y2, x3, y3) + distance(x1, y1, x3, y3)) / 2; } var quadratics = divideQuadratic(x1, y1, x2, y2, x3, y3, 0.5); var left = quadratics[0]; var right = quadratics[1]; left.push(iterationCount - 1); right.push(iterationCount - 1); return quadraticLength.apply(void 0, left) + quadraticLength.apply(void 0, right); } var quadratic = { box: function box(x1, y1, x2, y2, x3, y3) { var xExtrema = extrema(x1, x2, x3)[0]; var yExtrema = extrema(y1, y2, y3)[0]; // 控制点不加入 box 的计算 var xArr = [x1, x3]; var yArr = [y1, y3]; if (xExtrema !== undefined) { xArr.push(quadraticAt(x1, x2, x3, xExtrema)); } if (yExtrema !== undefined) { yArr.push(quadraticAt(y1, y2, y3, yExtrema)); } return getBBoxByArray(xArr, yArr); }, length: function length(x1, y1, x2, y2, x3, y3) { return quadraticLength(x1, y1, x2, y2, x3, y3, 3); }, nearestPoint: function nearestPoint$1(x1, y1, x2, y2, x3, y3, x0, y0) { return nearestPoint([x1, x2, x3], [y1, y2, y3], x0, y0, quadraticAt); }, pointDistance: function pointDistance(x1, y1, x2, y2, x3, y3, x0, y0) { var point = this.nearestPoint(x1, y1, x2, y2, x3, y3, x0, y0); return distance(point.x, point.y, x0, y0); }, interpolationAt: quadraticAt, pointAt: function pointAt(x1, y1, x2, y2, x3, y3, t) { return { x: quadraticAt(x1, x2, x3, t), y: quadraticAt(y1, y2, y3, t) }; }, divide: function divide(x1, y1, x2, y2, x3, y3, t) { return divideQuadratic(x1, y1, x2, y2, x3, y3, t); }, tangentAngle: function tangentAngle(x1, y1, x2, y2, x3, y3, t) { var dx = derivativeAt(x1, x2, x3, t); var dy = derivativeAt(y1, y2, y3, t); var angle = Math.atan2(dy, dx); return piMod(angle); } }; function cubicAt(p0, p1, p2, p3, t) { var onet = 1 - t; // t * t * t 的性能大概是 Math.pow(t, 3) 的三倍 return onet * onet * onet * p0 + 3 * p1 * t * onet * onet + 3 * p2 * t * t * onet + p3 * t * t * t; } function derivativeAt$1(p0, p1, p2, p3, t) { var onet = 1 - t; return 3 * (onet * onet * (p1 - p0) + 2 * onet * t * (p2 - p1) + t * t * (p3 - p2)); } function extrema$1(p0, p1, p2, p3) { var a = -3 * p0 + 9 * p1 - 9 * p2 + 3 * p3; var b = 6 * p0 - 12 * p1 + 6 * p2; var c = 3 * p1 - 3 * p0; var extremas = []; var t1; var t2; var discSqrt; if (isNumberEqual(a, 0)) { if (!isNumberEqual(b, 0)) { t1 = -c / b; if (t1 >= 0 && t1 <= 1) { extremas.push(t1); } } } else { var disc = b * b - 4 * a * c; if (isNumberEqual(disc, 0)) { extremas.push(-b / (2 * a)); } else if (disc > 0) { discSqrt = Math.sqrt(disc); t1 = (-b + discSqrt) / (2 * a); t2 = (-b - discSqrt) / (2 * a); if (t1 >= 0 && t1 <= 1) { extremas.push(t1); } if (t2 >= 0 && t2 <= 1) { extremas.push(t2); } } } return extremas; } // 分割贝塞尔曲线 function divideCubic(x1, y1, x2, y2, x3, y3, x4, y4, t) { // 划分点 var xt = cubicAt(x1, x2, x3, x4, t); var yt = cubicAt(y1, y2, y3, y4, t); // 计算两点之间的差值点 var c1 = line.pointAt(x1, y1, x2, y2, t); var c2 = line.pointAt(x2, y2, x3, y3, t); var c3 = line.pointAt(x3, y3, x4, y4, t); var c12 = line.pointAt(c1.x, c1.y, c2.x, c2.y, t); var c23 = line.pointAt(c2.x, c2.y, c3.x, c3.y, t); return [[x1, y1, c1.x, c1.y, c12.x, c12.y, xt, yt], [xt, yt, c23.x, c23.y, c3.x, c3.y, x4, y4]]; } // 使用迭代法取贝塞尔曲线的长度,二阶和三阶分开写,更清晰和便于调试 function cubicLength(x1, y1, x2, y2, x3, y3, x4, y4, iterationCount) { if (iterationCount === 0) { return snapLength([x1, x2, x3, x4], [y1, y2, y3, y4]); } var cubics = divideCubic(x1, y1, x2, y2, x3, y3, x4, y4, 0.5); var left = [].concat(cubics[0], [iterationCount - 1]); var right = [].concat(cubics[1], [iterationCount - 1]); return cubicLength.apply(void 0, left) + cubicLength.apply(void 0, right); } var cubic = { extrema: extrema$1, box: function box(x1, y1, x2, y2, x3, y3, x4, y4) { var xArr = [x1, x4]; var yArr = [y1, y4]; var xExtrema = extrema$1(x1, x2, x3, x4); var yExtrema = extrema$1(y1, y2, y3, y4); for (var i = 0; i < xExtrema.length; i++) { xArr.push(cubicAt(x1, x2, x3, x4, xExtrema[i])); } for (var _i = 0; _i < yExtrema.length; _i++) { yArr.push(cubicAt(y1, y2, y3, y4, yExtrema[_i])); } return getBBoxByArray(xArr, yArr); }, length: function length(x1, y1, x2, y2, x3, y3, x4, y4) { // 迭代三次,划分成 8 段求长度 return cubicLength(x1, y1, x2, y2, x3, y3, x4, y4, 3); }, nearestPoint: function nearestPoint$1(x1, y1, x2, y2, x3, y3, x4, y4, x0, y0, length) { return nearestPoint([x1, x2, x3, x4], [y1, y2, y3, y4], x0, y0, cubicAt, length); }, pointDistance: function pointDistance(x1, y1, x2, y2, x3, y3, x4, y4, x0, y0, length) { var point = this.nearestPoint(x1, y1, x2, y2, x3, y3, x4, y4, x0, y0, length); return distance(point.x, point.y, x0, y0); }, interpolationAt: cubicAt, pointAt: function pointAt(x1, y1, x2, y2, x3, y3, x4, y4, t) { return { x: cubicAt(x1, x2, x3, x4, t), y: cubicAt(y1, y2, y3, y4, t) }; }, divide: function divide(x1, y1, x2, y2, x3, y3, x4, y4, t) { return divideCubic(x1, y1, x2, y2, x3, y3, x4, y4, t); }, tangentAngle: function tangentAngle(x1, y1, x2, y2, x3, y3, x4, y4, t) { var dx = derivativeAt$1(x1, x2, x3, x4, t); var dy = derivativeAt$1(y1, y2, y3, y4, t); return piMod(Math.atan2(dy, dx)); } }; function distance$1(x1, y1, x2, y2) { var dx = x1 - x2; var dy = y1 - y2; return Math.sqrt(dx * dx + dy * dy); } function inBox(minX, minY, width, height, x, y) { return x >= minX && x <= minX + width && y >= minY && y <= minY + height; } function inRect(minX, minY, width, height, lineWidth, x, y) { var halfWidth = lineWidth / 2; // 将四个边看做矩形来检测,比边的检测算法要快 return inBox(minX - halfWidth, minY - halfWidth, width, lineWidth, x, y) || // 上边 inBox(minX + width - halfWidth, minY - halfWidth, lineWidth, height, x, y) || // 右边 inBox(minX + halfWidth, minY + height - halfWidth, width, lineWidth, x, y) || // 下边 inBox(minX - halfWidth, minY + halfWidth, lineWidth, height, x, y); // 左边 } function inArc(cx, cy, r, startAngle, endAngle, lineWidth, x, y) { var angle = (Math.atan2(y - cy, x - cx) + Math.PI * 2) % (Math.PI * 2); // 转换到 0 - 2 * Math.PI 之间 // if (angle < startAngle || angle > endAngle) { // return false; // } var point = { x: cx + r * Math.cos(angle), y: cy + r * Math.sin(angle) }; return distance$1(point.x, point.y, x, y) <= lineWidth / 2; } function inLine(x1, y1, x2, y2, lineWidth, x, y) { var minX = Math.min(x1, x2); var maxX = Math.max(x1, x2); var minY = Math.min(y1, y2); var maxY = Math.max(y1, y2); var halfWidth = lineWidth / 2; // 因为目前的方案是计算点到直线的距离,而有可能会在延长线上,所以要先判断是否在包围盒内 // 这种方案会在水平或者竖直的情况下载线的延长线上有半 lineWidth 的误差 if (!(x >= minX - halfWidth && x <= maxX + halfWidth && y >= minY - halfWidth && y <= maxY + halfWidth)) { return false; } // 因为已经计算了包围盒,所以仅需要计算到直线的距离即可,可以显著提升性能 return line.pointToLine(x1, y1, x2, y2, x, y) <= lineWidth / 2; } function inPolyline(points, lineWidth, x, y, isClose) { var count = points.length; if (count < 2) { return false; } for (var i = 0; i < count - 1; i++) { var x1 = points[i][0]; var y1 = points[i][1]; var x2 = points[i + 1][0]; var y2 = points[i + 1][1]; if (inLine(x1, y1, x2, y2, lineWidth, x, y)) { return true; } } // 如果封闭,则计算起始点和结束点的边 if (isClose) { var first = points[0]; var last = points[count - 1]; if (inLine(first[0], first[1], last[0], last[1], lineWidth, x, y)) { return true; } } return false; } // 多边形的射线检测,参考:https://blog.csdn.net/WilliamSun0122/article/details/77994526 var tolerance = 1e-6; // 三态函数,判断两个double在eps精度下的大小关系 function dcmp(x) { if (Math.abs(x) < tolerance) { return 0; } return x < 0 ? -1 : 1; } // 判断点Q是否在p1和p2的线段上 function onSegment(p1, p2, q) { if ((q[0] - p1[0]) * (p2[1] - p1[1]) === (p2[0] - p1[0]) * (q[1] - p1[1]) && Math.min(p1[0], p2[0]) <= q[0] && q[0] <= Math.max(p1[0], p2[0]) && Math.min(p1[1], p2[1]) <= q[1] && q[1] <= Math.max(p1[1], p2[1])) { return true; } return false; } // 判断点P在多边形内-射线法 function inPolygon(points, x, y) { var isHit = false; var n = points.length; if (n <= 2) { // svg 中点小于 3 个时,不显示,也无法被拾取 return false; } for (var i = 0; i < n; i++) { var p1 = points[i]; var p2 = points[(i + 1) % n]; if (onSegment(p1, p2, [x, y])) { // 点在多边形一条边上 return true; } // 前一个判断min(p1[1],p2[1]) 0 !== dcmp(p2[1] - y) > 0 && dcmp(x - (y - p1[1]) * (p1[0] - p2[0]) / (p1[1] - p2[1]) - p1[0]) < 0) { isHit = !isHit; } } return isHit; } function inPolygons(polygons, x, y) { var isHit = false; for (var i = 0; i < polygons.length; i++) { var points = polygons[i]; isHit = inPolygon(points, x, y); if (isHit) { break; } } return isHit; } function isPointInPath(displayObject, position, isClipPath) { var _displayObject$parsed = displayObject.parsedStyle, r = _displayObject$parsed.r, fill = _displayObject$parsed.fill, stroke = _displayObject$parsed.stroke, lineWidth = _displayObject$parsed.lineWidth, increasedLineWidthForHitTesting = _displayObject$parsed.increasedLineWidthForHitTesting, pointerEvents = _displayObject$parsed.pointerEvents; var halfLineWidth = ((lineWidth || 0) + (increasedLineWidthForHitTesting || 0)) / 2; var absDistance = distance$1(r, r, position.x, position.y); var _isFillOrStrokeAffect = gLite.isFillOrStrokeAffected(pointerEvents, fill, stroke), hasFill = _isFillOrStrokeAffect[0], hasStroke = _isFillOrStrokeAffect[1]; if (hasFill && hasStroke || isClipPath) { return absDistance <= r + halfLineWidth; } if (hasFill) { return absDistance <= r; } if (hasStroke) { return absDistance >= r - halfLineWidth && absDistance <= r + halfLineWidth; } return false; } function ellipseDistance(squareX, squareY, rx, ry) { return squareX / (rx * rx) + squareY / (ry * ry); } function isPointInPath$1(displayObject, position, isClipPath) { var _displayObject$parsed = displayObject.parsedStyle, rx = _displayObject$parsed.rx, ry = _displayObject$parsed.ry, fill = _displayObject$parsed.fill, stroke = _displayObject$parsed.stroke, lineWidth = _displayObject$parsed.lineWidth, increasedLineWidthForHitTesting = _displayObject$parsed.increasedLineWidthForHitTesting, pointerEvents = _displayObject$parsed.pointerEvents; var x = position.x, y = position.y; var _isFillOrStrokeAffect = gLite.isFillOrStrokeAffected(pointerEvents, fill, stroke), hasFill = _isFillOrStrokeAffect[0], hasStroke = _isFillOrStrokeAffect[1]; var halfLineWith = ((lineWidth || 0) + (increasedLineWidthForHitTesting || 0)) / 2; var squareX = (x - rx) * (x - rx); var squareY = (y - ry) * (y - ry); // 使用椭圆的公式: x*x/rx*rx + y*y/ry*ry = 1; if (hasFill && hasStroke || isClipPath) { return ellipseDistance(squareX, squareY, rx + halfLineWith, ry + halfLineWith) <= 1; } if (hasFill) { return ellipseDistance(squareX, squareY, rx, ry) <= 1; } if (hasStroke) { return ellipseDistance(squareX, squareY, rx - halfLineWith, ry - halfLineWith) >= 1 && ellipseDistance(squareX, squareY, rx + halfLineWith, ry + halfLineWith) <= 1; } return false; } function isPointInPath$2(displayObject, position, isClipPath) { var _displayObject$parsed = displayObject.parsedStyle, x1 = _displayObject$parsed.x1, y1 = _displayObject$parsed.y1, x2 = _displayObject$parsed.x2, y2 = _displayObject$parsed.y2, lineWidth = _displayObject$parsed.lineWidth, increasedLineWidthForHitTesting = _displayObject$parsed.increasedLineWidthForHitTesting, _displayObject$parsed2 = _displayObject$parsed.defX, x = _displayObject$parsed2 === void 0 ? 0 : _displayObject$parsed2, _displayObject$parsed3 = _displayObject$parsed.defY, y = _displayObject$parsed3 === void 0 ? 0 : _displayObject$parsed3, pointerEvents = _displayObject$parsed.pointerEvents, fill = _displayObject$parsed.fill, stroke = _displayObject$parsed.stroke; var _isFillOrStrokeAffect = gLite.isFillOrStrokeAffected(pointerEvents, fill, stroke), hasStroke = _isFillOrStrokeAffect[1]; if (!hasStroke && !isClipPath || !lineWidth) { return false; } return inLine(x1, y1, x2, y2, (lineWidth || 0) + (increasedLineWidthForHitTesting || 0), position.x + x, position.y + y); } function rotateVector(x, y, rad) { var X = x * Math.cos(rad) - y * Math.sin(rad); var Y = x * Math.sin(rad) + y * Math.cos(rad); return { x: X, y: Y }; } /** * Converts A (arc-to) segments to C (cubic-bezier-to). * * For more information of where this math came from visit: * http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes */ function arcToCubic(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2, recursive) { var x1 = X1; var y1 = Y1; var rx = RX; var ry = RY; var x2 = X2; var y2 = Y2; // for more information of where this Math came from visit: // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes var d120 = (Math.PI * 120) / 180; var rad = (Math.PI / 180) * (+angle || 0); /** @type {number[]} */ var res = []; var xy; var f1; var f2; var cx; var cy; if (!recursive) { xy = rotateVector(x1, y1, -rad); x1 = xy.x; y1 = xy.y; xy = rotateVector(x2, y2, -rad); x2 = xy.x; y2 = xy.y; var x = (x1 - x2) / 2; var y = (y1 - y2) / 2; var h = (x * x) / (rx * rx) + (y * y) / (ry * ry); if (h > 1) { h = Math.sqrt(h); rx *= h; ry *= h; } var rx2 = rx * rx; var ry2 = ry * ry; var k = (LAF === SF ? -1 : 1) * Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))); cx = (k * rx * y) / ry + (x1 + x2) / 2; cy = (k * -ry * x) / rx + (y1 + y2) / 2; // eslint-disable-next-line no-bitwise -- Impossible to satisfy no-bitwise f1 = Math.asin(((((y1 - cy) / ry) * Math.pow(10, 9)) >> 0) / Math.pow(10, 9)); // eslint-disable-next-line no-bitwise -- Impossible to satisfy no-bitwise f2 = Math.asin(((((y2 - cy) / ry) * Math.pow(10, 9)) >> 0) / Math.pow(10, 9)); f1 = x1 < cx ? Math.PI - f1 : f1; f2 = x2 < cx ? Math.PI - f2 : f2; if (f1 < 0) f1 = Math.PI * 2 + f1; if (f2 < 0) f2 = Math.PI * 2 + f2; if (SF && f1 > f2) { f1 -= Math.PI * 2; } if (!SF && f2 > f1) { f2 -= Math.PI * 2; } } else { f1 = recursive[0], f2 = recursive[1], cx = recursive[2], cy = recursive[3]; } var df = f2 - f1; if (Math.abs(df) > d120) { var f2old = f2; var x2old = x2; var y2old = y2; f2 = f1 + d120 * (SF && f2 > f1 ? 1 : -1); x2 = cx + rx * Math.cos(f2); y2 = cy + ry * Math.sin(f2); res = arcToCubic(x2, y2, rx, ry, angle, 0, SF, x2old, y2old, [f2, f2old, cx, cy]); } df = f2 - f1; var c1 = Math.cos(f1); var s1 = Math.sin(f1); var c2 = Math.cos(f2); var s2 = Math.sin(f2); var t = Math.tan(df / 4); var hx = (4 / 3) * rx * t; var hy = (4 / 3) * ry * t; var m1 = [x1, y1]; var m2 = [x1 + hx * s1, y1 - hy * c1]; var m3 = [x2 + hx * s2, y2 - hy * c2]; var m4 = [x2, y2]; m2[0] = 2 * m1[0] - m2[0]; m2[1] = 2 * m1[1] - m2[1]; if (recursive) { return m2.concat(m3, m4, res); // return [...m2, ...m3, ...m4, ...res]; } res = m2.concat(m3, m4, res); // res = [...m2, ...m3, ...m4, ...res]; var newres = []; for (var i = 0, ii = res.length; i < ii; i += 1) { newres[i] = i % 2 ? rotateVector(res[i - 1], res[i], rad).y : rotateVector(res[i], res[i + 1], rad).x; } return newres; } // const TAU = Math.PI * 2; // const mapToEllipse = ( // { x, y }: { x: number; y: number }, // rx: number, // ry: number, // cosphi: number, // sinphi: number, // centerx: number, // centery: number, // ) => { // x *= rx; // y *= ry; // const xp = cosphi * x - sinphi * y; // const yp = sinphi * x + cosphi * y; // return { // x: xp + centerx, // y: yp + centery, // }; // }; // const approxUnitArc = (ang1: number, ang2: number) => { // // If 90 degree circular arc, use a constant // // as derived from http://spencermortensen.com/articles/bezier-circle // const a = // ang2 === 1.5707963267948966 // ? 0.551915024494 // : ang2 === -1.5707963267948966 // ? -0.551915024494 // : (4 / 3) * Math.tan(ang2 / 4); // const x1 = Math.cos(ang1); // const y1 = Math.sin(ang1); // const x2 = Math.cos(ang1 + ang2); // const y2 = Math.sin(ang1 + ang2); // return [ // { // x: x1 - y1 * a, // y: y1 + x1 * a, // }, // { // x: x2 + y2 * a, // y: y2 - x2 * a, // }, // { // x: x2, // y: y2, // }, // ]; // }; // const vectorAngle = (ux: number, uy: number, vx: number, vy: number) => { // const sign = ux * vy - uy * vx < 0 ? -1 : 1; // let dot = ux * vx + uy * vy; // if (dot > 1) { // dot = 1; // } // if (dot < -1) { // dot = -1; // } // return sign * Math.acos(dot); // }; // const getArcCenter = ( // px: any, // py: any, // cx: any, // cy: any, // rx: number, // ry: number, // largeArcFlag: number, // sweepFlag: number, // sinphi: number, // cosphi: number, // pxp: number, // pyp: number, // ) => { // const rxsq = Math.pow(rx, 2); // const rysq = Math.pow(ry, 2); // const pxpsq = Math.pow(pxp, 2); // const pypsq = Math.pow(pyp, 2); // let radicant = rxsq * rysq - rxsq * pypsq - rysq * pxpsq; // if (radicant < 0) { // radicant = 0; // } // radicant /= rxsq * pypsq + rysq * pxpsq; // radicant = Math.sqrt(radicant) * (largeArcFlag === sweepFlag ? -1 : 1); // const centerxp = ((radicant * rx) / ry) * pyp; // const centeryp = ((radicant * -ry) / rx) * pxp; // const centerx = cosphi * centerxp - sinphi * centeryp + (px + cx) / 2; // const centery = sinphi * centerxp + cosphi * centeryp + (py + cy) / 2; // const vx1 = (pxp - centerxp) / rx; // const vy1 = (pyp - centeryp) / ry; // const vx2 = (-pxp - centerxp) / rx; // const vy2 = (-pyp - centeryp) / ry; // const ang1 = vectorAngle(1, 0, vx1, vy1); // let ang2 = vectorAngle(vx1, vy1, vx2, vy2); // if (sweepFlag === 0 && ang2 > 0) { // ang2 -= TAU; // } // if (sweepFlag === 1 && ang2 < 0) { // ang2 += TAU; // } // return [centerx, centery, ang1, ang2]; // }; // const arcToBezier = ({ px, py, cx, cy, rx, ry, xAxisRotation = 0, largeArcFlag = 0, sweepFlag = 0 }) => { // const curves = []; // if (rx === 0 || ry === 0) { // return [{ x1: 0, y1: 0, x2: 0, y2: 0, x: cx, y: cy }]; // } // const sinphi = Math.sin((xAxisRotation * TAU) / 360); // const cosphi = Math.cos((xAxisRotation * TAU) / 360); // const pxp = (cosphi * (px - cx)) / 2 + (sinphi * (py - cy)) / 2; // const pyp = (-sinphi * (px - cx)) / 2 + (cosphi * (py - cy)) / 2; // if (pxp === 0 && pyp === 0) { // return [{ x1: 0, y1: 0, x2: 0, y2: 0, x: cx, y: cy }]; // } // rx = Math.abs(rx); // ry = Math.abs(ry); // const lambda = Math.pow(pxp, 2) / Math.pow(rx, 2) + Math.pow(pyp, 2) / Math.pow(ry, 2); // if (lambda > 1) { // rx *= Math.sqrt(lambda); // ry *= Math.sqrt(lambda); // } // let [centerx, centery, ang1, ang2] = getArcCenter( // px, // py, // cx, // cy, // rx, // ry, // largeArcFlag, // sweepFlag, // sinphi, // cosphi, // pxp, // pyp, // ); // // If 'ang2' == 90.0000000001, then `ratio` will evaluate to // // 1.0000000001. This causes `segments` to be greater than one, which is an // // unecessary split, and adds extra points to the bezier curve. To alleviate // // this issue, we round to 1.0 when the ratio is close to 1.0. // let ratio = Math.abs(ang2) / (TAU / 4); // if (Math.abs(1.0 - ratio) < 0.0000001) { // ratio = 1.0; // } // const segments = Math.max(Math.ceil(ratio), 1); // ang2 /= segments; // for (let i = 0; i < segments; i++) { // curves.push(approxUnitArc(ang1, ang2)); // ang1 += ang2; // } // return curves.map((curve) => { // const { x: x1, y: y1 } = mapToEllipse(curve[0], rx, ry, cosphi, sinphi, centerx, centery); // const { x: x2, y: y2 } = mapToEllipse(curve[1], rx, ry, cosphi, sinphi, centerx, centery); // const { x, y } = mapToEllipse(curve[2], rx, ry, cosphi, sinphi, centerx, centery); // return { x1, y1, x2, y2, x, y }; // }); // }; // export function arcToCubic( // x1: number, // y1: number, // rx: number, // ry: number, // angle: number, // LAF: number, // SF: number, // x2: number, // y2: number, // ) { // const curves = arcToBezier({ // px: x1, // py: y1, // cx: x2, // cy: y2, // rx, // ry, // xAxisRotation: angle, // largeArcFlag: LAF, // sweepFlag: SF, // }); // return curves.reduce((prev, cur) => { // const { x1, y1, x2, y2, x, y } = cur; // prev.push(x1, y1, x2, y2, x, y); // return prev; // }, [] as number[]); // } var clamp = function (a, min, max) { if (a < min) { return min; } else if (a > max) { return max; } return a; }; // TODO: replace it with method in @antv/util function isPointInStroke(segments, lineWidth, px, py, length) { var isHit = false; var halfWidth = lineWidth / 2; for (var i = 0; i < segments.length; i++) { var segment = segments[i]; var currentPoint = segment.currentPoint, params = segment.params, prePoint = segment.prePoint, box = segment.box; // 如果在前面已经生成过包围盒,直接按照包围盒计算 if (box && !inBox(box.x - halfWidth, box.y - halfWidth, box.width + lineWidth, box.height + lineWidth, px, py)) { continue; } switch (segment.command) { // L 和 Z 都是直线, M 不进行拾取 case 'L': case 'Z': isHit = inLine(prePoint[0], prePoint[1], currentPoint[0], currentPoint[1], lineWidth, px, py); if (isHit) { return true; } break; case 'Q': var qDistance = quadratic.pointDistance(prePoint[0], prePoint[1], params[1], params[2], params[3], params[4], px, py); isHit = qDistance <= lineWidth / 2; if (isHit) { return true; } break; case 'C': var cDistance = cubic.pointDistance(prePoint[0], // 上一段结束位置, 即 C 的起始点 prePoint[1], params[1], // 'C' 的参数,1、2 为第一个控制点,3、4 为第二个控制点,5、6 为结束点 params[2], params[3], params[4], params[5], params[6], px, py, length); isHit = cDistance <= lineWidth / 2; if (isHit) { return true; } break; case 'A': // cache conversion result if (!segment.cubicParams) { segment.cubicParams = arcToCubic(prePoint[0], prePoint[1], params[1], params[2], params[3], params[4], params[5], params[6], params[7], undefined); } var args = segment.cubicParams; // fixArc var prePointInCubic = prePoint; for (var _i = 0; _i < args.length; _i += 6) { var _cDistance = cubic.pointDistance(prePointInCubic[0], // 上一段结束位置, 即 C 的起始点 prePointInCubic[1], args[_i], args[_i + 1], args[_i + 2], args[_i + 3], args[_i + 4], args[_i + 5], px, py, length); prePointInCubic = [args[_i + 4], args[_i + 5]]; isHit = _cDistance <= lineWidth / 2; if (isHit) { return true; } } break; } } return isHit; } function isPointInPath$3(displayObject, position, isClipPath, isPointInPath, renderingPluginContext, runtime) { var _displayObject$parsed = displayObject.parsedStyle, lineWidth = _displayObject$parsed.lineWidth, increasedLineWidthForHitTesting = _displayObject$parsed.increasedLineWidthForHitTesting, stroke = _displayObject$parsed.stroke, fill = _displayObject$parsed.fill, _displayObject$parsed2 = _displayObject$parsed.defX, x = _displayObject$parsed2 === void 0 ? 0 : _displayObject$parsed2, _displayObject$parsed3 = _displayObject$parsed.defY, y = _displayObject$parsed3 === void 0 ? 0 : _displayObject$parsed3, path = _displayObject$parsed.path, pointerEvents = _displayObject$parsed.pointerEvents; var segments = path.segments, hasArc = path.hasArc, polylines = path.polylines, polygons = path.polygons; var _isFillOrStrokeAffect = gLite.isFillOrStrokeAffected(pointerEvents, // Only a closed path can be filled. (polygons === null || polygons === void 0 ? void 0 : polygons.length) && fill, stroke), hasFill = _isFillOrStrokeAffect[0], hasStroke = _isFillOrStrokeAffect[1]; var totalLength = gLite.getOrCalculatePathTotalLength(displayObject); var isHit = false; if (hasFill || isClipPath) { if (hasArc) { // 存在曲线时,暂时使用 canvas 的 api 计算,后续可以进行多边形切割 isHit = isPointInPath(displayObject, position); } else { // 提取出来的多边形包含闭合的和非闭合的,在这里统一按照多边形处理 isHit = inPolygons(polygons, position.x + x, position.y + y) || inPolygons(polylines, position.x + x, position.y + y); } return isHit; } else if (hasStroke || isClipPath) { isHit = isPointInStroke(segments, (lineWidth || 0) + (increasedLineWidthForHitTesting || 0), position.x + x, position.y + y, totalLength); } return isHit; } function isPointInPath$4(displayObject, position, isClipPath) { var _displayObject$parsed = displayObject.parsedStyle, stroke = _displayObject$parsed.stroke, fill = _displayObject$parsed.fill, lineWidth = _displayObject$parsed.lineWidth, increasedLineWidthForHitTesting = _displayObject$parsed.increasedLineWidthForHitTesting, points = _displayObject$parsed.points, _displayObject$parsed2 = _displayObject$parsed.defX, x = _displayObject$parsed2 === void 0 ? 0 : _displayObject$parsed2, _displayObject$parsed3 = _displayObject$parsed.defY, y = _displayObject$parsed3 === void 0 ? 0 : _displayObject$parsed3, pointerEvents = _displayObject$parsed.pointerEvents; var _isFillOrStrokeAffect = gLite.isFillOrStrokeAffected(pointerEvents, fill, stroke), hasFill = _isFillOrStrokeAffect[0], hasStroke = _isFillOrStrokeAffect[1]; var isHit = false; if (hasStroke || isClipPath) { isHit = inPolyline(points.points, (lineWidth || 0) + (increasedLineWidthForHitTesting || 0), position.x + x, position.y + y, true); } if (!isHit && (hasFill || isClipPath)) { isHit = inPolygon(points.points, position.x + x, position.y + y); } return isHit; } function isPointInPath$5(displayObject, position, isClipPath) { var _displayObject$parsed = displayObject.parsedStyle, lineWidth = _displayObject$parsed.lineWidth, increasedLineWidthForHitTesting = _displayObject$parsed.increasedLineWidthForHitTesting, points = _displayObject$parsed.points, _displayObject$parsed2 = _displayObject$parsed.defX, x = _displayObject$parsed2 === void 0 ? 0 : _displayObject$parsed2, _displayObject$parsed3 = _displayObject$parsed.defY, y = _displayObject$parsed3 === void 0 ? 0 : _displayObject$parsed3, pointerEvents = _displayObject$parsed.pointerEvents, fill = _displayObject$parsed.fill, stroke = _displayObject$parsed.stroke; var _isFillOrStrokeAffect = gLite.isFillOrStrokeAffected(pointerEvents, fill, stroke), hasStroke = _isFillOrStrokeAffect[1]; if (!hasStroke && !isClipPath || !lineWidth) { return false; } return inPolyline(points.points, (lineWidth || 0) + (increasedLineWidthForHitTesting || 0), position.x + x, position.y + y, false); } function isPointInPath$6(displayObject, position, isClipPath, isPointInPath, runtime) { var _displayObject$parsed = displayObject.parsedStyle, radius = _displayObject$parsed.radius, fill = _displayObject$parsed.fill, stroke = _displayObject$parsed.stroke, lineWidth = _displayObject$parsed.lineWidth, increasedLineWidthForHitTesting = _displayObject$parsed.increasedLineWidthForHitTesting, width = _displayObject$parsed.width, height = _displayObject$parsed.height, pointerEvents = _displayObject$parsed.pointerEvents; var _isFillOrStrokeAffect = gLite.isFillOrStrokeAffected(pointerEvents, fill, stroke), hasFill = _isFillOrStrokeAffect[0], hasStroke = _isFillOrStrokeAffect[1]; var hasRadius = radius && radius.some(function (r) { return r !== 0; }); var lineWidthForHitTesting = (lineWidth || 0) + (increasedLineWidthForHitTesting || 0); // 无圆角时的策略 if (!hasRadius) { var halfWidth = lineWidthForHitTesting / 2; // 同时填充和带有边框 if (hasFill && hasStroke || isClipPath) { return inBox(0 - halfWidth, 0 - halfWidth, width + halfWidth, height + halfWidth, position.x, position.y); } // 仅填充 if (hasFill) { return inBox(0, 0, width, height, position.x, position.y); } if (hasStroke) { return inRect(0, 0, width, height, lineWidthForHitTesting, position.x, position.y); } } else { var isHit = false; if (hasStroke || isClipPath) { isHit = inRectWithRadius(0, 0, width, height, radius.map(function (r) { return clamp(r, 0, Math.min(Math.abs(width) / 2, Math.abs(height) / 2)); }), lineWidthForHitTesting, position.x, position.y); } // 仅填充时带有圆角的矩形直接通过图形拾取 // 以后可以改成纯数学的近似拾取,将圆弧切割成多边形 if (!isHit && (hasFill || isClipPath)) { isHit = isPointInPath(displayObject, position); } return isHit; } return false; } function inRectWithRadius(minX, minY, width, height, radiusArray, lineWidth, x, y) { var tlr = radiusArray[0], trr = radiusArray[1], brr = radiusArray[2], blr = radiusArray[3]; return inLine(minX + tlr, minY, minX + width - trr, minY, lineWidth, x, y) || inLine(minX + width, minY + trr, minX + width, minY + height - brr, lineWidth, x, y) || inLine(minX + width - brr, minY + height, minX + blr, minY + height, lineWidth, x, y) || inLine(minX, minY + height - blr, minX, minY + tlr, lineWidth, x, y) || inArc(minX + width - trr, minY + trr, trr, 1.5 * Math.PI, 2 * Math.PI, lineWidth, x, y) || inArc(minX + width - brr, minY + height - brr, brr, 0, 0.5 * Math.PI, lineWidth, x, y) || inArc(minX + blr, minY + height - blr, blr, 0.5 * Math.PI, Math.PI, lineWidth, x, y) || inArc(minX + tlr, minY + tlr, tlr, Math.PI, 1.5 * Math.PI, lineWidth, x, y); } function isPointInPath$7(displayObject, position, isClipPath, isPointInPath, renderingPluginContext, runtime) { var _displayObject$parsed = displayObject.parsedStyle, pointerEvents = _displayObject$parsed.pointerEvents, width = _displayObject$parsed.width, height = _displayObject$parsed.height; if (pointerEvents === 'non-transparent-pixel') { var offscreenCanvas = renderingPluginContext.config.offscreenCanvas; var canvas = runtime.offscreenCanvas.getOrCreateCanvas(offscreenCanvas); var context = runtime.offscreenCanvas.getOrCreateContext(offscreenCanvas, { willReadFrequently: true }); canvas.width = width; canvas.height = height; renderingPluginContext.defaultStyleRendererFactory[gLite.Shape.IMAGE].render(context, displayObject.parsedStyle, displayObject, undefined, undefined, undefined); var imagedata = context.getImageData(position.x, position.y, 1, 1).data; return imagedata.every(function (component) { return component !== 0; }); } return true; } var Plugin = /*#__PURE__*/function (_AbstractRendererPlug) { _inheritsLoose(Plugin, _AbstractRendererPlug); function Plugin() { var _this; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _AbstractRendererPlug.call.apply(_AbstractRendererPlug, [this].concat(args)) || this; _this.name = 'canvas-picker'; return _this; } var _proto = Plugin.prototype; _proto.init = function init() { var _pointInPathPickerFac; var trueFunc = function trueFunc() { return true; }; var pointInPathPickerFactory = (_pointInPathPickerFac = {}, _pointInPathPickerFac[gLite.Shape.CIRCLE] = isPointInPath, _pointInPathPickerFac[gLite.Shape.ELLIPSE] = isPointInPath$1, _pointInPathPickerFac[gLite.Shape.RECT] = isPointInPath$6, _pointInPathPickerFac[gLite.Shape.LINE] = isPointInPath$2, _pointInPathPickerFac[gLite.Shape.POLYLINE] = isPointInPath$5, _pointInPathPickerFac[gLite.Shape.POLYGON] = isPointInPath$4, _pointInPathPickerFac[gLite.Shape.PATH] = isPointInPath$3, _pointInPathPickerFac[gLite.Shape.TEXT] = trueFunc, _pointInPathPickerFac[gLite.Shape.GROUP] = null, _pointInPathPickerFac[gLite.Shape.IMAGE] = isPointInPath$7, _pointInPathPickerFac[gLite.Shape.HTML] = null, _pointInPathPickerFac[gLite.Shape.MESH] = null, _pointInPathPickerFac); // @ts-ignore this.context.pointInPathPickerFactory = pointInPathPickerFactory; this.addRenderingPlugin(new CanvasPickerPlugin()); }; _proto.destroy = function destroy() { // @ts-ignore delete this.context.pointInPathPickerFactory; this.removeAllRenderingPlugins(); }; return Plugin; }(gLite.AbstractRendererPlugin); exports.Plugin = Plugin; Object.defineProperty(exports, '__esModule', { value: true }); })));