vis.js is a dynamic, browser-based visualization library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

612 lines
20 KiB

9 years ago
  1. let util = require("../../../../../util");
  2. /**
  3. * The Base Class for all edges.
  4. *
  5. * @class EdgeBase
  6. */
  7. class EdgeBase {
  8. /**
  9. * @param {Object} options
  10. * @param {Object} body
  11. * @param {Label} labelModule
  12. * @constructor EdgeBase
  13. */
  14. constructor(options, body, labelModule) {
  15. this.body = body;
  16. this.labelModule = labelModule;
  17. this.options = {};
  18. this.setOptions(options);
  19. this.colorDirty = true;
  20. this.color = {};
  21. this.selectionWidth = 2;
  22. this.hoverWidth = 1.5;
  23. this.fromPoint = this.from;
  24. this.toPoint = this.to;
  25. }
  26. /**
  27. * Connects a node to itself
  28. */
  29. connect() {
  30. this.from = this.body.nodes[this.options.from];
  31. this.to = this.body.nodes[this.options.to];
  32. }
  33. /**
  34. *
  35. * @returns {boolean} always false
  36. */
  37. cleanup() {
  38. return false;
  39. }
  40. /**
  41. *
  42. * @param {Object} options
  43. */
  44. setOptions(options) {
  45. this.options = options;
  46. this.from = this.body.nodes[this.options.from];
  47. this.to = this.body.nodes[this.options.to];
  48. this.id = this.options.id;
  49. }
  50. /**
  51. * Redraw a edge as a line
  52. * Draw this edge in the given canvas
  53. * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
  54. *
  55. * @param {CanvasRenderingContext2D} ctx
  56. * @param {Array} values
  57. * @param {boolean} selected
  58. * @param {boolean} hover
  59. * @param {vis.Node} viaNode
  60. * @private
  61. */
  62. drawLine(ctx, values, selected, hover, viaNode) {
  63. // set style
  64. ctx.strokeStyle = this.getColor(ctx, values, selected, hover);
  65. ctx.lineWidth = values.width;
  66. if (values.dashes !== false) {
  67. this._drawDashedLine(ctx, values, viaNode);
  68. }
  69. else {
  70. this._drawLine(ctx, values, viaNode);
  71. }
  72. }
  73. /**
  74. *
  75. * @param {CanvasRenderingContext2D} ctx
  76. * @param {Array} values
  77. * @param {vis.Node} viaNode
  78. * @param {{x: Number, y: Number}} [fromPoint]
  79. * @param {{x: Number, y: Number}} [toPoint]
  80. * @private
  81. */
  82. _drawLine(ctx, values, viaNode, fromPoint, toPoint) {
  83. if (this.from != this.to) {
  84. // draw line
  85. this._line(ctx, values, viaNode, fromPoint, toPoint);
  86. }
  87. else {
  88. let [x,y,radius] = this._getCircleData(ctx);
  89. this._circle(ctx, values, x, y, radius);
  90. }
  91. }
  92. /**
  93. *
  94. * @param {CanvasRenderingContext2D} ctx
  95. * @param {Array} values
  96. * @param {vis.Node} viaNode
  97. * @param {{x: Number, y: Number}} [fromPoint] TODO: Remove in next major release
  98. * @param {{x: Number, y: Number}} [toPoint] TODO: Remove in next major release
  99. * @private
  100. */
  101. _drawDashedLine(ctx, values, viaNode, fromPoint, toPoint) { // eslint-disable-line no-unused-vars
  102. ctx.lineCap = 'round';
  103. let pattern = [5,5];
  104. if (Array.isArray(values.dashes) === true) {
  105. pattern = values.dashes;
  106. }
  107. // only firefox and chrome support this method, else we use the legacy one.
  108. if (ctx.setLineDash !== undefined) {
  109. ctx.save();
  110. // set dash settings for chrome or firefox
  111. ctx.setLineDash(pattern);
  112. ctx.lineDashOffset = 0;
  113. // draw the line
  114. if (this.from != this.to) {
  115. // draw line
  116. this._line(ctx, values, viaNode);
  117. }
  118. else {
  119. let [x,y,radius] = this._getCircleData(ctx);
  120. this._circle(ctx, values, x, y, radius);
  121. }
  122. // restore the dash settings.
  123. ctx.setLineDash([0]);
  124. ctx.lineDashOffset = 0;
  125. ctx.restore();
  126. }
  127. else { // unsupporting smooth lines
  128. if (this.from != this.to) {
  129. // draw line
  130. ctx.dashedLine(this.from.x, this.from.y, this.to.x, this.to.y, pattern);
  131. }
  132. else {
  133. let [x,y,radius] = this._getCircleData(ctx);
  134. this._circle(ctx, values, x, y, radius);
  135. }
  136. // draw shadow if enabled
  137. this.enableShadow(ctx, values);
  138. ctx.stroke();
  139. // disable shadows for other elements.
  140. this.disableShadow(ctx, values);
  141. }
  142. }
  143. /**
  144. *
  145. * @param {Node} nearNode
  146. * @param {CanvasRenderingContext2D} ctx
  147. * @param {Object} options
  148. * @returns {{x: Number, y: Number}}
  149. */
  150. findBorderPosition(nearNode, ctx, options) {
  151. if (this.from != this.to) {
  152. return this._findBorderPosition(nearNode, ctx, options);
  153. }
  154. else {
  155. return this._findBorderPositionCircle(nearNode, ctx, options);
  156. }
  157. }
  158. /**
  159. *
  160. * @param {CanvasRenderingContext2D} ctx
  161. * @returns {{from: ({x: number, y: number, t: number}|*), to: ({x: number, y: number, t: number}|*)}}
  162. */
  163. findBorderPositions(ctx) {
  164. let from = {};
  165. let to = {};
  166. if (this.from != this.to) {
  167. from = this._findBorderPosition(this.from, ctx);
  168. to = this._findBorderPosition(this.to, ctx);
  169. }
  170. else {
  171. let [x,y] = this._getCircleData(ctx).slice(0, 2);
  172. from = this._findBorderPositionCircle(this.from, ctx, {x, y, low:0.25, high:0.6, direction:-1});
  173. to = this._findBorderPositionCircle(this.from, ctx, {x, y, low:0.6, high:0.8, direction:1});
  174. }
  175. return {from, to};
  176. }
  177. /**
  178. *
  179. * @param {CanvasRenderingContext2D} ctx
  180. * @returns {Array<Number>} x, y, radius
  181. * @private
  182. */
  183. _getCircleData(ctx) {
  184. let x, y;
  185. let node = this.from;
  186. let radius = this.options.selfReferenceSize;
  187. if (ctx !== undefined) {
  188. if (node.shape.width === undefined) {
  189. node.shape.resize(ctx);
  190. }
  191. }
  192. // get circle coordinates
  193. if (node.shape.width > node.shape.height) {
  194. x = node.x + node.shape.width * 0.5;
  195. y = node.y - radius;
  196. }
  197. else {
  198. x = node.x + radius;
  199. y = node.y - node.shape.height * 0.5;
  200. }
  201. return [x,y,radius];
  202. }
  203. /**
  204. * Get a point on a circle
  205. * @param {Number} x
  206. * @param {Number} y
  207. * @param {Number} radius
  208. * @param {Number} percentage - Value between 0 (line start) and 1 (line end)
  209. * @return {Object} point
  210. * @private
  211. */
  212. _pointOnCircle(x, y, radius, percentage) {
  213. let angle = percentage * 2 * Math.PI;
  214. return {
  215. x: x + radius * Math.cos(angle),
  216. y: y - radius * Math.sin(angle)
  217. }
  218. }
  219. /**
  220. * This function uses binary search to look for the point where the circle crosses the border of the node.
  221. * @param {vis.Node} node
  222. * @param {CanvasRenderingContext2D} ctx
  223. * @param {Object} options
  224. * @returns {*}
  225. * @private
  226. */
  227. _findBorderPositionCircle(node, ctx, options) {
  228. let x = options.x;
  229. let y = options.y;
  230. let low = options.low;
  231. let high = options.high;
  232. let direction = options.direction;
  233. let maxIterations = 10;
  234. let iteration = 0;
  235. let radius = this.options.selfReferenceSize;
  236. let pos, angle, distanceToBorder, distanceToPoint, difference;
  237. let threshold = 0.05;
  238. let middle = (low + high) * 0.5;
  239. while (low <= high && iteration < maxIterations) {
  240. middle = (low + high) * 0.5;
  241. pos = this._pointOnCircle(x, y, radius, middle);
  242. angle = Math.atan2((node.y - pos.y), (node.x - pos.x));
  243. distanceToBorder = node.distanceToBorder(ctx, angle);
  244. distanceToPoint = Math.sqrt(Math.pow(pos.x - node.x, 2) + Math.pow(pos.y - node.y, 2));
  245. difference = distanceToBorder - distanceToPoint;
  246. if (Math.abs(difference) < threshold) {
  247. break; // found
  248. }
  249. else if (difference > 0) { // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node.
  250. if (direction > 0) {
  251. low = middle;
  252. }
  253. else {
  254. high = middle;
  255. }
  256. }
  257. else {
  258. if (direction > 0) {
  259. high = middle;
  260. }
  261. else {
  262. low = middle;
  263. }
  264. }
  265. iteration++;
  266. }
  267. pos.t = middle;
  268. return pos;
  269. }
  270. /**
  271. * Get the line width of the edge. Depends on width and whether one of the
  272. * connected nodes is selected.
  273. * @param {boolean} selected
  274. * @param {boolean} hover
  275. * @returns {Number} width
  276. * @private
  277. */
  278. getLineWidth(selected, hover) {
  279. if (selected === true) {
  280. return Math.max(this.selectionWidth, 0.3 / this.body.view.scale);
  281. }
  282. else {
  283. if (hover === true) {
  284. return Math.max(this.hoverWidth, 0.3 / this.body.view.scale);
  285. }
  286. else {
  287. return Math.max(this.options.width, 0.3 / this.body.view.scale);
  288. }
  289. }
  290. }
  291. /**
  292. *
  293. * @param {CanvasRenderingContext2D} ctx
  294. * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
  295. * @param {boolean} selected - Unused
  296. * @param {boolean} hover - Unused
  297. * @returns {string}
  298. */
  299. getColor(ctx, values, selected, hover) { // eslint-disable-line no-unused-vars
  300. if (values.inheritsColor !== false) {
  301. // when this is a loop edge, just use the 'from' method
  302. if ((values.inheritsColor === 'both') && (this.from.id !== this.to.id)) {
  303. let grd = ctx.createLinearGradient(this.from.x, this.from.y, this.to.x, this.to.y);
  304. let fromColor, toColor;
  305. fromColor = this.from.options.color.highlight.border;
  306. toColor = this.to.options.color.highlight.border;
  307. if ((this.from.selected === false) && (this.to.selected === false)) {
  308. fromColor = util.overrideOpacity(this.from.options.color.border, values.opacity);
  309. toColor = util.overrideOpacity(this.to.options.color.border, values.opacity);
  310. }
  311. else if ((this.from.selected === true) && (this.to.selected === false)) {
  312. toColor = this.to.options.color.border;
  313. }
  314. else if ((this.from.selected === false) && (this.to.selected === true)) {
  315. fromColor = this.from.options.color.border;
  316. }
  317. grd.addColorStop(0, fromColor);
  318. grd.addColorStop(1, toColor);
  319. // -------------------- this returns -------------------- //
  320. return grd;
  321. }
  322. if (values.inheritsColor === "to") {
  323. return util.overrideOpacity(this.to.options.color.border, values.opacity);
  324. } else { // "from"
  325. return util.overrideOpacity(this.from.options.color.border, values.opacity);
  326. }
  327. } else {
  328. return util.overrideOpacity(values.color, values.opacity);
  329. }
  330. }
  331. /**
  332. * Draw a line from a node to itself, a circle
  333. *
  334. * @param {CanvasRenderingContext2D} ctx
  335. * @param {Array} values
  336. * @param {Number} x
  337. * @param {Number} y
  338. * @param {Number} radius
  339. * @private
  340. */
  341. _circle(ctx, values, x, y, radius) {
  342. // draw shadow if enabled
  343. this.enableShadow(ctx, values);
  344. // draw a circle
  345. ctx.beginPath();
  346. ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
  347. ctx.stroke();
  348. // disable shadows for other elements.
  349. this.disableShadow(ctx, values);
  350. }
  351. /**
  352. * Calculate the distance between a point (x3,y3) and a line segment from
  353. * (x1,y1) to (x2,y2).
  354. * x3,y3 is the point.
  355. * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment
  356. * @param {number} x1
  357. * @param {number} y1
  358. * @param {number} x2
  359. * @param {number} y2
  360. * @param {number} x3
  361. * @param {number} y3
  362. * @param {vis.Node} via
  363. * @param {Array} values
  364. * @returns {number}
  365. * @private
  366. */
  367. getDistanceToEdge(x1, y1, x2, y2, x3, y3, via, values) { // eslint-disable-line no-unused-vars
  368. let returnValue = 0;
  369. if (this.from != this.to) {
  370. returnValue = this._getDistanceToEdge(x1, y1, x2, y2, x3, y3, via)
  371. }
  372. else {
  373. let [x,y,radius] = this._getCircleData(undefined);
  374. let dx = x - x3;
  375. let dy = y - y3;
  376. returnValue = Math.abs(Math.sqrt(dx * dx + dy * dy) - radius);
  377. }
  378. if (this.labelModule.size.left < x3 &&
  379. this.labelModule.size.left + this.labelModule.size.width > x3 &&
  380. this.labelModule.size.top < y3 &&
  381. this.labelModule.size.top + this.labelModule.size.height > y3) {
  382. return 0;
  383. }
  384. else {
  385. return returnValue;
  386. }
  387. }
  388. /**
  389. *
  390. * @param {number} x1
  391. * @param {number} y1
  392. * @param {number} x2
  393. * @param {number} y2
  394. * @param {number} x3
  395. * @param {number} y3
  396. * @returns {number}
  397. * @private
  398. * @static
  399. */
  400. _getDistanceToLine(x1, y1, x2, y2, x3, y3) {
  401. let px = x2 - x1;
  402. let py = y2 - y1;
  403. let something = px * px + py * py;
  404. let u = ((x3 - x1) * px + (y3 - y1) * py) / something;
  405. if (u > 1) {
  406. u = 1;
  407. }
  408. else if (u < 0) {
  409. u = 0;
  410. }
  411. let x = x1 + u * px;
  412. let y = y1 + u * py;
  413. let dx = x - x3;
  414. let dy = y - y3;
  415. //# Note: If the actual distance does not matter,
  416. //# if you only want to compare what this function
  417. //# returns to other results of this function, you
  418. //# can just return the squared distance instead
  419. //# (i.e. remove the sqrt) to gain a little performance
  420. return Math.sqrt(dx * dx + dy * dy);
  421. }
  422. /**
  423. * @param {CanvasRenderingContext2D} ctx
  424. * @param {string} position
  425. * @param {vis.Node} viaNode
  426. * @param {boolean} selected
  427. * @param {boolean} hover
  428. * @param {Array} values
  429. * @returns {{point: *, core: {x: number, y: number}, angle: *, length: number, type: *}}
  430. */
  431. getArrowData(ctx, position, viaNode, selected, hover, values) {
  432. // set lets
  433. let angle;
  434. let arrowPoint;
  435. let node1;
  436. let node2;
  437. let guideOffset;
  438. let scaleFactor;
  439. let type;
  440. let lineWidth = values.width;
  441. if (position === 'from') {
  442. node1 = this.from;
  443. node2 = this.to;
  444. guideOffset = 0.1;
  445. scaleFactor = values.fromArrowScale;
  446. type = values.fromArrowType;
  447. }
  448. else if (position === 'to') {
  449. node1 = this.to;
  450. node2 = this.from;
  451. guideOffset = -0.1;
  452. scaleFactor = values.toArrowScale;
  453. type = values.toArrowType;
  454. }
  455. else {
  456. node1 = this.to;
  457. node2 = this.from;
  458. scaleFactor = values.middleArrowScale;
  459. type = values.middleArrowType;
  460. }
  461. // if not connected to itself
  462. if (node1 != node2) {
  463. if (position !== 'middle') {
  464. // draw arrow head
  465. if (this.options.smooth.enabled === true) {
  466. arrowPoint = this.findBorderPosition(node1, ctx, { via: viaNode });
  467. let guidePos = this.getPoint(Math.max(0.0, Math.min(1.0, arrowPoint.t + guideOffset)), viaNode);
  468. angle = Math.atan2((arrowPoint.y - guidePos.y), (arrowPoint.x - guidePos.x));
  469. } else {
  470. angle = Math.atan2((node1.y - node2.y), (node1.x - node2.x));
  471. arrowPoint = this.findBorderPosition(node1, ctx);
  472. }
  473. } else {
  474. angle = Math.atan2((node1.y - node2.y), (node1.x - node2.x));
  475. arrowPoint = this.getPoint(0.5, viaNode); // this is 0.6 to account for the size of the arrow.
  476. }
  477. } else {
  478. // draw circle
  479. let [x,y,radius] = this._getCircleData(ctx);
  480. if (position === 'from') {
  481. arrowPoint = this.findBorderPosition(this.from, ctx, { x, y, low: 0.25, high: 0.6, direction: -1 });
  482. angle = arrowPoint.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI;
  483. } else if (position === 'to') {
  484. arrowPoint = this.findBorderPosition(this.from, ctx, { x, y, low: 0.6, high: 1.0, direction: 1 });
  485. angle = arrowPoint.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI;
  486. } else {
  487. arrowPoint = this._pointOnCircle(x, y, radius, 0.175);
  488. angle = 3.9269908169872414; // === 0.175 * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI;
  489. }
  490. }
  491. let length = 15 * scaleFactor + 3 * lineWidth; // 3* lineWidth is the width of the edge.
  492. var xi = arrowPoint.x - length * 0.9 * Math.cos(angle);
  493. var yi = arrowPoint.y - length * 0.9 * Math.sin(angle);
  494. let arrowCore = { x: xi, y: yi };
  495. return { point: arrowPoint, core: arrowCore, angle: angle, length: length, type: type };
  496. }
  497. /**
  498. *
  499. * @param {CanvasRenderingContext2D} ctx
  500. * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
  501. * @param {boolean} selected
  502. * @param {boolean} hover
  503. * @param {Object} arrowData
  504. */
  505. drawArrowHead(ctx, values, selected, hover, arrowData) {
  506. // set style
  507. ctx.strokeStyle = this.getColor(ctx, values, selected, hover);
  508. ctx.fillStyle = ctx.strokeStyle;
  509. ctx.lineWidth = values.width;
  510. if (arrowData.type && arrowData.type.toLowerCase() === 'circle') {
  511. // draw circle at the end of the line
  512. ctx.circleEndpoint(arrowData.point.x, arrowData.point.y, arrowData.angle, arrowData.length);
  513. } else {
  514. // draw arrow at the end of the line
  515. ctx.arrowEndpoint(arrowData.point.x, arrowData.point.y, arrowData.angle, arrowData.length);
  516. }
  517. // draw shadow if enabled
  518. this.enableShadow(ctx, values);
  519. ctx.fill();
  520. // disable shadows for other elements.
  521. this.disableShadow(ctx, values);
  522. }
  523. /**
  524. *
  525. * @param {CanvasRenderingContext2D} ctx
  526. * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
  527. * @static
  528. */
  529. enableShadow(ctx, values) {
  530. if (values.shadow === true) {
  531. ctx.shadowColor = values.shadowColor;
  532. ctx.shadowBlur = values.shadowSize;
  533. ctx.shadowOffsetX = values.shadowX;
  534. ctx.shadowOffsetY = values.shadowY;
  535. }
  536. }
  537. /**
  538. *
  539. * @param {CanvasRenderingContext2D} ctx
  540. * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
  541. * @static
  542. */
  543. disableShadow(ctx, values) {
  544. if (values.shadow === true) {
  545. ctx.shadowColor = 'rgba(0,0,0,0)';
  546. ctx.shadowBlur = 0;
  547. ctx.shadowOffsetX = 0;
  548. ctx.shadowOffsetY = 0;
  549. }
  550. }
  551. }
  552. export default EdgeBase;