venn.src.js 85 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002
  1. /**
  2. * @license Highcharts JS v9.1.1 (2021-06-04)
  3. *
  4. * (c) 2017-2021 Highsoft AS
  5. * Authors: Jon Arild Nygard
  6. *
  7. * License: www.highcharts.com/license
  8. */
  9. 'use strict';
  10. (function (factory) {
  11. if (typeof module === 'object' && module.exports) {
  12. factory['default'] = factory;
  13. module.exports = factory;
  14. } else if (typeof define === 'function' && define.amd) {
  15. define('highcharts/modules/venn', ['highcharts'], function (Highcharts) {
  16. factory(Highcharts);
  17. factory.Highcharts = Highcharts;
  18. return factory;
  19. });
  20. } else {
  21. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  22. }
  23. }(function (Highcharts) {
  24. var _modules = Highcharts ? Highcharts._modules : {};
  25. function _registerModule(obj, path, args, fn) {
  26. if (!obj.hasOwnProperty(path)) {
  27. obj[path] = fn.apply(null, args);
  28. }
  29. }
  30. _registerModule(_modules, 'Mixins/Geometry.js', [], function () {
  31. /* *
  32. *
  33. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  34. *
  35. * */
  36. /**
  37. * Calculates the center between a list of points.
  38. * @private
  39. * @param {Array<Highcharts.PositionObject>} points
  40. * A list of points to calculate the center of.
  41. * @return {Highcharts.PositionObject}
  42. * Calculated center
  43. */
  44. var getCenterOfPoints = function getCenterOfPoints(points) {
  45. var sum = points.reduce(function (sum,
  46. point) {
  47. sum.x += point.x;
  48. sum.y += point.y;
  49. return sum;
  50. }, { x: 0, y: 0 });
  51. return {
  52. x: sum.x / points.length,
  53. y: sum.y / points.length
  54. };
  55. };
  56. /**
  57. * Calculates the distance between two points based on their x and y
  58. * coordinates.
  59. * @private
  60. * @param {Highcharts.PositionObject} p1
  61. * The x and y coordinates of the first point.
  62. * @param {Highcharts.PositionObject} p2
  63. * The x and y coordinates of the second point.
  64. * @return {number}
  65. * Returns the distance between the points.
  66. */
  67. var getDistanceBetweenPoints = function getDistanceBetweenPoints(p1,
  68. p2) {
  69. return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
  70. };
  71. /**
  72. * Calculates the angle between two points.
  73. * @todo add unit tests.
  74. * @private
  75. * @param {Highcharts.PositionObject} p1 The first point.
  76. * @param {Highcharts.PositionObject} p2 The second point.
  77. * @return {number} Returns the angle in radians.
  78. */
  79. var getAngleBetweenPoints = function getAngleBetweenPoints(p1,
  80. p2) {
  81. return Math.atan2(p2.x - p1.x,
  82. p2.y - p1.y);
  83. };
  84. var geometry = {
  85. getAngleBetweenPoints: getAngleBetweenPoints,
  86. getCenterOfPoints: getCenterOfPoints,
  87. getDistanceBetweenPoints: getDistanceBetweenPoints
  88. };
  89. return geometry;
  90. });
  91. _registerModule(_modules, 'Mixins/GeometryCircles.js', [_modules['Mixins/Geometry.js']], function (Geometry) {
  92. /* *
  93. *
  94. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  95. *
  96. * */
  97. var getAngleBetweenPoints = Geometry.getAngleBetweenPoints,
  98. getCenterOfPoints = Geometry.getCenterOfPoints,
  99. getDistanceBetweenPoints = Geometry.getDistanceBetweenPoints;
  100. /**
  101. * @private
  102. * @param {number} x
  103. * Number to round
  104. * @param {number} decimals
  105. * Number of decimals to round to
  106. * @return {number}
  107. * Rounded number
  108. */
  109. function round(x, decimals) {
  110. var a = Math.pow(10,
  111. decimals);
  112. return Math.round(x * a) / a;
  113. }
  114. /**
  115. * Calculates the area of a circle based on its radius.
  116. * @private
  117. * @param {number} r
  118. * The radius of the circle.
  119. * @return {number}
  120. * Returns the area of the circle.
  121. */
  122. function getAreaOfCircle(r) {
  123. if (r <= 0) {
  124. throw new Error('radius of circle must be a positive number.');
  125. }
  126. return Math.PI * r * r;
  127. }
  128. /**
  129. * Calculates the area of a circular segment based on the radius of the circle
  130. * and the height of the segment.
  131. * See http://mathworld.wolfram.com/CircularSegment.html
  132. * @private
  133. * @param {number} r
  134. * The radius of the circle.
  135. * @param {number} h
  136. * The height of the circular segment.
  137. * @return {number}
  138. * Returns the area of the circular segment.
  139. */
  140. function getCircularSegmentArea(r, h) {
  141. return r * r * Math.acos(1 - h / r) - (r - h) * Math.sqrt(h * (2 * r - h));
  142. }
  143. /**
  144. * Calculates the area of overlap between two circles based on their radiuses
  145. * and the distance between them.
  146. * See http://mathworld.wolfram.com/Circle-CircleIntersection.html
  147. * @private
  148. * @param {number} r1
  149. * Radius of the first circle.
  150. * @param {number} r2
  151. * Radius of the second circle.
  152. * @param {number} d
  153. * The distance between the two circles.
  154. * @return {number}
  155. * Returns the area of overlap between the two circles.
  156. */
  157. function getOverlapBetweenCircles(r1, r2, d) {
  158. var overlap = 0;
  159. // If the distance is larger than the sum of the radiuses then the circles
  160. // does not overlap.
  161. if (d < r1 + r2) {
  162. if (d <= Math.abs(r2 - r1)) {
  163. // If the circles are completely overlapping, then the overlap
  164. // equals the area of the smallest circle.
  165. overlap = getAreaOfCircle(r1 < r2 ? r1 : r2);
  166. }
  167. else {
  168. // Height of first triangle segment.
  169. var d1 = (r1 * r1 - r2 * r2 + d * d) / (2 * d),
  170. // Height of second triangle segment.
  171. d2 = d - d1;
  172. overlap = (getCircularSegmentArea(r1, r1 - d1) +
  173. getCircularSegmentArea(r2, r2 - d2));
  174. }
  175. // Round the result to two decimals.
  176. overlap = round(overlap, 14);
  177. }
  178. return overlap;
  179. }
  180. /**
  181. * Calculates the intersection points of two circles.
  182. *
  183. * NOTE: does not handle floating errors well.
  184. * @private
  185. * @param {Highcharts.CircleObject} c1
  186. * The first circle.
  187. * @param {Highcharts.CircleObject} c2
  188. * The second sircle.
  189. * @return {Array<Highcharts.PositionObject>}
  190. * Returns the resulting intersection points.
  191. */
  192. function getCircleCircleIntersection(c1, c2) {
  193. var d = getDistanceBetweenPoints(c1,
  194. c2),
  195. r1 = c1.r,
  196. r2 = c2.r;
  197. var points = [];
  198. if (d < r1 + r2 && d > Math.abs(r1 - r2)) {
  199. // If the circles are overlapping, but not completely overlapping, then
  200. // it exists intersecting points.
  201. var r1Square = r1 * r1,
  202. r2Square = r2 * r2,
  203. // d^2 - r^2 + R^2 / 2d
  204. x = (r1Square - r2Square + d * d) / (2 * d),
  205. // y^2 = R^2 - x^2
  206. y = Math.sqrt(r1Square - x * x),
  207. x1 = c1.x,
  208. x2 = c2.x,
  209. y1 = c1.y,
  210. y2 = c2.y,
  211. x0 = x1 + x * (x2 - x1) / d,
  212. y0 = y1 + x * (y2 - y1) / d,
  213. rx = -(y2 - y1) * (y / d),
  214. ry = -(x2 - x1) * (y / d);
  215. points = [
  216. { x: round(x0 + rx, 14), y: round(y0 - ry, 14) },
  217. { x: round(x0 - rx, 14), y: round(y0 + ry, 14) }
  218. ];
  219. }
  220. return points;
  221. }
  222. /**
  223. * Calculates all the intersection points for between a list of circles.
  224. * @private
  225. * @param {Array<Highcharts.CircleObject>} circles
  226. * The circles to calculate the points from.
  227. * @return {Array<Highcharts.GeometryObject>}
  228. * Returns a list of intersection points.
  229. */
  230. function getCirclesIntersectionPoints(circles) {
  231. return circles.reduce(function (points, c1, i, arr) {
  232. var additional = arr.slice(i + 1)
  233. .reduce(function (points,
  234. c2,
  235. j) {
  236. var indexes = [i,
  237. j + i + 1];
  238. return points.concat(getCircleCircleIntersection(c1, c2)
  239. .map(function (p) {
  240. p.indexes = indexes;
  241. return p;
  242. }));
  243. }, []);
  244. return points.concat(additional);
  245. }, []);
  246. }
  247. /**
  248. * Tests wether the first circle is completely overlapping the second circle.
  249. *
  250. * @private
  251. * @param {Highcharts.CircleObject} circle1 The first circle.
  252. * @param {Highcharts.CircleObject} circle2 The The second circle.
  253. * @return {boolean} Returns true if circle1 is completely overlapping circle2,
  254. * false if not.
  255. */
  256. function isCircle1CompletelyOverlappingCircle2(circle1, circle2) {
  257. return getDistanceBetweenPoints(circle1, circle2) + circle2.r <
  258. circle1.r + 1e-10;
  259. }
  260. /**
  261. * Tests wether a point lies within a given circle.
  262. * @private
  263. * @param {Highcharts.PositionObject} point
  264. * The point to test for.
  265. * @param {Highcharts.CircleObject} circle
  266. * The circle to test if the point is within.
  267. * @return {boolean}
  268. * Returns true if the point is inside, false if outside.
  269. */
  270. function isPointInsideCircle(point, circle) {
  271. return getDistanceBetweenPoints(point, circle) <= circle.r + 1e-10;
  272. }
  273. /**
  274. * Tests wether a point lies within a set of circles.
  275. * @private
  276. * @param {Highcharts.PositionObject} point
  277. * The point to test.
  278. * @param {Array<Highcharts.CircleObject>} circles
  279. * The list of circles to test against.
  280. * @return {boolean}
  281. * Returns true if the point is inside all the circles, false if not.
  282. */
  283. function isPointInsideAllCircles(point, circles) {
  284. return !circles.some(function (circle) {
  285. return !isPointInsideCircle(point, circle);
  286. });
  287. }
  288. /**
  289. * Tests wether a point lies outside a set of circles.
  290. *
  291. * TODO: add unit tests.
  292. * @private
  293. * @param {Highcharts.PositionObject} point
  294. * The point to test.
  295. * @param {Array<Highcharts.CircleObject>} circles
  296. * The list of circles to test against.
  297. * @return {boolean}
  298. * Returns true if the point is outside all the circles, false if not.
  299. */
  300. function isPointOutsideAllCircles(point, circles) {
  301. return !circles.some(function (circle) {
  302. return isPointInsideCircle(point, circle);
  303. });
  304. }
  305. /**
  306. * Calculates the points for the polygon of the intersection area between a set
  307. * of circles.
  308. *
  309. * @private
  310. * @param {Array<Highcharts.CircleObject>} circles
  311. * List of circles to calculate polygon of.
  312. * @return {Array<Highcharts.GeometryObject>} Return list of points in the
  313. * intersection polygon.
  314. */
  315. function getCirclesIntersectionPolygon(circles) {
  316. return getCirclesIntersectionPoints(circles)
  317. .filter(function (p) {
  318. return isPointInsideAllCircles(p, circles);
  319. });
  320. }
  321. /**
  322. * Calculate the path for the area of overlap between a set of circles.
  323. * @todo handle cases with only 1 or 0 arcs.
  324. * @private
  325. * @param {Array<Highcharts.CircleObject>} circles
  326. * List of circles to calculate area of.
  327. * @return {Highcharts.GeometryIntersectionObject|undefined}
  328. * Returns the path for the area of overlap. Returns an empty string if
  329. * there are no intersection between all the circles.
  330. */
  331. function getAreaOfIntersectionBetweenCircles(circles) {
  332. var intersectionPoints = getCirclesIntersectionPolygon(circles),
  333. result;
  334. if (intersectionPoints.length > 1) {
  335. // Calculate the center of the intersection points.
  336. var center_1 = getCenterOfPoints(intersectionPoints);
  337. intersectionPoints = intersectionPoints
  338. // Calculate the angle between the center and the points.
  339. .map(function (p) {
  340. p.angle = getAngleBetweenPoints(center_1, p);
  341. return p;
  342. })
  343. // Sort the points by the angle to the center.
  344. .sort(function (a, b) {
  345. return b.angle - a.angle;
  346. });
  347. var startPoint = intersectionPoints[intersectionPoints.length - 1];
  348. var arcs = intersectionPoints
  349. .reduce(function (data,
  350. p1) {
  351. var startPoint = data.startPoint,
  352. midPoint = getCenterOfPoints([startPoint,
  353. p1]);
  354. // Calculate the arc from the intersection points and their
  355. // circles.
  356. var arc = p1.indexes
  357. // Filter out circles that are not included in both
  358. // intersection points.
  359. .filter(function (index) {
  360. return startPoint.indexes.indexOf(index) > -1;
  361. })
  362. // Iterate the circles of the intersection points and
  363. // calculate arcs.
  364. .reduce(function (arc, index) {
  365. var circle = circles[index],
  366. angle1 = getAngleBetweenPoints(circle,
  367. p1),
  368. angle2 = getAngleBetweenPoints(circle,
  369. startPoint),
  370. angleDiff = angle2 - angle1 +
  371. (angle2 < angle1 ? 2 * Math.PI : 0),
  372. angle = angle2 - angleDiff / 2;
  373. var width = getDistanceBetweenPoints(midPoint, {
  374. x: circle.x + circle.r * Math.sin(angle),
  375. y: circle.y + circle.r * Math.cos(angle)
  376. });
  377. var r = circle.r;
  378. // Width can sometimes become to large due to floating
  379. // point errors
  380. if (width > r * 2) {
  381. width = r * 2;
  382. }
  383. // Get the arc with the smallest width.
  384. if (!arc || arc.width > width) {
  385. arc = {
  386. r: r,
  387. largeArc: width > r ? 1 : 0,
  388. width: width,
  389. x: p1.x,
  390. y: p1.y
  391. };
  392. }
  393. // Return the chosen arc.
  394. return arc;
  395. }, null);
  396. // If we find an arc then add it to the list and update p2.
  397. if (arc) {
  398. var r = arc.r;
  399. data.arcs.push(['A', r, r, 0, arc.largeArc, 1, arc.x, arc.y]);
  400. data.startPoint = p1;
  401. }
  402. return data;
  403. }, {
  404. startPoint: startPoint,
  405. arcs: []
  406. }).arcs;
  407. if (arcs.length === 0) {
  408. // empty
  409. }
  410. else if (arcs.length === 1) {
  411. // empty
  412. }
  413. else {
  414. arcs.unshift(['M', startPoint.x, startPoint.y]);
  415. result = {
  416. center: center_1,
  417. d: arcs
  418. };
  419. }
  420. }
  421. return result;
  422. }
  423. var geometryCircles = {
  424. getAreaOfCircle: getAreaOfCircle,
  425. getAreaOfIntersectionBetweenCircles: getAreaOfIntersectionBetweenCircles,
  426. getCircleCircleIntersection: getCircleCircleIntersection,
  427. getCirclesIntersectionPoints: getCirclesIntersectionPoints,
  428. getCirclesIntersectionPolygon: getCirclesIntersectionPolygon,
  429. getCircularSegmentArea: getCircularSegmentArea,
  430. getOverlapBetweenCircles: getOverlapBetweenCircles,
  431. isCircle1CompletelyOverlappingCircle2: isCircle1CompletelyOverlappingCircle2,
  432. isPointInsideCircle: isPointInsideCircle,
  433. isPointInsideAllCircles: isPointInsideAllCircles,
  434. isPointOutsideAllCircles: isPointOutsideAllCircles,
  435. round: round
  436. };
  437. return geometryCircles;
  438. });
  439. _registerModule(_modules, 'Mixins/NelderMead.js', [], function () {
  440. /* *
  441. *
  442. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  443. *
  444. * */
  445. /* eslint-disable valid-jsdoc */
  446. var getCentroid = function (simplex) {
  447. var arr = simplex.slice(0, -1),
  448. length = arr.length,
  449. result = [],
  450. sum = function (data,
  451. point) {
  452. data.sum += point[data.i];
  453. return data;
  454. };
  455. for (var i = 0; i < length; i++) {
  456. result[i] = arr.reduce(sum, { sum: 0, i: i }).sum / length;
  457. }
  458. return result;
  459. };
  460. /**
  461. * Finds an optimal position for a given point.
  462. * @todo add unit tests.
  463. * @todo add constraints to optimize the algorithm.
  464. * @private
  465. * @param {Highcharts.NelderMeadTestFunction} fn
  466. * The function to test a point.
  467. * @param {Highcharts.NelderMeadPointArray} initial
  468. * The initial point to optimize.
  469. * @return {Highcharts.NelderMeadPointArray}
  470. * Returns the opimized position of a point.
  471. */
  472. var nelderMead = function nelderMead(fn,
  473. initial) {
  474. var maxIterations = 100,
  475. sortByFx = function (a,
  476. b) {
  477. return a.fx - b.fx;
  478. }, pRef = 1, // Reflection parameter
  479. pExp = 2, // Expansion parameter
  480. pCon = -0.5, // Contraction parameter
  481. pOCon = pCon * pRef, // Outwards contraction parameter
  482. pShrink = 0.5; // Shrink parameter
  483. /**
  484. * @private
  485. */
  486. var weightedSum = function weightedSum(weight1,
  487. v1,
  488. weight2,
  489. v2) {
  490. return v1.map(function (x,
  491. i) {
  492. return weight1 * x + weight2 * v2[i];
  493. });
  494. };
  495. /**
  496. * @private
  497. */
  498. var getSimplex = function getSimplex(initial) {
  499. var n = initial.length,
  500. simplex = new Array(n + 1);
  501. // Initial point to the simplex.
  502. simplex[0] = initial;
  503. simplex[0].fx = fn(initial);
  504. // Create a set of extra points based on the initial.
  505. for (var i = 0; i < n; ++i) {
  506. var point = initial.slice();
  507. point[i] = point[i] ? point[i] * 1.05 : 0.001;
  508. point.fx = fn(point);
  509. simplex[i + 1] = point;
  510. }
  511. return simplex;
  512. };
  513. var updateSimplex = function (simplex,
  514. point) {
  515. point.fx = fn(point);
  516. simplex[simplex.length - 1] = point;
  517. return simplex;
  518. };
  519. var shrinkSimplex = function (simplex) {
  520. var best = simplex[0];
  521. return simplex.map(function (point) {
  522. var p = weightedSum(1 - pShrink,
  523. best,
  524. pShrink,
  525. point);
  526. p.fx = fn(p);
  527. return p;
  528. });
  529. };
  530. var getPoint = function (centroid,
  531. worst,
  532. a,
  533. b) {
  534. var point = weightedSum(a,
  535. centroid,
  536. b,
  537. worst);
  538. point.fx = fn(point);
  539. return point;
  540. };
  541. // Create a simplex
  542. var simplex = getSimplex(initial);
  543. // Iterate from 0 to max iterations
  544. for (var i = 0; i < maxIterations; i++) {
  545. // Sort the simplex
  546. simplex.sort(sortByFx);
  547. // Create a centroid from the simplex
  548. var worst = simplex[simplex.length - 1];
  549. var centroid = getCentroid(simplex);
  550. // Calculate the reflected point.
  551. var reflected = getPoint(centroid,
  552. worst, 1 + pRef, -pRef);
  553. if (reflected.fx < simplex[0].fx) {
  554. // If reflected point is the best, then possibly expand.
  555. var expanded = getPoint(centroid,
  556. worst, 1 + pExp, -pExp);
  557. simplex = updateSimplex(simplex, (expanded.fx < reflected.fx) ? expanded : reflected);
  558. }
  559. else if (reflected.fx >= simplex[simplex.length - 2].fx) {
  560. // If the reflected point is worse than the second worse, then
  561. // contract.
  562. var contracted = void 0;
  563. if (reflected.fx > worst.fx) {
  564. // If the reflected is worse than the worst point, do a
  565. // contraction
  566. contracted = getPoint(centroid, worst, 1 + pCon, -pCon);
  567. if (contracted.fx < worst.fx) {
  568. simplex = updateSimplex(simplex, contracted);
  569. }
  570. else {
  571. simplex = shrinkSimplex(simplex);
  572. }
  573. }
  574. else {
  575. // Otherwise do an outwards contraction
  576. contracted = getPoint(centroid, worst, 1 - pOCon, pOCon);
  577. if (contracted.fx < reflected.fx) {
  578. simplex = updateSimplex(simplex, contracted);
  579. }
  580. else {
  581. simplex = shrinkSimplex(simplex);
  582. }
  583. }
  584. }
  585. else {
  586. simplex = updateSimplex(simplex, reflected);
  587. }
  588. }
  589. return simplex[0];
  590. };
  591. var nelderMeadMixin = {
  592. getCentroid: getCentroid,
  593. nelderMead: nelderMead
  594. };
  595. return nelderMeadMixin;
  596. });
  597. _registerModule(_modules, 'Mixins/DrawPoint.js', [], function () {
  598. /* *
  599. *
  600. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  601. *
  602. * */
  603. var isFn = function (x) {
  604. return typeof x === 'function';
  605. };
  606. /* eslint-disable no-invalid-this, valid-jsdoc */
  607. /**
  608. * Handles the drawing of a component.
  609. * Can be used for any type of component that reserves the graphic property, and
  610. * provides a shouldDraw on its context.
  611. *
  612. * @private
  613. * @function draw
  614. * @param {DrawPointParams} params
  615. * Parameters.
  616. *
  617. * @todo add type checking.
  618. * @todo export this function to enable usage
  619. */
  620. var draw = function draw(params) {
  621. var _this = this;
  622. var animatableAttribs = params.animatableAttribs,
  623. onComplete = params.onComplete,
  624. css = params.css,
  625. renderer = params.renderer;
  626. var animation = (this.series && this.series.chart.hasRendered) ?
  627. // Chart-level animation on updates
  628. void 0 :
  629. // Series-level animation on new points
  630. (this.series &&
  631. this.series.options.animation);
  632. var graphic = this.graphic;
  633. if (this.shouldDraw()) {
  634. if (!graphic) {
  635. this.graphic = graphic =
  636. renderer[params.shapeType](params.shapeArgs)
  637. .add(params.group);
  638. }
  639. graphic
  640. .css(css)
  641. .attr(params.attribs)
  642. .animate(animatableAttribs, params.isNew ? false : animation, onComplete);
  643. }
  644. else if (graphic) {
  645. var destroy_1 = function () {
  646. _this.graphic = graphic = (graphic && graphic.destroy());
  647. if (isFn(onComplete)) {
  648. onComplete();
  649. }
  650. };
  651. // animate only runs complete callback if something was animated.
  652. if (Object.keys(animatableAttribs).length) {
  653. graphic.animate(animatableAttribs, void 0, function () {
  654. destroy_1();
  655. });
  656. }
  657. else {
  658. destroy_1();
  659. }
  660. }
  661. };
  662. /**
  663. * An extended version of draw customized for points.
  664. * It calls additional methods that is expected when rendering a point.
  665. * @private
  666. * @param {Highcharts.Dictionary<any>} params Parameters
  667. */
  668. var drawPoint = function drawPoint(params) {
  669. var point = this,
  670. attribs = params.attribs = params.attribs || {};
  671. // Assigning class in dot notation does go well in IE8
  672. // eslint-disable-next-line dot-notation
  673. attribs['class'] = point.getClassName();
  674. // Call draw to render component
  675. draw.call(point, params);
  676. };
  677. var drawPointModule = {
  678. draw: draw,
  679. drawPoint: drawPoint,
  680. isFn: isFn
  681. };
  682. return drawPointModule;
  683. });
  684. _registerModule(_modules, 'Series/Venn/VennPoint.js', [_modules['Mixins/DrawPoint.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (DrawPointMixin, SeriesRegistry, U) {
  685. /* *
  686. *
  687. * Imports
  688. *
  689. * */
  690. var __extends = (this && this.__extends) || (function () {
  691. var extendStatics = function (d,
  692. b) {
  693. extendStatics = Object.setPrototypeOf ||
  694. ({ __proto__: [] } instanceof Array && function (d,
  695. b) { d.__proto__ = b; }) ||
  696. function (d,
  697. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  698. return extendStatics(d, b);
  699. };
  700. return function (d, b) {
  701. extendStatics(d, b);
  702. function __() { this.constructor = d; }
  703. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  704. };
  705. })();
  706. var ScatterSeries = SeriesRegistry.seriesTypes.scatter;
  707. var extend = U.extend,
  708. isNumber = U.isNumber;
  709. /* *
  710. *
  711. * Class
  712. *
  713. * */
  714. var VennPoint = /** @class */ (function (_super) {
  715. __extends(VennPoint, _super);
  716. function VennPoint() {
  717. /* *
  718. *
  719. * Properties
  720. *
  721. * */
  722. var _this = _super !== null && _super.apply(this,
  723. arguments) || this;
  724. _this.options = void 0;
  725. _this.series = void 0;
  726. return _this;
  727. /* eslint-enable valid-jsdoc */
  728. }
  729. /* *
  730. *
  731. * Functions
  732. *
  733. * */
  734. /* eslint-disable valid-jsdoc */
  735. VennPoint.prototype.isValid = function () {
  736. return isNumber(this.value);
  737. };
  738. VennPoint.prototype.shouldDraw = function () {
  739. var point = this;
  740. // Only draw points with single sets.
  741. return !!point.shapeArgs;
  742. };
  743. return VennPoint;
  744. }(ScatterSeries.prototype.pointClass));
  745. extend(VennPoint.prototype, {
  746. draw: DrawPointMixin.drawPoint
  747. });
  748. /* *
  749. *
  750. * Default Export
  751. *
  752. * */
  753. return VennPoint;
  754. });
  755. _registerModule(_modules, 'Series/Venn/VennUtils.js', [_modules['Mixins/GeometryCircles.js'], _modules['Mixins/Geometry.js'], _modules['Mixins/NelderMead.js'], _modules['Core/Utilities.js']], function (GeometryCirclesModule, GeometryMixin, NelderMeadMixin, U) {
  756. /* *
  757. *
  758. * Experimental Highcharts module which enables visualization of a Venn
  759. * diagram.
  760. *
  761. * (c) 2016-2021 Highsoft AS
  762. * Authors: Jon Arild Nygard
  763. *
  764. * Layout algorithm by Ben Frederickson:
  765. * https://www.benfrederickson.com/better-venn-diagrams/
  766. *
  767. * License: www.highcharts.com/license
  768. *
  769. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  770. *
  771. * */
  772. var getAreaOfCircle = GeometryCirclesModule.getAreaOfCircle,
  773. getCircleCircleIntersection = GeometryCirclesModule.getCircleCircleIntersection,
  774. getOverlapBetweenCirclesByDistance = GeometryCirclesModule.getOverlapBetweenCircles,
  775. isPointInsideAllCircles = GeometryCirclesModule.isPointInsideAllCircles,
  776. isPointInsideCircle = GeometryCirclesModule.isPointInsideCircle,
  777. isPointOutsideAllCircles = GeometryCirclesModule.isPointOutsideAllCircles;
  778. var getDistanceBetweenPoints = GeometryMixin.getDistanceBetweenPoints;
  779. var extend = U.extend,
  780. isArray = U.isArray,
  781. isNumber = U.isNumber,
  782. isObject = U.isObject,
  783. isString = U.isString;
  784. /* *
  785. *
  786. * Namespace
  787. *
  788. * */
  789. var VennUtils;
  790. (function (VennUtils) {
  791. /* *
  792. *
  793. * Properties
  794. *
  795. * */
  796. VennUtils.geometry = GeometryMixin;
  797. VennUtils.geometryCircles = GeometryCirclesModule;
  798. VennUtils.nelderMead = NelderMeadMixin;
  799. /* *
  800. *
  801. * Functions
  802. *
  803. * */
  804. /**
  805. * Takes an array of relations and adds the properties `totalOverlap` and
  806. * `overlapping` to each set. The property `totalOverlap` is the sum of
  807. * value for each relation where this set is included. The property
  808. * `overlapping` is a map of how much this set is overlapping another set.
  809. * NOTE: This algorithm ignores relations consisting of more than 2 sets.
  810. * @private
  811. * @param {Array<Highcharts.VennRelationObject>} relations
  812. * The list of relations that should be sorted.
  813. * @return {Array<Highcharts.VennRelationObject>}
  814. * Returns the modified input relations with added properties `totalOverlap`
  815. * and `overlapping`.
  816. */
  817. function addOverlapToSets(relations) {
  818. // Calculate the amount of overlap per set.
  819. var mapOfIdToProps = relations
  820. // Filter out relations consisting of 2 sets.
  821. .filter(function (relation) {
  822. return relation.sets.length === 2;
  823. })
  824. // Sum up the amount of overlap for each set.
  825. .reduce(function (map, relation) {
  826. var sets = relation.sets;
  827. sets.forEach(function (set, i, arr) {
  828. if (!isObject(map[set])) {
  829. map[set] = {
  830. overlapping: {},
  831. totalOverlap: 0
  832. };
  833. }
  834. map[set].totalOverlap += relation.value;
  835. map[set].overlapping[arr[1 - i]] = relation.value;
  836. });
  837. return map;
  838. }, {});
  839. relations
  840. // Filter out single sets
  841. .filter(isSet)
  842. // Extend the set with the calculated properties.
  843. .forEach(function (set) {
  844. var properties = mapOfIdToProps[set.sets[0]];
  845. extend(set, properties);
  846. });
  847. // Returns the modified relations.
  848. return relations;
  849. }
  850. VennUtils.addOverlapToSets = addOverlapToSets;
  851. /**
  852. * Finds the root of a given function. The root is the input value needed
  853. * for a function to return 0.
  854. *
  855. * See https://en.wikipedia.org/wiki/Bisection_method#Algorithm
  856. *
  857. * TODO: Add unit tests.
  858. *
  859. * @param {Function} f
  860. * The function to find the root of.
  861. * @param {number} a
  862. * The lowest number in the search range.
  863. * @param {number} b
  864. * The highest number in the search range.
  865. * @param {number} [tolerance=1e-10]
  866. * The allowed difference between the returned value and root.
  867. * @param {number} [maxIterations=100]
  868. * The maximum iterations allowed.
  869. * @return {number}
  870. * Root number.
  871. */
  872. function bisect(f, a, b, tolerance, maxIterations) {
  873. var fA = f(a),
  874. fB = f(b),
  875. nMax = maxIterations || 100,
  876. tol = tolerance || 1e-10,
  877. delta = b - a,
  878. n = 1,
  879. x,
  880. fX;
  881. if (a >= b) {
  882. throw new Error('a must be smaller than b.');
  883. }
  884. else if (fA * fB > 0) {
  885. throw new Error('f(a) and f(b) must have opposite signs.');
  886. }
  887. if (fA === 0) {
  888. x = a;
  889. }
  890. else if (fB === 0) {
  891. x = b;
  892. }
  893. else {
  894. while (n++ <= nMax && fX !== 0 && delta > tol) {
  895. delta = (b - a) / 2;
  896. x = a + delta;
  897. fX = f(x);
  898. // Update low and high for next search interval.
  899. if (fA * fX > 0) {
  900. a = x;
  901. }
  902. else {
  903. b = x;
  904. }
  905. }
  906. }
  907. return x;
  908. }
  909. /**
  910. * Uses the bisection method to make a best guess of the ideal distance
  911. * between two circles too get the desired overlap.
  912. * Currently there is no known formula to calculate the distance from the
  913. * area of overlap, which makes the bisection method preferred.
  914. * @private
  915. * @param {number} r1
  916. * Radius of the first circle.
  917. * @param {number} r2
  918. * Radiues of the second circle.
  919. * @param {number} overlap
  920. * The wanted overlap between the two circles.
  921. * @return {number}
  922. * Returns the distance needed to get the wanted overlap between the two
  923. * circles.
  924. */
  925. function getDistanceBetweenCirclesByOverlap(r1, r2, overlap) {
  926. var maxDistance = r1 + r2,
  927. distance;
  928. if (overlap <= 0) {
  929. // If overlap is below or equal to zero, then there is no overlap.
  930. distance = maxDistance;
  931. }
  932. else if (getAreaOfCircle(r1 < r2 ? r1 : r2) <= overlap) {
  933. // When area of overlap is larger than the area of the smallest
  934. // circle, then it is completely overlapping.
  935. distance = 0;
  936. }
  937. else {
  938. distance = bisect(function (x) {
  939. var actualOverlap = getOverlapBetweenCirclesByDistance(r1,
  940. r2,
  941. x);
  942. // Return the differance between wanted and actual overlap.
  943. return overlap - actualOverlap;
  944. }, 0, maxDistance);
  945. }
  946. return distance;
  947. }
  948. VennUtils.getDistanceBetweenCirclesByOverlap = getDistanceBetweenCirclesByOverlap;
  949. /**
  950. * Finds the available width for a label, by taking the label position and
  951. * finding the largest distance, which is inside all internal circles, and
  952. * outside all external circles.
  953. *
  954. * @private
  955. * @param {Highcharts.PositionObject} pos
  956. * The x and y coordinate of the label.
  957. * @param {Array<Highcharts.CircleObject>} internal
  958. * Internal circles.
  959. * @param {Array<Highcharts.CircleObject>} external
  960. * External circles.
  961. * @return {number}
  962. * Returns available width for the label.
  963. */
  964. function getLabelWidth(pos, internal, external) {
  965. var radius = internal.reduce(function (min,
  966. circle) {
  967. return Math.min(circle.r,
  968. min);
  969. }, Infinity),
  970. // Filter out external circles that are completely overlapping.
  971. filteredExternals = external.filter(function (circle) {
  972. return !isPointInsideCircle(pos, circle);
  973. });
  974. var findDistance = function (maxDistance,
  975. direction) {
  976. return bisect(function (x) {
  977. var testPos = {
  978. x: pos.x + (direction * x),
  979. y: pos.y
  980. },
  981. isValid = (isPointInsideAllCircles(testPos,
  982. internal) &&
  983. isPointOutsideAllCircles(testPos,
  984. filteredExternals));
  985. // If the position is valid, then we want to move towards the
  986. // max distance. If not, then we want to away from the max
  987. // distance.
  988. return -(maxDistance - x) + (isValid ? 0 : Number.MAX_VALUE);
  989. }, 0, maxDistance);
  990. };
  991. // Find the smallest distance of left and right.
  992. return Math.min(findDistance(radius, -1), findDistance(radius, 1)) * 2;
  993. }
  994. VennUtils.getLabelWidth = getLabelWidth;
  995. /**
  996. * Calculates a margin for a point based on the iternal and external
  997. * circles. The margin describes if the point is well placed within the
  998. * internal circles, and away from the external.
  999. * @private
  1000. * @todo add unit tests.
  1001. * @param {Highcharts.PositionObject} point
  1002. * The point to evaluate.
  1003. * @param {Array<Highcharts.CircleObject>} internal
  1004. * The internal circles.
  1005. * @param {Array<Highcharts.CircleObject>} external
  1006. * The external circles.
  1007. * @return {number}
  1008. * Returns the margin.
  1009. */
  1010. function getMarginFromCircles(point, internal, external) {
  1011. var margin = internal.reduce(function (margin,
  1012. circle) {
  1013. var m = circle.r - getDistanceBetweenPoints(point,
  1014. circle);
  1015. return (m <= margin) ? m : margin;
  1016. }, Number.MAX_VALUE);
  1017. margin = external.reduce(function (margin, circle) {
  1018. var m = getDistanceBetweenPoints(point,
  1019. circle) - circle.r;
  1020. return (m <= margin) ? m : margin;
  1021. }, margin);
  1022. return margin;
  1023. }
  1024. VennUtils.getMarginFromCircles = getMarginFromCircles;
  1025. /**
  1026. * Calculates the area of overlap between a list of circles.
  1027. * @private
  1028. * @todo add support for calculating overlap between more than 2 circles.
  1029. * @param {Array<Highcharts.CircleObject>} circles
  1030. * List of circles with their given positions.
  1031. * @return {number}
  1032. * Returns the area of overlap between all the circles.
  1033. */
  1034. function getOverlapBetweenCircles(circles) {
  1035. var overlap = 0;
  1036. // When there is only two circles we can find the overlap by using their
  1037. // radiuses and the distance between them.
  1038. if (circles.length === 2) {
  1039. var circle1 = circles[0];
  1040. var circle2 = circles[1];
  1041. overlap = getOverlapBetweenCirclesByDistance(circle1.r, circle2.r, getDistanceBetweenPoints(circle1, circle2));
  1042. }
  1043. return overlap;
  1044. }
  1045. // eslint-disable-next-line require-jsdoc
  1046. function isSet(x) {
  1047. return isArray(x.sets) && x.sets.length === 1;
  1048. }
  1049. VennUtils.isSet = isSet;
  1050. // eslint-disable-next-line require-jsdoc
  1051. function isValidRelation(x) {
  1052. var map = {};
  1053. return (isObject(x) &&
  1054. (isNumber(x.value) && x.value > -1) &&
  1055. (isArray(x.sets) && x.sets.length > 0) &&
  1056. !x.sets.some(function (set) {
  1057. var invalid = false;
  1058. if (!map[set] && isString(set)) {
  1059. map[set] = true;
  1060. }
  1061. else {
  1062. invalid = true;
  1063. }
  1064. return invalid;
  1065. }));
  1066. }
  1067. // eslint-disable-next-line require-jsdoc
  1068. function isValidSet(x) {
  1069. return (isValidRelation(x) && isSet(x) && x.value > 0);
  1070. }
  1071. /**
  1072. * Uses a greedy approach to position all the sets. Works well with a small
  1073. * number of sets, and are in these cases a good choice aesthetically.
  1074. * @private
  1075. * @param {Array<object>} relations List of the overlap between two or more
  1076. * sets, or the size of a single set.
  1077. * @return {Array<object>} List of circles and their calculated positions.
  1078. */
  1079. function layoutGreedyVenn(relations) {
  1080. var positionedSets = [],
  1081. mapOfIdToCircles = {};
  1082. // Define a circle for each set.
  1083. relations
  1084. .filter(function (relation) {
  1085. return relation.sets.length === 1;
  1086. }).forEach(function (relation) {
  1087. mapOfIdToCircles[relation.sets[0]] = relation.circle = {
  1088. x: Number.MAX_VALUE,
  1089. y: Number.MAX_VALUE,
  1090. r: Math.sqrt(relation.value / Math.PI)
  1091. };
  1092. });
  1093. /**
  1094. * Takes a set and updates the position, and add the set to the list of
  1095. * positioned sets.
  1096. * @private
  1097. * @param {object} set
  1098. * The set to add to its final position.
  1099. * @param {object} coordinates
  1100. * The coordinates to position the set at.
  1101. * @return {void}
  1102. */
  1103. var positionSet = function positionSet(set,
  1104. coordinates) {
  1105. var circle = set.circle;
  1106. circle.x = coordinates.x;
  1107. circle.y = coordinates.y;
  1108. positionedSets.push(set);
  1109. };
  1110. // Find overlap between sets. Ignore relations with more then 2 sets.
  1111. addOverlapToSets(relations);
  1112. // Sort sets by the sum of their size from large to small.
  1113. var sortedByOverlap = relations
  1114. .filter(isSet)
  1115. .sort(sortByTotalOverlap);
  1116. // Position the most overlapped set at 0,0.
  1117. positionSet(sortedByOverlap.shift(), { x: 0, y: 0 });
  1118. var relationsWithTwoSets = relations.filter(function (x) {
  1119. return x.sets.length === 2;
  1120. });
  1121. // Iterate and position the remaining sets.
  1122. sortedByOverlap.forEach(function (set) {
  1123. var circle = set.circle,
  1124. radius = circle.r,
  1125. overlapping = set.overlapping;
  1126. var bestPosition = positionedSets
  1127. .reduce(function (best,
  1128. positionedSet,
  1129. i) {
  1130. var positionedCircle = positionedSet.circle,
  1131. overlap = overlapping[positionedSet.sets[0]];
  1132. // Calculate the distance between the sets to get the
  1133. // correct overlap
  1134. var distance = getDistanceBetweenCirclesByOverlap(radius,
  1135. positionedCircle.r,
  1136. overlap);
  1137. // Create a list of possible coordinates calculated from
  1138. // distance.
  1139. var possibleCoordinates = [
  1140. { x: positionedCircle.x + distance,
  1141. y: positionedCircle.y },
  1142. { x: positionedCircle.x - distance,
  1143. y: positionedCircle.y },
  1144. { x: positionedCircle.x,
  1145. y: positionedCircle.y + distance },
  1146. { x: positionedCircle.x,
  1147. y: positionedCircle.y - distance }
  1148. ];
  1149. // If there are more circles overlapping, then add the
  1150. // intersection points as possible positions.
  1151. positionedSets.slice(i + 1).forEach(function (positionedSet2) {
  1152. var positionedCircle2 = positionedSet2.circle,
  1153. overlap2 = overlapping[positionedSet2.sets[0]],
  1154. distance2 = getDistanceBetweenCirclesByOverlap(radius,
  1155. positionedCircle2.r,
  1156. overlap2);
  1157. // Add intersections to list of coordinates.
  1158. possibleCoordinates = possibleCoordinates.concat(getCircleCircleIntersection({
  1159. x: positionedCircle.x,
  1160. y: positionedCircle.y,
  1161. r: distance
  1162. }, {
  1163. x: positionedCircle2.x,
  1164. y: positionedCircle2.y,
  1165. r: distance2
  1166. }));
  1167. });
  1168. // Iterate all suggested coordinates and find the best one.
  1169. possibleCoordinates.forEach(function (coordinates) {
  1170. circle.x = coordinates.x;
  1171. circle.y = coordinates.y;
  1172. // Calculate loss for the suggested coordinates.
  1173. var currentLoss = loss(mapOfIdToCircles,
  1174. relationsWithTwoSets);
  1175. // If the loss is better, then use these new coordinates
  1176. if (currentLoss < best.loss) {
  1177. best.loss = currentLoss;
  1178. best.coordinates = coordinates;
  1179. }
  1180. });
  1181. // Return resulting coordinates.
  1182. return best;
  1183. }, {
  1184. loss: Number.MAX_VALUE,
  1185. coordinates: void 0
  1186. });
  1187. // Add the set to its final position.
  1188. positionSet(set, bestPosition.coordinates);
  1189. });
  1190. // Return the positions of each set.
  1191. return mapOfIdToCircles;
  1192. }
  1193. VennUtils.layoutGreedyVenn = layoutGreedyVenn;
  1194. /**
  1195. * Calculates the difference between the desired overlap and the actual
  1196. * overlap between two circles.
  1197. * @private
  1198. * @param {Dictionary<Highcharts.CircleObject>} mapOfIdToCircle
  1199. * Map from id to circle.
  1200. * @param {Array<Highcharts.VennRelationObject>} relations
  1201. * List of relations to calculate the loss of.
  1202. * @return {number}
  1203. * Returns the loss between positions of the circles for the given
  1204. * relations.
  1205. */
  1206. function loss(mapOfIdToCircle, relations) {
  1207. var precision = 10e10;
  1208. // Iterate all the relations and calculate their individual loss.
  1209. return relations.reduce(function (totalLoss, relation) {
  1210. var loss = 0;
  1211. if (relation.sets.length > 1) {
  1212. var wantedOverlap = relation.value;
  1213. // Calculate the actual overlap between the sets.
  1214. var actualOverlap = getOverlapBetweenCircles(
  1215. // Get the circles for the given sets.
  1216. relation.sets.map(function (set) {
  1217. return mapOfIdToCircle[set];
  1218. }));
  1219. var diff = wantedOverlap - actualOverlap;
  1220. loss = Math.round((diff * diff) * precision) / precision;
  1221. }
  1222. // Add calculated loss to the sum.
  1223. return totalLoss + loss;
  1224. }, 0);
  1225. }
  1226. VennUtils.loss = loss;
  1227. /**
  1228. * Prepares the venn data so that it is usable for the layout function.
  1229. * Filter out sets, or intersections that includes sets, that are missing in
  1230. * the data or has (value < 1). Adds missing relations between sets in the
  1231. * data as value = 0.
  1232. * @private
  1233. * @param {Array<object>} data The raw input data.
  1234. * @return {Array<object>} Returns an array of valid venn data.
  1235. */
  1236. function processVennData(data) {
  1237. var d = isArray(data) ? data : [];
  1238. var validSets = d
  1239. .reduce(function (arr,
  1240. x) {
  1241. // Check if x is a valid set, and that it is not an duplicate.
  1242. if (isValidSet(x) && arr.indexOf(x.sets[0]) === -1) {
  1243. arr.push(x.sets[0]);
  1244. }
  1245. return arr;
  1246. }, [])
  1247. .sort();
  1248. var mapOfIdToRelation = d.reduce(function (mapOfIdToRelation,
  1249. relation) {
  1250. if (isValidRelation(relation) &&
  1251. !relation.sets.some(function (set) {
  1252. return validSets.indexOf(set) === -1;
  1253. })) {
  1254. mapOfIdToRelation[relation.sets.sort().join()] =
  1255. relation;
  1256. }
  1257. return mapOfIdToRelation;
  1258. }, {});
  1259. validSets.reduce(function (combinations, set, i, arr) {
  1260. var remaining = arr.slice(i + 1);
  1261. remaining.forEach(function (set2) {
  1262. combinations.push(set + ',' + set2);
  1263. });
  1264. return combinations;
  1265. }, []).forEach(function (combination) {
  1266. if (!mapOfIdToRelation[combination]) {
  1267. var obj = {
  1268. sets: combination.split(','),
  1269. value: 0
  1270. };
  1271. mapOfIdToRelation[combination] = obj;
  1272. }
  1273. });
  1274. // Transform map into array.
  1275. return Object
  1276. .keys(mapOfIdToRelation)
  1277. .map(function (id) {
  1278. return mapOfIdToRelation[id];
  1279. });
  1280. }
  1281. VennUtils.processVennData = processVennData;
  1282. /**
  1283. * Takes two sets and finds the one with the largest total overlap.
  1284. * @private
  1285. * @param {object} a The first set to compare.
  1286. * @param {object} b The second set to compare.
  1287. * @return {number} Returns 0 if a and b are equal, <0 if a is greater, >0 if b
  1288. * is greater.
  1289. */
  1290. function sortByTotalOverlap(a, b) {
  1291. return b.totalOverlap - a.totalOverlap;
  1292. }
  1293. VennUtils.sortByTotalOverlap = sortByTotalOverlap;
  1294. })(VennUtils || (VennUtils = {}));
  1295. /* *
  1296. *
  1297. * Default Export
  1298. *
  1299. * */
  1300. return VennUtils;
  1301. });
  1302. _registerModule(_modules, 'Series/Venn/VennSeries.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Color/Color.js'], _modules['Mixins/Geometry.js'], _modules['Mixins/GeometryCircles.js'], _modules['Mixins/NelderMead.js'], _modules['Core/Color/Palette.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Series/Venn/VennPoint.js'], _modules['Series/Venn/VennUtils.js'], _modules['Core/Utilities.js']], function (A, Color, GeometryMixin, GeometryCirclesModule, NelderMeadMixin, palette, SeriesRegistry, VennPoint, VennUtils, U) {
  1303. /* *
  1304. *
  1305. * Experimental Highcharts module which enables visualization of a Venn
  1306. * diagram.
  1307. *
  1308. * (c) 2016-2021 Highsoft AS
  1309. * Authors: Jon Arild Nygard
  1310. *
  1311. * Layout algorithm by Ben Frederickson:
  1312. * https://www.benfrederickson.com/better-venn-diagrams/
  1313. *
  1314. * License: www.highcharts.com/license
  1315. *
  1316. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1317. *
  1318. * */
  1319. var __extends = (this && this.__extends) || (function () {
  1320. var extendStatics = function (d,
  1321. b) {
  1322. extendStatics = Object.setPrototypeOf ||
  1323. ({ __proto__: [] } instanceof Array && function (d,
  1324. b) { d.__proto__ = b; }) ||
  1325. function (d,
  1326. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  1327. return extendStatics(d, b);
  1328. };
  1329. return function (d, b) {
  1330. extendStatics(d, b);
  1331. function __() { this.constructor = d; }
  1332. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  1333. };
  1334. })();
  1335. var animObject = A.animObject;
  1336. var color = Color.parse;
  1337. var getCenterOfPoints = GeometryMixin.getCenterOfPoints,
  1338. getDistanceBetweenPoints = GeometryMixin.getDistanceBetweenPoints;
  1339. var getAreaOfCircle = GeometryCirclesModule.getAreaOfCircle,
  1340. getAreaOfIntersectionBetweenCircles = GeometryCirclesModule.getAreaOfIntersectionBetweenCircles,
  1341. getCircleCircleIntersection = GeometryCirclesModule.getCircleCircleIntersection,
  1342. getCirclesIntersectionPolygon = GeometryCirclesModule.getCirclesIntersectionPolygon,
  1343. getOverlapBetweenCirclesByDistance = GeometryCirclesModule.getOverlapBetweenCircles,
  1344. isCircle1CompletelyOverlappingCircle2 = GeometryCirclesModule.isCircle1CompletelyOverlappingCircle2,
  1345. isPointInsideAllCircles = GeometryCirclesModule.isPointInsideAllCircles,
  1346. isPointInsideCircle = GeometryCirclesModule.isPointInsideCircle,
  1347. isPointOutsideAllCircles = GeometryCirclesModule.isPointOutsideAllCircles;
  1348. var nelderMead = NelderMeadMixin.nelderMead;
  1349. var ScatterSeries = SeriesRegistry.seriesTypes.scatter;
  1350. var addEvent = U.addEvent,
  1351. extend = U.extend,
  1352. isArray = U.isArray,
  1353. isNumber = U.isNumber,
  1354. isObject = U.isObject,
  1355. isString = U.isString,
  1356. merge = U.merge;
  1357. /* *
  1358. *
  1359. * Class
  1360. *
  1361. * */
  1362. /**
  1363. * @private
  1364. * @class
  1365. * @name Highcharts.seriesTypes.venn
  1366. *
  1367. * @augments Highcharts.Series
  1368. */
  1369. var VennSeries = /** @class */ (function (_super) {
  1370. __extends(VennSeries, _super);
  1371. function VennSeries() {
  1372. /* *
  1373. *
  1374. * Static Properties
  1375. *
  1376. * */
  1377. var _this = _super !== null && _super.apply(this,
  1378. arguments) || this;
  1379. /* *
  1380. *
  1381. * Properties
  1382. *
  1383. * */
  1384. _this.data = void 0;
  1385. _this.mapOfIdToRelation = void 0;
  1386. _this.options = void 0;
  1387. _this.points = void 0;
  1388. return _this;
  1389. /* eslint-enable valid-jsdoc */
  1390. }
  1391. /* *
  1392. *
  1393. * Static Functions
  1394. *
  1395. * */
  1396. /**
  1397. * Finds the optimal label position by looking for a position that has a low
  1398. * distance from the internal circles, and as large possible distane to the
  1399. * external circles.
  1400. * @private
  1401. * @todo Optimize the intial position.
  1402. * @todo Add unit tests.
  1403. * @param {Array<Highcharts.CircleObject>} internal
  1404. * Internal circles.
  1405. * @param {Array<Highcharts.CircleObject>} external
  1406. * External circles.
  1407. * @return {Highcharts.PositionObject}
  1408. * Returns the found position.
  1409. */
  1410. VennSeries.getLabelPosition = function (internal, external) {
  1411. // Get the best label position within the internal circles.
  1412. var best = internal.reduce(function (best,
  1413. circle) {
  1414. var d = circle.r / 2;
  1415. // Give a set of points with the circle to evaluate as the best
  1416. // label position.
  1417. return [
  1418. { x: circle.x, y: circle.y },
  1419. { x: circle.x + d, y: circle.y },
  1420. { x: circle.x - d, y: circle.y },
  1421. { x: circle.x, y: circle.y + d },
  1422. { x: circle.x, y: circle.y - d }
  1423. ]
  1424. // Iterate the given points and return the one with the largest
  1425. // margin.
  1426. .reduce(function (best, point) {
  1427. var margin = VennUtils.getMarginFromCircles(point,
  1428. internal,
  1429. external);
  1430. // If the margin better than the current best, then update
  1431. // sbest.
  1432. if (best.margin < margin) {
  1433. best.point = point;
  1434. best.margin = margin;
  1435. }
  1436. return best;
  1437. }, best);
  1438. }, {
  1439. point: void 0,
  1440. margin: -Number.MAX_VALUE
  1441. }).point;
  1442. // Use nelder mead to optimize the initial label position.
  1443. var optimal = nelderMead(function (p) {
  1444. return -(VennUtils.getMarginFromCircles({ x: p[0],
  1445. y: p[1] },
  1446. internal,
  1447. external));
  1448. }, [best.x, best.y]);
  1449. // Update best to be the point which was found to have the best margin.
  1450. best = {
  1451. x: optimal[0],
  1452. y: optimal[1]
  1453. };
  1454. if (!(isPointInsideAllCircles(best, internal) &&
  1455. isPointOutsideAllCircles(best, external))) {
  1456. // If point was either outside one of the internal, or inside one of
  1457. // the external, then it was invalid and should use a fallback.
  1458. if (internal.length > 1) {
  1459. best = getCenterOfPoints(getCirclesIntersectionPolygon(internal));
  1460. }
  1461. else {
  1462. best = {
  1463. x: internal[0].x,
  1464. y: internal[0].y
  1465. };
  1466. }
  1467. }
  1468. // Return the best point.
  1469. return best;
  1470. };
  1471. /**
  1472. * Calulates data label values for a given relations object.
  1473. *
  1474. * @private
  1475. * @todo add unit tests
  1476. * @param {Highcharts.VennRelationObject} relation A relations object.
  1477. * @param {Array<Highcharts.VennRelationObject>} setRelations The list of
  1478. * relations that is a set.
  1479. * @return {Highcharts.VennLabelValuesObject}
  1480. * Returns an object containing position and width of the label.
  1481. */
  1482. VennSeries.getLabelValues = function (relation, setRelations) {
  1483. var sets = relation.sets;
  1484. // Create a list of internal and external circles.
  1485. var data = setRelations.reduce(function (data,
  1486. set) {
  1487. // If the set exists in this relation, then it is internal,
  1488. // otherwise it will be external.
  1489. var isInternal = sets.indexOf(set.sets[0]) > -1;
  1490. var property = isInternal ? 'internal' : 'external';
  1491. // Add the circle to the list.
  1492. data[property].push(set.circle);
  1493. return data;
  1494. }, {
  1495. internal: [],
  1496. external: []
  1497. });
  1498. // Filter out external circles that are completely overlapping all
  1499. // internal
  1500. data.external = data.external.filter(function (externalCircle) {
  1501. return data.internal.some(function (internalCircle) {
  1502. return !isCircle1CompletelyOverlappingCircle2(externalCircle, internalCircle);
  1503. });
  1504. });
  1505. // Calulate the label position.
  1506. var position = VennSeries.getLabelPosition(data.internal,
  1507. data.external);
  1508. // Calculate the label width
  1509. var width = VennUtils.getLabelWidth(position,
  1510. data.internal,
  1511. data.external);
  1512. return {
  1513. position: position,
  1514. width: width
  1515. };
  1516. };
  1517. /**
  1518. * Calculates the positions, and the label values of all the sets in the
  1519. * venn diagram.
  1520. *
  1521. * @private
  1522. * @todo Add support for constrained MDS.
  1523. * @param {Array<Highchats.VennRelationObject>} relations
  1524. * List of the overlap between two or more sets, or the size of a single
  1525. * sset.
  1526. * @return {Highcharts.Dictionary<*>}
  1527. * List of circles and their calculated positions.
  1528. */
  1529. VennSeries.layout = function (relations) {
  1530. var mapOfIdToShape = {};
  1531. var mapOfIdToLabelValues = {};
  1532. // Calculate best initial positions by using greedy layout.
  1533. if (relations.length > 0) {
  1534. var mapOfIdToCircles_1 = VennUtils.layoutGreedyVenn(relations);
  1535. var setRelations_1 = relations.filter(VennUtils.isSet);
  1536. relations
  1537. .forEach(function (relation) {
  1538. var sets = relation.sets;
  1539. var id = sets.join();
  1540. // Get shape from map of circles, or calculate intersection.
  1541. var shape = VennUtils.isSet(relation) ?
  1542. mapOfIdToCircles_1[id] :
  1543. getAreaOfIntersectionBetweenCircles(sets.map(function (set) {
  1544. return mapOfIdToCircles_1[set];
  1545. }));
  1546. // Calculate label values if the set has a shape
  1547. if (shape) {
  1548. mapOfIdToShape[id] = shape;
  1549. mapOfIdToLabelValues[id] = VennSeries.getLabelValues(relation, setRelations_1);
  1550. }
  1551. });
  1552. }
  1553. return { mapOfIdToShape: mapOfIdToShape, mapOfIdToLabelValues: mapOfIdToLabelValues };
  1554. };
  1555. /**
  1556. * Calculates the proper scale to fit the cloud inside the plotting area.
  1557. * @private
  1558. * @todo add unit test
  1559. * @param {number} targetWidth
  1560. * Width of target area.
  1561. * @param {number} targetHeight
  1562. * Height of target area.
  1563. * @param {Highcharts.PolygonBoxObject} field
  1564. * The playing field.
  1565. * @return {Highcharts.Dictionary<number>}
  1566. * Returns the value to scale the playing field up to the size of the target
  1567. * area, and center of x and y.
  1568. */
  1569. VennSeries.getScale = function (targetWidth, targetHeight, field) {
  1570. var height = field.bottom - field.top, // top is smaller than bottom
  1571. width = field.right - field.left,
  1572. scaleX = width > 0 ? 1 / width * targetWidth : 1,
  1573. scaleY = height > 0 ? 1 / height * targetHeight : 1,
  1574. adjustX = (field.right + field.left) / 2,
  1575. adjustY = (field.top + field.bottom) / 2,
  1576. scale = Math.min(scaleX,
  1577. scaleY);
  1578. return {
  1579. scale: scale,
  1580. centerX: targetWidth / 2 - adjustX * scale,
  1581. centerY: targetHeight / 2 - adjustY * scale
  1582. };
  1583. };
  1584. /**
  1585. * If a circle is outside a give field, then the boundaries of the field is
  1586. * adjusted accordingly. Modifies the field object which is passed as the
  1587. * first parameter.
  1588. * @private
  1589. * @todo NOTE: Copied from wordcloud, can probably be unified.
  1590. * @param {Highcharts.PolygonBoxObject} field
  1591. * The bounding box of a playing field.
  1592. * @param {Highcharts.CircleObject} circle
  1593. * The bounding box for a placed point.
  1594. * @return {Highcharts.PolygonBoxObject}
  1595. * Returns a modified field object.
  1596. */
  1597. VennSeries.updateFieldBoundaries = function (field, circle) {
  1598. var left = circle.x - circle.r,
  1599. right = circle.x + circle.r,
  1600. bottom = circle.y + circle.r,
  1601. top = circle.y - circle.r;
  1602. // TODO improve type checking.
  1603. if (!isNumber(field.left) || field.left > left) {
  1604. field.left = left;
  1605. }
  1606. if (!isNumber(field.right) || field.right < right) {
  1607. field.right = right;
  1608. }
  1609. if (!isNumber(field.top) || field.top > top) {
  1610. field.top = top;
  1611. }
  1612. if (!isNumber(field.bottom) || field.bottom < bottom) {
  1613. field.bottom = bottom;
  1614. }
  1615. return field;
  1616. };
  1617. /* *
  1618. *
  1619. * Functions
  1620. *
  1621. * */
  1622. /* eslint-disable valid-jsdoc */
  1623. VennSeries.prototype.animate = function (init) {
  1624. if (!init) {
  1625. var series = this,
  1626. animOptions_1 = animObject(series.options.animation);
  1627. series.points.forEach(function (point) {
  1628. var args = point.shapeArgs;
  1629. if (point.graphic && args) {
  1630. var attr = {},
  1631. animate = {};
  1632. if (args.d) {
  1633. // If shape is a path, then animate opacity.
  1634. attr.opacity = 0.001;
  1635. }
  1636. else {
  1637. // If shape is a circle, then animate radius.
  1638. attr.r = 0;
  1639. animate.r = args.r;
  1640. }
  1641. point.graphic
  1642. .attr(attr)
  1643. .animate(animate, animOptions_1);
  1644. // If shape is path, then fade it in after the circles
  1645. // animation
  1646. if (args.d) {
  1647. setTimeout(function () {
  1648. if (point && point.graphic) {
  1649. point.graphic.animate({
  1650. opacity: 1
  1651. });
  1652. }
  1653. }, animOptions_1.duration);
  1654. }
  1655. }
  1656. }, series);
  1657. }
  1658. };
  1659. /**
  1660. * Draw the graphics for each point.
  1661. * @private
  1662. */
  1663. VennSeries.prototype.drawPoints = function () {
  1664. var series = this,
  1665. // Series properties
  1666. chart = series.chart,
  1667. group = series.group,
  1668. points = series.points || [],
  1669. // Chart properties
  1670. renderer = chart.renderer;
  1671. // Iterate all points and calculate and draw their graphics.
  1672. points.forEach(function (point) {
  1673. var attribs = {
  1674. zIndex: isArray(point.sets) ? point.sets.length : 0
  1675. },
  1676. shapeArgs = point.shapeArgs;
  1677. // Add point attribs
  1678. if (!chart.styledMode) {
  1679. extend(attribs, series.pointAttribs(point, point.state));
  1680. }
  1681. // Draw the point graphic.
  1682. point.draw({
  1683. isNew: !point.graphic,
  1684. animatableAttribs: shapeArgs,
  1685. attribs: attribs,
  1686. group: group,
  1687. renderer: renderer,
  1688. shapeType: shapeArgs && shapeArgs.d ? 'path' : 'circle'
  1689. });
  1690. });
  1691. };
  1692. VennSeries.prototype.init = function () {
  1693. ScatterSeries.prototype.init.apply(this, arguments);
  1694. // Venn's opacity is a different option from other series
  1695. delete this.opacity;
  1696. };
  1697. /**
  1698. * Calculates the style attributes for a point. The attributes can vary
  1699. * depending on the state of the point.
  1700. * @private
  1701. * @param {Highcharts.Point} point
  1702. * The point which will get the resulting attributes.
  1703. * @param {string} [state]
  1704. * The state of the point.
  1705. * @return {Highcharts.SVGAttributes}
  1706. * Returns the calculated attributes.
  1707. */
  1708. VennSeries.prototype.pointAttribs = function (point, state) {
  1709. var series = this,
  1710. seriesOptions = series.options || {},
  1711. pointOptions = point && point.options || {},
  1712. stateOptions = (state && seriesOptions.states[state]) || {},
  1713. options = merge(seriesOptions, { color: point && point.color },
  1714. pointOptions,
  1715. stateOptions);
  1716. // Return resulting values for the attributes.
  1717. return {
  1718. 'fill': color(options.color)
  1719. .brighten(options.brightness)
  1720. .get(),
  1721. // Set opacity directly to the SVG element, not to pattern #14372.
  1722. opacity: options.opacity,
  1723. 'stroke': options.borderColor,
  1724. 'stroke-width': options.borderWidth,
  1725. 'dashstyle': options.borderDashStyle
  1726. };
  1727. };
  1728. VennSeries.prototype.translate = function () {
  1729. var chart = this.chart;
  1730. this.processedXData = this.xData;
  1731. this.generatePoints();
  1732. // Process the data before passing it into the layout function.
  1733. var relations = VennUtils.processVennData(this.options.data);
  1734. // Calculate the positions of each circle.
  1735. var _a = VennSeries.layout(relations),
  1736. mapOfIdToShape = _a.mapOfIdToShape,
  1737. mapOfIdToLabelValues = _a.mapOfIdToLabelValues;
  1738. // Calculate the scale, and center of the plot area.
  1739. var field = Object.keys(mapOfIdToShape)
  1740. .filter(function (key) {
  1741. var shape = mapOfIdToShape[key];
  1742. return shape && isNumber(shape.r);
  1743. })
  1744. .reduce(function (field, key) {
  1745. return VennSeries.updateFieldBoundaries(field, mapOfIdToShape[key]);
  1746. }, { top: 0, bottom: 0, left: 0, right: 0 }), scaling = VennSeries.getScale(chart.plotWidth, chart.plotHeight, field), scale = scaling.scale, centerX = scaling.centerX, centerY = scaling.centerY;
  1747. // Iterate all points and calculate and draw their graphics.
  1748. this.points.forEach(function (point) {
  1749. var sets = isArray(point.sets) ? point.sets : [],
  1750. id = sets.join(),
  1751. shape = mapOfIdToShape[id],
  1752. shapeArgs,
  1753. dataLabelValues = mapOfIdToLabelValues[id] || {},
  1754. dataLabelWidth = dataLabelValues.width,
  1755. dataLabelPosition = dataLabelValues.position,
  1756. dlOptions = point.options && point.options.dataLabels;
  1757. if (shape) {
  1758. if (shape.r) {
  1759. shapeArgs = {
  1760. x: centerX + shape.x * scale,
  1761. y: centerY + shape.y * scale,
  1762. r: shape.r * scale
  1763. };
  1764. }
  1765. else if (shape.d) {
  1766. var d = shape.d;
  1767. d.forEach(function (seg) {
  1768. if (seg[0] === 'M') {
  1769. seg[1] = centerX + seg[1] * scale;
  1770. seg[2] = centerY + seg[2] * scale;
  1771. }
  1772. else if (seg[0] === 'A') {
  1773. seg[1] = seg[1] * scale;
  1774. seg[2] = seg[2] * scale;
  1775. seg[6] = centerX + seg[6] * scale;
  1776. seg[7] = centerY + seg[7] * scale;
  1777. }
  1778. });
  1779. shapeArgs = { d: d };
  1780. }
  1781. // Scale the position for the data label.
  1782. if (dataLabelPosition) {
  1783. dataLabelPosition.x = centerX + dataLabelPosition.x * scale;
  1784. dataLabelPosition.y = centerY + dataLabelPosition.y * scale;
  1785. }
  1786. else {
  1787. dataLabelPosition = {};
  1788. }
  1789. if (isNumber(dataLabelWidth)) {
  1790. dataLabelWidth = Math.round(dataLabelWidth * scale);
  1791. }
  1792. }
  1793. point.shapeArgs = shapeArgs;
  1794. // Placement for the data labels
  1795. if (dataLabelPosition && shapeArgs) {
  1796. point.plotX = dataLabelPosition.x;
  1797. point.plotY = dataLabelPosition.y;
  1798. }
  1799. // Add width for the data label
  1800. if (dataLabelWidth && shapeArgs) {
  1801. point.dlOptions = merge(true, {
  1802. style: {
  1803. width: dataLabelWidth
  1804. }
  1805. }, isObject(dlOptions, true) ? dlOptions : void 0);
  1806. }
  1807. // Set name for usage in tooltip and in data label.
  1808. point.name = point.options.name || sets.join('∩');
  1809. });
  1810. };
  1811. /**
  1812. * A Venn diagram displays all possible logical relations between a
  1813. * collection of different sets. The sets are represented by circles, and
  1814. * the relation between the sets are displayed by the overlap or lack of
  1815. * overlap between them. The venn diagram is a special case of Euler
  1816. * diagrams, which can also be displayed by this series type.
  1817. *
  1818. * @sample {highcharts} highcharts/demo/venn-diagram/
  1819. * Venn diagram
  1820. * @sample {highcharts} highcharts/demo/euler-diagram/
  1821. * Euler diagram
  1822. *
  1823. * @extends plotOptions.scatter
  1824. * @excluding connectEnds, connectNulls, cropThreshold, dragDrop,
  1825. * findNearestPointBy, getExtremesFromAll, jitter, label,
  1826. * linecap, lineWidth, linkedTo, marker, negativeColor,
  1827. * pointInterval, pointIntervalUnit, pointPlacement,
  1828. * pointStart, softThreshold, stacking, steps, threshold,
  1829. * xAxis, yAxis, zoneAxis, zones, dataSorting, boostThreshold,
  1830. * boostBlending
  1831. * @product highcharts
  1832. * @requires modules/venn
  1833. * @optionparent plotOptions.venn
  1834. */
  1835. VennSeries.defaultOptions = merge(ScatterSeries.defaultOptions, {
  1836. borderColor: palette.neutralColor20,
  1837. borderDashStyle: 'solid',
  1838. borderWidth: 1,
  1839. brighten: 0,
  1840. clip: false,
  1841. colorByPoint: true,
  1842. dataLabels: {
  1843. enabled: true,
  1844. verticalAlign: 'middle',
  1845. formatter: function () {
  1846. return this.point.name;
  1847. }
  1848. },
  1849. /**
  1850. * @ignore-option
  1851. * @private
  1852. */
  1853. inactiveOtherPoints: true,
  1854. marker: false,
  1855. opacity: 0.75,
  1856. showInLegend: false,
  1857. states: {
  1858. /**
  1859. * @excluding halo
  1860. */
  1861. hover: {
  1862. opacity: 1,
  1863. borderColor: palette.neutralColor80
  1864. },
  1865. /**
  1866. * @excluding halo
  1867. */
  1868. select: {
  1869. color: palette.neutralColor20,
  1870. borderColor: palette.neutralColor100,
  1871. animation: false
  1872. },
  1873. inactive: {
  1874. opacity: 0.075
  1875. }
  1876. },
  1877. tooltip: {
  1878. pointFormat: '{point.name}: {point.value}'
  1879. }
  1880. });
  1881. return VennSeries;
  1882. }(ScatterSeries));
  1883. extend(VennSeries.prototype, {
  1884. axisTypes: [],
  1885. directTouch: true,
  1886. isCartesian: false,
  1887. pointArrayMap: ['value'],
  1888. pointClass: VennPoint,
  1889. utils: VennUtils
  1890. });
  1891. SeriesRegistry.registerSeriesType('venn', VennSeries);
  1892. /* *
  1893. *
  1894. * Default Export
  1895. *
  1896. * */
  1897. /* *
  1898. *
  1899. * API Options
  1900. *
  1901. * */
  1902. /**
  1903. * A `venn` series. If the [type](#series.venn.type) option is
  1904. * not specified, it is inherited from [chart.type](#chart.type).
  1905. *
  1906. * @extends series,plotOptions.venn
  1907. * @excluding connectEnds, connectNulls, cropThreshold, dataParser, dataURL,
  1908. * findNearestPointBy, getExtremesFromAll, label, linecap, lineWidth,
  1909. * linkedTo, marker, negativeColor, pointInterval, pointIntervalUnit,
  1910. * pointPlacement, pointStart, softThreshold, stack, stacking, steps,
  1911. * threshold, xAxis, yAxis, zoneAxis, zones, dataSorting,
  1912. * boostThreshold, boostBlending
  1913. * @product highcharts
  1914. * @requires modules/venn
  1915. * @apioption series.venn
  1916. */
  1917. /**
  1918. * @type {Array<*>}
  1919. * @extends series.scatter.data
  1920. * @excluding marker, x, y
  1921. * @product highcharts
  1922. * @apioption series.venn.data
  1923. */
  1924. /**
  1925. * The name of the point. Used in data labels and tooltip. If name is not
  1926. * defined then it will default to the joined values in
  1927. * [sets](#series.venn.sets).
  1928. *
  1929. * @sample {highcharts} highcharts/demo/venn-diagram/
  1930. * Venn diagram
  1931. * @sample {highcharts} highcharts/demo/euler-diagram/
  1932. * Euler diagram
  1933. *
  1934. * @type {number}
  1935. * @since 7.0.0
  1936. * @product highcharts
  1937. * @apioption series.venn.data.name
  1938. */
  1939. /**
  1940. * The value of the point, resulting in a relative area of the circle, or area
  1941. * of overlap between two sets in the venn or euler diagram.
  1942. *
  1943. * @sample {highcharts} highcharts/demo/venn-diagram/
  1944. * Venn diagram
  1945. * @sample {highcharts} highcharts/demo/euler-diagram/
  1946. * Euler diagram
  1947. *
  1948. * @type {number}
  1949. * @since 7.0.0
  1950. * @product highcharts
  1951. * @apioption series.venn.data.value
  1952. */
  1953. /**
  1954. * The set or sets the options will be applied to. If a single entry is defined,
  1955. * then it will create a new set. If more than one entry is defined, then it
  1956. * will define the overlap between the sets in the array.
  1957. *
  1958. * @sample {highcharts} highcharts/demo/venn-diagram/
  1959. * Venn diagram
  1960. * @sample {highcharts} highcharts/demo/euler-diagram/
  1961. * Euler diagram
  1962. *
  1963. * @type {Array<string>}
  1964. * @since 7.0.0
  1965. * @product highcharts
  1966. * @apioption series.venn.data.sets
  1967. */
  1968. /**
  1969. * @excluding halo
  1970. * @apioption series.venn.states.hover
  1971. */
  1972. /**
  1973. * @excluding halo
  1974. * @apioption series.venn.states.select
  1975. */
  1976. ''; // detach doclets above
  1977. /* eslint-disable no-invalid-this */
  1978. // Modify final series options.
  1979. addEvent(VennSeries, 'afterSetOptions', function (e) {
  1980. var options = e.options,
  1981. states = options.states;
  1982. if (this.is('venn')) {
  1983. // Explicitly disable all halo options.
  1984. Object.keys(states).forEach(function (state) {
  1985. states[state].halo = false;
  1986. });
  1987. }
  1988. });
  1989. return VennSeries;
  1990. });
  1991. _registerModule(_modules, 'masters/modules/venn.src.js', [], function () {
  1992. });
  1993. }));