| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362 |
- import { Point, findClosestClipPathTarget, isFillOrStrokeAffected, getOrCalculatePathTotalLength, Shape, AbstractRendererPlugin } from '@antv/g-lite';
- import { vec3, mat4, vec2 } from 'gl-matrix';
- import { arcToCubic, clamp } from '@antv/util';
- 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.");
- }
- var tmpVec3a = vec3.create();
- var tmpVec3b = vec3.create();
- var tmpVec3c = vec3.create();
- var tmpMat4 = mat4.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 = mat4.invert(tmpMat4, worldTransform);
- // transform client position to local space, do picking in local space
- var localPosition = vec3.transformMat4(tmpVec3b, vec3.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 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 = vec3.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 = 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 (vec2.exactEquals(d, [0, 0])) {
- return Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
- }
- var u = [-d[1], d[0]];
- vec2.normalize(u, u);
- var a = [x - x1, y - y1];
- return Math.abs(vec2.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])<P.y<=max(p1[1],p2[1])
- // 后一个判断被测点 在 射线与边交点 的左边
- if (dcmp(p1[1] - y) > 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 = 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 = 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 = 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);
- }
- // 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 = 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 = 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 = 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 = 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 = 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[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[Shape.CIRCLE] = isPointInPath, _pointInPathPickerFac[Shape.ELLIPSE] = isPointInPath$1, _pointInPathPickerFac[Shape.RECT] = isPointInPath$6, _pointInPathPickerFac[Shape.LINE] = isPointInPath$2, _pointInPathPickerFac[Shape.POLYLINE] = isPointInPath$5, _pointInPathPickerFac[Shape.POLYGON] = isPointInPath$4, _pointInPathPickerFac[Shape.PATH] = isPointInPath$3, _pointInPathPickerFac[Shape.TEXT] = trueFunc, _pointInPathPickerFac[Shape.GROUP] = null, _pointInPathPickerFac[Shape.IMAGE] = isPointInPath$7, _pointInPathPickerFac[Shape.HTML] = null, _pointInPathPickerFac[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;
- }(AbstractRendererPlugin);
- export { Plugin };
|